diff options
author | Di Chen <dichen@redhat.com> | 2022-04-28 19:55:33 +0800 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2022-08-01 01:20:06 +0200 |
commit | 369c021c6eedae3665c1dbbaa4fc43afbbb698f4 (patch) | |
tree | 59f12753c030b3bbb990eb1aa003c5ec014953fa /src | |
parent | ccc157dc2b96e47d6d1bbb1b066ecbea4975051b (diff) | |
download | elfutils-369c021c6eedae3665c1dbbaa4fc43afbbb698f4.tar.gz |
readelf: Support --dynamic with --use-dynamic
Currently, eu-readelf is using section headers to dump the dynamic
segment information (print_dynamic -> handle_dynamic).
This patch adds new options to eu-readelf (-D, --use-dynamic)
for (-d, --dynamic).
https://sourceware.org/bugzilla/show_bug.cgi?id=28873
Signed-off-by: Di Chen <dichen@redhat.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 13 | ||||
-rw-r--r-- | src/readelf.c | 212 |
2 files changed, 199 insertions, 26 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 8c9f5ddd..db20a6ef 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,16 @@ +2022-04-28 Di Chen <dichen@redhat.com> + + * readelf.c (options): Add use-dynamic 'D'. + (use_dynamic_segment): New static bool. + (enum dyn_idx): New. + (get_dynscn_strtab): New function. + (get_dynscn_addrs): Likewise. + (find_offsets): Likewise. + (parse_opt): Handle 'D'. + (handle_dynamic): New argument phdr. Get data either through the shdr + or phdr. Print segment info when use_dynamic_segment. Use + get_dynscn_strtab. Get library name and paths through strtab_data. + 2022-05-09 Mark Wielaard <mark@klomp.org> * strip.c (remove_debug_relocations): Check gelf_getshdr, gelf_getrela, diff --git a/src/readelf.c b/src/readelf.c index 4b6aab2b..f4d973da 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -137,6 +137,8 @@ static const struct argp_option options[] = { "string-dump", 'p', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 }, { "archive-index", 'c', NULL, 0, N_("Display the symbol index of an archive"), 0 }, + { "use-dynamic", 'D', NULL, 0, + N_("Use the dynamic segment when possible for displaying info"), 0 }, { NULL, 0, NULL, 0, N_("Output control:"), 0 }, { "numeric-addresses", 'N', NULL, 0, @@ -195,6 +197,9 @@ static bool print_symbol_table; /* True if (only) the dynsym table should be printed. */ static bool print_dynsym_table; +/* True if reconstruct dynamic symbol table from the PT_DYNAMIC segment. */ +static bool use_dynamic_segment; + /* A specific section name, or NULL to print all symbol tables. */ static char *symbol_table_section; @@ -318,6 +323,24 @@ static void dump_strings (Ebl *ebl); static void print_strings (Ebl *ebl); static void dump_archive_index (Elf *, const char *); +enum dyn_idx +{ + i_strsz, + i_verneed, + i_verdef, + i_versym, + i_symtab, + i_strtab, + i_hash, + i_gnu_hash, + i_max +}; + +/* Declarations of local functions for use-dynamic. */ +static Elf_Data *get_dynscn_strtab (Elf *elf, GElf_Phdr *phdr); +static void get_dynscn_addrs (Elf *elf, GElf_Phdr *phdr, GElf_Addr addrs[i_max]); +static void find_offsets (Elf *elf, GElf_Addr main_bias, size_t n, + GElf_Addr addrs[n], GElf_Off offs[n]); /* Looked up once with gettext in main. */ static char *yes_str; @@ -429,6 +452,9 @@ parse_opt (int key, char *arg, print_dynamic_table = true; any_control_option = true; break; + case 'D': + use_dynamic_segment = true; + break; case 'e': print_debug_sections |= section_exception; any_control_option = true; @@ -1791,7 +1817,7 @@ get_dyn_ents (Elf_Data * dyn_data) static void -handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) +handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, GElf_Phdr *phdr) { int class = gelf_getclass (ebl->elf); GElf_Shdr glink_mem; @@ -1802,34 +1828,64 @@ handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) size_t dyn_ents; /* Get the data of the section. */ - data = elf_getdata (scn, NULL); + if (use_dynamic_segment) + data = elf_getdata_rawchunk(ebl->elf, phdr->p_offset, + phdr->p_filesz, ELF_T_DYN); + else + data = elf_getdata (scn, NULL); + if (data == NULL) return; /* Get the dynamic section entry number */ dyn_ents = get_dyn_ents (data); - /* Get the section header string table index. */ - if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)) - error_exit (0, _("cannot get section header string table index")); + if (!use_dynamic_segment) + { + /* Get the section header string table index. */ + if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)) + error_exit (0, _("cannot get section header string table index")); - glink = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &glink_mem); - if (glink == NULL) - error_exit (0, _("invalid sh_link value in section %zu"), - elf_ndxscn (scn)); + glink = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &glink_mem); + if (glink == NULL) + error_exit (0, _("invalid sh_link value in section %zu"), + elf_ndxscn (scn)); - printf (ngettext ("\ + printf (ngettext ("\ \nDynamic segment contains %lu entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", "\ \nDynamic segment contains %lu entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", - dyn_ents), - (unsigned long int) dyn_ents, - class == ELFCLASS32 ? 10 : 18, shdr->sh_addr, - shdr->sh_offset, - (int) shdr->sh_link, - elf_strptr (ebl->elf, shstrndx, glink->sh_name)); + dyn_ents), + (unsigned long int) dyn_ents, + class == ELFCLASS32 ? 10 : 18, shdr->sh_addr, + shdr->sh_offset, + (int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, glink->sh_name)); + } + else + { + printf (ngettext ("\ +\nDynamic segment contains %lu entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 "\n", + "\ +\nDynamic segment contains %lu entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 "\n", + dyn_ents), + (unsigned long int) dyn_ents, + class == ELFCLASS32 ? 10 : 18, phdr->p_paddr, + phdr->p_offset); + } + fputs_unlocked (_(" Type Value\n"), stdout); + /* if --use-dynamic option is enabled, + use the string table to get the related library info. */ + Elf_Data *strtab_data = NULL; + if (use_dynamic_segment) + { + strtab_data = get_dynscn_strtab(ebl->elf, phdr); + if (strtab_data == NULL) + error_exit (0, _("cannot get string table by using dynamic segment")); + } + for (cnt = 0; cnt < dyn_ents; ++cnt) { GElf_Dyn dynmem; @@ -1841,6 +1897,20 @@ handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) printf (" %-17s ", ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, sizeof (buf))); + char *name = NULL; + if (dyn->d_tag == DT_NEEDED + || dyn->d_tag == DT_SONAME + || dyn->d_tag == DT_RPATH + || dyn->d_tag == DT_RUNPATH) + { + if (! use_dynamic_segment) + name = elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val); + else if (dyn->d_un.d_ptr < strtab_data->d_size + && memrchr (strtab_data->d_buf + strtab_data->d_size - 1, '\0', + strtab_data->d_size - 1 - dyn->d_un.d_ptr) != NULL) + name = ((char *) strtab_data->d_buf) + dyn->d_un.d_ptr; + } + switch (dyn->d_tag) { case DT_NULL: @@ -1852,23 +1922,19 @@ handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) break; case DT_NEEDED: - printf (_("Shared library: [%s]\n"), - elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + printf (_("Shared library: [%s]\n"), name); break; case DT_SONAME: - printf (_("Library soname: [%s]\n"), - elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + printf (_("Library soname: [%s]\n"), name); break; case DT_RPATH: - printf (_("Library rpath: [%s]\n"), - elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + printf (_("Library rpath: [%s]\n"), name); break; case DT_RUNPATH: - printf (_("Library runpath: [%s]\n"), - elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val)); + printf (_("Library runpath: [%s]\n"), name); break; case DT_PLTRELSZ: @@ -1942,8 +2008,9 @@ print_dynamic (Ebl *ebl) Elf_Scn *scn = gelf_offscn (ebl->elf, phdr->p_offset); GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC) - handle_dynamic (ebl, scn, shdr); + if ((use_dynamic_segment && phdr != NULL) + || (shdr != NULL && shdr->sh_type == SHT_DYNAMIC)) + handle_dynamic (ebl, scn, shdr, phdr); break; } } @@ -4801,6 +4868,99 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, } +/* Turn the addresses into file offsets by using the phdrs. */ +static void +find_offsets(Elf *elf, GElf_Addr main_bias, size_t n, + GElf_Addr addrs[n], GElf_Off offs[n]) +{ + size_t unsolved = n; + for (size_t i = 0; i < phnum; ++i) { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr(elf, i, &phdr_mem); + if (phdr != NULL && phdr->p_type == PT_LOAD && phdr->p_memsz > 0) + for (size_t j = 0; j < n; ++j) + if (offs[j] == 0 && addrs[j] >= phdr->p_vaddr + main_bias && + addrs[j] - (phdr->p_vaddr + main_bias) < phdr->p_filesz) { + offs[j] = addrs[j] - (phdr->p_vaddr + main_bias) + phdr->p_offset; + if (--unsolved == 0) + break; + } + } +} + +/* The dynamic segment (type PT_DYNAMIC), contains the .dynamic section. + And .dynamic section contains an array of the dynamic structures. + We use the array to get: + DT_STRTAB: the address of the string table + DT_SYMTAB: the address of the symbol table + DT_STRSZ: the size, in bytes, of the string table + ... */ +static void +get_dynscn_addrs(Elf *elf, GElf_Phdr *phdr, GElf_Addr addrs[i_max]) +{ + Elf_Data *data = elf_getdata_rawchunk( + elf, phdr->p_offset, phdr->p_filesz, ELF_T_DYN); + + int dyn_idx = 0; + for (;; ++dyn_idx) { + GElf_Dyn dyn_mem; + GElf_Dyn *dyn = gelf_getdyn(data, dyn_idx, &dyn_mem); + /* DT_NULL Marks end of dynamic section. */ + if (dyn->d_tag == DT_NULL) + break; + + switch (dyn->d_tag) { + case DT_SYMTAB: + addrs[i_symtab] = dyn->d_un.d_ptr; + break; + + case DT_HASH: + addrs[i_hash] = dyn->d_un.d_ptr; + break; + + case DT_GNU_HASH: + addrs[i_gnu_hash] = dyn->d_un.d_ptr; + break; + + case DT_STRTAB: + addrs[i_strtab] = dyn->d_un.d_ptr; + break; + + case DT_VERSYM: + addrs[i_versym] = dyn->d_un.d_ptr; + break; + + case DT_VERDEF: + addrs[i_verdef] = dyn->d_un.d_ptr; + break; + + case DT_VERNEED: + addrs[i_verneed] = dyn->d_un.d_ptr; + break; + + case DT_STRSZ: + addrs[i_strsz] = dyn->d_un.d_val; + break; + } + } +} + + +/* Use dynamic segment to get data for the string table section. */ +static Elf_Data * +get_dynscn_strtab(Elf *elf, GElf_Phdr *phdr) +{ + Elf_Data *strtab_data; + GElf_Addr addrs[i_max] = {0,}; + GElf_Off offs[i_max] = {0,}; + get_dynscn_addrs(elf, phdr, addrs); + find_offsets(elf, 0, i_max, addrs, offs); + strtab_data = elf_getdata_rawchunk( + elf, offs[i_strtab], addrs[i_strsz], ELF_T_BYTE); + return strtab_data; +} + + struct listptr { Dwarf_Off offset:(64 - 3); |