Index: COFF/CMakeLists.txt =================================================================== --- COFF/CMakeLists.txt +++ COFF/CMakeLists.txt @@ -8,6 +8,7 @@ add_lld_library(lldCOFF Chunks.cpp + DebugTypes.cpp DLL.cpp Driver.cpp DriverUtils.cpp Index: COFF/DebugTypes.h =================================================================== --- COFF/DebugTypes.h +++ COFF/DebugTypes.h @@ -0,0 +1,51 @@ +//===- DebugTypes.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_DEBUGTYPES_H +#define LLD_COFF_DEBUGTYPES_H + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace codeview { +class PrecompRecord; +class TypeServer2Record; +} // namespace codeview +} // namespace llvm + +namespace lld { +namespace coff { + +class ObjFile; + +class TpiSource { +public: + enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB }; + + TpiSource(TpiKind K, ObjFile *F); + virtual ~TpiSource() {} + + const TpiKind Kind; + ObjFile *File; +}; + +TpiSource *makeTpiSource(ObjFile *F); +TpiSource *makeTypeServerSource(ObjFile *F); +TpiSource *makeUseTypeServerSource(ObjFile *F, + llvm::codeview::TypeServer2Record *TS); +TpiSource *makePrecompSource(ObjFile *F); +TpiSource *makeUsePrecompSource(ObjFile *F, + llvm::codeview::PrecompRecord *Precomp); + +// Temporary interface to get the dependency +template const T &retrieveDependencyInfo(TpiSource *Source); + +} // namespace coff +} // namespace lld + +#endif Index: COFF/DebugTypes.cpp =================================================================== --- COFF/DebugTypes.cpp +++ COFF/DebugTypes.cpp @@ -0,0 +1,84 @@ +//===- DebugTypes.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 +// +//===----------------------------------------------------------------------===// + +#include "DebugTypes.h" +#include "InputFiles.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" + +using namespace lld; +using namespace lld::coff; +using namespace llvm; +using namespace llvm::codeview; + +namespace { +class TypeServerSource : public TpiSource { +public: + TypeServerSource(ObjFile *F) : TpiSource(PDB, F) {} +}; + +class UseTypeServerSource : public TpiSource { +public: + UseTypeServerSource(ObjFile *F, TypeServer2Record *TS) + : TpiSource(UsingPDB, F), TypeServerDependency(*TS) {} + + // Information about the PDB type server dependency, that needs to be loaded + // in before merging this OBJ. + TypeServer2Record TypeServerDependency; +}; + +class PrecompSource : public TpiSource { +public: + PrecompSource(ObjFile *F) : TpiSource(PCH, F) {} +}; + +class UsePrecompSource : public TpiSource { +public: + UsePrecompSource(ObjFile *F, PrecompRecord *Precomp) + : TpiSource(UsingPCH, F), PrecompDependency(*Precomp) {} + + // Information about the Precomp OBJ dependency, that needs to be loaded in + // before merging this OBJ. + PrecompRecord PrecompDependency; +}; +} // namespace + +static std::vector> GC; + +TpiSource::TpiSource(TpiKind K, ObjFile *F) : Kind(K), File(F) { + GC.push_back(std::unique_ptr(this)); +} + +TpiSource *coff::makeTpiSource(ObjFile *F) { + return new TpiSource(TpiSource::Regular, F); +} + +TpiSource *coff::makeTypeServerSource(ObjFile *F) { + return new TypeServerSource(F); +} + +TpiSource *coff::makeUseTypeServerSource(ObjFile *F, TypeServer2Record *TS) { + return new UseTypeServerSource(F, TS); +} + +TpiSource *coff::makePrecompSource(ObjFile *F) { return new PrecompSource(F); } + +TpiSource *coff::makeUsePrecompSource(ObjFile *F, PrecompRecord *Precomp) { + return new UsePrecompSource(F, Precomp); +} + +template <> +const PrecompRecord &coff::retrieveDependencyInfo(TpiSource *Source) { + assert(Source->Kind == TpiSource::UsingPCH); + return ((UsePrecompSource *)Source)->PrecompDependency; +} + +template <> +const TypeServer2Record &coff::retrieveDependencyInfo(TpiSource *Source) { + assert(Source->Kind == TpiSource::UsingPDB); + return ((UseTypeServerSource *)Source)->TypeServerDependency; +} Index: COFF/InputFiles.h =================================================================== --- COFF/InputFiles.h +++ COFF/InputFiles.h @@ -51,6 +51,7 @@ class SectionChunk; class Symbol; class Undefined; +class TpiSource; // The root class of input files. class InputFile { @@ -161,6 +162,12 @@ // Tells whether this file was compiled with /hotpatch bool HotPatchable = false; + // If the OBJ has a .debug$T stream, this tells how it will be handled. + TpiSource *DebugTypesObj = nullptr; + + // The .debug$T stream if there's one. + llvm::Optional DebugTypes; + private: const coff_section* getSection(uint32_t I); const coff_section *getSection(COFFSymbolRef Sym) { @@ -170,6 +177,7 @@ void initializeChunks(); void initializeSymbols(); void initializeFlags(); + void initializeDependencies(); SectionChunk * readSection(uint32_t SectionNumber, Index: COFF/InputFiles.cpp =================================================================== --- COFF/InputFiles.cpp +++ COFF/InputFiles.cpp @@ -9,6 +9,7 @@ #include "InputFiles.h" #include "Chunks.h" #include "Config.h" +#include "DebugTypes.h" #include "Driver.h" #include "SymbolTable.h" #include "Symbols.h" @@ -22,6 +23,7 @@ #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Casting.h" @@ -129,6 +131,7 @@ initializeChunks(); initializeSymbols(); initializeFlags(); + initializeDependencies(); } const coff_section* ObjFile::getSection(uint32_t I) { @@ -656,6 +659,53 @@ } } +void ObjFile::initializeDependencies() { + if (!Config->Debug) + return; + + bool IsPCH = false; + + ArrayRef Data = getDebugSection(".debug$P"); + if (!Data.empty()) + IsPCH = true; + else + Data = getDebugSection(".debug$T"); + + if (Data.empty()) + return; + + CVTypeArray Types; + BinaryStreamReader Reader(Data, support::little); + cantFail(Reader.readArray(Types, Reader.getLength())); + + CVTypeArray::Iterator FirstType = Types.begin(); + if (FirstType == Types.end()) + return; + + DebugTypes.emplace(Types); + + if (IsPCH) { + DebugTypesObj = makePrecompSource(this); + return; + } + + if (FirstType->kind() == LF_TYPESERVER2) { + TypeServer2Record TS = cantFail( + TypeDeserializer::deserializeAs(FirstType->data())); + DebugTypesObj = makeUseTypeServerSource(this, &TS); + return; + } + + if (FirstType->kind() == LF_PRECOMP) { + PrecompRecord Precomp = cantFail( + TypeDeserializer::deserializeAs(FirstType->data())); + DebugTypesObj = makeUsePrecompSource(this, &Precomp); + return; + } + + DebugTypesObj = makeTpiSource(this); +} + StringRef ltrim1(StringRef S, const char *Chars) { if (!S.empty() && strchr(Chars, S[0])) return S.substr(1); Index: COFF/PDB.cpp =================================================================== --- COFF/PDB.cpp +++ COFF/PDB.cpp @@ -9,6 +9,7 @@ #include "PDB.h" #include "Chunks.h" #include "Config.h" +#include "DebugTypes.h" #include "Driver.h" #include "SymbolTable.h" #include "Symbols.h" @@ -129,15 +130,12 @@ CVIndexMap *ObjectIndexMap); /// Reads and makes available a PDB. - Expected maybeMergeTypeServerPDB(ObjFile *File, - const CVType &FirstType); + Expected 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. - Expected - mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType, - CVIndexMap *ObjectIndexMap); + Error mergeInPrecompHeaderObj(ObjFile *File, CVIndexMap *ObjectIndexMap); /// Reads and makes available a precompiled headers object. /// @@ -148,8 +146,7 @@ /// /// If the precompiled headers object was already loaded, this function will /// simply return its (remapped) TPI map. - Expected aquirePrecompObj(ObjFile *File, - PrecompRecord Precomp); + Expected aquirePrecompObj(ObjFile *File); /// Adds a precompiled headers object signature -> TPI mapping. std::pair @@ -362,21 +359,12 @@ PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) { ScopedTimer T(TypeMergingTimer); - bool IsPrecompiledHeader = false; - - ArrayRef Data = File->getDebugSection(".debug$T"); - if (Data.empty()) { - // Try again, Microsoft precompiled headers use .debug$P instead of - // .debug$T - Data = File->getDebugSection(".debug$P"); - IsPrecompiledHeader = true; - } - if (Data.empty()) - return *ObjectIndexMap; // no debug info + 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 (IsPrecompiledHeader) { + 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 (" + @@ -398,33 +386,28 @@ } } - BinaryByteStream Stream(Data, support::little); - CVTypeArray Types; - BinaryStreamReader Reader(Stream); - if (auto EC = Reader.readArray(Types, Reader.getLength())) - fatal("Reader::readArray failed: " + toString(std::move(EC))); - - auto FirstType = Types.begin(); - if (FirstType == Types.end()) - return *ObjectIndexMap; - - if (FirstType->kind() == LF_TYPESERVER2) { + 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, *FirstType); - } else if (FirstType->kind() == LF_PRECOMP) { + return maybeMergeTypeServerPDB(File); + } + + CVTypeArray &Types = *File->DebugTypes; + + 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. - auto E = mergeInPrecompHeaderObj(File, *FirstType, ObjectIndexMap); - if (!E) - return E.takeError(); - - // Drop LF_PRECOMP record from the input stream, as it needs to be replaced - // with the precompiled headers object type stream. - // Note that we can't just call Types.drop_front(), as we explicitly want to - // rebase the stream. + 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())); } @@ -493,11 +476,9 @@ } Expected -PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, const CVType &FirstType) { - TypeServer2Record TS; - if (auto EC = - TypeDeserializer::deserializeAs(const_cast(FirstType), TS)) - fatal("error reading record: " + toString(std::move(EC))); +PDBLinker::maybeMergeTypeServerPDB(ObjFile *File) { + const TypeServer2Record &TS = + retrieveDependencyInfo(File->DebugTypesObj); const codeview::GUID &TSId = TS.getGuid(); StringRef TSPath = TS.getName(); @@ -606,15 +587,12 @@ return IndexMap; } -Expected -PDBLinker::mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType, - CVIndexMap *ObjectIndexMap) { - PrecompRecord Precomp; - if (auto EC = TypeDeserializer::deserializeAs(const_cast(FirstType), - Precomp)) - fatal("error reading record: " + toString(std::move(EC))); +Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *File, + CVIndexMap *ObjectIndexMap) { + const PrecompRecord &Precomp = + retrieveDependencyInfo(File->DebugTypesObj); - auto E = aquirePrecompObj(File, Precomp); + Expected E = aquirePrecompObj(File); if (!E) return E.takeError(); @@ -622,7 +600,7 @@ assert(PrecompIndexMap.IsPrecompiledTypeMap); if (PrecompIndexMap.TPIMap.empty()) - return PrecompIndexMap; + return Error::success(); assert(Precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); assert(Precomp.getTypesCount() <= PrecompIndexMap.TPIMap.size()); @@ -630,7 +608,7 @@ ObjectIndexMap->TPIMap.append(PrecompIndexMap.TPIMap.begin(), PrecompIndexMap.TPIMap.begin() + Precomp.getTypesCount()); - return *ObjectIndexMap; + return Error::success(); } static bool equals_path(StringRef path1, StringRef path2) { @@ -666,8 +644,10 @@ return {IndexMap, false}; } -Expected -PDBLinker::aquirePrecompObj(ObjFile *File, PrecompRecord Precomp) { +Expected PDBLinker::aquirePrecompObj(ObjFile *File) { + const PrecompRecord &Precomp = + retrieveDependencyInfo(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());