From: Zachary Turner Date: Tue, 5 Dec 2017 23:58:18 +0000 (+0000) Subject: Teach llvm-pdbutil to dump types from object files. X-Git-Tag: android-x86-7.1-r4~7722 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=7c192eec2ff17d1815f0c3eef28e0846eeb0ba84;p=android-x86%2Fexternal-llvm.git Teach llvm-pdbutil to dump types from object files. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@319859 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h b/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h index 1d5117475bb..16d78692c83 100644 --- a/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h +++ b/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h @@ -67,6 +67,7 @@ public: void reset(ArrayRef Data, uint32_t RecordCountHint); void reset(StringRef Data, uint32_t RecordCountHint); + void reset(BinaryStreamReader &Reader, uint32_t RecordCountHint); uint32_t getOffsetOfType(TypeIndex Index); diff --git a/include/llvm/DebugInfo/CodeView/TypeHashing.h b/include/llvm/DebugInfo/CodeView/TypeHashing.h index 8f7d2abadca..4032bee1044 100644 --- a/include/llvm/DebugInfo/CodeView/TypeHashing.h +++ b/include/llvm/DebugInfo/CodeView/TypeHashing.h @@ -10,11 +10,14 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_TYPEHASHING_H #define LLVM_DEBUGINFO_CODEVIEW_TYPEHASHING_H +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/Hashing.h" + #include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/TypeCollection.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" -#include "llvm/ADT/DenseMapInfo.h" -#include "llvm/ADT/Hashing.h" +#include "llvm/Support/FormatProviders.h" namespace llvm { namespace codeview { @@ -29,7 +32,28 @@ struct LocallyHashedType { hash_code Hash; ArrayRef RecordData; + /// Given a type, compute its local hash. static LocallyHashedType hashType(ArrayRef RecordData); + + /// Given a sequence of types, compute all of the local hashes. + template + static std::vector hashTypes(Range &&Records) { + std::vector Hashes; + Hashes.reserve(std::distance(std::begin(Records), std::end(Records))); + for (const auto &R : Records) + Hashes.push_back(hashType(R)); + + return Hashes; + } + + static std::vector + hashTypeCollection(TypeCollection &Types) { + std::vector Hashes; + Types.ForEachRecord([&Hashes](TypeIndex TI, const CVType &Type) { + Hashes.push_back(hashType(Type.RecordData)); + }); + return Hashes; + } }; /// A globally hashed type represents a hash value that is sufficient to @@ -73,6 +97,15 @@ struct GloballyHashedType { return Hashes; } + + static std::vector + hashTypeCollection(TypeCollection &Types) { + std::vector Hashes; + Types.ForEachRecord([&Hashes](TypeIndex TI, const CVType &Type) { + Hashes.push_back(hashType(Type.RecordData, Hashes, Hashes)); + }); + return Hashes; + } }; } // namespace codeview @@ -114,6 +147,24 @@ template <> struct DenseMapInfo { } }; +template <> struct format_provider { +public: + static void format(const codeview::LocallyHashedType &V, + llvm::raw_ostream &Stream, StringRef Style) { + write_hex(Stream, V.Hash, HexPrintStyle::Upper, 8); + } +}; + +template <> struct format_provider { +public: + static void format(const codeview::GloballyHashedType &V, + llvm::raw_ostream &Stream, StringRef Style) { + for (uint8_t B : V.Hash) { + write_hex(Stream, B, HexPrintStyle::Upper, 2); + } + } +}; + } // namespace llvm #endif diff --git a/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp b/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp index bad291e8381..ca8007411ca 100644 --- a/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp +++ b/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp @@ -58,21 +58,27 @@ LazyRandomTypeCollection::LazyRandomTypeCollection(const CVTypeArray &Types, uint32_t NumRecords) : LazyRandomTypeCollection(Types, NumRecords, PartialOffsetArray()) {} -void LazyRandomTypeCollection::reset(StringRef Data, uint32_t RecordCountHint) { +void LazyRandomTypeCollection::reset(BinaryStreamReader &Reader, + uint32_t RecordCountHint) { Count = 0; PartialOffsets = PartialOffsetArray(); - BinaryStreamReader Reader(Data, support::little); - error(Reader.readArray(Types, Reader.getLength())); + error(Reader.readArray(Types, Reader.bytesRemaining())); // Clear and then resize, to make sure existing data gets destroyed. Records.clear(); Records.resize(RecordCountHint); } +void LazyRandomTypeCollection::reset(StringRef Data, uint32_t RecordCountHint) { + BinaryStreamReader Reader(Data, support::little); + reset(Reader, RecordCountHint); +} + void LazyRandomTypeCollection::reset(ArrayRef Data, uint32_t RecordCountHint) { - reset(toStringRef(Data), RecordCountHint); + BinaryStreamReader Reader(Data, support::little); + reset(Reader, RecordCountHint); } uint32_t LazyRandomTypeCollection::getOffsetOfType(TypeIndex Index) { diff --git a/test/DebugInfo/PDB/Inputs/obj-hashes-1.yaml b/test/DebugInfo/PDB/Inputs/obj-hashes-1.yaml new file mode 100644 index 00000000000..80d7b3c87b6 --- /dev/null +++ b/test/DebugInfo/PDB/Inputs/obj-hashes-1.yaml @@ -0,0 +1,50 @@ +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: '.debug$T' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + Types: + # char** + - Kind: LF_POINTER + Pointer: + ReferentType: 272 + Attrs: 32778 + # int** + - Kind: LF_POINTER + Pointer: + ReferentType: 372 + Attrs: 32778 + # int*** + - Kind: LF_POINTER + Pointer: + ReferentType: 4097 + Attrs: 32778 + # (char**, int***) + - Kind: LF_ARGLIST + ArgList: + ArgIndices: [ 4096, 4098 ] + # int** (char**, int***) + - Kind: LF_PROCEDURE + Procedure: + ReturnType: 4097 + CallConv: NearC + Options: [ None ] + ParameterCount: 2 + ArgumentList: 4099 +symbols: + - Name: '.debug$T' + Value: 0 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 68 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 2189213922 + Number: 6 +... diff --git a/test/DebugInfo/PDB/Inputs/obj-hashes-2.yaml b/test/DebugInfo/PDB/Inputs/obj-hashes-2.yaml new file mode 100644 index 00000000000..1c0aa2cc175 --- /dev/null +++ b/test/DebugInfo/PDB/Inputs/obj-hashes-2.yaml @@ -0,0 +1,55 @@ +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: '.debug$T' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + Types: + # int** + - Kind: LF_POINTER + Pointer: + ReferentType: 372 + Attrs: 32778 + # int*** + - Kind: LF_POINTER + Pointer: + ReferentType: 4096 + Attrs: 32778 + # char** + - Kind: LF_POINTER + Pointer: + ReferentType: 272 + Attrs: 32778 + # double** + - Kind: LF_POINTER + Pointer: + ReferentType: 321 + Attrs: 32778 + # (char**, int***) + - Kind: LF_ARGLIST + ArgList: + ArgIndices: [ 4098, 4097 ] + # int** (char**, int***) + - Kind: LF_PROCEDURE + Procedure: + ReturnType: 4096 + CallConv: NearC + Options: [ None ] + ParameterCount: 2 + ArgumentList: 4100 +symbols: + - Name: '.debug$T' + Value: 0 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 68 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 2189213922 + Number: 6 +... diff --git a/test/DebugInfo/PDB/obj-globalhash.test b/test/DebugInfo/PDB/obj-globalhash.test new file mode 100644 index 00000000000..629192905a4 --- /dev/null +++ b/test/DebugInfo/PDB/obj-globalhash.test @@ -0,0 +1,54 @@ +RUN: yaml2obj %p/Inputs/obj-hashes-1.yaml > %T/obj-hashes-1.obj +RUN: yaml2obj %p/Inputs/obj-hashes-2.yaml > %T/obj-hashes-2.obj +RUN: echo obj-hashes-1 > %T/hashes-combined.out +RUN: llvm-pdbutil dump -type-extras %T/obj-hashes-1.obj >> %T/hashes-combined.out +RUN: echo obj-hashes-2 >> %T/hashes-combined.out +RUN: llvm-pdbutil dump -type-extras %T/obj-hashes-2.obj >> %T/hashes-combined.out +RUN: cat %T/hashes-combined.out | FileCheck --check-prefix=CHECK-ONE %s +RUN: cat %T/hashes-combined.out | FileCheck --check-prefix=CHECK-TWO %s +RUN: cat %T/hashes-combined.out | FileCheck --check-prefix=CHECK-THREE %s +RUN: cat %T/hashes-combined.out | FileCheck --check-prefix=CHECK-FOUR %s +RUN: cat %T/hashes-combined.out | FileCheck --check-prefix=CHECK-FIVE %s +RUN: cat %T/hashes-combined.out | FileCheck --check-prefix=CHECK-SIX %s + +; char**. Both the local and global hashes should be the same, since the only +; back-references are for simple types which have fixed indices. +CHECK-ONE: obj-hashes-1 +CHECK-ONE: TI: 0x1001, LocalHash: 8C051421, GlobalHash: 8B2BA87CC27BF9D290A31A6070FA296AAA577E53 +CHECK-ONE: obj-hashes-2 +CHECK-ONE: TI: 0x1000, LocalHash: 8C051421, GlobalHash: 8B2BA87CC27BF9D290A31A6070FA296AAA577E53 + +; int**. Same as char**, both the local and global hashes should be the same. +CHECK-TWO: obj-hashes-1 +CHECK-TWO: TI: 0x1000, LocalHash: 8A50855E, GlobalHash: 1522A98D88FAF71B618D97BCAC2B89A424EC4805 +CHECK-TWO: obj-hashes-2 +CHECK-TWO: TI: 0x1002, LocalHash: 8A50855E, GlobalHash: 1522A98D88FAF71B618D97BCAC2B89A424EC4805 + +; int***. Different local hashes, since the referent type (int**) is not at the +; same TypeIndex in both streams. Same global hash, since they represent the +; same record. +CHECK-THREE: obj-hashes-1 +CHECK-THREE: TI: 0x1002, LocalHash: C1B0E34B, GlobalHash: EC11CE9F78D6BF61F8D913A9E2C98293782A7EB4 +CHECK-THREE: obj-hashes-2 +CHECK-THREE: TI: 0x1001, LocalHash: 813E462C, GlobalHash: EC11CE9F78D6BF61F8D913A9E2C98293782A7EB4 + +; arg list (char**, int***). Different local hashes, since the parameter types +; both occur at different TypeIndices in their respective input streams. Same +; global hash, since the global hash of all referenced types is the same in +; both streams. +CHECK-FOUR: obj-hashes-1 +CHECK-FOUR: TI: 0x1003, LocalHash: F9B7D117, GlobalHash: 1088AD64CEBC88D9E015058A159516AF20B79286 +CHECK-FOUR: obj-hashes-2 +CHECK-FOUR: TI: 0x1004, LocalHash: 29800A81, GlobalHash: 1088AD64CEBC88D9E015058A159516AF20B79286 + +; double**. This is only in stream 2, as a means to throw off the indexing. +CHECK-FIVE: obj-hashes-1 +CHECK-FIVE: obj-hashes-2 +CHECK-FIVE: TI: 0x1003, LocalHash: A8EEF56D, GlobalHash: 7803BBDB2947EF46BEA2310D102BD08F68315506 + +; int** (char**, int***). For the same logic as described in previous records, +; these two records have the same global hash but different local hashes. +CHECK-SIX: obj-hashes-1 +CHECK-SIX: TI: 0x1004, LocalHash: 25FFC8CB, GlobalHash: 457ABCB8AB70407594B5D72BF471B6BDECC99BC9 +CHECK-SIX: obj-hashes-2 +CHECK-SIX: TI: 0x1005, LocalHash: F34B0F86, GlobalHash: 457ABCB8AB70407594B5D72BF471B6BDECC99BC9 diff --git a/tools/llvm-pdbutil/DumpOutputStyle.cpp b/tools/llvm-pdbutil/DumpOutputStyle.cpp index 5b02d68bc7a..84355e7bd2e 100644 --- a/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -38,6 +38,7 @@ #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" #include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeHashing.h" #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" @@ -50,8 +51,8 @@ #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" -#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/PDBExtras.h" @@ -135,16 +136,23 @@ Error DumpOutputStyle::dump() { return EC; } - if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || - opts::dump::DumpTypeExtras) { - if (auto EC = dumpTpiStream(StreamTPI)) - return EC; - } + if (File.isObj()) { + if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || + opts::dump::DumpTypeExtras) + if (auto EC = dumpTypesFromObjectFile()) + return EC; + } else { + if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || + opts::dump::DumpTypeExtras) { + if (auto EC = dumpTpiStream(StreamTPI)) + return EC; + } - if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() || - opts::dump::DumpIdExtras) { - if (auto EC = dumpTpiStream(StreamIPI)) - return EC; + if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() || + opts::dump::DumpIdExtras) { + if (auto EC = dumpTpiStream(StreamIPI)) + return EC; + } } if (opts::dump::DumpGlobals) { @@ -913,15 +921,17 @@ static void buildDepSet(LazyRandomTypeCollection &Types, } } -static void dumpFullTypeStream(LinePrinter &Printer, - LazyRandomTypeCollection &Types, - TpiStream &Stream, bool Bytes, bool Extras) { - Printer.formatLine("Showing {0:N} records", Stream.getNumTypeRecords()); - uint32_t Width = - NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); +static void +dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, + uint32_t NumHashBuckets, + FixedStreamArray HashValues, + bool Bytes, bool Extras) { + + Printer.formatLine("Showing {0:N} records", Types.size()); + uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + Types.size()); MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, - Stream.getNumHashBuckets(), Stream.getHashValues()); + NumHashBuckets, HashValues); if (auto EC = codeview::visitTypeStream(Types, V)) { Printer.formatLine("An error occurred dumping type records: {0}", @@ -967,6 +977,55 @@ static void dumpPartialTypeStream(LinePrinter &Printer, } } +Error DumpOutputStyle::dumpTypesFromObjectFile() { + LazyRandomTypeCollection Types(100); + + for (const auto &S : getObj().sections()) { + StringRef SectionName; + if (auto EC = S.getName(SectionName)) + return errorCodeToError(EC); + + if (SectionName != ".debug$T") + continue; + StringRef Contents; + if (auto EC = S.getContents(Contents)) + return errorCodeToError(EC); + + uint32_t Magic; + BinaryStreamReader Reader(Contents, llvm::support::little); + if (auto EC = Reader.readInteger(Magic)) + return EC; + if (Magic != COFF::DEBUG_SECTION_MAGIC) + return make_error("Invalid CodeView debug section.", + inconvertibleErrorCode()); + + Types.reset(Reader, 100); + + if (opts::dump::DumpTypes) { + dumpFullTypeStream(P, Types, 0, {}, opts::dump::DumpTypeData, false); + } else if (opts::dump::DumpTypeExtras) { + auto LocalHashes = LocallyHashedType::hashTypeCollection(Types); + auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types); + assert(LocalHashes.size() == GlobalHashes.size()); + + P.formatLine("Local / Global hashes:"); + TypeIndex TI(TypeIndex::FirstNonSimpleIndex); + for (const auto &H : zip(LocalHashes, GlobalHashes)) { + AutoIndent Indent2(P); + LocallyHashedType &L = std::get<0>(H); + GloballyHashedType &G = std::get<1>(H); + + P.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI, L, G); + + ++TI; + } + P.NewLine(); + } + } + + return Error::success(); +} + Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); @@ -977,10 +1036,7 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { } AutoIndent Indent(P); - if (File.isObj()) { - P.formatLine("Dumping types is not supported for object files"); - return Error::success(); - } + assert(!File.isObj()); bool Present = false; bool DumpTypes = false; @@ -1017,7 +1073,8 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { if (DumpTypes || !Indices.empty()) { if (Indices.empty()) - dumpFullTypeStream(P, Types, Stream, DumpBytes, DumpExtras); + dumpFullTypeStream(P, Types, Stream.getNumHashBuckets(), + Stream.getHashValues(), DumpBytes, DumpExtras); else { std::vector TiList(Indices.begin(), Indices.end()); dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras, diff --git a/tools/llvm-pdbutil/DumpOutputStyle.h b/tools/llvm-pdbutil/DumpOutputStyle.h index 85598307863..3ce2884b271 100644 --- a/tools/llvm-pdbutil/DumpOutputStyle.h +++ b/tools/llvm-pdbutil/DumpOutputStyle.h @@ -80,6 +80,7 @@ private: Error dumpXmi(); Error dumpXme(); Error dumpTpiStream(uint32_t StreamIdx); + Error dumpTypesFromObjectFile(); Error dumpModules(); Error dumpModuleFiles(); Error dumpModuleSymsForPdb(); diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index ca2a9ec1615..0c7f7db5760 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -38,6 +38,7 @@ #include "llvm/DebugInfo/CodeView/SymbolDumper.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeHashing.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"