diff options
Diffstat (limited to 'COFF/SymbolTable.cpp')
-rw-r--r-- | COFF/SymbolTable.cpp | 494 |
1 files changed, 233 insertions, 261 deletions
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp index df9da4c36650..9cc0b75c1510 100644 --- a/COFF/SymbolTable.cpp +++ b/COFF/SymbolTable.cpp @@ -7,12 +7,12 @@ // //===----------------------------------------------------------------------===// +#include "SymbolTable.h" #include "Config.h" #include "Driver.h" #include "Error.h" -#include "SymbolTable.h" +#include "Memory.h" #include "Symbols.h" -#include "lld/Core/Parallel.h" #include "llvm/IR/LLVMContext.h" #include "llvm/LTO/legacy/LTOCodeGenerator.h" #include "llvm/Support/Debug.h" @@ -24,222 +24,265 @@ using namespace llvm; namespace lld { namespace coff { -void SymbolTable::addFile(std::unique_ptr<InputFile> FileP) { -#if LLVM_ENABLE_THREADS - std::launch Policy = std::launch::async; -#else - std::launch Policy = std::launch::deferred; -#endif +SymbolTable *Symtab; - InputFile *File = FileP.get(); - Files.push_back(std::move(FileP)); - if (auto *F = dyn_cast<ArchiveFile>(File)) { - ArchiveQueue.push_back( - std::async(Policy, [=]() { F->parse(); return F; })); - return; +void SymbolTable::addFile(InputFile *File) { + if (Config->Verbose) + outs() << "Reading " << toString(File) << "\n"; + File->parse(); + + MachineTypes MT = File->getMachineType(); + if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { + Config->Machine = MT; + } else if (MT != IMAGE_FILE_MACHINE_UNKNOWN && Config->Machine != MT) { + fatal(toString(File) + ": machine type " + machineToStr(MT) + + " conflicts with " + machineToStr(Config->Machine)); } - ObjectQueue.push_back( - std::async(Policy, [=]() { File->parse(); return File; })); + if (auto *F = dyn_cast<ObjectFile>(File)) { ObjectFiles.push_back(F); } else if (auto *F = dyn_cast<BitcodeFile>(File)) { BitcodeFiles.push_back(F); - } else { - ImportFiles.push_back(cast<ImportFile>(File)); + } else if (auto *F = dyn_cast<ImportFile>(File)) { + ImportFiles.push_back(F); } -} -void SymbolTable::step() { - if (queueEmpty()) + StringRef S = File->getDirectives(); + if (S.empty()) return; - readObjects(); - readArchives(); -} -void SymbolTable::run() { - while (!queueEmpty()) - step(); -} - -void SymbolTable::readArchives() { - if (ArchiveQueue.empty()) - return; - - // Add lazy symbols to the symbol table. Lazy symbols that conflict - // with existing undefined symbols are accumulated in LazySyms. - std::vector<Symbol *> LazySyms; - for (std::future<ArchiveFile *> &Future : ArchiveQueue) { - ArchiveFile *File = Future.get(); - if (Config->Verbose) - llvm::outs() << "Reading " << File->getShortName() << "\n"; - for (Lazy &Sym : File->getLazySymbols()) - addLazy(&Sym, &LazySyms); - } - ArchiveQueue.clear(); - - // Add archive member files to ObjectQueue that should resolve - // existing undefined symbols. - for (Symbol *Sym : LazySyms) - addMemberFile(cast<Lazy>(Sym->Body)); -} - -void SymbolTable::readObjects() { - if (ObjectQueue.empty()) - return; - - // Add defined and undefined symbols to the symbol table. - std::vector<StringRef> Directives; - for (size_t I = 0; I < ObjectQueue.size(); ++I) { - InputFile *File = ObjectQueue[I].get(); - if (Config->Verbose) - llvm::outs() << "Reading " << File->getShortName() << "\n"; - // Adding symbols may add more files to ObjectQueue - // (but not to ArchiveQueue). - for (SymbolBody *Sym : File->getSymbols()) - if (Sym->isExternal()) - addSymbol(Sym); - StringRef S = File->getDirectives(); - if (!S.empty()) { - Directives.push_back(S); - if (Config->Verbose) - llvm::outs() << "Directives: " << File->getShortName() - << ": " << S << "\n"; - } - } - ObjectQueue.clear(); - - // Parse directive sections. This may add files to - // ArchiveQueue and ObjectQueue. - for (StringRef S : Directives) - Driver->parseDirectives(S); -} - -bool SymbolTable::queueEmpty() { - return ArchiveQueue.empty() && ObjectQueue.empty(); + if (Config->Verbose) + outs() << "Directives: " << toString(File) << ": " << S << "\n"; + Driver->parseDirectives(S); } -void SymbolTable::reportRemainingUndefines(bool Resolve) { - llvm::SmallPtrSet<SymbolBody *, 8> Undefs; +void SymbolTable::reportRemainingUndefines() { + SmallPtrSet<SymbolBody *, 8> Undefs; for (auto &I : Symtab) { Symbol *Sym = I.second; - auto *Undef = dyn_cast<Undefined>(Sym->Body); + auto *Undef = dyn_cast<Undefined>(Sym->body()); if (!Undef) continue; + if (!Sym->IsUsedInRegularObj) + continue; StringRef Name = Undef->getName(); // A weak alias may have been resolved, so check for that. if (Defined *D = Undef->getWeakAlias()) { - if (Resolve) - Sym->Body = D; + // We resolve weak aliases by replacing the alias's SymbolBody with the + // target's SymbolBody. This causes all SymbolBody pointers referring to + // the old symbol to instead refer to the new symbol. However, we can't + // just blindly copy sizeof(Symbol::Body) bytes from D to Sym->Body + // because D may be an internal symbol, and internal symbols are stored as + // "unparented" SymbolBodies. For that reason we need to check which type + // of symbol we are dealing with and copy the correct number of bytes. + if (isa<DefinedRegular>(D)) + memcpy(Sym->Body.buffer, D, sizeof(DefinedRegular)); + else if (isa<DefinedAbsolute>(D)) + memcpy(Sym->Body.buffer, D, sizeof(DefinedAbsolute)); + else + // No other internal symbols are possible. + Sym->Body = D->symbol()->Body; continue; } // If we can resolve a symbol by removing __imp_ prefix, do that. // This odd rule is for compatibility with MSVC linker. if (Name.startswith("__imp_")) { Symbol *Imp = find(Name.substr(strlen("__imp_"))); - if (Imp && isa<Defined>(Imp->Body)) { - if (!Resolve) - continue; - auto *D = cast<Defined>(Imp->Body); - auto *S = new (Alloc) DefinedLocalImport(Name, D); - LocalImportChunks.push_back(S->getChunk()); - Sym->Body = S; + if (Imp && isa<Defined>(Imp->body())) { + auto *D = cast<Defined>(Imp->body()); + replaceBody<DefinedLocalImport>(Sym, Name, D); + LocalImportChunks.push_back( + cast<DefinedLocalImport>(Sym->body())->getChunk()); continue; } } // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. - if (Config->Force && Resolve) - Sym->Body = new (Alloc) DefinedAbsolute(Name, 0); - Undefs.insert(Sym->Body); + if (Config->Force) + replaceBody<DefinedAbsolute>(Sym, Name, 0); + Undefs.insert(Sym->body()); } if (Undefs.empty()) return; - for (Undefined *U : Config->GCRoot) - if (Undefs.count(U->repl())) - llvm::errs() << "<root>: undefined symbol: " << U->getName() << "\n"; - for (std::unique_ptr<InputFile> &File : Files) - if (!isa<ArchiveFile>(File.get())) - for (SymbolBody *Sym : File->getSymbols()) - if (Undefs.count(Sym->repl())) - llvm::errs() << File->getShortName() << ": undefined symbol: " - << Sym->getName() << "\n"; + for (SymbolBody *B : Config->GCRoot) + if (Undefs.count(B)) + errs() << "<root>: undefined symbol: " << B->getName() << "\n"; + for (ObjectFile *File : ObjectFiles) + for (SymbolBody *Sym : File->getSymbols()) + if (Undefs.count(Sym)) + errs() << toString(File) << ": undefined symbol: " << Sym->getName() + << "\n"; if (!Config->Force) fatal("link failed"); } -void SymbolTable::addLazy(Lazy *New, std::vector<Symbol *> *Accum) { - Symbol *Sym = insert(New); - if (Sym->Body == New) - return; - SymbolBody *Existing = Sym->Body; - if (isa<Defined>(Existing)) - return; - if (Lazy *L = dyn_cast<Lazy>(Existing)) - if (L->getFileIndex() < New->getFileIndex()) - return; - Sym->Body = New; - New->setBackref(Sym); - if (isa<Undefined>(Existing)) - Accum->push_back(Sym); +std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) { + Symbol *&Sym = Symtab[CachedHashStringRef(Name)]; + if (Sym) + return {Sym, false}; + Sym = make<Symbol>(); + Sym->IsUsedInRegularObj = false; + Sym->PendingArchiveLoad = false; + return {Sym, true}; } -void SymbolTable::addSymbol(SymbolBody *New) { - // Find an existing symbol or create and insert a new one. - assert(isa<Defined>(New) || isa<Undefined>(New)); - Symbol *Sym = insert(New); - if (Sym->Body == New) - return; - SymbolBody *Existing = Sym->Body; - - // If we have an undefined symbol and a lazy symbol, - // let the lazy symbol to read a member file. - if (auto *L = dyn_cast<Lazy>(Existing)) { - // Undefined symbols with weak aliases need not to be resolved, - // since they would be replaced with weak aliases if they remain - // undefined. - if (auto *U = dyn_cast<Undefined>(New)) { - if (!U->WeakAlias) { - addMemberFile(L); - return; - } +Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F, + bool IsWeakAlias) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + if (!F || !isa<BitcodeFile>(F)) + S->IsUsedInRegularObj = true; + if (WasInserted || (isa<Lazy>(S->body()) && IsWeakAlias)) { + replaceBody<Undefined>(S, Name); + return S; + } + if (auto *L = dyn_cast<Lazy>(S->body())) { + if (!S->PendingArchiveLoad) { + S->PendingArchiveLoad = true; + L->File->addMember(&L->Sym); } - Sym->Body = New; + } + return S; +} + +void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) { + StringRef Name = Sym.getName(); + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + if (WasInserted) { + replaceBody<Lazy>(S, F, Sym); return; } + auto *U = dyn_cast<Undefined>(S->body()); + if (!U || U->WeakAlias || S->PendingArchiveLoad) + return; + S->PendingArchiveLoad = true; + F->addMember(&Sym); +} + +void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) { + fatal("duplicate symbol: " + toString(*Existing->body()) + " in " + + toString(Existing->body()->getFile()) + " and in " + + (NewFile ? toString(NewFile) : "(internal)")); +} - // 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) - fatal("duplicate symbol: " + Existing->getDebugName() + " and " + - New->getDebugName()); - if (Comp < 0) - Sym->Body = New; +Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + S->IsUsedInRegularObj = true; + if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) + replaceBody<DefinedAbsolute>(S, N, Sym); + else if (!isa<DefinedCOFF>(S->body())) + reportDuplicate(S, nullptr); + return S; } -Symbol *SymbolTable::insert(SymbolBody *New) { - Symbol *&Sym = Symtab[New->getName()]; - if (Sym) { - New->setBackref(Sym); - return Sym; +Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + S->IsUsedInRegularObj = true; + if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) + replaceBody<DefinedAbsolute>(S, N, VA); + else if (!isa<DefinedCOFF>(S->body())) + reportDuplicate(S, nullptr); + return S; +} + +Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + S->IsUsedInRegularObj = true; + if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) + replaceBody<DefinedRelative>(S, N, VA); + else if (!isa<DefinedCOFF>(S->body())) + reportDuplicate(S, nullptr); + return S; +} + +Symbol *SymbolTable::addRegular(ObjectFile *F, COFFSymbolRef Sym, + SectionChunk *C) { + StringRef Name; + F->getCOFFObj()->getSymbolName(Sym, Name); + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + S->IsUsedInRegularObj = true; + if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) + replaceBody<DefinedRegular>(S, F, Sym, C); + else if (auto *R = dyn_cast<DefinedRegular>(S->body())) { + if (!C->isCOMDAT() || !R->isCOMDAT()) + reportDuplicate(S, F); + } else if (auto *B = dyn_cast<DefinedBitcode>(S->body())) { + if (B->IsReplaceable) + replaceBody<DefinedRegular>(S, F, Sym, C); + else if (!C->isCOMDAT()) + reportDuplicate(S, F); + } else + replaceBody<DefinedRegular>(S, F, Sym, C); + return S; +} + +Symbol *SymbolTable::addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) { + replaceBody<DefinedBitcode>(S, F, N, IsReplaceable); + return S; } - Sym = new (Alloc) Symbol(New); - New->setBackref(Sym); - return Sym; + if (isa<DefinedCommon>(S->body())) + return S; + if (IsReplaceable) + if (isa<DefinedRegular>(S->body()) || isa<DefinedBitcode>(S->body())) + return S; + reportDuplicate(S, F); + return S; } -// Reads an archive member file pointed by a given symbol. -void SymbolTable::addMemberFile(Lazy *Body) { - std::unique_ptr<InputFile> File = Body->getMember(); +Symbol *SymbolTable::addCommon(ObjectFile *F, COFFSymbolRef Sym, + CommonChunk *C) { + StringRef Name; + F->getCOFFObj()->getSymbolName(Sym, Name); + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + S->IsUsedInRegularObj = true; + if (WasInserted || !isa<DefinedCOFF>(S->body())) + replaceBody<DefinedCommon>(S, F, Sym, C); + else if (auto *DC = dyn_cast<DefinedCommon>(S->body())) + if (Sym.getValue() > DC->getSize()) + replaceBody<DefinedCommon>(S, F, Sym, C); + return S; +} - // getMember returns an empty buffer if the member was already - // read from the library. - if (!File) - return; - if (Config->Verbose) - llvm::outs() << "Loaded " << File->getShortName() << " for " - << Body->getName() << "\n"; - addFile(std::move(File)); +Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + S->IsUsedInRegularObj = true; + if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) + replaceBody<DefinedImportData>(S, N, F); + else if (!isa<DefinedCOFF>(S->body())) + reportDuplicate(S, nullptr); + return S; +} + +Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID, + uint16_t Machine) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + S->IsUsedInRegularObj = true; + if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) + replaceBody<DefinedImportThunk>(S, Name, ID, Machine); + else if (!isa<DefinedCOFF>(S->body())) + reportDuplicate(S, nullptr); + return S; } std::vector<Chunk *> SymbolTable::getChunks() { @@ -252,7 +295,7 @@ std::vector<Chunk *> SymbolTable::getChunks() { } Symbol *SymbolTable::find(StringRef Name) { - auto It = Symtab.find(Name); + auto It = Symtab.find(CachedHashStringRef(Name)); if (It == Symtab.end()) return nullptr; return It->second; @@ -266,7 +309,7 @@ Symbol *SymbolTable::findUnderscore(StringRef Name) { StringRef SymbolTable::findByPrefix(StringRef Prefix) { for (auto Pair : Symtab) { - StringRef Name = Pair.first; + StringRef Name = Pair.first.val(); if (Name.startswith(Prefix)) return Name; } @@ -275,7 +318,7 @@ StringRef SymbolTable::findByPrefix(StringRef Prefix) { StringRef SymbolTable::findMangle(StringRef Name) { if (Symbol *Sym = find(Name)) - if (!isa<Undefined>(Sym->Body)) + if (!isa<Undefined>(Sym->body())) return Name; if (Config->Machine != I386) return findByPrefix(("?" + Name + "@@Y").str()); @@ -289,39 +332,22 @@ StringRef SymbolTable::findMangle(StringRef Name) { return findByPrefix(("?" + Name.substr(1) + "@@Y").str()); } -void SymbolTable::mangleMaybe(Undefined *U) { - if (U->WeakAlias) - return; - if (!isa<Undefined>(U->repl())) +void SymbolTable::mangleMaybe(SymbolBody *B) { + auto *U = dyn_cast<Undefined>(B); + if (!U || U->WeakAlias) return; StringRef Alias = findMangle(U->getName()); if (!Alias.empty()) U->WeakAlias = addUndefined(Alias); } -Undefined *SymbolTable::addUndefined(StringRef Name) { - auto *New = new (Alloc) Undefined(Name); - addSymbol(New); - if (auto *U = dyn_cast<Undefined>(New->repl())) - return U; - return New; -} - -DefinedRelative *SymbolTable::addRelative(StringRef Name, uint64_t VA) { - auto *New = new (Alloc) DefinedRelative(Name, VA); - addSymbol(New); - return New; -} - -DefinedAbsolute *SymbolTable::addAbsolute(StringRef Name, uint64_t VA) { - auto *New = new (Alloc) DefinedAbsolute(Name, VA); - addSymbol(New); - return New; +SymbolBody *SymbolTable::addUndefined(StringRef Name) { + return addUndefined(Name, nullptr, false)->body(); } void SymbolTable::printMap(llvm::raw_ostream &OS) { for (ObjectFile *File : ObjectFiles) { - OS << File->getShortName() << ":\n"; + OS << toString(File) << ":\n"; for (SymbolBody *Body : File->getSymbols()) if (auto *R = dyn_cast<DefinedRegular>(Body)) if (R->getChunk()->isLive()) @@ -330,84 +356,32 @@ void SymbolTable::printMap(llvm::raw_ostream &OS) { } } -void SymbolTable::addCombinedLTOObject(ObjectFile *Obj) { - for (SymbolBody *Body : Obj->getSymbols()) { - if (!Body->isExternal()) - continue; - // We should not see any new undefined symbols at this point, but we'll - // diagnose them later in reportRemainingUndefines(). - StringRef Name = Body->getName(); - Symbol *Sym = insert(Body); - SymbolBody *Existing = Sym->Body; - - if (Existing == Body) - continue; - - if (isa<DefinedBitcode>(Existing)) { - Sym->Body = Body; - continue; - } - if (auto *L = dyn_cast<Lazy>(Existing)) { - // We may see new references to runtime library symbols such as __chkstk - // here. These symbols must be wholly defined in non-bitcode files. - addMemberFile(L); - continue; - } - - int Comp = Existing->compare(Body); - if (Comp == 0) - fatal("LTO: unexpected duplicate symbol: " + Name); - if (Comp < 0) - Sym->Body = Body; - } -} - void SymbolTable::addCombinedLTOObjects() { if (BitcodeFiles.empty()) return; - // Diagnose any undefined symbols early, but do not resolve weak externals, - // as resolution breaks the invariant that each Symbol points to a unique - // SymbolBody, which we rely on to replace DefinedBitcode symbols correctly. - reportRemainingUndefines(/*Resolve=*/false); - // Create an object file and add it to the symbol table by replacing any // DefinedBitcode symbols with the definitions in the object file. LTOCodeGenerator CG(BitcodeFile::Context); CG.setOptLevel(Config->LTOOptLevel); - std::vector<ObjectFile *> Objs = createLTOObjects(&CG); - - for (ObjectFile *Obj : Objs) - addCombinedLTOObject(Obj); - - size_t NumBitcodeFiles = BitcodeFiles.size(); - run(); - if (BitcodeFiles.size() != NumBitcodeFiles) - fatal("LTO: late loaded symbol created new bitcode reference"); + for (ObjectFile *Obj : createLTOObjects(&CG)) + Obj->parse(); } // Combine and compile bitcode files and then return the result // as a vector of regular COFF object files. std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) { - // All symbols referenced by non-bitcode objects must be preserved. - for (ObjectFile *File : ObjectFiles) - for (SymbolBody *Body : File->getSymbols()) - if (auto *S = dyn_cast<DefinedBitcode>(Body->repl())) - CG->addMustPreserveSymbol(S->getName()); - - // Likewise for bitcode symbols which we initially resolved to non-bitcode. + // All symbols referenced by non-bitcode objects, including GC roots, must be + // preserved. We must also replace bitcode symbols with undefined symbols so + // that they may be replaced with real definitions without conflicting. for (BitcodeFile *File : BitcodeFiles) - for (SymbolBody *Body : File->getSymbols()) - if (isa<DefinedBitcode>(Body) && !isa<DefinedBitcode>(Body->repl())) + for (SymbolBody *Body : File->getSymbols()) { + if (!isa<DefinedBitcode>(Body)) + continue; + if (Body->symbol()->IsUsedInRegularObj) CG->addMustPreserveSymbol(Body->getName()); - - // Likewise for other symbols that must be preserved. - for (Undefined *U : Config->GCRoot) { - if (auto *S = dyn_cast<DefinedBitcode>(U->repl())) - CG->addMustPreserveSymbol(S->getName()); - else if (auto *S = dyn_cast_or_null<DefinedBitcode>(U->getWeakAlias())) - CG->addMustPreserveSymbol(S->getName()); - } + replaceBody<Undefined>(Body->symbol(), Body->getName()); + } CG->setModule(BitcodeFiles[0]->takeModule()); for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I) @@ -434,10 +408,8 @@ std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) { std::vector<ObjectFile *> ObjFiles; for (SmallString<0> &Obj : Objs) { - auto *ObjFile = new ObjectFile(MemoryBufferRef(Obj, "<LTO object>")); - Files.emplace_back(ObjFile); + auto *ObjFile = make<ObjectFile>(MemoryBufferRef(Obj, "<LTO object>")); ObjectFiles.push_back(ObjFile); - ObjFile->parse(); ObjFiles.push_back(ObjFile); } |