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,155 @@ +## Check we print symbol versions, when they are available. + +## Test undefined symbols. +# 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 + +## version2sym and version3sym are invalid: undefined symbols cannot refer to .gnu.version_d. +## We still check their behaviors. +# 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) .hidden version5sym + +## Test defined symbols. +# 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 g D .gnu.version|0000000000000000 localversym +# DEF-NEXT:0000000000000000 g D .gnu.version|0000000000000000 globalversym +# DEF-NEXT:0000000000000000 g D .gnu.version|0000000000000000 v2 version2sym +# DEF-NEXT:0000000000000000 g D .gnu.version|0000000000000000 (v3hidden) version3sym +# DEF-NEXT:0000000000000000 g D .gnu.version|0000000000000000 (v4) version4sym +# DEF-NEXT:0000000000000000 g D .gnu.version|0000000000000000 (v5hidden) .hidden 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, 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=]] + Binding: STB_GLOBAL + - Name: globalversym + Index: [[INDEX=]] + Binding: STB_GLOBAL + - Name: version2sym + Index: [[INDEX=]] + Binding: STB_GLOBAL + - Name: version3sym + Index: [[INDEX=]] + Binding: STB_GLOBAL + - Name: version4sym + Index: [[INDEX=]] + Binding: STB_GLOBAL + - Name: version5sym + Index: [[INDEX=]] + Other: [ STV_HIDDEN ] + Binding: STB_GLOBAL + +## Test the output with a long version name. +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: llvm-objdump -T %t2 2>&1 | tr '\t' '|' | FileCheck %s \ +# RUN: --check-prefix=LONGNAME --match-full-lines --strict-whitespace + +# LONGNAME:DYNAMIC SYMBOL TABLE: +# LONGNAME-NEXT:0000000000000000 g D .gnu.version|0000000000000000 v2 sym1 +# LONGNAME-NEXT:0000000000000000 g D .gnu.version|0000000000000000 v3withverylongname sym2 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN +Sections: + - Name: .gnu.version + Type: SHT_GNU_versym + Flags: [ SHF_ALLOC ] + Entries: [ 1, 2, 3 ] + - Name: .gnu.version_d + Type: SHT_GNU_verdef + Flags: [ SHF_ALLOC ] + Link: .dynstr + AddressAlign: 0x4 + Info: 0x2 + Entries: + - VersionNdx: 2 + Names: + - v2 + - VersionNdx: 3 + Names: + - v3withverylongname + - Name: .dynsym + Type: SHT_DYNSYM +DynamicSymbols: + - Name: sym1 + Index: 1 + Binding: STB_GLOBAL + - Name: sym2 + Index: 1 + Binding: STB_GLOBAL + +## Check we report a warning when we are unable to read a SHT_GNU_versym section entry. +## In this case, the section has a size that is not a multiple of its sh_entsize. + +# RUN: yaml2obj -DVERSYMSIZE=0xff %s -o %t2-broken-versym.o +# RUN: llvm-objdump -T %t2-broken-versym.o 2>&1 | FileCheck %s --check-prefixes=VERSION-ERR1,NOVER + +# VERSION-ERR1:warning: {{.*}}: unable to read an entry with index 1 from SHT_GNU_versym section +# NOVER-NEXT:0000000000000000 D *UND* 0000000000000000 localversym +# NOVER-NEXT:0000000000000000 D *UND* 0000000000000000 globalversym +# NOVER-NEXT:0000000000000000 D *UND* 0000000000000000 version2sym +# NOVER-NEXT:0000000000000000 D *UND* 0000000000000000 version3sym +# NOVER-NEXT:0000000000000000 D *UND* 0000000000000000 version4sym +# NOVER-NEXT:0000000000000000 D *UND* 0000000000000000 .hidden version5sym 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,21 @@ } 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) { + reportWarning(toString(SymbolVersionsOrErr.takeError()), FileName); + SymbolVersionsOrErr = std::vector(); + (void)!SymbolVersionsOrErr; + } + 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 +2054,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() << ' ' << left_justify(Str, 12); + } + uint8_t Other = ELFSymbolRef(Symbol).getOther(); switch (Other) { case ELF::STV_DEFAULT: