diff --git a/docs/CommandGuide/llvm-readobj.rst b/docs/CommandGuide/llvm-readobj.rst index 417fcd05c8a..7d4679f2032 100644 --- a/docs/CommandGuide/llvm-readobj.rst +++ b/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/test/tools/llvm-readobj/demangle.test b/test/tools/llvm-readobj/demangle.test new file mode 100644 index 00000000000..ffd0053e414 --- /dev/null +++ b/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/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index 93254717e92..db6c7871863 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/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/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index 81ce7a59036..d6cb3b933fa 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/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/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h index 92ed098dc64..2bb571d9530 100644 --- a/tools/llvm-readobj/llvm-readobj.h +++ b/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