[lld] r251000 - [ELF2] Add support for GNU Hash section

Igor Kudrin via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 22 01:21:36 PDT 2015


Author: ikudrin
Date: Thu Oct 22 03:21:35 2015
New Revision: 251000

URL: http://llvm.org/viewvc/llvm-project?rev=251000&view=rev
Log:
[ELF2] Add support for GNU Hash section

This patch implements --hash-style command line switch.

* By default, or with "sysv" or "both" parameters, the linker generates
  a standard ELF hash section.
* With "gnu" or "both", it produces a GNU-style hash section.

That section requires the symbols in the dynamic symbol table section, which
are referenced in the GNU hash section, to be placed after not hashed ones and
to be sorted to correspond the order of hash buckets in the GNU Hash section.

The division function, as well as estimations for the section's parameters,
are just the first rough attempt and the subjects for further adjustments.

Differential Revision: http://reviews.llvm.org/D13815

Added:
    lld/trunk/test/elf2/gnu-hash-table.s
Modified:
    lld/trunk/ELF/Config.h
    lld/trunk/ELF/Driver.cpp
    lld/trunk/ELF/Options.td
    lld/trunk/ELF/OutputSections.cpp
    lld/trunk/ELF/OutputSections.h
    lld/trunk/ELF/Writer.cpp

Modified: lld/trunk/ELF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Config.h?rev=251000&r1=250999&r2=251000&view=diff
==============================================================================
--- lld/trunk/ELF/Config.h (original)
+++ lld/trunk/ELF/Config.h Thu Oct 22 03:21:35 2015
@@ -51,11 +51,13 @@ struct Configuration {
   bool DiscardNone;
   bool EnableNewDtags;
   bool ExportDynamic;
+  bool GnuHash = false;
   bool Mips64EL = false;
   bool NoInhibitExec;
   bool NoUndefined;
   bool Shared;
   bool Static = false;
+  bool SysvHash = true;
   bool Verbose;
   bool ZNodelete = false;
   bool ZNow = false;

Modified: lld/trunk/ELF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Driver.cpp?rev=251000&r1=250999&r2=251000&view=diff
==============================================================================
--- lld/trunk/ELF/Driver.cpp (original)
+++ lld/trunk/ELF/Driver.cpp Thu Oct 22 03:21:35 2015
@@ -155,6 +155,17 @@ void LinkerDriver::createFiles(opt::Inpu
   Config->SoName = getString(Args, OPT_soname);
   Config->Sysroot = getString(Args, OPT_sysroot);
 
+  if (auto *Arg = Args.getLastArg(OPT_hash_style)) {
+    StringRef S = Arg->getValue();
+    if (S == "gnu") {
+      Config->GnuHash = true;
+      Config->SysvHash = false;
+    } else if (S == "both") {
+      Config->GnuHash = true;
+    } else if (S != "sysv")
+      error("Unknown hash style: " + S);
+  }
+
   for (auto *Arg : Args.filtered(OPT_undefined))
     Config->Undefined.push_back(Arg->getValue());
 

Modified: lld/trunk/ELF/Options.td
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Options.td?rev=251000&r1=250999&r2=251000&view=diff
==============================================================================
--- lld/trunk/ELF/Options.td (original)
+++ lld/trunk/ELF/Options.td Thu Oct 22 03:21:35 2015
@@ -46,6 +46,9 @@ def export_dynamic : Flag<["--", "-"], "
 def fini : Separate<["-"], "fini">, MetaVarName<"<symbol>">,
   HelpText<"Specify a finalizer function">;
 
+def hash_style : Separate<["--", "-"], "hash-style">,
+  HelpText<"Specify hash style (sysv, gnu or both)">;
+
 def init : Separate<["-"], "init">, MetaVarName<"<symbol>">,
   HelpText<"Specify an initializer function">;
 
@@ -107,6 +110,7 @@ def alias_discard_all_x: Flag<["-"], "x"
 def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
 def alias_entry_e : Separate<["-"], "e">, Alias<entry>;
 def alias_fini_fini : Joined<["-"], "fini=">, Alias<fini>;
+def alias_hash_style_hash_style : Joined<["--", "-"], "hash-style=">, Alias<hash_style>;
 def alias_init_init : Joined<["-"], "init=">, Alias<init>;
 def alias_l__library : Joined<["--"], "library=">, Alias<l>;
 def alias_o_output : Joined<["--"], "output=">, Alias<o>;
@@ -124,7 +128,6 @@ def eh_frame_hdr : Flag<["--"], "eh-fram
 def end_group : Flag<["--"], "end-group">;
 def fatal_warnings : Flag<["--"], "fatal-warnings">;
 def gc_sections : Flag<["--"], "gc-sections">;
-def hash_style : Joined<["--"], "hash-style=">;
 def no_add_needed : Flag<["--"], "no-add-needed">;
 def no_fatal_warnings : Flag<["--"], "no-fatal-warnings">;
 def no_warn_mismatch : Flag<["--"], "no-warn-mismatch">;

Modified: lld/trunk/ELF/OutputSections.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/OutputSections.cpp?rev=251000&r1=250999&r2=251000&view=diff
==============================================================================
--- lld/trunk/ELF/OutputSections.cpp (original)
+++ lld/trunk/ELF/OutputSections.cpp Thu Oct 22 03:21:35 2015
@@ -11,6 +11,7 @@
 #include "Config.h"
 #include "SymbolTable.h"
 #include "Target.h"
+#include "llvm/Support/MathExtras.h"
 
 using namespace llvm;
 using namespace llvm::object;
@@ -238,7 +239,7 @@ HashTableSection<ELFT>::HashTableSection
   this->Header.sh_addralign = sizeof(Elf_Word);
 }
 
-static uint32_t hash(StringRef Name) {
+static uint32_t hashSysv(StringRef Name) {
   uint32_t H = 0;
   for (char C : Name) {
     H = (H << 4) + C;
@@ -272,15 +273,138 @@ template <class ELFT> void HashTableSect
   Elf_Word *Buckets = P;
   Elf_Word *Chains = P + NumSymbols;
 
-  for (SymbolBody *Body : Out<ELFT>::DynSymTab->getSymbols()) {
+  for (const typename SymbolTableSection<ELFT>::SymbolData &Item :
+       Out<ELFT>::DynSymTab->getSymbols()) {
+    SymbolBody *Body = Item.Body;
     StringRef Name = Body->getName();
     unsigned I = Body->getDynamicSymbolTableIndex();
-    uint32_t Hash = hash(Name) % NumSymbols;
+    uint32_t Hash = hashSysv(Name) % NumSymbols;
     Chains[I] = Buckets[Hash];
     Buckets[Hash] = I;
   }
 }
 
+static uint32_t hashGnu(StringRef Name) {
+  uint32_t H = 5381;
+  for (uint8_t C : Name)
+    H = (H << 5) + H + C;
+  return H;
+}
+
+template <class ELFT>
+GnuHashTableSection<ELFT>::GnuHashTableSection()
+    : OutputSectionBase<ELFT>(".gnu.hash", llvm::ELF::SHT_GNU_HASH,
+                              llvm::ELF::SHF_ALLOC) {
+  this->Header.sh_entsize = ELFT::Is64Bits ? 0 : 4;
+  this->Header.sh_addralign = ELFT::Is64Bits ? 8 : 4;
+}
+
+template <class ELFT>
+unsigned GnuHashTableSection<ELFT>::calcNBuckets(unsigned NumHashed) {
+  if (!NumHashed)
+    return 0;
+
+  // These values are prime numbers which are not greater than 2^(N-1) + 1.
+  // In result, for any particular NumHashed we return a prime number
+  // which is not greater than NumHashed.
+  static const unsigned Primes[] = {
+      1,   1,    3,    3,    7,    13,    31,    61,    127,   251,
+      509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071};
+
+  return Primes[std::min<unsigned>(Log2_32_Ceil(NumHashed),
+                                   array_lengthof(Primes) - 1)];
+}
+
+// Bloom filter estimation: at least 8 bits for each hashed symbol.
+// GNU Hash table requirement: it should be a power of 2,
+//   the minimum value is 1, even for an empty table.
+// Expected results for a 32-bit target:
+//   calcMaskWords(0..4)   = 1
+//   calcMaskWords(5..8)   = 2
+//   calcMaskWords(9..16)  = 4
+// For a 64-bit target:
+//   calcMaskWords(0..8)   = 1
+//   calcMaskWords(9..16)  = 2
+//   calcMaskWords(17..32) = 4
+template <class ELFT>
+unsigned GnuHashTableSection<ELFT>::calcMaskWords(unsigned NumHashed) {
+  if (!NumHashed)
+    return 1;
+  return NextPowerOf2((NumHashed - 1) / sizeof(Elf_Off));
+}
+
+template <class ELFT> void GnuHashTableSection<ELFT>::finalize() {
+  const unsigned NumHashed = Out<ELFT>::DynSymTab->getNumGnuHashSymbols();
+  NBuckets = calcNBuckets(NumHashed);
+  MaskWords = calcMaskWords(NumHashed);
+  // Second hash shift estimation: just predefined values.
+  Shift2 = ELFT::Is64Bits ? 6 : 5;
+
+  this->Header.sh_link = Out<ELFT>::DynSymTab->SectionIndex;
+  this->Header.sh_size = sizeof(Elf_Word) * 4            // Header
+                         + sizeof(Elf_Off) * MaskWords   // Bloom Filter
+                         + sizeof(Elf_Word) * NBuckets   // Hash Buckets
+                         + sizeof(Elf_Word) * NumHashed; // Hash Values
+}
+
+template <class ELFT> void GnuHashTableSection<ELFT>::writeTo(uint8_t *Buf) {
+  writeHeader(Buf);
+  if (!NBuckets) // There are no hashed symbols
+    return;
+  writeBloomFilter(Buf);
+  writeHashTable(Buf);
+}
+
+template <class ELFT>
+void GnuHashTableSection<ELFT>::writeHeader(uint8_t *&Buf) {
+  auto *P = reinterpret_cast<Elf_Word *>(Buf);
+  *P++ = NBuckets;
+  *P++ = Out<ELFT>::DynSymTab->getNumSymbols() -
+         Out<ELFT>::DynSymTab->getNumGnuHashSymbols();
+  *P++ = MaskWords;
+  *P++ = Shift2;
+  Buf = reinterpret_cast<uint8_t *>(P);
+}
+
+template <class ELFT>
+void GnuHashTableSection<ELFT>::writeBloomFilter(uint8_t *&Buf) {
+  unsigned C = sizeof(Elf_Off) * 8;
+
+  auto *Masks = reinterpret_cast<Elf_Off *>(Buf);
+  for (const typename SymbolTableSection<ELFT>::SymbolData &Item :
+       Out<ELFT>::DynSymTab->getGnuHashSymbols()) {
+    size_t Pos = (Item.GnuHash / C) & (MaskWords - 1);
+    uintX_t V = (uintX_t(1) << (Item.GnuHash % C)) |
+                (uintX_t(1) << ((Item.GnuHash >> Shift2) % C));
+    Masks[Pos] |= V;
+  }
+  Buf += sizeof(Elf_Off) * MaskWords;
+}
+
+template <class ELFT>
+void GnuHashTableSection<ELFT>::writeHashTable(uint8_t *Buf) {
+  Elf_Word *Buckets = reinterpret_cast<Elf_Word *>(Buf);
+  Elf_Word *Values = Buckets + NBuckets;
+
+  int PrevBucket = -1;
+  int I = 0;
+  for (const typename SymbolTableSection<ELFT>::SymbolData &Item :
+       Out<ELFT>::DynSymTab->getGnuHashSymbols()) {
+    int Bucket = Item.GnuHash % NBuckets;
+    assert(PrevBucket <= Bucket);
+    if (Bucket != PrevBucket) {
+      Buckets[Bucket] = Item.Body->getDynamicSymbolTableIndex();
+      PrevBucket = Bucket;
+      if (I > 0)
+        Values[I - 1] |= 1;
+    }
+    Values[I] = Item.GnuHash & ~1;
+    ++I;
+  }
+  if (I > 0)
+    Values[I - 1] |= 1;
+}
+
 template <class ELFT>
 DynamicSection<ELFT>::DynamicSection(SymbolTable<ELFT> &SymTab)
     : OutputSectionBase<ELFT>(".dynamic", llvm::ELF::SHT_DYNAMIC,
@@ -315,7 +439,10 @@ template <class ELFT> void DynamicSectio
   ++NumEntries; // DT_SYMENT
   ++NumEntries; // DT_STRTAB
   ++NumEntries; // DT_STRSZ
-  ++NumEntries; // DT_HASH
+  if (Out<ELFT>::GnuHashTab)
+    ++NumEntries; // DT_GNU_HASH
+  if (Out<ELFT>::HashTab)
+    ++NumEntries; // DT_HASH
 
   if (!Config->RPath.empty()) {
     ++NumEntries; // DT_RUNPATH / DT_RPATH
@@ -405,7 +532,10 @@ template <class ELFT> void DynamicSectio
   WritePtr(DT_SYMENT, sizeof(Elf_Sym));
   WritePtr(DT_STRTAB, Out<ELFT>::DynStrTab->getVA());
   WriteVal(DT_STRSZ, Out<ELFT>::DynStrTab->data().size());
-  WritePtr(DT_HASH, Out<ELFT>::HashTab->getVA());
+  if (Out<ELFT>::GnuHashTab)
+    WritePtr(DT_GNU_HASH, Out<ELFT>::GnuHashTab->getVA());
+  if (Out<ELFT>::HashTab)
+    WritePtr(DT_HASH, Out<ELFT>::HashTab->getVA());
 
   if (!Config->RPath.empty())
 
@@ -654,6 +784,11 @@ bool lld::elf2::includeInDynamicSymtab(c
   return B.isUsedInDynamicReloc();
 }
 
+bool lld::elf2::includeInGnuHashTable(const SymbolBody &B) {
+  // Assume that includeInDynamicSymtab() is already checked.
+  return !B.isUndefined();
+}
+
 template <class ELFT>
 bool lld::elf2::shouldKeepInSymtab(const ObjectFile<ELFT> &File,
                                    StringRef SymName,
@@ -673,6 +808,12 @@ bool lld::elf2::shouldKeepInSymtab(const
 }
 
 template <class ELFT>
+SymbolTableSection<ELFT>::SymbolData::SymbolData(SymbolBody *Body,
+                                                 bool HasGnuHash)
+    : Body(Body), HasGnuHash(HasGnuHash),
+      GnuHash(HasGnuHash ? hashGnu(Body->getName()) : 0) {}
+
+template <class ELFT>
 SymbolTableSection<ELFT>::SymbolTableSection(
     SymbolTable<ELFT> &Table, StringTableSection<ELFT> &StrTabSec)
     : OutputSectionBase<ELFT>(
@@ -694,15 +835,24 @@ template <class ELFT> void SymbolTableSe
 
   if (!StrTabSec.isDynamic()) {
     std::stable_sort(Symbols.begin(), Symbols.end(),
-                     [](SymbolBody *L, SymbolBody *R) {
-                       return getSymbolBinding(L) == STB_LOCAL &&
-                              getSymbolBinding(R) != STB_LOCAL;
+                     [](const SymbolData &L, const SymbolData &R) {
+                       return getSymbolBinding(L.Body) == STB_LOCAL &&
+                              getSymbolBinding(R.Body) != STB_LOCAL;
                      });
     return;
   }
+  if (NumGnuHashed) {
+    unsigned NBuckets = GnuHashTableSection<ELFT>::calcNBuckets(NumGnuHashed);
+    std::stable_sort(Symbols.begin(), Symbols.end(),
+                     [NBuckets](const SymbolData &L, const SymbolData &R) {
+                       if (!L.HasGnuHash || !R.HasGnuHash)
+                         return R.HasGnuHash;
+                       return L.GnuHash % NBuckets < R.GnuHash % NBuckets;
+                     });
+  }
   size_t I = 0;
-  for (SymbolBody *Body : Symbols)
-    Body->setDynamicSymbolTableIndex(++I);
+  for (const SymbolData &Item : Symbols)
+    Item.Body->setDynamicSymbolTableIndex(++I);
 }
 
 template <class ELFT>
@@ -715,8 +865,12 @@ void SymbolTableSection<ELFT>::addLocalS
 template <class ELFT>
 void SymbolTableSection<ELFT>::addSymbol(SymbolBody *Body) {
   StrTabSec.add(Body->getName());
-  Symbols.push_back(Body);
+  const bool HasGnuHash = StrTabSec.isDynamic() && Out<ELFT>::GnuHashTab &&
+                          includeInGnuHashTable(*Body);
+  Symbols.emplace_back(Body, HasGnuHash);
   ++NumVisible;
+  if (HasGnuHash)
+    ++NumGnuHashed;
 }
 
 template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
@@ -768,7 +922,8 @@ void SymbolTableSection<ELFT>::writeGlob
   // Write the internal symbol table contents to the output symbol table
   // pointed by Buf.
   auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
-  for (SymbolBody *Body : Symbols) {
+  for (const SymbolData &Item : Symbols) {
+    SymbolBody *Body = Item.Body;
     StringRef Name = Body->getName();
 
     ESym->st_name = StrTabSec.getFileOff(Name);
@@ -828,6 +983,12 @@ uint8_t SymbolTableSection<ELFT>::getSym
   return Body->isWeak() ? STB_WEAK : STB_GLOBAL;
 }
 
+template <class ELFT>
+ArrayRef<typename SymbolTableSection<ELFT>::SymbolData>
+SymbolTableSection<ELFT>::getGnuHashSymbols() const {
+  return getSymbols().slice(Symbols.size() - NumGnuHashed);
+}
+
 namespace lld {
 namespace elf2 {
 template class OutputSectionBase<ELF32LE>;
@@ -860,6 +1021,11 @@ template class InterpSection<ELF32BE>;
 template class InterpSection<ELF64LE>;
 template class InterpSection<ELF64BE>;
 
+template class GnuHashTableSection<ELF32LE>;
+template class GnuHashTableSection<ELF32BE>;
+template class GnuHashTableSection<ELF64LE>;
+template class GnuHashTableSection<ELF64BE>;
+
 template class HashTableSection<ELF32LE>;
 template class HashTableSection<ELF32BE>;
 template class HashTableSection<ELF64LE>;

Modified: lld/trunk/ELF/OutputSections.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/OutputSections.h?rev=251000&r1=250999&r2=251000&view=diff
==============================================================================
--- lld/trunk/ELF/OutputSections.h (original)
+++ lld/trunk/ELF/OutputSections.h Thu Oct 22 03:21:35 2015
@@ -57,6 +57,7 @@ bool canBePreempted(const SymbolBody *Bo
 template <class ELFT> bool includeInSymtab(const SymbolBody &B);
 
 bool includeInDynamicSymtab(const SymbolBody &B);
+bool includeInGnuHashTable(const SymbolBody &B);
 
 template <class ELFT>
 bool shouldKeepInSymtab(
@@ -176,8 +177,16 @@ public:
   void addSymbol(SymbolBody *Body);
   StringTableSection<ELFT> &getStrTabSec() const { return StrTabSec; }
   unsigned getNumSymbols() const { return NumVisible + 1; }
+  unsigned getNumGnuHashSymbols() const { return NumGnuHashed; }
 
-  ArrayRef<SymbolBody *> getSymbols() const { return Symbols; }
+  struct SymbolData {
+    SymbolData(SymbolBody *Body, bool HasGnuHash);
+    SymbolBody *Body;
+    bool HasGnuHash;
+    uint32_t GnuHash;
+  };
+  ArrayRef<SymbolData> getSymbols() const { return Symbols; }
+  ArrayRef<SymbolData> getGnuHashSymbols() const;
 
 private:
   void writeLocalSymbols(uint8_t *&Buf);
@@ -187,9 +196,10 @@ private:
 
   SymbolTable<ELFT> &Table;
   StringTableSection<ELFT> &StrTabSec;
-  std::vector<SymbolBody *> Symbols;
+  std::vector<SymbolData> Symbols;
   unsigned NumVisible = 0;
   unsigned NumLocals = 0;
+  unsigned NumGnuHashed = 0;
 };
 
 template <class ELFT>
@@ -283,6 +293,33 @@ public:
   void writeTo(uint8_t *Buf) override;
 };
 
+// Outputs GNU Hash section. For detailed explanation see:
+// https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections
+template <class ELFT>
+class GnuHashTableSection final : public OutputSectionBase<ELFT> {
+  typedef typename llvm::object::ELFFile<ELFT>::Elf_Off Elf_Off;
+  typedef typename llvm::object::ELFFile<ELFT>::Elf_Word Elf_Word;
+  typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
+
+public:
+  GnuHashTableSection();
+  void finalize() override;
+  void writeTo(uint8_t *Buf) override;
+
+  static unsigned calcNBuckets(unsigned NumHashed);
+
+private:
+  static unsigned calcMaskWords(unsigned NumHashed);
+
+  void writeHeader(uint8_t *&Buf);
+  void writeBloomFilter(uint8_t *&Buf);
+  void writeHashTable(uint8_t *Buf);
+
+  unsigned MaskWords;
+  unsigned NBuckets;
+  unsigned Shift2;
+};
+
 template <class ELFT>
 class DynamicSection final : public OutputSectionBase<ELFT> {
   typedef OutputSectionBase<ELFT> Base;
@@ -314,6 +351,7 @@ private:
 // until Writer is initialized.
 template <class ELFT> struct Out {
   static DynamicSection<ELFT> *Dynamic;
+  static GnuHashTableSection<ELFT> *GnuHashTab;
   static GotPltSection<ELFT> *GotPlt;
   static GotSection<ELFT> *Got;
   static HashTableSection<ELFT> *HashTab;
@@ -332,6 +370,7 @@ template <class ELFT> struct Out {
 };
 
 template <class ELFT> DynamicSection<ELFT> *Out<ELFT>::Dynamic;
+template <class ELFT> GnuHashTableSection<ELFT> *Out<ELFT>::GnuHashTab;
 template <class ELFT> GotPltSection<ELFT> *Out<ELFT>::GotPlt;
 template <class ELFT> GotSection<ELFT> *Out<ELFT>::Got;
 template <class ELFT> HashTableSection<ELFT> *Out<ELFT>::HashTab;

Modified: lld/trunk/ELF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.cpp?rev=251000&r1=250999&r2=251000&view=diff
==============================================================================
--- lld/trunk/ELF/Writer.cpp (original)
+++ lld/trunk/ELF/Writer.cpp Thu Oct 22 03:21:35 2015
@@ -114,7 +114,11 @@ template <class ELFT> void lld::elf2::wr
   SymbolTableSection<ELFT> DynSymTab(*Symtab, *Out<ELFT>::DynStrTab);
   Out<ELFT>::DynSymTab = &DynSymTab;
   HashTableSection<ELFT> HashTab;
-  Out<ELFT>::HashTab = &HashTab;
+  if (Config->SysvHash)
+    Out<ELFT>::HashTab = &HashTab;
+  GnuHashTableSection<ELFT> GnuHashTab;
+  if (Config->GnuHash)
+    Out<ELFT>::GnuHashTab = &GnuHashTab;
   bool IsRela = Symtab->shouldUseRela();
   RelocationSection<ELFT> RelaDyn(IsRela ? ".rela.dyn" : ".rel.dyn", IsRela);
   Out<ELFT>::RelaDyn = &RelaDyn;
@@ -521,7 +525,10 @@ template <class ELFT> void Writer<ELFT>:
   OutputSections.push_back(Out<ELFT>::StrTab);
   if (isOutputDynamic()) {
     OutputSections.push_back(Out<ELFT>::DynSymTab);
-    OutputSections.push_back(Out<ELFT>::HashTab);
+    if (Out<ELFT>::GnuHashTab)
+      OutputSections.push_back(Out<ELFT>::GnuHashTab);
+    if (Out<ELFT>::HashTab)
+      OutputSections.push_back(Out<ELFT>::HashTab);
     OutputSections.push_back(Out<ELFT>::Dynamic);
     OutputSections.push_back(Out<ELFT>::DynStrTab);
     if (Out<ELFT>::RelaDyn->hasRelocs())

Added: lld/trunk/test/elf2/gnu-hash-table.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/elf2/gnu-hash-table.s?rev=251000&view=auto
==============================================================================
--- lld/trunk/test/elf2/gnu-hash-table.s (added)
+++ lld/trunk/test/elf2/gnu-hash-table.s Thu Oct 22 03:21:35 2015
@@ -0,0 +1,193 @@
+# RUN: echo ".globl foo" > %te.s
+# RUN: llvm-mc -filetype=obj -triple=i386-pc-linux      %te.s -o %te-i386.o
+# RUN: llvm-mc -filetype=obj -triple=i386-pc-linux      %s    -o %t-i386.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux    %s    -o %t-x86_64.o
+# RUN: llvm-mc -filetype=obj -triple=powerpc64-pc-linux %s    -o %t-ppc64.o
+
+# RUN: ld.lld2 -shared --hash-style=gnu  -o %te-i386.so  %te-i386.o
+# RUN: ld.lld2 -shared  -hash-style=gnu  -o %t-i386.so   %t-i386.o
+# RUN: ld.lld2 -shared  -hash-style=gnu  -o %t-x86_64.so %t-x86_64.o
+# RUN: ld.lld2 -shared --hash-style both -o %t-ppc64.so  %t-ppc64.o
+
+# RUN: llvm-readobj -dyn-symbols -gnu-hash-table %te-i386.so \
+# RUN:   | FileCheck %s -check-prefix=EMPTY
+# RUN: llvm-readobj -sections -dyn-symbols -gnu-hash-table %t-i386.so \
+# RUN:   | FileCheck %s -check-prefix=I386
+# RUN: llvm-readobj -sections -dyn-symbols -gnu-hash-table %t-x86_64.so \
+# RUN:   | FileCheck %s -check-prefix=X86_64
+# RUN: llvm-readobj -sections -dyn-symbols -gnu-hash-table %t-ppc64.so \
+# RUN:   | FileCheck %s -check-prefix=PPC64
+
+# EMPTY:      DynamicSymbols [
+# EMPTY:        Symbol {
+# EMPTY:          Name: foo@
+# EMPTY-NEXT:     Value: 0x0
+# EMPTY-NEXT:     Size: 0
+# EMPTY-NEXT:     Binding: Global
+# EMPTY-NEXT:     Type: None
+# EMPTY-NEXT:     Other: 0
+# EMPTY-NEXT:     Section: Undefined
+# EMPTY-NEXT:   }
+# EMPTY-NEXT: ]
+# EMPTY:      GnuHashTable {
+# EMPTY-NEXT:   Num Buckets: 0
+# EMPTY-NEXT:   First Hashed Symbol Index: 2
+# EMPTY-NEXT:   Num Mask Words: 1
+# EMPTY-NEXT:   Shift Count: 5
+# EMPTY-NEXT:   Bloom Filter: [0x0]
+# EMPTY-NEXT:   Buckets: []
+# EMPTY-NEXT:   Values: []
+# EMPTY-NEXT: }
+
+# I386:      Format: ELF32-i386
+# I386:      Arch: i386
+# I386:      AddressSize: 32bit
+# I386:      Sections [
+# I386:          Name: .gnu.hash
+# I386-NEXT:     Type: SHT_GNU_HASH
+# I386-NEXT:     Flags [
+# I386-NEXT:       SHF_ALLOC
+# I386-NEXT:     ]
+# I386-NEXT:     Address:
+# I386-NEXT:     Offset:
+# I386-NEXT:     Size: 32
+# I386-NEXT:     Link:
+# I386-NEXT:     Info: 0
+# I386-NEXT:     AddressAlignment: 4
+# I386-NEXT:     EntrySize: 4
+# I386:      ]
+# I386:      DynamicSymbols [
+# I386:        Symbol {
+# I386:          Name: @
+# I386:          Binding: Local
+# I386:          Section: Undefined
+# I386:        }
+# I386:        Symbol {
+# I386:          Name: baz@
+# I386:          Binding: Global
+# I386:          Section: Undefined
+# I386:        }
+# I386:        Symbol {
+# I386:          Name: bar@
+# I386:          Binding: Global
+# I386:          Section: .text
+# I386:        }
+# I386:        Symbol {
+# I386:          Name: foo@
+# I386:          Binding: Global
+# I386:          Section: .text
+# I386:        }
+# I386:      ]
+# I386:      GnuHashTable {
+# I386-NEXT:   Num Buckets: 1
+# I386-NEXT:   First Hashed Symbol Index: 2
+# I386-NEXT:   Num Mask Words: 1
+# I386-NEXT:   Shift Count: 5
+# I386-NEXT:   Bloom Filter: [0x14000220]
+# I386-NEXT:   Buckets: [2]
+# I386-NEXT:   Values: [0xB8860BA, 0xB887389]
+# I386-NEXT: }
+
+# X86_64:      Format: ELF64-x86-64
+# X86_64:      Arch: x86_64
+# X86_64:      AddressSize: 64bit
+# X86_64:      Sections [
+# X86_64:          Name: .gnu.hash
+# X86_64-NEXT:     Type: SHT_GNU_HASH
+# X86_64-NEXT:     Flags [
+# X86_64-NEXT:       SHF_ALLOC
+# X86_64-NEXT:     ]
+# X86_64-NEXT:     Address:
+# X86_64-NEXT:     Offset:
+# X86_64-NEXT:     Size: 36
+# X86_64-NEXT:     Link:
+# X86_64-NEXT:     Info: 0
+# X86_64-NEXT:     AddressAlignment: 8
+# X86_64-NEXT:     EntrySize: 0
+# X86_64-NEXT:   }
+# X86_64:      ]
+# X86_64:      DynamicSymbols [
+# X86_64:        Symbol {
+# X86_64:          Name: @
+# X86_64:          Binding: Local
+# X86_64:          Section: Undefined
+# X86_64:        }
+# X86_64:        Symbol {
+# X86_64:          Name: baz@
+# X86_64:          Binding: Global
+# X86_64:          Section: Undefined
+# X86_64:        }
+# X86_64:        Symbol {
+# X86_64:          Name: bar@
+# X86_64:          Binding: Global
+# X86_64:          Section: .text
+# X86_64:        }
+# X86_64:        Symbol {
+# X86_64:          Name: foo@
+# X86_64:          Binding: Global
+# X86_64:          Section: .text
+# X86_64:        }
+# X86_64:      ]
+# X86_64:      GnuHashTable {
+# X86_64-NEXT:   Num Buckets: 1
+# X86_64-NEXT:   First Hashed Symbol Index: 2
+# X86_64-NEXT:   Num Mask Words: 1
+# X86_64-NEXT:   Shift Count: 6
+# X86_64-NEXT:   Bloom Filter: [0x400000000004204]
+# X86_64-NEXT:   Buckets: [2]
+# X86_64-NEXT:   Values: [0xB8860BA, 0xB887389]
+# X86_64-NEXT: }
+
+# PPC64:      Format: ELF64-ppc64
+# PPC64:      Arch: powerpc64
+# PPC64:      AddressSize: 64bit
+# PPC64:      Sections [
+# PPC64:          Name: .gnu.hash
+# PPC64-NEXT:     Type: SHT_GNU_HASH
+# PPC64-NEXT:     Flags [
+# PPC64-NEXT:       SHF_ALLOC
+# PPC64-NEXT:     ]
+# PPC64-NEXT:     Address: 0x180
+# PPC64-NEXT:     Offset: 0x180
+# PPC64-NEXT:     Size: 36
+# PPC64-NEXT:     Link: 1
+# PPC64-NEXT:     Info: 0
+# PPC64-NEXT:     AddressAlignment: 8
+# PPC64-NEXT:     EntrySize: 0
+# PPC64-NEXT:   }
+# PPC64:      ]
+# PPC64:      DynamicSymbols [
+# PPC64:        Symbol {
+# PPC64:          Name: @
+# PPC64:          Binding: Local
+# PPC64:          Section: Undefined
+# PPC64:        }
+# PPC64:        Symbol {
+# PPC64:          Name: baz@
+# PPC64:          Binding: Global
+# PPC64:          Section: Undefined
+# PPC64:        }
+# PPC64:        Symbol {
+# PPC64:          Name: bar@
+# PPC64:          Binding: Global
+# PPC64:          Section: .text
+# PPC64:        }
+# PPC64:        Symbol {
+# PPC64:          Name: foo@
+# PPC64:          Binding: Global
+# PPC64:          Section: .text
+# PPC64:        }
+# PPC64:      ]
+# PPC64:      GnuHashTable {
+# PPC64-NEXT:   Num Buckets: 1
+# PPC64-NEXT:   First Hashed Symbol Index: 2
+# PPC64-NEXT:   Num Mask Words: 1
+# PPC64-NEXT:   Shift Count: 6
+# PPC64-NEXT:   Bloom Filter: [0x400000000004204]
+# PPC64-NEXT:   Buckets: [2]
+# PPC64-NEXT:   Values: [0xB8860BA, 0xB887389]
+# PPC64-NEXT: }
+
+.globl foo,bar,baz
+foo:
+bar:




More information about the llvm-commits mailing list