Fix bad offset calculation for R_AARCH64_TLSDESC_* relocs.
[deliverable/binutils-gdb.git] / bfd / elf32-i386.c
index 7948fa99907b105af8fcad1005f7f8f18760e38d..999cd48ee4a69b49cef2fd87b501aba66bd4f832 100644 (file)
@@ -1,5 +1,5 @@
 /* Intel 80386/80486-specific support for 32-bit ELF
 /* Intel 80386/80486-specific support for 32-bit ELF
-   Copyright (C) 1993-2016 Free Software Foundation, Inc.
+   Copyright (C) 1993-2017 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -383,8 +383,9 @@ elf_i386_rtype_to_howto (bfd *abfd, unsigned r_type)
       && ((indx = r_type - R_386_vt_offset) - R_386_ext2
          >= R_386_vt - R_386_ext2))
     {
       && ((indx = r_type - R_386_vt_offset) - R_386_ext2
          >= R_386_vt - R_386_ext2))
     {
-      (*_bfd_error_handler) (_("%B: invalid relocation type %d"),
-                            abfd, (int) r_type);
+      /* xgettext:c-format */
+      _bfd_error_handler (_("%B: invalid relocation type %d"),
+                         abfd, (int) r_type);
       indx = R_386_NONE;
     }
   /* PR 17512: file: 0f67f69d.  */
       indx = R_386_NONE;
     }
   /* PR 17512: file: 0f67f69d.  */
@@ -540,15 +541,20 @@ elf_i386_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
    shared lib.  */
 #define ELIMINATE_COPY_RELOCS 1
 
    shared lib.  */
 #define ELIMINATE_COPY_RELOCS 1
 
-/* The size in bytes of an entry in the procedure linkage table.  */
+/* The size in bytes of an entry in the lazy procedure linkage table.  */
 
 
-#define PLT_ENTRY_SIZE 16
+#define LAZY_PLT_ENTRY_SIZE 16
 
 
-/* The first entry in an absolute procedure linkage table looks like
-   this.  See the SVR4 ABI i386 supplement to see how this works.
-   Will be padded to PLT_ENTRY_SIZE with htab->plt0_pad_byte.  */
+/* The size in bytes of an entry in the non-lazy procedure linkage
+   table.  */
 
 
-static const bfd_byte elf_i386_plt0_entry[12] =
+#define NON_LAZY_PLT_ENTRY_SIZE 8
+
+/* The first entry in an absolute lazy procedure linkage table looks
+   like this.  See the SVR4 ABI i386 supplement to see how this works.
+   Will be padded to LAZY_PLT_ENTRY_SIZE with lazy_plt->plt0_pad_byte.  */
+
+static const bfd_byte elf_i386_lazy_plt0_entry[12] =
 {
   0xff, 0x35,  /* pushl contents of address */
   0, 0, 0, 0,  /* replaced with address of .got + 4.  */
 {
   0xff, 0x35,  /* pushl contents of address */
   0, 0, 0, 0,  /* replaced with address of .got + 4.  */
@@ -556,10 +562,10 @@ static const bfd_byte elf_i386_plt0_entry[12] =
   0, 0, 0, 0   /* replaced with address of .got + 8.  */
 };
 
   0, 0, 0, 0   /* replaced with address of .got + 8.  */
 };
 
-/* Subsequent entries in an absolute procedure linkage table look like
-   this.  */
+/* Subsequent entries in an absolute lazy procedure linkage table look
+   like this.  */
 
 
-static const bfd_byte elf_i386_plt_entry[PLT_ENTRY_SIZE] =
+static const bfd_byte elf_i386_lazy_plt_entry[LAZY_PLT_ENTRY_SIZE] =
 {
   0xff, 0x25,  /* jmp indirect */
   0, 0, 0, 0,  /* replaced with address of this symbol in .got.  */
 {
   0xff, 0x25,  /* jmp indirect */
   0, 0, 0, 0,  /* replaced with address of this symbol in .got.  */
@@ -569,18 +575,20 @@ static const bfd_byte elf_i386_plt_entry[PLT_ENTRY_SIZE] =
   0, 0, 0, 0   /* replaced with offset to start of .plt.  */
 };
 
   0, 0, 0, 0   /* replaced with offset to start of .plt.  */
 };
 
-/* The first entry in a PIC procedure linkage table look like this.
-   Will be padded to PLT_ENTRY_SIZE with htab->plt0_pad_byte.  */
+/* The first entry in a PIC lazy procedure linkage table look like
+   this.  Will be padded to LAZY_PLT_ENTRY_SIZE with
+   lazy_plt->plt0_pad_byte.  */
 
 
-static const bfd_byte elf_i386_pic_plt0_entry[12] =
+static const bfd_byte elf_i386_pic_lazy_plt0_entry[12] =
 {
   0xff, 0xb3, 4, 0, 0, 0,      /* pushl 4(%ebx) */
   0xff, 0xa3, 8, 0, 0, 0       /* jmp *8(%ebx) */
 };
 
 {
   0xff, 0xb3, 4, 0, 0, 0,      /* pushl 4(%ebx) */
   0xff, 0xa3, 8, 0, 0, 0       /* jmp *8(%ebx) */
 };
 
-/* Subsequent entries in a PIC procedure linkage table look like this.  */
+/* Subsequent entries in a PIC lazy procedure linkage table look like
+   this.  */
 
 
-static const bfd_byte elf_i386_pic_plt_entry[PLT_ENTRY_SIZE] =
+static const bfd_byte elf_i386_pic_lazy_plt_entry[LAZY_PLT_ENTRY_SIZE] =
 {
   0xff, 0xa3,  /* jmp *offset(%ebx) */
   0, 0, 0, 0,  /* replaced with offset of this symbol in .got.  */
 {
   0xff, 0xa3,  /* jmp *offset(%ebx) */
   0, 0, 0, 0,  /* replaced with offset of this symbol in .got.  */
@@ -590,27 +598,82 @@ static const bfd_byte elf_i386_pic_plt_entry[PLT_ENTRY_SIZE] =
   0, 0, 0, 0   /* replaced with offset to start of .plt.  */
 };
 
   0, 0, 0, 0   /* replaced with offset to start of .plt.  */
 };
 
-/* Entries in the GOT procedure linkage table look like this.  */
+/* Entries in the non-lazy procedure linkage table look like this.  */
 
 
-static const bfd_byte elf_i386_got_plt_entry[8] =
+static const bfd_byte elf_i386_non_lazy_plt_entry[NON_LAZY_PLT_ENTRY_SIZE] =
 {
   0xff, 0x25,  /* jmp indirect */
   0, 0, 0, 0,  /* replaced with offset of this symbol in .got.  */
   0x66, 0x90   /* xchg %ax,%ax  */
 };
 
 {
   0xff, 0x25,  /* jmp indirect */
   0, 0, 0, 0,  /* replaced with offset of this symbol in .got.  */
   0x66, 0x90   /* xchg %ax,%ax  */
 };
 
-/* Entries in the PIC GOT procedure linkage table look like this.  */
+/* Entries in the PIC non-lazy procedure linkage table look like
+   this.  */
 
 
-static const bfd_byte elf_i386_pic_got_plt_entry[8] =
+static const bfd_byte elf_i386_pic_non_lazy_plt_entry[NON_LAZY_PLT_ENTRY_SIZE] =
 {
   0xff, 0xa3,  /* jmp *offset(%ebx)  */
   0, 0, 0, 0,  /* replaced with offset of this symbol in .got.  */
   0x66, 0x90   /* xchg %ax,%ax  */
 };
 
 {
   0xff, 0xa3,  /* jmp *offset(%ebx)  */
   0, 0, 0, 0,  /* replaced with offset of this symbol in .got.  */
   0x66, 0x90   /* xchg %ax,%ax  */
 };
 
-/* .eh_frame covering the .plt section.  */
+/* The first entry in an absolute IBT-enabled lazy procedure linkage
+   table looks like this.  */
+
+static const bfd_byte elf_i386_lazy_ibt_plt0_entry[LAZY_PLT_ENTRY_SIZE] =
+{
+  0xff, 0x35, 0, 0, 0, 0,      /* pushl GOT[1]       */
+  0xff, 0x25, 0, 0, 0, 0,      /* jmp *GOT[2]        */
+  0x0f, 0x1f, 0x40, 0x00       /* nopl 0(%rax)       */
+};
+
+/* Subsequent entries for an absolute IBT-enabled lazy procedure linkage
+   table look like this.  Subsequent entries for a PIC IBT-enabled lazy
+   procedure linkage table are the same.  */
+
+static const bfd_byte elf_i386_lazy_ibt_plt_entry[LAZY_PLT_ENTRY_SIZE] =
+{
+  0xf3, 0x0f, 0x1e, 0xfb,       /* endbr32                    */
+  0x68, 0, 0, 0, 0,             /* pushl immediate            */
+  0xe9, 0, 0, 0, 0,             /* jmp relative               */
+  0x66, 0x90                   /* xchg %ax,%ax               */
+};
+
+/* The first entry in a PIC IBT-enabled lazy procedure linkage table
+   look like.  */
+
+static const bfd_byte elf_i386_pic_lazy_ibt_plt0_entry[LAZY_PLT_ENTRY_SIZE] =
+{
+  0xff, 0xb3, 4, 0, 0, 0,      /* pushl 4(%ebx)      */
+  0xff, 0xa3, 8, 0, 0, 0,      /* jmp *8(%ebx)       */
+  0x0f, 0x1f, 0x40, 0x00       /* nopl 0(%rax)       */
+};
+
+/* Entries for branches with IBT-enabled in the absolute non-lazey
+   procedure linkage table look like this.  They have the same size
+   as the lazy PLT entry.  */
+
+static const bfd_byte elf_i386_non_lazy_ibt_plt_entry[LAZY_PLT_ENTRY_SIZE] =
+{
+  0xf3, 0x0f, 0x1e, 0xfb,            /* endbr32               */
+  0xff, 0x25, 0, 0, 0, 0,           /* jmp *name@GOT         */
+  0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00 /* nopw 0x0(%rax,%rax,1) */
+};
+
+/* Entries for branches with IBT-enabled in the PIC non-lazey procedure
+   linkage table look like this.  They have the same size as the lazy
+   PLT entry.  */
+
+static const bfd_byte elf_i386_pic_non_lazy_ibt_plt_entry[LAZY_PLT_ENTRY_SIZE] =
+{
+  0xf3, 0x0f, 0x1e, 0xfb,            /* endbr32               */
+  0xff, 0xa3, 0, 0, 0, 0,           /* jmp *name@GOT(%ebx)   */
+  0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00 /* nopw 0x0(%rax,%rax,1) */
+};
+
+/* .eh_frame covering the lazy .plt section.  */
 
 
-static const bfd_byte elf_i386_eh_frame_plt[] =
+static const bfd_byte elf_i386_eh_frame_lazy_plt[] =
 {
 #define PLT_CIE_LENGTH         20
 #define PLT_FDE_LENGTH         36
 {
 #define PLT_CIE_LENGTH         20
 #define PLT_FDE_LENGTH         36
@@ -647,17 +710,81 @@ static const bfd_byte elf_i386_eh_frame_plt[] =
   DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop
 };
 
   DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop
 };
 
-struct elf_i386_plt_layout
+/* .eh_frame covering the lazy .plt section with IBT-enabled.  */
+
+static const bfd_byte elf_i386_eh_frame_lazy_ibt_plt[] =
+{
+  PLT_CIE_LENGTH, 0, 0, 0,     /* CIE length */
+  0, 0, 0, 0,                  /* CIE ID */
+  1,                           /* CIE version */
+  'z', 'R', 0,                 /* Augmentation string */
+  1,                           /* Code alignment factor */
+  0x7c,                                /* Data alignment factor */
+  8,                           /* Return address column */
+  1,                           /* Augmentation size */
+  DW_EH_PE_pcrel | DW_EH_PE_sdata4, /* FDE encoding */
+  DW_CFA_def_cfa, 4, 4,                /* DW_CFA_def_cfa: r4 (esp) ofs 4 */
+  DW_CFA_offset + 8, 1,                /* DW_CFA_offset: r8 (eip) at cfa-4 */
+  DW_CFA_nop, DW_CFA_nop,
+
+  PLT_FDE_LENGTH, 0, 0, 0,     /* FDE length */
+  PLT_CIE_LENGTH + 8, 0, 0, 0, /* CIE pointer */
+  0, 0, 0, 0,                  /* R_386_PC32 .plt goes here */
+  0, 0, 0, 0,                  /* .plt size goes here */
+  0,                           /* Augmentation size */
+  DW_CFA_def_cfa_offset, 8,    /* DW_CFA_def_cfa_offset: 8 */
+  DW_CFA_advance_loc + 6,      /* DW_CFA_advance_loc: 6 to __PLT__+6 */
+  DW_CFA_def_cfa_offset, 12,   /* DW_CFA_def_cfa_offset: 12 */
+  DW_CFA_advance_loc + 10,     /* DW_CFA_advance_loc: 10 to __PLT__+16 */
+  DW_CFA_def_cfa_expression,   /* DW_CFA_def_cfa_expression */
+  11,                          /* Block length */
+  DW_OP_breg4, 4,              /* DW_OP_breg4 (esp): 4 */
+  DW_OP_breg8, 0,              /* DW_OP_breg8 (eip): 0 */
+  DW_OP_lit15, DW_OP_and, DW_OP_lit9, DW_OP_ge,
+  DW_OP_lit2, DW_OP_shl, DW_OP_plus,
+  DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop
+};
+
+/* .eh_frame covering the non-lazy .plt section.  */
+
+static const bfd_byte elf_i386_eh_frame_non_lazy_plt[] =
+{
+#define PLT_GOT_FDE_LENGTH             16
+  PLT_CIE_LENGTH, 0, 0, 0,     /* CIE length */
+  0, 0, 0, 0,                  /* CIE ID */
+  1,                           /* CIE version */
+  'z', 'R', 0,                 /* Augmentation string */
+  1,                           /* Code alignment factor */
+  0x7c,                                /* Data alignment factor */
+  8,                           /* Return address column */
+  1,                           /* Augmentation size */
+  DW_EH_PE_pcrel | DW_EH_PE_sdata4, /* FDE encoding */
+  DW_CFA_def_cfa, 4, 4,                /* DW_CFA_def_cfa: r4 (esp) ofs 4 */
+  DW_CFA_offset + 8, 1,                /* DW_CFA_offset: r8 (eip) at cfa-4 */
+  DW_CFA_nop, DW_CFA_nop,
+
+  PLT_GOT_FDE_LENGTH, 0, 0, 0, /* FDE length */
+  PLT_CIE_LENGTH + 8, 0, 0, 0, /* CIE pointer */
+  0, 0, 0, 0,                  /* the start of non-lazy .plt goes here */
+  0, 0, 0, 0,                  /* non-lazy .plt size goes here */
+  0,                           /* Augmentation size */
+  DW_CFA_nop, DW_CFA_nop, DW_CFA_nop
+};
+
+struct elf_i386_lazy_plt_layout
 {
 {
-  /* The first entry in an absolute procedure linkage table looks like this.  */
+  /* The first entry in an absolute lazy procedure linkage table looks
+     like this.  */
   const bfd_byte *plt0_entry;
   unsigned int plt0_entry_size;
 
   const bfd_byte *plt0_entry;
   unsigned int plt0_entry_size;
 
-  /* Offsets into plt0_entry that are to be replaced with GOT[1] and GOT[2].  */
+  /* Offsets into plt0_entry that are to be replaced with GOT[1] and
+     GOT[2].  */
   unsigned int plt0_got1_offset;
   unsigned int plt0_got2_offset;
 
   unsigned int plt0_got1_offset;
   unsigned int plt0_got2_offset;
 
-  /* Later entries in an absolute procedure linkage table look like this.  */
+  /* Later entries in an absolute lazy procedure linkage table look
+     like this.  */
   const bfd_byte *plt_entry;
   unsigned int plt_entry_size;
 
   const bfd_byte *plt_entry;
   unsigned int plt_entry_size;
 
@@ -666,40 +793,115 @@ struct elf_i386_plt_layout
   unsigned int plt_reloc_offset;  /* ... offset into relocation table. */
   unsigned int plt_plt_offset;    /* ... offset to start of .plt. */
 
   unsigned int plt_reloc_offset;  /* ... offset into relocation table. */
   unsigned int plt_plt_offset;    /* ... offset to start of .plt. */
 
-  /* Offset into plt_entry where the initial value of the GOT entry points.  */
+  /* Offset into plt_entry where the initial value of the GOT entry
+     points.  */
   unsigned int plt_lazy_offset;
 
   unsigned int plt_lazy_offset;
 
-  /* The first entry in a PIC procedure linkage table looks like this.  */
+  /* The first entry in a PIC lazy procedure linkage table looks like
+     this.  */
   const bfd_byte *pic_plt0_entry;
 
   const bfd_byte *pic_plt0_entry;
 
-  /* Subsequent entries in a PIC procedure linkage table look like this.  */
+  /* Subsequent entries in a PIC lazy procedure linkage table look
+     like this.  */
   const bfd_byte *pic_plt_entry;
 
   const bfd_byte *pic_plt_entry;
 
-  /* .eh_frame covering the .plt section.  */
+  /* .eh_frame covering the lazy .plt section.  */
+  const bfd_byte *eh_frame_plt;
+  unsigned int eh_frame_plt_size;
+};
+
+struct elf_i386_non_lazy_plt_layout
+{
+  /* Entries in an absolute non-lazy procedure linkage table look like
+     this.  */
+  const bfd_byte *plt_entry;
+  /* Entries in a PIC non-lazy procedure linkage table look like this.  */
+  const bfd_byte *pic_plt_entry;
+
+  unsigned int plt_entry_size;
+
+  /* Offsets into plt_entry that are to be replaced with...  */
+  unsigned int plt_got_offset;    /* ... address of this symbol in .got. */
+
+  /* .eh_frame covering the non-lazy .plt section.  */
   const bfd_byte *eh_frame_plt;
   unsigned int eh_frame_plt_size;
 };
 
   const bfd_byte *eh_frame_plt;
   unsigned int eh_frame_plt_size;
 };
 
-#define GET_PLT_ENTRY_SIZE(abfd) \
-  get_elf_i386_backend_data (abfd)->plt->plt_entry_size
+struct elf_i386_plt_layout
+{
+  /* The first entry in a lazy procedure linkage table looks like this.  */
+  const bfd_byte *plt0_entry;
+  /* Entries in a procedure linkage table look like this.  */
+  const bfd_byte *plt_entry;
+  unsigned int plt_entry_size;
+
+  /* 1 has PLT0.  */
+  unsigned int has_plt0;
+
+  /* Offsets into plt_entry that are to be replaced with...  */
+  unsigned int plt_got_offset;    /* ... address of this symbol in .got. */
+
+  /* .eh_frame covering the .plt section.  */
+  const bfd_byte *eh_frame_plt;
+  unsigned int eh_frame_plt_size;
+};
 
 /* These are the standard parameters.  */
 
 /* These are the standard parameters.  */
-static const struct elf_i386_plt_layout elf_i386_plt =
+static const struct elf_i386_lazy_plt_layout elf_i386_lazy_plt =
   {
   {
-    elf_i386_plt0_entry,                /* plt0_entry */
-    sizeof (elf_i386_plt0_entry),       /* plt0_entry_size */
+    elf_i386_lazy_plt0_entry,           /* plt0_entry */
+    sizeof (elf_i386_lazy_plt0_entry),  /* plt0_entry_size */
     2,                                  /* plt0_got1_offset */
     8,                                  /* plt0_got2_offset */
     2,                                  /* plt0_got1_offset */
     8,                                  /* plt0_got2_offset */
-    elf_i386_plt_entry,                 /* plt_entry */
-    PLT_ENTRY_SIZE,                     /* plt_entry_size */
+    elf_i386_lazy_plt_entry,            /* plt_entry */
+    LAZY_PLT_ENTRY_SIZE,                /* plt_entry_size */
     2,                                  /* plt_got_offset */
     7,                                  /* plt_reloc_offset */
     12,                                 /* plt_plt_offset */
     6,                                  /* plt_lazy_offset */
     2,                                  /* plt_got_offset */
     7,                                  /* plt_reloc_offset */
     12,                                 /* plt_plt_offset */
     6,                                  /* plt_lazy_offset */
-    elf_i386_pic_plt0_entry,            /* pic_plt0_entry */
-    elf_i386_pic_plt_entry,             /* pic_plt_entry */
-    elf_i386_eh_frame_plt,              /* eh_frame_plt */
-    sizeof (elf_i386_eh_frame_plt),     /* eh_frame_plt_size */
+    elf_i386_pic_lazy_plt0_entry,       /* pic_plt0_entry */
+    elf_i386_pic_lazy_plt_entry,        /* pic_plt_entry */
+    elf_i386_eh_frame_lazy_plt,         /* eh_frame_plt */
+    sizeof (elf_i386_eh_frame_lazy_plt) /* eh_frame_plt_size */
+  };
+
+static const struct elf_i386_non_lazy_plt_layout elf_i386_non_lazy_plt =
+  {
+    elf_i386_non_lazy_plt_entry,        /* plt_entry */
+    elf_i386_pic_non_lazy_plt_entry,    /* pic_plt_entry */
+    NON_LAZY_PLT_ENTRY_SIZE,            /* plt_entry_size */
+    2,                                  /* plt_got_offset */
+    elf_i386_eh_frame_non_lazy_plt,     /* eh_frame_plt */
+    sizeof (elf_i386_eh_frame_non_lazy_plt) /* eh_frame_plt_size */
+  };
+
+static const struct elf_i386_lazy_plt_layout elf_i386_lazy_ibt_plt =
+  {
+    elf_i386_lazy_ibt_plt0_entry,       /* plt0_entry */
+    sizeof (elf_i386_lazy_ibt_plt0_entry), /* plt0_entry_size */
+    2,                                  /* plt0_got1_offset */
+    8,                                  /* plt0_got2_offset */
+    elf_i386_lazy_ibt_plt_entry,        /* plt_entry */
+    LAZY_PLT_ENTRY_SIZE,                /* plt_entry_size */
+    4+2,                                /* plt_got_offset */
+    4+1,                                /* plt_reloc_offset */
+    4+6,                                /* plt_plt_offset */
+    0,                                  /* plt_lazy_offset */
+    elf_i386_pic_lazy_ibt_plt0_entry,   /* pic_plt0_entry */
+    elf_i386_lazy_ibt_plt_entry,        /* pic_plt_entry */
+    elf_i386_eh_frame_lazy_ibt_plt,     /* eh_frame_plt */
+    sizeof (elf_i386_eh_frame_lazy_ibt_plt) /* eh_frame_plt_size */
+  };
+
+static const struct elf_i386_non_lazy_plt_layout elf_i386_non_lazy_ibt_plt =
+  {
+    elf_i386_non_lazy_ibt_plt_entry,    /* plt_entry */
+    elf_i386_pic_non_lazy_ibt_plt_entry,/* pic_plt_entry */
+    LAZY_PLT_ENTRY_SIZE,                /* plt_entry_size */
+    4+2,                                /* plt_got_offset */
+    elf_i386_eh_frame_non_lazy_plt,     /* eh_frame_plt */
+    sizeof (elf_i386_eh_frame_non_lazy_plt) /* eh_frame_plt_size */
   };
 \f
 
   };
 \f
 
@@ -713,14 +915,16 @@ static const struct elf_i386_plt_layout elf_i386_plt =
 
 struct elf_i386_backend_data
 {
 
 struct elf_i386_backend_data
 {
-  /* Parameters describing PLT generation.  */
-  const struct elf_i386_plt_layout *plt;
-
   /* Value used to fill the unused bytes of the first PLT entry.  */
   bfd_byte plt0_pad_byte;
 
   /* Value used to fill the unused bytes of the first PLT entry.  */
   bfd_byte plt0_pad_byte;
 
-  /* True if the target system is VxWorks.  */
-  int is_vxworks;
+  /* Target system.  */
+  enum
+    {
+      is_normal,
+      is_vxworks,
+      is_nacl
+    } os;
 };
 
 #define get_elf_i386_backend_data(abfd) \
 };
 
 #define get_elf_i386_backend_data(abfd) \
@@ -730,9 +934,8 @@ struct elf_i386_backend_data
 /* These are the standard parameters.  */
 static const struct elf_i386_backend_data elf_i386_arch_bed =
   {
 /* These are the standard parameters.  */
 static const struct elf_i386_backend_data elf_i386_arch_bed =
   {
-    &elf_i386_plt,                      /* plt */
     0,                                  /* plt0_pad_byte */
     0,                                  /* plt0_pad_byte */
-    0,                                  /* is_vxworks */
+    is_normal                           /* os */
   };
 
 #define        elf_backend_arch_data   &elf_i386_arch_bed
   };
 
 #define        elf_backend_arch_data   &elf_i386_arch_bed
@@ -742,14 +945,28 @@ static const struct elf_i386_backend_data elf_i386_arch_bed =
    it isn't dynamic and
    1. Has non-GOT/non-PLT relocations in text section.  Or
    2. Has no GOT/PLT relocation.
    it isn't dynamic and
    1. Has non-GOT/non-PLT relocations in text section.  Or
    2. Has no GOT/PLT relocation.
+   Local undefined weak symbol is always resolved to 0.
  */
  */
-#define UNDEFINED_WEAK_RESOLVED_TO_ZERO(INFO, EH) \
+#define UNDEFINED_WEAK_RESOLVED_TO_ZERO(INFO, GOT_RELOC, EH)   \
   ((EH)->elf.root.type == bfd_link_hash_undefweak              \
   ((EH)->elf.root.type == bfd_link_hash_undefweak              \
-   && bfd_link_executable (INFO)                               \
-   && (elf_i386_hash_table (INFO)->interp == NULL              \
-       || !(EH)->has_got_reloc                                 \
-       || (EH)->has_non_got_reloc                              \
-       || !(INFO)->dynamic_undefined_weak))
+   && ((EH)->elf.forced_local                                  \
+       || (bfd_link_executable (INFO)                          \
+          && (elf_i386_hash_table (INFO)->interp == NULL       \
+              || !(GOT_RELOC)                                  \
+              || (EH)->has_non_got_reloc                       \
+              || !(INFO)->dynamic_undefined_weak))))
+
+/* Should copy relocation be generated for a symbol.  Don't generate
+   copy relocation against a protected symbol defined in a shared
+   object with GNU_PROPERTY_NO_COPY_ON_PROTECTED.  */
+#define SYMBOL_NO_COPYRELOC(INFO, EH) \
+  ((EH)->def_protected \
+   && ((EH)->elf.root.type == bfd_link_hash_defined \
+       || (EH)->elf.root.type == bfd_link_hash_defweak) \
+   && elf_has_no_copy_on_protected ((EH)->elf.root.u.def.section->owner) \
+   && ((EH)->elf.root.u.def.section->owner->flags & DYNAMIC) != 0 \
+   && ((EH)->elf.root.u.def.section->flags & SEC_CODE) == 0)
+
 
 /* i386 ELF linker hash entry.  */
 
 
 /* i386 ELF linker hash entry.  */
 
@@ -787,6 +1004,15 @@ struct elf_i386_link_hash_entry
   /* Symbol has non-GOT/non-PLT relocations in text sections.  */
   unsigned int has_non_got_reloc : 1;
 
   /* Symbol has non-GOT/non-PLT relocations in text sections.  */
   unsigned int has_non_got_reloc : 1;
 
+  /* Don't call finish_dynamic_symbol on this symbol.  */
+  unsigned int no_finish_dynamic_symbol : 1;
+
+  /* TRUE if symbol is __tls_get_addr.  */
+  unsigned int tls_get_addr : 1;
+
+  /* TRUE if symbol is defined as a protected symbol.  */
+  unsigned int def_protected : 1;
+
   /* 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;
@@ -795,6 +1021,9 @@ struct elf_i386_link_hash_entry
      GOT and PLT relocations against the same function.  */
   union gotplt_union plt_got;
 
      GOT and PLT relocations against the same function.  */
   union gotplt_union plt_got;
 
+  /* Information about the second PLT entry.   */
+  union gotplt_union plt_second;
+
   /* Offset of the GOTPLT entry reserved for the TLS descriptor,
      starting at the end of the jump table.  */
   bfd_vma tlsdesc_got;
   /* Offset of the GOTPLT entry reserved for the TLS descriptor,
      starting at the end of the jump table.  */
   bfd_vma tlsdesc_got;
@@ -842,10 +1071,20 @@ struct elf_i386_link_hash_table
 
   /* Short-cuts to get to dynamic linker sections.  */
   asection *interp;
 
   /* Short-cuts to get to dynamic linker sections.  */
   asection *interp;
-  asection *sdynbss;
-  asection *srelbss;
   asection *plt_eh_frame;
   asection *plt_eh_frame;
+  asection *plt_second;
+  asection *plt_second_eh_frame;
   asection *plt_got;
   asection *plt_got;
+  asection *plt_got_eh_frame;
+
+  /* Parameters describing PLT generation.  */
+  struct elf_i386_plt_layout plt;
+
+  /* Parameters describing lazy PLT generation.  */
+  const struct elf_i386_lazy_plt_layout *lazy_plt;
+
+  /* Parameters describing non-lazy PLT generation.  */
+  const struct elf_i386_non_lazy_plt_layout *non_lazy_plt;
 
   union
   {
 
   union
   {
@@ -922,6 +1161,9 @@ elf_i386_link_hash_newfunc (struct bfd_hash_entry *entry,
       eh->gotoff_ref = 0;
       eh->has_got_reloc = 0;
       eh->has_non_got_reloc = 0;
       eh->gotoff_ref = 0;
       eh->has_got_reloc = 0;
       eh->has_non_got_reloc = 0;
+      eh->no_finish_dynamic_symbol = 0;
+      eh->tls_get_addr = 0;
+      eh->def_protected = 0;
       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;
@@ -1050,67 +1292,6 @@ elf_i386_link_hash_table_create (bfd *abfd)
   return &ret->elf.root;
 }
 
   return &ret->elf.root;
 }
 
-/* Create .plt, .rel.plt, .got, .got.plt, .rel.got, .dynbss, and
-   .rel.bss sections in DYNOBJ, and set up shortcuts to them in our
-   hash table.  */
-
-static bfd_boolean
-elf_i386_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
-{
-  struct elf_i386_link_hash_table *htab;
-
-  if (!_bfd_elf_create_dynamic_sections (dynobj, info))
-    return FALSE;
-
-  htab = elf_i386_hash_table (info);
-  if (htab == NULL)
-    return FALSE;
-
-  htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss");
-  if (!htab->sdynbss)
-    abort ();
-
-  if (bfd_link_executable (info))
-    {
-      /* Always allow copy relocs for building executables.  */
-      asection *s = bfd_get_linker_section (dynobj, ".rel.bss");
-      if (s == NULL)
-       {
-         const struct elf_backend_data *bed = get_elf_backend_data (dynobj);
-         s = bfd_make_section_anyway_with_flags (dynobj,
-                                                 ".rel.bss",
-                                                 (bed->dynamic_sec_flags
-                                                  | SEC_READONLY));
-         if (s == NULL
-             || ! bfd_set_section_alignment (dynobj, s,
-                                             bed->s->log_file_align))
-           return FALSE;
-       }
-      htab->srelbss = s;
-    }
-
-  if (get_elf_i386_backend_data (dynobj)->is_vxworks
-      && !elf_vxworks_create_dynamic_sections (dynobj, info,
-                                              &htab->srelplt2))
-    return FALSE;
-
-  if (!info->no_ld_generated_unwind_info
-      && htab->plt_eh_frame == NULL
-      && htab->elf.splt != NULL)
-    {
-      flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
-                       | SEC_HAS_CONTENTS | SEC_IN_MEMORY
-                       | SEC_LINKER_CREATED);
-      htab->plt_eh_frame
-       = bfd_make_section_anyway_with_flags (dynobj, ".eh_frame", flags);
-      if (htab->plt_eh_frame == NULL
-         || !bfd_set_section_alignment (dynobj, htab->plt_eh_frame, 2))
-       return FALSE;
-    }
-
-  return TRUE;
-}
-
 /* Copy the extra info we tack onto an elf_link_hash_entry.  */
 
 static void
 /* Copy the extra info we tack onto an elf_link_hash_entry.  */
 
 static void
@@ -1175,7 +1356,8 @@ elf_i386_copy_indirect_symbol (struct bfd_link_info *info,
       /* If called to transfer flags for a weakdef during processing
         of elf_adjust_dynamic_symbol, don't copy non_got_ref.
         We clear it ourselves for ELIMINATE_COPY_RELOCS.  */
       /* If called to transfer flags for a weakdef during processing
         of elf_adjust_dynamic_symbol, don't copy non_got_ref.
         We clear it ourselves for ELIMINATE_COPY_RELOCS.  */
-      dir->ref_dynamic |= ind->ref_dynamic;
+      if (dir->versioned != versioned_hidden)
+       dir->ref_dynamic |= ind->ref_dynamic;
       dir->ref_regular |= ind->ref_regular;
       dir->ref_regular_nonweak |= ind->ref_regular_nonweak;
       dir->needs_plt |= ind->needs_plt;
       dir->ref_regular |= ind->ref_regular;
       dir->ref_regular_nonweak |= ind->ref_regular_nonweak;
       dir->needs_plt |= ind->needs_plt;
@@ -1197,7 +1379,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,
@@ -1205,26 +1387,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;
 
   offset = rel->r_offset;
   switch (r_type)
 
   offset = rel->r_offset;
   switch (r_type)
@@ -1234,69 +1402,110 @@ 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;
 
       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
 
       r_symndx = ELF32_R_SYM (rel[1].r_info);
       if (r_symndx < symtab_hdr->sh_info)
        return FALSE;
 
       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-      /* Use strncmp to check ___tls_get_addr since ___tls_get_addr
-        may be versioned.  */
-      return (h != NULL
-             && h->root.root.string != NULL
-             && (ELF32_R_TYPE (rel[1].r_info) == R_386_PC32
-                 || ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32)
-             && (strncmp (h->root.root.string, "___tls_get_addr",
-                          15) == 0));
+      if (h == NULL
+         || !((struct elf_i386_link_hash_entry *) h)->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:
@@ -1358,13 +1567,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;
@@ -1386,7 +1595,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;
@@ -1415,10 +1625,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;
 
@@ -1462,7 +1671,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))
     {
@@ -1491,11 +1700,12 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
            }
        }
 
            }
        }
 
-      (*_bfd_error_handler)
-       (_("%B: TLS transition from %s to %s against `%s' at 0x%lx "
+      _bfd_error_handler
+       /* xgettext:c-format */
+       (_("%B: TLS transition from %s to %s against `%s' at %#Lx "
           "in section `%A' failed"),
           "in section `%A' failed"),
-        abfd, sec, from->name, to->name, name,
-        (unsigned long) rel->r_offset);
+        abfd, from->name, to->name, name,
+        rel->r_offset, sec);
       bfd_set_error (bfd_error_bad_value);
       return FALSE;
     }
       bfd_set_error (bfd_error_bad_value);
       return FALSE;
     }
@@ -1504,84 +1714,359 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
   return TRUE;
 }
 
   return TRUE;
 }
 
-/* Rename some of the generic section flags to better document how they
-   are used here.  */
-#define need_convert_load sec_flg0
-
-/* Look through the relocs for a section during the first phase, and
-   calculate needed space in the global offset table, procedure linkage
-   table, and dynamic reloc sections.  */
+/* 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_check_relocs (bfd *abfd,
-                      struct bfd_link_info *info,
-                      asection *sec,
-                      const Elf_Internal_Rela *relocs)
+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;
 {
   struct elf_i386_link_hash_table *htab;
-  Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes;
-  const Elf_Internal_Rela *rel;
-  const Elf_Internal_Rela *rel_end;
-  asection *sreloc;
-  bfd_boolean use_plt_got;
+  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 (bfd_link_relocatable (info))
+  if (roff < 2)
     return TRUE;
 
     return TRUE;
 
-  BFD_ASSERT (is_i386_elf (abfd));
-
-  htab = elf_i386_hash_table (info);
-  if (htab == NULL)
-    return FALSE;
+  /* Addend for R_386_GOT32X relocations must be 0.  */
+  addend = bfd_get_32 (abfd, contents + roff);
+  if (addend != 0)
+    return TRUE;
 
 
-  use_plt_got = (!get_elf_i386_backend_data (abfd)->is_vxworks
-                && (get_elf_i386_backend_data (abfd)
-                    == &elf_i386_arch_bed));
+  htab = elf_i386_hash_table (link_info);
+  is_pic = bfd_link_pic (link_info);
 
 
-  symtab_hdr = &elf_symtab_hdr (abfd);
-  sym_hashes = elf_sym_hashes (abfd);
+  r_type = ELF32_R_TYPE (irel->r_info);
+  r_symndx = ELF32_R_SYM (irel->r_info);
 
 
-  sreloc = NULL;
+  modrm = bfd_get_8 (abfd, contents + roff - 1);
+  baseless = (modrm & 0xc7) == 0x5;
 
 
-  rel_end = relocs + sec->reloc_count;
-  for (rel = relocs; rel < rel_end; rel++)
+  if (baseless && is_pic)
     {
     {
-      unsigned int r_type;
-      unsigned long r_symndx;
-      struct elf_link_hash_entry *h;
-      struct elf_i386_link_hash_entry *eh;
-      Elf_Internal_Sym *isym;
+      /* For PIC, disallow R_386_GOT32X without a base register
+        since we don't know what the GOT base is.  */
       const char *name;
       const char *name;
-      bfd_boolean size_reloc;
-
-      r_symndx = ELF32_R_SYM (rel->r_info);
-      r_type = ELF32_R_TYPE (rel->r_info);
 
 
-      if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
+      if (h == NULL)
        {
        {
-         (*_bfd_error_handler) (_("%B: bad symbol index: %d"),
-                                abfd,
-                                r_symndx);
-         return FALSE;
+         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;
 
 
-      if (r_symndx < symtab_hdr->sh_info)
-       {
-         /* A local symbol.  */
-         isym = bfd_sym_from_r_symndx (&htab->sym_cache,
-                                       abfd, r_symndx);
-         if (isym == NULL)
-           return FALSE;
+      _bfd_error_handler
+       /* xgettext:c-format */
+       (_("%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;
+    }
 
 
-         /* 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;
+  opcode = bfd_get_8 (abfd, contents + roff - 2);
 
 
-             /* Fake a STT_GNU_IFUNC symbol.  */
+  /* 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)
+               {
+                 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.  start_stop is set on
+        __start_SECNAME/__stop_SECNAME which mark section SECNAME.  */
+      if (h->start_stop
+         || ((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.  */
+#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
+   table, and dynamic reloc sections.  */
+
+static bfd_boolean
+elf_i386_check_relocs (bfd *abfd,
+                      struct bfd_link_info *info,
+                      asection *sec,
+                      const Elf_Internal_Rela *relocs)
+{
+  struct elf_i386_link_hash_table *htab;
+  Elf_Internal_Shdr *symtab_hdr;
+  struct elf_link_hash_entry **sym_hashes;
+  const Elf_Internal_Rela *rel;
+  const Elf_Internal_Rela *rel_end;
+  asection *sreloc;
+  bfd_byte *contents;
+
+  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)
+    {
+      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;
+    }
+
+  symtab_hdr = &elf_symtab_hdr (abfd);
+  sym_hashes = elf_sym_hashes (abfd);
+
+  sreloc = NULL;
+
+  rel_end = relocs + sec->reloc_count;
+  for (rel = relocs; rel < rel_end; rel++)
+    {
+      unsigned int r_type;
+      unsigned int r_symndx;
+      struct elf_link_hash_entry *h;
+      struct elf_i386_link_hash_entry *eh;
+      Elf_Internal_Sym *isym;
+      const char *name;
+      bfd_boolean size_reloc;
+
+      r_symndx = ELF32_R_SYM (rel->r_info);
+      r_type = ELF32_R_TYPE (rel->r_info);
+
+      if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
+       {
+         /* xgettext:c-format */
+         _bfd_error_handler (_("%B: bad symbol index: %d"),
+                             abfd, r_symndx);
+         goto error_return;
+       }
+
+      if (r_symndx < symtab_hdr->sh_info)
+       {
+         /* A local symbol.  */
+         isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+                                       abfd, r_symndx);
+         if (isym == NULL)
+           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)
+               goto error_return;
+
+             /* Fake a STT_GNU_IFUNC symbol.  */
+             h->root.root.string = bfd_elf_sym_name (abfd, symtab_hdr,
+                                                     isym, NULL);
              h->type = STT_GNU_IFUNC;
              h->def_regular = 1;
              h->ref_regular = 1;
              h->type = STT_GNU_IFUNC;
              h->def_regular = 1;
              h->ref_regular = 1;
@@ -1603,42 +2088,23 @@ 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)
        {
-         switch (r_type)
-           {
-           default:
-             break;
-
-           case R_386_GOTOFF:
-             eh->gotoff_ref = 1;
-           case R_386_32:
-           case R_386_PC32:
-           case R_386_PLT32:
-           case R_386_GOT32:
-           case R_386_GOT32X:
-             if (htab->elf.dynobj == NULL)
-               htab->elf.dynobj = abfd;
-             /* Create the ifunc sections for static executables.  */
-             if (h->type == STT_GNU_IFUNC
-                 && !_bfd_elf_create_ifunc_sections (htab->elf.dynobj,
-                                                     info))
-               return FALSE;
-             break;
-           }
+         if (r_type == R_386_GOTOFF)
+           eh->gotoff_ref = 1;
 
          /* It is referenced by a non-shared object. */
          h->ref_regular = 1;
 
          /* It is referenced by a non-shared object. */
          h->ref_regular = 1;
-         h->root.non_ir_ref = 1;
+         h->root.non_ir_ref_regular = 1;
 
          if (h->type == STT_GNU_IFUNC)
            elf_tdata (info->output_bfd)->has_gnu_symbols
              |= elf_gnu_symbol_ifunc;
        }
 
 
          if (h->type == STT_GNU_IFUNC)
            elf_tdata (info->output_bfd)->has_gnu_symbols
              |= 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)
        {
@@ -1729,7 +2195,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);
@@ -1760,12 +2226,13 @@ elf_i386_check_relocs (bfd *abfd,
                    else
                      name = bfd_elf_sym_name (abfd, symtab_hdr, isym,
                                             NULL);
                    else
                      name = bfd_elf_sym_name (abfd, symtab_hdr, isym,
                                             NULL);
-                   (*_bfd_error_handler)
+                   _bfd_error_handler
+                     /* xgettext:c-format */
                      (_("%B: `%s' accessed both as normal and "
                         "thread local symbol"),
                       abfd, name);
                    bfd_set_error (bfd_error_bad_value);
                      (_("%B: `%s' accessed both as normal and "
                         "thread local symbol"),
                       abfd, name);
                    bfd_set_error (bfd_error_bad_value);
-                   return FALSE;
+                   goto error_return;
                  }
              }
 
                  }
              }
 
@@ -1782,13 +2249,6 @@ elf_i386_check_relocs (bfd *abfd,
        case R_386_GOTOFF:
        case R_386_GOTPC:
        create_got:
        case R_386_GOTOFF:
        case R_386_GOTPC:
        create_got:
-         if (htab->elf.sgot == NULL)
-           {
-             if (htab->elf.dynobj == NULL)
-               htab->elf.dynobj = abfd;
-             if (!_bfd_elf_create_got_section (htab->elf.dynobj, info))
-               return FALSE;
-           }
          if (r_type != R_386_TLS_IE)
            {
              if (eh != NULL)
          if (r_type != R_386_TLS_IE)
            {
              if (eh != NULL)
@@ -1811,15 +2271,12 @@ elf_i386_check_relocs (bfd *abfd,
          if (eh != NULL && (sec->flags & SEC_CODE) != 0)
            eh->has_non_got_reloc = 1;
 do_relocation:
          if (eh != NULL && (sec->flags & SEC_CODE) != 0)
            eh->has_non_got_reloc = 1;
 do_relocation:
-         /* STT_GNU_IFUNC symbol must go through PLT even if it is
-            locally defined and undefined symbol may turn out to be
-            a STT_GNU_IFUNC symbol later.  */
+         /* We are called after all symbols have been resolved.  Only
+            relocation against STT_GNU_IFUNC symbol must go through
+            PLT.  */
          if (h != NULL
              && (bfd_link_executable (info)
          if (h != NULL
              && (bfd_link_executable (info)
-                 || ((h->type == STT_GNU_IFUNC
-                      || h->root.type == bfd_link_hash_undefweak
-                      || h->root.type == bfd_link_hash_undefined)
-                     && SYMBOLIC_BIND (info, h))))
+                 || h->type == STT_GNU_IFUNC))
            {
              /* If this reloc is in a read-only section, we might
                 need a copy reloc.  We can't check reliably at this
            {
              /* If this reloc is in a read-only section, we might
                 need a copy reloc.  We can't check reliably at this
@@ -1829,9 +2286,13 @@ do_relocation:
                 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
@@ -1839,6 +2300,16 @@ do_relocation:
                     a function defined in a shared library.  */
                  if ((sec->flags & SEC_CODE) == 0)
                    h->pointer_equality_needed = 1;
                     a function defined in a shared library.  */
                  if ((sec->flags & SEC_CODE) == 0)
                    h->pointer_equality_needed = 1;
+                 else if (h->type == STT_GNU_IFUNC
+                          && bfd_link_pic (info))
+                   {
+                     _bfd_error_handler
+                       /* xgettext:c-format */
+                       (_("%B: unsupported non-PIC call to IFUNC `%s'"),
+                        abfd, h->root.root.string);
+                     bfd_set_error (bfd_error_bad_value);
+                     goto error_return;
+                   }
                }
              else
                {
                }
              else
                {
@@ -1872,18 +2343,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
                       && (! (bfd_link_pie (info)
                              || SYMBOLIC_BIND (info, h))
                           || h->root.type == bfd_link_hash_defweak
                           || !h->def_regular))))
               && (r_type != R_386_PC32
                   || (h != NULL
                       && (! (bfd_link_pie (info)
                              || SYMBOLIC_BIND (info, h))
                           || h->root.type == bfd_link_hash_defweak
                           || !h->def_regular))))
+             || (h != NULL
+                 && h->type == STT_GNU_IFUNC
+                 && r_type == R_386_32
+                 && (sec->flags & SEC_CODE) == 0)
              || (ELIMINATE_COPY_RELOCS
                  && !bfd_link_pic (info)
              || (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)))
@@ -1896,14 +2372,11 @@ do_size:
                 this reloc.  */
              if (sreloc == NULL)
                {
                 this reloc.  */
              if (sreloc == NULL)
                {
-                 if (htab->elf.dynobj == NULL)
-                   htab->elf.dynobj = abfd;
-
                  sreloc = _bfd_elf_make_dynamic_reloc_section
                    (sec, htab->elf.dynobj, 2, abfd, /*rela?*/ FALSE);
 
                  if (sreloc == NULL)
                  sreloc = _bfd_elf_make_dynamic_reloc_section
                    (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
@@ -1923,7 +2396,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)
@@ -1940,7 +2413,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;
@@ -1959,7 +2432,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
@@ -1968,53 +2441,36 @@ 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;
        }
 
          break;
 
        default:
          break;
        }
 
-      if (use_plt_got
-         && h != NULL
-         && h->plt.refcount > 0
-         && (((info->flags & DF_BIND_NOW) && !h->pointer_equality_needed)
-             || h->got.refcount > 0)
-         && htab->plt_got == NULL)
-       {
-         /* Create the GOT procedure linkage table.  */
-         unsigned int plt_got_align;
-         const struct elf_backend_data *bed;
-
-         bed = get_elf_backend_data (info->output_bfd);
-         BFD_ASSERT (sizeof (elf_i386_got_plt_entry) == 8
-                     && (sizeof (elf_i386_got_plt_entry)
-                         == sizeof (elf_i386_pic_got_plt_entry)));
-         plt_got_align = 3;
-
-         if (htab->elf.dynobj == NULL)
-           htab->elf.dynobj = abfd;
-         htab->plt_got
-           = bfd_make_section_anyway_with_flags (htab->elf.dynobj,
-                                                 ".plt.got",
-                                                 (bed->dynamic_sec_flags
-                                                  | SEC_ALLOC
-                                                  | SEC_CODE
-                                                  | SEC_LOAD
-                                                  | SEC_READONLY));
-         if (htab->plt_got == NULL
-             || !bfd_set_section_alignment (htab->elf.dynobj,
-                                            htab->plt_got,
-                                            plt_got_align))
-           return FALSE;
-       }
-
-      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
@@ -2047,6 +2503,7 @@ elf_i386_fixup_symbol (struct bfd_link_info *info,
 {
   if (h->dynindx != -1
       && UNDEFINED_WEAK_RESOLVED_TO_ZERO (info,
 {
   if (h->dynindx != -1
       && UNDEFINED_WEAK_RESOLVED_TO_ZERO (info,
+                                         elf_i386_hash_entry (h)->has_got_reloc,
                                          elf_i386_hash_entry (h)))
     {
       h->dynindx = -1;
                                          elf_i386_hash_entry (h)))
     {
       h->dynindx = -1;
@@ -2067,7 +2524,7 @@ elf_i386_adjust_dynamic_symbol (struct bfd_link_info *info,
                                struct elf_link_hash_entry *h)
 {
   struct elf_i386_link_hash_table *htab;
                                struct elf_link_hash_entry *h)
 {
   struct elf_i386_link_hash_table *htab;
-  asection *s;
+  asection *s, *srel;
   struct elf_i386_link_hash_entry *eh;
   struct elf_dyn_relocs *p;
 
   struct elf_i386_link_hash_entry *eh;
   struct elf_dyn_relocs *p;
 
@@ -2097,12 +2554,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;
+               }
            }
        }
 
            }
        }
 
@@ -2144,6 +2606,8 @@ elf_i386_adjust_dynamic_symbol (struct bfd_link_info *info,
        the link may change h->type.  So fix it now.  */
     h->plt.offset = (bfd_vma) -1;
 
        the link may change h->type.  So fix it now.  */
     h->plt.offset = (bfd_vma) -1;
 
+  eh = (struct elf_i386_link_hash_entry *) h;
+
   /* If this is a weak symbol, and there is a real definition, the
      processor independent code will have arranged for us to see the
      real definition first, and we can just use the same value.  */
   /* If this is a weak symbol, and there is a real definition, the
      processor independent code will have arranged for us to see the
      real definition first, and we can just use the same value.  */
@@ -2153,7 +2617,9 @@ elf_i386_adjust_dynamic_symbol (struct bfd_link_info *info,
                  || h->u.weakdef->root.type == bfd_link_hash_defweak);
       h->root.u.def.section = h->u.weakdef->root.u.def.section;
       h->root.u.def.value = h->u.weakdef->root.u.def.value;
                  || h->u.weakdef->root.type == bfd_link_hash_defweak);
       h->root.u.def.section = h->u.weakdef->root.u.def.section;
       h->root.u.def.value = h->u.weakdef->root.u.def.value;
-      if (ELIMINATE_COPY_RELOCS || info->nocopyreloc)
+      if (ELIMINATE_COPY_RELOCS
+         || info->nocopyreloc
+         || SYMBOL_NO_COPYRELOC (info, eh))
        h->non_got_ref = h->u.weakdef->non_got_ref;
       return TRUE;
     }
        h->non_got_ref = h->u.weakdef->non_got_ref;
       return TRUE;
     }
@@ -2171,12 +2637,11 @@ elf_i386_adjust_dynamic_symbol (struct bfd_link_info *info,
   /* If there are no references to this symbol that do not use the
      GOT nor R_386_GOTOFF relocation, we don't need to generate a copy
      reloc.  */
   /* If there are no references to this symbol that do not use the
      GOT nor R_386_GOTOFF relocation, we don't need to generate a copy
      reloc.  */
-  eh = (struct elf_i386_link_hash_entry *) h;
   if (!h->non_got_ref && !eh->gotoff_ref)
     return TRUE;
 
   /* If -z nocopyreloc was given, we won't generate them either.  */
   if (!h->non_got_ref && !eh->gotoff_ref)
     return TRUE;
 
   /* If -z nocopyreloc was given, we won't generate them either.  */
-  if (info->nocopyreloc)
+  if (info->nocopyreloc || SYMBOL_NO_COPYRELOC (info, eh))
     {
       h->non_got_ref = 0;
       return TRUE;
     {
       h->non_got_ref = 0;
       return TRUE;
@@ -2193,7 +2658,7 @@ elf_i386_adjust_dynamic_symbol (struct bfd_link_info *info,
      relocations) in an executable.  */
   if (ELIMINATE_COPY_RELOCS
       && !eh->gotoff_ref
      relocations) in an executable.  */
   if (ELIMINATE_COPY_RELOCS
       && !eh->gotoff_ref
-      && !get_elf_i386_backend_data (info->output_bfd)->is_vxworks)
+      && get_elf_i386_backend_data (info->output_bfd)->os != is_vxworks)
     {
       for (p = eh->dyn_relocs; p != NULL; p = p->next)
        {
     {
       for (p = eh->dyn_relocs; p != NULL; p = p->next)
        {
@@ -2222,14 +2687,22 @@ elf_i386_adjust_dynamic_symbol (struct bfd_link_info *info,
   /* We must generate a R_386_COPY reloc to tell the dynamic linker to
      copy the initial value out of the dynamic object and into the
      runtime process image.  */
   /* We must generate a R_386_COPY reloc to tell the dynamic linker to
      copy the initial value out of the dynamic object and into the
      runtime process image.  */
+  if ((h->root.u.def.section->flags & SEC_READONLY) != 0)
+    {
+      s = htab->elf.sdynrelro;
+      srel = htab->elf.sreldynrelro;
+    }
+  else
+    {
+      s = htab->elf.sdynbss;
+      srel = htab->elf.srelbss;
+    }
   if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
     {
   if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
     {
-      htab->srelbss->size += sizeof (Elf32_External_Rel);
+      srel->size += sizeof (Elf32_External_Rel);
       h->needs_copy = 1;
     }
 
       h->needs_copy = 1;
     }
 
-  s = htab->sdynbss;
-
   return _bfd_elf_adjust_dynamic_copy (info, h, s);
 }
 
   return _bfd_elf_adjust_dynamic_copy (info, h, s);
 }
 
@@ -2245,6 +2718,7 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   struct elf_dyn_relocs *p;
   unsigned plt_entry_size;
   bfd_boolean resolved_to_zero;
   struct elf_dyn_relocs *p;
   unsigned plt_entry_size;
   bfd_boolean resolved_to_zero;
+  const struct elf_i386_backend_data *bed;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
@@ -2256,9 +2730,13 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   if (htab == NULL)
     return FALSE;
 
   if (htab == NULL)
     return FALSE;
 
-  plt_entry_size = GET_PLT_ENTRY_SIZE (info->output_bfd);
+  bed = get_elf_i386_backend_data (info->output_bfd);
+
+  plt_entry_size = htab->plt.plt_entry_size;
 
 
-  resolved_to_zero = UNDEFINED_WEAK_RESOLVED_TO_ZERO (info, eh);
+  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.  */
 
   /* Clear the reference count of function pointer relocations if
      symbol isn't a normal function.  */
@@ -2287,39 +2765,47 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
      here if it is defined and referenced in a non-shared object.  */
   if (h->type == STT_GNU_IFUNC
       && h->def_regular)
      here if it is defined and referenced in a non-shared object.  */
   if (h->type == STT_GNU_IFUNC
       && h->def_regular)
-    return _bfd_elf_allocate_ifunc_dyn_relocs (info, h, &eh->dyn_relocs,
-                                              &htab->readonly_dynrelocs_against_ifunc,
-                                              plt_entry_size,
-                                              plt_entry_size, 4);
+    {
+      if (_bfd_elf_allocate_ifunc_dyn_relocs (info, h, &eh->dyn_relocs,
+                                             &htab->readonly_dynrelocs_against_ifunc,
+                                             plt_entry_size,
+                                             (htab->plt.has_plt0 *
+                                               plt_entry_size),
+                                              4, TRUE))
+       {
+         asection *s = htab->plt_second;
+         if (h->plt.offset != (bfd_vma) -1 && s != NULL)
+           {
+             /* Use the second PLT section if it is created.  */
+             eh->plt_second.offset = s->size;
+
+             /* Make room for this entry in the second PLT section.  */
+             s->size += htab->non_lazy_plt->plt_entry_size;
+           }
+
+         return TRUE;
+       }
+      else
+       return FALSE;
+    }
   /* 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
           && (h->plt.refcount > eh->func_pointer_refcount
               || eh->plt_got.refcount > 0))
     {
   /* 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
           && (h->plt.refcount > eh->func_pointer_refcount
               || eh->plt_got.refcount > 0))
     {
-      bfd_boolean use_plt_got;
+      bfd_boolean use_plt_got = eh->plt_got.refcount > 0;
 
       /* Clear the reference count of function pointer relocations
         if PLT is used.  */
       eh->func_pointer_refcount = 0;
 
 
       /* Clear the reference count of function pointer relocations
         if PLT is used.  */
       eh->func_pointer_refcount = 0;
 
-      if ((info->flags & DF_BIND_NOW) && !h->pointer_equality_needed)
-       {
-         /* Don't use the regular PLT for DF_BIND_NOW. */
-         h->plt.offset = (bfd_vma) -1;
-
-         /* Use the GOT PLT.  */
-         h->got.refcount = 1;
-         eh->plt_got.refcount = 1;
-       }
-
-      use_plt_got = eh->plt_got.refcount > 0;
-
       /* 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
       /* 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
-         && !resolved_to_zero)
+         && !resolved_to_zero
+         && h->root.type == bfd_link_hash_undefweak)
        {
          if (! bfd_elf_link_record_dynamic_symbol (info, h))
            return FALSE;
        {
          if (! bfd_elf_link_record_dynamic_symbol (info, h))
            return FALSE;
@@ -2329,18 +2815,23 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
          || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
        {
          asection *s = htab->elf.splt;
          || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
        {
          asection *s = htab->elf.splt;
+         asection *second_s = htab->plt_second;
          asection *got_s = htab->plt_got;
 
          /* If this is the first .plt entry, make room for the special
             first entry.  The .plt section is used by prelink to undo
             prelinking for dynamic relocations.  */
          if (s->size == 0)
          asection *got_s = htab->plt_got;
 
          /* If this is the first .plt entry, make room for the special
             first entry.  The .plt section is used by prelink to undo
             prelinking for dynamic relocations.  */
          if (s->size == 0)
-           s->size = plt_entry_size;
+           s->size = htab->plt.has_plt0 * plt_entry_size;
 
          if (use_plt_got)
            eh->plt_got.offset = got_s->size;
          else
 
          if (use_plt_got)
            eh->plt_got.offset = got_s->size;
          else
-           h->plt.offset = s->size;
+           {
+             h->plt.offset = s->size;
+             if (second_s)
+               eh->plt_second.offset = second_s->size;
+           }
 
          /* If this symbol is not defined in a regular file, and we are
             not generating a shared library, then set the symbol to this
 
          /* If this symbol is not defined in a regular file, and we are
             not generating a shared library, then set the symbol to this
@@ -2359,17 +2850,29 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                }
              else
                {
                }
              else
                {
-                 h->root.u.def.section = s;
-                 h->root.u.def.value = h->plt.offset;
+                 if (second_s)
+                   {
+                     /* We need to make a call to the entry of the
+                        second PLT instead of regular PLT entry.  */
+                     h->root.u.def.section = second_s;
+                     h->root.u.def.value = eh->plt_second.offset;
+                   }
+                 else
+                   {
+                     h->root.u.def.section = s;
+                     h->root.u.def.value = h->plt.offset;
+                   }
                }
            }
 
          /* Make room for this entry.  */
          if (use_plt_got)
                }
            }
 
          /* Make room for this entry.  */
          if (use_plt_got)
-           got_s->size += sizeof (elf_i386_got_plt_entry);
+           got_s->size += htab->non_lazy_plt->plt_entry_size;
          else
            {
              s->size += plt_entry_size;
          else
            {
              s->size += plt_entry_size;
+             if (second_s)
+               second_s->size += htab->non_lazy_plt->plt_entry_size;
 
              /* We also need to make an entry in the .got.plt section,
                 which will be placed in the .got section by the linker
 
              /* We also need to make an entry in the .got.plt section,
                 which will be placed in the .got section by the linker
@@ -2387,8 +2890,7 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                }
            }
 
                }
            }
 
-         if (get_elf_i386_backend_data (info->output_bfd)->is_vxworks
-              && !bfd_link_pic (info))
+         if (bed->os == is_vxworks && !bfd_link_pic (info))
            {
              /* VxWorks has a second set of relocations for each PLT entry
                 in executables.  They go in a separate relocation section,
            {
              /* VxWorks has a second set of relocations for each PLT entry
                 in executables.  They go in a separate relocation section,
@@ -2441,7 +2943,8 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
         Undefined weak syms won't yet be marked as dynamic.  */
       if (h->dynindx == -1
          && !h->forced_local
         Undefined weak syms won't yet be marked as dynamic.  */
       if (h->dynindx == -1
          && !h->forced_local
-         && !resolved_to_zero)
+         && !resolved_to_zero
+         && h->root.type == bfd_link_hash_undefweak)
        {
          if (! bfd_elf_link_record_dynamic_symbol (info, h))
            return FALSE;
        {
          if (! bfd_elf_link_record_dynamic_symbol (info, h))
            return FALSE;
@@ -2523,7 +3026,7 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
            }
        }
 
            }
        }
 
-      if (get_elf_i386_backend_data (info->output_bfd)->is_vxworks)
+      if (bed->os == is_vxworks)
        {
          struct elf_dyn_relocs **pp;
          for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
        {
          struct elf_dyn_relocs **pp;
          for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
@@ -2602,7 +3105,8 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
             Undefined weak syms won't yet be marked as dynamic.  */
          if (h->dynindx == -1
              && !h->forced_local
             Undefined weak syms won't yet be marked as dynamic.  */
          if (h->dynindx == -1
              && !h->forced_local
-             && !resolved_to_zero)
+             && !resolved_to_zero
+             && h->root.type == bfd_link_hash_undefweak)
            {
              if (! bfd_elf_link_record_dynamic_symbol (info, h))
                return FALSE;
            {
              if (! bfd_elf_link_record_dynamic_symbol (info, h))
                return FALSE;
@@ -2678,6 +3182,7 @@ elf_i386_readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 
          if ((info->warn_shared_textrel && bfd_link_pic (info))
              || info->error_textrel)
 
          if ((info->warn_shared_textrel && bfd_link_pic (info))
              || info->error_textrel)
+           /* xgettext:c-format */
            info->callbacks->einfo (_("%P: %B: warning: relocation against `%s' in readonly section `%A'\n"),
                                    p->sec->owner, h->root.root.string,
                                    p->sec);
            info->callbacks->einfo (_("%P: %B: warning: relocation against `%s' in readonly section `%A'\n"),
                                    p->sec->owner, h->root.root.string,
                                    p->sec);
@@ -2689,37 +3194,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 is_pic;
+  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.  */
@@ -2741,13 +3227,10 @@ 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);
 
   local_got_refcounts = elf_local_got_refcounts (abfd);
 
-  is_pic = bfd_link_pic (link_info);
-
   /* Get the section contents.  */
   if (elf_section_data (sec)->this_hdr.contents != NULL)
     contents = elf_section_data (sec)->this_hdr.contents;
   /* Get the section contents.  */
   if (elf_section_data (sec)->this_hdr.contents != NULL)
     contents = elf_section_data (sec)->this_hdr.contents;
@@ -2761,269 +3244,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 to_reloc_32;
-
-      if (r_type != R_386_GOT32 && r_type != R_386_GOT32X)
-       continue;
-
-      roff = irel->r_offset;
-      if (roff < 2)
-       continue;
+      bfd_boolean converted;
 
 
-      /* Addend for R_386_GOT32 and R_386_GOT32X relocations must be 0.  */
-      addend = bfd_get_32 (abfd, contents + roff);
-      if (addend != 0)
+      /* 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;
 
-      modrm = bfd_get_8 (abfd, contents + roff - 1);
-      baseless = (modrm & 0xc7) == 0x5;
-
-      if (r_type == R_386_GOT32X && baseless && is_pic)
-       {
-         /* For PIC, disallow R_386_GOT32X without a base register
-            since we don't know what the GOT base is.   Allow
-            R_386_GOT32 for existing object files.  */
-         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);
-
-      /* Convert mov to lea since it has been done for a while.  */
-      if (opcode != 0x8b)
-       {
-         /* Only convert R_386_GOT32X relocation for call, jmp or
-            one of adc, add, and, cmp, or, sbb, sub, test, xor
-            instructions.  */
-         if (r_type != R_386_GOT32X)
-           continue;
-       }
-
-      /* Convert to R_386_32 if PIC is false or there is no base
-        register.  */
-      to_reloc_32 = !is_pic || baseless;
-
-      /* Try to convert R_386_GOT32 and R_386_GOT32X.  Get the symbol
-        referred to by the reloc.  */
+      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;
 
-      /* Undefined weak symbol is only bound locally in executable
-        and its reference is resolved as 0.  */
-      if (UNDEFINED_WEAK_RESOLVED_TO_ZERO (link_info,
-                                          elf_i386_hash_entry (h)))
-       {
-         if (opcode == 0xff)
-           {
-             /* No direct branch to 0 for PIC.  */
-             if (is_pic)
-               continue;
-             else
-               goto convert_branch;
-           }
-         else
-           {
-             /* We can convert load of address 0 to R_386_32.  */
-             to_reloc_32 = TRUE;
-             goto convert_load;
-           }
-       }
+      converted = FALSE;
+      if (!elf_i386_convert_load_reloc (abfd, symtab_hdr, contents,
+                                       irel, h, &converted, link_info))
+       goto error_return;
 
 
-      if (opcode == 0xff)
+      if (converted)
        {
        {
-         /* 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))
+         changed = converted;
+         if (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;
-               }
-
-             changed_contents = TRUE;
-             changed_relocs = TRUE;
+             if (h->got.refcount > 0)
+               h->got.refcount -= 1;
            }
            }
-       }
-      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)
-           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))
+         else
            {
            {
-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)
-                   continue;
-
-                 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 (local_got_refcounts != NULL
+                 && local_got_refcounts[r_symndx] > 0)
+               local_got_refcounts[r_symndx] -= 1;
            }
        }
     }
            }
        }
     }
@@ -3031,7 +3295,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
        {
@@ -3042,7 +3306,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;
@@ -3078,20 +3342,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;
-         htab->interp = s;
-       }
-    }
-
   /* 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)
@@ -3127,7 +3377,8 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
                     linker script /DISCARD/, so we'll be discarding
                     the relocs too.  */
                }
                     linker script /DISCARD/, so we'll be discarding
                     the relocs too.  */
                }
-             else if (get_elf_i386_backend_data (output_bfd)->is_vxworks
+             else if ((get_elf_i386_backend_data (output_bfd)->os
+                       == is_vxworks)
                       && strcmp (p->sec->output_section->name,
                                  ".tls_vars") == 0)
                {
                       && strcmp (p->sec->output_section->name,
                                  ".tls_vars") == 0)
                {
@@ -3144,6 +3395,7 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
                      info->flags |= DF_TEXTREL;
                      if ((info->warn_shared_textrel && bfd_link_pic (info))
                          || info->error_textrel)
                      info->flags |= DF_TEXTREL;
                      if ((info->warn_shared_textrel && bfd_link_pic (info))
                          || info->error_textrel)
+                       /* xgettext:c-format */
                        info->callbacks->einfo (_("%P: %B: warning: relocation in readonly section `%A'\n"),
                                                p->sec->owner, p->sec);
                    }
                        info->callbacks->einfo (_("%P: %B: warning: relocation in readonly section `%A'\n"),
                                                p->sec->owner, p->sec);
                    }
@@ -3259,13 +3511,30 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
        htab->elf.sgotplt->size = 0;
     }
 
        htab->elf.sgotplt->size = 0;
     }
 
-
-  if (htab->plt_eh_frame != NULL
-      && htab->elf.splt != NULL
-      && htab->elf.splt->size != 0
-      && !bfd_is_abs_section (htab->elf.splt->output_section)
-      && _bfd_elf_eh_frame_present (info))
-    htab->plt_eh_frame->size = sizeof (elf_i386_eh_frame_plt);
+  if (_bfd_elf_eh_frame_present (info))
+    {
+      if (htab->plt_eh_frame != NULL
+         && htab->elf.splt != NULL
+         && htab->elf.splt->size != 0
+         && !bfd_is_abs_section (htab->elf.splt->output_section))
+       htab->plt_eh_frame->size = htab->plt.eh_frame_plt_size;
+
+      if (htab->plt_got_eh_frame != NULL
+         && htab->plt_got != NULL
+         && htab->plt_got->size != 0
+         && !bfd_is_abs_section (htab->plt_got->output_section))
+       htab->plt_got_eh_frame->size
+         = htab->non_lazy_plt->eh_frame_plt_size;
+
+      /* Unwind info for the second PLT and .plt.got sections are
+        identical.  */
+      if (htab->plt_second_eh_frame != NULL
+         && htab->plt_second != NULL
+         && htab->plt_second->size != 0
+         && !bfd_is_abs_section (htab->plt_second->output_section))
+       htab->plt_second_eh_frame->size
+         = htab->non_lazy_plt->eh_frame_plt_size;
+    }
 
   /* We now have determined the sizes of the various dynamic sections.
      Allocate memory for them.  */
 
   /* We now have determined the sizes of the various dynamic sections.
      Allocate memory for them.  */
@@ -3292,9 +3561,13 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
       else if (s == htab->elf.sgotplt
               || s == htab->elf.iplt
               || s == htab->elf.igotplt
       else if (s == htab->elf.sgotplt
               || s == htab->elf.iplt
               || s == htab->elf.igotplt
+              || s == htab->plt_second
               || s == htab->plt_got
               || s == htab->plt_eh_frame
               || s == htab->plt_got
               || s == htab->plt_eh_frame
-              || s == htab->sdynbss)
+              || s == htab->plt_got_eh_frame
+              || s == htab->plt_second_eh_frame
+              || s == htab->elf.sdynbss
+              || s == htab->elf.sdynrelro)
        {
          /* Strip these too.  */
        }
        {
          /* Strip these too.  */
        }
@@ -3347,12 +3620,35 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
   if (htab->plt_eh_frame != NULL
       && htab->plt_eh_frame->contents != NULL)
     {
   if (htab->plt_eh_frame != NULL
       && htab->plt_eh_frame->contents != NULL)
     {
-      memcpy (htab->plt_eh_frame->contents, elf_i386_eh_frame_plt,
-             sizeof (elf_i386_eh_frame_plt));
+      memcpy (htab->plt_eh_frame->contents,
+             htab->plt.eh_frame_plt,
+             htab->plt_eh_frame->size);
       bfd_put_32 (dynobj, htab->elf.splt->size,
                  htab->plt_eh_frame->contents + PLT_FDE_LEN_OFFSET);
     }
 
       bfd_put_32 (dynobj, htab->elf.splt->size,
                  htab->plt_eh_frame->contents + PLT_FDE_LEN_OFFSET);
     }
 
+  if (htab->plt_got_eh_frame != NULL
+      && htab->plt_got_eh_frame->contents != NULL)
+    {
+      memcpy (htab->plt_got_eh_frame->contents,
+             htab->non_lazy_plt->eh_frame_plt,
+             htab->plt_got_eh_frame->size);
+      bfd_put_32 (dynobj, htab->plt_got->size,
+                 (htab->plt_got_eh_frame->contents
+                  + PLT_FDE_LEN_OFFSET));
+    }
+
+  if (htab->plt_second_eh_frame != NULL
+      && htab->plt_second_eh_frame->contents != NULL)
+    {
+      memcpy (htab->plt_second_eh_frame->contents,
+             htab->non_lazy_plt->eh_frame_plt,
+             htab->plt_second_eh_frame->size);
+      bfd_put_32 (dynobj, htab->plt_second->size,
+                 (htab->plt_second_eh_frame->contents
+                  + PLT_FDE_LEN_OFFSET));
+    }
+
   if (htab->elf.dynamic_sections_created)
     {
       /* Add some entries to the .dynamic section.  We fill in the
   if (htab->elf.dynamic_sections_created)
     {
       /* Add some entries to the .dynamic section.  We fill in the
@@ -3375,14 +3671,14 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
             relocation.  */
          if (!add_dynamic_entry (DT_PLTGOT, 0))
            return FALSE;
             relocation.  */
          if (!add_dynamic_entry (DT_PLTGOT, 0))
            return FALSE;
+       }
 
 
-         if (htab->elf.srelplt->size != 0)
-           {
-             if (!add_dynamic_entry (DT_PLTRELSZ, 0)
-                 || !add_dynamic_entry (DT_PLTREL, DT_REL)
-                 || !add_dynamic_entry (DT_JMPREL, 0))
-               return FALSE;
-           }
+      if (htab->elf.srelplt->size != 0)
+       {
+         if (!add_dynamic_entry (DT_PLTRELSZ, 0)
+             || !add_dynamic_entry (DT_PLTREL, DT_REL)
+             || !add_dynamic_entry (DT_JMPREL, 0))
+           return FALSE;
        }
 
       if (relocs)
        }
 
       if (relocs)
@@ -3412,7 +3708,7 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
                return FALSE;
            }
        }
                return FALSE;
            }
        }
-      if (get_elf_i386_backend_data (output_bfd)->is_vxworks
+      if (get_elf_i386_backend_data (output_bfd)->os == is_vxworks
          && !elf_vxworks_add_dynamic_entries (output_bfd, info))
        return FALSE;
     }
          && !elf_vxworks_add_dynamic_entries (output_bfd, info))
        return FALSE;
     }
@@ -3581,6 +3877,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;
@@ -3590,14 +3890,15 @@ elf_i386_relocate_section (bfd *output_bfd,
   local_tlsdesc_gotents = elf_i386_local_tlsdesc_gotent (input_bfd);
   /* We have to handle relocations in vxworks .tls_vars sections
      specially, because the dynamic loader is 'weird'.  */
   local_tlsdesc_gotents = elf_i386_local_tlsdesc_gotent (input_bfd);
   /* We have to handle relocations in vxworks .tls_vars sections
      specially, because the dynamic loader is 'weird'.  */
-  is_vxworks_tls = (get_elf_i386_backend_data (output_bfd)->is_vxworks
+  is_vxworks_tls = ((get_elf_i386_backend_data (output_bfd)->os
+                    == is_vxworks)
                     && bfd_link_pic (info)
                    && !strcmp (input_section->output_section->name,
                                ".tls_vars"));
 
   elf_i386_set_tls_module_base (info);
 
                     && bfd_link_pic (info)
                    && !strcmp (input_section->output_section->name,
                                ".tls_vars"));
 
   elf_i386_set_tls_module_base (info);
 
-  plt_entry_size = GET_PLT_ENTRY_SIZE (output_bfd);
+  plt_entry_size = htab->plt.plt_entry_size;
 
   rel = wrel = relocs;
   relend = relocs + input_section->reloc_count;
 
   rel = wrel = relocs;
   relend = relocs + input_section->reloc_count;
@@ -3619,6 +3920,7 @@ elf_i386_relocate_section (bfd *output_bfd,
       bfd_vma st_size;
       asection *resolved_plt;
       bfd_boolean resolved_to_zero;
       bfd_vma st_size;
       asection *resolved_plt;
       bfd_boolean resolved_to_zero;
+      bfd_boolean relative_reloc;
 
       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
@@ -3634,13 +3936,8 @@ elf_i386_relocate_section (bfd *output_bfd,
              >= R_386_ext - R_386_standard)
          && ((indx = r_type - R_386_tls_offset) - R_386_ext
              >= R_386_ext2 - R_386_ext))
              >= R_386_ext - R_386_standard)
          && ((indx = r_type - R_386_tls_offset) - R_386_ext
              >= R_386_ext2 - R_386_ext))
-       {
-         (*_bfd_error_handler)
-           (_("%B: unrecognized relocation (0x%x) in section `%A'"),
-            input_bfd, input_section, r_type);
-         bfd_set_error (bfd_error_bad_value);
-         return FALSE;
-       }
+       return _bfd_unrecognized_reloc (input_bfd, input_section, r_type);
+
       howto = elf_howto_table + indx;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       howto = elf_howto_table + indx;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
@@ -3777,13 +4074,15 @@ elf_i386_relocate_section (bfd *output_bfd,
          continue;
        }
 
          continue;
        }
 
+      eh = (struct elf_i386_link_hash_entry *) h;
+
       /* Since STT_GNU_IFUNC symbol must go through PLT, we handle
         it here if it is defined in a non-shared object.  */
       if (h != NULL
          && h->type == STT_GNU_IFUNC
          && h->def_regular)
        {
       /* Since STT_GNU_IFUNC symbol must go through PLT, we handle
         it here if it is defined in a non-shared object.  */
       if (h != NULL
          && h->type == STT_GNU_IFUNC
          && h->def_regular)
        {
-         asection *plt, *gotplt, *base_got;
+         asection *gotplt, *base_got;
          bfd_vma plt_index;
          const char *name;
 
          bfd_vma plt_index;
          const char *name;
 
@@ -3796,92 +4095,33 @@ 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)
            {
-             plt = htab->elf.splt;
+             if (htab->plt_second != NULL)
+               {
+                 resolved_plt = htab->plt_second;
+                 plt_offset = eh->plt_second.offset;
+               }
+             else
+               {
+                 resolved_plt = htab->elf.splt;
+                 plt_offset = h->plt.offset;
+               }
              gotplt = htab->elf.sgotplt;
            }
          else
            {
              gotplt = htab->elf.sgotplt;
            }
          else
            {
-             plt = htab->elf.iplt;
+             resolved_plt = htab->elf.iplt;
+             plt_offset = h->plt.offset;
              gotplt = htab->elf.igotplt;
            }
 
              gotplt = htab->elf.igotplt;
            }
 
-         relocation = (plt->output_section->vma
-                       + plt->output_offset + h->plt.offset);
-
          switch (r_type)
            {
            default:
          switch (r_type)
            {
            default:
-             if (h->root.root.string)
-               name = h->root.root.string;
-             else
-               name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
-                                        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__);
-             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)
-               {
-                 Elf_Internal_Rela outrel;
-                 asection *sreloc;
-                 bfd_vma offset;
-
-                 /* Need a dynamic relocation to get the real function
-                    adddress.  */
-                 offset = _bfd_elf_section_offset (output_bfd,
-                                                   info,
-                                                   input_section,
-                                                   rel->r_offset);
-                 if (offset == (bfd_vma) -1
-                     || offset == (bfd_vma) -2)
-                   abort ();
-
-                 outrel.r_offset = (input_section->output_section->vma
-                                    + input_section->output_offset
-                                    + offset);
-
-                 if (h->dynindx == -1
-                     || h->forced_local
-                     || bfd_link_executable (info))
-                   {
-                     /* This symbol is resolved locally.  */
-                     outrel.r_info = ELF32_R_INFO (0, R_386_IRELATIVE);
-                     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),
-                                 contents + offset);
-                   }
-                 else
-                   outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
-
-                 sreloc = htab->elf.irelifunc;
-                 elf_append_rel (output_bfd, sreloc, &outrel);
-
-                 /* If this reloc is against an external symbol, we
-                    do not want to fiddle with the addend.  Otherwise,
-                    we need to include the symbol value so that it
-                    becomes an addend for the dynamic reloc.  For an
-                    internal symbol, we have updated addend.  */
-                 continue;
-               }
-             /* FALLTHROUGH */
-           case R_386_PC32:
-           case R_386_PLT32:
-             goto do_relocation;
+             break;
 
            case R_386_GOT32:
            case R_386_GOT32X:
 
            case R_386_GOT32:
            case R_386_GOT32X:
@@ -3897,9 +4137,13 @@ elf_i386_relocate_section (bfd *output_bfd,
                     even just remember the offset, as finish_dynamic_symbol
                     would use that as offset into .got.  */
 
                     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)
                    {
                  if (htab->elf.splt != NULL)
                    {
-                     plt_index = h->plt.offset / plt_entry_size - 1;
+                     plt_index = (h->plt.offset / plt_entry_size
+                                  - htab->plt.has_plt0);
                      off = (plt_index + 3) * 4;
                      base_got = htab->elf.sgotplt;
                    }
                      off = (plt_index + 3) * 4;
                      base_got = htab->elf.sgotplt;
                    }
@@ -3934,49 +4178,156 @@ elf_i386_relocate_section (bfd *output_bfd,
                    }
 
                  relocation = off;
                    }
 
                  relocation = off;
-
-                 /* Adjust for static executables.  */
-                 if (htab->elf.splt == NULL)
-                   relocation += gotplt->output_offset;
                }
              else
                }
              else
+               relocation = (base_got->output_section->vma
+                             + base_got->output_offset + off
+                             - gotplt->output_section->vma
+                             - gotplt->output_offset);
+
+             if (rel->r_offset > 1
+                 && (*(contents + rel->r_offset - 1) & 0xc7) == 0x5
+                 && *(contents + rel->r_offset - 2) != 0x8d)
+               {
+                 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)
                {
                {
-                 relocation = (base_got->output_section->vma
-                               + base_got->output_offset + off
-                               - gotplt->output_section->vma
-                               - gotplt->output_offset);
                  /* Adjust for static executables.  */
                  /* Adjust for static executables.  */
-                 if (htab->elf.splt == NULL)
-                   relocation += gotplt->output_offset;
+                 relocation += gotplt->output_offset;
                }
 
              goto do_relocation;
                }
 
              goto do_relocation;
-
-           case R_386_GOTOFF:
-             relocation -= (gotplt->output_section->vma
-                            + gotplt->output_offset);
-             goto do_relocation;
            }
            }
-       }
 
 
-      eh = (struct elf_i386_link_hash_entry *) h;
-      resolved_to_zero = (eh != NULL
-                         && UNDEFINED_WEAK_RESOLVED_TO_ZERO (info, eh));
+         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;
+           }
 
 
-      switch (r_type)
-       {
-       case R_386_GOT32X:
-         /* Avoid optimizing _DYNAMIC since ld.so may use its
-            link-time address.  */
-         if (h == htab->elf.hdynamic)
-           goto r_386_got32;
+         relocation = (resolved_plt->output_section->vma
+                       + resolved_plt->output_offset + plt_offset);
 
 
-         if (bfd_link_pic (info))
+         switch (r_type)
            {
            {
-             /* It is OK to convert mov to lea and 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.   */
+           default:
+bad_ifunc_reloc:
+             if (h->root.root.string)
+               name = h->root.root.string;
+             else
+               name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
+                                        NULL);
+             _bfd_error_handler
+               /* xgettext:c-format */
+               (_("%B: relocation %s against STT_GNU_IFUNC "
+                  "symbol `%s' isn't supported"), input_bfd,
+                howto->name, name);
+             bfd_set_error (bfd_error_bad_value);
+             return FALSE;
+
+           case R_386_32:
+             /* Generate dynamic relcoation only when there is a
+                non-GOT reference in a shared object.  */
+             if ((bfd_link_pic (info) && h->non_got_ref)
+                 || h->plt.offset == (bfd_vma) -1)
+               {
+                 Elf_Internal_Rela outrel;
+                 asection *sreloc;
+                 bfd_vma offset;
+
+do_ifunc_pointer:
+                 /* Need a dynamic relocation to get the real function
+                    adddress.  */
+                 offset = _bfd_elf_section_offset (output_bfd,
+                                                   info,
+                                                   input_section,
+                                                   rel->r_offset);
+                 if (offset == (bfd_vma) -1
+                     || offset == (bfd_vma) -2)
+                   abort ();
+
+                 outrel.r_offset = (input_section->output_section->vma
+                                    + input_section->output_offset
+                                    + offset);
+
+                 if (h->dynindx == -1
+                     || h->forced_local
+                     || bfd_link_executable (info))
+                   {
+                     info->callbacks->minfo (_("Local IFUNC function `%s' in %B\n"),
+                                             h->root.root.string,
+                                             h->root.u.def.section->owner);
+
+                     /* This symbol is resolved locally.  */
+                     outrel.r_info = ELF32_R_INFO (0, R_386_IRELATIVE);
+                     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),
+                                 contents + offset);
+                   }
+                 else
+                   outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
+
+                 /* 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
+                    do not want to fiddle with the addend.  Otherwise,
+                    we need to include the symbol value so that it
+                    becomes an addend for the dynamic reloc.  For an
+                    internal symbol, we have updated addend.  */
+                 continue;
+               }
+             /* FALLTHROUGH */
+           case R_386_PC32:
+           case R_386_PLT32:
+             goto do_relocation;
+
+           case R_386_GOTOFF:
+             relocation -= (gotplt->output_section->vma
+                            + gotplt->output_offset);
+             goto do_relocation;
+           }
+       }
+
+      resolved_to_zero = (eh != NULL
+                         && UNDEFINED_WEAK_RESOLVED_TO_ZERO (info,
+                                                             eh->has_got_reloc,
+                                                             eh));
+
+      switch (r_type)
+       {
+       case R_386_GOT32X:
+         /* Avoid optimizing _DYNAMIC since ld.so may use its
+            link-time address.  */
+         if (h == htab->elf.hdynamic)
+           goto r_386_got32;
+
+         if (bfd_link_pic (info))
+           {
+             /* It is OK to convert mov to lea and 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.   */
              unsigned int opcode, addend;
              addend = bfd_get_32 (input_bfd, contents + rel->r_offset);
              if (addend != 0)
              unsigned int opcode, addend;
              addend = bfd_get_32 (input_bfd, contents + rel->r_offset);
              if (addend != 0)
@@ -4008,7 +4359,8 @@ elf_i386_relocate_section (bfd *output_bfd,
                          + (h->got.offset & ~1) - offplt);
          else
            /* Use GOTPLT entry.  */
                          + (h->got.offset & ~1) - offplt);
          else
            /* Use GOTPLT entry.  */
-           relocation = (h->plt.offset / plt_entry_size - 1 + 3) * 4;
+           relocation = (h->plt.offset / plt_entry_size
+                         - htab->plt.has_plt0 + 3) * 4;
 
          if (!bfd_link_pic (info))
            {
 
          if (!bfd_link_pic (info))
            {
@@ -4030,6 +4382,7 @@ r_386_got32:
          if (htab->elf.sgot == NULL)
            abort ();
 
          if (htab->elf.sgot == NULL)
            abort ();
 
+         relative_reloc = FALSE;
          if (h != NULL)
            {
              bfd_boolean dyn;
          if (h != NULL)
            {
              bfd_boolean dyn;
@@ -4063,6 +4416,17 @@ r_386_got32:
                      bfd_put_32 (output_bfd, relocation,
                                  htab->elf.sgot->contents + off);
                      h->got.offset |= 1;
                      bfd_put_32 (output_bfd, relocation,
                                  htab->elf.sgot->contents + off);
                      h->got.offset |= 1;
+
+                     if (h->dynindx == -1
+                         && !h->forced_local
+                         && h->root.type != bfd_link_hash_undefweak
+                         && bfd_link_pic (info))
+                       {
+                         /* PR ld/21402: If this symbol isn't dynamic
+                            in PIC, generate R_386_RELATIVE here.  */
+                         eh->no_finish_dynamic_symbol = 1;
+                         relative_reloc = TRUE;
+                       }
                    }
                }
              else
                    }
                }
              else
@@ -4084,34 +4448,70 @@ r_386_got32:
                {
                  bfd_put_32 (output_bfd, relocation,
                              htab->elf.sgot->contents + off);
                {
                  bfd_put_32 (output_bfd, relocation,
                              htab->elf.sgot->contents + off);
+                 local_got_offsets[r_symndx] |= 1;
 
                  if (bfd_link_pic (info))
 
                  if (bfd_link_pic (info))
-                   {
-                     asection *s;
-                     Elf_Internal_Rela outrel;
-
-                     s = htab->elf.srelgot;
-                     if (s == NULL)
-                       abort ();
-
-                     outrel.r_offset = (htab->elf.sgot->output_section->vma
-                                        + htab->elf.sgot->output_offset
-                                        + off);
-                     outrel.r_info = ELF32_R_INFO (0, R_386_RELATIVE);
-                     elf_append_rel (output_bfd, s, &outrel);
-                   }
-
-                 local_got_offsets[r_symndx] |= 1;
+                   relative_reloc = TRUE;
                }
            }
 
                }
            }
 
+         if (relative_reloc)
+           {
+             asection *s;
+             Elf_Internal_Rela outrel;
+
+             s = htab->elf.srelgot;
+             if (s == NULL)
+               abort ();
+
+             outrel.r_offset = (htab->elf.sgot->output_section->vma
+                                + htab->elf.sgot->output_offset
+                                + off);
+             outrel.r_info = ELF32_R_INFO (0, R_386_RELATIVE);
+             elf_append_rel (output_bfd, s, &outrel);
+           }
+
          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 (rel->r_offset > 1
+             && (*(contents + rel->r_offset - 1) & 0xc7) == 0x5
+             && *(contents + rel->r_offset - 2) != 0x8d)
+           {
+             if (bfd_link_pic (info))
+               {
+                 /* For PIC, disallow R_386_GOT32 without a base
+                    register, except for "lea foo@GOT, %reg", since
+                    we don't know what the GOT base is.  */
+                 const char *name;
+
+disallow_got32:
+                 if (h == NULL || h->root.root.string == NULL)
+                   name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
+                                            NULL);
+                 else
+                   name = h->root.root.string;
+
+                 _bfd_error_handler
+                   /* xgettext:c-format */
+                   (_("%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:
@@ -4144,8 +4544,10 @@ r_386_got32:
                      break;
                    }
 
                      break;
                    }
 
-                 (*_bfd_error_handler)
-                   (_("%B: relocation R_386_GOTOFF against undefined %s `%s' can not be used when making a shared object"),
+                 _bfd_error_handler
+                   /* xgettext:c-format */
+                   (_("%B: relocation R_386_GOTOFF against undefined %s"
+                      " `%s' can not be used when making a shared object"),
                     input_bfd, v, h->root.root.string);
                  bfd_set_error (bfd_error_bad_value);
                  return FALSE;
                     input_bfd, v, h->root.root.string);
                  bfd_set_error (bfd_error_bad_value);
                  return FALSE;
@@ -4155,8 +4557,10 @@ r_386_got32:
                           || h->type == STT_OBJECT)
                       && ELF_ST_VISIBILITY (h->other) == STV_PROTECTED)
                {
                           || h->type == STT_OBJECT)
                       && ELF_ST_VISIBILITY (h->other) == STV_PROTECTED)
                {
-                 (*_bfd_error_handler)
-                   (_("%B: relocation R_386_GOTOFF against protected %s `%s' can not be used when making a shared object"),
+                 _bfd_error_handler
+                   /* xgettext:c-format */
+                   (_("%B: relocation R_386_GOTOFF against protected %s"
+                      " `%s' can not be used when making a shared object"),
                     input_bfd,
                     h->type == STT_FUNC ? "function" : "data",
                     h->root.root.string);
                     input_bfd,
                     h->type == STT_FUNC ? "function" : "data",
                     h->root.root.string);
@@ -4202,8 +4606,16 @@ r_386_got32:
 
          if (h->plt.offset != (bfd_vma) -1)
            {
 
          if (h->plt.offset != (bfd_vma) -1)
            {
-             resolved_plt = htab->elf.splt;
-             plt_offset = h->plt.offset;
+             if (htab->plt_second != NULL)
+               {
+                 resolved_plt = htab->plt_second;
+                 plt_offset = eh->plt_second.offset;
+               }
+             else
+               {
+                 resolved_plt = htab->elf.splt;
+                 plt_offset = h->plt.offset;
+               }
            }
          else
            {
            }
          else
            {
@@ -4342,7 +4754,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)
@@ -4354,30 +4766,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;
@@ -4684,21 +5105,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,
@@ -4717,7 +5150,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;
@@ -4803,18 +5236,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++;
@@ -4909,11 +5358,12 @@ r_386_got32:
          && _bfd_elf_section_offset (output_bfd, info, input_section,
                                      rel->r_offset) != (bfd_vma) -1)
        {
          && _bfd_elf_section_offset (output_bfd, info, input_section,
                                      rel->r_offset) != (bfd_vma) -1)
        {
-         (*_bfd_error_handler)
-           (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("%B(%A+%#Lx): unresolvable %s relocation against symbol `%s'"),
             input_bfd,
             input_section,
             input_bfd,
             input_section,
-            (long) rel->r_offset,
+            rel->r_offset,
             howto->name,
             h->root.root.string);
          return FALSE;
             howto->name,
             h->root.root.string);
          return FALSE;
@@ -4943,19 +5393,16 @@ 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
            {
          else
            {
-             (*_bfd_error_handler)
-               (_("%B(%A+0x%lx): reloc against `%s': error %d"),
+             _bfd_error_handler
+               /* xgettext:c-format */
+               (_("%B(%A+%#Lx): reloc against `%s': error %d"),
                 input_bfd, input_section,
                 input_bfd, input_section,
-                (long) rel->r_offset, name, (int) r);
+                rel->r_offset, name, (int) r);
              return FALSE;
            }
        }
              return FALSE;
            }
        }
@@ -5001,28 +5448,36 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
   const struct elf_i386_backend_data *abed;
   struct elf_i386_link_hash_entry *eh;
   bfd_boolean local_undefweak;
   const struct elf_i386_backend_data *abed;
   struct elf_i386_link_hash_entry *eh;
   bfd_boolean local_undefweak;
+  bfd_boolean use_plt_second;
 
   htab = elf_i386_hash_table (info);
   if (htab == NULL)
     return FALSE;
 
   abed = get_elf_i386_backend_data (output_bfd);
 
   htab = elf_i386_hash_table (info);
   if (htab == NULL)
     return FALSE;
 
   abed = get_elf_i386_backend_data (output_bfd);
-  plt_entry_size = GET_PLT_ENTRY_SIZE (output_bfd);
+  plt_entry_size = htab->plt.plt_entry_size;
+
+  /* Use the second PLT section only if there is .plt section.  */
+  use_plt_second = htab->elf.splt != NULL && htab->plt_second != NULL;
 
   eh = (struct elf_i386_link_hash_entry *) h;
 
   eh = (struct elf_i386_link_hash_entry *) h;
+  if (eh->no_finish_dynamic_symbol)
+    abort ();
 
   /* 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.  */
 
   /* 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);
+  local_undefweak = UNDEFINED_WEAK_RESOLVED_TO_ZERO (info,
+                                                    eh->has_got_reloc,
+                                                    eh);
 
   if (h->plt.offset != (bfd_vma) -1)
     {
 
   if (h->plt.offset != (bfd_vma) -1)
     {
-      bfd_vma plt_index;
+      bfd_vma plt_index, plt_offset;
       bfd_vma got_offset;
       Elf_Internal_Rela rel;
       bfd_byte *loc;
       bfd_vma got_offset;
       Elf_Internal_Rela rel;
       bfd_byte *loc;
-      asection *plt, *gotplt, *relplt;
+      asection *plt, *resolved_plt, *gotplt, *relplt;
 
       /* When building a static executable, use .iplt, .igot.plt and
         .rel.iplt sections for STT_GNU_IFUNC symbols.  */
 
       /* When building a static executable, use .iplt, .igot.plt and
         .rel.iplt sections for STT_GNU_IFUNC symbols.  */
@@ -5065,7 +5520,8 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
 
       if (plt == htab->elf.splt)
        {
 
       if (plt == htab->elf.splt)
        {
-         got_offset = h->plt.offset / plt_entry_size - 1;
+         got_offset = (h->plt.offset / plt_entry_size
+                       - htab->plt.has_plt0);
          got_offset = (got_offset + 3) * 4;
        }
       else
          got_offset = (got_offset + 3) * 4;
        }
       else
@@ -5074,19 +5530,40 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
          got_offset = got_offset * 4;
        }
 
          got_offset = got_offset * 4;
        }
 
-      /* Fill in the entry in the procedure linkage table.  */
+      /* Fill in the entry in the procedure linkage table and update
+        the first slot.  */
+      memcpy (plt->contents + h->plt.offset, htab->plt.plt_entry,
+             plt_entry_size);
+
+      if (use_plt_second)
+       {
+         const bfd_byte *plt_entry;
+         if (bfd_link_pic (info))
+           plt_entry = htab->non_lazy_plt->pic_plt_entry;
+         else
+           plt_entry = htab->non_lazy_plt->plt_entry;
+         memcpy (htab->plt_second->contents + eh->plt_second.offset,
+                 plt_entry, htab->non_lazy_plt->plt_entry_size);
+
+         resolved_plt = htab->plt_second;
+         plt_offset = eh->plt_second.offset;
+       }
+      else
+       {
+         resolved_plt = plt;
+         plt_offset = h->plt.offset;
+       }
+
       if (! bfd_link_pic (info))
        {
       if (! bfd_link_pic (info))
        {
-         memcpy (plt->contents + h->plt.offset, abed->plt->plt_entry,
-                 abed->plt->plt_entry_size);
          bfd_put_32 (output_bfd,
                      (gotplt->output_section->vma
                       + gotplt->output_offset
                       + got_offset),
          bfd_put_32 (output_bfd,
                      (gotplt->output_section->vma
                       + gotplt->output_offset
                       + got_offset),
-                     plt->contents + h->plt.offset
-                      + abed->plt->plt_got_offset);
+                     resolved_plt->contents + plt_offset
+                      + htab->plt.plt_got_offset);
 
 
-         if (abed->is_vxworks)
+         if (abed->os == is_vxworks)
            {
              int s, k, reloc_index;
 
            {
              int s, k, reloc_index;
 
@@ -5094,8 +5571,8 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
                 for this PLT entry.  */
 
              /* S: Current slot number (zero-based).  */
                 for this PLT entry.  */
 
              /* S: Current slot number (zero-based).  */
-             s = ((h->plt.offset - abed->plt->plt_entry_size)
-                   / abed->plt->plt_entry_size);
+             s = ((h->plt.offset - htab->plt.plt_entry_size)
+                   / htab->plt.plt_entry_size);
              /* K: Number of relocations for PLTResolve. */
              if (bfd_link_pic (info))
                k = PLTRESOLVE_RELOCS_SHLIB;
              /* K: Number of relocations for PLTResolve. */
              if (bfd_link_pic (info))
                k = PLTRESOLVE_RELOCS_SHLIB;
@@ -5107,8 +5584,8 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
              loc = (htab->srelplt2->contents + reloc_index
                     * sizeof (Elf32_External_Rel));
 
              loc = (htab->srelplt2->contents + reloc_index
                     * sizeof (Elf32_External_Rel));
 
-             rel.r_offset = (htab->elf.splt->output_section->vma
-                             + htab->elf.splt->output_offset
+             rel.r_offset = (plt->output_section->vma
+                             + plt->output_offset
                              + h->plt.offset + 2),
              rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_386_32);
              bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
                              + h->plt.offset + 2),
              rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_386_32);
              bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
@@ -5125,11 +5602,9 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
        }
       else
        {
        }
       else
        {
-         memcpy (plt->contents + h->plt.offset, abed->plt->pic_plt_entry,
-                 abed->plt->plt_entry_size);
          bfd_put_32 (output_bfd, got_offset,
          bfd_put_32 (output_bfd, got_offset,
-                     plt->contents + h->plt.offset
-                      + abed->plt->plt_got_offset);
+                     resolved_plt->contents + plt_offset
+                      + htab->plt.plt_got_offset);
        }
 
       /* Fill in the entry in the global offset table.  Leave the entry
        }
 
       /* Fill in the entry in the global offset table.  Leave the entry
@@ -5137,12 +5612,13 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
         against undefined weak symbol in PIE.  */
       if (!local_undefweak)
        {
         against undefined weak symbol in PIE.  */
       if (!local_undefweak)
        {
-         bfd_put_32 (output_bfd,
-                     (plt->output_section->vma
-                      + plt->output_offset
-                      + h->plt.offset
-                      + abed->plt->plt_lazy_offset),
-                     gotplt->contents + got_offset);
+         if (htab->plt.has_plt0)
+           bfd_put_32 (output_bfd,
+                       (plt->output_section->vma
+                        + plt->output_offset
+                        + h->plt.offset
+                        + htab->lazy_plt->plt_lazy_offset),
+                       gotplt->contents + got_offset);
 
          /* Fill in the entry in the .rel.plt section.  */
          rel.r_offset = (gotplt->output_section->vma
 
          /* Fill in the entry in the .rel.plt section.  */
          rel.r_offset = (gotplt->output_section->vma
@@ -5154,6 +5630,10 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
                  && h->def_regular
                  && h->type == STT_GNU_IFUNC))
            {
                  && h->def_regular
                  && h->type == STT_GNU_IFUNC))
            {
+             info->callbacks->minfo (_("Local IFUNC function `%s' in %B\n"),
+                                     h->root.root.string,
+                                     h->root.u.def.section->owner);
+
              /* 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.  */
              /* 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.  */
@@ -5175,17 +5655,19 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
          loc = relplt->contents + plt_index * sizeof (Elf32_External_Rel);
          bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
 
          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)
+         /* Don't fill the second and third slots in PLT entry for
+            static executables nor without PLT0.  */
+         if (plt == htab->elf.splt && htab->plt.has_plt0)
            {
              bfd_put_32 (output_bfd,
                          plt_index * sizeof (Elf32_External_Rel),
                          plt->contents + h->plt.offset
            {
              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);
+                         + htab->lazy_plt->plt_reloc_offset);
+             bfd_put_32 (output_bfd,
+                         - (h->plt.offset
+                            + htab->lazy_plt->plt_plt_offset + 4),
+                         (plt->contents + h->plt.offset
+                          + htab->lazy_plt->plt_plt_offset));
            }
        }
     }
            }
        }
     }
@@ -5195,9 +5677,6 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
       asection *plt, *got, *gotplt;
       const bfd_byte *got_plt_entry;
 
       asection *plt, *got, *gotplt;
       const bfd_byte *got_plt_entry;
 
-      /* Offset of displacement of the indirect jump.  */
-      bfd_vma plt_got_offset = 2;
-
       /* Set the entry in the GOT procedure linkage table.  */
       plt = htab->plt_got;
       got = htab->elf.sgot;
       /* Set the entry in the GOT procedure linkage table.  */
       plt = htab->plt_got;
       got = htab->elf.sgot;
@@ -5213,12 +5692,12 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
       /* Fill in the entry in the GOT procedure linkage table.  */
       if (! bfd_link_pic (info))
        {
       /* Fill in the entry in the GOT procedure linkage table.  */
       if (! bfd_link_pic (info))
        {
-         got_plt_entry = elf_i386_got_plt_entry;
+         got_plt_entry = htab->non_lazy_plt->plt_entry;
          got_offset += got->output_section->vma + got->output_offset;
        }
       else
        {
          got_offset += got->output_section->vma + got->output_offset;
        }
       else
        {
-         got_plt_entry = elf_i386_pic_got_plt_entry;
+         got_plt_entry = htab->non_lazy_plt->pic_plt_entry;
          got_offset += (got->output_section->vma
                         + got->output_offset
                         - gotplt->output_section->vma
          got_offset += (got->output_section->vma
                         + got->output_offset
                         - gotplt->output_section->vma
@@ -5227,9 +5706,10 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
 
       plt_offset = eh->plt_got.offset;
       memcpy (plt->contents + plt_offset, got_plt_entry,
 
       plt_offset = eh->plt_got.offset;
       memcpy (plt->contents + plt_offset, got_plt_entry,
-             sizeof (elf_i386_got_plt_entry));
+             htab->non_lazy_plt->plt_entry_size);
       bfd_put_32 (output_bfd, got_offset,
       bfd_put_32 (output_bfd, got_offset,
-                 plt->contents + plt_offset + plt_got_offset);
+                 (plt->contents + plt_offset
+                  + htab->non_lazy_plt->plt_got_offset));
     }
 
   if (!local_undefweak
     }
 
   if (!local_undefweak
@@ -5258,6 +5738,7 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
       && !local_undefweak)
     {
       Elf_Internal_Rela rel;
       && !local_undefweak)
     {
       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.  */
@@ -5277,7 +5758,32 @@ 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))
+               {
+                 info->callbacks->minfo (_("Local IFUNC function `%s' in %B\n"),
+                                         h->root.root.string,
+                                         h->root.u.def.section->owner);
+
+                 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;
@@ -5285,6 +5791,7 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
          else
            {
              asection *plt;
          else
            {
              asection *plt;
+             bfd_vma plt_offset;
 
              if (!h->pointer_equality_needed)
                abort ();
 
              if (!h->pointer_equality_needed)
                abort ();
@@ -5292,10 +5799,19 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
              /* For non-shared object, we can't use .got.plt, which
                 contains the real function addres if we need pointer
                 equality.  We load the GOT entry with the PLT entry.  */
              /* For non-shared object, we can't use .got.plt, which
                 contains the real function addres if we need pointer
                 equality.  We load the GOT entry with the PLT entry.  */
-             plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt;
+             if (htab->plt_second != NULL)
+               {
+                 plt = htab->plt_second;
+                 plt_offset = eh->plt_second.offset;
+               }
+             else
+               {
+                 plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt;
+                 plt_offset = h->plt.offset;
+               }
              bfd_put_32 (output_bfd,
                          (plt->output_section->vma
              bfd_put_32 (output_bfd,
                          (plt->output_section->vma
-                          + plt->output_offset + h->plt.offset),
+                          + plt->output_offset + plt_offset),
                          htab->elf.sgot->contents + h->got.offset);
              return TRUE;
            }
                          htab->elf.sgot->contents + h->got.offset);
              return TRUE;
            }
@@ -5315,26 +5831,32 @@ 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)
     {
       Elf_Internal_Rela rel;
     }
 
   if (h->needs_copy)
     {
       Elf_Internal_Rela rel;
+      asection *s;
 
       /* This symbol needs a copy reloc.  Set it up.  */
 
       if (h->dynindx == -1
          || (h->root.type != bfd_link_hash_defined
              && h->root.type != bfd_link_hash_defweak)
 
       /* This symbol needs a copy reloc.  Set it up.  */
 
       if (h->dynindx == -1
          || (h->root.type != bfd_link_hash_defined
              && h->root.type != bfd_link_hash_defweak)
-         || htab->srelbss == NULL)
+         || htab->elf.srelbss == NULL
+         || htab->elf.sreldynrelro == NULL)
        abort ();
 
       rel.r_offset = (h->root.u.def.value
                      + h->root.u.def.section->output_section->vma
                      + h->root.u.def.section->output_offset);
       rel.r_info = ELF32_R_INFO (h->dynindx, R_386_COPY);
        abort ();
 
       rel.r_offset = (h->root.u.def.value
                      + h->root.u.def.section->output_section->vma
                      + h->root.u.def.section->output_offset);
       rel.r_info = ELF32_R_INFO (h->dynindx, R_386_COPY);
-      elf_append_rel (output_bfd, htab->srelbss, &rel);
+      if (h->root.u.def.section == htab->elf.sdynrelro)
+       s = htab->elf.sreldynrelro;
+      else
+       s = htab->elf.srelbss;
+      elf_append_rel (output_bfd, s, &rel);
     }
 
   return TRUE;
     }
 
   return TRUE;
@@ -5392,19 +5914,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:
@@ -5454,7 +5981,7 @@ elf_i386_finish_dynamic_sections (bfd *output_bfd,
          switch (dyn.d_tag)
            {
            default:
          switch (dyn.d_tag)
            {
            default:
-             if (abed->is_vxworks
+             if (abed->os == is_vxworks
                   && elf_vxworks_finish_dynamic_entry (output_bfd, &dyn))
                break;
              continue;
                   && elf_vxworks_finish_dynamic_entry (output_bfd, &dyn))
                break;
              continue;
@@ -5473,145 +6000,120 @@ elf_i386_finish_dynamic_sections (bfd *output_bfd,
              s = htab->elf.srelplt;
              dyn.d_un.d_val = s->size;
              break;
              s = htab->elf.srelplt;
              dyn.d_un.d_val = s->size;
              break;
-
-           case DT_RELSZ:
-             /* My reading of the SVR4 ABI indicates that the
-                procedure linkage table relocs (DT_JMPREL) should be
-                included in the overall relocs (DT_REL).  This is
-                what Solaris does.  However, UnixWare can not handle
-                that case.  Therefore, we override the DT_RELSZ entry
-                here to make it not include the JMPREL relocs.  */
-             s = htab->elf.srelplt;
-             if (s == NULL)
-               continue;
-             dyn.d_un.d_val -= s->size;
-             break;
-
-           case DT_REL:
-             /* We may not be using the standard ELF linker script.
-                If .rel.plt is the first .rel section, we adjust
-                DT_REL to not include it.  */
-             s = htab->elf.srelplt;
-             if (s == NULL)
-               continue;
-             if (dyn.d_un.d_ptr != s->output_section->vma + s->output_offset)
-               continue;
-             dyn.d_un.d_ptr += s->size;
-             break;
            }
 
          bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
        }
 
            }
 
          bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
        }
 
-      /* Fill in the first entry in the procedure linkage table.  */
       if (htab->elf.splt && htab->elf.splt->size > 0)
        {
       if (htab->elf.splt && htab->elf.splt->size > 0)
        {
-         if (bfd_link_pic (info))
-           {
-             memcpy (htab->elf.splt->contents, abed->plt->pic_plt0_entry,
-                     abed->plt->plt0_entry_size);
-             memset (htab->elf.splt->contents + abed->plt->plt0_entry_size,
-                     abed->plt0_pad_byte,
-                     abed->plt->plt_entry_size - abed->plt->plt0_entry_size);
-           }
-         else
-           {
-             memcpy (htab->elf.splt->contents, abed->plt->plt0_entry,
-                     abed->plt->plt0_entry_size);
-             memset (htab->elf.splt->contents + abed->plt->plt0_entry_size,
-                     abed->plt0_pad_byte,
-                     abed->plt->plt_entry_size - abed->plt->plt0_entry_size);
-             bfd_put_32 (output_bfd,
-                         (htab->elf.sgotplt->output_section->vma
-                          + htab->elf.sgotplt->output_offset
-                          + 4),
-                         htab->elf.splt->contents
-                          + abed->plt->plt0_got1_offset);
-             bfd_put_32 (output_bfd,
-                         (htab->elf.sgotplt->output_section->vma
-                          + htab->elf.sgotplt->output_offset
-                          + 8),
-                         htab->elf.splt->contents
-                          + abed->plt->plt0_got2_offset);
-
-             if (abed->is_vxworks)
-               {
-                 Elf_Internal_Rela rel;
-
-                 /* Generate a relocation for _GLOBAL_OFFSET_TABLE_ + 4.
-                    On IA32 we use REL relocations so the addend goes in
-                    the PLT directly.  */
-                 rel.r_offset = (htab->elf.splt->output_section->vma
-                                 + htab->elf.splt->output_offset
-                                 + abed->plt->plt0_got1_offset);
-                 rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_386_32);
-                 bfd_elf32_swap_reloc_out (output_bfd, &rel,
-                                           htab->srelplt2->contents);
-                 /* Generate a relocation for _GLOBAL_OFFSET_TABLE_ + 8.  */
-                 rel.r_offset = (htab->elf.splt->output_section->vma
-                                 + htab->elf.splt->output_offset
-                                 + abed->plt->plt0_got2_offset);
-                 rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_386_32);
-                 bfd_elf32_swap_reloc_out (output_bfd, &rel,
-                                           htab->srelplt2->contents +
-                                           sizeof (Elf32_External_Rel));
-               }
-           }
-
          /* UnixWare sets the entsize of .plt to 4, although that doesn't
             really seem like the right value.  */
          elf_section_data (htab->elf.splt->output_section)
            ->this_hdr.sh_entsize = 4;
 
          /* UnixWare sets the entsize of .plt to 4, although that doesn't
             really seem like the right value.  */
          elf_section_data (htab->elf.splt->output_section)
            ->this_hdr.sh_entsize = 4;
 
-         /* Correct the .rel.plt.unloaded relocations.  */
-         if (abed->is_vxworks && !bfd_link_pic (info))
+         if (htab->plt.has_plt0)
            {
            {
-             int num_plts = (htab->elf.splt->size
-                              / abed->plt->plt_entry_size) - 1;
-             unsigned char *p;
+             /* Fill in the special first entry in the procedure linkage
+                table.  */
+             memcpy (htab->elf.splt->contents, htab->plt.plt0_entry,
+                     htab->lazy_plt->plt0_entry_size);
+             memset (htab->elf.splt->contents + htab->lazy_plt->plt0_entry_size,
+                     abed->plt0_pad_byte,
+                     htab->plt.plt_entry_size - htab->lazy_plt->plt0_entry_size);
+             if (!bfd_link_pic (info))
+               {
+                 bfd_put_32 (output_bfd,
+                             (htab->elf.sgotplt->output_section->vma
+                              + htab->elf.sgotplt->output_offset
+                              + 4),
+                             htab->elf.splt->contents
+                             + htab->lazy_plt->plt0_got1_offset);
+                 bfd_put_32 (output_bfd,
+                             (htab->elf.sgotplt->output_section->vma
+                              + htab->elf.sgotplt->output_offset
+                              + 8),
+                             htab->elf.splt->contents
+                             + htab->lazy_plt->plt0_got2_offset);
 
 
-             p = htab->srelplt2->contents;
-             if (bfd_link_pic (info))
-               p += PLTRESOLVE_RELOCS_SHLIB * sizeof (Elf32_External_Rel);
-             else
-               p += PLTRESOLVE_RELOCS * sizeof (Elf32_External_Rel);
+                 if (abed->os == is_vxworks)
+                   {
+                     Elf_Internal_Rela rel;
+                     int num_plts = (htab->elf.splt->size
+                                     / htab->plt.plt_entry_size) - 1;
+                     unsigned char *p;
+
+                     /* Generate a relocation for _GLOBAL_OFFSET_TABLE_
+                        + 4.  On IA32 we use REL relocations so the
+                        addend goes in the PLT directly.  */
+                     rel.r_offset = (htab->elf.splt->output_section->vma
+                                     + htab->elf.splt->output_offset
+                                     + htab->lazy_plt->plt0_got1_offset);
+                     rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx,
+                                                R_386_32);
+                     bfd_elf32_swap_reloc_out (output_bfd, &rel,
+                                               htab->srelplt2->contents);
+                     /* Generate a relocation for _GLOBAL_OFFSET_TABLE_
+                        + 8.  */
+                     rel.r_offset = (htab->elf.splt->output_section->vma
+                                     + htab->elf.splt->output_offset
+                                     + htab->lazy_plt->plt0_got2_offset);
+                     rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx,
+                                                R_386_32);
+                     bfd_elf32_swap_reloc_out (output_bfd, &rel,
+                                               htab->srelplt2->contents +
+                                               sizeof (Elf32_External_Rel));
+                     /* Correct the .rel.plt.unloaded relocations.  */
+                     p = htab->srelplt2->contents;
+                     if (bfd_link_pic (info))
+                       p += PLTRESOLVE_RELOCS_SHLIB * sizeof (Elf32_External_Rel);
+                     else
+                       p += PLTRESOLVE_RELOCS * sizeof (Elf32_External_Rel);
 
 
-             for (; num_plts; num_plts--)
-               {
-                 Elf_Internal_Rela rel;
-                 bfd_elf32_swap_reloc_in (output_bfd, p, &rel);
-                 rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_386_32);
-                 bfd_elf32_swap_reloc_out (output_bfd, &rel, p);
-                 p += sizeof (Elf32_External_Rel);
-
-                 bfd_elf32_swap_reloc_in (output_bfd, p, &rel);
-                 rel.r_info = ELF32_R_INFO (htab->elf.hplt->indx, R_386_32);
-                 bfd_elf32_swap_reloc_out (output_bfd, &rel, p);
-                 p += sizeof (Elf32_External_Rel);
+                     for (; num_plts; num_plts--)
+                       {
+                         bfd_elf32_swap_reloc_in (output_bfd, p, &rel);
+                         rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx,
+                                                    R_386_32);
+                         bfd_elf32_swap_reloc_out (output_bfd, &rel, p);
+                         p += sizeof (Elf32_External_Rel);
+
+                         bfd_elf32_swap_reloc_in (output_bfd, p, &rel);
+                         rel.r_info = ELF32_R_INFO (htab->elf.hplt->indx,
+                                                    R_386_32);
+                         bfd_elf32_swap_reloc_out (output_bfd, &rel, p);
+                         p += sizeof (Elf32_External_Rel);
+                       }
+                   }
                }
            }
        }
                }
            }
        }
+
+      if (htab->plt_got != NULL && htab->plt_got->size > 0)
+       elf_section_data (htab->plt_got->output_section)
+         ->this_hdr.sh_entsize = htab->non_lazy_plt->plt_entry_size;
+
+      if (htab->plt_second != NULL && htab->plt_second->size > 0)
+       elf_section_data (htab->plt_second->output_section)
+         ->this_hdr.sh_entsize = htab->non_lazy_plt->plt_entry_size;
     }
 
     }
 
-  if (htab->elf.sgotplt)
+  /* Fill in the first three entries in the global offset table.  */
+  if (htab->elf.sgotplt && htab->elf.sgotplt->size > 0)
     {
       if (bfd_is_abs_section (htab->elf.sgotplt->output_section))
        {
     {
       if (bfd_is_abs_section (htab->elf.sgotplt->output_section))
        {
-         (*_bfd_error_handler)
+         _bfd_error_handler
            (_("discarded output section: `%A'"), htab->elf.sgotplt);
          return FALSE;
        }
 
            (_("discarded output section: `%A'"), htab->elf.sgotplt);
          return FALSE;
        }
 
-      /* Fill in the first three entries in the global offset table.  */
-      if (htab->elf.sgotplt->size > 0)
-       {
-         bfd_put_32 (output_bfd,
-                     (sdyn == NULL ? 0
-                      : sdyn->output_section->vma + sdyn->output_offset),
-                     htab->elf.sgotplt->contents);
-         bfd_put_32 (output_bfd, 0, htab->elf.sgotplt->contents + 4);
-         bfd_put_32 (output_bfd, 0, htab->elf.sgotplt->contents + 8);
-       }
+      bfd_put_32 (output_bfd,
+                 (sdyn == NULL ? 0
+                  : sdyn->output_section->vma + sdyn->output_offset),
+                 htab->elf.sgotplt->contents);
+      bfd_put_32 (output_bfd, 0, htab->elf.sgotplt->contents + 4);
+      bfd_put_32 (output_bfd, 0, htab->elf.sgotplt->contents + 8);
 
       elf_section_data (htab->elf.sgotplt->output_section)->this_hdr.sh_entsize = 4;
     }
 
       elf_section_data (htab->elf.sgotplt->output_section)->this_hdr.sh_entsize = 4;
     }
@@ -5644,14 +6146,65 @@ elf_i386_finish_dynamic_sections (bfd *output_bfd,
        }
     }
 
        }
     }
 
+  /* Adjust .eh_frame for .plt.got section.  */
+  if (htab->plt_got_eh_frame != NULL
+      && htab->plt_got_eh_frame->contents != NULL)
+    {
+      if (htab->plt_got != NULL
+         && htab->plt_got->size != 0
+         && (htab->plt_got->flags & SEC_EXCLUDE) == 0
+         && htab->plt_got->output_section != NULL
+         && htab->plt_got_eh_frame->output_section != NULL)
+       {
+         bfd_vma plt_start = htab->plt_got->output_section->vma;
+         bfd_vma eh_frame_start = htab->plt_got_eh_frame->output_section->vma
+                                  + htab->plt_got_eh_frame->output_offset
+                                  + PLT_FDE_START_OFFSET;
+         bfd_put_signed_32 (dynobj, plt_start - eh_frame_start,
+                            htab->plt_got_eh_frame->contents
+                            + PLT_FDE_START_OFFSET);
+       }
+      if (htab->plt_got_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME)
+       {
+         if (! _bfd_elf_write_section_eh_frame (output_bfd, info,
+                                                htab->plt_got_eh_frame,
+                                                htab->plt_got_eh_frame->contents))
+           return FALSE;
+       }
+    }
+
+  /* Adjust .eh_frame for the second PLT section.  */
+  if (htab->plt_second_eh_frame != NULL
+      && htab->plt_second_eh_frame->contents != NULL)
+    {
+      if (htab->plt_second != NULL
+         && htab->plt_second->size != 0
+         && (htab->plt_second->flags & SEC_EXCLUDE) == 0
+         && htab->plt_second->output_section != NULL
+         && htab->plt_second_eh_frame->output_section != NULL)
+       {
+         bfd_vma plt_start = htab->plt_second->output_section->vma;
+         bfd_vma eh_frame_start
+           = (htab->plt_second_eh_frame->output_section->vma
+              + htab->plt_second_eh_frame->output_offset
+              + PLT_FDE_START_OFFSET);
+         bfd_put_signed_32 (dynobj, plt_start - eh_frame_start,
+                            htab->plt_second_eh_frame->contents
+                            + PLT_FDE_START_OFFSET);
+       }
+      if (htab->plt_second_eh_frame->sec_info_type
+         == SEC_INFO_TYPE_EH_FRAME)
+       {
+         if (! _bfd_elf_write_section_eh_frame (output_bfd, info,
+                                                htab->plt_second_eh_frame,
+                                                htab->plt_second_eh_frame->contents))
+           return FALSE;
+       }
+    }
+
   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 and GOT entries for local STT_GNU_IFUNC symbols.  */
-  htab_traverse (htab->loc_hash_table,
-                elf_i386_finish_local_dynamic_symbol,
-                info);
-
   /* Fill PLT entries for undefined weak symbols in PIE.  */
   if (bfd_link_pie (info))
     bfd_hash_traverse (&info->hash->table,
   /* Fill PLT entries for undefined weak symbols in PIE.  */
   if (bfd_link_pie (info))
     bfd_hash_traverse (&info->hash->table,
@@ -5661,96 +6214,432 @@ elf_i386_finish_dynamic_sections (bfd *output_bfd,
   return TRUE;
 }
 
   return TRUE;
 }
 
-/* Return an array of PLT entry symbol values.  */
+/* 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_vma *
-elf_i386_get_plt_sym_val (bfd *abfd, asymbol **dynsyms, asection *plt,
-                         asection *relplt)
+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)
 {
 {
-  bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
-  arelent *p;
-  long count, i;
-  bfd_vma *plt_sym_val;
-  bfd_vma plt_offset;
-  bfd_byte *plt_contents;
-  const struct elf_i386_backend_data *bed
-    = get_elf_i386_backend_data (abfd);
-  Elf_Internal_Shdr *hdr;
-
-  /* Get the .plt section contents.  */
-  plt_contents = (bfd_byte *) bfd_malloc (plt->size);
-  if (plt_contents == NULL)
-    return NULL;
-  if (!bfd_get_section_contents (abfd, (asection *) plt,
-                                plt_contents, 0, plt->size))
-    {
-bad_return:
-      free (plt_contents);
-      return NULL;
-    }
+  struct elf_i386_link_hash_table *htab = elf_i386_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
 
 
-  slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
-  if (! (*slurp_relocs) (abfd, relplt, dynsyms, TRUE))
-    goto bad_return;
+  /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols.  */
+  htab_traverse (htab->loc_hash_table,
+                elf_i386_finish_local_dynamic_symbol,
+                info);
+
+  return TRUE;
+}
 
 
-  hdr = &elf_section_data (relplt)->this_hdr;
-  count = relplt->size / hdr->sh_entsize;
+/* Sort relocs into address order.  */
 
 
-  plt_sym_val = (bfd_vma *) bfd_malloc (sizeof (bfd_vma) * count);
-  if (plt_sym_val == NULL)
-    goto bad_return;
+static int
+compare_relocs (const void *ap, const void *bp)
+{
+  const arelent *a = * (const arelent **) ap;
+  const arelent *b = * (const arelent **) bp;
+
+  if (a->address > b->address)
+    return 1;
+  else if (a->address < b->address)
+    return -1;
+  else
+    return 0;
+}
+
+enum elf_i386_plt_type
+{
+  plt_non_lazy = 0,
+  plt_lazy = 1 << 0,
+  plt_pic = 1 << 1,
+  plt_second = 1 << 2,
+  plt_unknown = -1
+};
+
+struct elf_i386_plt
+{
+  const char *name;
+  asection *sec;
+  bfd_byte *contents;
+  enum elf_i386_plt_type type;
+  unsigned int plt_got_offset;
+  unsigned int plt_entry_size;
+  long count;
+};
 
 
-  for (i = 0; i < count; i++)
-    plt_sym_val[i] = -1;
+/* Forward declaration.  */
+static const struct elf_i386_lazy_plt_layout elf_i386_nacl_plt;
 
 
-  plt_offset = bed->plt->plt_entry_size;
-  p = relplt->relocation;
-  for (i = 0; i < count; i++, p++)
+/* Similar to _bfd_elf_get_synthetic_symtab.  Support PLTs with all
+   dynamic relocations.   */
+
+static long
+elf_i386_get_synthetic_symtab (bfd *abfd,
+                              long symcount ATTRIBUTE_UNUSED,
+                              asymbol **syms ATTRIBUTE_UNUSED,
+                              long dynsymcount,
+                              asymbol **dynsyms,
+                              asymbol **ret)
+{
+  long size, count, i, n, len;
+  int j;
+  unsigned int plt_got_offset, plt_entry_size;
+  asymbol *s;
+  bfd_byte *plt_contents;
+  long dynrelcount, relsize;
+  arelent **dynrelbuf, *p;
+  const struct elf_i386_lazy_plt_layout *lazy_plt;
+  const struct elf_i386_non_lazy_plt_layout *non_lazy_plt;
+  const struct elf_i386_lazy_plt_layout *lazy_ibt_plt;
+  const struct elf_i386_non_lazy_plt_layout *non_lazy_ibt_plt;
+  asection *plt;
+  bfd_vma got_addr;
+  char *names;
+  enum elf_i386_plt_type plt_type;
+  struct elf_i386_plt plts[] =
     {
     {
-      long reloc_index;
+      { ".plt", NULL, NULL, plt_unknown, 0, 0, 0 },
+      { ".plt.got", NULL, NULL, plt_non_lazy, 0, 0, 0 },
+      { ".plt.sec", NULL, NULL, plt_second, 0, 0, 0 },
+      { NULL, NULL, NULL, plt_non_lazy, 0, 0, 0 }
+    };
+
+  *ret = NULL;
+
+  if ((abfd->flags & (DYNAMIC | EXEC_P)) == 0)
+    return 0;
+
+  if (dynsymcount <= 0)
+    return 0;
+
+  relsize = bfd_get_dynamic_reloc_upper_bound (abfd);
+  if (relsize <= 0)
+    return -1;
+
+  dynrelbuf = (arelent **) bfd_malloc (relsize);
+  if (dynrelbuf == NULL)
+    return -1;
+
+  dynrelcount = bfd_canonicalize_dynamic_reloc (abfd, dynrelbuf,
+                                               dynsyms);
+
+  /* Sort the relocs by address.  */
+  qsort (dynrelbuf, dynrelcount, sizeof (arelent *), compare_relocs);
 
 
-      /* Skip unknown relocation.  PR 17512: file: bc9d6cf5.  */
-      if (p->howto == NULL)
+  non_lazy_plt = NULL;
+  /* Silence GCC 6.  */
+  lazy_plt = NULL;
+  non_lazy_ibt_plt = NULL;
+  lazy_ibt_plt = NULL;
+  switch (get_elf_i386_backend_data (abfd)->os)
+    {
+    case is_normal:
+      non_lazy_plt = &elf_i386_non_lazy_plt;
+      lazy_ibt_plt = &elf_i386_lazy_ibt_plt;
+      non_lazy_ibt_plt = &elf_i386_non_lazy_ibt_plt;
+      /* Fall through */
+    case is_vxworks:
+      lazy_plt = &elf_i386_lazy_plt;
+      break;
+    case is_nacl:
+      lazy_plt = &elf_i386_nacl_plt;
+      break;
+    }
+
+  got_addr = 0;
+
+  count = 0;
+  for (j = 0; plts[j].name != NULL; j++)
+    {
+      plt = bfd_get_section_by_name (abfd, plts[j].name);
+      if (plt == NULL)
        continue;
 
        continue;
 
-      if (p->howto->type != R_386_JUMP_SLOT
-         && p->howto->type != R_386_IRELATIVE)
+      /* Get the PLT section contents.  */
+      plt_contents = (bfd_byte *) bfd_malloc (plt->size);
+      if (plt_contents == NULL)
+       break;
+      if (!bfd_get_section_contents (abfd, (asection *) plt,
+                                    plt_contents, 0, plt->size))
+       {
+         free (plt_contents);
+         break;
+       }
+
+      /* Check what kind of PLT it is.  */
+      plt_type = plt_unknown;
+      if (plts[j].type == plt_unknown)
+       {
+         /* Match lazy PLT first.  */
+         if (memcmp (plt_contents, lazy_plt->plt0_entry,
+                     lazy_plt->plt0_got1_offset) == 0)
+           {
+             /* The fist entry in the lazy IBT PLT is the same as the
+                normal lazy PLT.  */
+             if (lazy_ibt_plt != NULL
+                 && (memcmp (plt_contents + lazy_ibt_plt->plt_entry_size,
+                             lazy_ibt_plt->plt_entry,
+                             lazy_ibt_plt->plt_got_offset) == 0))
+               plt_type = plt_lazy | plt_second;
+             else
+               plt_type = plt_lazy;
+           }
+         else if (memcmp (plt_contents, lazy_plt->pic_plt0_entry,
+                          lazy_plt->plt0_got1_offset) == 0)
+           {
+             /* The fist entry in the PIC lazy IBT PLT is the same as
+                the normal PIC lazy PLT.  */
+             if (lazy_ibt_plt != NULL
+                 && (memcmp (plt_contents + lazy_ibt_plt->plt_entry_size,
+                             lazy_ibt_plt->pic_plt_entry,
+                             lazy_ibt_plt->plt_got_offset) == 0))
+               plt_type = plt_lazy | plt_pic | plt_second;
+             else
+               plt_type = plt_lazy | plt_pic;
+           }
+       }
+
+      if (non_lazy_plt != NULL
+         && (plt_type == plt_unknown || plt_type == plt_non_lazy))
+       {
+         /* Match non-lazy PLT.  */
+         if (memcmp (plt_contents, non_lazy_plt->plt_entry,
+                     non_lazy_plt->plt_got_offset) == 0)
+           plt_type = plt_non_lazy;
+         else if (memcmp (plt_contents, non_lazy_plt->pic_plt_entry,
+                          non_lazy_plt->plt_got_offset) == 0)
+           plt_type = plt_pic;
+       }
+
+      if ((non_lazy_ibt_plt != NULL)
+         && (plt_type == plt_unknown || plt_type == plt_second))
+       {
+         if (memcmp (plt_contents,
+                     non_lazy_ibt_plt->plt_entry,
+                     non_lazy_ibt_plt->plt_got_offset) == 0)
+           {
+             /* Match IBT PLT.  */
+             plt_type = plt_second;
+             non_lazy_plt = non_lazy_ibt_plt;
+           }
+         else if (memcmp (plt_contents,
+                          non_lazy_ibt_plt->pic_plt_entry,
+                          non_lazy_ibt_plt->plt_got_offset) == 0)
+           {
+             /* Match PIC IBT PLT.  */
+             plt_type = plt_second | plt_pic;
+             non_lazy_plt = non_lazy_ibt_plt;
+           }
+       }
+
+      if (plt_type == plt_unknown)
        continue;
 
        continue;
 
-      reloc_index = H_GET_32 (abfd, (plt_contents + plt_offset
-                                    + bed->plt->plt_reloc_offset));
-      reloc_index /= sizeof (Elf32_External_Rel);
-      if (reloc_index < count)
-       plt_sym_val[reloc_index] = plt->vma + plt_offset;
+      plts[j].sec = plt;
+      plts[j].type = plt_type;
 
 
-      plt_offset += bed->plt->plt_entry_size;
+      if ((plt_type & plt_lazy))
+       {
+         plts[j].plt_got_offset = lazy_plt->plt_got_offset;
+         plts[j].plt_entry_size = lazy_plt->plt_entry_size;
+         /* Skip PLT0 in lazy PLT.  */
+         i = 1;
+       }
+      else
+       {
+         plts[j].plt_got_offset = non_lazy_plt->plt_got_offset;
+         plts[j].plt_entry_size = non_lazy_plt->plt_entry_size;
+         i = 0;
+       }
 
 
-      /* PR binutils/18437: Skip extra relocations in the .rel.plt
-        section.  */
-      if (plt_offset >= plt->size)
-       break;
+      /* Skip lazy PLT when the second PLT is used.  */
+      if ((plt_type & (plt_lazy | plt_second))
+         == (plt_lazy | plt_second))
+       plts[j].count = 0;
+      else
+       {
+         n = plt->size / plts[j].plt_entry_size;
+         plts[j].count = n;
+         count += n - i;
+       }
+
+      plts[j].contents = plt_contents;
+
+      /* The _GLOBAL_OFFSET_TABLE_ address is needed.  */
+      if ((plt_type & plt_pic))
+       got_addr = (bfd_vma) -1;
     }
 
     }
 
-  free (plt_contents);
+  size = count * sizeof (asymbol);
 
 
-  return plt_sym_val;
-}
+  /* Allocate space for @plt suffixes.  */
+  n = 0;
+  for (i = 0; i < dynrelcount; i++)
+    {
+      p = dynrelbuf[i];
+      size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
+      if (p->addend != 0)
+       size += sizeof ("+0x") - 1 + 8;
+    }
 
 
-/* Similar to _bfd_elf_get_synthetic_symtab.  */
+  s = *ret = (asymbol *) bfd_zmalloc (size);
+  if (s == NULL)
+    {
+bad_return:
+      for (j = 0; plts[j].name != NULL; j++)
+       if (plts[j].contents != NULL)
+         free (plts[j].contents);
+      free (dynrelbuf);
+      return -1;
+    }
 
 
-static long
-elf_i386_get_synthetic_symtab (bfd *abfd,
-                              long symcount,
-                              asymbol **syms,
-                              long dynsymcount,
-                              asymbol **dynsyms,
-                              asymbol **ret)
-{
-  asection *plt = bfd_get_section_by_name (abfd, ".plt");
-  return _bfd_elf_ifunc_get_synthetic_symtab (abfd, symcount, syms,
-                                             dynsymcount, dynsyms, ret,
-                                             plt,
-                                             elf_i386_get_plt_sym_val);
+  if (got_addr)
+    {
+      /* Check .got.plt and then .got to get the _GLOBAL_OFFSET_TABLE_
+        address.  */
+      asection *sec = bfd_get_section_by_name (abfd, ".got.plt");
+      if (sec != NULL)
+       got_addr = sec->vma;
+      else
+       {
+         sec = bfd_get_section_by_name (abfd, ".got");
+         if (sec != NULL)
+           got_addr = sec->vma;
+       }
+
+      if (got_addr == (bfd_vma) -1)
+       goto bad_return;
+    }
+
+  /* Check for each PLT section.  */
+  names = (char *) (s + count);
+  size = 0;
+  n = 0;
+  for (j = 0; plts[j].name != NULL; j++)
+    if ((plt_contents = plts[j].contents) != NULL)
+      {
+       long k;
+       bfd_vma offset;
+
+       plt_got_offset = plts[j].plt_got_offset;
+       plt_entry_size = plts[j].plt_entry_size;
+
+       plt = plts[j].sec;
+
+       if ((plts[j].type & plt_lazy))
+         {
+           /* Skip PLT0 in lazy PLT.  */
+           k = 1;
+           offset = plt_entry_size;
+         }
+       else
+         {
+           k = 0;
+           offset = 0;
+         }
+
+       /* Check each PLT entry against dynamic relocations.  */
+       for (; k < plts[j].count; k++)
+         {
+           int off;
+           bfd_vma got_vma;
+           long min, max, mid;
+
+           /* Get the GOT offset, a signed 32-bit integer.  */
+           off = H_GET_32 (abfd, (plt_contents + offset
+                                  + plt_got_offset));
+           got_vma = got_addr + off;
+
+           /* Binary search.  */
+           p = dynrelbuf[0];
+           min = 0;
+           max = dynrelcount;
+           while ((min + 1) < max)
+             {
+               arelent *r;
+
+               mid = (min + max) / 2;
+               r = dynrelbuf[mid];
+               if (got_vma > r->address)
+                 min = mid;
+               else if (got_vma < r->address)
+                 max = mid;
+               else
+                 {
+                   p = r;
+                   break;
+                 }
+             }
+
+           /* Skip unknown relocation.  PR 17512: file: bc9d6cf5.  */
+           if (got_vma == p->address
+               && p->howto != NULL
+               && (p->howto->type == R_386_JUMP_SLOT
+                   || p->howto->type == R_386_GLOB_DAT
+                   || p->howto->type == R_386_IRELATIVE))
+             {
+               *s = **p->sym_ptr_ptr;
+               /* Undefined syms won't have BSF_LOCAL or BSF_GLOBAL
+                  set.  Since we are defining a symbol, ensure one
+                  of them is set.  */
+               if ((s->flags & BSF_LOCAL) == 0)
+                 s->flags |= BSF_GLOBAL;
+               s->flags |= BSF_SYNTHETIC;
+               /* This is no longer a section symbol.  */
+               s->flags &= ~BSF_SECTION_SYM;
+               s->section = plt;
+               s->the_bfd = plt->owner;
+               s->value = offset;
+               s->udata.p = NULL;
+               s->name = names;
+               len = strlen ((*p->sym_ptr_ptr)->name);
+               memcpy (names, (*p->sym_ptr_ptr)->name, len);
+               names += len;
+               if (p->addend != 0)
+                 {
+                   char buf[30], *a;
+
+                   memcpy (names, "+0x", sizeof ("+0x") - 1);
+                   names += sizeof ("+0x") - 1;
+                   bfd_sprintf_vma (abfd, buf, p->addend);
+                   for (a = buf; *a == '0'; ++a)
+                     ;
+                   size = strlen (a);
+                   memcpy (names, a, size);
+                   names += size;
+                 }
+               memcpy (names, "@plt", sizeof ("@plt"));
+               names += sizeof ("@plt");
+               n++;
+               s++;
+             }
+           offset += plt_entry_size;
+         }
+      }
+
+  /* PLT entries with R_386_TLS_DESC relocations are skipped.  */
+  if (n == 0)
+    goto bad_return;
+
+  count = n;
+
+  for (j = 0; plts[j].name != NULL; j++)
+    if (plts[j].contents != NULL)
+      free (plts[j].contents);
+
+  free (dynrelbuf);
+
+  return count;
 }
 
 /* Return TRUE if symbol should be hashed in the `.gnu.hash' section.  */
 }
 
 /* Return TRUE if symbol should be hashed in the `.gnu.hash' section.  */
@@ -5766,25 +6655,517 @@ 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.  */
+/* Parse i386 GNU properties.  */
+
+static enum elf_property_kind
+elf_i386_parse_gnu_properties (bfd *abfd, unsigned int type,
+                              bfd_byte *ptr, unsigned int datasz)
+{
+  elf_property *prop;
+
+  switch (type)
+    {
+    case GNU_PROPERTY_X86_ISA_1_USED:
+    case GNU_PROPERTY_X86_ISA_1_NEEDED:
+    case GNU_PROPERTY_X86_FEATURE_1_AND:
+      if (datasz != 4)
+       {
+         _bfd_error_handler
+           ((type == GNU_PROPERTY_X86_ISA_1_USED
+             ? _("error: %B: <corrupt x86 ISA used size: 0x%x>")
+             : (type == GNU_PROPERTY_X86_ISA_1_NEEDED
+                ? _("error: %B: <corrupt x86 ISA needed size: 0x%x>")
+                : _("error: %B: <corrupt x86 feature size: 0x%x>"))),
+            abfd, datasz);
+         return property_corrupt;
+       }
+      prop = _bfd_elf_get_property (abfd, type, datasz);
+      /* Combine properties of the same type.  */
+      prop->u.number |= bfd_h_get_32 (abfd, ptr);
+      prop->pr_kind = property_number;
+      break;
+
+    default:
+      return property_ignored;
+    }
+
+  return property_number;
+}
+
+/* Merge i386 GNU property BPROP with APROP.  If APROP isn't NULL,
+   return TRUE if APROP is updated.  Otherwise, return TRUE if BPROP
+   should be merged with ABFD.  */
 
 static bfd_boolean
 
 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)
+elf_i386_merge_gnu_properties (struct bfd_link_info *info,
+                              bfd *abfd ATTRIBUTE_UNUSED,
+                              elf_property *aprop,
+                              elf_property *bprop)
 {
 {
-  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;
+  unsigned int number, features;
+  bfd_boolean updated = FALSE;
+  unsigned int pr_type = aprop != NULL ? aprop->pr_type : bprop->pr_type;
 
 
-  return TRUE;
+  switch (pr_type)
+    {
+    case GNU_PROPERTY_X86_ISA_1_USED:
+    case GNU_PROPERTY_X86_ISA_1_NEEDED:
+      if (aprop != NULL && bprop != NULL)
+       {
+         number = aprop->u.number;
+         aprop->u.number = number | bprop->u.number;
+         updated = number != (unsigned int) aprop->u.number;
+       }
+      else
+       {
+         /* Return TRUE if APROP is NULL to indicate that BPROP should
+            be added to ABFD.  */
+         updated = aprop == NULL;
+       }
+      break;
+
+    case GNU_PROPERTY_X86_FEATURE_1_AND:
+      /* Only one of APROP and BPROP can be NULL:
+        1. APROP & BPROP when both APROP and BPROP aren't NULL.
+        2. If APROP is NULL, remove x86 feature.
+        3. Otherwise, do nothing.
+       */
+      if (aprop != NULL && bprop != NULL)
+       {
+         features = 0;
+         if (info->ibt)
+           features = GNU_PROPERTY_X86_FEATURE_1_IBT;
+         if (info->shstk)
+           features |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+         number = aprop->u.number;
+         /* Add GNU_PROPERTY_X86_FEATURE_1_IBT and
+            GNU_PROPERTY_X86_FEATURE_1_SHSTK.  */
+         aprop->u.number = (number & bprop->u.number) | features;
+         updated = number != (unsigned int) aprop->u.number;
+         /* Remove the property if all feature bits are cleared.  */
+         if (aprop->u.number == 0)
+           aprop->pr_kind = property_remove;
+       }
+      else
+       {
+         features = 0;
+         if (info->ibt)
+           features = GNU_PROPERTY_X86_FEATURE_1_IBT;
+         if (info->shstk)
+           features |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+         if (features)
+           {
+             /* Add GNU_PROPERTY_X86_FEATURE_1_IBT and
+                GNU_PROPERTY_X86_FEATURE_1_SHSTK.  */
+             if (aprop != NULL)
+               {
+                 number = aprop->u.number;
+                 aprop->u.number = number | features;
+                 updated = number != (unsigned int) aprop->u.number;
+               }
+             else
+               {
+                 bprop->u.number |= features;
+                 updated = TRUE;
+               }
+           }
+         else if (aprop != NULL)
+           {
+             aprop->pr_kind = property_remove;
+             updated = TRUE;
+           }
+       }
+      break;
+
+    default:
+      /* Never should happen.  */
+      abort ();
+    }
+
+  return updated;
+}
+
+/* Set up i386 GNU properties.  Return the first relocatable ELF input
+   with GNU properties if found.  Otherwise, return NULL.  */
+
+static bfd *
+elf_i386_link_setup_gnu_properties (struct bfd_link_info *info)
+{
+  bfd_boolean normal_target;
+  bfd_boolean lazy_plt;
+  asection *sec, *pltsec;
+  bfd *dynobj;
+  bfd_boolean use_ibt_plt;
+  unsigned int plt_alignment, features;
+  struct elf_i386_link_hash_table *htab;
+  bfd *pbfd;
+  bfd *ebfd = NULL;
+  elf_property *prop;
+
+  features = 0;
+  if (info->ibt)
+    features = GNU_PROPERTY_X86_FEATURE_1_IBT;
+  if (info->shstk)
+    features |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+
+  /* Find a normal input file with GNU property note.  */
+  for (pbfd = info->input_bfds;
+       pbfd != NULL;
+       pbfd = pbfd->link.next)
+    if (bfd_get_flavour (pbfd) == bfd_target_elf_flavour
+       && bfd_count_sections (pbfd) != 0)
+      {
+       ebfd = pbfd;
+
+       if (elf_properties (pbfd) != NULL)
+         break;
+      }
+
+  if (ebfd != NULL && features)
+    {
+      /* If features is set, add GNU_PROPERTY_X86_FEATURE_1_IBT and
+        GNU_PROPERTY_X86_FEATURE_1_SHSTK.  */
+      prop = _bfd_elf_get_property (ebfd,
+                                   GNU_PROPERTY_X86_FEATURE_1_AND,
+                                   4);
+      prop->u.number |= features;
+      prop->pr_kind = property_number;
+
+      /* Create the GNU property note section if needed.  */
+      if (pbfd == NULL)
+       {
+         sec = bfd_make_section_with_flags (ebfd,
+                                            NOTE_GNU_PROPERTY_SECTION_NAME,
+                                            (SEC_ALLOC
+                                             | SEC_LOAD
+                                             | SEC_IN_MEMORY
+                                             | SEC_READONLY
+                                             | SEC_HAS_CONTENTS
+                                             | SEC_DATA));
+         if (sec == NULL)
+           info->callbacks->einfo (_("%F: failed to create GNU property section\n"));
+
+         if (!bfd_set_section_alignment (ebfd, sec, 2))
+           {
+error_alignment:
+             info->callbacks->einfo (_("%F%A: failed to align section\n"),
+                                     sec);
+           }
+
+         elf_section_type (sec) = SHT_NOTE;
+       }
+    }
+
+  pbfd = _bfd_elf_link_setup_gnu_properties (info);
+
+  if (bfd_link_relocatable (info))
+    return pbfd;
+
+  htab = elf_i386_hash_table (info);
+  if (htab == NULL)
+    return pbfd;
+
+  use_ibt_plt = info->ibtplt || info->ibt;
+  if (!use_ibt_plt && pbfd != NULL)
+    {
+      /* Check if GNU_PROPERTY_X86_FEATURE_1_IBT is on.  */
+      elf_property_list *p;
+
+      /* The property list is sorted in order of type.  */
+      for (p = elf_properties (pbfd); p; p = p->next)
+       {
+         if (GNU_PROPERTY_X86_FEATURE_1_AND == p->property.pr_type)
+           {
+             use_ibt_plt = !!(p->property.u.number
+                              & GNU_PROPERTY_X86_FEATURE_1_IBT);
+             break;
+           }
+         else if (GNU_PROPERTY_X86_FEATURE_1_AND < p->property.pr_type)
+           break;
+       }
+    }
+
+  dynobj = htab->elf.dynobj;
+
+  /* Set htab->elf.dynobj here so that there is no need to check and
+     set it in check_relocs.  */
+  if (dynobj == NULL)
+    {
+      if (pbfd != NULL)
+       {
+         htab->elf.dynobj = pbfd;
+         dynobj = pbfd;
+       }
+      else
+       {
+         bfd *abfd;
+
+         /* Find a normal input file to hold linker created
+            sections.  */
+         for (abfd = info->input_bfds;
+              abfd != NULL;
+              abfd = abfd->link.next)
+           if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+               && (abfd->flags
+                   & (DYNAMIC | BFD_LINKER_CREATED | BFD_PLUGIN)) == 0)
+             {
+               htab->elf.dynobj = abfd;
+               dynobj = abfd;
+               break;
+             }
+       }
+    }
+
+  /* Even when lazy binding is disabled by "-z now", the PLT0 entry may
+     still be used with LD_AUDIT or LD_PROFILE if PLT entry is used for
+     canonical function address.  */
+  htab->plt.has_plt0 = 1;
+  normal_target = FALSE;
+
+  switch (get_elf_i386_backend_data (info->output_bfd)->os)
+    {
+    case is_normal:
+      if (use_ibt_plt)
+       {
+         htab->lazy_plt = &elf_i386_lazy_ibt_plt;
+         htab->non_lazy_plt = &elf_i386_non_lazy_ibt_plt;
+       }
+      else
+       {
+         htab->lazy_plt = &elf_i386_lazy_plt;
+         htab->non_lazy_plt = &elf_i386_non_lazy_plt;
+       }
+      normal_target = TRUE;
+      break;
+    case is_vxworks:
+      htab->lazy_plt = &elf_i386_lazy_plt;
+      htab->non_lazy_plt = NULL;
+      if (!elf_vxworks_create_dynamic_sections (dynobj, info,
+                                               &htab->srelplt2))
+       info->callbacks->einfo (_("%F: failed to create VxWorks dynamic sections\n"));
+      break;
+    case is_nacl:
+      htab->lazy_plt = &elf_i386_nacl_plt;
+      htab->non_lazy_plt = NULL;
+      break;
+    }
+
+  pltsec = htab->elf.splt;
+
+  /* If the non-lazy PLT is available, use it for all PLT entries if
+     there are no PLT0 or no .plt section.  */
+  if (htab->non_lazy_plt != NULL
+      && (!htab->plt.has_plt0 || pltsec == NULL))
+    {
+      lazy_plt = FALSE;
+      if (bfd_link_pic (info))
+       htab->plt.plt_entry
+         = htab->non_lazy_plt->pic_plt_entry;
+      else
+       htab->plt.plt_entry
+         = htab->non_lazy_plt->plt_entry;
+      htab->plt.plt_entry_size
+       = htab->non_lazy_plt->plt_entry_size;
+      htab->plt.plt_got_offset
+       = htab->non_lazy_plt->plt_got_offset;
+      htab->plt.eh_frame_plt_size
+       = htab->non_lazy_plt->eh_frame_plt_size;
+      htab->plt.eh_frame_plt
+       = htab->non_lazy_plt->eh_frame_plt;
+    }
+  else
+    {
+      lazy_plt = TRUE;
+      if (bfd_link_pic (info))
+       {
+         htab->plt.plt0_entry
+           = htab->lazy_plt->pic_plt0_entry;
+         htab->plt.plt_entry
+           = htab->lazy_plt->pic_plt_entry;
+       }
+      else
+       {
+         htab->plt.plt0_entry
+           = htab->lazy_plt->plt0_entry;
+         htab->plt.plt_entry
+           = htab->lazy_plt->plt_entry;
+       }
+      htab->plt.plt_entry_size
+       = htab->lazy_plt->plt_entry_size;
+      htab->plt.plt_got_offset
+       = htab->lazy_plt->plt_got_offset;
+      htab->plt.eh_frame_plt_size
+       = htab->lazy_plt->eh_frame_plt_size;
+      htab->plt.eh_frame_plt
+       = htab->lazy_plt->eh_frame_plt;
+    }
+
+  /* Return if there are no normal input files.  */
+  if (dynobj == NULL)
+    return pbfd;
+
+  /* Since create_dynamic_sections isn't always called, but GOT
+     relocations need GOT sections, create them here so that we
+     don't need to do it in check_relocs.  */
+  if (htab->elf.sgot == NULL
+      && !_bfd_elf_create_got_section (dynobj, info))
+    info->callbacks->einfo (_("%F: failed to create GOT sections\n"));
+
+  /* Create the ifunc sections here so that check_relocs can be
+     simplified.  */
+  if (!_bfd_elf_create_ifunc_sections (dynobj, info))
+    info->callbacks->einfo (_("%F: failed to create ifunc sections\n"));
+
+  plt_alignment = bfd_log2 (htab->plt.plt_entry_size);
+
+  if (pltsec != NULL)
+    {
+      /* Whe creating executable, 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;
+       }
+
+      /* Don't change PLT section alignment for NaCl since it uses
+        64-byte PLT entry and sets PLT section alignment to 32
+        bytes.  */
+      if (normal_target)
+       {
+         const struct elf_backend_data *bed
+           = get_elf_backend_data (dynobj);
+         flagword pltflags = (bed->dynamic_sec_flags
+                              | SEC_ALLOC
+                              | SEC_CODE
+                              | SEC_LOAD
+                              | SEC_READONLY);
+         unsigned int non_lazy_plt_alignment
+           = bfd_log2 (htab->non_lazy_plt->plt_entry_size);
+
+         sec = pltsec;
+         if (!bfd_set_section_alignment (sec->owner, sec,
+                                         plt_alignment))
+           goto error_alignment;
+
+         /* Create the GOT procedure linkage table.  */
+         sec = bfd_make_section_anyway_with_flags (dynobj,
+                                                   ".plt.got",
+                                                   pltflags);
+         if (sec == NULL)
+           info->callbacks->einfo (_("%F: failed to create GOT PLT section\n"));
+
+         if (!bfd_set_section_alignment (dynobj, sec,
+                                         non_lazy_plt_alignment))
+           goto error_alignment;
+
+         htab->plt_got = sec;
+
+         if (lazy_plt)
+           {
+             sec = NULL;
+
+             if (use_ibt_plt)
+               {
+                 /* Create the second PLT for Intel IBT support.  IBT
+                    PLT is supported only for non-NaCl target and is
+                    is needed only for lazy binding.  */
+                 sec = bfd_make_section_anyway_with_flags (dynobj,
+                                                           ".plt.sec",
+                                                           pltflags);
+                 if (sec == NULL)
+                   info->callbacks->einfo (_("%F: failed to create IBT-enabled PLT section\n"));
+
+                 if (!bfd_set_section_alignment (dynobj, sec,
+                                                 plt_alignment))
+                   goto error_alignment;
+               }
+
+             htab->plt_second = sec;
+           }
+       }
+
+      if (!info->no_ld_generated_unwind_info)
+       {
+         flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
+                           | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+                           | SEC_LINKER_CREATED);
+
+         sec = bfd_make_section_anyway_with_flags (dynobj,
+                                                   ".eh_frame",
+                                                   flags);
+         if (sec == NULL)
+           info->callbacks->einfo (_("%F: failed to create PLT .eh_frame section\n"));
+
+         if (!bfd_set_section_alignment (dynobj, sec, 2))
+           goto error_alignment;
+
+         htab->plt_eh_frame = sec;
+
+         if (htab->plt_got != NULL)
+           {
+             sec = bfd_make_section_anyway_with_flags (dynobj,
+                                                       ".eh_frame",
+                                                       flags);
+             if (sec == NULL)
+               info->callbacks->einfo (_("%F: failed to create GOT PLT .eh_frame section\n"));
+
+             if (!bfd_set_section_alignment (dynobj, sec, 2))
+               goto error_alignment;
+
+             htab->plt_got_eh_frame = sec;
+           }
+       }
+    }
+
+  if (normal_target)
+    {
+      /* The .iplt section is used for IFUNC symbols in static
+        executables.  */
+      sec = htab->elf.iplt;
+      if (sec != NULL
+         && !bfd_set_section_alignment (sec->owner, sec,
+                                        plt_alignment))
+       goto error_alignment;
+    }
+
+  return pbfd;
+}
+
+static bfd_boolean
+elf_i386_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
+{
+  if (!bfd_link_relocatable (info))
+    {
+      /* Check for ___tls_get_addr reference.  */
+      struct elf_link_hash_entry *h;
+      h = elf_link_hash_lookup (elf_hash_table (info), "___tls_get_addr",
+                               FALSE, FALSE, FALSE);
+      if (h != NULL)
+       ((struct elf_i386_link_hash_entry *) h)->tls_get_addr = 1;
+    }
+
+  /* Invoke the regular ELF backend linker to do all the work.  */
+  return _bfd_elf_link_check_relocs (abfd, info);
+}
+
+static void
+elf_i386_merge_symbol_attribute (struct elf_link_hash_entry *h,
+                                const Elf_Internal_Sym *isym,
+                                bfd_boolean definition,
+                                bfd_boolean dynamic ATTRIBUTE_UNUSED)
+{
+  if (definition)
+    {
+      struct elf_i386_link_hash_entry *eh
+       = (struct elf_i386_link_hash_entry *) h;
+      eh->def_protected = (ELF_ST_VISIBILITY (isym->st_other)
+                          == STV_PROTECTED);
+    }
 }
 
 #define TARGET_LITTLE_SYM              i386_elf32_vec
 }
 
 #define TARGET_LITTLE_SYM              i386_elf32_vec
@@ -5801,7 +7182,10 @@ elf_i386_add_symbol_hook (bfd * abfd,
 #define elf_backend_want_plt_sym       0
 #define elf_backend_got_header_size    12
 #define elf_backend_plt_alignment      4
 #define elf_backend_want_plt_sym       0
 #define elf_backend_got_header_size    12
 #define elf_backend_plt_alignment      4
+#define elf_backend_dtrel_excludes_plt 1
 #define elf_backend_extern_protected_data 1
 #define elf_backend_extern_protected_data 1
+#define elf_backend_caches_rawsize     1
+#define elf_backend_want_dynrelro      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
@@ -5814,15 +7198,17 @@ elf_i386_add_symbol_hook (bfd * abfd,
 #define bfd_elf32_bfd_reloc_type_lookup              elf_i386_reloc_type_lookup
 #define bfd_elf32_bfd_reloc_name_lookup              elf_i386_reloc_name_lookup
 #define bfd_elf32_get_synthetic_symtab       elf_i386_get_synthetic_symtab
 #define bfd_elf32_bfd_reloc_type_lookup              elf_i386_reloc_type_lookup
 #define bfd_elf32_bfd_reloc_name_lookup              elf_i386_reloc_name_lookup
 #define bfd_elf32_get_synthetic_symtab       elf_i386_get_synthetic_symtab
+#define bfd_elf32_bfd_link_check_relocs              elf_i386_link_check_relocs
 
 #define elf_backend_adjust_dynamic_symbol     elf_i386_adjust_dynamic_symbol
 #define elf_backend_relocs_compatible        _bfd_elf_relocs_compatible
 #define elf_backend_check_relocs             elf_i386_check_relocs
 #define elf_backend_copy_indirect_symbol      elf_i386_copy_indirect_symbol
 
 #define elf_backend_adjust_dynamic_symbol     elf_i386_adjust_dynamic_symbol
 #define elf_backend_relocs_compatible        _bfd_elf_relocs_compatible
 #define elf_backend_check_relocs             elf_i386_check_relocs
 #define elf_backend_copy_indirect_symbol      elf_i386_copy_indirect_symbol
-#define elf_backend_create_dynamic_sections   elf_i386_create_dynamic_sections
+#define elf_backend_create_dynamic_sections   _bfd_elf_create_dynamic_sections
 #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_grok_prstatus            elf_i386_grok_prstatus
 #define elf_backend_grok_psinfo                      elf_i386_grok_psinfo
 #define elf_backend_gc_mark_hook             elf_i386_gc_mark_hook
 #define elf_backend_grok_prstatus            elf_i386_grok_prstatus
 #define elf_backend_grok_psinfo                      elf_i386_grok_psinfo
@@ -5833,8 +7219,11 @@ 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
 #define elf_backend_fixup_symbol             elf_i386_fixup_symbol
+#define elf_backend_parse_gnu_properties      elf_i386_parse_gnu_properties
+#define elf_backend_merge_gnu_properties      elf_i386_merge_gnu_properties
+#define elf_backend_setup_gnu_properties      elf_i386_link_setup_gnu_properties
+#define elf_backend_merge_symbol_attribute    elf_i386_merge_symbol_attribute
 
 #include "elf32-target.h"
 
 
 #include "elf32-target.h"
 
@@ -5905,16 +7294,22 @@ elf_i386_fbsd_post_process_headers (bfd *abfd, struct bfd_link_info *info)
 #undef  elf_backend_strtab_flags
 #define elf_backend_strtab_flags       SHF_STRINGS
 
 #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
 static bfd_boolean
-elf32_i386_set_special_info_link (const bfd *ibfd ATTRIBUTE_UNUSED,
-                                 bfd *obfd ATTRIBUTE_UNUSED,
-                                 const Elf_Internal_Shdr *isection ATTRIBUTE_UNUSED,
-                                 Elf_Internal_Shdr *osection ATTRIBUTE_UNUSED)
+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
 {
   /* PR 19938: FIXME: Need to add code for setting the sh_info
-     and sh_link fields of Solaris specific section types.
+     and sh_link fields of Solaris specific section types.  */
+  return FALSE;
 
 
-     Based upon Oracle Solaris 11.3 Linkers and Libraries Guide, Ch. 13,
+  /* 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
      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
@@ -5974,11 +7369,10 @@ SHT_SUNW_verneed     The section header index of    The number of version
 
 SHT_SUNW_versym      The section header index of    0
  [0x6fffffff]        the associated symbol table.  */
 
 SHT_SUNW_versym      The section header index of    0
  [0x6fffffff]        the associated symbol table.  */
-  return FALSE;
 }
 
 }
 
-#undef  elf_backend_set_special_section_info_and_link
-#define elf_backend_set_special_section_info_and_link elf32_i386_set_special_info_link
+#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"
 
 
 #include "elf32-target.h"
 
@@ -6016,7 +7410,7 @@ elf32_iamcu_elf_object_p (bfd *abfd)
 #define elf_backend_want_plt_sym           0
 
 #undef  elf_backend_strtab_flags
 #define elf_backend_want_plt_sym           0
 
 #undef  elf_backend_strtab_flags
-#undef  elf_backend_set_special_section_info_and_link
+#undef  elf_backend_copy_special_section_fields
 
 #include "elf32-target.h"
 
 
 #include "elf32-target.h"
 
@@ -6162,7 +7556,7 @@ static const bfd_byte elf_i386_nacl_eh_frame_plt[] =
     DW_CFA_nop, DW_CFA_nop
   };
 
     DW_CFA_nop, DW_CFA_nop
   };
 
-static const struct elf_i386_plt_layout elf_i386_nacl_plt =
+static const struct elf_i386_lazy_plt_layout elf_i386_nacl_plt =
   {
     elf_i386_nacl_plt0_entry,          /* plt0_entry */
     sizeof (elf_i386_nacl_plt0_entry), /* plt0_entry_size */
   {
     elf_i386_nacl_plt0_entry,          /* plt0_entry */
     sizeof (elf_i386_nacl_plt0_entry), /* plt0_entry_size */
@@ -6177,14 +7571,13 @@ static const struct elf_i386_plt_layout elf_i386_nacl_plt =
     elf_i386_nacl_pic_plt0_entry,      /* pic_plt0_entry */
     elf_i386_nacl_pic_plt_entry,       /* pic_plt_entry */
     elf_i386_nacl_eh_frame_plt,                /* eh_frame_plt */
     elf_i386_nacl_pic_plt0_entry,      /* pic_plt0_entry */
     elf_i386_nacl_pic_plt_entry,       /* pic_plt_entry */
     elf_i386_nacl_eh_frame_plt,                /* eh_frame_plt */
-    sizeof (elf_i386_nacl_eh_frame_plt),/* eh_frame_plt_size */
+    sizeof (elf_i386_nacl_eh_frame_plt) /* eh_frame_plt_size */
   };
 
 static const struct elf_i386_backend_data elf_i386_nacl_arch_bed =
   {
   };
 
 static const struct elf_i386_backend_data elf_i386_nacl_arch_bed =
   {
-    &elf_i386_nacl_plt,                      /* plt */
-    0x90,                              /* plt0_pad_byte: nop insn */
-    0,                                  /* is_vxworks */
+    0x90,                               /* plt0_pad_byte: nop insn */
+    is_nacl                             /* os */
   };
 
 static bfd_boolean
   };
 
 static bfd_boolean
@@ -6222,14 +7615,15 @@ elf32_i386_nacl_elf_object_p (bfd *abfd)
 #undef TARGET_LITTLE_NAME
 #define TARGET_LITTLE_NAME             "elf32-i386-vxworks"
 #undef ELF_OSABI
 #undef TARGET_LITTLE_NAME
 #define TARGET_LITTLE_NAME             "elf32-i386-vxworks"
 #undef ELF_OSABI
+#undef ELF_MAXPAGESIZE
+#define ELF_MAXPAGESIZE                        0x1000
 #undef elf_backend_plt_alignment
 #define elf_backend_plt_alignment      4
 
 static const struct elf_i386_backend_data elf_i386_vxworks_arch_bed =
   {
 #undef elf_backend_plt_alignment
 #define elf_backend_plt_alignment      4
 
 static const struct elf_i386_backend_data elf_i386_vxworks_arch_bed =
   {
-    &elf_i386_plt,                      /* plt */
     0x90,                               /* plt0_pad_byte */
     0x90,                               /* plt0_pad_byte */
-    1,                                  /* is_vxworks */
+    is_vxworks                          /* os */
   };
 
 #undef elf_backend_arch_data
   };
 
 #undef elf_backend_arch_data
This page took 0.108502 seconds and 4 git commands to generate.