diff options
author | H.J. Lu <hjl.tools@gmail.com> | 2020-03-04 20:32:35 -0800 |
---|---|---|
committer | H.J. Lu <hjl.tools@gmail.com> | 2023-06-29 10:29:47 -0700 |
commit | 46675b6b8160e97485583522852ad86507bd9072 (patch) | |
tree | 7db5843df9cdc601838344c9b077f9f80e3d961e /bfd | |
parent | 02c1ba6c94d4bb3f53dfeeb4401c8434c7834a32 (diff) | |
download | binutils-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.h | 15 | ||||
-rw-r--r-- | bfd/elf.c | 729 | ||||
-rw-r--r-- | bfd/elfcode.h | 55 | ||||
-rw-r--r-- | bfd/elflink.c | 12 |
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 *); @@ -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 |