diff --git a/llvm/test/tools/llvm-readobj/elf-versioninfo.test b/llvm/test/tools/llvm-readobj/elf-versioninfo.test index 3c3d0eefaad9..e8113e4b2fed 100644 --- a/llvm/test/tools/llvm-readobj/elf-versioninfo.test +++ b/llvm/test/tools/llvm-readobj/elf-versioninfo.test @@ -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: } diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 5b4a78cf52fa..fa4bf36925aa 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -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 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 VersionMap; public: + Elf_Dyn_Range dynamic_table() const { + ErrorOr 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 void ELFDumper::LoadVersionMap() { LoadVersionNeeds(dot_gnu_version_r_sec); } + +template +static void printVersionSymbolSection(ELFDumper *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 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 +static void printVersionDefinitionSection(ELFDumper *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 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(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 void ELFDumper::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 StringRef ELFDumper::getSymbolVersion(StringRef StrTab, const Elf_Sym *symb, diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index 79c258283045..2ca1300439eb 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -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() { } diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index 3b40d5335caa..cb0c9c6418e6 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -221,6 +221,12 @@ namespace opts { PrintStackMap("stackmap", cl::desc("Display contents of stackmap section")); + // -version-info + cl::opt + 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();