[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