[llvm-branch-commits] [llvm] dd5c982 - [llvm-nm][ELF] - Make -D display symbol versions.

Georgii Rymar via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Jan 21 00:28:50 PST 2021


Author: Georgii Rymar
Date: 2021-01-21T11:23:45+03:00
New Revision: dd5c98280473a7f74c5e5a715839e4938b46a69c

URL: https://github.com/llvm/llvm-project/commit/dd5c98280473a7f74c5e5a715839e4938b46a69c
DIFF: https://github.com/llvm/llvm-project/commit/dd5c98280473a7f74c5e5a715839e4938b46a69c.diff

LOG: [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

Added: 
    

Modified: 
    llvm/test/tools/llvm-nm/dynamic.test
    llvm/tools/llvm-nm/llvm-nm.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llvm-nm/dynamic.test b/llvm/test/tools/llvm-nm/dynamic.test
index 7c7ec8241ec7..9d91aacb5717 100644
--- a/llvm/test/tools/llvm-nm/dynamic.test
+++ b/llvm/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 at v2
+# VERSIONED-SYMS-NEXT: U version3sym at v3hidden
+# VERSIONED-SYMS-NEXT: U version4sym at v4
+# VERSIONED-SYMS-NEXT: U version5sym at 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=<none>]]
+  - Name:         .gnu.version_d
+    Type:         SHT_GNU_verdef
+    Flags:        [ SHF_ALLOC ]
+    Link:         .dynstr
+    AddressAlign: 0x4
+    Info:         0x2
+    ShOffset:     [[VERDEFOFFSET=<none>]]
+    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/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp
index ccb54c3576fe..117538a08ed2 100644
--- a/llvm/tools/llvm-nm/llvm-nm.cpp
+++ b/llvm/tools/llvm-nm/llvm-nm.cpp
@@ -1686,10 +1686,77 @@ static void dumpSymbolsFromDLInfoMachO(MachOObjectFile &MachO) {
   }
 }
 
+template <class ELFT>
+static Expected<std::vector<std::string>>
+readSymbolVersionsELF(const ELFFile<ELFT> &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<std::string>{};
+
+  Expected<SmallVector<Optional<VersionEntry>, 0>> MapOrErr =
+      Obj.loadVersionMap(SymVerNeedSec, SymVerDefSec);
+  if (!MapOrErr)
+    return MapOrErr.takeError();
+
+  std::vector<std::string> Ret;
+  size_t I = 0;
+  for (auto It = Symbols.begin(), E = Symbols.end(); It != E; ++It) {
+    ++I;
+    Expected<const typename ELFT::Versym *> VerEntryOrErr =
+        Obj.template getEntry<typename ELFT::Versym>(*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<StringRef> 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<std::vector<std::string>>
+readSymbolVersionsELF(const ELFObjectFileBase &Obj,
+                      ELFObjectFileBase::elf_symbol_iterator_range Symbols) {
+  if (const auto *ELF = dyn_cast<ELF32LEObjectFile>(&Obj))
+    return readSymbolVersionsELF(ELF->getELFFile(), Obj.getFileName(), Symbols);
+  else if (const auto *ELF = dyn_cast<ELF32BEObjectFile>(&Obj))
+    return readSymbolVersionsELF(ELF->getELFFile(), Obj.getFileName(), Symbols);
+  else if (const auto *ELF = dyn_cast<ELF64LEObjectFile>(&Obj))
+    return readSymbolVersionsELF(ELF->getELFFile(), Obj.getFileName(), Symbols);
+  return readSymbolVersionsELF(cast<ELF64BEObjectFile>(&Obj)->getELFFile(),
+                               Obj.getFileName(), Symbols);
+}
+
 static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
                                       StringRef ArchiveName = {},
                                       StringRef ArchitectureName = {}) {
   auto Symbols = Obj.symbols();
+  std::vector<std::string> SymbolVersions;
   if (DynamicSyms) {
     const auto *E = dyn_cast<ELFObjectFileBase>(&Obj);
     if (!E) {
@@ -1697,6 +1764,14 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
       return;
     }
     Symbols = E->getDynamicSymbolIterators();
+
+    if (Expected<std::vector<std::string>> 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<uint32_t> 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);
     }


        


More information about the llvm-branch-commits mailing list