bfd/
[deliverable/binutils-gdb.git] / bfd / elf64-x86-64.c
index 08e610b1db8724b3ccf737996ad7ddd174f6c908..0502e581b88baa09016677be7785bf5140829f4c 100644 (file)
@@ -17,7 +17,7 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
 
 #include "bfd.h"
 #include "sysdep.h"
@@ -73,9 +73,9 @@ static reloc_howto_type x86_64_elf_howto_table[] =
        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,
+  HOWTO(R_X86_64_PC16,0, 1, 16, TRUE, 0, complain_overflow_signed,
        bfd_elf_generic_reloc, "R_X86_64_PC16", FALSE, 0xffff, 0xffff, TRUE),
-  HOWTO(R_X86_64_8, 0, 0, 8, FALSE, 0, complain_overflow_signed,
+  HOWTO(R_X86_64_8, 0, 0, 8, FALSE, 0, complain_overflow_bitfield,
        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),
@@ -94,7 +94,7 @@ static reloc_howto_type x86_64_elf_howto_table[] =
   HOWTO(R_X86_64_TLSLD, 0, 2, 32, TRUE, 0, complain_overflow_signed,
        bfd_elf_generic_reloc, "R_X86_64_TLSLD", FALSE, 0xffffffff,
        0xffffffff, TRUE),
-  HOWTO(R_X86_64_DTPOFF32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+  HOWTO(R_X86_64_DTPOFF32, 0, 2, 32, FALSE, 0, complain_overflow_signed,
        bfd_elf_generic_reloc, "R_X86_64_DTPOFF32", FALSE, 0xffffffff,
        0xffffffff, FALSE),
   HOWTO(R_X86_64_GOTTPOFF, 0, 2, 32, TRUE, 0, complain_overflow_signed,
@@ -103,6 +103,15 @@ static reloc_howto_type x86_64_elf_howto_table[] =
   HOWTO(R_X86_64_TPOFF32, 0, 2, 32, FALSE, 0, complain_overflow_signed,
        bfd_elf_generic_reloc, "R_X86_64_TPOFF32", FALSE, 0xffffffff,
        0xffffffff, FALSE),
+  HOWTO(R_X86_64_PC64, 0, 4, 64, TRUE, 0, complain_overflow_bitfield,
+       bfd_elf_generic_reloc, "R_X86_64_PC64", FALSE, MINUS_ONE, MINUS_ONE,
+       TRUE),
+  HOWTO(R_X86_64_GOTOFF64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+       bfd_elf_generic_reloc, "R_X86_64_GOTOFF64",
+       FALSE, MINUS_ONE, MINUS_ONE, FALSE),
+  HOWTO(R_X86_64_GOTPC32, 0, 2, 32, TRUE, 0, complain_overflow_signed,
+       bfd_elf_generic_reloc, "R_X86_64_GOTPC32",
+       FALSE, 0xffffffff, 0xffffffff, TRUE),
 
 /* GNU extension to record C++ vtable hierarchy.  */
   HOWTO (R_X86_64_GNU_VTINHERIT, 0, 4, 0, FALSE, 0, complain_overflow_dont,
@@ -147,6 +156,9 @@ static const struct elf_reloc_map x86_64_reloc_map[] =
   { BFD_RELOC_X86_64_DTPOFF32, R_X86_64_DTPOFF32, },
   { BFD_RELOC_X86_64_GOTTPOFF, R_X86_64_GOTTPOFF, },
   { BFD_RELOC_X86_64_TPOFF32,  R_X86_64_TPOFF32, },
+  { BFD_RELOC_64_PCREL,                R_X86_64_PC64, },
+  { BFD_RELOC_X86_64_GOTOFF64, R_X86_64_GOTOFF64, },
+  { BFD_RELOC_X86_64_GOTPC32,  R_X86_64_GOTPC32, },
   { BFD_RELOC_VTABLE_INHERIT,  R_X86_64_GNU_VTINHERIT, },
   { BFD_RELOC_VTABLE_ENTRY,    R_X86_64_GNU_VTENTRY, },
 };
@@ -177,16 +189,19 @@ elf64_x86_64_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *cache_ptr,
   unsigned r_type, i;
 
   r_type = ELF64_R_TYPE (dst->r_info);
-  if (r_type < (unsigned int) R_X86_64_GNU_VTINHERIT)
+  if (r_type < (unsigned int) R_X86_64_GNU_VTINHERIT
+      || r_type >= (unsigned int) R_X86_64_max)
     {
-      BFD_ASSERT (r_type <= (unsigned int) R_X86_64_TPOFF32);
+      if (r_type > (unsigned int) R_X86_64_GOTPC32)
+       {
+         (*_bfd_error_handler) (_("%B: invalid relocation type %d"),
+                                abfd, (int) r_type);
+         r_type = R_X86_64_NONE;
+       }
       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_TPOFF32 - 1);
-    }
+    i = r_type - ((unsigned int) R_X86_64_GNU_VTINHERIT - R_X86_64_GOTPC32 - 1);
   cache_ptr->howto = &x86_64_elf_howto_table[i];
   BFD_ASSERT (r_type == cache_ptr->howto->type);
 }
@@ -459,12 +474,13 @@ create_got_section (bfd *dynobj, struct bfd_link_info *info)
   if (!htab->sgot || !htab->sgotplt)
     abort ();
 
-  htab->srelgot = bfd_make_section (dynobj, ".rela.got");
+  htab->srelgot = bfd_make_section_with_flags (dynobj, ".rela.got",
+                                              (SEC_ALLOC | SEC_LOAD
+                                               | SEC_HAS_CONTENTS
+                                               | SEC_IN_MEMORY
+                                               | SEC_LINKER_CREATED
+                                               | SEC_READONLY));
   if (htab->srelgot == NULL
-      || ! bfd_set_section_flags (dynobj, htab->srelgot,
-                                 (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
-                                  | SEC_IN_MEMORY | SEC_LINKER_CREATED
-                                  | SEC_READONLY))
       || ! bfd_set_section_alignment (dynobj, htab->srelgot, 3))
     return FALSE;
   return TRUE;
@@ -651,7 +667,12 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec,
       if (r_symndx < symtab_hdr->sh_info)
        h = NULL;
       else
-       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+       {
+         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+         while (h->root.type == bfd_link_hash_indirect
+                || h->root.type == bfd_link_hash_warning)
+           h = (struct elf_link_hash_entry *) h->root.u.i.link;
+       }
 
       r_type = elf64_x86_64_tls_transition (info, r_type, h == NULL);
       switch (r_type)
@@ -748,7 +769,8 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec,
          }
          /* Fall through */
 
-         //case R_X86_64_GOTPCREL:
+       case R_X86_64_GOTOFF64:
+       case R_X86_64_GOTPC32:
        create_got:
          if (htab->sgot == NULL)
            {
@@ -801,6 +823,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec,
        case R_X86_64_PC8:
        case R_X86_64_PC16:
        case R_X86_64_PC32:
+       case R_X86_64_PC64:
        case R_X86_64_64:
          if (h != NULL && !info->shared)
            {
@@ -815,7 +838,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec,
              /* We may need a .plt entry if the function this reloc
                 refers to is in a shared lib.  */
              h->plt.refcount += 1;
-             if (r_type != R_X86_64_PC32)
+             if (r_type != R_X86_64_PC32 && r_type != R_X86_64_PC64)
                h->pointer_equality_needed = 1;
            }
 
@@ -844,7 +867,8 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec,
               && (sec->flags & SEC_ALLOC) != 0
               && (((r_type != R_X86_64_PC8)
                    && (r_type != R_X86_64_PC16)
-                   && (r_type != R_X86_64_PC32))
+                   && (r_type != R_X86_64_PC32)
+                   && (r_type != R_X86_64_PC64))
                   || (h != NULL
                       && (! info->symbolic
                           || h->root.type == bfd_link_hash_defweak
@@ -893,13 +917,14 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec,
                    {
                      flagword flags;
 
-                     sreloc = bfd_make_section (dynobj, name);
                      flags = (SEC_HAS_CONTENTS | SEC_READONLY
                               | SEC_IN_MEMORY | SEC_LINKER_CREATED);
                      if ((sec->flags & SEC_ALLOC) != 0)
                        flags |= SEC_ALLOC | SEC_LOAD;
+                     sreloc = bfd_make_section_with_flags (dynobj,
+                                                           name,
+                                                           flags);
                      if (sreloc == NULL
-                         || ! bfd_set_section_flags (dynobj, sreloc, flags)
                          || ! bfd_set_section_alignment (dynobj, sreloc, 3))
                        return FALSE;
                    }
@@ -946,7 +971,8 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec,
              p->count += 1;
              if (r_type == R_X86_64_PC8
                  || r_type == R_X86_64_PC16
-                 || r_type == R_X86_64_PC32)
+                 || r_type == R_X86_64_PC32
+                 || r_type == R_X86_64_PC64)
                p->pc_count += 1;
            }
          break;
@@ -1091,6 +1117,7 @@ elf64_x86_64_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
        case R_X86_64_PC8:
        case R_X86_64_PC16:
        case R_X86_64_PC32:
+       case R_X86_64_PC64:
          if (info->shared)
            break;
          /* Fall thru */
@@ -1622,7 +1649,8 @@ elf64_x86_64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
       if (s == htab->splt
          || s == htab->sgot
-         || s == htab->sgotplt)
+         || s == htab->sgotplt
+         || s == htab->sdynbss)
        {
          /* Strip this section if we don't need it; see the
             comment below.  */
@@ -1654,10 +1682,13 @@ elf64_x86_64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
             function which decides whether anything needs to go
             into these sections.  */
 
-         _bfd_strip_section_from_output (info, s);
+         s->flags |= SEC_EXCLUDE;
          continue;
        }
 
+      if ((s->flags & SEC_HAS_CONTENTS) == 0)
+       continue;
+
       /* Allocate memory for the section contents.  We use bfd_zalloc
         here in case unused entries are not reclaimed before the
         section's contents are written out.  This should not happen,
@@ -1938,6 +1969,42 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
          break;
 
+       case R_X86_64_GOTOFF64:
+         /* Relocation is relative to the start of the global offset
+            table.  */
+
+         /* Check to make sure it isn't a protected function symbol
+            for shared library since it may not be local when used
+            as function address.  */
+         if (info->shared
+             && h
+             && h->def_regular
+             && h->type == STT_FUNC
+             && ELF_ST_VISIBILITY (h->other) == STV_PROTECTED)
+           {
+             (*_bfd_error_handler)
+               (_("%B: relocation R_X86_64_GOTOFF64 against protected function `%s' can not be used when making a shared object"),
+                input_bfd, h->root.root.string);
+             bfd_set_error (bfd_error_bad_value);
+             return FALSE;
+           }
+
+         /* Note that sgot is not involved in this
+            calculation.  We always want the start of .got.plt.  If we
+            defined _GLOBAL_OFFSET_TABLE_ in a different way, as is
+            permitted by the ABI, we might have to change this
+            calculation.  */
+         relocation -= htab->sgotplt->output_section->vma
+                       + htab->sgotplt->output_offset;
+         break;
+
+       case R_X86_64_GOTPC32:
+         /* Use global offset table as symbol value.  */
+         relocation = htab->sgotplt->output_section->vma
+                      + htab->sgotplt->output_offset;
+         unresolved_reloc = FALSE;
+         break;
+
        case R_X86_64_PLT32:
          /* Relocation is to the entry for this symbol in the
             procedure linkage table.  */
@@ -1996,6 +2063,7 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
        case R_X86_64_8:
        case R_X86_64_16:
        case R_X86_64_32:
+       case R_X86_64_PC64:
        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.  */
@@ -2013,7 +2081,8 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                   || h->root.type != bfd_link_hash_undefweak)
               && ((r_type != R_X86_64_PC8
                    && r_type != R_X86_64_PC16
-                   && r_type != R_X86_64_PC32)
+                   && r_type != R_X86_64_PC32
+                   && r_type != R_X86_64_PC64)
                   || !SYMBOL_CALLS_LOCAL (info, h)))
              || (ELIMINATE_COPY_RELOCS
                  && !info->shared
@@ -2057,6 +2126,7 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                       && (r_type == R_X86_64_PC8
                           || r_type == R_X86_64_PC16
                           || r_type == R_X86_64_PC32
+                          || r_type == R_X86_64_PC64
                           || !info->shared
                           || !info->symbolic
                           || !h->def_regular))
@@ -2822,17 +2892,195 @@ elf64_x86_64_plt_sym_val (bfd_vma i, const asection *plt,
    is called when elfcode.h finds a section with an unknown type.  */
 
 static bfd_boolean
-elf64_x86_64_section_from_shdr (bfd *abfd, Elf_Internal_Shdr *hdr, const char *name)
+elf64_x86_64_section_from_shdr (bfd *abfd,
+                               Elf_Internal_Shdr *hdr,
+                               const char *name,
+                               int shindex)
 {
   if (hdr->sh_type != SHT_X86_64_UNWIND)
     return FALSE;
 
-  if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name))
+  if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex))
     return FALSE;
 
   return TRUE;
 }
 
+/* Hook called by the linker routine which adds symbols from an object
+   file.  We use it to put SHN_X86_64_LCOMMON items in .lbss, instead
+   of .bss.  */
+
+static bfd_boolean
+elf64_x86_64_add_symbol_hook (bfd *abfd,
+                             struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                             Elf_Internal_Sym *sym,
+                             const char **namep ATTRIBUTE_UNUSED,
+                             flagword *flagsp ATTRIBUTE_UNUSED,
+                             asection **secp, bfd_vma *valp)
+{
+  asection *lcomm;
+
+  switch (sym->st_shndx)
+    {
+    case SHN_X86_64_LCOMMON:
+      lcomm = bfd_get_section_by_name (abfd, "LARGE_COMMON");
+      if (lcomm == NULL)
+       {
+         lcomm = bfd_make_section_with_flags (abfd,
+                                              "LARGE_COMMON",
+                                              (SEC_ALLOC
+                                               | SEC_IS_COMMON
+                                               | SEC_LINKER_CREATED));
+         if (lcomm == NULL)
+           return FALSE;
+         elf_section_flags (lcomm) |= SHF_X86_64_LARGE;
+       }
+      *secp = lcomm;
+      *valp = sym->st_size;
+      break;
+    }
+  return TRUE;
+}
+
+
+/* Given a BFD section, try to locate the corresponding ELF section
+   index.  */
+
+static bfd_boolean
+elf64_x86_64_elf_section_from_bfd_section (bfd *abfd ATTRIBUTE_UNUSED,
+                                          asection *sec, int *index)
+{
+  if (sec == &_bfd_elf_large_com_section)
+    {
+      *index = SHN_X86_64_LCOMMON;
+      return TRUE;
+    }
+  return FALSE;
+}
+
+/* Process a symbol.  */
+
+static void
+elf64_x86_64_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED,
+                               asymbol *asym)
+{
+  elf_symbol_type *elfsym = (elf_symbol_type *) asym;
+
+  switch (elfsym->internal_elf_sym.st_shndx)
+    {
+    case SHN_X86_64_LCOMMON:
+      asym->section = &_bfd_elf_large_com_section;
+      asym->value = elfsym->internal_elf_sym.st_size;
+      /* Common symbol doesn't set BSF_GLOBAL.  */
+      asym->flags &= ~BSF_GLOBAL;
+      break;
+    }
+}
+
+static bfd_boolean
+elf64_x86_64_common_definition (Elf_Internal_Sym *sym)
+{
+  return (sym->st_shndx == SHN_COMMON
+         || sym->st_shndx == SHN_X86_64_LCOMMON);
+}
+
+static unsigned int
+elf64_x86_64_common_section_index (asection *sec)
+{
+  if ((elf_section_flags (sec) & SHF_X86_64_LARGE) == 0)
+    return SHN_COMMON;
+  else
+    return SHN_X86_64_LCOMMON;
+}
+
+static asection *
+elf64_x86_64_common_section (asection *sec)
+{
+  if ((elf_section_flags (sec) & SHF_X86_64_LARGE) == 0)
+    return bfd_com_section_ptr;
+  else
+    return &_bfd_elf_large_com_section;
+}
+
+static bfd_boolean
+elf64_x86_64_merge_symbol (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                          struct elf_link_hash_entry **sym_hash ATTRIBUTE_UNUSED,
+                          struct elf_link_hash_entry *h,
+                          Elf_Internal_Sym *sym,
+                          asection **psec ATTRIBUTE_UNUSED,
+                          bfd_vma *pvalue ATTRIBUTE_UNUSED,
+                          unsigned int *pold_alignment ATTRIBUTE_UNUSED,
+                          bfd_boolean *skip ATTRIBUTE_UNUSED,
+                          bfd_boolean *override ATTRIBUTE_UNUSED,
+                          bfd_boolean *type_change_ok ATTRIBUTE_UNUSED,
+                          bfd_boolean *size_change_ok ATTRIBUTE_UNUSED,
+                          bfd_boolean *newdef ATTRIBUTE_UNUSED,
+                          bfd_boolean *newdyn,
+                          bfd_boolean *newdyncommon ATTRIBUTE_UNUSED,
+                          bfd_boolean *newweak ATTRIBUTE_UNUSED,
+                          bfd *abfd ATTRIBUTE_UNUSED,
+                          asection **sec,
+                          bfd_boolean *olddef ATTRIBUTE_UNUSED,
+                          bfd_boolean *olddyn,
+                          bfd_boolean *olddyncommon ATTRIBUTE_UNUSED,
+                          bfd_boolean *oldweak ATTRIBUTE_UNUSED,
+                          bfd *oldbfd ATTRIBUTE_UNUSED,
+                          asection **oldsec)
+{
+  /* A normal common symbol and a large common symbol result in a
+     normal common symbol.  If we see the normal symbol first, we
+     do nothing since the first one will be used.  If we see the
+     large common symbol first, we need to change the large common
+     symbol to the normal common symbol.  */
+  if (!*olddyn
+      && h->root.type == bfd_link_hash_common
+      && !*newdyn
+      && bfd_is_com_section (*sec)
+      && *oldsec != *sec
+      && sym->st_shndx == SHN_COMMON
+      && (elf_section_flags (*oldsec) & SHF_X86_64_LARGE) != 0)
+    {
+      h->root.u.c.p->section = bfd_make_section_old_way (abfd,
+                                                        "COMMON");
+      h->root.u.c.p->section->flags = SEC_ALLOC;
+    }
+
+  return TRUE;
+}
+
+static int
+elf64_x86_64_additional_program_headers (bfd *abfd)
+{
+  asection *s;
+  int count = 0; 
+
+  /* Check to see if we need a large readonly segment.  */
+  s = bfd_get_section_by_name (abfd, ".lrodata");
+  if (s && (s->flags & SEC_LOAD))
+    count++;
+
+  /* Check to see if we need a large data segment.  Since .lbss sections
+     is placed right after the .bss section, there should be no need for
+     a large data segment just because of .lbss.  */
+  s = bfd_get_section_by_name (abfd, ".ldata");
+  if (s && (s->flags & SEC_LOAD))
+    count++;
+
+  return count;
+}
+
+static const struct bfd_elf_special_section 
+  elf64_x86_64_special_sections[]=
+{
+  { ".gnu.linkonce.lb",        16, -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE + SHF_X86_64_LARGE},
+  { ".gnu.linkonce.lr",        16, -2, SHT_PROGBITS, SHF_ALLOC + SHF_X86_64_LARGE},
+  { ".gnu.linkonce.lt",        16, -2, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR + SHF_X86_64_LARGE},
+  { ".lbss",   5, -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE + SHF_X86_64_LARGE},
+  { ".ldata",  6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_X86_64_LARGE},
+  { ".lrodata",        8, -2, SHT_PROGBITS, SHF_ALLOC + SHF_X86_64_LARGE},
+  { NULL,      0,  0, 0,            0 }
+};
+
 #define TARGET_LITTLE_SYM                  bfd_elf64_x86_64_vec
 #define TARGET_LITTLE_NAME                 "elf64-x86-64"
 #define ELF_ARCH                           bfd_arch_i386
@@ -2873,4 +3121,23 @@ elf64_x86_64_section_from_shdr (bfd *abfd, Elf_Internal_Shdr *hdr, const char *n
 #define elf_backend_section_from_shdr \
        elf64_x86_64_section_from_shdr
 
+#define elf_backend_section_from_bfd_section \
+  elf64_x86_64_elf_section_from_bfd_section
+#define elf_backend_add_symbol_hook \
+  elf64_x86_64_add_symbol_hook
+#define elf_backend_symbol_processing \
+  elf64_x86_64_symbol_processing
+#define elf_backend_common_section_index \
+  elf64_x86_64_common_section_index
+#define elf_backend_common_section \
+  elf64_x86_64_common_section
+#define elf_backend_common_definition \
+  elf64_x86_64_common_definition
+#define elf_backend_merge_symbol \
+  elf64_x86_64_merge_symbol
+#define elf_backend_special_sections \
+  elf64_x86_64_special_sections
+#define elf_backend_additional_program_headers \
+  elf64_x86_64_additional_program_headers
+
 #include "elf64-target.h"
This page took 0.030005 seconds and 4 git commands to generate.