From a6ee9fd642a18c20acc28dcb58c639a7e4c91b8b Mon Sep 17 00:00:00 2001 From: Hemant Kulkarni Date: Wed, 23 Nov 2016 18:04:23 +0000 Subject: [PATCH] llvm-readobj: Use hash tables to print dynamic symbols. -symbols prints both .symtab and .dynsym symbols for GNU style in ELF. -dyn-symbols prints symbols looking up through hash tables. This helps validate hash tables. llvm-svn: 287786 --- llvm/test/tools/llvm-readobj/gnu-symbols.test | 28 +++++ llvm/tools/llvm-readobj/ELFDumper.cpp | 109 +++++++++++++++++- 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/llvm/test/tools/llvm-readobj/gnu-symbols.test b/llvm/test/tools/llvm-readobj/gnu-symbols.test index aed38ef680be..0d8dcc8c5bb0 100644 --- a/llvm/test/tools/llvm-readobj/gnu-symbols.test +++ b/llvm/test/tools/llvm-readobj/gnu-symbols.test @@ -4,6 +4,8 @@ RUN: llvm-readobj -symbols %p/Inputs/relocs.obj.elf-x86_64 --elf-output-style=GN RUN: | FileCheck %s -check-prefix ELF64 RUN: llvm-readobj -symbols %p/Inputs/gnuhash.so.elf-x86_64 --elf-output-style=GNU \ RUN: | FileCheck %s -check-prefix DYN +RUN: llvm-readobj -dyn-symbols %p/Inputs/dynamic-table-exe.x86 --elf-output-style=GNU \ +RUN: | FileCheck %s -check-prefix HASH ELF32: Symbol table '.symtab' contains 5 entries: ELF32-NEXT: Num: Value Size Type Bind Vis Ndx Name @@ -44,3 +46,29 @@ DYN-NEXT: 8: 0000000000200268 0 NOTYPE GLOBAL DEFAULT 5 __bss_start DYN-NEXT: 9: 00000000000001b8 0 NOTYPE GLOBAL DEFAULT 4 foo DYN-NEXT: 10: 0000000000200268 0 NOTYPE GLOBAL DEFAULT 5 _edata DYN-NEXT: 11: 0000000000200268 0 NOTYPE GLOBAL DEFAULT 5 _end + +HASH: Symbol table of .hash for image: +HASH-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name +HASH-NEXT: 9 0: 00000000 0 FUNC GLOBAL DEFAULT UND __gxx_personality_v0@CXXABI_1.3 +HASH-NEXT: 13 0: 00001b64 0 NOTYPE GLOBAL DEFAULT ABS _edata@ +HASH-NEXT: 7 0: 00000000 0 FUNC GLOBAL DEFAULT UND _ZNSt14basic_ifstreamIcSt11char_traitsIcEEC1EPKcSt13_Ios_Openmode@GLIBCXX_3.4 +HASH-NEXT: 2 0: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses@ +HASH-NEXT: 1 0: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__@ +HASH-NEXT: 16 1: 00000850 81 FUNC GLOBAL DEFAULT 14 main@ +HASH-NEXT: 10 1: 00000000 0 FUNC GLOBAL DEFAULT UND _Unwind_Resume@GCC_3.0 +HASH-NEXT: 8 1: 00000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.0 +HASH-NEXT: 12 1: 00001b68 0 NOTYPE GLOBAL DEFAULT ABS _end@ +HASH-NEXT: 6 1: 00000000 0 FUNC GLOBAL DEFAULT UND _ZNSt14basic_ifstreamIcSt11char_traitsIcEED1Ev@GLIBCXX_3.4 +HASH-NEXT: 5 1: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable@ +HASH-NEXT: 4 1: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTable@ +HASH-NEXT: 3 1: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.0 +HASH-NEXT: 11 2: 00000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.1.3 +HASH-NEXT: 15 2: 00001b64 0 NOTYPE GLOBAL DEFAULT ABS __bss_start@ +HASH-NEXT: 14 2: 0000093c 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used@ +HASH: Symbol table of .gnu.hash for image: +HASH-NEXT: Num Buc: Value Size Type Bind Vis Ndx Name +HASH-NEXT: 12 0: 00001b68 0 NOTYPE GLOBAL DEFAULT ABS _end@ +HASH-NEXT: 13 0: 00001b64 0 NOTYPE GLOBAL DEFAULT ABS _edata@ +HASH-NEXT: 14 1: 0000093c 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used@ +HASH-NEXT: 15 1: 00001b64 0 NOTYPE GLOBAL DEFAULT ABS __bss_start@ +HASH-NEXT: 16 1: 00000850 81 FUNC GLOBAL DEFAULT 14 main@ diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 0fdf3919470b..5d7f92212adb 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -328,6 +328,8 @@ private: OS.flush(); return OS; } + void printHashedSymbol(const ELFO *Obj, const Elf_Sym *FirstSym, uint32_t Sym, + StringRef StrTable, uint32_t Bucket); void printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab, const Elf_Rela &R, bool IsRela); void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First, @@ -2812,15 +2814,120 @@ void GNUStyle::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, printField(Entry); OS << "\n"; } +template +void GNUStyle::printHashedSymbol(const ELFO *Obj, const Elf_Sym *FirstSym, + uint32_t Sym, StringRef StrTable, + uint32_t Bucket) { + std::string Num, Buc, Name, Value, Size, Binding, Type, Visibility, Section; + unsigned Width, Bias = 0; + if (ELFT::Is64Bits) { + Bias = 8; + Width = 16; + } else { + Bias = 0; + Width = 8; + } + Field Fields[9] = {0, 6, 11, 20 + Bias, 25 + Bias, + 34 + Bias, 41 + Bias, 49 + Bias, 53 + Bias}; + Num = to_string(format_decimal(Sym, 5)); + Buc = to_string(format_decimal(Bucket, 3)) + ":"; + + const auto Symbol = FirstSym + Sym; + Value = to_string(format_hex_no_prefix(Symbol->st_value, Width)); + Size = to_string(format_decimal(Symbol->st_size, 5)); + unsigned char SymbolType = Symbol->getType(); + if (Obj->getHeader()->e_machine == ELF::EM_AMDGPU && + SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS) + Type = printEnum(SymbolType, makeArrayRef(AMDGPUSymbolTypes)); + else + Type = printEnum(SymbolType, makeArrayRef(ElfSymbolTypes)); + unsigned Vis = Symbol->getVisibility(); + Binding = printEnum(Symbol->getBinding(), makeArrayRef(ElfSymbolBindings)); + Visibility = printEnum(Vis, makeArrayRef(ElfSymbolVisibilities)); + Section = getSymbolSectionNdx(Obj, Symbol, FirstSym); + Name = this->dumper()->getFullSymbolName(Symbol, StrTable, true); + Fields[0].Str = Num; + Fields[1].Str = Buc; + Fields[2].Str = Value; + Fields[3].Str = Size; + Fields[4].Str = Type; + Fields[5].Str = Binding; + Fields[6].Str = Visibility; + Fields[7].Str = Section; + Fields[8].Str = Name; + for (auto &Entry : Fields) + printField(Entry); + OS << "\n"; +} template void GNUStyle::printSymbols(const ELFO *Obj) { + if (opts::DynamicSymbols) + return; this->dumper()->printSymbolsHelper(true); this->dumper()->printSymbolsHelper(false); } template void GNUStyle::printDynamicSymbols(const ELFO *Obj) { - this->dumper()->printSymbolsHelper(true); + if (this->dumper()->getDynamicStringTable().size() == 0) + return; + auto StringTable = this->dumper()->getDynamicStringTable(); + auto DynSyms = this->dumper()->dynamic_symbols(); + auto GnuHash = this->dumper()->getGnuHashTable(); + auto SysVHash = this->dumper()->getHashTable(); + + // If no hash or .gnu.hash found, try using symbol table + if (GnuHash == nullptr && SysVHash == nullptr) + this->dumper()->printSymbolsHelper(true); + + // Try printing .hash + if (this->dumper()->getHashTable()) { + OS << "\n Symbol table of .hash for image:\n"; + if (ELFT::Is64Bits) + OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; + else + OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; + OS << "\n"; + + uint32_t NBuckets = SysVHash->nbucket; + uint32_t NChains = SysVHash->nchain; + auto Buckets = SysVHash->buckets(); + auto Chains = SysVHash->chains(); + for (uint32_t Buc = 0; Buc < NBuckets; Buc++) { + if (Buckets[Buc] == ELF::STN_UNDEF) + continue; + for (uint32_t Ch = Buckets[Buc]; Ch < NChains; Ch = Chains[Ch]) { + if (Ch == ELF::STN_UNDEF) + break; + printHashedSymbol(Obj, &DynSyms[0], Ch, StringTable, Buc); + } + } + } + + // Try printing .gnu.hash + if (GnuHash) { + OS << "\n Symbol table of .gnu.hash for image:\n"; + if (ELFT::Is64Bits) + OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; + else + OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; + OS << "\n"; + uint32_t NBuckets = GnuHash->nbuckets; + auto Buckets = GnuHash->buckets(); + for (uint32_t Buc = 0; Buc < NBuckets; Buc++) { + if (Buckets[Buc] == ELF::STN_UNDEF) + continue; + uint32_t Index = Buckets[Buc]; + uint32_t GnuHashable = Index - GnuHash->symndx; + // Print whole chain + while (true) { + printHashedSymbol(Obj, &DynSyms[0], Index++, StringTable, Buc); + // Chain ends at symbol with stopper bit + if ((GnuHash->values(DynSyms.size())[GnuHashable++] & 1) == 1) + break; + } + } + } } static inline std::string printPhdrFlags(unsigned Flag) {