Touches most files in bfd/, so likely will be blamed for everything..
[deliverable/binutils-gdb.git] / bfd / elf64-x86-64.c
index aedd2b8134f18ba57b41e81348df104c799257d1..24eb9f76a0b2fe4612ed60dff3bfe3b9669f05cd 100644 (file)
@@ -37,29 +37,41 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 static reloc_howto_type x86_64_elf_howto_table[] =
 {
   HOWTO(R_X86_64_NONE, 0, 0, 0, false, 0, complain_overflow_dont,
-       bfd_elf_generic_reloc, "R_X86_64_NONE", false, 0x00000000, 0x00000000, false),
+       bfd_elf_generic_reloc, "R_X86_64_NONE", false, 0x00000000, 0x00000000,
+       false),
   HOWTO(R_X86_64_64, 0, 4, 64, false, 0, complain_overflow_bitfield,
-       bfd_elf_generic_reloc, "R_X86_64_64", false, MINUS_ONE, MINUS_ONE, false),
+       bfd_elf_generic_reloc, "R_X86_64_64", false, MINUS_ONE, MINUS_ONE,
+       false),
   HOWTO(R_X86_64_PC32, 0, 4, 32, true, 0, complain_overflow_signed,
-       bfd_elf_generic_reloc, "R_X86_64_PC32", false, 0xffffffff, 0xffffffff, true),
+       bfd_elf_generic_reloc, "R_X86_64_PC32", false, 0xffffffff, 0xffffffff,
+       true),
   HOWTO(R_X86_64_GOT32, 0, 4, 32, false, 0, complain_overflow_signed,
-       bfd_elf_generic_reloc, "R_X86_64_GOT32", false, 0xffffffff, 0xffffffff, false),
+       bfd_elf_generic_reloc, "R_X86_64_GOT32", false, 0xffffffff, 0xffffffff,
+       false),
   HOWTO(R_X86_64_PLT32, 0, 4, 32, true, 0, complain_overflow_signed,
-       bfd_elf_generic_reloc, "R_X86_64_PLT32", false, 0xffffffff, 0xffffffff, true),
+       bfd_elf_generic_reloc, "R_X86_64_PLT32", false, 0xffffffff, 0xffffffff,
+       true),
   HOWTO(R_X86_64_COPY, 0, 4, 32, false, 0, complain_overflow_bitfield,
-       bfd_elf_generic_reloc, "R_X86_64_COPY", false, 0xffffffff, 0xffffffff, false),
+       bfd_elf_generic_reloc, "R_X86_64_COPY", false, 0xffffffff, 0xffffffff,
+       false),
   HOWTO(R_X86_64_GLOB_DAT, 0, 4, 64, false, 0, complain_overflow_bitfield,
-       bfd_elf_generic_reloc, "R_X86_64_GLOB_DAT", false, MINUS_ONE, MINUS_ONE, false),
+       bfd_elf_generic_reloc, "R_X86_64_GLOB_DAT", false, MINUS_ONE,
+       MINUS_ONE, false),
   HOWTO(R_X86_64_JUMP_SLOT, 0, 4, 64, false, 0, complain_overflow_bitfield,
-       bfd_elf_generic_reloc, "R_X86_64_JUMP_SLOT", false, MINUS_ONE, MINUS_ONE, false),
+       bfd_elf_generic_reloc, "R_X86_64_JUMP_SLOT", false, MINUS_ONE,
+       MINUS_ONE, false),
   HOWTO(R_X86_64_RELATIVE, 0, 4, 64, false, 0, complain_overflow_bitfield,
-       bfd_elf_generic_reloc, "R_X86_64_RELATIVE", false, MINUS_ONE, MINUS_ONE, false),
+       bfd_elf_generic_reloc, "R_X86_64_RELATIVE", false, MINUS_ONE,
+       MINUS_ONE, false),
   HOWTO(R_X86_64_GOTPCREL, 0, 4, 32, true,0 , complain_overflow_signed,
-       bfd_elf_generic_reloc, "R_X86_64_GOTPCREL", false, 0xffffffff, 0xffffffff, true),
+       bfd_elf_generic_reloc, "R_X86_64_GOTPCREL", false, 0xffffffff,
+       0xffffffff, true),
   HOWTO(R_X86_64_32, 0, 4, 32, false, 0, complain_overflow_unsigned,
-       bfd_elf_generic_reloc, "R_X86_64_32", false, 0xffffffff, 0xffffffff, false),
+       bfd_elf_generic_reloc, "R_X86_64_32", false, 0xffffffff, 0xffffffff,
+       false),
   HOWTO(R_X86_64_32S, 0, 4, 32, false, 0, complain_overflow_signed,
-       bfd_elf_generic_reloc, "R_X86_64_32S", false, 0xffffffff, 0xffffffff, false),
+       bfd_elf_generic_reloc, "R_X86_64_32S", false, 0xffffffff, 0xffffffff,
+       false),
   HOWTO(R_X86_64_16, 0, 1, 16, false, 0, complain_overflow_bitfield,
        bfd_elf_generic_reloc, "R_X86_64_16", false, 0xffff, 0xffff, false),
   HOWTO(R_X86_64_PC16,0, 1, 16, true, 0, complain_overflow_bitfield,
@@ -67,7 +79,16 @@ static reloc_howto_type x86_64_elf_howto_table[] =
   HOWTO(R_X86_64_8, 0, 0, 8, false, 0, complain_overflow_signed,
        bfd_elf_generic_reloc, "R_X86_64_8", false, 0xff, 0xff, false),
   HOWTO(R_X86_64_PC8, 0, 0, 8, true, 0, complain_overflow_signed,
-       bfd_elf_generic_reloc, "R_X86_64_PC8", false, 0xff, 0xff, true)
+       bfd_elf_generic_reloc, "R_X86_64_PC8", false, 0xff, 0xff, true),
+
+/* GNU extension to record C++ vtable hierarchy.  */
+  HOWTO (R_X86_64_GNU_VTINHERIT, 0, 4, 0, false, 0, complain_overflow_dont,
+        NULL, "R_X86_64_GNU_VTINHERIT", false, 0, 0, false),
+
+/* GNU extension to record C++ vtable member usage.  */
+  HOWTO (R_X86_64_GNU_VTENTRY, 0, 4, 0, false, 0, complain_overflow_dont,
+        _bfd_elf_rel_vtable_reloc_fn, "R_X86_64_GNU_VTENTRY", false, 0, 0,
+        false)
 };
 
 /* Map BFD relocs to the x86_64 elf relocs.  */
@@ -77,7 +98,7 @@ struct elf_reloc_map
   unsigned char elf_reloc_val;
 };
 
-static CONST struct elf_reloc_map x86_64_reloc_map[] =
+static const struct elf_reloc_map x86_64_reloc_map[] =
 {
   { BFD_RELOC_NONE,            R_X86_64_NONE, },
   { BFD_RELOC_64,              R_X86_64_64,   },
@@ -95,6 +116,8 @@ static CONST struct elf_reloc_map x86_64_reloc_map[] =
   { BFD_RELOC_16_PCREL,                R_X86_64_PC16, },
   { BFD_RELOC_8,               R_X86_64_8, },
   { BFD_RELOC_8_PCREL,         R_X86_64_PC8, },
+  { BFD_RELOC_VTABLE_INHERIT,  R_X86_64_GNU_VTINHERIT, },
+  { BFD_RELOC_VTABLE_ENTRY,    R_X86_64_GNU_VTENTRY, },
 };
 
 static reloc_howto_type *elf64_x86_64_reloc_type_lookup
@@ -103,6 +126,17 @@ static void elf64_x86_64_info_to_howto
   PARAMS ((bfd *, arelent *, Elf64_Internal_Rela *));
 static struct bfd_link_hash_table *elf64_x86_64_link_hash_table_create
   PARAMS ((bfd *));
+static boolean elf64_x86_64_elf_object_p PARAMS ((bfd *abfd));
+static boolean elf64_x86_64_check_relocs
+  PARAMS ((bfd *, struct bfd_link_info *, asection *sec,
+          const Elf_Internal_Rela *));
+static asection *elf64_x86_64_gc_mark_hook
+  PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *,
+          struct elf_link_hash_entry *, Elf_Internal_Sym *));
+
+static boolean elf64_x86_64_gc_sweep_hook
+  PARAMS ((bfd *, struct bfd_link_info *, asection *,
+          const Elf_Internal_Rela *));
 
 static struct bfd_hash_entry *elf64_x86_64_link_hash_newfunc
   PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
@@ -119,6 +153,7 @@ static boolean elf64_x86_64_finish_dynamic_symbol
           Elf_Internal_Sym *sym));
 static boolean elf64_x86_64_finish_dynamic_sections
   PARAMS ((bfd *, struct bfd_link_info *));
+static enum elf_reloc_type_class elf64_x86_64_reloc_type_class PARAMS ((int));
 
 /* Given a BFD reloc type, return a HOWTO structure.  */
 static reloc_howto_type *
@@ -145,11 +180,20 @@ elf64_x86_64_info_to_howto (abfd, cache_ptr, dst)
      arelent *cache_ptr;
      Elf64_Internal_Rela *dst;
 {
-  unsigned r_type;
+  unsigned r_type, i;
 
   r_type = ELF64_R_TYPE (dst->r_info);
-  BFD_ASSERT (r_type < (unsigned int) R_X86_64_max);
-  cache_ptr->howto = &x86_64_elf_howto_table[r_type];
+  if (r_type < (unsigned int) R_X86_64_GNU_VTINHERIT)
+    {
+      BFD_ASSERT (r_type <= (unsigned int) R_X86_64_PC8);
+      i = r_type;
+    }
+  else
+    {
+      BFD_ASSERT (r_type < (unsigned int) R_X86_64_max);
+      i = r_type - ((unsigned int) R_X86_64_GNU_VTINHERIT - R_X86_64_PC8 - 1);
+    }
+  cache_ptr->howto = &x86_64_elf_howto_table[i];
   BFD_ASSERT (r_type == cache_ptr->howto->type);
 }
 \f
@@ -173,18 +217,18 @@ elf64_x86_64_info_to_howto (abfd, cache_ptr, dst)
 
 static const bfd_byte elf64_x86_64_plt0_entry[PLT_ENTRY_SIZE] =
 {
-  0xff, 0xb3, 8, 0, 0, 0,      /* pushq GOT+8(%rip) */
-  0xff, 0xa3, 16, 0, 0, 0,     /* jmp GOT+16(%rip) */
-  0, 0, 0, 0                   /* pad out to 16 bytes.  */
+  0xff, 0x35, 8, 0, 0, 0,      /* pushq GOT+8(%rip)  */
+  0xff, 0x25, 16, 0, 0, 0,     /* jmpq *GOT+16(%rip) */
+  0x90, 0x90, 0x90, 0x90       /* pad out to 16 bytes with nops.  */
 };
 
 /* Subsequent entries in a procedure linkage table look like this.  */
 
 static const bfd_byte elf64_x86_64_plt_entry[PLT_ENTRY_SIZE] =
 {
-  0xff, 0xa3,  /* jmp *name@GOTPC(%rip) */
+  0xff, 0x25,  /* jmpq *name@GOTPC(%rip) */
   0, 0, 0, 0,  /* replaced with offset to this symbol in .got.  */
-  0x68,        /* pushq immediate */
+  0x68,                /* pushq immediate */
   0, 0, 0, 0,  /* replaced with index into relocation table.  */
   0xe9,                /* jmp relative */
   0, 0, 0, 0   /* replaced with offset to start of .plt0.  */
@@ -283,9 +327,9 @@ elf64_x86_64_link_hash_table_create (abfd)
      bfd *abfd;
 {
   struct elf64_x86_64_link_hash_table *ret;
+  bfd_size_type amt = sizeof (struct elf64_x86_64_link_hash_table);
 
-  ret = ((struct elf64_x86_64_link_hash_table *)
-        bfd_alloc (abfd, sizeof (struct elf64_x86_64_link_hash_table)));
+  ret = ((struct elf64_x86_64_link_hash_table *) bfd_alloc (abfd, amt));
   if (ret == (struct elf64_x86_64_link_hash_table *) NULL)
     return NULL;
 
@@ -299,7 +343,7 @@ elf64_x86_64_link_hash_table_create (abfd)
   return &ret->root.root;
 }
 
-boolean
+static boolean
 elf64_x86_64_elf_object_p (abfd)
      bfd *abfd;
 {
@@ -419,15 +463,16 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs)
              /* This is a global offset table entry for a local symbol.  */
              if (local_got_refcounts == NULL)
                {
-                 size_t size;
+                 bfd_size_type size;
 
-                 size = symtab_hdr->sh_info * sizeof (bfd_signed_vma);
+                 size = symtab_hdr->sh_info;
+                 size *= sizeof (bfd_signed_vma);
                  local_got_refcounts = ((bfd_signed_vma *)
                                         bfd_alloc (abfd, size));
                  if (local_got_refcounts == NULL)
                    return false;
                  elf_local_got_refcounts (abfd) = local_got_refcounts;
-                 memset (local_got_refcounts, -1, size);
+                 memset (local_got_refcounts, -1, (size_t) size);
                }
              if (local_got_refcounts[r_symndx] == -1)
                {
@@ -460,16 +505,17 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs)
          if (h == NULL)
            continue;
 
+         h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
          if (h->plt.refcount == -1)
-           {
-             h->plt.refcount = 1;
-             h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
-           }
+           h->plt.refcount = 1;
          else
            h->plt.refcount += 1;
          break;
 
+       case R_X86_64_8:
+       case R_X86_64_16:
        case R_X86_64_32:
+       case R_X86_64_64:
        case R_X86_64_32S:
        case R_X86_64_PC32:
          if (h != NULL)
@@ -491,7 +537,9 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs)
             and symbol visibility changes render the symbol local.  */
          if (info->shared
              && (sec->flags & SEC_ALLOC) != 0
-             && (ELF64_R_TYPE (rel->r_info) != R_X86_64_PC32
+             && (((ELF64_R_TYPE (rel->r_info) != R_X86_64_PC8)
+                 && (ELF64_R_TYPE (rel->r_info) != R_X86_64_PC16)
+                 && (ELF64_R_TYPE (rel->r_info) != R_X86_64_PC32))
                  || (h != NULL
                      && (! info->symbolic
                          || (h->elf_link_hash_flags
@@ -527,9 +575,11 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs)
                        flags |= SEC_ALLOC | SEC_LOAD;
                      if (sreloc == NULL
                          || ! bfd_set_section_flags (dynobj, sreloc, flags)
-                         || ! bfd_set_section_alignment (dynobj, sreloc, 2))
+                         || ! bfd_set_section_alignment (dynobj, sreloc, 3))
                        return false;
                    }
+                 if (sec->flags & SEC_READONLY)
+                   info->flags |= DF_TEXTREL;
                }
 
              sreloc->_raw_size += sizeof (Elf64_External_Rela);
@@ -541,7 +591,9 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs)
                 elf64_x86_64 linker hash table, which means that h is
                 really a pointer to an elf64_x86_64_link_hash_entry.  */
              if (h != NULL
-                 && ELF64_R_TYPE (rel->r_info) == R_X86_64_PC32)
+                 && ((ELF64_R_TYPE (rel->r_info) == R_X86_64_PC8)
+                     || (ELF64_R_TYPE (rel->r_info) == R_X86_64_PC16)
+                     || (ELF64_R_TYPE (rel->r_info) == R_X86_64_PC32)))
                {
                  struct elf64_x86_64_link_hash_entry *eh;
                  struct elf64_x86_64_pcrel_relocs_copied *p;
@@ -555,7 +607,7 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs)
                  if (p == NULL)
                    {
                      p = ((struct elf64_x86_64_pcrel_relocs_copied *)
-                          bfd_alloc (dynobj, sizeof *p));
+                          bfd_alloc (dynobj, (bfd_size_type) sizeof *p));
                      if (p == NULL)
                        return false;
                      p->next = eh->pcrel_relocs_copied;
@@ -568,6 +620,20 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs)
                }
            }
          break;
+
+         /* This relocation describes the C++ object vtable hierarchy.
+            Reconstruct it for later use during GC.  */
+       case R_X86_64_GNU_VTINHERIT:
+         if (!_bfd_elf64_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
+           return false;
+         break;
+
+         /* This relocation describes which C++ vtable entries are actually
+            used.  Record for later use during GC.  */
+       case R_X86_64_GNU_VTENTRY:
+         if (!_bfd_elf64_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+           return false;
+         break;
        }
     }
 
@@ -587,17 +653,25 @@ elf64_x86_64_gc_mark_hook (abfd, info, rel, h, sym)
 {
   if (h != NULL)
     {
-      switch (h->root.type)
+      switch (ELF64_R_TYPE (rel->r_info))
        {
-       case bfd_link_hash_defined:
-       case bfd_link_hash_defweak:
-         return h->root.u.def.section;
-
-       case bfd_link_hash_common:
-         return h->root.u.c.p->section;
+       case R_X86_64_GNU_VTINHERIT:
+       case R_X86_64_GNU_VTENTRY:
+         break;
 
        default:
-         break;
+         switch (h->root.type)
+           {
+           case bfd_link_hash_defined:
+           case bfd_link_hash_defweak:
+             return h->root.u.def.section;
+
+           case bfd_link_hash_common:
+             return h->root.u.c.p->section;
+
+           default:
+             break;
+           }
        }
     }
   else
@@ -876,14 +950,13 @@ elf64_x86_64_adjust_dynamic_symbol (info, h)
 
 static boolean
 elf64_x86_64_size_dynamic_sections (output_bfd, info)
-     bfd *output_bfd;
+     bfd *output_bfd ATTRIBUTE_UNUSED;
      struct bfd_link_info *info;
 {
   bfd *dynobj;
   asection *s;
   boolean plt;
   boolean relocs;
-  boolean reltext;
 
   dynobj = elf_hash_table (info)->dynobj;
   BFD_ASSERT (dynobj != NULL);
@@ -923,7 +996,7 @@ elf64_x86_64_size_dynamic_sections (output_bfd, info)
   /* The check_relocs and adjust_dynamic_symbol entry points have
      determined the sizes of the various dynamic sections.  Allocate
      memory for them.  */
-  plt = relocs = reltext = false;
+  plt = relocs = false;
   for (s = dynobj->sections; s != NULL; s = s->next)
     {
       const char *name;
@@ -968,29 +1041,8 @@ elf64_x86_64_size_dynamic_sections (output_bfd, info)
            }
          else
            {
-             asection *target;
-
-             /* Remember whether there are any reloc sections other
-                than .rela.plt.  */
              if (strcmp (name, ".rela.plt") != 0)
-               {
-                 const char *outname;
-
-                 relocs = true;
-
-                 /* If this relocation section applies to a read only
-                    section, then we probably need a DT_TEXTREL
-                    entry.  The entries in the .rela.plt section
-                    really apply to the .got section, which we
-                    created ourselves and so know is not readonly.  */
-                 outname = bfd_get_section_name (output_bfd,
-                                                 s->output_section);
-                 target = bfd_get_section_by_name (output_bfd, outname + 5);
-                 if (target != NULL
-                     && (target->flags & SEC_READONLY) != 0
-                     && (target->flags & SEC_ALLOC) != 0)
-                   reltext = true;
-               }
+               relocs = true;
 
              /* We use the reloc_count field as a counter if we need
                 to copy relocs into the output file.  */
@@ -1026,37 +1078,39 @@ elf64_x86_64_size_dynamic_sections (output_bfd, info)
         must add the entries now so that we get the correct size for
         the .dynamic section.  The DT_DEBUG entry is filled in by the
         dynamic linker and used by the debugger.  */
+#define add_dynamic_entry(TAG, VAL) \
+  bfd_elf64_add_dynamic_entry (info, (bfd_vma) (TAG), (bfd_vma) (VAL))
+
       if (! info->shared)
        {
-         if (! bfd_elf64_add_dynamic_entry (info, DT_DEBUG, 0))
+         if (!add_dynamic_entry (DT_DEBUG, 0))
            return false;
        }
 
       if (plt)
        {
-         if (! bfd_elf64_add_dynamic_entry (info, DT_PLTGOT, 0)
-             || ! bfd_elf64_add_dynamic_entry (info, DT_PLTRELSZ, 0)
-             || ! bfd_elf64_add_dynamic_entry (info, DT_PLTREL, DT_RELA)
-             || ! bfd_elf64_add_dynamic_entry (info, DT_JMPREL, 0))
+         if (!add_dynamic_entry (DT_PLTGOT, 0)
+             || !add_dynamic_entry (DT_PLTRELSZ, 0)
+             || !add_dynamic_entry (DT_PLTREL, DT_RELA)
+             || !add_dynamic_entry (DT_JMPREL, 0))
            return false;
        }
 
       if (relocs)
        {
-         if (! bfd_elf64_add_dynamic_entry (info, DT_RELA, 0)
-             || ! bfd_elf64_add_dynamic_entry (info, DT_RELASZ, 0)
-             || ! bfd_elf64_add_dynamic_entry (info, DT_RELAENT,
-                                               sizeof (Elf64_External_Rela)))
+         if (!add_dynamic_entry (DT_RELA, 0)
+             || !add_dynamic_entry (DT_RELASZ, 0)
+             || !add_dynamic_entry (DT_RELAENT, sizeof (Elf64_External_Rela)))
            return false;
        }
 
-      if (reltext)
+      if ((info->flags & DF_TEXTREL) != 0)
        {
-         if (! bfd_elf64_add_dynamic_entry (info, DT_TEXTREL, 0))
+         if (!add_dynamic_entry (DT_TEXTREL, 0))
            return false;
-         info->flags |= DF_TEXTREL;
        }
     }
+#undef add_dynamic_entry
 
   return true;
 }
@@ -1096,7 +1150,7 @@ elf64_x86_64_discard_copies (h, inf)
 
 static boolean
 elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section,
-                            contents, relocs, local_syms, local_sections)
+                              contents, relocs, local_syms, local_sections)
      bfd *output_bfd;
      struct bfd_link_info *info;
      bfd *input_bfd;
@@ -1143,6 +1197,9 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section,
       unsigned int indx;
 
       r_type = ELF64_R_TYPE (rela->r_info);
+      if (r_type == (int) R_X86_64_GNU_VTINHERIT
+         || r_type == (int) R_X86_64_GNU_VTENTRY)
+       continue;
 
       if ((indx = (unsigned) r_type) >= R_X86_64_max)
        {
@@ -1194,7 +1251,41 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section,
              || h->root.type == bfd_link_hash_defweak)
            {
              sec = h->root.u.def.section;
-             if (sec->output_section == NULL)
+             if ((r_type == R_X86_64_PLT32
+                  && splt != NULL
+                  && h->plt.offset != (bfd_vma) -1)
+                 || ((r_type == R_X86_64_GOT32 || r_type == R_X86_64_GOTPCREL)
+                     && elf_hash_table (info)->dynamic_sections_created
+                     && (!info->shared
+                         || (! info->symbolic && h->dynindx != -1)
+                         || (h->elf_link_hash_flags
+                             & ELF_LINK_HASH_DEF_REGULAR) == 0))
+                 || (info->shared
+                     && ((! info->symbolic && h->dynindx != -1)
+                         || (h->elf_link_hash_flags
+                             & ELF_LINK_HASH_DEF_REGULAR) == 0)
+                     && (r_type == R_X86_64_8
+                         || r_type == R_X86_64_16
+                         || r_type == R_X86_64_32
+                         || r_type == R_X86_64_64
+                         || r_type == R_X86_64_PC8
+                         || r_type == R_X86_64_PC16
+                         || r_type == R_X86_64_PC32)
+                      && ((input_section->flags & SEC_ALLOC) != 0
+                          /* DWARF will emit R_X86_64_32 relocations in its
+                             sections against symbols defined externally
+                             in shared libraries.  We can't do anything
+                             with them here.  */
+                          || ((input_section->flags & SEC_DEBUGGING) != 0
+                             && (h->elf_link_hash_flags
+                                 & ELF_LINK_HASH_DEF_DYNAMIC) != 0))))
+               {
+                 /* In these cases, we don't need the relocation
+                     value.  We check specially because in some
+                     obscure cases sec->output_section will be NULL.  */
+                 relocation = 0;
+               }
+             else if (sec->output_section == NULL)
                {
                  (*_bfd_error_handler)
                    (_("%s: warning: unresolvable relocation against symbol `%s' from %s section"),
@@ -1231,92 +1322,10 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_X86_64_GOT32:
          /* Relocation is to the entry for this symbol in the global
             offset table.  */
-         BFD_ASSERT (sgot != NULL);
-
-         if (h != NULL)
-           {
-             bfd_vma off = h->got.offset;
-             BFD_ASSERT (off != (bfd_vma) -1);
-
-             if (! elf_hash_table (info)->dynamic_sections_created
-                 || (info->shared
-                     && (info->symbolic || h->dynindx == -1)
-                     && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
-               {
-                 /* This is actually a static link, or it is a -Bsymbolic
-                    link and the symbol is defined locally, or the symbol
-                    was forced to be local because of a version file.  We
-                    must initialize this entry in the global offset table.
-                    Since the offset must always be a multiple of 8, we
-                    use the least significant bit to record whether we
-                    have initialized it already.
-
-                    When doing a dynamic link, we create a .rela.got
-                    relocation entry to initialize the value.  This is
-                    done in the finish_dynamic_symbol routine.  */
-                 if ((off & 1) != 0)
-                   off &= ~1;
-                 else
-                   {
-                     bfd_put_64 (output_bfd, relocation,
-                                 sgot->contents + off);
-                     h->got.offset |= 1;
-                   }
-               }
-             relocation = sgot->output_offset + off;
-           }
-         else
-           {
-             bfd_vma off;
-
-             BFD_ASSERT (local_got_offsets != NULL
-                         && local_got_offsets[r_symndx] != (bfd_vma) -1);
-
-             off = local_got_offsets[r_symndx];
-
-             /* The offset must always be a multiple of 8.  We use
-                the least significant bit to record whether we have
-                already generated the necessary reloc.  */
-             if ((off & 1) != 0)
-               off &= ~1;
-             else
-               {
-                 bfd_put_64 (output_bfd, relocation, sgot->contents + off);
-
-                 if (info->shared)
-                   {
-                     asection *srelgot;
-                     Elf_Internal_Rela outrel;
-
-                     /* We need to generate a R_X86_64_RELATIVE reloc
-                        for the dynamic linker.  */
-                     srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
-                     BFD_ASSERT (srelgot != NULL);
-
-                     outrel.r_offset = (sgot->output_section->vma
-                                        + sgot->output_offset
-                                        + off);
-                     outrel.r_info = ELF64_R_INFO (0, R_X86_64_RELATIVE);
-                     outrel.r_addend = relocation;
-                     bfd_elf64_swap_reloca_out (output_bfd, &outrel,
-                                                (((Elf64_External_Rela *)
-                                                  srelgot->contents)
-                                                 + srelgot->reloc_count));
-                     ++srelgot->reloc_count;
-                   }
-
-                 local_got_offsets[r_symndx] |= 1;
-               }
-
-             relocation = sgot->output_offset + off;
-           }
-
-         break;
-
        case R_X86_64_GOTPCREL:
          /* Use global offset table as symbol value.  */
-
          BFD_ASSERT (sgot != NULL);
+
          if (h != NULL)
            {
              bfd_vma off = h->got.offset;
@@ -1347,7 +1356,10 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section,
                      h->got.offset |= 1;
                    }
                }
-             relocation = sgot->output_offset + off;
+             if (r_type == R_X86_64_GOTPCREL)
+               relocation = sgot->output_section->vma + sgot->output_offset + off;
+             else
+               relocation = sgot->output_offset + off;
            }
          else
            {
@@ -1392,8 +1404,12 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section,
                  local_got_offsets[r_symndx] |= 1;
                }
 
-             relocation = sgot->output_section->vma + off;
+             if (r_type == R_X86_64_GOTPCREL)
+               relocation = sgot->output_section->vma + sgot->output_offset + off;
+             else
+               relocation = sgot->output_offset + off;
            }
+
          break;
 
        case R_X86_64_PLT32:
@@ -1418,23 +1434,21 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section,
                        + h->plt.offset);
          break;
 
-       case R_X86_64_8:
-       case R_X86_64_16:
-       case R_X86_64_32:
        case R_X86_64_PC8:
        case R_X86_64_PC16:
        case R_X86_64_PC32:
-         /* FIXME: The abi says the linker should make sure the value is
+         if (h == NULL || h->dynindx == -1
+             || (info->symbolic
+                 && h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))
+           break;
+         /* Fall through.  */
+       case R_X86_64_8:
+       case R_X86_64_16:
+       case R_X86_64_32:
+       case R_X86_64_64:
+         /* FIXME: The ABI says the linker should make sure the value is
             the same when it's zeroextended to 64 bit.  */
-         if (info->shared
-             && (input_section->flags & SEC_ALLOC) != 0
-             && ((r_type != R_X86_64_PC8 && r_type != R_X86_64_PC16
-                  && r_type != R_X86_64_PC32)
-                 || (h != NULL
-                     && h->dynindx != -1
-                     && (! info->symbolic
-                         || (h->elf_link_hash_flags
-                             & ELF_LINK_HASH_DEF_REGULAR) == 0))))
+         if (info->shared && (input_section->flags & SEC_ALLOC) != 0)
            {
              Elf_Internal_Rela outrel;
              boolean skip, relocate;
@@ -1489,22 +1503,21 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section,
                  memset (&outrel, 0, sizeof outrel);
                  relocate = false;
                }
-             else if ((r_type == R_X86_64_PC8) || (r_type == R_X86_64_PC16)
-                      || (r_type == R_X86_64_PC32))
+             /* h->dynindx may be -1 if this symbol was marked to
+                become local.  */
+             else if (h != NULL
+                      && ((! info->symbolic && h->dynindx != -1)
+                          || (h->elf_link_hash_flags
+                              & ELF_LINK_HASH_DEF_REGULAR) == 0))
                {
-                 BFD_ASSERT (h != NULL && h->dynindx != -1);
+                 BFD_ASSERT (h->dynindx != -1);
                  relocate = false;
                  outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
                  outrel.r_addend = relocation + rela->r_addend;
                }
              else
                {
-                 /* h->dynindx may be -1 if this symbol was marked to
-                    become local.  */
-                 if (h == NULL
-                     || ((info->symbolic || h->dynindx == -1)
-                         && (h->elf_link_hash_flags
-                             & ELF_LINK_HASH_DEF_REGULAR) != 0))
+                 if (r_type == R_X86_64_64)
                    {
                      relocate = true;
                      outrel.r_info = ELF64_R_INFO (0, R_X86_64_RELATIVE);
@@ -1512,11 +1525,38 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section,
                    }
                  else
                    {
-                     BFD_ASSERT (h->dynindx != -1);
+                     long sindx;
+
+                     if (h == NULL)
+                       sec = local_sections[r_symndx];
+                     else
+                       {
+                         BFD_ASSERT (h->root.type == bfd_link_hash_defined
+                                     || (h->root.type
+                                         == bfd_link_hash_defweak));
+                         sec = h->root.u.def.section;
+                       }
+                     if (sec != NULL && bfd_is_abs_section (sec))
+                       sindx = 0;
+                     else if (sec == NULL || sec->owner == NULL)
+                       {
+                         bfd_set_error (bfd_error_bad_value);
+                         return false;
+                       }
+                     else
+                       {
+                         asection *osec;
+
+                         osec = sec->output_section;
+                         sindx = elf_section_data (osec)->dynindx;
+                         BFD_ASSERT (sindx > 0);
+                       }
+
                      relocate = false;
-                     outrel.r_info = ELF64_R_INFO (h->dynindx, R_X86_64_32);
+                     outrel.r_info = ELF64_R_INFO (sindx, r_type);
                      outrel.r_addend = relocation + rela->r_addend;
                    }
+
                }
 
              bfd_elf64_swap_reloca_out (output_bfd, &outrel,
@@ -1620,7 +1660,7 @@ elf64_x86_64_finish_dynamic_symbol (output_bfd, info, h, sym)
 
       /* Get the offset into the .got table of the entry that
         corresponds to this function.  Each .got entry is GOT_ENTRY_SIZE
-        bytes. The first three are reserved.  */
+        bytes. The first three are reserved for the dynamic linker.  */
       got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
 
       /* Fill in the entry in the procedure linkage table.  */
@@ -1630,13 +1670,26 @@ elf64_x86_64_finish_dynamic_symbol (output_bfd, info, h, sym)
       /* Insert the relocation positions of the plt section.  The magic
         numbers at the end of the statements are the positions of the
         relocations in the plt section.  */
-      bfd_put_64 (output_bfd, got_offset, splt->contents + h->plt.offset + 2);
-      bfd_put_64 (output_bfd, plt_index * sizeof (Elf64_External_Rela),
+      /* Put offset for jmp *name@GOTPCREL(%rip), since the
+        instruction uses 6 bytes, subtract this value.  */
+      bfd_put_32 (output_bfd,
+                     (sgot->output_section->vma
+                      + sgot->output_offset
+                      + got_offset
+                      - splt->output_section->vma
+                      - splt->output_offset
+                      - h->plt.offset
+                      - 6),
+                 splt->contents + h->plt.offset + 2);
+      /* Put relocation index.  */
+      bfd_put_32 (output_bfd, plt_index,
                  splt->contents + h->plt.offset + 7);
-      bfd_put_64 (output_bfd, - (h->plt.offset + PLT_ENTRY_SIZE),
+      /* Put offset for jmp .PLT0.  */
+      bfd_put_32 (output_bfd, - (h->plt.offset + PLT_ENTRY_SIZE),
                  splt->contents + h->plt.offset + 12);
 
-      /* Fill in the entry in the global offset table. */
+      /* Fill in the entry in the global offset table, initially this
+        points to the pushq instruction in the PLT which is at offset 6.  */
       bfd_put_64 (output_bfd, (splt->output_section->vma + splt->output_offset
                               + h->plt.offset + 6),
                  sgot->contents + got_offset);
@@ -1666,6 +1719,53 @@ elf64_x86_64_finish_dynamic_symbol (output_bfd, info, h, sym)
        }
     }
 
+  if (h->got.offset != (bfd_vma) -1)
+    {
+      asection *sgot;
+      asection *srela;
+      Elf_Internal_Rela rela;
+
+      /* This symbol has an entry in the global offset table.  Set it
+         up.  */
+
+      sgot = bfd_get_section_by_name (dynobj, ".got");
+      srela = bfd_get_section_by_name (dynobj, ".rela.got");
+      BFD_ASSERT (sgot != NULL && srela != NULL);
+
+      rela.r_offset = (sgot->output_section->vma
+                      + sgot->output_offset
+                      + (h->got.offset &~ (bfd_vma) 1));
+
+      /* If this is a static link, or it is a -Bsymbolic link and the
+        symbol is defined locally or was forced to be local because
+        of a version file, we just want to emit a RELATIVE reloc.
+        The entry in the global offset table will already have been
+        initialized in the relocate_section function.  */
+      if (! elf_hash_table (info)->dynamic_sections_created
+         || (info->shared
+             && (info->symbolic || h->dynindx == -1)
+             && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
+       {
+         BFD_ASSERT((h->got.offset & 1) != 0);
+         rela.r_info = ELF64_R_INFO (0, R_X86_64_RELATIVE);
+         rela.r_addend = (h->root.u.def.value
+                          + h->root.u.def.section->output_section->vma
+                          + h->root.u.def.section->output_offset);
+       }
+      else
+       {
+         BFD_ASSERT((h->got.offset & 1) == 0);
+         bfd_put_64 (output_bfd, (bfd_vma) 0, sgot->contents + h->got.offset);
+         rela.r_info = ELF64_R_INFO (h->dynindx, R_X86_64_GLOB_DAT);
+         rela.r_addend = 0;
+       }
+
+      bfd_elf64_swap_reloca_out (output_bfd, &rela,
+                                ((Elf64_External_Rela *) srela->contents
+                                 + srela->reloc_count));
+      ++srela->reloc_count;
+    }
+
   if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0)
     {
       asection *s;
@@ -1713,6 +1813,8 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info)
 
   dynobj = elf_hash_table (info)->dynobj;
 
+  sgot = bfd_get_section_by_name (dynobj, ".got.plt");
+  BFD_ASSERT (sgot != NULL);
   sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
 
   if (elf_hash_table (info)->dynamic_sections_created)
@@ -1735,7 +1837,7 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info)
          switch (dyn.d_tag)
            {
            default:
-             break;
+             continue;
 
            case DT_PLTGOT:
              name = ".got";
@@ -1773,7 +1875,6 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info)
                (s->_cooked_size != 0 ? s->_cooked_size : s->_raw_size);
              break;
            }
-
          bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
        }
 
@@ -1782,7 +1883,29 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info)
       BFD_ASSERT (splt != NULL);
       if (splt->_raw_size > 0)
        {
+         /* Fill in the first entry in the procedure linkage table.  */
          memcpy (splt->contents, elf64_x86_64_plt0_entry, PLT_ENTRY_SIZE);
+         /* Add offset for pushq GOT+8(%rip), since the instruction
+            uses 6 bytes subtract this value.  */
+         bfd_put_32 (output_bfd,
+                     (sgot->output_section->vma
+                      + sgot->output_offset
+                      + 8
+                      - splt->output_section->vma
+                      - splt->output_offset
+                      - 6),
+                     splt->contents + 2);
+         /* Add offset for jmp *GOT+16(%rip). The 12 is the offset to
+            the end of the instruction.  */
+         bfd_put_32 (output_bfd,
+                     (sgot->output_section->vma
+                      + sgot->output_offset
+                      + 16
+                      - splt->output_section->vma
+                      - splt->output_offset
+                      - 12),
+                     splt->contents + 8);
+
        }
 
       elf_section_data (splt->output_section)->this_hdr.sh_entsize =
@@ -1791,8 +1914,6 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info)
 
   /* Set the first entry in the global offset table to the address of
      the dynamic section.  */
-  sgot = bfd_get_section_by_name (dynobj, ".got.plt");
-  BFD_ASSERT (sgot != NULL);
   if (sgot->_raw_size > 0)
     {
       if (sdyn == NULL)
@@ -1801,7 +1922,7 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info)
        bfd_put_64 (output_bfd,
                    sdyn->output_section->vma + sdyn->output_offset,
                    sgot->contents);
-      /* Write GOT[1] and GOT[2], needed for the linker.  */
+      /* Write GOT[1] and GOT[2], needed for the dynamic linker.  */
       bfd_put_64 (output_bfd, (bfd_vma) 0, sgot->contents + GOT_ENTRY_SIZE);
       bfd_put_64 (output_bfd, (bfd_vma) 0, sgot->contents + GOT_ENTRY_SIZE*2);
     }
@@ -1812,40 +1933,22 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info)
   return true;
 }
 
-/*
- * Why was the hash table entry size definition changed from
- * ARCH_SIZE/8 to 4? This breaks the 64 bit dynamic linker and
- * this is the only reason for the elf64_x86_64_size_info structure.
- */
-
-const struct elf_size_info elf64_86_64_size_info =
+static enum elf_reloc_type_class
+elf64_x86_64_reloc_type_class (type)
+     int type;
 {
-  sizeof (Elf64_External_Ehdr),
-  sizeof (Elf64_External_Phdr),
-  sizeof (Elf64_External_Shdr),
-  sizeof (Elf64_External_Rel),
-  sizeof (Elf64_External_Rela),
-  sizeof (Elf64_External_Sym),
-  sizeof (Elf64_External_Dyn),
-  sizeof (Elf_External_Note),
-  8,           /* hash-table entry size */
-  1,           /* internal relocations per external relocations */
-  64,          /* arch_size */
-  8,           /* file_align */
-  ELFCLASS64, EV_CURRENT,
-  bfd_elf64_write_out_phdrs,
-  bfd_elf64_write_shdrs_and_ehdr,
-  bfd_elf64_write_relocs,
-  bfd_elf64_swap_symbol_out,
-  bfd_elf64_slurp_reloc_table,
-  bfd_elf64_slurp_symbol_table,
-  bfd_elf64_swap_dyn_in,
-  bfd_elf64_swap_dyn_out,
-  NULL,
-  NULL,
-  NULL,
-  NULL
-};
+  switch (type)
+    {
+    case R_X86_64_RELATIVE:
+      return reloc_class_relative;
+    case R_X86_64_JUMP_SLOT:
+      return reloc_class_plt;
+    case R_X86_64_COPY:
+      return reloc_class_copy;
+    default:
+      return reloc_class_normal;
+    }
+}
 
 #define TARGET_LITTLE_SYM                  bfd_elf64_x86_64_vec
 #define TARGET_LITTLE_NAME                 "elf64-x86-64"
@@ -1853,8 +1956,6 @@ const struct elf_size_info elf64_86_64_size_info =
 #define ELF_MACHINE_CODE                   EM_X86_64
 #define ELF_MAXPAGESIZE                            0x100000
 
-#define elf_backend_size_info              elf64_86_64_size_info
-
 #define elf_backend_can_gc_sections        1
 #define elf_backend_want_got_plt           1
 #define elf_backend_plt_readonly           1
@@ -1880,5 +1981,6 @@ const struct elf_size_info elf64_86_64_size_info =
 #define elf_backend_relocate_section       elf64_x86_64_relocate_section
 #define elf_backend_size_dynamic_sections   elf64_x86_64_size_dynamic_sections
 #define elf_backend_object_p               elf64_x86_64_elf_object_p
+#define elf_backend_reloc_type_class       elf64_x86_64_reloc_type_class
 
 #include "elf64-target.h"
This page took 0.036608 seconds and 4 git commands to generate.