diff --git a/llvm/test/tools/llvm-objdump/ELF/dynsym-version.test b/llvm/test/tools/llvm-objdump/ELF/dynsym-version.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/dynsym-version.test @@ -0,0 +1,89 @@ +## Check we print symbol versions, when they are available. + +# RUN: yaml2obj %s -o %t-undef.o +# RUN: llvm-objdump -T %t-undef.o 2>&1 | tr '\t' '|' | FileCheck %s \ +# RUN: -DFILE=%t-undef.o --check-prefix=UNDEF --match-full-lines --strict-whitespace + +# UNDEF:DYNAMIC SYMBOL TABLE: +# UNDEF-NEXT:0000000000000000 D *UND*|0000000000000000 localversym +# UNDEF-NEXT:0000000000000000 D *UND*|0000000000000000 globalversym +# UNDEF-NEXT:0000000000000000 D *UND*|0000000000000000 (v2) version2sym +# UNDEF-NEXT:0000000000000000 D *UND*|0000000000000000 (v3hidden) version3sym +# UNDEF-NEXT:0000000000000000 D *UND*|0000000000000000 (v4) version4sym +# UNDEF-NEXT:0000000000000000 D *UND*|0000000000000000 (v5hidden) version5sym + + +# RUN: yaml2obj -DINDEX=0x1 %s -o %t-def.o +# RUN: llvm-objdump -T %t-def.o 2>&1 | tr '\t' '|' | FileCheck %s \ +# RUN: -DFILE=%t-def.o --check-prefix=DEF --match-full-lines --strict-whitespace + +# DEF:DYNAMIC SYMBOL TABLE: +# DEF-NEXT:0000000000000000 l D .gnu.version|0000000000000000 localversym +# DEF-NEXT:0000000000000000 l D .gnu.version|0000000000000000 globalversym +# DEF-NEXT:0000000000000000 l D .gnu.version|0000000000000000 v2 version2sym +# DEF-NEXT:0000000000000000 l D .gnu.version|0000000000000000 (v3hidden) version3sym +# DEF-NEXT:0000000000000000 l D .gnu.version|0000000000000000 (v4) version4sym +# DEF-NEXT:0000000000000000 l D .gnu.version|0000000000000000 (v5hidden) version5sym + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .gnu.version + Type: SHT_GNU_versym + Flags: [ SHF_ALLOC ] +## 0x8000 is a special VERSYM_HIDDEN bit. + Entries: [ 0, 0, 1, [[VERSYMENTRY=2]], 0x8003, 4, 0x8005 ] + ShSize: [[VERSYMSIZE=]] + - Name: .gnu.version_d + Type: SHT_GNU_verdef + Flags: [ SHF_ALLOC ] + Link: .dynstr + AddressAlign: 0x4 + Info: 0x2 + ShOffset: [[VERDEFOFFSET=]] + Entries: + - VersionNdx: 2 + Names: + - v2 + - VersionNdx: 3 + Names: + - v3hidden + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Link: .dynstr + Info: 0x2 + Dependencies: + - Version: 1 + File: file1.so + Entries: + - Name: v4 + Hash: 0 + Flags: 0 + Other: 4 + - Version: 1 + File: file2.0 + Entries: + - Name: v5hidden + Hash: 0 + Flags: 0 + Other: 5 + - Name: .dynsym + Type: SHT_DYNSYM + EntSize: [[ENTSIZE=]] +DynamicSymbols: + - Name: localversym + Index: [[INDEX=]] + - Name: globalversym + Index: [[INDEX=]] + - Name: version2sym + Index: [[INDEX=]] + - Name: version3sym + Index: [[INDEX=]] + - Name: version4sym + Index: [[INDEX=]] + - Name: version5sym + Index: [[INDEX=]] diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -26,6 +26,7 @@ class MachOObjectFile; class MachOUniversalBinary; class RelocationRef; +struct VersionEntry; } // namespace object namespace objdump { @@ -137,6 +138,7 @@ StringRef ArchitectureName = StringRef(), bool DumpDynamic = false); void printSymbol(const object::ObjectFile *O, const object::SymbolRef &Symbol, + ArrayRef SymbolVersions, StringRef FileName, StringRef ArchiveName, StringRef ArchitectureName, bool DumpDynamic); [[noreturn]] void reportError(StringRef File, const Twine &Message); diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -1921,7 +1921,8 @@ if (!DumpDynamic) { outs() << "\nSYMBOL TABLE:\n"; for (auto I = O->symbol_begin(); I != O->symbol_end(); ++I) - printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic); + printSymbol(O, *I, {}, FileName, ArchiveName, ArchitectureName, + DumpDynamic); return; } @@ -1934,12 +1935,20 @@ } const ELFObjectFileBase *ELF = cast(O); - for (auto I = ELF->getDynamicSymbolIterators().begin(); - I != ELF->getDynamicSymbolIterators().end(); ++I) - printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic); + auto Symbols = ELF->getDynamicSymbolIterators(); + Expected> SymbolVersionsOrErr = + ELF->readDynsymVersions(); + if (!SymbolVersionsOrErr) { + reportError(SymbolVersionsOrErr.takeError(), FileName, ArchiveName); + return; + } + for (auto &Sym : Symbols) + printSymbol(O, Sym, *SymbolVersionsOrErr, FileName, ArchiveName, + ArchitectureName, DumpDynamic); } void objdump::printSymbol(const ObjectFile *O, const SymbolRef &Symbol, + ArrayRef SymbolVersions, StringRef FileName, StringRef ArchiveName, StringRef ArchitectureName, bool DumpDynamic) { const MachOObjectFile *MachO = dyn_cast(O); @@ -2044,6 +2053,15 @@ } if (O->isELF()) { + if (!SymbolVersions.empty()) { + const VersionEntry &Ver = + SymbolVersions[Symbol.getRawDataRefImpl().d.b - 1]; + std::string Str; + if (!Ver.Name.empty()) + Str = Ver.IsVerDef ? ' ' + Ver.Name : '(' + Ver.Name + ')'; + outs() << format(" %-11s", Str.c_str()); + } + uint8_t Other = ELFSymbolRef(Symbol).getOther(); switch (Other) { case ELF::STV_DEFAULT: