ELF: New symbol table design.

This patch implements a new design for the symbol table that stores
SymbolBodies within a memory region of the Symbol object. Symbols are mutated
by constructing SymbolBodies in place over existing SymbolBodies, rather
than by mutating pointers. As mentioned in the initial proposal [1], this
memory layout helps reduce the cache miss rate by improving memory locality.

Performance numbers:

           old(s) new(s)
Without debug info:
chrome      7.178  6.432 (-11.5%)
LLVMgold.so 0.505  0.502 (-0.5%)
clang       0.954  0.827 (-15.4%)
llvm-as     0.052  0.045 (-15.5%)
With debug info:
scylla      5.695  5.613 (-1.5%)
clang      14.396 14.143 (-1.8%)

Performance counter results show that the fewer required indirections is
indeed the cause of the improved performance. For example, when linking
chrome, stalled cycles decreases from 14,556,444,002 to 12,959,238,310, and
instructions per cycle increases from 0.78 to 0.83. We are also executing
many fewer instructions (15,516,401,933 down to 15,002,434,310), probably
because we spend less time allocating SymbolBodies.

The new mechanism by which symbols are added to the symbol table is by calling
add* functions on the SymbolTable.

In this patch, I handle local symbols by storing them inside "unparented"
SymbolBodies. This is suboptimal, but if we do want to try to avoid allocating
these SymbolBodies, we can probably do that separately.

I also removed a few members from the SymbolBody class that were only being
used to pass information from the input file to the symbol table.

This patch implements the new design for the ELF linker only. I intend to
prepare a similar patch for the COFF linker.

[1] http://lists.llvm.org/pipermail/llvm-dev/2016-April/098832.html

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

llvm-svn: 268178
This commit is contained in:
Peter Collingbourne 2016-05-01 04:55:03 +00:00
parent f2f00fb11a
commit 4f9527065c
14 changed files with 633 additions and 505 deletions

View File

@ -447,6 +447,7 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
// all linker scripts have already been parsed.
template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
SymbolTable<ELFT> Symtab;
elf::Symtab<ELFT>::X = &Symtab;
std::unique_ptr<TargetInfo> TI(createTarget());
Target = TI.get();
@ -468,7 +469,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
if (!Config->Entry.empty()) {
StringRef S = Config->Entry;
if (S.getAsInteger(0, Config->EntryAddr))
Config->EntrySym = Symtab.addUndefined(S)->Backref;
Config->EntrySym = Symtab.addUndefined(S);
}
for (std::unique_ptr<InputFile> &F : Files)

View File

@ -11,6 +11,7 @@
#include "Driver.h"
#include "Error.h"
#include "InputSection.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/Analysis.h"
@ -330,11 +331,14 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
switch (Sym->st_shndx) {
case SHN_UNDEF:
return new (Alloc) Undefined(Name, Binding, Sym->st_other, Sym->getType(),
/*IsBitcode*/ false);
return Symtab<ELFT>::X
->addUndefined(Name, Binding, Sym->st_other, Sym->getType(), this)
->body();
case SHN_COMMON:
return new (Alloc) DefinedCommon(Name, Sym->st_size, Sym->st_value, Binding,
Sym->st_other, Sym->getType());
return Symtab<ELFT>::X
->addCommon(Name, Sym->st_size, Sym->st_value, Binding, Sym->st_other,
Sym->getType(), this)
->body();
}
switch (Binding) {
@ -344,22 +348,19 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
case STB_WEAK:
case STB_GNU_UNIQUE:
if (Sec == &InputSection<ELFT>::Discarded)
return new (Alloc) Undefined(Name, Binding, Sym->st_other, Sym->getType(),
/*IsBitcode*/ false);
return new (Alloc) DefinedRegular<ELFT>(Name, *Sym, Sec);
return Symtab<ELFT>::X
->addUndefined(Name, Binding, Sym->st_other, Sym->getType(), this)
->body();
return Symtab<ELFT>::X->addRegular(Name, *Sym, Sec)->body();
}
}
void ArchiveFile::parse() {
template <class ELFT> void ArchiveFile::parse() {
File = check(Archive::create(MB), "failed to parse archive");
// Allocate a buffer for Lazy objects.
size_t NumSyms = File->getNumberOfSymbols();
LazySymbols.reserve(NumSyms);
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &Sym : File->symbols())
LazySymbols.emplace_back(this, Sym);
Symtab<ELFT>::X->addLazyArchive(this, Sym);
}
// Returns a buffer pointing to a member file containing a given symbol.
@ -487,8 +488,6 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
std::vector<const Elf_Verdef *> Verdefs = parseVerdefs(Versym);
Elf_Sym_Range Syms = this->getElfSymbols(true);
uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end());
SymbolBodies.reserve(NumSymbols);
for (const Elf_Sym &Sym : Syms) {
unsigned VersymIndex = 0;
if (Versym) {
@ -507,16 +506,12 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
if (VersymIndex == 0 || (VersymIndex & VERSYM_HIDDEN))
continue;
}
SymbolBodies.emplace_back(this, Name, Sym, Verdefs[VersymIndex]);
Symtab<ELFT>::X->addShared(this, Name, Sym, Verdefs[VersymIndex]);
}
}
BitcodeFile::BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {}
bool BitcodeFile::classof(const InputFile *F) {
return F->kind() == BitcodeKind;
}
static uint8_t getGvVisibility(const GlobalValue *GV) {
switch (GV->getVisibility()) {
case GlobalValue::DefaultVisibility:
@ -529,21 +524,30 @@ static uint8_t getGvVisibility(const GlobalValue *GV) {
llvm_unreachable("unknown visibility");
}
SymbolBody *
BitcodeFile::createBody(const DenseSet<const Comdat *> &KeptComdats,
const IRObjectFile &Obj,
const BasicSymbolRef &Sym,
const GlobalValue *GV) {
template <class ELFT>
Symbol *BitcodeFile::createSymbol(const DenseSet<const Comdat *> &KeptComdats,
const IRObjectFile &Obj,
const BasicSymbolRef &Sym) {
const GlobalValue *GV = Obj.getSymbolGV(Sym.getRawDataRefImpl());
SmallString<64> Name;
raw_svector_ostream OS(Name);
Sym.printName(OS);
StringRef NameRef = Saver.save(StringRef(Name));
SymbolBody *Body;
uint32_t Flags = Sym.getFlags();
bool IsWeak = Flags & BasicSymbolRef::SF_Weak;
uint32_t Binding = IsWeak ? STB_WEAK : STB_GLOBAL;
uint8_t Type = STT_NOTYPE;
bool CanOmitFromDynSym = false;
// FIXME: Expose a thread-local flag for module asm symbols.
if (GV) {
if (GV->isThreadLocal())
Type = STT_TLS;
CanOmitFromDynSym = canBeOmittedFromSymbolTable(GV);
}
uint8_t Visibility;
if (GV)
Visibility = getGvVisibility(GV);
@ -554,46 +558,28 @@ BitcodeFile::createBody(const DenseSet<const Comdat *> &KeptComdats,
if (GV)
if (const Comdat *C = GV->getComdat())
if (!KeptComdats.count(C)) {
Body = new (Alloc) Undefined(NameRef, Binding, Visibility, /*Type*/ 0,
/*IsBitcode*/ true);
return Body;
}
if (!KeptComdats.count(C))
return Symtab<ELFT>::X->addUndefined(NameRef, Binding, Visibility, Type,
this);
const Module &M = Obj.getModule();
if (Flags & BasicSymbolRef::SF_Undefined)
return new (Alloc) Undefined(NameRef, Binding, Visibility, /*Type*/ 0,
/*IsBitcode*/ true);
return Symtab<ELFT>::X->addUndefined(NameRef, Binding, Visibility, Type,
this);
if (Flags & BasicSymbolRef::SF_Common) {
// FIXME: Set SF_Common flag correctly for module asm symbols, and expose
// size and alignment.
assert(GV);
const DataLayout &DL = M.getDataLayout();
uint64_t Size = DL.getTypeAllocSize(GV->getValueType());
return new (Alloc) DefinedCommon(NameRef, Size, GV->getAlignment(), Binding,
Visibility, /*Type*/ 0);
return Symtab<ELFT>::X->addCommon(NameRef, Size, GV->getAlignment(),
Binding, Visibility, STT_OBJECT, this);
}
return new (Alloc) DefinedBitcode(NameRef, IsWeak, Visibility);
return Symtab<ELFT>::X->addBitcode(NameRef, IsWeak, Visibility, Type,
CanOmitFromDynSym, this);
}
SymbolBody *
BitcodeFile::createSymbolBody(const DenseSet<const Comdat *> &KeptComdats,
const IRObjectFile &Obj,
const BasicSymbolRef &Sym) {
const GlobalValue *GV = Obj.getSymbolGV(Sym.getRawDataRefImpl());
SymbolBody *Body = createBody(KeptComdats, Obj, Sym, GV);
// FIXME: Expose a thread-local flag for module asm symbols.
if (GV) {
if (GV->isThreadLocal())
Body->Type = STT_TLS;
Body->CanOmitFromDynSym = canBeOmittedFromSymbolTable(GV);
}
return Body;
}
bool BitcodeFile::shouldSkip(const BasicSymbolRef &Sym) {
uint32_t Flags = Sym.getFlags();
bool BitcodeFile::shouldSkip(uint32_t Flags) {
if (!(Flags & BasicSymbolRef::SF_Global))
return true;
if (Flags & BasicSymbolRef::SF_FormatSpecific)
@ -601,6 +587,7 @@ bool BitcodeFile::shouldSkip(const BasicSymbolRef &Sym) {
return false;
}
template <class ELFT>
void BitcodeFile::parse(DenseSet<StringRef> &ComdatGroups) {
Obj = check(IRObjectFile::create(MB, Driver->Context));
const Module &M = Obj->getModule();
@ -613,8 +600,8 @@ void BitcodeFile::parse(DenseSet<StringRef> &ComdatGroups) {
}
for (const BasicSymbolRef &Sym : Obj->symbols())
if (!shouldSkip(Sym))
SymbolBodies.push_back(createSymbolBody(KeptComdats, *Obj, Sym));
if (!shouldSkip(Sym.getFlags()))
Symbols.push_back(createSymbol<ELFT>(KeptComdats, *Obj, Sym));
}
template <typename T>
@ -675,9 +662,10 @@ std::unique_ptr<InputFile> elf::createSharedFile(MemoryBufferRef MB) {
return createELFFile<SharedFile>(MB);
}
template <class ELFT>
void LazyObjectFile::parse() {
for (StringRef Sym : getSymbols())
LazySymbols.emplace_back(Sym, this->MB);
Symtab<ELFT>::X->addLazyObject(Sym, this->MB);
}
template <class ELFT> std::vector<StringRef> LazyObjectFile::getElfSymbols() {
@ -707,9 +695,10 @@ std::vector<StringRef> LazyObjectFile::getBitcodeSymbols() {
check(IRObjectFile::create(this->MB, Context));
std::vector<StringRef> V;
for (const BasicSymbolRef &Sym : Obj->symbols()) {
if (BitcodeFile::shouldSkip(Sym))
uint32_t Flags = Sym.getFlags();
if (BitcodeFile::shouldSkip(Flags))
continue;
if (Sym.getFlags() & BasicSymbolRef::SF_Undefined)
if (Flags & BasicSymbolRef::SF_Undefined)
continue;
SmallString<64> Name;
raw_svector_ostream OS(Name);
@ -737,6 +726,25 @@ std::vector<StringRef> LazyObjectFile::getSymbols() {
return getElfSymbols<ELF64BE>();
}
template void ArchiveFile::parse<ELF32LE>();
template void ArchiveFile::parse<ELF32BE>();
template void ArchiveFile::parse<ELF64LE>();
template void ArchiveFile::parse<ELF64BE>();
template void
BitcodeFile::parse<ELF32LE>(llvm::DenseSet<StringRef> &ComdatGroups);
template void
BitcodeFile::parse<ELF32BE>(llvm::DenseSet<StringRef> &ComdatGroups);
template void
BitcodeFile::parse<ELF64LE>(llvm::DenseSet<StringRef> &ComdatGroups);
template void
BitcodeFile::parse<ELF64BE>(llvm::DenseSet<StringRef> &ComdatGroups);
template void LazyObjectFile::parse<ELF32LE>();
template void LazyObjectFile::parse<ELF32BE>();
template void LazyObjectFile::parse<ELF64LE>();
template void LazyObjectFile::parse<ELF64BE>();
template class elf::ELFFileBase<ELF32LE>;
template class elf::ELFFileBase<ELF32BE>;
template class elf::ELFFileBase<ELF64LE>;

View File

@ -89,13 +89,14 @@ public:
uint32_t getSectionIndex(const Elf_Sym &Sym) const;
Elf_Sym_Range getElfSymbols(bool OnlyGlobals);
protected:
llvm::object::ELFFile<ELFT> ELFObj;
const Elf_Shdr *Symtab = nullptr;
ArrayRef<Elf_Word> SymtabSHNDX;
StringRef StringTable;
void initStringTable();
Elf_Sym_Range getElfSymbols(bool OnlyGlobals);
};
// .o file.
@ -126,7 +127,7 @@ public:
InputSectionBase<ELFT> *getSection(const Elf_Sym &Sym) const;
SymbolBody &getSymbolBody(uint32_t SymbolIndex) const {
return SymbolBodies[SymbolIndex]->repl();
return *SymbolBodies[SymbolIndex];
}
template <typename RelT> SymbolBody &getRelocTargetSym(const RelT &Rel) const {
@ -183,9 +184,7 @@ public:
return F->kind() == LazyObjectKind;
}
void parse();
llvm::MutableArrayRef<LazyObject> getLazySymbols() { return LazySymbols; }
template <class ELFT> void parse();
private:
std::vector<StringRef> getSymbols();
@ -194,7 +193,6 @@ private:
llvm::BumpPtrAllocator Alloc;
llvm::StringSaver Saver{Alloc};
std::vector<LazyObject> LazySymbols;
};
// An ArchiveFile object represents a .a file.
@ -202,43 +200,36 @@ class ArchiveFile : public InputFile {
public:
explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
void parse();
template <class ELFT> void parse();
// Returns a memory buffer for a given symbol. An empty memory buffer
// is returned if we have already returned the same memory buffer.
// (So that we don't instantiate same members more than once.)
MemoryBufferRef getMember(const Archive::Symbol *Sym);
llvm::MutableArrayRef<LazyArchive> getLazySymbols() { return LazySymbols; }
private:
std::unique_ptr<Archive> File;
std::vector<LazyArchive> LazySymbols;
llvm::DenseSet<uint64_t> Seen;
};
class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef M);
static bool classof(const InputFile *F);
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
template <class ELFT>
void parse(llvm::DenseSet<StringRef> &ComdatGroups);
ArrayRef<SymbolBody *> getSymbols() { return SymbolBodies; }
static bool shouldSkip(const llvm::object::BasicSymbolRef &Sym);
ArrayRef<Symbol *> getSymbols() { return Symbols; }
static bool shouldSkip(uint32_t Flags);
std::unique_ptr<llvm::object::IRObjectFile> Obj;
private:
std::vector<SymbolBody *> SymbolBodies;
std::vector<Symbol *> Symbols;
llvm::BumpPtrAllocator Alloc;
llvm::StringSaver Saver{Alloc};
SymbolBody *
createSymbolBody(const llvm::DenseSet<const llvm::Comdat *> &KeptComdats,
const llvm::object::IRObjectFile &Obj,
const llvm::object::BasicSymbolRef &Sym);
SymbolBody *
createBody(const llvm::DenseSet<const llvm::Comdat *> &KeptComdats,
const llvm::object::IRObjectFile &Obj,
const llvm::object::BasicSymbolRef &Sym,
const llvm::GlobalValue *GV);
template <class ELFT>
Symbol *createSymbol(const llvm::DenseSet<const llvm::Comdat *> &KeptComdats,
const llvm::object::IRObjectFile &Obj,
const llvm::object::BasicSymbolRef &Sym);
};
// .so file.
@ -251,7 +242,6 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
typedef typename ELFT::Versym Elf_Versym;
typedef typename ELFT::Verdef Elf_Verdef;
std::vector<SharedSymbol<ELFT>> SymbolBodies;
std::vector<StringRef> Undefs;
StringRef SoName;
const Elf_Shdr *VersymSec = nullptr;
@ -259,9 +249,6 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
public:
StringRef getSoName() const { return SoName; }
llvm::MutableArrayRef<SharedSymbol<ELFT>> getSharedSymbols() {
return SymbolBodies;
}
const Elf_Shdr *getSection(const Elf_Sym &Sym) const;
llvm::ArrayRef<StringRef> getUndefinedSymbols() { return Undefs; }

View File

@ -76,14 +76,14 @@ static void runLTOPasses(Module &M, TargetMachine &TM) {
}
static bool shouldInternalize(const SmallPtrSet<GlobalValue *, 8> &Used,
SymbolBody &B, GlobalValue *GV) {
if (B.Backref->IsUsedInRegularObj)
Symbol *S, GlobalValue *GV) {
if (S->IsUsedInRegularObj)
return false;
if (Used.count(GV))
return false;
return !B.Backref->includeInDynsym();
return !S->includeInDynsym();
}
BitcodeCompiler::BitcodeCompiler()
@ -94,7 +94,7 @@ void BitcodeCompiler::add(BitcodeFile &F) {
std::unique_ptr<IRObjectFile> Obj = std::move(F.Obj);
std::vector<GlobalValue *> Keep;
unsigned BodyIndex = 0;
ArrayRef<SymbolBody *> Bodies = F.getSymbols();
ArrayRef<Symbol *> Syms = F.getSymbols();
Module &M = Obj->getModule();
if (M.getDataLayoutStr().empty())
@ -106,19 +106,30 @@ void BitcodeCompiler::add(BitcodeFile &F) {
SmallPtrSet<GlobalValue *, 8> Used;
collectUsedGlobalVariables(M, Used, /* CompilerUsed */ false);
// This function is called if we know that the combined LTO object will
// provide a definition of a symbol. It undefines the symbol so that the
// definition in the combined LTO object will replace it when parsed.
auto Undefine = [](Symbol *S) {
replaceBody<Undefined>(S, S->body()->getName(), STV_DEFAULT, 0);
};
for (const BasicSymbolRef &Sym : Obj->symbols()) {
uint32_t Flags = Sym.getFlags();
GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl());
// Ignore module asm symbols.
if (!GV)
continue;
if (GV->hasAppendingLinkage()) {
if (GV && GV->hasAppendingLinkage())
Keep.push_back(GV);
if (BitcodeFile::shouldSkip(Flags))
continue;
Symbol *S = Syms[BodyIndex++];
if (Flags & BasicSymbolRef::SF_Undefined)
continue;
if (!GV) {
// Module asm symbol.
Undefine(S);
continue;
}
if (BitcodeFile::shouldSkip(Sym))
continue;
SymbolBody *B = Bodies[BodyIndex++];
if (!B || &B->repl() != B || !isa<DefinedBitcode>(B))
auto *B = dyn_cast<DefinedBitcode>(S->body());
if (!B || B->File != &F)
continue;
switch (GV->getLinkage()) {
default:
@ -136,8 +147,10 @@ void BitcodeCompiler::add(BitcodeFile &F) {
// we imported the symbols and satisfied undefined references
// to it. We can't just change linkage here because otherwise
// the IRMover will just rename the symbol.
if (shouldInternalize(Used, *B, GV))
if (shouldInternalize(Used, S, GV))
InternalizedSyms.insert(GV->getName());
else
Undefine(S);
Keep.push_back(GV);
}

View File

@ -137,15 +137,14 @@ template <class ELFT> void elf::markLive(SymbolTable<ELFT> *Symtab) {
Q.push_back(S);
};
auto MarkSymbol = [&](SymbolBody *Sym) {
if (Sym)
if (auto *D = dyn_cast<DefinedRegular<ELFT>>(Sym))
Enqueue({D->Section, D->Value});
auto MarkSymbol = [&](const SymbolBody *Sym) {
if (auto *D = dyn_cast_or_null<DefinedRegular<ELFT>>(Sym))
Enqueue({D->Section, D->Value});
};
// Add GC root symbols.
if (Config->EntrySym)
MarkSymbol(Config->EntrySym->Body);
MarkSymbol(Config->EntrySym->body());
MarkSymbol(Symtab->find(Config->Init));
MarkSymbol(Symtab->find(Config->Fini));
for (StringRef S : Config->Undefined)
@ -155,7 +154,7 @@ template <class ELFT> void elf::markLive(SymbolTable<ELFT> *Symtab) {
// file can interrupt other ELF file's symbols at runtime.
for (const Symbol *S : Symtab->getSymbols())
if (S->includeInDynsym())
MarkSymbol(S->Body);
MarkSymbol(S->body());
// Preserve special sections and those which are specified in linker
// script KEEP command.

View File

@ -144,9 +144,9 @@ template <class ELFT> void GotSection<ELFT>::addEntry(SymbolBody &Sym) {
}
template <class ELFT> bool GotSection<ELFT>::addDynTlsEntry(SymbolBody &Sym) {
if (Sym.hasGlobalDynIndex())
if (Sym.symbol()->GlobalDynIndex != -1U)
return false;
Sym.GlobalDynIndex = Entries.size();
Sym.symbol()->GlobalDynIndex = Entries.size();
// Global Dynamic TLS entries take two GOT slots.
Entries.push_back(&Sym);
Entries.push_back(nullptr);
@ -186,13 +186,13 @@ GotSection<ELFT>::getMipsLocalEntryOffset(uintX_t EntryValue) {
template <class ELFT>
typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getGlobalDynAddr(const SymbolBody &B) const {
return this->getVA() + B.GlobalDynIndex * sizeof(uintX_t);
return this->getVA() + B.symbol()->GlobalDynIndex * sizeof(uintX_t);
}
template <class ELFT>
typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getGlobalDynOffset(const SymbolBody &B) const {
return B.GlobalDynIndex * sizeof(uintX_t);
return B.symbol()->GlobalDynIndex * sizeof(uintX_t);
}
template <class ELFT>
@ -1371,7 +1371,7 @@ static bool sortMipsSymbols(const std::pair<SymbolBody *, unsigned> &L,
}
static uint8_t getSymbolBinding(SymbolBody *Body) {
Symbol *S = Body->Backref;
Symbol *S = Body->symbol();
uint8_t Visibility = S->Visibility;
if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
return STB_LOCAL;
@ -1472,7 +1472,7 @@ void SymbolTableSection<ELFT>::writeGlobalSymbols(uint8_t *Buf) {
ESym->setBindingAndType(getSymbolBinding(Body), Type);
ESym->st_size = Size;
ESym->st_name = StrOff;
ESym->setVisibility(Body->Backref->Visibility);
ESym->setVisibility(Body->symbol()->Visibility);
ESym->st_value = Body->getVA<ELFT>();
if (const OutputSectionBase<ELFT> *OutSec = getOutputSection(Body))

View File

@ -64,18 +64,14 @@ void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) {
// .a file
if (auto *F = dyn_cast<ArchiveFile>(FileP)) {
ArchiveFiles.emplace_back(cast<ArchiveFile>(File.release()));
F->parse();
for (Lazy &Sym : F->getLazySymbols())
addLazy(&Sym);
F->parse<ELFT>();
return;
}
// Lazy object file
if (auto *F = dyn_cast<LazyObjectFile>(FileP)) {
LazyObjectFiles.emplace_back(cast<LazyObjectFile>(File.release()));
F->parse();
for (Lazy &Sym : F->getLazySymbols())
addLazy(&Sym);
F->parse<ELFT>();
return;
}
@ -91,18 +87,13 @@ void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) {
SharedFiles.emplace_back(cast<SharedFile<ELFT>>(File.release()));
F->parseRest();
for (SharedSymbol<ELFT> &B : F->getSharedSymbols())
resolve(&B);
return;
}
// LLVM bitcode file
if (auto *F = dyn_cast<BitcodeFile>(FileP)) {
BitcodeFiles.emplace_back(cast<BitcodeFile>(File.release()));
F->parse(ComdatGroups);
for (SymbolBody *B : F->getSymbols())
if (B)
resolve(B);
F->parse<ELFT>(ComdatGroups);
return;
}
@ -110,8 +101,6 @@ void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) {
auto *F = cast<ObjectFile<ELFT>>(FileP);
ObjectFiles.emplace_back(cast<ObjectFile<ELFT>>(File.release()));
F->parse(ComdatGroups);
for (SymbolBody *B : F->getNonLocalSymbols())
resolve(B);
}
// This function is where all the optimizations of link-time
@ -137,41 +126,15 @@ template <class ELFT> void SymbolTable<ELFT>::addCombinedLtoObject() {
llvm::DenseSet<StringRef> DummyGroups;
Obj->parse(DummyGroups);
for (SymbolBody *Body : Obj->getNonLocalSymbols()) {
Symbol *Sym = insert(Body);
if (!Sym->Body->isUndefined() && Body->isUndefined())
continue;
Sym->Body = Body;
}
ObjectFiles.emplace_back(Obj);
}
}
// Add an undefined symbol.
template <class ELFT>
SymbolBody *SymbolTable<ELFT>::addUndefined(StringRef Name) {
auto *Sym = new (Alloc) Undefined(Name, STB_GLOBAL, STV_DEFAULT, /*Type*/ 0,
/*IsBitcode*/ false);
resolve(Sym);
return Sym;
}
template <class ELFT>
DefinedRegular<ELFT> *SymbolTable<ELFT>::addAbsolute(StringRef Name,
uint8_t Visibility) {
// Pass nullptr because absolute symbols have no corresponding input sections.
auto *Sym = new (Alloc) DefinedRegular<ELFT>(Name, STB_GLOBAL, Visibility);
resolve(Sym);
return Sym;
}
template <class ELFT>
SymbolBody *SymbolTable<ELFT>::addSynthetic(StringRef Name,
OutputSectionBase<ELFT> &Sec,
uintX_t Val) {
auto *Sym = new (Alloc) DefinedSynthetic<ELFT>(Name, Val, Sec);
resolve(Sym);
return Sym;
return cast<DefinedRegular<ELFT>>(
addRegular(Name, STB_GLOBAL, Visibility)->body());
}
// Add Name as an "ignored" symbol. An ignored symbol is a regular
@ -191,84 +154,47 @@ template <class ELFT> void SymbolTable<ELFT>::wrap(StringRef Name) {
if (!B)
return;
StringSaver Saver(Alloc);
Symbol *Sym = B->Backref;
Symbol *Real = addUndefined(Saver.save("__real_" + Name))->Backref;
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name))->Backref;
Real->Body = Sym->Body;
Sym->Body = Wrap->Body;
Symbol *Sym = B->symbol();
Symbol *Real = addUndefined(Saver.save("__real_" + Name));
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name));
// We rename symbols by replacing the old symbol's SymbolBody with the new
// symbol's SymbolBody. This causes all SymbolBody pointers referring to the
// old symbol to instead refer to the new symbol.
memcpy(Real->Body.buffer, Sym->Body.buffer, sizeof(Sym->Body));
memcpy(Sym->Body.buffer, Wrap->Body.buffer, sizeof(Wrap->Body));
}
// Returns a file from which symbol B was created.
// If B does not belong to any file, returns a nullptr.
// This function is slow, but it's okay as it is used only for error messages.
template <class ELFT> InputFile *SymbolTable<ELFT>::findFile(SymbolBody *B) {
// If this symbol has a definition, follow pointers in the symbol to its
// defining file.
if (auto *R = dyn_cast<DefinedRegular<ELFT>>(B))
if (auto *S = R->Section)
return S->getFile();
if (auto *SS = dyn_cast<SharedSymbol<ELFT>>(B))
return SS->File;
if (auto *BC = dyn_cast<DefinedBitcode>(B))
return BC->File;
// If not, we might be able to find it by searching symbol tables of files.
// This code is generally only used for undefined symbols. Note that we can't
// rely exclusively on a file search because we may find what was originally
// an undefined symbol that was later replaced with a defined symbol, and we
// want to return the file that defined the symbol.
for (const std::unique_ptr<ObjectFile<ELFT>> &F : ObjectFiles) {
ArrayRef<SymbolBody *> Syms = F->getSymbols();
if (std::find(Syms.begin(), Syms.end(), B) != Syms.end())
return F.get();
}
for (const std::unique_ptr<BitcodeFile> &F : BitcodeFiles) {
ArrayRef<SymbolBody *> Syms = F->getSymbols();
if (std::find(Syms.begin(), Syms.end(), B) != Syms.end())
ArrayRef<Symbol *> Syms = F->getSymbols();
if (std::find(Syms.begin(), Syms.end(), B->symbol()) != Syms.end())
return F.get();
}
return nullptr;
}
// Construct a string in the form of "Sym in File1 and File2".
// Used to construct an error message.
template <class ELFT>
std::string SymbolTable<ELFT>::conflictMsg(SymbolBody *Old, SymbolBody *New) {
InputFile *F1 = findFile(Old);
InputFile *F2 = findFile(New);
StringRef Sym = Old->getName();
return demangle(Sym) + " in " + getFilename(F1) + " and " + getFilename(F2);
}
// This function resolves conflicts if there's an existing symbol with
// the same name. Decisions are made based on symbol type.
template <class ELFT> void SymbolTable<ELFT>::resolve(SymbolBody *New) {
Symbol *Sym = insert(New);
if (Sym->Body == New)
return;
SymbolBody *Existing = Sym->Body;
if (auto *L = dyn_cast<Lazy>(Existing)) {
Sym->Binding = New->Binding;
if (New->isUndefined()) {
addMemberFile(New, L);
return;
}
// Found a definition for something also in an archive.
// Ignore the archive definition.
Sym->Body = New;
return;
}
if (New->isTls() != Existing->isTls()) {
error("TLS attribute mismatch for symbol: " + conflictMsg(Existing, New));
return;
}
// compare() returns -1, 0, or 1 if the lhs symbol is less preferable,
// equivalent (conflicting), or more preferable, respectively.
int Comp = Existing->compare(New);
if (Comp == 0) {
std::string S = "duplicate symbol: " + conflictMsg(Existing, New);
if (Config->AllowMultipleDefinition)
warning(S);
else
error(S);
return;
}
if (Comp < 0) {
Sym->Body = New;
if (!New->isShared())
Sym->Binding = New->Binding;
}
}
static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
if (VA == STV_DEFAULT)
return VB;
@ -277,25 +203,15 @@ static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
return std::min(VA, VB);
}
static bool shouldExport(SymbolBody *B) {
if (Config->Shared || Config->ExportDynamic) {
// Export most symbols except for those that do not need to be exported.
return !B->CanOmitFromDynSym;
}
// Make sure we preempt DSO symbols with default visibility.
return B->isShared() && B->getVisibility() == STV_DEFAULT;
}
// Find an existing symbol or create and insert a new one.
template <class ELFT> Symbol *SymbolTable<ELFT>::insert(SymbolBody *New) {
StringRef Name = New->getName();
template <class ELFT>
std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
unsigned NumSyms = SymVector.size();
auto P = Symtab.insert(std::make_pair(Name, NumSyms));
Symbol *Sym;
if (P.second) {
Sym = new (Alloc) Symbol;
Sym->Body = New;
Sym->Binding = New->isShared() ? (uint8_t)STB_GLOBAL : New->Binding;
Sym->Binding = STB_WEAK;
Sym->Visibility = STV_DEFAULT;
Sym->IsUsedInRegularObj = false;
Sym->ExportDynamic = false;
@ -304,69 +220,299 @@ template <class ELFT> Symbol *SymbolTable<ELFT>::insert(SymbolBody *New) {
} else {
Sym = SymVector[P.first->second];
}
New->Backref = Sym;
return {Sym, P.second};
}
// Merge in the new symbol's visibility. DSO symbols do not affect visibility
// in the output.
if (!New->isShared())
Sym->Visibility = getMinVisibility(Sym->Visibility, New->getVisibility());
Sym->ExportDynamic = Sym->ExportDynamic || shouldExport(New);
SymbolBody::Kind K = New->kind();
if (K == SymbolBody::DefinedRegularKind ||
K == SymbolBody::DefinedCommonKind ||
K == SymbolBody::DefinedSyntheticKind ||
(K == SymbolBody::UndefinedKind && !New->IsUndefinedBitcode))
Sym->IsUsedInRegularObj = true;
return Sym;
// Find an existing symbol or create and insert a new one, then apply the given
// attributes.
template <class ELFT>
std::pair<Symbol *, bool>
SymbolTable<ELFT>::insert(StringRef Name, uint8_t Type, uint8_t Visibility,
bool CanOmitFromDynSym, bool IsUsedInRegularObj,
InputFile *File) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
// Merge in the new symbol's visibility.
S->Visibility = getMinVisibility(S->Visibility, Visibility);
if (!CanOmitFromDynSym && (Config->Shared || Config->ExportDynamic))
S->ExportDynamic = true;
if (IsUsedInRegularObj)
S->IsUsedInRegularObj = true;
if (!WasInserted && ((Type == STT_TLS) != S->body()->isTls()))
error("TLS attribute mismatch for symbol: " +
conflictMsg(S->body(), File));
return {S, WasInserted};
}
// Construct a string in the form of "Sym in File1 and File2".
// Used to construct an error message.
template <typename ELFT>
std::string SymbolTable<ELFT>::conflictMsg(SymbolBody *Existing,
InputFile *NewFile) {
StringRef Sym = Existing->getName();
return demangle(Sym) + " in " + getFilename(findFile(Existing)) + " and " +
getFilename(NewFile);
}
template <class ELFT> Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name) {
return addUndefined(Name, STB_GLOBAL, STV_DEFAULT, /*Type*/ 0,
/*File*/ nullptr);
}
template <class ELFT>
Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, uint8_t Binding,
uint8_t StOther, uint8_t Type,
InputFile *File) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
insert(Name, Type, StOther & 3, /*CanOmitFromDynSym*/ false,
/*IsUsedInRegularObj*/ !File || !isa<BitcodeFile>(File), File);
if (WasInserted) {
S->Binding = Binding;
replaceBody<Undefined>(S, Name, StOther, Type);
return S;
}
if (Binding != STB_WEAK &&
(S->body()->isShared() || S->body()->isLazy()))
S->Binding = Binding;
if (auto *L = dyn_cast<Lazy>(S->body())) {
// An undefined weak will not fetch archive members, but we have to remember
// its type. See also comment in addLazyArchive.
if (S->isWeak())
L->Type = Type;
else if (auto F = L->getFile())
addFile(std::move(F));
}
return S;
}
// We have a new defined symbol with the specified binding. Return 1 if the new
// symbol should win, -1 if the new symbol should lose, or 0 if both symbols are
// strong defined symbols.
static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding) {
if (WasInserted)
return 1;
SymbolBody *Body = S->body();
if (Body->isLazy() || Body->isUndefined() || Body->isShared())
return 1;
if (Binding == STB_WEAK)
return -1;
if (S->isWeak())
return 1;
return 0;
}
// We have a new non-common defined symbol with the specified binding. Return 1
// if the new symbol should win, -1 if the new symbol should lose, or 0 if there
// is a conflict. If the new symbol wins, also update the binding.
static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding) {
if (int Cmp = compareDefined(S, WasInserted, Binding)) {
if (Cmp > 0)
S->Binding = Binding;
return Cmp;
}
if (isa<DefinedCommon>(S->body())) {
// Non-common symbols take precedence over common symbols.
if (Config->WarnCommon)
warning("common " + S->body()->getName() + " is overridden");
return 1;
}
return 0;
}
template <class ELFT>
Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
uint64_t Alignment, uint8_t Binding,
uint8_t StOther, uint8_t Type,
InputFile *File) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
insert(N, Type, StOther & 3, /*CanOmitFromDynSym*/ false,
/*IsUsedInRegularObj*/ true, File);
int Cmp = compareDefined(S, WasInserted, Binding);
if (Cmp > 0) {
S->Binding = Binding;
replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type);
} else if (Cmp == 0) {
auto *C = dyn_cast<DefinedCommon>(S->body());
if (!C) {
// Non-common symbols take precedence over common symbols.
if (Config->WarnCommon)
warning("common " + S->body()->getName() + " is overridden");
return S;
}
if (Config->WarnCommon)
warning("multiple common of " + S->body()->getName());
C->Size = std::max(C->Size, Size);
C->Alignment = std::max(C->Alignment, Alignment);
}
return S;
}
template <class ELFT>
void SymbolTable<ELFT>::reportDuplicate(SymbolBody *Existing,
InputFile *NewFile) {
std::string Msg = "duplicate symbol: " + conflictMsg(Existing, NewFile);
if (Config->AllowMultipleDefinition)
warning(Msg);
else
error(Msg);
}
template <typename ELFT>
Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, const Elf_Sym &Sym,
InputSectionBase<ELFT> *Section) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
insert(Name, Sym.getType(), Sym.getVisibility(),
/*CanOmitFromDynSym*/ false, /*IsUsedInRegularObj*/ true,
Section ? Section->getFile() : nullptr);
int Cmp = compareDefinedNonCommon(S, WasInserted, Sym.getBinding());
if (Cmp > 0)
replaceBody<DefinedRegular<ELFT>>(S, Name, Sym, Section);
else if (Cmp == 0)
reportDuplicate(S->body(), Section->getFile());
return S;
}
template <typename ELFT>
Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t Binding,
uint8_t StOther) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
insert(Name, STT_NOTYPE, StOther & 3, /*CanOmitFromDynSym*/ false,
/*IsUsedInRegularObj*/ true, nullptr);
int Cmp = compareDefinedNonCommon(S, WasInserted, Binding);
if (Cmp > 0)
replaceBody<DefinedRegular<ELFT>>(S, Name, StOther);
else if (Cmp == 0)
reportDuplicate(S->body(), nullptr);
return S;
}
template <typename ELFT>
Symbol *SymbolTable<ELFT>::addSynthetic(StringRef N,
OutputSectionBase<ELFT> &Section,
uintX_t Value) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
insert(N, STT_NOTYPE, STV_HIDDEN, /*CanOmitFromDynSym*/ false,
/*IsUsedInRegularObj*/ true, nullptr);
int Cmp = compareDefinedNonCommon(S, WasInserted, STB_GLOBAL);
if (Cmp > 0)
replaceBody<DefinedSynthetic<ELFT>>(S, N, Value, Section);
else if (Cmp == 0)
reportDuplicate(S->body(), nullptr);
return S;
}
template <typename ELFT>
void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *F, StringRef Name,
const Elf_Sym &Sym,
const typename ELFT::Verdef *Verdef) {
// DSO symbols do not affect visibility in the output, so we pass STV_DEFAULT
// as the visibility, which will leave the visibility in the symbol table
// unchanged.
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
insert(Name, Sym.getType(), STV_DEFAULT, /*CanOmitFromDynSym*/ true,
/*IsUsedInRegularObj*/ false, F);
// Make sure we preempt DSO symbols with default visibility.
if (Sym.getVisibility() == STV_DEFAULT)
S->ExportDynamic = true;
if (WasInserted || isa<Undefined>(S->body()))
replaceBody<SharedSymbol<ELFT>>(S, F, Name, Sym, Verdef);
}
template <class ELFT>
Symbol *SymbolTable<ELFT>::addBitcode(StringRef Name, bool IsWeak,
uint8_t StOther, uint8_t Type,
bool CanOmitFromDynSym, BitcodeFile *F) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name, Type, StOther & 3, CanOmitFromDynSym,
/*IsUsedInRegularObj*/ false, F);
int Cmp =
compareDefinedNonCommon(S, WasInserted, IsWeak ? STB_WEAK : STB_GLOBAL);
if (Cmp > 0)
replaceBody<DefinedBitcode>(S, Name, StOther, Type, F);
else if (Cmp == 0)
reportDuplicate(S->body(), F);
return S;
}
template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
auto It = Symtab.find(Name);
if (It == Symtab.end())
return nullptr;
return SymVector[It->second]->Body;
}
template <class ELFT> void SymbolTable<ELFT>::addLazy(Lazy *L) {
Symbol *Sym = insert(L);
SymbolBody *Cur = Sym->Body;
if (Cur == L)
return;
if (Cur->isUndefined()) {
Sym->Body = L;
addMemberFile(Cur, L);
}
return SymVector[It->second]->body();
}
template <class ELFT>
void SymbolTable<ELFT>::addMemberFile(SymbolBody *Undef, Lazy *L) {
// Weak undefined symbols should not fetch members from archives.
// If we were to keep old symbol we would not know that an archive member was
// available if a strong undefined symbol shows up afterwards in the link.
// If a strong undefined symbol never shows up, this lazy symbol will
// get to the end of the link and must be treated as the weak undefined one.
// We already marked this symbol as used when we added it to the symbol table,
// but we also need to preserve its binding and type.
if (Undef->isWeak()) {
// FIXME: Consider moving these members to Symbol.
L->Type = Undef->Type;
void SymbolTable<ELFT>::addLazyArchive(
ArchiveFile *F, const llvm::object::Archive::Symbol Sym) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Sym.getName());
if (WasInserted) {
replaceBody<LazyArchive>(S, F, Sym, STT_NOTYPE);
return;
}
if (!S->body()->isUndefined())
return;
// Fetch a member file that has the definition for L.
// getMember returns nullptr if the member was already read from the library.
if (std::unique_ptr<InputFile> File = L->getFile())
addFile(std::move(File));
// Weak undefined symbols should not fetch members from archives. If we were
// to keep old symbol we would not know that an archive member was available
// if a strong undefined symbol shows up afterwards in the link. If a strong
// undefined symbol never shows up, this lazy symbol will get to the end of
// the link and must be treated as the weak undefined one. We already marked
// this symbol as used when we added it to the symbol table, but we also need
// to preserve its type. FIXME: Move the Type field to Symbol.
if (S->isWeak()) {
replaceBody<LazyArchive>(S, F, Sym, S->body()->Type);
return;
}
MemoryBufferRef MBRef = F->getMember(&Sym);
if (!MBRef.getBuffer().empty())
addFile(createObjectFile(MBRef, F->getName()));
}
template <class ELFT>
void SymbolTable<ELFT>::addLazyObject(StringRef Name, MemoryBufferRef MBRef) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted) {
replaceBody<LazyObject>(S, Name, MBRef, STT_NOTYPE);
return;
}
if (!S->body()->isUndefined())
return;
// See comment for addLazyArchive above.
if (S->isWeak())
replaceBody<LazyObject>(S, Name, MBRef, S->body()->Type);
else
addFile(createObjectFile(MBRef));
}
// Process undefined (-u) flags by loading lazy symbols named by those flags.
template <class ELFT>
void SymbolTable<ELFT>::scanUndefinedFlags() {
template <class ELFT> void SymbolTable<ELFT>::scanUndefinedFlags() {
for (StringRef S : Config->Undefined)
if (SymbolBody *Sym = find(S))
if (auto *L = dyn_cast<Lazy>(Sym))
if (std::unique_ptr<InputFile> File = L->getFile())
addFile(std::move(File));
if (auto *L = dyn_cast_or_null<Lazy>(find(S)))
if (std::unique_ptr<InputFile> File = L->getFile())
addFile(std::move(File));
}
// This function takes care of the case in which shared libraries depend on
@ -381,7 +527,7 @@ template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
for (StringRef U : File->getUndefinedSymbols())
if (SymbolBody *Sym = find(U))
if (Sym->isDefined())
Sym->Backref->ExportDynamic = true;
Sym->symbol()->ExportDynamic = true;
}
// This function process the dynamic list option by marking all the symbols
@ -389,7 +535,7 @@ template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
template <class ELFT> void SymbolTable<ELFT>::scanDynamicList() {
for (StringRef S : Config->DynamicList)
if (SymbolBody *B = find(S))
B->Backref->ExportDynamic = true;
B->symbol()->ExportDynamic = true;
}
// This function processes the --version-script option by marking all global
@ -398,7 +544,7 @@ template <class ELFT> void SymbolTable<ELFT>::scanDynamicList() {
template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
for (StringRef S : Config->VersionScriptGlobals)
if (SymbolBody *B = find(S))
B->Backref->VersionScriptGlobal = true;
B->symbol()->VersionScriptGlobal = true;
}
template class elf::SymbolTable<ELF32LE>;

View File

@ -31,7 +31,9 @@ typedef llvm::CachedHash<StringRef> SymName;
// conflicts. For example, obviously, a defined symbol is better than
// an undefined symbol. Or, if there's a conflict between a lazy and a
// undefined, it'll read an archive member to read a real definition
// to replace the lazy symbol. The logic is implemented in resolve().
// to replace the lazy symbol. The logic is implemented in the
// add*() functions, which are called by input files as they are parsed. There
// is one add* function per symbol type.
template <class ELFT> class SymbolTable {
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::uint uintX_t;
@ -50,14 +52,32 @@ public:
return SharedFiles;
}
SymbolBody *addUndefined(StringRef Name);
DefinedRegular<ELFT> *addAbsolute(StringRef Name,
uint8_t Visibility = llvm::ELF::STV_HIDDEN);
SymbolBody *addSynthetic(StringRef Name, OutputSectionBase<ELFT> &Section,
uintX_t Value);
DefinedRegular<ELFT> *addIgnored(StringRef Name,
uint8_t Visibility = llvm::ELF::STV_HIDDEN);
Symbol *addUndefined(StringRef Name);
Symbol *addUndefined(StringRef Name, uint8_t Binding, uint8_t StOther,
uint8_t Type, InputFile *File);
Symbol *addRegular(StringRef Name, const Elf_Sym &Sym,
InputSectionBase<ELFT> *Section);
Symbol *addRegular(StringRef Name, uint8_t Binding, uint8_t StOther);
Symbol *addSynthetic(StringRef N, OutputSectionBase<ELFT> &Section,
uintX_t Value);
void addShared(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym,
const typename ELFT::Verdef *Verdef);
void addLazyArchive(ArchiveFile *F, const llvm::object::Archive::Symbol S);
void addLazyObject(StringRef Name, MemoryBufferRef MBRef);
Symbol *addBitcode(StringRef Name, bool IsWeak, uint8_t StOther, uint8_t Type,
bool CanOmitFromDynSym, BitcodeFile *File);
Symbol *addCommon(StringRef N, uint64_t Size, uint64_t Alignment,
uint8_t Binding, uint8_t StOther, uint8_t Type,
InputFile *File);
void scanUndefinedFlags();
void scanShlibUndefined();
void scanDynamicList();
@ -67,11 +87,13 @@ public:
InputFile *findFile(SymbolBody *B);
private:
Symbol *insert(SymbolBody *New);
void addLazy(Lazy *New);
void addMemberFile(SymbolBody *Undef, Lazy *L);
void resolve(SymbolBody *Body);
std::string conflictMsg(SymbolBody *Old, SymbolBody *New);
std::pair<Symbol *, bool> insert(StringRef Name);
std::pair<Symbol *, bool> insert(StringRef Name, uint8_t Type,
uint8_t Visibility, bool CanOmitFromDynSym,
bool IsUsedInRegularObj, InputFile *File);
std::string conflictMsg(SymbolBody *Existing, InputFile *NewFile);
void reportDuplicate(SymbolBody *Existing, InputFile *NewFile);
// The order the global symbols are in is not defined. We can use an arbitrary
// order, but it has to be reproducible. That is true even when cross linking.
@ -102,6 +124,9 @@ private:
std::unique_ptr<BitcodeCompiler> Lto;
};
template <class ELFT> struct Symtab { static SymbolTable<ELFT> *X; };
template <class ELFT> SymbolTable<ELFT> *Symtab<ELFT>::X;
} // namespace elf
} // namespace lld

View File

@ -79,7 +79,7 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body,
return 0;
case SymbolBody::LazyArchiveKind:
case SymbolBody::LazyObjectKind:
assert(Body.Backref->IsUsedInRegularObj && "lazy symbol reached writer");
assert(Body.symbol()->IsUsedInRegularObj && "lazy symbol reached writer");
return 0;
case SymbolBody::DefinedBitcodeKind:
llvm_unreachable("should have been replaced");
@ -89,22 +89,19 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body,
SymbolBody::SymbolBody(Kind K, uint32_t NameOffset, uint8_t StOther,
uint8_t Type)
: SymbolKind(K), Type(Type), Binding(STB_LOCAL), StOther(StOther),
: SymbolKind(K), IsLocal(true), Type(Type), StOther(StOther),
NameOffset(NameOffset) {
init();
}
SymbolBody::SymbolBody(Kind K, StringRef Name, uint8_t Binding, uint8_t StOther,
uint8_t Type)
: SymbolKind(K), Type(Type), Binding(Binding), StOther(StOther),
SymbolBody::SymbolBody(Kind K, StringRef Name, uint8_t StOther, uint8_t Type)
: SymbolKind(K), IsLocal(false), Type(Type), StOther(StOther),
Name({Name.data(), Name.size()}) {
assert(!isLocal());
init();
}
void SymbolBody::init() {
NeedsCopyOrPltAddr = false;
CanOmitFromDynSym = false;
}
// Returns true if a symbol can be replaced at load-time by a symbol
@ -122,14 +119,14 @@ bool SymbolBody::isPreemptible() const {
return false;
// Only symbols that appear in dynsym can be preempted.
if (!Backref->includeInDynsym())
if (!symbol()->includeInDynsym())
return false;
// Normally only default visibility symbols can be preempted, but -Bsymbolic
// means that not even they can be preempted.
if (Config->Bsymbolic || (Config->BsymbolicFunctions && isFunc()))
return !isDefined();
return Backref->Visibility == STV_DEFAULT;
return symbol()->Visibility == STV_DEFAULT;
}
template <class ELFT>
@ -177,79 +174,35 @@ template <class ELFT> typename ELFT::uint SymbolBody::getSize() const {
return 0;
}
// Returns 1, 0 or -1 if this symbol should take precedence
// over the Other, tie or lose, respectively.
int SymbolBody::compare(SymbolBody *Other) {
assert(!isLazy() && !Other->isLazy());
std::tuple<bool, bool, bool> L(isDefined(), !isShared(), !isWeak());
std::tuple<bool, bool, bool> R(Other->isDefined(), !Other->isShared(),
!Other->isWeak());
// Compare the two by symbol type.
if (L > R)
return -Other->compare(this);
if (L != R)
return -1;
if (!isDefined() || isShared() || isWeak())
return 1;
// If both are equal in terms of symbol type, then at least
// one of them must be a common symbol. Otherwise, they conflict.
auto *A = dyn_cast<DefinedCommon>(this);
auto *B = dyn_cast<DefinedCommon>(Other);
if (!A && !B)
return 0;
// If both are common, the larger one is chosen.
if (A && B) {
if (Config->WarnCommon)
warning("multiple common of " + A->getName());
A->Alignment = B->Alignment = std::max(A->Alignment, B->Alignment);
return A->Size < B->Size ? -1 : 1;
}
// Non-common symbols takes precedence over common symbols.
if (Config->WarnCommon)
warning("common " + this->getName() + " is overridden");
return A ? -1 : 1;
}
Defined::Defined(Kind K, StringRef Name, uint8_t Binding, uint8_t StOther,
uint8_t Type)
: SymbolBody(K, Name, Binding, StOther, Type) {}
Defined::Defined(Kind K, StringRef Name, uint8_t StOther, uint8_t Type)
: SymbolBody(K, Name, StOther, Type) {}
Defined::Defined(Kind K, uint32_t NameOffset, uint8_t StOther, uint8_t Type)
: SymbolBody(K, NameOffset, StOther, Type) {}
DefinedBitcode::DefinedBitcode(StringRef Name, bool IsWeak, uint8_t StOther)
: Defined(DefinedBitcodeKind, Name, IsWeak ? STB_WEAK : STB_GLOBAL,
StOther, 0 /* Type */) {}
DefinedBitcode::DefinedBitcode(StringRef Name, uint8_t StOther, uint8_t Type,
BitcodeFile *F)
: Defined(DefinedBitcodeKind, Name, StOther, Type), File(F) {}
bool DefinedBitcode::classof(const SymbolBody *S) {
return S->kind() == DefinedBitcodeKind;
}
Undefined::Undefined(StringRef Name, uint8_t Binding, uint8_t StOther,
uint8_t Type, bool IsBitcode)
: SymbolBody(SymbolBody::UndefinedKind, Name, Binding, StOther, Type) {
this->IsUndefinedBitcode = IsBitcode;
}
Undefined::Undefined(StringRef Name, uint8_t StOther, uint8_t Type)
: SymbolBody(SymbolBody::UndefinedKind, Name, StOther, Type) {}
Undefined::Undefined(uint32_t NameOffset, uint8_t StOther, uint8_t Type)
: SymbolBody(SymbolBody::UndefinedKind, NameOffset, StOther, Type) {
this->IsUndefinedBitcode = false;
}
: SymbolBody(SymbolBody::UndefinedKind, NameOffset, StOther, Type) {}
template <typename ELFT>
DefinedSynthetic<ELFT>::DefinedSynthetic(StringRef N, uintX_t Value,
OutputSectionBase<ELFT> &Section)
: Defined(SymbolBody::DefinedSyntheticKind, N, STB_GLOBAL, STV_HIDDEN,
0 /* Type */),
: Defined(SymbolBody::DefinedSyntheticKind, N, STV_HIDDEN, 0 /* Type */),
Value(Value), Section(Section) {}
DefinedCommon::DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment,
uint8_t Binding, uint8_t StOther, uint8_t Type)
: Defined(SymbolBody::DefinedCommonKind, N, Binding, StOther, Type),
uint8_t StOther, uint8_t Type)
: Defined(SymbolBody::DefinedCommonKind, N, StOther, Type),
Alignment(Alignment), Size(Size) {}
std::unique_ptr<InputFile> Lazy::getFile() {
@ -301,8 +254,8 @@ std::string elf::demangle(StringRef Name) {
bool Symbol::includeInDynsym() const {
if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
return false;
return (ExportDynamic && VersionScriptGlobal) || Body->isShared() ||
(Body->isUndefined() && Config->Shared);
return (ExportDynamic && VersionScriptGlobal) || body()->isShared() ||
(body()->isUndefined() && Config->Shared);
}
template uint32_t SymbolBody::template getVA<ELF32LE>(uint32_t) const;

View File

@ -20,11 +20,13 @@
#include "lld/Core/LLVM.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/AlignOf.h"
namespace lld {
namespace elf {
class ArchiveFile;
class BitcodeFile;
class InputFile;
class SymbolBody;
template <class ELFT> class ObjectFile;
@ -37,45 +39,7 @@ template <class ELFT> class SharedFile;
// it returns the unmodified string.
std::string demangle(StringRef Name);
// A real symbol object, SymbolBody, is usually accessed indirectly
// through a Symbol. There's always one Symbol for each symbol name.
// The resolver updates SymbolBody pointers as it resolves symbols.
// Symbol also holds computed properties of symbol names.
struct Symbol {
SymbolBody *Body;
// Symbol binding. This is on the Symbol to track changes during resolution.
// In particular:
// An undefined weak is still weak when it resolves to a shared library.
// An undefined weak will not fetch archive members, but we have to remember
// it is weak.
uint8_t Binding;
// Symbol visibility. This is the computed minimum visibility of all
// observed non-DSO symbols.
unsigned Visibility : 2;
// True if the symbol was used for linking and thus need to be added to the
// output file's symbol table. This is true for all symbols except for
// unreferenced DSO symbols and bitcode symbols that are unreferenced except
// by other bitcode objects.
unsigned IsUsedInRegularObj : 1;
// If this flag is true and the symbol has protected or default visibility, it
// will appear in .dynsym. This flag is set by interposable DSO symbols in
// executables, by most symbols in DSOs and executables built with
// --export-dynamic, and by dynamic lists.
unsigned ExportDynamic : 1;
// This flag acts as an additional filter on the dynamic symbol list. It is
// set if there is no version script, or if the symbol appears in the global
// section of the version script.
unsigned VersionScriptGlobal : 1;
bool includeInDynsym() const;
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
};
struct Symbol;
// The base class for real symbol classes.
class SymbolBody {
@ -95,9 +59,13 @@ public:
LazyObjectKind,
};
Symbol *symbol();
const Symbol *symbol() const {
return const_cast<SymbolBody *>(this)->symbol();
}
Kind kind() const { return static_cast<Kind>(SymbolKind); }
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
bool isUndefined() const { return SymbolKind == UndefinedKind; }
bool isDefined() const { return SymbolKind <= DefinedLast; }
bool isCommon() const { return SymbolKind == DefinedCommonKind; }
@ -105,7 +73,7 @@ public:
return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
}
bool isShared() const { return SymbolKind == SharedKind; }
bool isLocal() const { return Binding == llvm::ELF::STB_LOCAL; }
bool isLocal() const { return IsLocal; }
bool isPreemptible() const;
// Returns the symbol name.
@ -121,12 +89,10 @@ public:
uint8_t getVisibility() const { return StOther & 0x3; }
unsigned DynsymIndex = 0;
uint32_t GlobalDynIndex = -1;
uint32_t GotIndex = -1;
uint32_t GotPltIndex = -1;
uint32_t PltIndex = -1;
uint32_t ThunkIndex = -1;
bool hasGlobalDynIndex() { return GlobalDynIndex != uint32_t(-1); }
bool isInGot() const { return GotIndex != -1U; }
bool isInPlt() const { return PltIndex != -1U; }
bool hasThunk() const { return ThunkIndex != -1U; }
@ -142,46 +108,23 @@ public:
template <class ELFT> typename ELFT::uint getThunkVA() const;
template <class ELFT> typename ELFT::uint getSize() const;
// A SymbolBody has a backreference to a Symbol. Originally they are
// doubly-linked. A backreference will never change. But the pointer
// in the Symbol may be mutated by the resolver. If you have a
// pointer P to a SymbolBody and are not sure whether the resolver
// has chosen the object among other objects having the same name,
// you can access P->Backref->Body to get the resolver's result.
SymbolBody &repl() { return Backref ? *Backref->Body : *this; }
// Decides which symbol should "win" in the symbol table, this or
// the Other. Returns 1 if this wins, -1 if the Other wins, or 0 if
// they are duplicate (conflicting) symbols.
int compare(SymbolBody *Other);
protected:
SymbolBody(Kind K, StringRef Name, uint8_t Binding, uint8_t StOther,
uint8_t Type);
SymbolBody(Kind K, StringRef Name, uint8_t StOther, uint8_t Type);
SymbolBody(Kind K, uint32_t NameOffset, uint8_t StOther, uint8_t Type);
const unsigned SymbolKind : 8;
public:
// True if this symbol can be omitted from the symbol table if nothing else
// requires it to be there. Right now this is only used for linkonce_odr in
// LTO, but we could add the feature to ELF. It would be similar to
// MachO's .weak_def_can_be_hidden.
unsigned CanOmitFromDynSym : 1;
// True if the linker has to generate a copy relocation for this shared
// symbol or if the symbol should point to its plt entry.
unsigned NeedsCopyOrPltAddr : 1;
// True if the symbol is undefined and comes from a bitcode file. We need to
// keep track of this because undefined symbols only prevent internalization
// of bitcode symbols if they did not come from a bitcode file.
unsigned IsUndefinedBitcode : 1;
// True if this is a local symbol.
unsigned IsLocal : 1;
// The following fields have the same meaning as the ELF symbol attributes.
uint8_t Type; // symbol type
uint8_t Binding; // symbol binding
uint8_t StOther; // st_other field value
bool isSection() const { return Type == llvm::ELF::STT_SECTION; }
@ -191,8 +134,6 @@ public:
bool isObject() const { return Type == llvm::ELF::STT_OBJECT; }
bool isFile() const { return Type == llvm::ELF::STT_FILE; }
Symbol *Backref = nullptr;
protected:
struct Str {
const char *S;
@ -207,8 +148,7 @@ protected:
// The base class for any defined symbols.
class Defined : public SymbolBody {
public:
Defined(Kind K, StringRef Name, uint8_t Binding, uint8_t StOther,
uint8_t Type);
Defined(Kind K, StringRef Name, uint8_t StOther, uint8_t Type);
Defined(Kind K, uint32_t NameOffset, uint8_t StOther, uint8_t Type);
static bool classof(const SymbolBody *S) { return S->isDefined(); }
};
@ -216,14 +156,16 @@ public:
// The defined symbol in LLVM bitcode files.
class DefinedBitcode : public Defined {
public:
DefinedBitcode(StringRef Name, bool IsWeak, uint8_t StOther);
DefinedBitcode(StringRef Name, uint8_t StOther, uint8_t Type, BitcodeFile *F);
static bool classof(const SymbolBody *S);
BitcodeFile *File;
};
class DefinedCommon : public Defined {
public:
DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment, uint8_t Binding,
uint8_t StOther, uint8_t Type);
DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment, uint8_t StOther,
uint8_t Type);
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::DefinedCommonKind;
@ -247,8 +189,8 @@ template <class ELFT> class DefinedRegular : public Defined {
public:
DefinedRegular(StringRef Name, const Elf_Sym &Sym,
InputSectionBase<ELFT> *Section)
: Defined(SymbolBody::DefinedRegularKind, Name, Sym.getBinding(),
Sym.st_other, Sym.getType()),
: Defined(SymbolBody::DefinedRegularKind, Name, Sym.st_other,
Sym.getType()),
Value(Sym.st_value), Size(Sym.st_size),
Section(Section ? Section->Repl : NullInputSection) {}
@ -260,8 +202,8 @@ public:
assert(isLocal());
}
DefinedRegular(StringRef Name, uint8_t Binding, uint8_t StOther)
: Defined(SymbolBody::DefinedRegularKind, Name, Binding, StOther,
DefinedRegular(StringRef Name, uint8_t StOther)
: Defined(SymbolBody::DefinedRegularKind, Name, StOther,
llvm::ELF::STT_NOTYPE),
Value(0), Size(0), Section(NullInputSection) {}
@ -311,8 +253,7 @@ public:
class Undefined : public SymbolBody {
public:
Undefined(StringRef Name, uint8_t Binding, uint8_t StOther, uint8_t Type,
bool IsBitcode);
Undefined(StringRef Name, uint8_t StOther, uint8_t Type);
Undefined(uint32_t NameOffset, uint8_t StOther, uint8_t Type);
static bool classof(const SymbolBody *S) {
@ -332,8 +273,7 @@ public:
SharedSymbol(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym,
const Elf_Verdef *Verdef)
: Defined(SymbolBody::SharedKind, Name, Sym.getBinding(), Sym.st_other,
Sym.getType()),
: Defined(SymbolBody::SharedKind, Name, Sym.st_other, Sym.getType()),
File(F), Sym(Sym), Verdef(Verdef) {
// IFuncs defined in DSOs are treated as functions by the static linker.
if (isGnuIFunc())
@ -364,9 +304,8 @@ public:
// the same name, it will ask the Lazy to load a file.
class Lazy : public SymbolBody {
public:
Lazy(SymbolBody::Kind K, StringRef Name)
: SymbolBody(K, Name, llvm::ELF::STB_GLOBAL, llvm::ELF::STV_DEFAULT,
/* Type */ 0) {}
Lazy(SymbolBody::Kind K, StringRef Name, uint8_t Type)
: SymbolBody(K, Name, llvm::ELF::STV_DEFAULT, Type) {}
static bool classof(const SymbolBody *S) { return S->isLazy(); }
@ -378,8 +317,9 @@ public:
// LazyArchive symbols represents symbols in archive files.
class LazyArchive : public Lazy {
public:
LazyArchive(ArchiveFile *F, const llvm::object::Archive::Symbol S)
: Lazy(LazyArchiveKind, S.getName()), File(F), Sym(S) {}
LazyArchive(ArchiveFile *F, const llvm::object::Archive::Symbol S,
uint8_t Type)
: Lazy(LazyArchiveKind, S.getName(), Type), File(F), Sym(S) {}
static bool classof(const SymbolBody *S) {
return S->kind() == LazyArchiveKind;
@ -396,8 +336,8 @@ private:
// --start-lib and --end-lib options.
class LazyObject : public Lazy {
public:
LazyObject(StringRef Name, MemoryBufferRef M)
: Lazy(LazyObjectKind, Name), MBRef(M) {}
LazyObject(StringRef Name, MemoryBufferRef M, uint8_t Type)
: Lazy(LazyObjectKind, Name, Type), MBRef(M) {}
static bool classof(const SymbolBody *S) {
return S->kind() == LazyObjectKind;
@ -424,16 +364,8 @@ template <class ELFT> struct ElfSym {
static DefinedRegular<ELFT> *End;
static DefinedRegular<ELFT> *End2;
// The content for _gp symbol for MIPS target.
static SymbolBody *MipsGp;
static SymbolBody *MipsLocalGp;
// The content for _gp_disp symbol for MIPS target.
static SymbolBody *MipsGpDisp;
// __rel_iplt_start/__rel_iplt_end for signaling
// where R_[*]_IRELATIVE relocations do live.
static SymbolBody *RelaIpltStart;
static SymbolBody *RelaIpltEnd;
};
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Etext;
@ -442,11 +374,76 @@ template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Edata;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Edata2;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::End;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::End2;
template <class ELFT> SymbolBody *ElfSym<ELFT>::MipsGp;
template <class ELFT> SymbolBody *ElfSym<ELFT>::MipsLocalGp;
template <class ELFT> SymbolBody *ElfSym<ELFT>::MipsGpDisp;
template <class ELFT> SymbolBody *ElfSym<ELFT>::RelaIpltStart;
template <class ELFT> SymbolBody *ElfSym<ELFT>::RelaIpltEnd;
// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
// always one Symbol for each symbol name. The resolver updates the SymbolBody
// stored in the Body field of this object as it resolves symbols. Symbol also
// holds computed properties of symbol names.
struct Symbol {
uint32_t GlobalDynIndex = -1;
// Symbol binding. This is on the Symbol to track changes during resolution.
// In particular:
// An undefined weak is still weak when it resolves to a shared library.
// An undefined weak will not fetch archive members, but we have to remember
// it is weak.
uint8_t Binding;
// Symbol visibility. This is the computed minimum visibility of all
// observed non-DSO symbols.
unsigned Visibility : 2;
// True if the symbol was used for linking and thus need to be added to the
// output file's symbol table. This is true for all symbols except for
// unreferenced DSO symbols and bitcode symbols that are unreferenced except
// by other bitcode objects.
unsigned IsUsedInRegularObj : 1;
// If this flag is true and the symbol has protected or default visibility, it
// will appear in .dynsym. This flag is set by interposable DSO symbols in
// executables, by most symbols in DSOs and executables built with
// --export-dynamic, and by dynamic lists.
unsigned ExportDynamic : 1;
// This flag acts as an additional filter on the dynamic symbol list. It is
// set if there is no version script, or if the symbol appears in the global
// section of the version script.
unsigned VersionScriptGlobal : 1;
bool includeInDynsym() const;
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
// This field is used to store the Symbol's SymbolBody. This instantiation of
// AlignedCharArrayUnion gives us a struct with a char array field that is
// large and aligned enough to store any derived class of SymbolBody. We
// assume that the size and alignment of ELF64LE symbols is sufficient for any
// ELFT, and we verify this with the static_asserts in replaceBody.
llvm::AlignedCharArrayUnion<
DefinedBitcode, DefinedCommon, DefinedRegular<llvm::object::ELF64LE>,
DefinedSynthetic<llvm::object::ELF64LE>, Undefined,
SharedSymbol<llvm::object::ELF64LE>, LazyArchive, LazyObject>
Body;
SymbolBody *body() { return reinterpret_cast<SymbolBody *>(Body.buffer); }
const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
};
template <typename T, typename... ArgT>
void replaceBody(Symbol *S, ArgT &&... Arg) {
static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
static_assert(alignof(T) <= alignof(decltype(S->Body)),
"Body not aligned enough");
static_assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr,
"Not a SymbolBody");
new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
}
inline Symbol *SymbolBody::symbol() {
assert(!isLocal());
return reinterpret_cast<Symbol *>(reinterpret_cast<char *>(this) -
offsetof(Symbol, Body));
}
} // namespace elf
} // namespace lld

View File

@ -428,13 +428,8 @@ static int32_t findMipsPairedAddend(const uint8_t *Buf, const uint8_t *BufLoc,
// True if non-preemptable symbol always has the same value regardless of where
// the DSO is loaded.
template <class ELFT> static bool isAbsolute(const SymbolBody &Body) {
Symbol *Sym = Body.Backref;
if (Body.isUndefined()) {
if (!Sym)
return false; // undefined local. That is the dummy symbol 0.
if (Sym->isWeak())
return true; // always 0
}
if (Body.isUndefined())
return !Body.isLocal() && Body.symbol()->isWeak();
if (const auto *DR = dyn_cast<DefinedRegular<ELFT>>(&Body))
return DR->Section == nullptr; // Absolute symbol.
return false;
@ -771,7 +766,7 @@ static void reportUndefined(SymbolTable<ELFT> &Symtab, SymbolBody *Sym) {
if (Config->Relocatable)
return;
if (Config->Shared)
if (Sym->Backref->Visibility == STV_DEFAULT)
if (Sym->symbol()->Visibility == STV_DEFAULT)
return;
}
@ -1022,15 +1017,19 @@ void Writer<ELFT>::addCopyRelSymbol(SharedSymbol<ELFT> *SS) {
Out<ELFT>::Bss->updateAlign(Align);
uintX_t Shndx = SS->Sym.st_shndx;
uintX_t Value = SS->Sym.st_value;
// Look through the DSO's dynamic symbol for aliases and create a dynamic
// symbol for each one. This causes the copy relocation to correctly interpose
// any aliases.
for (SharedSymbol<ELFT> &S : SS->File->getSharedSymbols()) {
if (S.Sym.st_shndx != Shndx || S.Sym.st_value != Value)
// Look through the DSO's dynamic symbol table for aliases and create a
// dynamic symbol for each one. This causes the copy relocation to correctly
// interpose any aliases.
for (const Elf_Sym &S : SS->File->getElfSymbols(true)) {
if (S.st_shndx != Shndx || S.st_value != Value)
continue;
S.OffsetInBss = Off;
S.NeedsCopyOrPltAddr = true;
S.Backref->IsUsedInRegularObj = true;
auto *Alias = dyn_cast_or_null<SharedSymbol<ELFT>>(
Symtab.find(check(S.getName(SS->File->getStringTable()))));
if (!Alias)
continue;
Alias->OffsetInBss = Off;
Alias->NeedsCopyOrPltAddr = true;
Alias->symbol()->IsUsedInRegularObj = true;
}
Out<ELFT>::RelaDyn->addReloc(
{Target->CopyRel, Out<ELFT>::Bss, SS->OffsetInBss, false, SS, 0});
@ -1067,9 +1066,9 @@ bool Writer<ELFT>::isDiscarded(InputSectionBase<ELFT> *S) const {
}
template <class ELFT>
static SymbolBody *
addOptionalSynthetic(SymbolTable<ELFT> &Table, StringRef Name,
OutputSectionBase<ELFT> &Sec, typename ELFT::uint Val) {
static Symbol *addOptionalSynthetic(SymbolTable<ELFT> &Table, StringRef Name,
OutputSectionBase<ELFT> &Sec,
typename ELFT::uint Val) {
if (!Table.find(Name))
return nullptr;
return Table.addSynthetic(Name, Sec, Val);
@ -1085,16 +1084,15 @@ template <class ELFT> void Writer<ELFT>::addRelIpltSymbols() {
if (isOutputDynamic() || !Out<ELFT>::RelaPlt)
return;
StringRef S = Config->Rela ? "__rela_iplt_start" : "__rel_iplt_start";
ElfSym<ELFT>::RelaIpltStart =
addOptionalSynthetic(Symtab, S, *Out<ELFT>::RelaPlt, 0);
addOptionalSynthetic(Symtab, S, *Out<ELFT>::RelaPlt, 0);
S = Config->Rela ? "__rela_iplt_end" : "__rel_iplt_end";
ElfSym<ELFT>::RelaIpltEnd = addOptionalSynthetic(
Symtab, S, *Out<ELFT>::RelaPlt, DefinedSynthetic<ELFT>::SectionEnd);
addOptionalSynthetic(Symtab, S, *Out<ELFT>::RelaPlt,
DefinedSynthetic<ELFT>::SectionEnd);
}
template <class ELFT> static bool includeInSymtab(const SymbolBody &B) {
if (!B.Backref->IsUsedInRegularObj)
if (!B.symbol()->IsUsedInRegularObj)
return false;
if (auto *D = dyn_cast<DefinedRegular<ELFT>>(&B)) {
@ -1191,19 +1189,19 @@ template <class ELFT> void Writer<ELFT>::addReservedSymbols() {
// so that it points to an absolute address which is relative to GOT.
// See "Global Data Symbols" in Chapter 6 in the following document:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
ElfSym<ELFT>::MipsGp =
Symtab.addSynthetic("_gp", *Out<ELFT>::Got, MipsGPOffset);
Symtab.addSynthetic("_gp", *Out<ELFT>::Got, MipsGPOffset);
// On MIPS O32 ABI, _gp_disp is a magic symbol designates offset between
// start of function and 'gp' pointer into GOT.
ElfSym<ELFT>::MipsGpDisp =
addOptionalSynthetic(Symtab, "_gp_disp", *Out<ELFT>::Got, MipsGPOffset);
addOptionalSynthetic(Symtab, "_gp_disp", *Out<ELFT>::Got, MipsGPOffset)
->body();
// The __gnu_local_gp is a magic symbol equal to the current value of 'gp'
// pointer. This symbol is used in the code generated by .cpload pseudo-op
// in case of using -mno-shared option.
// https://sourceware.org/ml/binutils/2004-12/msg00094.html
ElfSym<ELFT>::MipsLocalGp = addOptionalSynthetic(
Symtab, "__gnu_local_gp", *Out<ELFT>::Got, MipsGPOffset);
addOptionalSynthetic(Symtab, "__gnu_local_gp", *Out<ELFT>::Got,
MipsGPOffset);
}
// In the assembly for 32 bit x86 the _GLOBAL_OFFSET_TABLE_ symbol
@ -1363,7 +1361,7 @@ template <class ELFT> void Writer<ELFT>::createSections() {
// synthesized ones. Visit all symbols to give the finishing touches.
std::vector<DefinedCommon *> CommonSymbols;
for (Symbol *S : Symtab.getSymbols()) {
SymbolBody *Body = S->Body;
SymbolBody *Body = S->body();
// Set "used" bit for --as-needed.
if (S->IsUsedInRegularObj && !S->isWeak())
@ -1781,7 +1779,7 @@ static uint32_t getMipsEFlags(bool Is64Bits) {
template <class ELFT> static typename ELFT::uint getEntryAddr() {
if (Symbol *S = Config->EntrySym)
return S->Body->getVA<ELFT>();
return S->body()->getVA<ELFT>();
if (Config->EntryAddr != uint64_t(-1))
return Config->EntryAddr;
return 0;

View File

@ -186,23 +186,24 @@ Once you understand their functions, the code of the linker should look obvious
* Symbol
Symbol is a pointer to a SymbolBody. There's only one Symbol for
each unique symbol name (this uniqueness is guaranteed by the symbol table).
Because SymbolBodies are created for each file independently,
there can be many SymbolBodies for the same name.
Thus, the relationship between Symbols and SymbolBodies is 1:N.
You can think of Symbols as handles for SymbolBodies.
A Symbol is a container for a SymbolBody. There's only one Symbol for each
unique symbol name (this uniqueness is guaranteed by the symbol table).
Each global symbol has only one SymbolBody at any one time, which is
the SymbolBody stored within a memory region of the Symbol large enough
to store any SymbolBody.
The resolver keeps the Symbol's pointer to always point to the "best" SymbolBody.
Pointer mutation is the resolve operation of this linker.
As the resolver reads symbols from input files, it replaces the Symbol's
SymbolBody with the "best" SymbolBody for its symbol name by constructing
the new SymbolBody in place on top of the existing SymbolBody. For example,
if the resolver is given a defined symbol, and the SymbolBody with its name
is undefined, it will construct a Defined SymbolBody over the Undefined
SymbolBody.
SymbolBodies have pointers to their Symbols.
That means you can always find the best SymbolBody from
any SymbolBody by following pointers twice.
This structure makes it very easy and cheap to find replacements for symbols.
For example, if you have an Undefined SymbolBody, you can find a Defined
SymbolBody for that symbol just by going to its Symbol and then to SymbolBody,
assuming the resolver have successfully resolved all undefined symbols.
This means that each SymbolBody pointer always points to the best SymbolBody,
and it is possible to get from a SymbolBody to a Symbol, or vice versa,
by adding or subtracting a fixed offset. This memory layout helps reduce
the cache miss rate through high locality and a small number of required
pointer indirections.
* SymbolTable

View File

@ -18,7 +18,7 @@ target triple = "x86_64-unknown-linux-gnu"
; SHARED-NEXT: Value: 0x2000
; SHARED-NEXT: Size: 1
; SHARED-NEXT: Binding: Global
; SHARED-NEXT: Type: None
; SHARED-NEXT: Type: Object
; SHARED-NEXT: Other: 0
; SHARED-NEXT: Section: .bss
; SHARED-NEXT: }

View File

@ -53,7 +53,7 @@ movl $5, b2
// CHECK: Name: a2
// CHECK-NEXT: Value: [[A]]
// CHECK-NEXT: Size: 1
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Binding: Weak
// CHECK-NEXT: Type: Object (0x1)
// CHECK-NEXT: Other: 0
// CHECK-NEXT: Section: .bss
@ -61,7 +61,7 @@ movl $5, b2
// CHECK: Name: b3
// CHECK-NEXT: Value: [[B]]
// CHECK-NEXT: Size: 1
// CHECK-NEXT: Binding: Global (0x1)
// CHECK-NEXT: Binding: Weak
// CHECK-NEXT: Type: Object (0x1)
// CHECK-NEXT: Other: 0
// CHECK-NEXT: Section: .bss