add_lld_library(lldCOFF
CallGraphSort.cpp
Chunks.cpp
+ COFFLinkerContext.cpp
DebugTypes.cpp
DLL.cpp
Driver.cpp
--- /dev/null
+//===- 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
--- /dev/null
+//===- 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<ObjFile *> objFileInstances;
+ std::map<std::string, PDBInputFile *> pdbInputFileInstances;
+ std::vector<ImportFile *> importFileInstances;
+ std::vector<BitcodeFile *> bitcodeFileInstances;
+
+ MergeChunk *mergeChunkInstances[Log2MaxSectionAlignment + 1] = {};
+
+ /// All sources of type information in the program.
+ std::vector<TpiSource *> tpiSourceList;
+
+ std::map<llvm::codeview::GUID, TpiSource *> typeServerSourceMappings;
+ std::map<uint32_t, TpiSource *> precompSourceMappings;
+
+ /// List of all output sections. After output sections are finalized, this
+ /// can be indexed by getOutputSection.
+ std::vector<OutputSection *> 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
//===----------------------------------------------------------------------===//
#include "CallGraphSort.h"
+#include "COFFLinkerContext.h"
#include "InputFiles.h"
#include "SymbolTable.h"
#include "Symbols.h"
class CallGraphSort {
public:
- CallGraphSort();
+ CallGraphSort(const COFFLinkerContext &ctx);
DenseMap<const SectionChunk *, int> run();
// 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<SectionPair, uint64_t> &profile = config->callGraphProfile;
DenseMap<const SectionChunk *, int> secToCluster;
// 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);
// 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<const SectionChunk *, int> coff::computeCallGraphProfileOrder() {
- return CallGraphSort().run();
+DenseMap<const SectionChunk *, int>
+coff::computeCallGraphProfileOrder(const COFFLinkerContext &ctx) {
+ return CallGraphSort(ctx).run();
}
namespace lld {
namespace coff {
class SectionChunk;
+class COFFLinkerContext;
-llvm::DenseMap<const SectionChunk *, int> computeCallGraphProfileOrder();
+llvm::DenseMap<const SectionChunk *, int>
+computeCallGraphProfileOrder(const COFFLinkerContext &ctx);
} // namespace coff
} // namespace lld
//===----------------------------------------------------------------------===//
#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"
// 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
}
}
-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<MergeChunk>(c->getAlignment());
mc->sections.push_back(c);
// 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.
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();
size_t getSize() const override;
void writeTo(uint8_t *buf) const override;
- static MergeChunk *instances[Log2MaxSectionAlignment + 1];
std::vector<SectionChunk *> sections;
private:
//===----------------------------------------------------------------------===//
#include "DLL.h"
+#include "COFFLinkerContext.h"
#include "Chunks.h"
#include "SymbolTable.h"
#include "llvm/Object/COFF.h"
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<std::vector<DefinedImportData *>> v = binImports(imports);
// call targets for Control Flow Guard.
StringRef symName = saver.save("__imp_load_" + extName);
s->loadThunkSym =
- cast<DefinedSynthetic>(symtab->addSynthetic(symName, t));
+ cast<DefinedSynthetic>(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<NullChunk>(8));
names.push_back(make<NullChunk>(8));
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<Chunk *> getChunks();
std::vector<Chunk *> getDataChunks();
ArrayRef<Chunk *> getCodeChunks() { return thunks; }
//===----------------------------------------------------------------------===//
#include "DebugTypes.h"
+#include "COFFLinkerContext.h"
#include "Chunks.h"
#include "Driver.h"
#include "InputFiles.h"
#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"
// 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();
if (!expectedInfo)
return;
Guid = expectedInfo->getGuid();
- auto it = mappings.emplace(Guid, this);
+ auto it = ctx.typeServerSourceMappings.emplace(Guid, this);
assert(it.second);
(void)it;
}
// The PDB signature GUID.
codeview::GUID Guid;
-
- static std::map<codeview::GUID, TypeServerSource *> mappings;
};
// Companion to TypeServerSource. Stores the index map for the IPI stream in the
// 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;
Expected<TypeServerSource *> 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;
// 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) + ")");
void loadGHashes() override;
bool isDependency() const override { return true; }
-
- static std::map<uint32_t, PrecompSource *> 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;
private:
Error mergeInPrecompHeaderObj();
+ PrecompSource *findObjByName(StringRef fileNameOnly);
+ PrecompSource *findPrecompSource(ObjFile *file, PrecompRecord &pr);
+ Expected<PrecompSource *> findPrecompMap(ObjFile *file, PrecompRecord &pr);
+
public:
// Information about the Precomp OBJ dependency, that needs to be loaded in
// before merging this OBJ.
};
} // namespace
-std::vector<TpiSource *> TpiSource::instances;
-ArrayRef<TpiSource *> TpiSource::dependencySources;
-ArrayRef<TpiSource *> 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.
consumeError(std::move(typeMergingError));
}
-void TpiSource::sortDependencies() {
- // Order dependencies first, but preserve the existing order.
- std::vector<TpiSource *> deps;
- std::vector<TpiSource *> 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>(TpiSource::Regular, file);
+TpiSource *lld::coff::makeTpiSource(COFFLinkerContext &ctx, ObjFile *file) {
+ return make<TpiSource>(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<TypeServerSource>(pdbInputFile);
+ auto *tpiSource = make<TypeServerSource>(ctx, pdbInputFile);
if (pdbInputFile->session->getPDBFile().hasPDBIpiStream())
- tpiSource->ipiSrc = make<TypeServerIpiSource>();
+ tpiSource->ipiSrc = make<TypeServerIpiSource>(ctx);
return tpiSource;
}
-TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file,
+TpiSource *lld::coff::makeUseTypeServerSource(COFFLinkerContext &ctx,
+ ObjFile *file,
TypeServer2Record ts) {
- return make<UseTypeServerSource>(file, ts);
+ return make<UseTypeServerSource>(ctx, file, ts);
}
-TpiSource *lld::coff::makePrecompSource(ObjFile *file) {
- return make<PrecompSource>(file);
+TpiSource *lld::coff::makePrecompSource(COFFLinkerContext &ctx, ObjFile *file) {
+ return make<PrecompSource>(ctx, file);
}
-TpiSource *lld::coff::makeUsePrecompSource(ObjFile *file,
+TpiSource *lld::coff::makeUsePrecompSource(COFFLinkerContext &ctx,
+ ObjFile *file,
PrecompRecord precomp) {
- return make<UsePrecompSource>(file, precomp);
+ return make<UsePrecompSource>(ctx, file, precomp);
}
-std::map<codeview::GUID, TypeServerSource *> TypeServerSource::mappings;
-
-std::map<uint32_t, PrecompSource *> PrecompSource::mappings;
-
bool TpiSource::remapTypeIndex(TypeIndex &ti, TiRefKind refKind) const {
if (ti.isSimple())
return true;
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())));
}
// 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<PrecompSource *> findPrecompMap(ObjFile *file,
- PrecompRecord &pr) {
+Expected<PrecompSource *> UsePrecompSource::findPrecompMap(ObjFile *file,
+ PrecompRecord &pr) {
PrecompSource *precomp = findPrecompSource(file, pr);
if (!precomp)
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.
//===----------------------------------------------------------------------===//
/// 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.
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
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
// - cell has non-matching key: hash collision, probe next cell
auto *cellPtr = reinterpret_cast<std::atomic<GHashCell> *>(&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)
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*
// 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
// 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)) {
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.
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
}
// 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<TpiSource *> deps;
+ std::vector<TpiSource *> 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
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();
- }
-}
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
// Walk over file->debugTypes and fill in the isItemIndex bit vector.
void fillIsItemIndexFromDebugT();
+ COFFLinkerContext &ctx;
+
public:
bool remapTypesInSymbolRecord(MutableArrayRef<uint8_t> rec);
return ghashIdx == endPrecompGHashIdx;
}
- /// All sources of type information in the program.
- static std::vector<TpiSource *> 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<TpiSource *> dependencySources;
-
- /// Object file sources. These must be processed after dependencySources.
- static ArrayRef<TpiSource *> 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;
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
//===----------------------------------------------------------------------===//
#include "Driver.h"
+#include "COFFLinkerContext.h"
#include "Config.h"
#include "DebugTypes.h"
#include "ICF.h"
namespace lld {
namespace coff {
-static Timer inputFileTimer("Input File Reading", Timer::root());
-
Configuration *config;
LinkerDriver *driver;
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]);
errorHandler().exitEarly = canExitEarly;
stderrOS.enable_colors(stderrOS.has_colors());
+ COFFLinkerContext ctx;
config = make<Configuration>();
- symtab = make<SymbolTable>();
- driver = make<LinkerDriver>();
+ driver = make<LinkerDriver>(ctx);
driver->linkerMain(args);
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<Undefined>(s);
}
addArchiveBuffer(m, "<whole-archive>", filename, memberIndex++);
return;
}
- symtab->addFile(make<ArchiveFile>(mbref));
+ ctx.symtab.addFile(make<ArchiveFile>(ctx, mbref));
break;
case file_magic::bitcode:
if (lazy)
- symtab->addFile(make<LazyObjFile>(mbref));
+ ctx.symtab.addFile(make<LazyObjFile>(ctx, mbref));
else
- symtab->addFile(make<BitcodeFile>(mbref, "", 0));
+ ctx.symtab.addFile(make<BitcodeFile>(ctx, mbref, "", 0));
break;
case file_magic::coff_object:
case file_magic::coff_import_library:
if (lazy)
- symtab->addFile(make<LazyObjFile>(mbref));
+ ctx.symtab.addFile(make<LazyObjFile>(ctx, mbref));
else
- symtab->addFile(make<ObjFile>(mbref));
+ ctx.symtab.addFile(make<ObjFile>(ctx, mbref));
break;
case file_magic::pdb:
- symtab->addFile(make<PDBInputFile>(mbref));
+ ctx.symtab.addFile(make<PDBInputFile>(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<DLLFile>(mbref));
+ ctx.symtab.addFile(make<DLLFile>(ctx, mbref));
break;
}
if (filename.endswith_insensitive(".dll")) {
uint64_t offsetInArchive) {
file_magic magic = identify_magic(mb.getBuffer());
if (magic == file_magic::coff_import_library) {
- InputFile *imp = make<ImportFile>(mb);
+ InputFile *imp = make<ImportFile>(ctx, mb);
imp->parentName = parentName;
- symtab->addFile(imp);
+ ctx.symtab.addFile(imp);
return;
}
InputFile *obj;
if (magic == file_magic::coff_object) {
- obj = make<ObjFile>(mb);
+ obj = make<ObjFile>(ctx, mb);
} else if (magic == file_magic::bitcode) {
- obj = make<BitcodeFile>(mb, parentName, offsetInArchive);
+ obj = make<BitcodeFile>(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);
}
}
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);
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();
}
}
bool LinkerDriver::run() {
- ScopedTimer t(inputFileTimer);
+ ScopedTimer t(ctx.inputFileTimer);
bool didWork = !taskQueue.empty();
while (!taskQueue.empty()) {
// 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("@")) {
// Get a list of all comdat sections for error checking.
DenseSet<StringRef> set;
- for (Chunk *c : symtab->getChunks())
+ for (Chunk *c : ctx.symtab.getChunks())
if (auto *sec = dyn_cast<SectionChunk>(c))
if (sec->sym)
set.insert(sec->sym->getName());
driver->takeBuffer(std::move(mb));
}
-static void parseCallGraphFile(StringRef path) {
+static void parseCallGraphFile(COFFLinkerContext &ctx, StringRef path) {
std::unique_ptr<MemoryBuffer> mb =
CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,
/*RequiresNullTerminator=*/false,
// Build a map from symbol name to section.
DenseMap<StringRef, Symbol *> map;
- for (ObjFile *file : ObjFile::instances)
+ for (ObjFile *file : ctx.objFileInstances)
for (Symbol *sym : file->getSymbols())
if (sym)
map[sym->getName()] = sym;
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<uint8_t> contents;
cantFail(
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)
// 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<Symbol *> syms = obj->getSymbols();
if (obj->addrsigSec) {
ArrayRef<uint8_t> contents;
void LinkerDriver::convertResources() {
std::vector<ObjFile *> resourceObjFiles;
- for (ObjFile *f : ObjFile::instances) {
+ for (ObjFile *f : ctx.objFileInstances) {
if (f->isResourceObjFile())
resourceObjFiles.push_back(f);
}
f->includeResourceChunks();
return;
}
- ObjFile *f = make<ObjFile>(convertResToCOFF(resources, resourceObjFiles));
- symtab->addFile(f);
+ ObjFile *f =
+ make<ObjFile>(ctx, convertResToCOFF(resources, resourceObjFiles));
+ ctx.symtab.addFile(f);
f->includeResourceChunks();
}
if (Optional<StringRef> path = doFindFile(arg->getValue()))
exporter.addWholeArchive(*path);
- symtab->forEachSymbol([&](Symbol *s) {
+ ctx.symtab.forEachSymbol([&](Symbol *s) {
auto *def = dyn_cast<Defined>(s);
- if (!exporter.shouldExport(def))
+ if (!exporter.shouldExport(ctx, def))
return;
if (!def->isGCRoot) {
}
void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
- ScopedTimer rootTimer(Timer::root());
+ ScopedTimer rootTimer(ctx.rootTimer);
// Needed for LTO.
InitializeAllTargetInfos();
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
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<Undefined>(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
// 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<LazyArchive>(symtab->find(arg->getValue())))
+ if (dyn_cast_or_null<LazyArchive>(ctx.symtab.find(arg->getValue())))
addUndefined(arg->getValue());
while (run());
}
// Create wrapped symbols for -wrap option.
- std::vector<WrappedSymbol> wrapped = addWrappedSymbols(args);
+ std::vector<WrappedSymbol> wrapped = addWrappedSymbols(ctx, args);
// Load more object files that might be needed for wrapped symbols.
if (!wrapped.empty())
while (run());
// 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();
}
// 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;
// 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
// 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;
// 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;
}
}
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;
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.
// 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<Defined>(symtab->findUnderscore(n));
+ Defined *d = dyn_cast_or_null<Defined>(ctx.symtab.findUnderscore(n));
if (d && !d->isGCRoot) {
d->isGCRoot = true;
config->gcroot.push_back(d);
}
}
- markLive(symtab->getChunks());
+ markLive(ctx);
}
// Needs to happen after the last call to addFile().
// 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
#ifndef LLD_COFF_DRIVER_H
#define LLD_COFF_DRIVER_H
+#include "COFFLinkerContext.h"
#include "Config.h"
#include "SymbolTable.h"
#include "lld/Common/LLVM.h"
class LinkerDriver {
public:
+ LinkerDriver(COFFLinkerContext &c) : ctx(c) {}
+
void linkerMain(llvm::ArrayRef<const char *> args);
// Used by the resolver to parse .drectve section contents.
StringRef doFindLib(StringRef filename);
StringRef doFindLibMinGW(StringRef filename);
+ bool findUnderscoreMangle(StringRef sym);
+
// Parses LIB environment which contains a list of search paths.
void addLibSearchPaths();
std::vector<MemoryBufferRef> resources;
llvm::StringSet<> directivesExports;
+
+ COFFLinkerContext &ctx;
};
// Functions below this line are defined in DriverUtils.cpp.
//===----------------------------------------------------------------------===//
#include "ICF.h"
+#include "COFFLinkerContext.h"
#include "Chunks.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
namespace lld {
namespace coff {
-static Timer icfTimer("ICF", Timer::root());
-
class ICF {
public:
- ICF(ICFLevel icfLevel) : icfLevel(icfLevel){};
- void run(ArrayRef<Chunk *> v);
+ ICF(COFFLinkerContext &c, ICFLevel icfLevel) : icfLevel(icfLevel), ctx(c){};
+ void run();
private:
void segregate(size_t begin, size_t end, bool constant);
int cnt = 0;
std::atomic<bool> repeat = {false};
ICFLevel icfLevel = ICFLevel::All;
+
+ COFFLinkerContext &ctx;
};
// Returns true if section S is subject of ICF.
// 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<Chunk *> 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<SectionChunk>(c)) {
if (isEligible(sc))
chunks.push_back(sc);
// 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++;
}
// Entry point to ICF.
-void doICF(ArrayRef<Chunk *> chunks, ICFLevel icfLevel) {
- ICF(icfLevel).run(chunks);
+void doICF(COFFLinkerContext &ctx, ICFLevel icfLevel) {
+ ICF(ctx, icfLevel).run();
}
} // namespace coff
namespace coff {
class Chunk;
+class COFFLinkerContext;
-void doICF(ArrayRef<Chunk *> chunks, ICFLevel);
+void doICF(COFFLinkerContext &ctx, ICFLevel);
} // namespace coff
} // namespace lld
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
+#include "COFFLinkerContext.h"
#include "Chunks.h"
#include "Config.h"
#include "DebugTypes.h"
.str();
}
-std::vector<ObjFile *> ObjFile::instances;
-std::map<std::string, PDBInputFile *> PDBInputFile::instances;
-std::vector<ImportFile *> ImportFile::instances;
-std::vector<BitcodeFile *> 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.
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.
// 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.
InputFile *file;
if (isBitcode(mb))
- file = make<BitcodeFile>(mb, "", 0, std::move(symbols));
+ file = make<BitcodeFile>(ctx, mb, "", 0, std::move(symbols));
else
- file = make<ObjFile>(mb, std::move(symbols));
+ file = make<ObjFile>(ctx, mb, std::move(symbols));
mb = {};
- symtab->addFile(file);
+ ctx.symtab.addFile(file);
}
void LazyObjFile::parse() {
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;
}
StringRef name = check(coffObj->getSymbolName(coffSym));
if (coffSym.isAbsolute() && ignoredSymbolName(name))
continue;
- symtab->addLazyObject(this, name);
+ ctx.symtab.addLazyObject(this, name);
i += coffSym.getNumberOfAuxSymbols();
}
}
// 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
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
// 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<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
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.
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,
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:
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;
// 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;
}
if (sym.isCommon()) {
auto *c = make<CommonChunk>(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()) {
return nullptr;
if (sym.isExternal())
- return symtab->addAbsolute(name, sym);
+ return ctx.symtab.addAbsolute(name, sym);
return make<DefinedAbsolute>(name, sym);
}
if (sym.isExternal()) {
std::tie(leader, prevailing) =
- symtab->addComdat(this, getName(), sym.getGeneric());
+ ctx.symtab.addComdat(this, getName(), sym.getGeneric());
} else {
leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
/*IsExternal*/ false, sym.getGeneric());
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;
}
// This object file is a PCH file that others will depend on.
if (isPCH) {
- debugTypesObj = makePrecompSource(this);
+ debugTypesObj = makePrecompSource(ctx, this);
return;
}
if (firstType->kind() == LF_TYPESERVER2) {
TypeServer2Record ts = cantFail(
TypeDeserializer::deserializeAs<TypeServer2Record>(firstType->data()));
- debugTypesObj = makeUseTypeServerSource(this, ts);
- PDBInputFile::enqueue(ts.getName(), this);
+ debugTypesObj = makeUseTypeServerSource(ctx, this, ts);
+ enqueuePdbFile(ts.getName(), this);
return;
}
if (firstType->kind() == LF_PRECOMP) {
PrecompRecord precomp = cantFail(
TypeDeserializer::deserializeAs<PrecompRecord>(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
// 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();
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<pdb::IPDBSession> thisSession;
loadErr.emplace(pdb::NativeSession::createFromPdb(
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
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<const coff_import_header *>(buf);
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<void>(symtab->addImportData(name, this));
+ static_cast<void>(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<DefinedImportData>(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<Symbol *> &&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());
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();
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<DefinedRegular>(sym)->data == nullptr)
cast<DefinedRegular>(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())
}
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);
}
}
p += s->symbolName.size() + 1;
memcpy(p, s->dllName.data(), s->dllName.size());
MemoryBufferRef mbref = MemoryBufferRef(StringRef(buf, size), s->dllName);
- ImportFile *impFile = make<ImportFile>(mbref);
- symtab->addFile(impFile);
+ ImportFile *impFile = make<ImportFile>(ctx, mbref);
+ ctx.symtab.addFile(impFile);
}
class DWARFCache;
namespace coff {
+class COFFLinkerContext;
std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *file);
// 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;
// .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;
// .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;
}
// .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<Symbol *> &&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<Symbol *> &&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;
bool isResourceObjFile() const { return !resourceChunks.empty(); }
- static std::vector<ObjFile *> 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.
return getSection(sym.getSectionNumber());
}
+ void enqueuePdbFile(StringRef path, ObjFile *fromFile);
+
void initializeChunks();
void initializeSymbols();
void initializeFlags();
// 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<std::string, PDBInputFile *> instances;
+ static PDBInputFile *findFromRecordPath(const COFFLinkerContext &ctx,
+ StringRef path, ObjFile *fromFile);
// Record possible errors while opening the PDB file
llvm::Optional<Error> loadErr;
// 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<ImportFile *> instances;
-
Symbol *impSym = nullptr;
Symbol *thunkSym = nullptr;
std::string dllName;
// 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<Symbol *> &&symbols);
~BitcodeFile();
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
ArrayRef<Symbol *> getSymbols() { return symbols; }
MachineTypes getMachineType() override;
- static std::vector<BitcodeFile *> instances;
std::unique_ptr<llvm::lto::InputFile> obj;
private:
// .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;
//===----------------------------------------------------------------------===//
#include "LLDMapFile.h"
+#include "COFFLinkerContext.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
}
// Returns a list of all symbols that we want to print out.
-static std::vector<DefinedRegular *> getSymbols() {
+static std::vector<DefinedRegular *> getSymbols(const COFFLinkerContext &ctx) {
std::vector<DefinedRegular *> v;
- for (ObjFile *file : ObjFile::instances)
+ for (ObjFile *file : ctx.objFileInstances)
for (Symbol *b : file->getSymbols())
if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
if (sym && !sym->getCOFFSymbol().isSectionDefinition())
return ret;
}
-void lld::coff::writeLLDMapFile(ArrayRef<OutputSection *> outputSections) {
+void lld::coff::writeLLDMapFile(const COFFLinkerContext &ctx) {
if (config->lldmapFile.empty())
return;
fatal("cannot open " + config->lldmapFile + ": " + ec.message());
// Collect symbol info that we want to print out.
- std::vector<DefinedRegular *> syms = getSymbols();
+ std::vector<DefinedRegular *> syms = getSymbols(ctx);
SymbolMapTy sectionSyms = getSectionSyms(syms);
DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
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';
#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<OutputSection *> outputSections);
+class COFFLinkerContext;
+void writeLLDMapFile(const COFFLinkerContext &ctx);
}
}
// Merge all the bitcode files we have seen, codegen the result
// and return the resulting objects.
-std::vector<InputFile *> BitcodeCompiler::compile() {
+std::vector<InputFile *> BitcodeCompiler::compile(COFFLinkerContext &ctx) {
unsigned maxTasks = ltoObj->getMaxTasks();
buf.resize(maxTasks);
files.resize(maxTasks);
if (config->saveTemps)
saveBuffer(buf[i], ltoObjName);
- ret.push_back(make<ObjFile>(MemoryBufferRef(objBuf, ltoObjName)));
+ ret.push_back(make<ObjFile>(ctx, MemoryBufferRef(objBuf, ltoObjName)));
}
return ret;
class BitcodeFile;
class InputFile;
+class COFFLinkerContext;
class BitcodeCompiler {
public:
~BitcodeCompiler();
void add(BitcodeFile &f);
- std::vector<InputFile *> compile();
+ std::vector<InputFile *> compile(COFFLinkerContext &ctx);
private:
std::unique_ptr<llvm::lto::LTO> ltoObj;
//===----------------------------------------------------------------------===//
#include "MapFile.h"
+#include "COFFLinkerContext.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
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);
// Returns the lists of all symbols that we want to print out.
static void getSymbols(std::vector<Defined *> &syms,
- std::vector<Defined *> &staticSyms) {
+ std::vector<Defined *> &staticSyms,
+ const COFFLinkerContext &ctx) {
- for (ObjFile *file : ObjFile::instances)
+ for (ObjFile *file : ctx.objFileInstances)
for (Symbol *b : file->getSymbols()) {
if (!b || !b->isLive())
continue;
}
}
- for (ImportFile *file : ImportFile::instances) {
+ for (ImportFile *file : ctx.importFileInstances) {
if (!file->live)
continue;
// Construct a map from symbols to their stringified representations.
static DenseMap<Defined *, std::string>
-getSymbolStrings(ArrayRef<Defined *> syms) {
+getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
std::vector<std::string> str(syms.size());
parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
raw_string_ostream os(str[i]);
fileDescr = "<common>";
} 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();
return ret;
}
-void lld::coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
+void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
if (config->mapFile.empty())
return;
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<Defined *> syms;
std::vector<Defined *> staticSyms;
- getSymbols(syms, staticSyms);
+ getSymbols(syms, staticSyms, ctx);
t2.stop();
- ScopedTimer t3(symbolStringsTimer);
- DenseMap<Defined *, std::string> symStr = getSymbolStrings(syms);
- DenseMap<Defined *, std::string> staticSymStr = getSymbolStrings(staticSyms);
+ ScopedTimer t3(ctx.symbolStringsTimer);
+ DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);
+ DenseMap<Defined *, std::string> 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, "");
// 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<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
for (Chunk *c : sec->chunks) {
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 ";
#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<OutputSection *> outputSections);
+class COFFLinkerContext;
+void writeMapFile(COFFLinkerContext &ctx);
}
}
//
//===----------------------------------------------------------------------===//
+#include "COFFLinkerContext.h"
#include "Chunks.h"
#include "Symbols.h"
#include "lld/Common/Timer.h"
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<Chunk *> 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
// 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<SectionChunk>(c))
if (sc->live && !sc->isDWARF())
worklist.push_back(sc);
enqueue(&c);
}
}
-
}
}
#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<Chunk *> chunks);
+void markLive(COFFLinkerContext &ctx);
} // namespace coff
} // namespace lld
//===----------------------------------------------------------------------===//
#include "MinGW.h"
+#include "COFFLinkerContext.h"
#include "Driver.h"
#include "InputFiles.h"
#include "SymbolTable.h"
excludeLibs.erase(libName);
}
-bool AutoExporter::shouldExport(Defined *sym) const {
+bool AutoExporter::shouldExport(const COFFLinkerContext &ctx,
+ Defined *sym) const {
if (!sym || !sym->getChunk())
return false;
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
// like they are not being used at all, so we explicitly set some flags so
// that LTO won't eliminate them.
std::vector<WrappedSymbol>
-lld::coff::addWrappedSymbols(opt::InputArgList &args) {
+lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
std::vector<WrappedSymbol> v;
DenseSet<StringRef> seen;
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
// 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<WrappedSymbol> wrapped) {
+void lld::coff::wrapSymbols(COFFLinkerContext &ctx,
+ ArrayRef<WrappedSymbol> wrapped) {
DenseMap<Symbol *, Symbol *> map;
for (const WrappedSymbol &w : wrapped) {
map[w.sym] = w.wrap;
map[w.real] = w.sym;
if (Defined *d = dyn_cast<Defined>(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
if (imp) {
DefinedLocalImport *wrapimp = make<DefinedLocalImport>(
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<Symbol *> syms = file->getMutableSymbols();
for (size_t i = 0, e = syms.size(); i != e; ++i)
if (Symbol *s = map.lookup(syms[i]))
namespace lld {
namespace coff {
+class COFFLinkerContext;
// Logic for deciding what symbols to export, when exporting all
// symbols for MinGW.
llvm::StringSet<> excludeLibs;
llvm::StringSet<> excludeObjects;
- bool shouldExport(Defined *sym) const;
+ bool shouldExport(const COFFLinkerContext &ctx, Defined *sym) const;
};
void writeDefFile(StringRef name);
Symbol *wrap;
};
-std::vector<WrappedSymbol> addWrappedSymbols(llvm::opt::InputArgList &args);
+std::vector<WrappedSymbol> addWrappedSymbols(COFFLinkerContext &ctx,
+ llvm::opt::InputArgList &args);
-void wrapSymbols(ArrayRef<WrappedSymbol> wrapped);
+void wrapSymbols(COFFLinkerContext &ctx, ArrayRef<WrappedSymbol> wrapped);
} // namespace coff
} // namespace lld
//===----------------------------------------------------------------------===//
#include "PDB.h"
+#include "COFFLinkerContext.h"
#include "Chunks.h"
#include "Config.h"
#include "DebugTypes.h"
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;
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.
void addPublicsToPDB();
/// Link info for each import file in the symbol table into the PDB.
- void addImportFilesToPDB(ArrayRef<OutputSection *> outputSections);
+ void addImportFilesToPDB();
void createModuleDBI(ObjFile *file);
std::vector<uint8_t> &storage);
/// Add the section map and section contributions to the PDB.
- void addSections(ArrayRef<OutputSection *> outputSections,
- ArrayRef<uint8_t> sectionTable);
+ void addSections(ArrayRef<uint8_t> sectionTable);
/// Write the PDB to disk and store the Guid generated for it in *Guid.
void commit(codeview::GUID *guid);
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;
});
}
-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);
static_cast<ObjFile *>(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;
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.
auto *secChunk = dyn_cast<SectionChunk>(c);
if (!secChunk || !secChunk->live)
continue;
- pdb::SectionContrib sc = createSectionContrib(secChunk, modi);
+ pdb::SectionContrib sc = createSectionContrib(ctx, secChunk, modi);
file->moduleDBI->setFirstSectionContrib(sc);
break;
}
// 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));
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();
}
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;
// 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());
t2.stop();
if (config->showSummary) {
- for_each(TpiSource::instances, [&](TpiSource *source) {
+ for_each(ctx.tpiSourceList, [&](TpiSource *source) {
nbTypeRecords += source->nbTypeRecords;
nbTypeRecordsBytes += source->nbTypeRecordsBytes;
});
}
void PDBLinker::addPublicsToPDB() {
- ScopedTimer t3(publicsLayoutTimer);
+ ScopedTimer t3(ctx.publicsLayoutTimer);
// Compute the public symbols.
auto &gsiBuilder = builder.getGsiBuilder();
std::vector<pdb::BulkPublic> 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<Defined>(s);
return;
}
}
- publics.push_back(createPublic(def));
+ publics.push_back(createPublic(ctx, def));
}
});
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");
}
// Add all import files as modules to the PDB.
-void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
- if (ImportFile::instances.empty())
+void PDBLinker::addImportFilesToPDB() {
+ if (ctx.importFileInstances.empty())
return;
std::map<std::string, llvm::pdb::DbiModuleDescriptorBuilder *> dllToModuleDbi;
- for (ImportFile *file : ImportFile::instances) {
+ for (ImportFile *file : ctx.importFileInstances) {
if (!file->live)
continue;
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.
DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym);
Chunk *thunkChunk = thunk->getChunk();
- OutputSection *thunkOS = thunkChunk->getOutputSection();
+ OutputSection *thunkOS = ctx.getOutputSection(thunkChunk);
ObjNameSym ons(SymbolRecordKind::ObjNameSym);
Compile3Sym cs(SymbolRecordKind::Compile3Sym);
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<OutputSection *> outputSections,
+void lld::coff::createPDB(COFFLinkerContext &ctx,
ArrayRef<uint8_t> 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);
dbiBuilder.setBuildNumber(14, 11);
}
-void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
- ArrayRef<uint8_t> sectionTable) {
+void PDBLinker::addSections(ArrayRef<uint8_t> sectionTable) {
// It's not entirely clear what this is, but the * Linker * module uses it.
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
nativePath = config->pdbPath;
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);
}
}
// 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.
class Timer;
namespace coff {
-class OutputSection;
class SectionChunk;
-class SymbolTable;
+class COFFLinkerContext;
-void createPDB(SymbolTable *symtab,
- llvm::ArrayRef<OutputSection *> outputSections,
- llvm::ArrayRef<uint8_t> sectionTable,
+void createPDB(COFFLinkerContext &ctx, llvm::ArrayRef<uint8_t> sectionTable,
llvm::codeview::DebugInfo *buildId);
llvm::Optional<std::pair<llvm::StringRef, uint32_t>>
getFileLineCodeView(const SectionChunk *c, uint32_t addr);
-extern Timer loadGHashTimer;
-extern Timer mergeGHashTimer;
-
} // namespace coff
} // namespace lld
//===----------------------------------------------------------------------===//
#include "SymbolTable.h"
+#include "COFFLinkerContext.h"
#include "Config.h"
#include "Driver.h"
#include "LTO.h"
return s;
}
-static Timer ltoTimer("LTO", Timer::root());
-
-SymbolTable *symtab;
-
void SymbolTable::addFile(InputFile *file) {
log("Reading " + toString(file));
file->parse();
}
if (auto *f = dyn_cast<ObjFile>(file)) {
- ObjFile::instances.push_back(f);
+ ctx.objFileInstances.push_back(f);
} else if (auto *f = dyn_cast<BitcodeFile>(file)) {
- BitcodeFile::instances.push_back(f);
+ ctx.bitcodeFileInstances.push_back(f);
} else if (auto *f = dyn_cast<ImportFile>(file)) {
- ImportFile::instances.push_back(f);
+ ctx.importFileInstances.push_back(f);
}
driver->parseDirectives(file);
/// 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<Symbol *> &undefs,
- const DenseMap<Symbol *, Symbol *> *localImports,
- const std::vector<ObjFile *> objFiles,
- const std::vector<BitcodeFile *> *bitcodeFiles) {
-
+static void reportProblemSymbols(
+ const COFFLinkerContext &ctx, const SmallPtrSetImpl<Symbol *> &undefs,
+ const DenseMap<Symbol *, Symbol *> *localImports, bool needBitcodeFiles) {
// Return early if there is nothing to report (which should be
// the common case).
if (undefs.empty() && (!localImports || localImports->empty()))
}
};
- 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)
undefs.insert(sym);
}
- reportProblemSymbols(undefs,
- /* localImports */ nullptr, ObjFile::instances,
- &BitcodeFile::instances);
+ reportProblemSymbols(ctx, undefs,
+ /* localImports */ nullptr, true);
}
void SymbolTable::resolveRemainingUndefines() {
}
reportProblemSymbols(
- undefs, config->warnLocallyDefinedImported ? &localImports : nullptr,
- ObjFile::instances, /* bitcode files no longer needed */ nullptr);
+ ctx, undefs, config->warnLocallyDefinedImported ? &localImports : nullptr,
+ false);
}
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
}
}
-std::vector<Chunk *> SymbolTable::getChunks() {
+std::vector<Chunk *> SymbolTable::getChunks() const {
std::vector<Chunk *> res;
- for (ObjFile *file : ObjFile::instances) {
+ for (ObjFile *file : ctx.objFileInstances) {
ArrayRef<Chunk *> 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);
}
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<ObjFile>(newObj);
obj->parse();
- ObjFile::instances.push_back(obj);
+ ctx.objFileInstances.push_back(obj);
}
}
class Chunk;
class CommonChunk;
+class COFFLinkerContext;
class Defined;
class DefinedAbsolute;
class DefinedRegular;
// 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.
bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
// Returns a list of chunks of selected symbols.
- std::vector<Chunk *> getChunks();
+ std::vector<Chunk *> 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
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> symMap;
std::unique_ptr<BitcodeCompiler> lto;
-};
-extern SymbolTable *symtab;
+ COFFLinkerContext &ctx;
+};
std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex);
#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"
class TypeMerger {
public:
- TypeMerger(llvm::BumpPtrAllocator &alloc);
+ TypeMerger(COFFLinkerContext &ctx, llvm::BumpPtrAllocator &alloc);
~TypeMerger();
// keyed by type index.
SmallVector<uint32_t, 0> tpiCounts;
SmallVector<uint32_t, 0> 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<TpiSource *> dependencySources;
+
+ /// Object file sources. These must be processed after dependencySources.
+ ArrayRef<TpiSource *> objectSources;
+
+ /// Sorts the dependencies and reassigns TpiSource indices.
+ void sortDependencies();
+
+private:
+ COFFLinkerContext &ctx;
};
} // namespace coff
//===----------------------------------------------------------------------===//
#include "Writer.h"
+#include "COFFLinkerContext.h"
#include "CallGraphSort.h"
#include "Config.h"
#include "DLL.h"
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<OutputSection *> 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<std::pair<COFF::DebugType, Chunk *>> &r,
+ DebugDirectoryChunk(COFFLinkerContext &c,
+ const std::vector<std::pair<COFF::DebugType, Chunk *>> &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);
for (const std::pair<COFF::DebugType, Chunk *>& 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;
mutable std::vector<support::ulittle32_t *> timeDateStamps;
const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
bool writeRepro;
+
+ COFFLinkerContext &ctx;
};
class CVDebugRecordChunk : public NonSectionChunk {
// 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:
// 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);
return;
size_t origNumChunks = 0;
- for (OutputSection *sec : outputSections) {
+ for (OutputSection *sec : ctx.outputSections) {
sec->origChunks = sec->chunks;
origNumChunks += sec->chunks.size();
}
// 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;
// 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;
}
// 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.
// The main function of the writer.
void Writer::run() {
- ScopedTimer t1(codeLayoutTimer);
+ ScopedTimer t1(ctx.codeLayoutTimer);
createImportTables();
createSections();
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)));
}
void Writer::sortSections() {
if (!config->callGraphProfile.empty()) {
- DenseMap<const SectionChunk *, int> order = computeCallGraphProfileOrder();
+ DenseMap<const SectionChunk *, int> order =
+ computeCallGraphProfileOrder(ctx);
for (auto it : order) {
if (DefinedRegular *sym = it.first->sym)
config->order[sym->getName()] = it.second;
OutputSection *&sec = sections[{name, outChars}];
if (!sec) {
sec = make<OutputSection>(name, outChars);
- outputSections.push_back(sec);
+ ctx.outputSections.push_back(sec);
}
return sec;
};
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<SectionChunk>(c);
if (sc && !sc->live) {
if (config->verbose)
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);
}
// 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<DebugDirectoryChunk>(debugRecords, config->repro);
+ debugDirectory =
+ make<DebugDirectoryChunk>(ctx, debugRecords, config->repro);
debugDirectory->setAlignment(4);
debugInfoSec->addChunk(debugDirectory);
}
// 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;
}
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;
if (!delayIdata.empty()) {
Defined *helper = cast<Defined>(config->delayLoadHelper);
- delayIdata.create(helper);
+ delayIdata.create(ctx, helper);
for (Chunk *c : delayIdata.getChunks())
didatSec->addChunk(c);
for (Chunk *c : delayIdata.getDataChunks())
// 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);
// 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)
Chunk *c = def->getChunk();
if (!c)
return None;
- OutputSection *os = c->getOutputSection();
+ OutputSection *os = ctx.getOutputSection(c);
if (!os)
return None;
// 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)
}
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<Defined>(b);
if (!d || d->writtenToSymtab)
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);
// 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;
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();
}
auto *coff = reinterpret_cast<coff_file_header *>(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;
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<Defined>(sym)) {
dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA();
dir[TLS_TABLE].Size = config->is64()
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<DefinedRegular>(sym)) {
SectionChunk *sc = b->getChunk();
assert(b->getRVA() >= sc->getRVA());
}
// Write section table
- for (OutputSection *sec : outputSections) {
+ for (OutputSection *sec : ctx.outputSections) {
sec->writeHeaderTo(buf);
buf += sizeof(coff_section);
}
sectionTable = ArrayRef<uint8_t>(
- buf - outputSections.size() * sizeof(coff_section), buf);
+ buf - ctx.outputSections.size() * sizeof(coff_section), buf);
if (outputSymtab.empty() && strtab.empty())
return;
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);
// 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");
std::vector<Symbol *> 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
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<DefinedAbsolute>(flagSym)->setVA(guardFlags);
}
tableChunk = make<RVATableChunk>(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<DefinedSynthetic>(t, t->getName(), tableChunk);
cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4));
}
void Writer::createRuntimePseudoRelocs() {
std::vector<RuntimePseudoReloc> rels;
- for (Chunk *c : symtab->getChunks()) {
+ for (Chunk *c : ctx.symtab.getChunks()) {
auto *sc = dyn_cast<SectionChunk>(c);
if (!sc || !sc->live)
continue;
EmptyChunk *endOfList = make<EmptyChunk>();
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<DefinedSynthetic>(headSym, headSym->getName(), table);
replaceSymbol<DefinedSynthetic>(endSym, endSym->getName(), endOfList);
}
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<DefinedSynthetic>(ctorListSym, ctorListSym->getName(),
ctorListHead);
replaceSymbol<DefinedSynthetic>(dtorListSym, dtorListSym->getName(),
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);
}
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
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();
};
}
OutputSection *Writer::findSection(StringRef name) {
- for (OutputSection *sec : outputSections)
+ for (OutputSection *sec : ctx.outputSections)
if (sec->name == name)
return sec;
return nullptr;
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;
return;
relocSec->chunks.clear();
std::vector<Baserel> 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.
void Writer::fixTlsAlignment() {
Defined *tlsSym =
- dyn_cast_or_null<Defined>(symtab->findUnderscore("_tls_used"));
+ dyn_cast_or_null<Defined>(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");
namespace lld {
namespace coff {
static const int pageSize = 4096;
+class COFFLinkerContext;
-void writeResult();
+void writeResult(COFFLinkerContext &ctx);
class PartialSection {
public:
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
parent.children.push_back(this);
}
-Timer &Timer::root() {
- static Timer rootTimer("Total Link Time");
- return rootTimer;
-}
-
void Timer::print() {
- double totalDuration = static_cast<double>(root().millis());
+ double totalDuration = static_cast<double>(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.
message(std::string(50, '-'));
- root().print(0, root().millis(), false);
+ print(0, millis(), false);
}
double Timer::millis() const {
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();
double millis() const;
private:
- explicit Timer(llvm::StringRef name);
void print(int depth, double totalDuration, bool recurse = true) const;
std::atomic<std::chrono::nanoseconds::rep> total;
sources = [
"CallGraphSort.cpp",
"Chunks.cpp",
+ "COFFLinkerContext.cpp",
"DLL.cpp",
"DebugTypes.cpp",
"Driver.cpp",