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" @@ -28,9 +29,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; @@ -42,6 +43,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; @@ -53,6 +56,18 @@ return c->osidx == 0 ? nullptr : outputSections[c->osidx - 1]; } + std::string maybeDemangleSymbol(StringRef symName) const; + std::string toString(coff::Symbol &b) const; + + // 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 Archive::Symbol &b) const; + + // Fake sections for parsing bitcode files. + FakeSectionChunk *ltoTextSectionChunk; + FakeSectionChunk *ltoDataSectionChunk; + // All timers used in the COFF linker. Timer rootTimer; Timer inputFileTimer; @@ -78,6 +93,8 @@ Timer publicsLayoutTimer; Timer tpiStreamLayoutTimer; Timer diskCommitTimer; + + Configuration config; }; } // namespace coff diff --git a/lld/COFF/COFFLinkerContext.cpp b/lld/COFF/COFFLinkerContext.cpp --- a/lld/COFF/COFFLinkerContext.cpp +++ b/lld/COFF/COFFLinkerContext.cpp @@ -10,14 +10,17 @@ //===----------------------------------------------------------------------===// #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 { namespace 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), @@ -34,7 +37,38 @@ 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); +} + +// Returns a symbol name for an error message. +std::string COFFLinkerContext::maybeDemangleSymbol(StringRef symName) const { + if (config.demangle) { + std::string prefix; + StringRef prefixless = symName; + if (prefixless.consume_front("__imp_")) + prefix = "__declspec(dllimport) "; + StringRef demangleInput = prefixless; + if (config.machine == I386) + demangleInput.consume_front("_"); + std::string demangled = llvm::demangle(std::string(demangleInput)); + if (demangled != demangleInput) + return prefix + llvm::demangle(std::string(demangleInput)); + return (prefix + prefixless).str(); + } + return std::string(symName); +} + +std::string COFFLinkerContext::toString(Symbol &b) const { + return maybeDemangleSymbol(b.getName()); +} + +std::string COFFLinkerContext::toCOFFString(const Archive::Symbol &b) const { + return maybeDemangleSymbol(b.getName()); +} } // namespace coff } // namespace lld diff --git a/lld/COFF/CallGraphSort.cpp b/lld/COFF/CallGraphSort.cpp --- a/lld/COFF/CallGraphSort.cpp +++ b/lld/COFF/CallGraphSort.cpp @@ -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 @@ -222,13 +222,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); @@ -474,24 +474,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 &c, Defined *s) + : NonSectionChunk(ImportThunkKind), impSymbol(s), ctx(c) {} 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; @@ -499,7 +501,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); } @@ -509,7 +512,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); } @@ -518,35 +522,46 @@ class RangeExtensionThunkARM : public NonSectionChunk { public: - explicit RangeExtensionThunkARM(Defined *t) : target(t) { setAlignment(2); } + explicit RangeExtensionThunkARM(COFFLinkerContext &c, Defined *t) + : target(t), ctx(c) { + 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 &c, Defined *t) + : target(t), ctx(c) { + 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 @@ -613,8 +628,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; @@ -670,7 +686,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 &c, uint64_t value) + : value(value), ctx(c) { setAlignment(getSize()); } size_t getSize() const override; @@ -678,6 +695,7 @@ private: uint64_t value; + COFFLinkerContext &ctx; }; // Return true if this file has the hotpatch flag set to true in the S_COMPILE3 @@ -698,6 +716,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 coff } // namespace lld diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp --- a/lld/COFF/Chunks.cpp +++ b/lld/COFF/Chunks.cpp @@ -52,8 +52,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; } @@ -93,21 +93,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; @@ -115,7 +126,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 " + @@ -124,13 +137,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 " + @@ -187,19 +205,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: @@ -297,7 +322,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; @@ -306,14 +332,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 " + @@ -323,12 +355,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 @@ -394,7 +427,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; } @@ -402,18 +435,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"); @@ -478,8 +512,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; @@ -511,7 +546,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); @@ -528,7 +563,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. @@ -554,7 +590,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: @@ -611,7 +647,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 " + @@ -717,7 +754,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); @@ -730,14 +768,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) { @@ -747,7 +784,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 { @@ -765,12 +802,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)); @@ -785,28 +822,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); } } @@ -926,8 +968,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; @@ -985,10 +1027,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 @@ -95,7 +95,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; @@ -284,8 +284,6 @@ bool stdcallFixup = false; }; -extern std::unique_ptr config; - } // namespace coff } // namespace lld diff --git a/lld/COFF/DLL.h b/lld/COFF/DLL.h --- a/lld/COFF/DLL.h +++ b/lld/COFF/DLL.h @@ -24,7 +24,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; @@ -38,9 +38,10 @@ // DelayLoadContents creates all chunks for the delay-load DLL import table. class DelayLoadContents { public: + DelayLoadContents(COFFLinkerContext &c) : ctx(c) {} 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; } @@ -61,19 +62,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 &c); std::vector chunks; uint64_t getRVA() { return chunks[0]->getRVA(); } uint64_t getSize() { return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA(); } + + COFFLinkerContext &ctx; }; } // namespace 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); @@ -326,47 +335,56 @@ class ThunkChunkX86 : public NonSectionChunk { public: - ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} + ThunkChunkX86(COFFLinkerContext &c, Defined *i, Chunk *tm) + : imp(i), tailMerge(tm), ctx(c) {} 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 &c, Chunk *d, Defined *h) + : desc(d), helper(h), ctx(c) {} 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 &c, Defined *i, Chunk *tm) + : imp(i), tailMerge(tm), ctx(c) { setAlignment(2); } @@ -374,7 +392,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); } @@ -384,11 +402,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 &c, Chunk *d, Defined *h) + : desc(d), helper(h), ctx(c) { setAlignment(2); } @@ -396,7 +418,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); } @@ -406,6 +428,9 @@ Chunk *desc = nullptr; Defined *helper = nullptr; + +private: + const COFFLinkerContext &ctx; }; class ThunkChunkARM64 : public NonSectionChunk { @@ -449,28 +474,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 @@ -510,17 +539,18 @@ class AddressTableChunk : public NonSectionChunk { public: - explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal + 1) {} + explicit AddressTableChunk(COFFLinkerContext &c, size_t maxOrdinal) + : size(maxOrdinal + 1), ctx(c) {} 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) { uint8_t *p = buf + e.ordinal * 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); @@ -534,6 +564,7 @@ private: size_t size; + const COFFLinkerContext &ctx; }; class NamePointersChunk : public NonSectionChunk { @@ -554,11 +585,12 @@ class ExportOrdinalChunk : public NonSectionChunk { public: - explicit ExportOrdinalChunk(size_t i) : size(i) {} + explicit ExportOrdinalChunk(const COFFLinkerContext &c, size_t i) + : size(i), ctx(c) {} 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; write16le(buf, e.ordinal); @@ -568,12 +600,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) { @@ -585,18 +618,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]); @@ -632,9 +665,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) { @@ -646,15 +679,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 @@ -689,13 +722,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: @@ -705,13 +738,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: @@ -719,20 +752,20 @@ } } -EdataContents::EdataContents() { +EdataContents::EdataContents(COFFLinkerContext &c) : ctx(c) { 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); @@ -740,7 +773,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 @@ -226,7 +226,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() : ""; @@ -296,7 +296,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; @@ -315,7 +315,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 @@ -342,7 +342,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(); @@ -371,7 +371,7 @@ ipiMap = ipiSrc->indexMapStorage; } - if (config->showSummary) { + if (ctx.config.showSummary) { nbTypeRecords = tpiMap.size() + ipiMap.size(); nbTypeRecordsBytes = expectedTpi->typeArray().getUnderlyingStream().getLength() + @@ -687,14 +687,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(); } @@ -747,7 +747,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. @@ -765,13 +765,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(); } @@ -849,7 +849,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 { namespace coff { -extern std::unique_ptr driver; - using llvm::COFF::MachineTypes; using llvm::COFF::WindowsSubsystem; using llvm::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 @@ -58,6 +51,8 @@ class ArgParser { public: + ArgParser(COFFLinkerContext &ctx); + // Parses command line options. llvm::opt::InputArgList parse(llvm::ArrayRef args); @@ -74,11 +69,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); @@ -107,6 +104,40 @@ bool findUnderscoreMangle(StringRef sym); + // Symbol names are mangled by prepending "_" on x86. + StringRef mangle(StringRef sym); + + 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(); @@ -154,61 +185,68 @@ llvm::StringSet<> directivesExports; COFFLinkerContext &ctx; -}; -// Functions below this line are defined in DriverUtils.cpp. + // Functions below this line are defined in DriverUtils.cpp. -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 parseSection(StringRef); + void parseAligncomm(StringRef); + void parsePDBPageSize(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); -// Parses a string in the form of "[:]" -void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine); + // 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(); + std::string createDefaultXml(); + std::string createManifestXmlWithInternalMt(StringRef defaultXml); + std::string createManifestXmlWithExternalMt(StringRef defaultXml); + std::string createManifestXml(); -// Create a resource file containing a manifest XML. -std::unique_ptr createManifestRes(); -void createSideBySideManifest(); + std::unique_ptr + createMemoryBufferForManifestRes(size_t manifestRes); -// Used for dllexported symbols. -Export parseExport(StringRef arg); -void fixupExports(); -void assignExportOrdinals(); + // Used for dllexported symbols. + Export parseExport(StringRef arg); + void fixupExports(); + void assignExportOrdinals(); -// 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); + // 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); + // Convert Windows resource files (.res files) to a .obj file. + MemoryBufferRef convertResToCOFF(ArrayRef mbs, + ArrayRef objs); + + void printHelp(const char *argv0); +}; // 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 @@ -58,9 +58,6 @@ namespace lld { namespace 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(). @@ -71,10 +68,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; } @@ -95,11 +89,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(); @@ -143,9 +137,9 @@ } // 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; } @@ -159,9 +153,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; } @@ -205,7 +199,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; } @@ -236,12 +230,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); }); } @@ -278,8 +272,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))); + ctx.toCOFFString(sym) + ": " + parentName + "(" + childName + + "): " + toString(std::move(e))); }; if (!c.getParent()->isThin()) { @@ -289,16 +283,16 @@ reportBufferError(mbOrErr.takeError(), check(c.getFullName())); MemoryBufferRef mb = mbOrErr.get(); enqueueTask([=]() { - driver->addArchiveBuffer(mb, toCOFFString(sym), parentName, - offsetInArchive); + ctx.driver.addArchiveBuffer(mb, ctx.toCOFFString(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 " + + ctx.toCOFFString(sym)); auto future = std::make_shared>( createFutureForFile(childName)); enqueueTask([=]() { @@ -307,14 +301,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)), + ctx.toCOFFString(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 @@ -326,7 +321,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); @@ -340,14 +335,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. @@ -368,7 +363,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); @@ -377,29 +372,29 @@ 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_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; } @@ -480,7 +475,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; } @@ -489,13 +484,13 @@ // consideration. This never returns the same path (in that case, // it returns None). Optional LinkerDriver::findLib(StringRef filename) { - if (config->noDefaultLibAll) + if (ctx.config.noDefaultLibAll) return None; if (!visitedLibs.insert(filename.lower()).second) return None; StringRef path = doFindLib(filename); - if (config->noDefaultLibs.count(path.lower())) + if (ctx.config.noDefaultLibs.count(path.lower())) return None; if (Optional id = getUniqueID(path)) @@ -521,7 +516,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; } @@ -550,15 +545,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"); @@ -575,9 +570,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 @@ -599,10 +594,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) { @@ -742,8 +737,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 ""; @@ -751,14 +747,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()); } @@ -770,26 +766,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); @@ -806,9 +802,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; } @@ -817,8 +813,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; } @@ -828,8 +824,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; } @@ -844,39 +840,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; @@ -888,7 +884,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); @@ -899,7 +895,7 @@ e2.data = e1.Data; e2.isPrivate = e1.Private; e2.constant = e1.Constant; - config->exports.push_back(e2); + ctx.config.exports.push_back(e2); } } @@ -921,7 +917,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("@")) { @@ -950,22 +946,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, @@ -982,7 +978,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; } @@ -1004,11 +1000,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) { @@ -1034,7 +1030,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; } } } @@ -1049,7 +1045,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 @@ -1087,12 +1083,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 '.'. @@ -1103,19 +1099,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%")) @@ -1129,7 +1128,7 @@ cursor = secondMark + 1; } - config->pdbAltPath = buf; + ctx.config.pdbAltPath = buf; } /// Convert resource files and potentially merge input resource object @@ -1143,7 +1142,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" @@ -1174,16 +1173,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; + AutoExporter exporter(ctx); for (auto *arg : args.filtered(OPT_wholearchive_file)) if (Optional path = doFindFile(arg->getValue())) @@ -1191,12 +1190,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; @@ -1206,7 +1205,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); }); } @@ -1256,7 +1255,7 @@ } // Parse command line options. - ArgParser parser; + ArgParser parser(ctx); opt::InputArgList args = parser.parse(argsArr); // Parse and evaluate -mllvm options. @@ -1291,13 +1290,13 @@ error(arg->getSpelling() + ": expected a positive integer, but got '" + arg->getValue() + "'"); parallel::strategy = hardware_concurrency(threads); - config->thinLTOJobs = v.str(); + ctx.config.thinLTOJobs = v.str(); } if (args.hasArg(OPT_show_timing)) - config->showTiming = true; + ctx.config.showTiming = true; - config->showSummary = args.hasArg(OPT_summary); + ctx.config.showSummary = args.hasArg(OPT_summary); // Handle --version, which is an lld extension. This option is a bit odd // because it doesn't start with "/", but we deliberately chose "--" to @@ -1309,7 +1308,7 @@ // Handle /lldmingw early, since it can potentially affect how other // options are handled. - config->mingw = args.hasArg(OPT_lldmingw); + ctx.config.mingw = args.hasArg(OPT_lldmingw); // Handle /linkrepro and /reproduce. if (Optional path = getReproduceFile(args)) { @@ -1326,7 +1325,7 @@ if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) { if (args.hasArg(OPT_deffile)) - config->noEntry = true; + ctx.config.noEntry = true; else fatal("no input files"); } @@ -1344,61 +1343,61 @@ StringRef(arg->getValue()).split(vec, ','); for (StringRef s : vec) { if (s == "4037") - config->warnMissingOrderSymbol = false; + ctx.config.warnMissingOrderSymbol = false; else if (s == "4099") - config->warnDebugInfoUnusable = false; + ctx.config.warnDebugInfoUnusable = false; else if (s == "4217") - config->warnLocallyDefinedImported = false; + ctx.config.warnLocallyDefinedImported = false; else if (s == "longsections") - config->warnLongSectionNames = false; + ctx.config.warnLongSectionNames = false; // Other warning numbers are ignored. } } // Handle /out if (auto *arg = args.getLastArg(OPT_out)) - config->outputFile = arg->getValue(); + ctx.config.outputFile = arg->getValue(); // Handle /verbose if (args.hasArg(OPT_verbose)) - config->verbose = true; - errorHandler().verbose = config->verbose; + ctx.config.verbose = true; + errorHandler().verbose = ctx.config.verbose; // Handle /force or /force:unresolved if (args.hasArg(OPT_force, OPT_force_unresolved)) - config->forceUnresolved = true; + ctx.config.forceUnresolved = true; // Handle /force or /force:multiple if (args.hasArg(OPT_force, OPT_force_multiple)) - config->forceMultiple = true; + ctx.config.forceMultiple = true; // Handle /force or /force:multipleres if (args.hasArg(OPT_force, OPT_force_multipleres)) - config->forceMultipleRes = true; + ctx.config.forceMultipleRes = true; // Handle /debug DebugKind debug = parseDebugKind(args); if (debug == DebugKind::Full || debug == DebugKind::Dwarf || debug == DebugKind::GHash || debug == DebugKind::NoGHash) { - config->debug = true; - config->incremental = true; + ctx.config.debug = true; + ctx.config.incremental = true; } // Handle /demangle - config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no); + ctx.config.demangle = args.hasFlag(OPT_demangle, OPT_demangle_no); // Handle /debugtype - config->debugTypes = parseDebugTypes(args); + ctx.config.debugTypes = parseDebugTypes(args); // Handle /driver[:uponly|:wdm]. - config->driverUponly = args.hasArg(OPT_driver_uponly) || + ctx.config.driverUponly = args.hasArg(OPT_driver_uponly) || + args.hasArg(OPT_driver_uponly_wdm) || + args.hasArg(OPT_driver_wdm_uponly); + ctx.config.driverWdm = args.hasArg(OPT_driver_wdm) || args.hasArg(OPT_driver_uponly_wdm) || args.hasArg(OPT_driver_wdm_uponly); - config->driverWdm = args.hasArg(OPT_driver_wdm) || - args.hasArg(OPT_driver_uponly_wdm) || - args.hasArg(OPT_driver_wdm_uponly); - config->driver = - config->driverUponly || config->driverWdm || args.hasArg(OPT_driver); + ctx.config.driver = ctx.config.driverUponly || ctx.config.driverWdm || + args.hasArg(OPT_driver); // Handle /pdb bool shouldCreatePDB = @@ -1406,24 +1405,24 @@ debug == DebugKind::NoGHash); if (shouldCreatePDB) { if (auto *arg = args.getLastArg(OPT_pdb)) - config->pdbPath = arg->getValue(); + ctx.config.pdbPath = arg->getValue(); if (auto *arg = args.getLastArg(OPT_pdbaltpath)) - config->pdbAltPath = arg->getValue(); + ctx.config.pdbAltPath = arg->getValue(); if (auto *arg = args.getLastArg(OPT_pdbpagesize)) parsePDBPageSize(arg->getValue()); if (args.hasArg(OPT_natvis)) - config->natvisFiles = args.getAllArgValues(OPT_natvis); + ctx.config.natvisFiles = args.getAllArgValues(OPT_natvis); if (args.hasArg(OPT_pdbstream)) { for (const StringRef value : args.getAllArgValues(OPT_pdbstream)) { const std::pair nameFile = value.split("="); const StringRef name = nameFile.first; const std::string file = nameFile.second.str(); - config->namedStreams[name] = file; + ctx.config.namedStreams[name] = file; } } if (auto *arg = args.getLastArg(OPT_pdb_source_path)) - config->pdbSourcePath = arg->getValue(); + ctx.config.pdbSourcePath = arg->getValue(); } // Handle /pdbstripped @@ -1433,15 +1432,15 @@ // Handle /noentry if (args.hasArg(OPT_noentry)) { if (args.hasArg(OPT_dll)) - config->noEntry = true; + ctx.config.noEntry = true; else error("/noentry must be specified with /dll"); } // Handle /dll if (args.hasArg(OPT_dll)) { - config->dll = true; - config->manifestID = 2; + ctx.config.dll = true; + ctx.config.manifestID = 2; } // Handle /dynamicbase and /fixed. We can't use hasFlag for /dynamicbase @@ -1450,7 +1449,7 @@ auto *dynamicBaseArg = args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no); if (dynamicBaseArg && dynamicBaseArg->getOption().getID() == OPT_dynamicbase_no) - config->dynamicBase = false; + ctx.config.dynamicBase = false; // MSDN claims "/FIXED:NO is the default setting for a DLL, and /FIXED is the // default setting for any other project type.", but link.exe defaults to @@ -1461,44 +1460,45 @@ dynamicBaseArg->getOption().getID() == OPT_dynamicbase) { error("/fixed must not be specified with /dynamicbase"); } else { - config->relocatable = false; - config->dynamicBase = false; + ctx.config.relocatable = false; + ctx.config.dynamicBase = false; } } // Handle /appcontainer - config->appContainer = + ctx.config.appContainer = args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false); // Handle /machine if (auto *arg = args.getLastArg(OPT_machine)) { - config->machine = getMachineType(arg->getValue()); - if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) + ctx.config.machine = getMachineType(arg->getValue()); + if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) fatal(Twine("unknown /machine argument: ") + arg->getValue()); } // Handle /nodefaultlib: for (auto *arg : args.filtered(OPT_nodefaultlib)) - config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); + ctx.config.noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); // Handle /nodefaultlib if (args.hasArg(OPT_nodefaultlib_all)) - config->noDefaultLibAll = true; + ctx.config.noDefaultLibAll = true; // Handle /base if (auto *arg = args.getLastArg(OPT_base)) - parseNumbers(arg->getValue(), &config->imageBase); + parseNumbers(arg->getValue(), &ctx.config.imageBase); // Handle /filealign if (auto *arg = args.getLastArg(OPT_filealign)) { - parseNumbers(arg->getValue(), &config->fileAlign); - if (!isPowerOf2_64(config->fileAlign)) - error("/filealign: not a power of two: " + Twine(config->fileAlign)); + parseNumbers(arg->getValue(), &ctx.config.fileAlign); + if (!isPowerOf2_64(ctx.config.fileAlign)) + error("/filealign: not a power of two: " + Twine(ctx.config.fileAlign)); } // Handle /stack if (auto *arg = args.getLastArg(OPT_stack)) - parseNumbers(arg->getValue(), &config->stackReserve, &config->stackCommit); + parseNumbers(arg->getValue(), &ctx.config.stackReserve, + &ctx.config.stackCommit); // Handle /guard:cf if (auto *arg = args.getLastArg(OPT_guard)) @@ -1506,43 +1506,44 @@ // Handle /heap if (auto *arg = args.getLastArg(OPT_heap)) - parseNumbers(arg->getValue(), &config->heapReserve, &config->heapCommit); + parseNumbers(arg->getValue(), &ctx.config.heapReserve, + &ctx.config.heapCommit); // Handle /version if (auto *arg = args.getLastArg(OPT_version)) - parseVersion(arg->getValue(), &config->majorImageVersion, - &config->minorImageVersion); + parseVersion(arg->getValue(), &ctx.config.majorImageVersion, + &ctx.config.minorImageVersion); // Handle /subsystem if (auto *arg = args.getLastArg(OPT_subsystem)) - parseSubsystem(arg->getValue(), &config->subsystem, - &config->majorSubsystemVersion, - &config->minorSubsystemVersion); + parseSubsystem(arg->getValue(), &ctx.config.subsystem, + &ctx.config.majorSubsystemVersion, + &ctx.config.minorSubsystemVersion); // Handle /osversion if (auto *arg = args.getLastArg(OPT_osversion)) { - parseVersion(arg->getValue(), &config->majorOSVersion, - &config->minorOSVersion); + parseVersion(arg->getValue(), &ctx.config.majorOSVersion, + &ctx.config.minorOSVersion); } else { - config->majorOSVersion = config->majorSubsystemVersion; - config->minorOSVersion = config->minorSubsystemVersion; + ctx.config.majorOSVersion = ctx.config.majorSubsystemVersion; + ctx.config.minorOSVersion = ctx.config.minorSubsystemVersion; } // Handle /timestamp if (llvm::opt::Arg *arg = args.getLastArg(OPT_timestamp, OPT_repro)) { if (arg->getOption().getID() == OPT_repro) { - config->timestamp = 0; - config->repro = true; + ctx.config.timestamp = 0; + ctx.config.repro = true; } else { - config->repro = false; + ctx.config.repro = false; StringRef value(arg->getValue()); - if (value.getAsInteger(0, config->timestamp)) + if (value.getAsInteger(0, ctx.config.timestamp)) fatal(Twine("invalid timestamp: ") + value + ". Expected 32-bit integer"); } } else { - config->repro = false; - config->timestamp = time(nullptr); + ctx.config.repro = false; + ctx.config.timestamp = time(nullptr); } // Handle /alternatename @@ -1555,7 +1556,7 @@ // Handle /implib if (auto *arg = args.getLastArg(OPT_implib)) - config->implib = arg->getValue(); + ctx.config.implib = arg->getValue(); // Handle /opt. bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile); @@ -1594,17 +1595,17 @@ ltoDebugPM = false; } else if (s.startswith("lldlto=")) { StringRef optLevel = s.substr(7); - if (optLevel.getAsInteger(10, config->ltoo) || config->ltoo > 3) + if (optLevel.getAsInteger(10, ctx.config.ltoo) || ctx.config.ltoo > 3) error("/opt:lldlto: invalid optimization level: " + optLevel); } else if (s.startswith("lldltojobs=")) { StringRef jobs = s.substr(11); if (!get_threadpool_strategy(jobs)) error("/opt:lldltojobs: invalid job count: " + jobs); - config->thinLTOJobs = jobs.str(); + ctx.config.thinLTOJobs = jobs.str(); } else if (s.startswith("lldltopartitions=")) { StringRef n = s.substr(17); - if (n.getAsInteger(10, config->ltoPartitions) || - config->ltoPartitions == 0) + if (n.getAsInteger(10, ctx.config.ltoPartitions) || + ctx.config.ltoPartitions == 0) error("/opt:lldltopartitions: invalid partition count: " + n); } else if (s != "lbr" && s != "nolbr") error("/opt: unknown option: " + s); @@ -1613,28 +1614,28 @@ if (!icfLevel) icfLevel = doGC ? ICFLevel::All : ICFLevel::None; - config->doGC = doGC; - config->doICF = icfLevel.getValue(); - config->tailMerge = - (tailMerge == 1 && config->doICF != ICFLevel::None) || tailMerge == 2; - config->ltoNewPassManager = ltoNewPM; - config->ltoDebugPassManager = ltoDebugPM; + ctx.config.doGC = doGC; + ctx.config.doICF = icfLevel.getValue(); + ctx.config.tailMerge = + (tailMerge == 1 && ctx.config.doICF != ICFLevel::None) || tailMerge == 2; + ctx.config.ltoNewPassManager = ltoNewPM; + ctx.config.ltoDebugPassManager = ltoDebugPM; // Handle /lldsavetemps if (args.hasArg(OPT_lldsavetemps)) - config->saveTemps = true; + ctx.config.saveTemps = true; // Handle /kill-at if (args.hasArg(OPT_kill_at)) - config->killAt = true; + ctx.config.killAt = true; // Handle /lldltocache if (auto *arg = args.getLastArg(OPT_lldltocache)) - config->ltoCache = arg->getValue(); + ctx.config.ltoCache = arg->getValue(); // Handle /lldsavecachepolicy if (auto *arg = args.getLastArg(OPT_lldltocachepolicy)) - config->ltoCachePolicy = CHECK( + ctx.config.ltoCachePolicy = CHECK( parseCachePruningPolicy(arg->getValue()), Twine("/lldltocachepolicy: invalid cache policy: ") + arg->getValue()); @@ -1654,7 +1655,7 @@ parseMerge(".xdata=.rdata"); parseMerge(".bss=.data"); - if (config->mingw) { + if (ctx.config.mingw) { parseMerge(".ctors=.rdata"); parseMerge(".dtors=.rdata"); parseMerge(".CRT=.rdata"); @@ -1666,8 +1667,8 @@ // Handle /align if (auto *arg = args.getLastArg(OPT_align)) { - parseNumbers(arg->getValue(), &config->align); - if (!isPowerOf2_64(config->align)) + parseNumbers(arg->getValue(), &ctx.config.align); + if (!isPowerOf2_64(ctx.config.align)) error("/align: not a power of two: " + StringRef(arg->getValue())); if (!args.hasArg(OPT_driver)) warn("/align specified without /driver; image may not run"); @@ -1679,12 +1680,12 @@ // Handle /manifestdependency. for (auto *arg : args.filtered(OPT_manifestdependency)) - config->manifestDependencies.insert(arg->getValue()); + ctx.config.manifestDependencies.insert(arg->getValue()); // Handle /manifest and /manifest: if (auto *arg = args.getLastArg(OPT_manifest, OPT_manifest_colon)) { if (arg->getOption().getID() == OPT_manifest) - config->manifest = Configuration::SideBySide; + ctx.config.manifest = Configuration::SideBySide; else parseManifest(arg->getValue()); } @@ -1695,94 +1696,97 @@ // Handle /manifestfile if (auto *arg = args.getLastArg(OPT_manifestfile)) - config->manifestFile = arg->getValue(); + ctx.config.manifestFile = arg->getValue(); // Handle /manifestinput for (auto *arg : args.filtered(OPT_manifestinput)) - config->manifestInput.push_back(arg->getValue()); + ctx.config.manifestInput.push_back(arg->getValue()); - if (!config->manifestInput.empty() && - config->manifest != Configuration::Embed) { + if (!ctx.config.manifestInput.empty() && + ctx.config.manifest != Configuration::Embed) { fatal("/manifestinput: requires /manifest:embed"); } - config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files); - config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) || - args.hasArg(OPT_thinlto_index_only_arg); - config->thinLTOIndexOnlyArg = + ctx.config.thinLTOEmitImportsFiles = + args.hasArg(OPT_thinlto_emit_imports_files); + ctx.config.thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) || + args.hasArg(OPT_thinlto_index_only_arg); + ctx.config.thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_arg); - config->thinLTOPrefixReplace = + ctx.config.thinLTOPrefixReplace = getOldNewOptions(args, OPT_thinlto_prefix_replace); - config->thinLTOObjectSuffixReplace = + ctx.config.thinLTOObjectSuffixReplace = getOldNewOptions(args, OPT_thinlto_object_suffix_replace); - config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path); - config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate); - config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file); + ctx.config.ltoObjPath = args.getLastArgValue(OPT_lto_obj_path); + ctx.config.ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate); + ctx.config.ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file); // Handle miscellaneous boolean flags. - config->ltoPGOWarnMismatch = args.hasFlag(OPT_lto_pgo_warn_mismatch, - OPT_lto_pgo_warn_mismatch_no, true); - config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); - config->allowIsolation = + ctx.config.ltoPGOWarnMismatch = args.hasFlag( + OPT_lto_pgo_warn_mismatch, OPT_lto_pgo_warn_mismatch_no, true); + ctx.config.allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); + ctx.config.allowIsolation = args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true); - config->incremental = + ctx.config.incremental = args.hasFlag(OPT_incremental, OPT_incremental_no, - !config->doGC && config->doICF == ICFLevel::None && + !ctx.config.doGC && ctx.config.doICF == ICFLevel::None && !args.hasArg(OPT_order) && !args.hasArg(OPT_profile)); - config->integrityCheck = + ctx.config.integrityCheck = args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false); - config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false); - config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); + ctx.config.cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false); + ctx.config.nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); for (auto *arg : args.filtered(OPT_swaprun)) parseSwaprun(arg->getValue()); - config->terminalServerAware = - !config->dll && args.hasFlag(OPT_tsaware, OPT_tsaware_no, true); - config->debugDwarf = debug == DebugKind::Dwarf; - config->debugGHashes = debug == DebugKind::GHash || debug == DebugKind::Full; - config->debugSymtab = debug == DebugKind::Symtab; - config->autoImport = - args.hasFlag(OPT_auto_import, OPT_auto_import_no, config->mingw); - config->pseudoRelocs = args.hasFlag( - OPT_runtime_pseudo_reloc, OPT_runtime_pseudo_reloc_no, config->mingw); - config->callGraphProfileSort = args.hasFlag( + ctx.config.terminalServerAware = + !ctx.config.dll && args.hasFlag(OPT_tsaware, OPT_tsaware_no, true); + ctx.config.debugDwarf = debug == DebugKind::Dwarf; + ctx.config.debugGHashes = + debug == DebugKind::GHash || debug == DebugKind::Full; + ctx.config.debugSymtab = debug == DebugKind::Symtab; + ctx.config.autoImport = + args.hasFlag(OPT_auto_import, OPT_auto_import_no, ctx.config.mingw); + ctx.config.pseudoRelocs = args.hasFlag( + OPT_runtime_pseudo_reloc, OPT_runtime_pseudo_reloc_no, ctx.config.mingw); + ctx.config.callGraphProfileSort = args.hasFlag( OPT_call_graph_profile_sort, OPT_call_graph_profile_sort_no, true); - config->stdcallFixup = - args.hasFlag(OPT_stdcall_fixup, OPT_stdcall_fixup_no, config->mingw); - config->warnStdcallFixup = !args.hasArg(OPT_stdcall_fixup); + ctx.config.stdcallFixup = + args.hasFlag(OPT_stdcall_fixup, OPT_stdcall_fixup_no, ctx.config.mingw); + ctx.config.warnStdcallFixup = !args.hasArg(OPT_stdcall_fixup); // Don't warn about long section names, such as .debug_info, for mingw or // when -debug:dwarf is requested. - if (config->mingw || config->debugDwarf) - config->warnLongSectionNames = false; + if (ctx.config.mingw || ctx.config.debugDwarf) + ctx.config.warnLongSectionNames = false; - config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file); - config->mapFile = getMapFile(args, OPT_map, OPT_map_file); + ctx.config.lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file); + ctx.config.mapFile = getMapFile(args, OPT_map, OPT_map_file); - if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) { - warn("/lldmap and /map have the same output file '" + config->mapFile + + if (ctx.config.lldmapFile != "" && + ctx.config.lldmapFile == ctx.config.mapFile) { + warn("/lldmap and /map have the same output file '" + ctx.config.mapFile + "'.\n>>> ignoring /lldmap"); - config->lldmapFile.clear(); + ctx.config.lldmapFile.clear(); } - if (config->incremental && args.hasArg(OPT_profile)) { + if (ctx.config.incremental && args.hasArg(OPT_profile)) { warn("ignoring '/incremental' due to '/profile' specification"); - config->incremental = false; + ctx.config.incremental = false; } - if (config->incremental && args.hasArg(OPT_order)) { + if (ctx.config.incremental && args.hasArg(OPT_order)) { warn("ignoring '/incremental' due to '/order' specification"); - config->incremental = false; + ctx.config.incremental = false; } - if (config->incremental && config->doGC) { + if (ctx.config.incremental && ctx.config.doGC) { warn("ignoring '/incremental' because REF is enabled; use '/opt:noref' to " "disable"); - config->incremental = false; + ctx.config.incremental = false; } - if (config->incremental && config->doICF != ICFLevel::None) { + if (ctx.config.incremental && ctx.config.doICF != ICFLevel::None) { warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to " "disable"); - config->incremental = false; + ctx.config.incremental = false; } if (errorCount()) @@ -1850,21 +1854,22 @@ // We should have inferred a machine type by now from the input files, but if // not we assume x64. - if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { + if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) { warn("/machine is not specified. x64 is assumed"); - config->machine = AMD64; + ctx.config.machine = AMD64; } - config->wordsize = config->is64() ? 8 : 4; + ctx.config.wordsize = ctx.config.is64() ? 8 : 4; // Handle /safeseh, x86 only, on by default, except for mingw. - if (config->machine == I386) { - config->safeSEH = args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw); - config->noSEH = args.hasArg(OPT_noseh); + if (ctx.config.machine == I386) { + ctx.config.safeSEH = + args.hasFlag(OPT_safeseh, OPT_safeseh_no, !ctx.config.mingw); + ctx.config.noSEH = args.hasArg(OPT_noseh); } // Handle /functionpadmin for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) - parseFunctionPadMin(arg, config->machine); + parseFunctionPadMin(arg); if (tar) tar->append("response.txt", @@ -1872,29 +1877,29 @@ ArrayRef(searchPaths).slice(1))); // Handle /largeaddressaware - config->largeAddressAware = args.hasFlag( - OPT_largeaddressaware, OPT_largeaddressaware_no, config->is64()); + ctx.config.largeAddressAware = args.hasFlag( + OPT_largeaddressaware, OPT_largeaddressaware_no, ctx.config.is64()); // Handle /highentropyva - config->highEntropyVA = - config->is64() && + ctx.config.highEntropyVA = + ctx.config.is64() && args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true); - if (!config->dynamicBase && - (config->machine == ARMNT || config->machine == ARM64)) + if (!ctx.config.dynamicBase && + (ctx.config.machine == ARMNT || ctx.config.machine == ARM64)) error("/dynamicbase:no is not compatible with " + - machineToStr(config->machine)); + machineToStr(ctx.config.machine)); // Handle /export for (auto *arg : args.filtered(OPT_export)) { Export e = parseExport(arg->getValue()); - if (config->machine == I386) { + if (ctx.config.machine == I386) { if (!isDecorated(e.name)) e.name = saver().save("_" + e.name); if (!e.extName.empty() && !isDecorated(e.extName)) e.extName = saver().save("_" + e.extName); } - config->exports.push_back(e); + ctx.config.exports.push_back(e); } // Handle /def @@ -1913,85 +1918,87 @@ // Windows specific -- if no /subsystem is given, we need to infer // that from entry point name. Must happen before /entry handling, // and after the early return when just writing an import library. - if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { - config->subsystem = inferSubsystem(); - if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) + if (ctx.config.subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { + ctx.config.subsystem = inferSubsystem(); + if (ctx.config.subsystem == IMAGE_SUBSYSTEM_UNKNOWN) fatal("subsystem must be defined"); } // Handle /entry and /dll if (auto *arg = args.getLastArg(OPT_entry)) { - config->entry = addUndefined(mangle(arg->getValue())); - } else if (!config->entry && !config->noEntry) { + ctx.config.entry = addUndefined(mangle(arg->getValue())); + } else if (!ctx.config.entry && !ctx.config.noEntry) { if (args.hasArg(OPT_dll)) { - StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12" - : "_DllMainCRTStartup"; - config->entry = addUndefined(s); - } else if (config->driverWdm) { + StringRef s = (ctx.config.machine == I386) ? "__DllMainCRTStartup@12" + : "_DllMainCRTStartup"; + ctx.config.entry = addUndefined(s); + } else if (ctx.config.driverWdm) { // /driver:wdm implies /entry:_NtProcessStartup - config->entry = addUndefined(mangle("_NtProcessStartup")); + ctx.config.entry = addUndefined(mangle("_NtProcessStartup")); } else { // Windows specific -- If entry point name is not given, we need to // infer that from user-defined entry name. StringRef s = findDefaultEntry(); if (s.empty()) fatal("entry point must be defined"); - config->entry = addUndefined(s); + ctx.config.entry = addUndefined(s); log("Entry name inferred: " + s); } } // Handle /delayload for (auto *arg : args.filtered(OPT_delayload)) { - config->delayLoads.insert(StringRef(arg->getValue()).lower()); - if (config->machine == I386) { - config->delayLoadHelper = addUndefined("___delayLoadHelper2@8"); + ctx.config.delayLoads.insert(StringRef(arg->getValue()).lower()); + if (ctx.config.machine == I386) { + ctx.config.delayLoadHelper = addUndefined("___delayLoadHelper2@8"); } else { - config->delayLoadHelper = addUndefined("__delayLoadHelper2"); + ctx.config.delayLoadHelper = addUndefined("__delayLoadHelper2"); } } // 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()); + if (ctx.config.outputFile.empty()) { + ctx.config.outputFile = getOutputPath( + (*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue(), + ctx.config.dll, ctx.config.driver); } // Fail early if an output file is not writable. - if (auto e = tryCreateFile(config->outputFile)) { - error("cannot open output file " + config->outputFile + ": " + e.message()); + if (auto e = tryCreateFile(ctx.config.outputFile)) { + error("cannot open output file " + ctx.config.outputFile + ": " + + e.message()); return; } if (shouldCreatePDB) { // Put the PDB next to the image if no /pdb flag was passed. - if (config->pdbPath.empty()) { - config->pdbPath = config->outputFile; - sys::path::replace_extension(config->pdbPath, ".pdb"); + if (ctx.config.pdbPath.empty()) { + ctx.config.pdbPath = ctx.config.outputFile; + sys::path::replace_extension(ctx.config.pdbPath, ".pdb"); } // The embedded PDB path should be the absolute path to the PDB if no // /pdbaltpath flag was passed. - if (config->pdbAltPath.empty()) { - config->pdbAltPath = config->pdbPath; + if (ctx.config.pdbAltPath.empty()) { + ctx.config.pdbAltPath = ctx.config.pdbPath; // It's important to make the path absolute and remove dots. This path // will eventually be written into the PE header, and certain Microsoft // tools won't work correctly if these assumptions are not held. - sys::fs::make_absolute(config->pdbAltPath); - sys::path::remove_dots(config->pdbAltPath); + sys::fs::make_absolute(ctx.config.pdbAltPath); + sys::path::remove_dots(ctx.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(); } } // Set default image base if /base is not given. - if (config->imageBase == uint64_t(-1)) - config->imageBase = getDefaultImageBase(); + if (ctx.config.imageBase == uint64_t(-1)) + ctx.config.imageBase = getDefaultImageBase(); ctx.symtab.addSynthetic(mangle("__ImageBase"), nullptr); - if (config->machine == I386) { + if (ctx.config.machine == I386) { ctx.symtab.addAbsolute("___safe_se_handler_table", 0); ctx.symtab.addAbsolute("___safe_se_handler_count", 0); } @@ -2009,11 +2016,11 @@ ctx.symtab.addAbsolute(mangle("__guard_eh_cont_count"), 0); ctx.symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0); - if (config->pseudoRelocs) { + if (ctx.config.pseudoRelocs) { ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0); } - if (config->mingw) { + if (ctx.config.mingw) { ctx.symtab.addAbsolute(mangle("__CTOR_LIST__"), 0); ctx.symtab.addAbsolute(mangle("__DTOR_LIST__"), 0); } @@ -2024,11 +2031,11 @@ do { // Windows specific -- if entry point is not found, // search for its mangled names. - if (config->entry) - mangleMaybe(config->entry); + if (ctx.config.entry) + mangleMaybe(ctx.config.entry); // Windows specific -- Make sure we resolve all dllexported symbols. - for (Export &e : config->exports) { + for (Export &e : ctx.config.exports) { if (!e.forwardTo.empty()) continue; e.sym = addUndefined(e.name); @@ -2038,7 +2045,7 @@ // Add weak aliases. Weak aliases is a mechanism to give remaining // undefined symbols final chance to be resolved successfully. - for (auto pair : config->alternateNames) { + for (auto pair : ctx.config.alternateNames) { StringRef from = pair.first; StringRef to = pair.second; Symbol *sym = ctx.symtab.find(from); @@ -2077,7 +2084,7 @@ if (!wrapped.empty()) while (run()); - if (config->autoImport || config->stdcallFixup) { + if (ctx.config.autoImport || ctx.config.stdcallFixup) { // MinGW specific. // Load any further object files that might be needed for doing automatic // imports, and do stdcall fixups. @@ -2106,13 +2113,13 @@ // If we are going to do codegen for link-time optimization, check for // unresolvable symbols first, so we don't spend time generating code that // will fail to link anyway. - if (!ctx.bitcodeFileInstances.empty() && !config->forceUnresolved) + if (!ctx.bitcodeFileInstances.empty() && !ctx.config.forceUnresolved) ctx.symtab.reportUnresolvable(); if (errorCount()) return; - config->hadExplicitExports = !config->exports.empty(); - if (config->mingw) { + ctx.config.hadExplicitExports = !ctx.config.exports.empty(); + if (ctx.config.mingw) { // In MinGW, all symbols are automatically exported if no symbols // are chosen to be exported. maybeExportMinGWSymbols(args); @@ -2125,8 +2132,8 @@ // 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. - if (config->thinLTOIndexOnly) + // in addCombinedLTOObject, so we are done if that's the case. + if (ctx.config.thinLTOIndexOnly) return; // If we generated native object files from bitcode files, this resolves @@ -2142,7 +2149,7 @@ if (errorCount()) return; - if (config->mingw) { + if (ctx.config.mingw) { // Make sure the crtend.o object is the last object file. This object // file can contain terminating section chunks that need to be placed // last. GNU ld processes files and static libraries explicitly in the @@ -2163,19 +2170,19 @@ // Windows specific -- when we are creating a .dll file, we also // need to create a .lib file. In MinGW mode, we only do that when the // -implib option is given explicitly, for compatibility with GNU ld. - if (!config->exports.empty() || config->dll) { + if (!ctx.config.exports.empty() || ctx.config.dll) { fixupExports(); - if (!config->mingw || !config->implib.empty()) + if (!ctx.config.mingw || !ctx.config.implib.empty()) createImportLibrary(/*asLib=*/false); assignExportOrdinals(); } // Handle /output-def (MinGW specific). if (auto *arg = args.getLastArg(OPT_output_def)) - writeDefFile(arg->getValue()); + writeDefFile(arg->getValue(), ctx.config.exports); // Set extra alignment for .comm symbols - for (auto pair : config->alignComm) { + for (auto pair : ctx.config.alignComm) { StringRef name = pair.first; uint32_t alignment = pair.second; @@ -2198,11 +2205,11 @@ // Windows specific -- Create an embedded or side-by-side manifest. // /manifestdependency: enables /manifest unless an explicit /manifest:no is // also passed. - if (config->manifest == Configuration::Embed) + if (ctx.config.manifest == Configuration::Embed) addBuffer(createManifestRes(), false, false); - else if (config->manifest == Configuration::SideBySide || - (config->manifest == Configuration::Default && - !config->manifestDependencies.empty())) + else if (ctx.config.manifest == Configuration::SideBySide || + (ctx.config.manifest == Configuration::Default && + !ctx.config.manifestDependencies.empty())) createSideBySideManifest(); // Handle /order. We want to do this at this moment because we @@ -2211,25 +2218,25 @@ 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()); - config->callGraphProfileSort = false; + parseOrderFile(arg->getValue()); + ctx.config.callGraphProfileSort = false; } // Handle /call-graph-ordering-file and /call-graph-profile-sort (default on). - if (config->callGraphProfileSort) { + if (ctx.config.callGraphProfileSort) { if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) { - parseCallGraphFile(ctx, arg->getValue()); + parseCallGraphFile(arg->getValue()); } readCallGraphsFromObjectFiles(ctx); } // Handle /print-symbol-order. if (auto *arg = args.getLastArg(OPT_print_symbol_order)) - config->printSymbolOrder = arg->getValue(); + ctx.config.printSymbolOrder = arg->getValue(); // Identify unreferenced COMDAT sections. - if (config->doGC) { - if (config->mingw) { + if (ctx.config.doGC) { + if (ctx.config.mingw) { // markLive doesn't traverse .eh_frame, but the personality function is // only reached that way. The proper solution would be to parse and // traverse the .eh_frame section, like the ELF linker does. @@ -2240,7 +2247,7 @@ Defined *d = dyn_cast_or_null(ctx.symtab.findUnderscore(n)); if (d && !d->isGCRoot) { d->isGCRoot = true; - config->gcroot.push_back(d); + ctx.config.gcroot.push_back(d); } } } @@ -2252,9 +2259,9 @@ convertResources(); // Identify identical COMDAT sections to merge them. - if (config->doICF != ICFLevel::None) { + if (ctx.config.doICF != ICFLevel::None) { findKeepUniqueSections(ctx); - doICF(ctx, config->doICF); + doICF(ctx); } // Write the result. @@ -2262,7 +2269,7 @@ // Stop early so we can print the results. rootTimer.stop(); - if (config->showTiming) + if (ctx.config.showTiming) ctx.rootTimer.print(); } 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" @@ -74,7 +74,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) { StringRef s1, s2; std::tie(s1, s2) = arg.split(','); if (s1.getAsInteger(0, *addr)) @@ -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) { StringRef s1, s2; std::tie(s1, s2) = arg.split('.'); if (s1.getAsInteger(10, *major)) @@ -95,30 +96,31 @@ 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")) - config->guardCF = GuardCFLevel::CF; + ctx.config.guardCF = GuardCFLevel::CF; else if (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) { StringRef sysStr, ver; std::tie(sysStr, ver) = arg.split(','); std::string sysStrLower = sysStr.lower(); @@ -144,20 +146,20 @@ // Parse a string of the form of "=". // Results are directly written to Config. -void parseAlternateName(StringRef s) { +void LinkerDriver::parseAlternateName(StringRef s) { StringRef from, to; std::tie(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) { StringRef from, to; std::tie(from, to) = s.split('='); if (from.empty() || to.empty()) @@ -166,7 +168,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; @@ -175,7 +177,7 @@ } } -void parsePDBPageSize(StringRef s) { +void LinkerDriver::parsePDBPageSize(StringRef s) { int v; if (s.getAsInteger(0, v)) { error("/pdbpagesize: invalid argument: " + s); @@ -186,7 +188,7 @@ return; } - config->pdbPageSize = v; + ctx.config.pdbPageSize = v; } static uint32_t parseSectionAttributes(StringRef s) { @@ -222,16 +224,16 @@ } // Parses /section option argument. -void parseSection(StringRef s) { +void LinkerDriver::parseSection(StringRef s) { StringRef name, attrs; std::tie(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) { StringRef name, align; std::tie(name, align) = s.split(','); if (name.empty() || align.empty()) { @@ -243,56 +245,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 (;;) { @@ -301,12 +304,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); @@ -315,14 +318,14 @@ // 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 { StringRef swaprun, newArg; std::tie(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 @@ -380,7 +383,7 @@ }; } -static std::string createDefaultXml() { +std::string LinkerDriver::createDefaultXml() { std::string ret; raw_string_ostream os(ret); @@ -389,17 +392,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" @@ -410,7 +413,8 @@ return os.str(); } -static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { +std::string +LinkerDriver::createManifestXmlWithInternalMt(StringRef defaultXml) { std::unique_ptr defaultXmlCopy = MemoryBuffer::getMemBufferCopy(defaultXml); @@ -419,11 +423,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))); } @@ -431,7 +435,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; @@ -448,14 +453,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)); @@ -467,9 +472,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()) @@ -478,14 +483,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"); } @@ -496,7 +501,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; @@ -508,7 +514,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. @@ -522,7 +528,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 = @@ -530,17 +536,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) @@ -552,7 +558,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(","); @@ -615,14 +621,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; } @@ -649,26 +655,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); @@ -677,9 +683,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) { @@ -691,21 +697,21 @@ continue; warn("duplicate /export option: " + e.name); } - config->exports = std::move(v); + ctx.config.exports = std::move(v); // Sort by name. - std::sort(config->exports.begin(), config->exports.end(), + std::sort(ctx.config.exports.begin(), ctx.config.exports.end(), [](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()) @@ -715,12 +721,12 @@ // 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) { StringRef k, v; std::tie(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 = @@ -729,14 +735,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) { @@ -761,18 +767,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())); @@ -799,8 +805,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) { @@ -836,6 +840,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. @@ -846,7 +852,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. @@ -855,8 +862,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()) { @@ -868,7 +875,7 @@ // Save the command line after response file expansion so we can write it to // the PDB if necessary. - config->argv = {expandedArgv.begin(), expandedArgv.end()}; + ctx.config.argv = {expandedArgv.begin(), expandedArgv.end()}; // Handle /WX early since it converts missing argument warnings to errors. errorHandler().fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false); @@ -880,7 +887,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) + @@ -922,7 +929,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"); @@ -952,10 +959,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 @@ -9,17 +9,12 @@ #ifndef LLD_COFF_ICF_H #define LLD_COFF_ICF_H -#include "Config.h" -#include "lld/Common/LLVM.h" -#include "llvm/ADT/ArrayRef.h" - namespace lld { namespace coff { -class Chunk; class COFFLinkerContext; -void doICF(COFFLinkerContext &ctx, ICFLevel); +void doICF(COFFLinkerContext &ctx); } // namespace coff } // namespace lld diff --git a/lld/COFF/ICF.cpp b/lld/COFF/ICF.cpp --- a/lld/COFF/ICF.cpp +++ b/lld/COFF/ICF.cpp @@ -39,7 +39,7 @@ class ICF { public: - ICF(COFFLinkerContext &c, ICFLevel icfLevel) : icfLevel(icfLevel), ctx(c){}; + ICF(COFFLinkerContext &c) : ctx(c){}; void run(); private: @@ -62,7 +62,6 @@ std::vector chunks; int cnt = 0; std::atomic repeat = {false}; - ICFLevel icfLevel = ICFLevel::All; COFFLinkerContext &ctx; }; @@ -85,7 +84,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; @@ -318,9 +317,7 @@ } // Entry point to ICF. -void doICF(COFFLinkerContext &ctx, ICFLevel icfLevel) { - ICF(ctx, icfLevel).run(); -} +void doICF(COFFLinkerContext &ctx) { ICF(ctx).run(); } } // namespace coff } // namespace lld 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 @@ -71,7 +71,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) { @@ -80,9 +80,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; } @@ -108,13 +108,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 " + ctx.toCOFFString(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) { @@ -236,7 +236,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) @@ -259,7 +259,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 @@ -365,7 +365,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); } @@ -399,7 +399,7 @@ } else if (Optional optSym = createDefined(coffSym, comdatDefs, prevailingComdat)) { symbols[i] = *optSym; - if (config->mingw && prevailingComdat) + if (ctx.config.mingw && prevailingComdat) recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap); } else { // createDefined() returns None if a symbol belongs to a section that @@ -420,7 +420,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) { @@ -435,7 +435,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. @@ -495,10 +495,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; } @@ -510,7 +510,7 @@ // seems better though. // (This behavior matches ModuleLinker::getComdatResult().) if (selection != leaderSelection) { - log(("conflicting comdat type for " + toString(*leader) + ": " + + log(("conflicting comdat type for " + ctx.toString(*leader) + ": " + Twine((int)leaderSelection) + " in " + toString(leader->getFile()) + " and " + Twine((int)selection) + " in " + toString(this)) .str()); @@ -529,7 +529,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; @@ -606,7 +606,7 @@ if (sym.isExternal()) return ctx.symtab.addAbsolute(name, sym); - return make(name, sym); + return make(ctx, name, sym); } int32_t sectionNumber = sym.getSectionNumber(); @@ -749,7 +749,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; @@ -898,7 +898,7 @@ if (!dwarf) return None; } - if (config->machine == I386) + if (ctx.config.machine == I386) var.consume_front("_"); Optional> ret = dwarf->getVariableLoc(var); if (!ret) @@ -926,9 +926,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); @@ -984,8 +987,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 @@ -1005,36 +1010,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 @@ -1046,9 +1024,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()) { @@ -1058,7 +1036,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; @@ -1074,7 +1052,7 @@ } symbols.push_back(sym); if (objSym.isUsed()) - config->gcroot.push_back(sym); + ctx.config.gcroot.push_back(sym); } directives = obj->getCOFFLinkerOpts(); } @@ -1100,10 +1078,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()); parallelForEachN((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 << ctx.toString(*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 @@ -29,6 +29,7 @@ namespace llvm { namespace lto { +struct Config; class LTO; } } @@ -42,11 +43,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; @@ -54,6 +55,11 @@ std::vector> files; 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,13 +54,13 @@ 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; @@ -72,49 +73,49 @@ // 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_; c.DisableVerify = true; 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.UseNewPM = config->ltoNewPassManager; - 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.UseNewPM = ctx.config.ltoNewPassManager; + 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; @@ -127,7 +128,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. @@ -156,7 +157,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); @@ -165,9 +166,9 @@ // 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()) + if (!ctx.config.ltoCache.empty()) cache = - check(localCache("ThinLTO", "Thin", config->ltoCache, + check(localCache("ThinLTO", "Thin", ctx.config.ltoCache, [&](size_t task, std::unique_ptr mb) { files[task] = std::move(mb); })); @@ -183,23 +184,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], config->ltoObjPath); + if (ctx.config.thinLTOIndexOnly) { + if (!ctx.config.ltoObjPath.empty()) + saveBuffer(buf[0], ctx.config.ltoObjPath); if (indexFile) indexFile->close(); return {}; } - if (!config->ltoCache.empty()) - pruneCache(config->ltoCache, config->ltoCachePolicy); + if (!ctx.config.ltoCache.empty()) + pruneCache(ctx.config.ltoCache, ctx.config.ltoCachePolicy); std::vector ret; for (unsigned i = 0; i != maxTasks; ++i) { @@ -209,7 +210,7 @@ // - foo.exe.lto.1.obj // - ... StringRef ltoObjName = - saver().save(Twine(config->outputFile) + ".lto" + + saver().save(Twine(ctx.config.outputFile) + ".lto" + (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj"); // Get the native object contents either from the cache or from memory. Do @@ -223,7 +224,7 @@ if (objBuf.empty()) continue; - if (config->saveTemps) + if (ctx.config.saveTemps) saveBuffer(buf[i], 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(); diff --git a/lld/COFF/MarkLive.cpp b/lld/COFF/MarkLive.cpp --- a/lld/COFF/MarkLive.cpp +++ b/lld/COFF/MarkLive.cpp @@ -52,7 +52,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,7 @@ // symbols for MinGW. class AutoExporter { public: - AutoExporter(); + AutoExporter(COFFLinkerContext &ctx); void addWholeArchive(StringRef path); @@ -35,10 +35,13 @@ llvm::StringSet<> excludeLibs; llvm::StringSet<> excludeObjects; - 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 @@ -23,7 +23,7 @@ using namespace lld; using namespace lld::coff; -AutoExporter::AutoExporter() { +AutoExporter::AutoExporter(COFFLinkerContext &c) : ctx(c) { excludeLibs = { "libgcc", "libgcc_s", @@ -78,7 +78,7 @@ "_NULL_THUNK_DATA", }; - if (config->machine == I386) { + if (ctx.config.machine == I386) { excludeSymbols = { "__NULL_IMPORT_DESCRIPTOR", "__pei386_runtime_relocator", @@ -122,8 +122,7 @@ excludeLibs.erase(libName); } -bool AutoExporter::shouldExport(const COFFLinkerContext &ctx, - Defined *sym) const { +bool AutoExporter::shouldExport(Defined *sym) const { if (!sym || !sym->getChunk()) return false; @@ -161,14 +160,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)) { @@ -180,9 +180,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); } @@ -206,8 +206,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 @@ -248,7 +250,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 @@ -143,6 +143,11 @@ void printStats(); private: + void pdbMakeAbsolute(SmallVectorImpl &fileName); + void translateIdSymbols(MutableArrayRef &recordData, + TpiSource *source); + void addCommonLinkerModuleSymbols(StringRef path, + pdb::DbiModuleDescriptorBuilder &mod); pdb::PDBFileBuilder builder; @@ -238,7 +243,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 @@ -253,7 +258,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); @@ -264,7 +269,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; @@ -335,8 +340,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); @@ -367,7 +372,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; @@ -572,7 +577,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( @@ -977,7 +982,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); } @@ -988,8 +993,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; } @@ -1086,11 +1091,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; } } @@ -1098,7 +1104,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; } @@ -1138,7 +1145,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. @@ -1151,8 +1158,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()); @@ -1160,7 +1168,7 @@ } t2.stop(); - if (config->showSummary) { + if (ctx.config.showSummary) { for_each(ctx.tpiSourceList, [&](TpiSource *source) { nbTypeRecords += source->nbTypeRecords; nbTypeRecordsBytes += source->nbTypeRecordsBytes; @@ -1187,7 +1195,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_")) { @@ -1205,7 +1213,7 @@ } void PDBLinker::printStats() { - if (!config->showSummary) + if (!ctx.config.showSummary) return; SmallString<256> buffer; @@ -1273,11 +1281,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()); @@ -1287,7 +1295,7 @@ } void PDBLinker::addNatvisFiles() { - for (StringRef file : config->natvisFiles) { + for (StringRef file : ctx.config.natvisFiles) { ErrorOr> dataOrErr = MemoryBuffer::getFile(file); if (!dataOrErr) { @@ -1297,16 +1305,16 @@ 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) { + for (const auto &streamFile : ctx.config.namedStreams) { const StringRef stream = streamFile.getKey(), file = streamFile.getValue(); ErrorOr> dataOrErr = MemoryBuffer::getFile(file); @@ -1316,7 +1324,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)); } } @@ -1363,8 +1371,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 @@ -1389,27 +1397,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"); @@ -1452,7 +1460,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; @@ -1465,7 +1473,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. @@ -1525,7 +1533,7 @@ ons.Name = file->dllName; ons.Signature = 0; - fillLinkerVerRecord(cs); + fillLinkerVerRecord(cs, ctx.config.machine); ts.Name = thunk->getName(); ts.Parent = 0; @@ -1589,7 +1597,7 @@ } void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) { - exitOnErr(builder.initialize(config->pdbPageSize)); + 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. @@ -1610,7 +1618,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 @@ -1621,7 +1629,7 @@ void PDBLinker::addSections(ArrayRef sectionTable) { // It's not entirely clear what this is, but the * Linker * module uses it. pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); - nativePath = config->pdbPath; + nativePath = ctx.config.pdbPath; pdbMakeAbsolute(nativePath); uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath); auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *")); @@ -1630,7 +1638,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()); @@ -1660,14 +1668,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: @@ -1692,7 +1700,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 @@ -48,7 +48,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 @@ -54,19 +54,19 @@ } MachineTypes mt = file->getMachineType(); - if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { - config->machine = mt; - } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) { + if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) { + ctx.config.machine = mt; + } 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 @@ 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 << ":(" << file->ctx.toString(*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: " << ctx.toString(*undefDiag.sym); const size_t maxUndefReferences = 3; size_t numDisplayedRefs = 0, numRefs = 0; @@ -257,7 +258,7 @@ } if (numDisplayedRefs < numRefs) os << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times"; - errorOrWarn(os.str()); + errorOrWarn(os.str(), ctx.config.forceUnresolved); } void SymbolTable::loadMinGWSymbols() { @@ -271,7 +272,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). @@ -292,7 +293,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); @@ -302,7 +303,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 @@ -360,7 +361,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()); @@ -385,12 +386,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: " + ctx.toString(*b), + ctx.config.forceUnresolved); if (localImports) if (Symbol *imp = localImports->lookup(b)) - warn(": locally defined symbol imported: " + toString(*imp) + + warn(": locally defined symbol imported: " + ctx.toString(*imp) + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } @@ -415,7 +417,7 @@ if (localImports) if (Symbol *imp = localImports->lookup(sym)) warn(toString(file) + - ": locally defined symbol imported: " + toString(*imp) + + ": locally defined symbol imported: " + ctx.toString(*imp) + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } }; @@ -428,7 +430,7 @@ processFile(file, file->getSymbols()); for (const UndefinedDiag &undefDiag : undefDiags) - reportUndefinedSymbol(undefDiag); + reportUndefinedSymbol(ctx, undefDiag); } void SymbolTable::reportUnresolvable() { @@ -448,7 +450,7 @@ } if (name.contains("_PchSym_")) continue; - if (config->autoImport && impSymbol(name)) + if (ctx.config.autoImport && impSymbol(name)) continue; undefs.insert(sym); } @@ -493,7 +495,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; @@ -505,19 +507,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) { @@ -652,7 +654,7 @@ uint32_t newSectionOffset) { std::string msg; llvm::raw_string_ostream os(msg); - os << "duplicate symbol: " << toString(*existing); + os << "duplicate symbol: " << ctx.toString(*existing); DefinedRegular *d = dyn_cast(existing); if (d && isa(d->getFile())) { @@ -664,7 +666,7 @@ os << getSourceLocation(newFile, newSc, newSectionOffset, existing->getName()); - if (config->forceMultiple) + if (ctx.config.forceMultiple) warn(os.str()); else error(os.str()); @@ -676,7 +678,7 @@ std::tie(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); @@ -691,7 +693,7 @@ std::tie(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); @@ -777,7 +779,7 @@ std::tie(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; } @@ -814,7 +816,7 @@ } Symbol *SymbolTable::findUnderscore(StringRef name) const { - if (config->machine == I386) + if (ctx.config.machine == I386) return find(("_" + name).str()); return find(name); } @@ -853,7 +855,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("_")) @@ -880,10 +882,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; @@ -240,29 +234,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 @@ -383,7 +373,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; @@ -405,8 +396,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; 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" @@ -26,30 +27,6 @@ static_assert(sizeof(SymbolUnion) <= 48, "symbols should be optimized for memory usage"); -// Returns a symbol name for an error message. -static std::string maybeDemangleSymbol(StringRef symName) { - if (config->demangle) { - std::string prefix; - StringRef prefixless = symName; - if (prefixless.consume_front("__imp_")) - prefix = "__declspec(dllimport) "; - StringRef demangleInput = prefixless; - if (config->machine == I386) - demangleInput.consume_front("_"); - std::string demangled = demangle(demangleInput, true); - if (demangled != demangleInput) - return prefix + demangle(demangleInput, true); - return (prefix + prefixless).str(); - } - return std::string(symName); -} -std::string toString(coff::Symbol &b) { - return maybeDemangleSymbol(b.getName()); -} -std::string toCOFFString(const Archive::Symbol &b) { - return maybeDemangleSymbol(b.getName()); -} - namespace coff { void Symbol::computeName() { @@ -102,23 +79,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 +108,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 " + + file->ctx.toCOFFString(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 " + + file->ctx.toCOFFString(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 @@ -33,13 +33,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 @@ -46,9 +46,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, @@ -233,6 +245,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(); @@ -327,14 +340,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. sprintf(hdr->Name, "/%d", 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)); @@ -347,8 +360,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: @@ -359,7 +372,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: @@ -378,19 +391,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"); @@ -411,7 +424,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; @@ -510,7 +523,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) @@ -538,7 +551,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; @@ -620,8 +633,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(); @@ -636,7 +649,7 @@ t1.stop(); - if (!config->pdbPath.empty() && config->debug) { + if (!ctx.config.pdbPath.empty() && ctx.config.debug) { assert(buildId); createPDB(ctx, sectionTable, buildId->buildId); } @@ -662,11 +675,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; }; @@ -745,7 +758,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. @@ -788,7 +801,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 @@ -798,7 +812,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; @@ -808,15 +822,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); } @@ -861,12 +875,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")) @@ -956,24 +970,24 @@ } // Create Debug Information Chunks - OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec; - if (config->debug || config->repro || config->cetCompat) { + OutputSection *debugInfoSec = ctx.config.mingw ? buildidSec : rdataSec; + if (ctx.config.debug || ctx.config.repro || ctx.config.cetCompat) { debugDirectory = - make(ctx, debugRecords, config->repro); + make(ctx, debugRecords, ctx.config.repro); debugDirectory->setAlignment(4); debugInfoSec->addChunk(debugDirectory); } - if (config->debug) { + if (ctx.config.debug) { // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We // 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}); } - if (config->cetCompat) { + if (ctx.config.cetCompat) { debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, make( IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT)}); @@ -986,17 +1000,17 @@ } // Create SEH table. x86-only. - if (config->safeSEH) + if (ctx.config.safeSEH) createSEHTable(); // Create /guard:cf tables if requested. - if (config->guardCF != GuardCFLevel::Off) + if (ctx.config.guardCF != GuardCFLevel::Off) createGuardCFTables(); - if (config->autoImport) + if (ctx.config.autoImport) createRuntimePseudoRelocs(); - if (config->mingw) + if (ctx.config.mingw) insertCtorDtorSymbols(); } @@ -1013,16 +1027,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(ctx.toString(*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: " + ctx.toString(*impSym)); delayIdata.add(impSym); } else { idata.add(impSym); @@ -1042,15 +1056,15 @@ continue; if (!isa(file->thunkSym)) - fatal(toString(*file->thunkSym) + " was replaced"); + fatal(ctx.toString(*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()) @@ -1064,9 +1078,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); } @@ -1075,9 +1089,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: " + ctx.toString(*e.sym)); } void Writer::removeUnusedSections() { @@ -1196,7 +1210,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"); @@ -1204,7 +1218,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); @@ -1232,7 +1246,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() { @@ -1241,7 +1255,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; @@ -1249,8 +1263,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; } @@ -1273,12 +1287,12 @@ sizeof(data_directory) * numberOfDataDirectory + sizeof(coff_section) * ctx.outputSections.size(); sizeOfHeaders += - config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); - sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign); + ctx.config.is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); + sizeOfHeaders = alignTo(sizeOfHeaders, ctx.config.fileAlign); fileSize = sizeOfHeaders; // The first page is kept unmapped. - uint64_t rva = alignTo(sizeOfHeaders, config->align); + uint64_t rva = alignTo(sizeOfHeaders, ctx.config.align); for (OutputSection *sec : ctx.outputSections) { if (sec == relocSec) @@ -1292,7 +1306,7 @@ (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) && (sec->header.Characteristics & IMAGE_SCN_MEM_READ) && (sec->header.Characteristics & IMAGE_SCN_MEM_EXECUTE); - uint32_t padding = isCodeSection ? config->functionPadMin : 0; + uint32_t padding = isCodeSection ? ctx.config.functionPadMin : 0; for (Chunk *c : sec->chunks) { if (padding && c->isHotPatchable()) @@ -1301,7 +1315,7 @@ c->setRVA(rva + virtualSize); virtualSize += c->getSize(); if (c->hasData) - rawSize = alignTo(virtualSize, config->fileAlign); + rawSize = alignTo(virtualSize, ctx.config.fileAlign); } if (virtualSize > UINT32_MAX) error("section larger than 4 GiB: " + sec->name); @@ -1309,10 +1323,10 @@ sec->header.SizeOfRawData = rawSize; if (rawSize != 0) sec->header.PointerToRawData = fileSize; - rva += alignTo(virtualSize, config->align); - fileSize += alignTo(rawSize, config->fileAlign); + rva += alignTo(virtualSize, ctx.config.align); + fileSize += alignTo(rawSize, ctx.config.fileAlign); } - sizeOfImage = alignTo(rva, config->align); + sizeOfImage = alignTo(rva, ctx.config.align); // Assign addresses to sections in MergeChunks. for (MergeChunk *mc : ctx.mergeChunkInstances) @@ -1349,22 +1363,22 @@ // Write COFF header auto *coff = reinterpret_cast(buf); buf += sizeof(*coff); - coff->Machine = config->machine; + coff->Machine = ctx.config.machine; coff->NumberOfSections = ctx.outputSections.size(); coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; - if (config->largeAddressAware) + if (ctx.config.largeAddressAware) coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; - if (!config->is64()) + if (!ctx.config.is64()) coff->Characteristics |= IMAGE_FILE_32BIT_MACHINE; - if (config->dll) + if (ctx.config.dll) coff->Characteristics |= IMAGE_FILE_DLL; - if (config->driverUponly) + if (ctx.config.driverUponly) coff->Characteristics |= IMAGE_FILE_UP_SYSTEM_ONLY; - if (!config->relocatable) + if (!ctx.config.relocatable) coff->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; - if (config->swaprunCD) + if (ctx.config.swaprunCD) coff->Characteristics |= IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP; - if (config->swaprunNet) + if (ctx.config.swaprunNet) coff->Characteristics |= IMAGE_FILE_NET_RUN_FROM_SWAP; coff->SizeOfOptionalHeader = sizeof(PEHeaderTy) + sizeof(data_directory) * numberOfDataDirectory; @@ -1372,7 +1386,7 @@ // Write PE header auto *pe = reinterpret_cast(buf); buf += sizeof(*pe); - pe->Magic = config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; + pe->Magic = ctx.config.is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; // If {Major,Minor}LinkerVersion is left at 0.0, then for some // reason signing the resulting PE file with Authenticode produces a @@ -1382,50 +1396,50 @@ pe->MajorLinkerVersion = 14; pe->MinorLinkerVersion = 0; - pe->ImageBase = config->imageBase; - pe->SectionAlignment = config->align; - pe->FileAlignment = config->fileAlign; - pe->MajorImageVersion = config->majorImageVersion; - pe->MinorImageVersion = config->minorImageVersion; - pe->MajorOperatingSystemVersion = config->majorOSVersion; - pe->MinorOperatingSystemVersion = config->minorOSVersion; - pe->MajorSubsystemVersion = config->majorSubsystemVersion; - pe->MinorSubsystemVersion = config->minorSubsystemVersion; - pe->Subsystem = config->subsystem; + pe->ImageBase = ctx.config.imageBase; + pe->SectionAlignment = ctx.config.align; + pe->FileAlignment = ctx.config.fileAlign; + pe->MajorImageVersion = ctx.config.majorImageVersion; + pe->MinorImageVersion = ctx.config.minorImageVersion; + pe->MajorOperatingSystemVersion = ctx.config.majorOSVersion; + pe->MinorOperatingSystemVersion = ctx.config.minorOSVersion; + pe->MajorSubsystemVersion = ctx.config.majorSubsystemVersion; + pe->MinorSubsystemVersion = ctx.config.minorSubsystemVersion; + pe->Subsystem = ctx.config.subsystem; pe->SizeOfImage = sizeOfImage; pe->SizeOfHeaders = sizeOfHeaders; - if (!config->noEntry) { - Defined *entry = cast(config->entry); + if (!ctx.config.noEntry) { + Defined *entry = cast(ctx.config.entry); pe->AddressOfEntryPoint = entry->getRVA(); // Pointer to thumb code must have the LSB set, so adjust it. - if (config->machine == ARMNT) + if (ctx.config.machine == ARMNT) pe->AddressOfEntryPoint |= 1; } - pe->SizeOfStackReserve = config->stackReserve; - pe->SizeOfStackCommit = config->stackCommit; - pe->SizeOfHeapReserve = config->heapReserve; - pe->SizeOfHeapCommit = config->heapCommit; - if (config->appContainer) + pe->SizeOfStackReserve = ctx.config.stackReserve; + pe->SizeOfStackCommit = ctx.config.stackCommit; + pe->SizeOfHeapReserve = ctx.config.heapReserve; + pe->SizeOfHeapCommit = ctx.config.heapCommit; + if (ctx.config.appContainer) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER; - if (config->driverWdm) + if (ctx.config.driverWdm) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER; - if (config->dynamicBase) + if (ctx.config.dynamicBase) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; - if (config->highEntropyVA) + if (ctx.config.highEntropyVA) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA; - if (!config->allowBind) + if (!ctx.config.allowBind) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND; - if (config->nxCompat) + if (ctx.config.nxCompat) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; - if (!config->allowIsolation) + if (!ctx.config.allowIsolation) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; - if (config->guardCF != GuardCFLevel::Off) + if (ctx.config.guardCF != GuardCFLevel::Off) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; - if (config->integrityCheck) + if (ctx.config.integrityCheck) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; - if (setNoSEHCharacteristic || config->noSEH) + if (setNoSEHCharacteristic || ctx.config.noSEH) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; - if (config->terminalServerAware) + if (ctx.config.terminalServerAware) pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; pe->NumberOfRvaAndSize = numberOfDataDirectory; if (textSec->getVirtualSize()) { @@ -1466,7 +1480,7 @@ if (Symbol *sym = ctx.symtab.findUnderscore("_tls_used")) { if (Defined *b = dyn_cast(sym)) { dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA(); - dir[TLS_TABLE].Size = config->is64() + dir[TLS_TABLE].Size = ctx.config.is64() ? sizeof(object::coff_tls_directory64) : sizeof(object::coff_tls_directory32); } @@ -1500,7 +1514,7 @@ // Write section table for (OutputSection *sec : ctx.outputSections) { - sec->writeHeaderTo(buf); + sec->writeHeaderTo(buf, ctx.config.debug); buf += sizeof(coff_section); } sectionTable = ArrayRef( @@ -1609,8 +1623,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. @@ -1619,7 +1633,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; @@ -1658,11 +1673,11 @@ } // Mark the image entry as address-taken. - if (config->entry) - maybeAddAddressTakenFunction(addressTakenSyms, config->entry); + if (ctx.config.entry) + maybeAddAddressTakenFunction(addressTakenSyms, ctx.config.entry); // Mark exported symbols in executable sections as address-taken. - for (Export &e : config->exports) + for (Export &e : ctx.config.exports) maybeAddAddressTakenFunction(addressTakenSyms, e.sym); // For each entry in the .giats table, check if it has a corresponding load @@ -1688,12 +1703,12 @@ "__guard_iat_count"); // Add the longjmp target table unless the user told us not to. - if (config->guardCF & GuardCFLevel::LongJmp) + if (ctx.config.guardCF & GuardCFLevel::LongJmp) maybeAddRVATable(std::move(longJmpTargets), "__guard_longjmp_table", "__guard_longjmp_count"); // Add the ehcont target table unless the user told us not to. - if (config->guardCF & GuardCFLevel::EHCont) + if (ctx.config.guardCF & GuardCFLevel::EHCont) maybeAddRVATable(std::move(ehContTargets), "__guard_eh_cont_table", "__guard_eh_cont_count", true); @@ -1701,9 +1716,9 @@ // /guard:cf was enabled. uint32_t guardFlags = uint32_t(coff_guard_flags::CFInstrumented) | uint32_t(coff_guard_flags::HasFidTable); - if (config->guardCF & GuardCFLevel::LongJmp) + if (ctx.config.guardCF & GuardCFLevel::LongJmp) guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); - if (config->guardCF & GuardCFLevel::EHCont) + if (ctx.config.guardCF & GuardCFLevel::EHCont) guardFlags |= uint32_t(coff_guard_flags::HasEHContTable); Symbol *flagSym = ctx.symtab.findUnderscore("__guard_flags"); cast(flagSym)->setVA(guardFlags); @@ -1797,7 +1812,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) @@ -1826,10 +1841,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); @@ -1846,7 +1861,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) @@ -1857,10 +1872,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(); @@ -1882,7 +1893,7 @@ // 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. - if (config->debug) { + if (ctx.config.debug) { assert(buildId && "BuildId is not set!"); // BuildId->BuildId was filled in when the PDB was written. } @@ -1895,15 +1906,15 @@ reinterpret_cast(buffer->getBufferStart()), buffer->getBufferSize()); - uint32_t timestamp = config->timestamp; + uint32_t timestamp = ctx.config.timestamp; uint64_t hash = 0; bool generateSyntheticBuildId = - config->mingw && config->debug && config->pdbPath.empty(); + ctx.config.mingw && ctx.config.debug && ctx.config.pdbPath.empty(); - if (config->repro || generateSyntheticBuildId) + if (ctx.config.repro || generateSyntheticBuildId) hash = xxHash64(outputFileData); - if (config->repro) + if (ctx.config.repro) timestamp = static_cast(hash); if (generateSyntheticBuildId) { @@ -1938,7 +1949,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) + @@ -1949,7 +1960,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) + @@ -1990,7 +2001,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() + @@ -2016,7 +2027,7 @@ // Add base relocations to .reloc section. void Writer::addBaserels() { - if (!config->relocatable) + if (!ctx.config.relocatable) return; relocSec->chunks.clear(); std::vector v; @@ -2079,14 +2090,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);