1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-23 03:02:36 +01:00

[llvm-objdump] Teach llvm-objdump dump dynamic symbols.

Summary:
This patch is to teach `llvm-objdump` dump dynamic symbols (`-T` and `--dynamic-syms`). Currently, this patch is not fully compatible with `gnu-objdump`, but I would like to continue working on this in next few patches. It has two issues.

1. Some symbols shouldn't be marked as global(g). (`-t/--syms` has same issue as well) (Fixed by D75659)
2. `gnu-objdump` can dump version information and *dynamically* insert before symbol name field.

`objdump -T a.out` gives:

```
DYNAMIC SYMBOL TABLE:
0000000000000000  w   D  *UND*  0000000000000000              _ITM_deregisterTMCloneTable
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 printf
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __libc_start_main
0000000000000000  w   D  *UND*  0000000000000000              __gmon_start__
0000000000000000  w   D  *UND*  0000000000000000              _ITM_registerTMCloneTable
0000000000000000  w   DF *UND*  0000000000000000  GLIBC_2.2.5 __cxa_finalize
```

`llvm-objdump -T a.out` gives:

```
DYNAMIC SYMBOL TABLE:
0000000000000000  w   D  *UND*  0000000000000000 _ITM_deregisterTMCloneTable
0000000000000000 g    DF *UND*  0000000000000000 printf
0000000000000000 g    DF *UND*  0000000000000000 __libc_start_main
0000000000000000  w   D  *UND*  0000000000000000 __gmon_start__
0000000000000000  w   D  *UND*  0000000000000000 _ITM_registerTMCloneTable
0000000000000000  w   DF *UND*  0000000000000000 __cxa_finalize
```

Reviewers: jhenderson, grimar, MaskRay, espindola

Reviewed By: jhenderson, grimar

Subscribers: emaste, rupprecht, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D75756
This commit is contained in:
vgxbj 2020-04-05 09:58:53 +08:00
parent b1f5813526
commit 4e26366ac1
5 changed files with 315 additions and 133 deletions

View File

@ -85,6 +85,10 @@ combined with other commands:
Display the symbol table.
.. option:: -T, --dynamic-syms
Display the contents of the dynamic symbol table.
.. option:: -u, --unwind-info
Display the unwind info of the input(s).

View File

@ -0,0 +1,107 @@
## Test that llvm-objdump can dump dynamic symbols.
# RUN: yaml2obj --docnum=1 %s -o %t1
# RUN: llvm-objdump --dynamic-syms %t1 | \
# RUN: FileCheck %s --match-full-lines --strict-whitespace --check-prefix=DYNSYM
# RUN: llvm-objdump -T %t1 | \
# RUN: FileCheck %s --match-full-lines --strict-whitespace --check-prefix=DYNSYM
# DYNSYM:DYNAMIC SYMBOL TABLE:
# DYNSYM-NEXT:0000000000000000 l DO .data 0000000000000000 localsym
# DYNSYM-NEXT:0000000000000000 g DO .data 0000000000000000 globalsym
# DYNSYM-NEXT:0000000000000000 u DO .data 0000000000000000 uniqueglobalsym
# DYNSYM-NEXT:0000000000000000 w DO .data 0000000000000000 weaksym
# DYNSYM-NEXT:0000000000000000 g Df .data 0000000000000000 filesym
# DYNSYM-NEXT:0000000000000000 g DF .data 0000000000000000 funcsym
# DYNSYM-EMPTY:
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_DYN
Machine: EM_X86_64
Sections:
- Name: .data
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_WRITE ]
DynamicSymbols:
- Name: localsym
Type: STT_OBJECT
Section: .data
Binding: STB_LOCAL
- Name: globalsym
Type: STT_OBJECT
Section: .data
Binding: STB_GLOBAL
- Name: uniqueglobalsym
Type: STT_OBJECT
Section: .data
Binding: STB_GNU_UNIQUE
- Name: weaksym
Type: STT_OBJECT
Section: .data
Binding: STB_WEAK
- Name: filesym
Type: STT_FILE
Section: .data
Binding: STB_GLOBAL
- Name: funcsym
Type: STT_FUNC
Section: .data
Binding: STB_GLOBAL
## Test dumping ELF files with no .dynsym section.
# RUN: yaml2obj --docnum=2 %s -o %t2
# RUN: llvm-objdump --dynamic-syms %t2 | \
# RUN: FileCheck %s --match-full-lines --strict-whitespace --check-prefix=NODYNSYM
# RUN: llvm-objdump -T %t2 | \
# RUN: FileCheck %s --match-full-lines --strict-whitespace --check-prefix=NODYNSYM
# NODYNSYM:DYNAMIC SYMBOL TABLE:
# NODYNSYM-EMPTY:
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_DYN
Machine: EM_X86_64
## Test dumping ELF files with logically empty .dynsym section (only has a 0-index NULL symbol).
# RUN: yaml2obj --docnum=3 %s -o %t3
# RUN: llvm-objdump --dynamic-syms %t3 | \
# RUN: FileCheck %s --match-full-lines --strict-whitespace --check-prefix=ONLY-NULL
# RUN: llvm-objdump -T %t3 | \
# RUN: FileCheck %s --match-full-lines --strict-whitespace --check-prefix=ONLY-NULL
# ONLY-NULL:DYNAMIC SYMBOL TABLE:
# ONLY-NULL-EMPTY:
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_DYN
Machine: EM_X86_64
DynamicSymbols: []
## Test dumping ELF files with truly empty .dynsym section (size of .dynsym section is 0).
# RUN: yaml2obj --docnum=4 %s -o %t4
# RUN: llvm-objdump --dynamic-syms %t4 | \
# RUN: FileCheck %s --match-full-lines --strict-whitespace --check-prefix=EMPTY
# RUN: llvm-objdump -T %t4 | \
# RUN: FileCheck %s --match-full-lines --strict-whitespace --check-prefix=EMPTY
# EMPTY:DYNAMIC SYMBOL TABLE:
# EMPTY-EMPTY:
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_DYN
Machine: EM_X86_64
Sections:
- Name: .dynsym
Type: SHT_DYNSYM
Size: 0

View File

@ -0,0 +1,31 @@
## Test dumping dynamic symbols reports a warning if the operation is unsupported for the file format.
# RUN: yaml2obj %s --docnum=1 -o %t.macho.o
# RUN: llvm-objdump --dynamic-syms %t.macho.o 2>&1 | \
# RUN: FileCheck %s -DFILE=%t.macho.o
# RUN: yaml2obj %s --docnum=2 -o %t.coff.o
# RUN: llvm-objdump --dynamic-syms %t.coff.o 2>&1 | \
# RUN: FileCheck %s -DFILE=%t.coff.o
# RUN: llvm-objdump --dynamic-syms %p/XCOFF/Inputs/xcoff-section-headers.o 2>&1 | \
# RUN: FileCheck %s -DFILE=%p/XCOFF/Inputs/xcoff-section-headers.o
# CHECK:DYNAMIC SYMBOL TABLE:
# CHECK-NEXT:{{.*}}llvm-objdump: warning: '[[FILE]]': this operation is not currently supported for this file format
# CHECK-EMPTY:
--- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x01000007
cpusubtype: 0x80000003
filetype: 0x00000002
ncmds: 0
sizeofcmds: 0
flags: 0x00218085
reserved: 0x00000000
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: []
sections: []
symbols: []

View File

@ -321,6 +321,15 @@ static cl::alias SymbolTableShort("t", cl::desc("Alias for --syms"),
cl::NotHidden, cl::Grouping,
cl::aliasopt(SymbolTable));
cl::opt<bool> DynamicSymbolTable(
"dynamic-syms",
cl::desc("Display the contents of the dynamic symbol table"),
cl::cat(ObjdumpCat));
static cl::alias DynamicSymbolTableShort("T",
cl::desc("Alias for --dynamic-syms"),
cl::NotHidden, cl::Grouping,
cl::aliasopt(DynamicSymbolTable));
cl::opt<std::string> TripleName("triple",
cl::desc("Target triple to disassemble for, "
"see -version for available targets"),
@ -1857,143 +1866,167 @@ void printSectionContents(const ObjectFile *Obj) {
}
void printSymbolTable(const ObjectFile *O, StringRef ArchiveName,
StringRef ArchitectureName) {
outs() << "SYMBOL TABLE:\n";
if (const COFFObjectFile *Coff = dyn_cast<const COFFObjectFile>(O)) {
printCOFFSymbolTable(Coff);
StringRef ArchitectureName, bool DumpDynamic) {
if (O->isCOFF() && !DumpDynamic) {
outs() << "SYMBOL TABLE:\n";
printCOFFSymbolTable(cast<const COFFObjectFile>(O));
return;
}
const StringRef FileName = O->getFileName();
const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(O);
for (auto I = O->symbol_begin(), E = O->symbol_end(); I != E; ++I) {
const SymbolRef &Symbol = *I;
uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName, ArchiveName,
ArchitectureName);
if ((Address < StartAddress) || (Address > StopAddress))
continue;
SymbolRef::Type Type = unwrapOrError(Symbol.getType(), FileName,
ArchiveName, ArchitectureName);
uint32_t Flags = Symbol.getFlags();
// Don't ask a Mach-O STAB symbol for its section unless you know that
// STAB symbol's section field refers to a valid section index. Otherwise
// the symbol may error trying to load a section that does not exist.
bool isSTAB = false;
if (MachO) {
DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
uint8_t NType = (MachO->is64Bit() ?
MachO->getSymbol64TableEntry(SymDRI).n_type:
MachO->getSymbolTableEntry(SymDRI).n_type);
if (NType & MachO::N_STAB)
isSTAB = true;
}
section_iterator Section = isSTAB ? O->section_end() :
unwrapOrError(Symbol.getSection(), FileName,
ArchiveName, ArchitectureName);
StringRef Name;
if (Type == SymbolRef::ST_Debug && Section != O->section_end()) {
if (Expected<StringRef> NameOrErr = Section->getName())
Name = *NameOrErr;
else
consumeError(NameOrErr.takeError());
} else {
Name = unwrapOrError(Symbol.getName(), FileName, ArchiveName,
ArchitectureName);
}
bool Global = Flags & SymbolRef::SF_Global;
bool Weak = Flags & SymbolRef::SF_Weak;
bool Absolute = Flags & SymbolRef::SF_Absolute;
bool Common = Flags & SymbolRef::SF_Common;
bool Hidden = Flags & SymbolRef::SF_Hidden;
char GlobLoc = ' ';
if ((Section != O->section_end() || Absolute) && !Weak)
GlobLoc = Global ? 'g' : 'l';
char IFunc = ' ';
if (isa<ELFObjectFileBase>(O)) {
if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC)
IFunc = 'i';
if (ELFSymbolRef(*I).getBinding() == ELF::STB_GNU_UNIQUE)
GlobLoc = 'u';
}
char Debug = (Type == SymbolRef::ST_Debug || Type == SymbolRef::ST_File)
? 'd' : ' ';
char FileFunc = ' ';
if (Type == SymbolRef::ST_File)
FileFunc = 'f';
else if (Type == SymbolRef::ST_Function)
FileFunc = 'F';
else if (Type == SymbolRef::ST_Data)
FileFunc = 'O';
const char *Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 :
"%08" PRIx64;
outs() << format(Fmt, Address) << " "
<< GlobLoc // Local -> 'l', Global -> 'g', Neither -> ' '
<< (Weak ? 'w' : ' ') // Weak?
<< ' ' // Constructor. Not supported yet.
<< ' ' // Warning. Not supported yet.
<< IFunc
<< Debug // Debugging (d) or dynamic (D) symbol.
<< FileFunc // Name of function (F), file (f) or object (O).
<< ' ';
if (Absolute) {
outs() << "*ABS*";
} else if (Common) {
outs() << "*COM*";
} else if (Section == O->section_end()) {
outs() << "*UND*";
} else {
if (const MachOObjectFile *MachO =
dyn_cast<const MachOObjectFile>(O)) {
DataRefImpl DR = Section->getRawDataRefImpl();
StringRef SegmentName = MachO->getSectionFinalSegmentName(DR);
outs() << SegmentName << ",";
}
StringRef SectionName =
unwrapOrError(Section->getName(), O->getFileName());
outs() << SectionName;
}
if (Common || isa<ELFObjectFileBase>(O)) {
uint64_t Val =
Common ? Symbol.getAlignment() : ELFSymbolRef(Symbol).getSize();
outs() << '\t' << format(Fmt, Val);
}
if (isa<ELFObjectFileBase>(O)) {
uint8_t Other = ELFSymbolRef(Symbol).getOther();
switch (Other) {
case ELF::STV_DEFAULT:
break;
case ELF::STV_INTERNAL:
outs() << " .internal";
break;
case ELF::STV_HIDDEN:
outs() << " .hidden";
break;
case ELF::STV_PROTECTED:
outs() << " .protected";
break;
default:
outs() << format(" 0x%02x", Other);
break;
}
} else if (Hidden) {
outs() << " .hidden";
}
if (Demangle)
outs() << ' ' << demangle(std::string(Name)) << '\n';
else
outs() << ' ' << Name << '\n';
if (!DumpDynamic) {
outs() << "SYMBOL TABLE:\n";
for (auto I = O->symbol_begin(); I != O->symbol_end(); ++I)
printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic);
return;
}
outs() << "DYNAMIC SYMBOL TABLE:\n";
if (!O->isELF()) {
reportWarning(
"this operation is not currently supported for this file format",
FileName);
return;
}
const ELFObjectFileBase *ELF = cast<const ELFObjectFileBase>(O);
for (auto I = ELF->getDynamicSymbolIterators().begin();
I != ELF->getDynamicSymbolIterators().end(); ++I)
printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic);
}
void printSymbol(const ObjectFile *O, const SymbolRef &Symbol,
StringRef FileName, StringRef ArchiveName,
StringRef ArchitectureName, bool DumpDynamic) {
const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(O);
uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName, ArchiveName,
ArchitectureName);
if ((Address < StartAddress) || (Address > StopAddress))
return;
SymbolRef::Type Type =
unwrapOrError(Symbol.getType(), FileName, ArchiveName, ArchitectureName);
uint32_t Flags = Symbol.getFlags();
// Don't ask a Mach-O STAB symbol for its section unless you know that
// STAB symbol's section field refers to a valid section index. Otherwise
// the symbol may error trying to load a section that does not exist.
bool IsSTAB = false;
if (MachO) {
DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
uint8_t NType =
(MachO->is64Bit() ? MachO->getSymbol64TableEntry(SymDRI).n_type
: MachO->getSymbolTableEntry(SymDRI).n_type);
if (NType & MachO::N_STAB)
IsSTAB = true;
}
section_iterator Section = IsSTAB
? O->section_end()
: unwrapOrError(Symbol.getSection(), FileName,
ArchiveName, ArchitectureName);
StringRef Name;
if (Type == SymbolRef::ST_Debug && Section != O->section_end()) {
if (Expected<StringRef> NameOrErr = Section->getName())
Name = *NameOrErr;
else
consumeError(NameOrErr.takeError());
} else {
Name = unwrapOrError(Symbol.getName(), FileName, ArchiveName,
ArchitectureName);
}
bool Global = Flags & SymbolRef::SF_Global;
bool Weak = Flags & SymbolRef::SF_Weak;
bool Absolute = Flags & SymbolRef::SF_Absolute;
bool Common = Flags & SymbolRef::SF_Common;
bool Hidden = Flags & SymbolRef::SF_Hidden;
char GlobLoc = ' ';
if ((Section != O->section_end() || Absolute) && !Weak)
GlobLoc = Global ? 'g' : 'l';
char IFunc = ' ';
if (isa<ELFObjectFileBase>(O)) {
if (ELFSymbolRef(Symbol).getELFType() == ELF::STT_GNU_IFUNC)
IFunc = 'i';
if (ELFSymbolRef(Symbol).getBinding() == ELF::STB_GNU_UNIQUE)
GlobLoc = 'u';
}
char Debug = ' ';
if (DumpDynamic)
Debug = 'D';
else if (Type == SymbolRef::ST_Debug || Type == SymbolRef::ST_File)
Debug = 'd';
char FileFunc = ' ';
if (Type == SymbolRef::ST_File)
FileFunc = 'f';
else if (Type == SymbolRef::ST_Function)
FileFunc = 'F';
else if (Type == SymbolRef::ST_Data)
FileFunc = 'O';
const char *Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
outs() << format(Fmt, Address) << " "
<< GlobLoc // Local -> 'l', Global -> 'g', Neither -> ' '
<< (Weak ? 'w' : ' ') // Weak?
<< ' ' // Constructor. Not supported yet.
<< ' ' // Warning. Not supported yet.
<< IFunc // Indirect reference to another symbol.
<< Debug // Debugging (d) or dynamic (D) symbol.
<< FileFunc // Name of function (F), file (f) or object (O).
<< ' ';
if (Absolute) {
outs() << "*ABS*";
} else if (Common) {
outs() << "*COM*";
} else if (Section == O->section_end()) {
outs() << "*UND*";
} else {
if (const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(O)) {
DataRefImpl DR = Section->getRawDataRefImpl();
StringRef SegmentName = MachO->getSectionFinalSegmentName(DR);
outs() << SegmentName << ",";
}
StringRef SectionName = unwrapOrError(Section->getName(), FileName);
outs() << SectionName;
}
if (Common || isa<ELFObjectFileBase>(O)) {
uint64_t Val =
Common ? Symbol.getAlignment() : ELFSymbolRef(Symbol).getSize();
outs() << '\t' << format(Fmt, Val);
}
if (isa<ELFObjectFileBase>(O)) {
uint8_t Other = ELFSymbolRef(Symbol).getOther();
switch (Other) {
case ELF::STV_DEFAULT:
break;
case ELF::STV_INTERNAL:
outs() << " .internal";
break;
case ELF::STV_HIDDEN:
outs() << " .hidden";
break;
case ELF::STV_PROTECTED:
outs() << " .protected";
break;
default:
outs() << format(" 0x%02x", Other);
break;
}
} else if (Hidden) {
outs() << " .hidden";
}
if (Demangle)
outs() << ' ' << demangle(std::string(Name)) << '\n';
else
outs() << ' ' << Name << '\n';
}
static void printUnwindInfo(const ObjectFile *O) {
@ -2237,6 +2270,9 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr,
printSectionHeaders(O);
if (SymbolTable)
printSymbolTable(O, ArchiveName);
if (DynamicSymbolTable)
printSymbolTable(O, ArchiveName, /*ArchitectureName=*/"",
/*DumpDynamic=*/true);
if (DwarfDumpType != DIDT_Null) {
std::unique_ptr<DIContext> DICtx = DWARFContext::create(*O);
// Dump the complete DWARF structure.
@ -2378,7 +2414,7 @@ int main(int argc, char **argv) {
if (!ArchiveHeaders && !Disassemble && DwarfDumpType == DIDT_Null &&
!DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST &&
!Relocations && !SectionHeaders && !SectionContents && !SymbolTable &&
!UnwindInfo && !FaultMapSection &&
!DynamicSymbolTable && !UnwindInfo && !FaultMapSection &&
!(MachOOpt &&
(Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie ||
FirstPrivateHeader || IndirectSymbols || InfoPlist || LazyBind ||

View File

@ -133,7 +133,11 @@ void printDynamicRelocations(const object::ObjectFile *O);
void printSectionHeaders(const object::ObjectFile *O);
void printSectionContents(const object::ObjectFile *O);
void printSymbolTable(const object::ObjectFile *O, StringRef ArchiveName,
StringRef ArchitectureName = StringRef());
StringRef ArchitectureName = StringRef(),
bool DumpDynamic = false);
void printSymbol(const object::ObjectFile *O, const object::SymbolRef &Symbol,
StringRef FileName, StringRef ArchiveName,
StringRef ArchitectureName, bool DumpDynamic);
LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Twine Message);
LLVM_ATTRIBUTE_NORETURN void reportError(Error E, StringRef FileName,
StringRef ArchiveName = "",