aboutsummaryrefslogtreecommitdiff
path: root/lld/COFF
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2020-07-26 19:36:28 +0000
committerDimitry Andric <dim@FreeBSD.org>2020-07-26 19:36:28 +0000
commitcfca06d7963fa0909f90483b42a6d7d194d01e08 (patch)
tree209fb2a2d68f8f277793fc8df46c753d31bc853b /lld/COFF
parent706b4fc47bbc608932d3b491ae19a3b9cde9497b (diff)
Notes
Diffstat (limited to 'lld/COFF')
-rw-r--r--lld/COFF/Chunks.cpp2
-rw-r--r--lld/COFF/Chunks.h13
-rw-r--r--lld/COFF/Config.h9
-rw-r--r--lld/COFF/DLL.cpp16
-rw-r--r--lld/COFF/DebugTypes.cpp538
-rw-r--r--lld/COFF/DebugTypes.h56
-rw-r--r--lld/COFF/Driver.cpp118
-rw-r--r--lld/COFF/Driver.h22
-rw-r--r--lld/COFF/DriverUtils.cpp67
-rw-r--r--lld/COFF/ICF.cpp21
-rw-r--r--lld/COFF/InputFiles.cpp189
-rw-r--r--lld/COFF/InputFiles.h46
-rw-r--r--lld/COFF/LLDMapFile.cpp123
-rw-r--r--lld/COFF/LLDMapFile.h21
-rw-r--r--lld/COFF/LTO.cpp61
-rw-r--r--lld/COFF/LTO.h2
-rw-r--r--lld/COFF/MapFile.cpp323
-rw-r--r--lld/COFF/MarkLive.cpp6
-rw-r--r--lld/COFF/MinGW.cpp10
-rw-r--r--lld/COFF/Options.td23
-rw-r--r--lld/COFF/PDB.cpp785
-rw-r--r--lld/COFF/SymbolTable.cpp18
-rw-r--r--lld/COFF/SymbolTable.h1
-rw-r--r--lld/COFF/Symbols.cpp32
-rw-r--r--lld/COFF/Symbols.h16
-rw-r--r--lld/COFF/TypeMerger.h13
-rw-r--r--lld/COFF/Writer.cpp81
27 files changed, 1573 insertions, 1039 deletions
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index 0e43d2b478b4..e04ceed505c2 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -333,7 +333,7 @@ static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk,
} else {
COFFSymbolRef coffSym =
check(file->getCOFFObj()->getSymbol(rel.SymbolTableIndex));
- file->getCOFFObj()->getSymbolName(coffSym, name);
+ name = check(file->getCOFFObj()->getSymbolName(coffSym));
}
std::vector<std::string> symbolLocations =
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index 7ae4ee735f4a..0528143383c5 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -269,7 +269,8 @@ public:
AssociatedIterator() = default;
AssociatedIterator(SectionChunk *head) : cur(head) {}
bool operator==(const AssociatedIterator &r) const { return cur == r.cur; }
- const SectionChunk &operator*() const { return *cur; }
+ // FIXME: Wrong const-ness, but it makes filter ranges work.
+ SectionChunk &operator*() const { return *cur; }
SectionChunk &operator*() { return *cur; }
AssociatedIterator &operator++() {
cur = cur->assocChildren;
@@ -486,7 +487,9 @@ public:
class ImportThunkChunkARM : public ImportThunkChunk {
public:
- explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) {}
+ explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) {
+ setAlignment(2);
+ }
size_t getSize() const override { return sizeof(importThunkARM); }
void getBaserels(std::vector<Baserel> *res) override;
void writeTo(uint8_t *buf) const override;
@@ -494,14 +497,16 @@ public:
class ImportThunkChunkARM64 : public ImportThunkChunk {
public:
- explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) {}
+ explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) {
+ setAlignment(4);
+ }
size_t getSize() const override { return sizeof(importThunkARM64); }
void writeTo(uint8_t *buf) const override;
};
class RangeExtensionThunkARM : public NonSectionChunk {
public:
- explicit RangeExtensionThunkARM(Defined *t) : target(t) {}
+ explicit RangeExtensionThunkARM(Defined *t) : target(t) { setAlignment(2); }
size_t getSize() const override;
void writeTo(uint8_t *buf) const override;
diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 2690ea5c4082..72d826b8bd17 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -110,6 +110,7 @@ struct Configuration {
bool showSummary = false;
unsigned debugTypes = static_cast<unsigned>(DebugType::None);
std::vector<std::string> natvisFiles;
+ llvm::StringMap<std::string> namedStreams;
llvm::SmallString<128> pdbAltPath;
llvm::SmallString<128> pdbPath;
llvm::SmallString<128> pdbSourcePath;
@@ -144,7 +145,7 @@ struct Configuration {
unsigned ltoo = 2;
// Used for /opt:lldltojobs=N
- unsigned thinLTOJobs = 0;
+ std::string thinLTOJobs;
// Used for /opt:lldltopartitions=N
unsigned ltoPartitions = 1;
@@ -182,6 +183,9 @@ struct Configuration {
llvm::StringMap<int> order;
// Used for /lldmap.
+ std::string lldmapFile;
+
+ // Used for /map.
std::string mapFile;
// Used for /thinlto-index-only:
@@ -211,6 +215,7 @@ struct Configuration {
uint32_t functionPadMin = 0;
bool dynamicBase = true;
bool allowBind = true;
+ bool cetCompat = false;
bool nxCompat = true;
bool allowIsolation = true;
bool terminalServerAware = true;
@@ -230,6 +235,8 @@ struct Configuration {
bool swaprunNet = false;
bool thinLTOEmitImportsFiles;
bool thinLTOIndexOnly;
+ bool autoImport = false;
+ bool pseudoRelocs = false;
};
extern Configuration *config;
diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp
index 39d9fbab63d5..50301ad91b1d 100644
--- a/lld/COFF/DLL.cpp
+++ b/lld/COFF/DLL.cpp
@@ -365,7 +365,9 @@ public:
class ThunkChunkARM : public NonSectionChunk {
public:
- ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
+ ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {
+ setAlignment(2);
+ }
size_t getSize() const override { return sizeof(thunkARM); }
@@ -385,7 +387,9 @@ public:
class TailMergeChunkARM : public NonSectionChunk {
public:
- TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {}
+ TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {
+ setAlignment(2);
+ }
size_t getSize() const override { return sizeof(tailMergeARM); }
@@ -405,7 +409,9 @@ public:
class ThunkChunkARM64 : public NonSectionChunk {
public:
- ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
+ ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {
+ setAlignment(4);
+ }
size_t getSize() const override { return sizeof(thunkARM64); }
@@ -422,7 +428,9 @@ public:
class TailMergeChunkARM64 : public NonSectionChunk {
public:
- TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) {}
+ TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) {
+ setAlignment(4);
+ }
size_t getSize() const override { return sizeof(tailMergeARM64); }
diff --git a/lld/COFF/DebugTypes.cpp b/lld/COFF/DebugTypes.cpp
index 0960f16b01e6..4790b0166799 100644
--- a/lld/COFF/DebugTypes.cpp
+++ b/lld/COFF/DebugTypes.cpp
@@ -7,22 +7,26 @@
//===----------------------------------------------------------------------===//
#include "DebugTypes.h"
+#include "Chunks.h"
#include "Driver.h"
#include "InputFiles.h"
+#include "TypeMerger.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
+#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/DebugInfo/PDB/GenericError.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/Support/Path.h"
using namespace llvm;
using namespace llvm::codeview;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
namespace {
// The TypeServerSource class represents a PDB type server, a file referenced by
@@ -34,36 +38,40 @@ namespace {
// before any dependent OBJ.
class TypeServerSource : public TpiSource {
public:
- explicit TypeServerSource(MemoryBufferRef m, llvm::pdb::NativeSession *s)
- : TpiSource(PDB, nullptr), session(s), mb(m) {}
-
- // Queue a PDB type server for loading in the COFF Driver
- static void enqueue(const ObjFile *dependentFile,
- const TypeServer2Record &ts);
-
- // Create an instance
- static Expected<TypeServerSource *> getInstance(MemoryBufferRef m);
-
- // Fetch the PDB instance loaded for a corresponding dependent OBJ.
- static Expected<TypeServerSource *>
- findFromFile(const ObjFile *dependentFile);
-
- static std::map<std::string, std::pair<std::string, TypeServerSource *>>
- instances;
-
- // The interface to the PDB (if it was opened successfully)
- std::unique_ptr<llvm::pdb::NativeSession> session;
-
-private:
- MemoryBufferRef mb;
+ explicit TypeServerSource(PDBInputFile *f)
+ : TpiSource(PDB, nullptr), pdbInputFile(f) {
+ if (f->loadErr && *f->loadErr)
+ return;
+ pdb::PDBFile &file = f->session->getPDBFile();
+ auto expectedInfo = file.getPDBInfoStream();
+ if (!expectedInfo)
+ return;
+ auto it = mappings.emplace(expectedInfo->getGuid(), this);
+ assert(it.second);
+ (void)it;
+ tsIndexMap.isTypeServerMap = true;
+ }
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
+ bool isDependency() const override { return true; }
+
+ PDBInputFile *pdbInputFile = nullptr;
+
+ CVIndexMap tsIndexMap;
+
+ static std::map<codeview::GUID, TypeServerSource *> mappings;
};
// This class represents the debug type stream of an OBJ file that depends on a
// PDB type server (see TypeServerSource).
class UseTypeServerSource : public TpiSource {
public:
- UseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts)
- : TpiSource(UsingPDB, f), typeServerDependency(*ts) {}
+ UseTypeServerSource(ObjFile *f, TypeServer2Record ts)
+ : TpiSource(UsingPDB, f), typeServerDependency(ts) {}
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
// Information about the PDB type server dependency, that needs to be loaded
// in before merging this OBJ.
@@ -76,15 +84,35 @@ public:
// such files, clang does not.
class PrecompSource : public TpiSource {
public:
- PrecompSource(const ObjFile *f) : TpiSource(PCH, f) {}
+ PrecompSource(ObjFile *f) : TpiSource(PCH, f) {
+ if (!f->pchSignature || !*f->pchSignature)
+ fatal(toString(f) +
+ " claims to be a PCH object, but does not have a valid signature");
+ auto it = mappings.emplace(*f->pchSignature, this);
+ if (!it.second)
+ fatal("a PCH object with the same signature has already been provided (" +
+ toString(it.first->second->file) + " and " + toString(file) + ")");
+ precompIndexMap.isPrecompiledTypeMap = true;
+ }
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
+ bool isDependency() const override { return true; }
+
+ CVIndexMap precompIndexMap;
+
+ static std::map<uint32_t, PrecompSource *> mappings;
};
// This class represents the debug type stream of an OBJ file that depends on a
// Microsoft precompiled headers OBJ (see PrecompSource).
class UsePrecompSource : public TpiSource {
public:
- UsePrecompSource(const ObjFile *f, const PrecompRecord *precomp)
- : TpiSource(UsingPCH, f), precompDependency(*precomp) {}
+ UsePrecompSource(ObjFile *f, PrecompRecord precomp)
+ : TpiSource(UsingPCH, f), precompDependency(precomp) {}
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
// Information about the Precomp OBJ dependency, that needs to be loaded in
// before merging this OBJ.
@@ -92,173 +120,363 @@ public:
};
} // namespace
-TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) {}
+static std::vector<TpiSource *> gc;
-TpiSource *makeTpiSource(const ObjFile *f) {
- return make<TpiSource>(TpiSource::Regular, f);
+TpiSource::TpiSource(TpiKind k, ObjFile *f) : kind(k), file(f) {
+ gc.push_back(this);
}
-TpiSource *makeUseTypeServerSource(const ObjFile *f,
- const TypeServer2Record *ts) {
- TypeServerSource::enqueue(f, *ts);
- return make<UseTypeServerSource>(f, ts);
+// Vtable key method.
+TpiSource::~TpiSource() = default;
+
+TpiSource *lld::coff::makeTpiSource(ObjFile *file) {
+ return make<TpiSource>(TpiSource::Regular, file);
}
-TpiSource *makePrecompSource(const ObjFile *f) {
- return make<PrecompSource>(f);
+TpiSource *lld::coff::makeTypeServerSource(PDBInputFile *pdbInputFile) {
+ return make<TypeServerSource>(pdbInputFile);
}
-TpiSource *makeUsePrecompSource(const ObjFile *f,
- const PrecompRecord *precomp) {
- return make<UsePrecompSource>(f, precomp);
+TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file,
+ TypeServer2Record ts) {
+ return make<UseTypeServerSource>(file, ts);
}
-template <>
-const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) {
- assert(source->kind == TpiSource::UsingPCH);
- return ((const UsePrecompSource *)source)->precompDependency;
+TpiSource *lld::coff::makePrecompSource(ObjFile *file) {
+ return make<PrecompSource>(file);
}
-template <>
-const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) {
- assert(source->kind == TpiSource::UsingPDB);
- return ((const UseTypeServerSource *)source)->typeServerDependency;
+TpiSource *lld::coff::makeUsePrecompSource(ObjFile *file,
+ PrecompRecord precomp) {
+ return make<UsePrecompSource>(file, precomp);
}
-std::map<std::string, std::pair<std::string, TypeServerSource *>>
- TypeServerSource::instances;
+void TpiSource::forEachSource(llvm::function_ref<void(TpiSource *)> fn) {
+ for_each(gc, fn);
+}
-// Make a PDB path assuming the PDB is in the same folder as the OBJ
-static std::string getPdbBaseName(const ObjFile *file, StringRef tSPath) {
- StringRef localPath =
- !file->parentName.empty() ? file->parentName : file->getName();
- SmallString<128> path = sys::path::parent_path(localPath);
+std::map<codeview::GUID, TypeServerSource *> TypeServerSource::mappings;
+
+std::map<uint32_t, PrecompSource *> PrecompSource::mappings;
+
+// A COFF .debug$H section is currently a clang extension. This function checks
+// if a .debug$H section is in a format that we expect / understand, so that we
+// can ignore any sections which are coincidentally also named .debug$H but do
+// not contain a format we recognize.
+static bool canUseDebugH(ArrayRef<uint8_t> debugH) {
+ if (debugH.size() < sizeof(object::debug_h_header))
+ return false;
+ auto *header =
+ reinterpret_cast<const object::debug_h_header *>(debugH.data());
+ debugH = debugH.drop_front(sizeof(object::debug_h_header));
+ return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
+ header->Version == 0 &&
+ header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) &&
+ (debugH.size() % 8 == 0);
+}
- // 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 path.str();
+static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *file) {
+ SectionChunk *sec =
+ SectionChunk::findByName(file->getDebugChunks(), ".debug$H");
+ if (!sec)
+ return llvm::None;
+ ArrayRef<uint8_t> contents = sec->getContents();
+ if (!canUseDebugH(contents))
+ return None;
+ return contents;
}
-// 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 path;
-#endif
+static ArrayRef<GloballyHashedType>
+getHashesFromDebugH(ArrayRef<uint8_t> debugH) {
+ assert(canUseDebugH(debugH));
+
+ debugH = debugH.drop_front(sizeof(object::debug_h_header));
+ uint32_t count = debugH.size() / sizeof(GloballyHashedType);
+ return {reinterpret_cast<const GloballyHashedType *>(debugH.data()), count};
}
-// If existing, return the actual PDB path on disk.
-static Optional<std::string> findPdbPath(StringRef pdbPath,
- const 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;
+// Merge .debug$T for a generic object file.
+Expected<const CVIndexMap *> TpiSource::mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) {
+ CVTypeArray types;
+ BinaryStreamReader reader(file->debugTypes, support::little);
+ cantFail(reader.readArray(types, reader.getLength()));
+
+ if (config->debugGHashes) {
+ ArrayRef<GloballyHashedType> hashes;
+ std::vector<GloballyHashedType> ownedHashes;
+ if (Optional<ArrayRef<uint8_t>> debugH = getDebugH(file))
+ hashes = getHashesFromDebugH(*debugH);
+ else {
+ ownedHashes = GloballyHashedType::hashTypes(types);
+ hashes = ownedHashes;
+ }
+
+ if (auto err = mergeTypeAndIdRecords(m->globalIDTable, m->globalTypeTable,
+ indexMap->tpiMap, types, hashes,
+ file->pchSignature))
+ fatal("codeview::mergeTypeAndIdRecords failed: " +
+ toString(std::move(err)));
+ } else {
+ if (auto err =
+ mergeTypeAndIdRecords(m->idTable, m->typeTable, indexMap->tpiMap,
+ types, file->pchSignature))
+ fatal("codeview::mergeTypeAndIdRecords failed: " +
+ toString(std::move(err)));
+ }
+
+ if (config->showSummary) {
+ // Count how many times we saw each type record in our input. This
+ // calculation requires a second pass over the type records to classify each
+ // record as a type or index. This is slow, but this code executes when
+ // collecting statistics.
+ m->tpiCounts.resize(m->getTypeTable().size());
+ m->ipiCounts.resize(m->getIDTable().size());
+ uint32_t srcIdx = 0;
+ for (CVType &ty : types) {
+ TypeIndex dstIdx = indexMap->tpiMap[srcIdx++];
+ // Type merging may fail, so a complex source type may become the simple
+ // NotTranslated type, which cannot be used as an array index.
+ if (dstIdx.isSimple())
+ continue;
+ SmallVectorImpl<uint32_t> &counts =
+ isIdRecord(ty.kind()) ? m->ipiCounts : m->tpiCounts;
+ ++counts[dstIdx.toArrayIndex()];
+ }
+ }
+
+ return indexMap;
}
-// Fetch the PDB instance that was already loaded by the COFF Driver.
-Expected<TypeServerSource *>
-TypeServerSource::findFromFile(const ObjFile *dependentFile) {
- const TypeServer2Record &ts =
- retrieveDependencyInfo<TypeServer2Record>(dependentFile->debugTypesObj);
+// Merge types from a type server PDB.
+Expected<const CVIndexMap *> TypeServerSource::mergeDebugT(TypeMerger *m,
+ CVIndexMap *) {
+ pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile();
+ Expected<pdb::TpiStream &> expectedTpi = pdbFile.getPDBTpiStream();
+ if (auto e = expectedTpi.takeError())
+ fatal("Type server does not have TPI stream: " + toString(std::move(e)));
+ pdb::TpiStream *maybeIpi = nullptr;
+ if (pdbFile.hasPDBIpiStream()) {
+ Expected<pdb::TpiStream &> expectedIpi = pdbFile.getPDBIpiStream();
+ if (auto e = expectedIpi.takeError())
+ fatal("Error getting type server IPI stream: " + toString(std::move(e)));
+ maybeIpi = &*expectedIpi;
+ }
+
+ if (config->debugGHashes) {
+ // PDBs do not actually store global hashes, so when merging a type server
+ // PDB we have to synthesize global hashes. To do this, we first synthesize
+ // global hashes for the TPI stream, since it is independent, then we
+ // synthesize hashes for the IPI stream, using the hashes for the TPI stream
+ // as inputs.
+ auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray());
+ Optional<uint32_t> endPrecomp;
+ // Merge TPI first, because the IPI stream will reference type indices.
+ if (auto err =
+ mergeTypeRecords(m->globalTypeTable, tsIndexMap.tpiMap,
+ expectedTpi->typeArray(), tpiHashes, endPrecomp))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
+
+ // Merge IPI.
+ if (maybeIpi) {
+ auto ipiHashes =
+ GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes);
+ if (auto err = mergeIdRecords(m->globalIDTable, tsIndexMap.tpiMap,
+ tsIndexMap.ipiMap, maybeIpi->typeArray(),
+ ipiHashes))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
+ }
+ } else {
+ // Merge TPI first, because the IPI stream will reference type indices.
+ if (auto err = mergeTypeRecords(m->typeTable, tsIndexMap.tpiMap,
+ expectedTpi->typeArray()))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
+
+ // Merge IPI.
+ if (maybeIpi) {
+ if (auto err = mergeIdRecords(m->idTable, tsIndexMap.tpiMap,
+ tsIndexMap.ipiMap, maybeIpi->typeArray()))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
+ }
+ }
+
+ if (config->showSummary) {
+ // Count how many times we saw each type record in our input. If a
+ // destination type index is present in the source to destination type index
+ // map, that means we saw it once in the input. Add it to our histogram.
+ m->tpiCounts.resize(m->getTypeTable().size());
+ m->ipiCounts.resize(m->getIDTable().size());
+ for (TypeIndex ti : tsIndexMap.tpiMap)
+ if (!ti.isSimple())
+ ++m->tpiCounts[ti.toArrayIndex()];
+ for (TypeIndex ti : tsIndexMap.ipiMap)
+ if (!ti.isSimple())
+ ++m->ipiCounts[ti.toArrayIndex()];
+ }
+
+ return &tsIndexMap;
+}
- Optional<std::string> p = findPdbPath(ts.Name, dependentFile);
- if (!p)
- return createFileError(ts.Name, errorCodeToError(std::error_code(
- ENOENT, std::generic_category())));
+Expected<const CVIndexMap *>
+UseTypeServerSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) {
+ const codeview::GUID &tsId = typeServerDependency.getGuid();
+ StringRef tsPath = typeServerDependency.getName();
+
+ TypeServerSource *tsSrc;
+ auto it = TypeServerSource::mappings.find(tsId);
+ if (it != TypeServerSource::mappings.end()) {
+ tsSrc = it->second;
+ } else {
+ // The file failed to load, lookup by name
+ PDBInputFile *pdb = PDBInputFile::findFromRecordPath(tsPath, file);
+ if (!pdb)
+ return createFileError(tsPath, errorCodeToError(std::error_code(
+ ENOENT, std::generic_category())));
+ // If an error occurred during loading, throw it now
+ if (pdb->loadErr && *pdb->loadErr)
+ return createFileError(tsPath, std::move(*pdb->loadErr));
+
+ tsSrc = (TypeServerSource *)pdb->debugTypesObj;
+ }
+
+ pdb::PDBFile &pdbSession = tsSrc->pdbInputFile->session->getPDBFile();
+ auto expectedInfo = pdbSession.getPDBInfoStream();
+ if (!expectedInfo)
+ return &tsSrc->tsIndexMap;
+
+ // Just because a file with a matching name was found and it was an actual
+ // PDB file doesn't mean it matches. For it to match the InfoStream's GUID
+ // must match the GUID specified in the TypeServer2 record.
+ if (expectedInfo->getGuid() != typeServerDependency.getGuid())
+ return createFileError(
+ tsPath,
+ make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
- auto it = TypeServerSource::instances.find(*p);
- // The PDB file exists on disk, at this point we expect it to have been
- // inserted in the map by TypeServerSource::loadPDB()
- assert(it != TypeServerSource::instances.end());
+ return &tsSrc->tsIndexMap;
+}
+
+static bool equalsPath(StringRef path1, StringRef path2) {
+#if defined(_WIN32)
+ return path1.equals_lower(path2);
+#else
+ return path1.equals(path2);
+#endif
+}
- std::pair<std::string, TypeServerSource *> &pdb = it->second;
+// Find by name an OBJ provided on the command line
+static PrecompSource *findObjByName(StringRef fileNameOnly) {
+ SmallString<128> currentPath;
+ for (auto kv : PrecompSource::mappings) {
+ StringRef currentFileName = sys::path::filename(kv.second->file->getName(),
+ sys::path::Style::windows);
+
+ // Compare based solely on the file name (link.exe behavior)
+ if (equalsPath(currentFileName, fileNameOnly))
+ return kv.second;
+ }
+ return nullptr;
+}
- if (!pdb.second)
+Expected<const CVIndexMap *> findPrecompMap(ObjFile *file, PrecompRecord &pr) {
+ // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP
+ // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly,
+ // the paths embedded in the OBJs are in the Windows format.
+ SmallString<128> prFileName =
+ sys::path::filename(pr.getPrecompFilePath(), sys::path::Style::windows);
+
+ PrecompSource *precomp;
+ auto it = PrecompSource::mappings.find(pr.getSignature());
+ if (it != PrecompSource::mappings.end()) {
+ precomp = it->second;
+ } else {
+ // Lookup by name
+ precomp = findObjByName(prFileName);
+ }
+
+ if (!precomp)
return createFileError(
- *p, createStringError(inconvertibleErrorCode(), pdb.first.c_str()));
+ prFileName,
+ make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
- pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile();
- pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream());
+ if (pr.getSignature() != file->pchSignature)
+ return createFileError(
+ toString(file),
+ make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
- // Just because a file with a matching name was found doesn't mean it can be
- // used. The GUID must match between the PDB header and the OBJ
- // TypeServer2 record. The 'Age' is used by MSVC incremental compilation.
- if (info.getGuid() != ts.getGuid())
+ if (pr.getSignature() != *precomp->file->pchSignature)
return createFileError(
- ts.Name,
- make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
+ toString(precomp->file),
+ make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
- return pdb.second;
+ return &precomp->precompIndexMap;
}
-// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
-// moved here.
-Expected<llvm::pdb::NativeSession *> findTypeServerSource(const ObjFile *f) {
- Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f);
- if (!ts)
- return ts.takeError();
- return ts.get()->session.get();
+/// Merges a precompiled headers TPI map into the current TPI map. The
+/// precompiled headers object will also be loaded and remapped in the
+/// process.
+static Expected<const CVIndexMap *>
+mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *indexMap,
+ PrecompRecord &precomp) {
+ auto e = findPrecompMap(file, precomp);
+ if (!e)
+ return e.takeError();
+
+ const CVIndexMap *precompIndexMap = *e;
+ assert(precompIndexMap->isPrecompiledTypeMap);
+
+ if (precompIndexMap->tpiMap.empty())
+ return precompIndexMap;
+
+ assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex);
+ assert(precomp.getTypesCount() <= precompIndexMap->tpiMap.size());
+ // Use the previously remapped index map from the precompiled headers.
+ indexMap->tpiMap.append(precompIndexMap->tpiMap.begin(),
+ precompIndexMap->tpiMap.begin() +
+ precomp.getTypesCount());
+ return indexMap;
}
-// Queue a PDB type server for loading in the COFF Driver
-void TypeServerSource::enqueue(const ObjFile *dependentFile,
- const TypeServer2Record &ts) {
- // Start by finding where the PDB is located (either the record path or next
- // to the OBJ file)
- Optional<std::string> p = findPdbPath(ts.Name, dependentFile);
- if (!p)
- return;
- auto it = TypeServerSource::instances.emplace(
- *p, std::pair<std::string, TypeServerSource *>{});
- if (!it.second)
- return; // another OBJ already scheduled this PDB for load
-
- driver->enqueuePath(*p, false, false);
+Expected<const CVIndexMap *>
+UsePrecompSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) {
+ // This object was compiled with /Yu, so process the corresponding
+ // precompiled headers object (/Yc) first. Some type indices in the current
+ // object are referencing data in the precompiled headers object, so we need
+ // both to be loaded.
+ auto e = mergeInPrecompHeaderObj(file, indexMap, precompDependency);
+ if (!e)
+ return e.takeError();
+
+ // Drop LF_PRECOMP record from the input stream, as it has been replaced
+ // with the precompiled headers Type stream in the mergeInPrecompHeaderObj()
+ // call above. Note that we can't just call Types.drop_front(), as we
+ // explicitly want to rebase the stream.
+ CVTypeArray types;
+ BinaryStreamReader reader(file->debugTypes, support::little);
+ cantFail(reader.readArray(types, reader.getLength()));
+ auto firstType = types.begin();
+ file->debugTypes = file->debugTypes.drop_front(firstType->RecordData.size());
+
+ return TpiSource::mergeDebugT(m, indexMap);
}
-// Create an instance of TypeServerSource or an error string if the PDB couldn't
-// be loaded. The error message will be displayed later, when the referring OBJ
-// will be merged in. NOTE - a PDB load failure is not a link error: some
-// debug info will simply be missing from the final PDB - that is the default
-// accepted behavior.
-void loadTypeServerSource(llvm::MemoryBufferRef m) {
- std::string path = normalizePdbPath(m.getBufferIdentifier());
-
- Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m);
- if (!ts)
- TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr};
- else
- TypeServerSource::instances[path] = {{}, *ts};
+Expected<const CVIndexMap *> PrecompSource::mergeDebugT(TypeMerger *m,
+ CVIndexMap *) {
+ // Note that we're not using the provided CVIndexMap. Instead, we use our
+ // local one. Precompiled headers objects need to save the index map for
+ // further reference by other objects which use the precompiled headers.
+ return TpiSource::mergeDebugT(m, &precompIndexMap);
}
-Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) {
- std::unique_ptr<llvm::pdb::IPDBSession> iSession;
- Error err = pdb::NativeSession::createFromPdb(
- MemoryBuffer::getMemBuffer(m, false), iSession);
- if (err)
- return std::move(err);
-
- std::unique_ptr<llvm::pdb::NativeSession> session(
- static_cast<pdb::NativeSession *>(iSession.release()));
-
- pdb::PDBFile &pdbFile = session->getPDBFile();
- Expected<pdb::InfoStream &> info = pdbFile.getPDBInfoStream();
- // All PDB Files should have an Info stream.
- if (!info)
- return info.takeError();
- return make<TypeServerSource>(m, session.release());
+uint32_t TpiSource::countTypeServerPDBs() {
+ return TypeServerSource::mappings.size();
}
-} // namespace coff
-} // namespace lld
+uint32_t TpiSource::countPrecompObjs() {
+ return PrecompSource::mappings.size();
+}
+
+void TpiSource::clear() {
+ gc.clear();
+ TypeServerSource::mappings.clear();
+ PrecompSource::mappings.clear();
+}
diff --git a/lld/COFF/DebugTypes.h b/lld/COFF/DebugTypes.h
index e37c727232d0..24d79d83e4c6 100644
--- a/lld/COFF/DebugTypes.h
+++ b/lld/COFF/DebugTypes.h
@@ -26,35 +26,55 @@ namespace lld {
namespace coff {
class ObjFile;
+class PDBInputFile;
+struct CVIndexMap;
+class TypeMerger;
class TpiSource {
public:
enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB };
- TpiSource(TpiKind k, const ObjFile *f);
- virtual ~TpiSource() {}
+ TpiSource(TpiKind k, ObjFile *f);
+ virtual ~TpiSource();
- const TpiKind kind;
- const ObjFile *file;
-};
+ /// Produce a mapping from the type and item indices used in the object
+ /// file to those in the destination PDB.
+ ///
+ /// If the object file uses a type server PDB (compiled with /Zi), merge TPI
+ /// and IPI from the type server PDB and return a map for it. Each unique type
+ /// server PDB is merged at most once, so this may return an existing index
+ /// mapping.
+ ///
+ /// If the object does not use a type server PDB (compiled with /Z7), we merge
+ /// all the type and item records from the .debug$S stream and fill in the
+ /// caller-provided ObjectIndexMap.
+ virtual llvm::Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap);
+ /// Is this a dependent file that needs to be processed first, before other
+ /// OBJs?
+ virtual bool isDependency() const { return false; }
+
+ static void forEachSource(llvm::function_ref<void(TpiSource *)> fn);
-TpiSource *makeTpiSource(const ObjFile *f);
-TpiSource *makeUseTypeServerSource(const ObjFile *f,
- const llvm::codeview::TypeServer2Record *ts);
-TpiSource *makePrecompSource(const ObjFile *f);
-TpiSource *makeUsePrecompSource(const ObjFile *f,
- const llvm::codeview::PrecompRecord *precomp);
+ static uint32_t countTypeServerPDBs();
+ static uint32_t countPrecompObjs();
-void loadTypeServerSource(llvm::MemoryBufferRef m);
+ /// Clear global data structures for TpiSources.
+ static void clear();
-// Temporary interface to get the dependency
-template <typename T> const T &retrieveDependencyInfo(const TpiSource *source);
+ const TpiKind kind;
+ ObjFile *file;
+};
-// Temporary interface until we move PDBLinker::maybeMergeTypeServerPDB here
-llvm::Expected<llvm::pdb::NativeSession *>
-findTypeServerSource(const ObjFile *f);
+TpiSource *makeTpiSource(ObjFile *file);
+TpiSource *makeTypeServerSource(PDBInputFile *pdbInputFile);
+TpiSource *makeUseTypeServerSource(ObjFile *file,
+ llvm::codeview::TypeServer2Record ts);
+TpiSource *makePrecompSource(ObjFile *file);
+TpiSource *makeUsePrecompSource(ObjFile *file,
+ llvm::codeview::PrecompRecord ts);
} // namespace coff
} // namespace lld
-#endif \ No newline at end of file
+#endif
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index f770fff80bcb..7372505bb616 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -21,7 +21,6 @@
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Filesystem.h"
#include "lld/Common/Memory.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/Optional.h"
@@ -39,6 +38,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TarWriter.h"
@@ -89,6 +89,8 @@ bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &stdoutOS,
ImportFile::instances.clear();
BitcodeFile::instances.clear();
memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances));
+ TpiSource::clear();
+
return !errorCount();
}
@@ -218,7 +220,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
symtab->addFile(make<ObjFile>(mbref));
break;
case file_magic::pdb:
- loadTypeServerSource(mbref);
+ symtab->addFile(make<PDBInputFile>(mbref));
break;
case file_magic::coff_cl_gl_object:
error(filename + ": is not a native COFF file. Recompile without /GL");
@@ -237,9 +239,9 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
}
void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
- auto future =
- std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
- std::string pathStr = path;
+ auto future = std::make_shared<std::future<MBErrPair>>(
+ createFutureForFile(std::string(path)));
+ std::string pathStr = std::string(path);
enqueueTask([=]() {
auto mbOrErr = future->get();
if (mbOrErr.second) {
@@ -251,7 +253,7 @@ 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 (COFFOptTable().findNearest(pathStr, nearest) > 1)
+ if (optTable.findNearest(pathStr, nearest) > 1)
error(msg);
else
error(msg + "; did you mean '" + nearest + "'");
@@ -343,11 +345,9 @@ void LinkerDriver::parseDirectives(InputFile *file) {
ArgParser parser;
// .drectve is always tokenized using Windows shell rules.
// /EXPORT: option can appear too many times, processing in fastpath.
- opt::InputArgList args;
- std::vector<StringRef> exports;
- std::tie(args, exports) = parser.parseDirectives(s);
+ ParsedDirectives directives = parser.parseDirectives(s);
- for (StringRef e : exports) {
+ for (StringRef e : directives.exports) {
// If a common header file contains dllexported function
// declarations, many object files may end up with having the
// same /EXPORT options. In order to save cost of parsing them,
@@ -366,7 +366,11 @@ void LinkerDriver::parseDirectives(InputFile *file) {
config->exports.push_back(exp);
}
- for (auto *arg : args) {
+ // Handle /include: in bulk.
+ for (StringRef inc : directives.includes)
+ addUndefined(inc);
+
+ for (auto *arg : directives.args) {
switch (arg->getOption().getID()) {
case OPT_aligncomm:
parseAligncomm(arg->getValue());
@@ -452,7 +456,7 @@ Optional<StringRef> LinkerDriver::findFile(StringRef filename) {
}
if (path.endswith_lower(".lib"))
- visitedLibs.insert(sys::path::filename(path));
+ visitedLibs.insert(std::string(sys::path::filename(path)));
return path;
}
@@ -624,6 +628,7 @@ static std::string createResponseFile(const opt::InputArgList &args,
break;
case OPT_implib:
case OPT_pdb:
+ case OPT_pdbstripped:
case OPT_out:
os << arg->getSpelling() << sys::path::filename(arg->getValue()) << "\n";
break;
@@ -640,7 +645,7 @@ static std::string createResponseFile(const opt::InputArgList &args,
for (StringRef path : filePaths)
os << quote(relativeToRoot(path)) << "\n";
- return data.str();
+ return std::string(data.str());
}
enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab };
@@ -706,24 +711,25 @@ static unsigned parseDebugTypes(const opt::InputArgList &args) {
return debugTypes;
}
-static std::string getMapFile(const opt::InputArgList &args) {
- auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file);
+static std::string getMapFile(const opt::InputArgList &args,
+ opt::OptSpecifier os, opt::OptSpecifier osFile) {
+ auto *arg = args.getLastArg(os, osFile);
if (!arg)
return "";
- if (arg->getOption().getID() == OPT_lldmap_file)
+ if (arg->getOption().getID() == osFile.getID())
return arg->getValue();
- assert(arg->getOption().getID() == OPT_lldmap);
+ assert(arg->getOption().getID() == os.getID());
StringRef outFile = config->outputFile;
return (outFile.substr(0, outFile.rfind('.')) + ".map").str();
}
static std::string getImplibPath() {
if (!config->implib.empty())
- return config->implib;
+ return std::string(config->implib);
SmallString<128> out = StringRef(config->outputFile);
sys::path::replace_extension(out, ".lib");
- return out.str();
+ return std::string(out.str());
}
// The import name is calculated as follows:
@@ -747,16 +753,16 @@ static std::string getImportName(bool asLib) {
(config->dll || asLib) ? ".dll" : ".exe");
}
- return out.str();
+ return std::string(out.str());
}
static void createImportLibrary(bool asLib) {
std::vector<COFFShortExport> exports;
for (Export &e1 : config->exports) {
COFFShortExport e2;
- e2.Name = e1.name;
- e2.SymbolName = e1.symbolName;
- e2.ExtName = e1.extName;
+ e2.Name = std::string(e1.name);
+ e2.SymbolName = std::string(e1.symbolName);
+ e2.ExtName = std::string(e1.extName);
e2.Ordinal = e1.ordinal;
e2.Noname = e1.noname;
e2.Data = e1.data;
@@ -817,8 +823,8 @@ static void parseModuleDefs(StringRef path) {
mb->getMemBufferRef(), config->machine, config->mingw));
if (config->outputFile.empty())
- config->outputFile = saver.save(m.OutputFile);
- config->importName = saver.save(m.ImportName);
+ config->outputFile = std::string(saver.save(m.OutputFile));
+ config->importName = std::string(saver.save(m.ImportName));
if (m.ImageBase)
config->imageBase = m.ImageBase;
if (m.StackReserve)
@@ -844,7 +850,8 @@ static void parseModuleDefs(StringRef path) {
// and set as "ExtName = Name". If Name has the form "OtherDll.Func",
// it shouldn't be a normal exported function but a forward to another
// DLL instead. This is supported by both MS and GNU linkers.
- if (e1.ExtName != e1.Name && StringRef(e1.Name).contains('.')) {
+ if (!e1.ExtName.empty() && e1.ExtName != e1.Name &&
+ StringRef(e1.Name).contains('.')) {
e2.name = saver.save(e1.ExtName);
e2.forwardTo = saver.save(e1.Name);
config->exports.push_back(e2);
@@ -903,7 +910,8 @@ static void parseOrderFile(StringRef arg) {
// All symbols that were not present in a given order file are
// considered to have the lowest priority 0 and are placed at
// end of an output section.
- for (std::string s : args::getLines(mb->getMemBufferRef())) {
+ for (StringRef arg : args::getLines(mb->getMemBufferRef())) {
+ std::string s(arg);
if (config->machine == I386 && !isDecorated(s))
s = "_" + s;
@@ -1093,13 +1101,15 @@ Optional<std::string> getReproduceFile(const opt::InputArgList &args) {
if (auto *arg = args.getLastArg(OPT_linkrepro)) {
SmallString<64> path = StringRef(arg->getValue());
sys::path::append(path, "repro.tar");
- return path.str().str();
+ return std::string(path);
}
return None;
}
void LinkerDriver::link(ArrayRef<const char *> argsArr) {
+ ScopedTimer rootTimer(Timer::root());
+
// Needed for LTO.
InitializeAllTargetInfos();
InitializeAllTargets();
@@ -1141,14 +1151,23 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
return;
}
- lld::threadsEnabled = args.hasFlag(OPT_threads, OPT_threads_no, true);
+ // /threads: takes a positive integer and provides the default value for
+ // /opt:lldltojobs=.
+ if (auto *arg = args.getLastArg(OPT_threads)) {
+ StringRef v(arg->getValue());
+ unsigned threads = 0;
+ if (!llvm::to_integer(v, threads, 0) || threads == 0)
+ error(arg->getSpelling() + ": expected a positive integer, but got '" +
+ arg->getValue() + "'");
+ parallel::strategy = hardware_concurrency(threads);
+ config->thinLTOJobs = v.str();
+ }
if (args.hasArg(OPT_show_timing))
config->showTiming = true;
config->showSummary = args.hasArg(OPT_summary);
- ScopedTimer t(Timer::root());
// Handle --version, which is an lld extension. This option is a bit odd
// because it doesn't start with "/", but we deliberately chose "--" to
// avoid conflict with /version and for compatibility with clang-cl.
@@ -1260,11 +1279,23 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
config->pdbAltPath = arg->getValue();
if (args.hasArg(OPT_natvis))
config->natvisFiles = args.getAllArgValues(OPT_natvis);
+ if (args.hasArg(OPT_pdbstream)) {
+ for (const StringRef value : args.getAllArgValues(OPT_pdbstream)) {
+ const std::pair<StringRef, StringRef> nameFile = value.split("=");
+ const StringRef name = nameFile.first;
+ const std::string file = nameFile.second.str();
+ config->namedStreams[name] = file;
+ }
+ }
if (auto *arg = args.getLastArg(OPT_pdb_source_path))
config->pdbSourcePath = arg->getValue();
}
+ // Handle /pdbstripped
+ if (args.hasArg(OPT_pdbstripped))
+ warn("ignoring /pdbstripped flag, it is not yet supported");
+
// Handle /noentry
if (args.hasArg(OPT_noentry)) {
if (args.hasArg(OPT_dll))
@@ -1410,9 +1441,9 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
error("/opt:lldlto: invalid optimization level: " + optLevel);
} else if (s.startswith("lldltojobs=")) {
StringRef jobs = s.substr(11);
- if (jobs.getAsInteger(10, config->thinLTOJobs) ||
- config->thinLTOJobs == 0)
+ if (!get_threadpool_strategy(jobs))
error("/opt:lldltojobs: invalid job count: " + jobs);
+ config->thinLTOJobs = jobs.str();
} else if (s.startswith("lldltopartitions=")) {
StringRef n = s.substr(17);
if (n.getAsInteger(10, config->ltoPartitions) ||
@@ -1543,6 +1574,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
!args.hasArg(OPT_profile));
config->integrityCheck =
args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
+ config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false);
config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
for (auto *arg : args.filtered(OPT_swaprun))
parseSwaprun(arg->getValue());
@@ -1551,13 +1583,24 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
config->debugDwarf = debug == DebugKind::Dwarf;
config->debugGHashes = debug == DebugKind::GHash;
config->debugSymtab = debug == DebugKind::Symtab;
+ config->autoImport =
+ args.hasFlag(OPT_auto_import, OPT_auto_import_no, config->mingw);
+ config->pseudoRelocs = args.hasFlag(
+ OPT_runtime_pseudo_reloc, OPT_runtime_pseudo_reloc_no, config->mingw);
// Don't warn about long section names, such as .debug_info, for mingw or when
// -debug:dwarf is requested.
if (config->mingw || config->debugDwarf)
config->warnLongSectionNames = false;
- config->mapFile = getMapFile(args);
+ config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file);
+ config->mapFile = getMapFile(args, OPT_map, OPT_map_file);
+
+ if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) {
+ warn("/lldmap and /map have the same output file '" + config->mapFile +
+ "'.\n>>> ignoring /lldmap");
+ config->lldmapFile.clear();
+ }
if (config->incremental && args.hasArg(OPT_profile)) {
warn("ignoring '/incremental' due to '/profile' specification");
@@ -1805,9 +1848,11 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// Needed for MSVC 2017 15.5 CRT.
symtab->addAbsolute(mangle("__enclave_config"), 0);
- if (config->mingw) {
+ if (config->pseudoRelocs) {
symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
+ }
+ if (config->mingw) {
symtab->addAbsolute(mangle("__CTOR_LIST__"), 0);
symtab->addAbsolute(mangle("__DTOR_LIST__"), 0);
}
@@ -1865,7 +1910,8 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
while (run());
}
- if (config->mingw) {
+ if (config->autoImport) {
+ // MinGW specific.
// Load any further object files that might be needed for doing automatic
// imports.
//
@@ -1997,7 +2043,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
writeResult();
// Stop early so we can print the results.
- Timer::root().stop();
+ rootTimer.stop();
if (config->showTiming)
Timer::root().print();
}
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index cc2f25a6f95e..3fee9b1fe50e 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -41,6 +41,21 @@ 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
+// one memory allocation per argument, and that is prohibitively slow for
+// parsing directives.
+struct ParsedDirectives {
+ std::vector<StringRef> exports;
+ std::vector<StringRef> includes;
+ llvm::opt::InputArgList args;
+};
+
class ArgParser {
public:
// Parses command line options.
@@ -52,16 +67,13 @@ public:
// Tokenizes a given string and then parses as command line options in
// .drectve section. /EXPORT options are returned in second element
// to be processed in fastpath.
- std::pair<llvm::opt::InputArgList, std::vector<StringRef>>
- parseDirectives(StringRef s);
+ ParsedDirectives parseDirectives(StringRef s);
private:
// Concatenate LINK environment variable.
void addLINK(SmallVector<const char *, 256> &argv);
std::vector<const char *> tokenize(StringRef s);
-
- COFFOptTable table;
};
class LinkerDriver {
@@ -75,6 +87,8 @@ public:
void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym,
StringRef parentName);
+ void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false); }
+
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp
index 301a5c5efa8a..6cb761abea4e 100644
--- a/lld/COFF/DriverUtils.cpp
+++ b/lld/COFF/DriverUtils.cpp
@@ -220,7 +220,8 @@ void parseAligncomm(StringRef s) {
error("/aligncomm: invalid argument: " + s);
return;
}
- config->alignComm[name] = std::max(config->alignComm[name], 1 << v);
+ config->alignComm[std::string(name)] =
+ std::max(config->alignComm[std::string(name)], 1 << v);
}
// Parses /functionpadmin option argument.
@@ -318,7 +319,7 @@ public:
SmallString<128> s;
if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s))
fatal("cannot create a temporary file: " + ec.message());
- path = s.str();
+ path = std::string(s.str());
if (!contents.empty()) {
std::error_code ec;
@@ -403,7 +404,7 @@ static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
toString(std::move(e)));
}
- return merger.getMergedManifest().get()->getBuffer();
+ return std::string(merger.getMergedManifest().get()->getBuffer());
}
static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
@@ -431,9 +432,10 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
e.add("/out:" + StringRef(user.path));
e.run();
- return CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path)
- .get()
- ->getBuffer();
+ return std::string(
+ CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path)
+ .get()
+ ->getBuffer());
}
static std::string createManifestXml() {
@@ -507,7 +509,7 @@ std::unique_ptr<MemoryBuffer> createManifestRes() {
}
void createSideBySideManifest() {
- std::string path = config->manifestFile;
+ std::string path = std::string(config->manifestFile);
if (path == "")
path = config->outputFile + ".manifest";
std::error_code ec;
@@ -765,6 +767,8 @@ static const 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) {
@@ -810,8 +814,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 = table.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.
@@ -820,8 +823,8 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
if (!args.hasArg(OPT_lldignoreenv))
addLINK(expandedArgv);
cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
- args = table.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()) {
@@ -845,7 +848,7 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
for (auto *arg : args.filtered(OPT_UNKNOWN)) {
std::string nearest;
- if (table.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) +
@@ -859,30 +862,38 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
}
// Tokenizes and parses a given string as command line in .drective section.
-// /EXPORT options are processed in fastpath.
-std::pair<opt::InputArgList, std::vector<StringRef>>
-ArgParser::parseDirectives(StringRef s) {
- std::vector<StringRef> exports;
+ParsedDirectives ArgParser::parseDirectives(StringRef s) {
+ ParsedDirectives result;
SmallVector<const char *, 16> rest;
- for (StringRef tok : tokenize(s)) {
+ // Handle /EXPORT and /INCLUDE in a fast path. These directives can appear for
+ // potentially every symbol in the object, so they must be handled quickly.
+ SmallVector<StringRef, 16> tokens;
+ cl::TokenizeWindowsCommandLineNoCopy(s, saver, tokens);
+ for (StringRef tok : tokens) {
if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:"))
- exports.push_back(tok.substr(strlen("/export:")));
- else
- rest.push_back(tok.data());
+ result.exports.push_back(tok.substr(strlen("/export:")));
+ else if (tok.startswith_lower("/include:") ||
+ tok.startswith_lower("-include:"))
+ result.includes.push_back(tok.substr(strlen("/include:")));
+ else {
+ // Save non-null-terminated strings to make proper C strings.
+ bool HasNul = tok.data()[tok.size()] == '\0';
+ rest.push_back(HasNul ? tok.data() : saver.save(tok).data());
+ }
}
// Make InputArgList from unparsed string vectors.
unsigned missingIndex;
unsigned missingCount;
- opt::InputArgList args = table.ParseArgs(rest, missingIndex, missingCount);
+ result.args = optTable.ParseArgs(rest, missingIndex, missingCount);
if (missingCount)
- fatal(Twine(args.getArgString(missingIndex)) + ": missing argument");
- for (auto *arg : args.filtered(OPT_UNKNOWN))
- warn("ignoring unknown argument: " + arg->getAsString(args));
- return {std::move(args), std::move(exports)};
+ fatal(Twine(result.args.getArgString(missingIndex)) + ": missing argument");
+ for (auto *arg : result.args.filtered(OPT_UNKNOWN))
+ warn("ignoring unknown argument: " + arg->getAsString(result.args));
+ return result;
}
// link.exe has an interesting feature. If LINK or _LINK_ environment
@@ -907,9 +918,9 @@ std::vector<const char *> ArgParser::tokenize(StringRef s) {
}
void printHelp(const char *argv0) {
- COFFOptTable().PrintHelp(lld::outs(),
- (std::string(argv0) + " [options] file...").c_str(),
- "LLVM Linker", false);
+ optTable.PrintHelp(lld::outs(),
+ (std::string(argv0) + " [options] file...").c_str(),
+ "LLVM Linker", false);
}
} // namespace coff
diff --git a/lld/COFF/ICF.cpp b/lld/COFF/ICF.cpp
index c821569e3457..1b33634b63d6 100644
--- a/lld/COFF/ICF.cpp
+++ b/lld/COFF/ICF.cpp
@@ -21,7 +21,6 @@
#include "Chunks.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/Debug.h"
@@ -127,15 +126,19 @@ void ICF::segregate(size_t begin, size_t end, bool constant) {
// Returns true if two sections' associative children are equal.
bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) {
- auto childClasses = [&](const SectionChunk *sc) {
- std::vector<uint32_t> classes;
- for (const SectionChunk &c : sc->children())
- if (!c.getSectionName().startswith(".debug") &&
- c.getSectionName() != ".gfids$y" && c.getSectionName() != ".gljmp$y")
- classes.push_back(c.eqClass[cnt % 2]);
- return classes;
+ // Ignore associated metadata sections that don't participate in ICF, such as
+ // debug info and CFGuard metadata.
+ auto considerForICF = [](const SectionChunk &assoc) {
+ StringRef Name = assoc.getSectionName();
+ return !(Name.startswith(".debug") || Name == ".gfids$y" ||
+ Name == ".gljmp$y");
};
- return childClasses(a) == childClasses(b);
+ auto ra = make_filter_range(a->children(), considerForICF);
+ auto rb = make_filter_range(b->children(), considerForICF);
+ return std::equal(ra.begin(), ra.end(), rb.begin(), rb.end(),
+ [&](const SectionChunk &ia, const SectionChunk &ib) {
+ return ia.eqClass[cnt % 2] == ib.eqClass[cnt % 2];
+ });
}
// Compare "non-moving" part of two sections, namely everything
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
diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h
index 805d9121d8bc..50323f596e2c 100644
--- a/lld/COFF/InputFiles.h
+++ b/lld/COFF/InputFiles.h
@@ -26,6 +26,7 @@ namespace llvm {
struct DILineInfo;
namespace pdb {
class DbiModuleDescriptorBuilder;
+class NativeSession;
}
namespace lto {
class InputFile;
@@ -64,6 +65,7 @@ public:
ArchiveKind,
ObjectKind,
LazyObjectKind,
+ PDBKind,
ImportKind,
BitcodeKind
};
@@ -140,7 +142,7 @@ public:
MachineTypes getMachineType() override;
ArrayRef<Chunk *> getChunks() { return chunks; }
ArrayRef<SectionChunk *> getDebugChunks() { return debugChunks; }
- ArrayRef<SectionChunk *> getSXDataChunks() { return sXDataChunks; }
+ ArrayRef<SectionChunk *> getSXDataChunks() { return sxDataChunks; }
ArrayRef<SectionChunk *> getGuardFidChunks() { return guardFidChunks; }
ArrayRef<SectionChunk *> getGuardLJmpChunks() { return guardLJmpChunks; }
ArrayRef<Symbol *> getSymbols() { return symbols; }
@@ -276,29 +278,55 @@ private:
// Chunks containing symbol table indices of exception handlers. Only used for
// 32-bit x86.
- std::vector<SectionChunk *> sXDataChunks;
+ std::vector<SectionChunk *> sxDataChunks;
// Chunks containing symbol table indices of address taken symbols and longjmp
// targets. These are not linked into the final binary when /guard:cf is set.
std::vector<SectionChunk *> guardFidChunks;
std::vector<SectionChunk *> guardLJmpChunks;
- // This vector contains the same chunks as Chunks, but they are
- // indexed such that you can get a SectionChunk by section index.
- // Nonexistent section indices are filled with null pointers.
- // (Because section number is 1-based, the first slot is always a
- // null pointer.)
- std::vector<SectionChunk *> sparseChunks;
-
// This vector contains a list of all symbols defined or referenced by this
// file. They are indexed such that you can get a Symbol by symbol
// index. Nonexistent indices (which are occupied by auxiliary
// symbols in the real symbol table) are filled with null pointers.
std::vector<Symbol *> symbols;
+ // This vector contains the same chunks as Chunks, but they are
+ // indexed such that you can get a SectionChunk by section index.
+ // Nonexistent section indices are filled with null pointers.
+ // (Because section number is 1-based, the first slot is always a
+ // null pointer.) This vector is only valid during initialization.
+ std::vector<SectionChunk *> sparseChunks;
+
DWARFCache *dwarf = nullptr;
};
+// This is a PDB type server dependency, that is not a input file per se, but
+// needs to be treated like one. Such files are discovered from the debug type
+// stream.
+class PDBInputFile : public InputFile {
+public:
+ explicit PDBInputFile(MemoryBufferRef m);
+ ~PDBInputFile();
+ static bool classof(const InputFile *f) { return f->kind() == PDBKind; }
+ void parse() override;
+
+ static void enqueue(StringRef path, ObjFile *fromFile);
+
+ static PDBInputFile *findFromRecordPath(StringRef path, ObjFile *fromFile);
+
+ static std::map<std::string, PDBInputFile *> instances;
+
+ // Record possible errors while opening the PDB file
+ llvm::Optional<Error> loadErr;
+
+ // This is the actual interface to the PDB (if it was opened successfully)
+ std::unique_ptr<llvm::pdb::NativeSession> session;
+
+ // If the PDB has a .debug$T stream, this tells how it will be handled.
+ TpiSource *debugTypesObj = nullptr;
+};
+
// This type represents import library members that contain DLL names
// and symbols exported from the DLLs. See Microsoft PE/COFF spec. 7
// for details about the format.
diff --git a/lld/COFF/LLDMapFile.cpp b/lld/COFF/LLDMapFile.cpp
new file mode 100644
index 000000000000..79df33a3535f
--- /dev/null
+++ b/lld/COFF/LLDMapFile.cpp
@@ -0,0 +1,123 @@
+//===- LLDMapFile.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the /lldmap option. It shows lists in order and
+// hierarchically the output sections, input sections, input files and
+// symbol:
+//
+// Address Size Align Out File Symbol
+// 00201000 00000015 4 .text
+// 00201000 0000000e 4 test.o:(.text)
+// 0020100e 00000000 0 local
+// 00201005 00000000 0 f(int)
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDMapFile.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "Writer.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace lld;
+using namespace lld::coff;
+
+using SymbolMapTy =
+ DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
+
+static constexpr char indent8[] = " "; // 8 spaces
+static constexpr char indent16[] = " "; // 16 spaces
+
+// Print out the first three columns of a line.
+static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
+ uint64_t align) {
+ os << format("%08llx %08llx %5lld ", addr, size, align);
+}
+
+// Returns a list of all symbols that we want to print out.
+static std::vector<DefinedRegular *> getSymbols() {
+ std::vector<DefinedRegular *> v;
+ for (ObjFile *file : ObjFile::instances)
+ for (Symbol *b : file->getSymbols())
+ if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
+ if (sym && !sym->getCOFFSymbol().isSectionDefinition())
+ v.push_back(sym);
+ return v;
+}
+
+// Returns a map from sections to their symbols.
+static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
+ SymbolMapTy ret;
+ for (DefinedRegular *s : syms)
+ ret[s->getChunk()].push_back(s);
+
+ // Sort symbols by address.
+ for (auto &it : ret) {
+ SmallVectorImpl<DefinedRegular *> &v = it.second;
+ std::stable_sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
+ return a->getRVA() < b->getRVA();
+ });
+ }
+ return ret;
+}
+
+// Construct a map from symbols to their stringified representations.
+static DenseMap<DefinedRegular *, std::string>
+getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
+ std::vector<std::string> str(syms.size());
+ parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
+ raw_string_ostream os(str[i]);
+ writeHeader(os, syms[i]->getRVA(), 0, 0);
+ os << indent16 << toString(*syms[i]);
+ });
+
+ DenseMap<DefinedRegular *, std::string> ret;
+ for (size_t i = 0, e = syms.size(); i < e; ++i)
+ ret[syms[i]] = std::move(str[i]);
+ return ret;
+}
+
+void lld::coff::writeLLDMapFile(ArrayRef<OutputSection *> outputSections) {
+ if (config->lldmapFile.empty())
+ return;
+
+ std::error_code ec;
+ raw_fd_ostream os(config->lldmapFile, ec, sys::fs::OF_None);
+ if (ec)
+ fatal("cannot open " + config->lldmapFile + ": " + ec.message());
+
+ // Collect symbol info that we want to print out.
+ std::vector<DefinedRegular *> syms = getSymbols();
+ SymbolMapTy sectionSyms = getSectionSyms(syms);
+ DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
+
+ // Print out the header line.
+ os << "Address Size Align Out In Symbol\n";
+
+ // Print out file contents.
+ for (OutputSection *sec : outputSections) {
+ writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
+ os << sec->name << '\n';
+
+ for (Chunk *c : sec->chunks) {
+ auto *sc = dyn_cast<SectionChunk>(c);
+ if (!sc)
+ continue;
+
+ writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment());
+ os << indent8 << sc->file->getName() << ":(" << sc->getSectionName()
+ << ")\n";
+ for (DefinedRegular *sym : sectionSyms[sc])
+ os << symStr[sym] << '\n';
+ }
+ }
+}
diff --git a/lld/COFF/LLDMapFile.h b/lld/COFF/LLDMapFile.h
new file mode 100644
index 000000000000..b731293a8625
--- /dev/null
+++ b/lld/COFF/LLDMapFile.h
@@ -0,0 +1,21 @@
+//===- LLDMapFile.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_LLDMAPFILE_H
+#define LLD_COFF_LLDMAPFILE_H
+
+#include "llvm/ADT/ArrayRef.h"
+
+namespace lld {
+namespace coff {
+class OutputSection;
+void writeLLDMapFile(llvm::ArrayRef<OutputSection *> outputSections);
+}
+}
+
+#endif
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 1c21236dce2b..bb44819e60f8 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -38,9 +38,8 @@
using namespace llvm;
using namespace llvm::object;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
// Creates an empty file to and returns a raw_fd_ostream to write to it.
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
@@ -55,9 +54,9 @@ static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
}
static std::string getThinLTOOutputFile(StringRef path) {
- return lto::getThinLTOOutputFile(path,
- config->thinLTOPrefixReplace.first,
- config->thinLTOPrefixReplace.second);
+ return lto::getThinLTOOutputFile(
+ std::string(path), std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second));
}
static lto::Config createConfig() {
@@ -82,6 +81,7 @@ static lto::Config createConfig() {
c.CPU = getCPUStr();
c.MAttrs = getMAttrs();
c.CGOptLevel = args::getCGOptLevel(config->ltoo);
+ c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
if (config->saveTemps)
checkError(c.addSaveTemps(std::string(config->outputFile) + ".",
@@ -99,10 +99,12 @@ BitcodeCompiler::BitcodeCompiler() {
if (config->thinLTOIndexOnly) {
auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
backend = lto::createWriteIndexesThinBackend(
- config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second,
+ std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second),
config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
- } else if (config->thinLTOJobs != 0) {
- backend = lto::createInProcessThinBackend(config->thinLTOJobs);
+ } else {
+ backend = lto::createInProcessThinBackend(
+ llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
}
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
@@ -143,7 +145,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
// Merge all the bitcode files we have seen, codegen the result
// and return the resulting objects.
-std::vector<StringRef> BitcodeCompiler::compile() {
+std::vector<InputFile *> BitcodeCompiler::compile() {
unsigned maxTasks = ltoObj->getMaxTasks();
buf.resize(maxTasks);
files.resize(maxTasks);
@@ -187,25 +189,32 @@ std::vector<StringRef> BitcodeCompiler::compile() {
if (!config->ltoCache.empty())
pruneCache(config->ltoCache, config->ltoCachePolicy);
- std::vector<StringRef> ret;
+ std::vector<InputFile *> ret;
for (unsigned i = 0; i != maxTasks; ++i) {
- if (buf[i].empty())
+ // Assign unique names to LTO objects. This ensures they have unique names
+ // in the PDB if one is produced. The names should look like:
+ // - foo.exe.lto.obj
+ // - foo.exe.lto.1.obj
+ // - ...
+ StringRef ltoObjName =
+ saver.save(Twine(config->outputFile) + ".lto" +
+ (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj");
+
+ // Get the native object contents either from the cache or from memory. Do
+ // not use the cached MemoryBuffer directly, or the PDB will not be
+ // deterministic.
+ StringRef objBuf;
+ if (files[i])
+ objBuf = files[i]->getBuffer();
+ else
+ objBuf = buf[i];
+ if (objBuf.empty())
continue;
- if (config->saveTemps) {
- if (i == 0)
- saveBuffer(buf[i], config->outputFile + ".lto.obj");
- else
- saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.obj");
- }
- ret.emplace_back(buf[i].data(), buf[i].size());
- }
- for (std::unique_ptr<MemoryBuffer> &file : files)
- if (file)
- ret.push_back(file->getBuffer());
+ if (config->saveTemps)
+ saveBuffer(buf[i], ltoObjName);
+ ret.push_back(make<ObjFile>(MemoryBufferRef(objBuf, ltoObjName)));
+ }
return ret;
}
-
-} // namespace coff
-} // namespace lld
diff --git a/lld/COFF/LTO.h b/lld/COFF/LTO.h
index 2a0cfa061c95..a2b321df7901 100644
--- a/lld/COFF/LTO.h
+++ b/lld/COFF/LTO.h
@@ -45,7 +45,7 @@ public:
~BitcodeCompiler();
void add(BitcodeFile &f);
- std::vector<StringRef> compile();
+ std::vector<InputFile *> compile();
private:
std::unique_ptr<llvm::lto::LTO> ltoObj;
diff --git a/lld/COFF/MapFile.cpp b/lld/COFF/MapFile.cpp
index 0fea60aab99b..41e169ef56e2 100644
--- a/lld/COFF/MapFile.cpp
+++ b/lld/COFF/MapFile.cpp
@@ -6,16 +6,25 @@
//
//===----------------------------------------------------------------------===//
//
-// This file implements the /lldmap option. It shows lists in order and
-// hierarchically the output sections, input sections, input files and
-// symbol:
+// This file implements the /map option in the same format as link.exe
+// (based on observations)
//
-// Address Size Align Out File Symbol
-// 00201000 00000015 4 .text
-// 00201000 0000000e 4 test.o:(.text)
-// 0020100e 00000000 0 local
-// 00201005 00000000 0 f(int)
+// Header (program name, timestamp info, preferred load address)
//
+// Section list (Start = Section index:Base address):
+// Start Length Name Class
+// 0001:00001000 00000015H .text CODE
+//
+// Symbols list:
+// Address Publics by Value Rva + Base Lib:Object
+// 0001:00001000 main 0000000140001000 main.obj
+// 0001:00001300 ?__scrt_common_main@@YAHXZ 0000000140001300 libcmt:exe_main.obj
+//
+// entry point at 0001:00000360
+//
+// Static symbols
+//
+// 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj
//===----------------------------------------------------------------------===//
#include "MapFile.h"
@@ -23,71 +32,176 @@
#include "Symbols.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
+#include "lld/Common/Timer.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::object;
+using namespace lld;
+using namespace lld::coff;
-namespace lld {
-namespace coff {
+static Timer totalMapTimer("MAP emission (Cumulative)", Timer::root());
+static Timer symbolGatherTimer("Gather symbols", totalMapTimer);
+static Timer symbolStringsTimer("Build symbol strings", totalMapTimer);
+static Timer writeTimer("Write to file", totalMapTimer);
-using SymbolMapTy =
- DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
+// Print out the first two columns of a line.
+static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
+ os << format(" %04x:%08llx", sec, addr);
+}
-static constexpr char indent8[] = " "; // 8 spaces
-static constexpr char indent16[] = " "; // 16 spaces
+// Write the time stamp with the format used by link.exe
+// It seems identical to strftime with "%c" on msvc build, but we need a
+// locale-agnostic version.
+static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
+ constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat"};
+ constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",
+ "May", "Jun", "Jul", "Aug",
+ "Sep", "Oct", "Nov", "Dec"};
+ tm *time = localtime(&tds);
+ os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],
+ months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,
+ time->tm_sec, time->tm_year + 1900);
+}
-// Print out the first three columns of a line.
-static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
- uint64_t align) {
- os << format("%08llx %08llx %5lld ", addr, size, align);
+static void sortUniqueSymbols(std::vector<Defined *> &syms) {
+ // Build helper vector
+ using SortEntry = std::pair<Defined *, size_t>;
+ std::vector<SortEntry> v;
+ v.resize(syms.size());
+ for (size_t i = 0, e = syms.size(); i < e; ++i)
+ v[i] = SortEntry(syms[i], i);
+
+ // Remove duplicate symbol pointers
+ parallelSort(v, std::less<SortEntry>());
+ auto end = std::unique(v.begin(), v.end(),
+ [](const SortEntry &a, const SortEntry &b) {
+ return a.first == b.first;
+ });
+ v.erase(end, v.end());
+
+ // Sort by RVA then original order
+ parallelSort(v, [](const SortEntry &a, const SortEntry &b) {
+ // Add config->imageBase to avoid comparing "negative" RVAs.
+ // This can happen with symbols of Absolute kind
+ uint64_t rvaa = config->imageBase + a.first->getRVA();
+ uint64_t rvab = config->imageBase + b.first->getRVA();
+ return rvaa < rvab || (rvaa == rvab && a.second < b.second);
+ });
+
+ syms.resize(v.size());
+ for (size_t i = 0, e = v.size(); i < e; ++i)
+ syms[i] = v[i].first;
}
-// Returns a list of all symbols that we want to print out.
-static std::vector<DefinedRegular *> getSymbols() {
- std::vector<DefinedRegular *> v;
+// Returns the lists of all symbols that we want to print out.
+static void getSymbols(std::vector<Defined *> &syms,
+ std::vector<Defined *> &staticSyms) {
+
for (ObjFile *file : ObjFile::instances)
- for (Symbol *b : file->getSymbols())
- if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
- if (sym && !sym->getCOFFSymbol().isSectionDefinition())
- v.push_back(sym);
- return v;
-}
+ for (Symbol *b : file->getSymbols()) {
+ if (!b || !b->isLive())
+ continue;
+ if (auto *sym = dyn_cast<DefinedCOFF>(b)) {
+ COFFSymbolRef symRef = sym->getCOFFSymbol();
+ if (!symRef.isSectionDefinition() &&
+ symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
+ if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)
+ staticSyms.push_back(sym);
+ else
+ syms.push_back(sym);
+ }
+ } else if (auto *sym = dyn_cast<Defined>(b)) {
+ syms.push_back(sym);
+ }
+ }
+
+ for (ImportFile *file : ImportFile::instances) {
+ if (!file->live)
+ continue;
+
+ if (!file->thunkSym)
+ continue;
+
+ if (!file->thunkLive)
+ continue;
+
+ if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym))
+ syms.push_back(thunkSym);
-// Returns a map from sections to their symbols.
-static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
- SymbolMapTy ret;
- for (DefinedRegular *s : syms)
- ret[s->getChunk()].push_back(s);
-
- // Sort symbols by address.
- for (auto &it : ret) {
- SmallVectorImpl<DefinedRegular *> &v = it.second;
- std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
- return a->getRVA() < b->getRVA();
- });
+ if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym))
+ syms.push_back(impSym);
}
- return ret;
+
+ sortUniqueSymbols(syms);
+ sortUniqueSymbols(staticSyms);
}
// Construct a map from symbols to their stringified representations.
-static DenseMap<DefinedRegular *, std::string>
-getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
+static DenseMap<Defined *, std::string>
+getSymbolStrings(ArrayRef<Defined *> syms) {
std::vector<std::string> str(syms.size());
parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
raw_string_ostream os(str[i]);
- writeHeader(os, syms[i]->getRVA(), 0, 0);
- os << indent16 << toString(*syms[i]);
+ Defined *sym = syms[i];
+
+ uint16_t sectionIdx = 0;
+ uint64_t address = 0;
+ SmallString<128> fileDescr;
+
+ if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {
+ address = absSym->getVA();
+ fileDescr = "<absolute>";
+ } else if (isa<DefinedSynthetic>(sym)) {
+ fileDescr = "<linker-defined>";
+ } else if (isa<DefinedCommon>(sym)) {
+ fileDescr = "<common>";
+ } else if (Chunk *chunk = sym->getChunk()) {
+ address = sym->getRVA();
+ if (OutputSection *sec = chunk->getOutputSection())
+ address -= sec->header.VirtualAddress;
+
+ sectionIdx = chunk->getOutputSectionIdx();
+
+ InputFile *file;
+ if (auto *impSym = dyn_cast<DefinedImportData>(sym))
+ file = impSym->file;
+ else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))
+ file = thunkSym->wrappedSym->file;
+ else
+ file = sym->getFile();
+
+ if (file) {
+ if (!file->parentName.empty()) {
+ fileDescr = sys::path::filename(file->parentName);
+ sys::path::replace_extension(fileDescr, "");
+ fileDescr += ":";
+ }
+ fileDescr += sys::path::filename(file->getName());
+ }
+ }
+ writeHeader(os, sectionIdx, address);
+ os << " ";
+ os << left_justify(sym->getName(), 26);
+ os << " ";
+ 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
+ os << fileDescr;
+ }
});
- DenseMap<DefinedRegular *, std::string> ret;
+ DenseMap<Defined *, std::string> ret;
for (size_t i = 0, e = syms.size(); i < e; ++i)
ret[syms[i]] = std::move(str[i]);
return ret;
}
-void writeMapFile(ArrayRef<OutputSection *> outputSections) {
+void lld::coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
if (config->mapFile.empty())
return;
@@ -96,32 +210,113 @@ void writeMapFile(ArrayRef<OutputSection *> outputSections) {
if (ec)
fatal("cannot open " + config->mapFile + ": " + ec.message());
+ ScopedTimer t1(totalMapTimer);
+
// Collect symbol info that we want to print out.
- std::vector<DefinedRegular *> syms = getSymbols();
- SymbolMapTy sectionSyms = getSectionSyms(syms);
- DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
+ ScopedTimer t2(symbolGatherTimer);
+ std::vector<Defined *> syms;
+ std::vector<Defined *> staticSyms;
+ getSymbols(syms, staticSyms);
+ t2.stop();
- // Print out the header line.
- os << "Address Size Align Out In Symbol\n";
+ ScopedTimer t3(symbolStringsTimer);
+ DenseMap<Defined *, std::string> symStr = getSymbolStrings(syms);
+ DenseMap<Defined *, std::string> staticSymStr = getSymbolStrings(staticSyms);
+ t3.stop();
- // Print out file contents.
- for (OutputSection *sec : outputSections) {
- writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
- os << sec->name << '\n';
+ ScopedTimer t4(writeTimer);
+ 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(config->timestamp, 8) << " (";
+ if (config->repro) {
+ os << "Repro mode";
+ } else {
+ writeFormattedTimestamp(os, config->timestamp);
+ }
+ os << ")\n";
+
+ os << "\n";
+ os << " Preferred load address is "
+ << format_hex_no_prefix(config->imageBase, 16) << "\n";
+ os << "\n";
+
+ // Print out section table.
+ os << " Start Length Name Class\n";
+
+ for (OutputSection *sec : outputSections) {
+ // Merge display of chunks with same sectionName
+ std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
for (Chunk *c : sec->chunks) {
auto *sc = dyn_cast<SectionChunk>(c);
if (!sc)
continue;
- writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment());
- os << indent8 << sc->file->getName() << ":(" << sc->getSectionName()
- << ")\n";
- for (DefinedRegular *sym : sectionSyms[sc])
- os << symStr[sym] << '\n';
+ if (ChunkRanges.empty() ||
+ c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
+ ChunkRanges.emplace_back(sc, sc);
+ } else {
+ ChunkRanges.back().second = sc;
+ }
+ }
+
+ const bool isCodeSection =
+ (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
+ (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
+ (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
+ StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
+
+ for (auto &cr : ChunkRanges) {
+ size_t size =
+ cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
+
+ auto address = cr.first->getRVA() - sec->header.VirtualAddress;
+ writeHeader(os, sec->sectionIndex, address);
+ os << " " << format_hex_no_prefix(size, 8) << "H";
+ os << " " << left_justify(cr.first->getSectionName(), 23);
+ os << " " << SectionClass;
+ os << '\n';
}
}
-}
-} // namespace coff
-} // namespace lld
+ // Print out the symbols table (without static symbols)
+ os << "\n";
+ os << " Address Publics by Value Rva+Base"
+ " Lib:Object\n";
+ os << "\n";
+ for (Defined *sym : syms)
+ os << symStr[sym] << '\n';
+
+ // Print out the entry point.
+ os << "\n";
+
+ uint16_t entrySecIndex = 0;
+ uint64_t entryAddress = 0;
+
+ if (!config->noEntry) {
+ Defined *entry = dyn_cast_or_null<Defined>(config->entry);
+ if (entry) {
+ Chunk *chunk = entry->getChunk();
+ entrySecIndex = chunk->getOutputSectionIdx();
+ entryAddress =
+ entry->getRVA() - chunk->getOutputSection()->header.VirtualAddress;
+ }
+ }
+ os << " entry point at ";
+ os << format("%04x:%08llx", entrySecIndex, entryAddress);
+ os << "\n";
+
+ // Print out the static symbols
+ os << "\n";
+ os << " Static symbols\n";
+ os << "\n";
+ for (Defined *sym : staticSyms)
+ os << staticSymStr[sym] << '\n';
+
+ t4.stop();
+ t1.stop();
+}
diff --git a/lld/COFF/MarkLive.cpp b/lld/COFF/MarkLive.cpp
index 6d34cb864e3c..0afa615a1933 100644
--- a/lld/COFF/MarkLive.cpp
+++ b/lld/COFF/MarkLive.cpp
@@ -28,10 +28,12 @@ void markLive(ArrayRef<Chunk *> chunks) {
// as we push, so sections never appear twice in the list.
SmallVector<SectionChunk *, 256> worklist;
- // COMDAT section chunks are dead by default. Add non-COMDAT chunks.
+ // COMDAT section chunks are dead by default. Add non-COMDAT chunks. Do not
+ // traverse DWARF sections. They are live, but they should not keep other
+ // sections alive.
for (Chunk *c : chunks)
if (auto *sc = dyn_cast<SectionChunk>(c))
- if (sc->live)
+ if (sc->live && !sc->isDWARF())
worklist.push_back(sc);
auto enqueue = [&](SectionChunk *c) {
diff --git a/lld/COFF/MinGW.cpp b/lld/COFF/MinGW.cpp
index 270cdaab4d9c..bded985f04d0 100644
--- a/lld/COFF/MinGW.cpp
+++ b/lld/COFF/MinGW.cpp
@@ -15,9 +15,8 @@
using namespace llvm;
using namespace llvm::COFF;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
AutoExporter::AutoExporter() {
excludeLibs = {
@@ -147,7 +146,7 @@ bool AutoExporter::shouldExport(Defined *sym) const {
return !excludeObjects.count(fileName);
}
-void writeDefFile(StringRef name) {
+void lld::coff::writeDefFile(StringRef name) {
std::error_code ec;
raw_fd_ostream os(name, ec, sys::fs::OF_None);
if (ec)
@@ -165,6 +164,3 @@ void writeDefFile(StringRef name) {
os << "\n";
}
}
-
-} // namespace coff
-} // namespace lld
diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index 468e4da7d054..212879e1d60b 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -16,6 +16,13 @@ multiclass B<string name, string help_on, string help_off> {
def _no : F<name#":no">, HelpText<help_off>;
}
+// Same as B<> above, but without help texts, for private undocumented
+// options.
+multiclass B_priv<string name> {
+ def "" : F<name>;
+ def _no : F<name#":no">;
+}
+
def align : P<"align", "Section alignment">;
def aligncomm : P<"aligncomm", "Set common symbol alignment">;
def alternatename : P<"alternatename", "Define weak alias">;
@@ -64,7 +71,11 @@ def natvis : P<"natvis", "Path to natvis file to embed in the PDB">;
def no_color_diagnostics: F<"no-color-diagnostics">,
HelpText<"Do not use colors in diagnostics">;
def pdb : P<"pdb", "PDB file path">;
+def pdbstripped : P<"pdbstripped", "Stripped PDB file path">;
def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">;
+def pdbstream : Joined<["/", "-", "/?", "-?"], "pdbstream:">,
+ MetaVarName<"<name>=<file>">,
+ HelpText<"Embed the contents of <file> in the PDB as named stream <name>">;
def section : P<"section", "Specify section attributes">;
def stack : P<"stack", "Size of the stack">;
def stub : P<"stub", "Specify DOS stub file">;
@@ -147,6 +158,8 @@ defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)",
defm appcontainer : B<"appcontainer",
"Image can only be run in an app container",
"Image can run outside an app container (default)">;
+defm cetcompat : B<"cetcompat", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack",
+ "Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack (default)">;
defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)",
"Disable ASLR (default when /fixed)">;
defm fixed : B<"fixed", "Disable base relocations",
@@ -178,6 +191,8 @@ def help : F<"help">;
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
// LLD extensions
+defm auto_import : B_priv<"auto-import">;
+defm runtime_pseudo_reloc : B_priv<"runtime-pseudo-reloc">;
def end_lib : F<"end-lib">,
HelpText<"Ends group of objects treated as if they were in a library">;
def exclude_all_symbols : F<"exclude-all-symbols">;
@@ -216,13 +231,15 @@ def lto_obj_path : P<
"output native object for merged LTO unit to this path">;
def dash_dash_version : Flag<["--"], "version">,
HelpText<"Print version information">;
-defm threads: B<"threads",
- "Run the linker multi-threaded (default)",
- "Do not run the linker multi-threaded">;
+def threads
+ : P<"threads", "Number of threads. '1' disables multi-threading. By "
+ "default all available hardware threads are used">;
// Flags for debugging
def lldmap : F<"lldmap">;
def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">;
+def map : F<"map">;
+def map_file : Joined<["/", "-", "/?", "-?"], "map:">;
def show_timing : F<"time">;
def summary : F<"summary">;
diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp
index f68c60a13254..49d04add5be0 100644
--- a/lld/COFF/PDB.cpp
+++ b/lld/COFF/PDB.cpp
@@ -16,7 +16,6 @@
#include "TypeMerger.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
@@ -27,11 +26,7 @@
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h"
#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
-#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
-#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
-#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
-#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/MSF/MSFCommon.h"
#include "llvm/DebugInfo/PDB/GenericError.h"
@@ -63,12 +58,11 @@
using namespace llvm;
using namespace llvm::codeview;
+using namespace lld;
+using namespace lld::coff;
using llvm::object::coff_section;
-namespace lld {
-namespace coff {
-
static ExitOnError exitOnErr;
static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
@@ -76,7 +70,7 @@ static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer);
static Timer typeMergingTimer("Type Merging", addObjectsTimer);
static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer);
-static Timer globalsLayoutTimer("Globals Stream Layout", totalPdbLinkTimer);
+static Timer publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer);
static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer);
static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer);
@@ -88,7 +82,7 @@ class PDBLinker {
public:
PDBLinker(SymbolTable *symtab)
- : alloc(), symtab(symtab), builder(alloc), tMerger(alloc) {
+ : symtab(symtab), builder(bAlloc), tMerger(bAlloc) {
// This isn't strictly necessary, but link.exe usually puts an empty string
// as the first "valid" string in the string table, so we do the same in
// order to maintain as much byte-for-byte compatibility as possible.
@@ -101,53 +95,26 @@ public:
/// Add natvis files specified on the command line.
void addNatvisFiles();
+ /// Add named streams specified on the command line.
+ void addNamedStreams();
+
/// Link CodeView from each object file in the symbol table into the PDB.
void addObjectsToPDB();
+ /// Add every live, defined public symbol to the PDB.
+ void addPublicsToPDB();
+
/// Link info for each import file in the symbol table into the PDB.
void addImportFilesToPDB(ArrayRef<OutputSection *> outputSections);
/// Link CodeView from a single object file into the target (output) PDB.
/// When a precompiled headers object is linked, its TPI map might be provided
/// externally.
- void addObjFile(ObjFile *file, CVIndexMap *externIndexMap = nullptr);
-
- /// Produce a mapping from the type and item indices used in the object
- /// file to those in the destination PDB.
- ///
- /// If the object file uses a type server PDB (compiled with /Zi), merge TPI
- /// and IPI from the type server PDB and return a map for it. Each unique type
- /// server PDB is merged at most once, so this may return an existing index
- /// mapping.
- ///
- /// If the object does not use a type server PDB (compiled with /Z7), we merge
- /// all the type and item records from the .debug$S stream and fill in the
- /// caller-provided objectIndexMap.
- Expected<const CVIndexMap &> mergeDebugT(ObjFile *file,
- CVIndexMap *objectIndexMap);
-
- /// Reads and makes available a PDB.
- Expected<const CVIndexMap &> maybeMergeTypeServerPDB(ObjFile *file);
-
- /// Merges a precompiled headers TPI map into the current TPI map. The
- /// precompiled headers object will also be loaded and remapped in the
- /// process.
- Error mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap);
-
- /// Reads and makes available a precompiled headers object.
- ///
- /// This is a requirement for objects compiled with cl.exe /Yu. In that
- /// case, the referenced object (which was compiled with /Yc) has to be loaded
- /// first. This is mainly because the current object's TPI stream has external
- /// references to the precompiled headers object.
- ///
- /// If the precompiled headers object was already loaded, this function will
- /// simply return its (remapped) TPI map.
- Expected<const CVIndexMap &> aquirePrecompObj(ObjFile *file);
-
- /// Adds a precompiled headers object signature -> TPI mapping.
- std::pair<CVIndexMap &, bool /*already there*/>
- registerPrecompiledHeaders(uint32_t signature);
+ void addDebug(TpiSource *source);
+
+ const CVIndexMap *mergeTypeRecords(TpiSource *source, CVIndexMap *localMap);
+
+ void addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap);
void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
std::vector<ulittle32_t *> &stringTableRefs,
@@ -164,8 +131,6 @@ public:
void printStats();
private:
- BumpPtrAllocator alloc;
-
SymbolTable *symtab;
pdb::PDBFileBuilder builder;
@@ -178,24 +143,10 @@ private:
llvm::SmallString<128> nativePath;
- std::vector<pdb::SecMapEntry> sectionMap;
-
- /// Type index mappings of type server PDBs that we've loaded so far.
- std::map<codeview::GUID, CVIndexMap> typeServerIndexMappings;
-
- /// Type index mappings of precompiled objects type map that we've loaded so
- /// far.
- std::map<uint32_t, CVIndexMap> precompTypeIndexMappings;
-
// For statistics
uint64_t globalSymbols = 0;
uint64_t moduleSymbols = 0;
uint64_t publicSymbols = 0;
-
- // When showSummary is enabled, these are histograms of TPI and IPI records
- // keyed by type index.
- SmallVector<uint32_t, 0> tpiCounts;
- SmallVector<uint32_t, 0> ipiCounts;
};
class DebugSHandler {
@@ -205,24 +156,20 @@ class DebugSHandler {
ObjFile &file;
/// The result of merging type indices.
- const CVIndexMap &indexMap;
+ const CVIndexMap *indexMap;
/// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by
/// index from other records in the .debug$S section. All of these strings
/// need to be added to the global PDB string table, and all references to
/// these strings need to have their indices re-written to refer to the
/// global PDB string table.
- DebugStringTableSubsectionRef cVStrTab;
+ DebugStringTableSubsectionRef cvStrTab;
/// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to
/// by other records in the .debug$S section and need to be merged into the
/// PDB.
DebugChecksumsSubsectionRef checksums;
- /// The DEBUG_S_INLINEELINES subsection. There can be only one of these per
- /// object file.
- DebugInlineeLinesSubsectionRef inlineeLines;
-
/// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of
/// these and they need not appear in any specific order. However, they
/// contain string table references which need to be re-written, so we
@@ -238,14 +185,13 @@ class DebugSHandler {
/// references.
std::vector<ulittle32_t *> stringTableReferences;
+ void mergeInlineeLines(const DebugSubsectionRecord &inlineeLines);
+
public:
- DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap &indexMap)
+ DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap *indexMap)
: linker(linker), file(file), indexMap(indexMap) {}
- void handleDebugS(lld::coff::SectionChunk &debugS);
-
- std::shared_ptr<DebugInlineeLinesSubsection>
- mergeInlineeLines(DebugChecksumsSubsection *newChecksums);
+ void handleDebugS(ArrayRef<uint8_t> relocatedDebugContents);
void finish();
};
@@ -290,42 +236,6 @@ static void pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
fileName = std::move(absoluteFileName);
}
-// A COFF .debug$H section is currently a clang extension. This function checks
-// if a .debug$H section is in a format that we expect / understand, so that we
-// can ignore any sections which are coincidentally also named .debug$H but do
-// not contain a format we recognize.
-static bool canUseDebugH(ArrayRef<uint8_t> debugH) {
- if (debugH.size() < sizeof(object::debug_h_header))
- return false;
- auto *header =
- reinterpret_cast<const object::debug_h_header *>(debugH.data());
- debugH = debugH.drop_front(sizeof(object::debug_h_header));
- return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
- header->Version == 0 &&
- header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) &&
- (debugH.size() % 8 == 0);
-}
-
-static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *file) {
- SectionChunk *sec =
- SectionChunk::findByName(file->getDebugChunks(), ".debug$H");
- if (!sec)
- return llvm::None;
- ArrayRef<uint8_t> contents = sec->getContents();
- if (!canUseDebugH(contents))
- return None;
- return contents;
-}
-
-static ArrayRef<GloballyHashedType>
-getHashesFromDebugH(ArrayRef<uint8_t> debugH) {
- assert(canUseDebugH(debugH));
-
- debugH = debugH.drop_front(sizeof(object::debug_h_header));
- uint32_t count = debugH.size() / sizeof(GloballyHashedType);
- return {reinterpret_cast<const GloballyHashedType *>(debugH.data()), count};
-}
-
static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder,
TypeCollection &typeTable) {
// Start the TPI or IPI stream header.
@@ -340,281 +250,6 @@ static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder,
});
}
-Expected<const CVIndexMap &>
-PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) {
- ScopedTimer t(typeMergingTimer);
-
- if (!file->debugTypesObj)
- return *objectIndexMap; // no Types stream
-
- // Precompiled headers objects need to save the index map for further
- // reference by other objects which use the precompiled headers.
- if (file->debugTypesObj->kind == TpiSource::PCH) {
- uint32_t pchSignature = file->pchSignature.getValueOr(0);
- if (pchSignature == 0)
- fatal("No signature found for the precompiled headers OBJ (" +
- file->getName() + ")");
-
- // When a precompiled headers object comes first on the command-line, we
- // update the mapping here. Otherwise, if an object referencing the
- // precompiled headers object comes first, the mapping is created in
- // aquirePrecompObj(), thus we would skip this block.
- if (!objectIndexMap->isPrecompiledTypeMap) {
- auto r = registerPrecompiledHeaders(pchSignature);
- if (r.second)
- fatal(
- "A precompiled headers OBJ with the same signature was already "
- "provided! (" +
- file->getName() + ")");
-
- objectIndexMap = &r.first;
- }
- }
-
- if (file->debugTypesObj->kind == TpiSource::UsingPDB) {
- // Look through type servers. If we've already seen this type server,
- // don't merge any type information.
- return maybeMergeTypeServerPDB(file);
- }
-
- CVTypeArray types;
- BinaryStreamReader reader(file->debugTypes, support::little);
- cantFail(reader.readArray(types, reader.getLength()));
-
- if (file->debugTypesObj->kind == TpiSource::UsingPCH) {
- // This object was compiled with /Yu, so process the corresponding
- // precompiled headers object (/Yc) first. Some type indices in the current
- // object are referencing data in the precompiled headers object, so we need
- // both to be loaded.
- Error e = mergeInPrecompHeaderObj(file, objectIndexMap);
- if (e)
- return std::move(e);
-
- // Drop LF_PRECOMP record from the input stream, as it has been replaced
- // with the precompiled headers Type stream in the mergeInPrecompHeaderObj()
- // call above. Note that we can't just call Types.drop_front(), as we
- // explicitly want to rebase the stream.
- CVTypeArray::Iterator firstType = types.begin();
- types.setUnderlyingStream(
- types.getUnderlyingStream().drop_front(firstType->RecordData.size()));
- }
-
- // Fill in the temporary, caller-provided ObjectIndexMap.
- if (config->debugGHashes) {
- ArrayRef<GloballyHashedType> hashes;
- std::vector<GloballyHashedType> ownedHashes;
- if (Optional<ArrayRef<uint8_t>> debugH = getDebugH(file))
- hashes = getHashesFromDebugH(*debugH);
- else {
- ownedHashes = GloballyHashedType::hashTypes(types);
- hashes = ownedHashes;
- }
-
- if (auto err = mergeTypeAndIdRecords(
- tMerger.globalIDTable, tMerger.globalTypeTable,
- objectIndexMap->tpiMap, types, hashes, file->pchSignature))
- fatal("codeview::mergeTypeAndIdRecords failed: " +
- toString(std::move(err)));
- } else {
- if (auto err = mergeTypeAndIdRecords(tMerger.iDTable, tMerger.typeTable,
- objectIndexMap->tpiMap, types,
- file->pchSignature))
- fatal("codeview::mergeTypeAndIdRecords failed: " +
- toString(std::move(err)));
- }
-
- if (config->showSummary) {
- // Count how many times we saw each type record in our input. This
- // calculation requires a second pass over the type records to classify each
- // record as a type or index. This is slow, but this code executes when
- // collecting statistics.
- tpiCounts.resize(tMerger.getTypeTable().size());
- ipiCounts.resize(tMerger.getIDTable().size());
- uint32_t srcIdx = 0;
- for (CVType &ty : types) {
- TypeIndex dstIdx = objectIndexMap->tpiMap[srcIdx++];
- // Type merging may fail, so a complex source type may become the simple
- // NotTranslated type, which cannot be used as an array index.
- if (dstIdx.isSimple())
- continue;
- SmallVectorImpl<uint32_t> &counts =
- isIdRecord(ty.kind()) ? ipiCounts : tpiCounts;
- ++counts[dstIdx.toArrayIndex()];
- }
- }
-
- return *objectIndexMap;
-}
-
-Expected<const CVIndexMap &> PDBLinker::maybeMergeTypeServerPDB(ObjFile *file) {
- Expected<llvm::pdb::NativeSession *> pdbSession = findTypeServerSource(file);
- if (!pdbSession)
- return pdbSession.takeError();
-
- pdb::PDBFile &pdbFile = pdbSession.get()->getPDBFile();
- pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream());
-
- auto it = typeServerIndexMappings.emplace(info.getGuid(), CVIndexMap());
- CVIndexMap &indexMap = it.first->second;
- if (!it.second)
- return indexMap; // already merged
-
- // Mark this map as a type server map.
- indexMap.isTypeServerMap = true;
-
- Expected<pdb::TpiStream &> expectedTpi = pdbFile.getPDBTpiStream();
- if (auto e = expectedTpi.takeError())
- fatal("Type server does not have TPI stream: " + toString(std::move(e)));
- pdb::TpiStream *maybeIpi = nullptr;
- if (pdbFile.hasPDBIpiStream()) {
- Expected<pdb::TpiStream &> expectedIpi = pdbFile.getPDBIpiStream();
- if (auto e = expectedIpi.takeError())
- fatal("Error getting type server IPI stream: " + toString(std::move(e)));
- maybeIpi = &*expectedIpi;
- }
-
- if (config->debugGHashes) {
- // PDBs do not actually store global hashes, so when merging a type server
- // PDB we have to synthesize global hashes. To do this, we first synthesize
- // global hashes for the TPI stream, since it is independent, then we
- // synthesize hashes for the IPI stream, using the hashes for the TPI stream
- // as inputs.
- auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray());
- Optional<uint32_t> endPrecomp;
- // Merge TPI first, because the IPI stream will reference type indices.
- if (auto err =
- mergeTypeRecords(tMerger.globalTypeTable, indexMap.tpiMap,
- expectedTpi->typeArray(), tpiHashes, endPrecomp))
- fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
-
- // Merge IPI.
- if (maybeIpi) {
- auto ipiHashes =
- GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes);
- if (auto err =
- mergeIdRecords(tMerger.globalIDTable, indexMap.tpiMap,
- indexMap.ipiMap, maybeIpi->typeArray(), ipiHashes))
- fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
- }
- } else {
- // Merge TPI first, because the IPI stream will reference type indices.
- if (auto err = mergeTypeRecords(tMerger.typeTable, indexMap.tpiMap,
- expectedTpi->typeArray()))
- fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
-
- // Merge IPI.
- if (maybeIpi) {
- if (auto err = mergeIdRecords(tMerger.iDTable, indexMap.tpiMap,
- indexMap.ipiMap, maybeIpi->typeArray()))
- fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
- }
- }
-
- if (config->showSummary) {
- // Count how many times we saw each type record in our input. If a
- // destination type index is present in the source to destination type index
- // map, that means we saw it once in the input. Add it to our histogram.
- tpiCounts.resize(tMerger.getTypeTable().size());
- ipiCounts.resize(tMerger.getIDTable().size());
- for (TypeIndex ti : indexMap.tpiMap)
- if (!ti.isSimple())
- ++tpiCounts[ti.toArrayIndex()];
- for (TypeIndex ti : indexMap.ipiMap)
- if (!ti.isSimple())
- ++ipiCounts[ti.toArrayIndex()];
- }
-
- return indexMap;
-}
-
-Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *file,
- CVIndexMap *objectIndexMap) {
- const PrecompRecord &precomp =
- retrieveDependencyInfo<PrecompRecord>(file->debugTypesObj);
-
- Expected<const CVIndexMap &> e = aquirePrecompObj(file);
- if (!e)
- return e.takeError();
-
- const CVIndexMap &precompIndexMap = *e;
- assert(precompIndexMap.isPrecompiledTypeMap);
-
- if (precompIndexMap.tpiMap.empty())
- return Error::success();
-
- assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex);
- assert(precomp.getTypesCount() <= precompIndexMap.tpiMap.size());
- // Use the previously remapped index map from the precompiled headers.
- objectIndexMap->tpiMap.append(precompIndexMap.tpiMap.begin(),
- precompIndexMap.tpiMap.begin() +
- precomp.getTypesCount());
- return Error::success();
-}
-
-static bool equals_path(StringRef path1, StringRef path2) {
-#if defined(_WIN32)
- return path1.equals_lower(path2);
-#else
- return path1.equals(path2);
-#endif
-}
-// Find by name an OBJ provided on the command line
-static ObjFile *findObjWithPrecompSignature(StringRef fileNameOnly,
- uint32_t precompSignature) {
- for (ObjFile *f : ObjFile::instances) {
- StringRef currentFileName = sys::path::filename(f->getName());
-
- if (f->pchSignature.hasValue() &&
- f->pchSignature.getValue() == precompSignature &&
- equals_path(fileNameOnly, currentFileName))
- return f;
- }
- return nullptr;
-}
-
-std::pair<CVIndexMap &, bool /*already there*/>
-PDBLinker::registerPrecompiledHeaders(uint32_t signature) {
- auto insertion = precompTypeIndexMappings.insert({signature, CVIndexMap()});
- CVIndexMap &indexMap = insertion.first->second;
- if (!insertion.second)
- return {indexMap, true};
- // Mark this map as a precompiled types map.
- indexMap.isPrecompiledTypeMap = true;
- return {indexMap, false};
-}
-
-Expected<const CVIndexMap &> PDBLinker::aquirePrecompObj(ObjFile *file) {
- const PrecompRecord &precomp =
- retrieveDependencyInfo<PrecompRecord>(file->debugTypesObj);
-
- // First, check if we already loaded the precompiled headers object with this
- // signature. Return the type index mapping if we've already seen it.
- auto r = registerPrecompiledHeaders(precomp.getSignature());
- if (r.second)
- return r.first;
-
- CVIndexMap &indexMap = r.first;
-
- // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP
- // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly,
- // the paths embedded in the OBJs are in the Windows format.
- SmallString<128> precompFileName = sys::path::filename(
- precomp.getPrecompFilePath(), sys::path::Style::windows);
-
- // link.exe requires that a precompiled headers object must always be provided
- // on the command-line, even if that's not necessary.
- auto precompFile =
- findObjWithPrecompSignature(precompFileName, precomp.Signature);
- if (!precompFile)
- return createFileError(
- precomp.getPrecompFilePath().str(),
- make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
-
- addObjFile(precompFile, &indexMap);
-
- return indexMap;
-}
-
static bool remapTypeIndex(TypeIndex &ti, ArrayRef<TypeIndex> typeIndexMap) {
if (ti.isSimple())
return true;
@@ -695,7 +330,7 @@ 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
static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
- TypeCollection &iDTable) {
+ TypeCollection &idTable) {
RecordPrefix *prefix = reinterpret_cast<RecordPrefix *>(recordData.data());
SymbolKind kind = symbolKind(recordData);
@@ -725,11 +360,10 @@ static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
// Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and
// in both cases we just need the second type index.
if (!ti->isSimple() && !ti->isNoneType()) {
- CVType funcIdData = iDTable.getType(*ti);
- SmallVector<TypeIndex, 2> indices;
- discoverTypeIndices(funcIdData, indices);
- assert(indices.size() == 2);
- *ti = indices[1];
+ CVType funcIdData = idTable.getType(*ti);
+ ArrayRef<uint8_t> tiBuf = funcIdData.data().slice(8, 4);
+ assert(tiBuf.size() == 4 && "corrupt LF_[MEM]FUNC_ID record");
+ *ti = *reinterpret_cast<const TypeIndex *>(tiBuf.data());
}
kind = (kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32
@@ -795,6 +429,7 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) {
switch (sym.kind()) {
case SymbolKind::S_GDATA32:
case SymbolKind::S_CONSTANT:
+ case SymbolKind::S_GTHREAD32:
// We really should not be seeing S_PROCREF and S_LPROCREF in the first place
// since they are synthesized by the linker in response to S_GPROC32 and
// S_LPROC32, but if we do see them, don't put them in the module stream I
@@ -807,17 +442,18 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) {
return !isGlobalScope;
// S_GDATA32 does not go in the module stream, but S_LDATA32 does.
case SymbolKind::S_LDATA32:
+ case SymbolKind::S_LTHREAD32:
default:
return true;
}
}
-static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) {
+static bool symbolGoesInGlobalsStream(const CVSymbol &sym,
+ bool isFunctionScope) {
switch (sym.kind()) {
case SymbolKind::S_CONSTANT:
case SymbolKind::S_GDATA32:
- // S_LDATA32 goes in both the module stream and the globals stream.
- case SymbolKind::S_LDATA32:
+ case SymbolKind::S_GTHREAD32:
case SymbolKind::S_GPROC32:
case SymbolKind::S_LPROC32:
// We really should not be seeing S_PROCREF and S_LPROCREF in the first place
@@ -826,9 +462,11 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) {
case SymbolKind::S_PROCREF:
case SymbolKind::S_LPROCREF:
return true;
- // S_UDT records go in the globals stream if it is a global S_UDT.
+ // Records that go in the globals stream, unless they are function-local.
case SymbolKind::S_UDT:
- return isGlobalScope;
+ case SymbolKind::S_LDATA32:
+ case SymbolKind::S_LTHREAD32:
+ return !isFunctionScope;
default:
return false;
}
@@ -840,6 +478,8 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex,
case SymbolKind::S_CONSTANT:
case SymbolKind::S_UDT:
case SymbolKind::S_GDATA32:
+ case SymbolKind::S_GTHREAD32:
+ case SymbolKind::S_LTHREAD32:
case SymbolKind::S_LDATA32:
case SymbolKind::S_PROCREF:
case SymbolKind::S_LPROCREF:
@@ -899,7 +539,7 @@ void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
MutableArrayRef<uint8_t> alignedSymbolMem;
if (needsRealignment) {
void *alignedData =
- alloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb));
+ bAlloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb));
alignedSymbolMem = makeMutableArrayRef(
reinterpret_cast<uint8_t *>(alignedData), totalRealignedSize);
}
@@ -953,7 +593,7 @@ void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
// adding the symbol to the module since we may need to get the next
// symbol offset, and writing to the module's symbol stream will update
// that offset.
- if (symbolGoesInGlobalsStream(sym, scopes.empty())) {
+ if (symbolGoesInGlobalsStream(sym, !scopes.empty())) {
addGlobalSymbol(builder.getGsiBuilder(),
file->moduleDBI->getModuleIndex(), curSymOffset, sym);
++globalSymbols;
@@ -980,16 +620,6 @@ void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
file->moduleDBI->addSymbolsInBulk(bulkSymbols);
}
-// Allocate memory for a .debug$S / .debug$F section and relocate it.
-static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &alloc,
- SectionChunk &debugChunk) {
- uint8_t *buffer = alloc.Allocate<uint8_t>(debugChunk.getSize());
- assert(debugChunk.getOutputSectionIdx() == 0 &&
- "debug sections should not be in output sections");
- debugChunk.writeTo(buffer);
- return makeArrayRef(buffer, debugChunk.getSize());
-}
-
static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) {
OutputSection *os = c ? c->getOutputSection() : nullptr;
pdb::SectionContrib sc;
@@ -1027,15 +657,19 @@ translateStringTableIndex(uint32_t objIndex,
return pdbStrTable.insert(*expectedString);
}
-void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) {
- DebugSubsectionArray subsections;
-
- ArrayRef<uint8_t> relocatedDebugContents = SectionChunk::consumeDebugMagic(
- relocateDebugChunk(linker.alloc, debugS), debugS.getSectionName());
+void DebugSHandler::handleDebugS(ArrayRef<uint8_t> relocatedDebugContents) {
+ relocatedDebugContents =
+ SectionChunk::consumeDebugMagic(relocatedDebugContents, ".debug$S");
+ DebugSubsectionArray subsections;
BinaryStreamReader reader(relocatedDebugContents, support::little);
exitOnErr(reader.readArray(subsections, relocatedDebugContents.size()));
+ // If there is no index map, use an empty one.
+ CVIndexMap tempIndexMap;
+ if (!indexMap)
+ indexMap = &tempIndexMap;
+
for (const DebugSubsectionRecord &ss : subsections) {
// Ignore subsections with the 'ignore' bit. Some versions of the Visual C++
// runtime have subsections with this bit set.
@@ -1044,9 +678,9 @@ void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) {
switch (ss.kind()) {
case DebugSubsectionKind::StringTable: {
- assert(!cVStrTab.valid() &&
+ assert(!cvStrTab.valid() &&
"Encountered multiple string table subsections!");
- exitOnErr(cVStrTab.initialize(ss.getRecordData()));
+ exitOnErr(cvStrTab.initialize(ss.getRecordData()));
break;
}
case DebugSubsectionKind::FileChecksums:
@@ -1060,9 +694,10 @@ void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) {
file.moduleDBI->addDebugSubsection(ss);
break;
case DebugSubsectionKind::InlineeLines:
- assert(!inlineeLines.valid() &&
- "Encountered multiple inlinee lines subsections!");
- exitOnErr(inlineeLines.initialize(ss.getRecordData()));
+ // The inlinee lines subsection also has file checksum table references
+ // that can be used directly, but it contains function id references that
+ // must be remapped.
+ mergeInlineeLines(ss);
break;
case DebugSubsectionKind::FrameData: {
// We need to re-write string table indices here, so save off all
@@ -1074,7 +709,7 @@ void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) {
break;
}
case DebugSubsectionKind::Symbols: {
- linker.mergeSymbolRecords(&file, indexMap, stringTableReferences,
+ linker.mergeSymbolRecords(&file, *indexMap, stringTableReferences,
ss.getRecordData());
break;
}
@@ -1114,39 +749,24 @@ getFileName(const DebugStringTableSubsectionRef &strings,
return strings.getString(offset);
}
-std::shared_ptr<DebugInlineeLinesSubsection>
-DebugSHandler::mergeInlineeLines(DebugChecksumsSubsection *newChecksums) {
- auto newInlineeLines = std::make_shared<DebugInlineeLinesSubsection>(
- *newChecksums, inlineeLines.hasExtraFiles());
+void DebugSHandler::mergeInlineeLines(
+ const DebugSubsectionRecord &inlineeSubsection) {
+ DebugInlineeLinesSubsectionRef inlineeLines;
+ exitOnErr(inlineeLines.initialize(inlineeSubsection.getRecordData()));
+ // Remap type indices in inlinee line records in place.
for (const InlineeSourceLine &line : inlineeLines) {
- TypeIndex inlinee = line.Header->Inlinee;
- uint32_t fileID = line.Header->FileID;
- uint32_t sourceLine = line.Header->SourceLineNum;
-
+ TypeIndex &inlinee = *const_cast<TypeIndex *>(&line.Header->Inlinee);
ArrayRef<TypeIndex> typeOrItemMap =
- indexMap.isTypeServerMap ? indexMap.ipiMap : indexMap.tpiMap;
+ indexMap->isTypeServerMap ? indexMap->ipiMap : indexMap->tpiMap;
if (!remapTypeIndex(inlinee, typeOrItemMap)) {
- log("ignoring inlinee line record in " + file.getName() +
+ log("bad inlinee line record in " + file.getName() +
" with bad inlinee index 0x" + utohexstr(inlinee.getIndex()));
- continue;
- }
-
- SmallString<128> filename =
- exitOnErr(getFileName(cVStrTab, checksums, fileID));
- pdbMakeAbsolute(filename);
- newInlineeLines->addInlineSite(inlinee, filename, sourceLine);
-
- if (inlineeLines.hasExtraFiles()) {
- for (uint32_t extraFileId : line.ExtraFiles) {
- filename = exitOnErr(getFileName(cVStrTab, checksums, extraFileId));
- pdbMakeAbsolute(filename);
- newInlineeLines->addExtraFile(filename);
- }
}
}
- return newInlineeLines;
+ // Add the modified inlinee line subsection directly.
+ file.moduleDBI->addDebugSubsection(inlineeSubsection);
}
void DebugSHandler::finish() {
@@ -1155,7 +775,7 @@ void DebugSHandler::finish() {
// We should have seen all debug subsections across the entire object file now
// which means that if a StringTable subsection and Checksums subsection were
// present, now is the time to handle them.
- if (!cVStrTab.valid()) {
+ if (!cvStrTab.valid()) {
if (checksums.valid())
fatal(".debug$S sections with a checksums subsection must also contain a "
"string table subsection");
@@ -1173,77 +793,92 @@ void DebugSHandler::finish() {
for (codeview::FrameData fd : fds) {
fd.RvaStart += *reloc;
fd.FrameFunc =
- translateStringTableIndex(fd.FrameFunc, cVStrTab, linker.pdbStrTab);
+ translateStringTableIndex(fd.FrameFunc, cvStrTab, linker.pdbStrTab);
dbiBuilder.addNewFpoData(fd);
}
}
for (ulittle32_t *ref : stringTableReferences)
- *ref = translateStringTableIndex(*ref, cVStrTab, linker.pdbStrTab);
+ *ref = translateStringTableIndex(*ref, cvStrTab, linker.pdbStrTab);
// Make a new file checksum table that refers to offsets in the PDB-wide
// string table. Generally the string table subsection appears after the
// checksum table, so we have to do this after looping over all the
- // subsections.
+ // subsections. The new checksum table must have the exact same layout and
+ // size as the original. Otherwise, the file references in the line and
+ // inlinee line tables will be incorrect.
auto newChecksums = std::make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
for (FileChecksumEntry &fc : checksums) {
SmallString<128> filename =
- exitOnErr(cVStrTab.getString(fc.FileNameOffset));
+ exitOnErr(cvStrTab.getString(fc.FileNameOffset));
pdbMakeAbsolute(filename);
exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename));
newChecksums->addChecksum(filename, fc.Kind, fc.Checksum);
}
-
- // Rewrite inlinee item indices if present.
- if (inlineeLines.valid())
- file.moduleDBI->addDebugSubsection(mergeInlineeLines(newChecksums.get()));
+ assert(checksums.getArray().getUnderlyingStream().getLength() ==
+ newChecksums->calculateSerializedSize() &&
+ "file checksum table must have same layout");
file.moduleDBI->addDebugSubsection(std::move(newChecksums));
}
-void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) {
- if (file->mergedIntoPDB)
+static void warnUnusable(InputFile *f, Error e) {
+ if (!config->warnDebugInfoUnusable) {
+ consumeError(std::move(e));
return;
- file->mergedIntoPDB = true;
+ }
+ auto msg = "Cannot use debug info for '" + toString(f) + "' [LNK4099]";
+ if (e)
+ warn(msg + "\n>>> failed to load reference " + toString(std::move(e)));
+ else
+ warn(msg);
+}
+const CVIndexMap *PDBLinker::mergeTypeRecords(TpiSource *source,
+ CVIndexMap *localMap) {
+ ScopedTimer t(typeMergingTimer);
// Before we can process symbol substreams from .debug$S, we need to process
// type information, file checksums, and the string table. Add type info to
// the PDB first, so that we can get the map from object file type and item
// indices to PDB type and item indices.
- CVIndexMap objectIndexMap;
- auto indexMapResult =
- mergeDebugT(file, externIndexMap ? externIndexMap : &objectIndexMap);
+ Expected<const CVIndexMap *> r = source->mergeDebugT(&tMerger, localMap);
// If the .debug$T sections fail to merge, assume there is no debug info.
- if (!indexMapResult) {
- if (!config->warnDebugInfoUnusable) {
- consumeError(indexMapResult.takeError());
- return;
- }
- warn("Cannot use debug info for '" + toString(file) + "' [LNK4099]\n" +
- ">>> failed to load reference " +
- StringRef(toString(indexMapResult.takeError())));
- return;
+ if (!r) {
+ warnUnusable(source->file, r.takeError());
+ return nullptr;
}
+ return *r;
+}
- ScopedTimer t(symbolMergingTimer);
+// Allocate memory for a .debug$S / .debug$F section and relocate it.
+static ArrayRef<uint8_t> relocateDebugChunk(SectionChunk &debugChunk) {
+ uint8_t *buffer = bAlloc.Allocate<uint8_t>(debugChunk.getSize());
+ assert(debugChunk.getOutputSectionIdx() == 0 &&
+ "debug sections should not be in output sections");
+ debugChunk.writeTo(buffer);
+ return makeArrayRef(buffer, debugChunk.getSize());
+}
+void PDBLinker::addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap) {
+ ScopedTimer t(symbolMergingTimer);
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
- DebugSHandler dsh(*this, *file, *indexMapResult);
+ DebugSHandler dsh(*this, *file, indexMap);
// Now do all live .debug$S and .debug$F sections.
for (SectionChunk *debugChunk : file->getDebugChunks()) {
if (!debugChunk->live || debugChunk->getSize() == 0)
continue;
- if (debugChunk->getSectionName() == ".debug$S") {
- dsh.handleDebugS(*debugChunk);
+ bool isDebugS = debugChunk->getSectionName() == ".debug$S";
+ bool isDebugF = debugChunk->getSectionName() == ".debug$F";
+ if (!isDebugS && !isDebugF)
continue;
- }
- if (debugChunk->getSectionName() == ".debug$F") {
- ArrayRef<uint8_t> relocatedDebugContents =
- relocateDebugChunk(alloc, *debugChunk);
+ ArrayRef<uint8_t> relocatedDebugContents = relocateDebugChunk(*debugChunk);
+ if (isDebugS) {
+ dsh.handleDebugS(relocatedDebugContents);
+ } else if (isDebugF) {
FixedStreamArray<object::FpoData> fpoRecords;
BinaryStreamReader reader(relocatedDebugContents, support::little);
uint32_t count = relocatedDebugContents.size() / sizeof(object::FpoData);
@@ -1253,7 +888,6 @@ void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) {
// can just copy it.
for (const object::FpoData &fd : fpoRecords)
dbiBuilder.addOldFpoData(fd);
- continue;
}
}
@@ -1265,43 +899,54 @@ void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) {
// path to the object into the PDB. If this is a plain object, we make its
// path absolute. If it's an object in an archive, we make the archive path
// absolute.
-static void createModuleDBI(pdb::PDBFileBuilder &builder) {
+static void createModuleDBI(pdb::PDBFileBuilder &builder, ObjFile *file) {
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
SmallString<128> objName;
- for (ObjFile *file : ObjFile::instances) {
+ bool inArchive = !file->parentName.empty();
+ objName = inArchive ? file->parentName : file->getName();
+ pdbMakeAbsolute(objName);
+ StringRef modName = inArchive ? file->getName() : StringRef(objName);
- bool inArchive = !file->parentName.empty();
- objName = inArchive ? file->parentName : file->getName();
- pdbMakeAbsolute(objName);
- StringRef modName = inArchive ? file->getName() : StringRef(objName);
+ file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName));
+ file->moduleDBI->setObjFileName(objName);
- file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName));
- file->moduleDBI->setObjFileName(objName);
+ ArrayRef<Chunk *> chunks = file->getChunks();
+ uint32_t modi = file->moduleDBI->getModuleIndex();
- ArrayRef<Chunk *> chunks = file->getChunks();
- uint32_t modi = file->moduleDBI->getModuleIndex();
-
- for (Chunk *c : chunks) {
- auto *secChunk = dyn_cast<SectionChunk>(c);
- if (!secChunk || !secChunk->live)
- continue;
- pdb::SectionContrib sc = createSectionContrib(secChunk, modi);
- file->moduleDBI->setFirstSectionContrib(sc);
- break;
- }
+ for (Chunk *c : chunks) {
+ auto *secChunk = dyn_cast<SectionChunk>(c);
+ if (!secChunk || !secChunk->live)
+ continue;
+ pdb::SectionContrib sc = createSectionContrib(secChunk, modi);
+ file->moduleDBI->setFirstSectionContrib(sc);
+ break;
}
}
-static PublicSym32 createPublic(Defined *def) {
- PublicSym32 pub(SymbolKind::S_PUB32);
- pub.Name = def->getName();
+void PDBLinker::addDebug(TpiSource *source) {
+ CVIndexMap localMap;
+ const CVIndexMap *indexMap = mergeTypeRecords(source, &localMap);
+
+ if (source->kind == TpiSource::PDB)
+ return; // No symbols in TypeServer PDBs
+
+ addDebugSymbols(source->file, indexMap);
+}
+
+static pdb::BulkPublic createPublic(Defined *def) {
+ pdb::BulkPublic pub;
+ pub.Name = def->getName().data();
+ pub.NameLen = def->getName().size();
+
+ PublicSymFlags flags = PublicSymFlags::None;
if (auto *d = dyn_cast<DefinedCOFF>(def)) {
if (d->getCOFFSymbol().isFunctionDefinition())
- pub.Flags = PublicSymFlags::Function;
+ flags = PublicSymFlags::Function;
} else if (isa<DefinedImportThunk>(def)) {
- pub.Flags = PublicSymFlags::Function;
+ flags = PublicSymFlags::Function;
}
+ pub.setFlags(flags);
OutputSection *os = def->getChunk()->getOutputSection();
assert(os && "all publics should be in final image");
@@ -1315,10 +960,30 @@ static PublicSym32 createPublic(Defined *def) {
void PDBLinker::addObjectsToPDB() {
ScopedTimer t1(addObjectsTimer);
- createModuleDBI(builder);
+ // Create module descriptors
+ for_each(ObjFile::instances,
+ [&](ObjFile *obj) { createModuleDBI(builder, obj); });
- for (ObjFile *file : ObjFile::instances)
- addObjFile(file);
+ // Merge OBJs that do not have debug types
+ for_each(ObjFile::instances, [&](ObjFile *obj) {
+ if (obj->debugTypesObj)
+ return;
+ // Even if there're no types, still merge non-symbol .Debug$S and .Debug$F
+ // sections
+ addDebugSymbols(obj, nullptr);
+ });
+
+ // Merge dependencies
+ TpiSource::forEachSource([&](TpiSource *source) {
+ if (source->isDependency())
+ addDebug(source);
+ });
+
+ // Merge regular and dependent OBJs
+ TpiSource::forEachSource([&](TpiSource *source) {
+ if (!source->isDependency())
+ addDebug(source);
+ });
builder.getStringTableBuilder().setStrings(pdbStrTab);
t1.stop();
@@ -1328,13 +993,16 @@ void PDBLinker::addObjectsToPDB() {
addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable());
addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable());
t2.stop();
+}
- ScopedTimer t3(globalsLayoutTimer);
- // Compute the public and global symbols.
+void PDBLinker::addPublicsToPDB() {
+ ScopedTimer t3(publicsLayoutTimer);
+ // Compute the public symbols.
auto &gsiBuilder = builder.getGsiBuilder();
- std::vector<PublicSym32> publics;
+ std::vector<pdb::BulkPublic> publics;
symtab->forEachSymbol([&publics](Symbol *s) {
- // Only emit defined, live symbols that have a chunk.
+ // Only emit external, defined, live symbols that have a chunk. Static,
+ // non-external symbols do not appear in the symbol table.
auto *def = dyn_cast<Defined>(s);
if (def && def->isLive() && def->getChunk())
publics.push_back(createPublic(def));
@@ -1342,12 +1010,7 @@ void PDBLinker::addObjectsToPDB() {
if (!publics.empty()) {
publicSymbols = publics.size();
- // Sort the public symbols and add them to the stream.
- parallelSort(publics, [](const PublicSym32 &l, const PublicSym32 &r) {
- return l.Name < r.Name;
- });
- for (const PublicSym32 &pub : publics)
- gsiBuilder.addPublicSymbol(pub);
+ gsiBuilder.addPublicSymbols(std::move(publics));
}
}
@@ -1367,8 +1030,8 @@ void PDBLinker::printStats() {
print(ObjFile::instances.size(),
"Input OBJ files (expanded from all cmd-line inputs)");
- print(typeServerIndexMappings.size(), "PDB type server dependencies");
- print(precompTypeIndexMappings.size(), "Precomp OBJ dependencies");
+ print(TpiSource::countTypeServerPDBs(), "PDB type server dependencies");
+ print(TpiSource::countPrecompObjs(), "Precomp OBJ dependencies");
print(tMerger.getTypeTable().size() + tMerger.getIDTable().size(),
"Merged TPI records");
print(pdbStrTab.size(), "Output PDB strings");
@@ -1388,6 +1051,8 @@ void PDBLinker::printStats() {
TypeIndex typeIndex;
uint64_t totalInputSize() const { return uint64_t(dupCount) * typeSize; }
bool operator<(const TypeSizeInfo &rhs) const {
+ if (totalInputSize() == rhs.totalInputSize())
+ return typeIndex < rhs.typeIndex;
return totalInputSize() < rhs.totalInputSize();
}
};
@@ -1420,8 +1085,8 @@ void PDBLinker::printStats() {
}
};
- printLargeInputTypeRecs("TPI", tpiCounts, tMerger.getTypeTable());
- printLargeInputTypeRecs("IPI", ipiCounts, tMerger.getIDTable());
+ printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable());
+ printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable());
message(buffer);
}
@@ -1438,6 +1103,19 @@ void PDBLinker::addNatvisFiles() {
}
}
+void PDBLinker::addNamedStreams() {
+ for (const auto &streamFile : config->namedStreams) {
+ const StringRef stream = streamFile.getKey(), file = streamFile.getValue();
+ ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr =
+ MemoryBuffer::getFile(file);
+ if (!dataOrErr) {
+ warn("Cannot open input file: " + file);
+ continue;
+ }
+ exitOnErr(builder.addNamedStream(stream, (*dataOrErr)->getBuffer()));
+ }
+}
+
static codeview::CPUType toCodeViewMachine(COFF::MachineTypes machine) {
switch (machine) {
case COFF::IMAGE_FILE_MACHINE_AMD64:
@@ -1473,7 +1151,7 @@ static std::string quote(ArrayRef<StringRef> args) {
a.split(s, '"');
r.append(join(s, "\"\""));
} else {
- r.append(a);
+ r.append(std::string(a));
}
if (hasWS || hasQ)
r.push_back('"');
@@ -1508,8 +1186,7 @@ static void fillLinkerVerRecord(Compile3Sym &cs) {
}
static void addCommonLinkerModuleSymbols(StringRef path,
- pdb::DbiModuleDescriptorBuilder &mod,
- BumpPtrAllocator &allocator) {
+ pdb::DbiModuleDescriptorBuilder &mod) {
ObjNameSym ons(SymbolRecordKind::ObjNameSym);
EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym);
Compile3Sym cs(SymbolRecordKind::Compile3Sym);
@@ -1536,17 +1213,16 @@ static void addCommonLinkerModuleSymbols(StringRef path,
ebs.Fields.push_back("cmd");
ebs.Fields.push_back(argStr);
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- ons, allocator, CodeViewContainer::Pdb));
+ ons, bAlloc, CodeViewContainer::Pdb));
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- cs, allocator, CodeViewContainer::Pdb));
+ cs, bAlloc, CodeViewContainer::Pdb));
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- ebs, allocator, CodeViewContainer::Pdb));
+ ebs, bAlloc, CodeViewContainer::Pdb));
}
static void addLinkerModuleCoffGroup(PartialSection *sec,
pdb::DbiModuleDescriptorBuilder &mod,
- OutputSection &os,
- BumpPtrAllocator &allocator) {
+ OutputSection &os) {
// If there's a section, there's at least one chunk
assert(!sec->chunks.empty());
const Chunk *firstChunk = *sec->chunks.begin();
@@ -1567,12 +1243,11 @@ static void addLinkerModuleCoffGroup(PartialSection *sec,
cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE;
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- cgs, allocator, CodeViewContainer::Pdb));
+ cgs, bAlloc, CodeViewContainer::Pdb));
}
static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
- OutputSection &os,
- BumpPtrAllocator &allocator) {
+ OutputSection &os) {
SectionSym sym(SymbolRecordKind::SectionSym);
sym.Alignment = 12; // 2^12 = 4KB
sym.Characteristics = os.header.Characteristics;
@@ -1581,7 +1256,7 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
sym.Rva = os.getRVA();
sym.SectionNumber = os.sectionIndex;
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- sym, allocator, CodeViewContainer::Pdb));
+ sym, bAlloc, CodeViewContainer::Pdb));
// Skip COFF groups in MinGW because it adds a significant footprint to the
// PDB, due to each function being in its own section
@@ -1590,7 +1265,7 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
// Output COFF groups for individual chunks of this section.
for (PartialSection *sec : os.contribSections) {
- addLinkerModuleCoffGroup(sec, mod, os, allocator);
+ addLinkerModuleCoffGroup(sec, mod, os);
}
}
@@ -1657,18 +1332,18 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA();
mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- ons, alloc, CodeViewContainer::Pdb));
+ ons, bAlloc, CodeViewContainer::Pdb));
mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- cs, alloc, CodeViewContainer::Pdb));
+ cs, bAlloc, CodeViewContainer::Pdb));
SmallVector<SymbolScope, 4> scopes;
CVSymbol newSym = codeview::SymbolSerializer::writeOneSymbol(
- ts, alloc, CodeViewContainer::Pdb);
+ ts, bAlloc, CodeViewContainer::Pdb);
scopeStackOpen(scopes, mod->getNextSymbolOffset(), newSym);
mod->addSymbol(newSym);
- newSym = codeview::SymbolSerializer::writeOneSymbol(es, alloc,
+ newSym = codeview::SymbolSerializer::writeOneSymbol(es, bAlloc,
CodeViewContainer::Pdb);
scopeStackClose(scopes, mod->getNextSymbolOffset(), file);
@@ -1681,10 +1356,10 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
}
// Creates a PDB file.
-void createPDB(SymbolTable *symtab,
- ArrayRef<OutputSection *> outputSections,
- ArrayRef<uint8_t> sectionTable,
- llvm::codeview::DebugInfo *buildId) {
+void lld::coff::createPDB(SymbolTable *symtab,
+ ArrayRef<OutputSection *> outputSections,
+ ArrayRef<uint8_t> sectionTable,
+ llvm::codeview::DebugInfo *buildId) {
ScopedTimer t1(totalPdbLinkTimer);
PDBLinker pdb(symtab);
@@ -1693,6 +1368,8 @@ void createPDB(SymbolTable *symtab,
pdb.addImportFilesToPDB(outputSections);
pdb.addSections(outputSections, sectionTable);
pdb.addNatvisFiles();
+ pdb.addNamedStreams();
+ pdb.addPublicsToPDB();
ScopedTimer t2(diskCommitTimer);
codeview::GUID guid;
@@ -1743,11 +1420,11 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath);
auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *"));
linkerModule.setPdbFilePathNI(pdbFilePathNI);
- addCommonLinkerModuleSymbols(nativePath, linkerModule, alloc);
+ addCommonLinkerModuleSymbols(nativePath, linkerModule);
// Add section contributions. They must be ordered by ascending RVA.
for (OutputSection *os : outputSections) {
- addLinkerModuleSectionSymbol(linkerModule, *os, alloc);
+ addLinkerModuleSectionSymbol(linkerModule, *os);
for (Chunk *c : os->chunks) {
pdb::SectionContrib sc =
createSectionContrib(c, linkerModule.getModuleIndex());
@@ -1766,8 +1443,7 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
ArrayRef<object::coff_section> sections = {
(const object::coff_section *)sectionTable.data(),
sectionTable.size() / sizeof(object::coff_section)};
- sectionMap = pdb::DbiStreamBuilder::createSectionMap(sections);
- dbiBuilder.setSectionMap(sectionMap);
+ dbiBuilder.createSectionMap(sections);
// Add COFF section header stream.
exitOnErr(
@@ -1801,7 +1477,7 @@ static uint32_t getSecrelReloc() {
// table are stored in the output arguments. Returns whether a line table was
// found.
static bool findLineTable(const SectionChunk *c, uint32_t addr,
- DebugStringTableSubsectionRef &cVStrTab,
+ DebugStringTableSubsectionRef &cvStrTab,
DebugChecksumsSubsectionRef &checksums,
DebugLinesSubsectionRef &lines,
uint32_t &offsetInLinetable) {
@@ -1833,9 +1509,9 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
for (const DebugSubsectionRecord &ss : subsections) {
switch (ss.kind()) {
case DebugSubsectionKind::StringTable: {
- assert(!cVStrTab.valid() &&
+ assert(!cvStrTab.valid() &&
"Encountered multiple string table subsections!");
- exitOnErr(cVStrTab.initialize(ss.getRecordData()));
+ exitOnErr(cvStrTab.initialize(ss.getRecordData()));
break;
}
case DebugSubsectionKind::FileChecksums:
@@ -1871,7 +1547,7 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
break;
}
- if (cVStrTab.valid() && checksums.valid() && lines.header())
+ if (cvStrTab.valid() && checksums.valid() && lines.header())
return true;
}
}
@@ -1883,15 +1559,15 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
// offset into the given chunk and return them, or None if a line table was
// not found.
Optional<std::pair<StringRef, uint32_t>>
-getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
+lld::coff::getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
ExitOnError exitOnErr;
- DebugStringTableSubsectionRef cVStrTab;
+ DebugStringTableSubsectionRef cvStrTab;
DebugChecksumsSubsectionRef checksums;
DebugLinesSubsectionRef lines;
uint32_t offsetInLinetable;
- if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable))
+ if (!findLineTable(c, addr, cvStrTab, checksums, lines, offsetInLinetable))
return None;
Optional<uint32_t> nameIndex;
@@ -1905,7 +1581,7 @@ getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
lineNumber = li.getStartLine();
}
StringRef filename =
- exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
+ exitOnErr(getFileName(cvStrTab, checksums, *nameIndex));
return std::make_pair(filename, *lineNumber);
}
nameIndex = entry.NameIndex;
@@ -1914,9 +1590,6 @@ getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
}
if (!nameIndex)
return None;
- StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
+ StringRef filename = exitOnErr(getFileName(cvStrTab, checksums, *nameIndex));
return std::make_pair(filename, *lineNumber);
}
-
-} // namespace coff
-} // namespace lld
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 7072f4d8d0e3..d4d2a159a639 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -204,7 +204,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
llvm::raw_string_ostream os(out);
os << "undefined symbol: " << toString(*undefDiag.sym);
- const size_t maxUndefReferences = 10;
+ const size_t maxUndefReferences = 3;
size_t i = 0, numRefs = 0;
for (const UndefinedDiag::File &ref : undefDiag.files) {
std::vector<std::string> symbolLocations =
@@ -438,7 +438,7 @@ void SymbolTable::resolveRemainingUndefines() {
if (name.contains("_PchSym_"))
continue;
- if (config->mingw && handleMinGWAutomaticImport(sym, name))
+ if (config->autoImport && handleMinGWAutomaticImport(sym, name))
continue;
// Remaining undefined symbols are not fatal if /force is specified.
@@ -789,20 +789,16 @@ Symbol *SymbolTable::addUndefined(StringRef name) {
return addUndefined(name, nullptr, false);
}
-std::vector<StringRef> SymbolTable::compileBitcodeFiles() {
- lto.reset(new BitcodeCompiler);
- for (BitcodeFile *f : BitcodeFile::instances)
- lto->add(*f);
- return lto->compile();
-}
-
void SymbolTable::addCombinedLTOObjects() {
if (BitcodeFile::instances.empty())
return;
ScopedTimer t(ltoTimer);
- for (StringRef object : compileBitcodeFiles()) {
- auto *obj = make<ObjFile>(MemoryBufferRef(object, "lto.tmp"));
+ lto.reset(new BitcodeCompiler);
+ for (BitcodeFile *f : BitcodeFile::instances)
+ lto->add(*f);
+ for (InputFile *newObj : lto->compile()) {
+ ObjFile *obj = cast<ObjFile>(newObj);
obj->parse();
ObjFile::instances.push_back(obj);
}
diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index cd8a53dcecdc..870a7151fa8e 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -77,7 +77,6 @@ public:
// BitcodeFiles and add them to the symbol table. Called after all files are
// added and before the writer writes results to a file.
void addCombinedLTOObjects();
- std::vector<StringRef> compileBitcodeFiles();
// Creates an Undefined symbol for a given name.
Symbol *addUndefined(StringRef name);
diff --git a/lld/COFF/Symbols.cpp b/lld/COFF/Symbols.cpp
index 938c9c527ffa..60ff72aeb522 100644
--- a/lld/COFF/Symbols.cpp
+++ b/lld/COFF/Symbols.cpp
@@ -36,12 +36,12 @@ static std::string maybeDemangleSymbol(StringRef symName) {
StringRef demangleInput = prefixless;
if (config->machine == I386)
demangleInput.consume_front("_");
- std::string demangled = demangle(demangleInput);
+ std::string demangled = demangle(std::string(demangleInput));
if (demangled != demangleInput)
- return prefix + demangle(demangleInput);
+ return prefix + demangle(std::string(demangleInput));
return (prefix + prefixless).str();
}
- return symName;
+ return std::string(symName);
}
std::string toString(coff::Symbol &b) {
return maybeDemangleSymbol(b.getName());
@@ -52,23 +52,15 @@ std::string toCOFFString(const Archive::Symbol &b) {
namespace coff {
-StringRef Symbol::getName() {
- // COFF symbol names are read lazily for a performance reason.
- // Non-external symbol names are never used by the linker except for logging
- // or debugging. Their internal references are resolved not by name but by
- // symbol index. And because they are not external, no one can refer them by
- // name. Object files contain lots of non-external symbols, and creating
- // StringRefs for them (which involves lots of strlen() on the string table)
- // is a waste of time.
- if (nameData == nullptr) {
- auto *d = cast<DefinedCOFF>(this);
- StringRef nameStr;
- cast<ObjFile>(d->file)->getCOFFObj()->getSymbolName(d->sym, nameStr);
- nameData = nameStr.data();
- nameSize = nameStr.size();
- assert(nameSize == nameStr.size() && "name length truncated");
- }
- return StringRef(nameData, nameSize);
+void Symbol::computeName() {
+ assert(nameData == nullptr &&
+ "should only compute the name once for DefinedCOFF symbols");
+ auto *d = cast<DefinedCOFF>(this);
+ StringRef nameStr =
+ check(cast<ObjFile>(d->file)->getCOFFObj()->getSymbolName(d->sym));
+ nameData = nameStr.data();
+ nameSize = nameStr.size();
+ assert(nameSize == nameStr.size() && "name length truncated");
}
InputFile *Symbol::getFile() {
diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h
index a8e70320b995..1da4df366966 100644
--- a/lld/COFF/Symbols.h
+++ b/lld/COFF/Symbols.h
@@ -69,7 +69,18 @@ public:
Kind kind() const { return static_cast<Kind>(symbolKind); }
// Returns the symbol name.
- StringRef getName();
+ StringRef getName() {
+ // COFF symbol names are read lazily for a performance reason.
+ // Non-external symbol names are never used by the linker except for logging
+ // or debugging. Their internal references are resolved not by name but by
+ // symbol index. And because they are not external, no one can refer them by
+ // name. Object files contain lots of non-external symbols, and creating
+ // StringRefs for them (which involves lots of strlen() on the string table)
+ // is a waste of time.
+ if (nameData == nullptr)
+ computeName();
+ return StringRef(nameData, nameSize);
+ }
void replaceKeepingName(Symbol *other, size_t size);
@@ -84,6 +95,9 @@ public:
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
}
+private:
+ void computeName();
+
protected:
friend SymbolTable;
explicit Symbol(Kind k, StringRef n = "")
diff --git a/lld/COFF/TypeMerger.h b/lld/COFF/TypeMerger.h
index e2cfe668cfa5..858f55b6856d 100644
--- a/lld/COFF/TypeMerger.h
+++ b/lld/COFF/TypeMerger.h
@@ -20,7 +20,7 @@ namespace coff {
class TypeMerger {
public:
TypeMerger(llvm::BumpPtrAllocator &alloc)
- : typeTable(alloc), iDTable(alloc), globalTypeTable(alloc),
+ : typeTable(alloc), idTable(alloc), globalTypeTable(alloc),
globalIDTable(alloc) {}
/// Get the type table or the global type table if /DEBUG:GHASH is enabled.
@@ -34,20 +34,25 @@ public:
inline llvm::codeview::TypeCollection &getIDTable() {
if (config->debugGHashes)
return globalIDTable;
- return iDTable;
+ return idTable;
}
/// Type records that will go into the PDB TPI stream.
llvm::codeview::MergingTypeTableBuilder typeTable;
/// Item records that will go into the PDB IPI stream.
- llvm::codeview::MergingTypeTableBuilder iDTable;
+ llvm::codeview::MergingTypeTableBuilder idTable;
/// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH)
llvm::codeview::GlobalTypeTableBuilder globalTypeTable;
/// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH)
llvm::codeview::GlobalTypeTableBuilder globalIDTable;
+
+ // When showSummary is enabled, these are histograms of TPI and IPI records
+ // keyed by type index.
+ SmallVector<uint32_t, 0> tpiCounts;
+ SmallVector<uint32_t, 0> ipiCounts;
};
/// Map from type index and item index in a type server PDB to the
@@ -62,4 +67,4 @@ struct CVIndexMap {
} // namespace coff
} // namespace lld
-#endif \ No newline at end of file
+#endif
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index fcad7739d300..3bcc1777f7ac 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -10,13 +10,13 @@
#include "Config.h"
#include "DLL.h"
#include "InputFiles.h"
+#include "LLDMapFile.h"
#include "MapFile.h"
#include "PDB.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
@@ -41,9 +41,8 @@ using namespace llvm::COFF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
/* To re-generate DOSProgram:
$ cat > /tmp/DOSProgram.asm
@@ -92,7 +91,8 @@ namespace {
class DebugDirectoryChunk : public NonSectionChunk {
public:
- DebugDirectoryChunk(const std::vector<Chunk *> &r, bool writeRepro)
+ DebugDirectoryChunk(const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
+ bool writeRepro)
: records(r), writeRepro(writeRepro) {}
size_t getSize() const override {
@@ -102,11 +102,11 @@ public:
void writeTo(uint8_t *b) const override {
auto *d = reinterpret_cast<debug_directory *>(b);
- for (const Chunk *record : records) {
- OutputSection *os = record->getOutputSection();
- uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA());
- fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(),
- record->getRVA(), offs);
+ for (const std::pair<COFF::DebugType, Chunk *>& record : records) {
+ Chunk *c = record.second;
+ OutputSection *os = c->getOutputSection();
+ uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());
+ fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);
++d;
}
@@ -141,7 +141,7 @@ private:
}
mutable std::vector<support::ulittle32_t *> timeDateStamps;
- const std::vector<Chunk *> &records;
+ const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
bool writeRepro;
};
@@ -166,6 +166,17 @@ public:
mutable codeview::DebugInfo *buildId = nullptr;
};
+class ExtendedDllCharacteristicsChunk : public NonSectionChunk {
+public:
+ ExtendedDllCharacteristicsChunk(uint32_t c) : characteristics(c) {}
+
+ size_t getSize() const override { return 4; }
+
+ void writeTo(uint8_t *buf) const override { write32le(buf, characteristics); }
+
+ uint32_t characteristics = 0;
+};
+
// PartialSection represents a group of chunks that contribute to an
// OutputSection. Collating a collection of PartialSections of same name and
// characteristics constitutes the OutputSection.
@@ -251,7 +262,7 @@ private:
bool setNoSEHCharacteristic = false;
DebugDirectoryChunk *debugDirectory = nullptr;
- std::vector<Chunk *> debugRecords;
+ std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
CVDebugRecordChunk *buildId = nullptr;
ArrayRef<uint8_t> sectionTable;
@@ -290,7 +301,7 @@ private:
static Timer codeLayoutTimer("Code Layout", Timer::root());
static Timer diskCommitTimer("Commit Output File", Timer::root());
-void writeResult() { Writer().run(); }
+void lld::coff::writeResult() { Writer().run(); }
void OutputSection::addChunk(Chunk *c) {
chunks.push_back(c);
@@ -622,6 +633,7 @@ void Writer::run() {
}
writeBuildId();
+ writeLLDMapFile(outputSections);
writeMapFile(outputSections);
if (errorCount())
@@ -921,8 +933,9 @@ void Writer::createMiscChunks() {
// Create Debug Information Chunks
OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
- if (config->debug || config->repro) {
+ if (config->debug || config->repro || config->cetCompat) {
debugDirectory = make<DebugDirectoryChunk>(debugRecords, config->repro);
+ debugDirectory->setAlignment(4);
debugInfoSec->addChunk(debugDirectory);
}
@@ -932,10 +945,20 @@ void Writer::createMiscChunks() {
// 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>();
- debugRecords.push_back(buildId);
+ debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId});
+ }
- for (Chunk *c : debugRecords)
- debugInfoSec->addChunk(c);
+ if (config->cetCompat) {
+ ExtendedDllCharacteristicsChunk *extendedDllChars =
+ make<ExtendedDllCharacteristicsChunk>(
+ IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT);
+ debugRecords.push_back(
+ {COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, extendedDllChars});
+ }
+
+ if (debugRecords.size() > 0) {
+ for (std::pair<COFF::DebugType, Chunk *> r : debugRecords)
+ debugInfoSec->addChunk(r.second);
}
// Create SEH table. x86-only.
@@ -946,11 +969,11 @@ void Writer::createMiscChunks() {
if (config->guardCF != GuardCFLevel::Off)
createGuardCFTables();
- if (config->mingw) {
+ if (config->autoImport)
createRuntimePseudoRelocs();
+ if (config->mingw)
insertCtorDtorSymbols();
- }
}
// Create .idata section for the DLL-imported symbol table.
@@ -1699,6 +1722,15 @@ void Writer::createRuntimePseudoRelocs() {
sc->getRuntimePseudoRelocs(rels);
}
+ if (!config->pseudoRelocs) {
+ // Not writing any pseudo relocs; if some were needed, error out and
+ // indicate what required them.
+ for (const RuntimePseudoReloc &rpr : rels)
+ error("automatic dllimport of " + rpr.sym->getName() + " in " +
+ toString(rpr.target->file) + " requires pseudo relocations");
+ return;
+ }
+
if (!rels.empty())
log("Writing " + Twine(rels.size()) + " runtime pseudo relocations");
PseudoRelocTableChunk *table = make<PseudoRelocTableChunk>(rels);
@@ -1832,6 +1864,10 @@ void Writer::sortExceptionTable() {
uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize();
if (config->machine == AMD64) {
struct Entry { ulittle32_t begin, end, unwind; };
+ if ((end - begin) % sizeof(Entry) != 0) {
+ fatal("unexpected .pdata size: " + Twine(end - begin) +
+ " is not a multiple of " + Twine(sizeof(Entry)));
+ }
parallelSort(
MutableArrayRef<Entry>((Entry *)begin, (Entry *)end),
[](const Entry &a, const Entry &b) { return a.begin < b.begin; });
@@ -1839,6 +1875,10 @@ void Writer::sortExceptionTable() {
}
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) +
+ " is not a multiple of " + Twine(sizeof(Entry)));
+ }
parallelSort(
MutableArrayRef<Entry>((Entry *)begin, (Entry *)end),
[](const Entry &a, const Entry &b) { return a.begin < b.begin; });
@@ -1950,6 +1990,3 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
return it->second;
return nullptr;
}
-
-} // namespace coff
-} // namespace lld