From d60319233028e2e74223fa6627e0ae4c7cc34f90 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Fri, 24 Mar 2017 17:26:38 +0000 Subject: [PATCH] [PDB] Split item and type records when merging type streams Summary: MSVC does this when producing a PDB. Reviewers: ruiu Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D31316 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@298717 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../DebugInfo/CodeView/TypeStreamMerger.h | 5 +- lib/DebugInfo/CodeView/TypeStreamMerger.cpp | 140 +++++++++++------- test/tools/llvm-readobj/codeview-merging.test | 56 ++++++- tools/llvm-readobj/COFFDumper.cpp | 49 ++++-- tools/llvm-readobj/ObjDumper.h | 6 +- tools/llvm-readobj/llvm-readobj.cpp | 10 +- 6 files changed, 189 insertions(+), 77 deletions(-) diff --git a/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h b/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h index a06b5b7ceed..2246f197e78 100644 --- a/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h +++ b/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h @@ -21,8 +21,9 @@ namespace codeview { class TypeServerHandler; /// Merges one type stream into another. Returns true on success. -Error mergeTypeStreams(TypeTableBuilder &DestStream, TypeServerHandler *Handler, - const CVTypeArray &Types); +Error mergeTypeStreams(TypeTableBuilder &DestIdStream, + TypeTableBuilder &DestTypeStream, + TypeServerHandler *Handler, const CVTypeArray &Types); } // end namespace codeview } // end namespace llvm diff --git a/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/lib/DebugInfo/CodeView/TypeStreamMerger.cpp index b01eaa87492..07d6a5684bb 100644 --- a/lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ b/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -52,11 +52,17 @@ namespace { /// - If the type record already exists in the destination stream, discard it /// and update the type index map to forward the source type index to the /// existing destination type index. +/// +/// As an additional complication, type stream merging actually produces two +/// streams: an item (or IPI) stream and a type stream, as this is what is +/// actually stored in the final PDB. We choose which records go where by +/// looking at the record kind. class TypeStreamMerger : public TypeVisitorCallbacks { public: - TypeStreamMerger(TypeTableBuilder &DestStream, TypeServerHandler *Handler) - : DestStream(DestStream), FieldListBuilder(DestStream), Handler(Handler) { - } + TypeStreamMerger(TypeTableBuilder &DestIdStream, + TypeTableBuilder &DestTypeStream, TypeServerHandler *Handler) + : DestIdStream(DestIdStream), DestTypeStream(DestTypeStream), + FieldListBuilder(DestTypeStream), Handler(Handler) {} /// TypeVisitorCallbacks overrides. #define TYPE_RECORD(EnumName, EnumVal, Name) \ @@ -85,7 +91,18 @@ private: std::move(*LastError), llvm::make_error(cv_error_code::corrupt_record)); } - IndexMap.push_back(DestStream.writeKnownType(R)); + IndexMap.push_back(DestTypeStream.writeKnownType(R)); + return Error::success(); + } + + template + Error writeIdRecord(RecordType &R, bool RemapSuccess) { + if (!RemapSuccess) { + LastError = joinErrors( + std::move(*LastError), + llvm::make_error(cv_error_code::corrupt_record)); + } + IndexMap.push_back(DestIdStream.writeKnownType(R)); return Error::success(); } @@ -104,7 +121,8 @@ private: BumpPtrAllocator Allocator; - TypeTableBuilder &DestStream; + TypeTableBuilder &DestIdStream; + TypeTableBuilder &DestTypeStream; FieldListRecordBuilder FieldListBuilder; TypeServerHandler *Handler; @@ -158,6 +176,62 @@ bool TypeStreamMerger::remapIndex(TypeIndex &Idx) { return false; } +//----------------------------------------------------------------------------// +// Item records +//----------------------------------------------------------------------------// + +Error TypeStreamMerger::visitKnownRecord(CVType &, FuncIdRecord &R) { + bool Success = true; + Success &= remapIndex(R.ParentScope); + Success &= remapIndex(R.FunctionType); + return writeIdRecord(R, Success); +} + +Error TypeStreamMerger::visitKnownRecord(CVType &, MemberFuncIdRecord &R) { + bool Success = true; + Success &= remapIndex(R.ClassType); + Success &= remapIndex(R.FunctionType); + return writeIdRecord(R, Success); +} + +Error TypeStreamMerger::visitKnownRecord(CVType &, StringIdRecord &R) { + return writeIdRecord(R, remapIndex(R.Id)); +} + +Error TypeStreamMerger::visitKnownRecord(CVType &, StringListRecord &R) { + bool Success = true; + for (TypeIndex &Str : R.StringIndices) + Success &= remapIndex(Str); + return writeIdRecord(R, Success); +} + +Error TypeStreamMerger::visitKnownRecord(CVType &, BuildInfoRecord &R) { + bool Success = true; + for (TypeIndex &Arg : R.ArgIndices) + Success &= remapIndex(Arg); + return writeIdRecord(R, Success); +} + +Error TypeStreamMerger::visitKnownRecord(CVType &, UdtSourceLineRecord &R) { + bool Success = true; + Success &= remapIndex(R.UDT); + Success &= remapIndex(R.SourceFile); + // FIXME: Translate UdtSourceLineRecord into UdtModSourceLineRecords in the + // IPI stream. + return writeIdRecord(R, Success); +} + +Error TypeStreamMerger::visitKnownRecord(CVType &, UdtModSourceLineRecord &R) { + bool Success = true; + Success &= remapIndex(R.UDT); + Success &= remapIndex(R.SourceFile); + return writeIdRecord(R, Success); +} + +//----------------------------------------------------------------------------// +// Type records +//----------------------------------------------------------------------------// + Error TypeStreamMerger::visitKnownRecord(CVType &, ModifierRecord &R) { return writeRecord(R, remapIndex(R.ModifiedType)); } @@ -178,13 +252,6 @@ Error TypeStreamMerger::visitKnownRecord(CVType &, MemberFunctionRecord &R) { return writeRecord(R, Success); } -Error TypeStreamMerger::visitKnownRecord(CVType &, MemberFuncIdRecord &R) { - bool Success = true; - Success &= remapIndex(R.ClassType); - Success &= remapIndex(R.FunctionType); - return writeRecord(R, Success); -} - Error TypeStreamMerger::visitKnownRecord(CVType &, ArgListRecord &R) { bool Success = true; for (TypeIndex &Arg : R.ArgIndices) @@ -192,13 +259,6 @@ Error TypeStreamMerger::visitKnownRecord(CVType &, ArgListRecord &R) { return writeRecord(R, Success); } -Error TypeStreamMerger::visitKnownRecord(CVType &, StringListRecord &R) { - bool Success = true; - for (TypeIndex &Str : R.StringIndices) - Success &= remapIndex(Str); - return writeRecord(R, Success); -} - Error TypeStreamMerger::visitKnownRecord(CVType &, PointerRecord &R) { bool Success = true; Success &= remapIndex(R.ReferentType); @@ -245,38 +305,6 @@ Error TypeStreamMerger::visitKnownRecord(CVType &, TypeServer2Record &R) { return writeRecord(R, true); } -Error TypeStreamMerger::visitKnownRecord(CVType &, StringIdRecord &R) { - return writeRecord(R, remapIndex(R.Id)); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &, FuncIdRecord &R) { - bool Success = true; - Success &= remapIndex(R.ParentScope); - Success &= remapIndex(R.FunctionType); - return writeRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &, UdtSourceLineRecord &R) { - bool Success = true; - Success &= remapIndex(R.UDT); - Success &= remapIndex(R.SourceFile); - return writeRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &, UdtModSourceLineRecord &R) { - bool Success = true; - Success &= remapIndex(R.UDT); - Success &= remapIndex(R.SourceFile); - return writeRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &, BuildInfoRecord &R) { - bool Success = true; - for (TypeIndex &Arg : R.ArgIndices) - Success &= remapIndex(Arg); - return writeRecord(R, Success); -} - Error TypeStreamMerger::visitKnownRecord(CVType &, VFTableRecord &R) { bool Success = true; Success &= remapIndex(R.CompleteClass); @@ -300,6 +328,10 @@ Error TypeStreamMerger::visitKnownRecord(CVType &, FieldListRecord &R) { return Error::success(); } +//----------------------------------------------------------------------------// +// Member records +//----------------------------------------------------------------------------// + Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, NestedTypeRecord &R) { return writeMember(R, remapIndex(R.Type)); @@ -381,8 +413,10 @@ Error TypeStreamMerger::mergeStream(const CVTypeArray &Types) { return Ret; } -Error llvm::codeview::mergeTypeStreams(TypeTableBuilder &DestStream, +Error llvm::codeview::mergeTypeStreams(TypeTableBuilder &DestIdStream, + TypeTableBuilder &DestTypeStream, TypeServerHandler *Handler, const CVTypeArray &Types) { - return TypeStreamMerger(DestStream, Handler).mergeStream(Types); + return TypeStreamMerger(DestIdStream, DestTypeStream, Handler) + .mergeStream(Types); } diff --git a/test/tools/llvm-readobj/codeview-merging.test b/test/tools/llvm-readobj/codeview-merging.test index 60894eff33e..4d453e5a116 100644 --- a/test/tools/llvm-readobj/codeview-merging.test +++ b/test/tools/llvm-readobj/codeview-merging.test @@ -21,6 +21,15 @@ RUN: llvm-readobj -codeview %S/Inputs/codeview-merging-1.obj | FileCheck %s --ch RUN: llvm-readobj -codeview %S/Inputs/codeview-merging-2.obj | FileCheck %s --check-prefix=OBJ2 RUN: llvm-readobj -codeview-merged-types %S/Inputs/codeview-merging-1.obj %S/Inputs/codeview-merging-2.obj | FileCheck %s +OBJ1: Procedure ({{.*}}) { +OBJ1-NEXT: TypeLeafKind: LF_PROCEDURE (0x1008) +OBJ1-NEXT: ReturnType: int (0x74) +OBJ1-NEXT: CallingConvention: NearC (0x0) +OBJ1-NEXT: FunctionOptions [ (0x0) +OBJ1-NEXT: ] +OBJ1-NEXT: NumParameters: 1 +OBJ1-NEXT: ArgListType: (A*) (0x1002) +OBJ1-NEXT: } OBJ1: FuncId (0x100D) { OBJ1-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601) OBJ1-NEXT: ParentScope: 0x0 @@ -50,16 +59,55 @@ OBJ2-NEXT: Name: g OBJ2-NEXT: } OBJ2-NOT: FuncId -CHECK: FuncId (0x100D) { +CHECK: MergedTypeStream [ +CHECK: Procedure ({{.*}}) { +CHECK-NEXT: TypeLeafKind: LF_PROCEDURE (0x1008) +CHECK-NEXT: ReturnType: int (0x74) +CHECK-NEXT: CallingConvention: NearC (0x0) +CHECK-NEXT: FunctionOptions [ (0x0) +CHECK-NEXT: ] +CHECK-NEXT: NumParameters: 1 +CHECK-NEXT: ArgListType: (A*) (0x1002) +CHECK-NEXT: } +CHECK: Struct (0x1007) { +CHECK-NEXT: TypeLeafKind: LF_STRUCTURE (0x1505) +CHECK-NEXT: MemberCount: 1 +CHECK-NEXT: Properties [ (0x200) +CHECK-NEXT: HasUniqueName (0x200) +CHECK-NEXT: ] +CHECK-NEXT: FieldList: (0x1006) +CHECK-NEXT: DerivedFrom: 0x0 +CHECK-NEXT: VShape: 0x0 +CHECK-NEXT: SizeOf: 8 +CHECK-NEXT: Name: B +CHECK-NEXT: LinkageName: .?AUB@@ +CHECK-NEXT: } +CHECK: ] + +CHECK: MergedIDStream [ +CHECK-NEXT: StringId (0x1000) { +CHECK-NEXT: TypeLeafKind: LF_STRING_ID (0x1605) +CHECK-NEXT: Id: 0x0 +CHECK-NEXT: StringData: d:\src\llvm\build\t.cpp +CHECK-NEXT: } +# Test that we contextually dump item ids and type ids from different databases. +CHECK-NEXT: UdtSourceLine (0x1001) { +CHECK-NEXT: TypeLeafKind: LF_UDT_SRC_LINE (0x1606) +CHECK-NEXT: UDT: B (0x1007) +CHECK-NEXT: SourceFile: d:\src\llvm\build\t.cpp (0x1000) +CHECK-NEXT: LineNumber: 3 +CHECK-NEXT: } +CHECK: FuncId (0x1002) { CHECK-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601) CHECK-NEXT: ParentScope: 0x0 -CHECK-NEXT: FunctionType: int (B*) (0x100C) +CHECK-NEXT: FunctionType: int (B*) CHECK-NEXT: Name: g CHECK-NEXT: } -CHECK-NEXT: FuncId (0x100E) { +CHECK-NEXT: FuncId (0x1003) { CHECK-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601) CHECK-NEXT: ParentScope: 0x0 -CHECK-NEXT: FunctionType: int (A*) (0x1003) +CHECK-NEXT: FunctionType: int (A*) CHECK-NEXT: Name: f CHECK-NEXT: } CHECK-NOT: FuncId +CHECK: ] diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index 263f66617f9..10427654167 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -79,7 +79,8 @@ public: void printCOFFBaseReloc() override; void printCOFFDebugDirectory() override; void printCodeViewDebugInfo() override; - void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVTypes) override; + void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVIDs, + llvm::codeview::TypeTableBuilder &CVTypes) override; void printStackMap() const override; private: void printSymbol(const SymbolRef &Sym); @@ -1064,7 +1065,8 @@ void COFFDumper::printFileNameForOffset(StringRef Label, uint32_t FileOffset) { W.printHex(Label, getFileNameForFileOffset(FileOffset), FileOffset); } -void COFFDumper::mergeCodeViewTypes(TypeTableBuilder &CVTypes) { +void COFFDumper::mergeCodeViewTypes(TypeTableBuilder &CVIDs, + TypeTableBuilder &CVTypes) { for (const SectionRef &S : Obj->sections()) { StringRef SectionName; error(S.getName(SectionName)); @@ -1086,7 +1088,7 @@ void COFFDumper::mergeCodeViewTypes(TypeTableBuilder &CVTypes) { error(object_error::parse_failed); } - if (auto EC = mergeTypeStreams(CVTypes, nullptr, Types)) { + if (auto EC = mergeTypeStreams(CVIDs, CVTypes, nullptr, Types)) { consumeError(std::move(EC)); return error(object_error::parse_failed); } @@ -1551,20 +1553,43 @@ void COFFDumper::printStackMap() const { } void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer, + llvm::codeview::TypeTableBuilder &IDTable, llvm::codeview::TypeTableBuilder &CVTypes) { // Flatten it first, then run our dumper on it. - ListScope S(Writer, "MergedTypeStream"); - SmallString<0> Buf; + SmallString<0> TypeBuf; CVTypes.ForEachRecord([&](TypeIndex TI, ArrayRef Record) { - Buf.append(Record.begin(), Record.end()); + TypeBuf.append(Record.begin(), Record.end()); }); TypeDatabase TypeDB; - CVTypeDumper CVTD(TypeDB); - TypeDumpVisitor TDV(TypeDB, &Writer, opts::CodeViewSubsectionBytes); - if (auto EC = - CVTD.dump({Buf.str().bytes_begin(), Buf.str().bytes_end()}, TDV)) { - Writer.flush(); - error(llvm::errorToErrorCode(std::move(EC))); + { + ListScope S(Writer, "MergedTypeStream"); + CVTypeDumper CVTD(TypeDB); + TypeDumpVisitor TDV(TypeDB, &Writer, opts::CodeViewSubsectionBytes); + if (auto EC = CVTD.dump( + {TypeBuf.str().bytes_begin(), TypeBuf.str().bytes_end()}, TDV)) { + Writer.flush(); + error(llvm::errorToErrorCode(std::move(EC))); + } + } + + // Flatten the id stream and print it next. The ID stream refers to names from + // the type stream. + SmallString<0> IDBuf; + IDTable.ForEachRecord([&](TypeIndex TI, ArrayRef Record) { + IDBuf.append(Record.begin(), Record.end()); + }); + + { + ListScope S(Writer, "MergedIDStream"); + TypeDatabase IDDB; + CVTypeDumper CVTD(IDDB); + TypeDumpVisitor TDV(TypeDB, &Writer, opts::CodeViewSubsectionBytes); + TDV.setItemDB(IDDB); + if (auto EC = CVTD.dump( + {IDBuf.str().bytes_begin(), IDBuf.str().bytes_end()}, TDV)) { + Writer.flush(); + error(llvm::errorToErrorCode(std::move(EC))); + } } } diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index 109087be79b..ff780dae578 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -68,7 +68,8 @@ public: virtual void printCOFFBaseReloc() { } virtual void printCOFFDebugDirectory() { } virtual void printCodeViewDebugInfo() { } - virtual void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVTypes) {} + virtual void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVIDs, + llvm::codeview::TypeTableBuilder &CVTypes) {} // Only implemented for MachO. virtual void printMachODataInCode() { } @@ -103,7 +104,8 @@ std::error_code createWasmDumper(const object::ObjectFile *Obj, void dumpCOFFImportFile(const object::COFFImportFile *File); void dumpCodeViewMergedTypes(ScopedPrinter &Writer, - llvm::codeview::TypeTableBuilder &CVTypes); + llvm::codeview::TypeTableBuilder &IDTable, + llvm::codeview::TypeTableBuilder &TypeTable); } // namespace llvm diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index d9178285751..bc2a62e799a 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -338,10 +338,12 @@ static bool isMipsArch(unsigned Arch) { } namespace { struct ReadObjTypeTableBuilder { - ReadObjTypeTableBuilder() : Allocator(), Builder(Allocator) {} + ReadObjTypeTableBuilder() + : Allocator(), IDTable(Allocator), TypeTable(Allocator) {} llvm::BumpPtrAllocator Allocator; - llvm::codeview::TypeTableBuilder Builder; + llvm::codeview::TypeTableBuilder IDTable; + llvm::codeview::TypeTableBuilder TypeTable; }; } static ReadObjTypeTableBuilder CVTypes; @@ -446,7 +448,7 @@ static void dumpObject(const ObjectFile *Obj) { if (opts::CodeView) Dumper->printCodeViewDebugInfo(); if (opts::CodeViewMergedTypes) - Dumper->mergeCodeViewTypes(CVTypes.Builder); + Dumper->mergeCodeViewTypes(CVTypes.IDTable, CVTypes.TypeTable); } if (Obj->isMachO()) { if (opts::MachODataInCode) @@ -551,7 +553,7 @@ int main(int argc, const char *argv[]) { if (opts::CodeViewMergedTypes) { ScopedPrinter W(outs()); - dumpCodeViewMergedTypes(W, CVTypes.Builder); + dumpCodeViewMergedTypes(W, CVTypes.IDTable, CVTypes.TypeTable); } return 0; -- 2.50.1