Convert indirect calls to direct when possible.
[external/binutils.git] / bfd / elf32-i386.c
index f6c9c65..4179572 100644 (file)
@@ -737,6 +737,20 @@ static const struct elf_i386_backend_data elf_i386_arch_bed =
 
 #define        elf_backend_arch_data   &elf_i386_arch_bed
 
 
 #define        elf_backend_arch_data   &elf_i386_arch_bed
 
+/* Is a undefined weak symbol which is resolved to 0.  Reference to an
+   undefined weak symbol is resolved to 0 when building executable if
+   it isn't dynamic and
+   1. Has non-GOT/non-PLT relocations in text section.  Or
+   2. Has no GOT/PLT relocation.
+ */
+#define UNDEFINED_WEAK_RESOLVED_TO_ZERO(INFO, GOT_RELOC, EH)   \
+  ((EH)->elf.root.type == bfd_link_hash_undefweak              \
+   && bfd_link_executable (INFO)                               \
+   && (elf_i386_hash_table (INFO)->interp == NULL              \
+       || !(GOT_RELOC)                                         \
+       || (EH)->has_non_got_reloc                              \
+       || !(INFO)->dynamic_undefined_weak))
+
 /* i386 ELF linker hash entry.  */
 
 struct elf_i386_link_hash_entry
 /* i386 ELF linker hash entry.  */
 
 struct elf_i386_link_hash_entry
@@ -767,6 +781,17 @@ struct elf_i386_link_hash_entry
   /* Symbol is referenced by R_386_GOTOFF relocation.  */
   unsigned int gotoff_ref : 1;
 
   /* Symbol is referenced by R_386_GOTOFF relocation.  */
   unsigned int gotoff_ref : 1;
 
+  /* Symbol has GOT or PLT relocations.  */
+  unsigned int has_got_reloc : 1;
+
+  /* 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;
   /* 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;
@@ -821,6 +846,7 @@ struct elf_i386_link_hash_table
   struct elf_link_hash_table elf;
 
   /* Short-cuts to get to dynamic linker sections.  */
   struct elf_link_hash_table elf;
 
   /* Short-cuts to get to dynamic linker sections.  */
+  asection *interp;
   asection *sdynbss;
   asection *srelbss;
   asection *plt_eh_frame;
   asection *sdynbss;
   asection *srelbss;
   asection *plt_eh_frame;
@@ -857,6 +883,10 @@ struct elf_i386_link_hash_table
 
   /* The index of the next unused R_386_IRELATIVE slot in .rel.plt.  */
   bfd_vma next_irelative_index;
 
   /* The index of the next unused R_386_IRELATIVE slot in .rel.plt.  */
   bfd_vma next_irelative_index;
+
+  /* TRUE if there are dynamic relocs against IFUNC symbols that apply
+     to read-only sections.  */
+  bfd_boolean readonly_dynrelocs_against_ifunc;
 };
 
 /* Get the i386 ELF linker hash table from a link_info structure.  */
 };
 
 /* Get the i386 ELF linker hash table from a link_info structure.  */
@@ -895,6 +925,9 @@ elf_i386_link_hash_newfunc (struct bfd_hash_entry *entry,
       eh->dyn_relocs = NULL;
       eh->tls_type = GOT_UNKNOWN;
       eh->gotoff_ref = 0;
       eh->dyn_relocs = NULL;
       eh->tls_type = GOT_UNKNOWN;
       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;
       eh->func_pointer_refcount = 0;
       eh->plt_got.offset = (bfd_vma) -1;
       eh->tlsdesc_got = (bfd_vma) -1;
@@ -1039,6 +1072,17 @@ elf_i386_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
   if (htab == NULL)
     return FALSE;
 
   if (htab == NULL)
     return FALSE;
 
+  /* Set the contents of the .interp section to the interpreter.  */
+  if (bfd_link_executable (info) && !info->nointerp)
+    {
+      asection *s = bfd_get_linker_section (dynobj, ".interp");
+      if (s == NULL)
+       abort ();
+      s->size = sizeof ELF_DYNAMIC_INTERPRETER;
+      s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER;
+      htab->interp = s;
+    }
+
   htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss");
   if (!htab->sdynbss)
     abort ();
   htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss");
   if (!htab->sdynbss)
     abort ();
@@ -1138,6 +1182,9 @@ elf_i386_copy_indirect_symbol (struct bfd_link_info *info,
      generate a R_386_COPY reloc.  */
   edir->gotoff_ref |= eind->gotoff_ref;
 
      generate a R_386_COPY reloc.  */
   edir->gotoff_ref |= eind->gotoff_ref;
 
+  edir->has_got_reloc |= eind->has_got_reloc;
+  edir->has_non_got_reloc |= eind->has_non_got_reloc;
+
   if (ELIMINATE_COPY_RELOCS
       && ind->root.type != bfd_link_hash_indirect
       && dir->dynamic_adjusted)
   if (ELIMINATE_COPY_RELOCS
       && ind->root.type != bfd_link_hash_indirect
       && dir->dynamic_adjusted)
@@ -1167,7 +1214,7 @@ elf_i386_copy_indirect_symbol (struct bfd_link_info *info,
    from R_TYPE.  */
 
 static bfd_boolean
    from R_TYPE.  */
 
 static bfd_boolean
-elf_i386_check_tls_transition (bfd *abfd, asection *sec,
+elf_i386_check_tls_transition (asection *sec,
                               bfd_byte *contents,
                               Elf_Internal_Shdr *symtab_hdr,
                               struct elf_link_hash_entry **sym_hashes,
                               bfd_byte *contents,
                               Elf_Internal_Shdr *symtab_hdr,
                               struct elf_link_hash_entry **sym_hashes,
@@ -1175,26 +1222,12 @@ elf_i386_check_tls_transition (bfd *abfd, asection *sec,
                               const Elf_Internal_Rela *rel,
                               const Elf_Internal_Rela *relend)
 {
                               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;
   unsigned long r_symndx;
   struct elf_link_hash_entry *h;
   bfd_vma offset;
-
-  /* Get the section contents.  */
-  if (contents == NULL)
-    {
-      if (elf_section_data (sec)->this_hdr.contents != NULL)
-       contents = elf_section_data (sec)->this_hdr.contents;
-      else
-       {
-         /* FIXME: How to better handle error condition?  */
-         if (!bfd_malloc_and_get_section (abfd, sec, &contents))
-           return FALSE;
-
-         /* Cache the section contents for elf_link_input_bfd.  */
-         elf_section_data (sec)->this_hdr.contents = contents;
-       }
-    }
+  bfd_byte *call;
+  bfd_boolean indirect_call, tls_get_addr;
 
   offset = rel->r_offset;
   switch (r_type)
 
   offset = rel->r_offset;
   switch (r_type)
@@ -1204,69 +1237,130 @@ elf_i386_check_tls_transition (bfd *abfd, asection *sec,
       if (offset < 2 || (rel + 1) >= relend)
        return FALSE;
 
       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
       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.  */
             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;
 
            return FALSE;
 
-         val = bfd_get_8 (abfd, contents + offset - 1);
          if (type == 0x04)
            {
          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 (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
            {
                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;
 
                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
                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;
 
             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;
            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;
 
 
       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];
       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:
 
     case R_386_TLS_IE:
       /* Check transition from IE access model:
@@ -1328,13 +1422,13 @@ elf_i386_check_tls_transition (bfd *abfd, asection *sec,
 
     case R_386_TLS_DESC_CALL:
       /* Check transition from GDesc access model:
 
     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)
        {
        */
       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;
        }
 
       return FALSE;
@@ -1356,7 +1450,8 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
                         const Elf_Internal_Rela *rel,
                         const Elf_Internal_Rela *relend,
                         struct elf_link_hash_entry *h,
                         const Elf_Internal_Rela *rel,
                         const Elf_Internal_Rela *relend,
                         struct elf_link_hash_entry *h,
-                        unsigned long r_symndx)
+                        unsigned long r_symndx,
+                        bfd_boolean from_relocate_section)
 {
   unsigned int from_type = *r_type;
   unsigned int to_type = from_type;
 {
   unsigned int from_type = *r_type;
   unsigned int to_type = from_type;
@@ -1385,10 +1480,9 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
            to_type = R_386_TLS_IE_32;
        }
 
            to_type = R_386_TLS_IE_32;
        }
 
-      /* When we are called from elf_i386_relocate_section, CONTENTS
-        isn't NULL and there may be additional transitions based on
-        TLS_TYPE.  */
-      if (contents != NULL)
+      /* When we are called from elf_i386_relocate_section, there may
+        be additional transitions based on TLS_TYPE.  */
+      if (from_relocate_section)
        {
          unsigned int new_to_type = to_type;
 
        {
          unsigned int new_to_type = to_type;
 
@@ -1432,7 +1526,7 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
 
   /* Check if the transition can be performed.  */
   if (check
 
   /* Check if the transition can be performed.  */
   if (check
-      && ! elf_i386_check_tls_transition (abfd, sec, contents,
+      && ! elf_i386_check_tls_transition (sec, contents,
                                          symtab_hdr, sym_hashes,
                                          from_type, rel, relend))
     {
                                          symtab_hdr, sym_hashes,
                                          from_type, rel, relend))
     {
@@ -1474,9 +1568,261 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
   return TRUE;
 }
 
   return TRUE;
 }
 
+/* With the local symbol, foo, we convert
+   mov foo@GOT[(%reg1)], %reg2
+   to
+   lea foo[@GOTOFF(%reg1)], %reg2
+   and convert
+   call/jmp *foo@GOT[(%reg)]
+   to
+   nop call foo/jmp foo nop
+   When PIC is false, convert
+   test %reg1, foo@GOT[(%reg2)]
+   to
+   test $foo, %reg1
+   and convert
+   binop foo@GOT[(%reg1)], %reg2
+   to
+   binop $foo, %reg2
+   where binop is one of adc, add, and, cmp, or, sbb, sub, xor
+   instructions.  */
+
+static
+bfd_boolean
+elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
+                            bfd_byte *contents,
+                            Elf_Internal_Rela *irel,
+                            struct elf_link_hash_entry *h,
+                            bfd_boolean *converted,
+                            struct bfd_link_info *link_info)
+{
+  struct elf_i386_link_hash_table *htab;
+  unsigned int opcode;
+  unsigned int modrm;
+  bfd_boolean baseless;
+  Elf_Internal_Sym *isym;
+  unsigned int addend;
+  unsigned int nop;
+  bfd_vma nop_offset;
+  bfd_boolean is_pic;
+  bfd_boolean to_reloc_32;
+  unsigned int r_type;
+  unsigned int r_symndx;
+  bfd_vma roff = irel->r_offset;
+
+  if (roff < 2)
+    return TRUE;
+
+  /* Addend for R_386_GOT32X relocations must be 0.  */
+  addend = bfd_get_32 (abfd, contents + roff);
+  if (addend != 0)
+    return TRUE;
+
+  htab = elf_i386_hash_table (link_info);
+  is_pic = bfd_link_pic (link_info);
+
+  r_type = ELF32_R_TYPE (irel->r_info);
+  r_symndx = ELF32_R_SYM (irel->r_info);
+
+  modrm = bfd_get_8 (abfd, contents + roff - 1);
+  baseless = (modrm & 0xc7) == 0x5;
+
+  if (baseless && is_pic)
+    {
+      /* For PIC, disallow R_386_GOT32X without a base register
+        since we don't know what the GOT base is.  */
+      const char *name;
+
+      if (h == NULL)
+       {
+         isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd,
+                                       r_symndx);
+         name = bfd_elf_sym_name (abfd, symtab_hdr, isym, NULL);
+       }
+      else
+       name = h->root.root.string;
+
+      (*_bfd_error_handler)
+       (_("%B: direct GOT relocation R_386_GOT32X against `%s' without base register can not be used when making a shared object"),
+        abfd, name);
+      return FALSE;
+    }
+
+  opcode = bfd_get_8 (abfd, contents + roff - 2);
+
+  /* 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_GOT32X.  Get the symbol referred to by the
+     reloc.  */
+  if (h == NULL)
+    {
+      if (opcode == 0x0ff)
+       /* Convert "call/jmp *foo@GOT[(%reg)]".  */
+       goto convert_branch;
+      else
+       /* Convert "mov foo@GOT[(%reg1)], %reg2",
+          "test %reg1, foo@GOT(%reg2)" and
+          "binop foo@GOT[(%reg1)], %reg2". */
+       goto convert_load;
+    }
+
+  /* Undefined weak symbol is only bound locally in executable
+     and its reference is resolved as 0.  */
+  if (UNDEFINED_WEAK_RESOLVED_TO_ZERO (link_info, TRUE,
+                                      elf_i386_hash_entry (h)))
+    {
+      if (opcode == 0xff)
+       {
+         /* No direct branch to 0 for PIC.  */
+         if (is_pic)
+           return TRUE;
+         else
+           goto convert_branch;
+       }
+      else
+       {
+         /* We can convert load of address 0 to R_386_32.  */
+         to_reloc_32 = TRUE;
+         goto convert_load;
+       }
+    }
+
+  if (opcode == 0xff)
+    {
+      /* We have "call/jmp *foo@GOT[(%reg)]".  */
+      if ((h->root.type == bfd_link_hash_defined
+          || h->root.type == bfd_link_hash_defweak)
+         && SYMBOL_REFERENCES_LOCAL (link_info, h))
+       {
+         /* The function is locally defined.   */
+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;
+             /* To support TLS optimization, always use addr32 prefix
+                for "call *___tls_get_addr@GOT(%reg)".  */
+             if (eh && eh->tls_get_addr == 1)
+               {
+                 nop = 0x67;
+                 nop_offset = irel->r_offset - 2;
+               }
+             else
+               {
+                 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
+           {
+             /* Convert to "jmp foo nop".  */
+             modrm = 0xe9;
+             nop = NOP_OPCODE;
+             nop_offset = roff + 3;
+             irel->r_offset -= 1;
+           }
+
+         bfd_put_8 (abfd, nop, contents + nop_offset);
+         bfd_put_8 (abfd, modrm, contents + irel->r_offset - 1);
+         /* When converting to PC-relative relocation, we
+            need to adjust addend by -4.  */
+         bfd_put_32 (abfd, -4, contents + irel->r_offset);
+         irel->r_info = ELF32_R_INFO (r_symndx, R_386_PC32);
+
+         *converted = TRUE;
+       }
+    }
+  else
+    {
+      /* We have "mov foo@GOT[(%re1g)], %reg2",
+        "test %reg1, foo@GOT(%reg2)" and
+        "binop foo@GOT[(%reg1)], %reg2".
+
+        Avoid optimizing _DYNAMIC since ld.so may use its
+        link-time address.  */
+      if (h == htab->elf.hdynamic)
+       return TRUE;
+
+      /* def_regular is set by an assignment in a linker script in
+        bfd_elf_record_link_assignment.  */
+      if ((h->def_regular
+          || h->root.type == bfd_link_hash_defined
+          || h->root.type == bfd_link_hash_defweak)
+         && SYMBOL_REFERENCES_LOCAL (link_info, h))
+       {
+convert_load:
+         if (opcode == 0x8b)
+           {
+             if (to_reloc_32)
+               {
+                 /* Convert "mov foo@GOT[(%reg1)], %reg2" to
+                    "mov $foo, %reg2" with R_386_32.  */
+                 r_type = R_386_32;
+                 modrm = 0xc0 | (modrm & 0x38) >> 3;
+                 bfd_put_8 (abfd, modrm, contents + roff - 1);
+                 opcode = 0xc7;
+               }
+             else
+               {
+                 /* Convert "mov foo@GOT(%reg1), %reg2" to
+                    "lea foo@GOTOFF(%reg1), %reg2".  */
+                 r_type = R_386_GOTOFF;
+                 opcode = 0x8d;
+               }
+           }
+         else
+           {
+             /* Only R_386_32 is supported.  */
+             if (!to_reloc_32)
+               return TRUE;
+
+             if (opcode == 0x85)
+               {
+                 /* Convert "test %reg1, foo@GOT(%reg2)" to
+                    "test $foo, %reg1".  */
+                 modrm = 0xc0 | (modrm & 0x38) >> 3;
+                 opcode = 0xf7;
+               }
+             else
+               {
+                 /* Convert "binop foo@GOT(%reg1), %reg2" to
+                    "binop $foo, %reg2".  */
+                 modrm = (0xc0
+                          | (modrm & 0x38) >> 3
+                          | (opcode & 0x3c));
+                 opcode = 0x81;
+               }
+             bfd_put_8 (abfd, modrm, contents + roff - 1);
+             r_type = R_386_32;
+           }
+
+         bfd_put_8 (abfd, opcode, contents + roff - 2);
+         irel->r_info = ELF32_R_INFO (r_symndx, r_type);
+
+         *converted = TRUE;
+       }
+    }
+
+  return TRUE;
+}
+
 /* Rename some of the generic section flags to better document how they
    are used here.  */
 /* Rename some of the generic section flags to better document how they
    are used here.  */
-#define need_convert_load sec_flg0
+#define need_convert_load      sec_flg0
+#define check_relocs_failed    sec_flg1
 
 /* Look through the relocs for a section during the first phase, and
    calculate needed space in the global offset table, procedure linkage
 
 /* Look through the relocs for a section during the first phase, and
    calculate needed space in the global offset table, procedure linkage
@@ -1494,16 +1840,38 @@ elf_i386_check_relocs (bfd *abfd,
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
   asection *sreloc;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
   asection *sreloc;
+  bfd_byte *contents;
   bfd_boolean use_plt_got;
 
   if (bfd_link_relocatable (info))
     return TRUE;
 
   bfd_boolean use_plt_got;
 
   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);
   if (htab == NULL)
   BFD_ASSERT (is_i386_elf (abfd));
 
   htab = elf_i386_hash_table (info);
   if (htab == NULL)
-    return FALSE;
+    {
+      sec->check_relocs_failed = 1;
+      return FALSE;
+    }
+
+  /* Get the section contents.  */
+  if (elf_section_data (sec)->this_hdr.contents != NULL)
+    contents = elf_section_data (sec)->this_hdr.contents;
+  else if (!bfd_malloc_and_get_section (abfd, sec, &contents))
+    {
+      sec->check_relocs_failed = 1;
+      return FALSE;
+    }
 
   use_plt_got = (!get_elf_i386_backend_data (abfd)->is_vxworks
                 && (get_elf_i386_backend_data (abfd)
 
   use_plt_got = (!get_elf_i386_backend_data (abfd)->is_vxworks
                 && (get_elf_i386_backend_data (abfd)
@@ -1533,7 +1901,7 @@ elf_i386_check_relocs (bfd *abfd,
          (*_bfd_error_handler) (_("%B: bad symbol index: %d"),
                                 abfd,
                                 r_symndx);
          (*_bfd_error_handler) (_("%B: bad symbol index: %d"),
                                 abfd,
                                 r_symndx);
-         return FALSE;
+         goto error_return;
        }
 
       if (r_symndx < symtab_hdr->sh_info)
        }
 
       if (r_symndx < symtab_hdr->sh_info)
@@ -1542,14 +1910,14 @@ elf_i386_check_relocs (bfd *abfd,
          isym = bfd_sym_from_r_symndx (&htab->sym_cache,
                                        abfd, r_symndx);
          if (isym == NULL)
          isym = bfd_sym_from_r_symndx (&htab->sym_cache,
                                        abfd, r_symndx);
          if (isym == NULL)
-           return FALSE;
+           goto error_return;
 
          /* Check relocation against local STT_GNU_IFUNC symbol.  */
          if (ELF32_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
            {
              h = elf_i386_get_local_sym_hash (htab, abfd, rel, TRUE);
              if (h == NULL)
 
          /* Check relocation against local STT_GNU_IFUNC symbol.  */
          if (ELF32_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
            {
              h = elf_i386_get_local_sym_hash (htab, abfd, rel, TRUE);
              if (h == NULL)
-               return FALSE;
+               goto error_return;
 
              /* Fake a STT_GNU_IFUNC symbol.  */
              h->type = STT_GNU_IFUNC;
 
              /* Fake a STT_GNU_IFUNC symbol.  */
              h->type = STT_GNU_IFUNC;
@@ -1573,10 +1941,6 @@ elf_i386_check_relocs (bfd *abfd,
       eh = (struct elf_i386_link_hash_entry *) h;
       if (h != NULL)
        {
       eh = (struct elf_i386_link_hash_entry *) h;
       if (h != NULL)
        {
-         /* Create the ifunc sections for static executables.  If we
-            never see an indirect function symbol nor we are building
-            a static executable, those sections will be empty and
-            won't appear in output.  */
          switch (r_type)
            {
            default:
          switch (r_type)
            {
            default:
@@ -1591,8 +1955,11 @@ elf_i386_check_relocs (bfd *abfd,
            case R_386_GOT32X:
              if (htab->elf.dynobj == NULL)
                htab->elf.dynobj = abfd;
            case R_386_GOT32X:
              if (htab->elf.dynobj == NULL)
                htab->elf.dynobj = abfd;
-             if (!_bfd_elf_create_ifunc_sections (htab->elf.dynobj, info))
-               return FALSE;
+             /* Create the ifunc sections for static executables.  */
+             if (h->type == STT_GNU_IFUNC
+                 && !_bfd_elf_create_ifunc_sections (htab->elf.dynobj,
+                                                     info))
+               goto error_return;
              break;
            }
 
              break;
            }
 
@@ -1605,11 +1972,11 @@ elf_i386_check_relocs (bfd *abfd,
              |= elf_gnu_symbol_ifunc;
        }
 
              |= elf_gnu_symbol_ifunc;
        }
 
-      if (! elf_i386_tls_transition (info, abfd, sec, NULL,
+      if (! elf_i386_tls_transition (info, abfd, sec, contents,
                                     symtab_hdr, sym_hashes,
                                     &r_type, GOT_UNKNOWN,
                                     symtab_hdr, sym_hashes,
                                     &r_type, GOT_UNKNOWN,
-                                    rel, rel_end, h, r_symndx))
-       return FALSE;
+                                    rel, rel_end, h, r_symndx, FALSE))
+       goto error_return;
 
       switch (r_type)
        {
 
       switch (r_type)
        {
@@ -1630,6 +1997,7 @@ elf_i386_check_relocs (bfd *abfd,
          if (h == NULL)
            continue;
 
          if (h == NULL)
            continue;
 
+         eh->has_got_reloc = 1;
          h->needs_plt = 1;
          h->plt.refcount += 1;
          break;
          h->needs_plt = 1;
          h->plt.refcount += 1;
          break;
@@ -1699,7 +2067,7 @@ elf_i386_check_relocs (bfd *abfd,
                    local_got_refcounts = (bfd_signed_vma *)
                         bfd_zalloc (abfd, size);
                    if (local_got_refcounts == NULL)
                    local_got_refcounts = (bfd_signed_vma *)
                         bfd_zalloc (abfd, size);
                    if (local_got_refcounts == NULL)
-                     return FALSE;
+                     goto error_return;
                    elf_local_got_refcounts (abfd) = local_got_refcounts;
                    elf_i386_local_tlsdesc_gotent (abfd)
                      = (bfd_vma *) (local_got_refcounts + symtab_hdr->sh_info);
                    elf_local_got_refcounts (abfd) = local_got_refcounts;
                    elf_i386_local_tlsdesc_gotent (abfd)
                      = (bfd_vma *) (local_got_refcounts + symtab_hdr->sh_info);
@@ -1735,7 +2103,7 @@ elf_i386_check_relocs (bfd *abfd,
                         "thread local symbol"),
                       abfd, name);
                    bfd_set_error (bfd_error_bad_value);
                         "thread local symbol"),
                       abfd, name);
                    bfd_set_error (bfd_error_bad_value);
-                   return FALSE;
+                   goto error_return;
                  }
              }
 
                  }
              }
 
@@ -1757,22 +2125,36 @@ elf_i386_check_relocs (bfd *abfd,
              if (htab->elf.dynobj == NULL)
                htab->elf.dynobj = abfd;
              if (!_bfd_elf_create_got_section (htab->elf.dynobj, info))
              if (htab->elf.dynobj == NULL)
                htab->elf.dynobj = abfd;
              if (!_bfd_elf_create_got_section (htab->elf.dynobj, info))
-               return FALSE;
+               goto error_return;
            }
          if (r_type != R_386_TLS_IE)
            }
          if (r_type != R_386_TLS_IE)
-           break;
+           {
+             if (eh != NULL)
+               eh->has_got_reloc = 1;
+             break;
+           }
          /* Fall through */
 
        case R_386_TLS_LE_32:
        case R_386_TLS_LE:
          /* Fall through */
 
        case R_386_TLS_LE_32:
        case R_386_TLS_LE:
+         if (eh != NULL)
+           eh->has_got_reloc = 1;
          if (bfd_link_executable (info))
            break;
          info->flags |= DF_STATIC_TLS;
          if (bfd_link_executable (info))
            break;
          info->flags |= DF_STATIC_TLS;
-         /* Fall through */
+         goto do_relocation;
 
        case R_386_32:
        case R_386_PC32:
 
        case R_386_32:
        case R_386_PC32:
-         if (h != NULL && bfd_link_executable (info))
+         if (eh != NULL && (sec->flags & SEC_CODE) != 0)
+           eh->has_non_got_reloc = 1;
+do_relocation:
+         /* 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))
            {
              /* If this reloc is in a read-only section, we might
                 need a copy reloc.  We can't check reliably at this
            {
              /* If this reloc is in a read-only section, we might
                 need a copy reloc.  We can't check reliably at this
@@ -1782,9 +2164,13 @@ elf_i386_check_relocs (bfd *abfd,
                 adjust_dynamic_symbol.  */
              h->non_got_ref = 1;
 
                 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
              if (r_type == R_386_PC32)
                {
                  /* Since something like ".long foo - ." may be used
@@ -1825,17 +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
             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)
          if ((bfd_link_pic (info)
-              && (sec->flags & SEC_ALLOC) != 0
               && (r_type != R_386_PC32
                   || (h != NULL
               && (r_type != R_386_PC32
                   || (h != NULL
-                      && (! SYMBOLIC_BIND (info, h)
+                      && (! (bfd_link_pie (info)
+                             || SYMBOLIC_BIND (info, h))
                           || h->root.type == bfd_link_hash_defweak
                           || !h->def_regular))))
                           || 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)
              || (ELIMINATE_COPY_RELOCS
                  && !bfd_link_pic (info)
-                 && (sec->flags & SEC_ALLOC) != 0
                  && h != NULL
                  && (h->root.type == bfd_link_hash_defweak
                      || !h->def_regular)))
                  && h != NULL
                  && (h->root.type == bfd_link_hash_defweak
                      || !h->def_regular)))
@@ -1855,7 +2247,7 @@ do_size:
                    (sec, htab->elf.dynobj, 2, abfd, /*rela?*/ FALSE);
 
                  if (sreloc == NULL)
                    (sec, htab->elf.dynobj, 2, abfd, /*rela?*/ FALSE);
 
                  if (sreloc == NULL)
-                   return FALSE;
+                   goto error_return;
                }
 
              /* If this is a global symbol, we count the number of
                }
 
              /* If this is a global symbol, we count the number of
@@ -1875,7 +2267,7 @@ do_size:
                  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
                                                abfd, r_symndx);
                  if (isym == NULL)
                  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
                                                abfd, r_symndx);
                  if (isym == NULL)
-                   return FALSE;
+                   goto error_return;
 
                  s = bfd_section_from_elf_index (abfd, isym->st_shndx);
                  if (s == NULL)
 
                  s = bfd_section_from_elf_index (abfd, isym->st_shndx);
                  if (s == NULL)
@@ -1892,7 +2284,7 @@ do_size:
                  p = (struct elf_dyn_relocs *) bfd_alloc (htab->elf.dynobj,
                                                            amt);
                  if (p == NULL)
                  p = (struct elf_dyn_relocs *) bfd_alloc (htab->elf.dynobj,
                                                            amt);
                  if (p == NULL)
-                   return FALSE;
+                   goto error_return;
                  p->next = *head;
                  *head = p;
                  p->sec = sec;
                  p->next = *head;
                  *head = p;
                  p->sec = sec;
@@ -1911,7 +2303,7 @@ do_size:
             Reconstruct it for later use during GC.  */
        case R_386_GNU_VTINHERIT:
          if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
             Reconstruct it for later use during GC.  */
        case R_386_GNU_VTINHERIT:
          if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
-           return FALSE;
+           goto error_return;
          break;
 
          /* This relocation describes which C++ vtable entries are actually
          break;
 
          /* This relocation describes which C++ vtable entries are actually
@@ -1920,7 +2312,7 @@ do_size:
          BFD_ASSERT (h != NULL);
          if (h != NULL
              && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset))
          BFD_ASSERT (h != NULL);
          if (h != NULL
              && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset))
-           return FALSE;
+           goto error_return;
          break;
 
        default:
          break;
 
        default:
@@ -1958,15 +2350,32 @@ do_size:
              || !bfd_set_section_alignment (htab->elf.dynobj,
                                             htab->plt_got,
                                             plt_got_align))
              || !bfd_set_section_alignment (htab->elf.dynobj,
                                             htab->plt_got,
                                             plt_got_align))
-           return FALSE;
+           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;
     }
 
          && (h == NULL || h->type != STT_GNU_IFUNC))
        sec->need_convert_load = 1;
     }
 
+  if (elf_section_data (sec)->this_hdr.contents != contents)
+    {
+      if (!info->keep_memory)
+       free (contents);
+      else
+       {
+         /* Cache the section contents for elf_link_input_bfd.  */
+         elf_section_data (sec)->this_hdr.contents = contents;
+       }
+    }
+
   return TRUE;
   return TRUE;
+
+error_return:
+  if (elf_section_data (sec)->this_hdr.contents != contents)
+    free (contents);
+  sec->check_relocs_failed = 1;
+  return FALSE;
 }
 
 /* Return the section that should be marked against GC for a given
 }
 
 /* Return the section that should be marked against GC for a given
@@ -1990,160 +2399,22 @@ elf_i386_gc_mark_hook (asection *sec,
   return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
 }
 
   return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
 }
 
-/* Update the got entry reference counts for the section being removed.  */
+/* Remove undefined weak symbol from the dynamic symbol table if it
+   is resolved to 0.   */
 
 static bfd_boolean
 
 static bfd_boolean
-elf_i386_gc_sweep_hook (bfd *abfd,
-                       struct bfd_link_info *info,
-                       asection *sec,
-                       const Elf_Internal_Rela *relocs)
+elf_i386_fixup_symbol (struct bfd_link_info *info,
+                      struct elf_link_hash_entry *h)
 {
 {
-  struct elf_i386_link_hash_table *htab;
-  Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes;
-  bfd_signed_vma *local_got_refcounts;
-  const Elf_Internal_Rela *rel, *relend;
-
-  if (bfd_link_relocatable (info))
-    return TRUE;
-
-  htab = elf_i386_hash_table (info);
-  if (htab == NULL)
-    return FALSE;
-
-  elf_section_data (sec)->local_dynrel = NULL;
-
-  symtab_hdr = &elf_symtab_hdr (abfd);
-  sym_hashes = elf_sym_hashes (abfd);
-  local_got_refcounts = elf_local_got_refcounts (abfd);
-
-  relend = relocs + sec->reloc_count;
-  for (rel = relocs; rel < relend; rel++)
+  if (h->dynindx != -1
+      && UNDEFINED_WEAK_RESOLVED_TO_ZERO (info,
+                                         elf_i386_hash_entry (h)->has_got_reloc,
+                                         elf_i386_hash_entry (h)))
     {
     {
-      unsigned long r_symndx;
-      unsigned int r_type;
-      struct elf_link_hash_entry *h = NULL;
-
-      r_symndx = ELF32_R_SYM (rel->r_info);
-      if (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;
-       }
-      else
-       {
-         /* A local symbol.  */
-         Elf_Internal_Sym *isym;
-
-         isym = bfd_sym_from_r_symndx (&htab->sym_cache,
-                                       abfd, r_symndx);
-
-         /* Check relocation against local STT_GNU_IFUNC symbol.  */
-         if (isym != NULL
-             && ELF32_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
-           {
-             h = elf_i386_get_local_sym_hash (htab, abfd, rel, FALSE);
-             if (h == NULL)
-               abort ();
-           }
-       }
-
-      if (h)
-       {
-         struct elf_i386_link_hash_entry *eh;
-         struct elf_dyn_relocs **pp;
-         struct elf_dyn_relocs *p;
-
-         eh = (struct elf_i386_link_hash_entry *) h;
-         for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
-           if (p->sec == sec)
-             {
-               /* Everything must go for SEC.  */
-               *pp = p->next;
-               break;
-             }
-       }
-
-      r_type = ELF32_R_TYPE (rel->r_info);
-      if (! elf_i386_tls_transition (info, abfd, sec, NULL,
-                                    symtab_hdr, sym_hashes,
-                                    &r_type, GOT_UNKNOWN,
-                                    rel, relend, h, r_symndx))
-       return FALSE;
-
-      switch (r_type)
-       {
-       case R_386_TLS_LDM:
-         if (htab->tls_ldm_got.refcount > 0)
-           htab->tls_ldm_got.refcount -= 1;
-         break;
-
-       case R_386_TLS_GD:
-       case R_386_TLS_GOTDESC:
-       case R_386_TLS_DESC_CALL:
-       case R_386_TLS_IE_32:
-       case R_386_TLS_IE:
-       case R_386_TLS_GOTIE:
-       case R_386_GOT32:
-       case R_386_GOT32X:
-         if (h != NULL)
-           {
-             if (h->got.refcount > 0)
-               h->got.refcount -= 1;
-             if (h->type == STT_GNU_IFUNC)
-               {
-                 if (h->plt.refcount > 0)
-                   h->plt.refcount -= 1;
-               }
-           }
-         else if (local_got_refcounts != NULL)
-           {
-             if (local_got_refcounts[r_symndx] > 0)
-               local_got_refcounts[r_symndx] -= 1;
-           }
-         break;
-
-       case R_386_32:
-       case R_386_PC32:
-       case R_386_SIZE32:
-         if (bfd_link_pic (info)
-             && (h == NULL || h->type != STT_GNU_IFUNC))
-           break;
-         /* Fall through */
-
-       case R_386_PLT32:
-         if (h != NULL)
-           {
-             if (h->plt.refcount > 0)
-               h->plt.refcount -= 1;
-             if (r_type == R_386_32
-                 && (sec->flags & SEC_READONLY) == 0)
-               {
-                 struct elf_i386_link_hash_entry *eh
-                   = (struct elf_i386_link_hash_entry *) h;
-                 if (eh->func_pointer_refcount > 0)
-                   eh->func_pointer_refcount -= 1;
-               }
-           }
-         break;
-
-       case R_386_GOTOFF:
-         if (h != NULL && h->type == STT_GNU_IFUNC)
-           {
-             if (h->got.refcount > 0)
-               h->got.refcount -= 1;
-             if (h->plt.refcount > 0)
-               h->plt.refcount -= 1;
-           }
-         break;
-
-       default:
-         break;
-       }
+      h->dynindx = -1;
+      _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
+                             h->dynstr_index);
     }
     }
-
   return TRUE;
 }
 
   return TRUE;
 }
 
@@ -2188,12 +2459,17 @@ elf_i386_adjust_dynamic_symbol (struct bfd_link_info *info,
 
          if (pc_count || count)
            {
 
          if (pc_count || count)
            {
-             h->needs_plt = 1;
              h->non_got_ref = 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;
+               }
            }
        }
 
            }
        }
 
@@ -2335,6 +2611,7 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   struct elf_i386_link_hash_entry *eh;
   struct elf_dyn_relocs *p;
   unsigned plt_entry_size;
   struct elf_i386_link_hash_entry *eh;
   struct elf_dyn_relocs *p;
   unsigned plt_entry_size;
+  bfd_boolean resolved_to_zero;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
@@ -2348,6 +2625,10 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 
   plt_entry_size = GET_PLT_ENTRY_SIZE (info->output_bfd);
 
 
   plt_entry_size = GET_PLT_ENTRY_SIZE (info->output_bfd);
 
+  resolved_to_zero = UNDEFINED_WEAK_RESOLVED_TO_ZERO (info,
+                                                     eh->has_got_reloc,
+                                                     eh);
+
   /* Clear the reference count of function pointer relocations if
      symbol isn't a normal function.  */
   if (h->type != STT_FUNC)
   /* Clear the reference count of function pointer relocations if
      symbol isn't a normal function.  */
   if (h->type != STT_FUNC)
@@ -2376,8 +2657,9 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   if (h->type == STT_GNU_IFUNC
       && h->def_regular)
     return _bfd_elf_allocate_ifunc_dyn_relocs (info, h, &eh->dyn_relocs,
   if (h->type == STT_GNU_IFUNC
       && h->def_regular)
     return _bfd_elf_allocate_ifunc_dyn_relocs (info, h, &eh->dyn_relocs,
-                                               plt_entry_size,
-                                              plt_entry_size, 4);
+                                              &htab->readonly_dynrelocs_against_ifunc,
+                                              plt_entry_size,
+                                              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
   /* 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
@@ -2405,7 +2687,8 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       /* Make sure this symbol is output as a dynamic symbol.
         Undefined weak syms won't yet be marked as dynamic.  */
       if (h->dynindx == -1
       /* Make sure this symbol is output as a dynamic symbol.
         Undefined weak syms won't yet be marked as dynamic.  */
       if (h->dynindx == -1
-         && !h->forced_local)
+         && !h->forced_local
+         && !resolved_to_zero)
        {
          if (! bfd_elf_link_record_dynamic_symbol (info, h))
            return FALSE;
        {
          if (! bfd_elf_link_record_dynamic_symbol (info, h))
            return FALSE;
@@ -2462,9 +2745,15 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                 script.  */
              htab->elf.sgotplt->size += 4;
 
                 script.  */
              htab->elf.sgotplt->size += 4;
 
-             /* We also need to make an entry in the .rel.plt section.  */
-             htab->elf.srelplt->size += sizeof (Elf32_External_Rel);
-             htab->elf.srelplt->reloc_count++;
+             /* There should be no PLT relocation against resolved
+                undefined weak symbol in executable.  */
+             if (!resolved_to_zero)
+               {
+                 /* We also need to make an entry in the .rel.plt
+                    section.  */
+                 htab->elf.srelplt->size += sizeof (Elf32_External_Rel);
+                 htab->elf.srelplt->reloc_count++;
+               }
            }
 
          if (get_elf_i386_backend_data (info->output_bfd)->is_vxworks
            }
 
          if (get_elf_i386_backend_data (info->output_bfd)->is_vxworks
@@ -2520,7 +2809,8 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       /* Make sure this symbol is output as a dynamic symbol.
         Undefined weak syms won't yet be marked as dynamic.  */
       if (h->dynindx == -1
       /* Make sure this symbol is output as a dynamic symbol.
         Undefined weak syms won't yet be marked as dynamic.  */
       if (h->dynindx == -1
-         && !h->forced_local)
+         && !h->forced_local
+         && !resolved_to_zero)
        {
          if (! bfd_elf_link_record_dynamic_symbol (info, h))
            return FALSE;
        {
          if (! bfd_elf_link_record_dynamic_symbol (info, h))
            return FALSE;
@@ -2548,7 +2838,8 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
         R_386_TLS_IE resp. R_386_TLS_GOTIE needs one dynamic relocation,
         (but if both R_386_TLS_IE_32 and R_386_TLS_IE is present, we
         need two), R_386_TLS_GD needs one if local symbol and two if
         R_386_TLS_IE resp. R_386_TLS_GOTIE needs one dynamic relocation,
         (but if both R_386_TLS_IE_32 and R_386_TLS_IE is present, we
         need two), R_386_TLS_GD needs one if local symbol and two if
-        global.  */
+        global.  No dynamic relocation against resolved undefined weak
+        symbol in executable.  */
       if (tls_type == GOT_TLS_IE_BOTH)
        htab->elf.srelgot->size += 2 * sizeof (Elf32_External_Rel);
       else if ((GOT_TLS_GD_P (tls_type) && h->dynindx == -1)
       if (tls_type == GOT_TLS_IE_BOTH)
        htab->elf.srelgot->size += 2 * sizeof (Elf32_External_Rel);
       else if ((GOT_TLS_GD_P (tls_type) && h->dynindx == -1)
@@ -2557,7 +2848,8 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       else if (GOT_TLS_GD_P (tls_type))
        htab->elf.srelgot->size += 2 * sizeof (Elf32_External_Rel);
       else if (! GOT_TLS_GDESC_P (tls_type)
       else if (GOT_TLS_GD_P (tls_type))
        htab->elf.srelgot->size += 2 * sizeof (Elf32_External_Rel);
       else if (! GOT_TLS_GDESC_P (tls_type)
-              && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+              && ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+                   && !resolved_to_zero)
                   || h->root.type != bfd_link_hash_undefweak)
               && (bfd_link_pic (info)
                   || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
                   || h->root.type != bfd_link_hash_undefweak)
               && (bfd_link_pic (info)
                   || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
@@ -2613,15 +2905,43 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
        }
 
       /* Also discard relocs on undefined weak syms with non-default
        }
 
       /* Also discard relocs on undefined weak syms with non-default
-        visibility.  */
+        visibility or in PIE.  */
       if (eh->dyn_relocs != NULL
          && h->root.type == bfd_link_hash_undefweak)
        {
       if (eh->dyn_relocs != NULL
          && h->root.type == bfd_link_hash_undefweak)
        {
-         if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
-           eh->dyn_relocs = NULL;
+         /* Undefined weak symbol is never bound locally in shared
+            library.  */
+         if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+             || resolved_to_zero)
+           {
+             if (h->non_got_ref)
+               {
+                 /* Keep dynamic non-GOT/non-PLT relocation so that we
+                    can branch to 0 without PLT.  */
+                 struct elf_dyn_relocs **pp;
+
+                 for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+                   if (p->pc_count == 0)
+                     *pp = p->next;
+                   else
+                     {
+                       /* Remove non-R_386_PC32 relocation.  */
+                       p->count = p->pc_count;
+                       pp = &p->next;
+                     }
 
 
-         /* Make sure undefined weak symbols are output as a dynamic
-            symbol in PIEs.  */
+                 if (eh->dyn_relocs != NULL)
+                   {
+                     /* Make sure undefined weak symbols are output
+                        as dynamic symbols in PIEs for dynamic non-GOT
+                        non-PLT reloations.  */
+                     if (! bfd_elf_link_record_dynamic_symbol (info, h))
+                       return FALSE;
+                   }
+               }
+             else
+               eh->dyn_relocs = NULL;
+           }
          else if (h->dynindx == -1
                   && !h->forced_local)
            {
          else if (h->dynindx == -1
                   && !h->forced_local)
            {
@@ -2637,7 +2957,10 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
         dynamic.  Keep dynamic relocations for run-time function
         pointer initialization.  */
 
         dynamic.  Keep dynamic relocations for run-time function
         pointer initialization.  */
 
-      if ((!h->non_got_ref || eh->func_pointer_refcount > 0)
+      if ((!h->non_got_ref
+          || eh->func_pointer_refcount > 0
+          || (h->root.type == bfd_link_hash_undefweak
+              && !resolved_to_zero))
          && ((h->def_dynamic
               && !h->def_regular)
              || (htab->elf.dynamic_sections_created
          && ((h->def_dynamic
               && !h->def_regular)
              || (htab->elf.dynamic_sections_created
@@ -2647,7 +2970,8 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
          /* Make sure this symbol is output as a dynamic symbol.
             Undefined weak syms won't yet be marked as dynamic.  */
          if (h->dynindx == -1
          /* Make sure this symbol is output as a dynamic symbol.
             Undefined weak syms won't yet be marked as dynamic.  */
          if (h->dynindx == -1
-             && !h->forced_local)
+             && !h->forced_local
+             && !resolved_to_zero)
            {
              if (! bfd_elf_link_record_dynamic_symbol (info, h))
                return FALSE;
            {
              if (! bfd_elf_link_record_dynamic_symbol (info, h))
                return FALSE;
@@ -2734,36 +3058,18 @@ elf_i386_readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   return TRUE;
 }
 
   return TRUE;
 }
 
-/* With the local symbol, foo, we convert
-   mov foo@GOT[(%reg1)], %reg2
-   to
-   lea foo[@GOTOFF(%reg1)], %reg2
-   and convert
-   call/jmp *foo@GOT[(%reg)]
-   to
-   nop call foo/jmp foo nop
-   When PIC is false, convert
-   test %reg1, foo@GOT[(%reg2)]
-   to
-   test $foo, %reg1
-   and convert
-   binop foo@GOT[(%reg1)], %reg2
-   to
-   binop $foo, %reg2
-   where binop is one of adc, add, and, cmp, or, sbb, sub, xor
-   instructions.  */
+/* Convert load via the GOT slot to load immediate.  */
 
 static bfd_boolean
 elf_i386_convert_load (bfd *abfd, asection *sec,
                       struct bfd_link_info *link_info)
 {
 
 static bfd_boolean
 elf_i386_convert_load (bfd *abfd, asection *sec,
                       struct bfd_link_info *link_info)
 {
+  struct elf_i386_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Rela *internal_relocs;
   Elf_Internal_Rela *irel, *irelend;
   bfd_byte *contents;
   Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Rela *internal_relocs;
   Elf_Internal_Rela *irel, *irelend;
   bfd_byte *contents;
-  struct elf_i386_link_hash_table *htab;
-  bfd_boolean changed_contents;
-  bfd_boolean changed_relocs;
+  bfd_boolean changed;
   bfd_signed_vma *local_got_refcounts;
 
   /* Don't even try to convert non-ELF outputs.  */
   bfd_signed_vma *local_got_refcounts;
 
   /* Don't even try to convert non-ELF outputs.  */
@@ -2785,9 +3091,8 @@ elf_i386_convert_load (bfd *abfd, asection *sec,
   if (internal_relocs == NULL)
     return FALSE;
 
   if (internal_relocs == NULL)
     return FALSE;
 
+  changed = FALSE;
   htab = elf_i386_hash_table (link_info);
   htab = elf_i386_hash_table (link_info);
-  changed_contents = FALSE;
-  changed_relocs = FALSE;
   local_got_refcounts = elf_local_got_refcounts (abfd);
 
   /* Get the section contents.  */
   local_got_refcounts = elf_local_got_refcounts (abfd);
 
   /* Get the section contents.  */
@@ -2803,249 +3108,50 @@ elf_i386_convert_load (bfd *abfd, asection *sec,
   for (irel = internal_relocs; irel < irelend; irel++)
     {
       unsigned int r_type = ELF32_R_TYPE (irel->r_info);
   for (irel = internal_relocs; irel < irelend; irel++)
     {
       unsigned int r_type = ELF32_R_TYPE (irel->r_info);
-      unsigned int r_symndx = ELF32_R_SYM (irel->r_info);
-      unsigned int indx;
+      unsigned int r_symndx;
       struct elf_link_hash_entry *h;
       struct elf_link_hash_entry *h;
-      unsigned int opcode;
-      unsigned int modrm;
-      bfd_vma roff;
-      bfd_boolean baseless;
-      Elf_Internal_Sym *isym;
-      unsigned int addend;
-      unsigned int nop;
-      bfd_vma nop_offset;
+      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;
 
        continue;
 
-      roff = irel->r_offset;
-      if (roff < 2)
-       continue;
-
-      /* Addend for R_386_GOT32 and R_386_GOT32X relocations must be 0.  */
-      addend = bfd_get_32 (abfd, contents + roff);
-      if (addend != 0)
-       continue;
-
-      modrm = bfd_get_8 (abfd, contents + roff - 1);
-      baseless = (modrm & 0xc7) == 0x5;
-
-      if (r_type == R_386_GOT32X
-         && baseless
-         && bfd_link_pic (link_info))
-       {
-         /* 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.  */
-         const char *name;
-
-         if (r_symndx < symtab_hdr->sh_info)
-           {
-             isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd,
-                                           r_symndx);
-             name = bfd_elf_sym_name (abfd, symtab_hdr, isym, NULL);
-           }
-         else
-           {
-             indx = r_symndx - symtab_hdr->sh_info;
-             h = elf_sym_hashes (abfd)[indx];
-             BFD_ASSERT (h != NULL);
-             name = h->root.root.string;
-           }
-
-         (*_bfd_error_handler)
-           (_("%B: direct GOT relocation R_386_GOT32X against `%s' without base register can not be used when making a shared object"),
-            abfd, name);
-         goto error_return;
-       }
-
-      opcode = bfd_get_8 (abfd, contents + roff - 2);
-
-      /* It is OK to convert mov to lea.  */
-      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)
-           continue;
-
-         /* It is OK to convert indirect branch to direct branch.  It
-            is OK to convert adc, add, and, cmp, or, sbb, sub, test,
-            xor only when PIC is false.   */
-         if (opcode != 0xff && bfd_link_pic (link_info))
-           continue;
-       }
-
-      /* Try to convert R_386_GOT32 and R_386_GOT32X.  Get the symbol
-        referred to by the reloc.  */
+      r_symndx = ELF32_R_SYM (irel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
       if (r_symndx < symtab_hdr->sh_info)
+       h = elf_i386_get_local_sym_hash (htab, sec->owner,
+                                        (const Elf_Internal_Rela *) irel,
+                                        FALSE);
+      else
        {
        {
-         isym = bfd_sym_from_r_symndx (&htab->sym_cache,
-                                       abfd, r_symndx);
-
-         /* STT_GNU_IFUNC must keep GOT32 relocations.  */
-         if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
-           continue;
-
-         h = NULL;
-         if (opcode == 0x0ff)
-           /* Convert "call/jmp *foo@GOT[(%reg)]".  */
-           goto convert_branch;
-         else
-           /* Convert "mov foo@GOT[(%reg1)], %reg2",
-              "test %reg1, foo@GOT(%reg2)" and
-              "binop foo@GOT[(%reg1)], %reg2". */
-           goto convert_load;
+         h = elf_sym_hashes (abfd)[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;
        }
 
        }
 
-      indx = r_symndx - symtab_hdr->sh_info;
-      h = elf_sym_hashes (abfd)[indx];
-      BFD_ASSERT (h != NULL);
-
-      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;
-
       /* STT_GNU_IFUNC must keep GOT32 relocations.  */
       /* STT_GNU_IFUNC must keep GOT32 relocations.  */
-      if (h->type == STT_GNU_IFUNC)
+      if (h != NULL && h->type == STT_GNU_IFUNC)
        continue;
 
        continue;
 
-      if (opcode == 0xff)
-       {
-         /* We have "call/jmp *foo@GOT[(%reg)]".  */
-         if ((h->root.type == bfd_link_hash_defined
-              || h->root.type == bfd_link_hash_defweak)
-             && SYMBOL_REFERENCES_LOCAL (link_info, h))
-           {
-             /* The function is locally defined.   */
-convert_branch:
-             /* Convert R_386_GOT32X to R_386_PC32.  */
-             if (modrm == 0x15 || (modrm & 0xf8) == 0x90)
-               {
-                 /* 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)
-                   {
-                     nop_offset = roff + 3;
-                     irel->r_offset -= 1;
-                   }
-                 else
-                   nop_offset = roff - 2;
-               }
-             else
-               {
-                 /* Convert to "jmp foo nop".  */
-                 modrm = 0xe9;
-                 nop = NOP_OPCODE;
-                 nop_offset = roff + 3;
-                 irel->r_offset -= 1;
-               }
-
-             bfd_put_8 (abfd, nop, contents + nop_offset);
-             bfd_put_8 (abfd, modrm, contents + irel->r_offset - 1);
-             /* When converting to PC-relative relocation, we
-                need to adjust addend by -4.  */
-             bfd_put_32 (abfd, -4, contents + irel->r_offset);
-             irel->r_info = ELF32_R_INFO (r_symndx, R_386_PC32);
-
-             if (h)
-               {
-                 if (h->got.refcount > 0)
-                   h->got.refcount -= 1;
-               }
-             else
-               {
-                 if (local_got_refcounts != NULL
-                     && local_got_refcounts[r_symndx] > 0)
-                   local_got_refcounts[r_symndx] -= 1;
-               }
+      converted = FALSE;
+      if (!elf_i386_convert_load_reloc (abfd, symtab_hdr, contents,
+                                       irel, h, &converted, link_info))
+       goto error_return;
 
 
-             changed_contents = TRUE;
-             changed_relocs = TRUE;
-           }
-       }
-      else
+      if (converted)
        {
        {
-         /* We have "mov foo@GOT[(%re1g)], %reg2",
-            "test %reg1, foo@GOT(%reg2)" and
-            "binop foo@GOT[(%reg1)], %reg2".
-
-            Avoid optimizing _DYNAMIC since ld.so may use its
-            link-time address.  */
-         if (h == htab->elf.hdynamic)
-           continue;
-
-         /* def_regular is set by an assignment in a linker script in
-            bfd_elf_record_link_assignment.  */
-         if ((h->def_regular
-              || h->root.type == bfd_link_hash_defined
-              || h->root.type == bfd_link_hash_defweak)
-             && SYMBOL_REFERENCES_LOCAL (link_info, h))
+         changed = converted;
+         if (h)
            {
            {
-convert_load:
-             if (opcode == 0x8b)
-               {
-                 /* Convert "mov foo@GOT(%reg1), %reg2" to
-                    "lea foo@GOTOFF(%reg1), %reg2".  */
-                 if (r_type == R_386_GOT32X
-                     && (baseless || !bfd_link_pic (link_info)))
-                   {
-                     r_type = R_386_32;
-                     /* For R_386_32, convert
-                        "lea foo@GOTOFF(%reg1), %reg2" to
-                        "lea foo@GOT, %reg2".  */
-                     if (!baseless)
-                       {
-                         modrm = 0x5 | (modrm & 0x38);
-                         bfd_put_8 (abfd, modrm, contents + roff - 1);
-                       }
-                   }
-                 else
-                   r_type = R_386_GOTOFF;
-                 opcode = 0x8d;
-               }
-             else
-               {
-                 if (opcode == 0x85)
-                   {
-                     /* Convert "test %reg1, foo@GOT(%reg2)" to
-                        "test $foo, %reg1".  */
-                     modrm = 0xc0 | (modrm & 0x38) >> 3;
-                     opcode = 0xf7;
-                   }
-                 else
-                   {
-                     /* Convert "binop foo@GOT(%reg1), %reg2" to
-                        "binop $foo, %reg2".  */
-                     modrm = (0xc0
-                              | (modrm & 0x38) >> 3
-                              | (opcode & 0x3c));
-                     opcode = 0x81;
-                   }
-                 bfd_put_8 (abfd, modrm, contents + roff - 1);
-                 r_type = R_386_32;
-               }
-
-             bfd_put_8 (abfd, opcode, contents + roff - 2);
-             irel->r_info = ELF32_R_INFO (r_symndx, r_type);
-
-             if (h)
-               {
-                 if (h->got.refcount > 0)
-                   h->got.refcount -= 1;
-               }
-             else
-               {
-                 if (local_got_refcounts != NULL
-                     && local_got_refcounts[r_symndx] > 0)
-                   local_got_refcounts[r_symndx] -= 1;
-               }
-
-             changed_contents = TRUE;
-             changed_relocs = TRUE;
+             if (h->got.refcount > 0)
+               h->got.refcount -= 1;
+           }
+         else
+           {
+             if (local_got_refcounts != NULL
+                 && local_got_refcounts[r_symndx] > 0)
+               local_got_refcounts[r_symndx] -= 1;
            }
        }
     }
            }
        }
     }
@@ -3053,7 +3159,7 @@ convert_load:
   if (contents != NULL
       && elf_section_data (sec)->this_hdr.contents != contents)
     {
   if (contents != NULL
       && elf_section_data (sec)->this_hdr.contents != contents)
     {
-      if (!changed_contents && !link_info->keep_memory)
+      if (!changed && !link_info->keep_memory)
        free (contents);
       else
        {
        free (contents);
       else
        {
@@ -3064,7 +3170,7 @@ convert_load:
 
   if (elf_section_data (sec)->relocs != internal_relocs)
     {
 
   if (elf_section_data (sec)->relocs != internal_relocs)
     {
-      if (!changed_relocs)
+      if (!changed)
        free (internal_relocs);
       else
        elf_section_data (sec)->relocs = internal_relocs;
        free (internal_relocs);
       else
        elf_section_data (sec)->relocs = internal_relocs;
@@ -3100,19 +3206,6 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
   if (dynobj == NULL)
     abort ();
 
   if (dynobj == NULL)
     abort ();
 
-  if (htab->elf.dynamic_sections_created)
-    {
-      /* Set the contents of the .interp section to the interpreter.  */
-      if (bfd_link_executable (info) && !info->nointerp)
-       {
-         s = bfd_get_linker_section (dynobj, ".interp");
-         if (s == NULL)
-           abort ();
-         s->size = sizeof ELF_DYNAMIC_INTERPRETER;
-         s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER;
-       }
-    }
-
   /* Set up .got offsets for local syms, and space for local dynamic
      relocs.  */
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
   /* Set up .got offsets for local syms, and space for local dynamic
      relocs.  */
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
@@ -3421,8 +3514,7 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
 
          if ((info->flags & DF_TEXTREL) != 0)
            {
 
          if ((info->flags & DF_TEXTREL) != 0)
            {
-             if ((elf_tdata (output_bfd)->has_gnu_symbols
-                  & elf_gnu_symbol_ifunc) == elf_gnu_symbol_ifunc)
+             if (htab->readonly_dynrelocs_against_ifunc)
                {
                  info->callbacks->einfo
                    (_("%P%X: read-only segment has dynamic IFUNC relocations; recompile with -fPIC\n"));
                {
                  info->callbacks->einfo
                    (_("%P%X: read-only segment has dynamic IFUNC relocations; recompile with -fPIC\n"));
@@ -3603,6 +3695,10 @@ elf_i386_relocate_section (bfd *output_bfd,
 
   BFD_ASSERT (is_i386_elf (input_bfd));
 
 
   BFD_ASSERT (is_i386_elf (input_bfd));
 
+  /* Skip if check_relocs failed.  */
+  if (input_section->check_relocs_failed)
+    return FALSE;
+
   htab = elf_i386_hash_table (info);
   if (htab == NULL)
     return FALSE;
   htab = elf_i386_hash_table (info);
   if (htab == NULL)
     return FALSE;
@@ -3640,6 +3736,7 @@ elf_i386_relocate_section (bfd *output_bfd,
       int tls_type;
       bfd_vma st_size;
       asection *resolved_plt;
       int tls_type;
       bfd_vma st_size;
       asection *resolved_plt;
+      bfd_boolean resolved_to_zero;
 
       r_type = ELF32_R_TYPE (rel->r_info);
       if (r_type == R_386_GNU_VTINHERIT
 
       r_type = ELF32_R_TYPE (rel->r_info);
       if (r_type == R_386_GNU_VTINHERIT
@@ -3817,8 +3914,6 @@ elf_i386_relocate_section (bfd *output_bfd,
                continue;
              abort ();
            }
                continue;
              abort ();
            }
-         else if (h->plt.offset == (bfd_vma) -1)
-           abort ();
 
          /* STT_GNU_IFUNC symbol must go through PLT.  */
          if (htab->elf.splt != NULL)
 
          /* STT_GNU_IFUNC symbol must go through PLT.  */
          if (htab->elf.splt != NULL)
@@ -3832,12 +3927,106 @@ elf_i386_relocate_section (bfd *output_bfd,
              gotplt = htab->elf.igotplt;
            }
 
              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:
          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
              if (h->root.root.string)
                name = h->root.root.string;
              else
@@ -3845,21 +4034,22 @@ elf_i386_relocate_section (bfd *output_bfd,
                                         NULL);
              (*_bfd_error_handler)
                (_("%B: relocation %s against STT_GNU_IFUNC "
                                         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.  */
              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;
 
                {
                  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,
                  /* Need a dynamic relocation to get the real function
                     adddress.  */
                  offset = _bfd_elf_section_offset (output_bfd,
@@ -3889,7 +4079,16 @@ elf_i386_relocate_section (bfd *output_bfd,
                  else
                    outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
 
                  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
                  elf_append_rel (output_bfd, sreloc, &outrel);
 
                  /* If this reloc is against an external symbol, we
@@ -3904,75 +4103,6 @@ elf_i386_relocate_section (bfd *output_bfd,
            case R_386_PLT32:
              goto do_relocation;
 
            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);
            case R_386_GOTOFF:
              relocation -= (gotplt->output_section->vma
                             + gotplt->output_offset);
@@ -3981,6 +4111,11 @@ elf_i386_relocate_section (bfd *output_bfd,
        }
 
       eh = (struct elf_i386_link_hash_entry *) h;
        }
 
       eh = (struct elf_i386_link_hash_entry *) h;
+      resolved_to_zero = (eh != NULL
+                         && UNDEFINED_WEAK_RESOLVED_TO_ZERO (info,
+                                                             eh->has_got_reloc,
+                                                             eh));
+
       switch (r_type)
        {
        case R_386_GOT32X:
       switch (r_type)
        {
        case R_386_GOT32X:
@@ -4126,10 +4261,39 @@ r_386_got32:
          if (off >= (bfd_vma) -2)
            abort ();
 
          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:
          break;
 
        case R_386_GOTOFF:
@@ -4246,22 +4410,29 @@ r_386_got32:
              || is_vxworks_tls)
            break;
 
              || is_vxworks_tls)
            break;
 
-         /* Copy dynamic function pointer relocations.  */
+         /* Copy dynamic function pointer relocations.  Don't generate
+            dynamic relocations against resolved undefined weak symbols
+            in PIE, except for R_386_PC32.  */
          if ((bfd_link_pic (info)
               && (h == NULL
          if ((bfd_link_pic (info)
               && (h == NULL
-                  || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
-                  || h->root.type != bfd_link_hash_undefweak)
+                  || ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+                       && (!resolved_to_zero
+                           || r_type == R_386_PC32))
+                      || h->root.type != bfd_link_hash_undefweak))
               && ((r_type != R_386_PC32 && r_type != R_386_SIZE32)
                   || !SYMBOL_CALLS_LOCAL (info, h)))
              || (ELIMINATE_COPY_RELOCS
                  && !bfd_link_pic (info)
                  && h != NULL
                  && h->dynindx != -1
               && ((r_type != R_386_PC32 && r_type != R_386_SIZE32)
                   || !SYMBOL_CALLS_LOCAL (info, h)))
              || (ELIMINATE_COPY_RELOCS
                  && !bfd_link_pic (info)
                  && h != NULL
                  && h->dynindx != -1
-                 && (!h->non_got_ref || eh->func_pointer_refcount > 0)
-                 && ((h->def_dynamic
-                      && !h->def_regular)
-                     || h->root.type == bfd_link_hash_undefweak
-                     || h->root.type == bfd_link_hash_undefined)))
+                 && (!h->non_got_ref
+                     || eh->func_pointer_refcount > 0
+                     || (h->root.type == bfd_link_hash_undefweak
+                         && !resolved_to_zero))
+                 && ((h->def_dynamic && !h->def_regular)
+                     /* Undefined weak symbol is bound locally when
+                        PIC is false.  */
+                     || h->root.type == bfd_link_hash_undefweak)))
            {
              Elf_Internal_Rela outrel;
              bfd_boolean skip, relocate;
            {
              Elf_Internal_Rela outrel;
              bfd_boolean skip, relocate;
@@ -4289,8 +4460,8 @@ r_386_got32:
              else if (h != NULL
                       && h->dynindx != -1
                       && (r_type == R_386_PC32
              else if (h != NULL
                       && h->dynindx != -1
                       && (r_type == R_386_PC32
-                          || !bfd_link_pic (info)
-                          || !SYMBOLIC_BIND (info, h)
+                          || !(bfd_link_executable (info)
+                               || SYMBOLIC_BIND (info, h))
                           || !h->def_regular))
                outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
              else
                           || !h->def_regular))
                outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
              else
@@ -4353,7 +4524,7 @@ r_386_got32:
                                         input_section, contents,
                                         symtab_hdr, sym_hashes,
                                         &r_type, tls_type, rel,
                                         input_section, contents,
                                         symtab_hdr, sym_hashes,
                                         &r_type, tls_type, rel,
-                                        relend, h, r_symndx))
+                                        relend, h, r_symndx, TRUE))
            return FALSE;
 
          if (r_type == R_386_TLS_LE_32)
            return FALSE;
 
          if (r_type == R_386_TLS_LE_32)
@@ -4365,30 +4536,39 @@ r_386_got32:
                  bfd_vma roff;
 
                  /* GD->LE transition.  */
                  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)
                    {
                  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).  */
                         (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
                    {
                      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).  */
                         (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;
                    }
                      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);
                  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;
                  rel++;
                  wrel++;
                  continue;
@@ -4695,21 +4875,33 @@ r_386_got32:
              bfd_vma roff;
 
              /* GD->IE transition.  */
              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)
                {
              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
                {
                  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,
                  roff = rel->r_offset - 2;
                }
              memcpy (contents + roff,
@@ -4728,7 +4920,7 @@ r_386_got32:
                          - htab->elf.sgotplt->output_section->vma
                          - htab->elf.sgotplt->output_offset,
                          contents + roff + 8);
                          - 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;
              rel++;
              wrel++;
              continue;
@@ -4814,18 +5006,34 @@ r_386_got32:
                                         input_section, contents,
                                         symtab_hdr, sym_hashes,
                                         &r_type, GOT_UNKNOWN, rel,
                                         input_section, contents,
                                         symtab_hdr, sym_hashes,
                                         &r_type, GOT_UNKNOWN, rel,
-                                        relend, h, r_symndx))
+                                        relend, h, r_symndx, TRUE))
            return FALSE;
 
          if (r_type != R_386_TLS_LDM)
            {
            return FALSE;
 
          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);
              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++;
              /* Skip R_386_PC32/R_386_PLT32.  */
              rel++;
              wrel++;
@@ -4954,13 +5162,9 @@ check_relocation_error:
            }
 
          if (r == bfd_reloc_overflow)
            }
 
          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)
          else
            {
              (*_bfd_error_handler)
@@ -5011,6 +5215,7 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
   unsigned plt_entry_size;
   const struct elf_i386_backend_data *abed;
   struct elf_i386_link_hash_entry *eh;
   unsigned plt_entry_size;
   const struct elf_i386_backend_data *abed;
   struct elf_i386_link_hash_entry *eh;
+  bfd_boolean local_undefweak;
 
   htab = elf_i386_hash_table (info);
   if (htab == NULL)
 
   htab = elf_i386_hash_table (info);
   if (htab == NULL)
@@ -5021,6 +5226,13 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
 
   eh = (struct elf_i386_link_hash_entry *) h;
 
 
   eh = (struct elf_i386_link_hash_entry *) h;
 
+  /* We keep PLT/GOT entries without dynamic PLT/GOT relocations for
+     resolved undefined weak symbols in executable so that their
+     references have value 0 at run-time.  */
+  local_undefweak = UNDEFINED_WEAK_RESOLVED_TO_ZERO (info,
+                                                    eh->has_got_reloc,
+                                                    eh);
+
   if (h->plt.offset != (bfd_vma) -1)
     {
       bfd_vma plt_index;
   if (h->plt.offset != (bfd_vma) -1)
     {
       bfd_vma plt_index;
@@ -5048,6 +5260,7 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
         it up.  */
 
       if ((h->dynindx == -1
         it up.  */
 
       if ((h->dynindx == -1
+          && !local_undefweak
           && !((h->forced_local || bfd_link_executable (info))
                && h->def_regular
                && h->type == STT_GNU_IFUNC))
           && !((h->forced_local || bfd_link_executable (info))
                && h->def_regular
                && h->type == STT_GNU_IFUNC))
@@ -5136,54 +5349,61 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
                       + abed->plt->plt_got_offset);
        }
 
                       + abed->plt->plt_got_offset);
        }
 
-      /* Fill in the entry in the global offset table.  */
-      bfd_put_32 (output_bfd,
-                 (plt->output_section->vma
-                  + plt->output_offset
-                  + h->plt.offset
-                  + abed->plt->plt_lazy_offset),
-                 gotplt->contents + got_offset);
-
-      /* Fill in the entry in the .rel.plt section.  */
-      rel.r_offset = (gotplt->output_section->vma
-                     + gotplt->output_offset
-                     + got_offset);
-      if (h->dynindx == -1
-         || ((bfd_link_executable (info)
-              || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
-             && h->def_regular
-              && h->type == STT_GNU_IFUNC))
+      /* Fill in the entry in the global offset table.  Leave the entry
+        as zero for undefined weak symbol in PIE.  No PLT relocation
+        against undefined weak symbol in PIE.  */
+      if (!local_undefweak)
        {
        {
-         /* If an STT_GNU_IFUNC symbol is locally defined, generate
-            R_386_IRELATIVE instead of R_386_JUMP_SLOT.  Store addend
-            in the .got.plt section.  */
          bfd_put_32 (output_bfd,
          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),
+                     (plt->output_section->vma
+                      + plt->output_offset
+                      + h->plt.offset
+                      + abed->plt->plt_lazy_offset),
                      gotplt->contents + got_offset);
                      gotplt->contents + got_offset);
-         rel.r_info = ELF32_R_INFO (0, R_386_IRELATIVE);
-         /* R_386_IRELATIVE comes last.  */
-         plt_index = htab->next_irelative_index--;
-       }
-      else
-       {
-         rel.r_info = ELF32_R_INFO (h->dynindx, R_386_JUMP_SLOT);
-         plt_index = htab->next_jump_slot_index++;
-       }
-      loc = relplt->contents + plt_index * sizeof (Elf32_External_Rel);
-      bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
 
 
-      /* Don't fill PLT entry for static executables.  */
-      if (plt == htab->elf.splt)
-       {
-         bfd_put_32 (output_bfd, plt_index * sizeof (Elf32_External_Rel),
-                     plt->contents + h->plt.offset
-                      + abed->plt->plt_reloc_offset);
-         bfd_put_32 (output_bfd, - (h->plt.offset
-                                     + abed->plt->plt_plt_offset + 4),
-                     plt->contents + h->plt.offset
-                      + abed->plt->plt_plt_offset);
+         /* Fill in the entry in the .rel.plt section.  */
+         rel.r_offset = (gotplt->output_section->vma
+                         + gotplt->output_offset
+                         + got_offset);
+         if (h->dynindx == -1
+             || ((bfd_link_executable (info)
+                  || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+                 && h->def_regular
+                 && h->type == STT_GNU_IFUNC))
+           {
+             /* If an STT_GNU_IFUNC symbol is locally defined, generate
+                R_386_IRELATIVE instead of R_386_JUMP_SLOT.  Store addend
+                in the .got.plt section.  */
+             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),
+                         gotplt->contents + got_offset);
+             rel.r_info = ELF32_R_INFO (0, R_386_IRELATIVE);
+             /* R_386_IRELATIVE comes last.  */
+             plt_index = htab->next_irelative_index--;
+           }
+         else
+           {
+             rel.r_info = ELF32_R_INFO (h->dynindx, R_386_JUMP_SLOT);
+             plt_index = htab->next_jump_slot_index++;
+           }
+
+         loc = relplt->contents + plt_index * sizeof (Elf32_External_Rel);
+         bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
+
+         /* Don't fill PLT entry for static executables.  */
+         if (plt == htab->elf.splt)
+           {
+             bfd_put_32 (output_bfd,
+                         plt_index * sizeof (Elf32_External_Rel),
+                         plt->contents + h->plt.offset
+                         + abed->plt->plt_reloc_offset);
+             bfd_put_32 (output_bfd, - (h->plt.offset
+                                        + abed->plt->plt_plt_offset + 4),
+                         plt->contents + h->plt.offset
+                         + abed->plt->plt_plt_offset);
+           }
        }
     }
   else if (eh->plt_got.offset != (bfd_vma) -1)
        }
     }
   else if (eh->plt_got.offset != (bfd_vma) -1)
@@ -5229,7 +5449,8 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
                  plt->contents + plt_offset + plt_got_offset);
     }
 
                  plt->contents + plt_offset + plt_got_offset);
     }
 
-  if (!h->def_regular
+  if (!local_undefweak
+      && !h->def_regular
       && (h->plt.offset != (bfd_vma) -1
          || eh->plt_got.offset != (bfd_vma) -1))
     {
       && (h->plt.offset != (bfd_vma) -1
          || eh->plt_got.offset != (bfd_vma) -1))
     {
@@ -5246,11 +5467,15 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
        sym->st_value = 0;
     }
 
        sym->st_value = 0;
     }
 
+  /* Don't generate dynamic GOT relocation against undefined weak
+     symbol in executable.  */
   if (h->got.offset != (bfd_vma) -1
       && ! GOT_TLS_GD_ANY_P (elf_i386_hash_entry(h)->tls_type)
   if (h->got.offset != (bfd_vma) -1
       && ! GOT_TLS_GD_ANY_P (elf_i386_hash_entry(h)->tls_type)
-      && (elf_i386_hash_entry(h)->tls_type & GOT_TLS_IE) == 0)
+      && (elf_i386_hash_entry(h)->tls_type & GOT_TLS_IE) == 0
+      && !local_undefweak)
     {
       Elf_Internal_Rela rel;
     {
       Elf_Internal_Rela rel;
+      asection *relgot = htab->elf.srelgot;
 
       /* This symbol has an entry in the global offset table.  Set it
         up.  */
 
       /* This symbol has an entry in the global offset table.  Set it
         up.  */
@@ -5270,7 +5495,28 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
       if (h->def_regular
          && h->type == STT_GNU_IFUNC)
        {
       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;
            {
              /* Generate R_386_GLOB_DAT.  */
              goto do_glob_dat;
@@ -5308,7 +5554,7 @@ do_glob_dat:
          rel.r_info = ELF32_R_INFO (h->dynindx, R_386_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)
     }
 
   if (h->needs_copy)
@@ -5348,6 +5594,25 @@ elf_i386_finish_local_dynamic_symbol (void **slot, void *inf)
                                         h, NULL);
 }
 
                                         h, NULL);
 }
 
+/* Finish up undefined weak symbol handling in PIE.  Fill its PLT entry
+   here since undefined weak symbol may not be dynamic and may not be
+   called for elf_i386_finish_dynamic_symbol.  */
+
+static bfd_boolean
+elf_i386_pie_finish_undefweak_symbol (struct bfd_hash_entry *bh,
+                                     void *inf)
+{
+  struct elf_link_hash_entry *h = (struct elf_link_hash_entry *) bh;
+  struct bfd_link_info *info = (struct bfd_link_info *) inf;
+
+  if (h->root.type != bfd_link_hash_undefweak
+      || h->dynindx != -1)
+    return TRUE;
+
+  return elf_i386_finish_dynamic_symbol (info->output_bfd,
+                                            info, h, NULL);
+}
+
 /* Used to decide how to sort relocs in an optimal manner for the
    dynamic linker, before writing them out.  */
 
 /* Used to decide how to sort relocs in an optimal manner for the
    dynamic linker, before writing them out.  */
 
@@ -5366,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);
       /* 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))
     {
     }
 
   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:
     case R_386_RELATIVE:
       return reloc_class_relative;
     case R_386_JUMP_SLOT:
@@ -5621,6 +5891,34 @@ 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;
 
   if (htab->elf.sgot && htab->elf.sgot->size > 0)
     elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize = 4;
 
+  /* Fill PLT entries for undefined weak symbols in PIE.  */
+  if (bfd_link_pie (info))
+    bfd_hash_traverse (&info->hash->table,
+                      elf_i386_pie_finish_undefweak_symbol,
+                      info);
+
+  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,
   /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols.  */
   htab_traverse (htab->loc_hash_table,
                 elf_i386_finish_local_dynamic_symbol,
@@ -5734,27 +6032,6 @@ elf_i386_hash_symbol (struct elf_link_hash_entry *h)
   return _bfd_elf_hash_symbol (h);
 }
 
   return _bfd_elf_hash_symbol (h);
 }
 
-/* Hook called by the linker routine which adds symbols from an object
-   file.  */
-
-static bfd_boolean
-elf_i386_add_symbol_hook (bfd * abfd,
-                         struct bfd_link_info * info,
-                         Elf_Internal_Sym * sym,
-                         const char ** namep ATTRIBUTE_UNUSED,
-                         flagword * flagsp ATTRIBUTE_UNUSED,
-                         asection ** secp ATTRIBUTE_UNUSED,
-                         bfd_vma * valp ATTRIBUTE_UNUSED)
-{
-  if (ELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE
-      && (abfd->flags & DYNAMIC) == 0
-      && bfd_get_flavour (info->output_bfd) == bfd_target_elf_flavour)
-    elf_tdata (info->output_bfd)->has_gnu_symbols
-      |= elf_gnu_symbol_unique;
-
-  return TRUE;
-}
-
 #define TARGET_LITTLE_SYM              i386_elf32_vec
 #define TARGET_LITTLE_NAME             "elf32-i386"
 #define ELF_ARCH                       bfd_arch_i386
 #define TARGET_LITTLE_SYM              i386_elf32_vec
 #define TARGET_LITTLE_NAME             "elf32-i386"
 #define ELF_ARCH                       bfd_arch_i386
@@ -5770,6 +6047,7 @@ elf_i386_add_symbol_hook (bfd * abfd,
 #define elf_backend_got_header_size    12
 #define elf_backend_plt_alignment      4
 #define elf_backend_extern_protected_data 1
 #define elf_backend_got_header_size    12
 #define elf_backend_plt_alignment      4
 #define elf_backend_extern_protected_data 1
+#define elf_backend_caches_rawsize     1
 
 /* Support RELA for objdump of prelink objects.  */
 #define elf_info_to_howto                    elf_i386_info_to_howto_rel
 
 /* Support RELA for objdump of prelink objects.  */
 #define elf_info_to_howto                    elf_i386_info_to_howto_rel
@@ -5791,8 +6069,8 @@ elf_i386_add_symbol_hook (bfd * abfd,
 #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_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_gc_mark_hook             elf_i386_gc_mark_hook
-#define elf_backend_gc_sweep_hook            elf_i386_gc_sweep_hook
 #define elf_backend_grok_prstatus            elf_i386_grok_prstatus
 #define elf_backend_grok_psinfo                      elf_i386_grok_psinfo
 #define elf_backend_reloc_type_class         elf_i386_reloc_type_class
 #define elf_backend_grok_prstatus            elf_i386_grok_prstatus
 #define elf_backend_grok_psinfo                      elf_i386_grok_psinfo
 #define elf_backend_reloc_type_class         elf_i386_reloc_type_class
@@ -5802,7 +6080,7 @@ elf_i386_add_symbol_hook (bfd * abfd,
 #define elf_backend_omit_section_dynsym \
   ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
 #define elf_backend_hash_symbol                      elf_i386_hash_symbol
 #define elf_backend_omit_section_dynsym \
   ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
 #define elf_backend_hash_symbol                      elf_i386_hash_symbol
-#define elf_backend_add_symbol_hook           elf_i386_add_symbol_hook
+#define elf_backend_fixup_symbol             elf_i386_fixup_symbol
 
 #include "elf32-target.h"
 
 
 #include "elf32-target.h"
 
@@ -5860,16 +6138,99 @@ elf_i386_fbsd_post_process_headers (bfd *abfd, struct bfd_link_info *info)
 
 /* The 32-bit static TLS arena size is rounded to the nearest 8-byte
    boundary.  */
 
 /* The 32-bit static TLS arena size is rounded to the nearest 8-byte
    boundary.  */
-#undef elf_backend_static_tls_alignment
+#undef  elf_backend_static_tls_alignment
 #define elf_backend_static_tls_alignment 8
 
 /* The Solaris 2 ABI requires a plt symbol on all platforms.
 
    Cf. Linker and Libraries Guide, Ch. 2, Link-Editor, Generating the Output
    File, p.63.  */
 #define elf_backend_static_tls_alignment 8
 
 /* The Solaris 2 ABI requires a plt symbol on all platforms.
 
    Cf. Linker and Libraries Guide, Ch. 2, Link-Editor, Generating the Output
    File, p.63.  */
-#undef elf_backend_want_plt_sym
+#undef  elf_backend_want_plt_sym
 #define elf_backend_want_plt_sym       1
 
 #define elf_backend_want_plt_sym       1
 
+#undef  elf_backend_strtab_flags
+#define elf_backend_strtab_flags       SHF_STRINGS
+
+/* Called to set the sh_flags, sh_link and sh_info fields of OSECTION which
+   has a type >= SHT_LOOS.  Returns TRUE if these fields were initialised 
+   FALSE otherwise.  ISECTION is the best guess matching section from the
+   input bfd IBFD, but it might be NULL.  */
+
+static bfd_boolean
+elf32_i386_copy_solaris_special_section_fields (const bfd *ibfd ATTRIBUTE_UNUSED,
+                                               bfd *obfd ATTRIBUTE_UNUSED,
+                                               const Elf_Internal_Shdr *isection ATTRIBUTE_UNUSED,
+                                               Elf_Internal_Shdr *osection ATTRIBUTE_UNUSED)
+{
+  /* PR 19938: FIXME: Need to add code for setting the sh_info
+     and sh_link fields of Solaris specific section types.  */
+  return FALSE;
+
+  /* Based upon Oracle Solaris 11.3 Linkers and Libraries Guide, Ch. 13,
+     Object File Format, Table 13-9  ELF sh_link and sh_info Interpretation:
+
+http://docs.oracle.com/cd/E53394_01/html/E54813/chapter6-94076.html#scrolltoc
+
+     The following values should be set:
+     
+Type                 Link                           Info
+-----------------------------------------------------------------------------
+SHT_SUNW_ancillary   The section header index of    0
+ [0x6fffffee]        the associated string table.
+       
+SHT_SUNW_capinfo     The section header index of    For a dynamic object, the
+ [0x6ffffff0]        the associated symbol table.   section header index of
+                                                    the associated
+                                                   SHT_SUNW_capchain table,
+                                                   otherwise 0.
+
+SHT_SUNW_symsort     The section header index of    0
+ [0x6ffffff1]        the associated symbol table.
+
+SHT_SUNW_tlssort     The section header index of    0
+ [0x6ffffff2]        the associated symbol table.
+       
+SHT_SUNW_LDYNSYM     The section header index of    One greater than the 
+ [0x6ffffff3]        the associated string table.   symbol table index of the
+                    This index is the same string  last local symbol, 
+                    table used by the SHT_DYNSYM   STB_LOCAL. Since
+                    section.                       SHT_SUNW_LDYNSYM only
+                                                   contains local symbols,
+                                                   sh_info is equivalent to
+                                                   the number of symbols in
+                                                   the table.
+
+SHT_SUNW_cap         If symbol capabilities exist,  If any capabilities refer
+ [0x6ffffff5]        the section header index of    to named strings, the
+                     the associated                 section header index of
+                    SHT_SUNW_capinfo table,        the associated string 
+                         otherwise 0.              table, otherwise 0.
+
+SHT_SUNW_move        The section header index of    0
+ [0x6ffffffa]        the associated symbol table.
+       
+SHT_SUNW_COMDAT      0                              0
+ [0x6ffffffb]
+
+SHT_SUNW_syminfo     The section header index of    The section header index
+ [0x6ffffffc]        the associated symbol table.   of the associated
+                                                   .dynamic section.
+
+SHT_SUNW_verdef      The section header index of    The number of version 
+ [0x6ffffffd]        the associated string table.   definitions within the
+                                                   section.
+
+SHT_SUNW_verneed     The section header index of    The number of version
+ [0x6ffffffe]        the associated string table.   dependencies within the
+                                                    section.
+
+SHT_SUNW_versym      The section header index of    0
+ [0x6fffffff]        the associated symbol table.  */
+}
+
+#undef  elf_backend_copy_special_section_fields
+#define elf_backend_copy_special_section_fields elf32_i386_copy_solaris_special_section_fields
+
 #include "elf32-target.h"
 
 /* Intel MCU support.  */
 #include "elf32-target.h"
 
 /* Intel MCU support.  */
@@ -5886,7 +6247,7 @@ elf32_iamcu_elf_object_p (bfd *abfd)
 #define TARGET_LITTLE_SYM              iamcu_elf32_vec
 #undef  TARGET_LITTLE_NAME
 #define TARGET_LITTLE_NAME             "elf32-iamcu"
 #define TARGET_LITTLE_SYM              iamcu_elf32_vec
 #undef  TARGET_LITTLE_NAME
 #define TARGET_LITTLE_NAME             "elf32-iamcu"
-#undef ELF_ARCH
+#undef  ELF_ARCH
 #define ELF_ARCH                       bfd_arch_iamcu
 
 #undef ELF_MACHINE_CODE
 #define ELF_ARCH                       bfd_arch_iamcu
 
 #undef ELF_MACHINE_CODE
@@ -5905,6 +6266,9 @@ elf32_iamcu_elf_object_p (bfd *abfd)
 #undef elf_backend_want_plt_sym
 #define elf_backend_want_plt_sym           0
 
 #undef elf_backend_want_plt_sym
 #define elf_backend_want_plt_sym           0
 
+#undef  elf_backend_strtab_flags
+#undef  elf_backend_copy_special_section_fields
+
 #include "elf32-target.h"
 
 /* Restore defaults.  */
 #include "elf32-target.h"
 
 /* Restore defaults.  */