aboutsummaryrefslogtreecommitdiff
path: root/bfd
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2020-03-04 20:32:35 -0800
committerH.J. Lu <hjl.tools@gmail.com>2023-06-29 10:29:47 -0700
commit46675b6b8160e97485583522852ad86507bd9072 (patch)
tree7db5843df9cdc601838344c9b077f9f80e3d961e /bfd
parent02c1ba6c94d4bb3f53dfeeb4401c8434c7834a32 (diff)
downloadbinutils-46675b6b8160e97485583522852ad86507bd9072.zip
binutils-46675b6b8160e97485583522852ad86507bd9072.tar.gz
binutils-46675b6b8160e97485583522852ad86507bd9072.tar.bz2
bfd: Improve nm and objdump without section header
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.
Diffstat (limited to 'bfd')
-rw-r--r--bfd/elf-bfd.h15
-rw-r--r--bfd/elf.c729
-rw-r--r--bfd/elfcode.h55
-rw-r--r--bfd/elflink.c12
4 files changed, 746 insertions, 65 deletions
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index e08c5a1..ec85676 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -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 *);
diff --git a/bfd/elf.c b/bfd/elf.c
index 4584b93..e506721 100644
--- 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)
diff --git a/bfd/elfcode.h b/bfd/elfcode.h
index 495e498..aae66bc 100644
--- a/bfd/elfcode.h
+++ b/bfd/elfcode.h
@@ -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;
}
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 4f87900..7217c2f 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -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