Automatic date update in version.in
[deliverable/binutils-gdb.git] / bfd / elf32-i386.c
index fcb22c431c5d4f499ccc06ffe21ec6da44a77386..417957214dbb6257e97fe1510ebd431807a1a256 100644 (file)
@@ -787,6 +787,11 @@ struct elf_i386_link_hash_entry
   /* Symbol has non-GOT/non-PLT relocations in text sections.  */
   unsigned int has_non_got_reloc : 1;
 
+  /* 0: symbol isn't ___tls_get_addr.
+     1: symbol is ___tls_get_addr.
+     2: symbol is unknown.  */
+  unsigned int tls_get_addr : 2;
+
   /* Reference count of C/C++ function pointer relocations in read-write
      section which can be resolved at run-time.  */
   bfd_signed_vma func_pointer_refcount;
@@ -922,6 +927,7 @@ elf_i386_link_hash_newfunc (struct bfd_hash_entry *entry,
       eh->gotoff_ref = 0;
       eh->has_got_reloc = 0;
       eh->has_non_got_reloc = 0;
+      eh->tls_get_addr = 2;
       eh->func_pointer_refcount = 0;
       eh->plt_got.offset = (bfd_vma) -1;
       eh->tlsdesc_got = (bfd_vma) -1;
@@ -1216,10 +1222,12 @@ elf_i386_check_tls_transition (asection *sec,
                               const Elf_Internal_Rela *rel,
                               const Elf_Internal_Rela *relend)
 {
-  unsigned int val, type;
+  unsigned int val, type, reg;
   unsigned long r_symndx;
   struct elf_link_hash_entry *h;
   bfd_vma offset;
+  bfd_byte *call;
+  bfd_boolean indirect_call, tls_get_addr;
 
   offset = rel->r_offset;
   switch (r_type)
@@ -1229,69 +1237,130 @@ elf_i386_check_tls_transition (asection *sec,
       if (offset < 2 || (rel + 1) >= relend)
        return FALSE;
 
-      type = bfd_get_8 (abfd, contents + offset - 2);
+      indirect_call = FALSE;
+      call = contents + offset + 4;
+      val = *(call - 5);
+      type = *(call - 6);
       if (r_type == R_386_TLS_GD)
        {
          /* Check transition from GD access model.  Only
-               leal foo@tlsgd(,%reg,1), %eax; call ___tls_get_addr
-               leal foo@tlsgd(%reg), %eax; call ___tls_get_addr; nop
+               leal foo@tlsgd(,%ebx,1), %eax
+               call ___tls_get_addr@PLT
+            or
+               leal foo@tlsgd(%ebx) %eax
+               call ___tls_get_addr@PLT
+               nop
+            or
+               leal foo@tlsgd(%reg), %eax
+               call *___tls_get_addr@GOT(%reg)
+               which may be converted to
+               addr32 call ___tls_get_addr
             can transit to different access model.  */
-         if ((offset + 10) > sec->size ||
-             (type != 0x8d && type != 0x04))
+         if ((offset + 10) > sec->size
+             || (type != 0x8d && type != 0x04))
            return FALSE;
 
-         val = bfd_get_8 (abfd, contents + offset - 1);
          if (type == 0x04)
            {
-             /* leal foo@tlsgd(,%reg,1), %eax; call ___tls_get_addr */
+             /* leal foo@tlsgd(,%ebx,1), %eax
+                call ___tls_get_addr@PLT  */
              if (offset < 3)
                return FALSE;
 
-             if (bfd_get_8 (abfd, contents + offset - 3) != 0x8d)
-               return FALSE;
-
-             if ((val & 0xc7) != 0x05 || val == (4 << 3))
+             if (*(call - 7) != 0x8d
+                 || val != 0x1d
+                 || call[0] != 0xe8)
                return FALSE;
            }
          else
            {
-             /* leal foo@tlsgd(%reg), %eax; call ___tls_get_addr; nop  */
-             if ((val & 0xf8) != 0x80 || (val & 7) == 4)
+             /* This must be
+                       leal foo@tlsgd(%ebx), %eax
+                       call ___tls_get_addr@PLT
+                       nop
+                or
+                       leal foo@tlsgd(%reg), %eax
+                       call *___tls_get_addr@GOT(%reg)
+                       which may be converted to
+                       addr32 call ___tls_get_addr
+
+                %eax can't be used as the GOT base register since it
+                is used to pass parameter to ___tls_get_addr.  */
+             reg = val & 7;
+             if ((val & 0xf8) != 0x80 || reg == 4 || reg == 0)
                return FALSE;
 
-             if (bfd_get_8 (abfd, contents + offset + 9) != 0x90)
+             indirect_call = call[0] == 0xff;
+             if (!(reg == 3 && call[0] == 0xe8 && call[5] == 0x90)
+                 && !(call[0] == 0x67 && call[1] == 0xe8)
+                 && !(indirect_call
+                      && (call[1] & 0xf8) == 0x90
+                      && (call[1] & 0x7) == reg))
                return FALSE;
            }
        }
       else
        {
          /* Check transition from LD access model.  Only
-               leal foo@tlsgd(%reg), %eax; call ___tls_get_addr
+               leal foo@tlsldm(%ebx), %eax
+               call ___tls_get_addr@PLT
+            or
+               leal foo@tlsldm(%reg), %eax
+               call *___tls_get_addr@GOT(%reg)
+               which may be converted to
+               addr32 call ___tls_get_addr
             can transit to different access model.  */
          if (type != 0x8d || (offset + 9) > sec->size)
            return FALSE;
 
-         val = bfd_get_8 (abfd, contents + offset - 1);
-         if ((val & 0xf8) != 0x80 || (val & 7) == 4)
+         /* %eax can't be used as the GOT base register since it is
+            used to pass parameter to ___tls_get_addr.  */
+         reg = val & 7;
+         if ((val & 0xf8) != 0x80 || reg == 4 || reg == 0)
            return FALSE;
-       }
 
-      if (bfd_get_8 (abfd, contents + offset + 4) != 0xe8)
-       return FALSE;
+         indirect_call = call[0] == 0xff;
+         if (!(reg == 3 && call[0] == 0xe8)
+             && !(call[0] == 0x67 && call[1] == 0xe8)
+             && !(indirect_call
+                  && (call[1] & 0xf8) == 0x90
+                  && (call[1] & 0x7) == reg))
+           return FALSE;
+       }
 
       r_symndx = ELF32_R_SYM (rel[1].r_info);
       if (r_symndx < symtab_hdr->sh_info)
        return FALSE;
 
+      tls_get_addr = FALSE;
       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-      /* Use strncmp to check ___tls_get_addr since ___tls_get_addr
-        may be versioned.  */
-      return (h != NULL
-             && h->root.root.string != NULL
-             && (ELF32_R_TYPE (rel[1].r_info) == R_386_PC32
-                 || ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32)
-             && (strncmp (h->root.root.string, "___tls_get_addr",
-                          15) == 0));
+      if (h != NULL && h->root.root.string != NULL)
+       {
+         struct elf_i386_link_hash_entry *eh
+           = (struct elf_i386_link_hash_entry *) h;
+         tls_get_addr = eh->tls_get_addr == 1;
+         if (eh->tls_get_addr > 1)
+           {
+             /* Use strncmp to check ___tls_get_addr since
+                ___tls_get_addr may be versioned.  */
+             if (strncmp (h->root.root.string, "___tls_get_addr", 15)
+                 == 0)
+               {
+                 eh->tls_get_addr = 1;
+                 tls_get_addr = TRUE;
+               }
+             else
+               eh->tls_get_addr = 0;
+           }
+       }
+
+      if (!tls_get_addr)
+       return FALSE;
+      else if (indirect_call)
+       return (ELF32_R_TYPE (rel[1].r_info) == R_386_GOT32X);
+      else
+       return (ELF32_R_TYPE (rel[1].r_info) == R_386_PC32
+               || ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32);
 
     case R_386_TLS_IE:
       /* Check transition from IE access model:
@@ -1353,13 +1422,13 @@ elf_i386_check_tls_transition (asection *sec,
 
     case R_386_TLS_DESC_CALL:
       /* Check transition from GDesc access model:
-               call *x@tlsdesc(%rax)
+               call *x@tlsdesc(%eax)
        */
       if (offset + 2 <= sec->size)
        {
-         /* Make sure that it's a call *x@tlsdesc(%rax).  */
-         static const unsigned char call[] = { 0xff, 0x10 };
-         return memcmp (contents + offset, call, 2) == 0;
+         /* Make sure that it's a call *x@tlsdesc(%eax).  */
+         call = contents + offset;
+         return call[0] == 0xff && call[1] == 0x10;
        }
 
       return FALSE;
@@ -1544,7 +1613,7 @@ elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
   if (roff < 2)
     return TRUE;
 
-  /* Addend for R_386_GOT32 and R_386_GOT32X relocations must be 0.  */
+  /* Addend for R_386_GOT32X relocations must be 0.  */
   addend = bfd_get_32 (abfd, contents + roff);
   if (addend != 0)
     return TRUE;
@@ -1558,11 +1627,10 @@ elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
   modrm = bfd_get_8 (abfd, contents + roff - 1);
   baseless = (modrm & 0xc7) == 0x5;
 
-  if (r_type == R_386_GOT32X && baseless && is_pic)
+  if (baseless && is_pic)
     {
       /* For PIC, disallow R_386_GOT32X without a base register
-        since we don't know what the GOT base is.   Allow
-        R_386_GOT32 for existing object files.  */
+        since we don't know what the GOT base is.  */
       const char *name;
 
       if (h == NULL)
@@ -1582,22 +1650,12 @@ elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
 
   opcode = bfd_get_8 (abfd, contents + roff - 2);
 
-  /* Convert mov to lea since it has been done for a while.  */
-  if (opcode != 0x8b)
-    {
-      /* Only convert R_386_GOT32X relocation for call, jmp or
-        one of adc, add, and, cmp, or, sbb, sub, test, xor
-        instructions.  */
-      if (r_type != R_386_GOT32X)
-       return TRUE;
-    }
-
   /* Convert to R_386_32 if PIC is false or there is no base
      register.  */
   to_reloc_32 = !is_pic || baseless;
 
-  /* Try to convert R_386_GOT32 and R_386_GOT32X.  Get the symbol
-     referred to by the reloc.  */
+  /* Try to convert R_386_GOT32X.  Get the symbol referred to by the
+     reloc.  */
   if (h == NULL)
     {
       if (opcode == 0x0ff)
@@ -1643,17 +1701,30 @@ convert_branch:
          /* Convert R_386_GOT32X to R_386_PC32.  */
          if (modrm == 0x15 || (modrm & 0xf8) == 0x90)
            {
+             struct elf_i386_link_hash_entry *eh
+               = (struct elf_i386_link_hash_entry *) h;
+
              /* Convert to "nop call foo".  ADDR_PREFIX_OPCODE
                 is a nop prefix.  */
              modrm = 0xe8;
-             nop = link_info->call_nop_byte;
-             if (link_info->call_nop_as_suffix)
+             /* To support TLS optimization, always use addr32 prefix
+                for "call *___tls_get_addr@GOT(%reg)".  */
+             if (eh && eh->tls_get_addr == 1)
                {
-                 nop_offset = roff + 3;
-                 irel->r_offset -= 1;
+                 nop = 0x67;
+                 nop_offset = irel->r_offset - 2;
                }
              else
-               nop_offset = roff - 2;
+               {
+                 nop = link_info->call_nop_byte;
+                 if (link_info->call_nop_as_suffix)
+                   {
+                     nop_offset = roff + 3;
+                     irel->r_offset -= 1;
+                   }
+                 else
+                   nop_offset = roff - 2;
+               }
            }
          else
            {
@@ -1775,6 +1846,15 @@ elf_i386_check_relocs (bfd *abfd,
   if (bfd_link_relocatable (info))
     return TRUE;
 
+  /* Don't do anything special with non-loaded, non-alloced sections.
+     In particular, any relocs in such sections should not affect GOT
+     and PLT reference counting (ie. we don't allow them to create GOT
+     or PLT entries), there's no possibility or desire to optimize TLS
+     relocs, and there's not much point in propagating relocs to shared
+     libs that the dynamic linker won't relocate.  */
+  if ((sec->flags & SEC_ALLOC) == 0)
+    return TRUE;
+
   BFD_ASSERT (is_i386_elf (abfd));
 
   htab = elf_i386_hash_table (info);
@@ -2069,15 +2149,12 @@ elf_i386_check_relocs (bfd *abfd,
          if (eh != NULL && (sec->flags & SEC_CODE) != 0)
            eh->has_non_got_reloc = 1;
 do_relocation:
-         /* STT_GNU_IFUNC symbol must go through PLT even if it is
-            locally defined and undefined symbol may turn out to be
-            a STT_GNU_IFUNC symbol later.  */
+         /* We are called after all symbols have been resolved.  Only
+            relocation against STT_GNU_IFUNC symbol must go through
+            PLT.  */
          if (h != NULL
              && (bfd_link_executable (info)
-                 || ((h->type == STT_GNU_IFUNC
-                      || h->root.type == bfd_link_hash_undefweak
-                      || h->root.type == bfd_link_hash_undefined)
-                     && SYMBOLIC_BIND (info, h))))
+                 || h->type == STT_GNU_IFUNC))
            {
              /* If this reloc is in a read-only section, we might
                 need a copy reloc.  We can't check reliably at this
@@ -2087,9 +2164,13 @@ do_relocation:
                 adjust_dynamic_symbol.  */
              h->non_got_ref = 1;
 
-             /* We may need a .plt entry if the function this reloc
-                refers to is in a shared lib.  */
-             h->plt.refcount += 1;
+             /* We may need a .plt entry if the symbol is a function
+                defined in a shared lib or is a STT_GNU_IFUNC function
+                referenced from the code or read-only section.  */
+             if (!h->def_regular
+                 || (sec->flags & (SEC_CODE | SEC_READONLY)) != 0)
+               h->plt.refcount += 1;
+
              if (r_type == R_386_PC32)
                {
                  /* Since something like ".long foo - ." may be used
@@ -2130,18 +2211,23 @@ do_size:
             If on the other hand, we are creating an executable, we
             may need to keep relocations for symbols satisfied by a
             dynamic library if we manage to avoid copy relocs for the
-            symbol.  */
+            symbol.
+
+            Generate dynamic pointer relocation against STT_GNU_IFUNC
+            symbol in the non-code section.  */
          if ((bfd_link_pic (info)
-              && (sec->flags & SEC_ALLOC) != 0
               && (r_type != R_386_PC32
                   || (h != NULL
                       && (! (bfd_link_pie (info)
                              || SYMBOLIC_BIND (info, h))
                           || h->root.type == bfd_link_hash_defweak
                           || !h->def_regular))))
+             || (h != NULL
+                 && h->type == STT_GNU_IFUNC
+                 && r_type == R_386_32
+                 && (sec->flags & SEC_CODE) == 0)
              || (ELIMINATE_COPY_RELOCS
                  && !bfd_link_pic (info)
-                 && (sec->flags & SEC_ALLOC) != 0
                  && h != NULL
                  && (h->root.type == bfd_link_hash_defweak
                      || !h->def_regular)))
@@ -2267,7 +2353,7 @@ do_size:
            goto error_return;
        }
 
-      if ((r_type == R_386_GOT32 || r_type == R_386_GOT32X)
+      if (r_type == R_386_GOT32X
          && (h == NULL || h->type != STT_GNU_IFUNC))
        sec->need_convert_load = 1;
     }
@@ -2373,12 +2459,17 @@ elf_i386_adjust_dynamic_symbol (struct bfd_link_info *info,
 
          if (pc_count || count)
            {
-             h->needs_plt = 1;
              h->non_got_ref = 1;
-             if (h->plt.refcount <= 0)
-               h->plt.refcount = 1;
-             else
-               h->plt.refcount += 1;
+             if (pc_count)
+               {
+                 /* Increment PLT reference count only for PC-relative
+                    references.  */
+                 h->needs_plt = 1;
+                 if (h->plt.refcount <= 0)
+                   h->plt.refcount = 1;
+                 else
+                   h->plt.refcount += 1;
+               }
            }
        }
 
@@ -2568,7 +2659,7 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
     return _bfd_elf_allocate_ifunc_dyn_relocs (info, h, &eh->dyn_relocs,
                                               &htab->readonly_dynrelocs_against_ifunc,
                                               plt_entry_size,
-                                              plt_entry_size, 4);
+                                              plt_entry_size, 4, TRUE);
   /* Don't create the PLT entry if there are only function pointer
      relocations which can be resolved at run-time.  */
   else if (htab->elf.dynamic_sections_created
@@ -3021,7 +3112,9 @@ elf_i386_convert_load (bfd *abfd, asection *sec,
       struct elf_link_hash_entry *h;
       bfd_boolean converted;
 
-      if (r_type != R_386_GOT32 && r_type != R_386_GOT32X)
+      /* Don't convert R_386_GOT32 since we can't tell if it is applied
+        to "mov $foo@GOT, %reg" which isn't a load via GOT.  */
+      if (r_type != R_386_GOT32X)
        continue;
 
       r_symndx = ELF32_R_SYM (irel->r_info);
@@ -3821,8 +3914,6 @@ elf_i386_relocate_section (bfd *output_bfd,
                continue;
              abort ();
            }
-         else if (h->plt.offset == (bfd_vma) -1)
-           abort ();
 
          /* STT_GNU_IFUNC symbol must go through PLT.  */
          if (htab->elf.splt != NULL)
@@ -3836,12 +3927,106 @@ elf_i386_relocate_section (bfd *output_bfd,
              gotplt = htab->elf.igotplt;
            }
 
+         switch (r_type)
+           {
+           default:
+             break;
+
+           case R_386_GOT32:
+           case R_386_GOT32X:
+             base_got = htab->elf.sgot;
+             off = h->got.offset;
+
+             if (base_got == NULL)
+               abort ();
+
+             if (off == (bfd_vma) -1)
+               {
+                 /* We can't use h->got.offset here to save state, or
+                    even just remember the offset, as finish_dynamic_symbol
+                    would use that as offset into .got.  */
+
+                 if (h->plt.offset == (bfd_vma) -1)
+                   abort ();
+
+                 if (htab->elf.splt != NULL)
+                   {
+                     plt_index = h->plt.offset / plt_entry_size - 1;
+                     off = (plt_index + 3) * 4;
+                     base_got = htab->elf.sgotplt;
+                   }
+                 else
+                   {
+                     plt_index = h->plt.offset / plt_entry_size;
+                     off = plt_index * 4;
+                     base_got = htab->elf.igotplt;
+                   }
+
+                 if (h->dynindx == -1
+                     || h->forced_local
+                     || info->symbolic)
+                   {
+                     /* This references the local defitionion.  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_32 (output_bfd, relocation,
+                                     base_got->contents + off);
+                         h->got.offset |= 1;
+                       }
+                   }
+
+                 relocation = off;
+               }
+             else
+               relocation = (base_got->output_section->vma
+                             + base_got->output_offset + off
+                             - gotplt->output_section->vma
+                             - gotplt->output_offset);
+
+             if ((*(contents + rel->r_offset - 1) & 0xc7) == 0x5)
+               {
+                 if (bfd_link_pic (info))
+                   goto disallow_got32;
+
+                 /* Add the GOT base if there is no base register.  */
+                 relocation += (gotplt->output_section->vma
+                                + gotplt->output_offset);
+               }
+             else if (htab->elf.splt == NULL)
+               {
+                 /* Adjust for static executables.  */
+                 relocation += gotplt->output_offset;
+               }
+
+             goto do_relocation;
+           }
+
+         if (h->plt.offset == (bfd_vma) -1)
+           {
+             /* Handle static pointers of STT_GNU_IFUNC symbols.  */
+             if (r_type == R_386_32
+                 && (input_section->flags & SEC_CODE) == 0)
+               goto do_ifunc_pointer;
+             goto bad_ifunc_reloc;
+           }
+
          relocation = (plt->output_section->vma
                        + plt->output_offset + h->plt.offset);
 
          switch (r_type)
            {
            default:
+bad_ifunc_reloc:
              if (h->root.root.string)
                name = h->root.root.string;
              else
@@ -3849,21 +4034,22 @@ elf_i386_relocate_section (bfd *output_bfd,
                                         NULL);
              (*_bfd_error_handler)
                (_("%B: relocation %s against STT_GNU_IFUNC "
-                  "symbol `%s' isn't handled by %s"), input_bfd,
-                elf_howto_table[r_type].name,
-                name, __FUNCTION__);
+                  "symbol `%s' isn't supported"), input_bfd,
+                howto->name, name);
              bfd_set_error (bfd_error_bad_value);
              return FALSE;
 
            case R_386_32:
              /* Generate dynamic relcoation only when there is a
                 non-GOT reference in a shared object.  */
-             if (bfd_link_pic (info) && h->non_got_ref)
+             if ((bfd_link_pic (info) && h->non_got_ref)
+                 || h->plt.offset == (bfd_vma) -1)
                {
                  Elf_Internal_Rela outrel;
                  asection *sreloc;
                  bfd_vma offset;
 
+do_ifunc_pointer:
                  /* Need a dynamic relocation to get the real function
                     adddress.  */
                  offset = _bfd_elf_section_offset (output_bfd,
@@ -3893,7 +4079,16 @@ elf_i386_relocate_section (bfd *output_bfd,
                  else
                    outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
 
-                 sreloc = htab->elf.irelifunc;
+                 /* Dynamic relocations are stored in
+                    1. .rel.ifunc section in PIC object.
+                    2. .rel.got section in dynamic executable.
+                    3. .rel.iplt section in static executable.  */
+                 if (bfd_link_pic (info))
+                   sreloc = htab->elf.irelifunc;
+                 else if (htab->elf.splt != NULL)
+                   sreloc = htab->elf.srelgot;
+                 else
+                   sreloc = htab->elf.irelplt;
                  elf_append_rel (output_bfd, sreloc, &outrel);
 
                  /* If this reloc is against an external symbol, we
@@ -3908,75 +4103,6 @@ elf_i386_relocate_section (bfd *output_bfd,
            case R_386_PLT32:
              goto do_relocation;
 
-           case R_386_GOT32:
-           case R_386_GOT32X:
-             base_got = htab->elf.sgot;
-             off = h->got.offset;
-
-             if (base_got == NULL)
-               abort ();
-
-             if (off == (bfd_vma) -1)
-               {
-                 /* We can't use h->got.offset here to save state, or
-                    even just remember the offset, as finish_dynamic_symbol
-                    would use that as offset into .got.  */
-
-                 if (htab->elf.splt != NULL)
-                   {
-                     plt_index = h->plt.offset / plt_entry_size - 1;
-                     off = (plt_index + 3) * 4;
-                     base_got = htab->elf.sgotplt;
-                   }
-                 else
-                   {
-                     plt_index = h->plt.offset / plt_entry_size;
-                     off = plt_index * 4;
-                     base_got = htab->elf.igotplt;
-                   }
-
-                 if (h->dynindx == -1
-                     || h->forced_local
-                     || info->symbolic)
-                   {
-                     /* This references the local defitionion.  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_32 (output_bfd, relocation,
-                                     base_got->contents + off);
-                         h->got.offset |= 1;
-                       }
-                   }
-
-                 relocation = off;
-
-                 /* Adjust for static executables.  */
-                 if (htab->elf.splt == NULL)
-                   relocation += gotplt->output_offset;
-               }
-             else
-               {
-                 relocation = (base_got->output_section->vma
-                               + base_got->output_offset + off
-                               - gotplt->output_section->vma
-                               - gotplt->output_offset);
-                 /* Adjust for static executables.  */
-                 if (htab->elf.splt == NULL)
-                   relocation += gotplt->output_offset;
-               }
-
-             goto do_relocation;
-
            case R_386_GOTOFF:
              relocation -= (gotplt->output_section->vma
                             + gotplt->output_offset);
@@ -4135,10 +4261,39 @@ r_386_got32:
          if (off >= (bfd_vma) -2)
            abort ();
 
-         relocation = htab->elf.sgot->output_section->vma
-                      + htab->elf.sgot->output_offset + off
-                      - htab->elf.sgotplt->output_section->vma
-                      - htab->elf.sgotplt->output_offset;
+         relocation = (htab->elf.sgot->output_section->vma
+                       + htab->elf.sgot->output_offset + off);
+         if ((*(contents + rel->r_offset - 1) & 0xc7) == 0x5)
+           {
+             if (bfd_link_pic (info))
+               {
+                 /* For PIC, disallow R_386_GOT32 without a base
+                    register since we don't know what the GOT base
+                    is.  */
+                 const char *name;
+
+disallow_got32:
+                 if (h == NULL)
+                   name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
+                                            NULL);
+                 else
+                   name = h->root.root.string;
+
+                 (*_bfd_error_handler)
+                   (_("%B: direct GOT relocation %s against `%s' without base register can not be used when making a shared object"),
+                    input_bfd, howto->name, name);
+                 bfd_set_error (bfd_error_bad_value);
+                 return FALSE;
+               }
+           }
+         else
+           {
+             /* Subtract the .got.plt section address only with a base
+                register.  */
+             relocation -= (htab->elf.sgotplt->output_section->vma
+                            + htab->elf.sgotplt->output_offset);
+           }
+
          break;
 
        case R_386_GOTOFF:
@@ -4381,30 +4536,39 @@ r_386_got32:
                  bfd_vma roff;
 
                  /* GD->LE transition.  */
-                 type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2);
+                 type = *(contents + rel->r_offset - 2);
                  if (type == 0x04)
                    {
-                     /* leal foo(,%reg,1), %eax; call ___tls_get_addr
-                        Change it into:
-                        movl %gs:0, %eax; subl $foo@tpoff, %eax
+                     /* Change
+                               leal foo@tlsgd(,%ebx,1), %eax
+                               call ___tls_get_addr@PLT
+                        into:
+                               movl %gs:0, %eax
+                               subl $foo@tpoff, %eax
                         (6 byte form of subl).  */
-                     memcpy (contents + rel->r_offset - 3,
-                             "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
                      roff = rel->r_offset + 5;
                    }
                  else
                    {
-                     /* leal foo(%reg), %eax; call ___tls_get_addr; nop
-                        Change it into:
-                        movl %gs:0, %eax; subl $foo@tpoff, %eax
+                     /* Change
+                               leal foo@tlsgd(%ebx), %eax
+                               call ___tls_get_addr@PLT
+                               nop
+                        or
+                               leal foo@tlsgd(%reg), %eax
+                               call *___tls_get_addr@GOT(%reg)
+                               which may be converted to
+                               addr32 call ___tls_get_addr
+                        into:
+                               movl %gs:0, %eax; subl $foo@tpoff, %eax
                         (6 byte form of subl).  */
-                     memcpy (contents + rel->r_offset - 2,
-                             "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
                      roff = rel->r_offset + 6;
                    }
+                 memcpy (contents + roff - 8,
+                         "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
                  bfd_put_32 (output_bfd, elf_i386_tpoff (info, relocation),
                              contents + roff);
-                 /* Skip R_386_PC32/R_386_PLT32.  */
+                 /* Skip R_386_PC32, R_386_PLT32 and R_386_GOT32X.  */
                  rel++;
                  wrel++;
                  continue;
@@ -4711,21 +4875,33 @@ r_386_got32:
              bfd_vma roff;
 
              /* GD->IE transition.  */
-             type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2);
-             val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1);
+             type = *(contents + rel->r_offset - 2);
+             val = *(contents + rel->r_offset - 1);
              if (type == 0x04)
                {
-                 /* leal foo(,%reg,1), %eax; call ___tls_get_addr
-                    Change it into:
-                    movl %gs:0, %eax; subl $foo@gottpoff(%reg), %eax.  */
+                 /* Change
+                       leal foo@tlsgd(,%ebx,1), %eax
+                       call ___tls_get_addr@PLT
+                    into:
+                       movl %gs:0, %eax
+                       subl $foo@gottpoff(%ebx), %eax.  */
                  val >>= 3;
                  roff = rel->r_offset - 3;
                }
              else
                {
-                 /* leal foo(%reg), %eax; call ___tls_get_addr; nop
-                    Change it into:
-                    movl %gs:0, %eax; subl $foo@gottpoff(%reg), %eax.  */
+                 /* Change
+                       leal foo@tlsgd(%ebx), %eax
+                       call ___tls_get_addr@PLT
+                       nop
+                    or
+                       leal foo@tlsgd(%reg), %eax
+                       call *___tls_get_addr@GOT(%reg)
+                       which may be converted to
+                       addr32 call ___tls_get_addr
+                    into:
+                       movl %gs:0, %eax;
+                       subl $foo@gottpoff(%reg), %eax.  */
                  roff = rel->r_offset - 2;
                }
              memcpy (contents + roff,
@@ -4744,7 +4920,7 @@ r_386_got32:
                          - htab->elf.sgotplt->output_section->vma
                          - htab->elf.sgotplt->output_offset,
                          contents + roff + 8);
-             /* Skip R_386_PLT32.  */
+             /* Skip R_386_PLT32 and R_386_GOT32X.  */
              rel++;
              wrel++;
              continue;
@@ -4835,13 +5011,29 @@ r_386_got32:
 
          if (r_type != R_386_TLS_LDM)
            {
-             /* LD->LE transition:
-                leal foo(%reg), %eax; call ___tls_get_addr.
-                We change it into:
-                movl %gs:0, %eax; nop; leal 0(%esi,1), %esi.  */
+             /* LD->LE transition.  Change
+                       leal foo@tlsldm(%ebx) %eax
+                       call ___tls_get_addr@PLT
+                into:
+                       movl %gs:0, %eax
+                       nop
+                       leal 0(%esi,1), %esi
+                or change
+                       leal foo@tlsldm(%reg) %eax
+                       call *___tls_get_addr@GOT(%reg)
+                       which may be converted to
+                       addr32 call ___tls_get_addr
+                into:
+                       movl %gs:0, %eax
+                       leal 0(%esi), %esi  */
              BFD_ASSERT (r_type == R_386_TLS_LE_32);
-             memcpy (contents + rel->r_offset - 2,
-                     "\x65\xa1\0\0\0\0\x90\x8d\x74\x26", 11);
+             if (*(contents + rel->r_offset + 4) == 0xff
+                 || *(contents + rel->r_offset + 4) == 0x67)
+               memcpy (contents + rel->r_offset - 2,
+                       "\x65\xa1\0\0\0\0\x8d\xb6\0\0\0", 12);
+             else
+               memcpy (contents + rel->r_offset - 2,
+                       "\x65\xa1\0\0\0\0\x90\x8d\x74\x26", 11);
              /* Skip R_386_PC32/R_386_PLT32.  */
              rel++;
              wrel++;
@@ -4970,13 +5162,9 @@ check_relocation_error:
            }
 
          if (r == bfd_reloc_overflow)
-           {
-             if (! ((*info->callbacks->reloc_overflow)
-                    (info, (h ? &h->root : NULL), name, howto->name,
-                     (bfd_vma) 0, input_bfd, input_section,
-                     rel->r_offset)))
-               return FALSE;
-           }
+           (*info->callbacks->reloc_overflow)
+             (info, (h ? &h->root : NULL), name, howto->name,
+              (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
          else
            {
              (*_bfd_error_handler)
@@ -5287,6 +5475,7 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
       && !local_undefweak)
     {
       Elf_Internal_Rela rel;
+      asection *relgot = htab->elf.srelgot;
 
       /* This symbol has an entry in the global offset table.  Set it
         up.  */
@@ -5306,7 +5495,28 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
       if (h->def_regular
          && h->type == STT_GNU_IFUNC)
        {
-         if (bfd_link_pic (info))
+         if (h->plt.offset == (bfd_vma) -1)
+           {
+             /* STT_GNU_IFUNC is referenced without PLT.  */
+             if (htab->elf.splt == NULL)
+               {
+                 /* use .rel[a].iplt section to store .got relocations
+                    in static executable.  */
+                 relgot = htab->elf.irelplt;
+               }
+             if (SYMBOL_REFERENCES_LOCAL (info, h))
+               {
+                 bfd_put_32 (output_bfd,
+                             (h->root.u.def.value
+                              + h->root.u.def.section->output_section->vma
+                              + h->root.u.def.section->output_offset),
+                             htab->elf.sgot->contents + h->got.offset);
+                 rel.r_info = ELF32_R_INFO (0, R_386_IRELATIVE);
+               }
+             else
+               goto do_glob_dat;
+           }
+         else if (bfd_link_pic (info))
            {
              /* Generate R_386_GLOB_DAT.  */
              goto do_glob_dat;
@@ -5344,7 +5554,7 @@ do_glob_dat:
          rel.r_info = ELF32_R_INFO (h->dynindx, R_386_GLOB_DAT);
        }
 
-      elf_append_rel (output_bfd, htab->elf.srelgot, &rel);
+      elf_append_rel (output_bfd, relgot, &rel);
     }
 
   if (h->needs_copy)
@@ -5421,19 +5631,24 @@ elf_i386_reloc_type_class (const struct bfd_link_info *info,
       /* Check relocation against STT_GNU_IFUNC symbol if there are
          dynamic symbols.  */
       unsigned long r_symndx = ELF32_R_SYM (rela->r_info);
-      Elf_Internal_Sym sym;
-      if (!bed->s->swap_symbol_in (abfd,
-                                  (htab->dynsym->contents
-                                   + r_symndx * sizeof (Elf32_External_Sym)),
-                                  0, &sym))
-       abort ();
+      if (r_symndx != STN_UNDEF)
+       {
+         Elf_Internal_Sym sym;
+         if (!bed->s->swap_symbol_in (abfd,
+                                      (htab->dynsym->contents
+                                       + r_symndx * sizeof (Elf32_External_Sym)),
+                                      0, &sym))
+           abort ();
 
-      if (ELF32_ST_TYPE (sym.st_info) == STT_GNU_IFUNC)
-       return reloc_class_ifunc;
+         if (ELF32_ST_TYPE (sym.st_info) == STT_GNU_IFUNC)
+           return reloc_class_ifunc;
+       }
     }
 
   switch (ELF32_R_TYPE (rela->r_info))
     {
+    case R_386_IRELATIVE:
+      return reloc_class_ifunc;
     case R_386_RELATIVE:
       return reloc_class_relative;
     case R_386_JUMP_SLOT:
@@ -5676,11 +5891,6 @@ elf_i386_finish_dynamic_sections (bfd *output_bfd,
   if (htab->elf.sgot && htab->elf.sgot->size > 0)
     elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize = 4;
 
-  /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols.  */
-  htab_traverse (htab->loc_hash_table,
-                elf_i386_finish_local_dynamic_symbol,
-                info);
-
   /* Fill PLT entries for undefined weak symbols in PIE.  */
   if (bfd_link_pie (info))
     bfd_hash_traverse (&info->hash->table,
@@ -5690,6 +5900,33 @@ elf_i386_finish_dynamic_sections (bfd *output_bfd,
   return TRUE;
 }
 
+/* Fill PLT/GOT entries and allocate dynamic relocations for local
+   STT_GNU_IFUNC symbols, which aren't in the ELF linker hash table.
+   It has to be done before elf_link_sort_relocs is called so that
+   dynamic relocations are properly sorted.  */
+
+static bfd_boolean
+elf_i386_output_arch_local_syms
+  (bfd *output_bfd ATTRIBUTE_UNUSED,
+   struct bfd_link_info *info,
+   void *flaginfo ATTRIBUTE_UNUSED,
+   int (*func) (void *, const char *,
+               Elf_Internal_Sym *,
+               asection *,
+               struct elf_link_hash_entry *) ATTRIBUTE_UNUSED)
+{
+  struct elf_i386_link_hash_table *htab = elf_i386_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
+  /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols.  */
+  htab_traverse (htab->loc_hash_table,
+                elf_i386_finish_local_dynamic_symbol,
+                info);
+
+  return TRUE;
+}
+
 /* Return an array of PLT entry symbol values.  */
 
 static bfd_vma *
@@ -5832,6 +6069,7 @@ elf_i386_hash_symbol (struct elf_link_hash_entry *h)
 #define elf_backend_fake_sections            elf_i386_fake_sections
 #define elf_backend_finish_dynamic_sections   elf_i386_finish_dynamic_sections
 #define elf_backend_finish_dynamic_symbol     elf_i386_finish_dynamic_symbol
+#define elf_backend_output_arch_local_syms     elf_i386_output_arch_local_syms
 #define elf_backend_gc_mark_hook             elf_i386_gc_mark_hook
 #define elf_backend_grok_prstatus            elf_i386_grok_prstatus
 #define elf_backend_grok_psinfo                      elf_i386_grok_psinfo
This page took 0.04492 seconds and 4 git commands to generate.