[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:
Alexandre Ganea 2018-11-05 19:20:47 +00:00
parent 1a9b072338
commit 71c43ceaf8
14 changed files with 399 additions and 84 deletions

View File

@ -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();

View File

@ -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.

View File

@ -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

View File

@ -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,

View File

@ -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> {

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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 "

View File

@ -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));
}
}