[2/7] bfd: Reconstruct dynamic symbol table from PT_DYNAMIC segment

Message ID 20200308234242.1225816-3-hjl.tools@gmail.com
State New
Headers show
Series
  • ELF: Don't require section header on ELF objects
Related show

Commit Message

H.J. Lu March 8, 2020, 11:42 p.m.
When there is no section header in an executable or shared library, we
reconstruct dynamic symbol table from the PT_DYNAMIC segment which
contains DT_HASH/DT_GNU_HASH/DT_MIPS_XHASH, DT_STRTAB, DT_SYMTAB,
DT_STRSZ and DT_SYMENT entries, which can be used to reconstruct dynamic
symbol table.  For DT_HASH, the number of dynamic symbol table entries
equals the number of chains.  For DT_GNU_HASH/DT_MIPS_XHASH, only
defined symbols with non-STB_LOCAL indings are in hash table.  Since
in dynamic symbol table, all symbols with STB_LOCAL binding are placed
before symbols with other bindings and all undefined symbols are placed
before defined ones, the highest symbol index in DT_GNU_HASH and
DT_MIPS_XHASH is the highest dynamic symbol table index.

dt_symtab, dt_symtab_count and dt_strtab are added to elf_obj_tdata to
store dynamic symbol table information.

	PR ld/25617
	* elf-bfd.h (elf_obj_tdata): Add dt_symtab, dt_symtab_count and
	dt_strtab.
	(elf_use_dt_symtab_p): New.
	(_bfd_elf_get_dynamic_symbols): Likewise.
	* elf.c (bfd_elf_get_elf_syms): Use dynamic symbol table if
	neeeded.
	(_bfd_elf_get_dynamic_symtab_upper_bound): Likewise.
	(offset_from_vma): New function.
	(get_hash_table_data): Likewise.
	(_bfd_elf_get_dynamic_symbols): Likewise.
	* elfcode.h (elf_object_p): Call _bfd_elf_get_dynamic_symbols
	to reconstruct dynamic symbol table from PT_DYNAMIC segment if
	there is no section header.
	(elf_slurp_symbol_table): Use dynamic symbol table if neeeded.
	Don't free isymbuf when dynamic symbol table is used.
	* elflink.c (elf_link_is_defined_archive_symbol): Likewise.
	(elf_link_add_object_symbols): Likewise.
---
 bfd/elf-bfd.h |   8 +
 bfd/elf.c     | 443 ++++++++++++++++++++++++++++++++++++++++++++++++++
 bfd/elfcode.h | 133 +++++++++++++--
 bfd/elflink.c | 148 +++++++++++------
 4 files changed, 673 insertions(+), 59 deletions(-)

-- 
2.24.1

Patch

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 5d9e0fedcd..b66dcaa010 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1876,6 +1876,9 @@  struct elf_obj_tdata
   Elf_Internal_Shdr dynversym_hdr;
   Elf_Internal_Shdr dynverref_hdr;
   Elf_Internal_Shdr dynverdef_hdr;
+  Elf_Internal_Sym *dt_symtab;
+  bfd_size_type dt_symtab_count;
+  char *dt_strtab;
   elf_section_list * symtab_shndx_list;
   bfd_vma gp;				/* The gp value */
   unsigned int gp_size;			/* The gp size */
@@ -2030,6 +2033,7 @@  struct elf_obj_tdata
 #define elf_dyn_lib_class(bfd)	(elf_tdata(bfd) -> dyn_lib_class)
 #define elf_bad_symtab(bfd)	(elf_tdata(bfd) -> bad_symtab)
 #define elf_flags_init(bfd)	(elf_tdata(bfd) -> o->flags_init)
+#define elf_use_dt_symtab_p(bfd) (elf_tdata(bfd) -> dt_symtab_count != 0)
 #define elf_known_obj_attributes(bfd) (elf_tdata (bfd) -> known_obj_attributes)
 #define elf_other_obj_attributes(bfd) (elf_tdata (bfd) -> other_obj_attributes)
 #define elf_known_obj_attributes_proc(bfd) \
@@ -2403,6 +2407,10 @@  extern bfd_reloc_status_type bfd_elf_perform_complex_relocation
 extern bfd_boolean _bfd_elf_setup_sections
   (bfd *);
 
+extern bfd_boolean _bfd_elf_get_dynamic_symbols
+  (bfd *, Elf_Internal_Phdr *, Elf_Internal_Phdr *, size_t,
+   bfd_size_type);
+
 extern struct bfd_link_hash_entry *bfd_elf_define_start_stop
   (struct bfd_link_info *, const char *, asection *);
 
diff --git a/bfd/elf.c b/bfd/elf.c
index e6db2ff64d..a2e78ded89 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -405,6 +405,17 @@  bfd_elf_get_elf_syms (bfd *ibfd,
   if (symcount == 0)
     return intsym_buf;
 
+  if (elf_use_dt_symtab_p (ibfd))
+    {
+      /* Use dynamic symbol table.  */
+      if (elf_tdata (ibfd)->dt_symtab_count != (symcount + symoffset))
+	{
+	  bfd_set_error (bfd_error_invalid_operation);
+	  return NULL;
+	}
+      return elf_tdata (ibfd)->dt_symtab + symoffset;
+    }
+
   /* Normal syms might have section extension entries.  */
   shndx_hdr = NULL;
   if (elf_symtab_shndx_list (ibfd) != NULL)
@@ -1881,6 +1892,431 @@  _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
   return FALSE;
 }
 
+/* Find the file offset corresponding to VMA by using the program
+   headers.  */
+
+static file_ptr
+offset_from_vma (Elf_Internal_Phdr *phdrs, size_t phnum, bfd_vma vma,
+		 bfd_vma size)
+{
+  Elf_Internal_Phdr *seg;
+  size_t i;
+
+  for (seg = phdrs, i = 0; i < phnum; ++seg, ++i)
+    if (seg->p_type == PT_LOAD
+	&& vma >= (seg->p_vaddr & -seg->p_align)
+	&& vma + size <= seg->p_vaddr + seg->p_filesz)
+      return (file_ptr) (vma - seg->p_vaddr + seg->p_offset);
+
+  bfd_set_error (bfd_error_invalid_operation);
+  return (file_ptr) -1;
+}
+
+/* Convert hash table to internal form.  */
+
+static bfd_vma *
+get_hash_table_data (bfd *abfd, bfd_size_type number,
+		     unsigned int ent_size, bfd_size_type filesize)
+{
+  unsigned char *e_data = NULL;
+  bfd_vma *i_data = NULL;
+  bfd_size_type size;
+
+  if (ent_size != 4 && ent_size != 8)
+    return NULL;
+
+  /* If the size_t type is smaller than the bfd_size_type, eg because
+     you are building a 32-bit tool on a 64-bit host, then make sure
+     that when (number) is cast to (size_t) no information is lost.  */
+  if (sizeof (size_t) < sizeof (bfd_size_type)
+      && (bfd_size_type) ((size_t) number) != number)
+    {
+      bfd_set_error (bfd_error_file_too_big);
+      return NULL;
+    }
+
+  size = ent_size * number;
+  /* Be kind to memory chekers (eg valgrind, address sanitizer) by not
+     attempting to allocate memory when the read is bound to fail.  */
+  if (size > filesize
+      || number >= ~(size_t) 0 / ent_size
+      || number >= ~(size_t) 0 / sizeof (*i_data))
+    {
+      bfd_set_error (bfd_error_file_too_big);
+      return NULL;
+    }
+
+  e_data = _bfd_malloc_and_read (abfd, size, size);
+  if (e_data == NULL)
+    return NULL;
+
+  i_data = (bfd_vma *) bfd_malloc (number * sizeof (*i_data));
+  if (i_data == NULL)
+    {
+      free (e_data);
+      return NULL;
+    }
+
+  if (ent_size == 4)
+    while (number--)
+      i_data[number] = bfd_get_32 (abfd, e_data + number * ent_size);
+  else
+    while (number--)
+      i_data[number] = bfd_get_64 (abfd, e_data + number * ent_size);
+
+  free (e_data);
+  return i_data;
+}
+
+/* Address of .MIPS.xhash section.  FIXME: What is the best way to
+   support DT_MIPS_XHASH?  */
+#define DT_MIPS_XHASH	       0x70000036
+
+/* Reconstruct dynamic symbol table from PT_DYNAMIC segment.  */
+
+bfd_boolean
+_bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr,
+			      Elf_Internal_Phdr *phdrs, size_t phnum,
+			      bfd_size_type filesize)
+{
+  bfd_byte *extdyn, *extdynend;
+  size_t extdynsize;
+  void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *);
+  bfd_boolean (*swap_symbol_in) (bfd *, const void *, const void *,
+				 Elf_Internal_Sym *);
+  Elf_Internal_Dyn dyn;
+  bfd_vma dt_hash = 0;
+  bfd_vma dt_gnu_hash = 0;
+  bfd_vma dt_mips_xhash = 0;
+  bfd_vma dt_strtab = 0;
+  bfd_vma dt_symtab = 0;
+  bfd_vma dt_strsz = 0;
+  bfd_byte *dynbuf = NULL;
+  char *strbuf = NULL;
+  bfd_vma *gnubuckets = NULL;
+  bfd_vma *gnuchains = NULL;
+  bfd_vma *mipsxlat = NULL;
+  file_ptr saved_filepos, filepos;
+  bfd_boolean res = FALSE;
+  bfd_size_type amt;
+  bfd_byte *esymbuf = NULL, *esym;
+  bfd_size_type symcount;
+  Elf_Internal_Sym *isymbuf = NULL;
+  Elf_Internal_Sym *isym, *isymend;
+  size_t extsym_size;
+  const struct elf_backend_data *bed;
+
+  /* Return TRUE if symbol table is bad.  */
+  if (elf_bad_symtab (abfd))
+    return TRUE;
+
+  /* Return TRUE if DT_HASH/DT_GNU_HASH have bee processed before.  */
+  if (elf_tdata (abfd)->dt_strtab != NULL)
+    return TRUE;
+
+  bed = get_elf_backend_data (abfd);
+
+  /* Save file position for elf_object_p.  */
+  saved_filepos = bfd_tell (abfd);
+
+  if (bfd_seek (abfd, phdr->p_offset, SEEK_SET) != 0)
+    goto error_return;
+
+  dynbuf = _bfd_malloc_and_read (abfd, phdr->p_filesz, phdr->p_filesz);
+  if (dynbuf == NULL)
+    goto error_return;
+
+  extsym_size = bed->s->sizeof_sym;
+  extdynsize = bed->s->sizeof_dyn;
+  swap_dyn_in = bed->s->swap_dyn_in;
+
+  extdyn = dynbuf;
+  if (phdr->p_filesz < extdynsize)
+    goto error_return;
+  extdynend = extdyn + phdr->p_filesz;
+  for (; extdyn <= (extdynend - extdynsize); extdyn += extdynsize)
+    {
+      swap_dyn_in (abfd, extdyn, &dyn);
+
+      if (dyn.d_tag == DT_NULL)
+	break;
+
+      switch (dyn.d_tag)
+	{
+	case DT_HASH:
+	  dt_hash = dyn.d_un.d_val;
+	  break;
+	case DT_GNU_HASH:
+	  if (bed->elf_machine_code != EM_MIPS
+	      && bed->elf_machine_code != EM_MIPS_RS3_LE)
+	    dt_gnu_hash = dyn.d_un.d_val;
+	  break;
+	case DT_STRTAB:
+	  dt_strtab = dyn.d_un.d_val;
+	  break;
+	case DT_SYMTAB:
+	  dt_symtab = dyn.d_un.d_val;
+	  break;
+	case DT_STRSZ:
+	  dt_strsz = dyn.d_un.d_val;
+	  break;
+	case DT_SYMENT:
+	  if (dyn.d_un.d_val != extsym_size)
+	    goto error_return;
+	  break;
+	default:
+	  if (dyn.d_tag == DT_MIPS_XHASH
+	      && (bed->elf_machine_code == EM_MIPS
+		  || bed->elf_machine_code == EM_MIPS_RS3_LE))
+	    {
+	      dt_gnu_hash = dyn.d_un.d_val;
+	      dt_mips_xhash = dyn.d_un.d_val;
+	    }
+	  break;
+	}
+    }
+
+  /* Check if we can reconstruct dynamic symbol table from PT_DYNAMIC
+     segment.  */
+  if ((!dt_hash && !dt_gnu_hash)
+      || !dt_strtab
+      || !dt_symtab
+      || !dt_strsz)
+    goto error_return;
+
+  /* Get dynamic string stable.  */
+  filepos = offset_from_vma (phdrs, phnum, dt_strtab, dt_strsz);
+  if (filepos == (file_ptr) -1
+      || bfd_seek (abfd, filepos, SEEK_SET) != 0)
+    goto error_return;
+
+  /* Dynamic string stable must be valid until ABFD is closed.  */
+  strbuf = (char *) _bfd_alloc_and_read (abfd, dt_strsz, dt_strsz);
+  if (strbuf == NULL)
+    goto error_return;
+
+  /* Get the real symbol count from DT_HASH or DT_GNU_HASH.  Prefer
+     DT_HASH since it is simpler than DT_GNU_HASH.  */
+  if (dt_hash)
+    {
+      unsigned char nb[16];
+      unsigned int hash_ent_size;
+
+      switch (bed->elf_machine_code)
+	{
+	case EM_ALPHA:
+	case EM_S390:
+	case EM_S390_OLD:
+	  if (bed->s->elfclass == ELFCLASS64)
+	    {
+	      hash_ent_size = 8;
+	      break;
+	    }
+	  /* FALLTHROUGH */
+	default:
+	  hash_ent_size = 4;
+	  break;
+	}
+
+      filepos = offset_from_vma (phdrs, phnum, dt_hash, sizeof (nb));
+      if (filepos == (file_ptr) -1
+	  || bfd_seek (abfd, filepos, SEEK_SET) != 0
+	  || (bfd_bread (nb, 2 * hash_ent_size, abfd)
+	      != (2 * hash_ent_size)))
+	goto error_return;
+
+      /* The number of dynamic symbol table entries equals the number
+	 of chains.  */
+      if (hash_ent_size == 8)
+	symcount = bfd_get_64 (abfd, nb + hash_ent_size);
+      else
+	symcount = bfd_get_32 (abfd, nb + hash_ent_size);
+    }
+  else
+    {
+      /* For DT_GNU_HASH, only defined symbols with non-STB_LOCAL
+	 bindings are in hash table.  Since in dynamic symbol table,
+	 all symbols with STB_LOCAL binding are placed before symbols
+	 with other bindings and all undefined symbols are placed
+	 before defined ones, the highest symbol index in DT_GNU_HASH
+	 is the highest dynamic symbol table index.  */
+      unsigned char nb[16];
+      bfd_vma ngnubuckets;
+      bfd_vma gnusymidx;
+      size_t i, ngnuchains;
+      bfd_vma maxchain = 0xffffffff, bitmaskwords;
+      bfd_vma buckets_vma;
+
+      filepos = offset_from_vma (phdrs, phnum, dt_gnu_hash,
+				 sizeof (nb));
+      if (filepos == (file_ptr) -1
+	  || bfd_seek (abfd, filepos, SEEK_SET) != 0
+	  || bfd_bread (nb, sizeof (nb), abfd) != sizeof (nb))
+	goto error_return;
+
+      ngnubuckets = bfd_get_32 (abfd, nb);
+      gnusymidx = bfd_get_32 (abfd, nb + 4);
+      bitmaskwords = bfd_get_32 (abfd, nb + 8);
+      buckets_vma = dt_gnu_hash + 16;
+      if (bed->s->elfclass == ELFCLASS32)
+	buckets_vma += bitmaskwords * 4;
+      else
+	buckets_vma += bitmaskwords * 8;
+      filepos = offset_from_vma (phdrs, phnum, buckets_vma, 4);
+      if (filepos == (file_ptr) -1
+	  || bfd_seek (abfd, filepos, SEEK_SET) != 0)
+	goto error_return;
+
+      gnubuckets = get_hash_table_data (abfd, ngnubuckets, 4, filesize);
+      if (gnubuckets == NULL)
+	goto error_return;
+
+      for (i = 0; i < ngnubuckets; i++)
+	if (gnubuckets[i] != 0)
+	  {
+	    if (gnubuckets[i] < gnusymidx)
+	      goto error_return;
+
+	    if (maxchain == 0xffffffff || gnubuckets[i] > maxchain)
+	      maxchain = gnubuckets[i];
+	  }
+
+      if (maxchain == 0xffffffff)
+	goto error_return;
+
+      maxchain -= gnusymidx;
+      filepos = offset_from_vma (phdrs, phnum,
+				 (buckets_vma +
+				  4 * (ngnubuckets + maxchain)),
+				 4);
+      if (filepos == (file_ptr) -1
+	  || bfd_seek (abfd, filepos, SEEK_SET) != 0)
+	goto error_return;
+
+      do
+	{
+	  if (bfd_bread (nb, 4, abfd) != 4)
+	    goto error_return;
+	  ++maxchain;
+	  if (maxchain == 0)
+	    goto error_return;
+	}
+      while ((bfd_get_32 (abfd, nb) & 1) == 0);
+
+      filepos = offset_from_vma (phdrs, phnum,
+				 (buckets_vma + 4 * ngnubuckets),
+				 4);
+      if (filepos == (file_ptr) -1
+	  || bfd_seek (abfd, filepos, SEEK_SET) != 0)
+	goto error_return;
+
+      gnuchains = get_hash_table_data (abfd, maxchain, 4, filesize);
+      if (gnubuckets == NULL)
+	goto error_return;
+      ngnuchains = maxchain;
+
+      if (dt_mips_xhash)
+	{
+	  filepos = offset_from_vma (phdrs, phnum,
+				     (buckets_vma
+				      + 4 * (ngnubuckets + maxchain)),
+				     4);
+	  if (filepos == (file_ptr) -1
+	      || bfd_seek (abfd, filepos, SEEK_SET) != 0)
+	    goto error_return;
+
+	  mipsxlat = get_hash_table_data (abfd, maxchain, 4, filesize);
+	  if (mipsxlat == NULL)
+	    goto error_return;
+	}
+
+      symcount = 0;
+      for (i = 0; i < ngnubuckets; ++i)
+	if (gnubuckets[i] != 0)
+	  {
+	    bfd_vma si = gnubuckets[i];
+	    bfd_vma off = si - gnusymidx;
+	    do
+	      {
+		if (mipsxlat)
+		  {
+		    if (mipsxlat[off] >= symcount)
+		      symcount = mipsxlat[off] + 1;
+		  }
+		else
+		  {
+		    if (si >= symcount)
+		      symcount = si + 1;
+		  }
+		si++;
+	      }
+	    while (off < ngnuchains && (gnuchains[off++] & 1) == 0);
+	  }
+    }
+
+  /* Swap in dynamic symbol stable.  */
+  if (_bfd_mul_overflow (symcount, extsym_size, &amt))
+    {
+      bfd_set_error (bfd_error_file_too_big);
+      goto error_return;
+    }
+
+  filepos = offset_from_vma (phdrs, phnum, dt_symtab, amt);
+  if (filepos == (file_ptr) -1
+      || bfd_seek (abfd, filepos, SEEK_SET) != 0)
+    goto error_return;
+  esymbuf = _bfd_malloc_and_read (abfd, amt, amt);
+  if (esymbuf == NULL)
+    goto error_return;
+
+  if (_bfd_mul_overflow (symcount, sizeof (Elf_Internal_Sym), &amt))
+    {
+      bfd_set_error (bfd_error_file_too_big);
+      goto error_return;
+    }
+
+  /* Dynamic symbol stable must be valid until ABFD is closed.  */
+  isymbuf = (Elf_Internal_Sym *) bfd_alloc (abfd, amt);
+  if (isymbuf == NULL)
+    goto error_return;
+
+  swap_symbol_in = bed->s->swap_symbol_in;
+
+  /* Convert the symbols to internal form.  */
+  isymend = isymbuf + symcount;
+  for (esym = esymbuf, isym = isymbuf;
+       isym < isymend;
+       esym += extsym_size, isym++)
+    if (!swap_symbol_in (abfd, esym, NULL, isym)
+	|| isym->st_name >= dt_strsz)
+      {
+	bfd_set_error (bfd_error_invalid_operation);
+	goto error_return;
+      }
+
+  elf_tdata (abfd)->dt_strtab = strbuf;
+  elf_tdata (abfd)->dt_symtab = isymbuf;
+  elf_tdata (abfd)->dt_symtab_count = symcount;
+
+  res = TRUE;
+
+ error_return:
+  /* Restore file position for elf_object_p.  */
+  if (bfd_seek (abfd, saved_filepos, SEEK_SET) != 0)
+    res = FALSE;
+  if (dynbuf != NULL)
+    free (dynbuf);
+  if (esymbuf != NULL)
+    free (esymbuf);
+  if (gnubuckets != NULL)
+    free (gnubuckets);
+  if (gnuchains != NULL)
+    free (gnuchains);
+  if (mipsxlat != NULL)
+    free (mipsxlat);
+  return res;
+}
+
 /* Get version string.  */
 
 const char *
@@ -8415,6 +8851,11 @@  _bfd_elf_get_dynamic_symtab_upper_bound (bfd *abfd)
 
   if (elf_dynsymtab (abfd) == 0)
     {
+      /* Check if there is dynamic symbol table.  */
+      symcount = elf_tdata (abfd)->dt_symtab_count;
+      if (symcount)
+	goto compute_symtab_size;
+
       bfd_set_error (bfd_error_invalid_operation);
       return -1;
     }
@@ -8425,6 +8866,8 @@  _bfd_elf_get_dynamic_symtab_upper_bound (bfd *abfd)
       bfd_set_error (bfd_error_file_too_big);
       return -1;
     }
+
+ compute_symtab_size:
   symtab_size = (symcount + 1) * (sizeof (asymbol *));
   if (symcount > 0)
     symtab_size -= sizeof (asymbol *);
diff --git a/bfd/elfcode.h b/bfd/elfcode.h
index 4dde24e02a..40289ca7ce 100644
--- a/bfd/elfcode.h
+++ b/bfd/elfcode.h
@@ -820,6 +820,21 @@  elf_object_p (bfd *abfd)
 	  if (bfd_bread (&x_phdr, sizeof x_phdr, abfd) != sizeof x_phdr)
 	    goto got_no_match;
 	  elf_swap_phdr_in (abfd, &x_phdr, i_phdr);
+	  if (i_phdr->p_filesz != 0)
+	    {
+	      if ((i_phdr->p_offset + i_phdr->p_filesz) > filesize)
+		goto got_no_match;
+	      /* Try to reconstruct dynamic symbol table from PT_DYNAMIC
+		 segment if there is no section header.  */
+	      if (i_phdr->p_type == PT_DYNAMIC
+		  && i_ehdrp->e_shstrndx == 0
+		  && i_ehdrp->e_shoff == 0
+		  && !_bfd_elf_get_dynamic_symbols (abfd, i_phdr,
+						    elf_tdata (abfd)->phdr,
+						    i_ehdrp->e_phnum,
+						    filesize))
+		goto got_no_match;
+	    }
 	}
     }
 
@@ -1189,6 +1204,7 @@  elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)
   Elf_External_Versym *xverbuf = NULL;
   const struct elf_backend_data *ebd;
   size_t amt;
+  asection *sec;
 
   /* Read each raw ELF symbol, converting from external ELF form to
      internal ELF form, and then using the information to create a
@@ -1223,7 +1239,9 @@  elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)
     }
 
   ebd = get_elf_backend_data (abfd);
-  symcount = hdr->sh_size / sizeof (Elf_External_Sym);
+  symcount = elf_tdata (abfd)->dt_symtab_count;
+  if (symcount == 0)
+    symcount = hdr->sh_size / sizeof (Elf_External_Sym);
   if (symcount == 0)
     sym = symbase = NULL;
   else
@@ -1279,7 +1297,11 @@  elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)
 	  memcpy (&sym->internal_elf_sym, isym, sizeof (Elf_Internal_Sym));
 
 	  sym->symbol.the_bfd = abfd;
-	  sym->symbol.name = bfd_elf_sym_name (abfd, hdr, isym, NULL);
+	  if (elf_use_dt_symtab_p (abfd))
+	    sym->symbol.name = (elf_tdata (abfd)->dt_strtab
+				+ isym->st_name);
+	  else
+	    sym->symbol.name = bfd_elf_sym_name (abfd, hdr, isym, NULL);
 	  sym->symbol.value = isym->st_value;
 
 	  if (isym->st_shndx == SHN_UNDEF)
@@ -1313,7 +1335,7 @@  elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)
 		 moment) about the alignment.  */
 	      sym->symbol.value = isym->st_size;
 	    }
-	  else
+	  else if (!elf_use_dt_symtab_p (abfd))
 	    {
 	      sym->symbol.section
 		= bfd_section_from_elf_index (abfd, isym->st_shndx);
@@ -1329,11 +1351,6 @@  elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)
 		}
 	    }
 
-	  /* If this is a relocatable file, then the symbol value is
-	     already section relative.  */
-	  if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0)
-	    sym->symbol.value -= sym->symbol.section->vma;
-
 	  switch (ELF_ST_BIND (isym->st_info))
 	    {
 	    case STB_LOCAL:
@@ -1355,23 +1372,87 @@  elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)
 	    {
 	    case STT_SECTION:
 	      sym->symbol.flags |= BSF_SECTION_SYM | BSF_DEBUGGING;
+	      if (sym->symbol.section == NULL
+		  && elf_use_dt_symtab_p (abfd))
+		sym->symbol.section = bfd_abs_section_ptr;
 	      break;
 	    case STT_FILE:
 	      sym->symbol.flags |= BSF_FILE | BSF_DEBUGGING;
+	      if (sym->symbol.section == NULL
+		  && elf_use_dt_symtab_p (abfd))
+		sym->symbol.section = bfd_abs_section_ptr;
 	      break;
 	    case STT_FUNC:
 	      sym->symbol.flags |= BSF_FUNCTION;
+	      if (sym->symbol.section == NULL
+		  && elf_use_dt_symtab_p (abfd))
+		{
+		  sec = bfd_get_section_by_name (abfd, ".text");
+		  if (sec == NULL)
+		    {
+		      flagword flags = (SEC_ALLOC
+					| SEC_CODE
+					| SEC_LOAD
+					| SEC_LINKER_CREATED);
+		      sec = bfd_make_section_with_flags (abfd,
+							 ".text",
+							 flags);
+		      if (sec == NULL)
+			goto error_return;
+		    }
+		  sym->symbol.section = sec;
+		}
 	      break;
 	    case STT_COMMON:
 	      /* FIXME: Do we have to put the size field into the value field
 		 as we do with symbols in SHN_COMMON sections (see above) ?  */
 	      sym->symbol.flags |= BSF_ELF_COMMON;
+	      if (sym->symbol.section == NULL
+		  && elf_use_dt_symtab_p (abfd))
+		sym->symbol.section = bfd_com_section_ptr;
 	      /* Fall through.  */
 	    case STT_OBJECT:
 	      sym->symbol.flags |= BSF_OBJECT;
+	      if (sym->symbol.section == NULL
+		  && elf_use_dt_symtab_p (abfd))
+		{
+		  sec = bfd_get_section_by_name (abfd, ".data");
+		  if (sec == NULL)
+		    {
+		      flagword flags = (SEC_ALLOC
+					| SEC_DATA
+					| SEC_LOAD
+					| SEC_LINKER_CREATED);
+		      sec = bfd_make_section_with_flags (abfd,
+							 ".data",
+							 flags);
+		      if (sec == NULL)
+			goto error_return;
+		    }
+		  sym->symbol.section = sec;
+		}
 	      break;
 	    case STT_TLS:
 	      sym->symbol.flags |= BSF_THREAD_LOCAL;
+	      if (sym->symbol.section == NULL
+		  && elf_use_dt_symtab_p (abfd))
+		{
+		  sec = bfd_get_section_by_name (abfd, ".tdata");
+		  if (sec == NULL)
+		    {
+		      flagword flags = (SEC_ALLOC
+					| SEC_DATA
+					| SEC_THREAD_LOCAL
+					| SEC_LOAD
+					| SEC_LINKER_CREATED);
+		      sec = bfd_make_section_with_flags (abfd,
+							 ".tdata",
+							 flags);
+		      if (sec == NULL)
+			goto error_return;
+		    }
+		  sym->symbol.section = sec;
+		}
 	      break;
 	    case STT_RELC:
 	      sym->symbol.flags |= BSF_RELC;
@@ -1381,9 +1462,37 @@  elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)
 	      break;
 	    case STT_GNU_IFUNC:
 	      sym->symbol.flags |= BSF_GNU_INDIRECT_FUNCTION;
+	      if (sym->symbol.section == NULL
+		  && elf_use_dt_symtab_p (abfd))
+		{
+		  sec = bfd_get_section_by_name (abfd, ".text");
+		  if (sec == NULL)
+		    {
+		      flagword flags = (SEC_ALLOC
+					| SEC_CODE
+					| SEC_LOAD
+					| SEC_LINKER_CREATED);
+		      sec = bfd_make_section_with_flags (abfd,
+							 ".text",
+							 flags);
+		      if (sec == NULL)
+			goto error_return;
+		    }
+		  sym->symbol.section = sec;
+		}
+	      break;
+	    default:
+	      if (sym->symbol.section == NULL
+		  && elf_use_dt_symtab_p (abfd))
+		sym->symbol.section = bfd_abs_section_ptr;
 	      break;
 	    }
 
+	  /* If this is a relocatable file, then the symbol value is
+	     already section relative.  */
+	  if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0)
+	    sym->symbol.value -= sym->symbol.section->vma;
+
 	  if (dynamic)
 	    sym->symbol.flags |= BSF_DYNAMIC;
 
@@ -1426,14 +1535,18 @@  elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)
 
   if (xverbuf != NULL)
     free (xverbuf);
-  if (isymbuf != NULL && hdr->contents != (unsigned char *) isymbuf)
+  if (isymbuf != NULL
+      && hdr->contents != (unsigned char *) isymbuf
+      && !elf_use_dt_symtab_p (abfd))
     free (isymbuf);
   return symcount;
 
  error_return:
   if (xverbuf != NULL)
     free (xverbuf);
-  if (isymbuf != NULL && hdr->contents != (unsigned char *) isymbuf)
+  if (isymbuf != NULL
+      && hdr->contents != (unsigned char *) isymbuf
+      && !elf_use_dt_symtab_p (abfd))
     free (isymbuf);
   return -1;
 }
diff --git a/bfd/elflink.c b/bfd/elflink.c
index c04712a10f..900c0ecdf1 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -3394,39 +3394,61 @@  elf_link_is_defined_archive_symbol (bfd * abfd, carsym * symdef)
   if (! bfd_check_format (abfd, bfd_object))
     return FALSE;
 
-  /* Select the appropriate symbol table.  If we don't know if the
-     object file is an IR object, give linker LTO plugin a chance to
-     get the correct symbol table.  */
-  if (abfd->plugin_format == bfd_plugin_yes
-#if BFD_SUPPORTS_PLUGINS
-      || (abfd->plugin_format == bfd_plugin_unknown
-	  && bfd_link_plugin_object_p (abfd))
-#endif
-      )
-    {
-      /* Use the IR symbol table if the object has been claimed by
-	 plugin.  */
-      abfd = abfd->plugin_dummy_bfd;
-      hdr = &elf_tdata (abfd)->symtab_hdr;
-    }
-  else if ((abfd->flags & DYNAMIC) == 0 || elf_dynsymtab (abfd) == 0)
-    hdr = &elf_tdata (abfd)->symtab_hdr;
-  else
-    hdr = &elf_tdata (abfd)->dynsymtab_hdr;
-
-  symcount = hdr->sh_size / get_elf_backend_data (abfd)->s->sizeof_sym;
-
-  /* The sh_info field of the symtab header tells us where the
-     external symbols start.  We don't care about the local symbols.  */
-  if (elf_bad_symtab (abfd))
+  extsymcount = elf_tdata (abfd)->dt_symtab_count;
+  if (extsymcount)
     {
-      extsymcount = symcount;
+      /* Find the first global symbol.  */
+      BFD_ASSERT (!elf_bad_symtab (abfd));
+      hdr = NULL;
       extsymoff = 0;
+      isym = elf_tdata (abfd)->dt_symtab;
+      isymend = isym + extsymcount;
+      for (; isym < isymend; isym++)
+	if (ELF_ST_BIND (isym->st_info) == STB_LOCAL)
+	  {
+	    extsymcount--;
+	    extsymoff++;
+	  }
+	else
+	  break;
     }
   else
     {
-      extsymcount = symcount - hdr->sh_info;
-      extsymoff = hdr->sh_info;
+      /* Select the appropriate symbol table.  If we don't know if the
+	 object file is an IR object, give linker LTO plugin a chance to
+	 get the correct symbol table.  */
+      if (abfd->plugin_format == bfd_plugin_yes
+#if BFD_SUPPORTS_PLUGINS
+	  || (abfd->plugin_format == bfd_plugin_unknown
+	      && bfd_link_plugin_object_p (abfd))
+#endif
+	 )
+	{
+	  /* Use the IR symbol table if the object has been claimed by
+	     plugin.  */
+	  abfd = abfd->plugin_dummy_bfd;
+	  hdr = &elf_tdata (abfd)->symtab_hdr;
+	}
+      else if ((abfd->flags & DYNAMIC) == 0 || elf_dynsymtab (abfd) == 0)
+	hdr = &elf_tdata (abfd)->symtab_hdr;
+      else
+	hdr = &elf_tdata (abfd)->dynsymtab_hdr;
+
+      symcount = hdr->sh_size / get_elf_backend_data (abfd)->s->sizeof_sym;
+
+      /* The sh_info field of the symtab header tells us where the
+	 external symbols start.  We don't care about the local
+	 symbols.  */
+      if (elf_bad_symtab (abfd))
+	{
+	  extsymcount = symcount;
+	  extsymoff = 0;
+	}
+      else
+	{
+	  extsymcount = symcount - hdr->sh_info;
+	  extsymoff = hdr->sh_info;
+	}
     }
 
   if (extsymcount == 0)
@@ -3444,8 +3466,11 @@  elf_link_is_defined_archive_symbol (bfd * abfd, carsym * symdef)
     {
       const char *name;
 
-      name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
-					      isym->st_name);
+      if (hdr)
+	name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
+						isym->st_name);
+      else
+	name = elf_tdata (abfd)->dt_strtab + isym->st_name;
       if (name == NULL)
 	break;
 
@@ -3456,7 +3481,8 @@  elf_link_is_defined_archive_symbol (bfd * abfd, carsym * symdef)
 	}
     }
 
-  free (isymbuf);
+  if (!elf_use_dt_symtab_p (abfd))
+    free (isymbuf);
 
   return result;
 }
@@ -4276,25 +4302,46 @@  elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
      will only see the .dynsym symbol table, so there is no reason to
      look at .symtab for a dynamic object.  */
 
-  if (! dynamic || elf_dynsymtab (abfd) == 0)
-    hdr = &elf_tdata (abfd)->symtab_hdr;
-  else
-    hdr = &elf_tdata (abfd)->dynsymtab_hdr;
-
-  symcount = hdr->sh_size / bed->s->sizeof_sym;
-
-  /* The sh_info field of the symtab header tells us where the
-     external symbols start.  We don't care about the local symbols at
-     this point.  */
-  if (elf_bad_symtab (abfd))
+  extsymcount = elf_tdata (abfd)->dt_symtab_count;
+  if (extsymcount)
     {
-      extsymcount = symcount;
+      /* Find the first global symbol.  */
+      BFD_ASSERT (!elf_bad_symtab (abfd));
+      hdr = NULL;
       extsymoff = 0;
+      isym = elf_tdata (abfd)->dt_symtab;
+      isymend = isym + extsymcount;
+      for (; isym < isymend; isym++)
+	if (ELF_ST_BIND (isym->st_info) == STB_LOCAL)
+	  {
+	    extsymcount--;
+	    extsymoff++;
+	  }
+	else
+	  break;
     }
   else
     {
-      extsymcount = symcount - hdr->sh_info;
-      extsymoff = hdr->sh_info;
+      if (! dynamic || elf_dynsymtab (abfd) == 0)
+	hdr = &elf_tdata (abfd)->symtab_hdr;
+      else
+	hdr = &elf_tdata (abfd)->dynsymtab_hdr;
+
+      symcount = hdr->sh_size / bed->s->sizeof_sym;
+
+      /* The sh_info field of the symtab header tells us where the
+	 external symbols start.  We don't care about the local
+	 symbols at this point.  */
+      if (elf_bad_symtab (abfd))
+	{
+	  extsymcount = symcount;
+	  extsymoff = 0;
+	}
+      else
+	{
+	  extsymcount = symcount - hdr->sh_info;
+	  extsymoff = hdr->sh_info;
+	}
     }
 
   sym_hash = elf_sym_hashes (abfd);
@@ -4549,8 +4596,11 @@  elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 	    value -= sec->vma;
 	}
 
-      name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
-					      isym->st_name);
+      if (hdr)
+	name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
+						isym->st_name);
+      else
+	name = elf_tdata (abfd)->dt_strtab + isym->st_name;
       if (name == NULL)
 	goto error_free_vers;
 
@@ -5203,7 +5253,7 @@  elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
       extversym = NULL;
     }
 
-  if (isymbuf != NULL)
+  if (isymbuf != NULL && !elf_use_dt_symtab_p (abfd))
     {
       free (isymbuf);
       isymbuf = NULL;
@@ -5580,7 +5630,7 @@  elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
   if (extversym != NULL)
     free (extversym);
  error_free_sym:
-  if (isymbuf != NULL)
+  if (isymbuf != NULL && !elf_use_dt_symtab_p (abfd))
     free (isymbuf);
  error_return:
   return FALSE;