#include "InputFiles.h"
#include "COFFLinkerContext.h"
#include "Chunks.h"
#include "Config.h"
#include "DebugTypes.h"
#include "Driver.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "lld/Common/DWARF.h"
#include "llvm-c/lto.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/TargetParser/Triple.h"
#include <cstring>
#include <optional>
#include <system_error>
#include <utility>
using namespace llvm;
using namespace llvm::COFF;
using namespace llvm::codeview;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace lld;
using namespace lld::coff;
using llvm::Triple;
using llvm::support::ulittle32_t;
static StringRef getBasename(StringRef path) {
return sys::path::filename(path, sys::path::Style::windows);
}
std::string lld::toString(const coff::InputFile *file) {
if (!file)
return "<internal>";
if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
return std::string(file->getName());
return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
")")
.str();
}
static void checkAndSetWeakAlias(COFFLinkerContext &ctx, InputFile *f,
Symbol *source, Symbol *target) {
if (auto *u = dyn_cast<Undefined>(source)) {
if (u->weakAlias && u->weakAlias != target) {
if (ctx.config.allowDuplicateWeak)
return;
ctx.symtab.reportDuplicate(source, f);
}
u->weakAlias = target;
}
}
static bool ignoredSymbolName(StringRef name) {
return name == "@feat.00" || name == "@comp.id";
}
ArchiveFile::ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m)
: InputFile(ctx, ArchiveKind, m) {}
void ArchiveFile::parse() {
file = CHECK(Archive::create(mb), this);
for (const Archive::Symbol &sym : file->symbols())
ctx.symtab.addLazyArchive(this, sym);
}
void ArchiveFile::addMember(const Archive::Symbol &sym) {
const Archive::Child &c =
CHECK(sym.getMember(),
"could not get the member for symbol " + toCOFFString(ctx, sym));
if (!seen.insert(c.getChildOffset()).second)
return;
ctx.driver.enqueueArchiveMember(c, sym, getName());
}
std::vector<MemoryBufferRef> lld::coff::getArchiveMembers(Archive *file) {
std::vector<MemoryBufferRef> v;
Error err = Error::success();
for (const Archive::Child &c : file->children(err)) {
MemoryBufferRef mbref =
CHECK(c.getMemoryBufferRef(),
file->getFileName() +
": could not get the buffer for a child of the archive");
v.push_back(mbref);
}
if (err)
fatal(file->getFileName() +
": Archive::children failed: " + toString(std::move(err)));
return v;
}
void ObjFile::parseLazy() {
std::unique_ptr<Binary> coffObjPtr = CHECK(createBinary(mb), this);
COFFObjectFile *coffObj = cast<COFFObjectFile>(coffObjPtr.get());
uint32_t numSymbols = coffObj->getNumberOfSymbols();
for (uint32_t i = 0; i < numSymbols; ++i) {
COFFSymbolRef coffSym = check(coffObj->getSymbol(i));
if (coffSym.isUndefined() || !coffSym.isExternal() ||
coffSym.isWeakExternal())
continue;
StringRef name = check(coffObj->getSymbolName(coffSym));
if (coffSym.isAbsolute() && ignoredSymbolName(name))
continue;
ctx.symtab.addLazyObject(this, name);
i += coffSym.getNumberOfAuxSymbols();
}
}
struct ECMapEntry {
ulittle32_t src;
ulittle32_t dst;
ulittle32_t type;
};
void ObjFile::initializeECThunks() {
for (SectionChunk *chunk : hybmpChunks) {
if (chunk->getContents().size() % sizeof(ECMapEntry)) {
error("Invalid .hybmp chunk size " + Twine(chunk->getContents().size()));
continue;
}
const uint8_t *end =
chunk->getContents().data() + chunk->getContents().size();
for (const uint8_t *iter = chunk->getContents().data(); iter != end;
iter += sizeof(ECMapEntry)) {
auto entry = reinterpret_cast<const ECMapEntry *>(iter);
switch (entry->type) {
case Arm64ECThunkType::Entry:
ctx.symtab.addEntryThunk(getSymbol(entry->src), getSymbol(entry->dst));
break;
case Arm64ECThunkType::Exit:
case Arm64ECThunkType::GuestExit:
break;
default:
warn("Ignoring unknown EC thunk type " + Twine(entry->type));
}
}
}
}
void ObjFile::parse() {
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
if (auto *obj = dyn_cast<COFFObjectFile>(bin.get())) {
bin.release();
coffObj.reset(obj);
} else {
fatal(toString(this) + " is not a COFF file");
}
initializeChunks();
initializeSymbols();
initializeFlags();
initializeDependencies();
initializeECThunks();
}
const coff_section *ObjFile::getSection(uint32_t i) {
auto sec = coffObj->getSection(i);
if (!sec)
fatal("getSection failed: #" + Twine(i) + ": " + toString(sec.takeError()));
return *sec;
}
static SectionChunk *const pendingComdat = reinterpret_cast<SectionChunk *>(1);
void ObjFile::initializeChunks() {
uint32_t numSections = coffObj->getNumberOfSections();
sparseChunks.resize(numSections + 1);
for (uint32_t i = 1; i < numSections + 1; ++i) {
const coff_section *sec = getSection(i);
if (sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
sparseChunks[i] = pendingComdat;
else
sparseChunks[i] = readSection(i, nullptr, "");
}
}
SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
const coff_aux_section_definition *def,
StringRef leaderName) {
const coff_section *sec = getSection(sectionNumber);
StringRef name;
if (Expected<StringRef> e = coffObj->getSectionName(sec))
name = *e;
else
fatal("getSectionName failed: #" + Twine(sectionNumber) + ": " +
toString(e.takeError()));
if (name == ".drectve") {
ArrayRef<uint8_t> data;
cantFail(coffObj->getSectionContents(sec, data));
directives = StringRef((const char *)data.data(), data.size());
return nullptr;
}
if (name == ".llvm_addrsig") {
addrsigSec = sec;
return nullptr;
}
if (name == ".llvm.call-graph-profile") {
callgraphSec = sec;
return nullptr;
}
if (!ctx.config.includeDwarfChunks && name.starts_with(".debug_"))
return nullptr;
if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
return nullptr;
SectionChunk *c;
if (isArm64EC(getMachineType()))
c = make<SectionChunkEC>(this, sec);
else
c = make<SectionChunk>(this, sec);
if (def)
c->checksum = def->CheckSum;
if (c->isCodeView())
debugChunks.push_back(c);
else if (name == ".gfids$y")
guardFidChunks.push_back(c);
else if (name == ".giats$y")
guardIATChunks.push_back(c);
else if (name == ".gljmp$y")
guardLJmpChunks.push_back(c);
else if (name == ".gehcont$y")
guardEHContChunks.push_back(c);
else if (name == ".sxdata")
sxDataChunks.push_back(c);
else if (isArm64EC(getMachineType()) && name == ".hybmp$x")
hybmpChunks.push_back(c);
else if (ctx.config.tailMerge && sec->NumberOfRelocations == 0 &&
name == ".rdata" && leaderName.starts_with("??_C@"))
MergeChunk::addSection(ctx, c);
else if (name == ".rsrc" || name.starts_with(".rsrc$"))
resourceChunks.push_back(c);
else
chunks.push_back(c);
return c;
}
void ObjFile::includeResourceChunks() {
chunks.insert(chunks.end(), resourceChunks.begin(), resourceChunks.end());
}
void ObjFile::readAssociativeDefinition(
COFFSymbolRef sym, const coff_aux_section_definition *def) {
readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj()));
}
void ObjFile::readAssociativeDefinition(COFFSymbolRef sym,
const coff_aux_section_definition *def,
uint32_t parentIndex) {
SectionChunk *parent = sparseChunks[parentIndex];
int32_t sectionNumber = sym.getSectionNumber();
auto diag = [&]() {
StringRef name = check(coffObj->getSymbolName(sym));
StringRef parentName;
const coff_section *parentSec = getSection(parentIndex);
if (Expected<StringRef> e = coffObj->getSectionName(parentSec))
parentName = *e;
error(toString(this) + ": associative comdat " + name + " (sec " +
Twine(sectionNumber) + ") has invalid reference to section " +
parentName + " (sec " + Twine(parentIndex) + ")");
};
if (parent == pendingComdat) {
diag();
return;
}
if (parent) {
SectionChunk *c = readSection(sectionNumber, def, "");
sparseChunks[sectionNumber] = c;
if (c) {
c->selection = IMAGE_COMDAT_SELECT_ASSOCIATIVE;
parent->addAssociative(c);
}
} else {
sparseChunks[sectionNumber] = nullptr;
}
}
void ObjFile::recordPrevailingSymbolForMingw(
COFFSymbolRef sym, DenseMap<StringRef, uint32_t> &prevailingSectionMap) {
int32_t sectionNumber = sym.getSectionNumber();
SectionChunk *sc = sparseChunks[sectionNumber];
if (sc && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) {
StringRef name = sc->getSectionName().split('$').second;
prevailingSectionMap[name] = sectionNumber;
}
}
void ObjFile::maybeAssociateSEHForMingw(
COFFSymbolRef sym, const coff_aux_section_definition *def,
const DenseMap<StringRef, uint32_t> &prevailingSectionMap) {
StringRef name = check(coffObj->getSymbolName(sym));
if (name.consume_front(".pdata$") || name.consume_front(".xdata$") ||
name.consume_front(".eh_frame$")) {
auto parentSym = prevailingSectionMap.find(name);
if (parentSym != prevailingSectionMap.end())
readAssociativeDefinition(sym, def, parentSym->second);
}
}
Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
SectionChunk *sc = sparseChunks[sym.getSectionNumber()];
if (sym.isExternal()) {
StringRef name = check(coffObj->getSymbolName(sym));
if (sc)
return ctx.symtab.addRegular(this, name, sym.getGeneric(), sc,
sym.getValue());
if (ctx.config.mingw && name.starts_with(".weak."))
return nullptr;
return ctx.symtab.addUndefined(name, this, false);
}
if (sc)
return make<DefinedRegular>(this, "", false,
false, sym.getGeneric(), sc);
return nullptr;
}
void ObjFile::initializeSymbols() {
uint32_t numSymbols = coffObj->getNumberOfSymbols();
symbols.resize(numSymbols);
SmallVector<std::pair<Symbol *, uint32_t>, 8> weakAliases;
std::vector<uint32_t> pendingIndexes;
pendingIndexes.reserve(numSymbols);
DenseMap<StringRef, uint32_t> prevailingSectionMap;
std::vector<const coff_aux_section_definition *> comdatDefs(
coffObj->getNumberOfSections() + 1);
for (uint32_t i = 0; i < numSymbols; ++i) {
COFFSymbolRef coffSym = check(coffObj->getSymbol(i));
bool prevailingComdat;
if (coffSym.isUndefined()) {
symbols[i] = createUndefined(coffSym);
} else if (coffSym.isWeakExternal()) {
symbols[i] = createUndefined(coffSym);
uint32_t tagIndex = coffSym.getAux<coff_aux_weak_external>()->TagIndex;
weakAliases.emplace_back(symbols[i], tagIndex);
} else if (std::optional<Symbol *> optSym =
createDefined(coffSym, comdatDefs, prevailingComdat)) {
symbols[i] = *optSym;
if (ctx.config.mingw && prevailingComdat)
recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap);
} else {
pendingIndexes.push_back(i);
}
i += coffSym.getNumberOfAuxSymbols();
}
for (uint32_t i : pendingIndexes) {
COFFSymbolRef sym = check(coffObj->getSymbol(i));
if (const coff_aux_section_definition *def = sym.getSectionDefinition()) {
if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
readAssociativeDefinition(sym, def);
else if (ctx.config.mingw)
maybeAssociateSEHForMingw(sym, def, prevailingSectionMap);
}
if (sparseChunks[sym.getSectionNumber()] == pendingComdat) {
StringRef name = check(coffObj->getSymbolName(sym));
log("comdat section " + name +
" without leader and unassociated, discarding");
continue;
}
symbols[i] = createRegular(sym);
}
for (auto &kv : weakAliases) {
Symbol *sym = kv.first;
uint32_t idx = kv.second;
checkAndSetWeakAlias(ctx, this, sym, symbols[idx]);
}
decltype(sparseChunks)().swap(sparseChunks);
}
Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
StringRef name = check(coffObj->getSymbolName(sym));
return ctx.symtab.addUndefined(name, this, sym.isWeakExternal());
}
static const coff_aux_section_definition *findSectionDef(COFFObjectFile *obj,
int32_t section) {
uint32_t numSymbols = obj->getNumberOfSymbols();
for (uint32_t i = 0; i < numSymbols; ++i) {
COFFSymbolRef sym = check(obj->getSymbol(i));
if (sym.getSectionNumber() != section)
continue;
if (const coff_aux_section_definition *def = sym.getSectionDefinition())
return def;
}
return nullptr;
}
void ObjFile::handleComdatSelection(
COFFSymbolRef sym, COMDATType &selection, bool &prevailing,
DefinedRegular *leader,
const llvm::object::coff_aux_section_definition *def) {
if (prevailing)
return;
SectionChunk *leaderChunk = leader->getChunk();
COMDATType leaderSelection = leaderChunk->selection;
assert(leader->data && "Comdat leader without SectionChunk?");
if (isa<BitcodeFile>(leader->file)) {
selection = leaderSelection = IMAGE_COMDAT_SELECT_ANY;
}
if ((selection == IMAGE_COMDAT_SELECT_ANY &&
leaderSelection == IMAGE_COMDAT_SELECT_LARGEST) ||
(selection == IMAGE_COMDAT_SELECT_LARGEST &&
leaderSelection == IMAGE_COMDAT_SELECT_ANY)) {
leaderSelection = selection = IMAGE_COMDAT_SELECT_LARGEST;
}
if (ctx.config.mingw && ((selection == IMAGE_COMDAT_SELECT_ANY &&
leaderSelection == IMAGE_COMDAT_SELECT_SAME_SIZE) ||
(selection == IMAGE_COMDAT_SELECT_SAME_SIZE &&
leaderSelection == IMAGE_COMDAT_SELECT_ANY))) {
leaderSelection = selection = IMAGE_COMDAT_SELECT_SAME_SIZE;
}
if (selection != leaderSelection) {
log(("conflicting comdat type for " + toString(ctx, *leader) + ": " +
Twine((int)leaderSelection) + " in " + toString(leader->getFile()) +
" and " + Twine((int)selection) + " in " + toString(this))
.str());
ctx.symtab.reportDuplicate(leader, this);
return;
}
switch (selection) {
case IMAGE_COMDAT_SELECT_NODUPLICATES:
ctx.symtab.reportDuplicate(leader, this);
break;
case IMAGE_COMDAT_SELECT_ANY:
break;
case IMAGE_COMDAT_SELECT_SAME_SIZE:
if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) {
if (!ctx.config.mingw) {
ctx.symtab.reportDuplicate(leader, this);
} else {
const coff_aux_section_definition *leaderDef = nullptr;
if (leaderChunk->file)
leaderDef = findSectionDef(leaderChunk->file->getCOFFObj(),
leaderChunk->getSectionNumber());
if (!leaderDef || leaderDef->Length != def->Length)
ctx.symtab.reportDuplicate(leader, this);
}
}
break;
case IMAGE_COMDAT_SELECT_EXACT_MATCH: {
SectionChunk newChunk(this, getSection(sym));
if (leaderChunk->getContents() != newChunk.getContents())
ctx.symtab.reportDuplicate(leader, this, &newChunk, sym.getValue());
break;
}
case IMAGE_COMDAT_SELECT_ASSOCIATIVE:
llvm_unreachable("createDefined not called for associative comdats");
case IMAGE_COMDAT_SELECT_LARGEST:
if (leaderChunk->getSize() < getSection(sym)->SizeOfRawData) {
StringRef name = check(coffObj->getSymbolName(sym));
replaceSymbol<DefinedRegular>(leader, this, name, true,
true, sym.getGeneric(),
nullptr);
prevailing = true;
}
break;
case IMAGE_COMDAT_SELECT_NEWEST:
llvm_unreachable("should have been rejected earlier");
}
}
std::optional<Symbol *> ObjFile::createDefined(
COFFSymbolRef sym,
std::vector<const coff_aux_section_definition *> &comdatDefs,
bool &prevailing) {
prevailing = false;
auto getName = [&]() { return check(coffObj->getSymbolName(sym)); };
if (sym.isCommon()) {
auto *c = make<CommonChunk>(sym);
chunks.push_back(c);
return ctx.symtab.addCommon(this, getName(), sym.getValue(),
sym.getGeneric(), c);
}
if (sym.isAbsolute()) {
StringRef name = getName();
if (name == "@feat.00")
feat00Flags = sym.getValue();
if (ignoredSymbolName(name))
return nullptr;
if (sym.isExternal())
return ctx.symtab.addAbsolute(name, sym);
return make<DefinedAbsolute>(ctx, name, sym);
}
int32_t sectionNumber = sym.getSectionNumber();
if (sectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
return nullptr;
if (llvm::COFF::isReservedSectionNumber(sectionNumber))
fatal(toString(this) + ": " + getName() +
" should not refer to special section " + Twine(sectionNumber));
if ((uint32_t)sectionNumber >= sparseChunks.size())
fatal(toString(this) + ": " + getName() +
" should not refer to non-existent section " + Twine(sectionNumber));
if (const coff_aux_section_definition *def = comdatDefs[sectionNumber]) {
comdatDefs[sectionNumber] = nullptr;
DefinedRegular *leader;
if (sym.isExternal()) {
std::tie(leader, prevailing) =
ctx.symtab.addComdat(this, getName(), sym.getGeneric());
} else {
leader = make<DefinedRegular>(this, "", false,
false, sym.getGeneric());
prevailing = true;
}
if (def->Selection < (int)IMAGE_COMDAT_SELECT_NODUPLICATES ||
def->Selection > (int)IMAGE_COMDAT_SELECT_LARGEST) {
fatal("unknown comdat type " + std::to_string((int)def->Selection) +
" for " + getName() + " in " + toString(this));
}
COMDATType selection = (COMDATType)def->Selection;
if (leader->isCOMDAT)
handleComdatSelection(sym, selection, prevailing, leader, def);
if (prevailing) {
SectionChunk *c = readSection(sectionNumber, def, getName());
sparseChunks[sectionNumber] = c;
if (!c)
return nullptr;
c->sym = cast<DefinedRegular>(leader);
c->selection = selection;
cast<DefinedRegular>(leader)->data = &c->repl;
} else {
sparseChunks[sectionNumber] = nullptr;
}
return leader;
}
if (sparseChunks[sectionNumber] == pendingComdat) {
if (const coff_aux_section_definition *def = sym.getSectionDefinition()) {
if (def->Selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE)
comdatDefs[sectionNumber] = def;
}
return std::nullopt;
}
return createRegular(sym);
}
MachineTypes ObjFile::getMachineType() {
if (coffObj)
return static_cast<MachineTypes>(coffObj->getMachine());
return IMAGE_FILE_MACHINE_UNKNOWN;
}
ArrayRef<uint8_t> ObjFile::getDebugSection(StringRef secName) {
if (SectionChunk *sec = SectionChunk::findByName(debugChunks, secName))
return sec->consumeDebugMagic();
return {};
}
void ObjFile::initializeFlags() {
ArrayRef<uint8_t> data = getDebugSection(".debug$S");
if (data.empty())
return;
DebugSubsectionArray subsections;
BinaryStreamReader reader(data, llvm::endianness::little);
ExitOnError exitOnErr;
exitOnErr(reader.readArray(subsections, data.size()));
for (const DebugSubsectionRecord &ss : subsections) {
if (ss.kind() != DebugSubsectionKind::Symbols)
continue;
unsigned offset = 0;
for (unsigned i = 0; i < 2; ++i) {
Expected<CVSymbol> sym = readSymbolFromStream(ss.getRecordData(), offset);
if (!sym) {
consumeError(sym.takeError());
return;
}
if (sym->kind() == SymbolKind::S_COMPILE3) {
auto cs =
cantFail(SymbolDeserializer::deserializeAs<Compile3Sym>(sym.get()));
hotPatchable =
(cs.Flags & CompileSym3Flags::HotPatch) != CompileSym3Flags::None;
}
if (sym->kind() == SymbolKind::S_OBJNAME) {
auto objName = cantFail(SymbolDeserializer::deserializeAs<ObjNameSym>(
sym.get()));
if (objName.Signature)
pchSignature = objName.Signature;
}
offset += sym->length();
}
}
}
void ObjFile::initializeDependencies() {
if (!ctx.config.debug)
return;
bool isPCH = false;
ArrayRef<uint8_t> data = getDebugSection(".debug$P");
if (!data.empty())
isPCH = true;
else
data = getDebugSection(".debug$T");
if (data.empty()) {
if (!debugChunks.empty())
debugTypesObj = makeTpiSource(ctx, this);
return;
}
CVTypeArray types;
BinaryStreamReader reader(data, llvm::endianness::little);
cantFail(reader.readArray(types, reader.getLength()));
CVTypeArray::Iterator firstType = types.begin();
if (firstType == types.end())
return;
debugTypes = data;
if (isPCH) {
debugTypesObj = makePrecompSource(ctx, this);
return;
}
if (firstType->kind() == LF_TYPESERVER2) {
TypeServer2Record ts = cantFail(
TypeDeserializer::deserializeAs<TypeServer2Record>(firstType->data()));
debugTypesObj = makeUseTypeServerSource(ctx, this, ts);
enqueuePdbFile(ts.getName(), this);
return;
}
if (firstType->kind() == LF_PRECOMP) {
PrecompRecord precomp = cantFail(
TypeDeserializer::deserializeAs<PrecompRecord>(firstType->data()));
if (precomp.Signature)
pchSignature = precomp.Signature;
debugTypesObj = makeUsePrecompSource(ctx, this, precomp);
debugTypes = debugTypes.drop_front(firstType->RecordData.size());
return;
}
debugTypesObj = makeTpiSource(ctx, this);
}
static std::string normalizePdbPath(StringRef path) {
#if defined(_WIN32)
return path.lower();
#else
return std::string(path);
#endif
}
static std::optional<std::string>
findPdbPath(StringRef pdbPath, ObjFile *dependentFile, StringRef outputPath) {
if (llvm::sys::fs::exists(pdbPath))
return normalizePdbPath(pdbPath);
StringRef objPath = !dependentFile->parentName.empty()
? dependentFile->parentName
: dependentFile->getName();
StringRef pdbName = sys::path::filename(pdbPath, sys::path::Style::windows);
SmallString<128> path;
sys::path::append(path, sys::path::parent_path(objPath), pdbName);
if (llvm::sys::fs::exists(path))
return normalizePdbPath(path);
path.clear();
sys::path::append(path, sys::path::parent_path(outputPath), pdbName);
if (llvm::sys::fs::exists(path))
return normalizePdbPath(path);
return std::nullopt;
}
PDBInputFile::PDBInputFile(COFFLinkerContext &ctx, MemoryBufferRef m)
: InputFile(ctx, PDBKind, m) {}
PDBInputFile::~PDBInputFile() = default;
PDBInputFile *PDBInputFile::findFromRecordPath(const COFFLinkerContext &ctx,
StringRef path,
ObjFile *fromFile) {
auto p = findPdbPath(path.str(), fromFile, ctx.config.outputFile);
if (!p)
return nullptr;
auto it = ctx.pdbInputFileInstances.find(*p);
if (it != ctx.pdbInputFileInstances.end())
return it->second;
return nullptr;
}
void PDBInputFile::parse() {
ctx.pdbInputFileInstances[mb.getBufferIdentifier().str()] = this;
std::unique_ptr<pdb::IPDBSession> thisSession;
Error E = pdb::NativeSession::createFromPdb(
MemoryBuffer::getMemBuffer(mb, false), thisSession);
if (E) {
loadErrorStr.emplace(toString(std::move(E)));
return;
}
session.reset(static_cast<pdb::NativeSession *>(thisSession.release()));
pdb::PDBFile &pdbFile = session->getPDBFile();
auto expectedInfo = pdbFile.getPDBInfoStream();
if (!expectedInfo) {
loadErrorStr.emplace(toString(expectedInfo.takeError()));
return;
}
debugTypesObj = makeTypeServerSource(ctx, this);
}
std::optional<std::pair<StringRef, uint32_t>>
ObjFile::getVariableLocation(StringRef var) {
if (!dwarf) {
dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj()));
if (!dwarf)
return std::nullopt;
}
if (ctx.config.machine == I386)
var.consume_front("_");
std::optional<std::pair<std::string, unsigned>> ret =
dwarf->getVariableLoc(var);
if (!ret)
return std::nullopt;
return std::make_pair(saver().save(ret->first), ret->second);
}
std::optional<DILineInfo> ObjFile::getDILineInfo(uint32_t offset,
uint32_t sectionIndex) {
if (!dwarf) {
dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj()));
if (!dwarf)
return std::nullopt;
}
return dwarf->getDILineInfo(offset, sectionIndex);
}
void ObjFile::enqueuePdbFile(StringRef path, ObjFile *fromFile) {
auto p = findPdbPath(path.str(), fromFile, ctx.config.outputFile);
if (!p)
return;
auto it = ctx.pdbInputFileInstances.emplace(*p, nullptr);
if (!it.second)
return;
ctx.driver.enqueuePDB(*p);
}
ImportFile::ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m)
: InputFile(ctx, ImportKind, m), live(!ctx.config.doGC), thunkLive(live) {}
void ImportFile::parse() {
const auto *hdr =
reinterpret_cast<const coff_import_header *>(mb.getBufferStart());
if (mb.getBufferSize() < sizeof(*hdr) ||
mb.getBufferSize() != sizeof(*hdr) + hdr->SizeOfData)
fatal("broken import library");
StringRef buf = mb.getBuffer().substr(sizeof(*hdr));
StringRef name = saver().save(buf.split('\0').first);
StringRef impName = saver().save("__imp_" + name);
buf = buf.substr(name.size() + 1);
dllName = buf.split('\0').first;
StringRef extName;
switch (hdr->getNameType()) {
case IMPORT_ORDINAL:
extName = "";
break;
case IMPORT_NAME:
extName = name;
break;
case IMPORT_NAME_NOPREFIX:
extName = ltrim1(name, "?@_");
break;
case IMPORT_NAME_UNDECORATE:
extName = ltrim1(name, "?@_");
extName = extName.substr(0, extName.find('@'));
break;
case IMPORT_NAME_EXPORTAS:
extName = buf.substr(dllName.size() + 1).split('\0').first;
break;
}
this->hdr = hdr;
externalName = extName;
impSym = ctx.symtab.addImportData(impName, this);
if (!impSym)
return;
if (hdr->getType() == llvm::COFF::IMPORT_CONST)
static_cast<void>(ctx.symtab.addImportData(name, this));
if (hdr->getType() == llvm::COFF::IMPORT_CODE)
thunkSym = ctx.symtab.addImportThunk(
name, cast_or_null<DefinedImportData>(impSym), hdr->Machine);
}
BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
StringRef archiveName, uint64_t offsetInArchive,
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);
MemoryBufferRef mbref(mb.getBuffer(),
saver().save(archiveName.empty()
? path
: archiveName +
sys::path::filename(path) +
utostr(offsetInArchive)));
obj = check(lto::InputFile::create(mbref));
}
BitcodeFile::~BitcodeFile() = default;
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)
comdat[i] =
ctx.symtab.addComdat(this, saver.save(obj->getComdatTable()[i].first));
for (const lto::InputFile::Symbol &objSym : obj->symbols()) {
StringRef symName = saver.save(objSym.getName());
int comdatIndex = objSym.getComdatIndex();
Symbol *sym;
SectionChunk *fakeSC = nullptr;
if (objSym.isExecutable())
fakeSC = &ctx.ltoTextSectionChunk.chunk;
else
fakeSC = &ctx.ltoDataSectionChunk.chunk;
if (objSym.isUndefined()) {
sym = ctx.symtab.addUndefined(symName, this, false);
if (objSym.isWeak())
sym->deferUndefined = true;
if (symName.starts_with("__imp_"))
sym->isUsedInRegularObj = true;
} else if (objSym.isCommon()) {
sym = ctx.symtab.addCommon(this, symName, objSym.getCommonSize());
} else if (objSym.isWeak() && objSym.isIndirect()) {
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);
} else if (comdatIndex != -1) {
if (symName == obj->getComdatTable()[comdatIndex].first) {
sym = comdat[comdatIndex].first;
if (cast<DefinedRegular>(sym)->data == nullptr)
cast<DefinedRegular>(sym)->data = &fakeSC->repl;
} else if (comdat[comdatIndex].second) {
sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC);
} else {
sym = ctx.symtab.addUndefined(symName, this, false);
}
} else {
sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC, 0,
objSym.isWeak());
}
symbols.push_back(sym);
if (objSym.isUsed())
ctx.config.gcroot.push_back(sym);
}
directives = saver.save(obj->getCOFFLinkerOpts());
}
void BitcodeFile::parseLazy() {
for (const lto::InputFile::Symbol &sym : obj->symbols())
if (!sym.isUndefined())
ctx.symtab.addLazyObject(this, sym.getName());
}
MachineTypes BitcodeFile::getMachineType() {
switch (Triple(obj->getTargetTriple()).getArch()) {
case Triple::x86_64:
return AMD64;
case Triple::x86:
return I386;
case Triple::arm:
case Triple::thumb:
return ARMNT;
case Triple::aarch64:
return ARM64;
default:
return IMAGE_FILE_MACHINE_UNKNOWN;
}
}
std::string lld::coff::replaceThinLTOSuffix(StringRef path, StringRef suffix,
StringRef repl) {
if (path.consume_back(suffix))
return (path + repl).str();
return std::string(path);
}
static bool isRVACode(COFFObjectFile *coffObj, uint64_t rva, InputFile *file) {
for (size_t i = 1, e = coffObj->getNumberOfSections(); i <= e; i++) {
const coff_section *sec = CHECK(coffObj->getSection(i), file);
if (rva >= sec->VirtualAddress &&
rva <= sec->VirtualAddress + sec->VirtualSize) {
return (sec->Characteristics & COFF::IMAGE_SCN_CNT_CODE) != 0;
}
}
return false;
}
void DLLFile::parse() {
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
if (auto *obj = dyn_cast<COFFObjectFile>(bin.get())) {
bin.release();
coffObj.reset(obj);
} else {
error(toString(this) + " is not a COFF file");
return;
}
if (!coffObj->getPE32Header() && !coffObj->getPE32PlusHeader()) {
error(toString(this) + " is not a PE-COFF executable");
return;
}
for (const auto &exp : coffObj->export_directories()) {
StringRef dllName, symbolName;
uint32_t exportRVA;
checkError(exp.getDllName(dllName));
checkError(exp.getSymbolName(symbolName));
checkError(exp.getExportRVA(exportRVA));
if (symbolName.empty())
continue;
bool code = isRVACode(coffObj.get(), exportRVA, this);
Symbol *s = make<Symbol>();
s->dllName = dllName;
s->symbolName = symbolName;
s->importType = code ? ImportType::IMPORT_CODE : ImportType::IMPORT_DATA;
s->nameType = ImportNameType::IMPORT_NAME;
if (coffObj->getMachine() == I386) {
s->symbolName = symbolName = saver().save("_" + symbolName);
s->nameType = ImportNameType::IMPORT_NAME_NOPREFIX;
}
StringRef impName = saver().save("__imp_" + symbolName);
ctx.symtab.addLazyDLLSymbol(this, s, impName);
if (code)
ctx.symtab.addLazyDLLSymbol(this, s, symbolName);
}
}
MachineTypes DLLFile::getMachineType() {
if (coffObj)
return static_cast<MachineTypes>(coffObj->getMachine());
return IMAGE_FILE_MACHINE_UNKNOWN;
}
void DLLFile::makeImport(DLLFile::Symbol *s) {
if (!seen.insert(s->symbolName).second)
return;
size_t impSize = s->dllName.size() + s->symbolName.size() + 2;
size_t size = sizeof(coff_import_header) + impSize;
char *buf = bAlloc().Allocate<char>(size);
memset(buf, 0, size);
char *p = buf;
auto *imp = reinterpret_cast<coff_import_header *>(p);
p += sizeof(*imp);
imp->Sig2 = 0xFFFF;
imp->Machine = coffObj->getMachine();
imp->SizeOfData = impSize;
imp->OrdinalHint = 0;
imp->TypeInfo = (s->nameType << 2) | s->importType;
memcpy(p, s->symbolName.data(), s->symbolName.size());
p += s->symbolName.size() + 1;
memcpy(p, s->dllName.data(), s->dllName.size());
MemoryBufferRef mbref = MemoryBufferRef(StringRef(buf, size), s->dllName);
ImportFile *impFile = make<ImportFile>(ctx, mbref);
ctx.symtab.addFile(impFile);
}