Index: test/tools/llvm-readobj/elf-versioninfo.test =================================================================== --- test/tools/llvm-readobj/elf-versioninfo.test +++ test/tools/llvm-readobj/elf-versioninfo.test @@ -1,106 +1,132 @@ // Test that llvm-readobj dumps version info tags correctly. -RUN: llvm-readobj -dynamic-table -V %p/Inputs/verdef.elf-x86-64 | FileCheck %s +RUN: llvm-readobj -dynamic-table -V %p/Inputs/verdef.elf-x86-64 | FileCheck %s --check-prefix=LLVM +RUN: llvm-readelf -dynamic-table -V %p/Inputs/verdef.elf-x86-64 | FileCheck %s --check-prefix=GNU -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: SHT_GNU_verdef { -CHECK-NEXT: Definition { -CHECK-NEXT: Version: 1 -CHECK-NEXT: Flags: Base (0x1) -CHECK-NEXT: Index: 1 -CHECK-NEXT: Hash: 430712 -CHECK-NEXT: Name: blah -CHECK-NEXT: } -CHECK-NEXT: Definition { -CHECK-NEXT: Version: 1 -CHECK-NEXT: Flags: 0x0 -CHECK-NEXT: Index: 2 -CHECK-NEXT: Hash: 175630257 -CHECK-NEXT: Name: VERSION1 -CHECK-NEXT: } -CHECK-NEXT: Definition { -CHECK-NEXT: Version: 1 -CHECK-NEXT: Flags: 0x0 -CHECK-NEXT: Index: 3 -CHECK-NEXT: Hash: 175630258 -CHECK-NEXT: Name: VERSION2 -CHECK-NEXT: Predecessor: VERSION1 -CHECK-NEXT: } -CHECK-NEXT: } - -RUN: llvm-readobj -V %p/Inputs/verneed.elf-x86-64 | FileCheck %s --check-prefix=VERNEED - -VERNEED: SHT_GNU_verneed { -VERNEED-NEXT: Dependency { -VERNEED-NEXT: Version: 1 -VERNEED-NEXT: Count: 2 -VERNEED-NEXT: FileName: verneed1.so.0 -VERNEED-NEXT: Entry { -VERNEED-NEXT: Hash: 1938 -VERNEED-NEXT: Flags: 0x0 -VERNEED-NEXT: Index: 3 -VERNEED-NEXT: Name: v2 -VERNEED-NEXT: } -VERNEED-NEXT: Entry { -VERNEED-NEXT: Hash: 1939 -VERNEED-NEXT: Flags: 0x0 -VERNEED-NEXT: Index: 2 -VERNEED-NEXT: Name: v3 -VERNEED-NEXT: } -VERNEED-NEXT: } -VERNEED-NEXT: Dependency { -VERNEED-NEXT: Version: 1 -VERNEED-NEXT: Count: 1 -VERNEED-NEXT: FileName: verneed2.so.0 -VERNEED-NEXT: Entry { -VERNEED-NEXT: Hash: 1937 -VERNEED-NEXT: Flags: 0x0 -VERNEED-NEXT: Index: 4 -VERNEED-NEXT: Name: v1 -VERNEED-NEXT: } -VERNEED-NEXT: } -VERNEED-NEXT: } +LLVM: 0x000000006FFFFFF0 VERSYM 0x24C +LLVM: 0x000000006FFFFFFC VERDEF 0x25C +LLVM: 0x000000006FFFFFFD VERDEFNUM 3 + +GNU: 0x000000006ffffff0 VERSYM 0x24c +GNU: 0x000000006ffffffc VERDEF 0x25c +GNU: 0x000000006ffffffd VERDEFNUM 3 + +LLVM: Version symbols { +LLVM-NEXT: Section Name: .gnu.version (20) +LLVM-NEXT: Address: 0x24C +LLVM-NEXT: Offset: 0x24C +LLVM-NEXT: Link: 1 +LLVM-NEXT: Symbols [ +LLVM-NEXT: Symbol { +LLVM-NEXT: Version: 0 +LLVM-NEXT: Name: {{$}} +LLVM-NEXT: } +LLVM-NEXT: Symbol { +LLVM-NEXT: Version: 1 +LLVM-NEXT: Name: _end{{$}} +LLVM-NEXT: } +LLVM-NEXT: Symbol { +LLVM-NEXT: Version: 1 +LLVM-NEXT: Name: _edata{{$}} +LLVM-NEXT: } +LLVM-NEXT: Symbol { +LLVM-NEXT: Version: 3 +LLVM-NEXT: Name: goo@@VERSION2 +LLVM-NEXT: } +LLVM-NEXT: Symbol { +LLVM-NEXT: Version: 1 +LLVM-NEXT: Name: __bss_start{{$}} +LLVM-NEXT: } +LLVM-NEXT: Symbol { +LLVM-NEXT: Version: 2 +LLVM-NEXT: Name: foo@@VERSION1 +LLVM-NEXT: } +LLVM-NEXT: Symbol { +LLVM-NEXT: Version: 2 +LLVM-NEXT: Name: VERSION1@@VERSION1 +LLVM-NEXT: } +LLVM-NEXT: Symbol { +LLVM-NEXT: Version: 3 +LLVM-NEXT: Name: VERSION2@@VERSION2 +LLVM-NEXT: } +LLVM-NEXT: ] +LLVM-NEXT: } + +LLVM: SHT_GNU_verdef { +LLVM-NEXT: Definition { +LLVM-NEXT: Version: 1 +LLVM-NEXT: Flags: Base (0x1) +LLVM-NEXT: Index: 1 +LLVM-NEXT: Hash: 430712 +LLVM-NEXT: Name: blah +LLVM-NEXT: } +LLVM-NEXT: Definition { +LLVM-NEXT: Version: 1 +LLVM-NEXT: Flags: none (0x0) +LLVM-NEXT: Index: 2 +LLVM-NEXT: Hash: 175630257 +LLVM-NEXT: Name: VERSION1 +LLVM-NEXT: } +LLVM-NEXT: Definition { +LLVM-NEXT: Version: 1 +LLVM-NEXT: Flags: none (0x0) +LLVM-NEXT: Index: 3 +LLVM-NEXT: Hash: 175630258 +LLVM-NEXT: Name: VERSION2 +LLVM-NEXT: Predecessor: VERSION1 +LLVM-NEXT: } +LLVM-NEXT: } + +GNU: Version symbols section '.gnu.version' contains 8 entries: +GNU-NEXT: Addr: 000000000000024c Offset: 0x00024c Link: 1 (.dynsym) +GNU-NEXT: 000: 0 (*local*) 1 (*global*) 1 (*global*) 3 (VERSION2) +GNU-NEXT: 004: 1 (*global*) 2 (VERSION1) 2 (VERSION1) 3 (VERSION2) + +GNU: Version definition section '.gnu.version_d' contains 3 entries: +GNU-NEXT: Addr: 0x000000000000025c Offset: 0x00025c Link: 2 (.dynstr) +GNU-NEXT: 000000: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: blah +GNU-NEXT: 0x001c: Rev: 1 Flags: none Index: 2 Cnt: 1 Name: VERSION1 +GNU-NEXT: 0x0038: Rev: 1 Flags: none Index: 3 Cnt: 2 Name: VERSION2 +GNU-NEXT: 0x0054: Parent 1: VERSION1 + +RUN: llvm-readobj -V %p/Inputs/verneed.elf-x86-64 | FileCheck %s --check-prefix=LLVM_VERNEED +RUN: llvm-readelf -V %p/Inputs/verneed.elf-x86-64 | FileCheck %s --check-prefix=GNU_VERNEED + +LLVM_VERNEED: SHT_GNU_verneed { +LLVM_VERNEED-NEXT: Dependency { +LLVM_VERNEED-NEXT: Version: 1 +LLVM_VERNEED-NEXT: Count: 2 +LLVM_VERNEED-NEXT: FileName: verneed1.so.0 +LLVM_VERNEED-NEXT: Entry { +LLVM_VERNEED-NEXT: Hash: 1938 +LLVM_VERNEED-NEXT: Flags: none (0x0) +LLVM_VERNEED-NEXT: Index: 3 +LLVM_VERNEED-NEXT: Name: v2 +LLVM_VERNEED-NEXT: } +LLVM_VERNEED-NEXT: Entry { +LLVM_VERNEED-NEXT: Hash: 1939 +LLVM_VERNEED-NEXT: Flags: none (0x0) +LLVM_VERNEED-NEXT: Index: 2 +LLVM_VERNEED-NEXT: Name: v3 +LLVM_VERNEED-NEXT: } +LLVM_VERNEED-NEXT: } +LLVM_VERNEED-NEXT: Dependency { +LLVM_VERNEED-NEXT: Version: 1 +LLVM_VERNEED-NEXT: Count: 1 +LLVM_VERNEED-NEXT: FileName: verneed2.so.0 +LLVM_VERNEED-NEXT: Entry { +LLVM_VERNEED-NEXT: Hash: 1937 +LLVM_VERNEED-NEXT: Flags: none (0x0) +LLVM_VERNEED-NEXT: Index: 4 +LLVM_VERNEED-NEXT: Name: v1 +LLVM_VERNEED-NEXT: } +LLVM_VERNEED-NEXT: } +LLVM_VERNEED-NEXT: } + +GNU_VERNEED: Version needs section '.gnu.version_r' contains 2 entries: +GNU_VERNEED-NEXT: Addr: 0x0000000000010230 Offset: 0x000230 Link: 5 (.dynstr) +GNU_VERNEED-NEXT: 000000: Version: 1 File: verneed1.so.0 Cnt: 2 +GNU_VERNEED-NEXT: 0x0020: Name: v2 Flags: none Version: 3 +GNU_VERNEED-NEXT: 0x0030: Name: v3 Flags: none Version: 2 +GNU_VERNEED-NEXT: 0x0010: Version: 1 File: verneed2.so.0 Cnt: 1 +GNU_VERNEED-NEXT: 0x0040: Name: v1 Flags: none Version: 4 Index: test/tools/yaml2obj/verdef-section.yaml =================================================================== --- test/tools/yaml2obj/verdef-section.yaml +++ test/tools/yaml2obj/verdef-section.yaml @@ -1,5 +1,5 @@ # RUN: yaml2obj %s -o %t -# RUN: llvm-readelf -V %t | FileCheck %s +# RUN: llvm-readobj -V %t | FileCheck %s # Check we are able to handle the SHT_GNU_verdef sections. Index: test/tools/yaml2obj/verneed-section.yaml =================================================================== --- test/tools/yaml2obj/verneed-section.yaml +++ test/tools/yaml2obj/verneed-section.yaml @@ -1,5 +1,5 @@ # RUN: yaml2obj %s -o %t -# RUN: llvm-readelf -V %t | FileCheck %s +# RUN: llvm-readobj -V %t | FileCheck %s # Check we are able to handle the SHT_GNU_verneed sections. Index: test/tools/yaml2obj/versym-section.yaml =================================================================== --- test/tools/yaml2obj/versym-section.yaml +++ test/tools/yaml2obj/versym-section.yaml @@ -1,5 +1,5 @@ # RUN: yaml2obj %s -o %t -# RUN: llvm-readelf -V %t | FileCheck %s +# RUN: llvm-readobj -V %t | FileCheck %s ## Check we are able to produce a valid SHT_GNU_versym ## section from its description. @@ -33,13 +33,13 @@ # CHECK-NEXT: FileName: dso.so.0 # CHECK-NEXT: Entry { # CHECK-NEXT: Hash: 1937 -# CHECK-NEXT: Flags: 0x0 +# CHECK-NEXT: Flags: none (0x0) # CHECK-NEXT: Index: 3 # CHECK-NEXT: Name: v1 # CHECK-NEXT: } # CHECK-NEXT: Entry { # CHECK-NEXT: Hash: 1938 -# CHECK-NEXT: Flags: 0x0 +# CHECK-NEXT: Flags: none (0x0) # CHECK-NEXT: Index: 4 # CHECK-NEXT: Name: v2 # CHECK-NEXT: } Index: tools/llvm-readobj/ELFDumper.cpp =================================================================== --- tools/llvm-readobj/ELFDumper.cpp +++ tools/llvm-readobj/ELFDumper.cpp @@ -281,6 +281,9 @@ const Elf_Shdr *getDotAddrsigSec() const { return DotAddrsigSec; } ArrayRef getShndxTable() const { return ShndxTable; } StringRef getDynamicStringTable() const { return DynamicStringTable; } + std::string getSymbolVersionByIndex(StringRef StrTab, + const uint32_t VersionIndex, + bool &IsDefault) const; const DynRegionInfo &getDynRelRegion() const { return DynRelRegion; } const DynRegionInfo &getDynRelaRegion() const { return DynRelaRegion; } const DynRegionInfo &getDynRelrRegion() const { return DynRelrRegion; } @@ -342,6 +345,12 @@ virtual void printProgramHeaders(const ELFFile *Obj, bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) = 0; + virtual void printVersionSymbolSection(const ELFFile *Obj, + const Elf_Shdr *Sec) = 0; + virtual void printVersionDefinitionSection(const ELFFile *Obj, + const Elf_Shdr *Sec) = 0; + virtual void printVersionDependencySection(const ELFFile *Obj, + const Elf_Shdr *Sec) = 0; virtual void printHashHistogram(const ELFFile *Obj) = 0; virtual void printCGProfile(const ELFFile *Obj) = 0; virtual void printAddrsig(const ELFFile *Obj) = 0; @@ -376,6 +385,12 @@ size_t Offset) override; void printProgramHeaders(const ELFO *Obj, bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) override; + void printVersionSymbolSection(const ELFFile *Obj, + const Elf_Shdr *Sec) override; + void printVersionDefinitionSection(const ELFFile *Obj, + const Elf_Shdr *Sec) override; + void printVersionDependencySection(const ELFFile *Obj, + const Elf_Shdr *Sec) override; void printHashHistogram(const ELFFile *Obj) override; void printCGProfile(const ELFFile *Obj) override; void printAddrsig(const ELFFile *Obj) override; @@ -470,6 +485,12 @@ void printDynamicRelocations(const ELFO *Obj) override; void printProgramHeaders(const ELFO *Obj, bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) override; + void printVersionSymbolSection(const ELFFile *Obj, + const Elf_Shdr *Sec) override; + void printVersionDefinitionSection(const ELFFile *Obj, + const Elf_Shdr *Sec) override; + void printVersionDependencySection(const ELFFile *Obj, + const Elf_Shdr *Sec) override; void printHashHistogram(const ELFFile *Obj) override; void printCGProfile(const ELFFile *Obj) override; void printAddrsig(const ELFFile *Obj) override; @@ -607,169 +628,16 @@ LoadVersionNeeds(dot_gnu_version_r_sec); } -template -static void printVersionSymbolSection(ELFDumper *Dumper, const ELFO *Obj, - const typename ELFO::Elf_Shdr *Sec, - ScopedPrinter &W) { - DictScope SS(W, "Version symbols"); - if (!Sec) - return; - StringRef Name = unwrapOrError(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 uint8_t *P = (const uint8_t *)Obj->base() + Sec->sh_offset; - StringRef StrTable = Dumper->getDynamicStringTable(); - - // Same number of entries in the dynamic symbol table (DT_SYMTAB). - ListScope Syms(W, "Symbols"); - for (const typename ELFO::Elf_Sym &Sym : Dumper->dynamic_symbols()) { - DictScope S(W, "Symbol"); - std::string FullSymbolName = - Dumper->getFullSymbolName(&Sym, StrTable, true /* IsDynamic */); - W.printNumber("Version", *P); - W.printString("Name", FullSymbolName); - P += sizeof(typename ELFO::Elf_Half); - } -} - -static const EnumEntry SymVersionFlags[] = { - {"Base", "BASE", VER_FLG_BASE}, - {"Weak", "WEAK", VER_FLG_WEAK}, - {"Info", "INFO", VER_FLG_INFO}}; - -template -static void printVersionDefinitionSection(ELFDumper *Dumper, - const ELFO *Obj, - const typename ELFO::Elf_Shdr *Sec, - ScopedPrinter &W) { - using VerDef = typename ELFO::Elf_Verdef; - using VerdAux = typename ELFO::Elf_Verdaux; - - DictScope SD(W, "SHT_GNU_verdef"); - if (!Sec) - return; - - const uint8_t *SecStartAddress = - (const uint8_t *)Obj->base() + Sec->sh_offset; - const uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size; - const uint8_t *P = SecStartAddress; - const typename ELFO::Elf_Shdr *StrTab = - unwrapOrError(Obj->getSection(Sec->sh_link)); - - unsigned VerDefsNum = Sec->sh_info; - while (VerDefsNum--) { - if (P + sizeof(VerDef) > SecEndAddress) - report_fatal_error("invalid offset in the section"); - - auto *VD = reinterpret_cast(P); - DictScope Def(W, "Definition"); - W.printNumber("Version", VD->vd_version); - W.printEnum("Flags", VD->vd_flags, makeArrayRef(SymVersionFlags)); - W.printNumber("Index", VD->vd_ndx); - W.printNumber("Hash", VD->vd_hash); - W.printString("Name", - StringRef((const char *)(Obj->base() + StrTab->sh_offset + - VD->getAux()->vda_name))); - if (!VD->vd_cnt) - report_fatal_error("at least one definition string must exist"); - if (VD->vd_cnt > 2) - report_fatal_error("more than one predecessor is not expected"); - - if (VD->vd_cnt == 2) { - const uint8_t *PAux = P + VD->vd_aux + VD->getAux()->vda_next; - const VerdAux *Aux = reinterpret_cast(PAux); - W.printString("Predecessor", - StringRef((const char *)(Obj->base() + StrTab->sh_offset + - Aux->vda_name))); - } - - P += VD->vd_next; - } -} - -template -static void printVersionDependencySection(ELFDumper *Dumper, - const ELFO *Obj, - const typename ELFO::Elf_Shdr *Sec, - ScopedPrinter &W) { - using VerNeed = typename ELFO::Elf_Verneed; - using VernAux = typename ELFO::Elf_Vernaux; - - DictScope SD(W, "SHT_GNU_verneed"); - if (!Sec) - return; - - const uint8_t *SecData = (const uint8_t *)Obj->base() + Sec->sh_offset; - const typename ELFO::Elf_Shdr *StrTab = - unwrapOrError(Obj->getSection(Sec->sh_link)); - - const uint8_t *P = SecData; - unsigned VerNeedNum = Sec->sh_info; - for (unsigned I = 0; I < VerNeedNum; ++I) { - const VerNeed *Need = reinterpret_cast(P); - DictScope Entry(W, "Dependency"); - W.printNumber("Version", Need->vn_version); - W.printNumber("Count", Need->vn_cnt); - W.printString("FileName", - StringRef((const char *)(Obj->base() + StrTab->sh_offset + - Need->vn_file))); - - const uint8_t *PAux = P + Need->vn_aux; - for (unsigned J = 0; J < Need->vn_cnt; ++J) { - const VernAux *Aux = reinterpret_cast(PAux); - DictScope Entry(W, "Entry"); - W.printNumber("Hash", Aux->vna_hash); - W.printEnum("Flags", Aux->vna_flags, makeArrayRef(SymVersionFlags)); - W.printNumber("Index", Aux->vna_other); - W.printString("Name", - StringRef((const char *)(Obj->base() + StrTab->sh_offset + - Aux->vna_name))); - PAux += Aux->vna_next; - } - P += Need->vn_next; - } -} - -template void ELFDumper::printVersionInfo() { - // Dump version symbol section. - printVersionSymbolSection(this, ObjF->getELFFile(), dot_gnu_version_sec, W); - - // Dump version definition section. - printVersionDefinitionSection(this, ObjF->getELFFile(), dot_gnu_version_d_sec, W); - - // Dump version dependency section. - printVersionDependencySection(this, ObjF->getELFFile(), dot_gnu_version_r_sec, W); -} - template -StringRef ELFDumper::getSymbolVersion(StringRef StrTab, - const Elf_Sym *symb, - bool &IsDefault) const { - // This is a dynamic symbol. Look in the GNU symbol version table. - if (!dot_gnu_version_sec) { - // No version table. - IsDefault = false; - return StringRef(""); - } - - // Determine the position in the symbol table of this entry. - size_t entry_index = (reinterpret_cast(symb) - - reinterpret_cast(DynSymRegion.Addr)) / - sizeof(Elf_Sym); - - // Get the corresponding version index entry - const Elf_Versym *vs = unwrapOrError( - ObjF->getELFFile()->template getEntry(dot_gnu_version_sec, entry_index)); - size_t version_index = vs->vs_index & ELF::VERSYM_VERSION; +std::string ELFDumper::getSymbolVersionByIndex( + StringRef StrTab, const uint32_t VersionIndex, bool &IsDefault) const { + size_t version_index = VersionIndex & ELF::VERSYM_VERSION; // Special markers for unversioned symbols. if (version_index == ELF::VER_NDX_LOCAL || version_index == ELF::VER_NDX_GLOBAL) { IsDefault = false; - return StringRef(""); + return ""; } // Lookup this symbol in the version table @@ -783,14 +651,38 @@ if (entry.isVerdef()) { // The first Verdaux entry holds the name. name_offset = entry.getVerdef()->getAux()->vda_name; - IsDefault = !(vs->vs_index & ELF::VERSYM_HIDDEN); + IsDefault = !(VersionIndex & ELF::VERSYM_HIDDEN); } else { name_offset = entry.getVernaux()->vna_name; IsDefault = false; } if (name_offset >= StrTab.size()) reportError("Invalid string offset"); - return StringRef(StrTab.data() + name_offset); + return std::string(StrTab.data() + name_offset); +} + +template +StringRef ELFDumper::getSymbolVersion(StringRef StrTab, + const Elf_Sym *symb, + bool &IsDefault) const { + // This is a dynamic symbol. Look in the GNU symbol version table. + if (!dot_gnu_version_sec) { + // No version table. + IsDefault = false; + return StringRef(""); + } + + // Determine the position in the symbol table of this entry. + size_t entry_index = (reinterpret_cast(symb) - + reinterpret_cast(DynSymRegion.Addr)) / + sizeof(Elf_Sym); + + // Get the corresponding version index entry + const Elf_Versym *vs = + unwrapOrError(ObjF->getELFFile()->template getEntry( + dot_gnu_version_sec, entry_index)); + return StringRef( + this->getSymbolVersionByIndex(StrTab, vs->vs_index, IsDefault)); } static std::string maybeDemangle(StringRef Name) { @@ -1114,6 +1006,12 @@ {"HIDDEN", "HIDDEN", ELF::STV_HIDDEN}, {"PROTECTED", "PROTECTED", ELF::STV_PROTECTED}}; +static const EnumEntry SymVersionFlags[] = { + {"none", "none", 0}, + {"Base", "BASE", VER_FLG_BASE}, + {"Weak", "WEAK", VER_FLG_WEAK}, + {"Info", "INFO", VER_FLG_INFO}}; + static const EnumEntry AMDGPUSymbolTypes[] = { { "AMDGPU_HSA_KERNEL", ELF::STT_AMDGPU_HSA_KERNEL } }; @@ -1627,11 +1525,25 @@ PrintDynamicSymbols); } -template +template void ELFDumper::printHashSymbols() { ELFDumperStyle->printHashSymbols(ObjF->getELFFile()); } +template void ELFDumper::printVersionInfo() { + // Dump version symbol section. + ELFDumperStyle->printVersionSymbolSection(ObjF->getELFFile(), + dot_gnu_version_sec); + + // Dump version definition section. + ELFDumperStyle->printVersionDefinitionSection(ObjF->getELFFile(), + dot_gnu_version_d_sec); + + // Dump version dependency section. + ELFDumperStyle->printVersionDependencySection(ObjF->getELFFile(), + dot_gnu_version_r_sec); +} + template void ELFDumper::printHashHistogram() { ELFDumperStyle->printHashHistogram(ObjF->getELFFile()); } @@ -3460,6 +3372,174 @@ } } +template +void GNUStyle::printVersionSymbolSection(const ELFFile *Obj, + const Elf_Shdr *Sec) { + if (!Sec) + return; + StringRef SecName = unwrapOrError(Obj->getSectionName(Sec)); + uint32_t Entries = Sec->sh_size / sizeof(Elf_Versym); + OS << "Version symbols section '" << SecName << "' " + << "contains " << Entries << " entries:\n"; + + const Elf_Shdr *SymTab = unwrapOrError(Obj->getSection(Sec->sh_link)); + StringRef SymTabName = unwrapOrError(Obj->getSectionName(SymTab)); + OS << " Addr: " << format_hex_no_prefix(Sec->sh_addr, 16) + << " Offset: " << format_hex(Sec->sh_offset, 8) + << " Link: " << Sec->sh_link << " (" << SymTabName << ")\n"; + + const uint8_t *VersymBuf = + reinterpret_cast(Obj->base() + Sec->sh_offset); + auto *Dumper = this->dumper(); + StringRef StrTable = Dumper->getDynamicStringTable(); + + // readelf prints 4 entries per line. + for (uint32_t VersymRow = 0; VersymRow < Entries; VersymRow += 4) { + OS << " " << format_hex_no_prefix(VersymRow, 3) << ":"; + + for (uint32_t VersymIndex = 0; + (VersymIndex < 4) && (VersymIndex + VersymRow) < Entries; + ++VersymIndex) { + const Elf_Versym *Versym = + reinterpret_cast(VersymBuf); + switch (Versym->vs_index) { + case 0: + OS << " 0 (*local*) "; + break; + case 1: + OS << " 1 (*global*) "; + break; + default: + OS << format("%4x%c", Versym->vs_index & ELF::VERSYM_VERSION, + Versym->vs_index & ELF::VERSYM_HIDDEN ? 'h' : ' '); + + bool IsDefault = true; + std::string VersionName = Dumper->getSymbolVersionByIndex( + StrTable, Versym->vs_index, IsDefault); + + if (!VersionName.empty()) + VersionName = "(" + VersionName + ")"; + else + VersionName = "(*invalid*)"; + OS << left_justify(VersionName, 13); + } + VersymBuf += sizeof(Elf_Versym); + } + OS << '\n'; + } + OS << '\n'; +} + +template +void GNUStyle::printVersionDefinitionSection(const ELFFile *Obj, + const Elf_Shdr *Sec) { + if (!Sec) + return; + const uint8_t *SecStartAddress = + (const uint8_t *)Obj->base() + Sec->sh_offset; + const uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size; + const uint8_t *VerdefBuf = SecStartAddress; + const Elf_Shdr *StrTab = unwrapOrError(Obj->getSection(Sec->sh_link)); + + StringRef StrTabName = unwrapOrError(Obj->getSectionName(StrTab)); + StringRef SecName = unwrapOrError(Obj->getSectionName(Sec)); + uint32_t Entries = Sec->sh_info; + OS << "Version definition section '" << SecName << "' contains " << Entries + << " entries:\n" + << " Addr: " << format_hex(Sec->sh_addr, 18) + << " Offset: " << format_hex(Sec->sh_offset, 8) + << " Link: " << Sec->sh_link << " (" << StrTabName << ")\n"; + + for (uint32_t VerdefOffset = 0, VerdefIndex = 0; VerdefIndex < Entries; + VerdefIndex++) { + if (VerdefBuf + sizeof(Elf_Verdef) > SecEndAddress) + // FIXME: report_fatal_error is not a good way to report error. We should + // emit a parsing error here and below. + report_fatal_error("invalid offset in the section"); + + const Elf_Verdef *Verdef = reinterpret_cast(VerdefBuf); + + OS << format(" %#06x:", VerdefOffset) << " Rev: " << Verdef->vd_version + << " Flags: " + << printEnum(Verdef->vd_flags, makeArrayRef(SymVersionFlags)) + << " Index: " << Verdef->vd_ndx << " Cnt: " << Verdef->vd_cnt + << " Name: " + << StringRef((const char *)(Obj->base() + StrTab->sh_offset + + Verdef->getAux()->vda_name)) + << '\n'; + + if (!Verdef->vd_cnt) + report_fatal_error("at least one definition string must exist"); + if (Verdef->vd_cnt > 2) + report_fatal_error("more than one predecessor is not expected"); + + if (Verdef->vd_cnt == 2) { + const uint8_t *VerdauxBuf = + VerdefBuf + Verdef->vd_aux + Verdef->getAux()->vda_next; + const Elf_Verdaux *Verdaux = + reinterpret_cast(VerdauxBuf); + + OS << format(" %#06x: Parent 1: ", + VerdefOffset + Verdef->vd_aux + Verdef->getAux()->vda_next) + << StringRef((const char *)(Obj->base() + StrTab->sh_offset + + Verdaux->vda_name)) + << '\n'; + } + VerdefBuf += Verdef->vd_next; + VerdefOffset += Verdef->vd_next; + } +} + +template +void GNUStyle::printVersionDependencySection(const ELFFile *Obj, + const Elf_Shdr *Sec) { + if (!Sec) + return; + const uint8_t *SecData = (const uint8_t *)Obj->base() + Sec->sh_offset; + const Elf_Shdr *StrTab = unwrapOrError(Obj->getSection(Sec->sh_link)); + StringRef SecName = unwrapOrError(Obj->getSectionName(Sec)); + StringRef StrTabName = unwrapOrError(Obj->getSectionName(StrTab)); + const uint8_t *VerneedBuf = SecData; + uint32_t Entries = Sec->sh_info; + + OS << "Version needs section '" << SecName << "' contains " << Entries + << " entries:\n" + << " Addr: " << format_hex(Sec->sh_addr, 18) + << " Offset: " << format_hex(Sec->sh_offset, 8) + << " Link: " << Sec->sh_link << " (" << StrTabName << ")\n"; + + for (uint32_t VerneedOffset = 0, VerneedIndex = 0; VerneedIndex < Entries; + ++VerneedIndex) { + const Elf_Verneed *Verneed = + reinterpret_cast(VerneedBuf); + const uint32_t VerneedEntries = Verneed->vn_cnt; + + OS << format(" %#06x:", VerneedOffset) + << " Version: " << Verneed->vn_version << " File: " + << StringRef((const char *)(Obj->base() + StrTab->sh_offset + + Verneed->vn_file)) + << " Cnt: " << VerneedEntries << "\n"; + + const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; + for (uint32_t VernauxIndex = 0, + VernauxOffset = VerneedOffset + Verneed->vn_aux; + VernauxIndex < VerneedEntries; ++VernauxIndex) { + const Elf_Vernaux *Vernaux = + reinterpret_cast(VernauxBuf); + OS << format(" %#06x:", VernauxOffset) << " Name: " + << StringRef((const char *)(Obj->base() + StrTab->sh_offset + + Vernaux->vna_name)) + << " Flags: " + << printEnum(Vernaux->vna_flags, makeArrayRef(SymVersionFlags)) + << " Version: " << Vernaux->vna_other << '\n'; + VernauxBuf += Vernaux->vna_next; + VernauxOffset += Vernaux->vna_next; + } + VerneedBuf += Verneed->vn_next; + VerneedOffset += Verneed->vn_next; + } +} + // Hash histogram shows statistics of how efficient the hash was for the // dynamic symbol table. The table shows number of hash buckets for different // lengths of chains as absolute number and percentage of the total buckets. @@ -4550,6 +4630,121 @@ } template +void LLVMStyle::printVersionSymbolSection(const ELFFile *Obj, + const Elf_Shdr *Sec) { + DictScope SS(W, "Version symbols"); + if (!Sec) + return; + + StringRef SecName = unwrapOrError(Obj->getSectionName(Sec)); + W.printNumber("Section Name", SecName, Sec->sh_name); + W.printHex("Address", Sec->sh_addr); + W.printHex("Offset", Sec->sh_offset); + W.printNumber("Link", Sec->sh_link); + + const uint8_t *VersymBuf = (const uint8_t *)Obj->base() + Sec->sh_offset; + StringRef StrTable = this->dumper()->getDynamicStringTable(); + + // Same number of entries in the dynamic symbol table (DT_SYMTAB). + ListScope Syms(W, "Symbols"); + for (const Elf_Sym &Sym : this->dumper()->dynamic_symbols()) { + DictScope S(W, "Symbol"); + const Elf_Versym *Versym = reinterpret_cast(VersymBuf); + std::string FullSymbolName = + this->dumper()->getFullSymbolName(&Sym, StrTable, true /* IsDynamic */); + W.printNumber("Version", Versym->vs_index); + W.printString("Name", FullSymbolName); + VersymBuf += sizeof(Elf_Versym); + } +} + +template +void LLVMStyle::printVersionDefinitionSection(const ELFFile *Obj, + const Elf_Shdr *Sec) { + DictScope SD(W, "SHT_GNU_verdef"); + if (!Sec) + return; + + const uint8_t *SecStartAddress = + (const uint8_t *)Obj->base() + Sec->sh_offset; + const uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size; + const uint8_t *VerdefBuf = SecStartAddress; + const Elf_Shdr *StrTab = unwrapOrError(Obj->getSection(Sec->sh_link)); + + unsigned VerDefsNum = Sec->sh_info; + while (VerDefsNum--) { + if (VerdefBuf + sizeof(Elf_Verdef) > SecEndAddress) + // FIXME: report_fatal_error is not a good way to report error. We should + // emit a parsing error here and below. + report_fatal_error("invalid offset in the section"); + + const Elf_Verdef *Verdef = reinterpret_cast(VerdefBuf); + DictScope Def(W, "Definition"); + W.printNumber("Version", Verdef->vd_version); + W.printEnum("Flags", Verdef->vd_flags, makeArrayRef(SymVersionFlags)); + W.printNumber("Index", Verdef->vd_ndx); + W.printNumber("Hash", Verdef->vd_hash); + W.printString("Name", + StringRef((const char *)(Obj->base() + StrTab->sh_offset + + Verdef->getAux()->vda_name))); + if (!Verdef->vd_cnt) + report_fatal_error("at least one definition string must exist"); + if (Verdef->vd_cnt > 2) + report_fatal_error("more than one predecessor is not expected"); + + if (Verdef->vd_cnt == 2) { + const uint8_t *VerdauxBuf = + VerdefBuf + Verdef->vd_aux + Verdef->getAux()->vda_next; + const Elf_Verdaux *Verdaux = + reinterpret_cast(VerdauxBuf); + W.printString("Predecessor", + StringRef((const char *)(Obj->base() + StrTab->sh_offset + + Verdaux->vda_name))); + } + VerdefBuf += Verdef->vd_next; + } +} + +template +void LLVMStyle::printVersionDependencySection(const ELFFile *Obj, + const Elf_Shdr *Sec) { + DictScope SD(W, "SHT_GNU_verneed"); + if (!Sec) + return; + + const uint8_t *SecData = (const uint8_t *)Obj->base() + Sec->sh_offset; + const Elf_Shdr *StrTab = unwrapOrError(Obj->getSection(Sec->sh_link)); + + const uint8_t *VerneedBuf = SecData; + unsigned VerneedNum = Sec->sh_info; + for (unsigned I = 0; I < VerneedNum; ++I) { + const Elf_Verneed *Verneed = + reinterpret_cast(VerneedBuf); + DictScope Entry(W, "Dependency"); + W.printNumber("Version", Verneed->vn_version); + W.printNumber("Count", Verneed->vn_cnt); + W.printString("FileName", + StringRef((const char *)(Obj->base() + StrTab->sh_offset + + Verneed->vn_file))); + + const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; + for (unsigned J = 0; J < Verneed->vn_cnt; ++J) { + const Elf_Vernaux *Vernaux = + reinterpret_cast(VernauxBuf); + DictScope Entry(W, "Entry"); + W.printNumber("Hash", Vernaux->vna_hash); + W.printEnum("Flags", Vernaux->vna_flags, makeArrayRef(SymVersionFlags)); + W.printNumber("Index", Vernaux->vna_other); + W.printString("Name", + StringRef((const char *)(Obj->base() + StrTab->sh_offset + + Vernaux->vna_name))); + VernauxBuf += Vernaux->vna_next; + } + VerneedBuf += Verneed->vn_next; + } +} + +template void LLVMStyle::printHashHistogram(const ELFFile *Obj) { W.startLine() << "Hash Histogram not implemented!\n"; }