Index: llvm/include/llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h =================================================================== --- llvm/include/llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h +++ llvm/include/llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h @@ -72,7 +72,7 @@ size_t TypeRecordBytes = 0; - Optional VerHeader; + PdbRaw_TpiVer VerHeader = PdbRaw_TpiVer::PdbTpiV80; std::vector> TypeRecords; std::vector TypeHashes; std::vector TypeIndexOffsets; Index: llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp +++ llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp @@ -69,7 +69,7 @@ uint32_t Count = TypeRecords.size(); - H->Version = *VerHeader; + H->Version = VerHeader; H->HeaderSize = sizeof(TpiStreamHeader); H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex; H->TypeIndexEnd = H->TypeIndexBegin + Count; Index: llvm/test/DebugInfo/PDB/Inputs/merge1.yaml =================================================================== --- /dev/null +++ llvm/test/DebugInfo/PDB/Inputs/merge1.yaml @@ -0,0 +1,52 @@ +--- +TpiStream: + Records: + # uint32_t* [Index: 0x1000] + - Kind: LF_POINTER + Pointer: + ReferentType: 117 + Attrs: 32778 + # int64_t* [Index: 0x1001] + - Kind: LF_POINTER + Pointer: + ReferentType: 118 + Attrs: 32778 + # struct OnlyInMerge1 [Index: 0x1002] + - Kind: LF_STRUCTURE + Class: + MemberCount: 0 + Options: [ None, ForwardReference, HasUniqueName ] + FieldList: 0 + Name: 'OnlyInMerge1' + UniqueName: 'OnlyInMerge1' + DerivationList: 0 + VTableShape: 0 + Size: 0 + # uint32_t** [Index: 0x1003] + - Kind: LF_POINTER + Pointer: + ReferentType: 4096 + Attrs: 32778 + # uint32_t*** [Index: 0x1004] + - Kind: LF_POINTER + Pointer: + ReferentType: 4099 + Attrs: 32778 + # int64_t* [Index: 0x1005] + - Kind: LF_POINTER + Pointer: + ReferentType: 4097 + Attrs: 32778 + # [uint32_t, uint32_t*, uint32_t**] [Index: 0x1006] + - Kind: LF_ARGLIST + ArgList: + ArgIndices: [ 117, 4096, 4099 ] + # uint32_t (uint32_t, uint32_t*, uint32_t**) [Index: 0x1007] + - Kind: LF_PROCEDURE + Procedure: + ReturnType: 117 + CallConv: NearC + Options: [ None ] + ParameterCount: 0 + ArgumentList: 4102 +... Index: llvm/test/DebugInfo/PDB/Inputs/merge2.yaml =================================================================== --- /dev/null +++ llvm/test/DebugInfo/PDB/Inputs/merge2.yaml @@ -0,0 +1,52 @@ +--- +TpiStream: + Records: + # uint32_t* [Index: 0x1000] + - Kind: LF_POINTER + Pointer: + ReferentType: 117 + Attrs: 32778 + # uint32_t** [Index: 0x1001] + - Kind: LF_POINTER + Pointer: + ReferentType: 4096 + Attrs: 32778 + # uint32_t*** [Index: 0x1002] + - Kind: LF_POINTER + Pointer: + ReferentType: 4097 + Attrs: 32778 + # [uint32_t, uint32_t*, uint32_t**] [Index: 0x1003] + - Kind: LF_ARGLIST + ArgList: + ArgIndices: [ 117, 4096, 4097 ] + # uint32_t (uint32_t, uint32_t*, uint32_t**) [Index: 0x1004] + - Kind: LF_PROCEDURE + Procedure: + ReturnType: 117 + CallConv: NearC + Options: [ None ] + ParameterCount: 0 + ArgumentList: 4099 + # int64_t* [Index: 0x1005] + - Kind: LF_POINTER + Pointer: + ReferentType: 118 + Attrs: 32778 + # int64_t** [Index: 0x1006] + - Kind: LF_POINTER + Pointer: + ReferentType: 4101 + Attrs: 32778 + # struct OnlyInMerge2 [Index: 0x1007] + - Kind: LF_STRUCTURE + Class: + MemberCount: 0 + Options: [ None, ForwardReference, HasUniqueName ] + FieldList: 0 + Name: 'OnlyInMerge2' + UniqueName: 'OnlyInMerge2' + DerivationList: 0 + VTableShape: 0 + Size: 0 +... Index: llvm/test/DebugInfo/PDB/pdbdump-mergetypes.test =================================================================== --- /dev/null +++ llvm/test/DebugInfo/PDB/pdbdump-mergetypes.test @@ -0,0 +1,24 @@ +; RUN: llvm-pdbdump yaml2pdb -pdb=%t.1.pdb %p/Inputs/merge1.yaml +; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2.pdb %p/Inputs/merge2.yaml +; RUN: llvm-pdbdump merge -pdb=%t.3.pdb %t.1.pdb %t.2.pdb +; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=MERGED %s +; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=ARGLIST %s + + +MERGED: Type Info Stream (TPI) +MERGED: Record count: 9 +MERGED-DAG: PointeeType: unsigned +MERGED-DAG: PointeeType: unsigned* +MERGED-DAG: PointeeType: unsigned** +MERGED-DAG: PointeeType: __int64 +MERGED-DAG: PointeeType: __int64* +MERGED-DAG: Name: OnlyInMerge1 +MERGED-DAG: Name: OnlyInMerge2 +MERGED-DAG: TypeLeafKind: LF_ARGLIST + +ARGLIST: TypeLeafKind: LF_ARGLIST +ARGLIST-NEXT: NumArgs: 3 +ARGLIST-NEXT: Arguments [ +ARGLIST-NEXT: ArgType: unsigned +ARGLIST-NEXT: ArgType: unsigned* +ARGLIST-NEXT: ArgType: unsigned** Index: llvm/tools/llvm-pdbdump/llvm-pdbdump.cpp =================================================================== --- llvm/tools/llvm-pdbdump/llvm-pdbdump.cpp +++ llvm/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -31,9 +31,11 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/config.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.h" #include "llvm/DebugInfo/CodeView/ModuleDebugInlineeLinesFragment.h" #include "llvm/DebugInfo/CodeView/ModuleDebugLineFragment.h" +#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #include "llvm/DebugInfo/MSF/MSFBuilder.h" #include "llvm/DebugInfo/PDB/GenericError.h" #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" @@ -100,6 +102,9 @@ AnalyzeSubcommand("analyze", "Analyze various aspects of a PDB's structure"); +cl::SubCommand MergeSubcommand("merge", + "Merge multiple PDBs into a single PDB"); + cl::OptionCategory TypeCategory("Symbol Type Options"); cl::OptionCategory FilterCategory("Filtering and Sorting Options"); cl::OptionCategory OtherOptions("Other Options"); @@ -441,6 +446,15 @@ cl::desc(""), cl::Required, cl::sub(AnalyzeSubcommand)); } + +namespace merge { +cl::list InputFilenames(cl::Positional, + cl::desc(""), + cl::OneOrMore, cl::sub(MergeSubcommand)); +cl::opt + PdbOutputFile("pdb", cl::desc("the name of the PDB file to write"), + cl::sub(MergeSubcommand)); +} } static ExitOnError ExitOnErr; @@ -828,6 +842,54 @@ outs().flush(); } +static void mergePdbs() { + BumpPtrAllocator Allocator; + TypeTableBuilder MergedTpi(Allocator); + TypeTableBuilder MergedIpi(Allocator); + + // Create a Tpi and Ipi type table with all types from all input files. + for (const auto &Path : opts::merge::InputFilenames) { + std::unique_ptr Session; + auto &File = loadPDB(Path, Session); + if (File.hasPDBTpiStream()) { + auto &Tpi = ExitOnErr(File.getPDBTpiStream()); + ExitOnErr(codeview::mergeTypeStreams(MergedIpi, MergedTpi, nullptr, + Tpi.typeArray())); + } + if (File.hasPDBIpiStream()) { + auto &Ipi = ExitOnErr(File.getPDBIpiStream()); + ExitOnErr(codeview::mergeTypeStreams(MergedIpi, MergedTpi, nullptr, + Ipi.typeArray())); + } + } + + // Then write the PDB. + PDBFileBuilder Builder(Allocator); + ExitOnErr(Builder.initialize(4096)); + // Add each of the reserved streams. We might not put any data in them, + // but at least they have to be present. + for (uint32_t I = 0; I < kSpecialStreamCount; ++I) + ExitOnErr(Builder.getMsfBuilder().addStream(0)); + + auto &DestTpi = Builder.getTpiBuilder(); + auto &DestIpi = Builder.getIpiBuilder(); + MergedTpi.ForEachRecord( + [&DestTpi](TypeIndex TI, MutableArrayRef Data) { + DestTpi.addTypeRecord(Data, None); + }); + MergedIpi.ForEachRecord( + [&DestIpi](TypeIndex TI, MutableArrayRef Data) { + DestIpi.addTypeRecord(Data, None); + }); + + SmallString<64> OutFile = opts::merge::PdbOutputFile; + if (OutFile.empty()) { + OutFile = opts::merge::InputFilenames[0]; + llvm::sys::path::replace_extension(OutFile, "merged.pdb"); + } + ExitOnErr(Builder.commit(OutFile)); +} + int main(int argc_, const char *argv_[]) { // Print a stack trace if we signal out. sys::PrintStackTraceOnErrorSignal(argv_[0]); @@ -949,6 +1011,12 @@ exit(1); } diff(opts::diff::InputFilenames[0], opts::diff::InputFilenames[1]); + } else if (opts::MergeSubcommand) { + if (opts::merge::InputFilenames.size() < 2) { + errs() << "merge subcommand requires at least 2 input files.\n"; + exit(1); + } + mergePdbs(); } outs().flush();