diff options
Diffstat (limited to 'bfd/elf.c')
-rw-r--r-- | bfd/elf.c | 729 |
1 files changed, 671 insertions, 58 deletions
@@ -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) |