i386: Improve PLT generation and synthetic PLT symbols
authorH.J. Lu <hjl.tools@gmail.com>
Mon, 8 May 2017 17:10:22 +0000 (10:10 -0700)
committerH.J. Lu <hjl.tools@gmail.com>
Mon, 8 May 2017 17:11:32 +0000 (10:11 -0700)
On i386, the procedure linkage table (PLT) is used to

1. Call external function.
2. Call internal IFUNC function.  The best implementation is selected
for the target processor at run-time.
3. Act as the canonical function address.
4. Support LD_AUDIT to audit external function calls.
5. Support LD_PROFILE to profile external function calls.

PLT looks like:

PLT0:  push  GOT[1]
       jmp   *GOT[2]
       nop
PLT1:  jmp   *GOT[name1_index]
       push  name1_reloc_index
       jmp   PLT0

GOT is an array of addresses.  Initially the GOT entry of name1 is
filled with the address of the "push name1_reloc_index" instruction.
The function, name1, is called via "jmp *GOT[name1]" in the PLT entry.
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.

1. With lazy binding, when the external function, name1, is called the
first time, dynamic linker is called via PLT0 to update GOT[name1_index]
with the actual address of name1 and transfers control to name1
afterwards.
2. PLT is also used to call a local IFUNC function, name1, run-time
loader updates GOT[name1_index] when loading the module.

This patch

1. Remove PLT layout configurations from i386 backend_data.
2. Add generic, lay and non-lazy PLT layout configurations to i386
link_hash_table.  Generic PLT layout includes the PLT entry templates,
information how to update the first instruction in PLT and PLT eh_frame
informaton, which are initialized in i386 setup_gnu_properties, based
on PIC and target selection.  PLT section alignment is also set to PLT
entry size for non-NaCl/VxWorks target.
3. Remove elf_i386_create_dynamic_sections.  create_dynamic_sections
isn't always called, but GOT relocations need GOT relocations.  Instead,
create all i386 specific dynamic sections in i386 setup_gnu_properties,
which initializes elf.dynobj, so that i386 check_relocs can be simplified.
4. Rewrite elf_i386_get_synthetic_symtab to check PLT sections against
all dynamic relocations to support both lazy and non-lazy PLTs.

bfd/

* elf32-i386.c (PLT_ENTRY_SIZE): Renamed to ...
(LAZY_PLT_ENTRY_SIZE): This.
(NON_LAZY_PLT_ENTRY_SIZE): New.
(elf_i386_plt0_entry): Renamed to ...
(elf_i386_lazy_plt0_entry): This.
(elf_i386_plt_entry): Renamed to ...
(elf_i386_lazy_plt_entry): This.
(elf_i386_pic_plt0_entry): Renamed to ...
(elf_i386_pic_lazy_plt0_entry): This.
(elf_i386_pic_plt_entry): Renamed to ...
(elf_i386_pic_lazy_plt_entry): This.
(elf_i386_got_plt_entry): Renamed to ...
(elf_i386_non_lazy_plt_entry): This.
(elf_i386_pic_got_plt_entry): Renamed to ...
(elf_i386_pic_non_lazy_plt_entry): This.
(elf_i386_eh_frame_plt): Renamed to ...
(elf_i386_eh_frame_lazy_plt): This.
(elf_i386_eh_frame_plt_got): Renamed to ...
(elf_i386_eh_frame_non_lazy_plt): This.
(elf_i386_plt_layout): Renamed to ...
(elf_i386_lazy_plt_layout): This.  Remove eh_frame_plt_got and
eh_frame_plt_got_size.
(elf_i386_non_lazy_plt_layout): New.
(elf_i386_plt_layout): Likewise.
(elf_i386_non_lazy_plt): Likewise.
(GET_PLT_ENTRY_SIZE): Removed.
(elf_i386_plt): Renamed to ...
(elf_i386_lazy_plt): This.
(elf_i386_backend_data): Remove plt.  Rename is_vxworks to os.
(elf_i386_arch_bed): Updated.
(elf_i386_link_hash_table): Add plt, lazy_plt and non_lazy_plt.
(elf_i386_create_dynamic_sections): Removed.
(elf_i386_check_relocs): Don't check elf.dynobj.  Don't call
_bfd_elf_create_ifunc_sections nor _bfd_elf_create_got_section.
(elf_i386_adjust_dynamic_symbol): Updated.
(elf_i386_allocate_dynrelocs): Updated.  Pass 0 as PLT header
size to _bfd_elf_allocate_ifunc_dyn_relocs and don't allocate
size for PLT0 if there is no PLT0.
(elf_i386_size_dynamic_sections): Updated.  Check whether GOT
output section is discarded only if GOT isn't empty.
(elf_i386_relocate_section): Updated.  Properly get PLT index
if there is no PLT0.
(elf_i386_finish_dynamic_symbol): Updated.  Don't fill the
second and third slots in the PLT entry if there is no PLT0.
(elf_i386_finish_dynamic_sections): Updated.  Don't fill PLT0
if there is no PLT0.  Set sh_entsize on the .plt.got section.
(elf_i386_nacl_plt): Forward declaration.
(elf_i386_get_plt_sym_val): Removed.
(elf_i386_get_synthetic_symtab): Rewrite to check PLT sections
against all dynamic relocations.
(elf_i386_link_setup_gnu_properties): New function.
(elf_backend_create_dynamic_sections): Updated.
(elf_backend_setup_gnu_properties): New.
(elf_i386_nacl_plt): Updated.
(elf_i386_nacl_arch_bed): Likewise.
(elf_i386_vxworks_arch_bed): Likewise.

ld/

* testsuite/ld-i386/i386.exp: Add some -z now tests.
* testsuite/ld-i386/plt-pic2.dd: New file.
* testsuite/ld-i386/plt2.dd: Likewise.
* testsuite/ld-i386/plt2.rd: Likewise.
* testsuite/ld-i386/plt2.s: Likewise.
* testsuite/ld-ifunc/ifunc-16-i386-now.d: Likewise.
* testsuite/ld-ifunc/ifunc-2-i386-now.d: Likewise.
* testsuite/ld-ifunc/ifunc-2-local-i386-now.d: Likewise.
* testsuite/ld-ifunc/pr17154-i386-now.d: Likewise.
* testsuite/ld-i386/pr20830.d: Update the .plt.got section
with func@plt.

13 files changed:
bfd/ChangeLog
bfd/elf32-i386.c
ld/ChangeLog
ld/testsuite/ld-i386/i386.exp
ld/testsuite/ld-i386/plt-pic2.dd [new file with mode: 0644]
ld/testsuite/ld-i386/plt2.dd [new file with mode: 0644]
ld/testsuite/ld-i386/plt2.rd [new file with mode: 0644]
ld/testsuite/ld-i386/plt2.s [new file with mode: 0644]
ld/testsuite/ld-i386/pr20830.d
ld/testsuite/ld-ifunc/ifunc-16-i386-now.d [new file with mode: 0644]
ld/testsuite/ld-ifunc/ifunc-2-i386-now.d [new file with mode: 0644]
ld/testsuite/ld-ifunc/ifunc-2-local-i386-now.d [new file with mode: 0644]
ld/testsuite/ld-ifunc/pr17154-i386-now.d [new file with mode: 0644]

index 8c7c021..cfdb6b6 100644 (file)
@@ -1,3 +1,62 @@
+2017-05-08  H.J. Lu  <hongjiu.lu@intel.com>
+
+       * elf32-i386.c (PLT_ENTRY_SIZE): Renamed to ...
+       (LAZY_PLT_ENTRY_SIZE): This.
+       (NON_LAZY_PLT_ENTRY_SIZE): New.
+       (elf_i386_plt0_entry): Renamed to ...
+       (elf_i386_lazy_plt0_entry): This.
+       (elf_i386_plt_entry): Renamed to ...
+       (elf_i386_lazy_plt_entry): This.
+       (elf_i386_pic_plt0_entry): Renamed to ...
+       (elf_i386_pic_lazy_plt0_entry): This.
+       (elf_i386_pic_plt_entry): Renamed to ...
+       (elf_i386_pic_lazy_plt_entry): This.
+       (elf_i386_got_plt_entry): Renamed to ...
+       (elf_i386_non_lazy_plt_entry): This.
+       (elf_i386_pic_got_plt_entry): Renamed to ...
+       (elf_i386_pic_non_lazy_plt_entry): This.
+       (elf_i386_eh_frame_plt): Renamed to ...
+       (elf_i386_eh_frame_lazy_plt): This.
+       (elf_i386_eh_frame_plt_got): Renamed to ...
+       (elf_i386_eh_frame_non_lazy_plt): This.
+       (elf_i386_plt_layout): Renamed to ...
+       (elf_i386_lazy_plt_layout): This.  Remove eh_frame_plt_got and
+       eh_frame_plt_got_size.
+       (elf_i386_non_lazy_plt_layout): New.
+       (elf_i386_plt_layout): Likewise.
+       (elf_i386_non_lazy_plt): Likewise.
+       (GET_PLT_ENTRY_SIZE): Removed.
+       (elf_i386_plt): Renamed to ...
+       (elf_i386_lazy_plt): This.
+       (elf_i386_backend_data): Remove plt.  Rename is_vxworks to os.
+       (elf_i386_arch_bed): Updated.
+       (elf_i386_link_hash_table): Add plt, lazy_plt and non_lazy_plt.
+       (elf_i386_create_dynamic_sections): Removed.
+       (elf_i386_check_relocs): Don't check elf.dynobj.  Don't call
+       _bfd_elf_create_ifunc_sections nor _bfd_elf_create_got_section.
+       (elf_i386_adjust_dynamic_symbol): Updated.
+       (elf_i386_allocate_dynrelocs): Updated.  Pass 0 as PLT header
+       size to _bfd_elf_allocate_ifunc_dyn_relocs and don't allocate
+       size for PLT0 if there is no PLT0.
+       (elf_i386_size_dynamic_sections): Updated.  Check whether GOT
+       output section is discarded only if GOT isn't empty.
+       (elf_i386_relocate_section): Updated.  Properly get PLT index
+       if there is no PLT0.
+       (elf_i386_finish_dynamic_symbol): Updated.  Don't fill the
+       second and third slots in the PLT entry if there is no PLT0.
+       (elf_i386_finish_dynamic_sections): Updated.  Don't fill PLT0
+       if there is no PLT0.  Set sh_entsize on the .plt.got section.
+       (elf_i386_nacl_plt): Forward declaration.
+       (elf_i386_get_plt_sym_val): Removed.
+       (elf_i386_get_synthetic_symtab): Rewrite to check PLT sections
+       against all dynamic relocations.
+       (elf_i386_link_setup_gnu_properties): New function.
+       (elf_backend_create_dynamic_sections): Updated.
+       (elf_backend_setup_gnu_properties): New.
+       (elf_i386_nacl_plt): Updated.
+       (elf_i386_nacl_arch_bed): Likewise.
+       (elf_i386_vxworks_arch_bed): Likewise.
+
 2017-05-08  Thomas Preud'homme  <thomas.preudhomme@arm.com>
 
        * elflink.c (elf_output_implib): Remove executable flag from import
index e07a81f..0707b5a 100644 (file)
@@ -541,15 +541,20 @@ elf_i386_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
    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.  */
@@ -557,10 +562,10 @@ static const bfd_byte elf_i386_plt0_entry[12] =
   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.  */
@@ -570,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.  */
 };
 
-/* 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) */
 };
 
-/* 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.  */
@@ -591,27 +598,28 @@ static const bfd_byte elf_i386_pic_plt_entry[PLT_ENTRY_SIZE] =
   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  */
 };
 
-/* 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  */
 };
 
-/* .eh_frame covering the .plt section.  */
+/* .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
@@ -648,9 +656,9 @@ static const bfd_byte elf_i386_eh_frame_plt[] =
   DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop
 };
 
-/* .eh_frame covering the .plt.got section.  */
+/* .eh_frame covering the non-lazy .plt section.  */
 
-static const bfd_byte elf_i386_eh_frame_plt_got[] =
+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 */
@@ -668,23 +676,26 @@ static const bfd_byte elf_i386_eh_frame_plt_got[] =
 
   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 .plt.got goes here */
-  0, 0, 0, 0,                  /* .plt.got size goes here */
+  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_plt_layout
+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;
 
-  /* 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;
 
-  /* 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;
 
@@ -693,46 +704,87 @@ struct elf_i386_plt_layout
   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;
 
-  /* 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;
 
-  /* 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;
 
-  /* .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;
+};
 
-  /* .eh_frame covering the .plt.got section.  */
-  const bfd_byte *eh_frame_plt_got;
-  unsigned int eh_frame_plt_got_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;
 };
 
-#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.  */
-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 */
-    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 */
-    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_eh_frame_plt_got,          /* eh_frame_plt_got */
-    sizeof (elf_i386_eh_frame_plt_got), /* eh_frame_plt_got_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 */
   };
 \f
 
@@ -746,14 +798,16 @@ static const struct elf_i386_plt_layout elf_i386_plt =
 
 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;
 
-  /* 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) \
@@ -763,9 +817,8 @@ struct elf_i386_backend_data
 /* 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,                                  /* is_vxworks */
+    is_normal                           /* os */
   };
 
 #define        elf_backend_arch_data   &elf_i386_arch_bed
@@ -887,6 +940,15 @@ struct elf_i386_link_hash_table
   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
   {
     bfd_signed_vma refcount;
@@ -1092,107 +1154,6 @@ elf_i386_link_hash_table_create (bfd *abfd)
   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;
-
-  /* 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;
-    }
-
-  if (get_elf_i386_backend_data (dynobj)->is_vxworks
-      && !elf_vxworks_create_dynamic_sections (dynobj, info,
-                                              &htab->srelplt2))
-    return FALSE;
-
-  if (htab->elf.splt != NULL)
-    {
-      if (htab->plt_got == NULL
-         && !get_elf_i386_backend_data (dynobj)->is_vxworks
-         && get_elf_i386_backend_data (dynobj) == &elf_i386_arch_bed)
-       {
-         /* Create the GOT procedure linkage table.  */
-         unsigned int plt_got_align;
-         const struct elf_backend_data *bed;
-
-         bed = get_elf_backend_data (dynobj);
-         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;
-
-         htab->plt_got
-           = bfd_make_section_anyway_with_flags (dynobj,
-                                                 ".plt.got",
-                                                 (bed->dynamic_sec_flags
-                                                  | SEC_ALLOC
-                                                  | SEC_CODE
-                                                  | SEC_LOAD
-                                                  | SEC_READONLY));
-         if (htab->plt_got == NULL
-             || !bfd_set_section_alignment (dynobj,
-                                            htab->plt_got,
-                                            plt_got_align))
-           return FALSE;
-       }
-
-      if (!info->no_ld_generated_unwind_info)
-       {
-         flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
-                           | SEC_HAS_CONTENTS | SEC_IN_MEMORY
-                           | SEC_LINKER_CREATED);
-
-         if (htab->plt_eh_frame == NULL)
-           {
-             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;
-           }
-
-         if (htab->plt_got_eh_frame == NULL
-             && htab->plt_got != NULL)
-           {
-             htab->plt_got_eh_frame
-               = bfd_make_section_anyway_with_flags (dynobj,
-                                                     ".eh_frame",
-                                                     flags);
-             if (htab->plt_got_eh_frame == NULL
-                 || !bfd_set_section_alignment (dynobj,
-                                                htab->plt_got_eh_frame,
-                                                2))
-               return FALSE;
-           }
-       }
-    }
-
-  return TRUE;
-}
-
 /* Copy the extra info we tack onto an elf_link_hash_entry.  */
 
 static void
@@ -2005,28 +1966,8 @@ elf_i386_check_relocs (bfd *abfd,
       eh = (struct elf_i386_link_hash_entry *) h;
       if (h != NULL)
        {
-         switch (r_type)
-           {
-           default:
-             break;
-
-           case R_386_GOTOFF:
-             eh->gotoff_ref = 1;
-             /* Fall through.  */
-           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))
-               goto error_return;
-             break;
-           }
+         if (r_type == R_386_GOTOFF)
+           eh->gotoff_ref = 1;
 
          /* It is referenced by a non-shared object. */
          h->ref_regular = 1;
@@ -2186,13 +2127,6 @@ elf_i386_check_relocs (bfd *abfd,
        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))
-               goto error_return;
-           }
          if (r_type != R_386_TLS_IE)
            {
              if (eh != NULL)
@@ -2321,9 +2255,6 @@ do_size:
                 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);
 
@@ -2607,7 +2538,7 @@ elf_i386_adjust_dynamic_symbol (struct bfd_link_info *info,
      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)
        {
@@ -2667,6 +2598,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;
+  const struct elf_i386_backend_data *bed;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
@@ -2678,7 +2610,9 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   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->has_got_reloc,
@@ -2714,7 +2648,9 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
     return _bfd_elf_allocate_ifunc_dyn_relocs (info, h, &eh->dyn_relocs,
                                               &htab->readonly_dynrelocs_against_ifunc,
                                               plt_entry_size,
-                                              plt_entry_size, 4, TRUE);
+                                              (htab->plt.has_plt0 *
+                                               plt_entry_size),
+                                              4, TRUE);
   /* Don't create the PLT entry if there are only function pointer
      relocations which can be resolved at run-time.  */
   else if (htab->elf.dynamic_sections_created
@@ -2762,7 +2698,7 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
             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;
@@ -2793,7 +2729,7 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 
          /* 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;
@@ -2814,8 +2750,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,
@@ -2951,7 +2886,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; )
@@ -3302,7 +3237,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.  */
                }
-             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)
                {
@@ -3441,15 +3377,14 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
          && htab->elf.splt != NULL
          && htab->elf.splt->size != 0
          && !bfd_is_abs_section (htab->elf.splt->output_section))
-       htab->plt_eh_frame->size
-         = get_elf_i386_backend_data (output_bfd)->plt->eh_frame_plt_size;
+       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
-         = get_elf_i386_backend_data (output_bfd)->plt->eh_frame_plt_got_size;
+         = htab->non_lazy_plt->eh_frame_plt_size;
     }
 
   /* We now have determined the sizes of the various dynamic sections.
@@ -3535,7 +3470,7 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
       && htab->plt_eh_frame->contents != NULL)
     {
       memcpy (htab->plt_eh_frame->contents,
-             get_elf_i386_backend_data (output_bfd)->plt->eh_frame_plt,
+             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);
@@ -3545,7 +3480,7 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
       && htab->plt_got_eh_frame->contents != NULL)
     {
       memcpy (htab->plt_got_eh_frame->contents,
-             get_elf_i386_backend_data (output_bfd)->plt->eh_frame_plt_got,
+             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
@@ -3611,7 +3546,7 @@ elf_i386_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
                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;
     }
@@ -3793,14 +3728,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'.  */
-  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);
 
-  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;
@@ -4038,7 +3974,8 @@ elf_i386_relocate_section (bfd *output_bfd,
 
                  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;
                    }
@@ -4251,7 +4188,8 @@ do_ifunc_pointer:
                          + (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))
            {
@@ -5337,7 +5275,7 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
     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;
 
   eh = (struct elf_i386_link_hash_entry *) h;
   if (eh->no_finish_dynamic_symbol)
@@ -5399,7 +5337,8 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
 
       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
@@ -5408,19 +5347,20 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
          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 (! 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),
                      plt->contents + h->plt.offset
-                      + abed->plt->plt_got_offset);
+                      + htab->plt.plt_got_offset);
 
-         if (abed->is_vxworks)
+         if (abed->os == is_vxworks)
            {
              int s, k, reloc_index;
 
@@ -5428,8 +5368,8 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
                 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;
@@ -5459,11 +5399,9 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
        }
       else
        {
-         memcpy (plt->contents + h->plt.offset, abed->plt->pic_plt_entry,
-                 abed->plt->plt_entry_size);
          bfd_put_32 (output_bfd, got_offset,
                      plt->contents + h->plt.offset
-                      + abed->plt->plt_got_offset);
+                      + htab->plt.plt_got_offset);
        }
 
       /* Fill in the entry in the global offset table.  Leave the entry
@@ -5471,12 +5409,13 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
         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
@@ -5509,17 +5448,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);
 
-         /* 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
-                         + 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));
            }
        }
     }
@@ -5529,9 +5470,6 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
       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;
@@ -5547,12 +5485,12 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
       /* 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_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
@@ -5561,9 +5499,10 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
 
       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,
-                 plt->contents + plt_offset + plt_got_offset);
+                 (plt->contents + plt_offset
+                  + htab->non_lazy_plt->plt_got_offset));
     }
 
   if (!local_undefweak
@@ -5821,7 +5760,7 @@ elf_i386_finish_dynamic_sections (bfd *output_bfd,
          switch (dyn.d_tag)
            {
            default:
-             if (abed->is_vxworks
+             if (abed->os == is_vxworks
                   && elf_vxworks_finish_dynamic_entry (output_bfd, &dyn))
                break;
              continue;
@@ -5852,88 +5791,90 @@ elf_i386_finish_dynamic_sections (bfd *output_bfd,
          elf_section_data (htab->elf.splt->output_section)
            ->this_hdr.sh_entsize = 4;
 
-         /* Fill in the special first entry in the procedure linkage
-            table.  */
-         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
+         if (htab->plt.has_plt0)
            {
-             memcpy (htab->elf.splt->contents, abed->plt->plt0_entry,
-                     abed->plt->plt0_entry_size);
-             memset (htab->elf.splt->contents + abed->plt->plt0_entry_size,
+             /* 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,
-                     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)
+                     htab->plt.plt_entry_size - htab->lazy_plt->plt0_entry_size);
+             if (!bfd_link_pic (info))
                {
-                 Elf_Internal_Rela rel;
-                 int num_plts = (htab->elf.splt->size
-                                 / abed->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
-                                 + 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));
-
-                 /* 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);
+                 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);
 
-                 for (; num_plts; num_plts--)
+                 if (abed->os == is_vxworks)
                    {
-                     bfd_elf32_swap_reloc_in (output_bfd, p, &rel);
+                     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, 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,
+                     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, p);
-                     p += sizeof (Elf32_External_Rel);
+                     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--)
+                       {
+                         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->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))
        {
@@ -5942,16 +5883,12 @@ elf_i386_finish_dynamic_sections (bfd *output_bfd,
          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;
     }
@@ -6050,96 +5987,358 @@ elf_i386_output_arch_local_syms
   return TRUE;
 }
 
-/* Return an array of PLT entry symbol values.  */
+/* Sort relocs into address order.  */
 
-static bfd_vma *
-elf_i386_get_plt_sym_val (bfd *abfd, asymbol **dynsyms, asection *plt,
-                         asection *relplt)
+static int
+compare_relocs (const void *ap, const void *bp)
 {
-  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;
+  const arelent *a = * (const arelent **) ap;
+  const arelent *b = * (const arelent **) bp;
 
-  /* 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))
+  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_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;
+};
+
+/* Forward declaration.  */
+static const struct elf_i386_lazy_plt_layout elf_i386_nacl_plt;
+
+/* 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;
+  int j;
+  unsigned int plt_got_offset, plt_entry_size;
+  asymbol *s;
+  bfd_byte *plt_contents;
+  long dynrelcount, relsize;
+  arelent **dynrelbuf;
+  const struct elf_i386_lazy_plt_layout *lazy_plt;
+  const struct elf_i386_non_lazy_plt_layout *non_lazy_plt;
+  asection *plt;
+  bfd_vma got_addr;
+  char *names;
+  enum elf_i386_plt_type plt_type;
+  struct elf_i386_plt plts[] =
     {
-bad_return:
-      free (plt_contents);
-      return NULL;
-    }
+      { ".plt", NULL, NULL, plt_unknown, 0, 0, 0 },
+      { ".plt.got", NULL, NULL, plt_non_lazy, 0, 0, 0 },
+      { NULL, }
+    };
 
-  slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
-  if (! (*slurp_relocs) (abfd, relplt, dynsyms, TRUE))
-    goto bad_return;
+  *ret = NULL;
 
-  hdr = &elf_section_data (relplt)->this_hdr;
-  count = relplt->size / hdr->sh_entsize;
+  if ((abfd->flags & (DYNAMIC | EXEC_P)) == 0)
+    return 0;
 
-  plt_sym_val = (bfd_vma *) bfd_malloc (sizeof (bfd_vma) * count);
-  if (plt_sym_val == NULL)
-    goto bad_return;
+  if (dynsymcount <= 0)
+    return 0;
 
-  for (i = 0; i < count; i++)
-    plt_sym_val[i] = -1;
+  relsize = bfd_get_dynamic_reloc_upper_bound (abfd);
+  if (relsize <= 0)
+    return -1;
 
-  plt_offset = bed->plt->plt_entry_size;
-  p = relplt->relocation;
-  for (i = 0; i < count; i++, p++)
+  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);
+
+  non_lazy_plt = NULL;
+  /* Silence GCC 6.  */
+  lazy_plt = NULL;
+  switch (get_elf_i386_backend_data (abfd)->os)
     {
-      long reloc_index;
+    case is_normal:
+      non_lazy_plt = &elf_i386_non_lazy_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;
 
-      /* Skip unknown relocation.  PR 17512: file: bc9d6cf5.  */
-      if (p->howto == NULL)
+  count = 0;
+  for (j = 0; plts[j].name != NULL; j++)
+    {
+      plt = bfd_get_section_by_name (abfd, plts[j].name);
+      if (plt == NULL)
        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)
+           plt_type = plt_lazy;
+         else if (memcmp (plt_contents, lazy_plt->pic_plt0_entry,
+                          lazy_plt->plt0_got1_offset) == 0)
+           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 (plt_type == plt_unknown)
        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;
+      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);
+  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;
+    }
 
-  return 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;
+       }
 
-/* Similar to _bfd_elf_get_synthetic_symtab.  */
+      if (got_addr == (bfd_vma) -1)
+       goto bad_return;
+    }
 
-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);
+  /* Check for each PLT section.  */
+  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;
+           arelent *p;
+
+           /* 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;
+               /* Store relocation for later use.  */
+               s->udata.p = p;
+               /* Add @plt to function name later.  */
+               size += strlen (s->name) + sizeof ("@plt");
+               if (p->addend != 0)
+                 size += sizeof ("+0x") - 1 + 8;
+               n++;
+               s++;
+             }
+           offset += plt_entry_size;
+         }
+      }
+
+  /* PLT entries with R_386_TLS_DESC relocations are skipped.  */
+  if (n == 0)
+    goto bad_return;
+
+  count = n;
+
+  /* Allocate space for @plt suffixes.  */
+  names = (char *) bfd_malloc (size);
+  if (s == NULL)
+    goto bad_return;
+
+  s = *ret;
+  for (i = 0; i < count; i++)
+    {
+      /* Add @plt to function name.  */
+      arelent *p = (arelent *) s->udata.p;
+      /* Clear it now.  */
+      s->udata.p = NULL;
+      size = strlen (s->name);
+      memcpy (names, s->name, size);
+      s->name = names;
+      names += size;
+      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");
+      s++;
+    }
+
+  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.  */
@@ -6227,6 +6426,239 @@ elf_i386_merge_gnu_properties (bfd *abfd ATTRIBUTE_UNUSED,
   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;
+  asection *sec, *pltsec;
+  bfd *dynobj;
+  unsigned int plt_alignment;
+  struct elf_i386_link_hash_table *htab;
+  bfd *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;
+
+  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)
+    {
+      bfd *abfd;
+
+      /* Find a normal input file to hold linker created
+        sections.  */
+      for (abfd = info->input_bfds;
+          abfd != NULL;
+          abfd = abfd->link.next)
+       if ((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:
+      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))
+    {
+      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
+    {
+      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 relocations, 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 (!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))
+       {
+error_alignment:
+         info->callbacks->einfo (_("%F%A: failed to align section\n"),
+                                 sec);
+       }
+    }
+
+  return pbfd;
+}
+
 #define TARGET_LITTLE_SYM              i386_elf32_vec
 #define TARGET_LITTLE_NAME             "elf32-i386"
 #define ELF_ARCH                       bfd_arch_i386
@@ -6262,7 +6694,7 @@ elf_i386_merge_gnu_properties (bfd *abfd ATTRIBUTE_UNUSED,
 #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
@@ -6280,6 +6712,7 @@ elf_i386_merge_gnu_properties (bfd *abfd ATTRIBUTE_UNUSED,
 #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
 
 #include "elf32-target.h"
 
@@ -6612,7 +7045,7 @@ static const bfd_byte elf_i386_nacl_eh_frame_plt[] =
     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 */
@@ -6627,16 +7060,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 */
-    sizeof (elf_i386_nacl_eh_frame_plt),/* eh_frame_plt_size */
-    NULL,                              /* eh_frame_plt_got */
-    0,                                 /* eh_frame_plt_got_size */
+    sizeof (elf_i386_nacl_eh_frame_plt) /* eh_frame_plt_size */
   };
 
 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
@@ -6681,9 +7111,8 @@ elf32_i386_nacl_elf_object_p (bfd *abfd)
 
 static const struct elf_i386_backend_data elf_i386_vxworks_arch_bed =
   {
-    &elf_i386_plt,                      /* plt */
     0x90,                               /* plt0_pad_byte */
-    1,                                  /* is_vxworks */
+    is_vxworks                          /* os */
   };
 
 #undef elf_backend_arch_data
index cd85a35..c56b3db 100644 (file)
@@ -1,3 +1,17 @@
+2017-05-08  H.J. Lu  <hongjiu.lu@intel.com>
+
+       * testsuite/ld-i386/i386.exp: Add some -z now tests.
+       * testsuite/ld-i386/plt-pic2.dd: New file.
+       * testsuite/ld-i386/plt2.dd: Likewise.
+       * testsuite/ld-i386/plt2.rd: Likewise.
+       * testsuite/ld-i386/plt2.s: Likewise.
+       * testsuite/ld-ifunc/ifunc-16-i386-now.d: Likewise.
+       * testsuite/ld-ifunc/ifunc-2-i386-now.d: Likewise.
+       * testsuite/ld-ifunc/ifunc-2-local-i386-now.d: Likewise.
+       * testsuite/ld-ifunc/pr17154-i386-now.d: Likewise.
+       * testsuite/ld-i386/pr20830.d: Update the .plt.got section
+       with func@plt.
+
 2017-05-08  Thomas Preud'homme  <thomas.preudhomme@arm.com>
 
        * testsuite/ld-arm/arm-elf.exp
index a709bcf..5cb741f 100644 (file)
@@ -1160,6 +1160,27 @@ if { !([istarget "i?86-*-linux*"]
     return
 }
 
+run_ld_link_tests [list \
+    [list \
+       "basic PLT generation (non-PIC, -z now)" \
+       "-z now -melf_i386 tmpdir/libpltlib.so" \
+       "" \
+       "--32" \
+       {plt2.s} \
+       {{readelf -SW plt2.rd} {objdump -dwr plt2.dd}} \
+       "plt2" \
+    ] \
+    [list \
+       "basic PLT generation (PIC, -z now)" \
+       "-z now -shared -melf_i386 tmpdir/libpltlib.so" \
+       "" \
+       "--32" \
+       {plt-pic.s} \
+       {{objdump -dwr plt-pic2.dd}} \
+       "plt-pic2.so" \
+    ] \
+]
+
 # Linux only tests
 run_dump_test "pltgot-1"
 run_dump_test "pltgot-2"
diff --git a/ld/testsuite/ld-i386/plt-pic2.dd b/ld/testsuite/ld-i386/plt-pic2.dd
new file mode 100644 (file)
index 0000000..aa311fe
--- /dev/null
@@ -0,0 +1,33 @@
+#source: plt-pic.s
+#as: --32
+#ld: -z now -shared -melf_i386
+#objdump: -dwr
+#target: i?86-*-*
+
+.*: +file format .*
+
+
+Disassembly of section .plt:
+
+0+180 <.plt>:
+ +[a-f0-9]+:   ff b3 04 00 00 00       pushl  0x4\(%ebx\)
+ +[a-f0-9]+:   ff a3 08 00 00 00       jmp    \*0x8\(%ebx\)
+ +[a-f0-9]+:   00 00                   add    %al,\(%eax\)
+       ...
+
+Disassembly of section .plt.got:
+
+0+190 <fn1@plt>:
+ +[a-f0-9]+:   ff a3 f8 ff ff ff       jmp    \*-0x8\(%ebx\)
+ +[a-f0-9]+:   66 90                   xchg   %ax,%ax
+
+0+198 <fn2@plt>:
+ +[a-f0-9]+:   ff a3 fc ff ff ff       jmp    \*-0x4\(%ebx\)
+ +[a-f0-9]+:   66 90                   xchg   %ax,%ax
+
+Disassembly of section .text:
+
+0+1a0 <foo>:
+ +[a-f0-9]+:   e8 eb ff ff ff          call   190 <fn1@plt>
+ +[a-f0-9]+:   e9 ee ff ff ff          jmp    198 <fn2@plt>
+#pass
diff --git a/ld/testsuite/ld-i386/plt2.dd b/ld/testsuite/ld-i386/plt2.dd
new file mode 100644 (file)
index 0000000..1a5ea6f
--- /dev/null
@@ -0,0 +1,35 @@
+#source: plt2.s
+#as: --32
+#ld: -z now -melf_i386
+#objdump: -dwr
+#target: i?86-*-*
+
+.*: +file format .*
+
+
+Disassembly of section .plt:
+
+0+80481c0 <.plt>:
+ +[a-f0-9]+:   ff 35 b4 92 04 08       pushl  0x80492b4
+ +[a-f0-9]+:   ff 25 b8 92 04 08       jmp    \*0x80492b8
+ +[a-f0-9]+:   00 00                   add    %al,\(%eax\)
+       ...
+
+0+80481d0 <fn1@plt>:
+ +[a-f0-9]+:   ff 25 bc 92 04 08       jmp    \*0x80492bc
+ +[a-f0-9]+:   68 00 00 00 00          push   \$0x0
+ +[a-f0-9]+:   e9 e0 ff ff ff          jmp    80481c0 <.plt>
+
+Disassembly of section .plt.got:
+
+0+80481e0 <fn2@plt>:
+ +[a-f0-9]+:   ff 25 ac 92 04 08       jmp    \*0x80492ac
+ +[a-f0-9]+:   66 90                   xchg   %ax,%ax
+
+Disassembly of section .text:
+
+0+80481e8 <_start>:
+ +[a-f0-9]+:   e8 e3 ff ff ff          call   80481d0 <fn1@plt>
+ +[a-f0-9]+:   e8 ee ff ff ff          call   80481e0 <fn2@plt>
+ +[a-f0-9]+:   81 7c 24 04 d0 81 04 08         cmpl   \$0x80481d0,0x4\(%esp\)
+#pass
diff --git a/ld/testsuite/ld-i386/plt2.rd b/ld/testsuite/ld-i386/plt2.rd
new file mode 100644 (file)
index 0000000..11952b4
--- /dev/null
@@ -0,0 +1,9 @@
+#source: plt2.s
+#as: --32
+#ld: -z now -melf_i386
+#readelf: -SW
+#target: i?86-*-*
+
+#...
+ +\[ *[0-9]+\] \.plt +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+20 +.* +AX +0 +0 +16
+#pass
diff --git a/ld/testsuite/ld-i386/plt2.s b/ld/testsuite/ld-i386/plt2.s
new file mode 100644 (file)
index 0000000..d902bac
--- /dev/null
@@ -0,0 +1,7 @@
+       .text
+       .globl _start
+       .type _start,@function
+_start:
+       call fn1
+       call fn2
+       cmpl $fn1, 4(%esp)
index caaadd7..55fe924 100644 (file)
@@ -48,13 +48,13 @@ Disassembly of section .plt:
 
 Disassembly of section .plt.got:
 
-0+180 <.plt.got>:
+0+180 <func@plt>:
  +[a-f0-9]+:   ff a3 fc ff ff ff       jmp    \*-0x4\(%ebx\)
  +[a-f0-9]+:   66 90                   xchg   %ax,%ax
 
 Disassembly of section .text:
 
 0+188 <foo>:
- +[a-f0-9]+:   e8 f3 ff ff ff          call   180 <.plt.got>
+ +[a-f0-9]+:   e8 f3 ff ff ff          call   180 <func@plt>
  +[a-f0-9]+:   8b 83 fc ff ff ff       mov    -0x4\(%ebx\),%eax
 #pass
diff --git a/ld/testsuite/ld-ifunc/ifunc-16-i386-now.d b/ld/testsuite/ld-ifunc/ifunc-16-i386-now.d
new file mode 100644 (file)
index 0000000..088b1f3
--- /dev/null
@@ -0,0 +1,14 @@
+#source: ifunc-16-x86.s
+#ld: -z now -shared -m elf_i386
+#as: --32
+#readelf: -r --wide
+#target: x86_64-*-* i?86-*-*
+#notarget: x86_64-*-nacl* i?86-*-nacl*
+
+Relocation section '.rel.dyn' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ ]+[0-9a-f]+[ ]+R_386_GLOB_DAT[ ]+0+[ ]+ifunc
+
+Relocation section '.rel.plt' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ ]+[0-9a-f]+[ ]+R_386_IRELATIVE[ ]*
diff --git a/ld/testsuite/ld-ifunc/ifunc-2-i386-now.d b/ld/testsuite/ld-ifunc/ifunc-2-i386-now.d
new file mode 100644 (file)
index 0000000..ebe7ded
--- /dev/null
@@ -0,0 +1,36 @@
+#source: ifunc-2-i386.s
+#ld: -z now -m elf_i386 -shared
+#as: --32
+#objdump: -dw
+#target: x86_64-*-* i?86-*-*
+#notarget: x86_64-*-nacl* i?86-*-nacl*
+
+.*: +file format .*
+
+
+Disassembly of section .plt:
+
+0+150 <.plt>:
+ +[a-f0-9]+:   ff b3 04 00 00 00       pushl  0x4\(%ebx\)
+ +[a-f0-9]+:   ff a3 08 00 00 00       jmp    \*0x8\(%ebx\)
+ +[a-f0-9]+:   00 00                   add    %al,\(%eax\)
+       ...
+
+0+160 <\*ABS\*@plt>:
+ +[a-f0-9]+:   ff a3 0c 00 00 00       jmp    \*0xc\(%ebx\)
+ +[a-f0-9]+:   68 00 00 00 00          push   \$0x0
+ +[a-f0-9]+:   e9 e0 ff ff ff          jmp    150 <.plt>
+
+Disassembly of section .text:
+
+0+170 <foo>:
+ +[a-f0-9]+:   c3                      ret    
+
+0+171 <bar>:
+ +[a-f0-9]+:   e8 00 00 00 00          call   176 <bar\+0x5>
+ +[a-f0-9]+:   5b                      pop    %ebx
+ +[a-f0-9]+:   81 c3 9e 10 00 00       add    \$0x109e,%ebx
+ +[a-f0-9]+:   e8 de ff ff ff          call   160 <\*ABS\*@plt>
+ +[a-f0-9]+:   8d 83 4c ef ff ff       lea    -0x10b4\(%ebx\),%eax
+ +[a-f0-9]+:   c3                      ret    
+#pass
diff --git a/ld/testsuite/ld-ifunc/ifunc-2-local-i386-now.d b/ld/testsuite/ld-ifunc/ifunc-2-local-i386-now.d
new file mode 100644 (file)
index 0000000..7cb2e54
--- /dev/null
@@ -0,0 +1,36 @@
+#source: ifunc-2-local-i386.s
+#ld: -z now -m elf_i386 -shared
+#as: --32
+#objdump: -dw
+#target: x86_64-*-* i?86-*-*
+#notarget: x86_64-*-nacl* i?86-*-nacl*
+
+.*: +file format .*
+
+
+Disassembly of section .plt:
+
+0+140 <.plt>:
+ +[a-f0-9]+:   ff b3 04 00 00 00       pushl  0x4\(%ebx\)
+ +[a-f0-9]+:   ff a3 08 00 00 00       jmp    \*0x8\(%ebx\)
+ +[a-f0-9]+:   00 00                   add    %al,\(%eax\)
+       ...
+
+0+150 <\*ABS\*@plt>:
+ +[a-f0-9]+:   ff a3 0c 00 00 00       jmp    \*0xc\(%ebx\)
+ +[a-f0-9]+:   68 00 00 00 00          push   \$0x0
+ +[a-f0-9]+:   e9 e0 ff ff ff          jmp    140 <.plt>
+
+Disassembly of section .text:
+
+0+160 <__GI_foo>:
+ +[a-f0-9]+:   c3                      ret    
+
+0+161 <bar>:
+ +[a-f0-9]+:   e8 00 00 00 00          call   166 <bar\+0x5>
+ +[a-f0-9]+:   5b                      pop    %ebx
+ +[a-f0-9]+:   81 c3 9e 10 00 00       add    \$0x109e,%ebx
+ +[a-f0-9]+:   e8 de ff ff ff          call   150 <\*ABS\*@plt>
+ +[a-f0-9]+:   8d 83 4c ef ff ff       lea    -0x10b4\(%ebx\),%eax
+ +[a-f0-9]+:   c3                      ret    
+#pass
diff --git a/ld/testsuite/ld-ifunc/pr17154-i386-now.d b/ld/testsuite/ld-ifunc/pr17154-i386-now.d
new file mode 100644 (file)
index 0000000..006af67
--- /dev/null
@@ -0,0 +1,52 @@
+#source: pr17154-x86.s
+#ld: -z now -m elf_i386 -shared
+#as: --32
+#objdump: -dw
+#target: x86_64-*-* i?86-*-*
+#notarget: x86_64-*-nacl* i?86-*-nacl*
+
+.*: +file format .*
+
+
+Disassembly of section .plt:
+
+0+1d0 <.plt>:
+ +[a-f0-9]+:   ff b3 04 00 00 00       pushl  0x4\(%ebx\)
+ +[a-f0-9]+:   ff a3 08 00 00 00       jmp    \*0x8\(%ebx\)
+ +[a-f0-9]+:   00 00                   add    %al,\(%eax\)
+       ...
+
+0+1e0 <\*ABS\*@plt>:
+ +[a-f0-9]+:   ff a3 0c 00 00 00       jmp    \*0xc\(%ebx\)
+ +[a-f0-9]+:   68 08 00 00 00          push   \$0x8
+ +[a-f0-9]+:   e9 e0 ff ff ff          jmp    1d0 <.plt>
+
+0+1f0 <\*ABS\*@plt>:
+ +[a-f0-9]+:   ff a3 10 00 00 00       jmp    \*0x10\(%ebx\)
+ +[a-f0-9]+:   68 00 00 00 00          push   \$0x0
+ +[a-f0-9]+:   e9 d0 ff ff ff          jmp    1d0 <.plt>
+
+Disassembly of section .plt.got:
+
+0+200 <func1@plt>:
+ +[a-f0-9]+:   ff a3 f8 ff ff ff       jmp    \*-0x8\(%ebx\)
+ +[a-f0-9]+:   66 90                   xchg   %ax,%ax
+
+0+208 <func2@plt>:
+ +[a-f0-9]+:   ff a3 fc ff ff ff       jmp    \*-0x4\(%ebx\)
+ +[a-f0-9]+:   66 90                   xchg   %ax,%ax
+
+Disassembly of section .text:
+
+0+210 <resolve1>:
+ +[a-f0-9]+:   e8 eb ff ff ff          call   200 <func1@plt>
+
+0+215 <g1>:
+ +[a-f0-9]+:   e9 d6 ff ff ff          jmp    1f0 <\*ABS\*@plt>
+
+0+21a <resolve2>:
+ +[a-f0-9]+:   e8 e9 ff ff ff          call   208 <func2@plt>
+
+0+21f <g2>:
+ +[a-f0-9]+:   e9 bc ff ff ff          jmp    1e0 <\*ABS\*@plt>
+#pass