2011-04-18 Tristan Gingold <gingold@adacore.com>
[deliverable/binutils-gdb.git] / bfd / elf64-x86-64.c
index ceb1a0a19a3949827396ea00a62ce83e610328f1..84ee1017831ce865a48511d68e878abce95cef89 100644 (file)
@@ -1,6 +1,7 @@
 /* X86-64 specific support for ELF
    Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
-   2010  Free Software Foundation, Inc.
+   2010, 2011
+   Free Software Foundation, Inc.
    Contributed by Jan Hubicka <jh@suse.cz>.
 
    This file is part of BFD, the Binary File Descriptor library.
@@ -495,7 +496,7 @@ struct elf_x86_64_link_hash_table
 
   bfd_vma (*r_info) (bfd_vma, bfd_vma);
   bfd_vma (*r_sym) (bfd_vma);
-  void (*swap_reloca_out) (bfd *, const Elf_Internal_Rela *, bfd_byte *);
+  unsigned int pointer_r_type;
   const char *dynamic_interpreter;
   int dynamic_interpreter_size;
 
@@ -659,7 +660,7 @@ elf_x86_64_link_hash_table_create (bfd *abfd)
     {
       ret->r_info = elf64_r_info;
       ret->r_sym = elf64_r_sym;
-      ret->swap_reloca_out = bfd_elf64_swap_reloca_out;
+      ret->pointer_r_type = R_X86_64_64;
       ret->dynamic_interpreter = ELF64_DYNAMIC_INTERPRETER;
       ret->dynamic_interpreter_size = sizeof ELF64_DYNAMIC_INTERPRETER;
     }
@@ -667,7 +668,7 @@ elf_x86_64_link_hash_table_create (bfd *abfd)
     {
       ret->r_info = elf32_r_info;
       ret->r_sym = elf32_r_sym;
-      ret->swap_reloca_out = bfd_elf32_swap_reloca_out;
+      ret->pointer_r_type = R_X86_64_32;
       ret->dynamic_interpreter = ELF32_DYNAMIC_INTERPRETER;
       ret->dynamic_interpreter_size = sizeof ELF32_DYNAMIC_INTERPRETER;
     }
@@ -865,18 +866,34 @@ elf_x86_64_check_tls_transition (bfd *abfd,
 
       if (r_type == R_X86_64_TLSGD)
        {
-         /* Check transition from GD access model.  Only
+         /* Check transition from GD access model.  For 64bit, only
                .byte 0x66; leaq foo@tlsgd(%rip), %rdi
                .word 0x6666; rex64; call __tls_get_addr
+            can transit to different access model.  For 32bit, only
+               leaq foo@tlsgd(%rip), %rdi
+               .word 0x6666; rex64; call __tls_get_addr
             can transit to different access model.  */
 
-         static x86_64_opcode32 leaq = { { 0x66, 0x48, 0x8d, 0x3d } },
-                                call = { { 0x66, 0x66, 0x48, 0xe8 } };
-         if (offset < 4
-             || (offset + 12) > sec->size
-             || bfd_get_32 (abfd, contents + offset - 4) != leaq.i
+         static x86_64_opcode32 call = { { 0x66, 0x66, 0x48, 0xe8 } };
+         if ((offset + 12) > sec->size
              || bfd_get_32 (abfd, contents + offset + 4) != call.i)
            return FALSE;
+
+         if (ABI_64_P (abfd))
+           {
+             static x86_64_opcode32 leaq = { { 0x66, 0x48, 0x8d, 0x3d } };
+             if (offset < 4
+                 || bfd_get_32 (abfd, contents + offset - 4) != leaq.i)
+               return FALSE;
+           }
+         else
+           {
+             static x86_64_opcode16 lea = { { 0x8d, 0x3d } };
+             if (offset < 3
+                 || bfd_get_8 (abfd, contents + offset - 3) != 0x48
+                 || bfd_get_16 (abfd, contents + offset - 2) != lea.i)
+               return FALSE;
+           }
        }
       else
        {
@@ -913,16 +930,29 @@ elf_x86_64_check_tls_transition (bfd *abfd,
 
     case R_X86_64_GOTTPOFF:
       /* Check transition from IE access model:
-               movq foo@gottpoff(%rip), %reg
-               addq foo@gottpoff(%rip), %reg
+               mov foo@gottpoff(%rip), %reg
+               add foo@gottpoff(%rip), %reg
        */
 
-      if (offset < 3 || (offset + 4) > sec->size)
-       return FALSE;
-
-      val = bfd_get_8 (abfd, contents + offset - 3);
-      if (val != 0x48 && val != 0x4c)
-       return FALSE;
+      /* Check REX prefix first.  */
+      if (offset >= 3 && (offset + 4) <= sec->size)
+       {
+         val = bfd_get_8 (abfd, contents + offset - 3);
+         if (val != 0x48 && val != 0x4c)
+           {
+             /* X32 may have 0x44 REX prefix or no REX prefix.  */
+             if (ABI_64_P (abfd))
+               return FALSE;
+           }
+       }
+      else
+       {
+         /* X32 may not have any REX prefix.  */
+         if (ABI_64_P (abfd))
+           return FALSE;
+         if (offset < 2 || (offset + 3) > sec->size)
+           return FALSE;
+       }
 
       val = bfd_get_8 (abfd, contents + offset - 2);
       if (val != 0x8b && val != 0x03)
@@ -1179,6 +1209,39 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
        }
 
+      /* Check invalid x32 relocations.  */
+      if (!ABI_64_P (abfd))
+       switch (r_type)
+         {
+         default:
+           break;
+
+         case R_X86_64_64:
+         case R_X86_64_DTPOFF64:
+         case R_X86_64_TPOFF64:
+         case R_X86_64_PC64:
+         case R_X86_64_GOTOFF64:
+         case R_X86_64_GOT64:
+         case R_X86_64_GOTPCREL64:
+         case R_X86_64_GOTPC64:
+         case R_X86_64_GOTPLT64:
+         case R_X86_64_PLTOFF64:
+             {
+               if (h)
+                 name = h->root.root.string;
+               else
+                 name = bfd_elf_sym_name (abfd, symtab_hdr, isym,
+                                          NULL);
+               (*_bfd_error_handler)
+                 (_("%B: relocation %s against symbol `%s' isn't "
+                    "supported in x32 mode"), abfd,
+                  x86_64_elf_howto_table[r_type].name, name);
+               bfd_set_error (bfd_error_bad_value);
+               return FALSE;
+             }
+           break;
+         }
+
       if (h != NULL)
        {
          /* Create the ifunc sections for static executables.  If we
@@ -1235,6 +1298,9 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  bfd_set_error (bfd_error_bad_value);
                  return FALSE;
 
+               case R_X86_64_32:
+                 if (ABI_64_P (abfd))
+                   goto not_pointer;
                case R_X86_64_64:
                  h->non_got_ref = 1;
                  h->pointer_equality_needed = 1;
@@ -1252,9 +1318,9 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  break;
 
                case R_X86_64_32S:
-               case R_X86_64_32:
                case R_X86_64_PC32:
                case R_X86_64_PC64:
+not_pointer:
                  h->non_got_ref = 1;
                  if (r_type != R_X86_64_PC32
                      && r_type != R_X86_64_PC64)
@@ -1451,16 +1517,17 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
            }
          goto create_got;
 
+       case R_X86_64_32:
+         if (!ABI_64_P (abfd))
+           goto pointer;
        case R_X86_64_8:
        case R_X86_64_16:
-       case R_X86_64_32:
        case R_X86_64_32S:
          /* Let's help debug shared library creation.  These relocs
             cannot be used in shared libs.  Don't error out for
             sections we don't care about, such as debug sections or
             non-constant sections.  */
          if (info->shared
-             && ABI_64_P (abfd)
              && (sec->flags & SEC_ALLOC) != 0
              && (sec->flags & SEC_READONLY) != 0)
            {
@@ -1481,6 +1548,7 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_X86_64_PC32:
        case R_X86_64_PC64:
        case R_X86_64_64:
+pointer:
          if (h != NULL && info->executable)
            {
              /* If this reloc is in a read-only section, we might
@@ -1545,7 +1613,8 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
                    htab->elf.dynobj = abfd;
 
                  sreloc = _bfd_elf_make_dynamic_reloc_section
-                   (sec, htab->elf.dynobj, 3, abfd, /*rela?*/ TRUE);
+                   (sec, htab->elf.dynobj, ABI_64_P (abfd) ? 3 : 2,
+                    abfd, /*rela?*/ TRUE);
 
                  if (sreloc == NULL)
                    return FALSE;
@@ -1699,7 +1768,7 @@ elf_x86_64_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
 
          /* Check relocation against local STT_GNU_IFUNC symbol.  */
          if (isym != NULL
-             && ELF64_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+             && ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
            {
              h = elf_x86_64_get_local_sym_hash (htab, abfd, rel, FALSE);
              if (h == NULL)
@@ -2832,6 +2901,10 @@ elf_x86_64_relocate_section (bfd *output_bfd,
                abort ();
              goto do_relocation;
 
+           case R_X86_64_32:
+             if (ABI_64_P (output_bfd))
+               goto do_relocation;
+             /* FALLTHROUGH */
            case R_X86_64_64: 
              if (rel->r_addend != 0)
                {
@@ -2895,8 +2968,7 @@ elf_x86_64_relocate_section (bfd *output_bfd,
                     internal symbol, we have updated addend.  */
                  continue;
                }
-
-           case R_X86_64_32:
+             /* FALLTHROUGH */
            case R_X86_64_PC32:
            case R_X86_64_PC64:
            case R_X86_64_PLT32:
@@ -2958,18 +3030,6 @@ elf_x86_64_relocate_section (bfd *output_bfd,
              relocation = (base_got->output_section->vma
                            + base_got->output_offset + off);
 
-             if (r_type != R_X86_64_GOTPCREL
-                 && r_type != R_X86_64_GOTPCREL64)
-               {
-                 asection *gotplt;
-                 if (htab->elf.splt != NULL)
-                   gotplt = htab->elf.sgotplt;
-                 else
-                   gotplt = htab->elf.igotplt;
-                 relocation -= (gotplt->output_section->vma
-                                - gotplt->output_offset);
-               }
-
              goto do_relocation;
            }
        }
@@ -3309,7 +3369,7 @@ elf_x86_64_relocate_section (bfd *output_bfd,
              else
                {
                  /* This symbol is local, or marked to become local.  */
-                 if (r_type == R_X86_64_64)
+                 if (r_type == htab->pointer_r_type)
                    {
                      relocate = TRUE;
                      outrel.r_info = htab->r_info (0, R_X86_64_RELATIVE);
@@ -3352,7 +3412,11 @@ elf_x86_64_relocate_section (bfd *output_bfd,
 
              sreloc = elf_section_data (input_section)->sreloc;
 
-             BFD_ASSERT (sreloc != NULL && sreloc->contents != NULL);
+             if (sreloc == NULL || sreloc->contents == NULL)
+               {
+                 r = bfd_reloc_notsupported;
+                 goto check_relocation_error;
+               }
 
              elf_append_rela (output_bfd, sreloc, &outrel);
 
@@ -3391,15 +3455,26 @@ elf_x86_64_relocate_section (bfd *output_bfd,
 
              if (ELF32_R_TYPE (rel->r_info) == R_X86_64_TLSGD)
                {
-                 /* GD->LE transition.
+                 /* GD->LE transition.  For 64bit, change
                     .byte 0x66; leaq foo@tlsgd(%rip), %rdi
                     .word 0x6666; rex64; call __tls_get_addr
-                    Change it into:
+                    into:
                     movq %fs:0, %rax
+                    leaq foo@tpoff(%rax), %rax
+                    For 32bit, change
+                    leaq foo@tlsgd(%rip), %rdi
+                    .word 0x6666; rex64; call __tls_get_addr
+                    into:
+                    movl %fs:0, %eax
                     leaq foo@tpoff(%rax), %rax */
-                 memcpy (contents + roff - 4,
-                         "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x8d\x80\0\0\0",
-                         16);
+                 if (ABI_64_P (output_bfd))
+                   memcpy (contents + roff - 4,
+                           "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x8d\x80\0\0\0",
+                           16);
+                 else
+                   memcpy (contents + roff - 3,
+                           "\x64\x8b\x04\x25\0\0\0\0\x48\x8d\x80\0\0\0",
+                           15);
                  bfd_put_32 (output_bfd,
                              elf_x86_64_tpoff (info, relocation),
                              contents + roff + 8);
@@ -3464,6 +3539,9 @@ elf_x86_64_relocate_section (bfd *output_bfd,
                      if (val == 0x4c)
                        bfd_put_8 (output_bfd, 0x49,
                                   contents + roff - 3);
+                     else if (!ABI_64_P (output_bfd) && val == 0x44)
+                       bfd_put_8 (output_bfd, 0x41,
+                                  contents + roff - 3);
                      bfd_put_8 (output_bfd, 0xc7,
                                 contents + roff - 2);
                      bfd_put_8 (output_bfd, 0xc0 | reg,
@@ -3476,6 +3554,9 @@ elf_x86_64_relocate_section (bfd *output_bfd,
                      if (val == 0x4c)
                        bfd_put_8 (output_bfd, 0x49,
                                   contents + roff - 3);
+                     else if (!ABI_64_P (output_bfd) && val == 0x44)
+                       bfd_put_8 (output_bfd, 0x41,
+                                  contents + roff - 3);
                      bfd_put_8 (output_bfd, 0x81,
                                 contents + roff - 2);
                      bfd_put_8 (output_bfd, 0xc0 | reg,
@@ -3487,6 +3568,9 @@ elf_x86_64_relocate_section (bfd *output_bfd,
                      if (val == 0x4c)
                        bfd_put_8 (output_bfd, 0x4d,
                                   contents + roff - 3);
+                     else if (!ABI_64_P (output_bfd) && val == 0x44)
+                       bfd_put_8 (output_bfd, 0x45,
+                                  contents + roff - 3);
                      bfd_put_8 (output_bfd, 0x8d,
                                 contents + roff - 2);
                      bfd_put_8 (output_bfd, 0x80 | reg | (reg << 3),
@@ -3618,15 +3702,26 @@ elf_x86_64_relocate_section (bfd *output_bfd,
 
              if (ELF32_R_TYPE (rel->r_info) == R_X86_64_TLSGD)
                {
-                 /* GD->IE transition.
+                 /* GD->IE transition.  For 64bit, change
                     .byte 0x66; leaq foo@tlsgd(%rip), %rdi
                     .word 0x6666; rex64; call __tls_get_addr@plt
-                    Change it into:
+                    into:
                     movq %fs:0, %rax
+                    addq foo@gottpoff(%rip), %rax
+                    For 32bit, change
+                    leaq foo@tlsgd(%rip), %rdi
+                    .word 0x6666; rex64; call __tls_get_addr@plt
+                    into:
+                    movl %fs:0, %eax
                     addq foo@gottpoff(%rip), %rax */
-                 memcpy (contents + roff - 4,
-                         "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x03\x05\0\0\0",
-                         16);
+                 if (ABI_64_P (output_bfd))
+                   memcpy (contents + roff - 4,
+                           "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x03\x05\0\0\0",
+                           16);
+                 else
+                   memcpy (contents + roff - 3,
+                           "\x64\x8b\x04\x25\0\0\0\0\x48\x03\x05\0\0\0",
+                           15);
 
                  relocation = (htab->elf.sgot->output_section->vma
                                + htab->elf.sgot->output_offset + off
@@ -3695,12 +3790,18 @@ elf_x86_64_relocate_section (bfd *output_bfd,
            {
              /* LD->LE transition:
                 leaq foo@tlsld(%rip), %rdi; call __tls_get_addr.
-                We change it into:
-                .word 0x6666; .byte 0x66; movl %fs:0, %rax.  */
+                For 64bit, we change it into:
+                .word 0x6666; .byte 0x66; movq %fs:0, %rax.
+                For 32bit, we change it into:
+                nopl 0x0(%rax); movl %fs:0, %eax.  */
 
              BFD_ASSERT (r_type == R_X86_64_TPOFF32);
-             memcpy (contents + rel->r_offset - 3,
-                     "\x66\x66\x66\x64\x48\x8b\x04\x25\0\0\0", 12);
+             if (ABI_64_P (output_bfd))
+               memcpy (contents + rel->r_offset - 3,
+                       "\x66\x66\x66\x64\x48\x8b\x04\x25\0\0\0", 12);
+             else
+               memcpy (contents + rel->r_offset - 3,
+                       "\x0f\x1f\x40\x00\x64\x8b\x04\x25\0\0\0", 12);
              /* Skip R_X86_64_PC32/R_X86_64_PLT32.  */
              rel++;
              continue;
@@ -3772,6 +3873,7 @@ do_relocation:
                                    contents, rel->r_offset,
                                    relocation, rel->r_addend);
 
+check_relocation_error:
       if (r != bfd_reloc_ok)
        {
          const char *name;
@@ -3945,7 +4047,7 @@ elf_x86_64_finish_dynamic_symbol (bfd *output_bfd,
 
       bed = get_elf_backend_data (output_bfd);
       loc = relplt->contents + plt_index * bed->s->sizeof_rela;
-      htab->swap_reloca_out (output_bfd, &rela, loc);
+      bed->s->swap_reloca_out (output_bfd, &rela, loc);
 
       if (!h->def_regular)
        {
@@ -4116,19 +4218,23 @@ elf_x86_64_finish_dynamic_sections (bfd *output_bfd,
 
   if (htab->elf.dynamic_sections_created)
     {
-      Elf64_External_Dyn *dyncon, *dynconend;
+      bfd_byte *dyncon, *dynconend;
+      const struct elf_backend_data *bed;
+      bfd_size_type sizeof_dyn;
 
       if (sdyn == NULL || htab->elf.sgot == NULL)
        abort ();
 
-      dyncon = (Elf64_External_Dyn *) sdyn->contents;
-      dynconend = (Elf64_External_Dyn *) (sdyn->contents + sdyn->size);
-      for (; dyncon < dynconend; dyncon++)
+      bed = get_elf_backend_data (dynobj);
+      sizeof_dyn = bed->s->sizeof_dyn;
+      dyncon = sdyn->contents;
+      dynconend = sdyn->contents + sdyn->size;
+      for (; dyncon < dynconend; dyncon += sizeof_dyn)
        {
          Elf_Internal_Dyn dyn;
          asection *s;
 
-         bfd_elf64_swap_dyn_in (dynobj, dyncon, &dyn);
+         (*bed->s->swap_dyn_in) (dynobj, dyncon, &dyn);
 
          switch (dyn.d_tag)
            {
@@ -4177,7 +4283,7 @@ elf_x86_64_finish_dynamic_sections (bfd *output_bfd,
              break;
            }
 
-         bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
+         (*bed->s->swap_dyn_out) (output_bfd, &dyn, dyncon);
        }
 
       /* Fill in the special first entry in the procedure linkage table.  */
@@ -4351,8 +4457,9 @@ elf_x86_64_add_symbol_hook (bfd *abfd,
     }
 
   if ((abfd->flags & DYNAMIC) == 0
-      && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
-    elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+      && (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
+         || ELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE))
+    elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
 
   return TRUE;
 }
@@ -4674,6 +4781,9 @@ elf64_l1om_elf_object_p (bfd *abfd)
 #undef  elf_backend_post_process_headers
 #undef  elf_backend_static_tls_alignment
 
+#undef elf_backend_want_plt_sym
+#define elf_backend_want_plt_sym           0
+
 #include "elf64-target.h"
 
 /* FreeBSD L1OM support.  */
This page took 0.03035 seconds and 4 git commands to generate.