[llvm-readobj] Teach ELFDumper about symbol versioning.
authorDavide Italiano <davide@freebsd.org>
Fri, 16 Oct 2015 23:19:01 +0000 (23:19 +0000)
committerDavide Italiano <davide@freebsd.org>
Fri, 16 Oct 2015 23:19:01 +0000 (23:19 +0000)
Differential Revision:  http://reviews.llvm.org/D13824

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@250575 91177308-0d34-0410-b5e6-96231b3b80d8

test/tools/llvm-readobj/elf-versioninfo.test
tools/llvm-readobj/ELFDumper.cpp
tools/llvm-readobj/ObjDumper.h
tools/llvm-readobj/llvm-readobj.cpp

index 3c3d0eefaad96d23ccb52ae7b86d753f10f5cb1e..e8113e4b2fed3b8b6c7952dca5f5a1bd92bbea65 100644 (file)
@@ -1,7 +1,81 @@
 // Test that llvm-readobj dumps version info tags correctly.
 
-RUN: llvm-readobj -dynamic-table %p/Inputs/verdef.elf-x86-64 | FileCheck %s
+RUN: llvm-readobj -dynamic-table -V %p/Inputs/verdef.elf-x86-64 | FileCheck %s
 
 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: Version definition {
+CHECK-NEXT:   Section Name: .gnu.version_d (70)
+CHECK-NEXT:   Address: 0x25C
+CHECK-NEXT:   Offset: 0x25C
+CHECK-NEXT:   Link: 2
+CHECK-NEXT:   Entries [
+CHECK-NEXT:     Entry {
+CHECK-NEXT:       Offset: 0x0
+CHECK-NEXT:       Rev: 1
+CHECK-NEXT:       Flags: 1
+CHECK-NEXT:       Index: 1
+CHECK-NEXT:       Cnt: 1
+CHECK-NEXT:       Name: blah
+CHECK-NEXT:     }
+CHECK-NEXT:     Entry {
+CHECK-NEXT:       Offset: 0x1C
+CHECK-NEXT:       Rev: 1
+CHECK-NEXT:       Flags: 0
+CHECK-NEXT:       Index: 2
+CHECK-NEXT:       Cnt: 1
+CHECK-NEXT:       Name: VERSION1
+CHECK-NEXT:     }
+CHECK-NEXT:     Entry {
+CHECK-NEXT:       Offset: 0x38
+CHECK-NEXT:       Rev: 1
+CHECK-NEXT:       Flags: 0
+CHECK-NEXT:       Index: 3
+CHECK-NEXT:       Cnt: 2
+CHECK-NEXT:       Name: VERSION2
+CHECK-NEXT:     }
+CHECK-NEXT:   ]
+CHECK-NEXT: }
index 5b4a78cf52fa7e36be290c6cd0d5522ec3f2e709..fa4bf36925aa07256aff6cea3806fd43197dbd11 100644 (file)
@@ -58,6 +58,7 @@ public:
   void printHashTable() override;
   void printGnuHashTable() override;
   void printLoadName() override;
+  void printVersionInfo() override;
 
   void printAttributes() override;
   void printMipsPLTGOT() override;
@@ -76,6 +77,7 @@ private:
   typedef typename ELFO::Elf_Rela Elf_Rela;
   typedef typename ELFO::Elf_Rela_Range Elf_Rela_Range;
   typedef typename ELFO::Elf_Phdr Elf_Phdr;
+  typedef typename ELFO::Elf_Half Elf_Half;
   typedef typename ELFO::Elf_Hash Elf_Hash;
   typedef typename ELFO::Elf_GnuHash Elf_GnuHash;
   typedef typename ELFO::Elf_Ehdr Elf_Ehdr;
@@ -85,6 +87,7 @@ private:
   typedef typename ELFO::Elf_Verneed Elf_Verneed;
   typedef typename ELFO::Elf_Vernaux Elf_Vernaux;
   typedef typename ELFO::Elf_Verdef Elf_Verdef;
+  typedef typename ELFO::Elf_Verdaux Elf_Verdaux;
 
   /// \brief Represents a region described by entries in the .dynamic table.
   struct DynRegionInfo {
@@ -119,12 +122,6 @@ private:
     error(Ret.getError());
     return *Ret;
   }
-  Elf_Dyn_Range dynamic_table() const {
-    ErrorOr<Elf_Dyn_Range> Ret = Obj->dynamic_table(DynamicProgHeader);
-    error(Ret.getError());
-    return *Ret;
-  }
-
   StringRef getSymbolVersion(StringRef StrTab, const Elf_Sym *symb,
                              bool &IsDefault);
   void LoadVersionMap();
@@ -171,6 +168,12 @@ private:
   mutable SmallVector<VersionMapEntry, 16> VersionMap;
 
 public:
+  Elf_Dyn_Range dynamic_table() const {
+    ErrorOr<Elf_Dyn_Range> Ret = Obj->dynamic_table(DynamicProgHeader);
+    error(Ret.getError());
+    return *Ret;
+  }
+
   std::string getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable,
                                 bool IsDynamic);
   const Elf_Shdr *getDotDynSymSec() const { return DotDynSymSec; }
@@ -302,6 +305,94 @@ template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() {
     LoadVersionNeeds(dot_gnu_version_r_sec);
 }
 
+
+template <typename ELFO, class ELFT>
+static void printVersionSymbolSection(ELFDumper<ELFT> *Dumper,
+                                      const ELFO *Obj,
+                                      const typename ELFO::Elf_Shdr *Sec,
+                                      StreamWriter &W) {
+  DictScope SS(W, "Version symbols");
+  if (!Sec)
+    return;
+  StringRef Name = errorOrDefault(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 typename ELFO::Elf_Shdr *DynSymSec = Dumper->getDotDynSymSec();
+  uint8_t *P = (uint8_t *)Obj->base() + Sec->sh_offset;
+  ErrorOr<StringRef> StrTableOrErr =
+      Obj->getStringTableForSymtab(*DynSymSec);
+  error(StrTableOrErr.getError());
+
+  // Same number of entries in the dynamic symbol table (DT_SYMTAB).
+  ListScope Syms(W, "Symbols");
+  for (const typename ELFO::Elf_Sym &Sym : Obj->symbols(DynSymSec)) {
+    DictScope S(W, "Symbol");
+    std::string FullSymbolName =
+        Dumper->getFullSymbolName(&Sym, *StrTableOrErr, true /* IsDynamic */);
+    W.printNumber("Version", *P);
+    W.printString("Name", FullSymbolName);
+    P += sizeof(typename ELFO::Elf_Half);
+  }
+}
+
+template <typename ELFO, class ELFT>
+static void printVersionDefinitionSection(ELFDumper<ELFT> *Dumper,
+                                          const ELFO *Obj,
+                                          const typename ELFO::Elf_Shdr *Sec,
+                                          StreamWriter &W) {
+  DictScope SD(W, "Version definition");
+  if (!Sec)
+    return;
+  StringRef Name = errorOrDefault(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);
+
+  unsigned verdef_entries = 0;
+  // The number of entries in the section SHT_GNU_verdef
+  // is determined by DT_VERDEFNUM tag.
+  for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) {
+    if (Dyn.d_tag == DT_VERDEFNUM)
+      verdef_entries = Dyn.d_un.d_val;
+  }
+  uint8_t *SecStartAddress = (uint8_t *)Obj->base() + Sec->sh_offset;
+  uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size;
+  uint8_t *P = SecStartAddress;
+  ErrorOr<const typename ELFO::Elf_Shdr *> StrTabOrErr =
+      Obj->getSection(Sec->sh_link);
+  error(StrTabOrErr.getError());
+
+  ListScope Entries(W, "Entries");
+  for (unsigned i = 0; i < verdef_entries; ++i) {
+    if (P + sizeof(typename ELFO::Elf_Verdef) > SecEndAddress)
+      report_fatal_error("invalid offset in the section");
+    auto *VD = reinterpret_cast<const typename ELFO::Elf_Verdef *>(P);
+    DictScope Entry(W, "Entry");
+    W.printHex("Offset", (uintptr_t)P - (uintptr_t)SecStartAddress);
+    W.printNumber("Rev", VD->vd_version);
+    // FIXME: print something more readable.
+    W.printNumber("Flags", VD->vd_flags);
+    W.printNumber("Index", VD->vd_ndx);
+    W.printNumber("Cnt", VD->vd_cnt);
+    W.printString("Name", StringRef((char *)(
+                              Obj->base() + (*StrTabOrErr)->sh_offset +
+                              VD->getAux()->vda_name)));
+    P += VD->vd_next;
+  }
+}
+
+template <typename ELFT> void ELFDumper<ELFT>::printVersionInfo() {
+  // Dump version symbol section.
+  printVersionSymbolSection(this, Obj, dot_gnu_version_sec, W);
+
+  // Dump version definition section.
+  printVersionDefinitionSection(this, Obj, dot_gnu_version_d_sec, W);
+}
+
 template <typename ELFT>
 StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab,
                                             const Elf_Sym *symb,
index 79c2582830451f0533db97878e78bb10a4796959..2ca1300439eb8f4ad45dfea5bf9850f27a186eb6 100644 (file)
@@ -41,6 +41,7 @@ public:
   virtual void printHashTable() { }
   virtual void printGnuHashTable() { }
   virtual void printLoadName() {}
+  virtual void printVersionInfo() {}
 
   // Only implemented for ARM ELF at this time.
   virtual void printAttributes() { }
index 3b40d5335caaaa7347f71ddf0614968271860940..cb0c9c6418e610737016b57075763f28cb5fc0d4 100644 (file)
@@ -221,6 +221,12 @@ namespace opts {
   PrintStackMap("stackmap",
                 cl::desc("Display contents of stackmap section"));
 
+  // -version-info
+  cl::opt<bool>
+      VersionInfo("version-info",
+                  cl::desc("Display ELF version sections (if present)"));
+  cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"),
+                             cl::aliasopt(VersionInfo));
 } // namespace opts
 
 namespace llvm {
@@ -328,6 +334,8 @@ static void dumpObject(const ObjectFile *Obj) {
     Dumper->printHashTable();
   if (opts::GnuHashTable)
     Dumper->printGnuHashTable();
+  if (opts::VersionInfo)
+    Dumper->printVersionInfo();
   if (Obj->getArch() == llvm::Triple::arm && Obj->isELF())
     if (opts::ARMAttributes)
       Dumper->printAttributes();