//===- SymbolTable.cpp ----------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "SymbolTable.h" #include "COFFLinkerContext.h" #include "Config.h" #include "Driver.h" #include "LTO.h" #include "PDB.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Timer.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/IR/LLVMContext.h" #include "llvm/LTO/LTO.h" #include "llvm/Support/Debug.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; namespace lld::coff { StringRef ltrim1(StringRef s, const char *chars) { if (!s.empty() && strchr(chars, s[0])) return s.substr(1); return s; } static bool compatibleMachineType(COFFLinkerContext &ctx, MachineTypes mt) { if (mt == IMAGE_FILE_MACHINE_UNKNOWN) return true; switch (ctx.config.machine) { case ARM64: return mt == ARM64 || mt == ARM64X; case ARM64EC: return COFF::isArm64EC(mt) || mt == AMD64; case ARM64X: return COFF::isAnyArm64(mt) || mt == AMD64; default: return ctx.config.machine == mt; } } void SymbolTable::addFile(InputFile *file) { log("Reading " + toString(file)); if (file->lazy) { if (auto *f = dyn_cast(file)) f->parseLazy(); else cast(file)->parseLazy(); } else { file->parse(); if (auto *f = dyn_cast(file)) { ctx.objFileInstances.push_back(f); } else if (auto *f = dyn_cast(file)) { if (ltoCompilationDone) { error("LTO object file " + toString(file) + " linked in after " "doing LTO compilation."); } ctx.bitcodeFileInstances.push_back(f); } else if (auto *f = dyn_cast(file)) { ctx.importFileInstances.push_back(f); } } MachineTypes mt = file->getMachineType(); if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) { ctx.config.machine = mt; ctx.driver.addWinSysRootLibSearchPaths(); } else if (!compatibleMachineType(ctx, mt)) { error(toString(file) + ": machine type " + machineToStr(mt) + " conflicts with " + machineToStr(ctx.config.machine)); return; } ctx.driver.parseDirectives(file); } static void errorOrWarn(const Twine &s, bool forceUnresolved) { if (forceUnresolved) warn(s); else error(s); } // Causes the file associated with a lazy symbol to be linked in. static void forceLazy(Symbol *s) { s->pendingArchiveLoad = true; switch (s->kind()) { case Symbol::Kind::LazyArchiveKind: { auto *l = cast(s); l->file->addMember(l->sym); break; } case Symbol::Kind::LazyObjectKind: { InputFile *file = cast(s)->file; file->ctx.symtab.addFile(file); break; } case Symbol::Kind::LazyDLLSymbolKind: { auto *l = cast(s); l->file->makeImport(l->sym); break; } default: llvm_unreachable( "symbol passed to forceLazy is not a LazyArchive or LazyObject"); } } // Returns the symbol in SC whose value is <= Addr that is closest to Addr. // This is generally the global variable or function whose definition contains // Addr. static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) { DefinedRegular *candidate = nullptr; for (Symbol *s : sc->file->getSymbols()) { auto *d = dyn_cast_or_null(s); if (!d || !d->data || d->file != sc->file || d->getChunk() != sc || d->getValue() > addr || (candidate && d->getValue() < candidate->getValue())) continue; candidate = d; } return candidate; } static std::vector getSymbolLocations(BitcodeFile *file) { std::string res("\n>>> referenced by "); StringRef source = file->obj->getSourceFileName(); if (!source.empty()) res += source.str() + "\n>>> "; res += toString(file); return {res}; } static std::optional> getFileLineDwarf(const SectionChunk *c, uint32_t addr) { std::optional optionalLineInfo = c->file->getDILineInfo(addr, c->getSectionNumber() - 1); if (!optionalLineInfo) return std::nullopt; const DILineInfo &lineInfo = *optionalLineInfo; if (lineInfo.FileName == DILineInfo::BadString) return std::nullopt; return std::make_pair(saver().save(lineInfo.FileName), lineInfo.Line); } static std::optional> getFileLine(const SectionChunk *c, uint32_t addr) { // MinGW can optionally use codeview, even if the default is dwarf. std::optional> fileLine = getFileLineCodeView(c, addr); // If codeview didn't yield any result, check dwarf in MinGW mode. if (!fileLine && c->file->ctx.config.mingw) fileLine = getFileLineDwarf(c, addr); return fileLine; } // Given a file and the index of a symbol in that file, returns a description // of all references to that symbol from that file. If no debug information is // available, returns just the name of the file, else one string per actual // reference as described in the debug info. // Returns up to maxStrings string descriptions, along with the total number of // locations found. static std::pair, size_t> getSymbolLocations(ObjFile *file, uint32_t symIndex, size_t maxStrings) { struct Location { Symbol *sym; std::pair fileLine; }; std::vector locations; size_t numLocations = 0; for (Chunk *c : file->getChunks()) { auto *sc = dyn_cast(c); if (!sc) continue; for (const coff_relocation &r : sc->getRelocs()) { if (r.SymbolTableIndex != symIndex) continue; numLocations++; if (locations.size() >= maxStrings) continue; std::optional> fileLine = getFileLine(sc, r.VirtualAddress); Symbol *sym = getSymbol(sc, r.VirtualAddress); if (fileLine) locations.push_back({sym, *fileLine}); else if (sym) locations.push_back({sym, {"", 0}}); } } if (maxStrings == 0) return std::make_pair(std::vector(), numLocations); if (numLocations == 0) return std::make_pair( std::vector{"\n>>> referenced by " + toString(file)}, 1); std::vector symbolLocations(locations.size()); size_t i = 0; for (Location loc : locations) { llvm::raw_string_ostream os(symbolLocations[i++]); os << "\n>>> referenced by "; if (!loc.fileLine.first.empty()) os << loc.fileLine.first << ":" << loc.fileLine.second << "\n>>> "; os << toString(file); if (loc.sym) os << ":(" << toString(file->ctx, *loc.sym) << ')'; } return std::make_pair(symbolLocations, numLocations); } std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex) { return getSymbolLocations(file, symIndex, SIZE_MAX).first; } static std::pair, size_t> getSymbolLocations(InputFile *file, uint32_t symIndex, size_t maxStrings) { if (auto *o = dyn_cast(file)) return getSymbolLocations(o, symIndex, maxStrings); if (auto *b = dyn_cast(file)) { std::vector symbolLocations = getSymbolLocations(b); size_t numLocations = symbolLocations.size(); if (symbolLocations.size() > maxStrings) symbolLocations.resize(maxStrings); return std::make_pair(symbolLocations, numLocations); } llvm_unreachable("unsupported file type passed to getSymbolLocations"); return std::make_pair(std::vector(), (size_t)0); } // For an undefined symbol, stores all files referencing it and the index of // the undefined symbol in each file. struct UndefinedDiag { Symbol *sym; struct File { InputFile *file; uint32_t symIndex; }; std::vector files; }; static void reportUndefinedSymbol(const COFFLinkerContext &ctx, const UndefinedDiag &undefDiag) { std::string out; llvm::raw_string_ostream os(out); os << "undefined symbol: " << toString(ctx, *undefDiag.sym); const size_t maxUndefReferences = 3; size_t numDisplayedRefs = 0, numRefs = 0; for (const UndefinedDiag::File &ref : undefDiag.files) { auto [symbolLocations, totalLocations] = getSymbolLocations( ref.file, ref.symIndex, maxUndefReferences - numDisplayedRefs); numRefs += totalLocations; numDisplayedRefs += symbolLocations.size(); for (const std::string &s : symbolLocations) { os << s; } } if (numDisplayedRefs < numRefs) os << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times"; errorOrWarn(os.str(), ctx.config.forceUnresolved); } void SymbolTable::loadMinGWSymbols() { for (auto &i : symMap) { Symbol *sym = i.second; auto *undef = dyn_cast(sym); if (!undef) continue; if (undef->getWeakAlias()) continue; StringRef name = undef->getName(); if (ctx.config.machine == I386 && ctx.config.stdcallFixup) { // Check if we can resolve an undefined decorated symbol by finding // the intended target as an undecorated symbol (only with a leading // underscore). StringRef origName = name; StringRef baseName = name; // Trim down stdcall/fastcall/vectorcall symbols to the base name. baseName = ltrim1(baseName, "_@"); baseName = baseName.substr(0, baseName.find('@')); // Add a leading underscore, as it would be in cdecl form. std::string newName = ("_" + baseName).str(); Symbol *l; if (newName != origName && (l = find(newName)) != nullptr) { // If we found a symbol and it is lazy; load it. if (l->isLazy() && !l->pendingArchiveLoad) { log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() + " for stdcall fixup"); forceLazy(l); } // If it's lazy or already defined, hook it up as weak alias. if (l->isLazy() || isa(l)) { if (ctx.config.warnStdcallFixup) warn("Resolving " + origName + " by linking to " + newName); else log("Resolving " + origName + " by linking to " + newName); undef->weakAlias = l; continue; } } } if (ctx.config.autoImport) { if (name.starts_with("__imp_")) continue; // If we have an undefined symbol, but we have a lazy symbol we could // load, load it. Symbol *l = find(("__imp_" + name).str()); if (!l || l->pendingArchiveLoad || !l->isLazy()) continue; log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() + " for automatic import"); forceLazy(l); } } } Defined *SymbolTable::impSymbol(StringRef name) { if (name.starts_with("__imp_")) return nullptr; return dyn_cast_or_null(find(("__imp_" + name).str())); } bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { Defined *imp = impSymbol(name); if (!imp) return false; // Replace the reference directly to a variable with a reference // to the import address table instead. This obviously isn't right, // but we mark the symbol as isRuntimePseudoReloc, and a later pass // will add runtime pseudo relocations for every relocation against // this Symbol. The runtime pseudo relocation framework expects the // reference itself to point at the IAT entry. size_t impSize = 0; if (isa(imp)) { log("Automatically importing " + name + " from " + cast(imp)->getDLLName()); impSize = sizeof(DefinedImportData); } else if (isa(imp)) { log("Automatically importing " + name + " from " + toString(cast(imp)->file)); impSize = sizeof(DefinedRegular); } else { warn("unable to automatically import " + name + " from " + imp->getName() + " from " + toString(cast(imp)->file) + "; unexpected symbol type"); return false; } sym->replaceKeepingName(imp, impSize); sym->isRuntimePseudoReloc = true; // There may exist symbols named .refptr. which only consist // of a single pointer to . If it turns out is // automatically imported, we don't need to keep the .refptr. // pointer at all, but redirect all accesses to it to the IAT entry // for __imp_ instead, and drop the whole .refptr. chunk. DefinedRegular *refptr = dyn_cast_or_null(find((".refptr." + name).str())); if (refptr && refptr->getChunk()->getSize() == ctx.config.wordsize) { SectionChunk *sc = dyn_cast_or_null(refptr->getChunk()); if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) { log("Replacing .refptr." + name + " with " + imp->getName()); refptr->getChunk()->live = false; refptr->replaceKeepingName(imp, impSize); } } return true; } /// Helper function for reportUnresolvable and resolveRemainingUndefines. /// This function emits an "undefined symbol" diagnostic for each symbol in /// undefs. If localImports is not nullptr, it also emits a "locally /// defined symbol imported" diagnostic for symbols in localImports. /// objFiles and bitcodeFiles (if not nullptr) are used to report where /// undefined symbols are referenced. static void reportProblemSymbols( const COFFLinkerContext &ctx, const SmallPtrSetImpl &undefs, const DenseMap *localImports, bool needBitcodeFiles) { // Return early if there is nothing to report (which should be // the common case). if (undefs.empty() && (!localImports || localImports->empty())) return; for (Symbol *b : ctx.config.gcroot) { if (undefs.count(b)) errorOrWarn(": undefined symbol: " + toString(ctx, *b), ctx.config.forceUnresolved); if (localImports) if (Symbol *imp = localImports->lookup(b)) warn(": locally defined symbol imported: " + toString(ctx, *imp) + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } std::vector undefDiags; DenseMap firstDiag; auto processFile = [&](InputFile *file, ArrayRef symbols) { uint32_t symIndex = (uint32_t)-1; for (Symbol *sym : symbols) { ++symIndex; if (!sym) continue; if (undefs.count(sym)) { auto it = firstDiag.find(sym); if (it == firstDiag.end()) { firstDiag[sym] = undefDiags.size(); undefDiags.push_back({sym, {{file, symIndex}}}); } else { undefDiags[it->second].files.push_back({file, symIndex}); } } if (localImports) if (Symbol *imp = localImports->lookup(sym)) warn(toString(file) + ": locally defined symbol imported: " + toString(ctx, *imp) + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } }; for (ObjFile *file : ctx.objFileInstances) processFile(file, file->getSymbols()); if (needBitcodeFiles) for (BitcodeFile *file : ctx.bitcodeFileInstances) processFile(file, file->getSymbols()); for (const UndefinedDiag &undefDiag : undefDiags) reportUndefinedSymbol(ctx, undefDiag); } void SymbolTable::reportUnresolvable() { SmallPtrSet undefs; for (auto &i : symMap) { Symbol *sym = i.second; auto *undef = dyn_cast(sym); if (!undef || sym->deferUndefined) continue; if (undef->getWeakAlias()) continue; StringRef name = undef->getName(); if (name.starts_with("__imp_")) { Symbol *imp = find(name.substr(strlen("__imp_"))); if (Defined *def = dyn_cast_or_null(imp)) { def->isUsedInRegularObj = true; continue; } } if (name.contains("_PchSym_")) continue; if (ctx.config.autoImport && impSymbol(name)) continue; undefs.insert(sym); } reportProblemSymbols(ctx, undefs, /* localImports */ nullptr, true); } void SymbolTable::resolveRemainingUndefines() { llvm::TimeTraceScope timeScope("Resolve remaining undefined symbols"); SmallPtrSet undefs; DenseMap localImports; for (auto &i : symMap) { Symbol *sym = i.second; auto *undef = dyn_cast(sym); 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()) { // We want to replace Sym with D. However, we can't just blindly // copy sizeof(SymbolUnion) bytes from D to Sym because D may be an // internal symbol, and internal symbols are stored as "unparented" // Symbols. For that reason we need to check which type of symbol we // are dealing with and copy the correct number of bytes. if (isa(d)) memcpy(sym, d, sizeof(DefinedRegular)); else if (isa(d)) memcpy(sym, d, sizeof(DefinedAbsolute)); else memcpy(sym, d, sizeof(SymbolUnion)); continue; } // If we can resolve a symbol by removing __imp_ prefix, do that. // This odd rule is for compatibility with MSVC linker. if (name.starts_with("__imp_")) { Symbol *imp = find(name.substr(strlen("__imp_"))); if (imp && isa(imp)) { auto *d = cast(imp); replaceSymbol(sym, ctx, name, d); localImportChunks.push_back(cast(sym)->getChunk()); localImports[sym] = d; continue; } } // We don't want to report missing Microsoft precompiled headers symbols. // A proper message will be emitted instead in PDBLinker::aquirePrecompObj if (name.contains("_PchSym_")) continue; if (ctx.config.autoImport && handleMinGWAutomaticImport(sym, name)) continue; // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. if (ctx.config.forceUnresolved) replaceSymbol(sym, ctx, name, 0); undefs.insert(sym); } reportProblemSymbols( ctx, undefs, ctx.config.warnLocallyDefinedImported ? &localImports : nullptr, false); } std::pair SymbolTable::insert(StringRef name) { bool inserted = false; Symbol *&sym = symMap[CachedHashStringRef(name)]; if (!sym) { sym = reinterpret_cast(make()); sym->isUsedInRegularObj = false; sym->pendingArchiveLoad = false; sym->canInline = true; inserted = true; } return {sym, inserted}; } std::pair SymbolTable::insert(StringRef name, InputFile *file) { std::pair result = insert(name); if (!file || !isa(file)) result.first->isUsedInRegularObj = true; return result; } void SymbolTable::addEntryThunk(Symbol *from, Symbol *to) { entryThunks.push_back({from, to}); } void SymbolTable::initializeEntryThunks() { for (auto it : entryThunks) { auto *to = dyn_cast(it.second); if (!to) continue; auto *from = dyn_cast(it.first); // We need to be able to add padding to the function and fill it with an // offset to its entry thunks. To ensure that padding the function is // feasible, functions are required to be COMDAT symbols with no offset. if (!from || !from->getChunk()->isCOMDAT() || cast(from)->getValue()) { error("non COMDAT symbol '" + from->getName() + "' in hybrid map"); continue; } from->getChunk()->setEntryThunk(to); } } Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, bool isWeakAlias) { auto [s, wasInserted] = insert(name, f); if (wasInserted || (s->isLazy() && isWeakAlias)) { replaceSymbol(s, name); return s; } if (s->isLazy()) forceLazy(s); return s; } void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) { StringRef name = sym.getName(); auto [s, wasInserted] = insert(name); if (wasInserted) { replaceSymbol(s, f, sym); return; } auto *u = dyn_cast(s); if (!u || u->weakAlias || s->pendingArchiveLoad) return; s->pendingArchiveLoad = true; f->addMember(sym); } void SymbolTable::addLazyObject(InputFile *f, StringRef n) { assert(f->lazy); auto [s, wasInserted] = insert(n, f); if (wasInserted) { replaceSymbol(s, f, n); return; } auto *u = dyn_cast(s); if (!u || u->weakAlias || s->pendingArchiveLoad) return; s->pendingArchiveLoad = true; f->lazy = false; addFile(f); } void SymbolTable::addLazyDLLSymbol(DLLFile *f, DLLFile::Symbol *sym, StringRef n) { auto [s, wasInserted] = insert(n); if (wasInserted) { replaceSymbol(s, f, sym, n); return; } auto *u = dyn_cast(s); if (!u || u->weakAlias || s->pendingArchiveLoad) return; s->pendingArchiveLoad = true; f->makeImport(sym); } static std::string getSourceLocationBitcode(BitcodeFile *file) { std::string res("\n>>> defined at "); StringRef source = file->obj->getSourceFileName(); if (!source.empty()) res += source.str() + "\n>>> "; res += toString(file); return res; } static std::string getSourceLocationObj(ObjFile *file, SectionChunk *sc, uint32_t offset, StringRef name) { std::optional> fileLine; if (sc) fileLine = getFileLine(sc, offset); if (!fileLine) fileLine = file->getVariableLocation(name); std::string res; llvm::raw_string_ostream os(res); os << "\n>>> defined at "; if (fileLine) os << fileLine->first << ":" << fileLine->second << "\n>>> "; os << toString(file); return os.str(); } static std::string getSourceLocation(InputFile *file, SectionChunk *sc, uint32_t offset, StringRef name) { if (!file) return ""; if (auto *o = dyn_cast(file)) return getSourceLocationObj(o, sc, offset, name); if (auto *b = dyn_cast(file)) return getSourceLocationBitcode(b); return "\n>>> defined at " + toString(file); } // Construct and print an error message in the form of: // // lld-link: error: duplicate symbol: foo // >>> defined at bar.c:30 // >>> bar.o // >>> defined at baz.c:563 // >>> baz.o void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile, SectionChunk *newSc, uint32_t newSectionOffset) { std::string msg; llvm::raw_string_ostream os(msg); os << "duplicate symbol: " << toString(ctx, *existing); DefinedRegular *d = dyn_cast(existing); if (d && isa(d->getFile())) { os << getSourceLocation(d->getFile(), d->getChunk(), d->getValue(), existing->getName()); } else { os << getSourceLocation(existing->getFile(), nullptr, 0, ""); } os << getSourceLocation(newFile, newSc, newSectionOffset, existing->getName()); if (ctx.config.forceMultiple) warn(os.str()); else error(os.str()); } Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { auto [s, wasInserted] = insert(n, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, ctx, n, sym); else if (auto *da = dyn_cast(s)) { if (da->getVA() != sym.getValue()) reportDuplicate(s, nullptr); } else if (!isa(s)) reportDuplicate(s, nullptr); return s; } Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) { auto [s, wasInserted] = insert(n, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, ctx, n, va); else if (auto *da = dyn_cast(s)) { if (da->getVA() != va) reportDuplicate(s, nullptr); } else if (!isa(s)) reportDuplicate(s, nullptr); return s; } Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) { auto [s, wasInserted] = insert(n, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, n, c); else if (!isa(s)) reportDuplicate(s, nullptr); return s; } Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, const coff_symbol_generic *sym, SectionChunk *c, uint32_t sectionOffset, bool isWeak) { auto [s, wasInserted] = insert(n, f); if (wasInserted || !isa(s) || s->isWeak) replaceSymbol(s, f, n, /*IsCOMDAT*/ false, /*IsExternal*/ true, sym, c, isWeak); else if (!isWeak) reportDuplicate(s, f, c, sectionOffset); return s; } std::pair SymbolTable::addComdat(InputFile *f, StringRef n, const coff_symbol_generic *sym) { auto [s, wasInserted] = insert(n, f); if (wasInserted || !isa(s)) { replaceSymbol(s, f, n, /*IsCOMDAT*/ true, /*IsExternal*/ true, sym, nullptr); return {cast(s), true}; } auto *existingSymbol = cast(s); if (!existingSymbol->isCOMDAT) reportDuplicate(s, f); return {existingSymbol, false}; } Symbol *SymbolTable::addCommon(InputFile *f, StringRef n, uint64_t size, const coff_symbol_generic *sym, CommonChunk *c) { auto [s, wasInserted] = insert(n, f); if (wasInserted || !isa(s)) replaceSymbol(s, f, n, size, sym, c); else if (auto *dc = dyn_cast(s)) if (size > dc->getSize()) replaceSymbol(s, f, n, size, sym, c); return s; } Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) { auto [s, wasInserted] = insert(n, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) { replaceSymbol(s, n, f); return s; } reportDuplicate(s, f); return nullptr; } Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id, uint16_t machine) { auto [s, wasInserted] = insert(name, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) { replaceSymbol(s, ctx, name, id, machine); return s; } reportDuplicate(s, id->file); return nullptr; } void SymbolTable::addLibcall(StringRef name) { Symbol *sym = findUnderscore(name); if (!sym) return; if (auto *l = dyn_cast(sym)) { MemoryBufferRef mb = l->getMemberBuffer(); if (isBitcode(mb)) addUndefined(sym->getName()); } else if (LazyObject *o = dyn_cast(sym)) { if (isBitcode(o->file->mb)) addUndefined(sym->getName()); } } std::vector SymbolTable::getChunks() const { std::vector res; for (ObjFile *file : ctx.objFileInstances) { ArrayRef v = file->getChunks(); res.insert(res.end(), v.begin(), v.end()); } return res; } Symbol *SymbolTable::find(StringRef name) const { return symMap.lookup(CachedHashStringRef(name)); } Symbol *SymbolTable::findUnderscore(StringRef name) const { if (ctx.config.machine == I386) return find(("_" + name).str()); return find(name); } // Return all symbols that start with Prefix, possibly ignoring the first // character of Prefix or the first character symbol. std::vector SymbolTable::getSymsWithPrefix(StringRef prefix) { std::vector syms; for (auto pair : symMap) { StringRef name = pair.first.val(); if (name.starts_with(prefix) || name.starts_with(prefix.drop_front()) || name.drop_front().starts_with(prefix) || name.drop_front().starts_with(prefix.drop_front())) { syms.push_back(pair.second); } } return syms; } Symbol *SymbolTable::findMangle(StringRef name) { if (Symbol *sym = find(name)) { if (auto *u = dyn_cast(sym)) { // We're specifically looking for weak aliases that ultimately resolve to // defined symbols, hence the call to getWeakAlias() instead of just using // the weakAlias member variable. This matches link.exe's behavior. if (Symbol *weakAlias = u->getWeakAlias()) return weakAlias; } else { return sym; } } // Efficient fuzzy string lookup is impossible with a hash table, so iterate // the symbol table once and collect all possibly matching symbols into this // vector. Then compare each possibly matching symbol with each possible // mangling. std::vector syms = getSymsWithPrefix(name); auto findByPrefix = [&syms](const Twine &t) -> Symbol * { std::string prefix = t.str(); for (auto *s : syms) if (s->getName().starts_with(prefix)) return s; return nullptr; }; // For non-x86, just look for C++ functions. if (ctx.config.machine != I386) return findByPrefix("?" + name + "@@Y"); if (!name.starts_with("_")) return nullptr; // Search for x86 stdcall function. if (Symbol *s = findByPrefix(name + "@")) return s; // Search for x86 fastcall function. if (Symbol *s = findByPrefix("@" + name.substr(1) + "@")) return s; // Search for x86 vectorcall function. if (Symbol *s = findByPrefix(name.substr(1) + "@@")) return s; // Search for x86 C++ non-member function. return findByPrefix("?" + name.substr(1) + "@@Y"); } Symbol *SymbolTable::addUndefined(StringRef name) { return addUndefined(name, nullptr, false); } void SymbolTable::compileBitcodeFiles() { ltoCompilationDone = true; if (ctx.bitcodeFileInstances.empty()) return; llvm::TimeTraceScope timeScope("Compile bitcode"); ScopedTimer t(ctx.ltoTimer); lto.reset(new BitcodeCompiler(ctx)); for (BitcodeFile *f : ctx.bitcodeFileInstances) lto->add(*f); for (InputFile *newObj : lto->compile()) { ObjFile *obj = cast(newObj); obj->parse(); ctx.objFileInstances.push_back(obj); } } } // namespace lld::coff