//===----------------------------------------------------------------------===//
#include "COFFLinkerContext.h"
-#include "Symbols.h"
#include "lld/Common/Memory.h"
-#include "llvm/BinaryFormat/COFF.h"
#include "llvm/DebugInfo/CodeView/TypeHashing.h"
-#include "llvm/Demangle/Demangle.h"
namespace lld::coff {
+
COFFLinkerContext::COFFLinkerContext()
- : driver(*this), symtab(*this), rootTimer("Total Linking Time"),
+ : symtab(*this), rootTimer("Total Linking Time"),
inputFileTimer("Input File Reading", rootTimer),
ltoTimer("LTO", rootTimer), gcTimer("GC", rootTimer),
icfTimer("ICF", rootTimer), codeLayoutTimer("Code Layout", rootTimer),
symbolMergingTimer("Symbol Merging", addObjectsTimer),
publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer),
tpiStreamLayoutTimer("TPI Stream Layout", 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<FakeSectionChunk>(<oTextSection.section);
- ltoDataSectionChunk = make<FakeSectionChunk>(<oDataSection.section);
-}
+ diskCommitTimer("Commit to Disk", totalPdbLinkTimer) {}
+
} // namespace lld::coff
//
//===----------------------------------------------------------------------===//
-#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"
COFFLinkerContext &operator=(const COFFLinkerContext &) = delete;
~COFFLinkerContext() = default;
- LinkerDriver driver;
+ void addTpiSource(TpiSource *tpi) { tpiSourceList.push_back(tpi); }
+
SymbolTable symtab;
- COFFOptTable optTable;
std::vector<ObjFile *> objFileInstances;
std::map<std::string, PDBInputFile *> pdbInputFileInstances;
/// All sources of type information in the program.
std::vector<TpiSource *> tpiSourceList;
- void addTpiSource(TpiSource *tpi) { tpiSourceList.push_back(tpi); }
-
std::map<llvm::codeview::GUID, TpiSource *> typeServerSourceMappings;
std::map<uint32_t, TpiSource *> precompSourceMappings;
return c->osidx == 0 ? nullptr : outputSections[c->osidx - 1];
}
- // Fake sections for parsing bitcode files.
- FakeSectionChunk *ltoTextSectionChunk;
- FakeSectionChunk *ltoDataSectionChunk;
-
// All timers used in the COFF linker.
Timer rootTimer;
Timer inputFileTimer;
Timer publicsLayoutTimer;
Timer tpiStreamLayoutTimer;
Timer diskCommitTimer;
-
- Configuration config;
};
} // namespace lld::coff
private:
std::vector<Cluster> clusters;
std::vector<const SectionChunk *> sections;
-
- const COFFLinkerContext &ctx;
};
// Maximum amount the combined cluster density can be worse than the original
// 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) : ctx(ctx) {
- const MapVector<SectionPair, uint64_t> &profile = ctx.config.callGraphProfile;
+CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) {
+ MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
DenseMap<const SectionChunk *, int> secToCluster;
auto getOrCreateNode = [&](const SectionChunk *isec) -> int {
};
// Create the graph.
- for (const std::pair<SectionPair, uint64_t> &c : profile) {
+ for (std::pair<SectionPair, uint64_t> &c : profile) {
const auto *fromSec = cast<SectionChunk>(c.first.first->repl);
const auto *toSec = cast<SectionChunk>(c.first.second->repl);
uint64_t weight = c.second;
break;
}
}
- if (!ctx.config.printSymbolOrder.empty()) {
+ if (!config->printSymbolOrder.empty()) {
std::error_code ec;
- raw_fd_ostream os(ctx.config.printSymbolOrder, ec, sys::fs::OF_None);
+ raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None);
if (ec) {
- error("cannot open " + ctx.config.printSymbolOrder + ": " + ec.message());
+ error("cannot open " + config->printSymbolOrder + ": " + ec.message());
return orderMap;
}
// Print the symbols ordered by C3, in the order of increasing curOrder
// 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 (file)
- live = !file->ctx.config.doGC || !isCOMDAT();
+ if (config)
+ live = !config->doGC || !isCOMDAT();
else
live = true;
}
add32(off, secRel);
}
-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");
-
+static void applySecIdx(uint8_t *off, OutputSection *os) {
// 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, numOutputSections + 1);
+ add16(off, DefinedAbsolute::numOutputSections + 1);
}
void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
- uint64_t s, uint64_t p,
- uint64_t imageBase) const {
+ uint64_t s, uint64_t p) const {
switch (type) {
- case IMAGE_REL_AMD64_ADDR32:
- add32(off, s + imageBase);
- break;
- case IMAGE_REL_AMD64_ADDR64:
- add64(off, s + imageBase);
- break;
+ 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_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;
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, file->ctx.outputSections.size());
- break;
+ case IMAGE_REL_AMD64_SECTION: applySecIdx(off, os); break;
case IMAGE_REL_AMD64_SECREL: applySecRel(this, off, os, s); break;
default:
error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
}
void SectionChunk::applyRelX86(uint8_t *off, uint16_t type, OutputSection *os,
- uint64_t s, uint64_t p,
- uint64_t imageBase) const {
+ uint64_t s, uint64_t p) const {
switch (type) {
case IMAGE_REL_I386_ABSOLUTE: break;
- case IMAGE_REL_I386_DIR32:
- add32(off, s + imageBase);
- break;
+ case IMAGE_REL_I386_DIR32: add32(off, s + config->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, file->ctx.outputSections.size());
- break;
+ case IMAGE_REL_I386_SECTION: applySecIdx(off, os); break;
case IMAGE_REL_I386_SECREL: applySecRel(this, off, os, s); break;
default:
error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
}
void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os,
- uint64_t s, uint64_t p,
- uint64_t imageBase) const {
+ uint64_t s, uint64_t p) 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 + imageBase);
- break;
+ case IMAGE_REL_ARM_ADDR32: add32(off, sx + config->imageBase); break;
case IMAGE_REL_ARM_ADDR32NB: add32(off, sx); break;
- case IMAGE_REL_ARM_MOV32T:
- applyMOV32T(off, sx + imageBase);
- break;
+ case IMAGE_REL_ARM_MOV32T: applyMOV32T(off, sx + config->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, file->ctx.outputSections.size());
- break;
+ case IMAGE_REL_ARM_SECTION: applySecIdx(off, os); break;
case IMAGE_REL_ARM_SECREL: applySecRel(this, off, os, s); break;
case IMAGE_REL_ARM_REL32: add32(off, sx - p - 4); break;
default:
}
void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
- uint64_t s, uint64_t p,
- uint64_t imageBase) const {
+ uint64_t s, uint64_t p) 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;
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 + imageBase);
- break;
+ case IMAGE_REL_ARM64_ADDR32: add32(off, s + config->imageBase); break;
case IMAGE_REL_ARM64_ADDR32NB: add32(off, s); break;
- case IMAGE_REL_ARM64_ADDR64:
- add64(off, s + imageBase);
- break;
+ case IMAGE_REL_ARM64_ADDR64: add64(off, s + config->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, file->ctx.outputSections.size());
- break;
+ case IMAGE_REL_ARM64_SECTION: applySecIdx(off, os); break;
case IMAGE_REL_ARM64_REL32: add32(off, s - p - 4); break;
default:
error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk,
Defined *sym,
- const coff_relocation &rel,
- bool isMinGW) {
+ const coff_relocation &rel) {
// 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() || isMinGW)
+ if (fromChunk->isCodeView() || fromChunk->isDWARF() || config->mingw)
return;
// Get the name of the symbol. If it's null, it was discarded early, so we
// it was an absolute or synthetic symbol.
if (!sym ||
(!os && !isa<DefinedAbsolute>(sym) && !isa<DefinedSynthetic>(sym))) {
- maybeReportRelocationToDiscarded(this, sym, rel, file->ctx.config.mingw);
+ maybeReportRelocationToDiscarded(this, sym, rel);
return;
}
// Compute the RVA of the relocation for relative relocations.
uint64_t p = rva + rel.VirtualAddress;
- uint64_t imageBase = file->ctx.config.imageBase;
- switch (file->ctx.config.machine) {
+ switch (config->machine) {
case AMD64:
- applyRelX64(off, rel.Type, os, s, p, imageBase);
+ applyRelX64(off, rel.Type, os, s, p);
break;
case I386:
- applyRelX86(off, rel.Type, os, s, p, imageBase);
+ applyRelX86(off, rel.Type, os, s, p);
break;
case ARMNT:
- applyRelARM(off, rel.Type, os, s, p, imageBase);
+ applyRelARM(off, rel.Type, os, s, p);
break;
case ARM64:
- applyRelARM64(off, rel.Type, os, s, p, imageBase);
+ applyRelARM64(off, rel.Type, os, s, p);
break;
default:
llvm_unreachable("unknown machine type");
child->assocChildren = next;
}
-static uint8_t getBaserelType(const coff_relocation &rel,
- llvm::COFF::MachineTypes machine) {
- switch (machine) {
+static uint8_t getBaserelType(const coff_relocation &rel) {
+ switch (config->machine) {
case AMD64:
if (rel.Type == IMAGE_REL_AMD64_ADDR64)
return IMAGE_REL_BASED_DIR64;
// Only called when base relocation is enabled.
void SectionChunk::getBaserels(std::vector<Baserel> *res) {
for (const coff_relocation &rel : getRelocs()) {
- uint8_t ty = getBaserelType(rel, file->ctx.config.machine);
+ uint8_t ty = getBaserelType(rel);
if (ty == IMAGE_REL_BASED_ABSOLUTE)
continue;
Symbol *target = file->getSymbol(rel.SymbolTableIndex);
// 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,
- llvm::COFF::MachineTypes machine) {
+static int getRuntimePseudoRelocSize(uint16_t type) {
// 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.
// 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 (machine) {
+ switch (config->machine) {
case AMD64:
switch (type) {
case IMAGE_REL_AMD64_ADDR64:
dyn_cast_or_null<Defined>(file->getSymbol(rel.SymbolTableIndex));
if (!target || !target->isRuntimePseudoReloc)
continue;
- int sizeInBits =
- getRuntimePseudoRelocSize(rel.Type, file->ctx.config.machine);
+ int sizeInBits = getRuntimePseudoRelocSize(rel.Type);
if (sizeInBits == 0) {
error("unable to automatically import from " + target->getName() +
" with relocation type " +
buf[str.size()] = '\0';
}
-ImportThunkChunkX64::ImportThunkChunkX64(COFFLinkerContext &ctx, Defined *s)
- : ImportThunkChunk(ctx, s) {
+ImportThunkChunkX64::ImportThunkChunkX64(Defined *s) : ImportThunkChunk(s) {
// Intel Optimization Manual says that all branch targets
// should be 16-byte aligned. MSVC linker does this too.
setAlignment(16);
}
void ImportThunkChunkX86::getBaserels(std::vector<Baserel> *res) {
- res->emplace_back(getRVA() + 2, ctx.config.machine);
+ res->emplace_back(getRVA() + 2);
}
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() + ctx.config.imageBase);
+ write32le(buf + 2,
+ impSymbol->getRVA() + config->imageBase);
}
void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *res) {
void ImportThunkChunkARM::writeTo(uint8_t *buf) const {
memcpy(buf, importThunkARM, sizeof(importThunkARM));
// Fix mov.w and mov.t operands.
- applyMOV32T(buf, impSymbol->getRVA() + ctx.config.imageBase);
+ applyMOV32T(buf, impSymbol->getRVA() + config->imageBase);
}
void ImportThunkChunkARM64::writeTo(uint8_t *buf) const {
};
size_t RangeExtensionThunkARM::getSize() const {
- assert(ctx.config.machine == ARMNT);
- (void)&ctx;
+ assert(config->machine == ARMNT);
return sizeof(armThunk);
}
void RangeExtensionThunkARM::writeTo(uint8_t *buf) const {
- assert(ctx.config.machine == ARMNT);
+ assert(config->machine == ARMNT);
uint64_t offset = target->getRVA() - rva - 12;
memcpy(buf, armThunk, sizeof(armThunk));
applyMOV32T(buf, uint32_t(offset));
};
size_t RangeExtensionThunkARM64::getSize() const {
- assert(ctx.config.machine == ARM64);
- (void)&ctx;
+ assert(config->machine == ARM64);
return sizeof(arm64Thunk);
}
void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const {
- assert(ctx.config.machine == ARM64);
+ assert(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<Baserel> *res) {
- res->emplace_back(getRVA(), ctx.config.machine);
+ res->emplace_back(getRVA());
}
-size_t LocalImportChunk::getSize() const { return ctx.config.wordsize; }
+size_t LocalImportChunk::getSize() const { return config->wordsize; }
void LocalImportChunk::writeTo(uint8_t *buf) const {
- if (ctx.config.is64()) {
- write64le(buf, sym->getRVA() + ctx.config.imageBase);
+ if (config->is64()) {
+ write64le(buf, sym->getRVA() + config->imageBase);
} else {
- write32le(buf, sym->getRVA() + ctx.config.imageBase);
+ write32le(buf, sym->getRVA() + config->imageBase);
}
}
memcpy(buf, data.data(), data.size());
}
-uint8_t Baserel::getDefaultType(llvm::COFF::MachineTypes machine) {
- switch (machine) {
+uint8_t Baserel::getDefaultType() {
+ switch (config->machine) {
case AMD64:
case ARM64:
return IMAGE_REL_BASED_DIR64;
}
// MinGW specific.
-size_t AbsolutePointerChunk::getSize() const { return ctx.config.wordsize; }
+size_t AbsolutePointerChunk::getSize() const { return config->wordsize; }
void AbsolutePointerChunk::writeTo(uint8_t *buf) const {
- if (ctx.config.is64()) {
+ if (config->is64()) {
write64le(buf, value);
} else {
write32le(buf, value);
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, uint64_t imageBase) const;
+ uint64_t p) const;
void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
- uint64_t p, uint64_t imageBase) const;
+ uint64_t p) const;
void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
- uint64_t p, uint64_t imageBase) const;
+ uint64_t p) const;
void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
- uint64_t p, uint64_t imageBase) const;
+ uint64_t p) const;
void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &res);
// contents will be a JMP instruction to some __imp_ symbol.
class ImportThunkChunk : public NonSectionChunk {
public:
- ImportThunkChunk(COFFLinkerContext &ctx, Defined *s)
- : NonSectionChunk(ImportThunkKind), impSymbol(s), ctx(ctx) {}
+ ImportThunkChunk(Defined *s)
+ : NonSectionChunk(ImportThunkKind), impSymbol(s) {}
static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; }
protected:
Defined *impSymbol;
- COFFLinkerContext &ctx;
};
class ImportThunkChunkX64 : public ImportThunkChunk {
public:
- explicit ImportThunkChunkX64(COFFLinkerContext &ctx, Defined *s);
+ explicit ImportThunkChunkX64(Defined *s);
size_t getSize() const override { return sizeof(importThunkX86); }
void writeTo(uint8_t *buf) const override;
};
class ImportThunkChunkX86 : public ImportThunkChunk {
public:
- explicit ImportThunkChunkX86(COFFLinkerContext &ctx, Defined *s)
- : ImportThunkChunk(ctx, s) {}
+ explicit ImportThunkChunkX86(Defined *s) : ImportThunkChunk(s) {}
size_t getSize() const override { return sizeof(importThunkX86); }
void getBaserels(std::vector<Baserel> *res) override;
void writeTo(uint8_t *buf) const override;
class ImportThunkChunkARM : public ImportThunkChunk {
public:
- explicit ImportThunkChunkARM(COFFLinkerContext &ctx, Defined *s)
- : ImportThunkChunk(ctx, s) {
+ explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) {
setAlignment(2);
}
size_t getSize() const override { return sizeof(importThunkARM); }
class ImportThunkChunkARM64 : public ImportThunkChunk {
public:
- explicit ImportThunkChunkARM64(COFFLinkerContext &ctx, Defined *s)
- : ImportThunkChunk(ctx, s) {
+ explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) {
setAlignment(4);
}
size_t getSize() const override { return sizeof(importThunkARM64); }
class RangeExtensionThunkARM : public NonSectionChunk {
public:
- explicit RangeExtensionThunkARM(COFFLinkerContext &ctx, Defined *t)
- : target(t), ctx(ctx) {
- setAlignment(2);
- }
+ explicit RangeExtensionThunkARM(Defined *t) : target(t) { 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(COFFLinkerContext &ctx, Defined *t)
- : target(t), ctx(ctx) {
- setAlignment(4);
- }
+ explicit RangeExtensionThunkARM64(Defined *t) : target(t) { 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(COFFLinkerContext &ctx, Defined *s);
+ explicit LocalImportChunk(Defined *s) : sym(s) {
+ setAlignment(config->wordsize);
+ }
size_t getSize() const override;
void getBaserels(std::vector<Baserel> *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
class Baserel {
public:
Baserel(uint32_t v, uint8_t ty) : rva(v), type(ty) {}
- explicit Baserel(uint32_t v, llvm::COFF::MachineTypes machine)
- : Baserel(v, getDefaultType(machine)) {}
- uint8_t getDefaultType(llvm::COFF::MachineTypes machine);
+ explicit Baserel(uint32_t v) : Baserel(v, getDefaultType()) {}
+ uint8_t getDefaultType();
uint32_t rva;
uint8_t type;
// MinGW specific. A Chunk that contains one pointer-sized absolute value.
class AbsolutePointerChunk : public NonSectionChunk {
public:
- AbsolutePointerChunk(COFFLinkerContext &ctx, uint64_t value)
- : value(value), ctx(ctx) {
+ AbsolutePointerChunk(uint64_t value) : value(value) {
setAlignment(getSize());
}
size_t getSize() const override;
private:
uint64_t value;
- COFFLinkerContext &ctx;
};
// Return true if this file has the hotpatch flag set to true in the S_COMPILE3
void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit);
void applyArm64Branch26(uint8_t *off, int64_t v);
-// Convenience class for initializing a SectionChunk with specific flags.
-class FakeSectionChunk {
-public:
- FakeSectionChunk(const coff_section *section) : chunk(nullptr, section) {
- // Comdats from LTO files can't be fully treated as regular comdats
- // at this point; we don't know what size or contents they are going to
- // have, so we can't do proper checking of such aspects of them.
- chunk.selection = llvm::COFF::IMAGE_COMDAT_SELECT_ANY;
- }
-
- SectionChunk chunk;
-};
-
} // namespace lld::coff
namespace llvm {
// Global configuration.
struct Configuration {
enum ManifestKind { Default, SideBySide, Embed, No };
- bool is64() const { return machine == AMD64 || machine == ARM64; }
+ bool is64() { return machine == AMD64 || machine == ARM64; }
llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN;
size_t wordsize;
bool writeCheckSum = false;
};
+extern std::unique_ptr<Configuration> config;
+
} // namespace lld::coff
#endif
// A chunk for the import descriptor table.
class LookupChunk : public NonSectionChunk {
public:
- explicit LookupChunk(COFFLinkerContext &ctx, Chunk *c)
- : hintName(c), ctx(ctx) {
- setAlignment(ctx.config.wordsize);
+ explicit LookupChunk(Chunk *c) : hintName(c) {
+ setAlignment(config->wordsize);
}
- size_t getSize() const override { return ctx.config.wordsize; }
+ size_t getSize() const override { return config->wordsize; }
void writeTo(uint8_t *buf) const override {
- if (ctx.config.is64())
+ if (config->is64())
write64le(buf, hintName->getRVA());
else
write32le(buf, hintName->getRVA());
}
Chunk *hintName;
-
-private:
- COFFLinkerContext &ctx;
};
// A chunk for the import descriptor table.
// See Microsoft PE/COFF spec 7.1. Import Header for details.
class OrdinalOnlyChunk : public NonSectionChunk {
public:
- explicit OrdinalOnlyChunk(COFFLinkerContext &c, uint16_t v)
- : ordinal(v), ctx(c) {
- setAlignment(ctx.config.wordsize);
+ explicit OrdinalOnlyChunk(uint16_t v) : ordinal(v) {
+ setAlignment(config->wordsize);
}
- size_t getSize() const override { return ctx.config.wordsize; }
+ size_t getSize() const override { return 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 (ctx.config.is64()) {
+ if (config->is64()) {
write64le(buf, (1ULL << 63) | ordinal);
} else {
write32le(buf, (1ULL << 31) | ordinal);
}
uint16_t ordinal;
-
-private:
- COFFLinkerContext &ctx;
};
// A chunk for the import descriptor table.
};
static std::vector<std::vector<DefinedImportData *>>
-binImports(COFFLinkerContext &ctx,
- const std::vector<DefinedImportData *> &imports) {
+binImports(const std::vector<DefinedImportData *> &imports) {
// Group DLL-imported symbols by DLL name because that's how
// symbols are laid out in the import descriptor table.
- auto less = [&ctx](const std::string &a, const std::string &b) {
- return ctx.config.dllOrder[a] < ctx.config.dllOrder[b];
+ auto less = [](const std::string &a, const std::string &b) {
+ return config->dllOrder[a] < config->dllOrder[b];
};
- std::map<std::string, std::vector<DefinedImportData *>, decltype(less)> m(
- less);
+ std::map<std::string, std::vector<DefinedImportData *>,
+ bool(*)(const std::string &, const std::string &)> m(less);
for (DefinedImportData *sym : imports)
m[sym->getDLLName().lower()].push_back(sym);
class ThunkChunkX86 : public NonSectionChunk {
public:
- ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
- : imp(i), tailMerge(tm), ctx(ctx) {}
+ ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
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() + ctx.config.imageBase);
+ write32le(buf + 1, imp->getRVA() + config->imageBase);
write32le(buf + 6, tailMerge->getRVA() - rva - 10);
}
void getBaserels(std::vector<Baserel> *res) override {
- res->emplace_back(rva + 1, ctx.config.machine);
+ res->emplace_back(rva + 1);
}
Defined *imp = nullptr;
Chunk *tailMerge = nullptr;
-
-private:
- const COFFLinkerContext &ctx;
};
class TailMergeChunkX86 : public NonSectionChunk {
public:
- TailMergeChunkX86(COFFLinkerContext &ctx, Chunk *d, Defined *h)
- : desc(d), helper(h), ctx(ctx) {}
+ TailMergeChunkX86(Chunk *d, Defined *h) : desc(d), helper(h) {}
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() + ctx.config.imageBase);
+ write32le(buf + 4, desc->getRVA() + config->imageBase);
write32le(buf + 9, helper->getRVA() - rva - 13);
}
void getBaserels(std::vector<Baserel> *res) override {
- res->emplace_back(rva + 4, ctx.config.machine);
+ res->emplace_back(rva + 4);
}
Chunk *desc = nullptr;
Defined *helper = nullptr;
-
-private:
- const COFFLinkerContext &ctx;
};
class ThunkChunkARM : public NonSectionChunk {
public:
- ThunkChunkARM(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
- : imp(i), tailMerge(tm), ctx(ctx) {
+ ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {
setAlignment(2);
}
void writeTo(uint8_t *buf) const override {
memcpy(buf, thunkARM, sizeof(thunkARM));
- applyMOV32T(buf + 0, imp->getRVA() + ctx.config.imageBase);
+ applyMOV32T(buf + 0, imp->getRVA() + config->imageBase);
applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12);
}
Defined *imp = nullptr;
Chunk *tailMerge = nullptr;
-
-private:
- const COFFLinkerContext &ctx;
};
class TailMergeChunkARM : public NonSectionChunk {
public:
- TailMergeChunkARM(COFFLinkerContext &ctx, Chunk *d, Defined *h)
- : desc(d), helper(h), ctx(ctx) {
+ TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {
setAlignment(2);
}
void writeTo(uint8_t *buf) const override {
memcpy(buf, tailMergeARM, sizeof(tailMergeARM));
- applyMOV32T(buf + 14, desc->getRVA() + ctx.config.imageBase);
+ applyMOV32T(buf + 14, desc->getRVA() + config->imageBase);
applyBranch24T(buf + 22, helper->getRVA() - rva - 26);
}
Chunk *desc = nullptr;
Defined *helper = nullptr;
-
-private:
- const COFFLinkerContext &ctx;
};
class ThunkChunkARM64 : public NonSectionChunk {
// A chunk for the import descriptor table.
class DelayAddressChunk : public NonSectionChunk {
public:
- explicit DelayAddressChunk(COFFLinkerContext &ctx, Chunk *c)
- : thunk(c), ctx(ctx) {
- setAlignment(ctx.config.wordsize);
+ explicit DelayAddressChunk(Chunk *c) : thunk(c) {
+ setAlignment(config->wordsize);
}
- size_t getSize() const override { return ctx.config.wordsize; }
+ size_t getSize() const override { return config->wordsize; }
void writeTo(uint8_t *buf) const override {
- if (ctx.config.is64()) {
- write64le(buf, thunk->getRVA() + ctx.config.imageBase);
+ if (config->is64()) {
+ write64le(buf, thunk->getRVA() + config->imageBase);
} else {
uint32_t bit = 0;
// Pointer to thumb code must have the LSB set, so adjust it.
- if (ctx.config.machine == ARMNT)
+ if (config->machine == ARMNT)
bit = 1;
- write32le(buf, (thunk->getRVA() + ctx.config.imageBase) | bit);
+ write32le(buf, (thunk->getRVA() + config->imageBase) | bit);
}
}
void getBaserels(std::vector<Baserel> *res) override {
- res->emplace_back(rva, ctx.config.machine);
+ res->emplace_back(rva);
}
Chunk *thunk;
-
-private:
- const COFFLinkerContext &ctx;
};
// Export table
class AddressTableChunk : public NonSectionChunk {
public:
- explicit AddressTableChunk(COFFLinkerContext &ctx, size_t maxOrdinal)
- : size(maxOrdinal), ctx(ctx) {}
+ explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal) {}
size_t getSize() const override { return size * 4; }
void writeTo(uint8_t *buf) const override {
memset(buf, 0, getSize());
- for (const Export &e : ctx.config.exports) {
+ for (const Export &e : config->exports) {
assert(e.ordinal != 0 && "Export symbol has invalid ordinal");
// OrdinalBase is 1, so subtract 1 to get the index.
uint8_t *p = buf + (e.ordinal - 1) * 4;
uint32_t bit = 0;
// Pointer to thumb code must have the LSB set, so adjust it.
- if (ctx.config.machine == ARMNT && !e.data)
+ if (config->machine == ARMNT && !e.data)
bit = 1;
if (e.forwardChunk) {
write32le(p, e.forwardChunk->getRVA() | bit);
private:
size_t size;
- const COFFLinkerContext &ctx;
};
class NamePointersChunk : public NonSectionChunk {
class ExportOrdinalChunk : public NonSectionChunk {
public:
- explicit ExportOrdinalChunk(const COFFLinkerContext &ctx, size_t i)
- : size(i), ctx(ctx) {}
+ explicit ExportOrdinalChunk(size_t i) : size(i) {}
size_t getSize() const override { return size * 2; }
void writeTo(uint8_t *buf) const override {
- for (const Export &e : ctx.config.exports) {
+ for (Export &e : config->exports) {
if (e.noname)
continue;
assert(e.ordinal != 0 && "Export symbol has invalid ordinal");
private:
size_t size;
- const COFFLinkerContext &ctx;
};
} // anonymous namespace
-void IdataContents::create(COFFLinkerContext &ctx) {
- std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
+void IdataContents::create() {
+ std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
// Create .idata contents for each DLL.
for (std::vector<DefinedImportData *> &syms : v) {
for (DefinedImportData *s : syms) {
uint16_t ord = s->getOrdinal();
if (s->getExternalName().empty()) {
- lookups.push_back(make<OrdinalOnlyChunk>(ctx, ord));
- addresses.push_back(make<OrdinalOnlyChunk>(ctx, ord));
+ lookups.push_back(make<OrdinalOnlyChunk>(ord));
+ addresses.push_back(make<OrdinalOnlyChunk>(ord));
continue;
}
auto *c = make<HintNameChunk>(s->getExternalName(), ord);
- lookups.push_back(make<LookupChunk>(ctx, c));
- addresses.push_back(make<LookupChunk>(ctx, c));
+ lookups.push_back(make<LookupChunk>(c));
+ addresses.push_back(make<LookupChunk>(c));
hints.push_back(c);
}
// Terminate with null values.
- lookups.push_back(make<NullChunk>(ctx.config.wordsize));
- addresses.push_back(make<NullChunk>(ctx.config.wordsize));
+ lookups.push_back(make<NullChunk>(config->wordsize));
+ addresses.push_back(make<NullChunk>(config->wordsize));
for (int i = 0, e = syms.size(); i < e; ++i)
syms[i]->setLocation(addresses[base + i]);
return dirs.size() * sizeof(delay_import_directory_table_entry);
}
-void DelayLoadContents::create(Defined *h) {
+void DelayLoadContents::create(COFFLinkerContext &ctx, Defined *h) {
helper = h;
- std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
+ std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
// Create .didat contents for each DLL.
for (std::vector<DefinedImportData *> &syms : v) {
Chunk *tm = newTailMergeChunk(dir);
for (DefinedImportData *s : syms) {
Chunk *t = newThunkChunk(s, tm);
- auto *a = make<DelayAddressChunk>(ctx, t);
+ auto *a = make<DelayAddressChunk>(t);
addresses.push_back(a);
thunks.push_back(t);
StringRef extName = s->getExternalName();
if (extName.empty()) {
- names.push_back(make<OrdinalOnlyChunk>(ctx, s->getOrdinal()));
+ names.push_back(make<OrdinalOnlyChunk>(s->getOrdinal()));
} else {
auto *c = make<HintNameChunk>(extName, 0);
- names.push_back(make<LookupChunk>(ctx, c));
+ names.push_back(make<LookupChunk>(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
}
Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
- switch (ctx.config.machine) {
+ switch (config->machine) {
case AMD64:
return make<TailMergeChunkX64>(dir, helper);
case I386:
- return make<TailMergeChunkX86>(ctx, dir, helper);
+ return make<TailMergeChunkX86>(dir, helper);
case ARMNT:
- return make<TailMergeChunkARM>(ctx, dir, helper);
+ return make<TailMergeChunkARM>(dir, helper);
case ARM64:
return make<TailMergeChunkARM64>(dir, helper);
default:
Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
Chunk *tailMerge) {
- switch (ctx.config.machine) {
+ switch (config->machine) {
case AMD64:
return make<ThunkChunkX64>(s, tailMerge);
case I386:
- return make<ThunkChunkX86>(ctx, s, tailMerge);
+ return make<ThunkChunkX86>(s, tailMerge);
case ARMNT:
- return make<ThunkChunkARM>(ctx, s, tailMerge);
+ return make<ThunkChunkARM>(s, tailMerge);
case ARM64:
return make<ThunkChunkARM64>(s, tailMerge);
default:
}
}
-EdataContents::EdataContents(COFFLinkerContext &ctx) : ctx(ctx) {
+EdataContents::EdataContents() {
uint16_t maxOrdinal = 0;
- for (Export &e : ctx.config.exports)
+ for (Export &e : config->exports)
maxOrdinal = std::max(maxOrdinal, e.ordinal);
- auto *dllName = make<StringChunk>(sys::path::filename(ctx.config.outputFile));
- auto *addressTab = make<AddressTableChunk>(ctx, maxOrdinal);
+ auto *dllName = make<StringChunk>(sys::path::filename(config->outputFile));
+ auto *addressTab = make<AddressTableChunk>(maxOrdinal);
std::vector<Chunk *> names;
- for (Export &e : ctx.config.exports)
+ for (Export &e : config->exports)
if (!e.noname)
names.push_back(make<StringChunk>(e.exportName));
std::vector<Chunk *> forwards;
- for (Export &e : ctx.config.exports) {
+ for (Export &e : config->exports) {
if (e.forwardTo.empty())
continue;
e.forwardChunk = make<StringChunk>(e.forwardTo);
}
auto *nameTab = make<NamePointersChunk>(names);
- auto *ordinalTab = make<ExportOrdinalChunk>(ctx, names.size());
+ auto *ordinalTab = make<ExportOrdinalChunk>(names.size());
auto *dir = make<ExportDirectoryChunk>(maxOrdinal, names.size(), dllName,
addressTab, nameTab, ordinalTab);
chunks.push_back(dir);
void add(DefinedImportData *sym) { imports.push_back(sym); }
bool empty() { return imports.empty(); }
- void create(COFFLinkerContext &ctx);
+ void create();
std::vector<DefinedImportData *> imports;
std::vector<Chunk *> dirs;
// DelayLoadContents creates all chunks for the delay-load DLL import table.
class DelayLoadContents {
public:
- DelayLoadContents(COFFLinkerContext &ctx) : ctx(ctx) {}
void add(DefinedImportData *sym) { imports.push_back(sym); }
bool empty() { return imports.empty(); }
- void create(Defined *helper);
+ void create(COFFLinkerContext &ctx, Defined *helper);
std::vector<Chunk *> getChunks();
std::vector<Chunk *> getDataChunks();
ArrayRef<Chunk *> getCodeChunks() { return thunks; }
std::vector<Chunk *> hintNames;
std::vector<Chunk *> thunks;
std::vector<Chunk *> dllNames;
-
- COFFLinkerContext &ctx;
};
// Windows-specific.
// EdataContents creates all chunks for the DLL export table.
class EdataContents {
public:
- EdataContents(COFFLinkerContext &ctx);
+ EdataContents();
std::vector<Chunk *> chunks;
uint64_t getRVA() { return chunks[0]->getRVA(); }
uint64_t getSize() {
return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA();
}
-
- COFFLinkerContext &ctx;
};
} // namespace lld::coff
reinterpret_cast<TypeIndex *>(contents.data() + ref.Offset), ref.Count);
for (TypeIndex &ti : indices) {
if (!remapTypeIndex(ti, ref.Kind)) {
- if (ctx.config.verbose) {
+ if (config->verbose) {
uint16_t kind =
reinterpret_cast<const RecordPrefix *>(rec.data())->RecordKind;
StringRef fname = file ? file->getName() : "<unknown PDB>";
// Merge .debug$T for a generic object file.
Error TpiSource::mergeDebugT(TypeMerger *m) {
- assert(!ctx.config.debugGHashes &&
+ assert(!config->debugGHashes &&
"use remapTpiWithGHashes when ghash is enabled");
CVTypeArray types;
tpiMap = indexMapStorage;
ipiMap = indexMapStorage;
- if (ctx.config.showSummary) {
+ if (config->showSummary) {
nbTypeRecords = indexMapStorage.size() - nbHeadIndices;
nbTypeRecordsBytes = reader.getLength();
// Count how many times we saw each type record in our input. This
// Merge types from a type server PDB.
Error TypeServerSource::mergeDebugT(TypeMerger *m) {
- assert(!ctx.config.debugGHashes &&
+ assert(!config->debugGHashes &&
"use remapTpiWithGHashes when ghash is enabled");
pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile();
ipiMap = ipiSrc->indexMapStorage;
}
- if (ctx.config.showSummary) {
+ if (config->showSummary) {
nbTypeRecords = tpiMap.size() + ipiMap.size();
nbTypeRecordsBytes =
expectedTpi->typeArray().getUnderlyingStream().getLength() +
}
void TpiSource::remapTpiWithGHashes(GHashState *g) {
- assert(ctx.config.debugGHashes && "ghashes must be enabled");
+ assert(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 (ctx.config.showSummary) {
+ if (config->showSummary) {
nbTypeRecords = ghashes.size();
nbTypeRecordsBytes = file->debugTypes.size();
}
// Merge types from a type server PDB.
void TypeServerSource::remapTpiWithGHashes(GHashState *g) {
- assert(ctx.config.debugGHashes && "ghashes must be enabled");
+ assert(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.
ipiSrc->ipiMap = ipiMap;
ipiSrc->mergeUniqueTypeRecords(typeArrayToBytes(ipi.typeArray()));
- if (ctx.config.showSummary) {
+ if (config->showSummary) {
nbTypeRecords = ipiSrc->ghashes.size();
nbTypeRecordsBytes = ipi.typeArray().getUnderlyingStream().getLength();
}
}
- if (ctx.config.showSummary) {
+ if (config->showSummary) {
nbTypeRecords += ghashes.size();
nbTypeRecordsBytes += tpi.typeArray().getUnderlyingStream().getLength();
}
mergeUniqueTypeRecords(file->debugTypes,
TypeIndex(precompDependency.getStartTypeIndex() +
precompDependency.getTypesCount()));
- if (ctx.config.showSummary) {
+ if (config->showSummary) {
nbTypeRecords = ghashes.size();
nbTypeRecordsBytes = file->debugTypes.size();
}
namespace lld::coff {
+std::unique_ptr<Configuration> config;
+std::unique_ptr<LinkerDriver> driver;
+
bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) {
// This driver-specific context will be freed later by lldMain().
ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now"
" (use /errorlimit:0 to see all errors)";
- ctx->driver.linkerMain(args);
+ config = std::make_unique<Configuration>();
+ driver = std::make_unique<LinkerDriver>(*ctx);
+
+ driver->linkerMain(args);
return errorCount() == 0;
}
// Drop directory components and replace extension with
// ".exe", ".dll" or ".sys".
-static std::string getOutputPath(StringRef path, bool isDll, bool isDriver) {
+static std::string getOutputPath(StringRef path) {
StringRef ext = ".exe";
- if (isDll)
+ if (config->dll)
ext = ".dll";
- else if (isDriver)
+ else if (config->driver)
ext = ".sys";
return (sys::path::stem(path) + ext).str();
}
// Symbol names are mangled by prepending "_" on x86.
-StringRef LinkerDriver::mangle(StringRef sym) {
- assert(ctx.config.machine != IMAGE_FILE_MACHINE_UNKNOWN);
- if (ctx.config.machine == I386)
+static StringRef mangle(StringRef sym) {
+ assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN);
+ if (config->machine == I386)
return saver().save("_" + sym);
return sym;
}
-llvm::Triple::ArchType LinkerDriver::getArch() {
- switch (ctx.config.machine) {
+static llvm::Triple::ArchType getArch() {
+ switch (config->machine) {
case I386:
return llvm::Triple::ArchType::x86;
case AMD64:
MemoryBufferRef mbref = *mb;
make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); // take ownership
- if (ctx.driver.tar)
- ctx.driver.tar->append(relativeToRoot(mbref.getBufferIdentifier()),
- mbref.getBuffer());
+ if (driver->tar)
+ driver->tar->append(relativeToRoot(mbref.getBufferIdentifier()),
+ mbref.getBuffer());
return mbref;
}
error(filename + ": is not a native COFF file. Recompile without /GL");
break;
case file_magic::pecoff_executable:
- if (ctx.config.mingw) {
+ if (config->mingw) {
ctx.symtab.addFile(make<DLLFile>(ctx, mbref));
break;
}
// the option `/nodefaultlib` than a reference to a file in the root
// directory.
std::string nearest;
- if (ctx.optTable.findNearest(pathStr, nearest) > 1)
+ if (optTable.findNearest(pathStr, nearest) > 1)
error(msg);
else
error(msg + "; did you mean '" + nearest + "'");
} else
- ctx.driver.addBuffer(std::move(mbOrErr.first), wholeArchive, lazy);
+ driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy);
});
}
auto reportBufferError = [=](Error &&e, StringRef childName) {
fatal("could not get the buffer for the member defining symbol " +
- toCOFFString(ctx, sym) + ": " + parentName + "(" + childName +
- "): " + toString(std::move(e)));
+ toCOFFString(sym) + ": " + parentName + "(" + childName + "): " +
+ toString(std::move(e)));
};
if (!c.getParent()->isThin()) {
reportBufferError(mbOrErr.takeError(), check(c.getFullName()));
MemoryBufferRef mb = mbOrErr.get();
enqueueTask([=]() {
- ctx.driver.addArchiveBuffer(mb, toCOFFString(ctx, sym), parentName,
- offsetInArchive);
+ driver->addArchiveBuffer(mb, toCOFFString(sym), parentName,
+ offsetInArchive);
});
return;
}
- std::string childName =
- CHECK(c.getFullName(),
- "could not get the filename for the member defining symbol " +
- toCOFFString(ctx, sym));
+ std::string childName = CHECK(
+ c.getFullName(),
+ "could not get the filename for the member defining symbol " +
+ toCOFFString(sym));
auto future = std::make_shared<std::future<MBErrPair>>(
createFutureForFile(childName));
enqueueTask([=]() {
reportBufferError(errorCodeToError(mbOrErr.second), childName);
// Pass empty string as archive name so that the original filename is
// used as the buffer identifier.
- ctx.driver.addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)),
- toCOFFString(ctx, sym), "",
- /*OffsetInArchive=*/0);
+ driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)),
+ toCOFFString(sym), "", /*OffsetInArchive=*/0);
});
}
-bool LinkerDriver::isDecorated(StringRef sym) {
+static bool isDecorated(StringRef sym) {
return sym.startswith("@") || sym.contains("@@") || sym.startswith("?") ||
- (!ctx.config.mingw && sym.contains('@'));
+ (!config->mingw && sym.contains('@'));
}
// Parses .drectve section contents and returns a list of files
log("Directives: " + toString(file) + ": " + s);
- ArgParser parser(ctx);
+ ArgParser parser;
// .drectve is always tokenized using Windows shell rules.
// /EXPORT: option can appear too many times, processing in fastpath.
ParsedDirectives directives = parser.parseDirectives(s);
continue;
Export exp = parseExport(e);
- if (ctx.config.machine == I386 && ctx.config.mingw) {
+ if (config->machine == I386 && 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;
- ctx.config.exports.push_back(exp);
+ config->exports.push_back(exp);
}
// Handle /include: in bulk.
enqueuePath(*path, false, false);
break;
case OPT_entry:
- ctx.config.entry = addUndefined(mangle(arg->getValue()));
+ config->entry = addUndefined(mangle(arg->getValue()));
break;
case OPT_failifmismatch:
checkFailIfMismatch(arg->getValue(), file);
addUndefined(arg->getValue());
break;
case OPT_manifestdependency:
- ctx.config.manifestDependencies.insert(arg->getValue());
+ config->manifestDependencies.insert(arg->getValue());
break;
case OPT_merge:
parseMerge(arg->getValue());
break;
case OPT_nodefaultlib:
- ctx.config.noDefaultLibs.insert(doFindLib(arg->getValue()).lower());
+ config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower());
break;
case OPT_release:
- ctx.config.writeCheckSum = true;
+ config->writeCheckSum = true;
break;
case OPT_section:
parseSection(arg->getValue());
break;
case OPT_stack:
- parseNumbers(arg->getValue(), &ctx.config.stackReserve,
- &ctx.config.stackCommit);
+ parseNumbers(arg->getValue(), &config->stackReserve,
+ &config->stackCommit);
break;
case OPT_subsystem: {
bool gotVersion = false;
- parseSubsystem(arg->getValue(), &ctx.config.subsystem,
- &ctx.config.majorSubsystemVersion,
- &ctx.config.minorSubsystemVersion, &gotVersion);
+ parseSubsystem(arg->getValue(), &config->subsystem,
+ &config->majorSubsystemVersion,
+ &config->minorSubsystemVersion, &gotVersion);
if (gotVersion) {
- ctx.config.majorOSVersion = ctx.config.majorSubsystemVersion;
- ctx.config.minorOSVersion = ctx.config.minorSubsystemVersion;
+ config->majorOSVersion = config->majorSubsystemVersion;
+ config->minorOSVersion = config->minorSubsystemVersion;
}
break;
}
// Find file from search paths. You can omit ".obj", this function takes
// care of that. Note that the returned path is not guaranteed to exist.
StringRef LinkerDriver::doFindFile(StringRef filename) {
- auto getFilename = [this](StringRef filename) -> StringRef {
- if (ctx.config.vfs)
- if (auto statOrErr = ctx.config.vfs->status(filename))
+ auto getFilename = [](StringRef filename) -> StringRef {
+ if (config->vfs)
+ if (auto statOrErr = config->vfs->status(filename))
return saver().save(statOrErr->getName());
return filename;
};
StringRef ret = doFindFile(filename);
// For MinGW, if the find above didn't turn up anything, try
// looking for a MinGW formatted library name.
- if (ctx.config.mingw && ret == filename)
+ if (config->mingw && ret == filename)
return doFindLibMinGW(filename);
return ret;
}
// consideration. This never returns the same path (in that case,
// it returns std::nullopt).
std::optional<StringRef> LinkerDriver::findLib(StringRef filename) {
- if (ctx.config.noDefaultLibAll)
+ if (config->noDefaultLibAll)
return std::nullopt;
if (!visitedLibs.insert(filename.lower()).second)
return std::nullopt;
StringRef path = doFindLib(filename);
- if (ctx.config.noDefaultLibs.count(path.lower()))
+ if (config->noDefaultLibs.count(path.lower()))
return std::nullopt;
if (std::optional<sys::fs::UniqueID> id = getUniqueID(path))
Symbol *b = ctx.symtab.addUndefined(name);
if (!b->isGCRoot) {
b->isGCRoot = true;
- ctx.config.gcroot.push_back(b);
+ config->gcroot.push_back(b);
}
return b;
}
// 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(ctx.config.subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
+ assert(config->subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
"must handle /subsystem before calling this");
- if (ctx.config.mingw)
- return mangle(ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
+ if (config->mingw)
+ return mangle(config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
? "WinMainCRTStartup"
: "mainCRTStartup");
- if (ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
+ if (config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
if (findUnderscoreMangle("wWinMain")) {
if (!findUnderscoreMangle("WinMain"))
return mangle("wWinMainCRTStartup");
}
WindowsSubsystem LinkerDriver::inferSubsystem() {
- if (ctx.config.dll)
+ if (config->dll)
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
- if (ctx.config.mingw)
+ if (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
return IMAGE_SUBSYSTEM_UNKNOWN;
}
-uint64_t LinkerDriver::getDefaultImageBase() {
- if (ctx.config.is64())
- return ctx.config.dll ? 0x180000000 : 0x140000000;
- return ctx.config.dll ? 0x10000000 : 0x400000;
+static uint64_t getDefaultImageBase() {
+ if (config->is64())
+ return config->dll ? 0x180000000 : 0x140000000;
+ return config->dll ? 0x10000000 : 0x400000;
}
static std::string rewritePath(StringRef s) {
return debugTypes;
}
-std::string LinkerDriver::getMapFile(const opt::InputArgList &args,
- opt::OptSpecifier os,
- opt::OptSpecifier osFile) {
+static std::string getMapFile(const opt::InputArgList &args,
+ opt::OptSpecifier os, opt::OptSpecifier osFile) {
auto *arg = args.getLastArg(os, osFile);
if (!arg)
return "";
return arg->getValue();
assert(arg->getOption().getID() == os.getID());
- StringRef outFile = ctx.config.outputFile;
+ StringRef outFile = config->outputFile;
return (outFile.substr(0, outFile.rfind('.')) + ".map").str();
}
-std::string LinkerDriver::getImplibPath() {
- if (!ctx.config.implib.empty())
- return std::string(ctx.config.implib);
- SmallString<128> out = StringRef(ctx.config.outputFile);
+static std::string getImplibPath() {
+ if (!config->implib.empty())
+ return std::string(config->implib);
+ SmallString<128> out = StringRef(config->outputFile);
sys::path::replace_extension(out, ".lib");
return std::string(out.str());
}
// LINK | {value} | {value}.{.dll/.exe} | {output name}
// LIB | {value} | {value}.dll | {output name}.dll
//
-std::string LinkerDriver::getImportName(bool asLib) {
+static std::string getImportName(bool asLib) {
SmallString<128> out;
- if (ctx.config.importName.empty()) {
- out.assign(sys::path::filename(ctx.config.outputFile));
+ if (config->importName.empty()) {
+ out.assign(sys::path::filename(config->outputFile));
if (asLib)
sys::path::replace_extension(out, ".dll");
} else {
- out.assign(ctx.config.importName);
+ out.assign(config->importName);
if (!sys::path::has_extension(out))
sys::path::replace_extension(out,
- (ctx.config.dll || asLib) ? ".dll" : ".exe");
+ (config->dll || asLib) ? ".dll" : ".exe");
}
return std::string(out.str());
}
-void LinkerDriver::createImportLibrary(bool asLib) {
+static void createImportLibrary(bool asLib) {
std::vector<COFFShortExport> exports;
- for (Export &e1 : ctx.config.exports) {
+ for (Export &e1 : config->exports) {
COFFShortExport e2;
e2.Name = std::string(e1.name);
e2.SymbolName = std::string(e1.symbolName);
std::string libName = getImportName(asLib);
std::string path = getImplibPath();
- if (!ctx.config.incremental) {
- checkError(writeImportLibrary(libName, path, exports, ctx.config.machine,
- ctx.config.mingw));
+ if (!config->incremental) {
+ checkError(writeImportLibrary(libName, path, exports, config->machine,
+ config->mingw));
return;
}
ErrorOr<std::unique_ptr<MemoryBuffer>> oldBuf = MemoryBuffer::getFile(
path, /*IsText=*/false, /*RequiresNullTerminator=*/false);
if (!oldBuf) {
- checkError(writeImportLibrary(libName, path, exports, ctx.config.machine,
- ctx.config.mingw));
+ checkError(writeImportLibrary(libName, path, exports, config->machine,
+ config->mingw));
return;
}
fatal("cannot create temporary file for import library " + path + ": " +
ec.message());
- if (Error e = writeImportLibrary(libName, tmpName, exports,
- ctx.config.machine, ctx.config.mingw)) {
+ if (Error e = writeImportLibrary(libName, tmpName, exports, config->machine,
+ config->mingw)) {
checkError(std::move(e));
return;
}
}
}
-void LinkerDriver::parseModuleDefs(StringRef path) {
+static void parseModuleDefs(StringRef path) {
std::unique_ptr<MemoryBuffer> mb =
CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,
/*RequiresNullTerminator=*/false,
/*IsVolatile=*/true),
"could not open " + path);
COFFModuleDefinition m = check(parseCOFFModuleDefinition(
- mb->getMemBufferRef(), ctx.config.machine, ctx.config.mingw));
+ mb->getMemBufferRef(), config->machine, config->mingw));
// Include in /reproduce: output if applicable.
- ctx.driver.takeBuffer(std::move(mb));
+ driver->takeBuffer(std::move(mb));
- if (ctx.config.outputFile.empty())
- ctx.config.outputFile = std::string(saver().save(m.OutputFile));
- ctx.config.importName = std::string(saver().save(m.ImportName));
+ if (config->outputFile.empty())
+ config->outputFile = std::string(saver().save(m.OutputFile));
+ config->importName = std::string(saver().save(m.ImportName));
if (m.ImageBase)
- ctx.config.imageBase = m.ImageBase;
+ config->imageBase = m.ImageBase;
if (m.StackReserve)
- ctx.config.stackReserve = m.StackReserve;
+ config->stackReserve = m.StackReserve;
if (m.StackCommit)
- ctx.config.stackCommit = m.StackCommit;
+ config->stackCommit = m.StackCommit;
if (m.HeapReserve)
- ctx.config.heapReserve = m.HeapReserve;
+ config->heapReserve = m.HeapReserve;
if (m.HeapCommit)
- ctx.config.heapCommit = m.HeapCommit;
+ config->heapCommit = m.HeapCommit;
if (m.MajorImageVersion)
- ctx.config.majorImageVersion = m.MajorImageVersion;
+ config->majorImageVersion = m.MajorImageVersion;
if (m.MinorImageVersion)
- ctx.config.minorImageVersion = m.MinorImageVersion;
+ config->minorImageVersion = m.MinorImageVersion;
if (m.MajorOSVersion)
- ctx.config.majorOSVersion = m.MajorOSVersion;
+ config->majorOSVersion = m.MajorOSVersion;
if (m.MinorOSVersion)
- ctx.config.minorOSVersion = m.MinorOSVersion;
+ config->minorOSVersion = m.MinorOSVersion;
for (COFFShortExport e1 : m.Exports) {
Export e2;
StringRef(e1.Name).contains('.')) {
e2.name = saver().save(e1.ExtName);
e2.forwardTo = saver().save(e1.Name);
- ctx.config.exports.push_back(e2);
+ config->exports.push_back(e2);
continue;
}
e2.name = saver().save(e1.Name);
e2.data = e1.Data;
e2.isPrivate = e1.Private;
e2.constant = e1.Constant;
- ctx.config.exports.push_back(e2);
+ config->exports.push_back(e2);
}
}
// 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.
-void LinkerDriver::parseOrderFile(StringRef arg) {
+static void parseOrderFile(COFFLinkerContext &ctx, StringRef arg) {
// For some reason, the MSVC linker requires a filename to be
// preceded by "@".
if (!arg.startswith("@")) {
// end of an output section.
for (StringRef arg : args::getLines(mb->getMemBufferRef())) {
std::string s(arg);
- if (ctx.config.machine == I386 && !isDecorated(s))
+ if (config->machine == I386 && !isDecorated(s))
s = "_" + s;
if (set.count(s) == 0) {
- if (ctx.config.warnMissingOrderSymbol)
+ if (config->warnMissingOrderSymbol)
warn("/order:" + arg + ": missing symbol: " + s + " [LNK4037]");
}
else
- ctx.config.order[s] = INT_MIN + ctx.config.order.size();
+ config->order[s] = INT_MIN + config->order.size();
}
// Include in /reproduce: output if applicable.
- ctx.driver.takeBuffer(std::move(mb));
+ driver->takeBuffer(std::move(mb));
}
-void LinkerDriver::parseCallGraphFile(StringRef path) {
+static void parseCallGraphFile(COFFLinkerContext &ctx, StringRef path) {
std::unique_ptr<MemoryBuffer> mb =
CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,
/*RequiresNullTerminator=*/false,
auto findSection = [&](StringRef name) -> SectionChunk * {
Symbol *sym = map.lookup(name);
if (!sym) {
- if (ctx.config.warnMissingOrderSymbol)
+ if (config->warnMissingOrderSymbol)
warn(path + ": no such symbol: " + name);
return nullptr;
}
if (SectionChunk *from = findSection(fields[0]))
if (SectionChunk *to = findSection(fields[1]))
- ctx.config.callGraphProfile[{from, to}] += count;
+ config->callGraphProfile[{from, to}] += count;
}
// Include in /reproduce: output if applicable.
- ctx.driver.takeBuffer(std::move(mb));
+ driver->takeBuffer(std::move(mb));
}
static void readCallGraphsFromObjectFiles(COFFLinkerContext &ctx) {
auto *from = dyn_cast_or_null<SectionChunk>(fromSym->getChunk());
auto *to = dyn_cast_or_null<SectionChunk>(toSym->getChunk());
if (from && to)
- ctx.config.callGraphProfile[{from, to}] += count;
+ config->callGraphProfile[{from, to}] += count;
}
}
}
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 : ctx.config.exports)
+ for (Export &r : config->exports)
markAddrsig(r.sym);
// Visit the address-significance table in each object file and mark each
// binary).
// lld only supports %_PDB% and %_EXT% and warns on references to all other env
// vars.
-void LinkerDriver::parsePDBAltPath() {
+static void parsePDBAltPath(StringRef altPath) {
SmallString<128> buf;
StringRef pdbBasename =
- sys::path::filename(ctx.config.pdbPath, sys::path::Style::windows);
+ sys::path::filename(config->pdbPath, sys::path::Style::windows);
StringRef binaryExtension =
- sys::path::extension(ctx.config.outputFile, sys::path::Style::windows);
+ sys::path::extension(config->outputFile, sys::path::Style::windows);
if (!binaryExtension.empty())
binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'.
// v v v
// a...%...%...
size_t cursor = 0;
- while (cursor < ctx.config.pdbAltPath.size()) {
+ while (cursor < altPath.size()) {
size_t firstMark, secondMark;
- if ((firstMark = ctx.config.pdbAltPath.find('%', cursor)) ==
- StringRef::npos ||
- (secondMark = ctx.config.pdbAltPath.find('%', firstMark + 1)) ==
- StringRef::npos) {
+ if ((firstMark = altPath.find('%', cursor)) == StringRef::npos ||
+ (secondMark = altPath.find('%', firstMark + 1)) == StringRef::npos) {
// Didn't find another full fragment, treat rest of string as literal.
- buf.append(ctx.config.pdbAltPath.substr(cursor));
+ buf.append(altPath.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(ctx.config.pdbAltPath.substr(cursor, firstMark - cursor));
- StringRef var =
- ctx.config.pdbAltPath.substr(firstMark, secondMark - firstMark + 1);
+ buf.append(altPath.substr(cursor, firstMark - cursor));
+ StringRef var = altPath.substr(firstMark, secondMark - firstMark + 1);
if (var.equals_insensitive("%_pdb%"))
buf.append(pdbBasename);
else if (var.equals_insensitive("%_ext%"))
cursor = secondMark + 1;
}
- ctx.config.pdbAltPath = buf;
+ config->pdbAltPath = buf;
}
/// Convert resource files and potentially merge input resource object
resourceObjFiles.push_back(f);
}
- if (!ctx.config.mingw &&
+ if (!config->mingw &&
(resourceObjFiles.size() > 1 ||
(resourceObjFiles.size() == 1 && !resources.empty()))) {
error((!resources.empty() ? "internal .obj file created from .res files"
// 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 (!ctx.config.dll)
+ if (!config->dll)
return;
- if (!ctx.config.exports.empty())
+ if (!config->exports.empty())
return;
if (args.hasArg(OPT_exclude_all_symbols))
return;
}
- AutoExporter exporter(ctx, excludedSymbols);
+ AutoExporter exporter(excludedSymbols);
for (auto *arg : args.filtered(OPT_wholearchive_file))
if (std::optional<StringRef> path = doFindFile(arg->getValue()))
ctx.symtab.forEachSymbol([&](Symbol *s) {
auto *def = dyn_cast<Defined>(s);
- if (!exporter.shouldExport(def))
+ if (!exporter.shouldExport(ctx, def))
return;
if (!def->isGCRoot) {
def->isGCRoot = true;
- ctx.config.gcroot.push_back(def);
+ config->gcroot.push_back(def);
}
Export e;
if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
e.data = true;
s->isUsedInRegularObj = true;
- ctx.config.exports.push_back(e);
+ config->exports.push_back(e);
});
}
void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
ScopedTimer rootTimer(ctx.rootTimer);
- Configuration *config = &ctx.config;
// Needed for LTO.
InitializeAllTargetInfos();
}
// Parse command line options.
- ArgParser parser(ctx);
+ ArgParser parser;
opt::InputArgList args = parser.parse(argsArr);
// Parse and evaluate -mllvm options.
// Handle /functionpadmin
for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
- parseFunctionPadMin(arg);
+ parseFunctionPadMin(arg, config->machine);
if (tar) {
tar->append("response.txt",
// 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(),
- config->dll, config->driver);
+ (*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue());
}
// Fail early if an output file is not writable.
sys::fs::make_absolute(config->pdbAltPath);
sys::path::remove_dots(config->pdbAltPath);
} else {
- // Don't do this earlier, so that ctx.OutputFile is ready.
- parsePDBAltPath();
+ // Don't do this earlier, so that Config->OutputFile is ready.
+ parsePDBAltPath(config->pdbAltPath);
}
}
// If -thinlto-index-only is given, we should create only "index
// files" and not object files. Index file creation is already done
- // in addCombinedLTOObject, so we are done if that's the case.
+ // in compileBitcodeFiles, so we are done if that's the case.
if (config->thinLTOIndexOnly)
return;
// Handle /output-def (MinGW specific).
if (auto *arg = args.getLastArg(OPT_output_def))
- writeDefFile(arg->getValue(), config->exports);
+ writeDefFile(arg->getValue());
// Set extra alignment for .comm symbols
for (auto pair : config->alignComm) {
if (auto *arg = args.getLastArg(OPT_order)) {
if (args.hasArg(OPT_call_graph_ordering_file))
error("/order and /call-graph-order-file may not be used together");
- parseOrderFile(arg->getValue());
+ parseOrderFile(ctx, arg->getValue());
config->callGraphProfileSort = false;
}
// Handle /call-graph-ordering-file and /call-graph-profile-sort (default on).
if (config->callGraphProfileSort) {
if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) {
- parseCallGraphFile(arg->getValue());
+ parseCallGraphFile(ctx, arg->getValue());
}
readCallGraphsFromObjectFiles(ctx);
}
// Identify identical COMDAT sections to merge them.
if (config->doICF != ICFLevel::None) {
findKeepUniqueSections(ctx);
- doICF(ctx);
+ doICF(ctx, config->doICF);
}
// Write the result.
#ifndef LLD_COFF_DRIVER_H
#define LLD_COFF_DRIVER_H
+#include "COFFLinkerContext.h"
#include "Config.h"
#include "SymbolTable.h"
#include "lld/Common/LLVM.h"
namespace lld::coff {
+extern std::unique_ptr<class LinkerDriver> driver;
+
using llvm::COFF::MachineTypes;
using llvm::COFF::WindowsSubsystem;
using std::optional;
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
class ArgParser {
public:
- ArgParser(COFFLinkerContext &ctx);
-
// Parses command line options.
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
void addLINK(SmallVector<const char *, 256> &argv);
std::vector<const char *> tokenize(StringRef s);
-
- COFFLinkerContext &ctx;
};
class LinkerDriver {
public:
- LinkerDriver(COFFLinkerContext &ctx) : ctx(ctx) {}
+ LinkerDriver(COFFLinkerContext &c) : ctx(c) {}
void linkerMain(llvm::ArrayRef<const char *> args);
// Determines the location of the sysroot based on `args`, environment, etc.
void detectWinSysRoot(const llvm::opt::InputArgList &args);
- // Symbol names are mangled by prepending "_" on x86.
- StringRef mangle(StringRef sym);
-
- llvm::Triple::ArchType getArch();
-
- uint64_t getDefaultImageBase();
-
- bool isDecorated(StringRef sym);
-
- std::string getMapFile(const llvm::opt::InputArgList &args,
- llvm::opt::OptSpecifier os,
- llvm::opt::OptSpecifier osFile);
-
- std::string getImplibPath();
-
- // The import name is calculated as follows:
- //
- // | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY
- // -----+----------------+---------------------+------------------
- // LINK | {value} | {value}.{.dll/.exe} | {output name}
- // LIB | {value} | {value}.dll | {output name}.dll
- //
- std::string getImportName(bool asLib);
-
- void createImportLibrary(bool asLib);
-
- void parseModuleDefs(StringRef path);
-
- // Parse an /order file. If an option is given, the linker places COMDAT
- // sections int he same order as their names appear in the given file.
- void parseOrderFile(StringRef arg);
-
- void parseCallGraphFile(StringRef path);
-
- void parsePDBAltPath();
-
// Parses LIB environment which contains a list of search paths.
void addLibSearchPaths();
llvm::SmallString<128> universalCRTLibPath;
int sdkMajor = 0;
llvm::SmallString<128> windowsSdkLibPath;
+};
- // Functions below this line are defined in DriverUtils.cpp.
-
- void printHelp(const char *argv0);
+// Functions below this line are defined in DriverUtils.cpp.
- // Parses a string in the form of "<integer>[,<integer>]".
- void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr);
+void printHelp(const char *argv0);
- void parseGuard(StringRef arg);
+// Parses a string in the form of "<integer>[,<integer>]".
+void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr);
- // Parses a string in the form of "<integer>[.<integer>]".
- // Minor's default value is 0.
- void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor);
+void parseGuard(StringRef arg);
- // Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
- void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
- uint32_t *minor, bool *gotVersion = nullptr);
+// Parses a string in the form of "<integer>[.<integer>]".
+// Minor's default value is 0.
+void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor);
- void parseAlternateName(StringRef);
- void parseMerge(StringRef);
- void parsePDBPageSize(StringRef);
- void parseSection(StringRef);
- void parseAligncomm(StringRef);
+// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
+void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
+ uint32_t *minor, bool *gotVersion = nullptr);
- // Parses a string in the form of "[:<integer>]"
- void parseFunctionPadMin(llvm::opt::Arg *a);
+void parseAlternateName(StringRef);
+void parseMerge(StringRef);
+void parsePDBPageSize(StringRef);
+void parseSection(StringRef);
+void parseAligncomm(StringRef);
- // Parses a string in the form of "EMBED[,=<integer>]|NO".
- void parseManifest(StringRef arg);
+// Parses a string in the form of "[:<integer>]"
+void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine);
- // Parses a string in the form of "level=<string>|uiAccess=<string>"
- void parseManifestUAC(StringRef arg);
+// Parses a string in the form of "EMBED[,=<integer>]|NO".
+void parseManifest(StringRef arg);
- // Parses a string in the form of "cd|net[,(cd|net)]*"
- void parseSwaprun(StringRef arg);
+// Parses a string in the form of "level=<string>|uiAccess=<string>"
+void parseManifestUAC(StringRef arg);
- // Create a resource file containing a manifest XML.
- std::unique_ptr<MemoryBuffer> createManifestRes();
- void createSideBySideManifest();
- std::string createDefaultXml();
- std::string createManifestXmlWithInternalMt(StringRef defaultXml);
- std::string createManifestXmlWithExternalMt(StringRef defaultXml);
- std::string createManifestXml();
+// Parses a string in the form of "cd|net[,(cd|net)]*"
+void parseSwaprun(StringRef arg);
- std::unique_ptr<llvm::WritableMemoryBuffer>
- createMemoryBufferForManifestRes(size_t manifestRes);
+// Create a resource file containing a manifest XML.
+std::unique_ptr<MemoryBuffer> createManifestRes();
+void createSideBySideManifest();
- // 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<MemoryBufferRef> mbs,
- ArrayRef<ObjFile *> objs);
-};
+// Convert Windows resource files (.res files) to a .obj file.
+MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
+ ArrayRef<ObjFile *> objs);
// Create enum with OPT_xxx values for each option in Options.td
enum {
//
//===----------------------------------------------------------------------===//
-#include "COFFLinkerContext.h"
+#include "Config.h"
#include "Driver.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
} // anonymous namespace
// Parses a string in the form of "<integer>[,<integer>]".
-void LinkerDriver::parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {
+void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {
auto [s1, s2] = arg.split(',');
if (s1.getAsInteger(0, *addr))
fatal("invalid number: " + s1);
// Parses a string in the form of "<integer>[.<integer>]".
// If second number is not present, Minor is set to 0.
-void LinkerDriver::parseVersion(StringRef arg, uint32_t *major,
- uint32_t *minor) {
+void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) {
auto [s1, s2] = arg.split('.');
if (s1.getAsInteger(10, *major))
fatal("invalid number: " + s1);
fatal("invalid number: " + s2);
}
-void LinkerDriver::parseGuard(StringRef fullArg) {
+void parseGuard(StringRef fullArg) {
SmallVector<StringRef, 1> splitArgs;
fullArg.split(splitArgs, ",");
for (StringRef arg : splitArgs) {
if (arg.equals_insensitive("no"))
- ctx.config.guardCF = GuardCFLevel::Off;
+ config->guardCF = GuardCFLevel::Off;
else if (arg.equals_insensitive("nolongjmp"))
- ctx.config.guardCF &= ~GuardCFLevel::LongJmp;
+ config->guardCF &= ~GuardCFLevel::LongJmp;
else if (arg.equals_insensitive("noehcont"))
- ctx.config.guardCF &= ~GuardCFLevel::EHCont;
+ config->guardCF &= ~GuardCFLevel::EHCont;
else if (arg.equals_insensitive("cf") || arg.equals_insensitive("longjmp"))
- ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp;
+ config->guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp;
else if (arg.equals_insensitive("ehcont"))
- ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont;
+ config->guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont;
else
fatal("invalid argument to /guard: " + arg);
}
}
// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
-void LinkerDriver::parseSubsystem(StringRef arg, WindowsSubsystem *sys,
- uint32_t *major, uint32_t *minor,
- bool *gotVersion) {
+void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
+ uint32_t *minor, bool *gotVersion) {
auto [sysStr, ver] = arg.split(',');
std::string sysStrLower = sysStr.lower();
*sys = StringSwitch<WindowsSubsystem>(sysStrLower)
// Parse a string of the form of "<from>=<to>".
// Results are directly written to Config.
-void LinkerDriver::parseAlternateName(StringRef s) {
+void parseAlternateName(StringRef s) {
auto [from, to] = s.split('=');
if (from.empty() || to.empty())
fatal("/alternatename: invalid argument: " + s);
- auto it = ctx.config.alternateNames.find(from);
- if (it != ctx.config.alternateNames.end() && it->second != to)
+ auto it = config->alternateNames.find(from);
+ if (it != config->alternateNames.end() && it->second != to)
fatal("/alternatename: conflicts: " + s);
- ctx.config.alternateNames.insert(it, std::make_pair(from, to));
+ config->alternateNames.insert(it, std::make_pair(from, to));
}
// Parse a string of the form of "<from>=<to>".
// Results are directly written to Config.
-void LinkerDriver::parseMerge(StringRef s) {
+void parseMerge(StringRef s) {
auto [from, to] = s.split('=');
if (from.empty() || to.empty())
fatal("/merge: invalid argument: " + s);
fatal("/merge: cannot merge '.rsrc' with any section");
if (from == ".reloc" || to == ".reloc")
fatal("/merge: cannot merge '.reloc' with any section");
- auto pair = ctx.config.merge.insert(std::make_pair(from, to));
+ auto pair = config->merge.insert(std::make_pair(from, to));
bool inserted = pair.second;
if (!inserted) {
StringRef existing = pair.first->second;
}
}
-void LinkerDriver::parsePDBPageSize(StringRef s) {
+void parsePDBPageSize(StringRef s) {
int v;
if (s.getAsInteger(0, v)) {
error("/pdbpagesize: invalid argument: " + s);
return;
}
- ctx.config.pdbPageSize = v;
+ config->pdbPageSize = v;
}
static uint32_t parseSectionAttributes(StringRef s) {
}
// Parses /section option argument.
-void LinkerDriver::parseSection(StringRef s) {
+void parseSection(StringRef s) {
auto [name, attrs] = s.split(',');
if (name.empty() || attrs.empty())
fatal("/section: invalid argument: " + s);
- ctx.config.section[name] = parseSectionAttributes(attrs);
+ config->section[name] = parseSectionAttributes(attrs);
}
// Parses /aligncomm option argument.
-void LinkerDriver::parseAligncomm(StringRef s) {
+void parseAligncomm(StringRef s) {
auto [name, align] = s.split(',');
if (name.empty() || align.empty()) {
error("/aligncomm: invalid argument: " + s);
error("/aligncomm: invalid argument: " + s);
return;
}
- ctx.config.alignComm[std::string(name)] =
- std::max(ctx.config.alignComm[std::string(name)], 1 << v);
+ config->alignComm[std::string(name)] =
+ std::max(config->alignComm[std::string(name)], 1 << v);
}
// Parses /functionpadmin option argument.
-void LinkerDriver::parseFunctionPadMin(llvm::opt::Arg *a) {
+void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) {
StringRef arg = a->getNumValues() ? a->getValue() : "";
if (!arg.empty()) {
// Optional padding in bytes is given.
- if (arg.getAsInteger(0, ctx.config.functionPadMin))
+ if (arg.getAsInteger(0, 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 (ctx.config.machine == I386) {
- ctx.config.functionPadMin = 5;
- } else if (ctx.config.machine == AMD64) {
- ctx.config.functionPadMin = 6;
+ if (machine == I386) {
+ config->functionPadMin = 5;
+ } else if (machine == AMD64) {
+ config->functionPadMin = 6;
} else {
error("/functionpadmin: invalid argument for this machine: " + arg);
}
}
// Parses a string in the form of "EMBED[,=<integer>]|NO".
-// Results are directly written to
-// Config.
-void LinkerDriver::parseManifest(StringRef arg) {
+// Results are directly written to Config.
+void parseManifest(StringRef arg) {
if (arg.equals_insensitive("no")) {
- ctx.config.manifest = Configuration::No;
+ config->manifest = Configuration::No;
return;
}
if (!arg.startswith_insensitive("embed"))
fatal("invalid option " + arg);
- ctx.config.manifest = Configuration::Embed;
+ 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, ctx.config.manifestID))
+ if (arg.getAsInteger(0, config->manifestID))
fatal("invalid option " + arg);
}
// Parses a string in the form of "level=<string>|uiAccess=<string>|NO".
// Results are directly written to Config.
-void LinkerDriver::parseManifestUAC(StringRef arg) {
+void parseManifestUAC(StringRef arg) {
if (arg.equals_insensitive("no")) {
- ctx.config.manifestUAC = false;
+ config->manifestUAC = false;
return;
}
for (;;) {
return;
if (arg.startswith_insensitive("level=")) {
arg = arg.substr(strlen("level="));
- std::tie(ctx.config.manifestLevel, arg) = arg.split(" ");
+ std::tie(config->manifestLevel, arg) = arg.split(" ");
continue;
}
if (arg.startswith_insensitive("uiaccess=")) {
arg = arg.substr(strlen("uiaccess="));
- std::tie(ctx.config.manifestUIAccess, arg) = arg.split(" ");
+ std::tie(config->manifestUIAccess, arg) = arg.split(" ");
continue;
}
fatal("invalid option " + arg);
// Parses a string in the form of "cd|net[,(cd|net)]*"
// Results are directly written to Config.
-void LinkerDriver::parseSwaprun(StringRef arg) {
+void parseSwaprun(StringRef arg) {
do {
auto [swaprun, newArg] = arg.split(',');
if (swaprun.equals_insensitive("cd"))
- ctx.config.swaprunCD = true;
+ config->swaprunCD = true;
else if (swaprun.equals_insensitive("net"))
- ctx.config.swaprunNet = true;
+ config->swaprunNet = true;
else if (swaprun.empty())
error("/swaprun: missing argument");
else
};
}
-std::string LinkerDriver::createDefaultXml() {
+static std::string createDefaultXml() {
std::string ret;
raw_string_ostream os(ret);
os << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
<< "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n"
<< " manifestVersion=\"1.0\">\n";
- if (ctx.config.manifestUAC) {
+ if (config->manifestUAC) {
os << " <trustInfo>\n"
<< " <security>\n"
<< " <requestedPrivileges>\n"
- << " <requestedExecutionLevel level=" << ctx.config.manifestLevel
- << " uiAccess=" << ctx.config.manifestUIAccess << "/>\n"
+ << " <requestedExecutionLevel level=" << config->manifestLevel
+ << " uiAccess=" << config->manifestUIAccess << "/>\n"
<< " </requestedPrivileges>\n"
<< " </security>\n"
<< " </trustInfo>\n";
}
- for (auto manifestDependency : ctx.config.manifestDependencies) {
+ for (auto manifestDependency : config->manifestDependencies) {
os << " <dependency>\n"
<< " <dependentAssembly>\n"
<< " <assemblyIdentity " << manifestDependency << " />\n"
return os.str();
}
-std::string
-LinkerDriver::createManifestXmlWithInternalMt(StringRef defaultXml) {
+static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
std::unique_ptr<MemoryBuffer> defaultXmlCopy =
MemoryBuffer::getMemBufferCopy(defaultXml);
fatal("internal manifest tool failed on default xml: " +
toString(std::move(e)));
- for (StringRef filename : ctx.config.manifestInput) {
+ for (StringRef filename : config->manifestInput) {
std::unique_ptr<MemoryBuffer> manifest =
check(MemoryBuffer::getFile(filename));
// Call takeBuffer to include in /reproduce: output if applicable.
- if (auto e = merger.merge(takeBuffer(std::move(manifest))))
+ if (auto e = merger.merge(driver->takeBuffer(std::move(manifest))))
fatal("internal manifest tool failed on file " + filename + ": " +
toString(std::move(e)));
}
return std::string(merger.getMergedManifest().get()->getBuffer());
}
-std::string
-LinkerDriver::createManifestXmlWithExternalMt(StringRef defaultXml) {
+static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
// Create the default manifest file as a temporary file.
TemporaryFile Default("defaultxml", "manifest");
std::error_code ec;
Executor e("mt.exe");
e.add("/manifest");
e.add(Default.path);
- for (StringRef filename : ctx.config.manifestInput) {
+ for (StringRef filename : config->manifestInput) {
e.add("/manifest");
e.add(filename);
// Manually add the file to the /reproduce: tar if needed.
- if (tar)
+ if (driver->tar)
if (auto mbOrErr = MemoryBuffer::getFile(filename))
- takeBuffer(std::move(*mbOrErr));
+ driver->takeBuffer(std::move(*mbOrErr));
}
e.add("/nologo");
e.add("/out:" + StringRef(user.path));
->getBuffer());
}
-std::string LinkerDriver::createManifestXml() {
+static std::string createManifestXml() {
std::string defaultXml = createDefaultXml();
- if (ctx.config.manifestInput.empty())
+ if (config->manifestInput.empty())
return defaultXml;
if (windows_manifest::isAvailable())
return createManifestXmlWithExternalMt(defaultXml);
}
-std::unique_ptr<WritableMemoryBuffer>
-LinkerDriver::createMemoryBufferForManifestRes(size_t manifestSize) {
+static std::unique_ptr<WritableMemoryBuffer>
+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, ctx.config.outputFile +
+ return WritableMemoryBuffer::getNewMemBuffer(resSize, config->outputFile +
".manifest.res");
}
buf += object::WIN_RES_NULL_ENTRY_SIZE;
}
-static void writeResEntryHeader(char *&buf, size_t manifestSize,
- int manifestID) {
+static void writeResEntryHeader(char *&buf, size_t manifestSize) {
// Write the prefix.
auto *prefix = reinterpret_cast<object::WinResHeaderPrefix *>(buf);
prefix->DataSize = manifestSize;
// Write the Type/Name IDs.
auto *iDs = reinterpret_cast<object::WinResIDs *>(buf);
iDs->setType(RT_MANIFEST);
- iDs->setName(manifestID);
+ iDs->setName(config->manifestID);
buf += sizeof(object::WinResIDs);
// Write the suffix.
}
// Create a resource file containing a manifest XML.
-std::unique_ptr<MemoryBuffer> LinkerDriver::createManifestRes() {
+std::unique_ptr<MemoryBuffer> createManifestRes() {
std::string manifest = createManifestXml();
std::unique_ptr<WritableMemoryBuffer> res =
char *buf = res->getBufferStart();
writeResFileHeader(buf);
- writeResEntryHeader(buf, manifest.size(), ctx.config.manifestID);
+ writeResEntryHeader(buf, manifest.size());
// Copy the manifest data into the .res file.
std::copy(manifest.begin(), manifest.end(), buf);
return std::move(res);
}
-void LinkerDriver::createSideBySideManifest() {
- std::string path = std::string(ctx.config.manifestFile);
+void createSideBySideManifest() {
+ std::string path = std::string(config->manifestFile);
if (path == "")
- path = ctx.config.outputFile + ".manifest";
+ path = config->outputFile + ".manifest";
std::error_code ec;
raw_fd_ostream out(path, ec, sys::fs::OF_TextWithCRLF);
if (ec)
// "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]"
// or "<name>=<dllname>.<name>".
// Used for parsing /export arguments.
-Export LinkerDriver::parseExport(StringRef arg) {
+Export parseExport(StringRef arg) {
Export e;
StringRef rest;
std::tie(e.name, rest) = arg.split(",");
fatal("invalid /export: " + arg);
}
-static StringRef undecorate(COFFLinkerContext &ctx, StringRef sym) {
- if (ctx.config.machine != I386)
+static StringRef undecorate(StringRef sym) {
+ if (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('@') && !ctx.config.mingw)
+ if (sym.startswith("_") && sym.contains('@') && !config->mingw)
return sym;
return sym.startswith("_") ? sym.substr(1) : sym;
}
// Performs error checking on all /export arguments.
// It also sets ordinals.
-void LinkerDriver::fixupExports() {
+void fixupExports() {
// Symbol ordinals must be unique.
std::set<uint16_t> ords;
- for (Export &e : ctx.config.exports) {
+ for (Export &e : config->exports) {
if (e.ordinal == 0)
continue;
if (!ords.insert(e.ordinal).second)
fatal("duplicate export ordinal: " + e.name);
}
- for (Export &e : ctx.config.exports) {
+ for (Export &e : config->exports) {
if (!e.forwardTo.empty()) {
- e.exportName = undecorate(ctx, e.name);
+ e.exportName = undecorate(e.name);
} else {
- e.exportName = undecorate(ctx, e.extName.empty() ? e.name : e.extName);
+ e.exportName = undecorate(e.extName.empty() ? e.name : e.extName);
}
}
- if (ctx.config.killAt && ctx.config.machine == I386) {
- for (Export &e : ctx.config.exports) {
+ if (config->killAt && config->machine == I386) {
+ for (Export &e : config->exports) {
e.name = killAt(e.name, true);
e.exportName = killAt(e.exportName, false);
e.extName = killAt(e.extName, true);
}
// Uniquefy by name.
- DenseMap<StringRef, Export *> map(ctx.config.exports.size());
+ DenseMap<StringRef, Export *> map(config->exports.size());
std::vector<Export> v;
- for (Export &e : ctx.config.exports) {
+ for (Export &e : config->exports) {
auto pair = map.insert(std::make_pair(e.exportName, &e));
bool inserted = pair.second;
if (inserted) {
continue;
warn("duplicate /export option: " + e.name);
}
- ctx.config.exports = std::move(v);
+ config->exports = std::move(v);
// Sort by name.
- llvm::sort(ctx.config.exports, [](const Export &a, const Export &b) {
+ llvm::sort(config->exports, [](const Export &a, const Export &b) {
return a.exportName < b.exportName;
});
}
-void LinkerDriver::assignExportOrdinals() {
+void assignExportOrdinals() {
// Assign unique ordinals if default (= 0).
uint32_t max = 0;
- for (Export &e : ctx.config.exports)
+ for (Export &e : config->exports)
max = std::max(max, (uint32_t)e.ordinal);
- for (Export &e : ctx.config.exports)
+ for (Export &e : config->exports)
if (e.ordinal == 0)
e.ordinal = ++max;
if (max > std::numeric_limits<uint16_t>::max())
// Parses a string in the form of "key=value" and check
// if value matches previous values for the same key.
-void LinkerDriver::checkFailIfMismatch(StringRef arg, InputFile *source) {
+void checkFailIfMismatch(StringRef arg, InputFile *source) {
auto [k, v] = arg.split('=');
if (k.empty() || v.empty())
fatal("/failifmismatch: invalid argument: " + arg);
- std::pair<StringRef, InputFile *> existing = ctx.config.mustMatch[k];
+ std::pair<StringRef, InputFile *> existing = config->mustMatch[k];
if (!existing.first.empty() && v != existing.first) {
std::string sourceStr = source ? toString(source) : "cmd-line";
std::string existingStr =
existingStr + " has value " + existing.first + "\n>>> " + sourceStr +
" has value " + v);
}
- ctx.config.mustMatch[k] = {v, source};
+ 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 LinkerDriver::convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
- ArrayRef<ObjFile *> objs) {
- object::WindowsResourceParser parser(/* MinGW */ ctx.config.mingw);
+MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
+ ArrayRef<ObjFile *> objs) {
+ object::WindowsResourceParser parser(/* MinGW */ config->mingw);
std::vector<std::string> duplicates;
for (MemoryBufferRef mb : mbs) {
fatal(toString(std::move(ec)));
}
- if (ctx.config.mingw)
+ if (config->mingw)
parser.cleanUpManifests(duplicates);
for (const auto &dupeDiag : duplicates)
- if (ctx.config.forceMultipleRes)
+ if (config->forceMultipleRes)
warn(dupeDiag);
else
error(dupeDiag);
Expected<std::unique_ptr<MemoryBuffer>> e =
- llvm::object::writeWindowsResourceCOFF(ctx.config.machine, parser,
- ctx.config.timestamp);
+ llvm::object::writeWindowsResourceCOFF(config->machine, parser,
+ config->timestamp);
if (!e)
fatal("failed to write .res to COFF: " + toString(e.takeError()));
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) {
return cl::TokenizeWindowsCommandLine;
}
-ArgParser::ArgParser(COFFLinkerContext &c) : ctx(c) {}
-
// Parses a given list of options.
opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
// Make InputArgList from string vectors.
// 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 =
- ctx.optTable.ParseArgs(argv, missingIndex, missingCount);
+ opt::InputArgList args = optTable.ParseArgs(argv, missingIndex, missingCount);
// Expand response files (arguments in the form of @<filename>) and insert
// flags from %LINK% and %_LINK_%, and then parse the argument again.
if (!args.hasArg(OPT_lldignoreenv))
addLINK(expandedArgv);
cl::ExpandResponseFiles(saver(), getQuotingStyle(args), expandedArgv);
- args = ctx.optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(),
- missingIndex, missingCount);
+ args = 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()) {
// Save the command line after response file expansion so we can write it to
// the PDB if necessary. Mimic MSVC, which skips input files.
- ctx.config.argv = {argv[0]};
+ config->argv = {argv[0]};
for (opt::Arg *arg : args) {
if (arg->getOption().getKind() != opt::Option::InputClass) {
- ctx.config.argv.push_back(args.getArgString(arg->getIndex()));
+ config->argv.push_back(args.getArgString(arg->getIndex()));
}
}
for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) {
std::string nearest;
- if (ctx.optTable.findNearest(arg->getAsString(args), nearest) > 1)
+ if (optTable.findNearest(arg->getAsString(args), nearest) > 1)
warn("ignoring unknown argument '" + arg->getAsString(args) + "'");
else
warn("ignoring unknown argument '" + arg->getAsString(args) +
unsigned missingIndex;
unsigned missingCount;
- result.args = ctx.optTable.ParseArgs(rest, missingIndex, missingCount);
+ result.args = optTable.ParseArgs(rest, missingIndex, missingCount);
if (missingCount)
fatal(Twine(result.args.getArgString(missingIndex)) + ": missing argument");
return std::vector<const char *>(tokens.begin(), tokens.end());
}
-void LinkerDriver::printHelp(const char *argv0) {
- ctx.optTable.printHelp(lld::outs(),
- (std::string(argv0) + " [options] file...").c_str(),
- "LLVM Linker", false);
+void printHelp(const char *argv0) {
+ optTable.printHelp(lld::outs(),
+ (std::string(argv0) + " [options] file...").c_str(),
+ "LLVM Linker", false);
}
} // namespace coff
class ICF {
public:
- ICF(COFFLinkerContext &c) : ctx(c){};
+ ICF(COFFLinkerContext &c, ICFLevel icfLevel) : icfLevel(icfLevel), ctx(c){};
void run();
private:
std::vector<SectionChunk *> chunks;
int cnt = 0;
std::atomic<bool> repeat = {false};
+ ICFLevel icfLevel = ICFLevel::All;
COFFLinkerContext &ctx;
};
return false;
// Under regular (not safe) ICF, all code sections are eligible.
- if ((ctx.config.doICF == ICFLevel::All) &&
+ if ((icfLevel == ICFLevel::All) &&
c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
return true;
}
// Entry point to ICF.
-void doICF(COFFLinkerContext &ctx) { ICF(ctx).run(); }
+void doICF(COFFLinkerContext &ctx, ICFLevel icfLevel) {
+ ICF(ctx, icfLevel).run();
+}
} // namespace lld::coff
namespace lld::coff {
+class Chunk;
class COFFLinkerContext;
-void doICF(COFFLinkerContext &ctx);
+void doICF(COFFLinkerContext &ctx, ICFLevel);
} // namespace lld::coff
/// 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(COFFLinkerContext &ctx, InputFile *f,
+static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f,
Symbol *source, Symbol *target) {
if (auto *u = dyn_cast<Undefined>(source)) {
if (u->weakAlias && u->weakAlias != target) {
// of another symbol emitted near the weak symbol.
// Just use the definition from the first object file that defined
// this weak symbol.
- if (ctx.config.mingw)
+ if (config->mingw)
return;
- ctx.symtab.reportDuplicate(source, f);
+ symtab->reportDuplicate(source, f);
}
u->weakAlias = target;
}
void ArchiveFile::addMember(const Archive::Symbol &sym) {
const Archive::Child &c =
CHECK(sym.getMember(),
- "could not get the member for symbol " + toCOFFString(ctx, sym));
+ "could not get the member for symbol " + toCOFFString(sym));
// Return an empty buffer if we have already returned the same buffer.
if (!seen.insert(c.getChildOffset()).second)
return;
- ctx.driver.enqueueArchiveMember(c, sym, getName());
+ driver->enqueueArchiveMember(c, sym, getName());
}
std::vector<MemoryBufferRef> lld::coff::getArchiveMembers(Archive *file) {
// and then write it to a separate .pdb file.
// Ignore DWARF debug info unless /debug is given.
- if (!ctx.config.debug && name.startswith(".debug_"))
+ if (!config->debug && name.startswith(".debug_"))
return nullptr;
if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
guardEHContChunks.push_back(c);
else if (name == ".sxdata")
sxDataChunks.push_back(c);
- else if (ctx.config.tailMerge && sec->NumberOfRelocations == 0 &&
+ else if (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
// 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 (ctx.config.mingw && name.startswith(".weak."))
+ if (config->mingw && name.startswith(".weak."))
return nullptr;
return ctx.symtab.addUndefined(name, this, false);
}
} else if (std::optional<Symbol *> optSym =
createDefined(coffSym, comdatDefs, prevailingComdat)) {
symbols[i] = *optSym;
- if (ctx.config.mingw && prevailingComdat)
+ if (config->mingw && prevailingComdat)
recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap);
} else {
// createDefined() returns std::nullopt if a symbol belongs to a section
if (const coff_aux_section_definition *def = sym.getSectionDefinition()) {
if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
readAssociativeDefinition(sym, def);
- else if (ctx.config.mingw)
+ else if (config->mingw)
maybeAssociateSEHForMingw(sym, def, prevailingSectionMap);
}
if (sparseChunks[sym.getSectionNumber()] == pendingComdat) {
for (auto &kv : weakAliases) {
Symbol *sym = kv.first;
uint32_t idx = kv.second;
- checkAndSetWeakAlias(ctx, this, sym, symbols[idx]);
+ checkAndSetWeakAlias(&ctx.symtab, this, sym, symbols[idx]);
}
// Free the memory used by sparseChunks now that symbol loading is finished.
// 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 (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))) {
+ 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))) {
leaderSelection = selection = IMAGE_COMDAT_SELECT_SAME_SIZE;
}
// seems better though.
// (This behavior matches ModuleLinker::getComdatResult().)
if (selection != leaderSelection) {
- log(("conflicting comdat type for " + toString(ctx, *leader) + ": " +
+ log(("conflicting comdat type for " + toString(*leader) + ": " +
Twine((int)leaderSelection) + " in " + toString(leader->getFile()) +
" and " + Twine((int)selection) + " in " + toString(this))
.str());
case IMAGE_COMDAT_SELECT_SAME_SIZE:
if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) {
- if (!ctx.config.mingw) {
+ if (!config->mingw) {
ctx.symtab.reportDuplicate(leader, this);
} else {
const coff_aux_section_definition *leaderDef = nullptr;
if (sym.isExternal())
return ctx.symtab.addAbsolute(name, sym);
- return make<DefinedAbsolute>(ctx, name, sym);
+ return make<DefinedAbsolute>(name, sym);
}
int32_t sectionNumber = sym.getSectionNumber();
// 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 (!ctx.config.debug)
+ if (!config->debug)
return;
bool isPCH = false;
if (!dwarf)
return std::nullopt;
}
- if (ctx.config.machine == I386)
+ if (config->machine == I386)
var.consume_front("_");
std::optional<std::pair<std::string, unsigned>> ret =
dwarf->getVariableLoc(var);
auto it = ctx.pdbInputFileInstances.emplace(*p, nullptr);
if (!it.second)
return; // already scheduled for load
- ctx.driver.enqueuePDB(*p);
+ 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<const coff_import_header *>(buf);
bool lazy)
: InputFile(ctx, BitcodeKind, mb, lazy) {
std::string path = mb.getBufferIdentifier().str();
- if (ctx.config.thinLTOIndexOnly)
- path = replaceThinLTOSuffix(mb.getBufferIdentifier(),
- ctx.config.thinLTOObjectSuffixReplace.first,
- ctx.config.thinLTOObjectSuffixReplace.second);
+ if (config->thinLTOIndexOnly)
+ path = replaceThinLTOSuffix(mb.getBufferIdentifier());
// ThinLTO assumes that all MemoryBufferRefs given to it have a unique
// name. If two archives define two members with the same name, this
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<std::pair<Symbol *, bool>> comdat(obj->getComdatTable().size());
for (size_t i = 0; i != obj->getComdatTable().size(); ++i)
// FIXME: Check nodeduplicate
Symbol *sym;
SectionChunk *fakeSC = nullptr;
if (objSym.isExecutable())
- fakeSC = &ctx.ltoTextSectionChunk->chunk;
+ fakeSC = <oTextSectionChunk.chunk;
else
- fakeSC = &ctx.ltoDataSectionChunk->chunk;
+ fakeSC = <oDataSectionChunk.chunk;
if (objSym.isUndefined()) {
sym = ctx.symtab.addUndefined(symName, this, false);
} else if (objSym.isCommon()) {
sym = ctx.symtab.addUndefined(symName, this, true);
std::string fallback = std::string(objSym.getCOFFWeakExternalFallback());
Symbol *alias = ctx.symtab.addUndefined(saver.save(fallback));
- checkAndSetWeakAlias(ctx, this, sym, alias);
+ checkAndSetWeakAlias(&ctx.symtab, this, sym, alias);
} else if (comdatIndex != -1) {
if (symName == obj->getComdatTable()[comdatIndex].first) {
sym = comdat[comdatIndex].first;
}
symbols.push_back(sym);
if (objSym.isUsed())
- ctx.config.gcroot.push_back(sym);
+ config->gcroot.push_back(sym);
}
directives = obj->getCOFFLinkerOpts();
}
}
}
-std::string lld::coff::replaceThinLTOSuffix(StringRef path, StringRef suffix,
- StringRef repl) {
+std::string lld::coff::replaceThinLTOSuffix(StringRef path) {
+ StringRef suffix = config->thinLTOObjectSuffixReplace.first;
+ StringRef repl = config->thinLTOObjectSuffixReplace.second;
+
if (path.consume_back(suffix))
return (path + repl).str();
return std::string(path);
// for details about the format.
class ImportFile : public InputFile {
public:
- explicit ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m);
+ explicit ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m)
+ : InputFile(ctx, ImportKind, m) {}
static bool classof(const InputFile *f) { return f->kind() == ImportKind; }
// 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;
- bool thunkLive;
+ bool live = !config->doGC;
+ bool thunkLive = !config->doGC;
};
// Used for LTO.
return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
}
-// 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);
+std::string replaceThinLTOSuffix(StringRef path);
} // namespace coff
std::string toString(const coff::InputFile *file);
// Construct a map from symbols to their stringified representations.
static DenseMap<DefinedRegular *, std::string>
-getSymbolStrings(const COFFLinkerContext &ctx,
- ArrayRef<DefinedRegular *> syms) {
+getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
std::vector<std::string> str(syms.size());
parallelFor((size_t)0, syms.size(), [&](size_t i) {
raw_string_ostream os(str[i]);
writeHeader(os, syms[i]->getRVA(), 0, 0);
- os << indent16 << toString(ctx, *syms[i]);
+ os << indent16 << toString(*syms[i]);
});
DenseMap<DefinedRegular *, std::string> ret;
}
void lld::coff::writeLLDMapFile(const COFFLinkerContext &ctx) {
- if (ctx.config.lldmapFile.empty())
+ if (config->lldmapFile.empty())
return;
std::error_code ec;
- raw_fd_ostream os(ctx.config.lldmapFile, ec, sys::fs::OF_None);
+ raw_fd_ostream os(config->lldmapFile, ec, sys::fs::OF_None);
if (ec)
- fatal("cannot open " + ctx.config.lldmapFile + ": " + ec.message());
+ fatal("cannot open " + config->lldmapFile + ": " + ec.message());
// Collect symbol info that we want to print out.
std::vector<DefinedRegular *> syms = getSymbols(ctx);
SymbolMapTy sectionSyms = getSectionSyms(syms);
- DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(ctx, syms);
+ DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
// Print out the header line.
os << "Address Size Align Out In Symbol\n";
//===----------------------------------------------------------------------===//
#include "LTO.h"
-#include "COFFLinkerContext.h"
#include "Config.h"
#include "InputFiles.h"
#include "Symbols.h"
return ret;
}
-std::string BitcodeCompiler::getThinLTOOutputFile(StringRef path) {
+static std::string getThinLTOOutputFile(StringRef path) {
return lto::getThinLTOOutputFile(
- std::string(path), std::string(ctx.config.thinLTOPrefixReplace.first),
- std::string(ctx.config.thinLTOPrefixReplace.second));
+ std::string(path), std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second));
}
-lto::Config BitcodeCompiler::createConfig() {
+static lto::Config createConfig() {
lto::Config c;
c.Options = initTargetOptionsFromCodeGenFlags();
c.Options.EmitAddrsig = true;
- for (StringRef C : ctx.config.mllvmOpts)
+ for (StringRef C : config->mllvmOpts)
c.MllvmArgs.emplace_back(C.str());
// Always emit a section per function/datum with LTO. LLVM LTO should get most
// 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 (ctx.config.machine == COFF::IMAGE_FILE_MACHINE_I386)
+ if (config->machine == COFF::IMAGE_FILE_MACHINE_I386)
c.RelocModel = Reloc::Static;
else
c.RelocModel = Reloc::PIC_;
c.DisableVerify = true;
#endif
c.DiagHandler = diagnosticHandler;
- c.OptLevel = ctx.config.ltoo;
+ c.OptLevel = config->ltoo;
c.CPU = getCPUStr();
c.MAttrs = getMAttrs();
- c.CGOptLevel = args::getCGOptLevel(ctx.config.ltoo);
- c.AlwaysEmitRegularLTOObj = !ctx.config.ltoObjPath.empty();
- c.DebugPassManager = ctx.config.ltoDebugPassManager;
- c.CSIRProfile = std::string(ctx.config.ltoCSProfileFile);
- c.RunCSIRInstr = ctx.config.ltoCSProfileGenerate;
- c.PGOWarnMismatch = ctx.config.ltoPGOWarnMismatch;
-
- if (ctx.config.saveTemps)
- checkError(c.addSaveTemps(std::string(ctx.config.outputFile) + ".",
+ c.CGOptLevel = args::getCGOptLevel(config->ltoo);
+ c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
+ c.DebugPassManager = config->ltoDebugPassManager;
+ c.CSIRProfile = std::string(config->ltoCSProfileFile);
+ c.RunCSIRInstr = config->ltoCSProfileGenerate;
+ c.PGOWarnMismatch = config->ltoPGOWarnMismatch;
+
+ if (config->saveTemps)
+ checkError(c.addSaveTemps(std::string(config->outputFile) + ".",
/*UseInputModulePath*/ true));
return c;
}
-BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) {
+BitcodeCompiler::BitcodeCompiler() {
// Initialize indexFile.
- if (!ctx.config.thinLTOIndexOnlyArg.empty())
- indexFile = openFile(ctx.config.thinLTOIndexOnlyArg);
+ if (!config->thinLTOIndexOnlyArg.empty())
+ indexFile = openFile(config->thinLTOIndexOnlyArg);
// Initialize ltoObj.
lto::ThinBackend backend;
- if (ctx.config.thinLTOIndexOnly) {
+ if (config->thinLTOIndexOnly) {
auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
backend = lto::createWriteIndexesThinBackend(
- std::string(ctx.config.thinLTOPrefixReplace.first),
- std::string(ctx.config.thinLTOPrefixReplace.second),
- ctx.config.thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
+ std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second),
+ config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
} else {
backend = lto::createInProcessThinBackend(
- llvm::heavyweight_hardware_concurrency(ctx.config.thinLTOJobs));
+ llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
}
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
- ctx.config.ltoPartitions);
+ config->ltoPartitions);
}
BitcodeCompiler::~BitcodeCompiler() = default;
std::vector<Symbol *> symBodies = f.getSymbols();
std::vector<lto::SymbolResolution> resols(symBodies.size());
- if (ctx.config.thinLTOIndexOnly)
+ if (config->thinLTOIndexOnly)
thinIndices.insert(obj.getName());
// Provide a resolution to the LTO API for each symbol.
// Merge all the bitcode files we have seen, codegen the result
// and return the resulting objects.
-std::vector<InputFile *> BitcodeCompiler::compile() {
+std::vector<InputFile *> BitcodeCompiler::compile(COFFLinkerContext &ctx) {
unsigned maxTasks = ltoObj->getMaxTasks();
buf.resize(maxTasks);
files.resize(maxTasks);
// native object files for ThinLTO incremental builds. If a path was
// specified, configure LTO to use it as the cache directory.
FileCache cache;
- if (!ctx.config.ltoCache.empty())
- cache = check(localCache("ThinLTO", "Thin", ctx.config.ltoCache,
+ if (!config->ltoCache.empty())
+ cache = check(localCache("ThinLTO", "Thin", config->ltoCache,
[&](size_t task, const Twine &moduleName,
std::unique_ptr<MemoryBuffer> mb) {
files[task] = std::move(mb);
for (StringRef s : thinIndices) {
std::string path = getThinLTOOutputFile(s);
openFile(path + ".thinlto.bc");
- if (ctx.config.thinLTOEmitImportsFiles)
+ if (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 (ctx.config.thinLTOIndexOnly) {
- if (!ctx.config.ltoObjPath.empty())
- saveBuffer(buf[0].second, ctx.config.ltoObjPath);
+ if (config->thinLTOIndexOnly) {
+ if (!config->ltoObjPath.empty())
+ saveBuffer(buf[0].second, config->ltoObjPath);
if (indexFile)
indexFile->close();
return {};
}
- if (!ctx.config.ltoCache.empty())
- pruneCache(ctx.config.ltoCache, ctx.config.ltoCachePolicy, files);
+ if (!config->ltoCache.empty())
+ pruneCache(config->ltoCache, config->ltoCachePolicy, files);
std::vector<InputFile *> ret;
for (unsigned i = 0; i != maxTasks; ++i) {
StringRef ltoObjName;
if (bitcodeFilePath == "ld-temp.o") {
ltoObjName =
- saver().save(Twine(ctx.config.outputFile) + ".lto" +
+ saver().save(Twine(config->outputFile) + ".lto" +
(i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj");
} else {
StringRef directory = sys::path::parent_path(bitcodeFilePath);
StringRef baseName = sys::path::filename(bitcodeFilePath);
- StringRef outputFileBaseName = sys::path::filename(ctx.config.outputFile);
+ StringRef outputFileBaseName = sys::path::filename(config->outputFile);
SmallString<64> path;
sys::path::append(path, directory,
outputFileBaseName + ".lto." + baseName);
sys::path::remove_dots(path, true);
ltoObjName = saver().save(path.str());
}
- if (ctx.config.saveTemps)
+ if (config->saveTemps)
saveBuffer(buf[i].second, ltoObjName);
ret.push_back(make<ObjFile>(ctx, MemoryBufferRef(objBuf, ltoObjName)));
}
#include <vector>
namespace llvm::lto {
-struct Config;
class LTO;
}
class BitcodeCompiler {
public:
- BitcodeCompiler(COFFLinkerContext &ctx);
+ BitcodeCompiler();
~BitcodeCompiler();
void add(BitcodeFile &f);
- std::vector<InputFile *> compile();
+ std::vector<InputFile *> compile(COFFLinkerContext &ctx);
private:
std::unique_ptr<llvm::lto::LTO> ltoObj;
std::vector<std::string> file_names;
std::unique_ptr<llvm::raw_fd_ostream> indexFile;
llvm::DenseSet<StringRef> thinIndices;
-
- std::string getThinLTOOutputFile(StringRef path);
- llvm::lto::Config createConfig();
-
- COFFLinkerContext &ctx;
};
}
time->tm_sec, time->tm_year + 1900);
}
-static void sortUniqueSymbols(std::vector<Defined *> &syms,
- uint64_t imageBase) {
+static void sortUniqueSymbols(std::vector<Defined *> &syms) {
// Build helper vector
using SortEntry = std::pair<Defined *, size_t>;
std::vector<SortEntry> v;
v.erase(end, v.end());
// Sort by RVA then original order
- parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) {
- // Add config.imageBase to avoid comparing "negative" RVAs.
+ parallelSort(v, [](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 = imageBase + a.first->getRVA();
- uint64_t rvab = imageBase + b.first->getRVA();
+ uint64_t rvaa = config->imageBase + a.first->getRVA();
+ uint64_t rvab = config->imageBase + b.first->getRVA();
return rvaa < rvab || (rvaa == rvab && a.second < b.second);
});
syms.push_back(impSym);
}
- sortUniqueSymbols(syms, ctx.config.imageBase);
- sortUniqueSymbols(staticSyms, ctx.config.imageBase);
+ sortUniqueSymbols(syms);
+ sortUniqueSymbols(staticSyms);
}
// Construct a map from symbols to their stringified representations.
os << " ";
os << left_justify(sym->getName(), 26);
os << " ";
- os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16);
+ os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16);
if (!fileDescr.empty()) {
os << " "; // FIXME : Handle "f" and "i" flags sometimes generated
// by link.exe in those spaces
}
void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
- if (ctx.config.mapFile.empty())
+ if (config->mapFile.empty())
return;
std::error_code ec;
- raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None);
+ raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
if (ec)
- fatal("cannot open " + ctx.config.mapFile + ": " + ec.message());
+ fatal("cannot open " + config->mapFile + ": " + ec.message());
ScopedTimer t1(ctx.totalMapTimer);
t3.stop();
ScopedTimer t4(ctx.writeTimer);
- SmallString<128> AppName = sys::path::filename(ctx.config.outputFile);
+ SmallString<128> AppName = sys::path::filename(config->outputFile);
sys::path::replace_extension(AppName, "");
// Print out the file header
os << " " << AppName << "\n";
os << "\n";
- os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8)
- << " (";
- if (ctx.config.repro) {
+ os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " (";
+ if (config->repro) {
os << "Repro mode";
} else {
- writeFormattedTimestamp(os, ctx.config.timestamp);
+ writeFormattedTimestamp(os, config->timestamp);
}
os << ")\n";
os << "\n";
os << " Preferred load address is "
- << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n";
+ << format_hex_no_prefix(config->imageBase, 16) << "\n";
os << "\n";
// Print out section table.
uint16_t entrySecIndex = 0;
uint64_t entryAddress = 0;
- if (!ctx.config.noEntry) {
- Defined *entry = dyn_cast_or_null<Defined>(ctx.config.entry);
+ if (!config->noEntry) {
+ Defined *entry = dyn_cast_or_null<Defined>(config->entry);
if (entry) {
Chunk *chunk = entry->getChunk();
entrySecIndex = chunk->getOutputSectionIdx();
os << staticSymStr[sym] << '\n';
// Print out the exported functions
- if (ctx.config.mapInfo) {
+ if (config->mapInfo) {
os << "\n";
os << " Exports\n";
os << "\n";
os << " ordinal name\n\n";
- for (Export &e : ctx.config.exports) {
+ for (Export &e : config->exports) {
os << format(" %7d", e.ordinal) << " " << e.name << "\n";
if (!e.extName.empty() && e.extName != e.name)
os << " exported name: " << e.extName << "\n";
};
// Add GC root chunks.
- for (Symbol *b : ctx.config.gcroot)
+ for (Symbol *b : config->gcroot)
addSym(b);
while (!worklist.empty()) {
using namespace lld::coff;
AutoExporter::AutoExporter(
- COFFLinkerContext &ctx,
const llvm::DenseSet<StringRef> &manualExcludeSymbols)
- : manualExcludeSymbols(manualExcludeSymbols), ctx(ctx) {
+ : manualExcludeSymbols(manualExcludeSymbols) {
excludeLibs = {
"libgcc",
"libgcc_s",
"_NULL_THUNK_DATA",
};
- if (ctx.config.machine == I386) {
+ if (config->machine == I386) {
excludeSymbols = {
"__NULL_IMPORT_DESCRIPTOR",
"__pei386_runtime_relocator",
excludeSymbols.insert(symbol);
}
-bool AutoExporter::shouldExport(Defined *sym) const {
+bool AutoExporter::shouldExport(const COFFLinkerContext &ctx,
+ Defined *sym) const {
if (!sym || !sym->getChunk())
return false;
return !excludeObjects.count(fileName);
}
-void lld::coff::writeDefFile(StringRef name,
- const std::vector<Export> &exports) {
+void lld::coff::writeDefFile(StringRef name) {
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 (const Export &e : exports) {
+ for (Export &e : config->exports) {
os << " " << e.exportName << " "
<< "@" << e.ordinal;
if (auto *def = dyn_cast_or_null<Defined>(e.sym)) {
}
}
-static StringRef mangle(Twine sym, MachineTypes machine) {
- assert(machine != IMAGE_FILE_MACHINE_UNKNOWN);
- if (machine == I386)
+static StringRef mangle(Twine sym) {
+ assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN);
+ if (config->machine == I386)
return saver().save("_" + sym);
return saver().save(sym);
}
if (!sym)
continue;
- Symbol *real =
- ctx.symtab.addUndefined(mangle("__real_" + name, ctx.config.machine));
- Symbol *wrap =
- ctx.symtab.addUndefined(mangle("__wrap_" + name, ctx.config.machine));
+ Symbol *real = ctx.symtab.addUndefined(mangle("__real_" + name));
+ Symbol *wrap = ctx.symtab.addUndefined(mangle("__wrap_" + name));
v.push_back({sym, real, wrap});
// These symbols may seem undefined initially, but don't bail out
// referenced it or not, though.)
if (imp) {
DefinedLocalImport *wrapimp = make<DefinedLocalImport>(
- ctx, saver().save("__imp_" + w.wrap->getName()), d);
+ saver().save("__imp_" + w.wrap->getName()), d);
ctx.symtab.localImportChunks.push_back(wrapimp->getChunk());
map[imp] = wrapimp;
}
// symbols for MinGW.
class AutoExporter {
public:
- AutoExporter(COFFLinkerContext &ctx,
- const llvm::DenseSet<StringRef> &manualExcludeSymbols);
+ AutoExporter(const llvm::DenseSet<StringRef> &manualExcludeSymbols);
void addWholeArchive(StringRef path);
void addExcludedSymbol(StringRef symbol);
const llvm::DenseSet<StringRef> &manualExcludeSymbols;
- bool shouldExport(Defined *sym) const;
-
-private:
- COFFLinkerContext &ctx;
+ bool shouldExport(const COFFLinkerContext &ctx, Defined *sym) const;
};
-void writeDefFile(StringRef name, const std::vector<Export> &exports);
+void writeDefFile(StringRef name);
// The -wrap option is a feature to rename symbols so that you can write
// wrappers for existing functions. If you pass `-wrap:foo`, all
using llvm::object::coff_section;
using llvm::pdb::StringTableFixup;
+static ExitOnError exitOnErr;
+
namespace {
class DebugSHandler;
void printStats();
private:
- void pdbMakeAbsolute(SmallVectorImpl<char> &fileName);
- void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
- TpiSource *source);
- void addCommonLinkerModuleSymbols(StringRef path,
- pdb::DbiModuleDescriptorBuilder &mod);
pdb::PDBFileBuilder builder;
// 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
-void PDBLinker::pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
+static void pdbMakeAbsolute(SmallVectorImpl<char> &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
// 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 (ctx.config.pdbSourcePath.empty()) {
+ if (config->pdbSourcePath.empty()) {
sys::path::native(fileName);
sys::fs::make_absolute(fileName);
sys::path::remove_dots(fileName, true);
// 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 = ctx.config.pdbSourcePath;
+ SmallString<128> absoluteFileName = config->pdbSourcePath;
sys::path::Style guessedStyle = absoluteFileName.startswith("/")
? sys::path::Style::posix
: sys::path::Style::windows;
}
/// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32
-void PDBLinker::translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
- TpiSource *source) {
+static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
+ TypeMerger &tMerger, TpiSource *source) {
RecordPrefix *prefix = reinterpret_cast<RecordPrefix *>(recordData.data());
SymbolKind kind = symbolKind(recordData);
// in both cases we just need the second type index.
if (!ti->isSimple() && !ti->isNoneType()) {
TypeIndex newType = TypeIndex(SimpleTypeKind::NotTranslated);
- if (ctx.config.debugGHashes) {
+ if (config->debugGHashes) {
auto idToType = tMerger.funcIdToType.find(*ti);
if (idToType != tMerger.funcIdToType.end())
newType = idToType->second;
// An object file may have S_xxx_ID symbols, but these get converted to
// "real" symbols in a PDB.
- translateIdSymbols(recordBytes, source);
+ translateIdSymbols(recordBytes, tMerger, source);
}
void PDBLinker::analyzeSymbolSubsection(
Error PDBLinker::writeAllModuleSymbolRecords(ObjFile *file,
BinaryStreamWriter &writer) {
- ExitOnError exitOnErr;
std::vector<uint8_t> storage;
SmallVector<uint32_t, 4> scopes;
contents = SectionChunk::consumeDebugMagic(contents, ".debug$S");
DebugSubsectionArray subsections;
BinaryStreamReader reader(contents, support::little);
- ExitOnError exitOnErr;
exitOnErr(reader.readArray(subsections, contents.size()));
debugChunk->sortRelocations();
TpiSource *source = debugChunk->file->debugTypesObj;
DebugInlineeLinesSubsectionRef inlineeLines;
BinaryStreamReader storageReader(relocatedBytes, support::little);
- ExitOnError exitOnErr;
exitOnErr(inlineeLines.initialize(storageReader));
for (const InlineeSourceLine &line : inlineeLines) {
TypeIndex &inlinee = *const_cast<TypeIndex *>(&line.Header->Inlinee);
return;
}
- ExitOnError exitOnErr;
-
// Handle FPO data. Each subsection begins with a single image base
// relocation, which is then added to the RvaStart of each frame data record
// when it is added to the PDB. The string table indices for the FPO program
for (const FileChecksumEntry &fc : checksums) {
SmallString<128> filename =
exitOnErr(cvStrTab.getString(fc.FileNameOffset));
- linker.pdbMakeAbsolute(filename);
+ pdbMakeAbsolute(filename);
exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename));
newChecksums->addChecksum(filename, fc.Kind, fc.Checksum);
}
file.moduleDBI->addDebugSubsection(std::move(newChecksums));
}
-static void warnUnusable(InputFile *f, Error e, bool shouldWarn) {
- if (!shouldWarn) {
+static void warnUnusable(InputFile *f, Error e) {
+ if (!config->warnDebugInfoUnusable) {
consumeError(std::move(e));
return;
}
return;
ScopedTimer t(ctx.symbolMergingTimer);
- ExitOnError exitOnErr;
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
DebugSHandler dsh(*this, *source->file, source);
// Now do all live .debug$S and .debug$F sections.
void PDBLinker::createModuleDBI(ObjFile *file) {
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
SmallString<128> objName;
- ExitOnError exitOnErr;
bool inArchive = !file->parentName.empty();
objName = inArchive ? file->parentName : file->getName();
// 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 (!ctx.config.debugGHashes) {
+ if (!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),
- ctx.config.warnDebugInfoUnusable);
+ warnUnusable(source->file, std::move(e));
return;
}
}
// If type merging failed, ignore the symbols.
Error typeError = std::move(source->typeMergingError);
if (typeError) {
- warnUnusable(source->file, std::move(typeError),
- ctx.config.warnDebugInfoUnusable);
+ warnUnusable(source->file, std::move(typeError));
return;
}
tMerger.sortDependencies();
// Merge type information from input files using global type hashing.
- if (ctx.config.debugGHashes)
+ if (config->debugGHashes)
tMerger.mergeTypesWithGHash();
// Merge dependencies and then regular objects.
// Construct TPI and IPI stream contents.
ScopedTimer t2(ctx.tpiStreamLayoutTimer);
-
// Collect all the merged types.
- if (ctx.config.debugGHashes) {
+ if (config->debugGHashes) {
addGHashTypeInfo(ctx, builder);
} else {
addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable());
}
t2.stop();
- if (ctx.config.showSummary) {
+ if (config->showSummary) {
for (TpiSource *source : ctx.tpiSourceList) {
nbTypeRecords += source->nbTypeRecords;
nbTypeRecordsBytes += source->nbTypeRecordsBytes;
StringRef name = def->getName();
if (name.data()[0] == '_' && name.data()[1] == '_') {
// Drop the '_' prefix for x86.
- if (ctx.config.machine == I386)
+ if (config->machine == I386)
name = name.drop_front(1);
if (name.startswith("__profd_") || name.startswith("__profc_") ||
name.startswith("__covrec_")) {
}
void PDBLinker::printStats() {
- if (!ctx.config.showSummary)
+ if (!config->showSummary)
return;
SmallString<256> buffer;
<< "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(), ctx.config.pdbPath);
+ tsis.back().typeIndex.getIndex(), config->pdbPath);
}
};
- if (!ctx.config.debugGHashes) {
+ if (!config->debugGHashes) {
// FIXME: Reimplement for ghash.
printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable());
printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable());
}
void PDBLinker::addNatvisFiles() {
- for (StringRef file : ctx.config.natvisFiles) {
+ for (StringRef file : config->natvisFiles) {
ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr =
MemoryBuffer::getFile(file);
if (!dataOrErr) {
std::unique_ptr<MemoryBuffer> data = std::move(*dataOrErr);
// Can't use takeBuffer() here since addInjectedSource() takes ownership.
- if (ctx.driver.tar)
- ctx.driver.tar->append(relativeToRoot(data->getBufferIdentifier()),
- data->getBuffer());
+ if (driver->tar)
+ driver->tar->append(relativeToRoot(data->getBufferIdentifier()),
+ data->getBuffer());
builder.addInjectedSource(file, std::move(data));
}
}
void PDBLinker::addNamedStreams() {
- ExitOnError exitOnErr;
- for (const auto &streamFile : ctx.config.namedStreams) {
+ for (const auto &streamFile : config->namedStreams) {
const StringRef stream = streamFile.getKey(), file = streamFile.getValue();
ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr =
MemoryBuffer::getFile(file);
}
std::unique_ptr<MemoryBuffer> data = std::move(*dataOrErr);
exitOnErr(builder.addNamedStream(stream, data->getBuffer()));
- ctx.driver.takeBuffer(std::move(data));
+ driver->takeBuffer(std::move(data));
}
}
return r;
}
-static void fillLinkerVerRecord(Compile3Sym &cs, MachineTypes machine) {
- cs.Machine = toCodeViewMachine(machine);
+static void fillLinkerVerRecord(Compile3Sym &cs) {
+ cs.Machine = toCodeViewMachine(config->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
cs.setLanguage(SourceLanguage::Link);
}
-void PDBLinker::addCommonLinkerModuleSymbols(
- StringRef path, pdb::DbiModuleDescriptorBuilder &mod) {
+static void addCommonLinkerModuleSymbols(StringRef path,
+ pdb::DbiModuleDescriptorBuilder &mod) {
ObjNameSym ons(SymbolRecordKind::ObjNameSym);
EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym);
Compile3Sym cs(SymbolRecordKind::Compile3Sym);
- fillLinkerVerRecord(cs, ctx.config.machine);
+ fillLinkerVerRecord(cs);
ons.Name = "* Linker *";
ons.Signature = 0;
- ArrayRef<StringRef> args = makeArrayRef(ctx.config.argv).drop_front();
+ ArrayRef<StringRef> args = makeArrayRef(config->argv).drop_front();
std::string argStr = quote(args);
ebs.Fields.push_back("cwd");
SmallString<64> cwd;
- if (ctx.config.pdbSourcePath.empty())
+ if (config->pdbSourcePath.empty())
sys::fs::current_path(cwd);
else
- cwd = ctx.config.pdbSourcePath;
+ cwd = config->pdbSourcePath;
ebs.Fields.push_back(cwd);
ebs.Fields.push_back("exe");
- SmallString<64> exe = ctx.config.argv[0];
+ SmallString<64> exe = config->argv[0];
pdbMakeAbsolute(exe);
ebs.Fields.push_back(exe);
ebs.Fields.push_back("pdb");
}
static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
- OutputSection &os, bool isMinGW) {
+ OutputSection &os) {
SectionSym sym(SymbolRecordKind::SectionSym);
sym.Alignment = 12; // 2^12 = 4KB
sym.Characteristics = os.header.Characteristics;
// Skip COFF groups in MinGW because it adds a significant footprint to the
// PDB, due to each function being in its own section
- if (isMinGW)
+ if (config->mingw)
return;
// Output COFF groups for individual chunks of this section.
if (ctx.importFileInstances.empty())
return;
- ExitOnError exitOnErr;
std::map<std::string, llvm::pdb::DbiModuleDescriptorBuilder *> dllToModuleDbi;
for (ImportFile *file : ctx.importFileInstances) {
ons.Name = file->dllName;
ons.Signature = 0;
- fillLinkerVerRecord(cs, ctx.config.machine);
+ fillLinkerVerRecord(cs);
ts.Name = thunk->getName();
ts.Parent = 0;
}
void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
- ExitOnError exitOnErr;
- exitOnErr(builder.initialize(ctx.config.pdbPageSize));
+ exitOnErr(builder.initialize(config->pdbPageSize));
buildId->Signature.CVSignature = OMF::Signature::PDB70;
// Signature is set to a hash of the PDB contents when the PDB is done.
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
dbiBuilder.setAge(buildId->PDB70.Age);
dbiBuilder.setVersionHeader(pdb::PdbDbiV70);
- dbiBuilder.setMachineType(ctx.config.machine);
+ dbiBuilder.setMachineType(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
}
void PDBLinker::addSections(ArrayRef<uint8_t> sectionTable) {
- ExitOnError exitOnErr;
// It's not entirely clear what this is, but the * Linker * module uses it.
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
- nativePath = ctx.config.pdbPath;
+ nativePath = config->pdbPath;
pdbMakeAbsolute(nativePath);
uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath);
auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *"));
// Add section contributions. They must be ordered by ascending RVA.
for (OutputSection *os : ctx.outputSections) {
- addLinkerModuleSectionSymbol(linkerModule, *os, ctx.config.mingw);
+ addLinkerModuleSectionSymbol(linkerModule, *os);
for (Chunk *c : os->chunks) {
pdb::SectionContrib sc =
createSectionContrib(ctx, c, linkerModule.getModuleIndex());
// 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(ctx.config.pdbPath, guid)) {
+ if (Error e = builder.commit(config->pdbPath, guid)) {
checkError(std::move(e));
- error("failed to write PDB file " + Twine(ctx.config.pdbPath));
+ error("failed to write PDB file " + Twine(config->pdbPath));
}
}
-static uint32_t getSecrelReloc(llvm::COFF::MachineTypes machine) {
- switch (machine) {
+static uint32_t getSecrelReloc() {
+ switch (config->machine) {
case AMD64:
return COFF::IMAGE_REL_AMD64_SECREL;
case I386:
DebugLinesSubsectionRef &lines,
uint32_t &offsetInLinetable) {
ExitOnError exitOnErr;
- const uint32_t secrelReloc = getSecrelReloc(c->file->ctx.config.machine);
+ uint32_t secrelReloc = getSecrelReloc();
for (SectionChunk *dbgC : c->file->getDebugChunks()) {
if (dbgC->getSectionName() != ".debug$S")
}
MachineTypes mt = file->getMachineType();
- if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) {
- ctx.config.machine = mt;
- ctx.driver.addWinSysRootLibSearchPaths();
- } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && ctx.config.machine != mt) {
+ if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
+ config->machine = mt;
+ driver->addWinSysRootLibSearchPaths();
+ } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) {
error(toString(file) + ": machine type " + machineToStr(mt) +
- " conflicts with " + machineToStr(ctx.config.machine));
+ " conflicts with " + machineToStr(config->machine));
return;
}
- ctx.driver.parseDirectives(file);
+ driver->parseDirectives(file);
}
-static void errorOrWarn(const Twine &s, bool forceUnresolved) {
- if (forceUnresolved)
+static void errorOrWarn(const Twine &s) {
+ if (config->forceUnresolved)
warn(s);
else
error(s);
std::optional<std::pair<StringRef, uint32_t>> fileLine =
getFileLineCodeView(c, addr);
// If codeview didn't yield any result, check dwarf in MinGW mode.
- if (!fileLine && c->file->ctx.config.mingw)
+ if (!fileLine && config->mingw)
fileLine = getFileLineDwarf(c, addr);
return fileLine;
}
<< "\n>>> ";
os << toString(file);
if (loc.sym)
- os << ":(" << toString(file->ctx, *loc.sym) << ')';
+ os << ":(" << toString(*loc.sym) << ')';
}
return std::make_pair(symbolLocations, numLocations);
}
std::vector<File> files;
};
-static void reportUndefinedSymbol(const COFFLinkerContext &ctx,
- const UndefinedDiag &undefDiag) {
+static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
std::string out;
llvm::raw_string_ostream os(out);
- os << "undefined symbol: " << toString(ctx, *undefDiag.sym);
+ os << "undefined symbol: " << toString(*undefDiag.sym);
const size_t maxUndefReferences = 3;
size_t numDisplayedRefs = 0, numRefs = 0;
}
if (numDisplayedRefs < numRefs)
os << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times";
- errorOrWarn(os.str(), ctx.config.forceUnresolved);
+ errorOrWarn(os.str());
}
void SymbolTable::loadMinGWSymbols() {
StringRef name = undef->getName();
- if (ctx.config.machine == I386 && ctx.config.stdcallFixup) {
+ if (config->machine == I386 && 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).
}
// If it's lazy or already defined, hook it up as weak alias.
if (l->isLazy() || isa<Defined>(l)) {
- if (ctx.config.warnStdcallFixup)
+ if (config->warnStdcallFixup)
warn("Resolving " + origName + " by linking to " + newName);
else
log("Resolving " + origName + " by linking to " + newName);
}
}
- if (ctx.config.autoImport) {
+ if (config->autoImport) {
if (name.startswith("__imp_"))
continue;
// If we have an undefined symbol, but we have a lazy symbol we could
// for __imp_<name> instead, and drop the whole .refptr.<name> chunk.
DefinedRegular *refptr =
dyn_cast_or_null<DefinedRegular>(find((".refptr." + name).str()));
- if (refptr && refptr->getChunk()->getSize() == ctx.config.wordsize) {
+ if (refptr && refptr->getChunk()->getSize() == config->wordsize) {
SectionChunk *sc = dyn_cast_or_null<SectionChunk>(refptr->getChunk());
if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) {
log("Replacing .refptr." + name + " with " + imp->getName());
if (undefs.empty() && (!localImports || localImports->empty()))
return;
- for (Symbol *b : ctx.config.gcroot) {
+ for (Symbol *b : config->gcroot) {
if (undefs.count(b))
- errorOrWarn("<root>: undefined symbol: " + toString(ctx, *b),
- ctx.config.forceUnresolved);
+ errorOrWarn("<root>: undefined symbol: " + toString(*b));
if (localImports)
if (Symbol *imp = localImports->lookup(b))
- warn("<root>: locally defined symbol imported: " + toString(ctx, *imp) +
+ warn("<root>: locally defined symbol imported: " + toString(*imp) +
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
}
if (localImports)
if (Symbol *imp = localImports->lookup(sym))
warn(toString(file) +
- ": locally defined symbol imported: " + toString(ctx, *imp) +
+ ": locally defined symbol imported: " + toString(*imp) +
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
}
};
processFile(file, file->getSymbols());
for (const UndefinedDiag &undefDiag : undefDiags)
- reportUndefinedSymbol(ctx, undefDiag);
+ reportUndefinedSymbol(undefDiag);
}
void SymbolTable::reportUnresolvable() {
}
if (name.contains("_PchSym_"))
continue;
- if (ctx.config.autoImport && impSymbol(name))
+ if (config->autoImport && impSymbol(name))
continue;
undefs.insert(sym);
}
Symbol *imp = find(name.substr(strlen("__imp_")));
if (imp && isa<Defined>(imp)) {
auto *d = cast<Defined>(imp);
- replaceSymbol<DefinedLocalImport>(sym, ctx, name, d);
+ replaceSymbol<DefinedLocalImport>(sym, name, d);
localImportChunks.push_back(cast<DefinedLocalImport>(sym)->getChunk());
localImports[sym] = d;
continue;
if (name.contains("_PchSym_"))
continue;
- if (ctx.config.autoImport && handleMinGWAutomaticImport(sym, name))
+ if (config->autoImport && handleMinGWAutomaticImport(sym, name))
continue;
// Remaining undefined symbols are not fatal if /force is specified.
// They are replaced with dummy defined symbols.
- if (ctx.config.forceUnresolved)
- replaceSymbol<DefinedAbsolute>(sym, ctx, name, 0);
+ if (config->forceUnresolved)
+ replaceSymbol<DefinedAbsolute>(sym, name, 0);
undefs.insert(sym);
}
reportProblemSymbols(
- ctx, undefs,
- ctx.config.warnLocallyDefinedImported ? &localImports : nullptr, false);
+ ctx, undefs, config->warnLocallyDefinedImported ? &localImports : nullptr,
+ false);
}
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
uint32_t newSectionOffset) {
std::string msg;
llvm::raw_string_ostream os(msg);
- os << "duplicate symbol: " << toString(ctx, *existing);
+ os << "duplicate symbol: " << toString(*existing);
DefinedRegular *d = dyn_cast<DefinedRegular>(existing);
if (d && isa<ObjFile>(d->getFile())) {
os << getSourceLocation(newFile, newSc, newSectionOffset,
existing->getName());
- if (ctx.config.forceMultiple)
+ if (config->forceMultiple)
warn(os.str());
else
error(os.str());
auto [s, wasInserted] = insert(n, nullptr);
s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || s->isLazy())
- replaceSymbol<DefinedAbsolute>(s, ctx, n, sym);
+ replaceSymbol<DefinedAbsolute>(s, n, sym);
else if (auto *da = dyn_cast<DefinedAbsolute>(s)) {
if (da->getVA() != sym.getValue())
reportDuplicate(s, nullptr);
auto [s, wasInserted] = insert(n, nullptr);
s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || s->isLazy())
- replaceSymbol<DefinedAbsolute>(s, ctx, n, va);
+ replaceSymbol<DefinedAbsolute>(s, n, va);
else if (auto *da = dyn_cast<DefinedAbsolute>(s)) {
if (da->getVA() != va)
reportDuplicate(s, nullptr);
auto [s, wasInserted] = insert(name, nullptr);
s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
- replaceSymbol<DefinedImportThunk>(s, ctx, name, id, machine);
+ replaceSymbol<DefinedImportThunk>(s, name, id, machine);
return s;
}
}
Symbol *SymbolTable::findUnderscore(StringRef name) const {
- if (ctx.config.machine == I386)
+ if (config->machine == I386)
return find(("_" + name).str());
return find(name);
}
};
// For non-x86, just look for C++ functions.
- if (ctx.config.machine != I386)
+ if (config->machine != I386)
return findByPrefix("?" + name + "@@Y");
if (!name.startswith("_"))
return;
ScopedTimer t(ctx.ltoTimer);
- lto.reset(new BitcodeCompiler(ctx));
+ lto.reset(new BitcodeCompiler());
for (BitcodeFile *f : ctx.bitcodeFileInstances)
lto->add(*f);
- for (InputFile *newObj : lto->compile()) {
+ for (InputFile *newObj : lto->compile(ctx)) {
ObjFile *obj = cast<ObjFile>(newObj);
obj->parse();
ctx.objFileInstances.push_back(obj);
// There is one add* function per symbol type.
class SymbolTable {
public:
- SymbolTable(COFFLinkerContext &c) : ctx(c) {}
+ SymbolTable(COFFLinkerContext &ctx) : ctx(ctx) {}
void addFile(InputFile *file);
//===----------------------------------------------------------------------===//
#include "Symbols.h"
-#include "COFFLinkerContext.h"
#include "InputFiles.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
"symbols should be optimized for memory usage");
// Returns a symbol name for an error message.
-static std::string maybeDemangleSymbol(const COFFLinkerContext &ctx,
- StringRef symName) {
- if (ctx.config.demangle) {
+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 (ctx.config.machine == I386)
+ if (config->machine == I386)
demangleInput.consume_front("_");
std::string demangled = demangle(demangleInput.str());
if (demangled != demangleInput)
}
return std::string(symName);
}
-std::string toString(const COFFLinkerContext &ctx, coff::Symbol &b) {
- return maybeDemangleSymbol(ctx, b.getName());
+std::string toString(coff::Symbol &b) {
+ return maybeDemangleSymbol(b.getName());
}
-std::string toCOFFString(const COFFLinkerContext &ctx,
- const Archive::Symbol &b) {
- return maybeDemangleSymbol(ctx, b.getName());
+std::string toCOFFString(const Archive::Symbol &b) {
+ return maybeDemangleSymbol(b.getName());
}
namespace coff {
return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(sym));
}
-uint64_t DefinedAbsolute::getRVA() { return va - ctx.config.imageBase; }
+uint16_t DefinedAbsolute::numOutputSections;
-static Chunk *makeImportThunk(COFFLinkerContext &ctx, DefinedImportData *s,
- uint16_t machine) {
+static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) {
if (machine == AMD64)
- return make<ImportThunkChunkX64>(ctx, s);
+ return make<ImportThunkChunkX64>(s);
if (machine == I386)
- return make<ImportThunkChunkX86>(ctx, s);
+ return make<ImportThunkChunkX86>(s);
if (machine == ARM64)
- return make<ImportThunkChunkARM64>(ctx, s);
+ return make<ImportThunkChunkARM64>(s);
assert(machine == ARMNT);
- return make<ImportThunkChunkARM>(ctx, s);
+ return make<ImportThunkChunkARM>(s);
}
-DefinedImportThunk::DefinedImportThunk(COFFLinkerContext &ctx, StringRef name,
- DefinedImportData *s, uint16_t machine)
+DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s,
+ uint16_t machine)
: Defined(DefinedImportThunkKind, name), wrappedSym(s),
- data(makeImportThunk(ctx, s, machine)) {}
+ data(makeImportThunk(s, machine)) {}
Defined *Undefined::getWeakAlias() {
// A weak alias may be a weak alias to another symbol, so check recursively.
MemoryBufferRef LazyArchive::getMemberBuffer() {
Archive::Child c =
- CHECK(sym.getMember(), "could not get the member for symbol " +
- toCOFFString(file->ctx, sym));
+ CHECK(sym.getMember(),
+ "could not get the member for symbol " + toCOFFString(sym));
return CHECK(c.getMemoryBufferRef(),
- "could not get the buffer for the member defining symbol " +
- toCOFFString(file->ctx, sym));
+ "could not get the buffer for the member defining symbol " +
+ toCOFFString(sym));
}
} // namespace coff
} // namespace lld
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;
using llvm::object::coff_symbol_generic;
class ArchiveFile;
-class COFFLinkerContext;
class InputFile;
class ObjFile;
class SymbolTable;
// Absolute symbols.
class DefinedAbsolute : public Defined {
public:
- DefinedAbsolute(const COFFLinkerContext &c, StringRef n, COFFSymbolRef s)
- : Defined(DefinedAbsoluteKind, n), va(s.getValue()), ctx(c) {
+ DefinedAbsolute(StringRef n, COFFSymbolRef s)
+ : Defined(DefinedAbsoluteKind, n), va(s.getValue()) {
isExternal = s.isExternal();
}
- DefinedAbsolute(const COFFLinkerContext &c, StringRef n, uint64_t v)
- : Defined(DefinedAbsoluteKind, n), va(v), ctx(c) {}
+ DefinedAbsolute(StringRef n, uint64_t v)
+ : Defined(DefinedAbsoluteKind, n), va(v) {}
static bool classof(const Symbol *s) {
return s->kind() == DefinedAbsoluteKind;
}
- uint64_t getRVA();
+ uint64_t getRVA() { return va - config->imageBase; }
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
// a regular name. A function pointer is given as a DefinedImportData.
class DefinedImportThunk : public Defined {
public:
- DefinedImportThunk(COFFLinkerContext &ctx, StringRef name,
- DefinedImportData *s, uint16_t machine);
+ DefinedImportThunk(StringRef name, DefinedImportData *s, uint16_t machine);
static bool classof(const Symbol *s) {
return s->kind() == DefinedImportThunkKind;
// This is here just for compatibility with MSVC.
class DefinedLocalImport : public Defined {
public:
- DefinedLocalImport(COFFLinkerContext &ctx, StringRef n, Defined *s)
- : Defined(DefinedLocalImportKind, n),
- data(make<LocalImportChunk>(ctx, s)) {}
+ DefinedLocalImport(StringRef n, Defined *s)
+ : Defined(DefinedLocalImportKind, n), data(make<LocalImportChunk>(s)) {}
static bool classof(const Symbol *s) {
return s->kind() == DefinedLocalImportKind;
}
} // namespace coff
-std::string toString(const coff::COFFLinkerContext &ctx, coff::Symbol &b);
-std::string toCOFFString(const coff::COFFLinkerContext &ctx,
- const llvm::object::Archive::Symbol &b);
-
} // namespace lld
#endif
/// Get the type table or the global type table if /DEBUG:GHASH is enabled.
inline llvm::codeview::TypeCollection &getTypeTable() {
- assert(!ctx.config.debugGHashes);
+ assert(!config->debugGHashes);
return typeTable;
}
/// Get the ID table or the global ID table if /DEBUG:GHASH is enabled.
inline llvm::codeview::TypeCollection &getIDTable() {
- assert(!ctx.config.debugGHashes);
+ assert(!config->debugGHashes);
return idTable;
}
class DebugDirectoryChunk : public NonSectionChunk {
public:
- DebugDirectoryChunk(const COFFLinkerContext &c,
+ DebugDirectoryChunk(COFFLinkerContext &c,
const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
bool writeRepro)
: records(r), writeRepro(writeRepro), ctx(c) {}
for (const std::pair<COFF::DebugType, Chunk *>& record : records) {
Chunk *c = record.second;
- const OutputSection *os = ctx.getOutputSection(c);
+ OutputSection *os = ctx.getOutputSection(c);
uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());
fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);
++d;
mutable std::vector<support::ulittle32_t *> timeDateStamps;
const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
bool writeRepro;
- const COFFLinkerContext &ctx;
+
+ COFFLinkerContext &ctx;
};
class CVDebugRecordChunk : public NonSectionChunk {
public:
- CVDebugRecordChunk(const COFFLinkerContext &c) : ctx(c) {}
-
size_t getSize() const override {
- return sizeof(codeview::DebugInfo) + ctx.config.pdbAltPath.size() + 1;
+ return sizeof(codeview::DebugInfo) + config->pdbAltPath.size() + 1;
}
void writeTo(uint8_t *b) const override {
// variable sized field (PDB Path)
char *p = reinterpret_cast<char *>(b + sizeof(*buildId));
- if (!ctx.config.pdbAltPath.empty())
- memcpy(p, ctx.config.pdbAltPath.data(), ctx.config.pdbAltPath.size());
- p[ctx.config.pdbAltPath.size()] = '\0';
+ if (!config->pdbAltPath.empty())
+ memcpy(p, config->pdbAltPath.data(), config->pdbAltPath.size());
+ p[config->pdbAltPath.size()] = '\0';
}
mutable codeview::DebugInfo *buildId = nullptr;
-
-private:
- const COFFLinkerContext &ctx;
};
class ExtendedDllCharacteristicsChunk : public NonSectionChunk {
// The writer writes a SymbolTable result to a file.
class Writer {
public:
- Writer(COFFLinkerContext &c)
- : buffer(errorHandler().outputBuffer), delayIdata(c), edata(c), ctx(c) {}
+ Writer(COFFLinkerContext &c) : buffer(errorHandler().outputBuffer), ctx(c) {}
void run();
private:
void mergeSections();
void removeUnusedSections();
void assignAddresses();
- bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin);
- std::pair<Defined *, bool> getThunk(DenseMap<uint64_t, Defined *> &lastThunks,
- Defined *target, uint64_t p,
- uint16_t type, int margin);
- bool createThunks(OutputSection *os, int margin);
- bool verifyRanges(const std::vector<Chunk *> chunks);
void finalizeAddresses();
void removeEmptySections();
void assignOutputSectionIndices();
void createSEHTable();
void createRuntimePseudoRelocs();
void insertCtorDtorSymbols();
- void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols);
void createGuardCFTables();
void markSymbolsForRVATable(ObjFile *file,
ArrayRef<SectionChunk *> symIdxChunks,
void sortExceptionTable();
void sortCRTSectionChunks(std::vector<Chunk *> &chunks);
void addSyntheticIdata();
- void sortBySectionOrder(std::vector<Chunk *> &chunks);
void fixPartialSectionChars(StringRef name, uint32_t chars);
bool fixGnuImportChunks();
void fixTlsAlignment();
}
// Write the section header to a given buffer.
-void OutputSection::writeHeaderTo(uint8_t *buf, bool isDebug) {
+void OutputSection::writeHeaderTo(uint8_t *buf) {
auto *hdr = reinterpret_cast<coff_section *>(buf);
*hdr = header;
if (stringTableOff) {
// If name is too long, write offset into the string table as a name.
encodeSectionName(hdr->Name, stringTableOff);
} else {
- assert(!isDebug || name.size() <= COFF::NameSize ||
+ assert(!config->debug || name.size() <= COFF::NameSize ||
(hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0);
strncpy(hdr->Name, name.data(),
std::min(name.size(), (size_t)COFF::NameSize));
// Check whether the target address S is in range from a relocation
// of type relType at address P.
-bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
- if (ctx.config.machine == ARMNT) {
+static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
+ if (config->machine == ARMNT) {
int64_t diff = AbsoluteDifference(s, p + 4) + margin;
switch (relType) {
case IMAGE_REL_ARM_BRANCH20T:
default:
return true;
}
- } else if (ctx.config.machine == ARM64) {
+ } else if (config->machine == ARM64) {
int64_t diff = AbsoluteDifference(s, p) + margin;
switch (relType) {
case IMAGE_REL_ARM64_BRANCH26:
// Return the last thunk for the given target if it is in range,
// or create a new one.
-std::pair<Defined *, bool>
-Writer::getThunk(DenseMap<uint64_t, Defined *> &lastThunks, Defined *target,
- uint64_t p, uint16_t type, int margin) {
+static std::pair<Defined *, bool>
+getThunk(DenseMap<uint64_t, Defined *> &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 (ctx.config.machine) {
+ switch (config->machine) {
case ARMNT:
- c = make<RangeExtensionThunkARM>(ctx, target);
+ c = make<RangeExtensionThunkARM>(target);
break;
case ARM64:
- c = make<RangeExtensionThunkARM64>(ctx, target);
+ c = make<RangeExtensionThunkARM64>(target);
break;
default:
llvm_unreachable("Unexpected architecture");
// 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.
-bool Writer::createThunks(OutputSection *os, int margin) {
+static bool createThunks(OutputSection *os, int margin) {
bool addressesChanged = false;
DenseMap<uint64_t, Defined *> lastThunks;
DenseMap<std::pair<ObjFile *, Defined *>, uint32_t> thunkSymtabIndices;
}
// Verify that all relocations are in range, with no extra margin requirements.
-bool Writer::verifyRanges(const std::vector<Chunk *> chunks) {
+static bool verifyRanges(const std::vector<Chunk *> chunks) {
for (Chunk *c : chunks) {
SectionChunk *sc = dyn_cast_or_null<SectionChunk>(c);
if (!sc)
// Assign addresses and add thunks if necessary.
void Writer::finalizeAddresses() {
assignAddresses();
- if (ctx.config.machine != ARMNT && ctx.config.machine != ARM64)
+ if (config->machine != ARMNT && config->machine != ARM64)
return;
size_t origNumChunks = 0;
}
void Writer::writePEChecksum() {
- if (!ctx.config.writeCheckSum) {
+ if (!config->writeCheckSum) {
return;
}
fatal("image size (" + Twine(fileSize) + ") " +
"exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")");
- openFile(ctx.config.outputFile);
- if (ctx.config.is64()) {
+ openFile(config->outputFile);
+ if (config->is64()) {
writeHeader<pe32plus_header>();
} else {
writeHeader<pe32_header>();
t1.stop();
- if (!ctx.config.pdbPath.empty() && ctx.config.debug) {
+ if (!config->pdbPath.empty() && config->debug) {
assert(buildId);
createPDB(ctx, sectionTable, buildId->buildId);
}
}
// For /order.
-void Writer::sortBySectionOrder(std::vector<Chunk *> &chunks) {
- auto getPriority = [&ctx = ctx](const Chunk *c) {
+static void sortBySectionOrder(std::vector<Chunk *> &chunks) {
+ auto getPriority = [](const Chunk *c) {
if (auto *sec = dyn_cast<SectionChunk>(c))
if (sec->sym)
- return ctx.config.order.lookup(sec->sym->getName());
+ return config->order.lookup(sec->sym->getName());
return 0;
};
// terminator in .idata$2.
void Writer::addSyntheticIdata() {
uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
- idata.create(ctx);
+ idata.create();
// Add the .idata content in the right section groups, to allow
// chunks from other linked in object files to be grouped together.
// 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,
- bool isMinGW) {
+static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) {
// On MinGW, comdat groups are formed by putting the comdat group name
// after the '$' in the section name. For .eh_frame$<symbol>, that must
// still be sorted before the .eh_frame trailer from crtend.o, thus just
// 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 (!isMinGW)
+ if (!config->mingw)
return false;
if (!sc || !sc->isCOMDAT())
return false;
}
void Writer::sortSections() {
- if (!ctx.config.callGraphProfile.empty()) {
+ if (!config->callGraphProfile.empty()) {
DenseMap<const SectionChunk *, int> order =
computeCallGraphProfileOrder(ctx);
for (auto it : order) {
if (DefinedRegular *sym = it.first->sym)
- ctx.config.order[sym->getName()] = it.second;
+ config->order[sym->getName()] = it.second;
}
}
- if (!ctx.config.order.empty())
+ if (!config->order.empty())
for (auto it : partialSections)
sortBySectionOrder(it.second->chunks);
}
for (Chunk *c : ctx.symtab.getChunks()) {
auto *sc = dyn_cast<SectionChunk>(c);
if (sc && !sc->live) {
- if (ctx.config.verbose)
+ if (config->verbose)
sc->printDiscardedMessage();
continue;
}
StringRef name = c->getSectionName();
- if (shouldStripSectionSuffix(sc, name, ctx.config.mingw))
+ if (shouldStripSectionSuffix(sc, name))
name = name.split('$').first;
if (name.startswith(".tls"))
}
void Writer::createMiscChunks() {
- Configuration *config = &ctx.config;
-
for (MergeChunk *p : ctx.mergeChunkInstances) {
if (p) {
p->finalizeContents();
// 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<CVDebugRecordChunk>(ctx);
+ buildId = make<CVDebugRecordChunk>();
debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId});
}
continue;
std::string dll = StringRef(file->dllName).lower();
- if (ctx.config.dllOrder.count(dll) == 0)
- ctx.config.dllOrder[dll] = ctx.config.dllOrder.size();
+ if (config->dllOrder.count(dll) == 0)
+ config->dllOrder[dll] = config->dllOrder.size();
if (file->impSym && !isa<DefinedImportData>(file->impSym))
- fatal(toString(ctx, *file->impSym) + " was replaced");
+ fatal(toString(*file->impSym) + " was replaced");
DefinedImportData *impSym = cast_or_null<DefinedImportData>(file->impSym);
- if (ctx.config.delayLoads.count(StringRef(file->dllName).lower())) {
+ if (config->delayLoads.count(StringRef(file->dllName).lower())) {
if (!file->thunkSym)
fatal("cannot delay-load " + toString(file) +
- " due to import of data: " + toString(ctx, *impSym));
+ " due to import of data: " + toString(*impSym));
delayIdata.add(impSym);
} else {
idata.add(impSym);
continue;
if (!isa<DefinedImportThunk>(file->thunkSym))
- fatal(toString(ctx, *file->thunkSym) + " was replaced");
+ fatal(toString(*file->thunkSym) + " was replaced");
DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym);
if (file->thunkLive)
textSec->addChunk(thunk->getChunk());
}
if (!delayIdata.empty()) {
- Defined *helper = cast<Defined>(ctx.config.delayLoadHelper);
- delayIdata.create(helper);
+ Defined *helper = cast<Defined>(config->delayLoadHelper);
+ delayIdata.create(ctx, helper);
for (Chunk *c : delayIdata.getChunks())
didatSec->addChunk(c);
for (Chunk *c : delayIdata.getDataChunks())
if (!edataSec->chunks.empty()) {
// Allow using a custom built export table from input object files, instead
// of having the linker synthesize the tables.
- if (ctx.config.hadExplicitExports)
+ if (config->hadExplicitExports)
warn("literal .edata sections override exports");
- } else if (!ctx.config.exports.empty()) {
+ } else if (!config->exports.empty()) {
for (Chunk *c : edata.chunks)
edataSec->addChunk(c);
}
edataEnd = edataSec->chunks.back();
}
// Warn on exported deleting destructor.
- for (auto e : ctx.config.exports)
+ for (auto e : config->exports)
if (e.sym && e.sym->getName().startswith("??_G"))
- warn("export of deleting dtor: " + toString(ctx, *e.sym));
+ warn("export of deleting dtor: " + toString(*e.sym));
}
void Writer::removeUnusedSections() {
continue;
if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0)
continue;
- if (ctx.config.warnLongSectionNames) {
+ if (config->warnLongSectionNames) {
warn("section name " + sec->name +
" is longer than 8 characters and will use a non-standard string "
"table");
sec->setStringTableOff(addEntryToStringTable(sec->name));
}
- if (ctx.config.debugDwarf || ctx.config.debugSymtab) {
+ if (config->debugDwarf || config->debugSymtab) {
for (ObjFile *file : ctx.objFileInstances) {
for (Symbol *b : file->getSymbols()) {
auto *d = dyn_cast_or_null<Defined>(b);
pointerToSymbolTable = fileOff;
fileOff += outputSymtab.size() * sizeof(coff_symbol16);
fileOff += 4 + strtab.size();
- fileSize = alignTo(fileOff, ctx.config.fileAlign);
+ fileSize = alignTo(fileOff, config->fileAlign);
}
void Writer::mergeSections() {
lastPdata = pdataSec->chunks.back();
}
- for (auto &p : ctx.config.merge) {
+ for (auto &p : config->merge) {
StringRef toName = p.second;
if (p.first == toName)
continue;
while (true) {
if (!names.insert(toName).second)
fatal("/merge: cycle found for section '" + p.first + "'");
- auto i = ctx.config.merge.find(toName);
- if (i == ctx.config.merge.end())
+ auto i = config->merge.find(toName);
+ if (i == config->merge.end())
break;
toName = i->second;
}
// Visits all sections to assign incremental, non-overlapping RVAs and
// file offsets.
void Writer::assignAddresses() {
- Configuration *config = &ctx.config;
-
sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
sizeof(data_directory) * numberOfDataDirectory +
sizeof(coff_section) * ctx.outputSections.size();
// under DOS, that program gets run (usually to just print an error message).
// When run under Windows, the loader looks at AddressOfNewExeHeader and uses
// the PE header instead.
- Configuration *config = &ctx.config;
uint8_t *buf = buffer->getBufferStart();
auto *dos = reinterpret_cast<dos_header *>(buf);
buf += sizeof(dos_header);
// Write section table
for (OutputSection *sec : ctx.outputSections) {
- sec->writeHeaderTo(buf, config->debug);
+ sec->writeHeaderTo(buf);
buf += sizeof(coff_section);
}
sectionTable = ArrayRef<uint8_t>(
// Visit all relocations from all section contributions of this object file and
// mark the relocation target as address-taken.
-void Writer::markSymbolsWithRelocations(ObjFile *file,
- SymbolRVASet &usedSymbols) {
+static void 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.
continue;
for (const coff_relocation &reloc : sc->getRelocs()) {
- if (ctx.config.machine == I386 &&
- reloc.Type == COFF::IMAGE_REL_I386_REL32)
+ if (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;
// address-taken functions. It is sorted and uniqued, just like the safe SEH
// table.
void Writer::createGuardCFTables() {
- Configuration *config = &ctx.config;
-
SymbolRVASet addressTakenSyms;
SymbolRVASet giatsRVASet;
std::vector<Symbol *> giatsSymbols;
sc->getRuntimePseudoRelocs(rels);
}
- if (!ctx.config.pseudoRelocs) {
+ if (!config->pseudoRelocs) {
// Not writing any pseudo relocs; if some were needed, error out and
// indicate what required them.
for (const RuntimePseudoReloc &rpr : rels)
// There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__
// and __DTOR_LIST__ respectively.
void Writer::insertCtorDtorSymbols() {
- AbsolutePointerChunk *ctorListHead = make<AbsolutePointerChunk>(ctx, -1);
- AbsolutePointerChunk *ctorListEnd = make<AbsolutePointerChunk>(ctx, 0);
- AbsolutePointerChunk *dtorListHead = make<AbsolutePointerChunk>(ctx, -1);
- AbsolutePointerChunk *dtorListEnd = make<AbsolutePointerChunk>(ctx, 0);
+ AbsolutePointerChunk *ctorListHead = make<AbsolutePointerChunk>(-1);
+ AbsolutePointerChunk *ctorListEnd = make<AbsolutePointerChunk>(0);
+ AbsolutePointerChunk *dtorListHead = make<AbsolutePointerChunk>(-1);
+ AbsolutePointerChunk *dtorListEnd = make<AbsolutePointerChunk>(0);
ctorsSec->insertChunkAtStart(ctorListHead);
ctorsSec->addChunk(ctorListEnd);
dtorsSec->insertChunkAtStart(dtorListHead);
// Handles /section options to allow users to overwrite
// section attributes.
void Writer::setSectionPermissions() {
- for (auto &p : ctx.config.section) {
+ for (auto &p : config->section) {
StringRef name = p.first;
uint32_t perm = p.second;
for (OutputSection *sec : ctx.outputSections)
// 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();
// 2) In all cases, the PE COFF file header also contains a timestamp.
// For reproducibility, instead of a timestamp we want to use a hash of the
// PE contents.
- Configuration *config = &ctx.config;
-
if (config->debug) {
assert(buildId && "BuildId is not set!");
// BuildId->BuildId was filled in when the PDB was written.
};
uint8_t *begin = bufAddr(firstPdata);
uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize();
- if (ctx.config.machine == AMD64) {
+ if (config->machine == AMD64) {
struct Entry { ulittle32_t begin, end, unwind; };
if ((end - begin) % sizeof(Entry) != 0) {
fatal("unexpected .pdata size: " + Twine(end - begin) +
[](const Entry &a, const Entry &b) { return a.begin < b.begin; });
return;
}
- if (ctx.config.machine == ARMNT || ctx.config.machine == ARM64) {
+ if (config->machine == ARMNT || config->machine == ARM64) {
struct Entry { ulittle32_t begin, unwind; };
if ((end - begin) % sizeof(Entry) != 0) {
fatal("unexpected .pdata size: " + Twine(end - begin) +
};
llvm::stable_sort(chunks, sectionChunkOrder);
- if (ctx.config.verbose) {
+ if (config->verbose) {
for (auto &c : chunks) {
auto sc = dyn_cast<SectionChunk>(c);
log(" " + sc->file->mb.getBufferIdentifier().str() +
// Add base relocations to .reloc section.
void Writer::addBaserels() {
- if (!ctx.config.relocatable)
+ if (!config->relocatable)
return;
relocSec->chunks.clear();
std::vector<Baserel> v;
uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff();
uint64_t tlsOffset = tlsSym->getRVA() - sec->getRVA();
- uint64_t directorySize = ctx.config.is64()
+ uint64_t directorySize = config->is64()
? sizeof(object::coff_tls_directory64)
: sizeof(object::coff_tls_directory32);
if (tlsOffset + directorySize > sec->getRawSize())
fatal("_tls_used sym is malformed");
- if (ctx.config.is64()) {
+ if (config->is64()) {
object::coff_tls_directory64 *tlsDir =
reinterpret_cast<object::coff_tls_directory64 *>(&secBuf[tlsOffset]);
tlsDir->setAlignment(tlsAlignment);
Symbol *sym = ctx.symtab.findUnderscore("_load_config_used");
auto *b = cast_if_present<DefinedRegular>(sym);
if (!b) {
- if (ctx.config.guardCF != GuardCFLevel::Off)
+ if (config->guardCF != GuardCFLevel::Off)
warn("Control Flow Guard is enabled but '_load_config_used' is missing");
return;
}
uint8_t *buf = buffer->getBufferStart();
uint8_t *secBuf = buf + sec->getFileOff();
uint8_t *symBuf = secBuf + (b->getRVA() - sec->getRVA());
- uint32_t expectedAlign = ctx.config.is64() ? 8 : 4;
+ uint32_t expectedAlign = config->is64() ? 8 : 4;
if (b->getChunk()->getAlignment() < expectedAlign)
warn("'_load_config_used' is misaligned (expected alignment to be " +
Twine(expectedAlign) + " bytes, got " +
Twine::utohexstr(b->getRVA()) + " not aligned to " +
Twine(expectedAlign) + " bytes)");
- if (ctx.config.is64())
+ if (config->is64())
checkLoadConfigGuardData(
reinterpret_cast<const coff_load_configuration64 *>(symBuf));
else
#define CHECK_VA(field, sym) \
if (auto *s = dyn_cast<DefinedSynthetic>(ctx.symtab.findUnderscore(sym))) \
- if (loadConfig->field != ctx.config.imageBase + s->getRVA()) \
+ if (loadConfig->field != config->imageBase + s->getRVA()) \
warn(#field " not set correctly in '_load_config_used'");
#define CHECK_ABSOLUTE(field, sym) \
if (loadConfig->field != s->getVA()) \
warn(#field " not set correctly in '_load_config_used'");
- if (ctx.config.guardCF == GuardCFLevel::Off)
+ if (config->guardCF == GuardCFLevel::Off)
return;
RETURN_IF_NOT_CONTAINS(GuardFlags)
CHECK_VA(GuardCFFunctionTable, "__guard_fids_table")
CHECK_ABSOLUTE(GuardAddressTakenIatEntryCount, "__guard_iat_count")
}
- if (!(ctx.config.guardCF & GuardCFLevel::LongJmp))
+ if (!(config->guardCF & GuardCFLevel::LongJmp))
return;
RETURN_IF_NOT_CONTAINS(GuardLongJumpTargetCount)
CHECK_VA(GuardLongJumpTargetTable, "__guard_longjmp_table")
CHECK_ABSOLUTE(GuardLongJumpTargetCount, "__guard_longjmp_count")
- if (!(ctx.config.guardCF & GuardCFLevel::EHCont))
+ if (!(config->guardCF & GuardCFLevel::EHCont))
return;
RETURN_IF_NOT_CONTAINS(GuardEHContinuationCount)
CHECK_VA(GuardEHContinuationTable, "__guard_eh_cont_table")
void insertChunkAtStart(Chunk *c);
void merge(OutputSection *other);
void setPermissions(uint32_t c);
- uint64_t getRVA() const { return header.VirtualAddress; }
- uint64_t getFileOff() const { return header.PointerToRawData; }
- void writeHeaderTo(uint8_t *buf, bool isDebug);
+ uint64_t getRVA() { return header.VirtualAddress; }
+ uint64_t getFileOff() { return header.PointerToRawData; }
+ void writeHeaderTo(uint8_t *buf);
void addContributingPartialSection(PartialSection *sec);
// Returns the size of this section in an executable memory image.