From e50d9cb3649bdf6521f92fd1e4cee3a690638159 Mon Sep 17 00:00:00 2001 From: James Henderson Date: Thu, 17 Jan 2019 15:34:12 +0000 Subject: [PATCH] [llvm-readobj][ELF]Add demangling support This change adds demangling support to the ELF side of llvm-readobj, under the switch --demangle/-C. The following places are demangled: symbol table dumps (static and dynamic), relocation dumps (static and dynamic), addrsig dumps, call graph profile dumps, and group section signature symbols. Although GNU readelf doesn't support demangling, it is still a useful feature to have, and brings it on a par with llvm-objdump's capabilities. This fixes https://bugs.llvm.org/show_bug.cgi?id=40054. Reviewed by: grimar, rupprecht Differential Revision: https://reviews.llvm.org/D56791 llvm-svn: 351450 --- llvm/docs/CommandGuide/llvm-readobj.rst | 4 + llvm/test/tools/llvm-readobj/demangle.test | 234 +++++++++++++++++++++ llvm/tools/llvm-readobj/ELFDumper.cpp | 52 ++--- llvm/tools/llvm-readobj/llvm-readobj.cpp | 6 + llvm/tools/llvm-readobj/llvm-readobj.h | 1 + 5 files changed, 273 insertions(+), 24 deletions(-) create mode 100644 llvm/test/tools/llvm-readobj/demangle.test diff --git a/llvm/docs/CommandGuide/llvm-readobj.rst b/llvm/docs/CommandGuide/llvm-readobj.rst index 417fcd05c8a2..7d4679f2032f 100644 --- a/llvm/docs/CommandGuide/llvm-readobj.rst +++ b/llvm/docs/CommandGuide/llvm-readobj.rst @@ -84,6 +84,10 @@ input. Otherwise, it will read from the specified ``filenames``. Display section groups (only for ELF object files). +.. option:: -demangle, -C + + Print demangled symbol names in the output. + EXIT STATUS ----------- diff --git a/llvm/test/tools/llvm-readobj/demangle.test b/llvm/test/tools/llvm-readobj/demangle.test new file mode 100644 index 000000000000..ffd0053e4147 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/demangle.test @@ -0,0 +1,234 @@ +## Show that llvm-readelf + llvm-readobj demangle symbol names in symbol tables +## (including dynamic symbols), relocations (including dynamic relocations), and groups. + +# RUN: yaml2obj %s > %t.so + +## Check LLVM output style. +# RUN: llvm-readobj --symbols --relocations --dyn-symbols --dyn-relocations \ +# RUN: --elf-section-groups --elf-cg-profile --addrsig \ +# RUN: --demangle %t.so > %t.llvm.long +# RUN: llvm-readobj --symbols --relocations --dyn-symbols --dyn-relocations \ +# RUN: --elf-section-groups --elf-cg-profile --addrsig \ +# RUN: -C %t.so > %t.llvm.short +# RUN: FileCheck %s --input-file %t.llvm.long --check-prefixes=LLVM-COMMON,LLVM-DEMANGLE +# RUN: diff %t.llvm.long %t.llvm.short + +## Check that default is no demangling. +# RUN: llvm-readobj --symbols --relocations --dyn-symbols --dyn-relocations \ +# RUN: --elf-section-groups --elf-cg-profile --addrsig \ +# RUN: %t.so > %t.llvm.default +# RUN: llvm-readobj --symbols --relocations --dyn-symbols --dyn-relocations \ +# RUN: --elf-section-groups --elf-cg-profile --addrsig \ +# RUN: --demangle=false %t.so > %t.llvm.nodemangle +# RUN: FileCheck %s --input-file %t.llvm.default --check-prefixes=LLVM-COMMON,LLVM-MANGLED +# RUN: diff %t.llvm.default %t.llvm.nodemangle + +# LLVM-COMMON: Relocations [ +# LLVM-COMMON: Section {{.*}} .rela.text.foo { +# LLVM-DEMANGLE-NEXT: {{ }}foo(char){{ }} +# LLVM-MANGLED-NEXT: {{ }}_Z3fooc{{ }} +# LLVM-COMMON-NEXT: } +# LLVM-COMMON: ] + +# LLVM-COMMON: Dynamic Relocations { +# LLVM-DEMANGLE-NEXT: {{ }}foo(int){{ }} +# LLVM-MANGLED-NEXT: {{ }}_Z3fooi{{ }} +# LLVM-COMMON-NEXT: } + +# LLVM-COMMON: Symbols [ +# LLVM-DEMANGLE: Name: foo(char){{ }} +# LLVM-DEMANGLE: Name: blah(float){{ }} +# LLVM-MANGLED: Name: _Z3fooc{{ }} +# LLVM-MANGLED: Name: _Z4blahf{{ }} +# LLVM-COMMON: ] + +# LLVM-COMMON: DynamicSymbols [ +# LLVM-DEMANGLE: Name: foo(int){{ }} +# LLVM-MANGLED: Name: _Z3fooi{{ }} +# LLVM-COMMON: ] + +# LLVM-COMMON: Groups { +# LLVM-DEMANGLE: Signature: foo(char){{$}} +# LLVM-MANGLED: Signature: _Z3fooc{{$}} +# LLVM-COMMON: } + +# LLVM-COMMON: CGProfile [ +# LLVM-DEMANGLE: From: foo(char){{ }} +# LLVM-DEMANGLE: To: blah(float){{ }} +# LLVM-MANGLED: From: _Z3fooc{{ }} +# LLVM-MANGLED: To: _Z4blahf{{ }} +# LLVM-COMMON: ] + +# LLVM-COMMON: Addrsig [ +# LLVM-DEMANGLE-NEXT: Sym: foo(char){{ }} +# LLVM-DEMANGLE-NEXT: Sym: blah(float){{ }} +# LLVM-MANGLED-NEXT: Sym: _Z3fooc{{ }} +# LLVM-MANGLED-NEXT: Sym: _Z4blahf{{ }} +# LLVM-COMMON-NEXT: ] + +## Check GNU output style. +## FIXME: The extra run for --symbols is because GNU mode only prints the dynamic symbols, +## if --dyn-symbols is specified, even if --symbols is specified. +# RUN: llvm-readelf --relocations --dyn-symbols --dyn-relocations \ +# RUN: --elf-section-groups --demangle %t.so > %t.gnu.long +# RUN: llvm-readelf --symbols --demangle %t.so >> %t.gnu.long +# RUN: llvm-readelf --relocations --dyn-symbols --dyn-relocations \ +# RUN: --elf-section-groups -C %t.so > %t.gnu.short +# RUN: llvm-readelf --symbols -C %t.so >> %t.gnu.short +# RUN: FileCheck %s --input-file %t.gnu.long --check-prefixes=GNU-COMMON,GNU-DEMANGLE +# RUN: diff %t.gnu.long %t.gnu.short + +## Check that default is no demangling. +# RUN: llvm-readelf --relocations --dyn-symbols --dyn-relocations \ +# RUN: --elf-section-groups %t.so > %t.gnu.default +# RUN: llvm-readelf --symbols %t.so >> %t.gnu.default +# RUN: llvm-readelf --relocations --dyn-symbols --dyn-relocations \ +# RUN: --elf-section-groups --demangle=false %t.so > %t.gnu.nodemangle +# RUN: llvm-readelf --symbols --demangle=false %t.so >> %t.gnu.nodemangle +# RUN: FileCheck %s --input-file %t.gnu.default --check-prefixes=GNU-COMMON,GNU-MANGLED +# RUN: diff %t.gnu.default %t.gnu.nodemangle + +# GNU-COMMON: Relocation section '.rela.text.foo' at offset {{.*}} contains 1 entries: +# GNU-COMMON-NEXT: Offset Info Type Symbol's Value Symbol's Name + Addend +# GNU-DEMANGLE-NEXT: foo(char){{ }} +# GNU-MANGLED-NEXT: _Z3fooc{{ }} + +# GNU-COMMON: 'RELA' relocation section at offset {{.*}} contains 24 bytes: +# GNU-COMMON-NEXT: Offset Info Type Symbol's Value Symbol's Name + Addend +# GNU-DEMANGLE-NEXT: foo(int){{ }} +# GNU-MANGLED-NEXT: _Z3fooi{{ }} + +# GNU-COMMON: Symbol table '.dynsym' contains 2 entries: +# GNU-COMMON-NEXT: Num: Value Size Type Bind Vis Ndx Name +# GNU-COMMON-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +# GNU-DEMANGLE-NEXT: foo(int){{$}} +# GNU-MANGLED-NEXT: _Z3fooi{{$}} + +# GNU-COMMON: COMDAT group section [{{.*}}] `.group' +# GNU-DEMANGLE-SAME: [foo(char)] +# GNU-MANGLED-SAME: [_Z3fooc] + +# GNU-COMMON: Symbol table '.symtab' contains 3 entries: +# GNU-COMMON-NEXT: Num: Value Size Type Bind Vis Ndx Name +# GNU-COMMON-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +# GNU-DEMANGLE-NEXT: foo(char){{$}} +# GNU-DEMANGLE-NEXT: blah(float){{$}} +# GNU-MANGLED-NEXT: _Z3fooc{{$}} +# GNU-MANGLED-NEXT: _Z4blahf{{$}} + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + ## TODO: Replace the raw section contents with more meaningful dynamic + ## tags/symbols/etc, once yaml2obj supports it. + ## FIXME: yaml2obj does not currently allow custom addresses for .dynstr and + ## .dynsym if DynamicSymbols are specified. + ## See https://bugs.llvm.org/show_bug.cgi?id=40339 + - Name: .dynstr + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + AddressAlign: 0x100 + EntSize: 0x1 + ## "\0_Z3fooi\0" + Content: "005f5a33666f6f6900" + - Name: .dynsym + Type: SHT_DYNSYM + Flags: [ SHF_ALLOC ] + Link: .dynstr + Info: 1 + Address: 0x100 + AddressAlign: 0x100 + EntSize: 0x18 + ## Null symbol; + ## st_name: 1; st_info: Global | Func; st_other: 0; + ## st_shndx: .text.foo; st_value: 0x2000; st_size: 0 + Content: "000000000000000000000000000000000000000000000000010000001200050000200000000000000000000000000000" + - Name: .rela.dyn + Type: SHT_RELA + Flags: [ SHF_ALLOC ] + Link: .dynsym + Info: .text.foo + Address: 0x200 + AddressAlign: 0x100 + EntSize: 0x18 + Relocations: + - Offset: 0x10 + ## FIXME: This should be a lookup in the corresponding symbol table, not necessarily the static symbol table. + ## See https://bugs.llvm.org/show_bug.cgi?id=40337. + Symbol: _Z3fooc + Type: R_X86_64_PC32 + Addend: 0x4 + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC ] + Link: .dynstr + Address: 0x1000 + AddressAlign: 0x1000 + ## DT_STRTAB - 0x0 + ## DT_STRSZ - 0x9 + ## DT_SYMTAB - 0x100 + ## DT_SYMENT - 0x18 + ## DT_RELA - 0x200 + ## DT_RELASZ - 0x18 + ## DT_RELAENT - 0x18 + ## DT_NULL - 0x0 + Content: "050000000000000000000000000000000a000000000000000900000000000000060000000000000000010000000000000b00000000000000180000000000000007000000000000000002000000000000080000000000000018000000000000000900000000000000180000000000000000000000000000000000000000000000" + - Name: .text.foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + Size: 0x40 + Address: 0x2000 + AddressAlign: 0x2000 + - Name: .group + Type: SHT_GROUP + Link: .symtab + Info: _Z3fooc + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .text.foo + - Name: .rela.text.foo + Type: SHT_RELA + Link: .symtab + Info: .text.foo + Relocations: + - Offset: 0x10 + Symbol: _Z3fooc + Type: R_X86_64_PC32 + - Name: .llvm.call-graph-profile + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Link: .symtab + EntSize: 16 + Content: "01000000020000002000000000000000" + - Name: .llvm_addrsig + Type: SHT_LLVM_ADDRSIG + Link: .symtab + Content: "0102" +Symbols: + Global: + - Name: _Z3fooc + Type: STT_FUNC + Section: .text.foo + - Name: _Z4blahf + Type: STT_FUNC + Section: .text.foo +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R, PF_X ] + VAddr: 0x0 + PAddr: 0x0 + Sections: + - Section: .dynsym + - Section: .dynstr + - Section: .rela.dyn + - Section: .dynamic + - Section: .text.foo + - Type: PT_DYNAMIC + Flags: [ PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Sections: + - Section: .dynamic diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 93254717e921..db6c78718639 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -30,6 +30,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Object/ELF.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ELFTypes.h" @@ -271,7 +272,7 @@ public: void getSectionNameIndex(const Elf_Sym *Symbol, const Elf_Sym *FirstSym, StringRef &SectionName, unsigned &SectionIndex) const; - StringRef getStaticSymbolName(uint32_t Index) const; + std::string getStaticSymbolName(uint32_t Index) const; void printSymbolsHelper(bool IsDynamic) const; const Elf_Shdr *getDotSymtabSec() const { return DotSymtabSec; } @@ -795,34 +796,37 @@ StringRef ELFDumper::getSymbolVersion(StringRef StrTab, return StringRef(StrTab.data() + name_offset); } +static std::string maybeDemangle(StringRef Name) { + return opts::Demangle ? demangle(Name) : Name.str(); +} + template -StringRef ELFDumper::getStaticSymbolName(uint32_t Index) const { +std::string ELFDumper::getStaticSymbolName(uint32_t Index) const { const ELFFile *Obj = ObjF->getELFFile(); StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*DotSymtabSec)); Elf_Sym_Range Syms = unwrapOrError(Obj->symbols(DotSymtabSec)); if (Index >= Syms.size()) reportError("Invalid symbol index"); const Elf_Sym *Sym = &Syms[Index]; - return unwrapOrError(Sym->getName(StrTable)); + return maybeDemangle(unwrapOrError(Sym->getName(StrTable))); } template std::string ELFDumper::getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable, bool IsDynamic) const { - StringRef SymbolName = unwrapOrError(Symbol->getName(StrTable)); + std::string SymbolName = + maybeDemangle(unwrapOrError(Symbol->getName(StrTable))); if (!IsDynamic) return SymbolName; - std::string FullSymbolName(SymbolName); - bool IsDefault; StringRef Version = getSymbolVersion(StrTable, &*Symbol, IsDefault); if (!Version.empty()) { - FullSymbolName += (IsDefault ? "@@" : "@"); - FullSymbolName += Version; + SymbolName += (IsDefault ? "@@" : "@"); + SymbolName += Version; } - return FullSymbolName; + return SymbolName; } template @@ -2599,7 +2603,7 @@ struct GroupMember { struct GroupSection { StringRef Name; - StringRef Signature; + std::string Signature; uint64_t ShName; uint64_t Index; uint32_t Link; @@ -2630,13 +2634,13 @@ std::vector getGroups(const ELFFile *Obj) { StringRef Name = unwrapOrError(Obj->getSectionName(&Sec)); StringRef Signature = StrTable.data() + Sym->st_name; - Ret.push_back({Name, - Signature, - Sec.sh_name, + Ret.push_back({Name, + maybeDemangle(Signature), + Sec.sh_name, I - 1, Sec.sh_link, Sec.sh_info, - Data[0], + Data[0], {}}); std::vector &GM = Ret.back().Members; @@ -2693,7 +2697,7 @@ void GNUStyle::printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab, const Elf_Rela &R, bool IsRela) { std::string Offset, Info, Addend, Value; SmallString<32> RelocName; - StringRef TargetName; + std::string TargetName; const Elf_Sym *Sym = nullptr; unsigned Width = ELFT::Is64Bits ? 16 : 8; unsigned Bias = ELFT::Is64Bits ? 8 : 0; @@ -2709,7 +2713,7 @@ void GNUStyle::printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab, TargetName = unwrapOrError(Obj->getSectionName(Sec)); } else if (Sym) { StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*SymTab)); - TargetName = unwrapOrError(Sym->getName(StrTable)); + TargetName = maybeDemangle(unwrapOrError(Sym->getName(StrTable))); } if (Sym && IsRela) { @@ -3375,7 +3379,7 @@ template void GNUStyle::printDynamicRelocation(const ELFO *Obj, Elf_Rela R, bool IsRela) { SmallString<32> RelocName; - StringRef SymbolName; + std::string SymbolName; unsigned Width = ELFT::Is64Bits ? 16 : 8; unsigned Bias = ELFT::Is64Bits ? 8 : 0; // First two fields are bit width dependent. The rest of them are after are @@ -3385,8 +3389,8 @@ void GNUStyle::printDynamicRelocation(const ELFO *Obj, Elf_Rela R, uint32_t SymIndex = R.getSymbol(Obj->isMips64EL()); const Elf_Sym *Sym = this->dumper()->dynamic_symbols().begin() + SymIndex; Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName); - SymbolName = - unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable())); + SymbolName = maybeDemangle( + unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable()))); std::string Addend, Info, Offset, Value; Offset = to_string(format_hex_no_prefix(R.r_offset, Width)); Info = to_string(format_hex_no_prefix(R.r_info, Width)); @@ -4249,7 +4253,7 @@ void LLVMStyle::printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab) { SmallString<32> RelocName; Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); - StringRef TargetName; + std::string TargetName; const Elf_Sym *Sym = unwrapOrError(Obj->getRelocationSymbol(&Rel, SymTab)); if (Sym && Sym->getType() == ELF::STT_SECTION) { const Elf_Shdr *Sec = unwrapOrError( @@ -4257,7 +4261,7 @@ void LLVMStyle::printRelocation(const ELFO *Obj, Elf_Rela Rel, TargetName = unwrapOrError(Obj->getSectionName(Sec)); } else if (Sym) { StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*SymTab)); - TargetName = unwrapOrError(Sym->getName(StrTable)); + TargetName = maybeDemangle(unwrapOrError(Sym->getName(StrTable))); } if (opts::ExpandRelocs) { @@ -4459,11 +4463,11 @@ template void LLVMStyle::printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel) { SmallString<32> RelocName; Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); - StringRef SymbolName; + std::string SymbolName; uint32_t SymIndex = Rel.getSymbol(Obj->isMips64EL()); const Elf_Sym *Sym = this->dumper()->dynamic_symbols().begin() + SymIndex; - SymbolName = - unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable())); + SymbolName = maybeDemangle( + unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable()))); if (opts::ExpandRelocs) { DictScope Group(W, "Relocation"); W.printHex("Offset", Rel.r_offset); diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index 81ce7a590364..d6cb3b933fa6 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -176,6 +176,12 @@ namespace opts { cl::alias HexDumpShort("x", cl::desc("Alias for --hex-dump"), cl::aliasopt(HexDump)); + // -demangle, -C + cl::opt Demangle("demangle", + cl::desc("Demangle symbol names in output")); + cl::alias DemangleShort("C", cl::desc("Alias for --demangle"), + cl::aliasopt(Demangle), cl::NotHidden); + // -hash-table cl::opt HashTable("hash-table", cl::desc("Display ELF hash table")); diff --git a/llvm/tools/llvm-readobj/llvm-readobj.h b/llvm/tools/llvm-readobj/llvm-readobj.h index 92ed098dc642..2bb571d95307 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.h +++ b/llvm/tools/llvm-readobj/llvm-readobj.h @@ -56,6 +56,7 @@ namespace opts { extern llvm::cl::opt ExpandRelocs; extern llvm::cl::opt RawRelr; extern llvm::cl::opt CodeViewSubsectionBytes; + extern llvm::cl::opt Demangle; enum OutputStyleTy { LLVM, GNU }; extern llvm::cl::opt Output; } // namespace opts