diff --git a/lld/COFF/COFFLinkerContext.h b/lld/COFF/COFFLinkerContext.h --- a/lld/COFF/COFFLinkerContext.h +++ b/lld/COFF/COFFLinkerContext.h @@ -6,12 +6,13 @@ // //===----------------------------------------------------------------------===// -#ifndef LLD_COFF_COFFLinkerContext_H -#define LLD_COFF_COFFLinkerContext_H +#ifndef LLD_COFF_COFFLINKERCONTEXT_H +#define LLD_COFF_COFFLINKERCONTEXT_H #include "Chunks.h" #include "Config.h" #include "DebugTypes.h" +#include "Driver.h" #include "InputFiles.h" #include "SymbolTable.h" #include "Writer.h" @@ -27,9 +28,9 @@ COFFLinkerContext &operator=(const COFFLinkerContext &) = delete; ~COFFLinkerContext() = default; - void addTpiSource(TpiSource *tpi) { tpiSourceList.push_back(tpi); } - + LinkerDriver driver; SymbolTable symtab; + COFFOptTable optTable; std::vector objFileInstances; std::map pdbInputFileInstances; @@ -41,6 +42,8 @@ /// All sources of type information in the program. std::vector tpiSourceList; + void addTpiSource(TpiSource *tpi) { tpiSourceList.push_back(tpi); } + std::map typeServerSourceMappings; std::map precompSourceMappings; @@ -52,6 +55,10 @@ return c->osidx == 0 ? nullptr : outputSections[c->osidx - 1]; } + // Fake sections for parsing bitcode files. + FakeSectionChunk *ltoTextSectionChunk; + FakeSectionChunk *ltoDataSectionChunk; + // All timers used in the COFF linker. Timer rootTimer; Timer inputFileTimer; @@ -77,6 +84,8 @@ Timer publicsLayoutTimer; Timer tpiStreamLayoutTimer; Timer diskCommitTimer; + + Configuration config; }; } // namespace lld::coff diff --git a/lld/COFF/COFFLinkerContext.cpp b/lld/COFF/COFFLinkerContext.cpp --- a/lld/COFF/COFFLinkerContext.cpp +++ b/lld/COFF/COFFLinkerContext.cpp @@ -10,13 +10,15 @@ //===----------------------------------------------------------------------===// #include "COFFLinkerContext.h" +#include "Symbols.h" #include "lld/Common/Memory.h" +#include "llvm/BinaryFormat/COFF.h" #include "llvm/DebugInfo/CodeView/TypeHashing.h" +#include "llvm/Demangle/Demangle.h" namespace lld::coff { - COFFLinkerContext::COFFLinkerContext() - : symtab(*this), rootTimer("Total Linking Time"), + : driver(*this), symtab(*this), rootTimer("Total Linking Time"), inputFileTimer("Input File Reading", rootTimer), ltoTimer("LTO", rootTimer), gcTimer("GC", rootTimer), icfTimer("ICF", rootTimer), codeLayoutTimer("Code Layout", rootTimer), @@ -33,6 +35,10 @@ symbolMergingTimer("Symbol Merging", addObjectsTimer), publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer), tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer), - diskCommitTimer("Commit to Disk", totalPdbLinkTimer) {} - + diskCommitTimer("Commit to Disk", totalPdbLinkTimer) { + FakeSection ltoTextSection(llvm::COFF::IMAGE_SCN_MEM_EXECUTE); + FakeSection ltoDataSection(llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA); + ltoTextSectionChunk = make(<oTextSection.section); + ltoDataSectionChunk = make(<oDataSection.section); +} } // namespace lld::coff diff --git a/lld/COFF/CallGraphSort.cpp b/lld/COFF/CallGraphSort.cpp --- a/lld/COFF/CallGraphSort.cpp +++ b/lld/COFF/CallGraphSort.cpp @@ -56,6 +56,8 @@ private: std::vector clusters; std::vector sections; + + const COFFLinkerContext &ctx; }; // Maximum amount the combined cluster density can be worse than the original @@ -71,8 +73,8 @@ // Take the edge list in Config->CallGraphProfile, resolve symbol names to // Symbols, and generate a graph between InputSections with the provided // weights. -CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) { - MapVector &profile = config->callGraphProfile; +CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) : ctx(ctx) { + const MapVector &profile = ctx.config.callGraphProfile; DenseMap secToCluster; auto getOrCreateNode = [&](const SectionChunk *isec) -> int { @@ -85,7 +87,7 @@ }; // Create the graph. - for (std::pair &c : profile) { + for (const std::pair &c : profile) { const auto *fromSec = cast(c.first.first->repl); const auto *toSec = cast(c.first.second->repl); uint64_t weight = c.second; @@ -205,11 +207,11 @@ break; } } - if (!config->printSymbolOrder.empty()) { + if (!ctx.config.printSymbolOrder.empty()) { std::error_code ec; - raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None); + raw_fd_ostream os(ctx.config.printSymbolOrder, ec, sys::fs::OF_None); if (ec) { - error("cannot open " + config->printSymbolOrder + ": " + ec.message()); + error("cannot open " + ctx.config.printSymbolOrder + ": " + ec.message()); return orderMap; } // Print the symbols ordered by C3, in the order of increasing curOrder diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h --- a/lld/COFF/Chunks.h +++ b/lld/COFF/Chunks.h @@ -238,13 +238,13 @@ bool isCOMDAT() const; void applyRelocation(uint8_t *off, const coff_relocation &rel) const; void applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, - uint64_t p) const; + uint64_t p, uint64_t imageBase) const; void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, - uint64_t p) const; + uint64_t p, uint64_t imageBase) const; void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, - uint64_t p) const; + uint64_t p, uint64_t imageBase) const; void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, - uint64_t p) const; + uint64_t p, uint64_t imageBase) const; void getRuntimePseudoRelocs(std::vector &res); @@ -490,24 +490,26 @@ // contents will be a JMP instruction to some __imp_ symbol. class ImportThunkChunk : public NonSectionChunk { public: - ImportThunkChunk(Defined *s) - : NonSectionChunk(ImportThunkKind), impSymbol(s) {} + ImportThunkChunk(COFFLinkerContext &ctx, Defined *s) + : NonSectionChunk(ImportThunkKind), impSymbol(s), ctx(ctx) {} static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; } protected: Defined *impSymbol; + COFFLinkerContext &ctx; }; class ImportThunkChunkX64 : public ImportThunkChunk { public: - explicit ImportThunkChunkX64(Defined *s); + explicit ImportThunkChunkX64(COFFLinkerContext &ctx, Defined *s); size_t getSize() const override { return sizeof(importThunkX86); } void writeTo(uint8_t *buf) const override; }; class ImportThunkChunkX86 : public ImportThunkChunk { public: - explicit ImportThunkChunkX86(Defined *s) : ImportThunkChunk(s) {} + explicit ImportThunkChunkX86(COFFLinkerContext &ctx, Defined *s) + : ImportThunkChunk(ctx, s) {} size_t getSize() const override { return sizeof(importThunkX86); } void getBaserels(std::vector *res) override; void writeTo(uint8_t *buf) const override; @@ -515,7 +517,8 @@ class ImportThunkChunkARM : public ImportThunkChunk { public: - explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) { + explicit ImportThunkChunkARM(COFFLinkerContext &ctx, Defined *s) + : ImportThunkChunk(ctx, s) { setAlignment(2); } size_t getSize() const override { return sizeof(importThunkARM); } @@ -525,7 +528,8 @@ class ImportThunkChunkARM64 : public ImportThunkChunk { public: - explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) { + explicit ImportThunkChunkARM64(COFFLinkerContext &ctx, Defined *s) + : ImportThunkChunk(ctx, s) { setAlignment(4); } size_t getSize() const override { return sizeof(importThunkARM64); } @@ -534,35 +538,46 @@ class RangeExtensionThunkARM : public NonSectionChunk { public: - explicit RangeExtensionThunkARM(Defined *t) : target(t) { setAlignment(2); } + explicit RangeExtensionThunkARM(COFFLinkerContext &ctx, Defined *t) + : target(t), ctx(ctx) { + setAlignment(2); + } size_t getSize() const override; void writeTo(uint8_t *buf) const override; Defined *target; + +private: + COFFLinkerContext &ctx; }; class RangeExtensionThunkARM64 : public NonSectionChunk { public: - explicit RangeExtensionThunkARM64(Defined *t) : target(t) { setAlignment(4); } + explicit RangeExtensionThunkARM64(COFFLinkerContext &ctx, Defined *t) + : target(t), ctx(ctx) { + setAlignment(4); + } size_t getSize() const override; void writeTo(uint8_t *buf) const override; Defined *target; + +private: + COFFLinkerContext &ctx; }; // Windows-specific. // See comments for DefinedLocalImport class. class LocalImportChunk : public NonSectionChunk { public: - explicit LocalImportChunk(Defined *s) : sym(s) { - setAlignment(config->wordsize); - } + explicit LocalImportChunk(COFFLinkerContext &ctx, Defined *s); size_t getSize() const override; void getBaserels(std::vector *res) override; void writeTo(uint8_t *buf) const override; private: Defined *sym; + COFFLinkerContext &ctx; }; // Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and @@ -629,8 +644,9 @@ class Baserel { public: Baserel(uint32_t v, uint8_t ty) : rva(v), type(ty) {} - explicit Baserel(uint32_t v) : Baserel(v, getDefaultType()) {} - uint8_t getDefaultType(); + explicit Baserel(uint32_t v, llvm::COFF::MachineTypes machine) + : Baserel(v, getDefaultType(machine)) {} + uint8_t getDefaultType(llvm::COFF::MachineTypes machine); uint32_t rva; uint8_t type; @@ -669,7 +685,8 @@ // MinGW specific. A Chunk that contains one pointer-sized absolute value. class AbsolutePointerChunk : public NonSectionChunk { public: - AbsolutePointerChunk(uint64_t value) : value(value) { + AbsolutePointerChunk(COFFLinkerContext &ctx, uint64_t value) + : value(value), ctx(ctx) { setAlignment(getSize()); } size_t getSize() const override; @@ -677,6 +694,7 @@ private: uint64_t value; + COFFLinkerContext &ctx; }; // Return true if this file has the hotpatch flag set to true in the S_COMPILE3 @@ -697,6 +715,19 @@ void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit); void applyArm64Branch26(uint8_t *off, int64_t v); +// Convenience class for initializing a SectionChunk with specific flags. +class FakeSectionChunk { +public: + FakeSectionChunk(const coff_section *section) : chunk(nullptr, section) { + // Comdats from LTO files can't be fully treated as regular comdats + // at this point; we don't know what size or contents they are going to + // have, so we can't do proper checking of such aspects of them. + chunk.selection = llvm::COFF::IMAGE_COMDAT_SELECT_ANY; + } + + SectionChunk chunk; +}; + } // namespace lld::coff namespace llvm { diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp --- a/lld/COFF/Chunks.cpp +++ b/lld/COFF/Chunks.cpp @@ -53,8 +53,8 @@ // enabled, treat non-comdat sections as roots. Generally optimized object // files will be built with -ffunction-sections or /Gy, so most things worth // stripping will be in a comdat. - if (config) - live = !config->doGC || !isCOMDAT(); + if (file) + live = !file->ctx.config.doGC || !isCOMDAT(); else live = true; } @@ -94,21 +94,32 @@ add32(off, secRel); } -static void applySecIdx(uint8_t *off, OutputSection *os) { +static void applySecIdx(uint8_t *off, OutputSection *os, + unsigned numOutputSections) { + // numOutputSections is the largest valid section index. Make sure that + // it fits in 16 bits. + assert(sizeof(numOutputSections) <= 0xffff && + "size of outputSections is too big"); + // Absolute symbol doesn't have section index, but section index relocation // against absolute symbol should be resolved to one plus the last output // section index. This is required for compatibility with MSVC. if (os) add16(off, os->sectionIndex); else - add16(off, DefinedAbsolute::numOutputSections + 1); + add16(off, numOutputSections + 1); } void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, - uint64_t s, uint64_t p) const { + uint64_t s, uint64_t p, + uint64_t imageBase) const { switch (type) { - case IMAGE_REL_AMD64_ADDR32: add32(off, s + config->imageBase); break; - case IMAGE_REL_AMD64_ADDR64: add64(off, s + config->imageBase); break; + case IMAGE_REL_AMD64_ADDR32: + add32(off, s + imageBase); + break; + case IMAGE_REL_AMD64_ADDR64: + add64(off, s + imageBase); + break; case IMAGE_REL_AMD64_ADDR32NB: add32(off, s); break; case IMAGE_REL_AMD64_REL32: add32(off, s - p - 4); break; case IMAGE_REL_AMD64_REL32_1: add32(off, s - p - 5); break; @@ -116,7 +127,9 @@ case IMAGE_REL_AMD64_REL32_3: add32(off, s - p - 7); break; case IMAGE_REL_AMD64_REL32_4: add32(off, s - p - 8); break; case IMAGE_REL_AMD64_REL32_5: add32(off, s - p - 9); break; - case IMAGE_REL_AMD64_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_AMD64_SECTION: + applySecIdx(off, os, file->ctx.outputSections.size()); + break; case IMAGE_REL_AMD64_SECREL: applySecRel(this, off, os, s); break; default: error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + @@ -125,13 +138,18 @@ } void SectionChunk::applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, - uint64_t s, uint64_t p) const { + uint64_t s, uint64_t p, + uint64_t imageBase) const { switch (type) { case IMAGE_REL_I386_ABSOLUTE: break; - case IMAGE_REL_I386_DIR32: add32(off, s + config->imageBase); break; + case IMAGE_REL_I386_DIR32: + add32(off, s + imageBase); + break; case IMAGE_REL_I386_DIR32NB: add32(off, s); break; case IMAGE_REL_I386_REL32: add32(off, s - p - 4); break; - case IMAGE_REL_I386_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_I386_SECTION: + applySecIdx(off, os, file->ctx.outputSections.size()); + break; case IMAGE_REL_I386_SECREL: applySecRel(this, off, os, s); break; default: error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + @@ -188,19 +206,26 @@ } void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, - uint64_t s, uint64_t p) const { + uint64_t s, uint64_t p, + uint64_t imageBase) const { // Pointer to thumb code must have the LSB set. uint64_t sx = s; if (os && (os->header.Characteristics & IMAGE_SCN_MEM_EXECUTE)) sx |= 1; switch (type) { - case IMAGE_REL_ARM_ADDR32: add32(off, sx + config->imageBase); break; + case IMAGE_REL_ARM_ADDR32: + add32(off, sx + imageBase); + break; case IMAGE_REL_ARM_ADDR32NB: add32(off, sx); break; - case IMAGE_REL_ARM_MOV32T: applyMOV32T(off, sx + config->imageBase); break; + case IMAGE_REL_ARM_MOV32T: + applyMOV32T(off, sx + imageBase); + break; case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(off, sx - p - 4); break; case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(off, sx - p - 4); break; case IMAGE_REL_ARM_BLX23T: applyBranch24T(off, sx - p - 4); break; - case IMAGE_REL_ARM_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_ARM_SECTION: + applySecIdx(off, os, file->ctx.outputSections.size()); + break; case IMAGE_REL_ARM_SECREL: applySecRel(this, off, os, s); break; case IMAGE_REL_ARM_REL32: add32(off, sx - p - 4); break; default: @@ -298,7 +323,8 @@ } void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, - uint64_t s, uint64_t p) const { + uint64_t s, uint64_t p, + uint64_t imageBase) const { switch (type) { case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(off, s, p, 12); break; case IMAGE_REL_ARM64_REL21: applyArm64Addr(off, s, p, 0); break; @@ -307,14 +333,20 @@ case IMAGE_REL_ARM64_BRANCH26: applyArm64Branch26(off, s - p); break; case IMAGE_REL_ARM64_BRANCH19: applyArm64Branch19(off, s - p); break; case IMAGE_REL_ARM64_BRANCH14: applyArm64Branch14(off, s - p); break; - case IMAGE_REL_ARM64_ADDR32: add32(off, s + config->imageBase); break; + case IMAGE_REL_ARM64_ADDR32: + add32(off, s + imageBase); + break; case IMAGE_REL_ARM64_ADDR32NB: add32(off, s); break; - case IMAGE_REL_ARM64_ADDR64: add64(off, s + config->imageBase); break; + case IMAGE_REL_ARM64_ADDR64: + add64(off, s + imageBase); + break; case IMAGE_REL_ARM64_SECREL: applySecRel(this, off, os, s); break; case IMAGE_REL_ARM64_SECREL_LOW12A: applySecRelLow12A(this, off, os, s); break; case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, off, os, s); break; case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, off, os, s); break; - case IMAGE_REL_ARM64_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_ARM64_SECTION: + applySecIdx(off, os, file->ctx.outputSections.size()); + break; case IMAGE_REL_ARM64_REL32: add32(off, s - p - 4); break; default: error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + @@ -324,12 +356,13 @@ static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk, Defined *sym, - const coff_relocation &rel) { + const coff_relocation &rel, + bool isMinGW) { // Don't report these errors when the relocation comes from a debug info // section or in mingw mode. MinGW mode object files (built by GCC) can // have leftover sections with relocations against discarded comdat // sections. Such sections are left as is, with relocations untouched. - if (fromChunk->isCodeView() || fromChunk->isDWARF() || config->mingw) + if (fromChunk->isCodeView() || fromChunk->isDWARF() || isMinGW) return; // Get the name of the symbol. If it's null, it was discarded early, so we @@ -395,7 +428,7 @@ // it was an absolute or synthetic symbol. if (!sym || (!os && !isa(sym) && !isa(sym))) { - maybeReportRelocationToDiscarded(this, sym, rel); + maybeReportRelocationToDiscarded(this, sym, rel, file->ctx.config.mingw); return; } @@ -403,18 +436,19 @@ // Compute the RVA of the relocation for relative relocations. uint64_t p = rva + rel.VirtualAddress; - switch (config->machine) { + uint64_t imageBase = file->ctx.config.imageBase; + switch (file->ctx.config.machine) { case AMD64: - applyRelX64(off, rel.Type, os, s, p); + applyRelX64(off, rel.Type, os, s, p, imageBase); break; case I386: - applyRelX86(off, rel.Type, os, s, p); + applyRelX86(off, rel.Type, os, s, p, imageBase); break; case ARMNT: - applyRelARM(off, rel.Type, os, s, p); + applyRelARM(off, rel.Type, os, s, p, imageBase); break; case ARM64: - applyRelARM64(off, rel.Type, os, s, p); + applyRelARM64(off, rel.Type, os, s, p, imageBase); break; default: llvm_unreachable("unknown machine type"); @@ -479,8 +513,9 @@ child->assocChildren = next; } -static uint8_t getBaserelType(const coff_relocation &rel) { - switch (config->machine) { +static uint8_t getBaserelType(const coff_relocation &rel, + llvm::COFF::MachineTypes machine) { + switch (machine) { case AMD64: if (rel.Type == IMAGE_REL_AMD64_ADDR64) return IMAGE_REL_BASED_DIR64; @@ -512,7 +547,7 @@ // Only called when base relocation is enabled. void SectionChunk::getBaserels(std::vector *res) { for (const coff_relocation &rel : getRelocs()) { - uint8_t ty = getBaserelType(rel); + uint8_t ty = getBaserelType(rel, file->ctx.config.machine); if (ty == IMAGE_REL_BASED_ABSOLUTE) continue; Symbol *target = file->getSymbol(rel.SymbolTableIndex); @@ -529,7 +564,8 @@ // another DLL) This returns the size the relocation is supposed to update, // in bits, or 0 if the relocation cannot be handled as a runtime pseudo // relocation. -static int getRuntimePseudoRelocSize(uint16_t type) { +static int getRuntimePseudoRelocSize(uint16_t type, + llvm::COFF::MachineTypes machine) { // Relocations that either contain an absolute address, or a plain // relative offset, since the runtime pseudo reloc implementation // adds 8/16/32/64 bit values to a memory address. @@ -555,7 +591,7 @@ // the image, or temporarily changed at runtime with VirtualProtect. // Since this only operates on direct address values, it doesn't work for // ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations. - switch (config->machine) { + switch (machine) { case AMD64: switch (type) { case IMAGE_REL_AMD64_ADDR64: @@ -612,7 +648,8 @@ dyn_cast_or_null(file->getSymbol(rel.SymbolTableIndex)); if (!target || !target->isRuntimePseudoReloc) continue; - int sizeInBits = getRuntimePseudoRelocSize(rel.Type); + int sizeInBits = + getRuntimePseudoRelocSize(rel.Type, file->ctx.config.machine); if (sizeInBits == 0) { error("unable to automatically import from " + target->getName() + " with relocation type " + @@ -718,7 +755,8 @@ buf[str.size()] = '\0'; } -ImportThunkChunkX64::ImportThunkChunkX64(Defined *s) : ImportThunkChunk(s) { +ImportThunkChunkX64::ImportThunkChunkX64(COFFLinkerContext &ctx, Defined *s) + : ImportThunkChunk(ctx, s) { // Intel Optimization Manual says that all branch targets // should be 16-byte aligned. MSVC linker does this too. setAlignment(16); @@ -731,14 +769,13 @@ } void ImportThunkChunkX86::getBaserels(std::vector *res) { - res->emplace_back(getRVA() + 2); + res->emplace_back(getRVA() + 2, ctx.config.machine); } void ImportThunkChunkX86::writeTo(uint8_t *buf) const { memcpy(buf, importThunkX86, sizeof(importThunkX86)); // The first two bytes is a JMP instruction. Fill its operand. - write32le(buf + 2, - impSymbol->getRVA() + config->imageBase); + write32le(buf + 2, impSymbol->getRVA() + ctx.config.imageBase); } void ImportThunkChunkARM::getBaserels(std::vector *res) { @@ -748,7 +785,7 @@ void ImportThunkChunkARM::writeTo(uint8_t *buf) const { memcpy(buf, importThunkARM, sizeof(importThunkARM)); // Fix mov.w and mov.t operands. - applyMOV32T(buf, impSymbol->getRVA() + config->imageBase); + applyMOV32T(buf, impSymbol->getRVA() + ctx.config.imageBase); } void ImportThunkChunkARM64::writeTo(uint8_t *buf) const { @@ -766,12 +803,12 @@ }; size_t RangeExtensionThunkARM::getSize() const { - assert(config->machine == ARMNT); + assert(ctx.config.machine == ARMNT); return sizeof(armThunk); } void RangeExtensionThunkARM::writeTo(uint8_t *buf) const { - assert(config->machine == ARMNT); + assert(ctx.config.machine == ARMNT); uint64_t offset = target->getRVA() - rva - 12; memcpy(buf, armThunk, sizeof(armThunk)); applyMOV32T(buf, uint32_t(offset)); @@ -786,28 +823,33 @@ }; size_t RangeExtensionThunkARM64::getSize() const { - assert(config->machine == ARM64); + assert(ctx.config.machine == ARM64); return sizeof(arm64Thunk); } void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const { - assert(config->machine == ARM64); + assert(ctx.config.machine == ARM64); memcpy(buf, arm64Thunk, sizeof(arm64Thunk)); applyArm64Addr(buf + 0, target->getRVA(), rva, 12); applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0); } +LocalImportChunk::LocalImportChunk(COFFLinkerContext &c, Defined *s) + : sym(s), ctx(c) { + setAlignment(ctx.config.wordsize); +} + void LocalImportChunk::getBaserels(std::vector *res) { - res->emplace_back(getRVA()); + res->emplace_back(getRVA(), ctx.config.machine); } -size_t LocalImportChunk::getSize() const { return config->wordsize; } +size_t LocalImportChunk::getSize() const { return ctx.config.wordsize; } void LocalImportChunk::writeTo(uint8_t *buf) const { - if (config->is64()) { - write64le(buf, sym->getRVA() + config->imageBase); + if (ctx.config.is64()) { + write64le(buf, sym->getRVA() + ctx.config.imageBase); } else { - write32le(buf, sym->getRVA() + config->imageBase); + write32le(buf, sym->getRVA() + ctx.config.imageBase); } } @@ -927,8 +969,8 @@ memcpy(buf, data.data(), data.size()); } -uint8_t Baserel::getDefaultType() { - switch (config->machine) { +uint8_t Baserel::getDefaultType(llvm::COFF::MachineTypes machine) { + switch (machine) { case AMD64: case ARM64: return IMAGE_REL_BASED_DIR64; @@ -986,10 +1028,10 @@ } // MinGW specific. -size_t AbsolutePointerChunk::getSize() const { return config->wordsize; } +size_t AbsolutePointerChunk::getSize() const { return ctx.config.wordsize; } void AbsolutePointerChunk::writeTo(uint8_t *buf) const { - if (config->is64()) { + if (ctx.config.is64()) { write64le(buf, value); } else { write32le(buf, value); diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -96,7 +96,7 @@ // Global configuration. struct Configuration { enum ManifestKind { Default, SideBySide, Embed, No }; - bool is64() { return machine == AMD64 || machine == ARM64; } + bool is64() const { return machine == AMD64 || machine == ARM64; } llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN; size_t wordsize; @@ -292,8 +292,6 @@ bool writeCheckSum = false; }; -extern std::unique_ptr config; - } // namespace lld::coff #endif diff --git a/lld/COFF/DLL.h b/lld/COFF/DLL.h --- a/lld/COFF/DLL.h +++ b/lld/COFF/DLL.h @@ -23,7 +23,7 @@ void add(DefinedImportData *sym) { imports.push_back(sym); } bool empty() { return imports.empty(); } - void create(); + void create(COFFLinkerContext &ctx); std::vector imports; std::vector dirs; @@ -37,9 +37,10 @@ // DelayLoadContents creates all chunks for the delay-load DLL import table. class DelayLoadContents { public: + DelayLoadContents(COFFLinkerContext &ctx) : ctx(ctx) {} void add(DefinedImportData *sym) { imports.push_back(sym); } bool empty() { return imports.empty(); } - void create(COFFLinkerContext &ctx, Defined *helper); + void create(Defined *helper); std::vector getChunks(); std::vector getDataChunks(); ArrayRef getCodeChunks() { return thunks; } @@ -60,19 +61,23 @@ std::vector hintNames; std::vector thunks; std::vector dllNames; + + COFFLinkerContext &ctx; }; // Windows-specific. // EdataContents creates all chunks for the DLL export table. class EdataContents { public: - EdataContents(); + EdataContents(COFFLinkerContext &ctx); std::vector chunks; uint64_t getRVA() { return chunks[0]->getRVA(); } uint64_t getSize() { return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA(); } + + COFFLinkerContext &ctx; }; } // namespace lld::coff diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp --- a/lld/COFF/DLL.cpp +++ b/lld/COFF/DLL.cpp @@ -61,19 +61,23 @@ // A chunk for the import descriptor table. class LookupChunk : public NonSectionChunk { public: - explicit LookupChunk(Chunk *c) : hintName(c) { - setAlignment(config->wordsize); + explicit LookupChunk(COFFLinkerContext &ctx, Chunk *c) + : hintName(c), ctx(ctx) { + setAlignment(ctx.config.wordsize); } - size_t getSize() const override { return config->wordsize; } + size_t getSize() const override { return ctx.config.wordsize; } void writeTo(uint8_t *buf) const override { - if (config->is64()) + if (ctx.config.is64()) write64le(buf, hintName->getRVA()); else write32le(buf, hintName->getRVA()); } Chunk *hintName; + +private: + COFFLinkerContext &ctx; }; // A chunk for the import descriptor table. @@ -81,15 +85,16 @@ // See Microsoft PE/COFF spec 7.1. Import Header for details. class OrdinalOnlyChunk : public NonSectionChunk { public: - explicit OrdinalOnlyChunk(uint16_t v) : ordinal(v) { - setAlignment(config->wordsize); + explicit OrdinalOnlyChunk(COFFLinkerContext &c, uint16_t v) + : ordinal(v), ctx(c) { + setAlignment(ctx.config.wordsize); } - size_t getSize() const override { return config->wordsize; } + size_t getSize() const override { return ctx.config.wordsize; } void writeTo(uint8_t *buf) const override { // An import-by-ordinal slot has MSB 1 to indicate that // this is import-by-ordinal (and not import-by-name). - if (config->is64()) { + if (ctx.config.is64()) { write64le(buf, (1ULL << 63) | ordinal); } else { write32le(buf, (1ULL << 31) | ordinal); @@ -97,6 +102,9 @@ } uint16_t ordinal; + +private: + COFFLinkerContext &ctx; }; // A chunk for the import descriptor table. @@ -135,14 +143,15 @@ }; static std::vector> -binImports(const std::vector &imports) { +binImports(COFFLinkerContext &ctx, + const std::vector &imports) { // Group DLL-imported symbols by DLL name because that's how // symbols are laid out in the import descriptor table. - auto less = [](const std::string &a, const std::string &b) { - return config->dllOrder[a] < config->dllOrder[b]; + auto less = [&ctx](const std::string &a, const std::string &b) { + return ctx.config.dllOrder[a] < ctx.config.dllOrder[b]; }; - std::map, - bool(*)(const std::string &, const std::string &)> m(less); + std::map, decltype(less)> m( + less); for (DefinedImportData *sym : imports) m[sym->getDLLName().lower()].push_back(sym); @@ -325,47 +334,56 @@ class ThunkChunkX86 : public NonSectionChunk { public: - ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} + ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm) + : imp(i), tailMerge(tm), ctx(ctx) {} size_t getSize() const override { return sizeof(thunkX86); } void writeTo(uint8_t *buf) const override { memcpy(buf, thunkX86, sizeof(thunkX86)); - write32le(buf + 1, imp->getRVA() + config->imageBase); + write32le(buf + 1, imp->getRVA() + ctx.config.imageBase); write32le(buf + 6, tailMerge->getRVA() - rva - 10); } void getBaserels(std::vector *res) override { - res->emplace_back(rva + 1); + res->emplace_back(rva + 1, ctx.config.machine); } Defined *imp = nullptr; Chunk *tailMerge = nullptr; + +private: + const COFFLinkerContext &ctx; }; class TailMergeChunkX86 : public NonSectionChunk { public: - TailMergeChunkX86(Chunk *d, Defined *h) : desc(d), helper(h) {} + TailMergeChunkX86(COFFLinkerContext &ctx, Chunk *d, Defined *h) + : desc(d), helper(h), ctx(ctx) {} size_t getSize() const override { return sizeof(tailMergeX86); } void writeTo(uint8_t *buf) const override { memcpy(buf, tailMergeX86, sizeof(tailMergeX86)); - write32le(buf + 4, desc->getRVA() + config->imageBase); + write32le(buf + 4, desc->getRVA() + ctx.config.imageBase); write32le(buf + 9, helper->getRVA() - rva - 13); } void getBaserels(std::vector *res) override { - res->emplace_back(rva + 4); + res->emplace_back(rva + 4, ctx.config.machine); } Chunk *desc = nullptr; Defined *helper = nullptr; + +private: + const COFFLinkerContext &ctx; }; class ThunkChunkARM : public NonSectionChunk { public: - ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) { + ThunkChunkARM(COFFLinkerContext &ctx, Defined *i, Chunk *tm) + : imp(i), tailMerge(tm), ctx(ctx) { setAlignment(2); } @@ -373,7 +391,7 @@ void writeTo(uint8_t *buf) const override { memcpy(buf, thunkARM, sizeof(thunkARM)); - applyMOV32T(buf + 0, imp->getRVA() + config->imageBase); + applyMOV32T(buf + 0, imp->getRVA() + ctx.config.imageBase); applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12); } @@ -383,11 +401,15 @@ Defined *imp = nullptr; Chunk *tailMerge = nullptr; + +private: + const COFFLinkerContext &ctx; }; class TailMergeChunkARM : public NonSectionChunk { public: - TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) { + TailMergeChunkARM(COFFLinkerContext &ctx, Chunk *d, Defined *h) + : desc(d), helper(h), ctx(ctx) { setAlignment(2); } @@ -395,7 +417,7 @@ void writeTo(uint8_t *buf) const override { memcpy(buf, tailMergeARM, sizeof(tailMergeARM)); - applyMOV32T(buf + 14, desc->getRVA() + config->imageBase); + applyMOV32T(buf + 14, desc->getRVA() + ctx.config.imageBase); applyBranch24T(buf + 22, helper->getRVA() - rva - 26); } @@ -405,6 +427,9 @@ Chunk *desc = nullptr; Defined *helper = nullptr; + +private: + const COFFLinkerContext &ctx; }; class ThunkChunkARM64 : public NonSectionChunk { @@ -448,28 +473,32 @@ // A chunk for the import descriptor table. class DelayAddressChunk : public NonSectionChunk { public: - explicit DelayAddressChunk(Chunk *c) : thunk(c) { - setAlignment(config->wordsize); + explicit DelayAddressChunk(COFFLinkerContext &ctx, Chunk *c) + : thunk(c), ctx(ctx) { + setAlignment(ctx.config.wordsize); } - size_t getSize() const override { return config->wordsize; } + size_t getSize() const override { return ctx.config.wordsize; } void writeTo(uint8_t *buf) const override { - if (config->is64()) { - write64le(buf, thunk->getRVA() + config->imageBase); + if (ctx.config.is64()) { + write64le(buf, thunk->getRVA() + ctx.config.imageBase); } else { uint32_t bit = 0; // Pointer to thumb code must have the LSB set, so adjust it. - if (config->machine == ARMNT) + if (ctx.config.machine == ARMNT) bit = 1; - write32le(buf, (thunk->getRVA() + config->imageBase) | bit); + write32le(buf, (thunk->getRVA() + ctx.config.imageBase) | bit); } } void getBaserels(std::vector *res) override { - res->emplace_back(rva); + res->emplace_back(rva, ctx.config.machine); } Chunk *thunk; + +private: + const COFFLinkerContext &ctx; }; // Export table @@ -509,19 +538,20 @@ class AddressTableChunk : public NonSectionChunk { public: - explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal) {} + explicit AddressTableChunk(COFFLinkerContext &ctx, size_t maxOrdinal) + : size(maxOrdinal), ctx(ctx) {} size_t getSize() const override { return size * 4; } void writeTo(uint8_t *buf) const override { memset(buf, 0, getSize()); - for (const Export &e : config->exports) { + for (const Export &e : ctx.config.exports) { assert(e.ordinal != 0 && "Export symbol has invalid ordinal"); // OrdinalBase is 1, so subtract 1 to get the index. uint8_t *p = buf + (e.ordinal - 1) * 4; uint32_t bit = 0; // Pointer to thumb code must have the LSB set, so adjust it. - if (config->machine == ARMNT && !e.data) + if (ctx.config.machine == ARMNT && !e.data) bit = 1; if (e.forwardChunk) { write32le(p, e.forwardChunk->getRVA() | bit); @@ -535,6 +565,7 @@ private: size_t size; + const COFFLinkerContext &ctx; }; class NamePointersChunk : public NonSectionChunk { @@ -555,11 +586,12 @@ class ExportOrdinalChunk : public NonSectionChunk { public: - explicit ExportOrdinalChunk(size_t i) : size(i) {} + explicit ExportOrdinalChunk(const COFFLinkerContext &ctx, size_t i) + : size(i), ctx(ctx) {} size_t getSize() const override { return size * 2; } void writeTo(uint8_t *buf) const override { - for (Export &e : config->exports) { + for (const Export &e : ctx.config.exports) { if (e.noname) continue; assert(e.ordinal != 0 && "Export symbol has invalid ordinal"); @@ -571,12 +603,13 @@ private: size_t size; + const COFFLinkerContext &ctx; }; } // anonymous namespace -void IdataContents::create() { - std::vector> v = binImports(imports); +void IdataContents::create(COFFLinkerContext &ctx) { + std::vector> v = binImports(ctx, imports); // Create .idata contents for each DLL. for (std::vector &syms : v) { @@ -588,18 +621,18 @@ for (DefinedImportData *s : syms) { uint16_t ord = s->getOrdinal(); if (s->getExternalName().empty()) { - lookups.push_back(make(ord)); - addresses.push_back(make(ord)); + lookups.push_back(make(ctx, ord)); + addresses.push_back(make(ctx, ord)); continue; } auto *c = make(s->getExternalName(), ord); - lookups.push_back(make(c)); - addresses.push_back(make(c)); + lookups.push_back(make(ctx, c)); + addresses.push_back(make(ctx, c)); hints.push_back(c); } // Terminate with null values. - lookups.push_back(make(config->wordsize)); - addresses.push_back(make(config->wordsize)); + lookups.push_back(make(ctx.config.wordsize)); + addresses.push_back(make(ctx.config.wordsize)); for (int i = 0, e = syms.size(); i < e; ++i) syms[i]->setLocation(addresses[base + i]); @@ -635,9 +668,9 @@ return dirs.size() * sizeof(delay_import_directory_table_entry); } -void DelayLoadContents::create(COFFLinkerContext &ctx, Defined *h) { +void DelayLoadContents::create(Defined *h) { helper = h; - std::vector> v = binImports(imports); + std::vector> v = binImports(ctx, imports); // Create .didat contents for each DLL. for (std::vector &syms : v) { @@ -649,15 +682,15 @@ Chunk *tm = newTailMergeChunk(dir); for (DefinedImportData *s : syms) { Chunk *t = newThunkChunk(s, tm); - auto *a = make(t); + auto *a = make(ctx, t); addresses.push_back(a); thunks.push_back(t); StringRef extName = s->getExternalName(); if (extName.empty()) { - names.push_back(make(s->getOrdinal())); + names.push_back(make(ctx, s->getOrdinal())); } else { auto *c = make(extName, 0); - names.push_back(make(c)); + names.push_back(make(ctx, c)); hintNames.push_back(c); // Add a syntentic symbol for this load thunk, using the "__imp___load" // prefix, in case this thunk needs to be added to the list of valid @@ -692,13 +725,13 @@ } Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) { - switch (config->machine) { + switch (ctx.config.machine) { case AMD64: return make(dir, helper); case I386: - return make(dir, helper); + return make(ctx, dir, helper); case ARMNT: - return make(dir, helper); + return make(ctx, dir, helper); case ARM64: return make(dir, helper); default: @@ -708,13 +741,13 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s, Chunk *tailMerge) { - switch (config->machine) { + switch (ctx.config.machine) { case AMD64: return make(s, tailMerge); case I386: - return make(s, tailMerge); + return make(ctx, s, tailMerge); case ARMNT: - return make(s, tailMerge); + return make(ctx, s, tailMerge); case ARM64: return make(s, tailMerge); default: @@ -722,20 +755,20 @@ } } -EdataContents::EdataContents() { +EdataContents::EdataContents(COFFLinkerContext &ctx) : ctx(ctx) { uint16_t maxOrdinal = 0; - for (Export &e : config->exports) + for (Export &e : ctx.config.exports) maxOrdinal = std::max(maxOrdinal, e.ordinal); - auto *dllName = make(sys::path::filename(config->outputFile)); - auto *addressTab = make(maxOrdinal); + auto *dllName = make(sys::path::filename(ctx.config.outputFile)); + auto *addressTab = make(ctx, maxOrdinal); std::vector names; - for (Export &e : config->exports) + for (Export &e : ctx.config.exports) if (!e.noname) names.push_back(make(e.exportName)); std::vector forwards; - for (Export &e : config->exports) { + for (Export &e : ctx.config.exports) { if (e.forwardTo.empty()) continue; e.forwardChunk = make(e.forwardTo); @@ -743,7 +776,7 @@ } auto *nameTab = make(names); - auto *ordinalTab = make(names.size()); + auto *ordinalTab = make(ctx, names.size()); auto *dir = make(maxOrdinal, names.size(), dllName, addressTab, nameTab, ordinalTab); chunks.push_back(dir); diff --git a/lld/COFF/DebugTypes.cpp b/lld/COFF/DebugTypes.cpp --- a/lld/COFF/DebugTypes.cpp +++ b/lld/COFF/DebugTypes.cpp @@ -235,7 +235,7 @@ reinterpret_cast(contents.data() + ref.Offset), ref.Count); for (TypeIndex &ti : indices) { if (!remapTypeIndex(ti, ref.Kind)) { - if (config->verbose) { + if (ctx.config.verbose) { uint16_t kind = reinterpret_cast(rec.data())->RecordKind; StringRef fname = file ? file->getName() : ""; @@ -305,7 +305,7 @@ // Merge .debug$T for a generic object file. Error TpiSource::mergeDebugT(TypeMerger *m) { - assert(!config->debugGHashes && + assert(!ctx.config.debugGHashes && "use remapTpiWithGHashes when ghash is enabled"); CVTypeArray types; @@ -329,7 +329,7 @@ tpiMap = indexMapStorage; ipiMap = indexMapStorage; - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords = indexMapStorage.size() - nbHeadIndices; nbTypeRecordsBytes = reader.getLength(); // Count how many times we saw each type record in our input. This @@ -356,7 +356,7 @@ // Merge types from a type server PDB. Error TypeServerSource::mergeDebugT(TypeMerger *m) { - assert(!config->debugGHashes && + assert(!ctx.config.debugGHashes && "use remapTpiWithGHashes when ghash is enabled"); pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile(); @@ -385,7 +385,7 @@ ipiMap = ipiSrc->indexMapStorage; } - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords = tpiMap.size() + ipiMap.size(); nbTypeRecordsBytes = expectedTpi->typeArray().getUnderlyingStream().getLength() + @@ -727,14 +727,14 @@ } void TpiSource::remapTpiWithGHashes(GHashState *g) { - assert(config->debugGHashes && "ghashes must be enabled"); + assert(ctx.config.debugGHashes && "ghashes must be enabled"); fillMapFromGHashes(g); tpiMap = indexMapStorage; ipiMap = indexMapStorage; mergeUniqueTypeRecords(file->debugTypes); // TODO: Free all unneeded ghash resources now that we have a full index map. - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords = ghashes.size(); nbTypeRecordsBytes = file->debugTypes.size(); } @@ -787,7 +787,7 @@ // Merge types from a type server PDB. void TypeServerSource::remapTpiWithGHashes(GHashState *g) { - assert(config->debugGHashes && "ghashes must be enabled"); + assert(ctx.config.debugGHashes && "ghashes must be enabled"); // IPI merging depends on TPI, so do TPI first, then do IPI. No need to // propagate errors, those should've been handled during ghash loading. @@ -805,13 +805,13 @@ ipiSrc->ipiMap = ipiMap; ipiSrc->mergeUniqueTypeRecords(typeArrayToBytes(ipi.typeArray())); - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords = ipiSrc->ghashes.size(); nbTypeRecordsBytes = ipi.typeArray().getUnderlyingStream().getLength(); } } - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords += ghashes.size(); nbTypeRecordsBytes += tpi.typeArray().getUnderlyingStream().getLength(); } @@ -898,7 +898,7 @@ mergeUniqueTypeRecords(file->debugTypes, TypeIndex(precompDependency.getStartTypeIndex() + precompDependency.getTypesCount())); - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords = ghashes.size(); nbTypeRecordsBytes = file->debugTypes.size(); } diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -9,7 +9,6 @@ #ifndef LLD_COFF_DRIVER_H #define LLD_COFF_DRIVER_H -#include "COFFLinkerContext.h" #include "Config.h" #include "SymbolTable.h" #include "lld/Common/LLVM.h" @@ -30,8 +29,6 @@ namespace lld::coff { -extern std::unique_ptr driver; - using llvm::COFF::MachineTypes; using llvm::COFF::WindowsSubsystem; using std::optional; @@ -41,10 +38,6 @@ COFFOptTable(); }; -// Constructing the option table is expensive. Use a global table to avoid doing -// it more than once. -extern COFFOptTable optTable; - // The result of parsing the .drective section. The /export: and /include: // options are handled separately because they reference symbols, and the number // of symbols can be quite large. The LLVM Option library will perform at least @@ -59,6 +52,8 @@ class ArgParser { public: + ArgParser(COFFLinkerContext &ctx); + // Parses command line options. llvm::opt::InputArgList parse(llvm::ArrayRef args); @@ -75,11 +70,13 @@ void addLINK(SmallVector &argv); std::vector tokenize(StringRef s); + + COFFLinkerContext &ctx; }; class LinkerDriver { public: - LinkerDriver(COFFLinkerContext &c) : ctx(c) {} + LinkerDriver(COFFLinkerContext &ctx) : ctx(ctx) {} void linkerMain(llvm::ArrayRef args); @@ -115,6 +112,42 @@ // Determines the location of the sysroot based on `args`, environment, etc. void detectWinSysRoot(const llvm::opt::InputArgList &args); + // Symbol names are mangled by prepending "_" on x86. + StringRef mangle(StringRef sym); + + llvm::Triple::ArchType getArch(); + + uint64_t getDefaultImageBase(); + + bool isDecorated(StringRef sym); + + std::string getMapFile(const llvm::opt::InputArgList &args, + llvm::opt::OptSpecifier os, + llvm::opt::OptSpecifier osFile); + + std::string getImplibPath(); + + // The import name is calculated as follows: + // + // | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY + // -----+----------------+---------------------+------------------ + // LINK | {value} | {value}.{.dll/.exe} | {output name} + // LIB | {value} | {value}.dll | {output name}.dll + // + std::string getImportName(bool asLib); + + void createImportLibrary(bool asLib); + + void parseModuleDefs(StringRef path); + + // Parse an /order file. If an option is given, the linker places COMDAT + // sections int he same order as their names appear in the given file. + void parseOrderFile(StringRef arg); + + void parseCallGraphFile(StringRef path); + + void parsePDBAltPath(); + // Parses LIB environment which contains a list of search paths. void addLibSearchPaths(); @@ -171,61 +204,68 @@ llvm::SmallString<128> universalCRTLibPath; int sdkMajor = 0; llvm::SmallString<128> windowsSdkLibPath; -}; -// Functions below this line are defined in DriverUtils.cpp. + // Functions below this line are defined in DriverUtils.cpp. -void printHelp(const char *argv0); + void printHelp(const char *argv0); -// Parses a string in the form of "[,]". -void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr); + // Parses a string in the form of "[,]". + void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr); -void parseGuard(StringRef arg); + void parseGuard(StringRef arg); -// Parses a string in the form of "[.]". -// Minor's default value is 0. -void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor); + // Parses a string in the form of "[.]". + // Minor's default value is 0. + void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor); -// Parses a string in the form of "[,[.]]". -void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, - uint32_t *minor, bool *gotVersion = nullptr); + // Parses a string in the form of "[,[.]]". + void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, + uint32_t *minor, bool *gotVersion = nullptr); -void parseAlternateName(StringRef); -void parseMerge(StringRef); -void parsePDBPageSize(StringRef); -void parseSection(StringRef); -void parseAligncomm(StringRef); + void parseAlternateName(StringRef); + void parseMerge(StringRef); + void parsePDBPageSize(StringRef); + void parseSection(StringRef); + void parseAligncomm(StringRef); -// Parses a string in the form of "[:]" -void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine); + // Parses a string in the form of "[:]" + void parseFunctionPadMin(llvm::opt::Arg *a); -// Parses a string in the form of "EMBED[,=]|NO". -void parseManifest(StringRef arg); + // Parses a string in the form of "EMBED[,=]|NO". + void parseManifest(StringRef arg); -// Parses a string in the form of "level=|uiAccess=" -void parseManifestUAC(StringRef arg); + // Parses a string in the form of "level=|uiAccess=" + void parseManifestUAC(StringRef arg); -// Parses a string in the form of "cd|net[,(cd|net)]*" -void parseSwaprun(StringRef arg); + // Parses a string in the form of "cd|net[,(cd|net)]*" + void parseSwaprun(StringRef arg); -// Create a resource file containing a manifest XML. -std::unique_ptr createManifestRes(); -void createSideBySideManifest(); + // Create a resource file containing a manifest XML. + std::unique_ptr createManifestRes(); + void createSideBySideManifest(); + std::string createDefaultXml(); + std::string createManifestXmlWithInternalMt(StringRef defaultXml); + std::string createManifestXmlWithExternalMt(StringRef defaultXml); + std::string createManifestXml(); -// Used for dllexported symbols. -Export parseExport(StringRef arg); -void fixupExports(); -void assignExportOrdinals(); + std::unique_ptr + createMemoryBufferForManifestRes(size_t manifestRes); -// Parses a string in the form of "key=value" and check -// if value matches previous values for the key. -// This feature used in the directive section to reject -// incompatible objects. -void checkFailIfMismatch(StringRef arg, InputFile *source); + // Used for dllexported symbols. + Export parseExport(StringRef arg); + void fixupExports(); + void assignExportOrdinals(); -// Convert Windows resource files (.res files) to a .obj file. -MemoryBufferRef convertResToCOFF(ArrayRef mbs, - ArrayRef objs); + // Parses a string in the form of "key=value" and check + // if value matches previous values for the key. + // This feature used in the directive section to reject + // incompatible objects. + void checkFailIfMismatch(StringRef arg, InputFile *source); + + // Convert Windows resource files (.res files) to a .obj file. + MemoryBufferRef convertResToCOFF(ArrayRef mbs, + ArrayRef objs); +}; // Create enum with OPT_xxx values for each option in Options.td enum { diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -61,9 +61,6 @@ namespace lld::coff { -std::unique_ptr config; -std::unique_ptr driver; - bool link(ArrayRef args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { // This driver-specific context will be freed later by lldMain(). @@ -74,10 +71,7 @@ ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now" " (use /errorlimit:0 to see all errors)"; - config = std::make_unique(); - driver = std::make_unique(*ctx); - - driver->linkerMain(args); + ctx->driver.linkerMain(args); return errorCount() == 0; } @@ -98,11 +92,11 @@ // Drop directory components and replace extension with // ".exe", ".dll" or ".sys". -static std::string getOutputPath(StringRef path) { +static std::string getOutputPath(StringRef path, bool isDll, bool isDriver) { StringRef ext = ".exe"; - if (config->dll) + if (isDll) ext = ".dll"; - else if (config->driver) + else if (isDriver) ext = ".sys"; return (sys::path::stem(path) + ext).str(); @@ -146,15 +140,15 @@ } // Symbol names are mangled by prepending "_" on x86. -static StringRef mangle(StringRef sym) { - assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); - if (config->machine == I386) +StringRef LinkerDriver::mangle(StringRef sym) { + assert(ctx.config.machine != IMAGE_FILE_MACHINE_UNKNOWN); + if (ctx.config.machine == I386) return saver().save("_" + sym); return sym; } -static llvm::Triple::ArchType getArch() { - switch (config->machine) { +llvm::Triple::ArchType LinkerDriver::getArch() { + switch (ctx.config.machine) { case I386: return llvm::Triple::ArchType::x86; case AMD64: @@ -177,9 +171,9 @@ MemoryBufferRef mbref = *mb; make>(std::move(mb)); // take ownership - if (driver->tar) - driver->tar->append(relativeToRoot(mbref.getBufferIdentifier()), - mbref.getBuffer()); + if (ctx.driver.tar) + ctx.driver.tar->append(relativeToRoot(mbref.getBufferIdentifier()), + mbref.getBuffer()); return mbref; } @@ -223,7 +217,7 @@ error(filename + ": is not a native COFF file. Recompile without /GL"); break; case file_magic::pecoff_executable: - if (config->mingw) { + if (ctx.config.mingw) { ctx.symtab.addFile(make(ctx, mbref)); break; } @@ -254,12 +248,12 @@ // the option `/nodefaultlib` than a reference to a file in the root // directory. std::string nearest; - if (optTable.findNearest(pathStr, nearest) > 1) + if (ctx.optTable.findNearest(pathStr, nearest) > 1) error(msg); else error(msg + "; did you mean '" + nearest + "'"); } else - driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy); + ctx.driver.addBuffer(std::move(mbOrErr.first), wholeArchive, lazy); }); } @@ -300,8 +294,8 @@ auto reportBufferError = [=](Error &&e, StringRef childName) { fatal("could not get the buffer for the member defining symbol " + - toCOFFString(sym) + ": " + parentName + "(" + childName + "): " + - toString(std::move(e))); + toCOFFString(ctx, sym) + ": " + parentName + "(" + childName + + "): " + toString(std::move(e))); }; if (!c.getParent()->isThin()) { @@ -311,16 +305,16 @@ reportBufferError(mbOrErr.takeError(), check(c.getFullName())); MemoryBufferRef mb = mbOrErr.get(); enqueueTask([=]() { - driver->addArchiveBuffer(mb, toCOFFString(sym), parentName, - offsetInArchive); + ctx.driver.addArchiveBuffer(mb, toCOFFString(ctx, sym), parentName, + offsetInArchive); }); return; } - std::string childName = CHECK( - c.getFullName(), - "could not get the filename for the member defining symbol " + - toCOFFString(sym)); + std::string childName = + CHECK(c.getFullName(), + "could not get the filename for the member defining symbol " + + toCOFFString(ctx, sym)); auto future = std::make_shared>( createFutureForFile(childName)); enqueueTask([=]() { @@ -329,14 +323,15 @@ reportBufferError(errorCodeToError(mbOrErr.second), childName); // Pass empty string as archive name so that the original filename is // used as the buffer identifier. - driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), - toCOFFString(sym), "", /*OffsetInArchive=*/0); + ctx.driver.addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), + toCOFFString(ctx, sym), "", + /*OffsetInArchive=*/0); }); } -static bool isDecorated(StringRef sym) { +bool LinkerDriver::isDecorated(StringRef sym) { return sym.startswith("@") || sym.contains("@@") || sym.startswith("?") || - (!config->mingw && sym.contains('@')); + (!ctx.config.mingw && sym.contains('@')); } // Parses .drectve section contents and returns a list of files @@ -348,7 +343,7 @@ log("Directives: " + toString(file) + ": " + s); - ArgParser parser; + ArgParser parser(ctx); // .drectve is always tokenized using Windows shell rules. // /EXPORT: option can appear too many times, processing in fastpath. ParsedDirectives directives = parser.parseDirectives(s); @@ -362,14 +357,14 @@ continue; Export exp = parseExport(e); - if (config->machine == I386 && config->mingw) { + if (ctx.config.machine == I386 && ctx.config.mingw) { if (!isDecorated(exp.name)) exp.name = saver().save("_" + exp.name); if (!exp.extName.empty() && !isDecorated(exp.extName)) exp.extName = saver().save("_" + exp.extName); } exp.directives = true; - config->exports.push_back(exp); + ctx.config.exports.push_back(exp); } // Handle /include: in bulk. @@ -398,7 +393,7 @@ enqueuePath(*path, false, false); break; case OPT_entry: - config->entry = addUndefined(mangle(arg->getValue())); + ctx.config.entry = addUndefined(mangle(arg->getValue())); break; case OPT_failifmismatch: checkFailIfMismatch(arg->getValue(), file); @@ -407,32 +402,32 @@ addUndefined(arg->getValue()); break; case OPT_manifestdependency: - config->manifestDependencies.insert(arg->getValue()); + ctx.config.manifestDependencies.insert(arg->getValue()); break; case OPT_merge: parseMerge(arg->getValue()); break; case OPT_nodefaultlib: - config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); + ctx.config.noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); break; case OPT_release: - config->writeCheckSum = true; + ctx.config.writeCheckSum = true; break; case OPT_section: parseSection(arg->getValue()); break; case OPT_stack: - parseNumbers(arg->getValue(), &config->stackReserve, - &config->stackCommit); + parseNumbers(arg->getValue(), &ctx.config.stackReserve, + &ctx.config.stackCommit); break; case OPT_subsystem: { bool gotVersion = false; - parseSubsystem(arg->getValue(), &config->subsystem, - &config->majorSubsystemVersion, - &config->minorSubsystemVersion, &gotVersion); + parseSubsystem(arg->getValue(), &ctx.config.subsystem, + &ctx.config.majorSubsystemVersion, + &ctx.config.minorSubsystemVersion, &gotVersion); if (gotVersion) { - config->majorOSVersion = config->majorSubsystemVersion; - config->minorOSVersion = config->minorSubsystemVersion; + ctx.config.majorOSVersion = ctx.config.majorSubsystemVersion; + ctx.config.minorOSVersion = ctx.config.minorSubsystemVersion; } break; } @@ -451,9 +446,9 @@ // Find file from search paths. You can omit ".obj", this function takes // care of that. Note that the returned path is not guaranteed to exist. StringRef LinkerDriver::doFindFile(StringRef filename) { - auto getFilename = [](StringRef filename) -> StringRef { - if (config->vfs) - if (auto statOrErr = config->vfs->status(filename)) + auto getFilename = [this](StringRef filename) -> StringRef { + if (ctx.config.vfs) + if (auto statOrErr = ctx.config.vfs->status(filename)) return saver().save(statOrErr->getName()); return filename; }; @@ -522,7 +517,7 @@ StringRef ret = doFindFile(filename); // For MinGW, if the find above didn't turn up anything, try // looking for a MinGW formatted library name. - if (config->mingw && ret == filename) + if (ctx.config.mingw && ret == filename) return doFindLibMinGW(filename); return ret; } @@ -531,13 +526,13 @@ // consideration. This never returns the same path (in that case, // it returns std::nullopt). std::optional LinkerDriver::findLib(StringRef filename) { - if (config->noDefaultLibAll) + if (ctx.config.noDefaultLibAll) return std::nullopt; if (!visitedLibs.insert(filename.lower()).second) return std::nullopt; StringRef path = doFindLib(filename); - if (config->noDefaultLibs.count(path.lower())) + if (ctx.config.noDefaultLibs.count(path.lower())) return std::nullopt; if (std::optional id = getUniqueID(path)) @@ -658,7 +653,7 @@ Symbol *b = ctx.symtab.addUndefined(name); if (!b->isGCRoot) { b->isGCRoot = true; - config->gcroot.push_back(b); + ctx.config.gcroot.push_back(b); } return b; } @@ -687,15 +682,15 @@ // each of which corresponds to a user-defined "main" function. This function // infers an entry point from a user-defined "main" function. StringRef LinkerDriver::findDefaultEntry() { - assert(config->subsystem != IMAGE_SUBSYSTEM_UNKNOWN && + assert(ctx.config.subsystem != IMAGE_SUBSYSTEM_UNKNOWN && "must handle /subsystem before calling this"); - if (config->mingw) - return mangle(config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI + if (ctx.config.mingw) + return mangle(ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI ? "WinMainCRTStartup" : "mainCRTStartup"); - if (config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { + if (ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { if (findUnderscoreMangle("wWinMain")) { if (!findUnderscoreMangle("WinMain")) return mangle("wWinMainCRTStartup"); @@ -712,9 +707,9 @@ } WindowsSubsystem LinkerDriver::inferSubsystem() { - if (config->dll) + if (ctx.config.dll) return IMAGE_SUBSYSTEM_WINDOWS_GUI; - if (config->mingw) + if (ctx.config.mingw) return IMAGE_SUBSYSTEM_WINDOWS_CUI; // Note that link.exe infers the subsystem from the presence of these // functions even if /entry: or /nodefaultlib are passed which causes them @@ -736,10 +731,10 @@ return IMAGE_SUBSYSTEM_UNKNOWN; } -static uint64_t getDefaultImageBase() { - if (config->is64()) - return config->dll ? 0x180000000 : 0x140000000; - return config->dll ? 0x10000000 : 0x400000; +uint64_t LinkerDriver::getDefaultImageBase() { + if (ctx.config.is64()) + return ctx.config.dll ? 0x180000000 : 0x140000000; + return ctx.config.dll ? 0x10000000 : 0x400000; } static std::string rewritePath(StringRef s) { @@ -880,8 +875,9 @@ return debugTypes; } -static std::string getMapFile(const opt::InputArgList &args, - opt::OptSpecifier os, opt::OptSpecifier osFile) { +std::string LinkerDriver::getMapFile(const opt::InputArgList &args, + opt::OptSpecifier os, + opt::OptSpecifier osFile) { auto *arg = args.getLastArg(os, osFile); if (!arg) return ""; @@ -889,14 +885,14 @@ return arg->getValue(); assert(arg->getOption().getID() == os.getID()); - StringRef outFile = config->outputFile; + StringRef outFile = ctx.config.outputFile; return (outFile.substr(0, outFile.rfind('.')) + ".map").str(); } -static std::string getImplibPath() { - if (!config->implib.empty()) - return std::string(config->implib); - SmallString<128> out = StringRef(config->outputFile); +std::string LinkerDriver::getImplibPath() { + if (!ctx.config.implib.empty()) + return std::string(ctx.config.implib); + SmallString<128> out = StringRef(ctx.config.outputFile); sys::path::replace_extension(out, ".lib"); return std::string(out.str()); } @@ -908,26 +904,26 @@ // LINK | {value} | {value}.{.dll/.exe} | {output name} // LIB | {value} | {value}.dll | {output name}.dll // -static std::string getImportName(bool asLib) { +std::string LinkerDriver::getImportName(bool asLib) { SmallString<128> out; - if (config->importName.empty()) { - out.assign(sys::path::filename(config->outputFile)); + if (ctx.config.importName.empty()) { + out.assign(sys::path::filename(ctx.config.outputFile)); if (asLib) sys::path::replace_extension(out, ".dll"); } else { - out.assign(config->importName); + out.assign(ctx.config.importName); if (!sys::path::has_extension(out)) sys::path::replace_extension(out, - (config->dll || asLib) ? ".dll" : ".exe"); + (ctx.config.dll || asLib) ? ".dll" : ".exe"); } return std::string(out.str()); } -static void createImportLibrary(bool asLib) { +void LinkerDriver::createImportLibrary(bool asLib) { std::vector exports; - for (Export &e1 : config->exports) { + for (Export &e1 : ctx.config.exports) { COFFShortExport e2; e2.Name = std::string(e1.name); e2.SymbolName = std::string(e1.symbolName); @@ -944,9 +940,9 @@ std::string libName = getImportName(asLib); std::string path = getImplibPath(); - if (!config->incremental) { - checkError(writeImportLibrary(libName, path, exports, config->machine, - config->mingw)); + if (!ctx.config.incremental) { + checkError(writeImportLibrary(libName, path, exports, ctx.config.machine, + ctx.config.mingw)); return; } @@ -955,8 +951,8 @@ ErrorOr> oldBuf = MemoryBuffer::getFile( path, /*IsText=*/false, /*RequiresNullTerminator=*/false); if (!oldBuf) { - checkError(writeImportLibrary(libName, path, exports, config->machine, - config->mingw)); + checkError(writeImportLibrary(libName, path, exports, ctx.config.machine, + ctx.config.mingw)); return; } @@ -966,8 +962,8 @@ fatal("cannot create temporary file for import library " + path + ": " + ec.message()); - if (Error e = writeImportLibrary(libName, tmpName, exports, config->machine, - config->mingw)) { + if (Error e = writeImportLibrary(libName, tmpName, exports, + ctx.config.machine, ctx.config.mingw)) { checkError(std::move(e)); return; } @@ -982,39 +978,39 @@ } } -static void parseModuleDefs(StringRef path) { +void LinkerDriver::parseModuleDefs(StringRef path) { std::unique_ptr mb = CHECK(MemoryBuffer::getFile(path, /*IsText=*/false, /*RequiresNullTerminator=*/false, /*IsVolatile=*/true), "could not open " + path); COFFModuleDefinition m = check(parseCOFFModuleDefinition( - mb->getMemBufferRef(), config->machine, config->mingw)); + mb->getMemBufferRef(), ctx.config.machine, ctx.config.mingw)); // Include in /reproduce: output if applicable. - driver->takeBuffer(std::move(mb)); + ctx.driver.takeBuffer(std::move(mb)); - if (config->outputFile.empty()) - config->outputFile = std::string(saver().save(m.OutputFile)); - config->importName = std::string(saver().save(m.ImportName)); + if (ctx.config.outputFile.empty()) + ctx.config.outputFile = std::string(saver().save(m.OutputFile)); + ctx.config.importName = std::string(saver().save(m.ImportName)); if (m.ImageBase) - config->imageBase = m.ImageBase; + ctx.config.imageBase = m.ImageBase; if (m.StackReserve) - config->stackReserve = m.StackReserve; + ctx.config.stackReserve = m.StackReserve; if (m.StackCommit) - config->stackCommit = m.StackCommit; + ctx.config.stackCommit = m.StackCommit; if (m.HeapReserve) - config->heapReserve = m.HeapReserve; + ctx.config.heapReserve = m.HeapReserve; if (m.HeapCommit) - config->heapCommit = m.HeapCommit; + ctx.config.heapCommit = m.HeapCommit; if (m.MajorImageVersion) - config->majorImageVersion = m.MajorImageVersion; + ctx.config.majorImageVersion = m.MajorImageVersion; if (m.MinorImageVersion) - config->minorImageVersion = m.MinorImageVersion; + ctx.config.minorImageVersion = m.MinorImageVersion; if (m.MajorOSVersion) - config->majorOSVersion = m.MajorOSVersion; + ctx.config.majorOSVersion = m.MajorOSVersion; if (m.MinorOSVersion) - config->minorOSVersion = m.MinorOSVersion; + ctx.config.minorOSVersion = m.MinorOSVersion; for (COFFShortExport e1 : m.Exports) { Export e2; @@ -1026,7 +1022,7 @@ StringRef(e1.Name).contains('.')) { e2.name = saver().save(e1.ExtName); e2.forwardTo = saver().save(e1.Name); - config->exports.push_back(e2); + ctx.config.exports.push_back(e2); continue; } e2.name = saver().save(e1.Name); @@ -1037,7 +1033,7 @@ e2.data = e1.Data; e2.isPrivate = e1.Private; e2.constant = e1.Constant; - config->exports.push_back(e2); + ctx.config.exports.push_back(e2); } } @@ -1059,7 +1055,7 @@ // Parse an /order file. If an option is given, the linker places // COMDAT sections in the same order as their names appear in the // given file. -static void parseOrderFile(COFFLinkerContext &ctx, StringRef arg) { +void LinkerDriver::parseOrderFile(StringRef arg) { // For some reason, the MSVC linker requires a filename to be // preceded by "@". if (!arg.startswith("@")) { @@ -1088,22 +1084,22 @@ // end of an output section. for (StringRef arg : args::getLines(mb->getMemBufferRef())) { std::string s(arg); - if (config->machine == I386 && !isDecorated(s)) + if (ctx.config.machine == I386 && !isDecorated(s)) s = "_" + s; if (set.count(s) == 0) { - if (config->warnMissingOrderSymbol) + if (ctx.config.warnMissingOrderSymbol) warn("/order:" + arg + ": missing symbol: " + s + " [LNK4037]"); } else - config->order[s] = INT_MIN + config->order.size(); + ctx.config.order[s] = INT_MIN + ctx.config.order.size(); } // Include in /reproduce: output if applicable. - driver->takeBuffer(std::move(mb)); + ctx.driver.takeBuffer(std::move(mb)); } -static void parseCallGraphFile(COFFLinkerContext &ctx, StringRef path) { +void LinkerDriver::parseCallGraphFile(StringRef path) { std::unique_ptr mb = CHECK(MemoryBuffer::getFile(path, /*IsText=*/false, /*RequiresNullTerminator=*/false, @@ -1120,7 +1116,7 @@ auto findSection = [&](StringRef name) -> SectionChunk * { Symbol *sym = map.lookup(name); if (!sym) { - if (config->warnMissingOrderSymbol) + if (ctx.config.warnMissingOrderSymbol) warn(path + ": no such symbol: " + name); return nullptr; } @@ -1142,11 +1138,11 @@ if (SectionChunk *from = findSection(fields[0])) if (SectionChunk *to = findSection(fields[1])) - config->callGraphProfile[{from, to}] += count; + ctx.config.callGraphProfile[{from, to}] += count; } // Include in /reproduce: output if applicable. - driver->takeBuffer(std::move(mb)); + ctx.driver.takeBuffer(std::move(mb)); } static void readCallGraphsFromObjectFiles(COFFLinkerContext &ctx) { @@ -1172,7 +1168,7 @@ auto *from = dyn_cast_or_null(fromSym->getChunk()); auto *to = dyn_cast_or_null(toSym->getChunk()); if (from && to) - config->callGraphProfile[{from, to}] += count; + ctx.config.callGraphProfile[{from, to}] += count; } } } @@ -1187,7 +1183,7 @@ 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) + for (Export &r : ctx.config.exports) markAddrsig(r.sym); // Visit the address-significance table in each object file and mark each @@ -1225,12 +1221,12 @@ // binary). // lld only supports %_PDB% and %_EXT% and warns on references to all other env // vars. -static void parsePDBAltPath(StringRef altPath) { +void LinkerDriver::parsePDBAltPath() { SmallString<128> buf; StringRef pdbBasename = - sys::path::filename(config->pdbPath, sys::path::Style::windows); + sys::path::filename(ctx.config.pdbPath, sys::path::Style::windows); StringRef binaryExtension = - sys::path::extension(config->outputFile, sys::path::Style::windows); + sys::path::extension(ctx.config.outputFile, sys::path::Style::windows); if (!binaryExtension.empty()) binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'. @@ -1241,19 +1237,22 @@ // v v v // a...%...%... size_t cursor = 0; - while (cursor < altPath.size()) { + while (cursor < ctx.config.pdbAltPath.size()) { size_t firstMark, secondMark; - if ((firstMark = altPath.find('%', cursor)) == StringRef::npos || - (secondMark = altPath.find('%', firstMark + 1)) == StringRef::npos) { + if ((firstMark = ctx.config.pdbAltPath.find('%', cursor)) == + StringRef::npos || + (secondMark = ctx.config.pdbAltPath.find('%', firstMark + 1)) == + StringRef::npos) { // Didn't find another full fragment, treat rest of string as literal. - buf.append(altPath.substr(cursor)); + buf.append(ctx.config.pdbAltPath.substr(cursor)); break; } // Found a full fragment. Append text in front of first %, and interpret // text between first and second % as variable name. - buf.append(altPath.substr(cursor, firstMark - cursor)); - StringRef var = altPath.substr(firstMark, secondMark - firstMark + 1); + buf.append(ctx.config.pdbAltPath.substr(cursor, firstMark - cursor)); + StringRef var = + ctx.config.pdbAltPath.substr(firstMark, secondMark - firstMark + 1); if (var.equals_insensitive("%_pdb%")) buf.append(pdbBasename); else if (var.equals_insensitive("%_ext%")) @@ -1267,7 +1266,7 @@ cursor = secondMark + 1; } - config->pdbAltPath = buf; + ctx.config.pdbAltPath = buf; } /// Convert resource files and potentially merge input resource object @@ -1281,7 +1280,7 @@ resourceObjFiles.push_back(f); } - if (!config->mingw && + if (!ctx.config.mingw && (resourceObjFiles.size() > 1 || (resourceObjFiles.size() == 1 && !resources.empty()))) { error((!resources.empty() ? "internal .obj file created from .res files" @@ -1312,16 +1311,16 @@ // than MinGW in the case that nothing is explicitly exported. void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { if (!args.hasArg(OPT_export_all_symbols)) { - if (!config->dll) + if (!ctx.config.dll) return; - if (!config->exports.empty()) + if (!ctx.config.exports.empty()) return; if (args.hasArg(OPT_exclude_all_symbols)) return; } - AutoExporter exporter(excludedSymbols); + AutoExporter exporter(ctx, excludedSymbols); for (auto *arg : args.filtered(OPT_wholearchive_file)) if (std::optional path = doFindFile(arg->getValue())) @@ -1336,12 +1335,12 @@ ctx.symtab.forEachSymbol([&](Symbol *s) { auto *def = dyn_cast(s); - if (!exporter.shouldExport(ctx, def)) + if (!exporter.shouldExport(def)) return; if (!def->isGCRoot) { def->isGCRoot = true; - config->gcroot.push_back(def); + ctx.config.gcroot.push_back(def); } Export e; @@ -1351,7 +1350,7 @@ if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) e.data = true; s->isUsedInRegularObj = true; - config->exports.push_back(e); + ctx.config.exports.push_back(e); }); } @@ -1404,6 +1403,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { ScopedTimer rootTimer(ctx.rootTimer); + Configuration *config = &ctx.config; // Needed for LTO. InitializeAllTargetInfos(); @@ -1423,7 +1423,7 @@ } // Parse command line options. - ArgParser parser; + ArgParser parser(ctx); opt::InputArgList args = parser.parse(argsArr); // Parse and evaluate -mllvm options. @@ -2036,7 +2036,7 @@ // Handle /functionpadmin for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) - parseFunctionPadMin(arg, config->machine); + parseFunctionPadMin(arg); if (tar) { tar->append("response.txt", @@ -2128,7 +2128,8 @@ // Set default image name if neither /out or /def set it. if (config->outputFile.empty()) { config->outputFile = getOutputPath( - (*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue()); + (*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue(), + config->dll, config->driver); } // Fail early if an output file is not writable. @@ -2174,8 +2175,8 @@ sys::fs::make_absolute(config->pdbAltPath); sys::path::remove_dots(config->pdbAltPath); } else { - // Don't do this earlier, so that Config->OutputFile is ready. - parsePDBAltPath(config->pdbAltPath); + // Don't do this earlier, so that ctx.OutputFile is ready. + parsePDBAltPath(); } } @@ -2317,7 +2318,7 @@ // If -thinlto-index-only is given, we should create only "index // files" and not object files. Index file creation is already done - // in compileBitcodeFiles, so we are done if that's the case. + // in addCombinedLTOObject, so we are done if that's the case. if (config->thinLTOIndexOnly) return; @@ -2364,7 +2365,7 @@ // Handle /output-def (MinGW specific). if (auto *arg = args.getLastArg(OPT_output_def)) - writeDefFile(arg->getValue()); + writeDefFile(arg->getValue(), config->exports); // Set extra alignment for .comm symbols for (auto pair : config->alignComm) { @@ -2403,14 +2404,14 @@ 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(ctx, arg->getValue()); + parseOrderFile(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(ctx, arg->getValue()); + parseCallGraphFile(arg->getValue()); } readCallGraphsFromObjectFiles(ctx); } @@ -2447,7 +2448,7 @@ // Identify identical COMDAT sections to merge them. if (config->doICF != ICFLevel::None) { findKeepUniqueSections(ctx); - doICF(ctx, config->doICF); + doICF(ctx); } // Write the result. diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "Config.h" +#include "COFFLinkerContext.h" #include "Driver.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" @@ -75,7 +75,7 @@ } // anonymous namespace // Parses a string in the form of "[,]". -void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) { +void LinkerDriver::parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) { auto [s1, s2] = arg.split(','); if (s1.getAsInteger(0, *addr)) fatal("invalid number: " + s1); @@ -85,7 +85,8 @@ // Parses a string in the form of "[.]". // If second number is not present, Minor is set to 0. -void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) { +void LinkerDriver::parseVersion(StringRef arg, uint32_t *major, + uint32_t *minor) { auto [s1, s2] = arg.split('.'); if (s1.getAsInteger(10, *major)) fatal("invalid number: " + s1); @@ -94,28 +95,29 @@ fatal("invalid number: " + s2); } -void parseGuard(StringRef fullArg) { +void LinkerDriver::parseGuard(StringRef fullArg) { SmallVector splitArgs; fullArg.split(splitArgs, ","); for (StringRef arg : splitArgs) { if (arg.equals_insensitive("no")) - config->guardCF = GuardCFLevel::Off; + ctx.config.guardCF = GuardCFLevel::Off; else if (arg.equals_insensitive("nolongjmp")) - config->guardCF &= ~GuardCFLevel::LongJmp; + ctx.config.guardCF &= ~GuardCFLevel::LongJmp; else if (arg.equals_insensitive("noehcont")) - config->guardCF &= ~GuardCFLevel::EHCont; + ctx.config.guardCF &= ~GuardCFLevel::EHCont; else if (arg.equals_insensitive("cf") || arg.equals_insensitive("longjmp")) - config->guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp; + ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp; else if (arg.equals_insensitive("ehcont")) - config->guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont; + ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont; else fatal("invalid argument to /guard: " + arg); } } // Parses a string in the form of "[,[.]]". -void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, - uint32_t *minor, bool *gotVersion) { +void LinkerDriver::parseSubsystem(StringRef arg, WindowsSubsystem *sys, + uint32_t *major, uint32_t *minor, + bool *gotVersion) { auto [sysStr, ver] = arg.split(','); std::string sysStrLower = sysStr.lower(); *sys = StringSwitch(sysStrLower) @@ -140,19 +142,19 @@ // Parse a string of the form of "=". // Results are directly written to Config. -void parseAlternateName(StringRef s) { +void LinkerDriver::parseAlternateName(StringRef s) { auto [from, to] = s.split('='); if (from.empty() || to.empty()) fatal("/alternatename: invalid argument: " + s); - auto it = config->alternateNames.find(from); - if (it != config->alternateNames.end() && it->second != to) + auto it = ctx.config.alternateNames.find(from); + if (it != ctx.config.alternateNames.end() && it->second != to) fatal("/alternatename: conflicts: " + s); - config->alternateNames.insert(it, std::make_pair(from, to)); + ctx.config.alternateNames.insert(it, std::make_pair(from, to)); } // Parse a string of the form of "=". // Results are directly written to Config. -void parseMerge(StringRef s) { +void LinkerDriver::parseMerge(StringRef s) { auto [from, to] = s.split('='); if (from.empty() || to.empty()) fatal("/merge: invalid argument: " + s); @@ -160,7 +162,7 @@ fatal("/merge: cannot merge '.rsrc' with any section"); if (from == ".reloc" || to == ".reloc") fatal("/merge: cannot merge '.reloc' with any section"); - auto pair = config->merge.insert(std::make_pair(from, to)); + auto pair = ctx.config.merge.insert(std::make_pair(from, to)); bool inserted = pair.second; if (!inserted) { StringRef existing = pair.first->second; @@ -169,7 +171,7 @@ } } -void parsePDBPageSize(StringRef s) { +void LinkerDriver::parsePDBPageSize(StringRef s) { int v; if (s.getAsInteger(0, v)) { error("/pdbpagesize: invalid argument: " + s); @@ -180,7 +182,7 @@ return; } - config->pdbPageSize = v; + ctx.config.pdbPageSize = v; } static uint32_t parseSectionAttributes(StringRef s) { @@ -216,15 +218,15 @@ } // Parses /section option argument. -void parseSection(StringRef s) { +void LinkerDriver::parseSection(StringRef s) { auto [name, attrs] = s.split(','); if (name.empty() || attrs.empty()) fatal("/section: invalid argument: " + s); - config->section[name] = parseSectionAttributes(attrs); + ctx.config.section[name] = parseSectionAttributes(attrs); } // Parses /aligncomm option argument. -void parseAligncomm(StringRef s) { +void LinkerDriver::parseAligncomm(StringRef s) { auto [name, align] = s.split(','); if (name.empty() || align.empty()) { error("/aligncomm: invalid argument: " + s); @@ -235,56 +237,57 @@ error("/aligncomm: invalid argument: " + s); return; } - config->alignComm[std::string(name)] = - std::max(config->alignComm[std::string(name)], 1 << v); + ctx.config.alignComm[std::string(name)] = + std::max(ctx.config.alignComm[std::string(name)], 1 << v); } // Parses /functionpadmin option argument. -void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) { +void LinkerDriver::parseFunctionPadMin(llvm::opt::Arg *a) { StringRef arg = a->getNumValues() ? a->getValue() : ""; if (!arg.empty()) { // Optional padding in bytes is given. - if (arg.getAsInteger(0, config->functionPadMin)) + if (arg.getAsInteger(0, ctx.config.functionPadMin)) error("/functionpadmin: invalid argument: " + arg); return; } // No optional argument given. // Set default padding based on machine, similar to link.exe. // There is no default padding for ARM platforms. - if (machine == I386) { - config->functionPadMin = 5; - } else if (machine == AMD64) { - config->functionPadMin = 6; + if (ctx.config.machine == I386) { + ctx.config.functionPadMin = 5; + } else if (ctx.config.machine == AMD64) { + ctx.config.functionPadMin = 6; } else { error("/functionpadmin: invalid argument for this machine: " + arg); } } // Parses a string in the form of "EMBED[,=]|NO". -// Results are directly written to Config. -void parseManifest(StringRef arg) { +// Results are directly written to +// Config. +void LinkerDriver::parseManifest(StringRef arg) { if (arg.equals_insensitive("no")) { - config->manifest = Configuration::No; + ctx.config.manifest = Configuration::No; return; } if (!arg.startswith_insensitive("embed")) fatal("invalid option " + arg); - config->manifest = Configuration::Embed; + ctx.config.manifest = Configuration::Embed; arg = arg.substr(strlen("embed")); if (arg.empty()) return; if (!arg.startswith_insensitive(",id=")) fatal("invalid option " + arg); arg = arg.substr(strlen(",id=")); - if (arg.getAsInteger(0, config->manifestID)) + if (arg.getAsInteger(0, ctx.config.manifestID)) fatal("invalid option " + arg); } // Parses a string in the form of "level=|uiAccess=|NO". // Results are directly written to Config. -void parseManifestUAC(StringRef arg) { +void LinkerDriver::parseManifestUAC(StringRef arg) { if (arg.equals_insensitive("no")) { - config->manifestUAC = false; + ctx.config.manifestUAC = false; return; } for (;;) { @@ -293,12 +296,12 @@ return; if (arg.startswith_insensitive("level=")) { arg = arg.substr(strlen("level=")); - std::tie(config->manifestLevel, arg) = arg.split(" "); + std::tie(ctx.config.manifestLevel, arg) = arg.split(" "); continue; } if (arg.startswith_insensitive("uiaccess=")) { arg = arg.substr(strlen("uiaccess=")); - std::tie(config->manifestUIAccess, arg) = arg.split(" "); + std::tie(ctx.config.manifestUIAccess, arg) = arg.split(" "); continue; } fatal("invalid option " + arg); @@ -307,13 +310,13 @@ // Parses a string in the form of "cd|net[,(cd|net)]*" // Results are directly written to Config. -void parseSwaprun(StringRef arg) { +void LinkerDriver::parseSwaprun(StringRef arg) { do { auto [swaprun, newArg] = arg.split(','); if (swaprun.equals_insensitive("cd")) - config->swaprunCD = true; + ctx.config.swaprunCD = true; else if (swaprun.equals_insensitive("net")) - config->swaprunNet = true; + ctx.config.swaprunNet = true; else if (swaprun.empty()) error("/swaprun: missing argument"); else @@ -371,7 +374,7 @@ }; } -static std::string createDefaultXml() { +std::string LinkerDriver::createDefaultXml() { std::string ret; raw_string_ostream os(ret); @@ -380,17 +383,17 @@ os << "\n" << "\n"; - if (config->manifestUAC) { + if (ctx.config.manifestUAC) { os << " \n" << " \n" << " \n" - << " \n" + << " \n" << " \n" << " \n" << " \n"; } - for (auto manifestDependency : config->manifestDependencies) { + for (auto manifestDependency : ctx.config.manifestDependencies) { os << " \n" << " \n" << " \n" @@ -401,7 +404,8 @@ return os.str(); } -static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { +std::string +LinkerDriver::createManifestXmlWithInternalMt(StringRef defaultXml) { std::unique_ptr defaultXmlCopy = MemoryBuffer::getMemBufferCopy(defaultXml); @@ -410,11 +414,11 @@ fatal("internal manifest tool failed on default xml: " + toString(std::move(e))); - for (StringRef filename : config->manifestInput) { + for (StringRef filename : ctx.config.manifestInput) { std::unique_ptr manifest = check(MemoryBuffer::getFile(filename)); // Call takeBuffer to include in /reproduce: output if applicable. - if (auto e = merger.merge(driver->takeBuffer(std::move(manifest)))) + if (auto e = merger.merge(takeBuffer(std::move(manifest)))) fatal("internal manifest tool failed on file " + filename + ": " + toString(std::move(e))); } @@ -422,7 +426,8 @@ return std::string(merger.getMergedManifest().get()->getBuffer()); } -static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { +std::string +LinkerDriver::createManifestXmlWithExternalMt(StringRef defaultXml) { // Create the default manifest file as a temporary file. TemporaryFile Default("defaultxml", "manifest"); std::error_code ec; @@ -439,14 +444,14 @@ Executor e("mt.exe"); e.add("/manifest"); e.add(Default.path); - for (StringRef filename : config->manifestInput) { + for (StringRef filename : ctx.config.manifestInput) { e.add("/manifest"); e.add(filename); // Manually add the file to the /reproduce: tar if needed. - if (driver->tar) + if (tar) if (auto mbOrErr = MemoryBuffer::getFile(filename)) - driver->takeBuffer(std::move(*mbOrErr)); + takeBuffer(std::move(*mbOrErr)); } e.add("/nologo"); e.add("/out:" + StringRef(user.path)); @@ -458,9 +463,9 @@ ->getBuffer()); } -static std::string createManifestXml() { +std::string LinkerDriver::createManifestXml() { std::string defaultXml = createDefaultXml(); - if (config->manifestInput.empty()) + if (ctx.config.manifestInput.empty()) return defaultXml; if (windows_manifest::isAvailable()) @@ -469,14 +474,14 @@ return createManifestXmlWithExternalMt(defaultXml); } -static std::unique_ptr -createMemoryBufferForManifestRes(size_t manifestSize) { +std::unique_ptr +LinkerDriver::createMemoryBufferForManifestRes(size_t manifestSize) { size_t resSize = alignTo( object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE + sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + sizeof(object::WinResHeaderSuffix) + manifestSize, object::WIN_RES_DATA_ALIGNMENT); - return WritableMemoryBuffer::getNewMemBuffer(resSize, config->outputFile + + return WritableMemoryBuffer::getNewMemBuffer(resSize, ctx.config.outputFile + ".manifest.res"); } @@ -487,7 +492,8 @@ buf += object::WIN_RES_NULL_ENTRY_SIZE; } -static void writeResEntryHeader(char *&buf, size_t manifestSize) { +static void writeResEntryHeader(char *&buf, size_t manifestSize, + int manifestID) { // Write the prefix. auto *prefix = reinterpret_cast(buf); prefix->DataSize = manifestSize; @@ -499,7 +505,7 @@ // Write the Type/Name IDs. auto *iDs = reinterpret_cast(buf); iDs->setType(RT_MANIFEST); - iDs->setName(config->manifestID); + iDs->setName(manifestID); buf += sizeof(object::WinResIDs); // Write the suffix. @@ -513,7 +519,7 @@ } // Create a resource file containing a manifest XML. -std::unique_ptr createManifestRes() { +std::unique_ptr LinkerDriver::createManifestRes() { std::string manifest = createManifestXml(); std::unique_ptr res = @@ -521,17 +527,17 @@ char *buf = res->getBufferStart(); writeResFileHeader(buf); - writeResEntryHeader(buf, manifest.size()); + writeResEntryHeader(buf, manifest.size(), ctx.config.manifestID); // Copy the manifest data into the .res file. std::copy(manifest.begin(), manifest.end(), buf); return std::move(res); } -void createSideBySideManifest() { - std::string path = std::string(config->manifestFile); +void LinkerDriver::createSideBySideManifest() { + std::string path = std::string(ctx.config.manifestFile); if (path == "") - path = config->outputFile + ".manifest"; + path = ctx.config.outputFile + ".manifest"; std::error_code ec; raw_fd_ostream out(path, ec, sys::fs::OF_TextWithCRLF); if (ec) @@ -543,7 +549,7 @@ // "[=][,@ordinal[,NONAME]][,DATA][,PRIVATE]" // or "=.". // Used for parsing /export arguments. -Export parseExport(StringRef arg) { +Export LinkerDriver::parseExport(StringRef arg) { Export e; StringRef rest; std::tie(e.name, rest) = arg.split(","); @@ -605,14 +611,14 @@ fatal("invalid /export: " + arg); } -static StringRef undecorate(StringRef sym) { - if (config->machine != I386) +static StringRef undecorate(COFFLinkerContext &ctx, StringRef sym) { + if (ctx.config.machine != I386) return sym; // In MSVC mode, a fully decorated stdcall function is exported // as-is with the leading underscore (with type IMPORT_NAME). // In MinGW mode, a decorated stdcall function gets the underscore // removed, just like normal cdecl functions. - if (sym.startswith("_") && sym.contains('@') && !config->mingw) + if (sym.startswith("_") && sym.contains('@') && !ctx.config.mingw) return sym; return sym.startswith("_") ? sym.substr(1) : sym; } @@ -639,26 +645,26 @@ // Performs error checking on all /export arguments. // It also sets ordinals. -void fixupExports() { +void LinkerDriver::fixupExports() { // Symbol ordinals must be unique. std::set ords; - for (Export &e : config->exports) { + for (Export &e : ctx.config.exports) { if (e.ordinal == 0) continue; if (!ords.insert(e.ordinal).second) fatal("duplicate export ordinal: " + e.name); } - for (Export &e : config->exports) { + for (Export &e : ctx.config.exports) { if (!e.forwardTo.empty()) { - e.exportName = undecorate(e.name); + e.exportName = undecorate(ctx, e.name); } else { - e.exportName = undecorate(e.extName.empty() ? e.name : e.extName); + e.exportName = undecorate(ctx, e.extName.empty() ? e.name : e.extName); } } - if (config->killAt && config->machine == I386) { - for (Export &e : config->exports) { + if (ctx.config.killAt && ctx.config.machine == I386) { + for (Export &e : ctx.config.exports) { e.name = killAt(e.name, true); e.exportName = killAt(e.exportName, false); e.extName = killAt(e.extName, true); @@ -667,9 +673,9 @@ } // Uniquefy by name. - DenseMap map(config->exports.size()); + DenseMap map(ctx.config.exports.size()); std::vector v; - for (Export &e : config->exports) { + for (Export &e : ctx.config.exports) { auto pair = map.insert(std::make_pair(e.exportName, &e)); bool inserted = pair.second; if (inserted) { @@ -681,20 +687,20 @@ continue; warn("duplicate /export option: " + e.name); } - config->exports = std::move(v); + ctx.config.exports = std::move(v); // Sort by name. - llvm::sort(config->exports, [](const Export &a, const Export &b) { + llvm::sort(ctx.config.exports, [](const Export &a, const Export &b) { return a.exportName < b.exportName; }); } -void assignExportOrdinals() { +void LinkerDriver::assignExportOrdinals() { // Assign unique ordinals if default (= 0). uint32_t max = 0; - for (Export &e : config->exports) + for (Export &e : ctx.config.exports) max = std::max(max, (uint32_t)e.ordinal); - for (Export &e : config->exports) + for (Export &e : ctx.config.exports) if (e.ordinal == 0) e.ordinal = ++max; if (max > std::numeric_limits::max()) @@ -704,11 +710,11 @@ // Parses a string in the form of "key=value" and check // if value matches previous values for the same key. -void checkFailIfMismatch(StringRef arg, InputFile *source) { +void LinkerDriver::checkFailIfMismatch(StringRef arg, InputFile *source) { auto [k, v] = arg.split('='); if (k.empty() || v.empty()) fatal("/failifmismatch: invalid argument: " + arg); - std::pair existing = config->mustMatch[k]; + std::pair existing = ctx.config.mustMatch[k]; if (!existing.first.empty() && v != existing.first) { std::string sourceStr = source ? toString(source) : "cmd-line"; std::string existingStr = @@ -717,14 +723,14 @@ existingStr + " has value " + existing.first + "\n>>> " + sourceStr + " has value " + v); } - config->mustMatch[k] = {v, source}; + ctx.config.mustMatch[k] = {v, source}; } // Convert Windows resource files (.res files) to a .obj file. // Does what cvtres.exe does, but in-process and cross-platform. -MemoryBufferRef convertResToCOFF(ArrayRef mbs, - ArrayRef objs) { - object::WindowsResourceParser parser(/* MinGW */ config->mingw); +MemoryBufferRef LinkerDriver::convertResToCOFF(ArrayRef mbs, + ArrayRef objs) { + object::WindowsResourceParser parser(/* MinGW */ ctx.config.mingw); std::vector duplicates; for (MemoryBufferRef mb : mbs) { @@ -749,18 +755,18 @@ fatal(toString(std::move(ec))); } - if (config->mingw) + if (ctx.config.mingw) parser.cleanUpManifests(duplicates); for (const auto &dupeDiag : duplicates) - if (config->forceMultipleRes) + if (ctx.config.forceMultipleRes) warn(dupeDiag); else error(dupeDiag); Expected> e = - llvm::object::writeWindowsResourceCOFF(config->machine, parser, - config->timestamp); + llvm::object::writeWindowsResourceCOFF(ctx.config.machine, parser, + ctx.config.timestamp); if (!e) fatal("failed to write .res to COFF: " + toString(e.takeError())); @@ -790,8 +796,6 @@ COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {} -COFFOptTable optTable; - // Set color diagnostics according to --color-diagnostics={auto,always,never} // or --no-color-diagnostics flags. static void handleColorDiagnostics(opt::InputArgList &args) { @@ -827,6 +831,8 @@ return cl::TokenizeWindowsCommandLine; } +ArgParser::ArgParser(COFFLinkerContext &c) : ctx(c) {} + // Parses a given list of options. opt::InputArgList ArgParser::parse(ArrayRef argv) { // Make InputArgList from string vectors. @@ -837,7 +843,8 @@ // options so we parse here before and ignore all the options but // --rsp-quoting and /lldignoreenv. // (This means --rsp-quoting can't be added through %LINK%.) - opt::InputArgList args = optTable.ParseArgs(argv, missingIndex, missingCount); + opt::InputArgList args = + ctx.optTable.ParseArgs(argv, missingIndex, missingCount); // Expand response files (arguments in the form of @) and insert // flags from %LINK% and %_LINK_%, and then parse the argument again. @@ -846,8 +853,8 @@ if (!args.hasArg(OPT_lldignoreenv)) addLINK(expandedArgv); cl::ExpandResponseFiles(saver(), getQuotingStyle(args), expandedArgv); - args = optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(), - missingIndex, missingCount); + args = ctx.optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(), + missingIndex, missingCount); // Print the real command line if response files are expanded. if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) { @@ -859,10 +866,10 @@ // Save the command line after response file expansion so we can write it to // the PDB if necessary. Mimic MSVC, which skips input files. - config->argv = {argv[0]}; + ctx.config.argv = {argv[0]}; for (opt::Arg *arg : args) { if (arg->getOption().getKind() != opt::Option::InputClass) { - config->argv.push_back(args.getArgString(arg->getIndex())); + ctx.config.argv.push_back(args.getArgString(arg->getIndex())); } } @@ -876,7 +883,7 @@ for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) { std::string nearest; - if (optTable.findNearest(arg->getAsString(args), nearest) > 1) + if (ctx.optTable.findNearest(arg->getAsString(args), nearest) > 1) warn("ignoring unknown argument '" + arg->getAsString(args) + "'"); else warn("ignoring unknown argument '" + arg->getAsString(args) + @@ -921,7 +928,7 @@ unsigned missingIndex; unsigned missingCount; - result.args = optTable.ParseArgs(rest, missingIndex, missingCount); + result.args = ctx.optTable.ParseArgs(rest, missingIndex, missingCount); if (missingCount) fatal(Twine(result.args.getArgString(missingIndex)) + ": missing argument"); @@ -951,10 +958,10 @@ return std::vector(tokens.begin(), tokens.end()); } -void printHelp(const char *argv0) { - optTable.printHelp(lld::outs(), - (std::string(argv0) + " [options] file...").c_str(), - "LLVM Linker", false); +void LinkerDriver::printHelp(const char *argv0) { + ctx.optTable.printHelp(lld::outs(), + (std::string(argv0) + " [options] file...").c_str(), + "LLVM Linker", false); } } // namespace coff diff --git a/lld/COFF/ICF.h b/lld/COFF/ICF.h --- a/lld/COFF/ICF.h +++ b/lld/COFF/ICF.h @@ -15,10 +15,9 @@ namespace lld::coff { -class Chunk; class COFFLinkerContext; -void doICF(COFFLinkerContext &ctx, ICFLevel); +void doICF(COFFLinkerContext &ctx); } // namespace lld::coff diff --git a/lld/COFF/ICF.cpp b/lld/COFF/ICF.cpp --- a/lld/COFF/ICF.cpp +++ b/lld/COFF/ICF.cpp @@ -38,7 +38,7 @@ class ICF { public: - ICF(COFFLinkerContext &c, ICFLevel icfLevel) : icfLevel(icfLevel), ctx(c){}; + ICF(COFFLinkerContext &c) : ctx(c){}; void run(); private: @@ -61,7 +61,6 @@ std::vector chunks; int cnt = 0; std::atomic repeat = {false}; - ICFLevel icfLevel = ICFLevel::All; COFFLinkerContext &ctx; }; @@ -84,7 +83,7 @@ return false; // Under regular (not safe) ICF, all code sections are eligible. - if ((icfLevel == ICFLevel::All) && + if ((ctx.config.doICF == ICFLevel::All) && c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) return true; @@ -317,8 +316,6 @@ } // Entry point to ICF. -void doICF(COFFLinkerContext &ctx, ICFLevel icfLevel) { - ICF(ctx, icfLevel).run(); -} +void doICF(COFFLinkerContext &ctx) { ICF(ctx).run(); } } // namespace lld::coff diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h --- a/lld/COFF/InputFiles.h +++ b/lld/COFF/InputFiles.h @@ -333,8 +333,7 @@ // for details about the format. class ImportFile : public InputFile { public: - explicit ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m) - : InputFile(ctx, ImportKind, m) {} + explicit ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m); static bool classof(const InputFile *f) { return f->kind() == ImportKind; } @@ -358,8 +357,8 @@ // symbols provided by this import library member. We also track whether the // imported symbol is used separately from whether the thunk is used in order // to avoid creating unnecessary thunks. - bool live = !config->doGC; - bool thunkLive = !config->doGC; + bool live; + bool thunkLive; }; // Used for LTO. @@ -408,7 +407,16 @@ return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; } -std::string replaceThinLTOSuffix(StringRef path); +// Convenience class for initializing a coff_section with specific flags. +class FakeSection { +public: + FakeSection(int c) { section.Characteristics = c; } + + coff_section section; +}; + +std::string replaceThinLTOSuffix(StringRef path, StringRef suffix, + StringRef repl); } // namespace coff std::string toString(const coff::InputFile *file); diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -72,7 +72,7 @@ /// 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. -static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f, +static void checkAndSetWeakAlias(COFFLinkerContext &ctx, InputFile *f, Symbol *source, Symbol *target) { if (auto *u = dyn_cast(source)) { if (u->weakAlias && u->weakAlias != target) { @@ -81,9 +81,9 @@ // of another symbol emitted near the weak symbol. // Just use the definition from the first object file that defined // this weak symbol. - if (config->mingw) + if (ctx.config.mingw) return; - symtab->reportDuplicate(source, f); + ctx.symtab.reportDuplicate(source, f); } u->weakAlias = target; } @@ -109,13 +109,13 @@ void ArchiveFile::addMember(const Archive::Symbol &sym) { const Archive::Child &c = CHECK(sym.getMember(), - "could not get the member for symbol " + toCOFFString(sym)); + "could not get the member for symbol " + toCOFFString(ctx, sym)); // Return an empty buffer if we have already returned the same buffer. if (!seen.insert(c.getChildOffset()).second) return; - driver->enqueueArchiveMember(c, sym, getName()); + ctx.driver.enqueueArchiveMember(c, sym, getName()); } std::vector lld::coff::getArchiveMembers(Archive *file) { @@ -237,7 +237,7 @@ // and then write it to a separate .pdb file. // Ignore DWARF debug info unless /debug is given. - if (!config->debug && name.startswith(".debug_")) + if (!ctx.config.debug && name.startswith(".debug_")) return nullptr; if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) @@ -260,7 +260,7 @@ guardEHContChunks.push_back(c); else if (name == ".sxdata") sxDataChunks.push_back(c); - else if (config->tailMerge && sec->NumberOfRelocations == 0 && + else if (ctx.config.tailMerge && sec->NumberOfRelocations == 0 && name == ".rdata" && leaderName.startswith("??_C@")) // COFF sections that look like string literal sections (i.e. no // relocations, in .rdata, leader symbol name matches the MSVC name mangling @@ -366,7 +366,7 @@ // everything should be fine. If something actually refers to the symbol // (e.g. the undefined weak alias), linking will fail due to undefined // references at the end. - if (config->mingw && name.startswith(".weak.")) + if (ctx.config.mingw && name.startswith(".weak.")) return nullptr; return ctx.symtab.addUndefined(name, this, false); } @@ -400,7 +400,7 @@ } else if (std::optional optSym = createDefined(coffSym, comdatDefs, prevailingComdat)) { symbols[i] = *optSym; - if (config->mingw && prevailingComdat) + if (ctx.config.mingw && prevailingComdat) recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap); } else { // createDefined() returns std::nullopt if a symbol belongs to a section @@ -421,7 +421,7 @@ if (const coff_aux_section_definition *def = sym.getSectionDefinition()) { if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) readAssociativeDefinition(sym, def); - else if (config->mingw) + else if (ctx.config.mingw) maybeAssociateSEHForMingw(sym, def, prevailingSectionMap); } if (sparseChunks[sym.getSectionNumber()] == pendingComdat) { @@ -436,7 +436,7 @@ for (auto &kv : weakAliases) { Symbol *sym = kv.first; uint32_t idx = kv.second; - checkAndSetWeakAlias(&ctx.symtab, this, sym, symbols[idx]); + checkAndSetWeakAlias(ctx, this, sym, symbols[idx]); } // Free the memory used by sparseChunks now that symbol loading is finished. @@ -496,10 +496,10 @@ // Clang on the other hand picks "any". To be able to link two object files // with a __declspec(selectany) declaration, one compiled with gcc and the // other with clang, we merge them as proper "same size as" - if (config->mingw && ((selection == IMAGE_COMDAT_SELECT_ANY && - leaderSelection == IMAGE_COMDAT_SELECT_SAME_SIZE) || - (selection == IMAGE_COMDAT_SELECT_SAME_SIZE && - leaderSelection == IMAGE_COMDAT_SELECT_ANY))) { + if (ctx.config.mingw && ((selection == IMAGE_COMDAT_SELECT_ANY && + leaderSelection == IMAGE_COMDAT_SELECT_SAME_SIZE) || + (selection == IMAGE_COMDAT_SELECT_SAME_SIZE && + leaderSelection == IMAGE_COMDAT_SELECT_ANY))) { leaderSelection = selection = IMAGE_COMDAT_SELECT_SAME_SIZE; } @@ -511,7 +511,7 @@ // seems better though. // (This behavior matches ModuleLinker::getComdatResult().) if (selection != leaderSelection) { - log(("conflicting comdat type for " + toString(*leader) + ": " + + log(("conflicting comdat type for " + toString(ctx, *leader) + ": " + Twine((int)leaderSelection) + " in " + toString(leader->getFile()) + " and " + Twine((int)selection) + " in " + toString(this)) .str()); @@ -530,7 +530,7 @@ case IMAGE_COMDAT_SELECT_SAME_SIZE: if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) { - if (!config->mingw) { + if (!ctx.config.mingw) { ctx.symtab.reportDuplicate(leader, this); } else { const coff_aux_section_definition *leaderDef = nullptr; @@ -607,7 +607,7 @@ if (sym.isExternal()) return ctx.symtab.addAbsolute(name, sym); - return make(name, sym); + return make(ctx, name, sym); } int32_t sectionNumber = sym.getSectionNumber(); @@ -751,7 +751,7 @@ // DebugTypes.h). Both cases only happen with cl.exe: clang-cl produces regular // output even with /Yc and /Yu and with /Zi. void ObjFile::initializeDependencies() { - if (!config->debug) + if (!ctx.config.debug) return; bool isPCH = false; @@ -906,7 +906,7 @@ if (!dwarf) return std::nullopt; } - if (config->machine == I386) + if (ctx.config.machine == I386) var.consume_front("_"); std::optional> ret = dwarf->getVariableLoc(var); @@ -935,9 +935,12 @@ auto it = ctx.pdbInputFileInstances.emplace(*p, nullptr); if (!it.second) return; // already scheduled for load - driver->enqueuePDB(*p); + ctx.driver.enqueuePDB(*p); } +ImportFile::ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m) + : InputFile(ctx, ImportKind, m), live(!ctx.config.doGC), thunkLive(live) {} + void ImportFile::parse() { const char *buf = mb.getBufferStart(); const auto *hdr = reinterpret_cast(buf); @@ -993,8 +996,10 @@ bool lazy) : InputFile(ctx, BitcodeKind, mb, lazy) { std::string path = mb.getBufferIdentifier().str(); - if (config->thinLTOIndexOnly) - path = replaceThinLTOSuffix(mb.getBufferIdentifier()); + if (ctx.config.thinLTOIndexOnly) + path = replaceThinLTOSuffix(mb.getBufferIdentifier(), + ctx.config.thinLTOObjectSuffixReplace.first, + ctx.config.thinLTOObjectSuffixReplace.second); // ThinLTO assumes that all MemoryBufferRefs given to it have a unique // name. If two archives define two members with the same name, this @@ -1014,36 +1019,9 @@ BitcodeFile::~BitcodeFile() = default; -namespace { -// Convenience class for initializing a coff_section with specific flags. -class FakeSection { -public: - FakeSection(int c) { section.Characteristics = c; } - - coff_section section; -}; - -// Convenience class for initializing a SectionChunk with specific flags. -class FakeSectionChunk { -public: - FakeSectionChunk(const coff_section *section) : chunk(nullptr, section) { - // Comdats from LTO files can't be fully treated as regular comdats - // at this point; we don't know what size or contents they are going to - // have, so we can't do proper checking of such aspects of them. - chunk.selection = IMAGE_COMDAT_SELECT_ANY; - } - - SectionChunk chunk; -}; - -FakeSection ltoTextSection(IMAGE_SCN_MEM_EXECUTE); -FakeSection ltoDataSection(IMAGE_SCN_CNT_INITIALIZED_DATA); -FakeSectionChunk ltoTextSectionChunk(<oTextSection.section); -FakeSectionChunk ltoDataSectionChunk(<oDataSection.section); -} // namespace - void BitcodeFile::parse() { llvm::StringSaver &saver = lld::saver(); + std::vector> comdat(obj->getComdatTable().size()); for (size_t i = 0; i != obj->getComdatTable().size(); ++i) // FIXME: Check nodeduplicate @@ -1055,9 +1033,9 @@ Symbol *sym; SectionChunk *fakeSC = nullptr; if (objSym.isExecutable()) - fakeSC = <oTextSectionChunk.chunk; + fakeSC = &ctx.ltoTextSectionChunk->chunk; else - fakeSC = <oDataSectionChunk.chunk; + fakeSC = &ctx.ltoDataSectionChunk->chunk; if (objSym.isUndefined()) { sym = ctx.symtab.addUndefined(symName, this, false); } else if (objSym.isCommon()) { @@ -1067,7 +1045,7 @@ sym = ctx.symtab.addUndefined(symName, this, true); std::string fallback = std::string(objSym.getCOFFWeakExternalFallback()); Symbol *alias = ctx.symtab.addUndefined(saver.save(fallback)); - checkAndSetWeakAlias(&ctx.symtab, this, sym, alias); + checkAndSetWeakAlias(ctx, this, sym, alias); } else if (comdatIndex != -1) { if (symName == obj->getComdatTable()[comdatIndex].first) { sym = comdat[comdatIndex].first; @@ -1084,7 +1062,7 @@ } symbols.push_back(sym); if (objSym.isUsed()) - config->gcroot.push_back(sym); + ctx.config.gcroot.push_back(sym); } directives = obj->getCOFFLinkerOpts(); } @@ -1110,10 +1088,8 @@ } } -std::string lld::coff::replaceThinLTOSuffix(StringRef path) { - StringRef suffix = config->thinLTOObjectSuffixReplace.first; - StringRef repl = config->thinLTOObjectSuffixReplace.second; - +std::string lld::coff::replaceThinLTOSuffix(StringRef path, StringRef suffix, + StringRef repl) { if (path.consume_back(suffix)) return (path + repl).str(); return std::string(path); diff --git a/lld/COFF/LLDMapFile.cpp b/lld/COFF/LLDMapFile.cpp --- a/lld/COFF/LLDMapFile.cpp +++ b/lld/COFF/LLDMapFile.cpp @@ -73,12 +73,13 @@ // Construct a map from symbols to their stringified representations. static DenseMap -getSymbolStrings(ArrayRef syms) { +getSymbolStrings(const COFFLinkerContext &ctx, + ArrayRef syms) { std::vector str(syms.size()); parallelFor((size_t)0, syms.size(), [&](size_t i) { raw_string_ostream os(str[i]); writeHeader(os, syms[i]->getRVA(), 0, 0); - os << indent16 << toString(*syms[i]); + os << indent16 << toString(ctx, *syms[i]); }); DenseMap ret; @@ -88,18 +89,18 @@ } void lld::coff::writeLLDMapFile(const COFFLinkerContext &ctx) { - if (config->lldmapFile.empty()) + if (ctx.config.lldmapFile.empty()) return; std::error_code ec; - raw_fd_ostream os(config->lldmapFile, ec, sys::fs::OF_None); + raw_fd_ostream os(ctx.config.lldmapFile, ec, sys::fs::OF_None); if (ec) - fatal("cannot open " + config->lldmapFile + ": " + ec.message()); + fatal("cannot open " + ctx.config.lldmapFile + ": " + ec.message()); // Collect symbol info that we want to print out. std::vector syms = getSymbols(ctx); SymbolMapTy sectionSyms = getSectionSyms(syms); - DenseMap symStr = getSymbolStrings(syms); + DenseMap symStr = getSymbolStrings(ctx, syms); // Print out the header line. os << "Address Size Align Out In Symbol\n"; diff --git a/lld/COFF/LTO.h b/lld/COFF/LTO.h --- a/lld/COFF/LTO.h +++ b/lld/COFF/LTO.h @@ -28,6 +28,7 @@ #include namespace llvm::lto { +struct Config; class LTO; } @@ -39,11 +40,11 @@ class BitcodeCompiler { public: - BitcodeCompiler(); + BitcodeCompiler(COFFLinkerContext &ctx); ~BitcodeCompiler(); void add(BitcodeFile &f); - std::vector compile(COFFLinkerContext &ctx); + std::vector compile(); private: std::unique_ptr ltoObj; @@ -52,6 +53,11 @@ std::vector file_names; std::unique_ptr indexFile; llvm::DenseSet thinIndices; + + std::string getThinLTOOutputFile(StringRef path); + llvm::lto::Config createConfig(); + + COFFLinkerContext &ctx; }; } diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp --- a/lld/COFF/LTO.cpp +++ b/lld/COFF/LTO.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "LTO.h" +#include "COFFLinkerContext.h" #include "Config.h" #include "InputFiles.h" #include "Symbols.h" @@ -53,17 +54,17 @@ return ret; } -static std::string getThinLTOOutputFile(StringRef path) { +std::string BitcodeCompiler::getThinLTOOutputFile(StringRef path) { return lto::getThinLTOOutputFile( - std::string(path), std::string(config->thinLTOPrefixReplace.first), - std::string(config->thinLTOPrefixReplace.second)); + std::string(path), std::string(ctx.config.thinLTOPrefixReplace.first), + std::string(ctx.config.thinLTOPrefixReplace.second)); } -static lto::Config createConfig() { +lto::Config BitcodeCompiler::createConfig() { lto::Config c; c.Options = initTargetOptionsFromCodeGenFlags(); c.Options.EmitAddrsig = true; - for (StringRef C : config->mllvmOpts) + for (StringRef C : ctx.config.mllvmOpts) c.MllvmArgs.emplace_back(C.str()); // Always emit a section per function/datum with LTO. LLVM LTO should get most @@ -74,7 +75,7 @@ // Use static reloc model on 32-bit x86 because it usually results in more // compact code, and because there are also known code generation bugs when // using the PIC model (see PR34306). - if (config->machine == COFF::IMAGE_FILE_MACHINE_I386) + if (ctx.config.machine == COFF::IMAGE_FILE_MACHINE_I386) c.RelocModel = Reloc::Static; else c.RelocModel = Reloc::PIC_; @@ -84,42 +85,42 @@ c.DisableVerify = true; #endif c.DiagHandler = diagnosticHandler; - c.OptLevel = config->ltoo; + c.OptLevel = ctx.config.ltoo; c.CPU = getCPUStr(); c.MAttrs = getMAttrs(); - c.CGOptLevel = args::getCGOptLevel(config->ltoo); - c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty(); - c.DebugPassManager = config->ltoDebugPassManager; - c.CSIRProfile = std::string(config->ltoCSProfileFile); - c.RunCSIRInstr = config->ltoCSProfileGenerate; - c.PGOWarnMismatch = config->ltoPGOWarnMismatch; - - if (config->saveTemps) - checkError(c.addSaveTemps(std::string(config->outputFile) + ".", + c.CGOptLevel = args::getCGOptLevel(ctx.config.ltoo); + c.AlwaysEmitRegularLTOObj = !ctx.config.ltoObjPath.empty(); + c.DebugPassManager = ctx.config.ltoDebugPassManager; + c.CSIRProfile = std::string(ctx.config.ltoCSProfileFile); + c.RunCSIRInstr = ctx.config.ltoCSProfileGenerate; + c.PGOWarnMismatch = ctx.config.ltoPGOWarnMismatch; + + if (ctx.config.saveTemps) + checkError(c.addSaveTemps(std::string(ctx.config.outputFile) + ".", /*UseInputModulePath*/ true)); return c; } -BitcodeCompiler::BitcodeCompiler() { +BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) { // Initialize indexFile. - if (!config->thinLTOIndexOnlyArg.empty()) - indexFile = openFile(config->thinLTOIndexOnlyArg); + if (!ctx.config.thinLTOIndexOnlyArg.empty()) + indexFile = openFile(ctx.config.thinLTOIndexOnlyArg); // Initialize ltoObj. lto::ThinBackend backend; - if (config->thinLTOIndexOnly) { + if (ctx.config.thinLTOIndexOnly) { auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); }; backend = lto::createWriteIndexesThinBackend( - std::string(config->thinLTOPrefixReplace.first), - std::string(config->thinLTOPrefixReplace.second), - config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite); + std::string(ctx.config.thinLTOPrefixReplace.first), + std::string(ctx.config.thinLTOPrefixReplace.second), + ctx.config.thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite); } else { backend = lto::createInProcessThinBackend( - llvm::heavyweight_hardware_concurrency(config->thinLTOJobs)); + llvm::heavyweight_hardware_concurrency(ctx.config.thinLTOJobs)); } ltoObj = std::make_unique(createConfig(), backend, - config->ltoPartitions); + ctx.config.ltoPartitions); } BitcodeCompiler::~BitcodeCompiler() = default; @@ -132,7 +133,7 @@ std::vector symBodies = f.getSymbols(); std::vector resols(symBodies.size()); - if (config->thinLTOIndexOnly) + if (ctx.config.thinLTOIndexOnly) thinIndices.insert(obj.getName()); // Provide a resolution to the LTO API for each symbol. @@ -161,7 +162,7 @@ // Merge all the bitcode files we have seen, codegen the result // and return the resulting objects. -std::vector BitcodeCompiler::compile(COFFLinkerContext &ctx) { +std::vector BitcodeCompiler::compile() { unsigned maxTasks = ltoObj->getMaxTasks(); buf.resize(maxTasks); files.resize(maxTasks); @@ -171,8 +172,8 @@ // native object files for ThinLTO incremental builds. If a path was // specified, configure LTO to use it as the cache directory. FileCache cache; - if (!config->ltoCache.empty()) - cache = check(localCache("ThinLTO", "Thin", config->ltoCache, + if (!ctx.config.ltoCache.empty()) + cache = check(localCache("ThinLTO", "Thin", ctx.config.ltoCache, [&](size_t task, const Twine &moduleName, std::unique_ptr mb) { files[task] = std::move(mb); @@ -191,23 +192,23 @@ for (StringRef s : thinIndices) { std::string path = getThinLTOOutputFile(s); openFile(path + ".thinlto.bc"); - if (config->thinLTOEmitImportsFiles) + if (ctx.config.thinLTOEmitImportsFiles) openFile(path + ".imports"); } // ThinLTO with index only option is required to generate only the index // files. After that, we exit from linker and ThinLTO backend runs in a // distributed environment. - if (config->thinLTOIndexOnly) { - if (!config->ltoObjPath.empty()) - saveBuffer(buf[0].second, config->ltoObjPath); + if (ctx.config.thinLTOIndexOnly) { + if (!ctx.config.ltoObjPath.empty()) + saveBuffer(buf[0].second, ctx.config.ltoObjPath); if (indexFile) indexFile->close(); return {}; } - if (!config->ltoCache.empty()) - pruneCache(config->ltoCache, config->ltoCachePolicy, files); + if (!ctx.config.ltoCache.empty()) + pruneCache(ctx.config.ltoCache, ctx.config.ltoCachePolicy, files); std::vector ret; for (unsigned i = 0; i != maxTasks; ++i) { @@ -231,19 +232,19 @@ StringRef ltoObjName; if (bitcodeFilePath == "ld-temp.o") { ltoObjName = - saver().save(Twine(config->outputFile) + ".lto" + + saver().save(Twine(ctx.config.outputFile) + ".lto" + (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj"); } else { StringRef directory = sys::path::parent_path(bitcodeFilePath); StringRef baseName = sys::path::filename(bitcodeFilePath); - StringRef outputFileBaseName = sys::path::filename(config->outputFile); + StringRef outputFileBaseName = sys::path::filename(ctx.config.outputFile); SmallString<64> path; sys::path::append(path, directory, outputFileBaseName + ".lto." + baseName); sys::path::remove_dots(path, true); ltoObjName = saver().save(path.str()); } - if (config->saveTemps) + if (ctx.config.saveTemps) saveBuffer(buf[i].second, ltoObjName); ret.push_back(make(ctx, MemoryBufferRef(objBuf, ltoObjName))); } diff --git a/lld/COFF/MapFile.cpp b/lld/COFF/MapFile.cpp --- a/lld/COFF/MapFile.cpp +++ b/lld/COFF/MapFile.cpp @@ -63,7 +63,8 @@ time->tm_sec, time->tm_year + 1900); } -static void sortUniqueSymbols(std::vector &syms) { +static void sortUniqueSymbols(std::vector &syms, + uint64_t imageBase) { // Build helper vector using SortEntry = std::pair; std::vector v; @@ -80,11 +81,11 @@ v.erase(end, v.end()); // Sort by RVA then original order - parallelSort(v, [](const SortEntry &a, const SortEntry &b) { - // Add config->imageBase to avoid comparing "negative" RVAs. + parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) { + // Add config.imageBase to avoid comparing "negative" RVAs. // This can happen with symbols of Absolute kind - uint64_t rvaa = config->imageBase + a.first->getRVA(); - uint64_t rvab = config->imageBase + b.first->getRVA(); + uint64_t rvaa = imageBase + a.first->getRVA(); + uint64_t rvab = imageBase + b.first->getRVA(); return rvaa < rvab || (rvaa == rvab && a.second < b.second); }); @@ -133,8 +134,8 @@ syms.push_back(impSym); } - sortUniqueSymbols(syms); - sortUniqueSymbols(staticSyms); + sortUniqueSymbols(syms, ctx.config.imageBase); + sortUniqueSymbols(staticSyms, ctx.config.imageBase); } // Construct a map from symbols to their stringified representations. @@ -184,7 +185,7 @@ os << " "; os << left_justify(sym->getName(), 26); os << " "; - os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16); + os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16); if (!fileDescr.empty()) { os << " "; // FIXME : Handle "f" and "i" flags sometimes generated // by link.exe in those spaces @@ -199,13 +200,13 @@ } void lld::coff::writeMapFile(COFFLinkerContext &ctx) { - if (config->mapFile.empty()) + if (ctx.config.mapFile.empty()) return; std::error_code ec; - raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None); + raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None); if (ec) - fatal("cannot open " + config->mapFile + ": " + ec.message()); + fatal("cannot open " + ctx.config.mapFile + ": " + ec.message()); ScopedTimer t1(ctx.totalMapTimer); @@ -223,24 +224,25 @@ t3.stop(); ScopedTimer t4(ctx.writeTimer); - SmallString<128> AppName = sys::path::filename(config->outputFile); + SmallString<128> AppName = sys::path::filename(ctx.config.outputFile); sys::path::replace_extension(AppName, ""); // Print out the file header os << " " << AppName << "\n"; os << "\n"; - os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " ("; - if (config->repro) { + os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8) + << " ("; + if (ctx.config.repro) { os << "Repro mode"; } else { - writeFormattedTimestamp(os, config->timestamp); + writeFormattedTimestamp(os, ctx.config.timestamp); } os << ")\n"; os << "\n"; os << " Preferred load address is " - << format_hex_no_prefix(config->imageBase, 16) << "\n"; + << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n"; os << "\n"; // Print out section table. @@ -295,8 +297,8 @@ uint16_t entrySecIndex = 0; uint64_t entryAddress = 0; - if (!config->noEntry) { - Defined *entry = dyn_cast_or_null(config->entry); + if (!ctx.config.noEntry) { + Defined *entry = dyn_cast_or_null(ctx.config.entry); if (entry) { Chunk *chunk = entry->getChunk(); entrySecIndex = chunk->getOutputSectionIdx(); @@ -316,12 +318,12 @@ os << staticSymStr[sym] << '\n'; // Print out the exported functions - if (config->mapInfo) { + if (ctx.config.mapInfo) { os << "\n"; os << " Exports\n"; os << "\n"; os << " ordinal name\n\n"; - for (Export &e : config->exports) { + for (Export &e : ctx.config.exports) { os << format(" %7d", e.ordinal) << " " << e.name << "\n"; if (!e.extName.empty() && e.extName != e.name) os << " exported name: " << e.extName << "\n"; diff --git a/lld/COFF/MarkLive.cpp b/lld/COFF/MarkLive.cpp --- a/lld/COFF/MarkLive.cpp +++ b/lld/COFF/MarkLive.cpp @@ -51,7 +51,7 @@ }; // Add GC root chunks. - for (Symbol *b : config->gcroot) + for (Symbol *b : ctx.config.gcroot) addSym(b); while (!worklist.empty()) { diff --git a/lld/COFF/MinGW.h b/lld/COFF/MinGW.h --- a/lld/COFF/MinGW.h +++ b/lld/COFF/MinGW.h @@ -25,7 +25,8 @@ // symbols for MinGW. class AutoExporter { public: - AutoExporter(const llvm::DenseSet &manualExcludeSymbols); + AutoExporter(COFFLinkerContext &ctx, + const llvm::DenseSet &manualExcludeSymbols); void addWholeArchive(StringRef path); void addExcludedSymbol(StringRef symbol); @@ -38,10 +39,13 @@ const llvm::DenseSet &manualExcludeSymbols; - bool shouldExport(const COFFLinkerContext &ctx, Defined *sym) const; + bool shouldExport(Defined *sym) const; + +private: + COFFLinkerContext &ctx; }; -void writeDefFile(StringRef name); +void writeDefFile(StringRef name, const std::vector &exports); // The -wrap option is a feature to rename symbols so that you can write // wrappers for existing functions. If you pass `-wrap:foo`, all diff --git a/lld/COFF/MinGW.cpp b/lld/COFF/MinGW.cpp --- a/lld/COFF/MinGW.cpp +++ b/lld/COFF/MinGW.cpp @@ -24,8 +24,9 @@ using namespace lld::coff; AutoExporter::AutoExporter( + COFFLinkerContext &ctx, const llvm::DenseSet &manualExcludeSymbols) - : manualExcludeSymbols(manualExcludeSymbols) { + : manualExcludeSymbols(manualExcludeSymbols), ctx(ctx) { excludeLibs = { "libgcc", "libgcc_s", @@ -80,7 +81,7 @@ "_NULL_THUNK_DATA", }; - if (config->machine == I386) { + if (ctx.config.machine == I386) { excludeSymbols = { "__NULL_IMPORT_DESCRIPTOR", "__pei386_runtime_relocator", @@ -128,8 +129,7 @@ excludeSymbols.insert(symbol); } -bool AutoExporter::shouldExport(const COFFLinkerContext &ctx, - Defined *sym) const { +bool AutoExporter::shouldExport(Defined *sym) const { if (!sym || !sym->getChunk()) return false; @@ -167,14 +167,15 @@ return !excludeObjects.count(fileName); } -void lld::coff::writeDefFile(StringRef name) { +void lld::coff::writeDefFile(StringRef name, + const std::vector &exports) { std::error_code ec; raw_fd_ostream os(name, ec, sys::fs::OF_None); if (ec) fatal("cannot open " + name + ": " + ec.message()); os << "EXPORTS\n"; - for (Export &e : config->exports) { + for (const Export &e : exports) { os << " " << e.exportName << " " << "@" << e.ordinal; if (auto *def = dyn_cast_or_null(e.sym)) { @@ -186,9 +187,9 @@ } } -static StringRef mangle(Twine sym) { - assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); - if (config->machine == I386) +static StringRef mangle(Twine sym, MachineTypes machine) { + assert(machine != IMAGE_FILE_MACHINE_UNKNOWN); + if (machine == I386) return saver().save("_" + sym); return saver().save(sym); } @@ -212,8 +213,10 @@ if (!sym) continue; - Symbol *real = ctx.symtab.addUndefined(mangle("__real_" + name)); - Symbol *wrap = ctx.symtab.addUndefined(mangle("__wrap_" + name)); + Symbol *real = + ctx.symtab.addUndefined(mangle("__real_" + name, ctx.config.machine)); + Symbol *wrap = + ctx.symtab.addUndefined(mangle("__wrap_" + name, ctx.config.machine)); v.push_back({sym, real, wrap}); // These symbols may seem undefined initially, but don't bail out @@ -254,7 +257,7 @@ // referenced it or not, though.) if (imp) { DefinedLocalImport *wrapimp = make( - saver().save("__imp_" + w.wrap->getName()), d); + ctx, saver().save("__imp_" + w.wrap->getName()), d); ctx.symtab.localImportChunks.push_back(wrapimp->getChunk()); map[imp] = wrapimp; } diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp --- a/lld/COFF/PDB.cpp +++ b/lld/COFF/PDB.cpp @@ -67,8 +67,6 @@ using llvm::object::coff_section; using llvm::pdb::StringTableFixup; -static ExitOnError exitOnErr; - namespace { class DebugSHandler; @@ -146,6 +144,11 @@ void printStats(); private: + void pdbMakeAbsolute(SmallVectorImpl &fileName); + void translateIdSymbols(MutableArrayRef &recordData, + TpiSource *source); + void addCommonLinkerModuleSymbols(StringRef path, + pdb::DbiModuleDescriptorBuilder &mod); pdb::PDBFileBuilder builder; @@ -241,7 +244,7 @@ // Visual Studio's debugger requires absolute paths in various places in the // PDB to work without additional configuration: // https://docs.microsoft.com/en-us/visualstudio/debugger/debug-source-files-common-properties-solution-property-pages-dialog-box -static void pdbMakeAbsolute(SmallVectorImpl &fileName) { +void PDBLinker::pdbMakeAbsolute(SmallVectorImpl &fileName) { // The default behavior is to produce paths that are valid within the context // of the machine that you perform the link on. If the linker is running on // a POSIX system, we will output absolute POSIX paths. If the linker is @@ -256,7 +259,7 @@ // It's not absolute in any path syntax. Relative paths necessarily refer to // the local file system, so we can make it native without ending up with a // nonsensical path. - if (config->pdbSourcePath.empty()) { + if (ctx.config.pdbSourcePath.empty()) { sys::path::native(fileName); sys::fs::make_absolute(fileName); sys::path::remove_dots(fileName, true); @@ -267,7 +270,7 @@ // Since PDB's are more of a Windows thing, we make this conservative and only // decide that it's a unix path if we're fairly certain. Specifically, if // it starts with a forward slash. - SmallString<128> absoluteFileName = config->pdbSourcePath; + SmallString<128> absoluteFileName = ctx.config.pdbSourcePath; sys::path::Style guessedStyle = absoluteFileName.startswith("/") ? sys::path::Style::posix : sys::path::Style::windows; @@ -338,8 +341,8 @@ } /// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32 -static void translateIdSymbols(MutableArrayRef &recordData, - TypeMerger &tMerger, TpiSource *source) { +void PDBLinker::translateIdSymbols(MutableArrayRef &recordData, + TpiSource *source) { RecordPrefix *prefix = reinterpret_cast(recordData.data()); SymbolKind kind = symbolKind(recordData); @@ -370,7 +373,7 @@ // in both cases we just need the second type index. if (!ti->isSimple() && !ti->isNoneType()) { TypeIndex newType = TypeIndex(SimpleTypeKind::NotTranslated); - if (config->debugGHashes) { + if (ctx.config.debugGHashes) { auto idToType = tMerger.funcIdToType.find(*ti); if (idToType != tMerger.funcIdToType.end()) newType = idToType->second; @@ -575,7 +578,7 @@ // An object file may have S_xxx_ID symbols, but these get converted to // "real" symbols in a PDB. - translateIdSymbols(recordBytes, tMerger, source); + translateIdSymbols(recordBytes, source); } void PDBLinker::analyzeSymbolSubsection( @@ -642,6 +645,7 @@ Error PDBLinker::writeAllModuleSymbolRecords(ObjFile *file, BinaryStreamWriter &writer) { + ExitOnError exitOnErr; std::vector storage; SmallVector scopes; @@ -758,6 +762,7 @@ contents = SectionChunk::consumeDebugMagic(contents, ".debug$S"); DebugSubsectionArray subsections; BinaryStreamReader reader(contents, support::little); + ExitOnError exitOnErr; exitOnErr(reader.readArray(subsections, contents.size())); debugChunk->sortRelocations(); @@ -867,6 +872,7 @@ TpiSource *source = debugChunk->file->debugTypesObj; DebugInlineeLinesSubsectionRef inlineeLines; BinaryStreamReader storageReader(relocatedBytes, support::little); + ExitOnError exitOnErr; exitOnErr(inlineeLines.initialize(storageReader)); for (const InlineeSourceLine &line : inlineeLines) { TypeIndex &inlinee = *const_cast(&line.Header->Inlinee); @@ -935,6 +941,8 @@ return; } + ExitOnError exitOnErr; + // Handle FPO data. Each subsection begins with a single image base // relocation, which is then added to the RvaStart of each frame data record // when it is added to the PDB. The string table indices for the FPO program @@ -984,7 +992,7 @@ for (const FileChecksumEntry &fc : checksums) { SmallString<128> filename = exitOnErr(cvStrTab.getString(fc.FileNameOffset)); - pdbMakeAbsolute(filename); + linker.pdbMakeAbsolute(filename); exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename)); newChecksums->addChecksum(filename, fc.Kind, fc.Checksum); } @@ -995,8 +1003,8 @@ file.moduleDBI->addDebugSubsection(std::move(newChecksums)); } -static void warnUnusable(InputFile *f, Error e) { - if (!config->warnDebugInfoUnusable) { +static void warnUnusable(InputFile *f, Error e, bool shouldWarn) { + if (!shouldWarn) { consumeError(std::move(e)); return; } @@ -1023,6 +1031,7 @@ return; ScopedTimer t(ctx.symbolMergingTimer); + ExitOnError exitOnErr; pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); DebugSHandler dsh(*this, *source->file, source); // Now do all live .debug$S and .debug$F sections. @@ -1064,6 +1073,7 @@ void PDBLinker::createModuleDBI(ObjFile *file) { pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); SmallString<128> objName; + ExitOnError exitOnErr; bool inArchive = !file->parentName.empty(); objName = inArchive ? file->parentName : file->getName(); @@ -1093,11 +1103,12 @@ // the PDB first, so that we can get the map from object file type and item // indices to PDB type and item indices. If we are using ghashes, types have // already been merged. - if (!config->debugGHashes) { + if (!ctx.config.debugGHashes) { ScopedTimer t(ctx.typeMergingTimer); if (Error e = source->mergeDebugT(&tMerger)) { // If type merging failed, ignore the symbols. - warnUnusable(source->file, std::move(e)); + warnUnusable(source->file, std::move(e), + ctx.config.warnDebugInfoUnusable); return; } } @@ -1105,7 +1116,8 @@ // If type merging failed, ignore the symbols. Error typeError = std::move(source->typeMergingError); if (typeError) { - warnUnusable(source->file, std::move(typeError)); + warnUnusable(source->file, std::move(typeError), + ctx.config.warnDebugInfoUnusable); return; } @@ -1146,7 +1158,7 @@ tMerger.sortDependencies(); // Merge type information from input files using global type hashing. - if (config->debugGHashes) + if (ctx.config.debugGHashes) tMerger.mergeTypesWithGHash(); // Merge dependencies and then regular objects. @@ -1160,8 +1172,9 @@ // Construct TPI and IPI stream contents. ScopedTimer t2(ctx.tpiStreamLayoutTimer); + // Collect all the merged types. - if (config->debugGHashes) { + if (ctx.config.debugGHashes) { addGHashTypeInfo(ctx, builder); } else { addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable()); @@ -1169,7 +1182,7 @@ } t2.stop(); - if (config->showSummary) { + if (ctx.config.showSummary) { for (TpiSource *source : ctx.tpiSourceList) { nbTypeRecords += source->nbTypeRecords; nbTypeRecordsBytes += source->nbTypeRecordsBytes; @@ -1196,7 +1209,7 @@ StringRef name = def->getName(); if (name.data()[0] == '_' && name.data()[1] == '_') { // Drop the '_' prefix for x86. - if (config->machine == I386) + if (ctx.config.machine == I386) name = name.drop_front(1); if (name.startswith("__profd_") || name.startswith("__profc_") || name.startswith("__covrec_")) { @@ -1214,7 +1227,7 @@ } void PDBLinker::printStats() { - if (!config->showSummary) + if (!ctx.config.showSummary) return; SmallString<256> buffer; @@ -1282,11 +1295,11 @@ << "Run llvm-pdbutil to print details about a particular record:\n"; stream << formatv("llvm-pdbutil dump -{0}s -{0}-index {1:X} {2}\n", (name == "TPI" ? "type" : "id"), - tsis.back().typeIndex.getIndex(), config->pdbPath); + tsis.back().typeIndex.getIndex(), ctx.config.pdbPath); } }; - if (!config->debugGHashes) { + if (!ctx.config.debugGHashes) { // FIXME: Reimplement for ghash. printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable()); printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable()); @@ -1296,7 +1309,7 @@ } void PDBLinker::addNatvisFiles() { - for (StringRef file : config->natvisFiles) { + for (StringRef file : ctx.config.natvisFiles) { ErrorOr> dataOrErr = MemoryBuffer::getFile(file); if (!dataOrErr) { @@ -1306,16 +1319,17 @@ std::unique_ptr data = std::move(*dataOrErr); // Can't use takeBuffer() here since addInjectedSource() takes ownership. - if (driver->tar) - driver->tar->append(relativeToRoot(data->getBufferIdentifier()), - data->getBuffer()); + if (ctx.driver.tar) + ctx.driver.tar->append(relativeToRoot(data->getBufferIdentifier()), + data->getBuffer()); builder.addInjectedSource(file, std::move(data)); } } void PDBLinker::addNamedStreams() { - for (const auto &streamFile : config->namedStreams) { + ExitOnError exitOnErr; + for (const auto &streamFile : ctx.config.namedStreams) { const StringRef stream = streamFile.getKey(), file = streamFile.getValue(); ErrorOr> dataOrErr = MemoryBuffer::getFile(file); @@ -1325,7 +1339,7 @@ } std::unique_ptr data = std::move(*dataOrErr); exitOnErr(builder.addNamedStream(stream, data->getBuffer())); - driver->takeBuffer(std::move(data)); + ctx.driver.takeBuffer(std::move(data)); } } @@ -1372,8 +1386,8 @@ return r; } -static void fillLinkerVerRecord(Compile3Sym &cs) { - cs.Machine = toCodeViewMachine(config->machine); +static void fillLinkerVerRecord(Compile3Sym &cs, MachineTypes machine) { + cs.Machine = toCodeViewMachine(machine); // Interestingly, if we set the string to 0.0.0.0, then when trying to view // local variables WinDbg emits an error that private symbols are not present. // By setting this to a valid MSVC linker version string, local variables are @@ -1398,27 +1412,27 @@ cs.setLanguage(SourceLanguage::Link); } -static void addCommonLinkerModuleSymbols(StringRef path, - pdb::DbiModuleDescriptorBuilder &mod) { +void PDBLinker::addCommonLinkerModuleSymbols( + StringRef path, pdb::DbiModuleDescriptorBuilder &mod) { ObjNameSym ons(SymbolRecordKind::ObjNameSym); EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym); Compile3Sym cs(SymbolRecordKind::Compile3Sym); - fillLinkerVerRecord(cs); + fillLinkerVerRecord(cs, ctx.config.machine); ons.Name = "* Linker *"; ons.Signature = 0; - ArrayRef args = makeArrayRef(config->argv).drop_front(); + ArrayRef args = makeArrayRef(ctx.config.argv).drop_front(); std::string argStr = quote(args); ebs.Fields.push_back("cwd"); SmallString<64> cwd; - if (config->pdbSourcePath.empty()) + if (ctx.config.pdbSourcePath.empty()) sys::fs::current_path(cwd); else - cwd = config->pdbSourcePath; + cwd = ctx.config.pdbSourcePath; ebs.Fields.push_back(cwd); ebs.Fields.push_back("exe"); - SmallString<64> exe = config->argv[0]; + SmallString<64> exe = ctx.config.argv[0]; pdbMakeAbsolute(exe); ebs.Fields.push_back(exe); ebs.Fields.push_back("pdb"); @@ -1461,7 +1475,7 @@ } static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod, - OutputSection &os) { + OutputSection &os, bool isMinGW) { SectionSym sym(SymbolRecordKind::SectionSym); sym.Alignment = 12; // 2^12 = 4KB sym.Characteristics = os.header.Characteristics; @@ -1474,7 +1488,7 @@ // Skip COFF groups in MinGW because it adds a significant footprint to the // PDB, due to each function being in its own section - if (config->mingw) + if (isMinGW) return; // Output COFF groups for individual chunks of this section. @@ -1488,6 +1502,7 @@ if (ctx.importFileInstances.empty()) return; + ExitOnError exitOnErr; std::map dllToModuleDbi; for (ImportFile *file : ctx.importFileInstances) { @@ -1534,7 +1549,7 @@ ons.Name = file->dllName; ons.Signature = 0; - fillLinkerVerRecord(cs); + fillLinkerVerRecord(cs, ctx.config.machine); ts.Name = thunk->getName(); ts.Parent = 0; @@ -1598,7 +1613,8 @@ } void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) { - exitOnErr(builder.initialize(config->pdbPageSize)); + ExitOnError exitOnErr; + exitOnErr(builder.initialize(ctx.config.pdbPageSize)); buildId->Signature.CVSignature = OMF::Signature::PDB70; // Signature is set to a hash of the PDB contents when the PDB is done. @@ -1619,7 +1635,7 @@ pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); dbiBuilder.setAge(buildId->PDB70.Age); dbiBuilder.setVersionHeader(pdb::PdbDbiV70); - dbiBuilder.setMachineType(config->machine); + dbiBuilder.setMachineType(ctx.config.machine); // Technically we are not link.exe 14.11, but there are known cases where // debugging tools on Windows expect Microsoft-specific version numbers or // they fail to work at all. Since we know we produce PDBs that are @@ -1628,9 +1644,10 @@ } void PDBLinker::addSections(ArrayRef sectionTable) { + ExitOnError exitOnErr; // It's not entirely clear what this is, but the * Linker * module uses it. pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); - nativePath = config->pdbPath; + nativePath = ctx.config.pdbPath; pdbMakeAbsolute(nativePath); uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath); auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *")); @@ -1639,7 +1656,7 @@ // Add section contributions. They must be ordered by ascending RVA. for (OutputSection *os : ctx.outputSections) { - addLinkerModuleSectionSymbol(linkerModule, *os); + addLinkerModuleSectionSymbol(linkerModule, *os, ctx.config.mingw); for (Chunk *c : os->chunks) { pdb::SectionContrib sc = createSectionContrib(ctx, c, linkerModule.getModuleIndex()); @@ -1669,14 +1686,14 @@ // Print an error and continue if PDB writing fails. This is done mainly so // the user can see the output of /time and /summary, which is very helpful // when trying to figure out why a PDB file is too large. - if (Error e = builder.commit(config->pdbPath, guid)) { + if (Error e = builder.commit(ctx.config.pdbPath, guid)) { checkError(std::move(e)); - error("failed to write PDB file " + Twine(config->pdbPath)); + error("failed to write PDB file " + Twine(ctx.config.pdbPath)); } } -static uint32_t getSecrelReloc() { - switch (config->machine) { +static uint32_t getSecrelReloc(llvm::COFF::MachineTypes machine) { + switch (machine) { case AMD64: return COFF::IMAGE_REL_AMD64_SECREL; case I386: @@ -1701,7 +1718,7 @@ DebugLinesSubsectionRef &lines, uint32_t &offsetInLinetable) { ExitOnError exitOnErr; - uint32_t secrelReloc = getSecrelReloc(); + const uint32_t secrelReloc = getSecrelReloc(c->file->ctx.config.machine); for (SectionChunk *dbgC : c->file->getDebugChunks()) { if (dbgC->getSectionName() != ".debug$S") diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -47,7 +47,7 @@ // There is one add* function per symbol type. class SymbolTable { public: - SymbolTable(COFFLinkerContext &ctx) : ctx(ctx) {} + SymbolTable(COFFLinkerContext &c) : ctx(c) {} void addFile(InputFile *file); diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -53,20 +53,20 @@ } MachineTypes mt = file->getMachineType(); - if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { - config->machine = mt; - driver->addWinSysRootLibSearchPaths(); - } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) { + if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) { + ctx.config.machine = mt; + ctx.driver.addWinSysRootLibSearchPaths(); + } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && ctx.config.machine != mt) { error(toString(file) + ": machine type " + machineToStr(mt) + - " conflicts with " + machineToStr(config->machine)); + " conflicts with " + machineToStr(ctx.config.machine)); return; } - driver->parseDirectives(file); + ctx.driver.parseDirectives(file); } -static void errorOrWarn(const Twine &s) { - if (config->forceUnresolved) +static void errorOrWarn(const Twine &s, bool forceUnresolved) { + if (forceUnresolved) warn(s); else error(s); @@ -143,7 +143,7 @@ std::optional> fileLine = getFileLineCodeView(c, addr); // If codeview didn't yield any result, check dwarf in MinGW mode. - if (!fileLine && config->mingw) + if (!fileLine && c->file->ctx.config.mingw) fileLine = getFileLineDwarf(c, addr); return fileLine; } @@ -201,7 +201,7 @@ << "\n>>> "; os << toString(file); if (loc.sym) - os << ":(" << toString(*loc.sym) << ')'; + os << ":(" << toString(file->ctx, *loc.sym) << ')'; } return std::make_pair(symbolLocations, numLocations); } @@ -236,10 +236,11 @@ std::vector files; }; -static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) { +static void reportUndefinedSymbol(const COFFLinkerContext &ctx, + const UndefinedDiag &undefDiag) { std::string out; llvm::raw_string_ostream os(out); - os << "undefined symbol: " << toString(*undefDiag.sym); + os << "undefined symbol: " << toString(ctx, *undefDiag.sym); const size_t maxUndefReferences = 3; size_t numDisplayedRefs = 0, numRefs = 0; @@ -255,7 +256,7 @@ } if (numDisplayedRefs < numRefs) os << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times"; - errorOrWarn(os.str()); + errorOrWarn(os.str(), ctx.config.forceUnresolved); } void SymbolTable::loadMinGWSymbols() { @@ -269,7 +270,7 @@ StringRef name = undef->getName(); - if (config->machine == I386 && config->stdcallFixup) { + if (ctx.config.machine == I386 && ctx.config.stdcallFixup) { // Check if we can resolve an undefined decorated symbol by finding // the intended target as an undecorated symbol (only with a leading // underscore). @@ -290,7 +291,7 @@ } // If it's lazy or already defined, hook it up as weak alias. if (l->isLazy() || isa(l)) { - if (config->warnStdcallFixup) + if (ctx.config.warnStdcallFixup) warn("Resolving " + origName + " by linking to " + newName); else log("Resolving " + origName + " by linking to " + newName); @@ -300,7 +301,7 @@ } } - if (config->autoImport) { + if (ctx.config.autoImport) { if (name.startswith("__imp_")) continue; // If we have an undefined symbol, but we have a lazy symbol we could @@ -358,7 +359,7 @@ // for __imp_ instead, and drop the whole .refptr. chunk. DefinedRegular *refptr = dyn_cast_or_null(find((".refptr." + name).str())); - if (refptr && refptr->getChunk()->getSize() == config->wordsize) { + if (refptr && refptr->getChunk()->getSize() == ctx.config.wordsize) { SectionChunk *sc = dyn_cast_or_null(refptr->getChunk()); if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) { log("Replacing .refptr." + name + " with " + imp->getName()); @@ -383,12 +384,13 @@ if (undefs.empty() && (!localImports || localImports->empty())) return; - for (Symbol *b : config->gcroot) { + for (Symbol *b : ctx.config.gcroot) { if (undefs.count(b)) - errorOrWarn(": undefined symbol: " + toString(*b)); + errorOrWarn(": undefined symbol: " + toString(ctx, *b), + ctx.config.forceUnresolved); if (localImports) if (Symbol *imp = localImports->lookup(b)) - warn(": locally defined symbol imported: " + toString(*imp) + + warn(": locally defined symbol imported: " + toString(ctx, *imp) + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } @@ -413,7 +415,7 @@ if (localImports) if (Symbol *imp = localImports->lookup(sym)) warn(toString(file) + - ": locally defined symbol imported: " + toString(*imp) + + ": locally defined symbol imported: " + toString(ctx, *imp) + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } }; @@ -426,7 +428,7 @@ processFile(file, file->getSymbols()); for (const UndefinedDiag &undefDiag : undefDiags) - reportUndefinedSymbol(undefDiag); + reportUndefinedSymbol(ctx, undefDiag); } void SymbolTable::reportUnresolvable() { @@ -446,7 +448,7 @@ } if (name.contains("_PchSym_")) continue; - if (config->autoImport && impSymbol(name)) + if (ctx.config.autoImport && impSymbol(name)) continue; undefs.insert(sym); } @@ -491,7 +493,7 @@ Symbol *imp = find(name.substr(strlen("__imp_"))); if (imp && isa(imp)) { auto *d = cast(imp); - replaceSymbol(sym, name, d); + replaceSymbol(sym, ctx, name, d); localImportChunks.push_back(cast(sym)->getChunk()); localImports[sym] = d; continue; @@ -503,19 +505,19 @@ if (name.contains("_PchSym_")) continue; - if (config->autoImport && handleMinGWAutomaticImport(sym, name)) + if (ctx.config.autoImport && handleMinGWAutomaticImport(sym, name)) continue; // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. - if (config->forceUnresolved) - replaceSymbol(sym, name, 0); + if (ctx.config.forceUnresolved) + replaceSymbol(sym, ctx, name, 0); undefs.insert(sym); } reportProblemSymbols( - ctx, undefs, config->warnLocallyDefinedImported ? &localImports : nullptr, - false); + ctx, undefs, + ctx.config.warnLocallyDefinedImported ? &localImports : nullptr, false); } std::pair SymbolTable::insert(StringRef name) { @@ -642,7 +644,7 @@ uint32_t newSectionOffset) { std::string msg; llvm::raw_string_ostream os(msg); - os << "duplicate symbol: " << toString(*existing); + os << "duplicate symbol: " << toString(ctx, *existing); DefinedRegular *d = dyn_cast(existing); if (d && isa(d->getFile())) { @@ -654,7 +656,7 @@ os << getSourceLocation(newFile, newSc, newSectionOffset, existing->getName()); - if (config->forceMultiple) + if (ctx.config.forceMultiple) warn(os.str()); else error(os.str()); @@ -664,7 +666,7 @@ auto [s, wasInserted] = insert(n, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) - replaceSymbol(s, n, sym); + replaceSymbol(s, ctx, n, sym); else if (auto *da = dyn_cast(s)) { if (da->getVA() != sym.getValue()) reportDuplicate(s, nullptr); @@ -677,7 +679,7 @@ auto [s, wasInserted] = insert(n, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) - replaceSymbol(s, n, va); + replaceSymbol(s, ctx, n, va); else if (auto *da = dyn_cast(s)) { if (da->getVA() != va) reportDuplicate(s, nullptr); @@ -751,7 +753,7 @@ auto [s, wasInserted] = insert(name, nullptr); s->isUsedInRegularObj = true; if (wasInserted || isa(s) || s->isLazy()) { - replaceSymbol(s, name, id, machine); + replaceSymbol(s, ctx, name, id, machine); return s; } @@ -788,7 +790,7 @@ } Symbol *SymbolTable::findUnderscore(StringRef name) const { - if (config->machine == I386) + if (ctx.config.machine == I386) return find(("_" + name).str()); return find(name); } @@ -835,7 +837,7 @@ }; // For non-x86, just look for C++ functions. - if (config->machine != I386) + if (ctx.config.machine != I386) return findByPrefix("?" + name + "@@Y"); if (!name.startswith("_")) @@ -862,10 +864,10 @@ return; ScopedTimer t(ctx.ltoTimer); - lto.reset(new BitcodeCompiler()); + lto.reset(new BitcodeCompiler(ctx)); for (BitcodeFile *f : ctx.bitcodeFileInstances) lto->add(*f); - for (InputFile *newObj : lto->compile(ctx)) { + for (InputFile *newObj : lto->compile()) { ObjFile *obj = cast(newObj); obj->parse(); ctx.objFileInstances.push_back(obj); diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h --- a/lld/COFF/Symbols.h +++ b/lld/COFF/Symbols.h @@ -22,13 +22,6 @@ namespace lld { -std::string toString(coff::Symbol &b); - -// There are two different ways to convert an Archive::Symbol to a string: -// One for Microsoft name mangling and one for Itanium name mangling. -// Call the functions toCOFFString and toELFString, not just toString. -std::string toCOFFString(const coff::Archive::Symbol &b); - namespace coff { using llvm::object::Archive; @@ -37,6 +30,7 @@ using llvm::object::coff_symbol_generic; class ArchiveFile; +class COFFLinkerContext; class InputFile; class ObjFile; class SymbolTable; @@ -250,29 +244,25 @@ // Absolute symbols. class DefinedAbsolute : public Defined { public: - DefinedAbsolute(StringRef n, COFFSymbolRef s) - : Defined(DefinedAbsoluteKind, n), va(s.getValue()) { + DefinedAbsolute(const COFFLinkerContext &c, StringRef n, COFFSymbolRef s) + : Defined(DefinedAbsoluteKind, n), va(s.getValue()), ctx(c) { isExternal = s.isExternal(); } - DefinedAbsolute(StringRef n, uint64_t v) - : Defined(DefinedAbsoluteKind, n), va(v) {} + DefinedAbsolute(const COFFLinkerContext &c, StringRef n, uint64_t v) + : Defined(DefinedAbsoluteKind, n), va(v), ctx(c) {} static bool classof(const Symbol *s) { return s->kind() == DefinedAbsoluteKind; } - uint64_t getRVA() { return va - config->imageBase; } + uint64_t getRVA(); void setVA(uint64_t v) { va = v; } uint64_t getVA() const { return va; } - // Section index relocations against absolute symbols resolve to - // this 16 bit number, and it is the largest valid section index - // plus one. This variable keeps it. - static uint16_t numOutputSections; - private: uint64_t va; + const COFFLinkerContext &ctx; }; // This symbol is used for linker-synthesized symbols like __ImageBase and @@ -393,7 +383,8 @@ // a regular name. A function pointer is given as a DefinedImportData. class DefinedImportThunk : public Defined { public: - DefinedImportThunk(StringRef name, DefinedImportData *s, uint16_t machine); + DefinedImportThunk(COFFLinkerContext &ctx, StringRef name, + DefinedImportData *s, uint16_t machine); static bool classof(const Symbol *s) { return s->kind() == DefinedImportThunkKind; @@ -415,8 +406,9 @@ // This is here just for compatibility with MSVC. class DefinedLocalImport : public Defined { public: - DefinedLocalImport(StringRef n, Defined *s) - : Defined(DefinedLocalImportKind, n), data(make(s)) {} + DefinedLocalImport(COFFLinkerContext &ctx, StringRef n, Defined *s) + : Defined(DefinedLocalImportKind, n), + data(make(ctx, s)) {} static bool classof(const Symbol *s) { return s->kind() == DefinedLocalImportKind; @@ -511,6 +503,10 @@ } } // namespace coff +std::string toString(const coff::COFFLinkerContext &ctx, coff::Symbol &b); +std::string toCOFFString(const coff::COFFLinkerContext &ctx, + const llvm::object::Archive::Symbol &b); + } // namespace lld #endif diff --git a/lld/COFF/Symbols.cpp b/lld/COFF/Symbols.cpp --- a/lld/COFF/Symbols.cpp +++ b/lld/COFF/Symbols.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Symbols.h" +#include "COFFLinkerContext.h" #include "InputFiles.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" @@ -27,14 +28,15 @@ "symbols should be optimized for memory usage"); // Returns a symbol name for an error message. -static std::string maybeDemangleSymbol(StringRef symName) { - if (config->demangle) { +static std::string maybeDemangleSymbol(const COFFLinkerContext &ctx, + StringRef symName) { + if (ctx.config.demangle) { std::string prefix; StringRef prefixless = symName; if (prefixless.consume_front("__imp_")) prefix = "__declspec(dllimport) "; StringRef demangleInput = prefixless; - if (config->machine == I386) + if (ctx.config.machine == I386) demangleInput.consume_front("_"); std::string demangled = demangle(demangleInput.str()); if (demangled != demangleInput) @@ -43,11 +45,12 @@ } return std::string(symName); } -std::string toString(coff::Symbol &b) { - return maybeDemangleSymbol(b.getName()); +std::string toString(const COFFLinkerContext &ctx, coff::Symbol &b) { + return maybeDemangleSymbol(ctx, b.getName()); } -std::string toCOFFString(const Archive::Symbol &b) { - return maybeDemangleSymbol(b.getName()); +std::string toCOFFString(const COFFLinkerContext &ctx, + const Archive::Symbol &b) { + return maybeDemangleSymbol(ctx, b.getName()); } namespace coff { @@ -102,23 +105,24 @@ return COFFSymbolRef(reinterpret_cast(sym)); } -uint16_t DefinedAbsolute::numOutputSections; +uint64_t DefinedAbsolute::getRVA() { return va - ctx.config.imageBase; } -static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) { +static Chunk *makeImportThunk(COFFLinkerContext &ctx, DefinedImportData *s, + uint16_t machine) { if (machine == AMD64) - return make(s); + return make(ctx, s); if (machine == I386) - return make(s); + return make(ctx, s); if (machine == ARM64) - return make(s); + return make(ctx, s); assert(machine == ARMNT); - return make(s); + return make(ctx, s); } -DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s, - uint16_t machine) +DefinedImportThunk::DefinedImportThunk(COFFLinkerContext &ctx, StringRef name, + DefinedImportData *s, uint16_t machine) : Defined(DefinedImportThunkKind, name), wrappedSym(s), - data(makeImportThunk(s, machine)) {} + data(makeImportThunk(ctx, s, machine)) {} Defined *Undefined::getWeakAlias() { // A weak alias may be a weak alias to another symbol, so check recursively. @@ -130,11 +134,11 @@ MemoryBufferRef LazyArchive::getMemberBuffer() { Archive::Child c = - CHECK(sym.getMember(), - "could not get the member for symbol " + toCOFFString(sym)); + CHECK(sym.getMember(), "could not get the member for symbol " + + toCOFFString(file->ctx, sym)); return CHECK(c.getMemoryBufferRef(), - "could not get the buffer for the member defining symbol " + - toCOFFString(sym)); + "could not get the buffer for the member defining symbol " + + toCOFFString(file->ctx, sym)); } } // namespace coff } // namespace lld diff --git a/lld/COFF/TypeMerger.h b/lld/COFF/TypeMerger.h --- a/lld/COFF/TypeMerger.h +++ b/lld/COFF/TypeMerger.h @@ -32,13 +32,13 @@ /// Get the type table or the global type table if /DEBUG:GHASH is enabled. inline llvm::codeview::TypeCollection &getTypeTable() { - assert(!config->debugGHashes); + assert(!ctx.config.debugGHashes); return typeTable; } /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled. inline llvm::codeview::TypeCollection &getIDTable() { - assert(!config->debugGHashes); + assert(!ctx.config.debugGHashes); return idTable; } diff --git a/lld/COFF/Writer.h b/lld/COFF/Writer.h --- a/lld/COFF/Writer.h +++ b/lld/COFF/Writer.h @@ -45,9 +45,9 @@ void insertChunkAtStart(Chunk *c); void merge(OutputSection *other); void setPermissions(uint32_t c); - uint64_t getRVA() { return header.VirtualAddress; } - uint64_t getFileOff() { return header.PointerToRawData; } - void writeHeaderTo(uint8_t *buf); + uint64_t getRVA() const { return header.VirtualAddress; } + uint64_t getFileOff() const { return header.PointerToRawData; } + void writeHeaderTo(uint8_t *buf, bool isDebug); void addContributingPartialSection(PartialSection *sec); // Returns the size of this section in an executable memory image. diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -85,7 +85,7 @@ class DebugDirectoryChunk : public NonSectionChunk { public: - DebugDirectoryChunk(COFFLinkerContext &c, + DebugDirectoryChunk(const COFFLinkerContext &c, const std::vector> &r, bool writeRepro) : records(r), writeRepro(writeRepro), ctx(c) {} @@ -99,7 +99,7 @@ for (const std::pair& record : records) { Chunk *c = record.second; - OutputSection *os = ctx.getOutputSection(c); + const OutputSection *os = ctx.getOutputSection(c); uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA()); fillEntry(d, record.first, c->getSize(), c->getRVA(), offs); ++d; @@ -138,14 +138,15 @@ mutable std::vector timeDateStamps; const std::vector> &records; bool writeRepro; - - COFFLinkerContext &ctx; + const COFFLinkerContext &ctx; }; class CVDebugRecordChunk : public NonSectionChunk { public: + CVDebugRecordChunk(const COFFLinkerContext &c) : ctx(c) {} + size_t getSize() const override { - return sizeof(codeview::DebugInfo) + config->pdbAltPath.size() + 1; + return sizeof(codeview::DebugInfo) + ctx.config.pdbAltPath.size() + 1; } void writeTo(uint8_t *b) const override { @@ -155,12 +156,15 @@ // variable sized field (PDB Path) char *p = reinterpret_cast(b + sizeof(*buildId)); - if (!config->pdbAltPath.empty()) - memcpy(p, config->pdbAltPath.data(), config->pdbAltPath.size()); - p[config->pdbAltPath.size()] = '\0'; + if (!ctx.config.pdbAltPath.empty()) + memcpy(p, ctx.config.pdbAltPath.data(), ctx.config.pdbAltPath.size()); + p[ctx.config.pdbAltPath.size()] = '\0'; } mutable codeview::DebugInfo *buildId = nullptr; + +private: + const COFFLinkerContext &ctx; }; class ExtendedDllCharacteristicsChunk : public NonSectionChunk { @@ -195,7 +199,8 @@ // The writer writes a SymbolTable result to a file. class Writer { public: - Writer(COFFLinkerContext &c) : buffer(errorHandler().outputBuffer), ctx(c) {} + Writer(COFFLinkerContext &c) + : buffer(errorHandler().outputBuffer), delayIdata(c), edata(c), ctx(c) {} void run(); private: @@ -208,6 +213,12 @@ void mergeSections(); void removeUnusedSections(); void assignAddresses(); + bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin); + std::pair getThunk(DenseMap &lastThunks, + Defined *target, uint64_t p, + uint16_t type, int margin); + bool createThunks(OutputSection *os, int margin); + bool verifyRanges(const std::vector chunks); void finalizeAddresses(); void removeEmptySections(); void assignOutputSectionIndices(); @@ -217,6 +228,7 @@ void createSEHTable(); void createRuntimePseudoRelocs(); void insertCtorDtorSymbols(); + void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols); void createGuardCFTables(); void markSymbolsForRVATable(ObjFile *file, ArrayRef symIdxChunks, @@ -234,6 +246,7 @@ void sortExceptionTable(); void sortCRTSectionChunks(std::vector &chunks); void addSyntheticIdata(); + void sortBySectionOrder(std::vector &chunks); void fixPartialSectionChars(StringRef name, uint32_t chars); bool fixGnuImportChunks(); void fixTlsAlignment(); @@ -331,14 +344,14 @@ } // Write the section header to a given buffer. -void OutputSection::writeHeaderTo(uint8_t *buf) { +void OutputSection::writeHeaderTo(uint8_t *buf, bool isDebug) { auto *hdr = reinterpret_cast(buf); *hdr = header; if (stringTableOff) { // If name is too long, write offset into the string table as a name. encodeSectionName(hdr->Name, stringTableOff); } else { - assert(!config->debug || name.size() <= COFF::NameSize || + assert(!isDebug || name.size() <= COFF::NameSize || (hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0); strncpy(hdr->Name, name.data(), std::min(name.size(), (size_t)COFF::NameSize)); @@ -351,8 +364,8 @@ // Check whether the target address S is in range from a relocation // of type relType at address P. -static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { - if (config->machine == ARMNT) { +bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { + if (ctx.config.machine == ARMNT) { int64_t diff = AbsoluteDifference(s, p + 4) + margin; switch (relType) { case IMAGE_REL_ARM_BRANCH20T: @@ -363,7 +376,7 @@ default: return true; } - } else if (config->machine == ARM64) { + } else if (ctx.config.machine == ARM64) { int64_t diff = AbsoluteDifference(s, p) + margin; switch (relType) { case IMAGE_REL_ARM64_BRANCH26: @@ -382,19 +395,19 @@ // Return the last thunk for the given target if it is in range, // or create a new one. -static std::pair -getThunk(DenseMap &lastThunks, Defined *target, uint64_t p, - uint16_t type, int margin) { +std::pair +Writer::getThunk(DenseMap &lastThunks, Defined *target, + uint64_t p, uint16_t type, int margin) { Defined *&lastThunk = lastThunks[target->getRVA()]; if (lastThunk && isInRange(type, lastThunk->getRVA(), p, margin)) return {lastThunk, false}; Chunk *c; - switch (config->machine) { + switch (ctx.config.machine) { case ARMNT: - c = make(target); + c = make(ctx, target); break; case ARM64: - c = make(target); + c = make(ctx, target); break; default: llvm_unreachable("Unexpected architecture"); @@ -415,7 +428,7 @@ // After adding thunks, we verify that all relocations are in range (with // no extra margin requirements). If this failed, we restart (throwing away // the previously created thunks) and retry with a wider margin. -static bool createThunks(OutputSection *os, int margin) { +bool Writer::createThunks(OutputSection *os, int margin) { bool addressesChanged = false; DenseMap lastThunks; DenseMap, uint32_t> thunkSymtabIndices; @@ -511,7 +524,7 @@ } // Verify that all relocations are in range, with no extra margin requirements. -static bool verifyRanges(const std::vector chunks) { +bool Writer::verifyRanges(const std::vector chunks) { for (Chunk *c : chunks) { SectionChunk *sc = dyn_cast_or_null(c); if (!sc) @@ -539,7 +552,7 @@ // Assign addresses and add thunks if necessary. void Writer::finalizeAddresses() { assignAddresses(); - if (config->machine != ARMNT && config->machine != ARM64) + if (ctx.config.machine != ARMNT && ctx.config.machine != ARM64) return; size_t origNumChunks = 0; @@ -600,7 +613,7 @@ } void Writer::writePEChecksum() { - if (!config->writeCheckSum) { + if (!ctx.config.writeCheckSum) { return; } @@ -658,8 +671,8 @@ fatal("image size (" + Twine(fileSize) + ") " + "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); - openFile(config->outputFile); - if (config->is64()) { + openFile(ctx.config.outputFile); + if (ctx.config.is64()) { writeHeader(); } else { writeHeader(); @@ -675,7 +688,7 @@ t1.stop(); - if (!config->pdbPath.empty() && config->debug) { + if (!ctx.config.pdbPath.empty() && ctx.config.debug) { assert(buildId); createPDB(ctx, sectionTable, buildId->buildId); } @@ -704,11 +717,11 @@ } // For /order. -static void sortBySectionOrder(std::vector &chunks) { - auto getPriority = [](const Chunk *c) { +void Writer::sortBySectionOrder(std::vector &chunks) { + auto getPriority = [&ctx = ctx](const Chunk *c) { if (auto *sec = dyn_cast(c)) if (sec->sym) - return config->order.lookup(sec->sym->getName()); + return ctx.config.order.lookup(sec->sym->getName()); return 0; }; @@ -787,7 +800,7 @@ // terminator in .idata$2. void Writer::addSyntheticIdata() { uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; - idata.create(); + idata.create(ctx); // Add the .idata content in the right section groups, to allow // chunks from other linked in object files to be grouped together. @@ -830,7 +843,8 @@ // Return whether a SectionChunk's suffix (the dollar and any trailing // suffix) should be removed and sorted into the main suffixless // PartialSection. -static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) { +static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name, + bool isMinGW) { // On MinGW, comdat groups are formed by putting the comdat group name // after the '$' in the section name. For .eh_frame$, that must // still be sorted before the .eh_frame trailer from crtend.o, thus just @@ -840,7 +854,7 @@ // hypothetical case of comdat .CRT$XCU, we definitely need to keep the // suffix for sorting. Thus, to play it safe, only strip the suffix for // the standard sections. - if (!config->mingw) + if (!isMinGW) return false; if (!sc || !sc->isCOMDAT()) return false; @@ -850,15 +864,15 @@ } void Writer::sortSections() { - if (!config->callGraphProfile.empty()) { + if (!ctx.config.callGraphProfile.empty()) { DenseMap order = computeCallGraphProfileOrder(ctx); for (auto it : order) { if (DefinedRegular *sym = it.first->sym) - config->order[sym->getName()] = it.second; + ctx.config.order[sym->getName()] = it.second; } } - if (!config->order.empty()) + if (!ctx.config.order.empty()) for (auto it : partialSections) sortBySectionOrder(it.second->chunks); } @@ -903,12 +917,12 @@ for (Chunk *c : ctx.symtab.getChunks()) { auto *sc = dyn_cast(c); if (sc && !sc->live) { - if (config->verbose) + if (ctx.config.verbose) sc->printDiscardedMessage(); continue; } StringRef name = c->getSectionName(); - if (shouldStripSectionSuffix(sc, name)) + if (shouldStripSectionSuffix(sc, name, ctx.config.mingw)) name = name.split('$').first; if (name.startswith(".tls")) @@ -990,6 +1004,8 @@ } void Writer::createMiscChunks() { + Configuration *config = &ctx.config; + for (MergeChunk *p : ctx.mergeChunkInstances) { if (p) { p->finalizeContents(); @@ -1017,7 +1033,7 @@ // output a PDB no matter what, and this chunk provides the only means of // allowing a debugger to match a PDB and an executable. So we need it even // if we're ultimately not going to write CodeView data to the PDB. - buildId = make(); + buildId = make(ctx); debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId}); } @@ -1061,16 +1077,16 @@ continue; std::string dll = StringRef(file->dllName).lower(); - if (config->dllOrder.count(dll) == 0) - config->dllOrder[dll] = config->dllOrder.size(); + if (ctx.config.dllOrder.count(dll) == 0) + ctx.config.dllOrder[dll] = ctx.config.dllOrder.size(); if (file->impSym && !isa(file->impSym)) - fatal(toString(*file->impSym) + " was replaced"); + fatal(toString(ctx, *file->impSym) + " was replaced"); DefinedImportData *impSym = cast_or_null(file->impSym); - if (config->delayLoads.count(StringRef(file->dllName).lower())) { + if (ctx.config.delayLoads.count(StringRef(file->dllName).lower())) { if (!file->thunkSym) fatal("cannot delay-load " + toString(file) + - " due to import of data: " + toString(*impSym)); + " due to import of data: " + toString(ctx, *impSym)); delayIdata.add(impSym); } else { idata.add(impSym); @@ -1090,15 +1106,15 @@ continue; if (!isa(file->thunkSym)) - fatal(toString(*file->thunkSym) + " was replaced"); + fatal(toString(ctx, *file->thunkSym) + " was replaced"); DefinedImportThunk *thunk = cast(file->thunkSym); if (file->thunkLive) textSec->addChunk(thunk->getChunk()); } if (!delayIdata.empty()) { - Defined *helper = cast(config->delayLoadHelper); - delayIdata.create(ctx, helper); + Defined *helper = cast(ctx.config.delayLoadHelper); + delayIdata.create(helper); for (Chunk *c : delayIdata.getChunks()) didatSec->addChunk(c); for (Chunk *c : delayIdata.getDataChunks()) @@ -1112,9 +1128,9 @@ if (!edataSec->chunks.empty()) { // Allow using a custom built export table from input object files, instead // of having the linker synthesize the tables. - if (config->hadExplicitExports) + if (ctx.config.hadExplicitExports) warn("literal .edata sections override exports"); - } else if (!config->exports.empty()) { + } else if (!ctx.config.exports.empty()) { for (Chunk *c : edata.chunks) edataSec->addChunk(c); } @@ -1123,9 +1139,9 @@ edataEnd = edataSec->chunks.back(); } // Warn on exported deleting destructor. - for (auto e : config->exports) + for (auto e : ctx.config.exports) if (e.sym && e.sym->getName().startswith("??_G")) - warn("export of deleting dtor: " + toString(*e.sym)); + warn("export of deleting dtor: " + toString(ctx, *e.sym)); } void Writer::removeUnusedSections() { @@ -1252,7 +1268,7 @@ continue; if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) continue; - if (config->warnLongSectionNames) { + if (ctx.config.warnLongSectionNames) { warn("section name " + sec->name + " is longer than 8 characters and will use a non-standard string " "table"); @@ -1260,7 +1276,7 @@ sec->setStringTableOff(addEntryToStringTable(sec->name)); } - if (config->debugDwarf || config->debugSymtab) { + if (ctx.config.debugDwarf || ctx.config.debugSymtab) { for (ObjFile *file : ctx.objFileInstances) { for (Symbol *b : file->getSymbols()) { auto *d = dyn_cast_or_null(b); @@ -1297,7 +1313,7 @@ pointerToSymbolTable = fileOff; fileOff += outputSymtab.size() * sizeof(coff_symbol16); fileOff += 4 + strtab.size(); - fileSize = alignTo(fileOff, config->fileAlign); + fileSize = alignTo(fileOff, ctx.config.fileAlign); } void Writer::mergeSections() { @@ -1306,7 +1322,7 @@ lastPdata = pdataSec->chunks.back(); } - for (auto &p : config->merge) { + for (auto &p : ctx.config.merge) { StringRef toName = p.second; if (p.first == toName) continue; @@ -1314,8 +1330,8 @@ while (true) { if (!names.insert(toName).second) fatal("/merge: cycle found for section '" + p.first + "'"); - auto i = config->merge.find(toName); - if (i == config->merge.end()) + auto i = ctx.config.merge.find(toName); + if (i == ctx.config.merge.end()) break; toName = i->second; } @@ -1334,6 +1350,8 @@ // Visits all sections to assign incremental, non-overlapping RVAs and // file offsets. void Writer::assignAddresses() { + Configuration *config = &ctx.config; + sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + sizeof(data_directory) * numberOfDataDirectory + sizeof(coff_section) * ctx.outputSections.size(); @@ -1391,6 +1409,7 @@ // under DOS, that program gets run (usually to just print an error message). // When run under Windows, the loader looks at AddressOfNewExeHeader and uses // the PE header instead. + Configuration *config = &ctx.config; uint8_t *buf = buffer->getBufferStart(); auto *dos = reinterpret_cast(buf); buf += sizeof(dos_header); @@ -1565,7 +1584,7 @@ // Write section table for (OutputSection *sec : ctx.outputSections) { - sec->writeHeaderTo(buf); + sec->writeHeaderTo(buf, config->debug); buf += sizeof(coff_section); } sectionTable = ArrayRef( @@ -1674,8 +1693,8 @@ // Visit all relocations from all section contributions of this object file and // mark the relocation target as address-taken. -static void markSymbolsWithRelocations(ObjFile *file, - SymbolRVASet &usedSymbols) { +void Writer::markSymbolsWithRelocations(ObjFile *file, + SymbolRVASet &usedSymbols) { for (Chunk *c : file->getChunks()) { // We only care about live section chunks. Common chunks and other chunks // don't generally contain relocations. @@ -1684,7 +1703,8 @@ continue; for (const coff_relocation &reloc : sc->getRelocs()) { - if (config->machine == I386 && reloc.Type == COFF::IMAGE_REL_I386_REL32) + if (ctx.config.machine == I386 && + reloc.Type == COFF::IMAGE_REL_I386_REL32) // Ignore relative relocations on x86. On x86_64 they can't be ignored // since they're also used to compute absolute addresses. continue; @@ -1699,6 +1719,8 @@ // address-taken functions. It is sorted and uniqued, just like the safe SEH // table. void Writer::createGuardCFTables() { + Configuration *config = &ctx.config; + SymbolRVASet addressTakenSyms; SymbolRVASet giatsRVASet; std::vector giatsSymbols; @@ -1862,7 +1884,7 @@ sc->getRuntimePseudoRelocs(rels); } - if (!config->pseudoRelocs) { + if (!ctx.config.pseudoRelocs) { // Not writing any pseudo relocs; if some were needed, error out and // indicate what required them. for (const RuntimePseudoReloc &rpr : rels) @@ -1891,10 +1913,10 @@ // There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__ // and __DTOR_LIST__ respectively. void Writer::insertCtorDtorSymbols() { - AbsolutePointerChunk *ctorListHead = make(-1); - AbsolutePointerChunk *ctorListEnd = make(0); - AbsolutePointerChunk *dtorListHead = make(-1); - AbsolutePointerChunk *dtorListEnd = make(0); + AbsolutePointerChunk *ctorListHead = make(ctx, -1); + AbsolutePointerChunk *ctorListEnd = make(ctx, 0); + AbsolutePointerChunk *dtorListHead = make(ctx, -1); + AbsolutePointerChunk *dtorListEnd = make(ctx, 0); ctorsSec->insertChunkAtStart(ctorListHead); ctorsSec->addChunk(ctorListEnd); dtorsSec->insertChunkAtStart(dtorListHead); @@ -1911,7 +1933,7 @@ // Handles /section options to allow users to overwrite // section attributes. void Writer::setSectionPermissions() { - for (auto &p : config->section) { + for (auto &p : ctx.config.section) { StringRef name = p.first; uint32_t perm = p.second; for (OutputSection *sec : ctx.outputSections) @@ -1922,10 +1944,6 @@ // Write section contents to a mmap'ed file. void Writer::writeSections() { - // Record the number of sections to apply section index relocations - // against absolute symbols. See applySecIdx in Chunks.cpp.. - DefinedAbsolute::numOutputSections = ctx.outputSections.size(); - uint8_t *buf = buffer->getBufferStart(); for (OutputSection *sec : ctx.outputSections) { uint8_t *secBuf = buf + sec->getFileOff(); @@ -1947,6 +1965,8 @@ // 2) In all cases, the PE COFF file header also contains a timestamp. // For reproducibility, instead of a timestamp we want to use a hash of the // PE contents. + Configuration *config = &ctx.config; + if (config->debug) { assert(buildId && "BuildId is not set!"); // BuildId->BuildId was filled in when the PDB was written. @@ -2003,7 +2023,7 @@ }; uint8_t *begin = bufAddr(firstPdata); uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize(); - if (config->machine == AMD64) { + if (ctx.config.machine == AMD64) { struct Entry { ulittle32_t begin, end, unwind; }; if ((end - begin) % sizeof(Entry) != 0) { fatal("unexpected .pdata size: " + Twine(end - begin) + @@ -2014,7 +2034,7 @@ [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); return; } - if (config->machine == ARMNT || config->machine == ARM64) { + if (ctx.config.machine == ARMNT || ctx.config.machine == ARM64) { struct Entry { ulittle32_t begin, unwind; }; if ((end - begin) % sizeof(Entry) != 0) { fatal("unexpected .pdata size: " + Twine(end - begin) + @@ -2055,7 +2075,7 @@ }; llvm::stable_sort(chunks, sectionChunkOrder); - if (config->verbose) { + if (ctx.config.verbose) { for (auto &c : chunks) { auto sc = dyn_cast(c); log(" " + sc->file->mb.getBufferIdentifier().str() + @@ -2081,7 +2101,7 @@ // Add base relocations to .reloc section. void Writer::addBaserels() { - if (!config->relocatable) + if (!ctx.config.relocatable) return; relocSec->chunks.clear(); std::vector v; @@ -2144,14 +2164,14 @@ uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff(); uint64_t tlsOffset = tlsSym->getRVA() - sec->getRVA(); - uint64_t directorySize = config->is64() + uint64_t directorySize = ctx.config.is64() ? sizeof(object::coff_tls_directory64) : sizeof(object::coff_tls_directory32); if (tlsOffset + directorySize > sec->getRawSize()) fatal("_tls_used sym is malformed"); - if (config->is64()) { + if (ctx.config.is64()) { object::coff_tls_directory64 *tlsDir = reinterpret_cast(&secBuf[tlsOffset]); tlsDir->setAlignment(tlsAlignment); @@ -2166,7 +2186,7 @@ Symbol *sym = ctx.symtab.findUnderscore("_load_config_used"); auto *b = cast_if_present(sym); if (!b) { - if (config->guardCF != GuardCFLevel::Off) + if (ctx.config.guardCF != GuardCFLevel::Off) warn("Control Flow Guard is enabled but '_load_config_used' is missing"); return; } @@ -2175,7 +2195,7 @@ uint8_t *buf = buffer->getBufferStart(); uint8_t *secBuf = buf + sec->getFileOff(); uint8_t *symBuf = secBuf + (b->getRVA() - sec->getRVA()); - uint32_t expectedAlign = config->is64() ? 8 : 4; + uint32_t expectedAlign = ctx.config.is64() ? 8 : 4; if (b->getChunk()->getAlignment() < expectedAlign) warn("'_load_config_used' is misaligned (expected alignment to be " + Twine(expectedAlign) + " bytes, got " + @@ -2185,7 +2205,7 @@ Twine::utohexstr(b->getRVA()) + " not aligned to " + Twine(expectedAlign) + " bytes)"); - if (config->is64()) + if (ctx.config.is64()) checkLoadConfigGuardData( reinterpret_cast(symBuf)); else @@ -2208,7 +2228,7 @@ #define CHECK_VA(field, sym) \ if (auto *s = dyn_cast(ctx.symtab.findUnderscore(sym))) \ - if (loadConfig->field != config->imageBase + s->getRVA()) \ + if (loadConfig->field != ctx.config.imageBase + s->getRVA()) \ warn(#field " not set correctly in '_load_config_used'"); #define CHECK_ABSOLUTE(field, sym) \ @@ -2216,7 +2236,7 @@ if (loadConfig->field != s->getVA()) \ warn(#field " not set correctly in '_load_config_used'"); - if (config->guardCF == GuardCFLevel::Off) + if (ctx.config.guardCF == GuardCFLevel::Off) return; RETURN_IF_NOT_CONTAINS(GuardFlags) CHECK_VA(GuardCFFunctionTable, "__guard_fids_table") @@ -2227,13 +2247,13 @@ CHECK_ABSOLUTE(GuardAddressTakenIatEntryCount, "__guard_iat_count") } - if (!(config->guardCF & GuardCFLevel::LongJmp)) + if (!(ctx.config.guardCF & GuardCFLevel::LongJmp)) return; RETURN_IF_NOT_CONTAINS(GuardLongJumpTargetCount) CHECK_VA(GuardLongJumpTargetTable, "__guard_longjmp_table") CHECK_ABSOLUTE(GuardLongJumpTargetCount, "__guard_longjmp_count") - if (!(config->guardCF & GuardCFLevel::EHCont)) + if (!(ctx.config.guardCF & GuardCFLevel::EHCont)) return; RETURN_IF_NOT_CONTAINS(GuardEHContinuationCount) CHECK_VA(GuardEHContinuationTable, "__guard_eh_cont_table")