bfd: Improve nm and objdump without section header
authorH.J. Lu <hjl.tools@gmail.com>
Thu, 5 Mar 2020 04:32:35 +0000 (20:32 -0800)
committerH.J. Lu <hjl.tools@gmail.com>
Thu, 29 Jun 2023 17:29:47 +0000 (10:29 -0700)
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.  We can also get symbol version from DT_VERSYM, DT_VERDEF
and DT_VERNEED entries.

dt_symtab, dt_versym, dt_verdef, dt_verneed, dt_symtab_count,
dt_verdef_count, dt_verneed_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_verdef, dt_verneed,
dt_symtab_count, dt_verdef_count, dt_verneed_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.
(_bfd_elf_slurp_version_tables): 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.
(_bfd_elf_get_symbol_version_name): 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
bfd/elf.c
bfd/elfcode.h
bfd/elflink.c

index e08c5a110e5f9aa2d72bc0a66215e9d2bf796529..ec85676451940a6eb5f88304072416f99d88adac 100644 (file)
@@ -2031,6 +2031,14 @@ 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_byte *dt_versym;
+  bfd_byte *dt_verdef;
+  bfd_byte *dt_verneed;
+  size_t dt_symtab_count;
+  size_t dt_verdef_count;
+  size_t dt_verneed_count;
+  char *dt_strtab;
   elf_section_list * symtab_shndx_list;
   bfd_vma gp;                          /* The gp value */
   unsigned int gp_size;                        /* The gp size */
@@ -2194,6 +2202,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) \
@@ -2587,6 +2596,12 @@ extern bfd_reloc_status_type bfd_elf_perform_complex_relocation
 extern bool _bfd_elf_setup_sections
   (bfd *);
 
+extern bool _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 *);
 
index 4584b93a7ae0abd6643603a7569e74d12b899178..e506721222eaa1607985eb6d8684fb5e939bf6df 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -397,6 +397,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)
@@ -1873,6 +1884,551 @@ _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,
+                size_t size, size_t *max_size_p)
+{
+  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)
+      {
+       if (max_size_p)
+         *max_size_p = seg->p_vaddr + seg->p_filesz - vma;
+       return 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 ((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.  */
+
+bool
+_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 *);
+  bool (*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;
+  size_t dt_strsz = 0;
+  bfd_vma dt_versym = 0;
+  bfd_vma dt_verdef = 0;
+  bfd_vma dt_verneed = 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;
+  bool res = false;
+  size_t amt;
+  bfd_byte *esymbuf = NULL, *esym;
+  bfd_size_type symcount;
+  Elf_Internal_Sym *isymbuf = NULL;
+  Elf_Internal_Sym *isym, *isymend;
+  bfd_byte *versym = NULL;
+  bfd_byte *verdef = NULL;
+  bfd_byte *verneed = NULL;
+  size_t verdef_size;
+  size_t verneed_size;
+  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;
+       case DT_VERSYM:
+         dt_versym = dyn.d_un.d_val;
+         break;
+       case DT_VERDEF:
+         dt_verdef = dyn.d_un.d_val;
+         break;
+       case DT_VERNEED:
+         dt_verneed = dyn.d_un.d_val;
+         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, NULL);
+  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),
+                                NULL);
+      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), NULL);
+      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, NULL);
+      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)
+       {
+         symcount = 0;
+         goto empty_gnu_hash;
+       }
+
+      maxchain -= gnusymidx;
+      filepos = offset_from_vma (phdrs, phnum,
+                                (buckets_vma +
+                                 4 * (ngnubuckets + maxchain)),
+                                4, NULL);
+      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, NULL);
+      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, NULL);
+         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, NULL);
+  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;
+      }
+
+  if (dt_versym)
+    {
+      /* Swap in DT_VERSYM.  */
+      if (_bfd_mul_overflow (symcount, 2, &amt))
+       {
+         bfd_set_error (bfd_error_file_too_big);
+         goto error_return;
+       }
+
+      filepos = offset_from_vma (phdrs, phnum, dt_versym, amt, NULL);
+      if (filepos == (file_ptr) -1
+         || bfd_seek (abfd, filepos, SEEK_SET) != 0)
+       goto error_return;
+
+      /* DT_VERSYM info must be valid until ABFD is closed.  */
+      versym = _bfd_alloc_and_read (abfd, amt, amt);
+
+      if (dt_verdef)
+       {
+         /* Read in DT_VERDEF.  */
+         filepos = offset_from_vma (phdrs, phnum, dt_verdef,
+                                    0, &verdef_size);
+         if (filepos == (file_ptr) -1
+             || bfd_seek (abfd, filepos, SEEK_SET) != 0)
+           goto error_return;
+
+         /* DT_VERDEF info must be valid until ABFD is closed.  */
+         verdef = _bfd_alloc_and_read (abfd, verdef_size,
+                                       verdef_size);
+       }
+
+      if (dt_verneed)
+       {
+         /* Read in DT_VERNEED.  */
+         filepos = offset_from_vma (phdrs, phnum, dt_verneed,
+                                    0, &verneed_size);
+         if (filepos == (file_ptr) -1
+             || bfd_seek (abfd, filepos, SEEK_SET) != 0)
+           goto error_return;
+
+         /* DT_VERNEED info must be valid until ABFD is closed.  */
+         verneed = _bfd_alloc_and_read (abfd, verneed_size,
+                                        verneed_size);
+       }
+    }
+
+ empty_gnu_hash:
+  elf_tdata (abfd)->dt_strtab = strbuf;
+  elf_tdata (abfd)->dt_symtab = isymbuf;
+  elf_tdata (abfd)->dt_symtab_count = symcount;
+  elf_tdata (abfd)->dt_versym = versym;
+  elf_tdata (abfd)->dt_verdef = verdef;
+  elf_tdata (abfd)->dt_verneed = verneed;
+  elf_tdata (abfd)->dt_verdef_count
+    = verdef_size / sizeof (Elf_External_Verdef);
+  elf_tdata (abfd)->dt_verneed_count
+    = verneed_size / sizeof (Elf_External_Verneed);
+
+  res = true;
+
+ error_return:
+  /* Restore file position for elf_object_p.  */
+  if (bfd_seek (abfd, saved_filepos, SEEK_SET) != 0)
+    res = false;
+  free (dynbuf);
+  free (esymbuf);
+  free (gnubuckets);
+  free (gnuchains);
+  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;
+    }
+
+  return sec;
+}
+
 /* Get version name.  If BASE_P is TRUE, return "Base" for VER_FLG_BASE
    and return symbol version for symbol version itself.   */
 
@@ -1882,8 +2438,11 @@ _bfd_elf_get_symbol_version_string (bfd *abfd, asymbol *symbol,
                                    bool *hidden)
 {
   const char *version_string = NULL;
-  if (elf_dynversym (abfd) != 0
-      && (elf_dynverdef (abfd) != 0 || elf_dynverref (abfd) != 0))
+  if ((elf_dynversym (abfd) != 0
+       && (elf_dynverdef (abfd) != 0 || elf_dynverref (abfd) != 0))
+      || (elf_tdata (abfd)->dt_versym != NULL
+         && (elf_tdata (abfd)->dt_verdef != NULL
+             || elf_tdata (abfd)->dt_verneed != NULL)))
     {
       unsigned int vernum = ((elf_symbol_type *) symbol)->version;
 
@@ -8613,6 +9172,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;
     }
@@ -8623,6 +9187,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 * (sizeof (asymbol *));
   if (symcount == 0)
     symtab_size = sizeof (asymbol *);
@@ -8830,35 +9396,51 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
   unsigned int freeidx = 0;
   size_t amt;
 
-  if (elf_dynverref (abfd) != 0)
+  if (elf_dynverref (abfd) != 0 || elf_tdata (abfd)->dt_verneed != NULL)
     {
       Elf_Internal_Shdr *hdr;
       Elf_External_Verneed *everneed;
       Elf_Internal_Verneed *iverneed;
       unsigned int i;
       bfd_byte *contents_end;
+      size_t verneed_count;
+      size_t verneed_size;
 
-      hdr = &elf_tdata (abfd)->dynverref_hdr;
-
-      if (hdr->sh_info > hdr->sh_size / sizeof (Elf_External_Verneed))
+      if (elf_tdata (abfd)->dt_verneed != NULL)
        {
-       error_return_bad_verref:
-         _bfd_error_handler
-           (_("%pB: .gnu.version_r invalid entry"), abfd);
-         bfd_set_error (bfd_error_bad_value);
-       error_return_verref:
-         elf_tdata (abfd)->verref = NULL;
-         elf_tdata (abfd)->cverrefs = 0;
-         goto error_return;
+         hdr = NULL;
+         contents = elf_tdata (abfd)->dt_verneed;
+         verneed_count = elf_tdata (abfd)->dt_verneed_count;
+         verneed_size = verneed_count * sizeof (Elf_External_Verneed);
        }
+      else
+       {
+         hdr = &elf_tdata (abfd)->dynverref_hdr;
 
-      if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0)
-       goto error_return_verref;
-      contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size);
-      if (contents == NULL)
-       goto error_return_verref;
+         if (hdr->sh_info > hdr->sh_size / sizeof (Elf_External_Verneed))
+           {
+           error_return_bad_verref:
+             _bfd_error_handler
+               (_("%pB: .gnu.version_r invalid entry"), abfd);
+             bfd_set_error (bfd_error_bad_value);
+           error_return_verref:
+             elf_tdata (abfd)->verref = NULL;
+             elf_tdata (abfd)->cverrefs = 0;
+             goto error_return;
+           }
+
+         if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0)
+           goto error_return_verref;
+         contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size);
+         if (contents == NULL)
+           goto error_return_verref;
+
+         verneed_size = hdr->sh_size;
+         verneed_count = hdr->sh_info;
+       }
 
-      if (_bfd_mul_overflow (hdr->sh_info, sizeof (Elf_Internal_Verneed), &amt))
+      if (_bfd_mul_overflow (verneed_count,
+                            sizeof (Elf_Internal_Verneed), &amt))
        {
          bfd_set_error (bfd_error_file_too_big);
          goto error_return_verref;
@@ -8871,10 +9453,11 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
 
       BFD_ASSERT (sizeof (Elf_External_Verneed)
                  == sizeof (Elf_External_Vernaux));
-      contents_end = contents + hdr->sh_size - sizeof (Elf_External_Verneed);
+      contents_end = (contents + verneed_size
+                     - sizeof (Elf_External_Verneed));
       everneed = (Elf_External_Verneed *) contents;
       iverneed = elf_tdata (abfd)->verref;
-      for (i = 0; i < hdr->sh_info; i++, iverneed++)
+      for (i = 0; i < verneed_count; i++, iverneed++)
        {
          Elf_External_Vernaux *evernaux;
          Elf_Internal_Vernaux *ivernaux;
@@ -8884,9 +9467,13 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
 
          iverneed->vn_bfd = abfd;
 
-         iverneed->vn_filename =
-           bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
-                                            iverneed->vn_file);
+         if (elf_use_dt_symtab_p (abfd))
+           iverneed->vn_filename
+             = elf_tdata (abfd)->dt_strtab + iverneed->vn_file;
+         else
+           iverneed->vn_filename
+             = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
+                                                iverneed->vn_file);
          if (iverneed->vn_filename == NULL)
            goto error_return_bad_verref;
 
@@ -8917,9 +9504,13 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
            {
              _bfd_elf_swap_vernaux_in (abfd, evernaux, ivernaux);
 
-             ivernaux->vna_nodename =
-               bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
-                                                ivernaux->vna_name);
+             if (elf_use_dt_symtab_p (abfd))
+               ivernaux->vna_nodename
+                 = elf_tdata (abfd)->dt_strtab + ivernaux->vna_name;
+             else
+               ivernaux->vna_nodename
+                 = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
+                                                    ivernaux->vna_name);
              if (ivernaux->vna_nodename == NULL)
                goto error_return_bad_verref;
 
@@ -8958,11 +9549,12 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
        }
       elf_tdata (abfd)->cverrefs = i;
 
-      free (contents);
+      if (elf_tdata (abfd)->dt_verneed == NULL)
+       free (contents);
       contents = NULL;
     }
 
-  if (elf_dynverdef (abfd) != 0)
+  if (elf_dynverdef (abfd) != 0 || elf_tdata (abfd)->dt_verdef != NULL)
     {
       Elf_Internal_Shdr *hdr;
       Elf_External_Verdef *everdef;
@@ -8972,40 +9564,56 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
       unsigned int i;
       unsigned int maxidx;
       bfd_byte *contents_end_def, *contents_end_aux;
+      size_t verdef_count;
+      size_t verdef_size;
 
-      hdr = &elf_tdata (abfd)->dynverdef_hdr;
-
-      if (hdr->sh_size < sizeof (Elf_External_Verdef))
+      if (elf_tdata (abfd)->dt_verdef != NULL)
        {
-       error_return_bad_verdef:
-         _bfd_error_handler
-           (_("%pB: .gnu.version_d invalid entry"), abfd);
-         bfd_set_error (bfd_error_bad_value);
-       error_return_verdef:
-         elf_tdata (abfd)->verdef = NULL;
-         elf_tdata (abfd)->cverdefs = 0;
-         goto error_return;
+         hdr = NULL;
+         contents = elf_tdata (abfd)->dt_verdef;
+         verdef_count = elf_tdata (abfd)->dt_verdef_count;
+         verdef_size = verdef_count * sizeof (Elf_External_Verdef);
        }
+      else
+       {
+         hdr = &elf_tdata (abfd)->dynverdef_hdr;
 
-      if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0)
-       goto error_return_verdef;
-      contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size);
-      if (contents == NULL)
-       goto error_return_verdef;
+         if (hdr->sh_size < sizeof (Elf_External_Verdef))
+           {
+           error_return_bad_verdef:
+             _bfd_error_handler
+               (_("%pB: .gnu.version_d invalid entry"), abfd);
+             bfd_set_error (bfd_error_bad_value);
+           error_return_verdef:
+             elf_tdata (abfd)->verdef = NULL;
+             elf_tdata (abfd)->cverdefs = 0;
+             goto error_return;
+           }
+
+         if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0)
+           goto error_return_verdef;
+         contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size);
+         if (contents == NULL)
+           goto error_return_verdef;
 
-      BFD_ASSERT (sizeof (Elf_External_Verdef)
-                 >= sizeof (Elf_External_Verdaux));
-      contents_end_def = contents + hdr->sh_size
-                        - sizeof (Elf_External_Verdef);
-      contents_end_aux = contents + hdr->sh_size
-                        - sizeof (Elf_External_Verdaux);
+         BFD_ASSERT (sizeof (Elf_External_Verdef)
+                     >= sizeof (Elf_External_Verdaux));
+
+         verdef_count = hdr->sh_info;
+         verdef_size = hdr->sh_size;
+       }
+
+      contents_end_def = (contents + verdef_size
+                         - sizeof (Elf_External_Verdef));
+      contents_end_aux = (contents + verdef_size
+                         - sizeof (Elf_External_Verdaux));
 
       /* We know the number of entries in the section but not the maximum
         index.  Therefore we have to run through all entries and find
         the maximum.  */
       everdef = (Elf_External_Verdef *) contents;
       maxidx = 0;
-      for (i = 0; i < hdr->sh_info; ++i)
+      for (i = 0; i < verdef_count; ++i)
        {
          _bfd_elf_swap_verdef_in (abfd, everdef, &iverdefmem);
 
@@ -9048,7 +9656,7 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
 
       everdef = (Elf_External_Verdef *) contents;
       iverdefarr = elf_tdata (abfd)->verdef;
-      for (i = 0; i < hdr->sh_info; i++)
+      for (i = 0; i < verdef_count; ++i)
        {
          Elf_External_Verdaux *everdaux;
          Elf_Internal_Verdaux *iverdaux;
@@ -9091,9 +9699,13 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
            {
              _bfd_elf_swap_verdaux_in (abfd, everdaux, iverdaux);
 
-             iverdaux->vda_nodename =
-               bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
-                                                iverdaux->vda_name);
+             if (elf_use_dt_symtab_p (abfd))
+               iverdaux->vda_nodename
+                 = elf_tdata (abfd)->dt_strtab + iverdaux->vda_name;
+             else
+               iverdaux->vda_nodename
+                 = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
+                                                    iverdaux->vda_name);
              if (iverdaux->vda_nodename == NULL)
                goto error_return_bad_verdef;
 
@@ -9128,7 +9740,8 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
                     ((bfd_byte *) everdef + iverdef->vd_next));
        }
 
-      free (contents);
+      if (elf_tdata (abfd)->dt_verdef == NULL)
+       free (contents);
       contents = NULL;
     }
   else if (default_imported_symver)
index 495e498838d72aafc8d1fb058882deb95a4bde7d..aae66bcebf85575dd02d8c6e6f97a835c566ba13 100644 (file)
@@ -839,6 +839,21 @@ elf_object_p (bfd *abfd)
                  abfd->read_only = 1;
                }
            }
+         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;
+           }
        }
     }
 
@@ -1245,7 +1260,9 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool dynamic)
       if ((elf_dynverdef (abfd) != 0
           && elf_tdata (abfd)->verdef == NULL)
          || (elf_dynverref (abfd) != 0
-             && elf_tdata (abfd)->verref == NULL))
+             && elf_tdata (abfd)->verref == NULL)
+         || elf_tdata (abfd)->dt_verdef != NULL
+         || elf_tdata (abfd)->dt_verneed != NULL)
        {
          if (!_bfd_elf_slurp_version_tables (abfd, false))
            return -1;
@@ -1253,11 +1270,15 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool 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
     {
+      size_t i;
+
       isymbuf = bfd_elf_get_elf_syms (abfd, hdr, symcount, 0,
                                      NULL, NULL, NULL);
       if (isymbuf == NULL)
@@ -1304,12 +1325,18 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool dynamic)
       if (xver != NULL)
        ++xver;
       isymend = isymbuf + symcount;
-      for (isym = isymbuf + 1, sym = symbase; isym < isymend; isym++, sym++)
+      for (isym = isymbuf + 1, sym = symbase, i = 1;
+          isym < isymend;
+          isym++, sym++, i++)
        {
          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)
@@ -1343,6 +1370,15 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool 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
@@ -1423,7 +1459,10 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool dynamic)
          if (dynamic)
            sym->symbol.flags |= BSF_DYNAMIC;
 
-         if (xver != NULL)
+         if (elf_tdata (abfd)->dt_versym)
+           sym->version = bfd_get_16 (abfd,
+                                      elf_tdata (abfd)->dt_versym + 2 * i);
+         else if (xver != NULL)
            {
              Elf_Internal_Versym iversym;
 
@@ -1461,13 +1500,15 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool dynamic)
     }
 
   free (xverbuf);
-  if (hdr->contents != (unsigned char *) isymbuf)
+  if (hdr->contents != (unsigned char *) isymbuf
+      && !elf_use_dt_symtab_p (abfd))
     free (isymbuf);
   return symcount;
 
  error_return:
   free (xverbuf);
-  if (hdr->contents != (unsigned char *) isymbuf)
+  if (hdr->contents != (unsigned char *) isymbuf
+      && !elf_use_dt_symtab_p (abfd))
     free (isymbuf);
   return -1;
 }
index 4f879005c226ba07028ee4191ef9a91c90d14607..7217c2f038baf9cf1df122cc5bb82ac99c00a51e 100644 (file)
@@ -3571,6 +3571,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.  */
@@ -4233,6 +4239,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