forked from OSchip/llvm-project
[COFF][LLD] Add link support for Microsoft precompiled headers OBJs
This change allows for link-time merging of debugging information from Microsoft precompiled types OBJs compiled with cl.exe /Z7 /Yc and /Yu. This fixes llvm.org/PR34278 Differential Revision: https://reviews.llvm.org/D45213 llvm-svn: 346154
This commit is contained in:
parent
1a9b072338
commit
71c43ceaf8
|
@ -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();
|
||||
|
|
288
lld/COFF/PDB.cpp
288
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;
|
||||
|
||||
// This is a /Z7 object. Fill in the temporary, caller-provided
|
||||
// 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();
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue