mirror of
https://github.com/intel/llvm.git
synced 2026-01-16 05:32:28 +08:00
Revert "[LLD] Remove global state in lld/COFF"
This reverts commit7370ff624d. (and47fb8ae2f9). This commit broke the symbol type in import libraries generated for mingw autoexported symbols, when the source files were built with LTO. I'll commit a testcase that showcases this issue after the revert.
This commit is contained in:
@@ -10,15 +10,13 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#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),
|
||||
@@ -35,10 +33,6 @@ COFFLinkerContext::COFFLinkerContext()
|
||||
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
|
||||
|
||||
@@ -6,13 +6,12 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_COFFLINKERCONTEXT_H
|
||||
#define LLD_COFF_COFFLINKERCONTEXT_H
|
||||
#ifndef LLD_COFF_COFFLinkerContext_H
|
||||
#define LLD_COFF_COFFLinkerContext_H
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "Config.h"
|
||||
#include "DebugTypes.h"
|
||||
#include "Driver.h"
|
||||
#include "InputFiles.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Writer.h"
|
||||
@@ -28,9 +27,9 @@ public:
|
||||
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;
|
||||
@@ -42,8 +41,6 @@ public:
|
||||
/// 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;
|
||||
|
||||
@@ -55,10 +52,6 @@ public:
|
||||
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;
|
||||
@@ -84,8 +77,6 @@ public:
|
||||
Timer publicsLayoutTimer;
|
||||
Timer tpiStreamLayoutTimer;
|
||||
Timer diskCommitTimer;
|
||||
|
||||
Configuration config;
|
||||
};
|
||||
|
||||
} // namespace lld::coff
|
||||
|
||||
@@ -56,8 +56,6 @@ public:
|
||||
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
|
||||
@@ -73,8 +71,8 @@ using SectionPair = std::pair<const SectionChunk *, const SectionChunk *>;
|
||||
// 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 {
|
||||
@@ -87,7 +85,7 @@ CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) : ctx(ctx) {
|
||||
};
|
||||
|
||||
// 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;
|
||||
@@ -207,11 +205,11 @@ DenseMap<const SectionChunk *, int> CallGraphSort::run() {
|
||||
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
|
||||
|
||||
@@ -53,8 +53,8 @@ SectionChunk::SectionChunk(ObjFile *f, const coff_section *h)
|
||||
// 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;
|
||||
}
|
||||
@@ -94,32 +94,21 @@ static void applySecRel(const SectionChunk *sec, uint8_t *off,
|
||||
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;
|
||||
@@ -127,9 +116,7 @@ void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
|
||||
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 " +
|
||||
@@ -138,18 +125,13 @@ void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
|
||||
}
|
||||
|
||||
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 " +
|
||||
@@ -206,26 +188,19 @@ void applyBranch24T(uint8_t *off, int32_t v) {
|
||||
}
|
||||
|
||||
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:
|
||||
@@ -323,8 +298,7 @@ static void applyArm64Branch14(uint8_t *off, int64_t v) {
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -333,20 +307,14 @@ void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
|
||||
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 " +
|
||||
@@ -356,13 +324,12 @@ void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
|
||||
|
||||
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
|
||||
@@ -428,7 +395,7 @@ void SectionChunk::applyRelocation(uint8_t *off,
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -436,19 +403,18 @@ void SectionChunk::applyRelocation(uint8_t *off,
|
||||
|
||||
// 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");
|
||||
@@ -513,9 +479,8 @@ void SectionChunk::addAssociative(SectionChunk *child) {
|
||||
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;
|
||||
@@ -547,7 +512,7 @@ static uint8_t getBaserelType(const coff_relocation &rel,
|
||||
// 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);
|
||||
@@ -564,8 +529,7 @@ void SectionChunk::getBaserels(std::vector<Baserel> *res) {
|
||||
// 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.
|
||||
@@ -591,7 +555,7 @@ static int getRuntimePseudoRelocSize(uint16_t type,
|
||||
// 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:
|
||||
@@ -648,8 +612,7 @@ void SectionChunk::getRuntimePseudoRelocs(
|
||||
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 " +
|
||||
@@ -755,8 +718,7 @@ void StringChunk::writeTo(uint8_t *buf) const {
|
||||
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);
|
||||
@@ -769,13 +731,14 @@ void ImportThunkChunkX64::writeTo(uint8_t *buf) const {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -785,7 +748,7 @@ 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 {
|
||||
@@ -803,13 +766,12 @@ const uint8_t armThunk[] = {
|
||||
};
|
||||
|
||||
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));
|
||||
@@ -824,34 +786,28 @@ const uint8_t arm64Thunk[] = {
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -971,8 +927,8 @@ void BaserelChunk::writeTo(uint8_t *buf) const {
|
||||
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;
|
||||
@@ -1030,10 +986,10 @@ void MergeChunk::writeTo(uint8_t *buf) const {
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -238,13 +238,13 @@ public:
|
||||
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);
|
||||
|
||||
@@ -490,26 +490,24 @@ static const uint8_t importThunkARM64[] = {
|
||||
// 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;
|
||||
@@ -517,8 +515,7 @@ public:
|
||||
|
||||
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); }
|
||||
@@ -528,8 +525,7 @@ public:
|
||||
|
||||
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); }
|
||||
@@ -538,46 +534,35 @@ public:
|
||||
|
||||
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
|
||||
@@ -644,9 +629,8 @@ private:
|
||||
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;
|
||||
@@ -685,8 +669,7 @@ private:
|
||||
// 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;
|
||||
@@ -694,7 +677,6 @@ public:
|
||||
|
||||
private:
|
||||
uint64_t value;
|
||||
COFFLinkerContext &ctx;
|
||||
};
|
||||
|
||||
// Return true if this file has the hotpatch flag set to true in the S_COMPILE3
|
||||
@@ -715,19 +697,6 @@ void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift);
|
||||
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 {
|
||||
|
||||
@@ -96,7 +96,7 @@ enum class ICFLevel {
|
||||
// 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;
|
||||
@@ -292,6 +292,8 @@ struct Configuration {
|
||||
bool writeCheckSum = false;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<Configuration> config;
|
||||
|
||||
} // namespace lld::coff
|
||||
|
||||
#endif
|
||||
|
||||
157
lld/COFF/DLL.cpp
157
lld/COFF/DLL.cpp
@@ -61,23 +61,19 @@ private:
|
||||
// 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.
|
||||
@@ -85,16 +81,15 @@ private:
|
||||
// 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);
|
||||
@@ -102,9 +97,6 @@ public:
|
||||
}
|
||||
|
||||
uint16_t ordinal;
|
||||
|
||||
private:
|
||||
COFFLinkerContext &ctx;
|
||||
};
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
@@ -143,15 +135,14 @@ private:
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
@@ -334,56 +325,47 @@ public:
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -391,7 +373,7 @@ public:
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -401,15 +383,11 @@ public:
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -417,7 +395,7 @@ public:
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -427,9 +405,6 @@ public:
|
||||
|
||||
Chunk *desc = nullptr;
|
||||
Defined *helper = nullptr;
|
||||
|
||||
private:
|
||||
const COFFLinkerContext &ctx;
|
||||
};
|
||||
|
||||
class ThunkChunkARM64 : public NonSectionChunk {
|
||||
@@ -473,32 +448,28 @@ public:
|
||||
// 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
|
||||
@@ -538,20 +509,19 @@ public:
|
||||
|
||||
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);
|
||||
@@ -565,7 +535,6 @@ public:
|
||||
|
||||
private:
|
||||
size_t size;
|
||||
const COFFLinkerContext &ctx;
|
||||
};
|
||||
|
||||
class NamePointersChunk : public NonSectionChunk {
|
||||
@@ -586,12 +555,11 @@ private:
|
||||
|
||||
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");
|
||||
@@ -603,13 +571,12 @@ public:
|
||||
|
||||
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) {
|
||||
@@ -621,18 +588,18 @@ void IdataContents::create(COFFLinkerContext &ctx) {
|
||||
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]);
|
||||
@@ -668,9 +635,9 @@ uint64_t DelayLoadContents::getDirSize() {
|
||||
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) {
|
||||
@@ -682,15 +649,15 @@ void DelayLoadContents::create(Defined *h) {
|
||||
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
|
||||
@@ -725,13 +692,13 @@ void DelayLoadContents::create(Defined *h) {
|
||||
}
|
||||
|
||||
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:
|
||||
@@ -741,13 +708,13 @@ Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
|
||||
|
||||
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:
|
||||
@@ -755,20 +722,20 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -776,7 +743,7 @@ EdataContents::EdataContents(COFFLinkerContext &ctx) : ctx(ctx) {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
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;
|
||||
@@ -37,10 +37,9 @@ public:
|
||||
// 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; }
|
||||
@@ -61,23 +60,19 @@ private:
|
||||
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
|
||||
|
||||
@@ -235,7 +235,7 @@ void TpiSource::remapRecord(MutableArrayRef<uint8_t> rec,
|
||||
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>";
|
||||
@@ -305,7 +305,7 @@ getHashesFromDebugH(ArrayRef<uint8_t> debugH) {
|
||||
|
||||
// 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;
|
||||
@@ -329,7 +329,7 @@ Error TpiSource::mergeDebugT(TypeMerger *m) {
|
||||
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
|
||||
@@ -356,7 +356,7 @@ Error TpiSource::mergeDebugT(TypeMerger *m) {
|
||||
|
||||
// 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();
|
||||
@@ -385,7 +385,7 @@ Error TypeServerSource::mergeDebugT(TypeMerger *m) {
|
||||
ipiMap = ipiSrc->indexMapStorage;
|
||||
}
|
||||
|
||||
if (ctx.config.showSummary) {
|
||||
if (config->showSummary) {
|
||||
nbTypeRecords = tpiMap.size() + ipiMap.size();
|
||||
nbTypeRecordsBytes =
|
||||
expectedTpi->typeArray().getUnderlyingStream().getLength() +
|
||||
@@ -727,14 +727,14 @@ void TpiSource::mergeUniqueTypeRecords(ArrayRef<uint8_t> typeRecords,
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -787,7 +787,7 @@ static ArrayRef<uint8_t> typeArrayToBytes(const CVTypeArray &types) {
|
||||
|
||||
// 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.
|
||||
@@ -805,13 +805,13 @@ void TypeServerSource::remapTpiWithGHashes(GHashState *g) {
|
||||
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();
|
||||
}
|
||||
@@ -898,7 +898,7 @@ void UsePrecompSource::remapTpiWithGHashes(GHashState *g) {
|
||||
mergeUniqueTypeRecords(file->debugTypes,
|
||||
TypeIndex(precompDependency.getStartTypeIndex() +
|
||||
precompDependency.getTypesCount()));
|
||||
if (ctx.config.showSummary) {
|
||||
if (config->showSummary) {
|
||||
nbTypeRecords = ghashes.size();
|
||||
nbTypeRecordsBytes = file->debugTypes.size();
|
||||
}
|
||||
|
||||
@@ -61,6 +61,9 @@ using namespace llvm::sys;
|
||||
|
||||
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().
|
||||
@@ -71,7 +74,10 @@ bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
|
||||
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;
|
||||
}
|
||||
@@ -92,11 +98,11 @@ static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
|
||||
|
||||
// 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();
|
||||
@@ -140,15 +146,15 @@ static std::future<MBErrPair> createFutureForFile(std::string path) {
|
||||
}
|
||||
|
||||
// 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:
|
||||
@@ -171,9 +177,9 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -217,7 +223,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
||||
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;
|
||||
}
|
||||
@@ -248,12 +254,12 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -294,8 +300,8 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
|
||||
|
||||
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()) {
|
||||
@@ -305,16 +311,16 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
|
||||
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([=]() {
|
||||
@@ -323,15 +329,14 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
|
||||
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
|
||||
@@ -343,7 +348,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
|
||||
|
||||
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);
|
||||
@@ -357,14 +362,14 @@ void LinkerDriver::parseDirectives(InputFile *file) {
|
||||
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.
|
||||
@@ -393,7 +398,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
|
||||
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);
|
||||
@@ -402,32 +407,32 @@ void LinkerDriver::parseDirectives(InputFile *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;
|
||||
}
|
||||
@@ -446,9 +451,9 @@ void LinkerDriver::parseDirectives(InputFile *file) {
|
||||
// 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;
|
||||
};
|
||||
@@ -517,7 +522,7 @@ StringRef LinkerDriver::doFindLib(StringRef 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;
|
||||
}
|
||||
@@ -526,13 +531,13 @@ StringRef LinkerDriver::doFindLib(StringRef filename) {
|
||||
// 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))
|
||||
@@ -653,7 +658,7 @@ Symbol *LinkerDriver::addUndefined(StringRef name) {
|
||||
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;
|
||||
}
|
||||
@@ -682,15 +687,15 @@ StringRef LinkerDriver::mangleMaybe(Symbol *s) {
|
||||
// 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");
|
||||
@@ -707,9 +712,9 @@ StringRef LinkerDriver::findDefaultEntry() {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -731,10 +736,10 @@ WindowsSubsystem LinkerDriver::inferSubsystem() {
|
||||
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) {
|
||||
@@ -875,9 +880,8 @@ static unsigned parseDebugTypes(const opt::InputArgList &args) {
|
||||
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 "";
|
||||
@@ -885,14 +889,14 @@ std::string LinkerDriver::getMapFile(const opt::InputArgList &args,
|
||||
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());
|
||||
}
|
||||
@@ -904,26 +908,26 @@ std::string LinkerDriver::getImplibPath() {
|
||||
// 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);
|
||||
@@ -940,9 +944,9 @@ void LinkerDriver::createImportLibrary(bool asLib) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -951,8 +955,8 @@ void LinkerDriver::createImportLibrary(bool asLib) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -962,8 +966,8 @@ void LinkerDriver::createImportLibrary(bool asLib) {
|
||||
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;
|
||||
}
|
||||
@@ -978,39 +982,39 @@ void LinkerDriver::createImportLibrary(bool asLib) {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -1022,7 +1026,7 @@ void LinkerDriver::parseModuleDefs(StringRef path) {
|
||||
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);
|
||||
@@ -1033,7 +1037,7 @@ void LinkerDriver::parseModuleDefs(StringRef path) {
|
||||
e2.data = e1.Data;
|
||||
e2.isPrivate = e1.Private;
|
||||
e2.constant = e1.Constant;
|
||||
ctx.config.exports.push_back(e2);
|
||||
config->exports.push_back(e2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1055,7 +1059,7 @@ bool LinkerDriver::run() {
|
||||
// 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("@")) {
|
||||
@@ -1084,22 +1088,22 @@ void LinkerDriver::parseOrderFile(StringRef arg) {
|
||||
// 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,
|
||||
@@ -1116,7 +1120,7 @@ void LinkerDriver::parseCallGraphFile(StringRef path) {
|
||||
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;
|
||||
}
|
||||
@@ -1138,11 +1142,11 @@ void LinkerDriver::parseCallGraphFile(StringRef path) {
|
||||
|
||||
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) {
|
||||
@@ -1168,7 +1172,7 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1183,7 +1187,7 @@ static void markAddrsig(Symbol *s) {
|
||||
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
|
||||
@@ -1221,12 +1225,12 @@ static void findKeepUniqueSections(COFFLinkerContext &ctx) {
|
||||
// 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 '.'.
|
||||
|
||||
@@ -1237,22 +1241,19 @@ void LinkerDriver::parsePDBAltPath() {
|
||||
// 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%"))
|
||||
@@ -1266,7 +1267,7 @@ void LinkerDriver::parsePDBAltPath() {
|
||||
cursor = secondMark + 1;
|
||||
}
|
||||
|
||||
ctx.config.pdbAltPath = buf;
|
||||
config->pdbAltPath = buf;
|
||||
}
|
||||
|
||||
/// Convert resource files and potentially merge input resource object
|
||||
@@ -1280,7 +1281,7 @@ void LinkerDriver::convertResources() {
|
||||
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"
|
||||
@@ -1311,16 +1312,16 @@ void LinkerDriver::convertResources() {
|
||||
// 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()))
|
||||
@@ -1335,12 +1336,12 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
|
||||
|
||||
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;
|
||||
@@ -1350,7 +1351,7 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
|
||||
if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
|
||||
e.data = true;
|
||||
s->isUsedInRegularObj = true;
|
||||
ctx.config.exports.push_back(e);
|
||||
config->exports.push_back(e);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1403,7 +1404,6 @@ getVFS(const opt::InputArgList &args) {
|
||||
|
||||
void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||
ScopedTimer rootTimer(ctx.rootTimer);
|
||||
Configuration *config = &ctx.config;
|
||||
|
||||
// Needed for LTO.
|
||||
InitializeAllTargetInfos();
|
||||
@@ -1423,7 +1423,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||
}
|
||||
|
||||
// Parse command line options.
|
||||
ArgParser parser(ctx);
|
||||
ArgParser parser;
|
||||
opt::InputArgList args = parser.parse(argsArr);
|
||||
|
||||
// Parse and evaluate -mllvm options.
|
||||
@@ -2036,7 +2036,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||
|
||||
// Handle /functionpadmin
|
||||
for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
|
||||
parseFunctionPadMin(arg);
|
||||
parseFunctionPadMin(arg, config->machine);
|
||||
|
||||
if (tar) {
|
||||
tar->append("response.txt",
|
||||
@@ -2128,8 +2128,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||
// 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.
|
||||
@@ -2175,8 +2174,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2318,7 +2317,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||
|
||||
// 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;
|
||||
|
||||
@@ -2365,7 +2364,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||
|
||||
// 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) {
|
||||
@@ -2404,14 +2403,14 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||
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);
|
||||
}
|
||||
@@ -2448,7 +2447,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
|
||||
// Identify identical COMDAT sections to merge them.
|
||||
if (config->doICF != ICFLevel::None) {
|
||||
findKeepUniqueSections(ctx);
|
||||
doICF(ctx);
|
||||
doICF(ctx, config->doICF);
|
||||
}
|
||||
|
||||
// Write the result.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#ifndef LLD_COFF_DRIVER_H
|
||||
#define LLD_COFF_DRIVER_H
|
||||
|
||||
#include "COFFLinkerContext.h"
|
||||
#include "Config.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
@@ -29,6 +30,8 @@
|
||||
|
||||
namespace lld::coff {
|
||||
|
||||
extern std::unique_ptr<class LinkerDriver> driver;
|
||||
|
||||
using llvm::COFF::MachineTypes;
|
||||
using llvm::COFF::WindowsSubsystem;
|
||||
using std::optional;
|
||||
@@ -38,6 +41,10 @@ public:
|
||||
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
|
||||
@@ -52,8 +59,6 @@ struct ParsedDirectives {
|
||||
|
||||
class ArgParser {
|
||||
public:
|
||||
ArgParser(COFFLinkerContext &ctx);
|
||||
|
||||
// Parses command line options.
|
||||
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
|
||||
|
||||
@@ -70,13 +75,11 @@ private:
|
||||
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);
|
||||
|
||||
@@ -112,42 +115,6 @@ private:
|
||||
// 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();
|
||||
|
||||
@@ -204,69 +171,62 @@ private:
|
||||
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);
|
||||
|
||||
// Parses a string in the form of "<integer>[,<integer>]".
|
||||
void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr);
|
||||
|
||||
void parseGuard(StringRef arg);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
void parseAlternateName(StringRef);
|
||||
void parseMerge(StringRef);
|
||||
void parsePDBPageSize(StringRef);
|
||||
void parseSection(StringRef);
|
||||
void parseAligncomm(StringRef);
|
||||
|
||||
// Parses a string in the form of "[:<integer>]"
|
||||
void parseFunctionPadMin(llvm::opt::Arg *a);
|
||||
|
||||
// Parses a string in the form of "EMBED[,=<integer>]|NO".
|
||||
void parseManifest(StringRef arg);
|
||||
|
||||
// Parses a string in the form of "level=<string>|uiAccess=<string>"
|
||||
void parseManifestUAC(StringRef arg);
|
||||
|
||||
// Parses a string in the form of "cd|net[,(cd|net)]*"
|
||||
void parseSwaprun(StringRef arg);
|
||||
|
||||
// Create a resource file containing a manifest XML.
|
||||
std::unique_ptr<MemoryBuffer> createManifestRes();
|
||||
void createSideBySideManifest();
|
||||
std::string createDefaultXml();
|
||||
std::string createManifestXmlWithInternalMt(StringRef defaultXml);
|
||||
std::string createManifestXmlWithExternalMt(StringRef defaultXml);
|
||||
std::string createManifestXml();
|
||||
|
||||
std::unique_ptr<llvm::WritableMemoryBuffer>
|
||||
createMemoryBufferForManifestRes(size_t manifestRes);
|
||||
|
||||
// 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);
|
||||
|
||||
// Convert Windows resource files (.res files) to a .obj file.
|
||||
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
|
||||
ArrayRef<ObjFile *> objs);
|
||||
};
|
||||
|
||||
// Functions below this line are defined in DriverUtils.cpp.
|
||||
|
||||
void printHelp(const char *argv0);
|
||||
|
||||
// Parses a string in the form of "<integer>[,<integer>]".
|
||||
void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr);
|
||||
|
||||
void parseGuard(StringRef arg);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
void parseAlternateName(StringRef);
|
||||
void parseMerge(StringRef);
|
||||
void parsePDBPageSize(StringRef);
|
||||
void parseSection(StringRef);
|
||||
void parseAligncomm(StringRef);
|
||||
|
||||
// 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 "EMBED[,=<integer>]|NO".
|
||||
void parseManifest(StringRef arg);
|
||||
|
||||
// Parses a string in the form of "level=<string>|uiAccess=<string>"
|
||||
void parseManifestUAC(StringRef arg);
|
||||
|
||||
// Parses a string in the form of "cd|net[,(cd|net)]*"
|
||||
void parseSwaprun(StringRef arg);
|
||||
|
||||
// Create a resource file containing a manifest XML.
|
||||
std::unique_ptr<MemoryBuffer> createManifestRes();
|
||||
void createSideBySideManifest();
|
||||
|
||||
// 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);
|
||||
|
||||
// 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 {
|
||||
OPT_INVALID = 0,
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "COFFLinkerContext.h"
|
||||
#include "Config.h"
|
||||
#include "Driver.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
} // 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);
|
||||
@@ -85,8 +85,7 @@ void LinkerDriver::parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {
|
||||
|
||||
// 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);
|
||||
@@ -95,29 +94,28 @@ void LinkerDriver::parseVersion(StringRef arg, uint32_t *major,
|
||||
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)
|
||||
@@ -142,19 +140,19 @@ void LinkerDriver::parseSubsystem(StringRef arg, WindowsSubsystem *sys,
|
||||
|
||||
// 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);
|
||||
@@ -162,7 +160,7 @@ void LinkerDriver::parseMerge(StringRef 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;
|
||||
@@ -171,7 +169,7 @@ void LinkerDriver::parseMerge(StringRef s) {
|
||||
}
|
||||
}
|
||||
|
||||
void LinkerDriver::parsePDBPageSize(StringRef s) {
|
||||
void parsePDBPageSize(StringRef s) {
|
||||
int v;
|
||||
if (s.getAsInteger(0, v)) {
|
||||
error("/pdbpagesize: invalid argument: " + s);
|
||||
@@ -182,7 +180,7 @@ void LinkerDriver::parsePDBPageSize(StringRef s) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.config.pdbPageSize = v;
|
||||
config->pdbPageSize = v;
|
||||
}
|
||||
|
||||
static uint32_t parseSectionAttributes(StringRef s) {
|
||||
@@ -218,15 +216,15 @@ 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);
|
||||
@@ -237,57 +235,56 @@ void LinkerDriver::parseAligncomm(StringRef 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 (;;) {
|
||||
@@ -296,12 +293,12 @@ void LinkerDriver::parseManifestUAC(StringRef arg) {
|
||||
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);
|
||||
@@ -310,13 +307,13 @@ void LinkerDriver::parseManifestUAC(StringRef 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
|
||||
@@ -374,7 +371,7 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
std::string LinkerDriver::createDefaultXml() {
|
||||
static std::string createDefaultXml() {
|
||||
std::string ret;
|
||||
raw_string_ostream os(ret);
|
||||
|
||||
@@ -383,17 +380,17 @@ std::string LinkerDriver::createDefaultXml() {
|
||||
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"
|
||||
@@ -404,8 +401,7 @@ std::string LinkerDriver::createDefaultXml() {
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
LinkerDriver::createManifestXmlWithInternalMt(StringRef defaultXml) {
|
||||
static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
|
||||
std::unique_ptr<MemoryBuffer> defaultXmlCopy =
|
||||
MemoryBuffer::getMemBufferCopy(defaultXml);
|
||||
|
||||
@@ -414,11 +410,11 @@ LinkerDriver::createManifestXmlWithInternalMt(StringRef 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)));
|
||||
}
|
||||
@@ -426,8 +422,7 @@ LinkerDriver::createManifestXmlWithInternalMt(StringRef defaultXml) {
|
||||
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;
|
||||
@@ -444,14 +439,14 @@ LinkerDriver::createManifestXmlWithExternalMt(StringRef defaultXml) {
|
||||
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));
|
||||
@@ -463,9 +458,9 @@ LinkerDriver::createManifestXmlWithExternalMt(StringRef defaultXml) {
|
||||
->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())
|
||||
@@ -474,14 +469,14 @@ std::string LinkerDriver::createManifestXml() {
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -492,8 +487,7 @@ static void writeResFileHeader(char *&buf) {
|
||||
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;
|
||||
@@ -505,7 +499,7 @@ static void writeResEntryHeader(char *&buf, size_t 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.
|
||||
@@ -519,7 +513,7 @@ static void writeResEntryHeader(char *&buf, size_t manifestSize,
|
||||
}
|
||||
|
||||
// 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 =
|
||||
@@ -527,17 +521,17 @@ std::unique_ptr<MemoryBuffer> LinkerDriver::createManifestRes() {
|
||||
|
||||
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)
|
||||
@@ -549,7 +543,7 @@ void LinkerDriver::createSideBySideManifest() {
|
||||
// "<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(",");
|
||||
@@ -611,14 +605,14 @@ err:
|
||||
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;
|
||||
}
|
||||
@@ -645,26 +639,26 @@ static StringRef killAt(StringRef sym, bool prefix) {
|
||||
|
||||
// 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);
|
||||
@@ -673,9 +667,9 @@ void LinkerDriver::fixupExports() {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -687,20 +681,20 @@ void LinkerDriver::fixupExports() {
|
||||
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())
|
||||
@@ -710,11 +704,11 @@ void LinkerDriver::assignExportOrdinals() {
|
||||
|
||||
// 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 =
|
||||
@@ -723,14 +717,14 @@ void LinkerDriver::checkFailIfMismatch(StringRef arg, InputFile *source) {
|
||||
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) {
|
||||
@@ -755,18 +749,18 @@ MemoryBufferRef LinkerDriver::convertResToCOFF(ArrayRef<MemoryBufferRef> 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()));
|
||||
|
||||
@@ -796,6 +790,8 @@ static constexpr llvm::opt::OptTable::Info infoTable[] = {
|
||||
|
||||
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) {
|
||||
@@ -831,8 +827,6 @@ static cl::TokenizerCallback getQuotingStyle(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.
|
||||
@@ -843,8 +837,7 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
|
||||
// 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.
|
||||
@@ -853,8 +846,8 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
|
||||
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()) {
|
||||
@@ -866,10 +859,10 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
|
||||
|
||||
// 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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,7 +876,7 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
|
||||
|
||||
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) +
|
||||
@@ -928,7 +921,7 @@ ParsedDirectives ArgParser::parseDirectives(StringRef s) {
|
||||
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");
|
||||
@@ -958,10 +951,10 @@ std::vector<const char *> ArgParser::tokenize(StringRef s) {
|
||||
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
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace lld::coff {
|
||||
|
||||
class ICF {
|
||||
public:
|
||||
ICF(COFFLinkerContext &c) : ctx(c){};
|
||||
ICF(COFFLinkerContext &c, ICFLevel icfLevel) : icfLevel(icfLevel), ctx(c){};
|
||||
void run();
|
||||
|
||||
private:
|
||||
@@ -61,6 +61,7 @@ private:
|
||||
std::vector<SectionChunk *> chunks;
|
||||
int cnt = 0;
|
||||
std::atomic<bool> repeat = {false};
|
||||
ICFLevel icfLevel = ICFLevel::All;
|
||||
|
||||
COFFLinkerContext &ctx;
|
||||
};
|
||||
@@ -83,7 +84,7 @@ bool ICF::isEligible(SectionChunk *c) {
|
||||
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;
|
||||
|
||||
@@ -316,6 +317,8 @@ void ICF::run() {
|
||||
}
|
||||
|
||||
// Entry point to ICF.
|
||||
void doICF(COFFLinkerContext &ctx) { ICF(ctx).run(); }
|
||||
void doICF(COFFLinkerContext &ctx, ICFLevel icfLevel) {
|
||||
ICF(ctx, icfLevel).run();
|
||||
}
|
||||
|
||||
} // namespace lld::coff
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
|
||||
namespace lld::coff {
|
||||
|
||||
class Chunk;
|
||||
class COFFLinkerContext;
|
||||
|
||||
void doICF(COFFLinkerContext &ctx);
|
||||
void doICF(COFFLinkerContext &ctx, ICFLevel);
|
||||
|
||||
} // namespace lld::coff
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ std::string lld::toString(const coff::InputFile *file) {
|
||||
/// 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) {
|
||||
@@ -81,9 +81,9 @@ static void checkAndSetWeakAlias(COFFLinkerContext &ctx, InputFile *f,
|
||||
// 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;
|
||||
}
|
||||
@@ -109,13 +109,13 @@ void ArchiveFile::parse() {
|
||||
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) {
|
||||
@@ -237,7 +237,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
|
||||
// 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)
|
||||
@@ -260,7 +260,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
|
||||
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
|
||||
@@ -366,7 +366,7 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
|
||||
// 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);
|
||||
}
|
||||
@@ -400,7 +400,7 @@ void ObjFile::initializeSymbols() {
|
||||
} 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
|
||||
@@ -421,7 +421,7 @@ void ObjFile::initializeSymbols() {
|
||||
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) {
|
||||
@@ -436,7 +436,7 @@ void ObjFile::initializeSymbols() {
|
||||
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.
|
||||
@@ -496,10 +496,10 @@ void ObjFile::handleComdatSelection(
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -511,7 +511,7 @@ void ObjFile::handleComdatSelection(
|
||||
// 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());
|
||||
@@ -530,7 +530,7 @@ void ObjFile::handleComdatSelection(
|
||||
|
||||
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;
|
||||
@@ -607,7 +607,7 @@ std::optional<Symbol *> ObjFile::createDefined(
|
||||
|
||||
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();
|
||||
@@ -751,7 +751,7 @@ void ObjFile::initializeFlags() {
|
||||
// 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;
|
||||
@@ -906,7 +906,7 @@ ObjFile::getVariableLocation(StringRef var) {
|
||||
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);
|
||||
@@ -935,12 +935,9 @@ void ObjFile::enqueuePdbFile(StringRef path, ObjFile *fromFile) {
|
||||
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);
|
||||
@@ -996,10 +993,8 @@ BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
|
||||
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
|
||||
@@ -1019,9 +1014,36 @@ BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
|
||||
|
||||
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
|
||||
@@ -1033,9 +1055,9 @@ void BitcodeFile::parse() {
|
||||
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()) {
|
||||
@@ -1045,7 +1067,7 @@ void BitcodeFile::parse() {
|
||||
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;
|
||||
@@ -1062,7 +1084,7 @@ void BitcodeFile::parse() {
|
||||
}
|
||||
symbols.push_back(sym);
|
||||
if (objSym.isUsed())
|
||||
ctx.config.gcroot.push_back(sym);
|
||||
config->gcroot.push_back(sym);
|
||||
}
|
||||
directives = obj->getCOFFLinkerOpts();
|
||||
}
|
||||
@@ -1088,8 +1110,10 @@ MachineTypes BitcodeFile::getMachineType() {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -333,7 +333,8 @@ public:
|
||||
// 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; }
|
||||
|
||||
@@ -357,8 +358,8 @@ public:
|
||||
// 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.
|
||||
@@ -407,16 +408,7 @@ inline bool isBitcode(MemoryBufferRef mb) {
|
||||
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);
|
||||
|
||||
@@ -73,13 +73,12 @@ static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
|
||||
|
||||
// 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;
|
||||
@@ -89,18 +88,18 @@ getSymbolStrings(const COFFLinkerContext &ctx,
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "LTO.h"
|
||||
#include "COFFLinkerContext.h"
|
||||
#include "Config.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
@@ -54,17 +53,17 @@ static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
|
||||
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
|
||||
@@ -75,7 +74,7 @@ lto::Config BitcodeCompiler::createConfig() {
|
||||
// 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_;
|
||||
@@ -85,42 +84,42 @@ lto::Config BitcodeCompiler::createConfig() {
|
||||
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;
|
||||
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 (ctx.config.saveTemps)
|
||||
checkError(c.addSaveTemps(std::string(ctx.config.outputFile) + ".",
|
||||
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;
|
||||
@@ -133,7 +132,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
|
||||
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.
|
||||
@@ -162,7 +161,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
|
||||
|
||||
// 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);
|
||||
@@ -172,8 +171,8 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
|
||||
// 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);
|
||||
@@ -192,23 +191,23 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
|
||||
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) {
|
||||
@@ -232,19 +231,19 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
|
||||
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)));
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <vector>
|
||||
|
||||
namespace llvm::lto {
|
||||
struct Config;
|
||||
class LTO;
|
||||
}
|
||||
|
||||
@@ -40,11 +39,11 @@ class COFFLinkerContext;
|
||||
|
||||
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;
|
||||
@@ -53,11 +52,6 @@ private:
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -63,8 +63,7 @@ static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
|
||||
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;
|
||||
@@ -81,11 +80,11 @@ static void sortUniqueSymbols(std::vector<Defined *> &syms,
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -134,8 +133,8 @@ static void getSymbols(const COFFLinkerContext &ctx,
|
||||
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.
|
||||
@@ -185,7 +184,7 @@ getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
|
||||
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
|
||||
@@ -200,13 +199,13 @@ getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -224,25 +223,24 @@ void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
|
||||
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.
|
||||
@@ -297,8 +295,8 @@ void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
|
||||
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();
|
||||
@@ -318,12 +316,12 @@ void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
|
||||
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";
|
||||
|
||||
@@ -51,7 +51,7 @@ void markLive(COFFLinkerContext &ctx) {
|
||||
};
|
||||
|
||||
// Add GC root chunks.
|
||||
for (Symbol *b : ctx.config.gcroot)
|
||||
for (Symbol *b : config->gcroot)
|
||||
addSym(b);
|
||||
|
||||
while (!worklist.empty()) {
|
||||
|
||||
@@ -24,9 +24,8 @@ using namespace lld;
|
||||
using namespace lld::coff;
|
||||
|
||||
AutoExporter::AutoExporter(
|
||||
COFFLinkerContext &ctx,
|
||||
const llvm::DenseSet<StringRef> &manualExcludeSymbols)
|
||||
: manualExcludeSymbols(manualExcludeSymbols), ctx(ctx) {
|
||||
: manualExcludeSymbols(manualExcludeSymbols) {
|
||||
excludeLibs = {
|
||||
"libgcc",
|
||||
"libgcc_s",
|
||||
@@ -81,7 +80,7 @@ AutoExporter::AutoExporter(
|
||||
"_NULL_THUNK_DATA",
|
||||
};
|
||||
|
||||
if (ctx.config.machine == I386) {
|
||||
if (config->machine == I386) {
|
||||
excludeSymbols = {
|
||||
"__NULL_IMPORT_DESCRIPTOR",
|
||||
"__pei386_runtime_relocator",
|
||||
@@ -129,7 +128,8 @@ void AutoExporter::addExcludedSymbol(StringRef symbol) {
|
||||
excludeSymbols.insert(symbol);
|
||||
}
|
||||
|
||||
bool AutoExporter::shouldExport(Defined *sym) const {
|
||||
bool AutoExporter::shouldExport(const COFFLinkerContext &ctx,
|
||||
Defined *sym) const {
|
||||
if (!sym || !sym->getChunk())
|
||||
return false;
|
||||
|
||||
@@ -167,15 +167,14 @@ bool AutoExporter::shouldExport(Defined *sym) const {
|
||||
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)) {
|
||||
@@ -187,9 +186,9 @@ void lld::coff::writeDefFile(StringRef name,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -213,10 +212,8 @@ lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
|
||||
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
|
||||
@@ -257,7 +254,7 @@ void lld::coff::wrapSymbols(COFFLinkerContext &ctx,
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ class COFFLinkerContext;
|
||||
// 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);
|
||||
@@ -39,13 +38,10 @@ public:
|
||||
|
||||
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
|
||||
|
||||
115
lld/COFF/PDB.cpp
115
lld/COFF/PDB.cpp
@@ -67,6 +67,8 @@ using namespace lld::coff;
|
||||
using llvm::object::coff_section;
|
||||
using llvm::pdb::StringTableFixup;
|
||||
|
||||
static ExitOnError exitOnErr;
|
||||
|
||||
namespace {
|
||||
class DebugSHandler;
|
||||
|
||||
@@ -144,11 +146,6 @@ public:
|
||||
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;
|
||||
|
||||
@@ -244,7 +241,7 @@ public:
|
||||
// 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
|
||||
@@ -259,7 +256,7 @@ void PDBLinker::pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
|
||||
// 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);
|
||||
@@ -270,7 +267,7 @@ void PDBLinker::pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
|
||||
// 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;
|
||||
@@ -341,8 +338,8 @@ static SymbolKind symbolKind(ArrayRef<uint8_t> recordData) {
|
||||
}
|
||||
|
||||
/// 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);
|
||||
@@ -373,7 +370,7 @@ void PDBLinker::translateIdSymbols(MutableArrayRef<uint8_t> &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;
|
||||
@@ -578,7 +575,7 @@ void PDBLinker::writeSymbolRecord(SectionChunk *debugChunk,
|
||||
|
||||
// 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(
|
||||
@@ -645,7 +642,6 @@ void PDBLinker::analyzeSymbolSubsection(
|
||||
|
||||
Error PDBLinker::writeAllModuleSymbolRecords(ObjFile *file,
|
||||
BinaryStreamWriter &writer) {
|
||||
ExitOnError exitOnErr;
|
||||
std::vector<uint8_t> storage;
|
||||
SmallVector<uint32_t, 4> scopes;
|
||||
|
||||
@@ -762,7 +758,6 @@ void DebugSHandler::handleDebugS(SectionChunk *debugChunk) {
|
||||
contents = SectionChunk::consumeDebugMagic(contents, ".debug$S");
|
||||
DebugSubsectionArray subsections;
|
||||
BinaryStreamReader reader(contents, support::little);
|
||||
ExitOnError exitOnErr;
|
||||
exitOnErr(reader.readArray(subsections, contents.size()));
|
||||
debugChunk->sortRelocations();
|
||||
|
||||
@@ -872,7 +867,6 @@ Error UnrelocatedDebugSubsection::commit(BinaryStreamWriter &writer) const {
|
||||
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);
|
||||
@@ -941,8 +935,6 @@ void DebugSHandler::finish() {
|
||||
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
|
||||
@@ -992,7 +984,7 @@ void DebugSHandler::finish() {
|
||||
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);
|
||||
}
|
||||
@@ -1003,8 +995,8 @@ void DebugSHandler::finish() {
|
||||
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;
|
||||
}
|
||||
@@ -1031,7 +1023,6 @@ void PDBLinker::addDebugSymbols(TpiSource *source) {
|
||||
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.
|
||||
@@ -1073,7 +1064,6 @@ void PDBLinker::addDebugSymbols(TpiSource *source) {
|
||||
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();
|
||||
@@ -1103,12 +1093,11 @@ void PDBLinker::addDebug(TpiSource *source) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -1116,8 +1105,7 @@ void PDBLinker::addDebug(TpiSource *source) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -1158,7 +1146,7 @@ void PDBLinker::addObjectsToPDB() {
|
||||
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.
|
||||
@@ -1172,9 +1160,8 @@ void PDBLinker::addObjectsToPDB() {
|
||||
|
||||
// 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());
|
||||
@@ -1182,7 +1169,7 @@ void PDBLinker::addObjectsToPDB() {
|
||||
}
|
||||
t2.stop();
|
||||
|
||||
if (ctx.config.showSummary) {
|
||||
if (config->showSummary) {
|
||||
for (TpiSource *source : ctx.tpiSourceList) {
|
||||
nbTypeRecords += source->nbTypeRecords;
|
||||
nbTypeRecordsBytes += source->nbTypeRecordsBytes;
|
||||
@@ -1209,7 +1196,7 @@ void PDBLinker::addPublicsToPDB() {
|
||||
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_")) {
|
||||
@@ -1227,7 +1214,7 @@ void PDBLinker::addPublicsToPDB() {
|
||||
}
|
||||
|
||||
void PDBLinker::printStats() {
|
||||
if (!ctx.config.showSummary)
|
||||
if (!config->showSummary)
|
||||
return;
|
||||
|
||||
SmallString<256> buffer;
|
||||
@@ -1295,11 +1282,11 @@ void PDBLinker::printStats() {
|
||||
<< "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());
|
||||
@@ -1309,7 +1296,7 @@ void PDBLinker::printStats() {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -1319,17 +1306,16 @@ void PDBLinker::addNatvisFiles() {
|
||||
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);
|
||||
@@ -1339,7 +1325,7 @@ void PDBLinker::addNamedStreams() {
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1386,8 +1372,8 @@ static std::string quote(ArrayRef<StringRef> args) {
|
||||
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
|
||||
@@ -1412,27 +1398,27 @@ static void fillLinkerVerRecord(Compile3Sym &cs, MachineTypes machine) {
|
||||
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");
|
||||
@@ -1475,7 +1461,7 @@ static void addLinkerModuleCoffGroup(PartialSection *sec,
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -1488,7 +1474,7 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
|
||||
|
||||
// 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.
|
||||
@@ -1502,7 +1488,6 @@ void PDBLinker::addImportFilesToPDB() {
|
||||
if (ctx.importFileInstances.empty())
|
||||
return;
|
||||
|
||||
ExitOnError exitOnErr;
|
||||
std::map<std::string, llvm::pdb::DbiModuleDescriptorBuilder *> dllToModuleDbi;
|
||||
|
||||
for (ImportFile *file : ctx.importFileInstances) {
|
||||
@@ -1549,7 +1534,7 @@ void PDBLinker::addImportFilesToPDB() {
|
||||
ons.Name = file->dllName;
|
||||
ons.Signature = 0;
|
||||
|
||||
fillLinkerVerRecord(cs, ctx.config.machine);
|
||||
fillLinkerVerRecord(cs);
|
||||
|
||||
ts.Name = thunk->getName();
|
||||
ts.Parent = 0;
|
||||
@@ -1613,8 +1598,7 @@ void lld::coff::createPDB(COFFLinkerContext &ctx,
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -1635,7 +1619,7 @@ void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
|
||||
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
|
||||
@@ -1644,10 +1628,9 @@ void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
|
||||
}
|
||||
|
||||
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 *"));
|
||||
@@ -1656,7 +1639,7 @@ void PDBLinker::addSections(ArrayRef<uint8_t> sectionTable) {
|
||||
|
||||
// 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());
|
||||
@@ -1686,14 +1669,14 @@ void PDBLinker::commit(codeview::GUID *guid) {
|
||||
// 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:
|
||||
@@ -1718,7 +1701,7 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
|
||||
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")
|
||||
|
||||
@@ -53,20 +53,20 @@ void SymbolTable::addFile(InputFile *file) {
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -143,7 +143,7 @@ getFileLine(const SectionChunk *c, uint32_t addr) {
|
||||
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;
|
||||
}
|
||||
@@ -201,7 +201,7 @@ getSymbolLocations(ObjFile *file, uint32_t symIndex, size_t maxStrings) {
|
||||
<< "\n>>> ";
|
||||
os << toString(file);
|
||||
if (loc.sym)
|
||||
os << ":(" << toString(file->ctx, *loc.sym) << ')';
|
||||
os << ":(" << toString(*loc.sym) << ')';
|
||||
}
|
||||
return std::make_pair(symbolLocations, numLocations);
|
||||
}
|
||||
@@ -236,11 +236,10 @@ struct UndefinedDiag {
|
||||
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;
|
||||
@@ -256,7 +255,7 @@ static void reportUndefinedSymbol(const COFFLinkerContext &ctx,
|
||||
}
|
||||
if (numDisplayedRefs < numRefs)
|
||||
os << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times";
|
||||
errorOrWarn(os.str(), ctx.config.forceUnresolved);
|
||||
errorOrWarn(os.str());
|
||||
}
|
||||
|
||||
void SymbolTable::loadMinGWSymbols() {
|
||||
@@ -270,7 +269,7 @@ 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).
|
||||
@@ -291,7 +290,7 @@ void SymbolTable::loadMinGWSymbols() {
|
||||
}
|
||||
// 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);
|
||||
@@ -301,7 +300,7 @@ void SymbolTable::loadMinGWSymbols() {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -359,7 +358,7 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
|
||||
// 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());
|
||||
@@ -384,13 +383,12 @@ static void reportProblemSymbols(
|
||||
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]");
|
||||
}
|
||||
|
||||
@@ -415,7 +413,7 @@ static void reportProblemSymbols(
|
||||
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]");
|
||||
}
|
||||
};
|
||||
@@ -428,7 +426,7 @@ static void reportProblemSymbols(
|
||||
processFile(file, file->getSymbols());
|
||||
|
||||
for (const UndefinedDiag &undefDiag : undefDiags)
|
||||
reportUndefinedSymbol(ctx, undefDiag);
|
||||
reportUndefinedSymbol(undefDiag);
|
||||
}
|
||||
|
||||
void SymbolTable::reportUnresolvable() {
|
||||
@@ -448,7 +446,7 @@ void SymbolTable::reportUnresolvable() {
|
||||
}
|
||||
if (name.contains("_PchSym_"))
|
||||
continue;
|
||||
if (ctx.config.autoImport && impSymbol(name))
|
||||
if (config->autoImport && impSymbol(name))
|
||||
continue;
|
||||
undefs.insert(sym);
|
||||
}
|
||||
@@ -493,7 +491,7 @@ void SymbolTable::resolveRemainingUndefines() {
|
||||
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;
|
||||
@@ -505,19 +503,19 @@ void SymbolTable::resolveRemainingUndefines() {
|
||||
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) {
|
||||
@@ -644,7 +642,7 @@ void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile,
|
||||
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())) {
|
||||
@@ -656,7 +654,7 @@ void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile,
|
||||
os << getSourceLocation(newFile, newSc, newSectionOffset,
|
||||
existing->getName());
|
||||
|
||||
if (ctx.config.forceMultiple)
|
||||
if (config->forceMultiple)
|
||||
warn(os.str());
|
||||
else
|
||||
error(os.str());
|
||||
@@ -666,7 +664,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
|
||||
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);
|
||||
@@ -679,7 +677,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) {
|
||||
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);
|
||||
@@ -753,7 +751,7 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -790,7 +788,7 @@ Symbol *SymbolTable::find(StringRef name) const {
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::findUnderscore(StringRef name) const {
|
||||
if (ctx.config.machine == I386)
|
||||
if (config->machine == I386)
|
||||
return find(("_" + name).str());
|
||||
return find(name);
|
||||
}
|
||||
@@ -837,7 +835,7 @@ Symbol *SymbolTable::findMangle(StringRef 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("_"))
|
||||
@@ -864,10 +862,10 @@ void SymbolTable::compileBitcodeFiles() {
|
||||
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);
|
||||
|
||||
@@ -47,7 +47,7 @@ class Symbol;
|
||||
// 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);
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Symbols.h"
|
||||
#include "COFFLinkerContext.h"
|
||||
#include "InputFiles.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
@@ -28,15 +27,14 @@ static_assert(sizeof(SymbolUnion) <= 48,
|
||||
"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)
|
||||
@@ -45,12 +43,11 @@ static std::string maybeDemangleSymbol(const COFFLinkerContext &ctx,
|
||||
}
|
||||
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 {
|
||||
@@ -105,24 +102,23 @@ COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
|
||||
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.
|
||||
@@ -134,11 +130,11 @@ Defined *Undefined::getWeakAlias() {
|
||||
|
||||
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
|
||||
|
||||
@@ -22,6 +22,13 @@
|
||||
|
||||
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;
|
||||
@@ -30,7 +37,6 @@ using llvm::object::coff_import_header;
|
||||
using llvm::object::coff_symbol_generic;
|
||||
|
||||
class ArchiveFile;
|
||||
class COFFLinkerContext;
|
||||
class InputFile;
|
||||
class ObjFile;
|
||||
class SymbolTable;
|
||||
@@ -244,25 +250,29 @@ private:
|
||||
// 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
|
||||
@@ -383,8 +393,7 @@ public:
|
||||
// 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;
|
||||
@@ -406,9 +415,8 @@ private:
|
||||
// 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;
|
||||
@@ -503,10 +511,6 @@ void replaceSymbol(Symbol *s, ArgT &&... arg) {
|
||||
}
|
||||
} // 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
|
||||
|
||||
@@ -32,13 +32,13 @@ public:
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace {
|
||||
|
||||
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) {}
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
|
||||
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;
|
||||
@@ -138,15 +138,14 @@ private:
|
||||
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 {
|
||||
@@ -156,15 +155,12 @@ public:
|
||||
|
||||
// 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 {
|
||||
@@ -199,8 +195,7 @@ public:
|
||||
// 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:
|
||||
@@ -213,12 +208,6 @@ 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();
|
||||
@@ -228,7 +217,6 @@ private:
|
||||
void createSEHTable();
|
||||
void createRuntimePseudoRelocs();
|
||||
void insertCtorDtorSymbols();
|
||||
void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols);
|
||||
void createGuardCFTables();
|
||||
void markSymbolsForRVATable(ObjFile *file,
|
||||
ArrayRef<SectionChunk *> symIdxChunks,
|
||||
@@ -246,7 +234,6 @@ private:
|
||||
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();
|
||||
@@ -344,14 +331,14 @@ void OutputSection::merge(OutputSection *other) {
|
||||
}
|
||||
|
||||
// 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));
|
||||
@@ -364,8 +351,8 @@ void OutputSection::addContributingPartialSection(PartialSection *sec) {
|
||||
|
||||
// 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:
|
||||
@@ -376,7 +363,7 @@ bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
|
||||
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:
|
||||
@@ -395,19 +382,19 @@ bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
|
||||
|
||||
// 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");
|
||||
@@ -428,7 +415,7 @@ Writer::getThunk(DenseMap<uint64_t, Defined *> &lastThunks, Defined *target,
|
||||
// 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;
|
||||
@@ -524,7 +511,7 @@ bool Writer::createThunks(OutputSection *os, int margin) {
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -552,7 +539,7 @@ bool Writer::verifyRanges(const std::vector<Chunk *> chunks) {
|
||||
// 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;
|
||||
@@ -613,7 +600,7 @@ void Writer::finalizeAddresses() {
|
||||
}
|
||||
|
||||
void Writer::writePEChecksum() {
|
||||
if (!ctx.config.writeCheckSum) {
|
||||
if (!config->writeCheckSum) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -671,8 +658,8 @@ void Writer::run() {
|
||||
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>();
|
||||
@@ -688,7 +675,7 @@ void Writer::run() {
|
||||
|
||||
t1.stop();
|
||||
|
||||
if (!ctx.config.pdbPath.empty() && ctx.config.debug) {
|
||||
if (!config->pdbPath.empty() && config->debug) {
|
||||
assert(buildId);
|
||||
createPDB(ctx, sectionTable, buildId->buildId);
|
||||
}
|
||||
@@ -717,11 +704,11 @@ static StringRef getOutputSectionName(StringRef name) {
|
||||
}
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
@@ -800,7 +787,7 @@ bool Writer::fixGnuImportChunks() {
|
||||
// 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.
|
||||
@@ -843,8 +830,7 @@ void Writer::locateImportTables() {
|
||||
// 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
|
||||
@@ -854,7 +840,7 @@ static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name,
|
||||
// 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;
|
||||
@@ -864,15 +850,15 @@ static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -917,12 +903,12 @@ void Writer::createSections() {
|
||||
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"))
|
||||
@@ -1004,8 +990,6 @@ void Writer::createSections() {
|
||||
}
|
||||
|
||||
void Writer::createMiscChunks() {
|
||||
Configuration *config = &ctx.config;
|
||||
|
||||
for (MergeChunk *p : ctx.mergeChunkInstances) {
|
||||
if (p) {
|
||||
p->finalizeContents();
|
||||
@@ -1033,7 +1017,7 @@ void Writer::createMiscChunks() {
|
||||
// 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});
|
||||
}
|
||||
|
||||
@@ -1077,16 +1061,16 @@ void Writer::createImportTables() {
|
||||
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);
|
||||
@@ -1106,15 +1090,15 @@ void Writer::appendImportThunks() {
|
||||
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())
|
||||
@@ -1128,9 +1112,9 @@ void Writer::createExportTable() {
|
||||
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);
|
||||
}
|
||||
@@ -1139,9 +1123,9 @@ void Writer::createExportTable() {
|
||||
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() {
|
||||
@@ -1268,7 +1252,7 @@ void Writer::createSymbolAndStringTable() {
|
||||
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");
|
||||
@@ -1276,7 +1260,7 @@ void Writer::createSymbolAndStringTable() {
|
||||
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);
|
||||
@@ -1313,7 +1297,7 @@ void Writer::createSymbolAndStringTable() {
|
||||
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() {
|
||||
@@ -1322,7 +1306,7 @@ 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;
|
||||
@@ -1330,8 +1314,8 @@ void Writer::mergeSections() {
|
||||
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;
|
||||
}
|
||||
@@ -1350,8 +1334,6 @@ void Writer::mergeSections() {
|
||||
// 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();
|
||||
@@ -1409,7 +1391,6 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||
// 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);
|
||||
@@ -1584,7 +1565,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||
|
||||
// Write section table
|
||||
for (OutputSection *sec : ctx.outputSections) {
|
||||
sec->writeHeaderTo(buf, config->debug);
|
||||
sec->writeHeaderTo(buf);
|
||||
buf += sizeof(coff_section);
|
||||
}
|
||||
sectionTable = ArrayRef<uint8_t>(
|
||||
@@ -1693,8 +1674,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
|
||||
|
||||
// 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.
|
||||
@@ -1703,8 +1684,7 @@ void Writer::markSymbolsWithRelocations(ObjFile *file,
|
||||
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;
|
||||
@@ -1719,8 +1699,6 @@ void Writer::markSymbolsWithRelocations(ObjFile *file,
|
||||
// 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;
|
||||
@@ -1884,7 +1862,7 @@ void Writer::createRuntimePseudoRelocs() {
|
||||
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)
|
||||
@@ -1913,10 +1891,10 @@ void Writer::createRuntimePseudoRelocs() {
|
||||
// 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);
|
||||
@@ -1933,7 +1911,7 @@ void Writer::insertCtorDtorSymbols() {
|
||||
// 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)
|
||||
@@ -1944,6 +1922,10 @@ void Writer::setSectionPermissions() {
|
||||
|
||||
// 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();
|
||||
@@ -1965,8 +1947,6 @@ void Writer::writeBuildId() {
|
||||
// 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.
|
||||
@@ -2023,7 +2003,7 @@ void Writer::sortExceptionTable() {
|
||||
};
|
||||
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) +
|
||||
@@ -2034,7 +2014,7 @@ void Writer::sortExceptionTable() {
|
||||
[](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) +
|
||||
@@ -2075,7 +2055,7 @@ void Writer::sortCRTSectionChunks(std::vector<Chunk *> &chunks) {
|
||||
};
|
||||
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() +
|
||||
@@ -2101,7 +2081,7 @@ uint32_t Writer::getSizeOfInitializedData() {
|
||||
|
||||
// Add base relocations to .reloc section.
|
||||
void Writer::addBaserels() {
|
||||
if (!ctx.config.relocatable)
|
||||
if (!config->relocatable)
|
||||
return;
|
||||
relocSec->chunks.clear();
|
||||
std::vector<Baserel> v;
|
||||
@@ -2164,14 +2144,14 @@ void Writer::fixTlsAlignment() {
|
||||
|
||||
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);
|
||||
@@ -2186,7 +2166,7 @@ void Writer::checkLoadConfig() {
|
||||
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;
|
||||
}
|
||||
@@ -2195,7 +2175,7 @@ void Writer::checkLoadConfig() {
|
||||
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 " +
|
||||
@@ -2205,7 +2185,7 @@ void Writer::checkLoadConfig() {
|
||||
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
|
||||
@@ -2228,7 +2208,7 @@ void Writer::checkLoadConfigGuardData(const T *loadConfig) {
|
||||
|
||||
#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) \
|
||||
@@ -2236,7 +2216,7 @@ void Writer::checkLoadConfigGuardData(const T *loadConfig) {
|
||||
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")
|
||||
@@ -2247,13 +2227,13 @@ void Writer::checkLoadConfigGuardData(const T *loadConfig) {
|
||||
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")
|
||||
|
||||
@@ -45,9 +45,9 @@ public:
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user