forked from OSchip/llvm-project
[llvm-readobj] Teach ELFDumper about symbol versioning.
Differential Revision: http://reviews.llvm.org/D13824 llvm-svn: 250575
This commit is contained in:
parent
91c7321ea9
commit
4f05f32bb7
|
@ -1,7 +1,81 @@
|
|||
// Test that llvm-readobj dumps version info tags correctly.
|
||||
|
||||
RUN: llvm-readobj -dynamic-table %p/Inputs/verdef.elf-x86-64 | FileCheck %s
|
||||
RUN: llvm-readobj -dynamic-table -V %p/Inputs/verdef.elf-x86-64 | FileCheck %s
|
||||
|
||||
CHECK: 0x000000006FFFFFF0 VERSYM 0x24C
|
||||
CHECK: 0x000000006FFFFFFC VERDEF 0x25C
|
||||
CHECK: 0x000000006FFFFFFD VERDEFNUM 3
|
||||
|
||||
CHECK: Version symbols {
|
||||
CHECK-NEXT: Section Name: .gnu.version (20)
|
||||
CHECK-NEXT: Address: 0x24C
|
||||
CHECK-NEXT: Offset: 0x24C
|
||||
CHECK-NEXT: Link: 1
|
||||
CHECK-NEXT: Symbols [
|
||||
CHECK-NEXT: Symbol {
|
||||
CHECK-NEXT: Version: 0
|
||||
CHECK-NEXT: Name: @
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: Symbol {
|
||||
CHECK-NEXT: Version: 1
|
||||
CHECK-NEXT: Name: _end@
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: Symbol {
|
||||
CHECK-NEXT: Version: 1
|
||||
CHECK-NEXT: Name: _edata@
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: Symbol {
|
||||
CHECK-NEXT: Version: 3
|
||||
CHECK-NEXT: Name: goo@@VERSION2
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: Symbol {
|
||||
CHECK-NEXT: Version: 1
|
||||
CHECK-NEXT: Name: __bss_start@
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: Symbol {
|
||||
CHECK-NEXT: Version: 2
|
||||
CHECK-NEXT: Name: foo@@VERSION1
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: Symbol {
|
||||
CHECK-NEXT: Version: 2
|
||||
CHECK-NEXT: Name: VERSION1@@VERSION1
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: Symbol {
|
||||
CHECK-NEXT: Version: 3
|
||||
CHECK-NEXT: Name: VERSION2@@VERSION2
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: }
|
||||
|
||||
CHECK: Version definition {
|
||||
CHECK-NEXT: Section Name: .gnu.version_d (70)
|
||||
CHECK-NEXT: Address: 0x25C
|
||||
CHECK-NEXT: Offset: 0x25C
|
||||
CHECK-NEXT: Link: 2
|
||||
CHECK-NEXT: Entries [
|
||||
CHECK-NEXT: Entry {
|
||||
CHECK-NEXT: Offset: 0x0
|
||||
CHECK-NEXT: Rev: 1
|
||||
CHECK-NEXT: Flags: 1
|
||||
CHECK-NEXT: Index: 1
|
||||
CHECK-NEXT: Cnt: 1
|
||||
CHECK-NEXT: Name: blah
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: Entry {
|
||||
CHECK-NEXT: Offset: 0x1C
|
||||
CHECK-NEXT: Rev: 1
|
||||
CHECK-NEXT: Flags: 0
|
||||
CHECK-NEXT: Index: 2
|
||||
CHECK-NEXT: Cnt: 1
|
||||
CHECK-NEXT: Name: VERSION1
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: Entry {
|
||||
CHECK-NEXT: Offset: 0x38
|
||||
CHECK-NEXT: Rev: 1
|
||||
CHECK-NEXT: Flags: 0
|
||||
CHECK-NEXT: Index: 3
|
||||
CHECK-NEXT: Cnt: 2
|
||||
CHECK-NEXT: Name: VERSION2
|
||||
CHECK-NEXT: }
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: }
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
void printHashTable() override;
|
||||
void printGnuHashTable() override;
|
||||
void printLoadName() override;
|
||||
void printVersionInfo() override;
|
||||
|
||||
void printAttributes() override;
|
||||
void printMipsPLTGOT() override;
|
||||
|
@ -76,6 +77,7 @@ private:
|
|||
typedef typename ELFO::Elf_Rela Elf_Rela;
|
||||
typedef typename ELFO::Elf_Rela_Range Elf_Rela_Range;
|
||||
typedef typename ELFO::Elf_Phdr Elf_Phdr;
|
||||
typedef typename ELFO::Elf_Half Elf_Half;
|
||||
typedef typename ELFO::Elf_Hash Elf_Hash;
|
||||
typedef typename ELFO::Elf_GnuHash Elf_GnuHash;
|
||||
typedef typename ELFO::Elf_Ehdr Elf_Ehdr;
|
||||
|
@ -85,6 +87,7 @@ private:
|
|||
typedef typename ELFO::Elf_Verneed Elf_Verneed;
|
||||
typedef typename ELFO::Elf_Vernaux Elf_Vernaux;
|
||||
typedef typename ELFO::Elf_Verdef Elf_Verdef;
|
||||
typedef typename ELFO::Elf_Verdaux Elf_Verdaux;
|
||||
|
||||
/// \brief Represents a region described by entries in the .dynamic table.
|
||||
struct DynRegionInfo {
|
||||
|
@ -119,12 +122,6 @@ private:
|
|||
error(Ret.getError());
|
||||
return *Ret;
|
||||
}
|
||||
Elf_Dyn_Range dynamic_table() const {
|
||||
ErrorOr<Elf_Dyn_Range> Ret = Obj->dynamic_table(DynamicProgHeader);
|
||||
error(Ret.getError());
|
||||
return *Ret;
|
||||
}
|
||||
|
||||
StringRef getSymbolVersion(StringRef StrTab, const Elf_Sym *symb,
|
||||
bool &IsDefault);
|
||||
void LoadVersionMap();
|
||||
|
@ -171,6 +168,12 @@ private:
|
|||
mutable SmallVector<VersionMapEntry, 16> VersionMap;
|
||||
|
||||
public:
|
||||
Elf_Dyn_Range dynamic_table() const {
|
||||
ErrorOr<Elf_Dyn_Range> Ret = Obj->dynamic_table(DynamicProgHeader);
|
||||
error(Ret.getError());
|
||||
return *Ret;
|
||||
}
|
||||
|
||||
std::string getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable,
|
||||
bool IsDynamic);
|
||||
const Elf_Shdr *getDotDynSymSec() const { return DotDynSymSec; }
|
||||
|
@ -302,6 +305,94 @@ template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() {
|
|||
LoadVersionNeeds(dot_gnu_version_r_sec);
|
||||
}
|
||||
|
||||
|
||||
template <typename ELFO, class ELFT>
|
||||
static void printVersionSymbolSection(ELFDumper<ELFT> *Dumper,
|
||||
const ELFO *Obj,
|
||||
const typename ELFO::Elf_Shdr *Sec,
|
||||
StreamWriter &W) {
|
||||
DictScope SS(W, "Version symbols");
|
||||
if (!Sec)
|
||||
return;
|
||||
StringRef Name = errorOrDefault(Obj->getSectionName(Sec));
|
||||
W.printNumber("Section Name", Name, Sec->sh_name);
|
||||
W.printHex("Address", Sec->sh_addr);
|
||||
W.printHex("Offset", Sec->sh_offset);
|
||||
W.printNumber("Link", Sec->sh_link);
|
||||
|
||||
const typename ELFO::Elf_Shdr *DynSymSec = Dumper->getDotDynSymSec();
|
||||
uint8_t *P = (uint8_t *)Obj->base() + Sec->sh_offset;
|
||||
ErrorOr<StringRef> StrTableOrErr =
|
||||
Obj->getStringTableForSymtab(*DynSymSec);
|
||||
error(StrTableOrErr.getError());
|
||||
|
||||
// Same number of entries in the dynamic symbol table (DT_SYMTAB).
|
||||
ListScope Syms(W, "Symbols");
|
||||
for (const typename ELFO::Elf_Sym &Sym : Obj->symbols(DynSymSec)) {
|
||||
DictScope S(W, "Symbol");
|
||||
std::string FullSymbolName =
|
||||
Dumper->getFullSymbolName(&Sym, *StrTableOrErr, true /* IsDynamic */);
|
||||
W.printNumber("Version", *P);
|
||||
W.printString("Name", FullSymbolName);
|
||||
P += sizeof(typename ELFO::Elf_Half);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ELFO, class ELFT>
|
||||
static void printVersionDefinitionSection(ELFDumper<ELFT> *Dumper,
|
||||
const ELFO *Obj,
|
||||
const typename ELFO::Elf_Shdr *Sec,
|
||||
StreamWriter &W) {
|
||||
DictScope SD(W, "Version definition");
|
||||
if (!Sec)
|
||||
return;
|
||||
StringRef Name = errorOrDefault(Obj->getSectionName(Sec));
|
||||
W.printNumber("Section Name", Name, Sec->sh_name);
|
||||
W.printHex("Address", Sec->sh_addr);
|
||||
W.printHex("Offset", Sec->sh_offset);
|
||||
W.printNumber("Link", Sec->sh_link);
|
||||
|
||||
unsigned verdef_entries = 0;
|
||||
// The number of entries in the section SHT_GNU_verdef
|
||||
// is determined by DT_VERDEFNUM tag.
|
||||
for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) {
|
||||
if (Dyn.d_tag == DT_VERDEFNUM)
|
||||
verdef_entries = Dyn.d_un.d_val;
|
||||
}
|
||||
uint8_t *SecStartAddress = (uint8_t *)Obj->base() + Sec->sh_offset;
|
||||
uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size;
|
||||
uint8_t *P = SecStartAddress;
|
||||
ErrorOr<const typename ELFO::Elf_Shdr *> StrTabOrErr =
|
||||
Obj->getSection(Sec->sh_link);
|
||||
error(StrTabOrErr.getError());
|
||||
|
||||
ListScope Entries(W, "Entries");
|
||||
for (unsigned i = 0; i < verdef_entries; ++i) {
|
||||
if (P + sizeof(typename ELFO::Elf_Verdef) > SecEndAddress)
|
||||
report_fatal_error("invalid offset in the section");
|
||||
auto *VD = reinterpret_cast<const typename ELFO::Elf_Verdef *>(P);
|
||||
DictScope Entry(W, "Entry");
|
||||
W.printHex("Offset", (uintptr_t)P - (uintptr_t)SecStartAddress);
|
||||
W.printNumber("Rev", VD->vd_version);
|
||||
// FIXME: print something more readable.
|
||||
W.printNumber("Flags", VD->vd_flags);
|
||||
W.printNumber("Index", VD->vd_ndx);
|
||||
W.printNumber("Cnt", VD->vd_cnt);
|
||||
W.printString("Name", StringRef((char *)(
|
||||
Obj->base() + (*StrTabOrErr)->sh_offset +
|
||||
VD->getAux()->vda_name)));
|
||||
P += VD->vd_next;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ELFT> void ELFDumper<ELFT>::printVersionInfo() {
|
||||
// Dump version symbol section.
|
||||
printVersionSymbolSection(this, Obj, dot_gnu_version_sec, W);
|
||||
|
||||
// Dump version definition section.
|
||||
printVersionDefinitionSection(this, Obj, dot_gnu_version_d_sec, W);
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab,
|
||||
const Elf_Sym *symb,
|
||||
|
|
|
@ -41,6 +41,7 @@ public:
|
|||
virtual void printHashTable() { }
|
||||
virtual void printGnuHashTable() { }
|
||||
virtual void printLoadName() {}
|
||||
virtual void printVersionInfo() {}
|
||||
|
||||
// Only implemented for ARM ELF at this time.
|
||||
virtual void printAttributes() { }
|
||||
|
|
|
@ -221,6 +221,12 @@ namespace opts {
|
|||
PrintStackMap("stackmap",
|
||||
cl::desc("Display contents of stackmap section"));
|
||||
|
||||
// -version-info
|
||||
cl::opt<bool>
|
||||
VersionInfo("version-info",
|
||||
cl::desc("Display ELF version sections (if present)"));
|
||||
cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"),
|
||||
cl::aliasopt(VersionInfo));
|
||||
} // namespace opts
|
||||
|
||||
namespace llvm {
|
||||
|
@ -328,6 +334,8 @@ static void dumpObject(const ObjectFile *Obj) {
|
|||
Dumper->printHashTable();
|
||||
if (opts::GnuHashTable)
|
||||
Dumper->printGnuHashTable();
|
||||
if (opts::VersionInfo)
|
||||
Dumper->printVersionInfo();
|
||||
if (Obj->getArch() == llvm::Triple::arm && Obj->isELF())
|
||||
if (opts::ARMAttributes)
|
||||
Dumper->printAttributes();
|
||||
|
|
Loading…
Reference in New Issue