aboutsummaryrefslogtreecommitdiff
path: root/lld/COFF/InputFiles.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lld/COFF/InputFiles.cpp')
-rw-r--r--lld/COFF/InputFiles.cpp189
1 files changed, 142 insertions, 47 deletions
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index d884201ba31b..0adc2b91bd99 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -25,6 +25,8 @@
#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"
@@ -44,32 +46,31 @@ 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;
-namespace lld {
-
// Returns the last element of a path, which is supposed to be a filename.
static StringRef getBasename(StringRef path) {
return sys::path::filename(path, sys::path::Style::windows);
}
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
-std::string toString(const coff::InputFile *file) {
+std::string lld::toString(const coff::InputFile *file) {
if (!file)
return "<internal>";
if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
- return file->getName();
+ return std::string(file->getName());
return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
")")
.str();
}
-namespace coff {
-
std::vector<ObjFile *> ObjFile::instances;
+std::map<std::string, PDBInputFile *> PDBInputFile::instances;
std::vector<ImportFile *> ImportFile::instances;
std::vector<BitcodeFile *> BitcodeFile::instances;
@@ -121,7 +122,7 @@ void ArchiveFile::addMember(const Archive::Symbol &sym) {
driver->enqueueArchiveMember(c, sym, getName());
}
-std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
+std::vector<MemoryBufferRef> lld::coff::getArchiveMembers(Archive *file) {
std::vector<MemoryBufferRef> v;
Error err = Error::success();
for (const Archive::Child &c : file->children(err)) {
@@ -171,8 +172,7 @@ void LazyObjFile::parse() {
if (coffSym.isUndefined() || !coffSym.isExternal() ||
coffSym.isWeakExternal())
continue;
- StringRef name;
- coffObj->getSymbolName(coffSym, name);
+ StringRef name = check(coffObj->getSymbolName(coffSym));
if (coffSym.isAbsolute() && ignoredSymbolName(name))
continue;
symtab->addLazyObject(this, name);
@@ -198,11 +198,11 @@ void ObjFile::parse() {
initializeDependencies();
}
-const coff_section* ObjFile::getSection(uint32_t i) {
- const coff_section *sec;
- if (auto ec = coffObj->getSection(i, sec))
- fatal("getSection failed: #" + Twine(i) + ": " + ec.message());
- return sec;
+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;
}
// We set SectionChunk pointers in the SparseChunks vector to this value
@@ -215,7 +215,6 @@ static SectionChunk *const pendingComdat = reinterpret_cast<SectionChunk *>(1);
void ObjFile::initializeChunks() {
uint32_t numSections = coffObj->getNumberOfSections();
- chunks.reserve(numSections);
sparseChunks.resize(numSections + 1);
for (uint32_t i = 1; i < numSections + 1; ++i) {
const coff_section *sec = getSection(i);
@@ -279,7 +278,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
else if (name == ".gljmp$y")
guardLJmpChunks.push_back(c);
else if (name == ".sxdata")
- sXDataChunks.push_back(c);
+ sxDataChunks.push_back(c);
else if (config->tailMerge && sec->NumberOfRelocations == 0 &&
name == ".rdata" && leaderName.startswith("??_C@"))
// COFF sections that look like string literal sections (i.e. no
@@ -310,9 +309,9 @@ void ObjFile::readAssociativeDefinition(COFFSymbolRef sym,
int32_t sectionNumber = sym.getSectionNumber();
auto diag = [&]() {
- StringRef name, parentName;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
+ StringRef parentName;
const coff_section *parentSec = getSection(parentIndex);
if (Expected<StringRef> e = coffObj->getSectionName(parentSec))
parentName = *e;
@@ -353,7 +352,7 @@ void ObjFile::recordPrevailingSymbolForMingw(
SectionChunk *sc = sparseChunks[sectionNumber];
if (sc && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) {
StringRef name;
- coffObj->getSymbolName(sym, name);
+ name = check(coffObj->getSymbolName(sym));
if (getMachineType() == I386)
name.consume_front("_");
prevailingSectionMap[name] = sectionNumber;
@@ -363,8 +362,7 @@ void ObjFile::recordPrevailingSymbolForMingw(
void ObjFile::maybeAssociateSEHForMingw(
COFFSymbolRef sym, const coff_aux_section_definition *def,
const DenseMap<StringRef, uint32_t> &prevailingSectionMap) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
if (name.consume_front(".pdata$") || name.consume_front(".xdata$") ||
name.consume_front(".eh_frame$")) {
// For MinGW, treat .[px]data$<func> and .eh_frame$<func> as implicitly
@@ -378,8 +376,7 @@ void ObjFile::maybeAssociateSEHForMingw(
Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
SectionChunk *sc = sparseChunks[sym.getSectionNumber()];
if (sym.isExternal()) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
if (sc)
return symtab->addRegular(this, name, sym.getGeneric(), sc,
sym.getValue());
@@ -447,8 +444,7 @@ void ObjFile::initializeSymbols() {
maybeAssociateSEHForMingw(sym, def, prevailingSectionMap);
}
if (sparseChunks[sym.getSectionNumber()] == pendingComdat) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
log("comdat section " + name +
" without leader and unassociated, discarding");
continue;
@@ -461,11 +457,13 @@ void ObjFile::initializeSymbols() {
uint32_t idx = kv.second;
checkAndSetWeakAlias(symtab, this, sym, symbols[idx]);
}
+
+ // Free the memory used by sparseChunks now that symbol loading is finished.
+ decltype(sparseChunks)().swap(sparseChunks);
}
Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
return symtab->addUndefined(name, this, sym.isWeakExternal());
}
@@ -500,6 +498,17 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
leaderSelection = selection = IMAGE_COMDAT_SELECT_LARGEST;
}
+ // GCCs __declspec(selectany) doesn't actually pick "any" but "same size as".
+ // Clang on the other hand picks "any". To be able to link two object files
+ // with a __declspec(selectany) declaration, one compiled with gcc and the
+ // other with clang, we merge them as proper "same size as"
+ if (config->mingw && ((selection == IMAGE_COMDAT_SELECT_ANY &&
+ leaderSelection == IMAGE_COMDAT_SELECT_SAME_SIZE) ||
+ (selection == IMAGE_COMDAT_SELECT_SAME_SIZE &&
+ leaderSelection == IMAGE_COMDAT_SELECT_ANY))) {
+ leaderSelection = selection = IMAGE_COMDAT_SELECT_SAME_SIZE;
+ }
+
// Other than that, comdat selections must match. This is a bit more
// strict than link.exe which allows merging "any" and "largest" if "any"
// is the first symbol the linker sees, and it allows merging "largest"
@@ -550,8 +559,7 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
case IMAGE_COMDAT_SELECT_LARGEST:
if (leaderChunk->getSize() < getSection(sym)->SizeOfRawData) {
// Replace the existing comdat symbol with the new one.
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
// FIXME: This is incorrect: With /opt:noref, the previous sections
// make it into the final executable as well. Correct handling would
// be to undo reading of the whole old section that's being replaced,
@@ -575,11 +583,7 @@ Optional<Symbol *> ObjFile::createDefined(
std::vector<const coff_aux_section_definition *> &comdatDefs,
bool &prevailing) {
prevailing = false;
- auto getName = [&]() {
- StringRef s;
- coffObj->getSymbolName(sym, s);
- return s;
- };
+ auto getName = [&]() { return check(coffObj->getSymbolName(sym)); };
if (sym.isCommon()) {
auto *c = make<CommonChunk>(sym);
@@ -756,10 +760,11 @@ void ObjFile::initializeDependencies() {
if (data.empty())
return;
+ // Get the first type record. It will indicate if this object uses a type
+ // server (/Zi) or a PCH file (/Yu).
CVTypeArray types;
BinaryStreamReader reader(data, support::little);
cantFail(reader.readArray(types, reader.getLength()));
-
CVTypeArray::Iterator firstType = types.begin();
if (firstType == types.end())
return;
@@ -767,28 +772,120 @@ void ObjFile::initializeDependencies() {
// Remember the .debug$T or .debug$P section.
debugTypes = data;
+ // This object file is a PCH file that others will depend on.
if (isPCH) {
debugTypesObj = makePrecompSource(this);
return;
}
+ // This object file was compiled with /Zi. Enqueue the PDB dependency.
if (firstType->kind() == LF_TYPESERVER2) {
TypeServer2Record ts = cantFail(
TypeDeserializer::deserializeAs<TypeServer2Record>(firstType->data()));
- debugTypesObj = makeUseTypeServerSource(this, &ts);
+ debugTypesObj = makeUseTypeServerSource(this, ts);
+ PDBInputFile::enqueue(ts.getName(), this);
return;
}
+ // This object was compiled with /Yu. It uses types from another object file
+ // with a matching signature.
if (firstType->kind() == LF_PRECOMP) {
PrecompRecord precomp = cantFail(
TypeDeserializer::deserializeAs<PrecompRecord>(firstType->data()));
- debugTypesObj = makeUsePrecompSource(this, &precomp);
+ debugTypesObj = makeUsePrecompSource(this, precomp);
return;
}
+ // This is a plain old object file.
debugTypesObj = makeTpiSource(this);
}
+// Make a PDB path assuming the PDB is in the same folder as the OBJ
+static std::string getPdbBaseName(ObjFile *file, StringRef tSPath) {
+ StringRef localPath =
+ !file->parentName.empty() ? file->parentName : file->getName();
+ SmallString<128> path = sys::path::parent_path(localPath);
+
+ // Currently, type server PDBs are only created by MSVC cl, which only runs
+ // on Windows, so we can assume type server paths are Windows style.
+ sys::path::append(path,
+ sys::path::filename(tSPath, sys::path::Style::windows));
+ return std::string(path.str());
+}
+
+// The casing of the PDB path stamped in the OBJ can differ from the actual path
+// on disk. With this, we ensure to always use lowercase as a key for the
+// PDBInputFile::instances map, at least on Windows.
+static std::string normalizePdbPath(StringRef path) {
+#if defined(_WIN32)
+ return path.lower();
+#else // LINUX
+ return std::string(path);
+#endif
+}
+
+// If existing, return the actual PDB path on disk.
+static Optional<std::string> findPdbPath(StringRef pdbPath,
+ ObjFile *dependentFile) {
+ // Ensure the file exists before anything else. In some cases, if the path
+ // points to a removable device, Driver::enqueuePath() would fail with an
+ // error (EAGAIN, "resource unavailable try again") which we want to skip
+ // silently.
+ if (llvm::sys::fs::exists(pdbPath))
+ return normalizePdbPath(pdbPath);
+ std::string ret = getPdbBaseName(dependentFile, pdbPath);
+ if (llvm::sys::fs::exists(ret))
+ return normalizePdbPath(ret);
+ return None;
+}
+
+PDBInputFile::PDBInputFile(MemoryBufferRef m) : InputFile(PDBKind, m) {}
+
+PDBInputFile::~PDBInputFile() = default;
+
+PDBInputFile *PDBInputFile::findFromRecordPath(StringRef path,
+ ObjFile *fromFile) {
+ auto p = findPdbPath(path.str(), fromFile);
+ if (!p)
+ return nullptr;
+ auto it = PDBInputFile::instances.find(*p);
+ if (it != PDBInputFile::instances.end())
+ return it->second;
+ return nullptr;
+}
+
+void PDBInputFile::enqueue(StringRef path, ObjFile *fromFile) {
+ auto p = findPdbPath(path.str(), fromFile);
+ if (!p)
+ return;
+ auto it = PDBInputFile::instances.emplace(*p, nullptr);
+ if (!it.second)
+ return; // already scheduled for load
+ driver->enqueuePDB(*p);
+}
+
+void PDBInputFile::parse() {
+ PDBInputFile::instances[mb.getBufferIdentifier().str()] = this;
+
+ std::unique_ptr<pdb::IPDBSession> thisSession;
+ loadErr.emplace(pdb::NativeSession::createFromPdb(
+ MemoryBuffer::getMemBuffer(mb, false), thisSession));
+ if (*loadErr)
+ return; // fail silently at this point - the error will be handled later,
+ // when merging the debug type stream
+
+ session.reset(static_cast<pdb::NativeSession *>(thisSession.release()));
+
+ pdb::PDBFile &pdbFile = session->getPDBFile();
+ auto expectedInfo = pdbFile.getPDBInfoStream();
+ // All PDB Files should have an Info stream.
+ if (!expectedInfo) {
+ loadErr.emplace(expectedInfo.takeError());
+ return;
+ }
+ debugTypesObj = makeTypeServerSource(this);
+}
+
// Used only for DWARF debug info, which is not common (except in MinGW
// environments). This returns an optional pair of file name and line
// number for where the variable was defined.
@@ -820,7 +917,7 @@ Optional<DILineInfo> ObjFile::getDILineInfo(uint32_t offset,
return dwarf->getDILineInfo(offset, sectionIndex);
}
-StringRef ltrim1(StringRef s, const char *chars) {
+static StringRef ltrim1(StringRef s, const char *chars) {
if (!s.empty() && strchr(chars, s[0]))
return s.substr(1);
return s;
@@ -838,7 +935,7 @@ void ImportFile::parse() {
StringRef name = saver.save(StringRef(buf + sizeof(*hdr)));
StringRef impName = saver.save("__imp_" + name);
const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1;
- dllName = StringRef(nameStart);
+ dllName = std::string(StringRef(nameStart));
StringRef extName;
switch (hdr->getNameType()) {
case IMPORT_ORDINAL:
@@ -896,8 +993,9 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
// filename unique.
MemoryBufferRef mbref(
mb.getBuffer(),
- saver.save(archiveName + path +
- (archiveName.empty() ? "" : utostr(offsetInArchive))));
+ saver.save(archiveName.empty() ? path
+ : archiveName + sys::path::filename(path) +
+ utostr(offsetInArchive)));
obj = check(lto::InputFile::create(mbref));
}
@@ -921,7 +1019,7 @@ void BitcodeFile::parse() {
} else if (objSym.isWeak() && objSym.isIndirect()) {
// Weak external.
sym = symtab->addUndefined(symName, this, true);
- std::string fallback = objSym.getCOFFWeakExternalFallback();
+ std::string fallback = std::string(objSym.getCOFFWeakExternalFallback());
Symbol *alias = symtab->addUndefined(saver.save(fallback));
checkAndSetWeakAlias(symtab, this, sym, alias);
} else if (comdatIndex != -1) {
@@ -956,14 +1054,11 @@ MachineTypes BitcodeFile::getMachineType() {
}
}
-std::string replaceThinLTOSuffix(StringRef path) {
+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 path;
+ return std::string(path);
}
-
-} // namespace coff
-} // namespace lld