Prevent a buffer overrun error when attempting to parse a corrupt ELF file.
[deliverable/binutils-gdb.git] / bfd / elf64-ppc.c
index 5826c2288aa988b57a183007e71d095267a1dc78..5be3fe00f18dc75a0c7e3bcf9967a8f909567bd9 100644 (file)
@@ -1,5 +1,5 @@
 /* PowerPC64-specific support for 64-bit ELF.
-   Copyright (C) 1999-2018 Free Software Foundation, Inc.
+   Copyright (C) 1999-2019 Free Software Foundation, Inc.
    Written by Linus Nordberg, Swox AB <info@swox.com>,
    based on elf32-ppc.c by Ian Lance Taylor.
    Largely rewritten by Alan Modra.
@@ -6193,6 +6193,9 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info,
   struct ppc_link_hash_entry *eh;
   _bfd_elf_link_hash_hide_symbol (info, h, force_local);
 
+  if (ppc_hash_table (info) == NULL)
+    return;
+
   eh = (struct ppc_link_hash_entry *) h;
   if (eh->is_func_descriptor)
     {
@@ -9626,6 +9629,86 @@ size_offset (bfd_vma off)
   return size + 16;
 }
 
+static unsigned int
+num_relocs_for_offset (bfd_vma off)
+{
+  unsigned int num_rel;
+  if (off + 0x8000 < 0x10000)
+    num_rel = 1;
+  else if (off + 0x80008000ULL < 0x100000000ULL)
+    num_rel = 2;
+  else
+    {
+      num_rel = 1;
+      if (off + 0x800000000000ULL >= 0x1000000000000ULL
+         && ((off >> 32) & 0xffff) != 0)
+       num_rel += 1;
+      if (PPC_HI (off) != 0)
+       num_rel += 1;
+      if (PPC_LO (off) != 0)
+       num_rel += 1;
+    }
+  return num_rel;
+}
+
+static Elf_Internal_Rela *
+emit_relocs_for_offset (struct bfd_link_info *info, Elf_Internal_Rela *r,
+                       bfd_vma roff, bfd_vma targ, bfd_vma off)
+{
+  bfd_vma relative_targ = targ - (roff - 8);
+  if (bfd_big_endian (info->output_bfd))
+    roff += 2;
+  r->r_offset = roff;
+  r->r_addend = relative_targ + roff;
+  if (off + 0x8000 < 0x10000)
+    r->r_info = ELF64_R_INFO (0, R_PPC64_REL16);
+  else if (off + 0x80008000ULL < 0x100000000ULL)
+    {
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HA);
+      ++r;
+      roff += 4;
+      r->r_offset = roff;
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_LO);
+      r->r_addend = relative_targ + roff;
+    }
+  else
+    {
+      if (off + 0x800000000000ULL < 0x1000000000000ULL)
+       r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHER);
+      else
+       {
+         r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHEST);
+         if (((off >> 32) & 0xffff) != 0)
+           {
+             ++r;
+             roff += 4;
+             r->r_offset = roff;
+             r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHER);
+             r->r_addend = relative_targ + roff;
+           }
+       }
+      if (((off >> 32) & 0xffffffffULL) != 0)
+       roff += 4;
+      if (PPC_HI (off) != 0)
+       {
+         ++r;
+         roff += 4;
+         r->r_offset = roff;
+         r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGH);
+         r->r_addend = relative_targ + roff;
+       }
+      if (PPC_LO (off) != 0)
+       {
+         ++r;
+         roff += 4;
+         r->r_offset = roff;
+         r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_LO);
+         r->r_addend = relative_targ + roff;
+       }
+    }
+  return r;
+}
+
 /* Emit .eh_frame opcode to advance pc by DELTA.  */
 
 static bfd_byte *
@@ -10049,6 +10132,65 @@ get_relocs (asection *sec, int count)
   return relocs;
 }
 
+/* Convert the relocs R[0] thru R[-NUM_REL+1], which are all no-symbol
+   forms, to the equivalent relocs against the global symbol given by
+   STUB_ENTRY->H.  */
+
+static bfd_boolean
+use_global_in_relocs (struct ppc_link_hash_table *htab,
+                     struct ppc_stub_hash_entry *stub_entry,
+                     Elf_Internal_Rela *r, unsigned int num_rel)
+{
+  struct elf_link_hash_entry **hashes;
+  unsigned long symndx;
+  struct ppc_link_hash_entry *h;
+  bfd_vma symval;
+
+  /* Relocs are always against symbols in their own object file.  Fake
+     up global sym hashes for the stub bfd (which has no symbols).  */
+  hashes = elf_sym_hashes (htab->params->stub_bfd);
+  if (hashes == NULL)
+    {
+      bfd_size_type hsize;
+
+      /* When called the first time, stub_globals will contain the
+        total number of symbols seen during stub sizing.  After
+        allocating, stub_globals is used as an index to fill the
+        hashes array.  */
+      hsize = (htab->stub_globals + 1) * sizeof (*hashes);
+      hashes = bfd_zalloc (htab->params->stub_bfd, hsize);
+      if (hashes == NULL)
+       return FALSE;
+      elf_sym_hashes (htab->params->stub_bfd) = hashes;
+      htab->stub_globals = 1;
+    }
+  symndx = htab->stub_globals++;
+  h = stub_entry->h;
+  hashes[symndx] = &h->elf;
+  if (h->oh != NULL && h->oh->is_func)
+    h = ppc_follow_link (h->oh);
+  BFD_ASSERT (h->elf.root.type == bfd_link_hash_defined
+             || h->elf.root.type == bfd_link_hash_defweak);
+  symval = (h->elf.root.u.def.value
+           + h->elf.root.u.def.section->output_offset
+           + h->elf.root.u.def.section->output_section->vma);
+  while (num_rel-- != 0)
+    {
+      r->r_info = ELF64_R_INFO (symndx, ELF64_R_TYPE (r->r_info));
+      if (h->elf.root.u.def.section != stub_entry->target_section)
+       {
+         /* H is an opd symbol.  The addend must be zero, and the
+            branch reloc is the only one we can convert.  */
+         r->r_addend = 0;
+         break;
+       }
+      else
+       r->r_addend -= symval;
+      --r;
+    }
+  return TRUE;
+}
+
 static bfd_vma
 get_r2off (struct bfd_link_info *info,
           struct ppc_stub_hash_entry *stub_entry)
@@ -10092,10 +10234,11 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   struct bfd_link_info *info;
   struct ppc_link_hash_table *htab;
   bfd_byte *loc;
-  bfd_byte *p;
+  bfd_byte *p, *relp;
   bfd_vma targ, off;
   Elf_Internal_Rela *r;
   asection *plt;
+  int num_rel;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
@@ -10171,41 +10314,9 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          r->r_offset = p - 4 - stub_entry->group->stub_sec->contents;
          r->r_info = ELF64_R_INFO (0, R_PPC64_REL24);
          r->r_addend = targ;
-         if (stub_entry->h != NULL)
-           {
-             struct elf_link_hash_entry **hashes;
-             unsigned long symndx;
-             struct ppc_link_hash_entry *h;
-
-             hashes = elf_sym_hashes (htab->params->stub_bfd);
-             if (hashes == NULL)
-               {
-                 bfd_size_type hsize;
-
-                 hsize = (htab->stub_globals + 1) * sizeof (*hashes);
-                 hashes = bfd_zalloc (htab->params->stub_bfd, hsize);
-                 if (hashes == NULL)
-                   return FALSE;
-                 elf_sym_hashes (htab->params->stub_bfd) = hashes;
-                 htab->stub_globals = 1;
-               }
-             symndx = htab->stub_globals++;
-             h = stub_entry->h;
-             hashes[symndx] = &h->elf;
-             r->r_info = ELF64_R_INFO (symndx, R_PPC64_REL24);
-             if (h->oh != NULL && h->oh->is_func)
-               h = ppc_follow_link (h->oh);
-             if (h->elf.root.u.def.section != stub_entry->target_section)
-               /* H is an opd symbol.  The addend must be zero.  */
-               r->r_addend = 0;
-             else
-               {
-                 off = (h->elf.root.u.def.value
-                        + h->elf.root.u.def.section->output_offset
-                        + h->elf.root.u.def.section->output_section->vma);
-                 r->r_addend -= off;
-               }
-           }
+         if (stub_entry->h != NULL
+             && !use_global_in_relocs (htab, stub_entry, r, 1))
+           return FALSE;
        }
       break;
 
@@ -10408,6 +10519,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                + stub_entry->target_section->output_section->vma);
       off = targ - off;
 
+      relp = p;
+      num_rel = 0;
       /* The notoc stubs calculate their target (either a PLT entry or
         the global entry point of a function) relative to the PC
         returned by the "bcl" two instructions past the start of the
@@ -10419,6 +10532,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       if (stub_entry->stub_type <= ppc_stub_long_branch_both)
        {
          bfd_vma from;
+         num_rel = 1;
          from = (stub_entry->stub_offset
                  + stub_entry->group->stub_sec->output_offset
                  + stub_entry->group->stub_sec->output_section->vma
@@ -10434,6 +10548,29 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
        }
       p += 4;
 
+      if (info->emitrelocations)
+       {
+         bfd_vma roff;
+         num_rel += num_relocs_for_offset (off);
+         r = get_relocs (stub_entry->group->stub_sec, num_rel);
+         if (r == NULL)
+           return FALSE;
+         roff = relp + 16 - stub_entry->group->stub_sec->contents;
+         r = emit_relocs_for_offset (info, r, roff, targ, off);
+         if (stub_entry->stub_type == ppc_stub_long_branch_notoc
+             || stub_entry->stub_type == ppc_stub_long_branch_both)
+           {
+             ++r;
+             roff = p - 4 - stub_entry->group->stub_sec->contents;
+             r->r_offset = roff;
+             r->r_info = ELF64_R_INFO (0, R_PPC64_REL24);
+             r->r_addend = targ;
+             if (stub_entry->h != NULL
+                 && !use_global_in_relocs (htab, stub_entry, r, num_rel))
+               return FALSE;
+           }
+       }
+
       if (htab->glink_eh_frame != NULL
        && htab->glink_eh_frame->size != 0)
        {
@@ -10759,6 +10896,13 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              + stub_entry->target_section->output_section->vma);
       off = targ - off;
 
+      if (info->emitrelocations)
+       {
+         stub_entry->group->stub_sec->reloc_count
+           += num_relocs_for_offset (off);
+         stub_entry->group->stub_sec->flags |= SEC_RELOC;
+       }
+
       extra = size_offset (off - 8);
       /* Include branch insn plus those in the offset sequence.  */
       size += 4 + extra;
@@ -10786,6 +10930,8 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                                    - ppc_stub_long_branch_notoc);
          size += 4;
        }
+      else if (info->emitrelocations)
+       stub_entry->group->stub_sec->reloc_count +=1;
       break;
 
     case ppc_stub_plt_call_notoc:
@@ -10821,6 +10967,13 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          off -= pad;
        }
 
+      if (info->emitrelocations)
+       {
+         stub_entry->group->stub_sec->reloc_count
+           += num_relocs_for_offset (off - 8);
+         stub_entry->group->stub_sec->flags |= SEC_RELOC;
+       }
+
       size = plt_stub_size (htab, stub_entry, off);
 
       /* After the bcl, lr has been modified so we need to emit
@@ -12184,7 +12337,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                    = hash ? hash->elf.type : ELF_ST_TYPE (sym->st_info);
                  stub_entry->other = hash ? hash->elf.other : sym->st_other;
 
-                 if (stub_entry->h != NULL)
+                 if (hash != NULL
+                     && (hash->elf.root.type == bfd_link_hash_defined
+                         || hash->elf.root.type == bfd_link_hash_defweak))
                    htab->stub_globals += 1;
                }
 
@@ -12730,7 +12885,8 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info)
                }
 
              val = sym->st_value + ent->addend;
-             val += PPC64_LOCAL_ENTRY_OFFSET (sym->st_other);
+             if (ELF_ST_TYPE (sym->st_info) != STT_GNU_IFUNC)
+               val += PPC64_LOCAL_ENTRY_OFFSET (sym->st_other);
              if (sym_sec != NULL && sym_sec->output_section != NULL)
                val += sym_sec->output_offset + sym_sec->output_section->vma;
 
@@ -13204,7 +13360,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   if (input_section->owner == htab->params->stub_bfd)
     return TRUE;
 
-  BFD_ASSERT (is_ppc64_elf (input_bfd));
+  if (!is_ppc64_elf (input_bfd))
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return FALSE;
+    }
 
   local_got_ents = elf_local_got_ents (input_bfd);
   TOCstart = elf_gp (output_bfd);
@@ -13339,7 +13499,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        {
          _bfd_clear_contents (ppc64_elf_howto_table[r_type],
                               input_bfd, input_section,
-                              contents + rel->r_offset);
+                              contents, rel->r_offset);
          wrel->r_offset = rel->r_offset;
          wrel->r_info = 0;
          wrel->r_addend = 0;
@@ -13681,7 +13841,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              bfd_put_32 (input_bfd, insn1,
                          contents + rel->r_offset - d_offset);
              if (offset != (bfd_vma) -1)
-               bfd_put_32 (input_bfd, insn2, contents + offset);
+               {
+                 bfd_put_32 (input_bfd, insn2, contents + offset);
+                 if (offset + 8 <= input_section->size)
+                   {
+                     insn2 = bfd_get_32 (input_bfd, contents + offset + 4);
+                     if (insn2 == LD_R2_0R1 + STK_TOC (htab))
+                       bfd_put_32 (input_bfd, NOP, contents + offset + 4);
+                   }
+               }
              if ((tls_mask & tls_gd) == 0
                  && (tls_gd == 0 || toc_symndx != 0))
                {
This page took 0.030723 seconds and 4 git commands to generate.