diff --git a/lld/COFF/DebugTypes.h b/lld/COFF/DebugTypes.h --- a/lld/COFF/DebugTypes.h +++ b/lld/COFF/DebugTypes.h @@ -9,6 +9,8 @@ #ifndef LLD_COFF_DEBUGTYPES_H #define LLD_COFF_DEBUGTYPES_H +#include "lld/Common/LLVM.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" @@ -25,14 +27,15 @@ namespace lld { namespace coff { +using llvm::codeview::TypeIndex; + class ObjFile; class PDBInputFile; -struct CVIndexMap; class TypeMerger; class TpiSource { public: - enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB }; + enum TpiKind { Regular, PCH, UsingPCH, PDB, PDBIpi, UsingPDB }; TpiSource(TpiKind k, ObjFile *f); virtual ~TpiSource(); @@ -48,8 +51,8 @@ /// 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 mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap); + virtual Error mergeDebugT(TypeMerger *m); + /// Is this a dependent file that needs to be processed first, before other /// OBJs? virtual bool isDependency() const { return false; } @@ -64,6 +67,15 @@ const TpiKind kind; ObjFile *file; + + // Storage for tpiMap or ipiMap, depending on the kind of source. + llvm::SmallVector indexMapStorage; + + // Source type index to PDB type index mapping for type and item records. + // These mappings will be the same for /Z7 objects, and distinct for /Zi + // objects. + llvm::ArrayRef tpiMap; + llvm::ArrayRef ipiMap; }; TpiSource *makeTpiSource(ObjFile *file); diff --git a/lld/COFF/DebugTypes.cpp b/lld/COFF/DebugTypes.cpp --- a/lld/COFF/DebugTypes.cpp +++ b/lld/COFF/DebugTypes.cpp @@ -29,6 +29,8 @@ using namespace lld::coff; namespace { +class TypeServerIpiSource; + // The TypeServerSource class represents a PDB type server, a file referenced by // OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ // files, therefore there must be only once instance per OBJ lot. The file path @@ -49,20 +51,35 @@ auto it = mappings.emplace(expectedInfo->getGuid(), this); assert(it.second); (void)it; - tsIndexMap.isTypeServerMap = true; } - Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; + Error mergeDebugT(TypeMerger *m) override; bool isDependency() const override { return true; } PDBInputFile *pdbInputFile = nullptr; - CVIndexMap tsIndexMap; + // TpiSource for IPI stream. + TypeServerIpiSource *ipiSrc = nullptr; static std::map mappings; }; +// Companion to TypeServerSource. Stores the index map for the IPI stream in the +// PDB. Modeling PDBs with two sources for TPI and IPI helps establish the +// invariant of one type index space per source. +class TypeServerIpiSource : public TpiSource { +public: + explicit TypeServerIpiSource() : TpiSource(PDBIpi, nullptr) {} + + friend class TypeServerSource; + + // IPI merging is handled in TypeServerSource::mergeDebugT, since it depends + // directly on type merging. + Error mergeDebugT(TypeMerger *m) override { return Error::success(); } + + bool isDependency() const override { return true; } +}; + // This class represents the debug type stream of an OBJ file that depends on a // PDB type server (see TypeServerSource). class UseTypeServerSource : public TpiSource { @@ -70,8 +87,7 @@ UseTypeServerSource(ObjFile *f, TypeServer2Record ts) : TpiSource(UsingPDB, f), typeServerDependency(ts) {} - Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; + Error mergeDebugT(TypeMerger *m) override; // Information about the PDB type server dependency, that needs to be loaded // in before merging this OBJ. @@ -92,15 +108,10 @@ 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 mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; bool isDependency() const override { return true; } - CVIndexMap precompIndexMap; - static std::map mappings; }; @@ -111,8 +122,7 @@ UsePrecompSource(ObjFile *f, PrecompRecord precomp) : TpiSource(UsingPCH, f), precompDependency(precomp) {} - Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; + Error mergeDebugT(TypeMerger *m) override; // Information about the Precomp OBJ dependency, that needs to be loaded in // before merging this OBJ. @@ -134,7 +144,11 @@ } TpiSource *lld::coff::makeTypeServerSource(PDBInputFile *pdbInputFile) { - return make(pdbInputFile); + // Type server sources come in pairs: the TPI stream, and the IPI stream. + auto *tpiSource = make(pdbInputFile); + if (pdbInputFile->session->getPDBFile().hasPDBIpiStream()) + tpiSource->ipiSrc = make(); + return tpiSource; } TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file, @@ -196,8 +210,7 @@ } // Merge .debug$T for a generic object file. -Expected TpiSource::mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) { +Error TpiSource::mergeDebugT(TypeMerger *m) { CVTypeArray types; BinaryStreamReader reader(file->debugTypes, support::little); cantFail(reader.readArray(types, reader.getLength())); @@ -213,18 +226,22 @@ } if (auto err = mergeTypeAndIdRecords(m->globalIDTable, m->globalTypeTable, - indexMap->tpiMap, types, hashes, + indexMapStorage, types, hashes, file->pchSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(err))); } else { if (auto err = - mergeTypeAndIdRecords(m->idTable, m->typeTable, indexMap->tpiMap, + mergeTypeAndIdRecords(m->idTable, m->typeTable, indexMapStorage, types, file->pchSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(err))); } + // In an object, there is only one mapping for both types and items. + tpiMap = indexMapStorage; + ipiMap = indexMapStorage; + 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 @@ -234,7 +251,7 @@ m->ipiCounts.resize(m->getIDTable().size()); uint32_t srcIdx = 0; for (CVType &ty : types) { - TypeIndex dstIdx = indexMap->tpiMap[srcIdx++]; + TypeIndex dstIdx = 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()) @@ -245,12 +262,11 @@ } } - return indexMap; + return Error::success(); } // Merge types from a type server PDB. -Expected TypeServerSource::mergeDebugT(TypeMerger *m, - CVIndexMap *) { +Error TypeServerSource::mergeDebugT(TypeMerger *m) { pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile(); Expected expectedTpi = pdbFile.getPDBTpiStream(); if (auto e = expectedTpi.takeError()) @@ -273,30 +289,34 @@ Optional endPrecomp; // Merge TPI first, because the IPI stream will reference type indices. if (auto err = - mergeTypeRecords(m->globalTypeTable, tsIndexMap.tpiMap, + mergeTypeRecords(m->globalTypeTable, indexMapStorage, expectedTpi->typeArray(), tpiHashes, endPrecomp)) fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); + tpiMap = indexMapStorage; // Merge IPI. if (maybeIpi) { auto ipiHashes = GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes); - if (auto err = mergeIdRecords(m->globalIDTable, tsIndexMap.tpiMap, - tsIndexMap.ipiMap, maybeIpi->typeArray(), - ipiHashes)) + if (auto err = + mergeIdRecords(m->globalIDTable, tpiMap, ipiSrc->indexMapStorage, + maybeIpi->typeArray(), ipiHashes)) fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); + ipiMap = ipiSrc->indexMapStorage; } } else { // Merge TPI first, because the IPI stream will reference type indices. - if (auto err = mergeTypeRecords(m->typeTable, tsIndexMap.tpiMap, + if (auto err = mergeTypeRecords(m->typeTable, indexMapStorage, expectedTpi->typeArray())) fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); + tpiMap = indexMapStorage; // Merge IPI. if (maybeIpi) { - if (auto err = mergeIdRecords(m->idTable, tsIndexMap.tpiMap, - tsIndexMap.ipiMap, maybeIpi->typeArray())) + if (auto err = mergeIdRecords(m->idTable, tpiMap, ipiSrc->indexMapStorage, + maybeIpi->typeArray())) fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); + ipiMap = ipiSrc->indexMapStorage; } } @@ -306,19 +326,18 @@ // 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) + for (TypeIndex ti : tpiMap) if (!ti.isSimple()) ++m->tpiCounts[ti.toArrayIndex()]; - for (TypeIndex ti : tsIndexMap.ipiMap) + for (TypeIndex ti : ipiMap) if (!ti.isSimple()) ++m->ipiCounts[ti.toArrayIndex()]; } - return &tsIndexMap; + return Error::success(); } -Expected -UseTypeServerSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) { +Error UseTypeServerSource::mergeDebugT(TypeMerger *m) { const codeview::GUID &tsId = typeServerDependency.getGuid(); StringRef tsPath = typeServerDependency.getName(); @@ -342,7 +361,7 @@ pdb::PDBFile &pdbSession = tsSrc->pdbInputFile->session->getPDBFile(); auto expectedInfo = pdbSession.getPDBInfoStream(); if (!expectedInfo) - return &tsSrc->tsIndexMap; + return expectedInfo.takeError(); // 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 @@ -352,7 +371,10 @@ tsPath, make_error(pdb::pdb_error_code::signature_out_of_date)); - return &tsSrc->tsIndexMap; + // Reuse the type index map of the type server. + tpiMap = tsSrc->tpiMap; + ipiMap = tsSrc->ipiMap; + return Error::success(); } static bool equalsPath(StringRef path1, StringRef path2) { @@ -377,8 +399,8 @@ return nullptr; } -static Expected findPrecompMap(ObjFile *file, - PrecompRecord &pr) { +static Expected 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. @@ -409,53 +431,42 @@ toString(precomp->file), make_error(pdb::pdb_error_code::no_matching_pch)); - return &precomp->precompIndexMap; + return precomp; } /// 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 -mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *indexMap, +static Error +mergeInPrecompHeaderObj(ObjFile *file, + SmallVectorImpl &indexMapStorage, 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; + PrecompSource *precompSrc = *e; + if (precompSrc->tpiMap.empty()) + return Error::success(); assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); - assert(precomp.getTypesCount() <= precompIndexMap->tpiMap.size()); + assert(precomp.getTypesCount() <= precompSrc->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; + indexMapStorage.append(precompSrc->tpiMap.begin(), + precompSrc->tpiMap.begin() + precomp.getTypesCount()); + return Error::success(); } -Expected -UsePrecompSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) { +Error UsePrecompSource::mergeDebugT(TypeMerger *m) { // 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(); - - return TpiSource::mergeDebugT(m, indexMap); -} + if (Error e = + mergeInPrecompHeaderObj(file, indexMapStorage, precompDependency)) + return e; -Expected 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); + return TpiSource::mergeDebugT(m); } uint32_t TpiSource::countTypeServerPDBs() { diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -785,8 +785,14 @@ else data = getDebugSection(".debug$T"); - if (data.empty()) + // Don't make a TpiSource for objects with no debug info. If the object has + // symbols but no types, make a plain, empty TpiSource anyway, because it + // simplifies adding the symbols later. + if (data.empty()) { + if (!debugChunks.empty()) + debugTypesObj = makeTpiSource(this); return; + } // Get the first type record. It will indicate if this object uses a type // server (/Zi) or a PCH file (/Yu). diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp --- a/lld/COFF/PDB.cpp +++ b/lld/COFF/PDB.cpp @@ -112,11 +112,11 @@ /// externally. void addDebug(TpiSource *source); - const CVIndexMap *mergeTypeRecords(TpiSource *source, CVIndexMap *localMap); + bool mergeTypeRecords(TpiSource *source); - void addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap); + void addDebugSymbols(TpiSource *source); - void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, + void mergeSymbolRecords(TpiSource *source, std::vector &stringTableRefs, BinaryStreamRef symData); @@ -156,7 +156,7 @@ ObjFile &file; /// The result of merging type indices. - const CVIndexMap *indexMap; + TpiSource *source; /// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by /// index from other records in the .debug$S section. All of these strings @@ -188,8 +188,8 @@ void mergeInlineeLines(const DebugSubsectionRecord &inlineeLines); public: - DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap *indexMap) - : linker(linker), file(file), indexMap(indexMap) {} + DebugSHandler(PDBLinker &linker, ObjFile &file, TpiSource *source) + : linker(linker), file(file), source(source) {} void handleDebugS(ArrayRef relocatedDebugContents); @@ -261,7 +261,7 @@ static void remapTypesInSymbolRecord(ObjFile *file, SymbolKind symKind, MutableArrayRef recordBytes, - const CVIndexMap &indexMap, + TpiSource *source, ArrayRef typeRefs) { MutableArrayRef contents = recordBytes.drop_front(sizeof(RecordPrefix)); @@ -271,10 +271,9 @@ fatal("symbol record too short"); // This can be an item index or a type index. Choose the appropriate map. - ArrayRef typeOrItemMap = indexMap.tpiMap; bool isItemIndex = ref.Kind == TiRefKind::IndexRef; - if (isItemIndex && indexMap.isTypeServerMap) - typeOrItemMap = indexMap.ipiMap; + ArrayRef typeOrItemMap = + isItemIndex ? source->ipiMap : source->tpiMap; MutableArrayRef tIs( reinterpret_cast(contents.data() + ref.Offset), ref.Count); @@ -505,9 +504,10 @@ } } -void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, +void PDBLinker::mergeSymbolRecords(TpiSource *source, std::vector &stringTableRefs, BinaryStreamRef symData) { + ObjFile *file = source->file; ArrayRef symsBuffer; cantFail(symData.readBytes(0, symData.getLength(), symsBuffer)); SmallVector scopes; @@ -571,7 +571,7 @@ } // Re-map all the type index references. - remapTypesInSymbolRecord(file, sym.kind(), recordBytes, indexMap, + remapTypesInSymbolRecord(file, sym.kind(), recordBytes, source, typeRefs); // An object file may have S_xxx_ID symbols, but these get converted to @@ -665,11 +665,6 @@ 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. @@ -709,7 +704,7 @@ break; } case DebugSubsectionKind::Symbols: { - linker.mergeSymbolRecords(&file, *indexMap, stringTableReferences, + linker.mergeSymbolRecords(source, stringTableReferences, ss.getRecordData()); break; } @@ -757,9 +752,7 @@ // Remap type indices in inlinee line records in place. for (const InlineeSourceLine &line : inlineeLines) { TypeIndex &inlinee = *const_cast(&line.Header->Inlinee); - ArrayRef typeOrItemMap = - indexMap->isTypeServerMap ? indexMap->ipiMap : indexMap->tpiMap; - if (!remapTypeIndex(inlinee, typeOrItemMap)) { + if (!remapTypeIndex(inlinee, source->ipiMap)) { log("bad inlinee line record in " + file.getName() + " with bad inlinee index 0x" + utohexstr(inlinee.getIndex())); } @@ -834,21 +827,18 @@ warn(msg); } -const CVIndexMap *PDBLinker::mergeTypeRecords(TpiSource *source, - CVIndexMap *localMap) { +bool PDBLinker::mergeTypeRecords(TpiSource *source) { 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. - Expected r = source->mergeDebugT(&tMerger, localMap); - - // If the .debug$T sections fail to merge, assume there is no debug info. - if (!r) { - warnUnusable(source->file, r.takeError()); - return nullptr; + if (Error e = source->mergeDebugT(&tMerger)) { + // If the .debug$T sections fail to merge, assume there is no debug info. + warnUnusable(source->file, std::move(e)); + return false; } - return *r; + return true; } // Allocate memory for a .debug$S / .debug$F section and relocate it. @@ -860,12 +850,17 @@ return makeArrayRef(buffer, debugChunk.getSize()); } -void PDBLinker::addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap) { +void PDBLinker::addDebugSymbols(TpiSource *source) { + // If this TpiSource doesn't have an object file, it must be from a type + // server PDB. Type server PDBs do not contain symbols, so stop here. + if (!source->file) + return; + ScopedTimer t(symbolMergingTimer); pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); - DebugSHandler dsh(*this, *file, indexMap); + DebugSHandler dsh(*this, *source->file, source); // Now do all live .debug$S and .debug$F sections. - for (SectionChunk *debugChunk : file->getDebugChunks()) { + for (SectionChunk *debugChunk : source->file->getDebugChunks()) { if (!debugChunk->live || debugChunk->getSize() == 0) continue; @@ -925,13 +920,9 @@ } 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); + // If type merging failed, ignore the symbols. + if (mergeTypeRecords(source)) + addDebugSymbols(source); } static pdb::BulkPublic createPublic(Defined *def) { @@ -964,15 +955,6 @@ for_each(ObjFile::instances, [&](ObjFile *obj) { createModuleDBI(builder, obj); }); - // 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()) diff --git a/lld/COFF/TypeMerger.h b/lld/COFF/TypeMerger.h --- a/lld/COFF/TypeMerger.h +++ b/lld/COFF/TypeMerger.h @@ -55,15 +55,6 @@ SmallVector ipiCounts; }; -/// Map from type index and item index in a type server PDB to the -/// corresponding index in the destination PDB. -struct CVIndexMap { - llvm::SmallVector tpiMap; - llvm::SmallVector ipiMap; - bool isTypeServerMap = false; - bool isPrecompiledTypeMap = false; -}; - } // namespace coff } // namespace lld