[2/7] bfd: Improve nm and objdump without section header

Message ID 20200310001224.131714-4-hjl.tools@gmail.com
State New
Headers show
Series
  • ELF: Omit section header on ELF objects
Related show

Commit Message

H.J. Lu March 10, 2020, 12:12 a.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, to improve nm and objdump.  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 DT_GNU_HASH/DT_MIPS_XHASH place all
symbols with STB_LOCAL binding before symbols with other bindings and
all undefined symbols defined ones in dynamic symbol table, the highest
symbol index in DT_GNU_HASH/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.
	(_bfd_elf_get_section_from_dynamic_symbol): 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.
	(_bfd_elf_get_section_from_dynamic_symbol): 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): Return wrong
	format error when dynamic symbol table is used.
	(elf_link_add_object_symbols): Likewise.
---
 bfd/elf-bfd.h |  10 +
 bfd/elf.c     | 497 ++++++++++++++++++++++++++++++++++++++++++++++++++
 bfd/elfcode.h |  42 ++++-
 bfd/elflink.c |  12 ++
 4 files changed, 557 insertions(+), 4 deletions(-)

-- 
2.24.1

Comments

Fangrui Song March 12, 2020, 3:11 a.m. | #1
On 2020-03-09, H.J. Lu wrote:
>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, to improve nm and objdump.  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 DT_GNU_HASH/DT_MIPS_XHASH place all

>symbols with STB_LOCAL binding before symbols with other bindings and

>all undefined symbols defined ones in dynamic symbol table, the highest

>symbol index in DT_GNU_HASH/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.

>	(_bfd_elf_get_section_from_dynamic_symbol): 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.

>	(_bfd_elf_get_section_from_dynamic_symbol): 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): Return wrong

>	format error when dynamic symbol table is used.

>	(elf_link_add_object_symbols): Likewise.

>---

> bfd/elf-bfd.h |  10 +

> bfd/elf.c     | 497 ++++++++++++++++++++++++++++++++++++++++++++++++++

> bfd/elfcode.h |  42 ++++-

> bfd/elflink.c |  12 ++

> 4 files changed, 557 insertions(+), 4 deletions(-)

>

>diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h

>index 5d9e0fedcd..09692b155a 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,12 @@ 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 asection *_bfd_elf_get_section_from_dynamic_symbol

>+  (bfd *, Elf_Internal_Sym *);

>+

> 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..aa689dcab3 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,485 @@ _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 checkers (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

>+


If DT_MIPS_XHASH support turns out to be tricky, maybe that part can be
deferred.

I am waiting for DT_MIPS_XHASH stakeholders to prove it is worth it and
should not be reverted instead.

https://sourceware.org/pipermail/binutils/2019-December/108886.html 
https://sourceware.org/pipermail/binutils/2020-January/109362.html

>+/* 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 table.  */

>+  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 table 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 table.  */

>+  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 table 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;

>+}

>+

>+/* Reconstruct section from dynamic symbol.  */

>+

>+asection *

>+_bfd_elf_get_section_from_dynamic_symbol (bfd *abfd,

>+					  Elf_Internal_Sym *isym)

>+{

>+  asection *sec;

>+  flagword flags;

>+

>+  if (!elf_use_dt_symtab_p (abfd))

>+    return NULL;

>+

>+  flags = SEC_ALLOC | SEC_LOAD;

>+  switch (ELF_ST_TYPE (isym->st_info))

>+    {

>+    case STT_FUNC:

>+    case STT_GNU_IFUNC:

>+      sec = bfd_get_section_by_name (abfd, ".text");

>+      if (sec == NULL)

>+	sec = bfd_make_section_with_flags (abfd,

>+					   ".text",

>+					   flags | SEC_CODE);

>+      break;

>+    case STT_COMMON:

>+      sec = bfd_com_section_ptr;

>+      break;

>+    case STT_OBJECT:

>+      sec = bfd_get_section_by_name (abfd, ".data");

>+      if (sec == NULL)

>+	sec = bfd_make_section_with_flags (abfd,

>+					   ".data",

>+					   flags | SEC_DATA);

>+      break;

>+    case STT_TLS:

>+      sec = bfd_get_section_by_name (abfd, ".tdata");

>+      if (sec == NULL)

>+	sec = bfd_make_section_with_flags (abfd,

>+					   ".tdata",

>+					   (flags

>+					    | SEC_DATA

>+					    | SEC_THREAD_LOCAL));

>+      break;

>+    default:

>+      sec = bfd_abs_section_ptr;

>+      break;

>+    }

>+

>+  /* NB: Don't place this section in output.  */

>+  if (sec && 0)

>+    sec->output_section = bfd_abs_section_ptr;

>+

>+  return sec;

>+}

>+

> /* Get version string.  */

>

> const char *

>@@ -8415,6 +8905,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 +8920,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..9124015a3e 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;

>+	    }

> 	}

>     }

>

>@@ -1223,7 +1238,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 +1296,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,6 +1334,15 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)

> 		 moment) about the alignment.  */

> 	      sym->symbol.value = isym->st_size;

> 	    }

>+	  else if (elf_use_dt_symtab_p (abfd))

>+	    {

>+	      asection *sec;

>+	      sec = _bfd_elf_get_section_from_dynamic_symbol (abfd,

>+							      isym);

>+	      if (sec == NULL)

>+		goto error_return;

>+	      sym->symbol.section = sec;

>+	    }

> 	  else

> 	    {

> 	      sym->symbol.section

>@@ -1426,14 +1456,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..ee047889c5 100644

>--- a/bfd/elflink.c

>+++ b/bfd/elflink.c

>@@ -3394,6 +3394,12 @@ elf_link_is_defined_archive_symbol (bfd * abfd, carsym * symdef)

>   if (! bfd_check_format (abfd, bfd_object))

>     return FALSE;

>

>+  if (elf_use_dt_symtab_p (abfd))

>+    {

>+      bfd_set_error (bfd_error_wrong_format);

>+      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.  */

>@@ -3927,6 +3933,12 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)

>   htab = elf_hash_table (info);

>   bed = get_elf_backend_data (abfd);

>

>+  if (elf_use_dt_symtab_p (abfd))

>+    {

>+      bfd_set_error (bfd_error_wrong_format);

>+      return FALSE;

>+    }

>+

>   if ((abfd->flags & DYNAMIC) == 0)

>     dynamic = FALSE;

>   else

>-- 

>2.24.1

>

Patch

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 5d9e0fedcd..09692b155a 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,12 @@  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 asection *_bfd_elf_get_section_from_dynamic_symbol
+  (bfd *, Elf_Internal_Sym *);
+
 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..aa689dcab3 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,485 @@  _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 checkers (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 table.  */
+  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 table 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 table.  */
+  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 table 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;
+}
+
+/* Reconstruct section from dynamic symbol.  */
+
+asection *
+_bfd_elf_get_section_from_dynamic_symbol (bfd *abfd,
+					  Elf_Internal_Sym *isym)
+{
+  asection *sec;
+  flagword flags;
+
+  if (!elf_use_dt_symtab_p (abfd))
+    return NULL;
+
+  flags = SEC_ALLOC | SEC_LOAD;
+  switch (ELF_ST_TYPE (isym->st_info))
+    {
+    case STT_FUNC:
+    case STT_GNU_IFUNC:
+      sec = bfd_get_section_by_name (abfd, ".text");
+      if (sec == NULL)
+	sec = bfd_make_section_with_flags (abfd,
+					   ".text",
+					   flags | SEC_CODE);
+      break;
+    case STT_COMMON:
+      sec = bfd_com_section_ptr;
+      break;
+    case STT_OBJECT:
+      sec = bfd_get_section_by_name (abfd, ".data");
+      if (sec == NULL)
+	sec = bfd_make_section_with_flags (abfd,
+					   ".data",
+					   flags | SEC_DATA);
+      break;
+    case STT_TLS:
+      sec = bfd_get_section_by_name (abfd, ".tdata");
+      if (sec == NULL)
+	sec = bfd_make_section_with_flags (abfd,
+					   ".tdata",
+					   (flags
+					    | SEC_DATA
+					    | SEC_THREAD_LOCAL));
+      break;
+    default:
+      sec = bfd_abs_section_ptr;
+      break;
+    }
+
+  /* NB: Don't place this section in output.  */
+  if (sec && 0)
+    sec->output_section = bfd_abs_section_ptr;
+
+  return sec;
+}
+
 /* Get version string.  */
 
 const char *
@@ -8415,6 +8905,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 +8920,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..9124015a3e 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;
+	    }
 	}
     }
 
@@ -1223,7 +1238,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 +1296,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,6 +1334,15 @@  elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)
 		 moment) about the alignment.  */
 	      sym->symbol.value = isym->st_size;
 	    }
+	  else if (elf_use_dt_symtab_p (abfd))
+	    {
+	      asection *sec;
+	      sec = _bfd_elf_get_section_from_dynamic_symbol (abfd,
+							      isym);
+	      if (sec == NULL)
+		goto error_return;
+	      sym->symbol.section = sec;
+	    }
 	  else
 	    {
 	      sym->symbol.section
@@ -1426,14 +1456,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..ee047889c5 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -3394,6 +3394,12 @@  elf_link_is_defined_archive_symbol (bfd * abfd, carsym * symdef)
   if (! bfd_check_format (abfd, bfd_object))
     return FALSE;
 
+  if (elf_use_dt_symtab_p (abfd))
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      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.  */
@@ -3927,6 +3933,12 @@  elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
   htab = elf_hash_table (info);
   bed = get_elf_backend_data (abfd);
 
+  if (elf_use_dt_symtab_p (abfd))
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return FALSE;
+    }
+
   if ((abfd->flags & DYNAMIC) == 0)
     dynamic = FALSE;
   else