diff --git a/lld/COFF/CMakeLists.txt b/lld/COFF/CMakeLists.txt --- a/lld/COFF/CMakeLists.txt +++ b/lld/COFF/CMakeLists.txt @@ -5,6 +5,7 @@ add_lld_library(lldCOFF CallGraphSort.cpp Chunks.cpp + COFFLinkerContext.cpp DebugTypes.cpp DLL.cpp Driver.cpp diff --git a/lld/COFF/COFFLinkerContext.h b/lld/COFF/COFFLinkerContext.h new file mode 100644 --- /dev/null +++ b/lld/COFF/COFFLinkerContext.h @@ -0,0 +1,88 @@ +//===- COFFLinkerContext.h --------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_COFFLinkerContext_H +#define LLD_COFF_COFFLinkerContext_H + +#include "Chunks.h" +#include "Config.h" +#include "DebugTypes.h" +#include "InputFiles.h" +#include "SymbolTable.h" +#include "Writer.h" +#include "lld/Common/Timer.h" + +namespace lld { +namespace coff { + +class COFFLinkerContext { +public: + COFFLinkerContext(); + COFFLinkerContext(const COFFLinkerContext &) = delete; + COFFLinkerContext &operator=(const COFFLinkerContext &) = delete; + ~COFFLinkerContext(); + + void addTpiSource(TpiSource *tpi) { tpiSourceList.push_back(tpi); } + + /// Free heap allocated ghashes. + void clearGHashes(); + + SymbolTable symtab; + + std::vector objFileInstances; + std::map pdbInputFileInstances; + std::vector importFileInstances; + std::vector bitcodeFileInstances; + + MergeChunk *mergeChunkInstances[Log2MaxSectionAlignment + 1] = {}; + + /// All sources of type information in the program. + std::vector tpiSourceList; + + std::map typeServerSourceMappings; + std::map precompSourceMappings; + + /// List of all output sections. After output sections are finalized, this + /// can be indexed by getOutputSection. + std::vector outputSections; + + OutputSection *getOutputSection(const Chunk *c) const { + return c->osidx == 0 ? nullptr : outputSections[c->osidx - 1]; + } + + // All timers used in the COFF linker. + Timer rootTimer; + Timer inputFileTimer; + Timer ltoTimer; + Timer gcTimer; + Timer icfTimer; + + // Writer timers. + Timer codeLayoutTimer; + Timer outputCommitTimer; + Timer totalMapTimer; + Timer symbolGatherTimer; + Timer symbolStringsTimer; + Timer writeTimer; + + // PDB timers. + Timer totalPdbLinkTimer; + Timer addObjectsTimer; + Timer symbolMergingTimer; + Timer typeMergingTimer; + Timer tpiStreamLayoutTimer; + Timer publicsLayoutTimer; + Timer diskCommitTimer; + Timer loadGHashTimer; + Timer mergeGHashTimer; +}; + +} // namespace coff +} // namespace lld + +#endif diff --git a/lld/COFF/COFFLinkerContext.cpp b/lld/COFF/COFFLinkerContext.cpp new file mode 100644 --- /dev/null +++ b/lld/COFF/COFFLinkerContext.cpp @@ -0,0 +1,53 @@ +//===- COFFContext.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 +// +//===----------------------------------------------------------------------===// +// +// Description +// +//===----------------------------------------------------------------------===// + +#include "COFFLinkerContext.h" +#include "lld/Common/Memory.h" +#include "llvm/DebugInfo/CodeView/TypeHashing.h" + +namespace lld { +namespace coff { + +COFFLinkerContext::COFFLinkerContext() + : symtab(*this), rootTimer("Total Linking Time"), + inputFileTimer("Input File Reading", rootTimer), + ltoTimer("LTO", rootTimer), gcTimer("GC", rootTimer), + icfTimer("ICF", rootTimer), codeLayoutTimer("Code Layout", rootTimer), + outputCommitTimer("Commit Output File", rootTimer), + totalMapTimer("MAP Emission (Cumulative)", rootTimer), + symbolGatherTimer("Gather Symbols", totalMapTimer), + symbolStringsTimer("Build Symbol strings", totalMapTimer), + writeTimer("Write to File", totalMapTimer), + totalPdbLinkTimer("PDB Emission (Cumulative)", rootTimer), + addObjectsTimer("Add Objects", totalPdbLinkTimer), + symbolMergingTimer("Symbol Merging", addObjectsTimer), + typeMergingTimer("Type Merging", addObjectsTimer), + tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer), + publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer), + diskCommitTimer("Commit to Disk", totalPdbLinkTimer), + loadGHashTimer("Global Type Hashing", addObjectsTimer), + mergeGHashTimer("GHash Type Merging", addObjectsTimer) {} + +COFFLinkerContext::~COFFLinkerContext() { clearGHashes(); } + +void COFFLinkerContext::clearGHashes() { + for (TpiSource *src : tpiSourceList) { + if (src->ownedGHashes) + delete[] src->ghashes.data(); + src->ghashes = {}; + src->isItemIndex.clear(); + src->uniqueTypes.clear(); + } +} + +} // namespace coff +} // namespace lld diff --git a/lld/COFF/CallGraphSort.h b/lld/COFF/CallGraphSort.h --- a/lld/COFF/CallGraphSort.h +++ b/lld/COFF/CallGraphSort.h @@ -14,8 +14,10 @@ namespace lld { namespace coff { class SectionChunk; +class COFFLinkerContext; -llvm::DenseMap computeCallGraphProfileOrder(); +llvm::DenseMap +computeCallGraphProfileOrder(const COFFLinkerContext &ctx); } // namespace coff } // namespace lld diff --git a/lld/COFF/CallGraphSort.cpp b/lld/COFF/CallGraphSort.cpp --- a/lld/COFF/CallGraphSort.cpp +++ b/lld/COFF/CallGraphSort.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "CallGraphSort.h" +#include "COFFLinkerContext.h" #include "InputFiles.h" #include "SymbolTable.h" #include "Symbols.h" @@ -48,7 +49,7 @@ class CallGraphSort { public: - CallGraphSort(); + CallGraphSort(const COFFLinkerContext &ctx); DenseMap run(); @@ -70,7 +71,7 @@ // Take the edge list in Config->CallGraphProfile, resolve symbol names to // Symbols, and generate a graph between InputSections with the provided // weights. -CallGraphSort::CallGraphSort() { +CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) { MapVector &profile = config->callGraphProfile; DenseMap secToCluster; @@ -95,7 +96,7 @@ // output. This messes with the cluster size and density calculations. We // would also end up moving input sections in other output sections without // moving them closer to what calls them. - if (fromSec->getOutputSection() != toSec->getOutputSection()) + if (ctx.getOutputSection(fromSec) != ctx.getOutputSection(toSec)) continue; int from = getOrCreateNode(fromSec); @@ -240,6 +241,7 @@ // This first builds a call graph based on the profile data then merges sections // according to the C³ heuristic. All clusters are then sorted by a density // metric to further improve locality. -DenseMap coff::computeCallGraphProfileOrder() { - return CallGraphSort().run(); +DenseMap +coff::computeCallGraphProfileOrder(const COFFLinkerContext &ctx) { + return CallGraphSort(ctx).run(); } diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h --- a/lld/COFF/Chunks.h +++ b/lld/COFF/Chunks.h @@ -101,7 +101,6 @@ // chunk has a back pointer to an output section. void setOutputSectionIdx(uint16_t o) { osidx = o; } uint16_t getOutputSectionIdx() const { return osidx; } - OutputSection *getOutputSection() const; // Windows-specific. // Collect all locations that contain absolute addresses for base relocations. @@ -415,7 +414,7 @@ class MergeChunk : public NonSectionChunk { public: MergeChunk(uint32_t alignment); - static void addSection(SectionChunk *c); + static void addSection(COFFLinkerContext &ctx, SectionChunk *c); void finalizeContents(); void assignSubsectionRVAs(); @@ -424,7 +423,6 @@ size_t getSize() const override; void writeTo(uint8_t *buf) const override; - static MergeChunk *instances[Log2MaxSectionAlignment + 1]; std::vector sections; private: diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp --- a/lld/COFF/Chunks.cpp +++ b/lld/COFF/Chunks.cpp @@ -7,10 +7,11 @@ //===----------------------------------------------------------------------===// #include "Chunks.h" +#include "COFFLinkerContext.h" #include "InputFiles.h" +#include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" -#include "SymbolTable.h" #include "lld/Common/ErrorHandler.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" @@ -385,7 +386,7 @@ // section is needed to compute SECREL and SECTION relocations used in debug // info. Chunk *c = sym ? sym->getChunk() : nullptr; - OutputSection *os = c ? c->getOutputSection() : nullptr; + OutputSection *os = c ? file->ctx.getOutputSection(c) : nullptr; // Skip the relocation if it refers to a discarded section, and diagnose it // as an error if appropriate. If a symbol was discarded early, it may be @@ -938,18 +939,16 @@ } } -MergeChunk *MergeChunk::instances[Log2MaxSectionAlignment + 1] = {}; - MergeChunk::MergeChunk(uint32_t alignment) : builder(StringTableBuilder::RAW, alignment) { setAlignment(alignment); } -void MergeChunk::addSection(SectionChunk *c) { +void MergeChunk::addSection(COFFLinkerContext &ctx, SectionChunk *c) { assert(isPowerOf2_32(c->getAlignment())); uint8_t p2Align = llvm::Log2_32(c->getAlignment()); - assert(p2Align < array_lengthof(instances)); - auto *&mc = instances[p2Align]; + assert(p2Align < array_lengthof(ctx.mergeChunkInstances)); + auto *&mc = ctx.mergeChunkInstances[p2Align]; if (!mc) mc = make(c->getAlignment()); mc->sections.push_back(c); diff --git a/lld/COFF/DLL.h b/lld/COFF/DLL.h --- a/lld/COFF/DLL.h +++ b/lld/COFF/DLL.h @@ -40,7 +40,7 @@ public: void add(DefinedImportData *sym) { imports.push_back(sym); } bool empty() { return imports.empty(); } - void create(Defined *helper); + void create(COFFLinkerContext &ctx, Defined *helper); std::vector getChunks(); std::vector getDataChunks(); ArrayRef getCodeChunks() { return thunks; } diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp --- a/lld/COFF/DLL.cpp +++ b/lld/COFF/DLL.cpp @@ -18,6 +18,7 @@ //===----------------------------------------------------------------------===// #include "DLL.h" +#include "COFFLinkerContext.h" #include "Chunks.h" #include "SymbolTable.h" #include "llvm/Object/COFF.h" @@ -631,7 +632,7 @@ return dirs.size() * sizeof(delay_import_directory_table_entry); } -void DelayLoadContents::create(Defined *h) { +void DelayLoadContents::create(COFFLinkerContext &ctx, Defined *h) { helper = h; std::vector> v = binImports(imports); @@ -660,13 +661,13 @@ // call targets for Control Flow Guard. StringRef symName = saver.save("__imp_load_" + extName); s->loadThunkSym = - cast(symtab->addSynthetic(symName, t)); + cast(ctx.symtab.addSynthetic(symName, t)); } } thunks.push_back(tm); StringRef tmName = saver.save("__tailMerge_" + syms[0]->getDLLName().lower()); - symtab->addSynthetic(tmName, tm); + ctx.symtab.addSynthetic(tmName, tm); // Terminate with null values. addresses.push_back(make(8)); names.push_back(make(8)); diff --git a/lld/COFF/DebugTypes.h b/lld/COFF/DebugTypes.h --- a/lld/COFF/DebugTypes.h +++ b/lld/COFF/DebugTypes.h @@ -37,12 +37,13 @@ class PDBInputFile; class TypeMerger; struct GHashState; +class COFFLinkerContext; class TpiSource { public: enum TpiKind : uint8_t { Regular, PCH, UsingPCH, PDB, PDBIpi, UsingPDB }; - TpiSource(TpiKind k, ObjFile *f); + TpiSource(COFFLinkerContext &ctx, TpiKind k, ObjFile *f); virtual ~TpiSource(); /// Produce a mapping from the type and item indices used in the object @@ -93,6 +94,8 @@ // Walk over file->debugTypes and fill in the isItemIndex bit vector. void fillIsItemIndexFromDebugT(); + COFFLinkerContext &ctx; + public: bool remapTypesInSymbolRecord(MutableArrayRef rec); @@ -109,29 +112,6 @@ return ghashIdx == endPrecompGHashIdx; } - /// All sources of type information in the program. - static std::vector instances; - - /// Dependency type sources, such as type servers or PCH object files. These - /// must be processed before objects that rely on them. Set by - /// TpiSources::sortDependencies. - static ArrayRef dependencySources; - - /// Object file sources. These must be processed after dependencySources. - static ArrayRef objectSources; - - /// Sorts the dependencies and reassigns TpiSource indices. - static void sortDependencies(); - - static uint32_t countTypeServerPDBs(); - static uint32_t countPrecompObjs(); - - /// Free heap allocated ghashes. - static void clearGHashes(); - - /// Clear global data structures for TpiSources. - static void clear(); - const TpiKind kind; bool ownedGHashes = true; uint32_t tpiSrcIdx = 0; @@ -186,12 +166,13 @@ uint64_t nbTypeRecordsBytes = 0; }; -TpiSource *makeTpiSource(ObjFile *file); -TpiSource *makeTypeServerSource(PDBInputFile *pdbInputFile); -TpiSource *makeUseTypeServerSource(ObjFile *file, +TpiSource *makeTpiSource(COFFLinkerContext &ctx, ObjFile *f); +TpiSource *makeTypeServerSource(COFFLinkerContext &ctx, + PDBInputFile *pdbInputFile); +TpiSource *makeUseTypeServerSource(COFFLinkerContext &ctx, ObjFile *file, llvm::codeview::TypeServer2Record ts); -TpiSource *makePrecompSource(ObjFile *file); -TpiSource *makeUsePrecompSource(ObjFile *file, +TpiSource *makePrecompSource(COFFLinkerContext &ctx, ObjFile *file); +TpiSource *makeUsePrecompSource(COFFLinkerContext &ctx, ObjFile *file, llvm::codeview::PrecompRecord ts); } // namespace coff diff --git a/lld/COFF/DebugTypes.cpp b/lld/COFF/DebugTypes.cpp --- a/lld/COFF/DebugTypes.cpp +++ b/lld/COFF/DebugTypes.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "DebugTypes.h" +#include "COFFLinkerContext.h" #include "Chunks.h" #include "Driver.h" #include "InputFiles.h" @@ -14,7 +15,6 @@ #include "TypeMerger.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" -#include "lld/Common/Timer.h" #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" @@ -46,8 +46,8 @@ // before any dependent OBJ. class TypeServerSource : public TpiSource { public: - explicit TypeServerSource(PDBInputFile *f) - : TpiSource(PDB, nullptr), pdbInputFile(f) { + explicit TypeServerSource(COFFLinkerContext &ctx, PDBInputFile *f) + : TpiSource(ctx, PDB, nullptr), pdbInputFile(f) { if (f->loadErr && *f->loadErr) return; pdb::PDBFile &file = f->session->getPDBFile(); @@ -55,7 +55,7 @@ if (!expectedInfo) return; Guid = expectedInfo->getGuid(); - auto it = mappings.emplace(Guid, this); + auto it = ctx.typeServerSourceMappings.emplace(Guid, this); assert(it.second); (void)it; } @@ -74,8 +74,6 @@ // The PDB signature GUID. codeview::GUID Guid; - - static std::map mappings; }; // Companion to TypeServerSource. Stores the index map for the IPI stream in the @@ -83,7 +81,8 @@ // invariant of one type index space per source. class TypeServerIpiSource : public TpiSource { public: - explicit TypeServerIpiSource() : TpiSource(PDBIpi, nullptr) {} + explicit TypeServerIpiSource(COFFLinkerContext &ctx) + : TpiSource(ctx, PDBIpi, nullptr) {} friend class TypeServerSource; @@ -101,8 +100,8 @@ Expected getTypeServerSource(); public: - UseTypeServerSource(ObjFile *f, TypeServer2Record ts) - : TpiSource(UsingPDB, f), typeServerDependency(ts) {} + UseTypeServerSource(COFFLinkerContext &ctx, ObjFile *f, TypeServer2Record ts) + : TpiSource(ctx, UsingPDB, f), typeServerDependency(ts) {} Error mergeDebugT(TypeMerger *m) override; @@ -121,11 +120,11 @@ // such files, clang does not. class PrecompSource : public TpiSource { public: - PrecompSource(ObjFile *f) : TpiSource(PCH, f) { + PrecompSource(COFFLinkerContext &ctx, ObjFile *f) : TpiSource(ctx, PCH, f) { if (!f->pchSignature || !*f->pchSignature) fatal(toString(f) + " claims to be a PCH object, but does not have a valid signature"); - auto it = mappings.emplace(*f->pchSignature, this); + auto it = ctx.precompSourceMappings.emplace(*f->pchSignature, this); if (!it.second) fatal("a PCH object with the same signature has already been provided (" + toString(it.first->second->file) + " and " + toString(file) + ")"); @@ -134,16 +133,14 @@ void loadGHashes() override; bool isDependency() const override { return true; } - - static std::map mappings; }; // This class represents the debug type stream of an OBJ file that depends on a // Microsoft precompiled headers OBJ (see PrecompSource). class UsePrecompSource : public TpiSource { public: - UsePrecompSource(ObjFile *f, PrecompRecord precomp) - : TpiSource(UsingPCH, f), precompDependency(precomp) {} + UsePrecompSource(COFFLinkerContext &ctx, ObjFile *f, PrecompRecord precomp) + : TpiSource(ctx, UsingPCH, f), precompDependency(precomp) {} Error mergeDebugT(TypeMerger *m) override; @@ -153,6 +150,10 @@ private: Error mergeInPrecompHeaderObj(); + PrecompSource *findObjByName(StringRef fileNameOnly); + PrecompSource *findPrecompSource(ObjFile *file, PrecompRecord &pr); + Expected findPrecompMap(ObjFile *file, PrecompRecord &pr); + public: // Information about the Precomp OBJ dependency, that needs to be loaded in // before merging this OBJ. @@ -160,13 +161,9 @@ }; } // namespace -std::vector TpiSource::instances; -ArrayRef TpiSource::dependencySources; -ArrayRef TpiSource::objectSources; - -TpiSource::TpiSource(TpiKind k, ObjFile *f) - : kind(k), tpiSrcIdx(instances.size()), file(f) { - instances.push_back(this); +TpiSource::TpiSource(COFFLinkerContext &ctx, TpiKind k, ObjFile *f) + : ctx(ctx), kind(k), tpiSrcIdx(ctx.tpiSourceList.size()), file(f) { + ctx.addTpiSource(this); } // Vtable key method. @@ -175,52 +172,35 @@ consumeError(std::move(typeMergingError)); } -void TpiSource::sortDependencies() { - // Order dependencies first, but preserve the existing order. - std::vector deps; - std::vector objs; - for (TpiSource *s : instances) - (s->isDependency() ? deps : objs).push_back(s); - uint32_t numDeps = deps.size(); - uint32_t numObjs = objs.size(); - instances = std::move(deps); - instances.insert(instances.end(), objs.begin(), objs.end()); - for (uint32_t i = 0, e = instances.size(); i < e; ++i) - instances[i]->tpiSrcIdx = i; - dependencySources = makeArrayRef(instances.data(), numDeps); - objectSources = makeArrayRef(instances.data() + numDeps, numObjs); -} - -TpiSource *lld::coff::makeTpiSource(ObjFile *file) { - return make(TpiSource::Regular, file); +TpiSource *lld::coff::makeTpiSource(COFFLinkerContext &ctx, ObjFile *file) { + return make(ctx, TpiSource::Regular, file); } -TpiSource *lld::coff::makeTypeServerSource(PDBInputFile *pdbInputFile) { +TpiSource *lld::coff::makeTypeServerSource(COFFLinkerContext &ctx, + PDBInputFile *pdbInputFile) { // Type server sources come in pairs: the TPI stream, and the IPI stream. - auto *tpiSource = make(pdbInputFile); + auto *tpiSource = make(ctx, pdbInputFile); if (pdbInputFile->session->getPDBFile().hasPDBIpiStream()) - tpiSource->ipiSrc = make(); + tpiSource->ipiSrc = make(ctx); return tpiSource; } -TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file, +TpiSource *lld::coff::makeUseTypeServerSource(COFFLinkerContext &ctx, + ObjFile *file, TypeServer2Record ts) { - return make(file, ts); + return make(ctx, file, ts); } -TpiSource *lld::coff::makePrecompSource(ObjFile *file) { - return make(file); +TpiSource *lld::coff::makePrecompSource(COFFLinkerContext &ctx, ObjFile *file) { + return make(ctx, file); } -TpiSource *lld::coff::makeUsePrecompSource(ObjFile *file, +TpiSource *lld::coff::makeUsePrecompSource(COFFLinkerContext &ctx, + ObjFile *file, PrecompRecord precomp) { - return make(file, precomp); + return make(ctx, file, precomp); } -std::map TypeServerSource::mappings; - -std::map PrecompSource::mappings; - bool TpiSource::remapTypeIndex(TypeIndex &ti, TiRefKind refKind) const { if (ti.isSimple()) return true; @@ -419,12 +399,12 @@ StringRef tsPath = typeServerDependency.getName(); TypeServerSource *tsSrc; - auto it = TypeServerSource::mappings.find(tsId); - if (it != TypeServerSource::mappings.end()) { - tsSrc = it->second; + auto it = ctx.typeServerSourceMappings.find(tsId); + if (it != ctx.typeServerSourceMappings.end()) { + tsSrc = (TypeServerSource *)it->second; } else { // The file failed to load, lookup by name - PDBInputFile *pdb = PDBInputFile::findFromRecordPath(tsPath, file); + PDBInputFile *pdb = PDBInputFile::findFromRecordPath(ctx, tsPath, file); if (!pdb) return createFileError(tsPath, errorCodeToError(std::error_code( ENOENT, std::generic_category()))); @@ -471,36 +451,37 @@ } // Find by name an OBJ provided on the command line -static PrecompSource *findObjByName(StringRef fileNameOnly) { +PrecompSource *UsePrecompSource::findObjByName(StringRef fileNameOnly) { SmallString<128> currentPath; - for (auto kv : PrecompSource::mappings) { + for (auto kv : ctx.precompSourceMappings) { StringRef currentFileName = sys::path::filename(kv.second->file->getName(), sys::path::Style::windows); // Compare based solely on the file name (link.exe behavior) if (equalsPath(currentFileName, fileNameOnly)) - return kv.second; + return (PrecompSource *)kv.second; } return nullptr; } -static PrecompSource *findPrecompSource(ObjFile *file, PrecompRecord &pr) { +PrecompSource *UsePrecompSource::findPrecompSource(ObjFile *file, + PrecompRecord &pr) { // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly, // the paths embedded in the OBJs are in the Windows format. SmallString<128> prFileName = sys::path::filename(pr.getPrecompFilePath(), sys::path::Style::windows); - auto it = PrecompSource::mappings.find(pr.getSignature()); - if (it != PrecompSource::mappings.end()) { - return it->second; + auto it = ctx.precompSourceMappings.find(pr.getSignature()); + if (it != ctx.precompSourceMappings.end()) { + return (PrecompSource *)it->second; } // Lookup by name return findObjByName(prFileName); } -static Expected findPrecompMap(ObjFile *file, - PrecompRecord &pr) { +Expected UsePrecompSource::findPrecompMap(ObjFile *file, + PrecompRecord &pr) { PrecompSource *precomp = findPrecompSource(file, pr); if (!precomp) @@ -555,22 +536,6 @@ return TpiSource::mergeDebugT(m); } -uint32_t TpiSource::countTypeServerPDBs() { - return TypeServerSource::mappings.size(); -} - -uint32_t TpiSource::countPrecompObjs() { - return PrecompSource::mappings.size(); -} - -void TpiSource::clear() { - // Clean up any owned ghash allocations. - clearGHashes(); - TpiSource::instances.clear(); - TypeServerSource::mappings.clear(); - PrecompSource::mappings.clear(); -} - //===----------------------------------------------------------------------===// // Parellel GHash type merging implementation. //===----------------------------------------------------------------------===// @@ -926,7 +891,8 @@ /// Insert the cell with the given ghash into the table. Return the insertion /// position in the table. It is safe for the caller to store the insertion /// position because the table cannot be resized. - uint32_t insert(GloballyHashedType ghash, GHashCell newCell); + uint32_t insert(COFFLinkerContext &ctx, GloballyHashedType ghash, + GHashCell newCell); }; /// A ghash table cell for deduplicating types from TpiSources. @@ -965,8 +931,8 @@ bool isItem() const { return data & (1ULL << 63U); } /// Get the ghash key for this cell. - GloballyHashedType getGHash() const { - return TpiSource::instances[getTpiSrcIdx()]->ghashes[getGHashIdx()]; + GloballyHashedType getGHash(const COFFLinkerContext &ctx) const { + return ctx.tpiSourceList[getTpiSrcIdx()]->ghashes[getGHashIdx()]; } /// The priority function for the cell. The data is stored such that lower @@ -996,7 +962,8 @@ tableSize = newTableSize; } -uint32_t GHashTable::insert(GloballyHashedType ghash, GHashCell newCell) { +uint32_t GHashTable::insert(COFFLinkerContext &ctx, GloballyHashedType ghash, + GHashCell newCell) { assert(!newCell.isEmpty() && "cannot insert empty cell value"); // FIXME: The low bytes of SHA1 have low entropy for short records, which @@ -1015,7 +982,7 @@ // - cell has non-matching key: hash collision, probe next cell auto *cellPtr = reinterpret_cast *>(&table[idx]); GHashCell oldCell(cellPtr->load()); - while (oldCell.isEmpty() || oldCell.getGHash() == ghash) { + while (oldCell.isEmpty() || oldCell.getGHash(ctx) == ghash) { // Check if there is an existing ghash entry with a higher priority // (earlier ordering). If so, this is a duplicate, we are done. if (!oldCell.isEmpty() && oldCell < newCell) @@ -1040,22 +1007,22 @@ llvm_unreachable("left infloop"); } -TypeMerger::TypeMerger(llvm::BumpPtrAllocator &alloc) - : typeTable(alloc), idTable(alloc) {} +TypeMerger::TypeMerger(COFFLinkerContext &c, llvm::BumpPtrAllocator &alloc) + : typeTable(alloc), idTable(alloc), ctx(c) {} TypeMerger::~TypeMerger() = default; void TypeMerger::mergeTypesWithGHash() { // Load ghashes. Do type servers and PCH objects first. { - ScopedTimer t1(loadGHashTimer); - parallelForEach(TpiSource::dependencySources, + ScopedTimer t1(ctx.loadGHashTimer); + parallelForEach(dependencySources, [&](TpiSource *source) { source->loadGHashes(); }); - parallelForEach(TpiSource::objectSources, + parallelForEach(objectSources, [&](TpiSource *source) { source->loadGHashes(); }); } - ScopedTimer t2(mergeGHashTimer); + ScopedTimer t2(ctx.mergeGHashTimer); GHashState ghashState; // Estimate the size of hash table needed to deduplicate ghashes. This *must* @@ -1066,7 +1033,7 @@ // small compared to total memory usage, at eight bytes per input type record, // and most input type records are larger than eight bytes. size_t tableSize = 0; - for (TpiSource *source : TpiSource::instances) + for (TpiSource *source : ctx.tpiSourceList) tableSize += source->ghashes.size(); // Cap the table size so that we can use 32-bit cell indices. Type indices are @@ -1080,8 +1047,8 @@ // position. Because the table does not rehash, the position will not change // under insertion. After insertion is done, the value of the cell can be read // to retrieve the final PDB type index. - parallelForEachN(0, TpiSource::instances.size(), [&](size_t tpiSrcIdx) { - TpiSource *source = TpiSource::instances[tpiSrcIdx]; + parallelForEachN(0, ctx.tpiSourceList.size(), [&](size_t tpiSrcIdx) { + TpiSource *source = ctx.tpiSourceList[tpiSrcIdx]; source->indexMapStorage.resize(source->ghashes.size()); for (uint32_t i = 0, e = source->ghashes.size(); i < e; i++) { if (source->shouldOmitFromPdb(i)) { @@ -1091,7 +1058,7 @@ GloballyHashedType ghash = source->ghashes[i]; bool isItem = source->isItemIndex.test(i); uint32_t cellIdx = - ghashState.table.insert(ghash, GHashCell(isItem, tpiSrcIdx, i)); + ghashState.table.insert(ctx, ghash, GHashCell(isItem, tpiSrcIdx, i)); // Store the ghash cell index as a type index in indexMapStorage. Later // we will replace it with the PDB type index. @@ -1137,7 +1104,7 @@ for (uint32_t i = 0, e = entries.size(); i < e; ++i) { auto &cell = entries[i]; uint32_t tpiSrcIdx = cell.getTpiSrcIdx(); - TpiSource *source = TpiSource::instances[tpiSrcIdx]; + TpiSource *source = ctx.tpiSourceList[tpiSrcIdx]; source->uniqueTypes.push_back(cell.getGHashIdx()); // Update the ghash table to store the destination PDB type index in the @@ -1150,21 +1117,37 @@ } // In parallel, remap all types. - for_each(TpiSource::dependencySources, [&](TpiSource *source) { + for_each(dependencySources, [&](TpiSource *source) { source->remapTpiWithGHashes(&ghashState); }); - parallelForEach(TpiSource::objectSources, [&](TpiSource *source) { + parallelForEach(objectSources, [&](TpiSource *source) { source->remapTpiWithGHashes(&ghashState); }); // Build a global map of from function ID to function type. - for (TpiSource *source : TpiSource::instances) { + for (TpiSource *source : ctx.tpiSourceList) { for (auto idToType : source->funcIdToType) funcIdToType.insert(idToType); source->funcIdToType.clear(); } - TpiSource::clearGHashes(); + ctx.clearGHashes(); +} + +void TypeMerger::sortDependencies() { + // Order dependencies first, but preserve the existing order. + std::vector deps; + std::vector objs; + for (TpiSource *s : ctx.tpiSourceList) + (s->isDependency() ? deps : objs).push_back(s); + uint32_t numDeps = deps.size(); + uint32_t numObjs = objs.size(); + ctx.tpiSourceList = std::move(deps); + ctx.tpiSourceList.insert(ctx.tpiSourceList.end(), objs.begin(), objs.end()); + for (uint32_t i = 0, e = ctx.tpiSourceList.size(); i < e; ++i) + ctx.tpiSourceList[i]->tpiSrcIdx = i; + dependencySources = makeArrayRef(ctx.tpiSourceList.data(), numDeps); + objectSources = makeArrayRef(ctx.tpiSourceList.data() + numDeps, numObjs); } /// Given the index into the ghash table for a particular type, return the type @@ -1187,13 +1170,3 @@ loadPdbTypeIndexFromCell(g, fakeCellIndex.toArrayIndex()); } } - -void TpiSource::clearGHashes() { - for (TpiSource *src : TpiSource::instances) { - if (src->ownedGHashes) - delete[] src->ghashes.data(); - src->ghashes = {}; - src->isItemIndex.clear(); - src->uniqueTypes.clear(); - } -} diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -9,6 +9,7 @@ #ifndef LLD_COFF_DRIVER_H #define LLD_COFF_DRIVER_H +#include "COFFLinkerContext.h" #include "Config.h" #include "SymbolTable.h" #include "lld/Common/LLVM.h" @@ -78,6 +79,8 @@ class LinkerDriver { public: + LinkerDriver(COFFLinkerContext &c) : ctx(c) {} + void linkerMain(llvm::ArrayRef args); // Used by the resolver to parse .drectve section contents. @@ -103,6 +106,8 @@ StringRef doFindLib(StringRef filename); StringRef doFindLibMinGW(StringRef filename); + bool findUnderscoreMangle(StringRef sym); + // Parses LIB environment which contains a list of search paths. void addLibSearchPaths(); @@ -148,6 +153,8 @@ std::vector resources; llvm::StringSet<> directivesExports; + + COFFLinkerContext &ctx; }; // Functions below this line are defined in DriverUtils.cpp. diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Driver.h" +#include "COFFLinkerContext.h" #include "Config.h" #include "DebugTypes.h" #include "ICF.h" @@ -59,8 +60,6 @@ namespace lld { namespace coff { -static Timer inputFileTimer("Input File Reading", Timer::root()); - Configuration *config; LinkerDriver *driver; @@ -70,14 +69,7 @@ lld::stderrOS = &stderrOS; errorHandler().cleanupCallback = []() { - TpiSource::clear(); freeArena(); - ObjFile::instances.clear(); - PDBInputFile::instances.clear(); - ImportFile::instances.clear(); - BitcodeFile::instances.clear(); - memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances)); - OutputSection::clear(); }; errorHandler().logName = args::getFilenameWithoutExe(args[0]); @@ -87,9 +79,9 @@ errorHandler().exitEarly = canExitEarly; stderrOS.enable_colors(stderrOS.has_colors()); + COFFLinkerContext ctx; config = make(); - symtab = make(); - driver = make(); + driver = make(ctx); driver->linkerMain(args); @@ -174,8 +166,8 @@ return sym; } -static bool findUnderscoreMangle(StringRef sym) { - Symbol *s = symtab->findMangle(mangle(sym)); +bool LinkerDriver::findUnderscoreMangle(StringRef sym) { + Symbol *s = ctx.symtab.findMangle(mangle(sym)); return s && !isa(s); } @@ -213,30 +205,30 @@ addArchiveBuffer(m, "", filename, memberIndex++); return; } - symtab->addFile(make(mbref)); + ctx.symtab.addFile(make(ctx, mbref)); break; case file_magic::bitcode: if (lazy) - symtab->addFile(make(mbref)); + ctx.symtab.addFile(make(ctx, mbref)); else - symtab->addFile(make(mbref, "", 0)); + ctx.symtab.addFile(make(ctx, mbref, "", 0)); break; case file_magic::coff_object: case file_magic::coff_import_library: if (lazy) - symtab->addFile(make(mbref)); + ctx.symtab.addFile(make(ctx, mbref)); else - symtab->addFile(make(mbref)); + ctx.symtab.addFile(make(ctx, mbref)); break; case file_magic::pdb: - symtab->addFile(make(mbref)); + ctx.symtab.addFile(make(ctx, mbref)); break; case file_magic::coff_cl_gl_object: error(filename + ": is not a native COFF file. Recompile without /GL"); break; case file_magic::pecoff_executable: if (config->mingw) { - symtab->addFile(make(mbref)); + ctx.symtab.addFile(make(ctx, mbref)); break; } if (filename.endswith_insensitive(".dll")) { @@ -280,24 +272,24 @@ uint64_t offsetInArchive) { file_magic magic = identify_magic(mb.getBuffer()); if (magic == file_magic::coff_import_library) { - InputFile *imp = make(mb); + InputFile *imp = make(ctx, mb); imp->parentName = parentName; - symtab->addFile(imp); + ctx.symtab.addFile(imp); return; } InputFile *obj; if (magic == file_magic::coff_object) { - obj = make(mb); + obj = make(ctx, mb); } else if (magic == file_magic::bitcode) { - obj = make(mb, parentName, offsetInArchive); + obj = make(ctx, mb, parentName, offsetInArchive); } else { error("unknown file type: " + mb.getBufferIdentifier()); return; } obj->parentName = parentName; - symtab->addFile(obj); + ctx.symtab.addFile(obj); log("Loaded " + toString(obj) + " for " + symName); } @@ -547,7 +539,7 @@ } Symbol *LinkerDriver::addUndefined(StringRef name) { - Symbol *b = symtab->addUndefined(name); + Symbol *b = ctx.symtab.addUndefined(name); if (!b->isGCRoot) { b->isGCRoot = true; config->gcroot.push_back(b); @@ -562,14 +554,14 @@ return ""; // Otherwise, see if a similar, mangled symbol exists in the symbol table. - Symbol *mangled = symtab->findMangle(unmangled->getName()); + Symbol *mangled = ctx.symtab.findMangle(unmangled->getName()); if (!mangled) return ""; // If we find a similar mangled symbol, make this an alias to it and return // its name. log(unmangled->getName() + " aliased to " + mangled->getName()); - unmangled->weakAlias = symtab->addUndefined(mangled->getName()); + unmangled->weakAlias = ctx.symtab.addUndefined(mangled->getName()); return mangled->getName(); } @@ -939,7 +931,7 @@ } bool LinkerDriver::run() { - ScopedTimer t(inputFileTimer); + ScopedTimer t(ctx.inputFileTimer); bool didWork = !taskQueue.empty(); while (!taskQueue.empty()) { @@ -952,7 +944,7 @@ // Parse an /order file. If an option is given, the linker places // COMDAT sections in the same order as their names appear in the // given file. -static void parseOrderFile(StringRef arg) { +static void parseOrderFile(COFFLinkerContext &ctx, StringRef arg) { // For some reason, the MSVC linker requires a filename to be // preceded by "@". if (!arg.startswith("@")) { @@ -962,7 +954,7 @@ // Get a list of all comdat sections for error checking. DenseSet set; - for (Chunk *c : symtab->getChunks()) + for (Chunk *c : ctx.symtab.getChunks()) if (auto *sec = dyn_cast(c)) if (sec->sym) set.insert(sec->sym->getName()); @@ -996,7 +988,7 @@ driver->takeBuffer(std::move(mb)); } -static void parseCallGraphFile(StringRef path) { +static void parseCallGraphFile(COFFLinkerContext &ctx, StringRef path) { std::unique_ptr mb = CHECK(MemoryBuffer::getFile(path, /*IsText=*/false, /*RequiresNullTerminator=*/false, @@ -1005,7 +997,7 @@ // Build a map from symbol name to section. DenseMap map; - for (ObjFile *file : ObjFile::instances) + for (ObjFile *file : ctx.objFileInstances) for (Symbol *sym : file->getSymbols()) if (sym) map[sym->getName()] = sym; @@ -1042,8 +1034,8 @@ driver->takeBuffer(std::move(mb)); } -static void readCallGraphsFromObjectFiles() { - for (ObjFile *obj : ObjFile::instances) { +static void readCallGraphsFromObjectFiles(COFFLinkerContext &ctx) { + for (ObjFile *obj : ctx.objFileInstances) { if (obj->callgraphSec) { ArrayRef contents; cantFail( @@ -1077,7 +1069,7 @@ c->keepUnique = true; } -static void findKeepUniqueSections() { +static void findKeepUniqueSections(COFFLinkerContext &ctx) { // Exported symbols could be address-significant in other executables or DSOs, // so we conservatively mark them as address-significant. for (Export &r : config->exports) @@ -1085,7 +1077,7 @@ // Visit the address-significance table in each object file and mark each // referenced symbol as address-significant. - for (ObjFile *obj : ObjFile::instances) { + for (ObjFile *obj : ctx.objFileInstances) { ArrayRef syms = obj->getSymbols(); if (obj->addrsigSec) { ArrayRef contents; @@ -1169,7 +1161,7 @@ void LinkerDriver::convertResources() { std::vector resourceObjFiles; - for (ObjFile *f : ObjFile::instances) { + for (ObjFile *f : ctx.objFileInstances) { if (f->isResourceObjFile()) resourceObjFiles.push_back(f); } @@ -1191,8 +1183,9 @@ f->includeResourceChunks(); return; } - ObjFile *f = make(convertResToCOFF(resources, resourceObjFiles)); - symtab->addFile(f); + ObjFile *f = + make(ctx, convertResToCOFF(resources, resourceObjFiles)); + ctx.symtab.addFile(f); f->includeResourceChunks(); } @@ -1219,9 +1212,9 @@ if (Optional path = doFindFile(arg->getValue())) exporter.addWholeArchive(*path); - symtab->forEachSymbol([&](Symbol *s) { + ctx.symtab.forEachSymbol([&](Symbol *s) { auto *def = dyn_cast(s); - if (!exporter.shouldExport(def)) + if (!exporter.shouldExport(ctx, def)) return; if (!def->isGCRoot) { @@ -1266,7 +1259,7 @@ } void LinkerDriver::linkerMain(ArrayRef argsArr) { - ScopedTimer rootTimer(Timer::root()); + ScopedTimer rootTimer(ctx.rootTimer); // Needed for LTO. InitializeAllTargetInfos(); @@ -2018,32 +2011,32 @@ if (config->imageBase == uint64_t(-1)) config->imageBase = getDefaultImageBase(); - symtab->addSynthetic(mangle("__ImageBase"), nullptr); + ctx.symtab.addSynthetic(mangle("__ImageBase"), nullptr); if (config->machine == I386) { - symtab->addAbsolute("___safe_se_handler_table", 0); - symtab->addAbsolute("___safe_se_handler_count", 0); + ctx.symtab.addAbsolute("___safe_se_handler_table", 0); + ctx.symtab.addAbsolute("___safe_se_handler_count", 0); } - symtab->addAbsolute(mangle("__guard_fids_count"), 0); - symtab->addAbsolute(mangle("__guard_fids_table"), 0); - symtab->addAbsolute(mangle("__guard_flags"), 0); - symtab->addAbsolute(mangle("__guard_iat_count"), 0); - symtab->addAbsolute(mangle("__guard_iat_table"), 0); - symtab->addAbsolute(mangle("__guard_longjmp_count"), 0); - symtab->addAbsolute(mangle("__guard_longjmp_table"), 0); + ctx.symtab.addAbsolute(mangle("__guard_fids_count"), 0); + ctx.symtab.addAbsolute(mangle("__guard_fids_table"), 0); + ctx.symtab.addAbsolute(mangle("__guard_flags"), 0); + ctx.symtab.addAbsolute(mangle("__guard_iat_count"), 0); + ctx.symtab.addAbsolute(mangle("__guard_iat_table"), 0); + ctx.symtab.addAbsolute(mangle("__guard_longjmp_count"), 0); + ctx.symtab.addAbsolute(mangle("__guard_longjmp_table"), 0); // Needed for MSVC 2017 15.5 CRT. - symtab->addAbsolute(mangle("__enclave_config"), 0); + ctx.symtab.addAbsolute(mangle("__enclave_config"), 0); // Needed for MSVC 2019 16.8 CRT. - symtab->addAbsolute(mangle("__guard_eh_cont_count"), 0); - symtab->addAbsolute(mangle("__guard_eh_cont_table"), 0); + ctx.symtab.addAbsolute(mangle("__guard_eh_cont_count"), 0); + ctx.symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0); if (config->pseudoRelocs) { - symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); - symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0); + ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); + ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0); } if (config->mingw) { - symtab->addAbsolute(mangle("__CTOR_LIST__"), 0); - symtab->addAbsolute(mangle("__DTOR_LIST__"), 0); + ctx.symtab.addAbsolute(mangle("__CTOR_LIST__"), 0); + ctx.symtab.addAbsolute(mangle("__DTOR_LIST__"), 0); } // This code may add new undefined symbols to the link, which may enqueue more @@ -2069,12 +2062,12 @@ for (auto pair : config->alternateNames) { StringRef from = pair.first; StringRef to = pair.second; - Symbol *sym = symtab->find(from); + Symbol *sym = ctx.symtab.find(from); if (!sym) continue; if (auto *u = dyn_cast(sym)) if (!u->weakAlias) - u->weakAlias = symtab->addUndefined(to); + u->weakAlias = ctx.symtab.addUndefined(to); } // If any inputs are bitcode files, the LTO code generator may create @@ -2082,25 +2075,25 @@ // file's symbol table. If any of those library functions are defined in a // bitcode file in an archive member, we need to arrange to use LTO to // compile those archive members by adding them to the link beforehand. - if (!BitcodeFile::instances.empty()) + if (!ctx.bitcodeFileInstances.empty()) for (auto *s : lto::LTO::getRuntimeLibcallSymbols()) - symtab->addLibcall(s); + ctx.symtab.addLibcall(s); // Windows specific -- if __load_config_used can be resolved, resolve it. - if (symtab->findUnderscore("_load_config_used")) + if (ctx.symtab.findUnderscore("_load_config_used")) addUndefined(mangle("_load_config_used")); } while (run()); if (args.hasArg(OPT_include_optional)) { // Handle /includeoptional for (auto *arg : args.filtered(OPT_include_optional)) - if (dyn_cast_or_null(symtab->find(arg->getValue()))) + if (dyn_cast_or_null(ctx.symtab.find(arg->getValue()))) addUndefined(arg->getValue()); while (run()); } // Create wrapped symbols for -wrap option. - std::vector wrapped = addWrappedSymbols(args); + std::vector wrapped = addWrappedSymbols(ctx, args); // Load more object files that might be needed for wrapped symbols. if (!wrapped.empty()) while (run()); @@ -2126,7 +2119,7 @@ // If it ends up pulling in more object files from static libraries, // (and maybe doing more stdcall fixups along the way), this would need // to loop these two calls. - symtab->loadMinGWSymbols(); + ctx.symtab.loadMinGWSymbols(); run(); } @@ -2134,8 +2127,8 @@ // If we are going to do codegen for link-time optimization, check for // unresolvable symbols first, so we don't spend time generating code that // will fail to link anyway. - if (!BitcodeFile::instances.empty() && !config->forceUnresolved) - symtab->reportUnresolvable(); + if (!ctx.bitcodeFileInstances.empty() && !config->forceUnresolved) + ctx.symtab.reportUnresolvable(); if (errorCount()) return; @@ -2149,7 +2142,7 @@ // Do LTO by compiling bitcode input files to a set of native COFF files then // link those files (unless -thinlto-index-only was given, in which case we // resolve symbols and write indices, but don't generate native code or link). - symtab->addCombinedLTOObjects(); + ctx.symtab.addCombinedLTOObjects(); // If -thinlto-index-only is given, we should create only "index // files" and not object files. Index file creation is already done @@ -2163,10 +2156,10 @@ // Apply symbol renames for -wrap. if (!wrapped.empty()) - wrapSymbols(wrapped); + wrapSymbols(ctx, wrapped); // Resolve remaining undefined symbols and warn about imported locals. - symtab->resolveRemainingUndefines(); + ctx.symtab.resolveRemainingUndefines(); if (errorCount()) return; @@ -2177,12 +2170,12 @@ // order provided on the command line, while lld will pull in needed // files from static libraries only after the last object file on the // command line. - for (auto i = ObjFile::instances.begin(), e = ObjFile::instances.end(); + for (auto i = ctx.objFileInstances.begin(), e = ctx.objFileInstances.end(); i != e; i++) { ObjFile *file = *i; if (isCrtend(file->getName())) { - ObjFile::instances.erase(i); - ObjFile::instances.push_back(file); + ctx.objFileInstances.erase(i); + ctx.objFileInstances.push_back(file); break; } } @@ -2207,7 +2200,7 @@ StringRef name = pair.first; uint32_t alignment = pair.second; - Symbol *sym = symtab->find(name); + Symbol *sym = ctx.symtab.find(name); if (!sym) { warn("/aligncomm symbol " + name + " not found"); continue; @@ -2239,16 +2232,16 @@ if (auto *arg = args.getLastArg(OPT_order)) { if (args.hasArg(OPT_call_graph_ordering_file)) error("/order and /call-graph-order-file may not be used together"); - parseOrderFile(arg->getValue()); + parseOrderFile(ctx, arg->getValue()); config->callGraphProfileSort = false; } // Handle /call-graph-ordering-file and /call-graph-profile-sort (default on). if (config->callGraphProfileSort) { if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) { - parseCallGraphFile(arg->getValue()); + parseCallGraphFile(ctx, arg->getValue()); } - readCallGraphsFromObjectFiles(); + readCallGraphsFromObjectFiles(ctx); } // Handle /print-symbol-order. @@ -2265,7 +2258,7 @@ // functions. This doesn't bring in more object files, but only marks // functions that already have been included to be retained. for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0"}) { - Defined *d = dyn_cast_or_null(symtab->findUnderscore(n)); + Defined *d = dyn_cast_or_null(ctx.symtab.findUnderscore(n)); if (d && !d->isGCRoot) { d->isGCRoot = true; config->gcroot.push_back(d); @@ -2273,7 +2266,7 @@ } } - markLive(symtab->getChunks()); + markLive(ctx); } // Needs to happen after the last call to addFile(). @@ -2281,17 +2274,17 @@ // Identify identical COMDAT sections to merge them. if (config->doICF != ICFLevel::None) { - findKeepUniqueSections(); - doICF(symtab->getChunks(), config->doICF); + findKeepUniqueSections(ctx); + doICF(ctx, config->doICF); } // Write the result. - writeResult(); + writeResult(ctx); // Stop early so we can print the results. rootTimer.stop(); if (config->showTiming) - Timer::root().print(); + ctx.rootTimer.print(); } } // namespace coff diff --git a/lld/COFF/ICF.h b/lld/COFF/ICF.h --- a/lld/COFF/ICF.h +++ b/lld/COFF/ICF.h @@ -17,8 +17,9 @@ namespace coff { class Chunk; +class COFFLinkerContext; -void doICF(ArrayRef chunks, ICFLevel); +void doICF(COFFLinkerContext &ctx, ICFLevel); } // namespace coff } // namespace lld diff --git a/lld/COFF/ICF.cpp b/lld/COFF/ICF.cpp --- a/lld/COFF/ICF.cpp +++ b/lld/COFF/ICF.cpp @@ -18,6 +18,7 @@ //===----------------------------------------------------------------------===// #include "ICF.h" +#include "COFFLinkerContext.h" #include "Chunks.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" @@ -36,12 +37,10 @@ namespace lld { namespace coff { -static Timer icfTimer("ICF", Timer::root()); - class ICF { public: - ICF(ICFLevel icfLevel) : icfLevel(icfLevel){}; - void run(ArrayRef v); + ICF(COFFLinkerContext &c, ICFLevel icfLevel) : icfLevel(icfLevel), ctx(c){}; + void run(); private: void segregate(size_t begin, size_t end, bool constant); @@ -64,6 +63,8 @@ int cnt = 0; std::atomic repeat = {false}; ICFLevel icfLevel = ICFLevel::All; + + COFFLinkerContext &ctx; }; // Returns true if section S is subject of ICF. @@ -246,12 +247,12 @@ // Merge identical COMDAT sections. // Two sections are considered the same if their section headers, // contents and relocations are all the same. -void ICF::run(ArrayRef vec) { - ScopedTimer t(icfTimer); +void ICF::run() { + ScopedTimer t(ctx.icfTimer); // Collect only mergeable sections and group by hash value. uint32_t nextId = 1; - for (Chunk *c : vec) { + for (Chunk *c : ctx.symtab.getChunks()) { if (auto *sc = dyn_cast(c)) { if (isEligible(sc)) chunks.push_back(sc); @@ -262,7 +263,7 @@ // Make sure that ICF doesn't merge sections that are being handled by string // tail merging. - for (MergeChunk *mc : MergeChunk::instances) + for (MergeChunk *mc : ctx.mergeChunkInstances) if (mc) for (SectionChunk *sc : mc->sections) sc->eqClass[0] = nextId++; @@ -317,8 +318,8 @@ } // Entry point to ICF. -void doICF(ArrayRef chunks, ICFLevel icfLevel) { - ICF(icfLevel).run(chunks); +void doICF(COFFLinkerContext &ctx, ICFLevel icfLevel) { + ICF(ctx, icfLevel).run(); } } // namespace coff diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h --- a/lld/COFF/InputFiles.h +++ b/lld/COFF/InputFiles.h @@ -38,6 +38,7 @@ class DWARFCache; namespace coff { +class COFFLinkerContext; std::vector getArchiveMembers(llvm::object::Archive *file); @@ -91,8 +92,11 @@ // Returns .drectve section contents if exist. StringRef getDirectives() { return directives; } + COFFLinkerContext &ctx; + protected: - InputFile(Kind k, MemoryBufferRef m) : mb(m), fileKind(k) {} + InputFile(COFFLinkerContext &c, Kind k, MemoryBufferRef m) + : mb(m), ctx(c), fileKind(k) {} StringRef directives; @@ -103,7 +107,7 @@ // .lib or .a file. class ArchiveFile : public InputFile { public: - explicit ArchiveFile(MemoryBufferRef m); + explicit ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m); static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } void parse() override; @@ -120,7 +124,8 @@ // .obj or .o file between -start-lib and -end-lib. class LazyObjFile : public InputFile { public: - explicit LazyObjFile(MemoryBufferRef m) : InputFile(LazyObjectKind, m) {} + explicit LazyObjFile(COFFLinkerContext &ctx, MemoryBufferRef m) + : InputFile(ctx, LazyObjectKind, m) {} static bool classof(const InputFile *f) { return f->kind() == LazyObjectKind; } @@ -136,9 +141,11 @@ // .obj or .o file. This may be a member of an archive file. class ObjFile : public InputFile { public: - explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {} - explicit ObjFile(MemoryBufferRef m, std::vector &&symbols) - : InputFile(ObjectKind, m), symbols(std::move(symbols)) {} + explicit ObjFile(COFFLinkerContext &ctx, MemoryBufferRef m) + : InputFile(ctx, ObjectKind, m) {} + explicit ObjFile(COFFLinkerContext &ctx, MemoryBufferRef m, + std::vector &&symbols) + : InputFile(ctx, ObjectKind, m), symbols(std::move(symbols)) {} static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } void parse() override; MachineTypes getMachineType() override; @@ -175,8 +182,6 @@ bool isResourceObjFile() const { return !resourceChunks.empty(); } - static std::vector instances; - // Flags in the absolute @feat.00 symbol if it is present. These usually // indicate if an object was compiled with certain security features enabled // like stack guard, safeseh, /guard:cf, or other things. @@ -228,6 +233,8 @@ return getSection(sym.getSectionNumber()); } + void enqueuePdbFile(StringRef path, ObjFile *fromFile); + void initializeChunks(); void initializeSymbols(); void initializeFlags(); @@ -318,16 +325,13 @@ // stream. class PDBInputFile : public InputFile { public: - explicit PDBInputFile(MemoryBufferRef m); + explicit PDBInputFile(COFFLinkerContext &ctx, MemoryBufferRef m); ~PDBInputFile(); static bool classof(const InputFile *f) { return f->kind() == PDBKind; } void parse() override; - static void enqueue(StringRef path, ObjFile *fromFile); - - static PDBInputFile *findFromRecordPath(StringRef path, ObjFile *fromFile); - - static std::map instances; + static PDBInputFile *findFromRecordPath(const COFFLinkerContext &ctx, + StringRef path, ObjFile *fromFile); // Record possible errors while opening the PDB file llvm::Optional loadErr; @@ -344,12 +348,11 @@ // for details about the format. class ImportFile : public InputFile { public: - explicit ImportFile(MemoryBufferRef m) : InputFile(ImportKind, m) {} + explicit ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m) + : InputFile(ctx, ImportKind, m) {} static bool classof(const InputFile *f) { return f->kind() == ImportKind; } - static std::vector instances; - Symbol *impSym = nullptr; Symbol *thunkSym = nullptr; std::string dllName; @@ -377,16 +380,15 @@ // Used for LTO. class BitcodeFile : public InputFile { public: - BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb, StringRef archiveName, uint64_t offsetInArchive); - explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName, - uint64_t offsetInArchive, + explicit BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef m, + StringRef archiveName, uint64_t offsetInArchive, std::vector &&symbols); ~BitcodeFile(); static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } ArrayRef getSymbols() { return symbols; } MachineTypes getMachineType() override; - static std::vector instances; std::unique_ptr obj; private: @@ -398,7 +400,8 @@ // .dll file. MinGW only. class DLLFile : public InputFile { public: - explicit DLLFile(MemoryBufferRef m) : InputFile(DLLKind, m) {} + explicit DLLFile(COFFLinkerContext &ctx, MemoryBufferRef m) + : InputFile(ctx, DLLKind, m) {} static bool classof(const InputFile *f) { return f->kind() == DLLKind; } void parse() override; MachineTypes getMachineType() override; diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "COFFLinkerContext.h" #include "Chunks.h" #include "Config.h" #include "DebugTypes.h" @@ -69,11 +70,6 @@ .str(); } -std::vector ObjFile::instances; -std::map PDBInputFile::instances; -std::vector ImportFile::instances; -std::vector BitcodeFile::instances; - /// Checks that Source is compatible with being a weak alias to Target. /// If Source is Undefined and has no weak alias set, makes it a weak /// alias to Target. @@ -98,7 +94,8 @@ return name == "@feat.00" || name == "@comp.id"; } -ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {} +ArchiveFile::ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m) + : InputFile(ctx, ArchiveKind, m) {} void ArchiveFile::parse() { // Parse a MemoryBufferRef as an archive file. @@ -106,7 +103,7 @@ // Read the symbol table to construct Lazy objects. for (const Archive::Symbol &sym : file->symbols()) - symtab->addLazyArchive(this, sym); + ctx.symtab.addLazyArchive(this, sym); } // Returns a buffer pointing to a member file containing a given symbol. @@ -144,11 +141,11 @@ InputFile *file; if (isBitcode(mb)) - file = make(mb, "", 0, std::move(symbols)); + file = make(ctx, mb, "", 0, std::move(symbols)); else - file = make(mb, std::move(symbols)); + file = make(ctx, mb, std::move(symbols)); mb = {}; - symtab->addFile(file); + ctx.symtab.addFile(file); } void LazyObjFile::parse() { @@ -158,7 +155,7 @@ CHECK(lto::InputFile::create(this->mb), this); for (const lto::InputFile::Symbol &sym : obj->symbols()) { if (!sym.isUndefined()) - symtab->addLazyObject(this, sym.getName()); + ctx.symtab.addLazyObject(this, sym.getName()); } return; } @@ -175,7 +172,7 @@ StringRef name = check(coffObj->getSymbolName(coffSym)); if (coffSym.isAbsolute() && ignoredSymbolName(name)) continue; - symtab->addLazyObject(this, name); + ctx.symtab.addLazyObject(this, name); i += coffSym.getNumberOfAuxSymbols(); } } @@ -293,7 +290,7 @@ // COFF sections that look like string literal sections (i.e. no // relocations, in .rdata, leader symbol name matches the MSVC name mangling // for string literals) are subject to string tail merging. - MergeChunk::addSection(c); + MergeChunk::addSection(ctx, c); else if (name == ".rsrc" || name.startswith(".rsrc$")) resourceChunks.push_back(c); else @@ -387,8 +384,8 @@ if (sym.isExternal()) { StringRef name = check(coffObj->getSymbolName(sym)); if (sc) - return symtab->addRegular(this, name, sym.getGeneric(), sc, - sym.getValue()); + return ctx.symtab.addRegular(this, name, sym.getGeneric(), sc, + sym.getValue()); // For MinGW symbols named .weak.* that point to a discarded section, // don't create an Undefined symbol. If nothing ever refers to the symbol, // everything should be fine. If something actually refers to the symbol @@ -396,7 +393,7 @@ // references at the end. if (config->mingw && name.startswith(".weak.")) return nullptr; - return symtab->addUndefined(name, this, false); + return ctx.symtab.addUndefined(name, this, false); } if (sc) return make(this, /*Name*/ "", /*IsCOMDAT*/ false, @@ -464,7 +461,7 @@ for (auto &kv : weakAliases) { Symbol *sym = kv.first; uint32_t idx = kv.second; - checkAndSetWeakAlias(symtab, this, sym, symbols[idx]); + checkAndSetWeakAlias(&ctx.symtab, this, sym, symbols[idx]); } // Free the memory used by sparseChunks now that symbol loading is finished. @@ -473,7 +470,7 @@ Symbol *ObjFile::createUndefined(COFFSymbolRef sym) { StringRef name = check(coffObj->getSymbolName(sym)); - return symtab->addUndefined(name, this, sym.isWeakExternal()); + return ctx.symtab.addUndefined(name, this, sym.isWeakExternal()); } static const coff_aux_section_definition *findSectionDef(COFFObjectFile *obj, @@ -543,13 +540,13 @@ Twine((int)leaderSelection) + " in " + toString(leader->getFile()) + " and " + Twine((int)selection) + " in " + toString(this)) .str()); - symtab->reportDuplicate(leader, this); + ctx.symtab.reportDuplicate(leader, this); return; } switch (selection) { case IMAGE_COMDAT_SELECT_NODUPLICATES: - symtab->reportDuplicate(leader, this); + ctx.symtab.reportDuplicate(leader, this); break; case IMAGE_COMDAT_SELECT_ANY: @@ -559,14 +556,14 @@ case IMAGE_COMDAT_SELECT_SAME_SIZE: if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) { if (!config->mingw) { - symtab->reportDuplicate(leader, this); + ctx.symtab.reportDuplicate(leader, this); } else { const coff_aux_section_definition *leaderDef = nullptr; if (leaderChunk->file) leaderDef = findSectionDef(leaderChunk->file->getCOFFObj(), leaderChunk->getSectionNumber()); if (!leaderDef || leaderDef->Length != def->Length) - symtab->reportDuplicate(leader, this); + ctx.symtab.reportDuplicate(leader, this); } } break; @@ -577,7 +574,7 @@ // if the two comdat sections have e.g. different alignment. // Match that. if (leaderChunk->getContents() != newChunk.getContents()) - symtab->reportDuplicate(leader, this, &newChunk, sym.getValue()); + ctx.symtab.reportDuplicate(leader, this, &newChunk, sym.getValue()); break; } @@ -620,8 +617,8 @@ if (sym.isCommon()) { auto *c = make(sym); chunks.push_back(c); - return symtab->addCommon(this, getName(), sym.getValue(), sym.getGeneric(), - c); + return ctx.symtab.addCommon(this, getName(), sym.getValue(), + sym.getGeneric(), c); } if (sym.isAbsolute()) { @@ -634,7 +631,7 @@ return nullptr; if (sym.isExternal()) - return symtab->addAbsolute(name, sym); + return ctx.symtab.addAbsolute(name, sym); return make(name, sym); } @@ -667,7 +664,7 @@ if (sym.isExternal()) { std::tie(leader, prevailing) = - symtab->addComdat(this, getName(), sym.getGeneric()); + ctx.symtab.addComdat(this, getName(), sym.getGeneric()); } else { leader = make(this, /*Name*/ "", /*IsCOMDAT*/ false, /*IsExternal*/ false, sym.getGeneric()); @@ -789,12 +786,11 @@ else data = getDebugSection(".debug$T"); - // Don't make a TpiSource for objects with no debug info. If the object has // symbols but no types, make a plain, empty TpiSource anyway, because it // simplifies adding the symbols later. if (data.empty()) { if (!debugChunks.empty()) - debugTypesObj = makeTpiSource(this); + debugTypesObj = makeTpiSource(ctx, this); return; } @@ -812,7 +808,7 @@ // This object file is a PCH file that others will depend on. if (isPCH) { - debugTypesObj = makePrecompSource(this); + debugTypesObj = makePrecompSource(ctx, this); return; } @@ -820,8 +816,8 @@ if (firstType->kind() == LF_TYPESERVER2) { TypeServer2Record ts = cantFail( TypeDeserializer::deserializeAs(firstType->data())); - debugTypesObj = makeUseTypeServerSource(this, ts); - PDBInputFile::enqueue(ts.getName(), this); + debugTypesObj = makeUseTypeServerSource(ctx, this, ts); + enqueuePdbFile(ts.getName(), this); return; } @@ -830,14 +826,14 @@ if (firstType->kind() == LF_PRECOMP) { PrecompRecord precomp = cantFail( TypeDeserializer::deserializeAs(firstType->data())); - debugTypesObj = makeUsePrecompSource(this, precomp); + debugTypesObj = makeUsePrecompSource(ctx, this, precomp); // Drop the LF_PRECOMP record from the input stream. debugTypes = debugTypes.drop_front(firstType->RecordData.size()); return; } // This is a plain old object file. - debugTypesObj = makeTpiSource(this); + debugTypesObj = makeTpiSource(ctx, this); } // Make a PDB path assuming the PDB is in the same folder as the OBJ @@ -855,7 +851,7 @@ // The casing of the PDB path stamped in the OBJ can differ from the actual path // on disk. With this, we ensure to always use lowercase as a key for the -// PDBInputFile::instances map, at least on Windows. +// pdbInputFileInstances map, at least on Windows. static std::string normalizePdbPath(StringRef path) { #if defined(_WIN32) return path.lower(); @@ -879,33 +875,25 @@ return None; } -PDBInputFile::PDBInputFile(MemoryBufferRef m) : InputFile(PDBKind, m) {} +PDBInputFile::PDBInputFile(COFFLinkerContext &ctx, MemoryBufferRef m) + : InputFile(ctx, PDBKind, m) {} PDBInputFile::~PDBInputFile() = default; -PDBInputFile *PDBInputFile::findFromRecordPath(StringRef path, +PDBInputFile *PDBInputFile::findFromRecordPath(const COFFLinkerContext &ctx, + StringRef path, ObjFile *fromFile) { auto p = findPdbPath(path.str(), fromFile); if (!p) return nullptr; - auto it = PDBInputFile::instances.find(*p); - if (it != PDBInputFile::instances.end()) + auto it = ctx.pdbInputFileInstances.find(*p); + if (it != ctx.pdbInputFileInstances.end()) return it->second; return nullptr; } -void PDBInputFile::enqueue(StringRef path, ObjFile *fromFile) { - auto p = findPdbPath(path.str(), fromFile); - if (!p) - return; - auto it = PDBInputFile::instances.emplace(*p, nullptr); - if (!it.second) - return; // already scheduled for load - driver->enqueuePDB(*p); -} - void PDBInputFile::parse() { - PDBInputFile::instances[mb.getBufferIdentifier().str()] = this; + ctx.pdbInputFileInstances[mb.getBufferIdentifier().str()] = this; std::unique_ptr thisSession; loadErr.emplace(pdb::NativeSession::createFromPdb( @@ -923,7 +911,7 @@ loadErr.emplace(expectedInfo.takeError()); return; } - debugTypesObj = makeTypeServerSource(this); + debugTypesObj = makeTypeServerSource(ctx, this); } // Used only for DWARF debug info, which is not common (except in MinGW @@ -957,6 +945,16 @@ return dwarf->getDILineInfo(offset, sectionIndex); } +void ObjFile::enqueuePdbFile(StringRef path, ObjFile *fromFile) { + auto p = findPdbPath(path.str(), fromFile); + if (!p) + return; + auto it = ctx.pdbInputFileInstances.emplace(*p, nullptr); + if (!it.second) + return; // already scheduled for load + driver->enqueuePDB(*p); +} + void ImportFile::parse() { const char *buf = mb.getBufferStart(); const auto *hdr = reinterpret_cast(buf); @@ -990,31 +988,31 @@ this->hdr = hdr; externalName = extName; - impSym = symtab->addImportData(impName, this); + impSym = ctx.symtab.addImportData(impName, this); // If this was a duplicate, we logged an error but may continue; // in this case, impSym is nullptr. if (!impSym) return; if (hdr->getType() == llvm::COFF::IMPORT_CONST) - static_cast(symtab->addImportData(name, this)); + static_cast(ctx.symtab.addImportData(name, this)); // If type is function, we need to create a thunk which jump to an // address pointed by the __imp_ symbol. (This allows you to call // DLL functions just like regular non-DLL functions.) if (hdr->getType() == llvm::COFF::IMPORT_CODE) - thunkSym = symtab->addImportThunk( + thunkSym = ctx.symtab.addImportThunk( name, cast_or_null(impSym), hdr->Machine); } -BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive) - : BitcodeFile(mb, archiveName, offsetInArchive, {}) {} +BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb, + StringRef archiveName, uint64_t offsetInArchive) + : BitcodeFile(ctx, mb, archiveName, offsetInArchive, {}) {} -BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive, +BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb, + StringRef archiveName, uint64_t offsetInArchive, std::vector &&symbols) - : InputFile(BitcodeKind, mb), symbols(std::move(symbols)) { + : InputFile(ctx, BitcodeKind, mb), symbols(std::move(symbols)) { std::string path = mb.getBufferIdentifier().str(); if (config->thinLTOIndexOnly) path = replaceThinLTOSuffix(mb.getBufferIdentifier()); @@ -1069,7 +1067,7 @@ for (size_t i = 0; i != obj->getComdatTable().size(); ++i) // FIXME: Check nodeduplicate comdat[i] = - symtab->addComdat(this, saver.save(obj->getComdatTable()[i].first)); + ctx.symtab.addComdat(this, saver.save(obj->getComdatTable()[i].first)); for (const lto::InputFile::Symbol &objSym : obj->symbols()) { StringRef symName = saver.save(objSym.getName()); int comdatIndex = objSym.getComdatIndex(); @@ -1080,27 +1078,27 @@ else fakeSC = <oDataSectionChunk.chunk; if (objSym.isUndefined()) { - sym = symtab->addUndefined(symName, this, false); + sym = ctx.symtab.addUndefined(symName, this, false); } else if (objSym.isCommon()) { - sym = symtab->addCommon(this, symName, objSym.getCommonSize()); + sym = ctx.symtab.addCommon(this, symName, objSym.getCommonSize()); } else if (objSym.isWeak() && objSym.isIndirect()) { // Weak external. - sym = symtab->addUndefined(symName, this, true); + sym = ctx.symtab.addUndefined(symName, this, true); std::string fallback = std::string(objSym.getCOFFWeakExternalFallback()); - Symbol *alias = symtab->addUndefined(saver.save(fallback)); - checkAndSetWeakAlias(symtab, this, sym, alias); + Symbol *alias = ctx.symtab.addUndefined(saver.save(fallback)); + checkAndSetWeakAlias(&ctx.symtab, this, sym, alias); } else if (comdatIndex != -1) { if (symName == obj->getComdatTable()[comdatIndex].first) { sym = comdat[comdatIndex].first; if (cast(sym)->data == nullptr) cast(sym)->data = &fakeSC->repl; } else if (comdat[comdatIndex].second) { - sym = symtab->addRegular(this, symName, nullptr, fakeSC); + sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC); } else { - sym = symtab->addUndefined(symName, this, false); + sym = ctx.symtab.addUndefined(symName, this, false); } } else { - sym = symtab->addRegular(this, symName, nullptr, fakeSC); + sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC); } symbols.push_back(sym); if (objSym.isUsed()) @@ -1185,9 +1183,9 @@ } StringRef impName = saver.save("__imp_" + symbolName); - symtab->addLazyDLLSymbol(this, s, impName); + ctx.symtab.addLazyDLLSymbol(this, s, impName); if (code) - symtab->addLazyDLLSymbol(this, s, symbolName); + ctx.symtab.addLazyDLLSymbol(this, s, symbolName); } } @@ -1219,6 +1217,6 @@ p += s->symbolName.size() + 1; memcpy(p, s->dllName.data(), s->dllName.size()); MemoryBufferRef mbref = MemoryBufferRef(StringRef(buf, size), s->dllName); - ImportFile *impFile = make(mbref); - symtab->addFile(impFile); + ImportFile *impFile = make(ctx, mbref); + ctx.symtab.addFile(impFile); } diff --git a/lld/COFF/LLDMapFile.h b/lld/COFF/LLDMapFile.h --- a/lld/COFF/LLDMapFile.h +++ b/lld/COFF/LLDMapFile.h @@ -9,12 +9,10 @@ #ifndef LLD_COFF_LLDMAPFILE_H #define LLD_COFF_LLDMAPFILE_H -#include "llvm/ADT/ArrayRef.h" - namespace lld { namespace coff { -class OutputSection; -void writeLLDMapFile(llvm::ArrayRef outputSections); +class COFFLinkerContext; +void writeLLDMapFile(const COFFLinkerContext &ctx); } } diff --git a/lld/COFF/LLDMapFile.cpp b/lld/COFF/LLDMapFile.cpp --- a/lld/COFF/LLDMapFile.cpp +++ b/lld/COFF/LLDMapFile.cpp @@ -19,6 +19,7 @@ //===----------------------------------------------------------------------===// #include "LLDMapFile.h" +#include "COFFLinkerContext.h" #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" @@ -44,9 +45,9 @@ } // Returns a list of all symbols that we want to print out. -static std::vector getSymbols() { +static std::vector getSymbols(const COFFLinkerContext &ctx) { std::vector v; - for (ObjFile *file : ObjFile::instances) + for (ObjFile *file : ctx.objFileInstances) for (Symbol *b : file->getSymbols()) if (auto *sym = dyn_cast_or_null(b)) if (sym && !sym->getCOFFSymbol().isSectionDefinition()) @@ -86,7 +87,7 @@ return ret; } -void lld::coff::writeLLDMapFile(ArrayRef outputSections) { +void lld::coff::writeLLDMapFile(const COFFLinkerContext &ctx) { if (config->lldmapFile.empty()) return; @@ -96,7 +97,7 @@ fatal("cannot open " + config->lldmapFile + ": " + ec.message()); // Collect symbol info that we want to print out. - std::vector syms = getSymbols(); + std::vector syms = getSymbols(ctx); SymbolMapTy sectionSyms = getSectionSyms(syms); DenseMap symStr = getSymbolStrings(syms); @@ -104,7 +105,7 @@ os << "Address Size Align Out In Symbol\n"; // Print out file contents. - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize); os << sec->name << '\n'; diff --git a/lld/COFF/LTO.h b/lld/COFF/LTO.h --- a/lld/COFF/LTO.h +++ b/lld/COFF/LTO.h @@ -38,6 +38,7 @@ class BitcodeFile; class InputFile; +class COFFLinkerContext; class BitcodeCompiler { public: @@ -45,7 +46,7 @@ ~BitcodeCompiler(); void add(BitcodeFile &f); - std::vector compile(); + std::vector compile(COFFLinkerContext &ctx); private: std::unique_ptr ltoObj; diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp --- a/lld/COFF/LTO.cpp +++ b/lld/COFF/LTO.cpp @@ -156,7 +156,7 @@ // Merge all the bitcode files we have seen, codegen the result // and return the resulting objects. -std::vector BitcodeCompiler::compile() { +std::vector BitcodeCompiler::compile(COFFLinkerContext &ctx) { unsigned maxTasks = ltoObj->getMaxTasks(); buf.resize(maxTasks); files.resize(maxTasks); @@ -224,7 +224,7 @@ if (config->saveTemps) saveBuffer(buf[i], ltoObjName); - ret.push_back(make(MemoryBufferRef(objBuf, ltoObjName))); + ret.push_back(make(ctx, MemoryBufferRef(objBuf, ltoObjName))); } return ret; diff --git a/lld/COFF/MapFile.h b/lld/COFF/MapFile.h --- a/lld/COFF/MapFile.h +++ b/lld/COFF/MapFile.h @@ -9,12 +9,10 @@ #ifndef LLD_COFF_MAPFILE_H #define LLD_COFF_MAPFILE_H -#include "llvm/ADT/ArrayRef.h" - namespace lld { namespace coff { -class OutputSection; -void writeMapFile(llvm::ArrayRef outputSections); +class COFFLinkerContext; +void writeMapFile(COFFLinkerContext &ctx); } } diff --git a/lld/COFF/MapFile.cpp b/lld/COFF/MapFile.cpp --- a/lld/COFF/MapFile.cpp +++ b/lld/COFF/MapFile.cpp @@ -28,6 +28,7 @@ //===----------------------------------------------------------------------===// #include "MapFile.h" +#include "COFFLinkerContext.h" #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" @@ -42,11 +43,6 @@ using namespace lld; using namespace lld::coff; -static Timer totalMapTimer("MAP emission (Cumulative)", Timer::root()); -static Timer symbolGatherTimer("Gather symbols", totalMapTimer); -static Timer symbolStringsTimer("Build symbol strings", totalMapTimer); -static Timer writeTimer("Write to file", totalMapTimer); - // Print out the first two columns of a line. static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) { os << format(" %04x:%08llx", sec, addr); @@ -99,9 +95,10 @@ // Returns the lists of all symbols that we want to print out. static void getSymbols(std::vector &syms, - std::vector &staticSyms) { + std::vector &staticSyms, + const COFFLinkerContext &ctx) { - for (ObjFile *file : ObjFile::instances) + for (ObjFile *file : ctx.objFileInstances) for (Symbol *b : file->getSymbols()) { if (!b || !b->isLive()) continue; @@ -119,7 +116,7 @@ } } - for (ImportFile *file : ImportFile::instances) { + for (ImportFile *file : ctx.importFileInstances) { if (!file->live) continue; @@ -142,7 +139,7 @@ // Construct a map from symbols to their stringified representations. static DenseMap -getSymbolStrings(ArrayRef syms) { +getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef syms) { std::vector str(syms.size()); parallelForEachN((size_t)0, syms.size(), [&](size_t i) { raw_string_ostream os(str[i]); @@ -161,7 +158,7 @@ fileDescr = ""; } else if (Chunk *chunk = sym->getChunk()) { address = sym->getRVA(); - if (OutputSection *sec = chunk->getOutputSection()) + if (OutputSection *sec = ctx.getOutputSection(chunk)) address -= sec->header.VirtualAddress; sectionIdx = chunk->getOutputSectionIdx(); @@ -201,7 +198,7 @@ return ret; } -void lld::coff::writeMapFile(ArrayRef outputSections) { +void lld::coff::writeMapFile(COFFLinkerContext &ctx) { if (config->mapFile.empty()) return; @@ -210,21 +207,22 @@ if (ec) fatal("cannot open " + config->mapFile + ": " + ec.message()); - ScopedTimer t1(totalMapTimer); + ScopedTimer t1(ctx.totalMapTimer); // Collect symbol info that we want to print out. - ScopedTimer t2(symbolGatherTimer); + ScopedTimer t2(ctx.symbolGatherTimer); std::vector syms; std::vector staticSyms; - getSymbols(syms, staticSyms); + getSymbols(syms, staticSyms, ctx); t2.stop(); - ScopedTimer t3(symbolStringsTimer); - DenseMap symStr = getSymbolStrings(syms); - DenseMap staticSymStr = getSymbolStrings(staticSyms); + ScopedTimer t3(ctx.symbolStringsTimer); + DenseMap symStr = getSymbolStrings(ctx, syms); + DenseMap staticSymStr = + getSymbolStrings(ctx, staticSyms); t3.stop(); - ScopedTimer t4(writeTimer); + ScopedTimer t4(ctx.writeTimer); SmallString<128> AppName = sys::path::filename(config->outputFile); sys::path::replace_extension(AppName, ""); @@ -248,7 +246,7 @@ // Print out section table. os << " Start Length Name Class\n"; - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { // Merge display of chunks with same sectionName std::vector> ChunkRanges; for (Chunk *c : sec->chunks) { @@ -303,7 +301,7 @@ Chunk *chunk = entry->getChunk(); entrySecIndex = chunk->getOutputSectionIdx(); entryAddress = - entry->getRVA() - chunk->getOutputSection()->header.VirtualAddress; + entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress; } } os << " entry point at "; diff --git a/lld/COFF/MarkLive.h b/lld/COFF/MarkLive.h --- a/lld/COFF/MarkLive.h +++ b/lld/COFF/MarkLive.h @@ -10,14 +10,13 @@ #define LLD_COFF_MARKLIVE_H #include "lld/Common/LLVM.h" -#include "llvm/ADT/ArrayRef.h" namespace lld { namespace coff { -class Chunk; +class COFFLinkerContext; -void markLive(ArrayRef chunks); +void markLive(COFFLinkerContext &ctx); } // namespace coff } // namespace lld diff --git a/lld/COFF/MarkLive.cpp b/lld/COFF/MarkLive.cpp --- a/lld/COFF/MarkLive.cpp +++ b/lld/COFF/MarkLive.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "COFFLinkerContext.h" #include "Chunks.h" #include "Symbols.h" #include "lld/Common/Timer.h" @@ -15,13 +16,11 @@ namespace lld { namespace coff { -static Timer gctimer("GC", Timer::root()); - // Set live bit on for each reachable chunk. Unmarked (unreachable) // COMDAT chunks will be ignored by Writer, so they will be excluded // from the final output. -void markLive(ArrayRef chunks) { - ScopedTimer t(gctimer); +void markLive(COFFLinkerContext &ctx) { + ScopedTimer t(ctx.gcTimer); // We build up a worklist of sections which have been marked as live. We only // push into the worklist when we discover an unmarked section, and we mark @@ -31,7 +30,7 @@ // COMDAT section chunks are dead by default. Add non-COMDAT chunks. Do not // traverse DWARF sections. They are live, but they should not keep other // sections alive. - for (Chunk *c : chunks) + for (Chunk *c : ctx.symtab.getChunks()) if (auto *sc = dyn_cast(c)) if (sc->live && !sc->isDWARF()) worklist.push_back(sc); @@ -70,6 +69,5 @@ enqueue(&c); } } - } } diff --git a/lld/COFF/MinGW.h b/lld/COFF/MinGW.h --- a/lld/COFF/MinGW.h +++ b/lld/COFF/MinGW.h @@ -19,6 +19,7 @@ namespace lld { namespace coff { +class COFFLinkerContext; // Logic for deciding what symbols to export, when exporting all // symbols for MinGW. @@ -34,7 +35,7 @@ llvm::StringSet<> excludeLibs; llvm::StringSet<> excludeObjects; - bool shouldExport(Defined *sym) const; + bool shouldExport(const COFFLinkerContext &ctx, Defined *sym) const; }; void writeDefFile(StringRef name); @@ -53,9 +54,10 @@ Symbol *wrap; }; -std::vector addWrappedSymbols(llvm::opt::InputArgList &args); +std::vector addWrappedSymbols(COFFLinkerContext &ctx, + llvm::opt::InputArgList &args); -void wrapSymbols(ArrayRef wrapped); +void wrapSymbols(COFFLinkerContext &ctx, ArrayRef wrapped); } // namespace coff } // namespace lld diff --git a/lld/COFF/MinGW.cpp b/lld/COFF/MinGW.cpp --- a/lld/COFF/MinGW.cpp +++ b/lld/COFF/MinGW.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "MinGW.h" +#include "COFFLinkerContext.h" #include "Driver.h" #include "InputFiles.h" #include "SymbolTable.h" @@ -122,7 +123,8 @@ excludeLibs.erase(libName); } -bool AutoExporter::shouldExport(Defined *sym) const { +bool AutoExporter::shouldExport(const COFFLinkerContext &ctx, + Defined *sym) const { if (!sym || !sym->getChunk()) return false; @@ -141,7 +143,7 @@ return false; // If a corresponding __imp_ symbol exists and is defined, don't export it. - if (symtab->find(("__imp_" + sym->getName()).str())) + if (ctx.symtab.find(("__imp_" + sym->getName()).str())) return false; // Check that file is non-null before dereferencing it, symbols not @@ -192,7 +194,7 @@ // like they are not being used at all, so we explicitly set some flags so // that LTO won't eliminate them. std::vector -lld::coff::addWrappedSymbols(opt::InputArgList &args) { +lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) { std::vector v; DenseSet seen; @@ -201,18 +203,18 @@ if (!seen.insert(name).second) continue; - Symbol *sym = symtab->findUnderscore(name); + Symbol *sym = ctx.symtab.findUnderscore(name); if (!sym) continue; - Symbol *real = symtab->addUndefined(mangle("__real_" + name)); - Symbol *wrap = symtab->addUndefined(mangle("__wrap_" + name)); + Symbol *real = ctx.symtab.addUndefined(mangle("__real_" + name)); + Symbol *wrap = ctx.symtab.addUndefined(mangle("__wrap_" + name)); v.push_back({sym, real, wrap}); // These symbols may seem undefined initially, but don't bail out - // at symtab->reportUnresolvable() due to them, but let wrapSymbols + // at symtab.reportUnresolvable() due to them, but let wrapSymbols // below sort things out before checking finally with - // symtab->resolveRemainingUndefines(). + // symtab.resolveRemainingUndefines(). sym->deferUndefined = true; real->deferUndefined = true; // We want to tell LTO not to inline symbols to be overwritten @@ -233,13 +235,14 @@ // When this function is executed, only InputFiles and symbol table // contain pointers to symbol objects. We visit them to replace pointers, // so that wrapped symbols are swapped as instructed by the command line. -void lld::coff::wrapSymbols(ArrayRef wrapped) { +void lld::coff::wrapSymbols(COFFLinkerContext &ctx, + ArrayRef wrapped) { DenseMap map; for (const WrappedSymbol &w : wrapped) { map[w.sym] = w.wrap; map[w.real] = w.sym; if (Defined *d = dyn_cast(w.wrap)) { - Symbol *imp = symtab->find(("__imp_" + w.sym->getName()).str()); + Symbol *imp = ctx.symtab.find(("__imp_" + w.sym->getName()).str()); // Create a new defined local import for the wrap symbol. If // no imp prefixed symbol existed, there's no need for it. // (We can't easily distinguish whether any object file actually @@ -247,14 +250,14 @@ if (imp) { DefinedLocalImport *wrapimp = make( saver.save("__imp_" + w.wrap->getName()), d); - symtab->localImportChunks.push_back(wrapimp->getChunk()); + ctx.symtab.localImportChunks.push_back(wrapimp->getChunk()); map[imp] = wrapimp; } } } // Update pointers in input files. - parallelForEach(ObjFile::instances, [&](ObjFile *file) { + parallelForEach(ctx.objFileInstances, [&](ObjFile *file) { MutableArrayRef syms = file->getMutableSymbols(); for (size_t i = 0, e = syms.size(); i != e; ++i) if (Symbol *s = map.lookup(syms[i])) diff --git a/lld/COFF/PDB.h b/lld/COFF/PDB.h --- a/lld/COFF/PDB.h +++ b/lld/COFF/PDB.h @@ -23,21 +23,15 @@ class Timer; namespace coff { -class OutputSection; class SectionChunk; -class SymbolTable; +class COFFLinkerContext; -void createPDB(SymbolTable *symtab, - llvm::ArrayRef outputSections, - llvm::ArrayRef sectionTable, +void createPDB(COFFLinkerContext &ctx, llvm::ArrayRef sectionTable, llvm::codeview::DebugInfo *buildId); llvm::Optional> getFileLineCodeView(const SectionChunk *c, uint32_t addr); -extern Timer loadGHashTimer; -extern Timer mergeGHashTimer; - } // namespace coff } // namespace lld diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp --- a/lld/COFF/PDB.cpp +++ b/lld/COFF/PDB.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "PDB.h" +#include "COFFLinkerContext.h" #include "Chunks.h" #include "Config.h" #include "DebugTypes.h" @@ -66,16 +67,6 @@ static ExitOnError exitOnErr; -static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root()); -static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer); -Timer lld::coff::loadGHashTimer("Global Type Hashing", addObjectsTimer); -Timer lld::coff::mergeGHashTimer("GHash Type Merging", addObjectsTimer); -static Timer typeMergingTimer("Type Merging", addObjectsTimer); -static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer); -static Timer publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer); -static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer); -static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer); - namespace { class DebugSHandler; @@ -83,8 +74,8 @@ friend DebugSHandler; public: - PDBLinker(SymbolTable *symtab) - : symtab(symtab), builder(bAlloc), tMerger(bAlloc) { + PDBLinker(COFFLinkerContext &ctx) + : builder(bAlloc), tMerger(ctx, bAlloc), ctx(ctx) { // This isn't strictly necessary, but link.exe usually puts an empty string // as the first "valid" string in the string table, so we do the same in // order to maintain as much byte-for-byte compatibility as possible. @@ -107,7 +98,7 @@ void addPublicsToPDB(); /// Link info for each import file in the symbol table into the PDB. - void addImportFilesToPDB(ArrayRef outputSections); + void addImportFilesToPDB(); void createModuleDBI(ObjFile *file); @@ -144,8 +135,7 @@ std::vector &storage); /// Add the section map and section contributions to the PDB. - void addSections(ArrayRef outputSections, - ArrayRef sectionTable); + void addSections(ArrayRef sectionTable); /// Write the PDB to disk and store the Guid generated for it in *Guid. void commit(codeview::GUID *guid); @@ -154,12 +144,13 @@ void printStats(); private: - SymbolTable *symtab; pdb::PDBFileBuilder builder; TypeMerger tMerger; + COFFLinkerContext &ctx; + /// PDBs use a single global string table for filenames in the file checksum /// table. DebugStringTableSubsection pdbStrTab; @@ -299,11 +290,12 @@ }); } -static void addGHashTypeInfo(pdb::PDBFileBuilder &builder) { +static void addGHashTypeInfo(COFFLinkerContext &ctx, + pdb::PDBFileBuilder &builder) { // Start the TPI or IPI stream header. builder.getTpiBuilder().setVersionHeader(pdb::PdbTpiV80); builder.getIpiBuilder().setVersionHeader(pdb::PdbTpiV80); - for_each(TpiSource::instances, [&](TpiSource *source) { + for_each(ctx.tpiSourceList, [&](TpiSource *source) { builder.getTpiBuilder().addTypeRecords(source->mergedTpi.recs, source->mergedTpi.recSizes, source->mergedTpi.recHashes); @@ -719,8 +711,9 @@ static_cast(obj), writer); } -static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) { - OutputSection *os = c ? c->getOutputSection() : nullptr; +static pdb::SectionContrib createSectionContrib(COFFLinkerContext &ctx, + const Chunk *c, uint32_t modi) { + OutputSection *os = c ? ctx.getOutputSection(c) : nullptr; pdb::SectionContrib sc; memset(&sc, 0, sizeof(sc)); sc.ISect = os ? os->sectionIndex : llvm::pdb::kInvalidStreamIndex; @@ -1023,7 +1016,7 @@ if (!source->file) return; - ScopedTimer t(symbolMergingTimer); + ScopedTimer t(ctx.symbolMergingTimer); pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); DebugSHandler dsh(*this, *source->file, source); // Now do all live .debug$S and .debug$F sections. @@ -1082,7 +1075,7 @@ auto *secChunk = dyn_cast(c); if (!secChunk || !secChunk->live) continue; - pdb::SectionContrib sc = createSectionContrib(secChunk, modi); + pdb::SectionContrib sc = createSectionContrib(ctx, secChunk, modi); file->moduleDBI->setFirstSectionContrib(sc); break; } @@ -1095,7 +1088,7 @@ // indices to PDB type and item indices. If we are using ghashes, types have // already been merged. if (!config->debugGHashes) { - ScopedTimer t(typeMergingTimer); + ScopedTimer t(ctx.typeMergingTimer); if (Error e = source->mergeDebugT(&tMerger)) { // If type merging failed, ignore the symbols. warnUnusable(source->file, std::move(e)); @@ -1113,7 +1106,7 @@ addDebugSymbols(source); } -static pdb::BulkPublic createPublic(Defined *def) { +static pdb::BulkPublic createPublic(COFFLinkerContext &ctx, Defined *def) { pdb::BulkPublic pub; pub.Name = def->getName().data(); pub.NameLen = def->getName().size(); @@ -1127,7 +1120,7 @@ } pub.setFlags(flags); - OutputSection *os = def->getChunk()->getOutputSection(); + OutputSection *os = ctx.getOutputSection(def->getChunk()); assert(os && "all publics should be in final image"); pub.Offset = def->getRVA() - os->getRVA(); pub.Segment = os->sectionIndex; @@ -1137,32 +1130,31 @@ // Add all object files to the PDB. Merge .debug$T sections into IpiData and // TpiData. void PDBLinker::addObjectsToPDB() { - ScopedTimer t1(addObjectsTimer); + ScopedTimer t1(ctx.addObjectsTimer); // Create module descriptors - for_each(ObjFile::instances, [&](ObjFile *obj) { createModuleDBI(obj); }); + for_each(ctx.objFileInstances, [&](ObjFile *obj) { createModuleDBI(obj); }); // Reorder dependency type sources to come first. - TpiSource::sortDependencies(); + tMerger.sortDependencies(); // Merge type information from input files using global type hashing. if (config->debugGHashes) tMerger.mergeTypesWithGHash(); // Merge dependencies and then regular objects. - for_each(TpiSource::dependencySources, - [&](TpiSource *source) { addDebug(source); }); - for_each(TpiSource::objectSources, + for_each(tMerger.dependencySources, [&](TpiSource *source) { addDebug(source); }); + for_each(tMerger.objectSources, [&](TpiSource *source) { addDebug(source); }); builder.getStringTableBuilder().setStrings(pdbStrTab); t1.stop(); // Construct TPI and IPI stream contents. - ScopedTimer t2(tpiStreamLayoutTimer); + ScopedTimer t2(ctx.tpiStreamLayoutTimer); // Collect all the merged types. if (config->debugGHashes) { - addGHashTypeInfo(builder); + addGHashTypeInfo(ctx, builder); } else { addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable()); addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable()); @@ -1170,7 +1162,7 @@ t2.stop(); if (config->showSummary) { - for_each(TpiSource::instances, [&](TpiSource *source) { + for_each(ctx.tpiSourceList, [&](TpiSource *source) { nbTypeRecords += source->nbTypeRecords; nbTypeRecordsBytes += source->nbTypeRecordsBytes; }); @@ -1178,11 +1170,11 @@ } void PDBLinker::addPublicsToPDB() { - ScopedTimer t3(publicsLayoutTimer); + ScopedTimer t3(ctx.publicsLayoutTimer); // Compute the public symbols. auto &gsiBuilder = builder.getGsiBuilder(); std::vector publics; - symtab->forEachSymbol([&publics](Symbol *s) { + ctx.symtab.forEachSymbol([&publics, this](Symbol *s) { // Only emit external, defined, live symbols that have a chunk. Static, // non-external symbols do not appear in the symbol table. auto *def = dyn_cast(s); @@ -1203,7 +1195,7 @@ return; } } - publics.push_back(createPublic(def)); + publics.push_back(createPublic(ctx, def)); } }); @@ -1227,10 +1219,10 @@ stream << format_decimal(v, 15) << " " << s << '\n'; }; - print(ObjFile::instances.size(), + print(ctx.objFileInstances.size(), "Input OBJ files (expanded from all cmd-line inputs)"); - print(TpiSource::countTypeServerPDBs(), "PDB type server dependencies"); - print(TpiSource::countPrecompObjs(), "Precomp OBJ dependencies"); + print(ctx.typeServerSourceMappings.size(), "PDB type server dependencies"); + print(ctx.precompSourceMappings.size(), "Precomp OBJ dependencies"); print(nbTypeRecords, "Input type records"); print(nbTypeRecordsBytes, "Input type records bytes"); print(builder.getTpiBuilder().getRecordCount(), "Merged TPI records"); @@ -1483,13 +1475,13 @@ } // Add all import files as modules to the PDB. -void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { - if (ImportFile::instances.empty()) +void PDBLinker::addImportFilesToPDB() { + if (ctx.importFileInstances.empty()) return; std::map dllToModuleDbi; - for (ImportFile *file : ImportFile::instances) { + for (ImportFile *file : ctx.importFileInstances) { if (!file->live) continue; @@ -1513,7 +1505,7 @@ exitOnErr(dbiBuilder.addModuleInfo(file->dllName)); firstMod.setObjFileName(libPath); pdb::SectionContrib sc = - createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex); + createSectionContrib(ctx, nullptr, llvm::pdb::kInvalidStreamIndex); firstMod.setFirstSectionContrib(sc); // The second module is where the import stream goes. @@ -1523,7 +1515,7 @@ DefinedImportThunk *thunk = cast(file->thunkSym); Chunk *thunkChunk = thunk->getChunk(); - OutputSection *thunkOS = thunkChunk->getOutputSection(); + OutputSection *thunkOS = ctx.getOutputSection(thunkChunk); ObjNameSym ons(SymbolRecordKind::ObjNameSym); Compile3Sym cs(SymbolRecordKind::Compile3Sym); @@ -1565,28 +1557,27 @@ mod->addSymbol(newSym); pdb::SectionContrib sc = - createSectionContrib(thunk->getChunk(), mod->getModuleIndex()); + createSectionContrib(ctx, thunk->getChunk(), mod->getModuleIndex()); mod->setFirstSectionContrib(sc); } } // Creates a PDB file. -void lld::coff::createPDB(SymbolTable *symtab, - ArrayRef outputSections, +void lld::coff::createPDB(COFFLinkerContext &ctx, ArrayRef sectionTable, llvm::codeview::DebugInfo *buildId) { - ScopedTimer t1(totalPdbLinkTimer); - PDBLinker pdb(symtab); + ScopedTimer t1(ctx.totalPdbLinkTimer); + PDBLinker pdb(ctx); pdb.initialize(buildId); pdb.addObjectsToPDB(); - pdb.addImportFilesToPDB(outputSections); - pdb.addSections(outputSections, sectionTable); + pdb.addImportFilesToPDB(); + pdb.addSections(sectionTable); pdb.addNatvisFiles(); pdb.addNamedStreams(); pdb.addPublicsToPDB(); - ScopedTimer t2(diskCommitTimer); + ScopedTimer t2(ctx.diskCommitTimer); codeview::GUID guid; pdb.commit(&guid); memcpy(&buildId->PDB70.Signature, &guid, 16); @@ -1626,8 +1617,7 @@ dbiBuilder.setBuildNumber(14, 11); } -void PDBLinker::addSections(ArrayRef outputSections, - ArrayRef sectionTable) { +void PDBLinker::addSections(ArrayRef sectionTable) { // It's not entirely clear what this is, but the * Linker * module uses it. pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); nativePath = config->pdbPath; @@ -1638,11 +1628,11 @@ addCommonLinkerModuleSymbols(nativePath, linkerModule); // Add section contributions. They must be ordered by ascending RVA. - for (OutputSection *os : outputSections) { + for (OutputSection *os : ctx.outputSections) { addLinkerModuleSectionSymbol(linkerModule, *os); for (Chunk *c : os->chunks) { pdb::SectionContrib sc = - createSectionContrib(c, linkerModule.getModuleIndex()); + createSectionContrib(ctx, c, linkerModule.getModuleIndex()); builder.getDbiBuilder().addSectionContrib(sc); } } @@ -1651,7 +1641,7 @@ // to provide trampolines thunks for incremental function patching. Set this // as "unused" because LLD doesn't support /INCREMENTAL link. pdb::SectionContrib sc = - createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex); + createSectionContrib(ctx, nullptr, llvm::pdb::kInvalidStreamIndex); linkerModule.setFirstSectionContrib(sc); // Add Section Map stream. diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -25,6 +25,7 @@ class Chunk; class CommonChunk; +class COFFLinkerContext; class Defined; class DefinedAbsolute; class DefinedRegular; @@ -47,6 +48,8 @@ // There is one add* function per symbol type. class SymbolTable { public: + SymbolTable(COFFLinkerContext &ctx) : ctx(ctx) {} + void addFile(InputFile *file); // Emit errors for symbols that cannot be resolved. @@ -63,11 +66,11 @@ bool handleMinGWAutomaticImport(Symbol *sym, StringRef name); // Returns a list of chunks of selected symbols. - std::vector getChunks(); + std::vector getChunks() const; // Returns a symbol for a given name. Returns a nullptr if not found. - Symbol *find(StringRef name); - Symbol *findUnderscore(StringRef name); + Symbol *find(StringRef name) const; + Symbol *findUnderscore(StringRef name) const; // Occasionally we have to resolve an undefined symbol to its // mangled symbol. This function tries to find a mangled name @@ -131,9 +134,9 @@ llvm::DenseMap symMap; std::unique_ptr lto; -}; -extern SymbolTable *symtab; + COFFLinkerContext &ctx; +}; std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex); diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "SymbolTable.h" +#include "COFFLinkerContext.h" #include "Config.h" #include "Driver.h" #include "LTO.h" @@ -34,10 +35,6 @@ return s; } -static Timer ltoTimer("LTO", Timer::root()); - -SymbolTable *symtab; - void SymbolTable::addFile(InputFile *file) { log("Reading " + toString(file)); file->parse(); @@ -52,11 +49,11 @@ } if (auto *f = dyn_cast(file)) { - ObjFile::instances.push_back(f); + ctx.objFileInstances.push_back(f); } else if (auto *f = dyn_cast(file)) { - BitcodeFile::instances.push_back(f); + ctx.bitcodeFileInstances.push_back(f); } else if (auto *f = dyn_cast(file)) { - ImportFile::instances.push_back(f); + ctx.importFileInstances.push_back(f); } driver->parseDirectives(file); @@ -372,12 +369,9 @@ /// 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 SmallPtrSetImpl &undefs, - const DenseMap *localImports, - const std::vector objFiles, - const std::vector *bitcodeFiles) { - +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())) @@ -418,11 +412,11 @@ } }; - for (ObjFile *file : objFiles) + for (ObjFile *file : ctx.objFileInstances) processFile(file, file->getSymbols()); - if (bitcodeFiles) - for (BitcodeFile *file : *bitcodeFiles) + if (needBitcodeFiles) + for (BitcodeFile *file : ctx.bitcodeFileInstances) processFile(file, file->getSymbols()); for (const UndefinedDiag &undefDiag : undefDiags) @@ -451,9 +445,8 @@ undefs.insert(sym); } - reportProblemSymbols(undefs, - /* localImports */ nullptr, ObjFile::instances, - &BitcodeFile::instances); + reportProblemSymbols(ctx, undefs, + /* localImports */ nullptr, true); } void SymbolTable::resolveRemainingUndefines() { @@ -515,8 +508,8 @@ } reportProblemSymbols( - undefs, config->warnLocallyDefinedImported ? &localImports : nullptr, - ObjFile::instances, /* bitcode files no longer needed */ nullptr); + ctx, undefs, config->warnLocallyDefinedImported ? &localImports : nullptr, + false); } std::pair SymbolTable::insert(StringRef name) { @@ -797,20 +790,20 @@ } } -std::vector SymbolTable::getChunks() { +std::vector SymbolTable::getChunks() const { std::vector res; - for (ObjFile *file : ObjFile::instances) { + for (ObjFile *file : ctx.objFileInstances) { ArrayRef v = file->getChunks(); res.insert(res.end(), v.begin(), v.end()); } return res; } -Symbol *SymbolTable::find(StringRef name) { +Symbol *SymbolTable::find(StringRef name) const { return symMap.lookup(CachedHashStringRef(name)); } -Symbol *SymbolTable::findUnderscore(StringRef name) { +Symbol *SymbolTable::findUnderscore(StringRef name) const { if (config->machine == I386) return find(("_" + name).str()); return find(name); @@ -873,17 +866,17 @@ } void SymbolTable::addCombinedLTOObjects() { - if (BitcodeFile::instances.empty()) + if (ctx.bitcodeFileInstances.empty()) return; - ScopedTimer t(ltoTimer); - lto.reset(new BitcodeCompiler); - for (BitcodeFile *f : BitcodeFile::instances) + ScopedTimer t(ctx.ltoTimer); + lto.reset(new BitcodeCompiler()); + for (BitcodeFile *f : ctx.bitcodeFileInstances) lto->add(*f); - for (InputFile *newObj : lto->compile()) { + for (InputFile *newObj : lto->compile(ctx)) { ObjFile *obj = cast(newObj); obj->parse(); - ObjFile::instances.push_back(obj); + ctx.objFileInstances.push_back(obj); } } diff --git a/lld/COFF/TypeMerger.h b/lld/COFF/TypeMerger.h --- a/lld/COFF/TypeMerger.h +++ b/lld/COFF/TypeMerger.h @@ -10,6 +10,8 @@ #define LLD_COFF_TYPEMERGER_H #include "Config.h" +#include "DebugTypes.h" +#include "lld/Common/Timer.h" #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeHashing.h" #include "llvm/Support/Allocator.h" @@ -25,7 +27,7 @@ class TypeMerger { public: - TypeMerger(llvm::BumpPtrAllocator &alloc); + TypeMerger(COFFLinkerContext &ctx, llvm::BumpPtrAllocator &alloc); ~TypeMerger(); @@ -59,6 +61,20 @@ // keyed by type index. SmallVector tpiCounts; SmallVector ipiCounts; + + /// Dependency type sources, such as type servers or PCH object files. These + /// must be processed before objects that rely on them. Set by + /// sortDependencies. + ArrayRef dependencySources; + + /// Object file sources. These must be processed after dependencySources. + ArrayRef objectSources; + + /// Sorts the dependencies and reassigns TpiSource indices. + void sortDependencies(); + +private: + COFFLinkerContext &ctx; }; } // namespace coff diff --git a/lld/COFF/Writer.h b/lld/COFF/Writer.h --- a/lld/COFF/Writer.h +++ b/lld/COFF/Writer.h @@ -19,8 +19,9 @@ namespace lld { namespace coff { static const int pageSize = 4096; +class COFFLinkerContext; -void writeResult(); +void writeResult(COFFLinkerContext &ctx); class PartialSection { public: @@ -50,9 +51,6 @@ void writeHeaderTo(uint8_t *buf); void addContributingPartialSection(PartialSection *sec); - // Clear the output sections static container. - static void clear(); - // Returns the size of this section in an executable memory image. // This may be smaller than the raw size (the raw size is multiple // of disk sector size, so there may be padding at end), or may be diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Writer.h" +#include "COFFLinkerContext.h" #include "CallGraphSort.h" #include "Config.h" #include "DLL.h" @@ -80,23 +81,14 @@ static const int numberOfDataDirectory = 16; -// Global vector of all output sections. After output sections are finalized, -// this can be indexed by Chunk::getOutputSection. -static std::vector outputSections; - -OutputSection *Chunk::getOutputSection() const { - return osidx == 0 ? nullptr : outputSections[osidx - 1]; -} - -void OutputSection::clear() { outputSections.clear(); } - namespace { class DebugDirectoryChunk : public NonSectionChunk { public: - DebugDirectoryChunk(const std::vector> &r, + DebugDirectoryChunk(COFFLinkerContext &c, + const std::vector> &r, bool writeRepro) - : records(r), writeRepro(writeRepro) {} + : records(r), writeRepro(writeRepro), ctx(c) {} size_t getSize() const override { return (records.size() + int(writeRepro)) * sizeof(debug_directory); @@ -107,7 +99,7 @@ for (const std::pair& record : records) { Chunk *c = record.second; - OutputSection *os = c->getOutputSection(); + OutputSection *os = ctx.getOutputSection(c); uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA()); fillEntry(d, record.first, c->getSize(), c->getRVA(), offs); ++d; @@ -146,6 +138,8 @@ mutable std::vector timeDateStamps; const std::vector> &records; bool writeRepro; + + COFFLinkerContext &ctx; }; class CVDebugRecordChunk : public NonSectionChunk { @@ -201,7 +195,7 @@ // The writer writes a SymbolTable result to a file. class Writer { public: - Writer() : buffer(errorHandler().outputBuffer) {} + Writer(COFFLinkerContext &c) : buffer(errorHandler().outputBuffer), ctx(c) {} void run(); private: @@ -304,13 +298,12 @@ // files, so we need to keep track of them separately. Chunk *firstPdata = nullptr; Chunk *lastPdata; + + COFFLinkerContext &ctx; }; } // anonymous namespace -static Timer codeLayoutTimer("Code Layout", Timer::root()); -static Timer diskCommitTimer("Commit Output File", Timer::root()); - -void lld::coff::writeResult() { Writer().run(); } +void lld::coff::writeResult(COFFLinkerContext &ctx) { Writer(ctx).run(); } void OutputSection::addChunk(Chunk *c) { chunks.push_back(c); @@ -549,7 +542,7 @@ return; size_t origNumChunks = 0; - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { sec->origChunks = sec->chunks; origNumChunks += sec->chunks.size(); } @@ -561,7 +554,7 @@ // adding them turned out ok. bool rangesOk = true; size_t numChunks = 0; - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { if (!verifyRanges(sec->chunks)) { rangesOk = false; break; @@ -582,7 +575,7 @@ // If the previous pass didn't work out, reset everything back to the // original conditions before retrying with a wider margin. This should // ideally never happen under real circumstances. - for (OutputSection *sec : outputSections) + for (OutputSection *sec : ctx.outputSections) sec->chunks = sec->origChunks; margin *= 2; } @@ -590,7 +583,7 @@ // Try adding thunks everywhere where it is needed, with a margin // to avoid things going out of range due to the added thunks. bool addressesChanged = false; - for (OutputSection *sec : outputSections) + for (OutputSection *sec : ctx.outputSections) addressesChanged |= createThunks(sec, margin); // If the verification above thought we needed thunks, we should have // added some. @@ -607,7 +600,7 @@ // The main function of the writer. void Writer::run() { - ScopedTimer t1(codeLayoutTimer); + ScopedTimer t1(ctx.codeLayoutTimer); createImportTables(); createSections(); @@ -645,17 +638,17 @@ if (!config->pdbPath.empty() && config->debug) { assert(buildId); - createPDB(symtab, outputSections, sectionTable, buildId->buildId); + createPDB(ctx, sectionTable, buildId->buildId); } writeBuildId(); - writeLLDMapFile(outputSections); - writeMapFile(outputSections); + writeLLDMapFile(ctx); + writeMapFile(ctx); if (errorCount()) return; - ScopedTimer t2(diskCommitTimer); + ScopedTimer t2(ctx.outputCommitTimer); if (auto e = buffer->commit()) fatal("failed to write the output file: " + toString(std::move(e))); } @@ -816,7 +809,8 @@ void Writer::sortSections() { if (!config->callGraphProfile.empty()) { - DenseMap order = computeCallGraphProfileOrder(); + DenseMap order = + computeCallGraphProfileOrder(ctx); for (auto it : order) { if (DefinedRegular *sym = it.first->sym) config->order[sym->getName()] = it.second; @@ -843,7 +837,7 @@ OutputSection *&sec = sections[{name, outChars}]; if (!sec) { sec = make(name, outChars); - outputSections.push_back(sec); + ctx.outputSections.push_back(sec); } return sec; }; @@ -864,7 +858,7 @@ dtorsSec = createSection(".dtors", data | r | w); // Then bin chunks by name and output characteristics. - for (Chunk *c : symtab->getChunks()) { + for (Chunk *c : ctx.symtab.getChunks()) { auto *sc = dyn_cast(c); if (sc && !sc->live) { if (config->verbose) @@ -941,14 +935,14 @@ return 1; return 0; }; - llvm::stable_sort(outputSections, + llvm::stable_sort(ctx.outputSections, [&](const OutputSection *s, const OutputSection *t) { return sectionOrder(s) < sectionOrder(t); }); } void Writer::createMiscChunks() { - for (MergeChunk *p : MergeChunk::instances) { + for (MergeChunk *p : ctx.mergeChunkInstances) { if (p) { p->finalizeContents(); rdataSec->addChunk(p); @@ -956,15 +950,16 @@ } // Create thunks for locally-dllimported symbols. - if (!symtab->localImportChunks.empty()) { - for (Chunk *c : symtab->localImportChunks) + if (!ctx.symtab.localImportChunks.empty()) { + for (Chunk *c : ctx.symtab.localImportChunks) rdataSec->addChunk(c); } // Create Debug Information Chunks OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec; if (config->debug || config->repro || config->cetCompat) { - debugDirectory = make(debugRecords, config->repro); + debugDirectory = + make(ctx, debugRecords, config->repro); debugDirectory->setAlignment(4); debugInfoSec->addChunk(debugDirectory); } @@ -1013,7 +1008,7 @@ // Initialize DLLOrder so that import entries are ordered in // the same order as in the command line. (That affects DLL // initialization order, and this ordering is MSVC-compatible.) - for (ImportFile *file : ImportFile::instances) { + for (ImportFile *file : ctx.importFileInstances) { if (!file->live) continue; @@ -1036,10 +1031,10 @@ } void Writer::appendImportThunks() { - if (ImportFile::instances.empty()) + if (ctx.importFileInstances.empty()) return; - for (ImportFile *file : ImportFile::instances) { + for (ImportFile *file : ctx.importFileInstances) { if (!file->live) continue; @@ -1055,7 +1050,7 @@ if (!delayIdata.empty()) { Defined *helper = cast(config->delayLoadHelper); - delayIdata.create(helper); + delayIdata.create(ctx, helper); for (Chunk *c : delayIdata.getChunks()) didatSec->addChunk(c); for (Chunk *c : delayIdata.getDataChunks()) @@ -1095,25 +1090,25 @@ // later. Only remove sections that have no Chunks at all. return s->chunks.empty(); }; - outputSections.erase( - std::remove_if(outputSections.begin(), outputSections.end(), isUnused), - outputSections.end()); + ctx.outputSections.erase(std::remove_if(ctx.outputSections.begin(), + ctx.outputSections.end(), isUnused), + ctx.outputSections.end()); } // The Windows loader doesn't seem to like empty sections, // so we remove them if any. void Writer::removeEmptySections() { auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; }; - outputSections.erase( - std::remove_if(outputSections.begin(), outputSections.end(), isEmpty), - outputSections.end()); + ctx.outputSections.erase(std::remove_if(ctx.outputSections.begin(), + ctx.outputSections.end(), isEmpty), + ctx.outputSections.end()); } void Writer::assignOutputSectionIndices() { // Assign final output section indices, and assign each chunk to its output // section. uint32_t idx = 1; - for (OutputSection *os : outputSections) { + for (OutputSection *os : ctx.outputSections) { os->sectionIndex = idx; for (Chunk *c : os->chunks) c->setOutputSectionIdx(idx); @@ -1122,7 +1117,7 @@ // Merge chunks are containers of chunks, so assign those an output section // too. - for (MergeChunk *mc : MergeChunk::instances) + for (MergeChunk *mc : ctx.mergeChunkInstances) if (mc) for (SectionChunk *sc : mc->sections) if (sc && sc->live) @@ -1153,7 +1148,7 @@ Chunk *c = def->getChunk(); if (!c) return None; - OutputSection *os = c->getOutputSection(); + OutputSection *os = ctx.getOutputSection(c); if (!os) return None; @@ -1200,7 +1195,7 @@ // solution where discardable sections have long names preserved and // non-discardable sections have their names truncated, to ensure that any // section which is mapped at runtime also has its name mapped at runtime. - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { if (sec->name.size() <= COFF::NameSize) continue; if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) @@ -1214,7 +1209,7 @@ } if (config->debugDwarf || config->debugSymtab) { - for (ObjFile *file : ObjFile::instances) { + for (ObjFile *file : ctx.objFileInstances) { for (Symbol *b : file->getSymbols()) { auto *d = dyn_cast_or_null(b); if (!d || d->writtenToSymtab) @@ -1274,7 +1269,7 @@ void Writer::assignAddresses() { sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + sizeof(data_directory) * numberOfDataDirectory + - sizeof(coff_section) * outputSections.size(); + sizeof(coff_section) * ctx.outputSections.size(); sizeOfHeaders += config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign); @@ -1283,7 +1278,7 @@ // The first page is kept unmapped. uint64_t rva = alignTo(sizeOfHeaders, config->align); - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { if (sec == relocSec) addBaserels(); uint64_t rawSize = 0, virtualSize = 0; @@ -1318,7 +1313,7 @@ sizeOfImage = alignTo(rva, config->align); // Assign addresses to sections in MergeChunks. - for (MergeChunk *mc : MergeChunk::instances) + for (MergeChunk *mc : ctx.mergeChunkInstances) if (mc) mc->assignSubsectionRVAs(); } @@ -1353,7 +1348,7 @@ auto *coff = reinterpret_cast(buf); buf += sizeof(*coff); coff->Machine = config->machine; - coff->NumberOfSections = outputSections.size(); + coff->NumberOfSections = ctx.outputSections.size(); coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; if (config->largeAddressAware) coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; @@ -1466,7 +1461,7 @@ dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA(); dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize(); } - if (Symbol *sym = symtab->findUnderscore("_tls_used")) { + if (Symbol *sym = ctx.symtab.findUnderscore("_tls_used")) { if (Defined *b = dyn_cast(sym)) { dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA(); dir[TLS_TABLE].Size = config->is64() @@ -1478,7 +1473,7 @@ dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA(); dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize(); } - if (Symbol *sym = symtab->findUnderscore("_load_config_used")) { + if (Symbol *sym = ctx.symtab.findUnderscore("_load_config_used")) { if (auto *b = dyn_cast(sym)) { SectionChunk *sc = b->getChunk(); assert(b->getRVA() >= sc->getRVA()); @@ -1502,12 +1497,12 @@ } // Write section table - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { sec->writeHeaderTo(buf); buf += sizeof(coff_section); } sectionTable = ArrayRef( - buf - outputSections.size() * sizeof(coff_section), buf); + buf - ctx.outputSections.size() * sizeof(coff_section), buf); if (outputSymtab.empty() && strtab.empty()) return; @@ -1535,7 +1530,7 @@ void Writer::createSEHTable() { SymbolRVASet handlers; - for (ObjFile *file : ObjFile::instances) { + for (ObjFile *file : ctx.objFileInstances) { if (!file->hasSafeSEH()) error("/safeseh: " + file->getName() + " is not compatible with SEH"); markSymbolsForRVATable(file, file->getSXDataChunks(), handlers); @@ -1544,7 +1539,7 @@ // Set the "no SEH" characteristic if there really were no handlers, or if // there is no load config object to point to the table of handlers. setNoSEHCharacteristic = - handlers.empty() || !symtab->findUnderscore("_load_config_used"); + handlers.empty() || !ctx.symtab.findUnderscore("_load_config_used"); maybeAddRVATable(std::move(handlers), "__safe_se_handler_table", "__safe_se_handler_count"); @@ -1642,7 +1637,7 @@ std::vector giatsSymbols; SymbolRVASet longJmpTargets; SymbolRVASet ehContTargets; - for (ObjFile *file : ObjFile::instances) { + for (ObjFile *file : ctx.objFileInstances) { // If the object was compiled with /guard:cf, the address taken symbols // are in .gfids$y sections, the longjmp targets are in .gljmp$y sections, // and ehcont targets are in .gehcont$y sections. If the object was not @@ -1708,7 +1703,7 @@ guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); if (config->guardCF & GuardCFLevel::EHCont) guardFlags |= uint32_t(coff_guard_flags::HasEHContTable); - Symbol *flagSym = symtab->findUnderscore("__guard_flags"); + Symbol *flagSym = ctx.symtab.findUnderscore("__guard_flags"); cast(flagSym)->setVA(guardFlags); } @@ -1780,8 +1775,8 @@ tableChunk = make(std::move(tableSymbols)); rdataSec->addChunk(tableChunk); - Symbol *t = symtab->findUnderscore(tableSym); - Symbol *c = symtab->findUnderscore(countSym); + Symbol *t = ctx.symtab.findUnderscore(tableSym); + Symbol *c = ctx.symtab.findUnderscore(countSym); replaceSymbol(t, t->getName(), tableChunk); cast(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4)); } @@ -1793,7 +1788,7 @@ void Writer::createRuntimePseudoRelocs() { std::vector rels; - for (Chunk *c : symtab->getChunks()) { + for (Chunk *c : ctx.symtab.getChunks()) { auto *sc = dyn_cast(c); if (!sc || !sc->live) continue; @@ -1816,8 +1811,9 @@ EmptyChunk *endOfList = make(); rdataSec->addChunk(endOfList); - Symbol *headSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); - Symbol *endSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); + Symbol *headSym = ctx.symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); + Symbol *endSym = + ctx.symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); replaceSymbol(headSym, headSym->getName(), table); replaceSymbol(endSym, endSym->getName(), endOfList); } @@ -1837,8 +1833,8 @@ dtorsSec->insertChunkAtStart(dtorListHead); dtorsSec->addChunk(dtorListEnd); - Symbol *ctorListSym = symtab->findUnderscore("__CTOR_LIST__"); - Symbol *dtorListSym = symtab->findUnderscore("__DTOR_LIST__"); + Symbol *ctorListSym = ctx.symtab.findUnderscore("__CTOR_LIST__"); + Symbol *dtorListSym = ctx.symtab.findUnderscore("__DTOR_LIST__"); replaceSymbol(ctorListSym, ctorListSym->getName(), ctorListHead); replaceSymbol(dtorListSym, dtorListSym->getName(), @@ -1851,7 +1847,7 @@ for (auto &p : config->section) { StringRef name = p.first; uint32_t perm = p.second; - for (OutputSection *sec : outputSections) + for (OutputSection *sec : ctx.outputSections) if (sec->name == name) sec->setPermissions(perm); } @@ -1861,10 +1857,10 @@ void Writer::writeSections() { // Record the number of sections to apply section index relocations // against absolute symbols. See applySecIdx in Chunks.cpp.. - DefinedAbsolute::numOutputSections = outputSections.size(); + DefinedAbsolute::numOutputSections = ctx.outputSections.size(); uint8_t *buf = buffer->getBufferStart(); - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { uint8_t *secBuf = buf + sec->getFileOff(); // Fill gaps between functions in .text with INT3 instructions // instead of leaving as NUL bytes (which can be interpreted as @@ -1934,7 +1930,7 @@ return; // We assume .pdata contains function table entries only. auto bufAddr = [&](Chunk *c) { - OutputSection *os = c->getOutputSection(); + OutputSection *os = ctx.getOutputSection(c); return buffer->getBufferStart() + os->getFileOff() + c->getRVA() - os->getRVA(); }; @@ -2002,7 +1998,7 @@ } OutputSection *Writer::findSection(StringRef name) { - for (OutputSection *sec : outputSections) + for (OutputSection *sec : ctx.outputSections) if (sec->name == name) return sec; return nullptr; @@ -2010,7 +2006,7 @@ uint32_t Writer::getSizeOfInitializedData() { uint32_t res = 0; - for (OutputSection *s : outputSections) + for (OutputSection *s : ctx.outputSections) if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) res += s->getRawSize(); return res; @@ -2022,7 +2018,7 @@ return; relocSec->chunks.clear(); std::vector v; - for (OutputSection *sec : outputSections) { + for (OutputSection *sec : ctx.outputSections) { if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) continue; // Collect all locations for base relocations. @@ -2071,11 +2067,11 @@ void Writer::fixTlsAlignment() { Defined *tlsSym = - dyn_cast_or_null(symtab->findUnderscore("_tls_used")); + dyn_cast_or_null(ctx.symtab.findUnderscore("_tls_used")); if (!tlsSym) return; - OutputSection *sec = tlsSym->getChunk()->getOutputSection(); + OutputSection *sec = ctx.getOutputSection(tlsSym->getChunk()); assert(sec && tlsSym->getRVA() >= sec->getRVA() && "no output section for _tls_used"); diff --git a/lld/Common/Timer.cpp b/lld/Common/Timer.cpp --- a/lld/Common/Timer.cpp +++ b/lld/Common/Timer.cpp @@ -31,13 +31,8 @@ parent.children.push_back(this); } -Timer &Timer::root() { - static Timer rootTimer("Total Link Time"); - return rootTimer; -} - void Timer::print() { - double totalDuration = static_cast(root().millis()); + double totalDuration = static_cast(millis()); // We want to print the grand total under all the intermediate phases, so we // print all children first, then print the total under that. @@ -47,7 +42,7 @@ message(std::string(50, '-')); - root().print(0, root().millis(), false); + print(0, millis(), false); } double Timer::millis() const { diff --git a/lld/include/lld/Common/Timer.h b/lld/include/lld/Common/Timer.h --- a/lld/include/lld/Common/Timer.h +++ b/lld/include/lld/Common/Timer.h @@ -38,7 +38,8 @@ public: Timer(llvm::StringRef name, Timer &parent); - static Timer &root(); + // Creates the root timer. + explicit Timer(llvm::StringRef name); void addToTotal(std::chrono::nanoseconds time) { total += time.count(); } void print(); @@ -46,7 +47,6 @@ double millis() const; private: - explicit Timer(llvm::StringRef name); void print(int depth, double totalDuration, bool recurse = true) const; std::atomic total; diff --git a/llvm/utils/gn/secondary/lld/COFF/BUILD.gn b/llvm/utils/gn/secondary/lld/COFF/BUILD.gn --- a/llvm/utils/gn/secondary/lld/COFF/BUILD.gn +++ b/llvm/utils/gn/secondary/lld/COFF/BUILD.gn @@ -29,6 +29,7 @@ sources = [ "CallGraphSort.cpp", "Chunks.cpp", + "COFFLinkerContext.cpp", "DLL.cpp", "DebugTypes.cpp", "Driver.cpp",