+2014-12-04  H.J. Lu  <hongjiu.lu@intel.com>
+
+       PR binutils/17677
+       * elf-bfd.h (_bfd_elf_ifunc_get_synthetic_symtab): New prototype.
+       * elf-ifunc.c (_bfd_elf_ifunc_get_synthetic_symtab): New
+       function.
+       * elf32-i386.c (elf_i386_plt_sym_val): Removed.
+       (elf_backend_plt_sym_val): Likewise.
+       (elf_i386_get_plt_sym_val): New.
+       (elf_i386_get_synthetic_symtab): Likewise.
+       (bfd_elf32_get_synthetic_symtab): Likewise.
+       * elf64-x86-64.c (elf_x86_64_plt_sym_val): Removed.
+       (elf_x86_64_plt_sym_val_offset_plt_bnd): Likewise.
+       (elf_backend_plt_sym_val): Likewise.
+       (elf_x86_64_get_plt_sym_val): New.
+       (elf_x86_64_get_synthetic_symtab): Use
+       _bfd_elf_ifunc_get_synthetic_symtab.
+       (bfd_elf64_get_synthetic_symtab): Don't undefine for NaCl.
+
 2014-12-04  Alan Modra  <amodra@gmail.com>
 
        PR 17666
 
 extern bfd_boolean _bfd_elf_allocate_ifunc_dyn_relocs
   (struct bfd_link_info *, struct elf_link_hash_entry *,
    struct elf_dyn_relocs **, unsigned int, unsigned int, unsigned int);
+extern long _bfd_elf_ifunc_get_synthetic_symtab
+  (bfd *, long, asymbol **, long, asymbol **, asymbol **, asection *,
+   bfd_vma *(*) (bfd *, asymbol **, asection *, asection *));
 
 extern void elf_append_rela (bfd *, asection *, Elf_Internal_Rela *);
 extern void elf_append_rel (bfd *, asection *, Elf_Internal_Rela *);
 
 
   return TRUE;
 }
+
+/* Similar to _bfd_elf_get_synthetic_symtab, optimized for unsorted PLT
+   entries.  PLT is the PLT section.  PLT_SYM_VAL is a function pointer
+   which returns an array of PLT entry symbol values.  */
+
+long
+_bfd_elf_ifunc_get_synthetic_symtab
+  (bfd *abfd, long symcount ATTRIBUTE_UNUSED,
+   asymbol **syms ATTRIBUTE_UNUSED, long dynsymcount, asymbol **dynsyms,
+   asymbol **ret, asection *plt,
+   bfd_vma *(*get_plt_sym_val) (bfd *, asymbol **, asection *, asection *))
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  asection *relplt;
+  asymbol *s;
+  const char *relplt_name;
+  bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
+  arelent *p;
+  long count, i, n;
+  size_t size;
+  Elf_Internal_Shdr *hdr;
+  char *names;
+  bfd_vma *plt_sym_val;
+
+  *ret = NULL;
+
+  if (plt == NULL)
+    return 0;
+
+  if ((abfd->flags & (DYNAMIC | EXEC_P)) == 0)
+    return 0;
+
+  if (dynsymcount <= 0)
+    return 0;
+
+  relplt_name = bed->relplt_name;
+  if (relplt_name == NULL)
+    relplt_name = bed->rela_plts_and_copies_p ? ".rela.plt" : ".rel.plt";
+  relplt = bfd_get_section_by_name (abfd, relplt_name);
+  if (relplt == NULL)
+    return 0;
+
+  hdr = &elf_section_data (relplt)->this_hdr;
+  if (hdr->sh_link != elf_dynsymtab (abfd)
+      || (hdr->sh_type != SHT_REL && hdr->sh_type != SHT_RELA))
+    return 0;
+
+  slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
+  if (! (*slurp_relocs) (abfd, relplt, dynsyms, TRUE))
+    return -1;
+
+  count = relplt->size / hdr->sh_entsize;
+  size = count * sizeof (asymbol);
+  p = relplt->relocation;
+  for (i = 0; i < count; i++, p += bed->s->int_rels_per_ext_rel)
+    {
+      size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
+      if (p->addend != 0)
+       {
+#ifdef BFD64
+         size += sizeof ("+0x") - 1 + 8 + 8 * (bed->s->elfclass == ELFCLASS64);
+#else
+         size += sizeof ("+0x") - 1 + 8;
+#endif
+       }
+    }
+
+  plt_sym_val = get_plt_sym_val (abfd, dynsyms, plt, relplt);
+  if (plt_sym_val == NULL)
+    return -1;
+
+  s = *ret = (asymbol *) bfd_malloc (size);
+  if (s == NULL)
+    {
+      free (plt_sym_val);
+      return -1;
+    }
+
+  names = (char *) (s + count);
+  p = relplt->relocation;
+  n = 0;
+  for (i = 0; i < count; i++, p += bed->s->int_rels_per_ext_rel)
+    {
+      size_t len;
+      bfd_vma addr;
+
+      addr = plt_sym_val[i];
+      if (addr == (bfd_vma) -1)
+       continue;
+
+      *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;
+      s->section = plt;
+      s->value = addr - plt->vma;
+      s->name = names;
+      s->udata.p = NULL;
+      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)
+           ;
+         len = strlen (a);
+         memcpy (names, a, len);
+         names += len;
+       }
+      memcpy (names, "@plt", sizeof ("@plt"));
+      names += sizeof ("@plt");
+      ++s, ++n;
+    }
+
+  free (plt_sym_val);
+
+  return n;
+}
 
   return TRUE;
 }
 
-/* Return address in section PLT for the Ith GOTPLT relocation, for
-   relocation REL or (bfd_vma) -1 if it should not be included.  */
+/* Return an array of PLT entry symbol values.  */
 
-static bfd_vma
-elf_i386_plt_sym_val (bfd_vma i, const asection *plt, const arelent *rel)
+static bfd_vma *
+elf_i386_get_plt_sym_val (bfd *abfd, asymbol **dynsyms, asection *plt,
+                         asection *relplt)
 {
-  bfd *abfd;
-  const struct elf_i386_backend_data *bed;
+  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;
+    }
 
-  /* Only match R_386_JUMP_SLOT and R_386_IRELATIVE.  */
-  if (rel->howto->type != R_386_JUMP_SLOT
-      && rel->howto->type != R_386_IRELATIVE)
-    return (bfd_vma) -1;
+  slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
+  if (! (*slurp_relocs) (abfd, relplt, dynsyms, TRUE))
+    goto bad_return;
 
-  abfd = plt->owner;
-  bed = get_elf_i386_backend_data (abfd);
-  plt_offset = bed->plt->plt_entry_size;
+  hdr = &elf_section_data (relplt)->this_hdr;
+  count = relplt->size / hdr->sh_entsize;
+
+  plt_sym_val = (bfd_vma *) bfd_malloc (sizeof (bfd_vma) * count);
+  if (plt_sym_val == NULL)
+    goto bad_return;
 
-  if (elf_elfheader (abfd)->e_ident[EI_OSABI] != ELFOSABI_GNU)
-    return plt->vma + (i + 1) * plt_offset;
+  for (i = 0; i < count; i++, p++)
+    plt_sym_val[i] = -1;
 
-  while (plt_offset < plt->size)
+  plt_offset = bed->plt->plt_entry_size;
+  p = relplt->relocation;
+  for (i = 0; i < count; i++, p++)
     {
-      bfd_vma reloc_offset;
-      bfd_byte reloc_offset_raw[4];
-
-      if (!bfd_get_section_contents (abfd, (asection *) plt,
-                                    reloc_offset_raw,
-                                    plt_offset + bed->plt->plt_reloc_offset,
-                                    sizeof (reloc_offset_raw)))
-       return (bfd_vma) -1;
-
-      reloc_offset = H_GET_32 (abfd, reloc_offset_raw);
-      if (reloc_offset == i * sizeof (Elf32_External_Rel))
-       return plt->vma + plt_offset;
+      long reloc_index;
+
+      if (p->howto->type != R_386_JUMP_SLOT
+         && p->howto->type != R_386_IRELATIVE)
+       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)
+       abort ();
+      plt_sym_val[reloc_index] = plt->vma + plt_offset;
       plt_offset += bed->plt->plt_entry_size;
     }
 
-  abort ();
+  free (plt_contents);
+
+  return plt_sym_val;
+}
+
+/* Similar to _bfd_elf_get_synthetic_symtab.  */
+
+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);
 }
 
 /* Return TRUE if symbol should be hashed in the `.gnu.hash' section.  */
 #define bfd_elf32_bfd_link_hash_table_create  elf_i386_link_hash_table_create
 #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 elf_backend_adjust_dynamic_symbol     elf_i386_adjust_dynamic_symbol
 #define elf_backend_relocs_compatible        _bfd_elf_relocs_compatible
 #define elf_backend_always_size_sections      elf_i386_always_size_sections
 #define elf_backend_omit_section_dynsym \
   ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
-#define elf_backend_plt_sym_val                      elf_i386_plt_sym_val
 #define elf_backend_hash_symbol                      elf_i386_hash_symbol
 #define elf_backend_add_symbol_hook           elf_i386_add_symbol_hook
 
 
   return TRUE;
 }
 
-/* Return address in section PLT for the Ith GOTPLT relocation, for
-   relocation REL or (bfd_vma) -1 if it should not be included.  */
+/* Return an array of PLT entry symbol values.  */
 
-static bfd_vma
-elf_x86_64_plt_sym_val (bfd_vma i, const asection *plt,
-                       const arelent *rel)
+static bfd_vma *
+elf_x86_64_get_plt_sym_val (bfd *abfd, asymbol **dynsyms, asection *plt,
+                           asection *relplt)
 {
-  bfd *abfd;
-  const struct elf_x86_64_backend_data *bed;
+  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_x86_64_backend_data *bed;
+  Elf_Internal_Shdr *hdr;
+  asection *plt_bnd;
 
-  /* Only match R_X86_64_JUMP_SLOT and R_X86_64_IRELATIVE.  */
-  if (rel->howto->type != R_X86_64_JUMP_SLOT
-      && rel->howto->type != R_X86_64_IRELATIVE)
-    return (bfd_vma) -1;
-
-  abfd = plt->owner;
-  bed = get_elf_x86_64_backend_data (abfd);
-  plt_offset = bed->plt_entry_size;
-
-  if (elf_elfheader (abfd)->e_ident[EI_OSABI] != ELFOSABI_GNU)
-    return plt->vma + (i + 1) * plt_offset;
+  /* Get the .plt section contents.  PLT passed down may point to the
+     .plt.bnd section.  Make sure that PLT always points to the .plt
+     section.  */
+  plt_bnd = bfd_get_section_by_name (abfd, ".plt.bnd");
+  if (plt_bnd)
+    {
+      if (plt != plt_bnd)
+       abort ();
+      plt = bfd_get_section_by_name (abfd, ".plt");
+      if (plt == NULL)
+       abort ();
+      bed = &elf_x86_64_bnd_arch_bed;
+    }
+  else
+    bed = get_elf_x86_64_backend_data (abfd);
 
-  while (plt_offset < plt->size)
+  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))
     {
-      bfd_vma reloc_index;
-      bfd_byte reloc_index_raw[4];
-
-      if (!bfd_get_section_contents (abfd, (asection *) plt,
-                                    reloc_index_raw,
-                                    plt_offset + bed->plt_reloc_offset,
-                                    sizeof (reloc_index_raw)))
-       return (bfd_vma) -1;
-
-      reloc_index = H_GET_32 (abfd, reloc_index_raw);
-      if (reloc_index == i)
-       return plt->vma + plt_offset;
-      plt_offset += bed->plt_entry_size;
+bad_return:
+      free (plt_contents);
+      return NULL;
     }
 
-  abort ();
-}
+  slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
+  if (! (*slurp_relocs) (abfd, relplt, dynsyms, TRUE))
+    goto bad_return;
 
-/* Return offset in .plt.bnd section for the Ith GOTPLT relocation with
-   PLT section, or (bfd_vma) -1 if it should not be included.  */
+  hdr = &elf_section_data (relplt)->this_hdr;
+  count = relplt->size / hdr->sh_entsize;
 
-static bfd_vma
-elf_x86_64_plt_sym_val_offset_plt_bnd (bfd_vma i, const asection *plt)
-{
-  const struct elf_x86_64_backend_data *bed = &elf_x86_64_bnd_arch_bed;
-  bfd *abfd = plt->owner;
-  bfd_vma plt_offset = bed->plt_entry_size;
+  plt_sym_val = (bfd_vma *) bfd_malloc (sizeof (bfd_vma) * count);
+  if (plt_sym_val == NULL)
+    goto bad_return;
 
-  if (elf_elfheader (abfd)->e_ident[EI_OSABI] != ELFOSABI_GNU)
-    return i * sizeof (elf_x86_64_legacy_plt2_entry);
+  for (i = 0; i < count; i++, p++)
+    plt_sym_val[i] = -1;
 
-  while (plt_offset < plt->size)
+  plt_offset = bed->plt_entry_size;
+  p = relplt->relocation;
+  for (i = 0; i < count; i++, p++)
     {
-      bfd_vma reloc_index;
-      bfd_byte reloc_index_raw[4];
+      long reloc_index;
 
-      if (!bfd_get_section_contents (abfd, (asection *) plt,
-                                    reloc_index_raw,
-                                    plt_offset + bed->plt_reloc_offset,
-                                    sizeof (reloc_index_raw)))
-       return (bfd_vma) -1;
+      if (p->howto->type != R_X86_64_JUMP_SLOT
+         && p->howto->type != R_X86_64_IRELATIVE)
+       continue;
 
-      reloc_index = H_GET_32 (abfd, reloc_index_raw);
-      if (reloc_index == i)
+      reloc_index = H_GET_32 (abfd, (plt_contents + plt_offset
+                                    + bed->plt_reloc_offset));
+      if (reloc_index >= count)
+       abort ();
+      if (plt_bnd)
        {
          /* This is the index in .plt section.  */
          long plt_index = plt_offset / bed->plt_entry_size;
-         /* Return the offset in .plt.bnd section.  */
-         return (plt_index - 1) * sizeof (elf_x86_64_legacy_plt2_entry);
+         /* Store VMA + the offset in .plt.bnd section.  */
+         plt_sym_val[reloc_index] =
+           (plt_bnd->vma
+            + (plt_index - 1) * sizeof (elf_x86_64_legacy_plt2_entry));
        }
+      else
+       plt_sym_val[reloc_index] = plt->vma + plt_offset;
       plt_offset += bed->plt_entry_size;
     }
 
-  abort ();
+  free (plt_contents);
+
+  return plt_sym_val;
 }
 
 /* Similar to _bfd_elf_get_synthetic_symtab, with .plt.bnd section
                                 asymbol **dynsyms,
                                 asymbol **ret)
 {
-  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
-  asection *relplt;
-  asymbol *s;
-  bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
-  arelent *p;
-  long count, i, n;
-  size_t size;
-  Elf_Internal_Shdr *hdr;
-  char *names;
-  asection *plt, *plt_push;
-
-  plt_push = bfd_get_section_by_name (abfd, ".plt");
-  if (plt_push == NULL)
-    return 0;
-
-  plt = bfd_get_section_by_name (abfd, ".plt.bnd");
-  /* Use the generic ELF version if there is no .plt.bnd section.  */
+  /* Pass the .plt.bnd section to _bfd_elf_ifunc_get_synthetic_symtab
+     as PLT if it exists.  */
+  asection *plt = bfd_get_section_by_name (abfd, ".plt.bnd");
   if (plt == NULL)
-    return _bfd_elf_get_synthetic_symtab (abfd, symcount, syms,
-                                         dynsymcount, dynsyms, ret);
-
-  *ret = NULL;
-
-  if ((abfd->flags & (DYNAMIC | EXEC_P)) == 0)
-    return 0;
-
-  if (dynsymcount <= 0)
-    return 0;
-
-  relplt = bfd_get_section_by_name (abfd, ".rela.plt");
-  if (relplt == NULL)
-    return 0;
-
-  hdr = &elf_section_data (relplt)->this_hdr;
-  if (hdr->sh_link != elf_dynsymtab (abfd)
-      || (hdr->sh_type != SHT_REL && hdr->sh_type != SHT_RELA))
-    return 0;
-
-  slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
-  if (! (*slurp_relocs) (abfd, relplt, dynsyms, TRUE))
-    return -1;
-
-  count = relplt->size / hdr->sh_entsize;
-  size = count * sizeof (asymbol);
-  p = relplt->relocation;
-  for (i = 0; i < count; i++, p += bed->s->int_rels_per_ext_rel)
-    {
-      size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
-      if (p->addend != 0)
-       size += sizeof ("+0x") - 1 + 8 + 8;
-    }
-
-  s = *ret = (asymbol *) bfd_malloc (size);
-  if (s == NULL)
-    return -1;
-
-  names = (char *) (s + count);
-  p = relplt->relocation;
-  n = 0;
-  for (i = 0; i < count; i++, p++)
-    {
-      bfd_vma offset;
-      size_t len;
-
-      if (p->howto->type != R_X86_64_JUMP_SLOT
-         && p->howto->type != R_X86_64_IRELATIVE)
-       continue;
-
-      offset = elf_x86_64_plt_sym_val_offset_plt_bnd (i, plt_push);
-
-      *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;
-      s->section = plt;
-      s->value = offset;
-      s->name = names;
-      s->udata.p = NULL;
-      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)
-           ;
-         len = strlen (a);
-         memcpy (names, a, len);
-         names += len;
-       }
-      memcpy (names, "@plt", sizeof ("@plt"));
-      names += sizeof ("@plt");
-      ++s, ++n;
-    }
-
-  return n;
+    plt = bfd_get_section_by_name (abfd, ".plt");
+  return _bfd_elf_ifunc_get_synthetic_symtab (abfd, symcount, syms,
+                                             dynsymcount, dynsyms, ret,
+                                             plt,
+                                             elf_x86_64_get_plt_sym_val);
 }
 
 /* Handle an x86-64 specific section when reading an object file.  This
 #define elf_backend_size_dynamic_sections   elf_x86_64_size_dynamic_sections
 #define elf_backend_always_size_sections    elf_x86_64_always_size_sections
 #define elf_backend_init_index_section     _bfd_elf_init_1_index_section
-#define elf_backend_plt_sym_val                    elf_x86_64_plt_sym_val
 #define elf_backend_object_p               elf64_x86_64_elf_object_p
 #define bfd_elf64_mkobject                 elf_x86_64_mkobject
 #define bfd_elf64_get_synthetic_symtab     elf_x86_64_get_synthetic_symtab
 
 #include "elf64-target.h"
 
-#undef bfd_elf64_get_synthetic_symtab
-
 /* Native Client support.  */
 
 static bfd_boolean