diff options
-rw-r--r-- | src/readelf.c | 289 |
1 files changed, 283 insertions, 6 deletions
diff --git a/src/readelf.c b/src/readelf.c index 451f8400..9e1e9b73 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -307,7 +307,8 @@ static void handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, static void handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr); static bool print_symtab (Ebl *ebl, int type); -static void handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static bool handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); +static bool handle_dynamic_symtab (Ebl *ebl); static void print_verinfo (Ebl *ebl); static void handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); static void handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); @@ -327,7 +328,9 @@ enum dyn_idx { i_strsz, i_verneed, + i_verneednum, i_verdef, + i_verdefnum, i_versym, i_symtab, i_strtab, @@ -1042,7 +1045,7 @@ process_elf_file (Dwfl_Module *dwflmod, int fd) symtab_printed |= print_symtab (ebl, SHT_DYNSYM); if (print_version_info) print_verinfo (ebl); - if (print_symbol_table) + if (print_symbol_table && !use_dynamic_segment) symtab_printed |= print_symtab (ebl, SHT_SYMTAB); if ((print_symbol_table || print_dynsym_table) @@ -2442,6 +2445,12 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) static bool print_symtab (Ebl *ebl, int type) { + /* Use the dynamic section info to display symbol tables. */ + if (use_dynamic_segment && type == SHT_DYNSYM) + { + return handle_dynamic_symtab(ebl); + } + /* Find the symbol table(s). For this we have to search through the section table. */ Elf_Scn *scn = NULL; @@ -2480,16 +2489,275 @@ print_symtab (Ebl *ebl, int type) _("cannot get section [%zd] header: %s"), elf_ndxscn (scn), elf_errmsg (-1)); } - handle_symtab (ebl, scn, shdr); - symtab_printed = true; + symtab_printed = handle_symtab (ebl, scn, shdr); } } return symtab_printed; } +static bool +handle_dynamic_symtab (Ebl *ebl) +{ + GElf_Phdr *phdr = NULL; + /* phnum is a static variable which already fetched in function process_elf_file. */ + for (size_t i = 0; i < phnum; ++i) { + GElf_Phdr phdr_mem; + phdr = gelf_getphdr(ebl->elf, i, &phdr_mem); + if (phdr->p_type == PT_DYNAMIC) { + break; + } + } + if (phdr == NULL) + return false; -static void + GElf_Addr addrs[i_max] = {0,}; + GElf_Off offs[i_max] = {0,}; + get_dynscn_addrs(ebl->elf, phdr, addrs); + find_offsets(ebl->elf, 0, i_max, addrs, offs); + + size_t syments; + + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr(ebl->elf, &ehdr_mem); + + if (offs[i_hash] != 0) { + /* In the original format, .hash says the size of .dynsym. */ + + size_t entsz = SH_ENTSIZE_HASH(ehdr); + Elf_Data *data = + elf_getdata_rawchunk(ebl->elf, offs[i_hash] + entsz, entsz, + (entsz == 4 ? ELF_T_WORD : ELF_T_XWORD)); + if (data != NULL) + syments = (entsz == 4 ? *(const GElf_Word *)data->d_buf + : *(const GElf_Xword *)data->d_buf); + } + if (offs[i_gnu_hash] != 0 && syments == 0) { + /* In the new format, we can derive it with some work. */ + + const struct { + Elf32_Word nbuckets; + Elf32_Word symndx; + Elf32_Word maskwords; + Elf32_Word shift2; + } * header; + + Elf_Data *data = elf_getdata_rawchunk(ebl->elf, offs[i_gnu_hash], + sizeof *header, ELF_T_WORD); + if (data != NULL) { + header = data->d_buf; + Elf32_Word nbuckets = header->nbuckets; + Elf32_Word symndx = header->symndx; + GElf_Off buckets_at = (offs[i_gnu_hash] + sizeof *header + + (gelf_getclass(ebl->elf) * sizeof(Elf32_Word) * + header->maskwords)); + + // elf_getdata_rawchunk takes a size_t, make sure it + // doesn't overflow. + #if SIZE_MAX <= UINT32_MAX + if (nbuckets > SIZE_MAX / sizeof(Elf32_Word)) + data = NULL; + else + #endif + data = elf_getdata_rawchunk(ebl->elf, buckets_at, + nbuckets * sizeof(Elf32_Word), ELF_T_WORD); + if (data != NULL && symndx < nbuckets) { + const Elf32_Word *const buckets = data->d_buf; + Elf32_Word maxndx = symndx; + for (Elf32_Word bucket = 0; bucket < nbuckets; ++bucket) + if (buckets[bucket] > maxndx) + maxndx = buckets[bucket]; + + GElf_Off hasharr_at = (buckets_at + nbuckets * sizeof(Elf32_Word)); + hasharr_at += (maxndx - symndx) * sizeof(Elf32_Word); + do { + data = elf_getdata_rawchunk(ebl->elf, hasharr_at, + sizeof(Elf32_Word), ELF_T_WORD); + if (data != NULL && (*(const Elf32_Word *)data->d_buf & 1u)) { + syments = maxndx + 1; + break; + } + ++maxndx; + hasharr_at += sizeof(Elf32_Word); + } while (data != NULL); + } + } + } + if (offs[i_strtab] > offs[i_symtab] && syments == 0) + syments = ((offs[i_strtab] - offs[i_symtab]) / + gelf_fsize(ebl->elf, ELF_T_SYM, 1, EV_CURRENT)); + + if (syments <= 0 || offs[i_strtab] == 0 || offs[i_symtab] == 0) { + error_exit(0, _("Dynamic symbol information is not available for displaying symbols.")); + } + + /* All the data chunk initializaion. */ + Elf_Data *symdata = NULL; + Elf_Data *symstrdata = NULL; + Elf_Data *versym_data = NULL; + Elf_Data *verdef_data = NULL; + Elf_Data *verneed_data = NULL; + + symdata = elf_getdata_rawchunk( + ebl->elf, offs[i_symtab], + gelf_fsize(ebl->elf, ELF_T_SYM, syments, EV_CURRENT), ELF_T_SYM); + symstrdata = elf_getdata_rawchunk(ebl->elf, offs[i_strtab], addrs[i_strsz], + ELF_T_BYTE); + versym_data = elf_getdata_rawchunk(ebl->elf, offs[i_versym], + syments * sizeof(Elf64_Half), ELF_T_HALF); + + /* Get the verneed_data without vernaux. */ + verneed_data = elf_getdata_rawchunk( + ebl->elf, offs[i_verneed], addrs[i_verneednum] * sizeof(Elf64_Verneed), + ELF_T_VNEED); + size_t vernauxnum = 0; + size_t vn_next_offset = 0; + + for (size_t i = 0; i < addrs[i_verneednum]; i++) { + GElf_Verneed *verneed = + (GElf_Verneed *)(verneed_data->d_buf + vn_next_offset); + vernauxnum += verneed->vn_cnt; + vn_next_offset += verneed->vn_next; + } + + /* Update the verneed_data to include the vernaux. */ + verneed_data = elf_getdata_rawchunk( + ebl->elf, offs[i_verneed], + (addrs[i_verneednum] + vernauxnum) * sizeof(GElf_Verneed), ELF_T_VNEED); + + /* Get the verdef_data without verdaux. */ + verdef_data = elf_getdata_rawchunk(ebl->elf, offs[i_verdef], + addrs[i_verdefnum] * sizeof(Elf64_Verdef), + ELF_T_VDEF); + size_t verdauxnum = 0; + size_t vd_next_offset = 0; + + for (size_t i = 0; i < addrs[i_verdefnum]; i++) { + GElf_Verdef *verdef = (GElf_Verdef *)(verdef_data->d_buf + vd_next_offset); + verdauxnum += verdef->vd_cnt; + vd_next_offset += verdef->vd_next; + } + + /* Update the verdef_data to include the verdaux. */ + verdef_data = elf_getdata_rawchunk( + ebl->elf, offs[i_verdef], + (addrs[i_verdefnum] + verdauxnum) * sizeof(GElf_Verdef), ELF_T_VDEF); + + for (size_t i = 0; i < syments; i++) { + /* Get the symbol table entry. */ + GElf_Sym sym_mem; + GElf_Sym *sym; + sym = gelf_getsym(symdata, i, &sym_mem); + + char bindbuf[64]; + char typebuf[64]; + char scnbuf[64]; + int class = gelf_getclass(ebl->elf); + Elf32_Word xndx; + xndx = sym->st_shndx; + + printf(_("\ +%5ld: %0*" PRIx64 " %6" PRId64 " %-7s %-6s %-9s %6s %s"), + i, class == ELFCLASS32 ? 8 : 16, sym->st_value, sym->st_size, + ebl_symbol_type_name(ebl, GELF_ST_TYPE(sym->st_info), typebuf, + sizeof(typebuf)), + ebl_symbol_binding_name(ebl, GELF_ST_BIND(sym->st_info), bindbuf, + sizeof(bindbuf)), + get_visibility_type(GELF_ST_VISIBILITY(sym->st_other)), + ebl_section_name(ebl, sym->st_shndx, xndx, scnbuf, sizeof(scnbuf), + NULL, shnum), + ((char *)symstrdata->d_buf) + sym->st_name); + + if (versym_data != NULL) { + /* Get the version information. */ + GElf_Versym versym_mem; + GElf_Versym *versym = gelf_getversym(versym_data, i, &versym_mem); + + if (versym != NULL && ((*versym & 0x8000) != 0 || *versym > 1)) { + GElf_Vernaux vernaux_mem; + GElf_Vernaux *vernaux = NULL; + size_t vn_offset = 0; + GElf_Verneed verneed_mem; + GElf_Verneed *verneed = (GElf_Verneed *)verneed_data->d_buf; + + while (verneed != NULL) { + size_t vna_offset = vn_offset; + + vernaux = gelf_getvernaux(verneed_data, vna_offset += verneed->vn_aux, + &vernaux_mem); + while (vernaux != NULL && vernaux->vna_other != *versym && + vernaux->vna_next != 0 && + (verneed_data->d_size - vna_offset >= vernaux->vna_next)) { + /* Update the offset. */ + vna_offset += vernaux->vna_next; + + vernaux = + (vernaux->vna_next == 0 + ? NULL + : gelf_getvernaux(verneed_data, vna_offset, &vernaux_mem)); + } + + /* Check whether we found the version. */ + if (vernaux != NULL && vernaux->vna_other == *versym) + /* Found it. */ + break; + + if (verneed_data->d_size - vn_offset < verneed->vn_next) + break; + + vn_offset += verneed->vn_next; + verneed = + (verneed->vn_next == 0 + ? NULL + : gelf_getverneed(verneed_data, vn_offset, &verneed_mem)); + } + + if (vernaux != NULL && vernaux->vna_other == *versym) { + printf("@%s (%u)", (char *)symstrdata->d_buf + vernaux->vna_name, + (unsigned int)vernaux->vna_other); + } + + if (addrs[i_verdefnum] && *versym != 0x8001) { + /* We must test both. */ + size_t vd_offset = 0; + + GElf_Verdef verdef_mem; + GElf_Verdef *verdef = gelf_getverdef(verdef_data, 0, &verdef_mem); + while (verdef != NULL) { + if (verdef->vd_ndx == (*versym & 0x7fff)) + /* Found the definition. */ + break; + + if (verdef_data->d_size - vd_offset < verdef->vd_next) + break; + + vd_offset += verdef->vd_next; + verdef = + (verdef->vd_next == 0 + ? NULL + : gelf_getverdef(verdef_data, vd_offset, &verdef_mem)); + } + + if (verdef != NULL) { + GElf_Verdaux verdaux_mem; + GElf_Verdaux *verdaux = gelf_getverdaux( + verdef_data, vd_offset + verdef->vd_aux, &verdaux_mem); + + if (verdaux != NULL) + printf((*versym & 0x8000) ? "@%s" : "@@%s", + (char *)symstrdata->d_buf + verdaux->vda_name); + } + } + } + } + putchar_unlocked('\n'); + } + return true; +} + + + +static bool handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { Elf_Data *versym_data = NULL; @@ -2503,7 +2771,7 @@ handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the data of the section. */ Elf_Data *data = elf_getdata (scn, NULL); if (data == NULL) - return; + return false; /* Find out whether we have other sections we might need. */ Elf_Scn *runscn = NULL; @@ -2730,6 +2998,7 @@ handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) putchar_unlocked ('\n'); } + return true; } @@ -4955,10 +5224,18 @@ get_dynscn_addrs(Elf *elf, GElf_Phdr *phdr, GElf_Addr addrs[i_max]) addrs[i_verdef] = dyn->d_un.d_ptr; break; + case DT_VERDEFNUM: + addrs[i_verdefnum] = dyn->d_un.d_val; + break; + case DT_VERNEED: addrs[i_verneed] = dyn->d_un.d_ptr; break; + case DT_VERNEEDNUM: + addrs[i_verneednum] = dyn->d_un.d_val; + break; + case DT_STRSZ: addrs[i_strsz] = dyn->d_un.d_val; break; |