summaryrefslogtreecommitdiff
path: root/lld/COFF/PDB.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lld/COFF/PDB.cpp')
-rw-r--r--lld/COFF/PDB.cpp785
1 files changed, 229 insertions, 556 deletions
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