From bb4d2ac2cc637c61232624d9d359b8d3f031e3e9 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Tue, 25 Nov 2014 06:47:44 -0800 Subject: Display symbol version when dumping dynrelocs Both readelf/objdump know how to get symbol version string for dynamic symbols. This patch extracts this functionality into a separate function and uses it to add symbol version string to versioned symbol names when dumping dynamic relocations. bfd/ PR binutils/16496 * elf-bfd.h (bfd_elf_get_symbol_version_string): New. * elf.c (bfd_elf_get_symbol_version_string): New. Extracted from bfd_elf_print_symbol. (bfd_elf_print_symbol): Use it. binutils/ PR binutils/16496 * objdump.c (objdump_print_symname): Call bfd_elf_get_symbol_version_string to get ELF symbol version string. Append version string if needed. * readelf.c (versioned_symbol_info): New enum. (get_symbol_version_string): New. Extracted from process_symbol_table. (dump_relocations): Add a new argument to indicate if dynamic symbol table is used. Use get_symbol_version_string to get symbol version string for dynamic symbol. Append version string if needed. (process_relocs): Updated dump_relocations call. (process_symbol_table): Use get_symbol_version_string. ld/testsuite/ PR binutils/16496 * ld-cris/weakref3.d: Add symbol version string to versioned symbol names in dynamic relocation. * ld-cris/weakref4.d: Likewise. * ld-elfvers/vers24.rd: Likewise. * ld-elf/pr16496a.c: New file. * ld-elf/pr16496a.map: Likewise. * ld-elf/pr16496b.c: Likewise. * ld-elf/pr16496b.od: Likewise. * ld-elf/shared.exp (build_tests): Add libpr16496a.so and libpr16496b.so tests. --- binutils/ChangeLog | 17 +++ binutils/objdump.c | 23 +++- binutils/readelf.c | 393 +++++++++++++++++++++++++++++++---------------------- 3 files changed, 269 insertions(+), 164 deletions(-) (limited to 'binutils') diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 3727644..f4d9aa6 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,20 @@ +2014-11-25 H.J. Lu + + PR binutils/16496 + * objdump.c (objdump_print_symname): Call + bfd_elf_get_symbol_version_string to get ELF symbol version + string. Append version string if needed. + + * readelf.c (versioned_symbol_info): New enum. + (get_symbol_version_string): New. Extracted from + process_symbol_table. + (dump_relocations): Add a new argument to indicate if dynamic + symbol table is used. Use get_symbol_version_string to get + symbol version string for dynamic symbol. Append version string + if needed. + (process_relocs): Updated dump_relocations call. + (process_symbol_table): Use get_symbol_version_string. + 2014-11-24 H.J. Lu * configure: Regenerated. diff --git a/binutils/objdump.c b/binutils/objdump.c index da68f39..e04c3ee 100644 --- a/binutils/objdump.c +++ b/binutils/objdump.c @@ -795,7 +795,8 @@ objdump_print_symname (bfd *abfd, struct disassemble_info *inf, asymbol *sym) { char *alloc; - const char *name; + const char *name, *version_string = NULL; + bfd_boolean hidden = FALSE; alloc = NULL; name = bfd_asymbol_name (sym); @@ -807,10 +808,26 @@ objdump_print_symname (bfd *abfd, struct disassemble_info *inf, name = alloc; } + if (bfd_get_flavour (abfd) == bfd_target_elf_flavour) + version_string = bfd_elf_get_symbol_version_string (abfd, sym, + &hidden); + + if (bfd_is_und_section (bfd_get_section (sym))) + hidden = TRUE; + if (inf != NULL) - (*inf->fprintf_func) (inf->stream, "%s", name); + { + (*inf->fprintf_func) (inf->stream, "%s", name); + if (version_string && *version_string != '\0') + (*inf->fprintf_func) (inf->stream, hidden ? "@%s" : "@@%s", + version_string); + } else - printf ("%s", name); + { + printf ("%s", name); + if (version_string && *version_string != '\0') + printf (hidden ? "@%s" : "@@%s", version_string); + } if (alloc != NULL) free (alloc); diff --git a/binutils/readelf.c b/binutils/readelf.c index e8ce279..71fc827 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -272,6 +272,20 @@ typedef enum print_mode } print_mode; +/* Versioned symbol info. */ +enum versioned_symbol_info +{ + symbol_undefined, + symbol_hidden, + symbol_public +}; + +static const char *get_symbol_version_string + (FILE *file, int is_dynsym, const char *strtab, + unsigned long int strtab_size, unsigned int si, + Elf_Internal_Sym *psym, enum versioned_symbol_info *sym_info, + unsigned short *vna_other); + #define UNKNOWN -1 #define SECTION_NAME(X) \ @@ -1015,7 +1029,8 @@ dump_relocations (FILE * file, unsigned long nsyms, char * strtab, unsigned long strtablen, - int is_rela) + int is_rela, + int is_dynsym) { unsigned int i; Elf_Internal_Rela * rels; @@ -1448,9 +1463,20 @@ dump_relocations (FILE * file, else { Elf_Internal_Sym * psym; + const char * version_string; + enum versioned_symbol_info sym_info; + unsigned short vna_other; psym = symtab + symtab_index; + version_string + = get_symbol_version_string (file, is_dynsym, + strtab, strtablen, + symtab_index, + psym, + &sym_info, + &vna_other); + printf (" "); if (ELF_ST_TYPE (psym->st_info) == STT_GNU_IFUNC) @@ -1477,6 +1503,9 @@ dump_relocations (FILE * file, name = strtab + psym->st_name; len = print_symbol (width, name); + if (version_string) + printf (sym_info == symbol_public ? "@@%s" : "@%s", + version_string); printf ("()%-*s", len <= width ? (width + 1) - len : 1, " "); } else @@ -1533,7 +1562,12 @@ dump_relocations (FILE * file, else if (psym->st_name >= strtablen) printf (_(""), psym->st_name); else - print_symbol (22, strtab + psym->st_name); + { + print_symbol (22, strtab + psym->st_name); + if (version_string) + printf (sym_info == symbol_public ? "@@%s" : "@%s", + version_string); + } if (is_rela) { @@ -6097,7 +6131,8 @@ process_relocs (FILE * file) offset_from_vma (file, rel_offset, rel_size), rel_size, dynamic_symbols, num_dynamic_syms, - dynamic_strings, dynamic_strings_length, is_rela); + dynamic_strings, dynamic_strings_length, + is_rela, 1); } } @@ -6172,14 +6207,16 @@ process_relocs (FILE * file) } dump_relocations (file, rel_offset, rel_size, - symtab, nsyms, strtab, strtablen, is_rela); + symtab, nsyms, strtab, strtablen, + is_rela, + symsec->sh_type == SHT_DYNSYM); if (strtab) free (strtab); free (symtab); } else dump_relocations (file, rel_offset, rel_size, - NULL, 0, NULL, 0, is_rela); + NULL, 0, NULL, 0, is_rela, 0); found = 1; } @@ -9879,6 +9916,181 @@ print_dynamic_symbol (bfd_vma si, unsigned long hn) putchar ('\n'); } +static const char * +get_symbol_version_string (FILE *file, int is_dynsym, + const char *strtab, + unsigned long int strtab_size, + unsigned int si, Elf_Internal_Sym *psym, + enum versioned_symbol_info *sym_info, + unsigned short *vna_other) +{ + const char *version_string = NULL; + + if (is_dynsym + && version_info[DT_VERSIONTAGIDX (DT_VERSYM)] != 0) + { + unsigned char data[2]; + unsigned short vers_data; + unsigned long offset; + int is_nobits; + int check_def; + + offset = offset_from_vma + (file, version_info[DT_VERSIONTAGIDX (DT_VERSYM)], + sizeof data + si * sizeof (vers_data)); + + if (get_data (&data, file, offset + si * sizeof (vers_data), + sizeof (data), 1, _("version data")) == NULL) + return NULL; + + vers_data = byte_get (data, 2); + + is_nobits = (psym->st_shndx < elf_header.e_shnum + && section_headers[psym->st_shndx].sh_type + == SHT_NOBITS); + + check_def = (psym->st_shndx != SHN_UNDEF); + + if ((vers_data & VERSYM_HIDDEN) || vers_data > 1) + { + if (version_info[DT_VERSIONTAGIDX (DT_VERNEED)] + && (is_nobits || ! check_def)) + { + Elf_External_Verneed evn; + Elf_Internal_Verneed ivn; + Elf_Internal_Vernaux ivna; + + /* We must test both. */ + offset = offset_from_vma + (file, version_info[DT_VERSIONTAGIDX (DT_VERNEED)], + sizeof evn); + + do + { + unsigned long vna_off; + + if (get_data (&evn, file, offset, sizeof (evn), 1, + _("version need")) == NULL) + { + ivna.vna_next = 0; + ivna.vna_other = 0; + ivna.vna_name = 0; + break; + } + + ivn.vn_aux = BYTE_GET (evn.vn_aux); + ivn.vn_next = BYTE_GET (evn.vn_next); + + vna_off = offset + ivn.vn_aux; + + do + { + Elf_External_Vernaux evna; + + if (get_data (&evna, file, vna_off, + sizeof (evna), 1, + _("version need aux (3)")) == NULL) + { + ivna.vna_next = 0; + ivna.vna_other = 0; + ivna.vna_name = 0; + } + else + { + ivna.vna_other = BYTE_GET (evna.vna_other); + ivna.vna_next = BYTE_GET (evna.vna_next); + ivna.vna_name = BYTE_GET (evna.vna_name); + } + + vna_off += ivna.vna_next; + } + while (ivna.vna_other != vers_data + && ivna.vna_next != 0); + + if (ivna.vna_other == vers_data) + break; + + offset += ivn.vn_next; + } + while (ivn.vn_next != 0); + + if (ivna.vna_other == vers_data) + { + *sym_info = symbol_undefined; + *vna_other = ivna.vna_other; + version_string = (ivna.vna_name < strtab_size + ? strtab + ivna.vna_name + : _("")); + check_def = 0; + } + else if (! is_nobits) + error (_("bad dynamic symbol\n")); + else + check_def = 1; + } + + if (check_def) + { + if (vers_data != 0x8001 + && version_info[DT_VERSIONTAGIDX (DT_VERDEF)]) + { + Elf_Internal_Verdef ivd; + Elf_Internal_Verdaux ivda; + Elf_External_Verdaux evda; + unsigned long off; + + off = offset_from_vma + (file, + version_info[DT_VERSIONTAGIDX (DT_VERDEF)], + sizeof (Elf_External_Verdef)); + + do + { + Elf_External_Verdef evd; + + if (get_data (&evd, file, off, sizeof (evd), + 1, _("version def")) == NULL) + { + ivd.vd_ndx = 0; + ivd.vd_aux = 0; + ivd.vd_next = 0; + } + else + { + ivd.vd_ndx = BYTE_GET (evd.vd_ndx); + ivd.vd_aux = BYTE_GET (evd.vd_aux); + ivd.vd_next = BYTE_GET (evd.vd_next); + } + + off += ivd.vd_next; + } + while (ivd.vd_ndx != (vers_data & VERSYM_VERSION) + && ivd.vd_next != 0); + + off -= ivd.vd_next; + off += ivd.vd_aux; + + if (get_data (&evda, file, off, sizeof (evda), + 1, _("version def aux")) == NULL) + return version_string; + + ivda.vda_name = BYTE_GET (evda.vda_name); + + if (psym->st_name != ivda.vda_name) + { + *sym_info = ((vers_data & VERSYM_HIDDEN) != 0 + ? symbol_hidden : symbol_public); + version_string = (ivda.vda_name < strtab_size + ? strtab + ivda.vda_name + : _("")); + } + } + } + } + } + return version_string; +} + /* Dump the symbol table. */ static int process_symbol_table (FILE * file) @@ -10179,6 +10391,10 @@ process_symbol_table (FILE * file) for (si = 0, psym = symtab; si < num_syms; si++, psym++) { + const char *version_string; + enum versioned_symbol_info sym_info; + unsigned short vna_other; + printf ("%6d: ", si); print_vma (psym->st_value, LONG_HEX); putchar (' '); @@ -10195,163 +10411,18 @@ process_symbol_table (FILE * file) print_symbol (25, psym->st_name < strtab_size ? strtab + psym->st_name : _("")); - if (section->sh_type == SHT_DYNSYM - && version_info[DT_VERSIONTAGIDX (DT_VERSYM)] != 0) + version_string + = get_symbol_version_string (file, + section->sh_type == SHT_DYNSYM, + strtab, strtab_size, si, + psym, &sym_info, &vna_other); + if (version_string) { - unsigned char data[2]; - unsigned short vers_data; - unsigned long offset; - int is_nobits; - int check_def; - - offset = offset_from_vma - (file, version_info[DT_VERSIONTAGIDX (DT_VERSYM)], - sizeof data + si * sizeof (vers_data)); - - if (get_data (&data, file, offset + si * sizeof (vers_data), - sizeof (data), 1, _("version data")) == NULL) - break; - - vers_data = byte_get (data, 2); - - is_nobits = (psym->st_shndx < elf_header.e_shnum - && section_headers[psym->st_shndx].sh_type - == SHT_NOBITS); - - check_def = (psym->st_shndx != SHN_UNDEF); - - if ((vers_data & VERSYM_HIDDEN) || vers_data > 1) - { - if (version_info[DT_VERSIONTAGIDX (DT_VERNEED)] - && (is_nobits || ! check_def)) - { - Elf_External_Verneed evn; - Elf_Internal_Verneed ivn; - Elf_Internal_Vernaux ivna; - - /* We must test both. */ - offset = offset_from_vma - (file, version_info[DT_VERSIONTAGIDX (DT_VERNEED)], - sizeof evn); - - do - { - unsigned long vna_off; - - if (get_data (&evn, file, offset, sizeof (evn), 1, - _("version need")) == NULL) - { - ivna.vna_next = 0; - ivna.vna_other = 0; - ivna.vna_name = 0; - break; - } - - ivn.vn_aux = BYTE_GET (evn.vn_aux); - ivn.vn_next = BYTE_GET (evn.vn_next); - - vna_off = offset + ivn.vn_aux; - - do - { - Elf_External_Vernaux evna; - - if (get_data (&evna, file, vna_off, - sizeof (evna), 1, - _("version need aux (3)")) == NULL) - { - ivna.vna_next = 0; - ivna.vna_other = 0; - ivna.vna_name = 0; - } - else - { - ivna.vna_other = BYTE_GET (evna.vna_other); - ivna.vna_next = BYTE_GET (evna.vna_next); - ivna.vna_name = BYTE_GET (evna.vna_name); - } - - vna_off += ivna.vna_next; - } - while (ivna.vna_other != vers_data - && ivna.vna_next != 0); - - if (ivna.vna_other == vers_data) - break; - - offset += ivn.vn_next; - } - while (ivn.vn_next != 0); - - if (ivna.vna_other == vers_data) - { - printf ("@%s (%d)", - ivna.vna_name < strtab_size - ? strtab + ivna.vna_name : _(""), - ivna.vna_other); - check_def = 0; - } - else if (! is_nobits) - error (_("bad dynamic symbol\n")); - else - check_def = 1; - } - - if (check_def) - { - if (vers_data != 0x8001 - && version_info[DT_VERSIONTAGIDX (DT_VERDEF)]) - { - Elf_Internal_Verdef ivd; - Elf_Internal_Verdaux ivda; - Elf_External_Verdaux evda; - unsigned long off; - - off = offset_from_vma - (file, - version_info[DT_VERSIONTAGIDX (DT_VERDEF)], - sizeof (Elf_External_Verdef)); - - do - { - Elf_External_Verdef evd; - - if (get_data (&evd, file, off, sizeof (evd), - 1, _("version def")) == NULL) - { - ivd.vd_ndx = 0; - ivd.vd_aux = 0; - ivd.vd_next = 0; - } - else - { - ivd.vd_ndx = BYTE_GET (evd.vd_ndx); - ivd.vd_aux = BYTE_GET (evd.vd_aux); - ivd.vd_next = BYTE_GET (evd.vd_next); - } - - off += ivd.vd_next; - } - while (ivd.vd_ndx != (vers_data & VERSYM_VERSION) - && ivd.vd_next != 0); - - off -= ivd.vd_next; - off += ivd.vd_aux; - - if (get_data (&evda, file, off, sizeof (evda), - 1, _("version def aux")) == NULL) - break; - - ivda.vda_name = BYTE_GET (evda.vda_name); - - if (psym->st_name != ivda.vda_name) - printf ((vers_data & VERSYM_HIDDEN) - ? "@%s" : "@@%s", - ivda.vda_name < strtab_size - ? strtab + ivda.vda_name : _("")); - } - } - } + if (sym_info == symbol_undefined) + printf ("@%s (%d)", version_string, vna_other); + else + printf (sym_info == symbol_hidden ? "@%s" : "@@%s", + version_string); } putchar ('\n'); -- cgit v1.1