diff options
-rw-r--r-- | lld/ELF/Driver.cpp | 3 | ||||
-rw-r--r-- | lld/ELF/InputFiles.cpp | 126 | ||||
-rw-r--r-- | lld/ELF/InputFiles.h | 41 | ||||
-rw-r--r-- | lld/ELF/LTO.cpp | 39 | ||||
-rw-r--r-- | lld/ELF/MarkLive.cpp | 11 | ||||
-rw-r--r-- | lld/ELF/OutputSections.cpp | 12 | ||||
-rw-r--r-- | lld/ELF/SymbolTable.cpp | 470 | ||||
-rw-r--r-- | lld/ELF/SymbolTable.h | 43 | ||||
-rw-r--r-- | lld/ELF/Symbols.cpp | 85 | ||||
-rw-r--r-- | lld/ELF/Symbols.h | 205 | ||||
-rw-r--r-- | lld/ELF/Writer.cpp | 60 | ||||
-rw-r--r-- | lld/docs/NewLLD.rst | 35 | ||||
-rw-r--r-- | lld/test/ELF/lto/common2.ll | 2 | ||||
-rw-r--r-- | lld/test/ELF/relocation-copy-alias.s | 4 |
14 files changed, 632 insertions, 504 deletions
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index da7ea1c7dfa..cc7855292f7 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -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) diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 2c7124e6a05..b65540cb7a4 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -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>; diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index 326fd5e401e..37cb4d50676 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -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; } diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp index 6980993e7f5..bf9e1d2ba3c 100644 --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -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; - } - if (BitcodeFile::shouldSkip(Sym)) + Symbol *S = Syms[BodyIndex++]; + if (Flags & BasicSymbolRef::SF_Undefined) + continue; + if (!GV) { + // Module asm symbol. + Undefine(S); 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); } diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp index 0d598745c99..0c9fedb3932 100644 --- a/lld/ELF/MarkLive.cpp +++ b/lld/ELF/MarkLive.cpp @@ -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. diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index ba745ad0e80..13d5805d8a2 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -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)) diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index aecd3aeb6e3..2c1554f3552 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -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; - - // 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; + return {Sym, P.second}; +} + +// 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; + 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) +template <class ELFT> +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; + + // 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; - if (Cur->isUndefined()) { - Sym->Body = L; - addMemberFile(Cur, L); } + MemoryBufferRef MBRef = F->getMember(&Sym); + if (!MBRef.getBuffer().empty()) + addFile(createObjectFile(MBRef, F->getName())); } 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>::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; - // 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)); + // 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>; diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h index 92078bcf614..16de475fffb 100644 --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -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 diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index f73a9c71da0..d4e2026c7c5 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -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; diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index f6fd0aa511e..03d023619e2 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -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 diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index ca668ebe349..8fc2595b229 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -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; + auto *Alias = dyn_cast_or_null<SharedSymbol<ELFT>>( + Symtab.find(check(S.getName(SS->File->getStringTable())))); + if (!Alias) continue; - S.OffsetInBss = Off; - S.NeedsCopyOrPltAddr = true; - S.Backref->IsUsedInRegularObj = true; + 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; diff --git a/lld/docs/NewLLD.rst b/lld/docs/NewLLD.rst index 13b5af7409c..a9af40a3ea1 100644 --- a/lld/docs/NewLLD.rst +++ b/lld/docs/NewLLD.rst @@ -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. - - The resolver keeps the Symbol's pointer to always point to the "best" SymbolBody. - Pointer mutation is the resolve operation of this linker. - - 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. + 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. + + 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. + + 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 diff --git a/lld/test/ELF/lto/common2.ll b/lld/test/ELF/lto/common2.ll index 04b19259c92..59a2676e4fc 100644 --- a/lld/test/ELF/lto/common2.ll +++ b/lld/test/ELF/lto/common2.ll @@ -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: } diff --git a/lld/test/ELF/relocation-copy-alias.s b/lld/test/ELF/relocation-copy-alias.s index b7758f75c31..15712e39bc9 100644 --- a/lld/test/ELF/relocation-copy-alias.s +++ b/lld/test/ELF/relocation-copy-alias.s @@ -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 |