diff options
| -rw-r--r-- | lld/COFF/InputFiles.h | 11 | ||||
| -rw-r--r-- | lld/COFF/PDB.cpp | 288 | ||||
| -rw-r--r-- | lld/test/COFF/Inputs/precomp-a.obj | bin | 0 -> 2598 bytes | |||
| -rw-r--r-- | lld/test/COFF/Inputs/precomp-b.obj | bin | 0 -> 2257 bytes | |||
| -rw-r--r-- | lld/test/COFF/Inputs/precomp-invalid.obj | bin | 0 -> 2257 bytes | |||
| -rw-r--r-- | lld/test/COFF/Inputs/precomp.obj | bin | 0 -> 62392 bytes | |||
| -rw-r--r-- | lld/test/COFF/precomp-link.test | 37 | ||||
| -rw-r--r-- | llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h | 9 | ||||
| -rw-r--r-- | llvm/include/llvm/DebugInfo/PDB/GenericError.h | 15 | ||||
| -rw-r--r-- | llvm/include/llvm/Support/BinaryStreamArray.h | 2 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/CodeView/CodeViewError.cpp | 4 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp | 113 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/PDB/GenericError.cpp | 2 | ||||
| -rw-r--r-- | llvm/tools/llvm-readobj/COFFDumper.cpp | 4 |
14 files changed, 400 insertions, 85 deletions
diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h index 2b045358fca..9e156078422 100644 --- a/lld/COFF/InputFiles.h +++ b/lld/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 @@ public: 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<ObjFile *> Instances; // Flags in the absolute @feat.00 symbol if it is present. These usually @@ -147,6 +151,11 @@ public: 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<llvm::codeview::EndPrecompRecord> EndPrecomp; + private: void initializeChunks(); void initializeSymbols(); diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp index 50785c327b9..fc501d9fbcc 100644 --- a/lld/COFF/PDB.cpp +++ b/lld/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 @@ struct CVIndexMap { SmallVector<TypeIndex, 0> TPIMap; SmallVector<TypeIndex, 0> IPIMap; bool IsTypeServerMap = false; + bool IsPrecompiledTypeMap = false; }; class DebugSHandler; @@ -106,8 +108,10 @@ public: /// 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 @@ public: /// 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); + Expected<const CVIndexMap &> mergeDebugT(ObjFile *File, + CVIndexMap *ObjectIndexMap); - Expected<const CVIndexMap&> maybeMergeTypeServerPDB(ObjFile *File, - TypeServer2Record &TS); + /// Reads and makes available a PDB. + Expected<const CVIndexMap &> 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<const CVIndexMap &> + 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<const CVIndexMap &> aquirePrecompObj(ObjFile *File, + PrecompRecord Precomp); + + /// Adds a precompiled headers object signature -> TPI mapping. + std::pair<CVIndexMap &, bool /*already there*/> + registerPrecompiledHeaders(uint32_t Signature); /// Add the section map and section contributions to the PDB. void addSections(ArrayRef<OutputSection *> OutputSections, @@ -168,6 +196,10 @@ private: /// Type index mappings of type server PDBs that we've loaded so far. std::map<GUID, CVIndexMap> TypeServerIndexMappings; + /// Type index mappings of precompiled objects type map that we've loaded so + /// far. + std::map<uint32_t, CVIndexMap> PrecompTypeIndexMappings; + /// List of TypeServer PDBs which cannot be loaded. /// Cached to prevent repeated load attempts. std::map<GUID, std::string> MissingTypeServerPDBs; @@ -324,27 +356,79 @@ static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder, }); } -static Optional<TypeServer2Record> -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<CVType &>(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<uint8_t> 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<CVSymbol> Sym = readSymbolFromStream(SS.getRecordData(), 0); + if (!Sym) { + consumeError(Sym.takeError()); + continue; + } + if (auto ObjName = SymbolDeserializer::deserializeAs<ObjNameSym>(Sym.get())) + return ObjName->Signature; + } + return 0; } -Expected<const CVIndexMap&> PDBLinker::mergeDebugT(ObjFile *File, - CVIndexMap &ObjectIndexMap) { +Expected<const CVIndexMap &> +PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) { ScopedTimer T(TypeMergingTimer); + bool IsPrecompiledHeader = false; + ArrayRef<uint8_t> 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; @@ -352,13 +436,29 @@ Expected<const CVIndexMap&> PDBLinker::mergeDebugT(ObjFile *File, 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<TypeServer2Record> 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<GloballyHashedType> Hashes; std::vector<GloballyHashedType> OwnedHashes; @@ -370,16 +470,18 @@ Expected<const CVIndexMap&> PDBLinker::mergeDebugT(ObjFile *File, } 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<std::unique_ptr<pdb::NativeSession>> @@ -420,7 +522,12 @@ tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) { } Expected<const CVIndexMap &> -PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, TypeServer2Record &TS) { +PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, const CVType &FirstType) { + TypeServer2Record TS; + if (auto EC = + TypeDeserializer::deserializeAs(const_cast<CVType &>(FirstType), TS)) + fatal("error reading record: " + toString(std::move(EC))); + const GUID &TSId = TS.getGuid(); StringRef TSPath = TS.getName(); @@ -502,9 +609,10 @@ PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, TypeServer2Record &TS) { auto IpiHashes = GloballyHashedType::hashIds(ExpectedIpi->typeArray(), TpiHashes); + Optional<EndPrecompRecord> 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. @@ -527,6 +635,113 @@ PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, TypeServer2Record &TS) { return IndexMap; } +Expected<const CVIndexMap &> +PDBLinker::mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType, + CVIndexMap *ObjectIndexMap) { + PrecompRecord Precomp; + if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(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<std::pair<ObjFile *, std::string>> +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<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, 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::PDBError>(pdb::pdb_error_code::signature_out_of_date); + + return IndexMap; +} + static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) { if (TI.isSimple()) return true; @@ -1027,7 +1242,9 @@ void DebugSHandler::finish() { 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 @@ -1057,7 +1274,8 @@ void PDBLinker::addObjFile(ObjFile *File) { // 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) { diff --git a/lld/test/COFF/Inputs/precomp-a.obj b/lld/test/COFF/Inputs/precomp-a.obj Binary files differnew file mode 100644 index 00000000000..b63392a7a86 --- /dev/null +++ b/lld/test/COFF/Inputs/precomp-a.obj diff --git a/lld/test/COFF/Inputs/precomp-b.obj b/lld/test/COFF/Inputs/precomp-b.obj Binary files differnew file mode 100644 index 00000000000..d590ecebe28 --- /dev/null +++ b/lld/test/COFF/Inputs/precomp-b.obj diff --git a/lld/test/COFF/Inputs/precomp-invalid.obj b/lld/test/COFF/Inputs/precomp-invalid.obj Binary files differnew file mode 100644 index 00000000000..2ad270a6e61 --- /dev/null +++ b/lld/test/COFF/Inputs/precomp-invalid.obj diff --git a/lld/test/COFF/Inputs/precomp.obj b/lld/test/COFF/Inputs/precomp.obj Binary files differnew file mode 100644 index 00000000000..424077e68dc --- /dev/null +++ b/lld/test/COFF/Inputs/precomp.obj diff --git a/lld/test/COFF/precomp-link.test b/lld/test/COFF/precomp-link.test new file mode 100644 index 00000000000..602d85ae56e --- /dev/null +++ b/lld/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
diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h b/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h index 583740d2eb4..a84f074237d 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h +++ b/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h @@ -83,18 +83,21 @@ Error mergeIdRecords(MergingTypeTableBuilder &Dest, ArrayRef<TypeIndex> Types, Error mergeTypeAndIdRecords(MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, SmallVectorImpl<TypeIndex> &SourceToDest, - const CVTypeArray &IdsAndTypes); + const CVTypeArray &IdsAndTypes, + Optional<EndPrecompRecord> &EndPrecomp); Error mergeTypeAndIdRecords(GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &IdsAndTypes, - ArrayRef<GloballyHashedType> Hashes); + ArrayRef<GloballyHashedType> Hashes, + Optional<EndPrecompRecord> &EndPrecomp); Error mergeTypeRecords(GlobalTypeTableBuilder &Dest, SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &Types, - ArrayRef<GloballyHashedType> Hashes); + ArrayRef<GloballyHashedType> Hashes, + Optional<EndPrecompRecord> &EndPrecomp); Error mergeIdRecords(GlobalTypeTableBuilder &Dest, ArrayRef<TypeIndex> Types, SmallVectorImpl<TypeIndex> &SourceToDest, diff --git a/llvm/include/llvm/DebugInfo/PDB/GenericError.h b/llvm/include/llvm/DebugInfo/PDB/GenericError.h index 4e2e8b163b5..7b5a8529596 100644 --- a/llvm/include/llvm/DebugInfo/PDB/GenericError.h +++ b/llvm/include/llvm/DebugInfo/PDB/GenericError.h @@ -21,24 +21,23 @@ enum class pdb_error_code { 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<llvm::pdb::pdb_error_code> : std::true_type {}; +template <> +struct is_error_code_enum<llvm::pdb::pdb_error_code> : 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<int>(E), PDBErrCategory()); - } +inline std::error_code make_error_code(pdb_error_code E) { + return std::error_code(static_cast<int>(E), PDBErrCategory()); +} /// Base class for errors originating when parsing raw PDB files class PDBError : public ErrorInfo<PDBError, StringError> { diff --git a/llvm/include/llvm/Support/BinaryStreamArray.h b/llvm/include/llvm/Support/BinaryStreamArray.h index d1571cb37fc..7b8a95b4573 100644 --- a/llvm/include/llvm/Support/BinaryStreamArray.h +++ b/llvm/include/llvm/Support/BinaryStreamArray.h @@ -125,6 +125,8 @@ public: 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; diff --git a/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp b/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp index 914157ef0c1..2a9753add31 100644 --- a/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp +++ b/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp @@ -41,6 +41,8 @@ public: }; static llvm::ManagedStatic<CodeViewErrorCategory> CodeViewErrCategory; -const std::error_category &llvm::codeview::CVErrorCategory() { return *CodeViewErrCategory; } +const std::error_category &llvm::codeview::CVErrorCategory() { + return *CodeViewErrCategory; +} char CodeViewError::ID; diff --git a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp index 1a1d516ff3c..803818226e5 100644 --- a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ b/llvm/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 @@ class TypeStreamMerger { public: explicit TypeStreamMerger(SmallVectorImpl<TypeIndex> &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 @@ public: // Local hashing entry points Error mergeTypesAndIds(MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, - const CVTypeArray &IdsAndTypes); + const CVTypeArray &IdsAndTypes, + Optional<EndPrecompRecord> &EP); Error mergeIdRecords(MergingTypeTableBuilder &Dest, ArrayRef<TypeIndex> TypeSourceToDest, const CVTypeArray &Ids); @@ -82,13 +89,15 @@ public: Error mergeTypesAndIds(GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes, - ArrayRef<GloballyHashedType> Hashes); + ArrayRef<GloballyHashedType> Hashes, + Optional<EndPrecompRecord> &EP); Error mergeIdRecords(GlobalTypeTableBuilder &Dest, ArrayRef<TypeIndex> TypeSourceToDest, const CVTypeArray &Ids, ArrayRef<GloballyHashedType> Hashes); Error mergeTypeRecords(GlobalTypeTableBuilder &Dest, const CVTypeArray &Types, - ArrayRef<GloballyHashedType> Hashes); + ArrayRef<GloballyHashedType> Hashes, + Optional<EndPrecompRecord> &EP); private: Error doit(const CVTypeArray &Types); @@ -156,6 +165,8 @@ private: return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); } + Expected<bool> shouldRemapType(const CVType &Type); + Optional<Error> LastError; bool UseGlobalHashes = false; @@ -185,6 +196,8 @@ private: /// Temporary storage that we use to copy a record's data while re-writing /// its type indices. SmallVector<uint8_t, 256> RemapStorage; + + Optional<EndPrecompRecord> EndPrecomp; }; } // end anonymous namespace @@ -261,22 +274,27 @@ Error TypeStreamMerger::mergeIdRecords(MergingTypeTableBuilder &Dest, Error TypeStreamMerger::mergeTypesAndIds(MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, - const CVTypeArray &IdsAndTypes) { + const CVTypeArray &IdsAndTypes, + Optional<EndPrecompRecord> &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<GloballyHashedType> Hashes) { + ArrayRef<GloballyHashedType> Hashes, + Optional<EndPrecompRecord> &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::mergeIdRecords(GlobalTypeTableBuilder &Dest, Error TypeStreamMerger::mergeTypesAndIds(GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes, - ArrayRef<GloballyHashedType> Hashes) { + ArrayRef<GloballyHashedType> Hashes, + Optional<EndPrecompRecord> &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::remapAllTypes(const CVTypeArray &Types) { } Error TypeStreamMerger::remapType(const CVType &Type) { - auto DoSerialize = - [this, Type](MutableArrayRef<uint8_t> Storage) -> ArrayRef<uint8_t> { - 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<uint8_t> Result = DoSerialize(RemapStorage); - if (!Result.empty()) - DestIdx = Dest.insertRecordBytes(Result); + if (*R) { + auto DoSerialize = + [this, Type](MutableArrayRef<uint8_t> Storage) -> ArrayRef<uint8_t> { + 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<uint8_t> Result = DoSerialize(RemapStorage); + if (!Result.empty()) + DestIdx = Dest.insertRecordBytes(Result); + } } addMapping(DestIdx); @@ -418,25 +444,29 @@ Error llvm::codeview::mergeIdRecords(MergingTypeTableBuilder &Dest, Error llvm::codeview::mergeTypeAndIdRecords( MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, - SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &IdsAndTypes) { + SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &IdsAndTypes, + Optional<EndPrecompRecord> &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<TypeIndex> &SourceToDest, const CVTypeArray &IdsAndTypes, - ArrayRef<GloballyHashedType> Hashes) { + ArrayRef<GloballyHashedType> Hashes, + Optional<EndPrecompRecord> &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<TypeIndex> &SourceToDest, const CVTypeArray &Types, - ArrayRef<GloballyHashedType> Hashes) { + ArrayRef<GloballyHashedType> Hashes, + Optional<EndPrecompRecord> &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 @@ Error llvm::codeview::mergeIdRecords(GlobalTypeTableBuilder &Dest, TypeStreamMerger M(SourceToDest); return M.mergeIdRecords(Dest, Types, Ids, Hashes); } + +Expected<bool> 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<CVType &>(Type), + EndPrecomp.getValue())) + return joinErrors(std::move(EC), errorCorruptRecord()); + return false; + } + return true; +} diff --git a/llvm/lib/DebugInfo/PDB/GenericError.cpp b/llvm/lib/DebugInfo/PDB/GenericError.cpp index 95f6c15cd30..5f5ff69fe3f 100644 --- a/llvm/lib/DebugInfo/PDB/GenericError.cpp +++ b/llvm/lib/DebugInfo/PDB/GenericError.cpp @@ -24,8 +24,6 @@ public: switch (static_cast<pdb_error_code>(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 " diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp index 26fe1aa622f..7f590713993 100644 --- a/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -1248,7 +1248,9 @@ void COFFDumper::mergeCodeViewTypes(MergingTypeTableBuilder &CVIDs, error(object_error::parse_failed); } SmallVector<TypeIndex, 128> SourceToDest; - if (auto EC = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types)) + Optional<EndPrecompRecord> EndPrecomp; + if (auto EC = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types, + EndPrecomp)) return error(std::move(EC)); } } |

