[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
This commit is contained in:
James Henderson 2019-01-17 15:34:12 +00:00
parent 81cff31ccf
commit e50d9cb364
5 changed files with 273 additions and 24 deletions

View File

@ -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
-----------

View File

@ -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

View File

@ -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<ELFT>::getSymbolVersion(StringRef StrTab,
return StringRef(StrTab.data() + name_offset);
}
static std::string maybeDemangle(StringRef Name) {
return opts::Demangle ? demangle(Name) : Name.str();
}
template <typename ELFT>
StringRef ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const {
std::string ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const {
const ELFFile<ELFT> *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 <typename ELFT>
std::string ELFDumper<ELFT>::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 <typename ELFT>
@ -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<GroupSection> getGroups(const ELFFile<ELFT> *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<GroupMember> &GM = Ret.back().Members;
@ -2693,7 +2697,7 @@ void GNUStyle<ELFT>::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<ELFT>::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 <class ELFT>
void GNUStyle<ELFT>::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<ELFT>::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<ELFT>::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<ELFT>::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 <class ELFT>
void LLVMStyle<ELFT>::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);

View File

@ -176,6 +176,12 @@ namespace opts {
cl::alias HexDumpShort("x", cl::desc("Alias for --hex-dump"),
cl::aliasopt(HexDump));
// -demangle, -C
cl::opt<bool> 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<bool> HashTable("hash-table",
cl::desc("Display ELF hash table"));

View File

@ -56,6 +56,7 @@ namespace opts {
extern llvm::cl::opt<bool> ExpandRelocs;
extern llvm::cl::opt<bool> RawRelr;
extern llvm::cl::opt<bool> CodeViewSubsectionBytes;
extern llvm::cl::opt<bool> Demangle;
enum OutputStyleTy { LLVM, GNU };
extern llvm::cl::opt<OutputStyleTy> Output;
} // namespace opts