From e9ef9e434c063d801be8032068212b27c17d2137 Mon Sep 17 00:00:00 2001 From: Georgii Rymar Date: Thu, 14 Jan 2021 16:20:35 +0300 Subject: [PATCH] [llvm-nm][ELF] - Make -D display symbol versions. This fixes https://bugs.llvm.org/show_bug.cgi?id=48670. Since binutils 2.35, nm -D displays symbol versions by default. This patch teaches llvm-nm to do the same. Differential revision: https://reviews.llvm.org/D94907 --- test/tools/llvm-nm/dynamic.test | 103 ++++++++++++++++++++++++++++++++ tools/llvm-nm/llvm-nm.cpp | 80 +++++++++++++++++++++++++ 2 files changed, 183 insertions(+) diff --git a/test/tools/llvm-nm/dynamic.test b/test/tools/llvm-nm/dynamic.test index 7c7ec8241ec..9d91aacb571 100644 --- a/test/tools/llvm-nm/dynamic.test +++ b/test/tools/llvm-nm/dynamic.test @@ -60,3 +60,106 @@ Sections: - Name: .dynsym Type: SHT_DYNSYM Size: 0 + +## Check we print symbol versions, when they are available. + +# RUN: yaml2obj --docnum=4 %s -o %t4.o +# RUN: llvm-nm --dynamic %t4.o 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t4.o --check-prefix=VERSIONED-SYMS + +# VERSIONED-SYMS: U globalversym +# VERSIONED-SYMS-NEXT: U localversym +# VERSIONED-SYMS-NEXT: U version2sym@v2 +# VERSIONED-SYMS-NEXT: U version3sym@v3hidden +# VERSIONED-SYMS-NEXT: U version4sym@v4 +# VERSIONED-SYMS-NEXT: U version5sym@v5hidden + +--- !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 +DynamicSymbols: + - Name: localversym + - Name: globalversym + - Name: version2sym + - Name: version3sym + - Name: version4sym + - Name: version5sym + +## In the following cases we check we report warnings when unable to read symbol version. +## Check that we still print unversioned symbol names. + +## Case 1: check we report a warning when unable to read symbol versions +## from a broken SHT_GNU_verdef section. In this case its sh_offset +## field has a too large value that goes past the EOF. + +# RUN: yaml2obj --docnum=4 -DVERDEFOFFSET=0xffffffff %s -o %t4-broken-verdef.o +# RUN: llvm-nm --dynamic %t4-broken-verdef.o 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t4-broken-verdef.o --check-prefixes=VERSION-ERR,VERSION-ERR1 + +# VERSION-ERR1: warning: unable to read symbol versions: cannot read content of SHT_GNU_verdef section with index 2: section [index 2] has a sh_offset (0xffffffff) + sh_size (0x38) that is greater than the file size (0x438) +# VERSION-ERR2: warning: unable to read symbol versions: unable to read an entry with index 1 from SHT_GNU_versym section with index 1: section [index 1] has an invalid sh_size (255) which is not a multiple of its sh_entsize (2) +# VERSION-ERR3: warning: unable to read symbol versions: unable to get a version for entry 3 of SHT_GNU_versym section with index 1: SHT_GNU_versym section refers to a version index 255 which is missing + +# VERSION-ERR-NEXT: U globalversym{{$}} +# VERSION-ERR-NEXT: U localversym{{$}} +# VERSION-ERR-NEXT: U version2sym{{$}} +# VERSION-ERR-NEXT: U version3sym{{$}} +# VERSION-ERR-NEXT: U version4sym{{$}} +# VERSION-ERR-NEXT: U version5sym{{$}} + +## Case 2: 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 --docnum=4 -DVERSYMSIZE=0xff %s -o %t4-broken-versym.o +# RUN: llvm-nm --dynamic %t4-broken-versym.o 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t4-broken-versym.o --check-prefixes=VERSION-ERR,VERSION-ERR2 + +## Case 3: check we report a warning when we are unable to get a vesrion for a SHT_GNU_versym section entry. +## In this case the SHT_GNU_versym section refers to a version index 255 which is missing. + +# RUN: yaml2obj --docnum=4 -DVERSYMENTRY=0xff %s -o %t4-broken-index.o +# RUN: llvm-nm --dynamic %t4-broken-index.o 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t4-broken-index.o --check-prefixes=VERSION-ERR,VERSION-ERR3 diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp index ccb54c3576f..117538a08ed 100644 --- a/tools/llvm-nm/llvm-nm.cpp +++ b/tools/llvm-nm/llvm-nm.cpp @@ -1686,10 +1686,77 @@ static void dumpSymbolsFromDLInfoMachO(MachOObjectFile &MachO) { } } +template +static Expected> +readSymbolVersionsELF(const ELFFile &Obj, StringRef FileName, + ELFObjectFileBase::elf_symbol_iterator_range Symbols) { + using Elf_Shdr = typename ELFT::Shdr; + + // We called sections() earlier, so can't fail here. + typename ELFT::ShdrRange SectionsOrErr = cantFail(Obj.sections()); + const Elf_Shdr *SymVerSec = nullptr; + const Elf_Shdr *SymVerNeedSec = nullptr; + const Elf_Shdr *SymVerDefSec = nullptr; + for (const Elf_Shdr &Sec : SectionsOrErr) { + if (Sec.sh_type == ELF::SHT_GNU_versym) + SymVerSec = &Sec; + else if (Sec.sh_type == ELF::SHT_GNU_verdef) + SymVerDefSec = &Sec; + else if (Sec.sh_type == ELF::SHT_GNU_verneed) + SymVerNeedSec = &Sec; + } + + if (!SymVerSec) + return std::vector{}; + + Expected, 0>> MapOrErr = + Obj.loadVersionMap(SymVerNeedSec, SymVerDefSec); + if (!MapOrErr) + return MapOrErr.takeError(); + + std::vector Ret; + size_t I = 0; + for (auto It = Symbols.begin(), E = Symbols.end(); It != E; ++It) { + ++I; + Expected VerEntryOrErr = + Obj.template getEntry(*SymVerSec, I); + if (!VerEntryOrErr) + return createError("unable to read an entry with index " + Twine(I) + + " from " + describe(Obj, *SymVerSec) + ": " + + toString(VerEntryOrErr.takeError())); + + bool IsDefault; + Expected VerOrErr = Obj.getSymbolVersionByIndex( + (*VerEntryOrErr)->vs_index, IsDefault, *MapOrErr); + if (!VerOrErr) + return createError("unable to get a version for entry " + Twine(I) + + " of " + describe(Obj, *SymVerSec) + ": " + + toString(VerOrErr.takeError())); + + Ret.push_back((*VerOrErr).str()); + } + + return Ret; +} + +static Expected> +readSymbolVersionsELF(const ELFObjectFileBase &Obj, + ELFObjectFileBase::elf_symbol_iterator_range Symbols) { + if (const auto *ELF = dyn_cast(&Obj)) + return readSymbolVersionsELF(ELF->getELFFile(), Obj.getFileName(), Symbols); + else if (const auto *ELF = dyn_cast(&Obj)) + return readSymbolVersionsELF(ELF->getELFFile(), Obj.getFileName(), Symbols); + else if (const auto *ELF = dyn_cast(&Obj)) + return readSymbolVersionsELF(ELF->getELFFile(), Obj.getFileName(), Symbols); + return readSymbolVersionsELF(cast(&Obj)->getELFFile(), + Obj.getFileName(), Symbols); +} + static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, StringRef ArchiveName = {}, StringRef ArchitectureName = {}) { auto Symbols = Obj.symbols(); + std::vector SymbolVersions; if (DynamicSyms) { const auto *E = dyn_cast(&Obj); if (!E) { @@ -1697,6 +1764,14 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, return; } Symbols = E->getDynamicSymbolIterators(); + + if (Expected> VersionsOrErr = + readSymbolVersionsELF(*E, Symbols)) + SymbolVersions = std::move(*VersionsOrErr); + else + WithColor::warning(errs(), ToolName) + << "unable to read symbol versions: " + << toString(VersionsOrErr.takeError()) << "\n"; } // If a "-s segname sectname" option was specified and this is a Mach-O @@ -1710,7 +1785,9 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, return; } if (!(MachO && DyldInfoOnly)) { + size_t I = -1; for (BasicSymbolRef Sym : Symbols) { + ++I; Expected SymFlagsOrErr = Sym.getFlags(); if (!SymFlagsOrErr) { error(SymFlagsOrErr.takeError(), Obj.getFileName()); @@ -1750,6 +1827,9 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, } else error(std::move(E), Obj.getFileName()); } + if (!SymbolVersions.empty() && !SymbolVersions[I].empty()) + S.Name += "@" + SymbolVersions[I]; + S.Sym = Sym; SymbolList.push_back(S); }