Index: lld/trunk/COFF/InputFiles.h =================================================================== --- lld/trunk/COFF/InputFiles.h +++ lld/trunk/COFF/InputFiles.h @@ -15,6 +15,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" @@ -122,9 +123,12 @@ return Symbols[SymbolIndex]; } - // Returns the underying COFF file. + // Returns the underlying COFF file. COFFObjectFile *getCOFFObj() { return COFFObj.get(); } + // Whether the object was already merged into the final PDB or not + bool wasProcessedForPDB() const { return !!ModuleDBI; } + static std::vector Instances; // Flags in the absolute @feat.00 symbol if it is present. These usually @@ -147,6 +151,11 @@ const coff_section *AddrsigSec = nullptr; + // When using Microsoft precompiled headers, this is the PCH's key. + // The same key is used by both the precompiled object, and objects using the + // precompiled object. Any difference indicates out-of-date objects. + llvm::Optional EndPrecomp; + private: void initializeChunks(); void initializeSymbols(); Index: lld/trunk/COFF/PDB.cpp =================================================================== --- lld/trunk/COFF/PDB.cpp +++ lld/trunk/COFF/PDB.cpp @@ -49,6 +49,7 @@ #include "llvm/Object/CVDebugRecord.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/JamCRC.h" #include "llvm/Support/Path.h" @@ -80,6 +81,7 @@ SmallVector TPIMap; SmallVector IPIMap; bool IsTypeServerMap = false; + bool IsPrecompiledTypeMap = false; }; class DebugSHandler; @@ -106,8 +108,10 @@ /// Link CodeView from each object file in the symbol table into the PDB. void addObjectsToPDB(); - /// Link CodeView from a single object file into the PDB. - void addObjFile(ObjFile *File); + /// 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. @@ -120,11 +124,35 @@ /// 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 mergeDebugT(ObjFile *File, - CVIndexMap &ObjectIndexMap); + Expected mergeDebugT(ObjFile *File, + CVIndexMap *ObjectIndexMap); - Expected maybeMergeTypeServerPDB(ObjFile *File, - TypeServer2Record &TS); + /// Reads and makes available a PDB. + Expected maybeMergeTypeServerPDB(ObjFile *File, + const CVType &FirstType); + + /// 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); + + /// 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 aquirePrecompObj(ObjFile *File, + PrecompRecord Precomp); + + /// Adds a precompiled headers object signature -> TPI mapping. + std::pair + registerPrecompiledHeaders(uint32_t Signature); /// Add the section map and section contributions to the PDB. void addSections(ArrayRef OutputSections, @@ -168,6 +196,10 @@ /// Type index mappings of type server PDBs that we've loaded so far. std::map TypeServerIndexMappings; + /// Type index mappings of precompiled objects type map that we've loaded so + /// far. + std::map PrecompTypeIndexMappings; + /// List of TypeServer PDBs which cannot be loaded. /// Cached to prevent repeated load attempts. std::map MissingTypeServerPDBs; @@ -291,27 +323,79 @@ }); } -static Optional -maybeReadTypeServerRecord(CVTypeArray &Types) { - auto I = Types.begin(); - if (I == Types.end()) - return None; - const CVType &Type = *I; - if (Type.kind() != LF_TYPESERVER2) - return None; - TypeServer2Record TS; - if (auto EC = TypeDeserializer::deserializeAs(const_cast(Type), TS)) - fatal("error reading type server record: " + toString(std::move(EC))); - return std::move(TS); +// OBJs usually start their symbol stream with a S_OBJNAME record. This record +// also contains the signature/key of the current PCH session. The signature +// must be same for all objects which depend on the precompiled object. +// Recompiling the precompiled headers will generate a new PCH key and thus +// invalidate all the dependent objects. +static uint32_t extractPCHSignature(ObjFile *File) { + auto DbgIt = find_if(File->getDebugChunks(), [](auto &C) { + return C->getSectionName() == ".debug$S"; + }); + if (!DbgIt) + return 0; + + ArrayRef Contents = + consumeDebugMagic((*DbgIt)->getContents(), ".debug$S"); + DebugSubsectionArray Subsections; + BinaryStreamReader Reader(Contents, support::little); + ExitOnErr(Reader.readArray(Subsections, Contents.size())); + + for (const DebugSubsectionRecord &SS : Subsections) { + if (SS.kind() != DebugSubsectionKind::Symbols) + continue; + + // If it's there, the S_OBJNAME record shall come first in the stream. + Expected Sym = readSymbolFromStream(SS.getRecordData(), 0); + if (!Sym) { + consumeError(Sym.takeError()); + continue; + } + if (auto ObjName = SymbolDeserializer::deserializeAs(Sym.get())) + return ObjName->Signature; + } + return 0; } -Expected PDBLinker::mergeDebugT(ObjFile *File, - CVIndexMap &ObjectIndexMap) { +Expected +PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) { ScopedTimer T(TypeMergingTimer); + bool IsPrecompiledHeader = false; + ArrayRef Data = getDebugSection(File, ".debug$T"); + if (Data.empty()) { + // Try again, Microsoft precompiled headers use .debug$P instead of + // .debug$T + Data = getDebugSection(File, ".debug$P"); + IsPrecompiledHeader = true; + } if (Data.empty()) - return ObjectIndexMap; + return *ObjectIndexMap; // no debug info + + // Precompiled headers objects need to save the index map for further + // reference by other objects which use the precompiled headers. + if (IsPrecompiledHeader) { + uint32_t PCHSignature = extractPCHSignature(File); + 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; + } + } BinaryByteStream Stream(Data, support::little); CVTypeArray Types; @@ -319,13 +403,29 @@ if (auto EC = Reader.readArray(Types, Reader.getLength())) fatal("Reader::readArray failed: " + toString(std::move(EC))); - // Look through type servers. If we've already seen this type server, don't - // merge any type information. - if (Optional TS = maybeReadTypeServerRecord(Types)) - return maybeMergeTypeServerPDB(File, *TS); + auto FirstType = Types.begin(); + if (FirstType == Types.end()) + return *ObjectIndexMap; + + if (FirstType->kind() == LF_TYPESERVER2) { + // 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) { + // 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. + Types.drop_front(); + } - // This is a /Z7 object. Fill in the temporary, caller-provided - // ObjectIndexMap. + // Fill in the temporary, caller-provided ObjectIndexMap. if (Config->DebugGHashes) { ArrayRef Hashes; std::vector OwnedHashes; @@ -337,16 +437,18 @@ } if (auto Err = mergeTypeAndIdRecords(GlobalIDTable, GlobalTypeTable, - ObjectIndexMap.TPIMap, Types, Hashes)) + ObjectIndexMap->TPIMap, Types, Hashes, + File->EndPrecomp)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(Err))); } else { - if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable, - ObjectIndexMap.TPIMap, Types)) + if (auto Err = + mergeTypeAndIdRecords(IDTable, TypeTable, ObjectIndexMap->TPIMap, + Types, File->EndPrecomp)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(Err))); } - return ObjectIndexMap; + return *ObjectIndexMap; } static Expected> @@ -387,7 +489,12 @@ } Expected -PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, TypeServer2Record &TS) { +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))); + const GUID &TSId = TS.getGuid(); StringRef TSPath = TS.getName(); @@ -467,9 +574,10 @@ auto IpiHashes = GloballyHashedType::hashIds(ExpectedIpi->typeArray(), TpiHashes); + Optional EndPrecomp; // Merge TPI first, because the IPI stream will reference type indices. if (auto Err = mergeTypeRecords(GlobalTypeTable, IndexMap.TPIMap, - ExpectedTpi->typeArray(), TpiHashes)) + ExpectedTpi->typeArray(), TpiHashes, EndPrecomp)) fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err))); // Merge IPI. @@ -492,6 +600,113 @@ 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))); + + auto E = aquirePrecompObj(File, Precomp); + if (!E) + return createFileError(Precomp.getPrecompFilePath().str(), 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. + ObjectIndexMap->TPIMap.append(PrecompIndexMap.TPIMap.begin(), + PrecompIndexMap.TPIMap.begin() + + Precomp.getTypesCount()); + return *ObjectIndexMap; +} + +static bool equals_path(StringRef path1, StringRef path2) { +#if defined(_WIN32) + return path1.equals_lower(path2); +#else + return path1.equals(path2); +#endif +} + +// Find an OBJ provided on the command line, either by name or full path +static Optional> +findObjByName(StringRef NameOrPath) { + SmallString<128> CurrentPath; + + StringRef FileNameOnly = sys::path::filename(NameOrPath); + + for (ObjFile *F : ObjFile::Instances) { + CurrentPath = F->getName(); + sys::fs::make_absolute(CurrentPath); + + // First compare with the full path name + if (equals_path(CurrentPath, NameOrPath)) + return std::make_pair(F, CurrentPath.str().str()); + + StringRef CurrentFileName = sys::path::filename(CurrentPath); + + // Otherwise compare based solely on the file name (link.exe behavior) + if (equals_path(CurrentFileName, FileNameOnly)) + return std::make_pair(F, CurrentPath.str().str()); + } + return {}; +} + +std::pair +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 +PDBLinker::aquirePrecompObj(ObjFile *File, PrecompRecord Precomp) { + // 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; + + SmallString<128> PrecompPath = Precomp.getPrecompFilePath(); + sys::fs::make_absolute(PrecompPath); + + // 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. + sys::path::native(PrecompPath, 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 PrecompFilePath = findObjByName(PrecompPath); + if (!PrecompFilePath) + return errorCodeToError(std::error_code(ENOENT, std::generic_category())); + + ObjFile *CurrentFile = PrecompFilePath->first; + + addObjFile(CurrentFile, &IndexMap); + + if (!CurrentFile->EndPrecomp) + fatal(PrecompFilePath->second + " is not a precompiled headers object"); + + if (Precomp.getSignature() != CurrentFile->EndPrecomp->getSignature()) + return make_error(pdb::pdb_error_code::signature_out_of_date); + + return IndexMap; +} + static bool remapTypeIndex(TypeIndex &TI, ArrayRef TypeIndexMap) { if (TI.isSimple()) return true; @@ -998,7 +1213,9 @@ File.ModuleDBI->addDebugSubsection(std::move(NewChecksums)); } -void PDBLinker::addObjFile(ObjFile *File) { +void PDBLinker::addObjFile(ObjFile *File, CVIndexMap *ExternIndexMap) { + if (File->wasProcessedForPDB()) + return; // Add a module descriptor for every object file. We need to put an absolute // 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 @@ -1029,7 +1246,8 @@ // 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, ObjectIndexMap); + auto IndexMapResult = + mergeDebugT(File, ExternIndexMap ? ExternIndexMap : &ObjectIndexMap); // If the .debug$T sections fail to merge, assume there is no debug info. if (!IndexMapResult) { Index: lld/trunk/test/COFF/precomp-link.test =================================================================== --- lld/trunk/test/COFF/precomp-link.test +++ lld/trunk/test/COFF/precomp-link.test @@ -0,0 +1,37 @@ +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf +RUN: llvm-pdbutil dump -types %t.pdb | FileCheck %s + +RUN: lld-link %S/Inputs/precomp.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf +RUN: llvm-pdbutil dump -types %t.pdb | FileCheck %s + +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-invalid.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf 2>&1 | FileCheck %s -check-prefix FAILURE + +FAILURE: warning: Cannot use debug info for 'precomp-invalid.obj' +FAILURE-NEXT: failed to load reference '{{.*}}precomp.obj': The signature does not match; the file(s) might be out of date. + +CHECK: Types (TPI Stream) +CHECK-NOT: LF_PRECOMP +CHECK-NOT: LF_ENDPRECOMP + +// precomp.h +#pragma once +int Function(char A); + +// precomp.cpp +#include "precomp.h" + +// a.cpp +#include "precomp.h" +int main(void) { + Function('a'); + return 0; +} + +// b.cpp +#include "precomp.h" +int Function(char a) { + return (int)a; +} + +// cl.exe precomp.cpp /Z7 /Ycprecomp.h /c +// cl.exe a.cpp b.cpp /Z7 /Yuprecomp.h /c Index: llvm/trunk/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h +++ llvm/trunk/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h @@ -83,18 +83,21 @@ Error mergeTypeAndIdRecords(MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, SmallVectorImpl &SourceToDest, - const CVTypeArray &IdsAndTypes); + const CVTypeArray &IdsAndTypes, + Optional &EndPrecomp); Error mergeTypeAndIdRecords(GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, SmallVectorImpl &SourceToDest, const CVTypeArray &IdsAndTypes, - ArrayRef Hashes); + ArrayRef Hashes, + Optional &EndPrecomp); Error mergeTypeRecords(GlobalTypeTableBuilder &Dest, SmallVectorImpl &SourceToDest, const CVTypeArray &Types, - ArrayRef Hashes); + ArrayRef Hashes, + Optional &EndPrecomp); Error mergeIdRecords(GlobalTypeTableBuilder &Dest, ArrayRef Types, SmallVectorImpl &SourceToDest, Index: llvm/trunk/include/llvm/DebugInfo/PDB/GenericError.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/PDB/GenericError.h +++ llvm/trunk/include/llvm/DebugInfo/PDB/GenericError.h @@ -21,24 +21,23 @@ dia_sdk_not_present, dia_failed_loading, signature_out_of_date, - type_server_not_found, unspecified, }; -} // namespace codeview +} // namespace pdb } // namespace llvm namespace std { - template <> - struct is_error_code_enum : std::true_type {}; +template <> +struct is_error_code_enum : std::true_type {}; } // namespace std namespace llvm { namespace pdb { - const std::error_category &PDBErrCategory(); +const std::error_category &PDBErrCategory(); - inline std::error_code make_error_code(pdb_error_code E) { - return std::error_code(static_cast(E), PDBErrCategory()); - } +inline std::error_code make_error_code(pdb_error_code E) { + return std::error_code(static_cast(E), PDBErrCategory()); +} /// Base class for errors originating when parsing raw PDB files class PDBError : public ErrorInfo { Index: llvm/trunk/include/llvm/Support/BinaryStreamArray.h =================================================================== --- llvm/trunk/include/llvm/Support/BinaryStreamArray.h +++ llvm/trunk/include/llvm/Support/BinaryStreamArray.h @@ -125,6 +125,8 @@ BinaryStreamRef getUnderlyingStream() const { return Stream; } void setUnderlyingStream(BinaryStreamRef S) { Stream = S; } + void drop_front() { Stream = Stream.drop_front(begin()->length()); } + private: BinaryStreamRef Stream; Extractor E; Index: llvm/trunk/lib/DebugInfo/CodeView/CodeViewError.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/CodeView/CodeViewError.cpp +++ llvm/trunk/lib/DebugInfo/CodeView/CodeViewError.cpp @@ -41,6 +41,8 @@ }; static llvm::ManagedStatic CodeViewErrCategory; -const std::error_category &llvm::codeview::CVErrorCategory() { return *CodeViewErrCategory; } +const std::error_category &llvm::codeview::CVErrorCategory() { + return *CodeViewErrCategory; +} char CodeViewError::ID; Index: llvm/trunk/lib/DebugInfo/CodeView/TypeStreamMerger.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ llvm/trunk/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -12,6 +12,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" @@ -63,7 +64,12 @@ public: explicit TypeStreamMerger(SmallVectorImpl &SourceToDest) : IndexMap(SourceToDest) { - SourceToDest.clear(); + // When dealing with precompiled headers objects, all data in SourceToDest + // belongs to the precompiled headers object, and is assumed to be already + // remapped to the target PDB. Any forthcoming type that will be merged in + // might potentially back-reference this data. We also don't want to resolve + // twice the types in the precompiled object. + CurIndex += SourceToDest.size(); } static const TypeIndex Untranslated; @@ -71,7 +77,8 @@ // Local hashing entry points Error mergeTypesAndIds(MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, - const CVTypeArray &IdsAndTypes); + const CVTypeArray &IdsAndTypes, + Optional &EP); Error mergeIdRecords(MergingTypeTableBuilder &Dest, ArrayRef TypeSourceToDest, const CVTypeArray &Ids); @@ -82,13 +89,15 @@ Error mergeTypesAndIds(GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes, - ArrayRef Hashes); + ArrayRef Hashes, + Optional &EP); Error mergeIdRecords(GlobalTypeTableBuilder &Dest, ArrayRef TypeSourceToDest, const CVTypeArray &Ids, ArrayRef Hashes); Error mergeTypeRecords(GlobalTypeTableBuilder &Dest, const CVTypeArray &Types, - ArrayRef Hashes); + ArrayRef Hashes, + Optional &EP); private: Error doit(const CVTypeArray &Types); @@ -156,6 +165,8 @@ return llvm::make_error(cv_error_code::corrupt_record); } + Expected shouldRemapType(const CVType &Type); + Optional LastError; bool UseGlobalHashes = false; @@ -185,6 +196,8 @@ /// Temporary storage that we use to copy a record's data while re-writing /// its type indices. SmallVector RemapStorage; + + Optional EndPrecomp; }; } // end anonymous namespace @@ -261,22 +274,27 @@ Error TypeStreamMerger::mergeTypesAndIds(MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, - const CVTypeArray &IdsAndTypes) { + const CVTypeArray &IdsAndTypes, + Optional &EP) { DestIdStream = &DestIds; DestTypeStream = &DestTypes; UseGlobalHashes = false; - return doit(IdsAndTypes); + auto Err = doit(IdsAndTypes); + EP = EndPrecomp; + return Err; } // Global hashing entry points Error TypeStreamMerger::mergeTypeRecords(GlobalTypeTableBuilder &Dest, const CVTypeArray &Types, - ArrayRef Hashes) { + ArrayRef Hashes, + Optional &EP) { DestGlobalTypeStream = &Dest; UseGlobalHashes = true; GlobalHashes = Hashes; - - return doit(Types); + auto Err = doit(Types); + EP = EndPrecomp; + return Err; } Error TypeStreamMerger::mergeIdRecords(GlobalTypeTableBuilder &Dest, @@ -294,12 +312,15 @@ Error TypeStreamMerger::mergeTypesAndIds(GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes, - ArrayRef Hashes) { + ArrayRef Hashes, + Optional &EP) { DestGlobalIdStream = &DestIds; DestGlobalTypeStream = &DestTypes; UseGlobalHashes = true; GlobalHashes = Hashes; - return doit(IdsAndTypes); + auto Err = doit(IdsAndTypes); + EP = EndPrecomp; + return Err; } Error TypeStreamMerger::doit(const CVTypeArray &Types) { @@ -345,25 +366,30 @@ } Error TypeStreamMerger::remapType(const CVType &Type) { - auto DoSerialize = - [this, Type](MutableArrayRef Storage) -> ArrayRef { - return remapIndices(Type, Storage); - }; + auto R = shouldRemapType(Type); + if (!R) + return R.takeError(); TypeIndex DestIdx = Untranslated; - if (LLVM_LIKELY(UseGlobalHashes)) { - GlobalTypeTableBuilder &Dest = - isIdRecord(Type.kind()) ? *DestGlobalIdStream : *DestGlobalTypeStream; - GloballyHashedType H = GlobalHashes[CurIndex.toArrayIndex()]; - DestIdx = Dest.insertRecordAs(H, Type.RecordData.size(), DoSerialize); - } else { - MergingTypeTableBuilder &Dest = - isIdRecord(Type.kind()) ? *DestIdStream : *DestTypeStream; - - RemapStorage.resize(Type.RecordData.size()); - ArrayRef Result = DoSerialize(RemapStorage); - if (!Result.empty()) - DestIdx = Dest.insertRecordBytes(Result); + if (*R) { + auto DoSerialize = + [this, Type](MutableArrayRef Storage) -> ArrayRef { + return remapIndices(Type, Storage); + }; + if (LLVM_LIKELY(UseGlobalHashes)) { + GlobalTypeTableBuilder &Dest = + isIdRecord(Type.kind()) ? *DestGlobalIdStream : *DestGlobalTypeStream; + GloballyHashedType H = GlobalHashes[CurIndex.toArrayIndex()]; + DestIdx = Dest.insertRecordAs(H, Type.RecordData.size(), DoSerialize); + } else { + MergingTypeTableBuilder &Dest = + isIdRecord(Type.kind()) ? *DestIdStream : *DestTypeStream; + + RemapStorage.resize(Type.RecordData.size()); + ArrayRef Result = DoSerialize(RemapStorage); + if (!Result.empty()) + DestIdx = Dest.insertRecordBytes(Result); + } } addMapping(DestIdx); @@ -418,25 +444,29 @@ Error llvm::codeview::mergeTypeAndIdRecords( MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, - SmallVectorImpl &SourceToDest, const CVTypeArray &IdsAndTypes) { + SmallVectorImpl &SourceToDest, const CVTypeArray &IdsAndTypes, + Optional &EndPrecomp) { TypeStreamMerger M(SourceToDest); - return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes); + return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, EndPrecomp); } Error llvm::codeview::mergeTypeAndIdRecords( GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, SmallVectorImpl &SourceToDest, const CVTypeArray &IdsAndTypes, - ArrayRef Hashes) { + ArrayRef Hashes, + Optional &EndPrecomp) { TypeStreamMerger M(SourceToDest); - return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, Hashes); + return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, Hashes, + EndPrecomp); } Error llvm::codeview::mergeTypeRecords(GlobalTypeTableBuilder &Dest, SmallVectorImpl &SourceToDest, const CVTypeArray &Types, - ArrayRef Hashes) { + ArrayRef Hashes, + Optional &EndPrecomp) { TypeStreamMerger M(SourceToDest); - return M.mergeTypeRecords(Dest, Types, Hashes); + return M.mergeTypeRecords(Dest, Types, Hashes, EndPrecomp); } Error llvm::codeview::mergeIdRecords(GlobalTypeTableBuilder &Dest, @@ -447,3 +477,18 @@ TypeStreamMerger M(SourceToDest); return M.mergeIdRecords(Dest, Types, Ids, Hashes); } + +Expected TypeStreamMerger::shouldRemapType(const CVType &Type) { + // For object files containing precompiled types, we need to extract the + // signature, through EndPrecompRecord. This is done here for performance + // reasons, to avoid re-parsing the Types stream. + if (Type.kind() == LF_ENDPRECOMP) { + assert(!EndPrecomp); + EndPrecomp.emplace(); + if (auto EC = TypeDeserializer::deserializeAs(const_cast(Type), + EndPrecomp.getValue())) + return joinErrors(std::move(EC), errorCorruptRecord()); + return false; + } + return true; +} Index: llvm/trunk/lib/DebugInfo/PDB/GenericError.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/PDB/GenericError.cpp +++ llvm/trunk/lib/DebugInfo/PDB/GenericError.cpp @@ -24,8 +24,6 @@ switch (static_cast(Condition)) { case pdb_error_code::unspecified: return "An unknown error has occurred."; - case pdb_error_code::type_server_not_found: - return "Type server PDB was not found."; case pdb_error_code::dia_sdk_not_present: return "LLVM was not compiled with support for DIA. This usually means " "that you are not using MSVC, or your Visual Studio " Index: llvm/trunk/tools/llvm-readobj/COFFDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-readobj/COFFDumper.cpp +++ llvm/trunk/tools/llvm-readobj/COFFDumper.cpp @@ -1248,7 +1248,9 @@ error(object_error::parse_failed); } SmallVector SourceToDest; - if (auto EC = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types)) + Optional EndPrecomp; + if (auto EC = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types, + EndPrecomp)) return error(std::move(EC)); } }