//===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "DumpOutputStyle.h" #include "FormatUtil.h" #include "MinimalSymbolDumper.h" #include "MinimalTypeDumper.h" #include "StreamUtil.h" #include "llvm-pdbutil.h" #include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" #include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" #include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" #include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" #include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" #include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h" #include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" #include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h" #include "llvm/DebugInfo/CodeView/EnumTables.h" #include "llvm/DebugInfo/CodeView/Formatters.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/Line.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/CodeView/SymbolDumper.h" #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" #include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" #include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/EnumTables.h" #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #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/RawError.h" #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/PDBExtras.h" #include "llvm/Object/COFF.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" #include using namespace llvm; using namespace llvm::codeview; using namespace llvm::msf; using namespace llvm::pdb; DumpOutputStyle::DumpOutputStyle(PDBFile &File) : File(File), P(2, false, outs()) {} Error DumpOutputStyle::dump() { if (opts::dump::DumpSummary) { if (auto EC = dumpFileSummary()) return EC; P.NewLine(); } if (opts::dump::DumpStreams) { if (auto EC = dumpStreamSummary()) return EC; P.NewLine(); } if (opts::dump::DumpStringTable) { if (auto EC = dumpStringTable()) return EC; P.NewLine(); } if (opts::dump::DumpModules) { if (auto EC = dumpModules()) return EC; } if (opts::dump::DumpModuleFiles) { if (auto EC = dumpModuleFiles()) return EC; } if (opts::dump::DumpLines) { if (auto EC = dumpLines()) return EC; } if (opts::dump::DumpInlineeLines) { if (auto EC = dumpInlineeLines()) return EC; } if (opts::dump::DumpXmi) { if (auto EC = dumpXmi()) return EC; } if (opts::dump::DumpXme) { if (auto EC = dumpXme()) return EC; } 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::DumpPublics) { if (auto EC = dumpPublics()) return EC; } if (opts::dump::DumpSymbols) { if (auto EC = dumpModuleSyms()) return EC; } if (opts::dump::DumpSectionContribs) { if (auto EC = dumpSectionContribs()) return EC; } if (opts::dump::DumpSectionMap) { if (auto EC = dumpSectionMap()) return EC; } return Error::success(); } static void printHeader(LinePrinter &P, const Twine &S) { P.NewLine(); P.formatLine("{0,=60}", S); P.formatLine("{0}", fmt_repeat('=', 60)); } Error DumpOutputStyle::dumpFileSummary() { printHeader(P, "Summary"); ExitOnError Err("Invalid PDB Format"); AutoIndent Indent(P); P.formatLine("Block Size: {0}", File.getBlockSize()); P.formatLine("Number of blocks: {0}", File.getBlockCount()); P.formatLine("Number of streams: {0}", File.getNumStreams()); auto &PS = Err(File.getPDBInfoStream()); P.formatLine("Signature: {0}", PS.getSignature()); P.formatLine("Age: {0}", PS.getAge()); P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid)); P.formatLine("Features: {0:x+}", static_cast(PS.getFeatures())); P.formatLine("Has Debug Info: {0}", File.hasPDBDbiStream()); P.formatLine("Has Types: {0}", File.hasPDBTpiStream()); P.formatLine("Has IDs: {0}", File.hasPDBIpiStream()); P.formatLine("Has Globals: {0}", File.hasPDBGlobalsStream()); P.formatLine("Has Publics: {0}", File.hasPDBPublicsStream()); if (File.hasPDBDbiStream()) { auto &DBI = Err(File.getPDBDbiStream()); P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked()); P.formatLine("Has conflicting types: {0}", DBI.hasCTypes()); P.formatLine("Is stripped: {0}", DBI.isStripped()); } return Error::success(); } Error DumpOutputStyle::dumpStreamSummary() { printHeader(P, "Streams"); if (StreamPurposes.empty()) discoverStreamPurposes(File, StreamPurposes); AutoIndent Indent(P); uint32_t StreamCount = File.getNumStreams(); for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { P.formatLine( "Stream {0}: [{1}] ({2} bytes)", fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)), StreamPurposes[StreamIdx], File.getStreamByteSize(StreamIdx)); if (opts::dump::DumpStreamBlocks) { auto Blocks = File.getStreamBlockList(StreamIdx); std::vector BV(Blocks.begin(), Blocks.end()); P.formatLine(" {0} Blocks: [{1}]", fmt_repeat(' ', NumDigits(StreamCount)), make_range(BV.begin(), BV.end())); } } return Error::success(); } static Expected getModuleDebugStream(PDBFile &File, uint32_t Index) { ExitOnError Err("Unexpected error"); auto &Dbi = Err(File.getPDBDbiStream()); const auto &Modules = Dbi.modules(); auto Modi = Modules.getModuleDescriptor(Index); uint16_t ModiStream = Modi.getModuleStreamIndex(); if (ModiStream == kInvalidStreamIndex) return make_error(raw_error_code::no_stream, "Module stream not present"); auto ModStreamData = MappedBlockStream::createIndexedStream( File.getMsfLayout(), File.getMsfBuffer(), ModiStream, File.getAllocator()); ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); if (auto EC = ModS.reload()) return make_error(raw_error_code::corrupt_file, "Invalid module stream"); return std::move(ModS); } static std::string formatChecksumKind(FileChecksumKind Kind) { switch (Kind) { RETURN_CASE(FileChecksumKind, None, "None"); RETURN_CASE(FileChecksumKind, MD5, "MD5"); RETURN_CASE(FileChecksumKind, SHA1, "SHA-1"); RETURN_CASE(FileChecksumKind, SHA256, "SHA-256"); } return formatUnknownEnum(Kind); } namespace { class StringsAndChecksumsPrinter { const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) { ExitOnError Err("Unexpected error processing modules"); return Err(File.getStringTable()).getStringTable(); } template void formatInternal(LinePrinter &Printer, bool Append, Args &&... args) const { if (Append) Printer.format(std::forward(args)...); else Printer.formatLine(std::forward(args)...); } public: StringsAndChecksumsPrinter(PDBFile &File, uint32_t Modi) : Records(extractStringTable(File)) { auto MDS = getModuleDebugStream(File, Modi); if (!MDS) { consumeError(MDS.takeError()); return; } DebugStream = llvm::make_unique(std::move(*MDS)); Records.initialize(MDS->subsections()); if (Records.hasChecksums()) { for (const auto &Entry : Records.checksums()) { auto S = Records.strings().getString(Entry.FileNameOffset); if (!S) continue; ChecksumsByFile[*S] = Entry; } } } Expected getNameFromStringTable(uint32_t Offset) const { return Records.strings().getString(Offset); } void formatFromFileName(LinePrinter &Printer, StringRef File, bool Append = false) const { auto FC = ChecksumsByFile.find(File); if (FC == ChecksumsByFile.end()) { formatInternal(Printer, Append, "- (no checksum) {0}", File); return; } formatInternal(Printer, Append, "- ({0}: {1}) {2}", formatChecksumKind(FC->getValue().Kind), toHex(FC->getValue().Checksum), File); } void formatFromChecksumsOffset(LinePrinter &Printer, uint32_t Offset, bool Append = false) const { if (!Records.hasChecksums()) { formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); return; } auto Iter = Records.checksums().getArray().at(Offset); if (Iter == Records.checksums().getArray().end()) { formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); return; } uint32_t FO = Iter->FileNameOffset; auto ExpectedFile = getNameFromStringTable(FO); if (!ExpectedFile) { formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); consumeError(ExpectedFile.takeError()); return; } if (Iter->Kind == FileChecksumKind::None) { formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile); } else { formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile, formatChecksumKind(Iter->Kind), toHex(Iter->Checksum)); } } std::unique_ptr DebugStream; StringsAndChecksumsRef Records; StringMap ChecksumsByFile; }; } // namespace template static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel, CallbackT Callback) { AutoIndent Indent(P); if (!File.hasPDBDbiStream()) { P.formatLine("DBI Stream not present"); return; } ExitOnError Err("Unexpected error processing modules"); auto &Stream = Err(File.getPDBDbiStream()); const DbiModuleList &Modules = Stream.modules(); uint32_t Count = Modules.getModuleCount(); uint32_t Digits = NumDigits(Count); for (uint32_t I = 0; I < Count; ++I) { auto Modi = Modules.getModuleDescriptor(I); P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, Digits), Modi.getModuleName()); StringsAndChecksumsPrinter Strings(File, I); AutoIndent Indent2(P, IndentLevel); Callback(I, Strings); } } template static void iterateModuleSubsections( PDBFile &File, LinePrinter &P, uint32_t IndentLevel, llvm::function_ref Callback) { iterateModules( File, P, IndentLevel, [&File, &Callback](uint32_t Modi, StringsAndChecksumsPrinter &Strings) { auto MDS = getModuleDebugStream(File, Modi); if (!MDS) { consumeError(MDS.takeError()); return; } for (const auto &SS : MDS->subsections()) { SubsectionT Subsection; if (SS.kind() != Subsection.kind()) continue; BinaryStreamReader Reader(SS.getRecordData()); if (auto EC = Subsection.initialize(Reader)) continue; Callback(Modi, Strings, Subsection); } }); } Error DumpOutputStyle::dumpModules() { printHeader(P, "Modules"); AutoIndent Indent(P); if (!File.hasPDBDbiStream()) { P.formatLine("DBI Stream not present"); return Error::success(); } ExitOnError Err("Unexpected error processing modules"); auto &Stream = Err(File.getPDBDbiStream()); const DbiModuleList &Modules = Stream.modules(); uint32_t Count = Modules.getModuleCount(); uint32_t Digits = NumDigits(Count); for (uint32_t I = 0; I < Count; ++I) { auto Modi = Modules.getModuleDescriptor(I); P.formatLine("Mod {0:4} | Name: `{1}`: ", fmt_align(I, AlignStyle::Right, Digits), Modi.getModuleName()); P.formatLine(" Obj: `{0}`: ", Modi.getObjFileName()); P.formatLine(" debug stream: {0}, # files: {1}, has ec info: {2}", Modi.getModuleStreamIndex(), Modi.getNumberOfFiles(), Modi.hasECInfo()); StringRef PdbFilePath = Err(Stream.getECName(Modi.getPdbFilePathNameIndex())); StringRef SrcFilePath = Err(Stream.getECName(Modi.getSourceFileNameIndex())); P.formatLine(" pdb file ni: {0} `{1}`, src file ni: {2} `{3}`", Modi.getPdbFilePathNameIndex(), PdbFilePath, Modi.getSourceFileNameIndex(), SrcFilePath); } return Error::success(); } Error DumpOutputStyle::dumpModuleFiles() { printHeader(P, "Files"); ExitOnError Err("Unexpected error processing modules"); iterateModules( File, P, 11, [this, &Err](uint32_t Modi, StringsAndChecksumsPrinter &Strings) { auto &Stream = Err(File.getPDBDbiStream()); const DbiModuleList &Modules = Stream.modules(); for (const auto &F : Modules.source_files(Modi)) { Strings.formatFromFileName(P, F); } }); return Error::success(); } static void typesetLinesAndColumns(PDBFile &File, LinePrinter &P, uint32_t Start, const LineColumnEntry &E) { const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5; // Let's try to keep it under 100 characters constexpr uint32_t kMaxRowLength = 100; // At least 3 spaces between columns. uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3); uint32_t ItemsLeft = E.LineNumbers.size(); auto LineIter = E.LineNumbers.begin(); while (ItemsLeft != 0) { uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow); for (uint32_t I = 0; I < RowColumns; ++I) { LineInfo Line(LineIter->Flags); std::string LineStr; if (Line.isAlwaysStepInto()) LineStr = "ASI"; else if (Line.isNeverStepInto()) LineStr = "NSI"; else LineStr = utostr(Line.getStartLine()); char Statement = Line.isStatement() ? ' ' : '!'; P.format("{0} {1:X-} {2} ", fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber), fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'), Statement); ++LineIter; --ItemsLeft; } P.NewLine(); } } Error DumpOutputStyle::dumpLines() { printHeader(P, "Lines"); uint32_t LastModi = UINT32_MAX; uint32_t LastNameIndex = UINT32_MAX; iterateModuleSubsections( File, P, 4, [this, &LastModi, &LastNameIndex](uint32_t Modi, StringsAndChecksumsPrinter &Strings, DebugLinesSubsectionRef &Lines) { uint16_t Segment = Lines.header()->RelocSegment; uint32_t Begin = Lines.header()->RelocOffset; uint32_t End = Begin + Lines.header()->CodeSize; for (const auto &Block : Lines) { if (LastModi != Modi || LastNameIndex != Block.NameIndex) { LastModi = Modi; LastNameIndex = Block.NameIndex; Strings.formatFromChecksumsOffset(P, Block.NameIndex); } AutoIndent Indent(P, 2); P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End); uint32_t Count = Block.LineNumbers.size(); if (Lines.hasColumnInfo()) P.format("line/column/addr entries = {0}", Count); else P.format("line/addr entries = {0}", Count); P.NewLine(); typesetLinesAndColumns(File, P, Begin, Block); } }); return Error::success(); } Error DumpOutputStyle::dumpInlineeLines() { printHeader(P, "Inlinee Lines"); iterateModuleSubsections( File, P, 2, [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, DebugInlineeLinesSubsectionRef &Lines) { P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File"); for (const auto &Entry : Lines) { P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee, fmtle(Entry.Header->SourceLineNum)); Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true); } P.NewLine(); }); return Error::success(); } Error DumpOutputStyle::dumpXmi() { printHeader(P, "Cross Module Imports"); iterateModuleSubsections( File, P, 2, [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, DebugCrossModuleImportsSubsectionRef &Imports) { P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs"); for (const auto &Xmi : Imports) { auto ExpectedModule = Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset); StringRef Module; SmallString<32> ModuleStorage; if (!ExpectedModule) { Module = "(unknown module)"; consumeError(ExpectedModule.takeError()); } else Module = *ExpectedModule; if (Module.size() > 32) { ModuleStorage = "..."; ModuleStorage += Module.take_back(32 - 3); Module = ModuleStorage; } std::vector TIs; for (const auto I : Xmi.Imports) TIs.push_back(formatv("{0,+10:X+}", fmtle(I))); std::string Result = typesetItemList(TIs, P.getIndentLevel() + 35, 12, " "); P.formatLine("{0,+32} | {1}", Module, Result); } }); return Error::success(); } Error DumpOutputStyle::dumpXme() { printHeader(P, "Cross Module Exports"); iterateModuleSubsections( File, P, 2, [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, DebugCrossModuleExportsSubsectionRef &Exports) { P.formatLine("{0,-10} | {1}", "Local ID", "Global ID"); for (const auto &Export : Exports) { P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local), TypeIndex(Export.Global)); } }); return Error::success(); } Error DumpOutputStyle::dumpStringTable() { printHeader(P, "String Table"); AutoIndent Indent(P); auto IS = File.getStringTable(); if (!IS) { P.formatLine("Not present in file"); consumeError(IS.takeError()); return Error::success(); } if (IS->name_ids().empty()) { P.formatLine("Empty"); return Error::success(); } auto MaxID = std::max_element(IS->name_ids().begin(), IS->name_ids().end()); uint32_t Digits = NumDigits(*MaxID); P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits), "String"); std::vector SortedIDs(IS->name_ids().begin(), IS->name_ids().end()); std::sort(SortedIDs.begin(), SortedIDs.end()); for (uint32_t I : SortedIDs) { auto ES = IS->getStringForID(I); llvm::SmallString<32> Str; if (!ES) { consumeError(ES.takeError()); Str = "Error reading string"; } else if (!ES->empty()) { Str.append("'"); Str.append(*ES); Str.append("'"); } if (!Str.empty()) P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), Str); } return Error::success(); } static void buildDepSet(LazyRandomTypeCollection &Types, ArrayRef Indices, std::map &DepSet) { SmallVector DepList; for (const auto &I : Indices) { TypeIndex TI(I); if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType()) continue; CVType Type = Types.getType(TI); DepSet[TI] = Type; codeview::discoverTypeIndices(Type, DepList); buildDepSet(Types, DepList, DepSet); } } 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()); MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, Stream.getNumHashBuckets(), Stream.getHashValues()); if (auto EC = codeview::visitTypeStream(Types, V)) { Printer.formatLine("An error occurred dumping type records: {0}", toString(std::move(EC))); } } static void dumpPartialTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, TpiStream &Stream, ArrayRef TiList, bool Bytes, bool Extras, bool Deps) { uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, Stream.getNumHashBuckets(), Stream.getHashValues()); if (opts::dump::DumpTypeDependents) { // If we need to dump all dependents, then iterate each index and find // all dependents, adding them to a map ordered by TypeIndex. std::map DepSet; buildDepSet(Types, TiList, DepSet); Printer.formatLine( "Showing {0:N} records and their dependents ({1:N} records total)", TiList.size(), DepSet.size()); for (auto &Dep : DepSet) { if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V)) Printer.formatLine("An error occurred dumping type record {0}: {1}", Dep.first, toString(std::move(EC))); } } else { Printer.formatLine("Showing {0:N} records.", TiList.size()); for (const auto &I : TiList) { TypeIndex TI(I); CVType Type = Types.getType(TI); if (auto EC = codeview::visitTypeRecord(Type, TI, V)) Printer.formatLine("An error occurred dumping type record {0}: {1}", TI, toString(std::move(EC))); } } } Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); bool Present = false; bool DumpTypes = false; bool DumpBytes = false; bool DumpExtras = false; std::vector Indices; if (StreamIdx == StreamTPI) { printHeader(P, "Types (TPI Stream)"); Present = File.hasPDBTpiStream(); DumpTypes = opts::dump::DumpTypes; DumpBytes = opts::dump::DumpTypeData; DumpExtras = opts::dump::DumpTypeExtras; Indices.assign(opts::dump::DumpTypeIndex.begin(), opts::dump::DumpTypeIndex.end()); } else if (StreamIdx == StreamIPI) { printHeader(P, "Types (IPI Stream)"); Present = File.hasPDBIpiStream(); DumpTypes = opts::dump::DumpIds; DumpBytes = opts::dump::DumpIdData; DumpExtras = opts::dump::DumpIdExtras; Indices.assign(opts::dump::DumpIdIndex.begin(), opts::dump::DumpIdIndex.end()); } AutoIndent Indent(P); if (!Present) { P.formatLine("Stream not present"); return Error::success(); } ExitOnError Err("Unexpected error processing types"); auto &Stream = Err((StreamIdx == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream()); auto &Types = Err(initializeTypes(StreamIdx)); if (DumpTypes || !Indices.empty()) { if (Indices.empty()) dumpFullTypeStream(P, Types, Stream, DumpBytes, DumpExtras); else { std::vector TiList(Indices.begin(), Indices.end()); dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras, opts::dump::DumpTypeDependents); } } if (DumpExtras) { P.NewLine(); auto IndexOffsets = Stream.getTypeIndexOffsets(); P.formatLine("Type Index Offsets:"); for (const auto &IO : IndexOffsets) { AutoIndent Indent2(P); P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset)); } P.NewLine(); P.formatLine("Hash Adjusters:"); auto &Adjusters = Stream.getHashAdjusters(); auto &Strings = Err(File.getStringTable()); for (const auto &A : Adjusters) { AutoIndent Indent2(P); auto ExpectedStr = Strings.getStringForID(A.first); TypeIndex TI(A.second); if (ExpectedStr) P.formatLine("`{0}` -> {1}", *ExpectedStr, TI); else { P.formatLine("unknown str id ({0}) -> {1}", A.first, TI); consumeError(ExpectedStr.takeError()); } } } return Error::success(); } Expected DumpOutputStyle::initializeTypes(uint32_t SN) { auto &TypeCollection = (SN == StreamTPI) ? TpiTypes : IpiTypes; auto Tpi = (SN == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream(); if (!Tpi) return Tpi.takeError(); if (!TypeCollection) { auto &Types = Tpi->typeArray(); uint32_t Count = Tpi->getNumTypeRecords(); auto Offsets = Tpi->getTypeIndexOffsets(); TypeCollection = llvm::make_unique(Types, Count, Offsets); } return *TypeCollection; } Error DumpOutputStyle::dumpModuleSyms() { printHeader(P, "Symbols"); AutoIndent Indent(P); if (!File.hasPDBDbiStream()) { P.formatLine("DBI Stream not present"); return Error::success(); } ExitOnError Err("Unexpected error processing symbols"); auto &Stream = Err(File.getPDBDbiStream()); auto &Types = Err(initializeTypes(StreamTPI)); const DbiModuleList &Modules = Stream.modules(); uint32_t Count = Modules.getModuleCount(); uint32_t Digits = NumDigits(Count); for (uint32_t I = 0; I < Count; ++I) { auto Modi = Modules.getModuleDescriptor(I); P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, Digits), Modi.getModuleName()); uint16_t ModiStream = Modi.getModuleStreamIndex(); if (ModiStream == kInvalidStreamIndex) { P.formatLine(" "); continue; } auto ModStreamData = MappedBlockStream::createIndexedStream( File.getMsfLayout(), File.getMsfBuffer(), ModiStream, File.getAllocator()); ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); if (auto EC = ModS.reload()) { P.formatLine("Error loading module stream {0}. {1}", I, toString(std::move(EC))); continue; } SymbolVisitorCallbackPipeline Pipeline; SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types); Pipeline.addCallbackToPipeline(Deserializer); Pipeline.addCallbackToPipeline(Dumper); CVSymbolVisitor Visitor(Pipeline); auto SS = ModS.getSymbolsSubstream(); if (auto EC = Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) { P.formatLine("Error while processing symbol records. {0}", toString(std::move(EC))); continue; } } return Error::success(); } Error DumpOutputStyle::dumpPublics() { printHeader(P, "Public Symbols"); AutoIndent Indent(P); if (!File.hasPDBPublicsStream()) { P.formatLine("Publics stream not present"); return Error::success(); } ExitOnError Err("Error dumping publics stream"); auto &Types = Err(initializeTypes(StreamTPI)); auto &Publics = Err(File.getPDBPublicsStream()); SymbolVisitorCallbackPipeline Pipeline; SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types); Pipeline.addCallbackToPipeline(Deserializer); Pipeline.addCallbackToPipeline(Dumper); CVSymbolVisitor Visitor(Pipeline); auto ExpectedSymbols = Publics.getSymbolArray(); if (!ExpectedSymbols) { P.formatLine("Could not read public symbol record stream"); return Error::success(); } if (auto EC = Visitor.visitSymbolStream(*ExpectedSymbols, 0)) P.formatLine("Error while processing public symbol records. {0}", toString(std::move(EC))); return Error::success(); } static std::string formatSectionCharacteristics(uint32_t IndentLevel, uint32_t C) { using SC = COFF::SectionCharacteristics; std::vector Opts; if (C == COFF::SC_Invalid) return "invalid"; if (C == 0) return "none"; PUSH_FLAG(SC, IMAGE_SCN_TYPE_NOLOAD, C, "IMAGE_SCN_TYPE_NOLOAD"); PUSH_FLAG(SC, IMAGE_SCN_TYPE_NO_PAD, C, "IMAGE_SCN_TYPE_NO_PAD"); PUSH_FLAG(SC, IMAGE_SCN_CNT_CODE, C, "IMAGE_SCN_CNT_CODE"); PUSH_FLAG(SC, IMAGE_SCN_CNT_INITIALIZED_DATA, C, "IMAGE_SCN_CNT_INITIALIZED_DATA"); PUSH_FLAG(SC, IMAGE_SCN_CNT_UNINITIALIZED_DATA, C, "IMAGE_SCN_CNT_UNINITIALIZED_DATA"); PUSH_FLAG(SC, IMAGE_SCN_LNK_OTHER, C, "IMAGE_SCN_LNK_OTHER"); PUSH_FLAG(SC, IMAGE_SCN_LNK_INFO, C, "IMAGE_SCN_LNK_INFO"); PUSH_FLAG(SC, IMAGE_SCN_LNK_REMOVE, C, "IMAGE_SCN_LNK_REMOVE"); PUSH_FLAG(SC, IMAGE_SCN_LNK_COMDAT, C, "IMAGE_SCN_LNK_COMDAT"); PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); PUSH_FLAG(SC, IMAGE_SCN_MEM_PURGEABLE, C, "IMAGE_SCN_MEM_PURGEABLE"); PUSH_FLAG(SC, IMAGE_SCN_MEM_16BIT, C, "IMAGE_SCN_MEM_16BIT"); PUSH_FLAG(SC, IMAGE_SCN_MEM_LOCKED, C, "IMAGE_SCN_MEM_LOCKED"); PUSH_FLAG(SC, IMAGE_SCN_MEM_PRELOAD, C, "IMAGE_SCN_MEM_PRELOAD"); PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1BYTES, C, "IMAGE_SCN_ALIGN_1BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2BYTES, C, "IMAGE_SCN_ALIGN_2BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4BYTES, C, "IMAGE_SCN_ALIGN_4BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8BYTES, C, "IMAGE_SCN_ALIGN_8BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_16BYTES, C, "IMAGE_SCN_ALIGN_16BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_32BYTES, C, "IMAGE_SCN_ALIGN_32BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_64BYTES, C, "IMAGE_SCN_ALIGN_64BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_128BYTES, C, "IMAGE_SCN_ALIGN_128BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_256BYTES, C, "IMAGE_SCN_ALIGN_256BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_512BYTES, C, "IMAGE_SCN_ALIGN_512BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1024BYTES, C, "IMAGE_SCN_ALIGN_1024BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2048BYTES, C, "IMAGE_SCN_ALIGN_2048BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4096BYTES, C, "IMAGE_SCN_ALIGN_4096BYTES"); PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8192BYTES, C, "IMAGE_SCN_ALIGN_8192BYTES"); PUSH_FLAG(SC, IMAGE_SCN_LNK_NRELOC_OVFL, C, "IMAGE_SCN_LNK_NRELOC_OVFL"); PUSH_FLAG(SC, IMAGE_SCN_MEM_DISCARDABLE, C, "IMAGE_SCN_MEM_DISCARDABLE"); PUSH_FLAG(SC, IMAGE_SCN_MEM_NOT_CACHED, C, "IMAGE_SCN_MEM_NOT_CACHED"); PUSH_FLAG(SC, IMAGE_SCN_MEM_NOT_PAGED, C, "IMAGE_SCN_MEM_NOT_PAGED"); PUSH_FLAG(SC, IMAGE_SCN_MEM_SHARED, C, "IMAGE_SCN_MEM_SHARED"); PUSH_FLAG(SC, IMAGE_SCN_MEM_EXECUTE, C, "IMAGE_SCN_MEM_EXECUTE"); PUSH_FLAG(SC, IMAGE_SCN_MEM_READ, C, "IMAGE_SCN_MEM_READ"); PUSH_FLAG(SC, IMAGE_SCN_MEM_WRITE, C, "IMAGE_SCN_MEM_WRITE"); return typesetItemList(Opts, IndentLevel, 3, " | "); } static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel, OMFSegDescFlags Flags) { std::vector Opts; if (Flags == OMFSegDescFlags::None) return "none"; PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read"); PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write"); PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute"); PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr"); PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector"); PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr"); PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group"); return typesetItemList(Opts, IndentLevel, 4, " | "); } Error DumpOutputStyle::dumpSectionContribs() { printHeader(P, "Section Contributions"); ExitOnError Err("Error dumping publics stream"); AutoIndent Indent(P); if (!File.hasPDBDbiStream()) { P.formatLine( "Section contribs require a DBI Stream, which could not be loaded"); return Error::success(); } auto &Dbi = Err(File.getPDBDbiStream()); class Visitor : public ISectionContribVisitor { public: Visitor(LinePrinter &P) : P(P) {} void visit(const SectionContrib &SC) override { P.formatLine( "SC | mod = {2}, {0}, size = {1}, data crc = {3}, reloc crc = {4}", formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size), fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc)); P.formatLine(" {0}", formatSectionCharacteristics(P.getIndentLevel() + 6, SC.Characteristics)); } void visit(const SectionContrib2 &SC) override { P.formatLine("SC2 | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " "crc = {4}, coff section = {5}", formatSegmentOffset(SC.Base.ISect, SC.Base.Off), fmtle(SC.Base.Size), fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc), fmtle(SC.Base.RelocCrc), fmtle(SC.ISectCoff)); P.formatLine(" {0}", formatSectionCharacteristics(P.getIndentLevel() + 6, SC.Base.Characteristics)); } private: LinePrinter &P; }; Visitor V(P); Dbi.visitSectionContributions(V); return Error::success(); } Error DumpOutputStyle::dumpSectionMap() { printHeader(P, "Section Map"); ExitOnError Err("Error dumping section map"); AutoIndent Indent(P); if (!File.hasPDBDbiStream()) { P.formatLine("Dumping the section map requires a DBI Stream, which could " "not be loaded"); return Error::success(); } auto &Dbi = Err(File.getPDBDbiStream()); uint32_t I = 0; for (auto &M : Dbi.getSectionMap()) { P.formatLine( "Section {0:4} | ovl = {0}, group = {1}, frame = {2}, name = {3}", I, fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName)); P.formatLine(" class = {0}, offset = {1}, size = {2}", fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength)); P.formatLine(" flags = {0}", formatSegMapDescriptorFlag( P.getIndentLevel() + 13, static_cast(uint16_t(M.Flags)))); ++I; } return Error::success(); }