[PATCH] D13815: [ELF2] Add support for Gnu Hash section

Rafael EspĂ­ndola via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 20 10:56:36 PDT 2015


Rebased patch attached. I did a merge to review the patch.

On 20 October 2015 at 03:57, Igor Kudrin <ikudrin.dev at gmail.com> wrote:
> ikudrin marked 16 inline comments as done.
> ikudrin added a comment.
>
> http://reviews.llvm.org/D13815
>
>
>
-------------- next part --------------
diff --git a/ELF/Config.h b/ELF/Config.h
index 5aa1e00..9b1d6e3 100644
--- a/ELF/Config.h
+++ b/ELF/Config.h
@@ -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;
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index 14a1d3d..04a8ac9 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -155,6 +155,17 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
   Config->SoName = getString(Args, OPT_soname);
   Config->Sysroot = getString(Args, OPT_sysroot);
 
+  if (auto *Arg = Args.getLastArg(OPT_hash_style)) {
+    StringRef Style = Arg->getValue();
+    if (Style == "gnu") {
+      Config->GnuHash = true;
+      Config->SysvHash = false;
+    } else if (Style == "both") {
+      Config->GnuHash = true;
+    } else if (Style != "sysv")
+      error("Unknown hash style: " + Style);
+  }
+
   for (auto *Arg : Args.filtered(OPT_undefined))
     Config->Undefined.push_back(Arg->getValue());
 
diff --git a/ELF/Options.td b/ELF/Options.td
index 77aa452..6e34071 100644
--- a/ELF/Options.td
+++ b/ELF/Options.td
@@ -46,6 +46,9 @@ def export_dynamic : Flag<["--", "-"], "export-dynamic">,
 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">, Alias<discard_all>;
 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-frame-hdr">;
 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">;
diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp
index 95238db..46f1eb8 100644
--- a/ELF/OutputSections.cpp
+++ b/ELF/OutputSections.cpp
@@ -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;
@@ -250,17 +251,9 @@ static uint32_t hash(StringRef Name) {
   return H;
 }
 
-template <class ELFT> void HashTableSection<ELFT>::addSymbol(SymbolBody *S) {
-  StringRef Name = S->getName();
-  Out<ELFT>::DynSymTab->addSymbol(Name);
-  Hashes.push_back(hash(Name));
-  S->setDynamicSymbolTableIndex(Hashes.size());
-}
-
 template <class ELFT> void HashTableSection<ELFT>::finalize() {
   this->Header.sh_link = Out<ELFT>::DynSymTab->SectionIndex;
 
-  assert(Out<ELFT>::DynSymTab->getNumSymbols() == Hashes.size() + 1);
   unsigned NumEntries = 2;                 // nbucket and nchain.
   NumEntries += Out<ELFT>::DynSymTab->getNumSymbols(); // The chain entries.
 
@@ -280,13 +273,129 @@ template <class ELFT> void HashTableSection<ELFT>::writeTo(uint8_t *Buf) {
   Elf_Word *Buckets = P;
   Elf_Word *Chains = P + NumSymbols;
 
-  for (unsigned I = 1; I < NumSymbols; ++I) {
-    uint32_t Hash = Hashes[I - 1] % NumSymbols;
+  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 = 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 & 0xffffffff;
+}
+
+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.
+  // No deep investigation was done, it just looks good for the first try.
+  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)];
+}
+
+template <class ELFT>
+unsigned GnuHashTableSection<ELFT>::calcMaskWords(unsigned NumHashed) {
+  if (!NumHashed)
+    return 1;
+  // Bloom filter estimation: at least 8 bits for each hashed symbol.
+  return std::max<unsigned>((1u << Log2_32_Ceil(NumHashed)) / sizeof(Elf_Off),
+                            1);
+}
+
+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;
+  Buf += sizeof(Elf_Word) * 4;
+  writeBloomFilter(Buf);
+  Buf += sizeof(Elf_Off) * MaskWords;
+  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;
+}
+
+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())
+    Masks[(Item.GnuHash / C) & (MaskWords - 1)] |=
+        (static_cast<uintX_t>(1) << (Item.GnuHash % C)) |
+        (static_cast<uintX_t>(1) << ((Item.GnuHash >> Shift2) % C));
+}
+
+template <class ELFT>
+void GnuHashTableSection<ELFT>::writeHashTable(uint8_t *Buf) {
+  Elf_Word *Buckets = reinterpret_cast<Elf_Word *>(Buf);
+  Elf_Word *Values = Buckets + NBuckets;
+
+  int PrevBucketIndex = -1;
+  int PrevValueIndex = -1;
+  int ValueIndex = 0;
+  for (const typename SymbolTableSection<ELFT>::SymbolData &Item :
+       Out<ELFT>::DynSymTab->getGnuHashSymbols()) {
+    int BucketIndex = Item.GnuHash % NBuckets;
+    assert(PrevBucketIndex <= BucketIndex);
+    if (BucketIndex != PrevBucketIndex) {
+      if (PrevValueIndex >= 0)
+        Values[PrevValueIndex] |= 1;
+      Buckets[BucketIndex] = ValueIndex + 1;
+      PrevBucketIndex = BucketIndex;
+    }
+    Values[ValueIndex] = Item.GnuHash & ~1;
+    PrevValueIndex = ValueIndex++;
+  }
+  if (PrevValueIndex >= 0)
+    Values[PrevValueIndex] |= 1;
+}
+
 template <class ELFT>
 DynamicSection<ELFT>::DynamicSection(SymbolTable<ELFT> &SymTab)
     : OutputSectionBase<ELFT>(".dynamic", llvm::ELF::SHT_DYNAMIC,
@@ -321,7 +430,10 @@ template <class ELFT> void DynamicSection<ELFT>::finalize() {
   ++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
@@ -397,7 +509,10 @@ template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *Buf) {
   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())
 
@@ -659,6 +774,11 @@ bool lld::elf2::includeInDynamicSymtab(const SymbolBody &B) {
   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,
@@ -678,6 +798,12 @@ bool lld::elf2::shouldKeepInSymtab(const ObjectFile<ELFT> &File,
 }
 
 template <class ELFT>
+SymbolTableSection<ELFT>::SymbolData::SymbolData(SymbolBody *Body,
+                                                 bool GnuHashed)
+    : Body(Body), GnuHashed(GnuHashed),
+      GnuHash(GnuHashed ? hashGnu(Body->getName()) : 0) {}
+
+template <class ELFT>
 SymbolTableSection<ELFT>::SymbolTableSection(
     SymbolTable<ELFT> &Table, StringTableSection<ELFT> &StrTabSec)
     : OutputSectionBase<ELFT>(
@@ -696,14 +822,50 @@ template <class ELFT> void SymbolTableSection<ELFT>::finalize() {
   this->Header.sh_size = getNumSymbols() * sizeof(Elf_Sym);
   this->Header.sh_link = StrTabSec.SectionIndex;
   this->Header.sh_info = NumLocals + 1;
+
+  const unsigned NBuckets =
+      NumGnuHashed ? GnuHashTableSection<ELFT>::calcNBuckets(NumGnuHashed) : 0;
+  std::stable_sort(
+      Symbols.begin(), Symbols.end(),
+      [NBuckets](const SymbolData &L, const SymbolData &R) -> bool {
+        const bool LLocal = getSymbolBinding(L.Body) == STB_LOCAL;
+        const bool RLocal = getSymbolBinding(R.Body) == STB_LOCAL;
+        if (LLocal || RLocal)
+          return !RLocal;
+        if (!L.GnuHashed || !R.GnuHashed)
+          return R.GnuHashed;
+        return L.GnuHash % NBuckets < R.GnuHash % NBuckets;
+      });
+  if (StrTabSec.isDynamic()) {
+    // All symbols which belong to Gnu Hash section
+    // should be placed after not hashed symbols.
+    assert(std::is_sorted(Symbols.begin(), Symbols.end(),
+                          [](const SymbolData &L, const SymbolData &R) {
+                            return !L.GnuHashed && R.GnuHashed;
+                          }));
+    unsigned Index = 0;
+    for (const SymbolData &Item : Symbols) {
+      Item.Body->setDynamicSymbolTableIndex(++Index);
+    }
+  }
 }
 
 template <class ELFT>
-void SymbolTableSection<ELFT>::addSymbol(StringRef Name, bool isLocal) {
+void SymbolTableSection<ELFT>::addLocalSymbol(StringRef Name) {
   StrTabSec.add(Name);
   ++NumVisible;
-  if (isLocal)
-    ++NumLocals;
+  ++NumLocals;
+}
+
+template <class ELFT>
+void SymbolTableSection<ELFT>::addSymbol(SymbolBody *Body) {
+  StrTabSec.add(Body->getName());
+  const bool GnuHashed = StrTabSec.isDynamic() && Out<ELFT>::GnuHashTab &&
+                         includeInGnuHashTable(*Body);
+  Symbols.emplace_back(Body, GnuHashed);
+  ++NumVisible;
+  if (GnuHashed)
+    ++NumGnuHashed;
 }
 
 template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
@@ -754,18 +916,10 @@ template <class ELFT>
 void SymbolTableSection<ELFT>::writeGlobalSymbols(uint8_t *Buf) {
   // Write the internal symbol table contents to the output symbol table
   // pointed by Buf.
-  uint8_t *Start = Buf;
-  for (const std::pair<StringRef, Symbol *> &P : Table.getSymbols()) {
-    StringRef Name = P.first;
-    Symbol *Sym = P.second;
-    SymbolBody *Body = Sym->Body;
-    if (!includeInSymtab<ELFT>(*Body))
-      continue;
-    if (StrTabSec.isDynamic() && !includeInDynamicSymtab(*Body))
-      continue;
-
-    auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
-    Buf += sizeof(*ESym);
+  auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
+  for (const SymbolData &Item : Symbols) {
+    SymbolBody *Body = Item.Body;
+    StringRef Name = Body->getName();
 
     ESym->st_name = StrTabSec.getFileOff(Name);
 
@@ -789,19 +943,16 @@ void SymbolTableSection<ELFT>::writeGlobalSymbols(uint8_t *Buf) {
       break;
     }
 
-    unsigned char Binding = Body->isWeak() ? STB_WEAK : STB_GLOBAL;
+    unsigned char Binding = getSymbolBinding(Body);
     unsigned char Type = STT_NOTYPE;
     uintX_t Size = 0;
     if (const auto *EBody = dyn_cast<ELFSymbolBody<ELFT>>(Body)) {
       const Elf_Sym &InputSym = EBody->Sym;
-      Binding = InputSym.getBinding();
       Type = InputSym.getType();
       Size = InputSym.st_size;
     }
 
     unsigned char Visibility = Body->getMostConstrainingVisibility();
-    if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
-      Binding = STB_LOCAL;
 
     ESym->setBindingAndType(Binding, Type);
     ESym->st_size = Size;
@@ -815,13 +966,31 @@ void SymbolTableSection<ELFT>::writeGlobalSymbols(uint8_t *Buf) {
       ESym->st_shndx = SHN_ABS;
     else if (OutSec)
       ESym->st_shndx = OutSec->SectionIndex;
+
+    ++ESym;
   }
-  if (!StrTabSec.isDynamic())
-    std::stable_sort(
-        reinterpret_cast<Elf_Sym *>(Start), reinterpret_cast<Elf_Sym *>(Buf),
-        [](const Elf_Sym &A, const Elf_Sym &B) -> bool {
-          return A.getBinding() == STB_LOCAL && B.getBinding() != STB_LOCAL;
-        });
+}
+
+template <class ELFT>
+unsigned char SymbolTableSection<ELFT>::getSymbolBinding(SymbolBody *Body) {
+  unsigned char Visibility = Body->getMostConstrainingVisibility();
+  if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
+    return STB_LOCAL;
+  if (const auto *EBody = dyn_cast<ELFSymbolBody<ELFT>>(Body))
+    return EBody->Sym.getBinding();
+  return Body->isWeak() ? STB_WEAK : STB_GLOBAL;
+}
+
+template <class ELFT>
+ArrayRef<typename SymbolTableSection<ELFT>::SymbolData>
+SymbolTableSection<ELFT>::getSymbols() const {
+  return Symbols;
+}
+
+template <class ELFT>
+ArrayRef<typename SymbolTableSection<ELFT>::SymbolData>
+SymbolTableSection<ELFT>::getGnuHashSymbols() const {
+  return getSymbols().slice(Symbols.size() - NumGnuHashed);
 }
 
 namespace lld {
@@ -856,6 +1025,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>;
diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h
index 5b7b3ba..844f2ee 100644
--- a/ELF/OutputSections.h
+++ b/ELF/OutputSections.h
@@ -57,6 +57,7 @@ bool canBePreempted(const SymbolBody *Body, bool NeedsGot);
 template <class ELFT> bool includeInSymtab(const SymbolBody &B);
 
 bool includeInDynamicSymtab(const SymbolBody &B);
+bool includeInGnuHashTable(const SymbolBody &B);
 
 template <class ELFT>
 bool shouldKeepInSymtab(
@@ -172,18 +173,33 @@ public:
 
   void finalize() override;
   void writeTo(uint8_t *Buf) override;
-  void addSymbol(StringRef Name, bool isLocal = false);
+  void addLocalSymbol(StringRef Name);
+  void addSymbol(SymbolBody *Body);
   StringTableSection<ELFT> &getStrTabSec() const { return StrTabSec; }
   unsigned getNumSymbols() const { return NumVisible + 1; }
+  unsigned getNumGnuHashSymbols() const { return NumGnuHashed; }
+
+  struct SymbolData {
+    SymbolData(SymbolBody *Body, bool GnuHashed);
+    SymbolBody *Body;
+    bool GnuHashed;
+    uint32_t GnuHash;
+  };
+  ArrayRef<SymbolData> getSymbols() const;
+  ArrayRef<SymbolData> getGnuHashSymbols() const;
 
 private:
   void writeLocalSymbols(uint8_t *&Buf);
   void writeGlobalSymbols(uint8_t *Buf);
 
+  static unsigned char getSymbolBinding(SymbolBody *Body);
+
   SymbolTable<ELFT> &Table;
   StringTableSection<ELFT> &StrTabSec;
+  std::vector<SymbolData> Symbols;
   unsigned NumVisible = 0;
   unsigned NumLocals = 0;
+  unsigned NumGnuHashed = 0;
 };
 
 template <class ELFT>
@@ -273,12 +289,35 @@ class HashTableSection final : public OutputSectionBase<ELFT> {
 
 public:
   HashTableSection();
-  void addSymbol(SymbolBody *S);
+  void finalize() override;
+  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:
-  std::vector<uint32_t> Hashes;
+  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>
@@ -310,6 +349,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;
@@ -328,6 +368,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;
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 6178a94..b6c7b94 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -114,7 +114,11 @@ template <class ELFT> void lld::elf2::writeResult(SymbolTable<ELFT> *Symtab) {
   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;
@@ -282,7 +286,7 @@ template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
       StringRef SymName = *SymNameOrErr;
       if (!shouldKeepInSymtab<ELFT>(*F, SymName, Sym))
         continue;
-      Out<ELFT>::SymTab->addSymbol(SymName, true);
+      Out<ELFT>::SymTab->addLocalSymbol(SymName);
     }
   }
 }
@@ -498,7 +502,6 @@ template <class ELFT> void Writer<ELFT>::createSections() {
   // FIXME: Try to avoid the extra walk over all global symbols.
   std::vector<DefinedCommon<ELFT> *> CommonSymbols;
   for (auto &P : Symtab.getSymbols()) {
-    StringRef Name = P.first;
     SymbolBody *Body = P.second->Body;
     if (auto *U = dyn_cast<Undefined<ELFT>>(Body))
       if (!U->isWeak() && !U->canKeepUndefined())
@@ -508,10 +511,11 @@ template <class ELFT> void Writer<ELFT>::createSections() {
       CommonSymbols.push_back(C);
     if (!includeInSymtab<ELFT>(*Body))
       continue;
-    Out<ELFT>::SymTab->addSymbol(Name);
+    Out<ELFT>::SymTab->addSymbol(Body);
 
-    if (isOutputDynamic() && includeInDynamicSymtab(*Body))
-      Out<ELFT>::HashTab->addSymbol(Body);
+    if (isOutputDynamic() && includeInDynamicSymtab(*Body)) {
+      Out<ELFT>::DynSymTab->addSymbol(Body);
+    }
   }
   addCommonSymbols(CommonSymbols);
 
@@ -522,7 +526,10 @@ template <class ELFT> void Writer<ELFT>::createSections() {
   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())
diff --git a/test/elf2/gnu-hash-table.s b/test/elf2/gnu-hash-table.s
new file mode 100644
index 0000000..197c045
--- /dev/null
+++ b/test/elf2/gnu-hash-table.s
@@ -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 (0x1)
+# EMPTY-NEXT:     Type: None (0x0)
+# EMPTY-NEXT:     Other: 0
+# EMPTY-NEXT:     Section: Undefined (0x0)
+# 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 (0x6FFFFFF6)
+# I386-NEXT:     Flags [ (0x2)
+# I386-NEXT:       SHF_ALLOC (0x2)
+# 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 (0x0)
+# I386:          Section: Undefined (0x0)
+# I386:        }
+# I386:        Symbol {
+# I386:          Name: baz@
+# I386:          Binding: Global (0x1)
+# I386:          Section: Undefined (0x0)
+# I386:        }
+# I386:        Symbol {
+# I386:          Name: bar@
+# I386:          Binding: Global (0x1)
+# I386:          Section: .text
+# I386:        }
+# I386:        Symbol {
+# I386:          Name: foo@
+# I386:          Binding: Global (0x1)
+# 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: [1]
+# 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: [1]
+# 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: [1]
+# PPC64-NEXT:   Values: [0xB8860BA, 0xB887389]
+# PPC64-NEXT: }
+
+.globl foo,bar,baz
+foo:
+bar:


More information about the llvm-commits mailing list