1 //===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "DumpOutputStyle.h" 11 12 #include "FormatUtil.h" 13 #include "MinimalSymbolDumper.h" 14 #include "MinimalTypeDumper.h" 15 #include "StreamUtil.h" 16 #include "llvm-pdbutil.h" 17 18 #include "llvm/ADT/STLExtras.h" 19 #include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" 20 #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" 21 #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" 22 #include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" 23 #include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" 24 #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" 25 #include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" 26 #include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" 27 #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" 28 #include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h" 29 #include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" 30 #include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h" 31 #include "llvm/DebugInfo/CodeView/EnumTables.h" 32 #include "llvm/DebugInfo/CodeView/Formatters.h" 33 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" 34 #include "llvm/DebugInfo/CodeView/Line.h" 35 #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" 36 #include "llvm/DebugInfo/CodeView/SymbolDumper.h" 37 #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" 38 #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" 39 #include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" 40 #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" 41 #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" 42 #include "llvm/DebugInfo/MSF/MappedBlockStream.h" 43 #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" 44 #include "llvm/DebugInfo/PDB/Native/DbiStream.h" 45 #include "llvm/DebugInfo/PDB/Native/EnumTables.h" 46 #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" 47 #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" 48 #include "llvm/DebugInfo/PDB/Native/InfoStream.h" 49 #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" 50 #include "llvm/DebugInfo/PDB/Native/PDBFile.h" 51 #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" 52 #include "llvm/DebugInfo/PDB/Native/RawError.h" 53 #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" 54 #include "llvm/DebugInfo/PDB/Native/TpiStream.h" 55 #include "llvm/DebugInfo/PDB/PDBExtras.h" 56 #include "llvm/Object/COFF.h" 57 #include "llvm/Support/BinaryStreamReader.h" 58 #include "llvm/Support/FormatAdapters.h" 59 #include "llvm/Support/FormatVariadic.h" 60 61 #include <unordered_map> 62 63 using namespace llvm; 64 using namespace llvm::codeview; 65 using namespace llvm::msf; 66 using namespace llvm::pdb; 67 68 DumpOutputStyle::DumpOutputStyle(PDBFile &File) 69 : File(File), P(2, false, outs()) {} 70 71 Error DumpOutputStyle::dump() { 72 if (opts::dump::DumpSummary) { 73 if (auto EC = dumpFileSummary()) 74 return EC; 75 P.NewLine(); 76 } 77 78 if (opts::dump::DumpStreams) { 79 if (auto EC = dumpStreamSummary()) 80 return EC; 81 P.NewLine(); 82 } 83 84 if (opts::dump::DumpStringTable) { 85 if (auto EC = dumpStringTable()) 86 return EC; 87 P.NewLine(); 88 } 89 90 if (opts::dump::DumpModules) { 91 if (auto EC = dumpModules()) 92 return EC; 93 } 94 95 if (opts::dump::DumpModuleFiles) { 96 if (auto EC = dumpModuleFiles()) 97 return EC; 98 } 99 100 if (opts::dump::DumpLines) { 101 if (auto EC = dumpLines()) 102 return EC; 103 } 104 105 if (opts::dump::DumpInlineeLines) { 106 if (auto EC = dumpInlineeLines()) 107 return EC; 108 } 109 110 if (opts::dump::DumpXmi) { 111 if (auto EC = dumpXmi()) 112 return EC; 113 } 114 115 if (opts::dump::DumpXme) { 116 if (auto EC = dumpXme()) 117 return EC; 118 } 119 120 if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || 121 opts::dump::DumpTypeExtras) { 122 if (auto EC = dumpTpiStream(StreamTPI)) 123 return EC; 124 } 125 126 if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() || 127 opts::dump::DumpIdExtras) { 128 if (auto EC = dumpTpiStream(StreamIPI)) 129 return EC; 130 } 131 132 if (opts::dump::DumpPublics) { 133 if (auto EC = dumpPublics()) 134 return EC; 135 } 136 137 if (opts::dump::DumpSymbols) { 138 if (auto EC = dumpModuleSyms()) 139 return EC; 140 } 141 142 if (opts::dump::DumpSectionContribs) { 143 if (auto EC = dumpSectionContribs()) 144 return EC; 145 } 146 147 if (opts::dump::DumpSectionMap) { 148 if (auto EC = dumpSectionMap()) 149 return EC; 150 } 151 152 return Error::success(); 153 } 154 155 static void printHeader(LinePrinter &P, const Twine &S) { 156 P.NewLine(); 157 P.formatLine("{0,=60}", S); 158 P.formatLine("{0}", fmt_repeat('=', 60)); 159 } 160 161 Error DumpOutputStyle::dumpFileSummary() { 162 printHeader(P, "Summary"); 163 164 ExitOnError Err("Invalid PDB Format"); 165 166 AutoIndent Indent(P); 167 P.formatLine("Block Size: {0}", File.getBlockSize()); 168 P.formatLine("Number of blocks: {0}", File.getBlockCount()); 169 P.formatLine("Number of streams: {0}", File.getNumStreams()); 170 171 auto &PS = Err(File.getPDBInfoStream()); 172 P.formatLine("Signature: {0}", PS.getSignature()); 173 P.formatLine("Age: {0}", PS.getAge()); 174 P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid)); 175 P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures())); 176 P.formatLine("Has Debug Info: {0}", File.hasPDBDbiStream()); 177 P.formatLine("Has Types: {0}", File.hasPDBTpiStream()); 178 P.formatLine("Has IDs: {0}", File.hasPDBIpiStream()); 179 P.formatLine("Has Globals: {0}", File.hasPDBGlobalsStream()); 180 P.formatLine("Has Publics: {0}", File.hasPDBPublicsStream()); 181 if (File.hasPDBDbiStream()) { 182 auto &DBI = Err(File.getPDBDbiStream()); 183 P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked()); 184 P.formatLine("Has conflicting types: {0}", DBI.hasCTypes()); 185 P.formatLine("Is stripped: {0}", DBI.isStripped()); 186 } 187 188 return Error::success(); 189 } 190 191 Error DumpOutputStyle::dumpStreamSummary() { 192 printHeader(P, "Streams"); 193 194 if (StreamPurposes.empty()) 195 discoverStreamPurposes(File, StreamPurposes); 196 197 AutoIndent Indent(P); 198 uint32_t StreamCount = File.getNumStreams(); 199 200 for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { 201 P.formatLine( 202 "Stream {0}: [{1}] ({2} bytes)", 203 fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)), 204 StreamPurposes[StreamIdx], File.getStreamByteSize(StreamIdx)); 205 if (opts::dump::DumpStreamBlocks) { 206 auto Blocks = File.getStreamBlockList(StreamIdx); 207 std::vector<uint32_t> BV(Blocks.begin(), Blocks.end()); 208 P.formatLine(" {0} Blocks: [{1}]", 209 fmt_repeat(' ', NumDigits(StreamCount)), 210 make_range(BV.begin(), BV.end())); 211 } 212 } 213 214 return Error::success(); 215 } 216 217 static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File, 218 uint32_t Index) { 219 ExitOnError Err("Unexpected error"); 220 221 auto &Dbi = Err(File.getPDBDbiStream()); 222 const auto &Modules = Dbi.modules(); 223 auto Modi = Modules.getModuleDescriptor(Index); 224 225 uint16_t ModiStream = Modi.getModuleStreamIndex(); 226 if (ModiStream == kInvalidStreamIndex) 227 return make_error<RawError>(raw_error_code::no_stream, 228 "Module stream not present"); 229 230 auto ModStreamData = MappedBlockStream::createIndexedStream( 231 File.getMsfLayout(), File.getMsfBuffer(), ModiStream, 232 File.getAllocator()); 233 234 ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); 235 if (auto EC = ModS.reload()) 236 return make_error<RawError>(raw_error_code::corrupt_file, 237 "Invalid module stream"); 238 239 return std::move(ModS); 240 } 241 242 static std::string formatChecksumKind(FileChecksumKind Kind) { 243 switch (Kind) { 244 RETURN_CASE(FileChecksumKind, None, "None"); 245 RETURN_CASE(FileChecksumKind, MD5, "MD5"); 246 RETURN_CASE(FileChecksumKind, SHA1, "SHA-1"); 247 RETURN_CASE(FileChecksumKind, SHA256, "SHA-256"); 248 } 249 return formatUnknownEnum(Kind); 250 } 251 252 namespace { 253 class StringsAndChecksumsPrinter { 254 const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) { 255 ExitOnError Err("Unexpected error processing modules"); 256 return Err(File.getStringTable()).getStringTable(); 257 } 258 259 template <typename... Args> 260 void formatInternal(LinePrinter &Printer, bool Append, 261 Args &&... args) const { 262 if (Append) 263 Printer.format(std::forward<Args>(args)...); 264 else 265 Printer.formatLine(std::forward<Args>(args)...); 266 } 267 268 public: 269 StringsAndChecksumsPrinter(PDBFile &File, uint32_t Modi) 270 : Records(extractStringTable(File)) { 271 auto MDS = getModuleDebugStream(File, Modi); 272 if (!MDS) { 273 consumeError(MDS.takeError()); 274 return; 275 } 276 277 DebugStream = llvm::make_unique<ModuleDebugStreamRef>(std::move(*MDS)); 278 Records.initialize(MDS->subsections()); 279 if (Records.hasChecksums()) { 280 for (const auto &Entry : Records.checksums()) { 281 auto S = Records.strings().getString(Entry.FileNameOffset); 282 if (!S) 283 continue; 284 ChecksumsByFile[*S] = Entry; 285 } 286 } 287 } 288 289 Expected<StringRef> getNameFromStringTable(uint32_t Offset) const { 290 return Records.strings().getString(Offset); 291 } 292 293 void formatFromFileName(LinePrinter &Printer, StringRef File, 294 bool Append = false) const { 295 auto FC = ChecksumsByFile.find(File); 296 if (FC == ChecksumsByFile.end()) { 297 formatInternal(Printer, Append, "- (no checksum) {0}", File); 298 return; 299 } 300 301 formatInternal(Printer, Append, "- ({0}: {1}) {2}", 302 formatChecksumKind(FC->getValue().Kind), 303 toHex(FC->getValue().Checksum), File); 304 } 305 306 void formatFromChecksumsOffset(LinePrinter &Printer, uint32_t Offset, 307 bool Append = false) const { 308 if (!Records.hasChecksums()) { 309 formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); 310 return; 311 } 312 313 auto Iter = Records.checksums().getArray().at(Offset); 314 if (Iter == Records.checksums().getArray().end()) { 315 formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); 316 return; 317 } 318 319 uint32_t FO = Iter->FileNameOffset; 320 auto ExpectedFile = getNameFromStringTable(FO); 321 if (!ExpectedFile) { 322 formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); 323 consumeError(ExpectedFile.takeError()); 324 return; 325 } 326 if (Iter->Kind == FileChecksumKind::None) { 327 formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile); 328 } else { 329 formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile, 330 formatChecksumKind(Iter->Kind), toHex(Iter->Checksum)); 331 } 332 } 333 334 std::unique_ptr<ModuleDebugStreamRef> DebugStream; 335 StringsAndChecksumsRef Records; 336 StringMap<FileChecksumEntry> ChecksumsByFile; 337 }; 338 } // namespace 339 340 template <typename CallbackT> 341 static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel, 342 CallbackT Callback) { 343 AutoIndent Indent(P); 344 if (!File.hasPDBDbiStream()) { 345 P.formatLine("DBI Stream not present"); 346 return; 347 } 348 349 ExitOnError Err("Unexpected error processing modules"); 350 351 auto &Stream = Err(File.getPDBDbiStream()); 352 353 const DbiModuleList &Modules = Stream.modules(); 354 uint32_t Count = Modules.getModuleCount(); 355 uint32_t Digits = NumDigits(Count); 356 for (uint32_t I = 0; I < Count; ++I) { 357 auto Modi = Modules.getModuleDescriptor(I); 358 P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, Digits), 359 Modi.getModuleName()); 360 361 StringsAndChecksumsPrinter Strings(File, I); 362 AutoIndent Indent2(P, IndentLevel); 363 Callback(I, Strings); 364 } 365 } 366 367 template <typename SubsectionT> 368 static void iterateModuleSubsections( 369 PDBFile &File, LinePrinter &P, uint32_t IndentLevel, 370 llvm::function_ref<void(uint32_t, StringsAndChecksumsPrinter &, 371 SubsectionT &)> 372 Callback) { 373 374 iterateModules( 375 File, P, IndentLevel, 376 [&File, &Callback](uint32_t Modi, StringsAndChecksumsPrinter &Strings) { 377 auto MDS = getModuleDebugStream(File, Modi); 378 if (!MDS) { 379 consumeError(MDS.takeError()); 380 return; 381 } 382 383 for (const auto &SS : MDS->subsections()) { 384 SubsectionT Subsection; 385 386 if (SS.kind() != Subsection.kind()) 387 continue; 388 389 BinaryStreamReader Reader(SS.getRecordData()); 390 if (auto EC = Subsection.initialize(Reader)) 391 continue; 392 Callback(Modi, Strings, Subsection); 393 } 394 }); 395 } 396 397 Error DumpOutputStyle::dumpModules() { 398 printHeader(P, "Modules"); 399 400 AutoIndent Indent(P); 401 if (!File.hasPDBDbiStream()) { 402 P.formatLine("DBI Stream not present"); 403 return Error::success(); 404 } 405 406 ExitOnError Err("Unexpected error processing modules"); 407 408 auto &Stream = Err(File.getPDBDbiStream()); 409 410 const DbiModuleList &Modules = Stream.modules(); 411 uint32_t Count = Modules.getModuleCount(); 412 uint32_t Digits = NumDigits(Count); 413 for (uint32_t I = 0; I < Count; ++I) { 414 auto Modi = Modules.getModuleDescriptor(I); 415 P.formatLine("Mod {0:4} | Name: `{1}`: ", 416 fmt_align(I, AlignStyle::Right, Digits), Modi.getModuleName()); 417 P.formatLine(" Obj: `{0}`: ", Modi.getObjFileName()); 418 P.formatLine(" debug stream: {0}, # files: {1}, has ec info: {2}", 419 Modi.getModuleStreamIndex(), Modi.getNumberOfFiles(), 420 Modi.hasECInfo()); 421 StringRef PdbFilePath = 422 Err(Stream.getECName(Modi.getPdbFilePathNameIndex())); 423 StringRef SrcFilePath = 424 Err(Stream.getECName(Modi.getSourceFileNameIndex())); 425 P.formatLine(" pdb file ni: {0} `{1}`, src file ni: {2} `{3}`", 426 Modi.getPdbFilePathNameIndex(), PdbFilePath, 427 Modi.getSourceFileNameIndex(), SrcFilePath); 428 } 429 return Error::success(); 430 } 431 432 Error DumpOutputStyle::dumpModuleFiles() { 433 printHeader(P, "Files"); 434 435 ExitOnError Err("Unexpected error processing modules"); 436 437 iterateModules( 438 File, P, 11, 439 [this, &Err](uint32_t Modi, StringsAndChecksumsPrinter &Strings) { 440 auto &Stream = Err(File.getPDBDbiStream()); 441 442 const DbiModuleList &Modules = Stream.modules(); 443 for (const auto &F : Modules.source_files(Modi)) { 444 Strings.formatFromFileName(P, F); 445 } 446 }); 447 return Error::success(); 448 } 449 450 static void typesetLinesAndColumns(PDBFile &File, LinePrinter &P, 451 uint32_t Start, const LineColumnEntry &E) { 452 const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number 453 uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5; 454 455 // Let's try to keep it under 100 characters 456 constexpr uint32_t kMaxRowLength = 100; 457 // At least 3 spaces between columns. 458 uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3); 459 uint32_t ItemsLeft = E.LineNumbers.size(); 460 auto LineIter = E.LineNumbers.begin(); 461 while (ItemsLeft != 0) { 462 uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow); 463 for (uint32_t I = 0; I < RowColumns; ++I) { 464 LineInfo Line(LineIter->Flags); 465 std::string LineStr; 466 if (Line.isAlwaysStepInto()) 467 LineStr = "ASI"; 468 else if (Line.isNeverStepInto()) 469 LineStr = "NSI"; 470 else 471 LineStr = utostr(Line.getStartLine()); 472 char Statement = Line.isStatement() ? ' ' : '!'; 473 P.format("{0} {1:X-} {2} ", 474 fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber), 475 fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'), 476 Statement); 477 ++LineIter; 478 --ItemsLeft; 479 } 480 P.NewLine(); 481 } 482 } 483 484 Error DumpOutputStyle::dumpLines() { 485 printHeader(P, "Lines"); 486 487 uint32_t LastModi = UINT32_MAX; 488 uint32_t LastNameIndex = UINT32_MAX; 489 iterateModuleSubsections<DebugLinesSubsectionRef>( 490 File, P, 4, 491 [this, &LastModi, &LastNameIndex](uint32_t Modi, 492 StringsAndChecksumsPrinter &Strings, 493 DebugLinesSubsectionRef &Lines) { 494 uint16_t Segment = Lines.header()->RelocSegment; 495 uint32_t Begin = Lines.header()->RelocOffset; 496 uint32_t End = Begin + Lines.header()->CodeSize; 497 for (const auto &Block : Lines) { 498 if (LastModi != Modi || LastNameIndex != Block.NameIndex) { 499 LastModi = Modi; 500 LastNameIndex = Block.NameIndex; 501 Strings.formatFromChecksumsOffset(P, Block.NameIndex); 502 } 503 504 AutoIndent Indent(P, 2); 505 P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End); 506 uint32_t Count = Block.LineNumbers.size(); 507 if (Lines.hasColumnInfo()) 508 P.format("line/column/addr entries = {0}", Count); 509 else 510 P.format("line/addr entries = {0}", Count); 511 512 P.NewLine(); 513 typesetLinesAndColumns(File, P, Begin, Block); 514 } 515 }); 516 517 return Error::success(); 518 } 519 520 Error DumpOutputStyle::dumpInlineeLines() { 521 printHeader(P, "Inlinee Lines"); 522 523 iterateModuleSubsections<DebugInlineeLinesSubsectionRef>( 524 File, P, 2, 525 [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, 526 DebugInlineeLinesSubsectionRef &Lines) { 527 P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File"); 528 for (const auto &Entry : Lines) { 529 P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee, 530 fmtle(Entry.Header->SourceLineNum)); 531 Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true); 532 } 533 P.NewLine(); 534 }); 535 536 return Error::success(); 537 } 538 539 Error DumpOutputStyle::dumpXmi() { 540 printHeader(P, "Cross Module Imports"); 541 iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>( 542 File, P, 2, 543 [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, 544 DebugCrossModuleImportsSubsectionRef &Imports) { 545 P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs"); 546 547 for (const auto &Xmi : Imports) { 548 auto ExpectedModule = 549 Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset); 550 StringRef Module; 551 SmallString<32> ModuleStorage; 552 if (!ExpectedModule) { 553 Module = "(unknown module)"; 554 consumeError(ExpectedModule.takeError()); 555 } else 556 Module = *ExpectedModule; 557 if (Module.size() > 32) { 558 ModuleStorage = "..."; 559 ModuleStorage += Module.take_back(32 - 3); 560 Module = ModuleStorage; 561 } 562 std::vector<std::string> TIs; 563 for (const auto I : Xmi.Imports) 564 TIs.push_back(formatv("{0,+10:X+}", fmtle(I))); 565 std::string Result = 566 typesetItemList(TIs, P.getIndentLevel() + 35, 12, " "); 567 P.formatLine("{0,+32} | {1}", Module, Result); 568 } 569 }); 570 571 return Error::success(); 572 } 573 574 Error DumpOutputStyle::dumpXme() { 575 printHeader(P, "Cross Module Exports"); 576 577 iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>( 578 File, P, 2, 579 [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, 580 DebugCrossModuleExportsSubsectionRef &Exports) { 581 P.formatLine("{0,-10} | {1}", "Local ID", "Global ID"); 582 for (const auto &Export : Exports) { 583 P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local), 584 TypeIndex(Export.Global)); 585 } 586 }); 587 588 return Error::success(); 589 } 590 591 Error DumpOutputStyle::dumpStringTable() { 592 printHeader(P, "String Table"); 593 594 AutoIndent Indent(P); 595 auto IS = File.getStringTable(); 596 if (!IS) { 597 P.formatLine("Not present in file"); 598 consumeError(IS.takeError()); 599 return Error::success(); 600 } 601 602 if (IS->name_ids().empty()) { 603 P.formatLine("Empty"); 604 return Error::success(); 605 } 606 607 auto MaxID = std::max_element(IS->name_ids().begin(), IS->name_ids().end()); 608 uint32_t Digits = NumDigits(*MaxID); 609 610 P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits), 611 "String"); 612 613 std::vector<uint32_t> SortedIDs(IS->name_ids().begin(), IS->name_ids().end()); 614 std::sort(SortedIDs.begin(), SortedIDs.end()); 615 for (uint32_t I : SortedIDs) { 616 auto ES = IS->getStringForID(I); 617 llvm::SmallString<32> Str; 618 if (!ES) { 619 consumeError(ES.takeError()); 620 Str = "Error reading string"; 621 } else if (!ES->empty()) { 622 Str.append("'"); 623 Str.append(*ES); 624 Str.append("'"); 625 } 626 627 if (!Str.empty()) 628 P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), Str); 629 } 630 return Error::success(); 631 } 632 633 static void buildDepSet(LazyRandomTypeCollection &Types, 634 ArrayRef<TypeIndex> Indices, 635 std::map<TypeIndex, CVType> &DepSet) { 636 SmallVector<TypeIndex, 4> DepList; 637 for (const auto &I : Indices) { 638 TypeIndex TI(I); 639 if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType()) 640 continue; 641 642 CVType Type = Types.getType(TI); 643 DepSet[TI] = Type; 644 codeview::discoverTypeIndices(Type, DepList); 645 buildDepSet(Types, DepList, DepSet); 646 } 647 } 648 649 static void dumpFullTypeStream(LinePrinter &Printer, 650 LazyRandomTypeCollection &Types, 651 TpiStream &Stream, bool Bytes, bool Extras) { 652 Printer.formatLine("Showing {0:N} records", Stream.getNumTypeRecords()); 653 uint32_t Width = 654 NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); 655 656 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, 657 Stream.getNumHashBuckets(), Stream.getHashValues()); 658 659 if (auto EC = codeview::visitTypeStream(Types, V)) { 660 Printer.formatLine("An error occurred dumping type records: {0}", 661 toString(std::move(EC))); 662 } 663 } 664 665 static void dumpPartialTypeStream(LinePrinter &Printer, 666 LazyRandomTypeCollection &Types, 667 TpiStream &Stream, ArrayRef<TypeIndex> TiList, 668 bool Bytes, bool Extras, bool Deps) { 669 uint32_t Width = 670 NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); 671 672 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, 673 Stream.getNumHashBuckets(), Stream.getHashValues()); 674 675 if (opts::dump::DumpTypeDependents) { 676 // If we need to dump all dependents, then iterate each index and find 677 // all dependents, adding them to a map ordered by TypeIndex. 678 std::map<TypeIndex, CVType> DepSet; 679 buildDepSet(Types, TiList, DepSet); 680 681 Printer.formatLine( 682 "Showing {0:N} records and their dependents ({1:N} records total)", 683 TiList.size(), DepSet.size()); 684 685 for (auto &Dep : DepSet) { 686 if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V)) 687 Printer.formatLine("An error occurred dumping type record {0}: {1}", 688 Dep.first, toString(std::move(EC))); 689 } 690 } else { 691 Printer.formatLine("Showing {0:N} records.", TiList.size()); 692 693 for (const auto &I : TiList) { 694 TypeIndex TI(I); 695 CVType Type = Types.getType(TI); 696 if (auto EC = codeview::visitTypeRecord(Type, TI, V)) 697 Printer.formatLine("An error occurred dumping type record {0}: {1}", TI, 698 toString(std::move(EC))); 699 } 700 } 701 } 702 703 Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { 704 assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); 705 706 bool Present = false; 707 bool DumpTypes = false; 708 bool DumpBytes = false; 709 bool DumpExtras = false; 710 std::vector<uint32_t> Indices; 711 if (StreamIdx == StreamTPI) { 712 printHeader(P, "Types (TPI Stream)"); 713 Present = File.hasPDBTpiStream(); 714 DumpTypes = opts::dump::DumpTypes; 715 DumpBytes = opts::dump::DumpTypeData; 716 DumpExtras = opts::dump::DumpTypeExtras; 717 Indices.assign(opts::dump::DumpTypeIndex.begin(), 718 opts::dump::DumpTypeIndex.end()); 719 } else if (StreamIdx == StreamIPI) { 720 printHeader(P, "Types (IPI Stream)"); 721 Present = File.hasPDBIpiStream(); 722 DumpTypes = opts::dump::DumpIds; 723 DumpBytes = opts::dump::DumpIdData; 724 DumpExtras = opts::dump::DumpIdExtras; 725 Indices.assign(opts::dump::DumpIdIndex.begin(), 726 opts::dump::DumpIdIndex.end()); 727 } 728 729 AutoIndent Indent(P); 730 if (!Present) { 731 P.formatLine("Stream not present"); 732 return Error::success(); 733 } 734 735 ExitOnError Err("Unexpected error processing types"); 736 737 auto &Stream = Err((StreamIdx == StreamTPI) ? File.getPDBTpiStream() 738 : File.getPDBIpiStream()); 739 740 auto &Types = Err(initializeTypes(StreamIdx)); 741 742 if (DumpTypes || !Indices.empty()) { 743 if (Indices.empty()) 744 dumpFullTypeStream(P, Types, Stream, DumpBytes, DumpExtras); 745 else { 746 std::vector<TypeIndex> TiList(Indices.begin(), Indices.end()); 747 dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras, 748 opts::dump::DumpTypeDependents); 749 } 750 } 751 752 if (DumpExtras) { 753 P.NewLine(); 754 auto IndexOffsets = Stream.getTypeIndexOffsets(); 755 P.formatLine("Type Index Offsets:"); 756 for (const auto &IO : IndexOffsets) { 757 AutoIndent Indent2(P); 758 P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset)); 759 } 760 761 P.NewLine(); 762 P.formatLine("Hash Adjusters:"); 763 auto &Adjusters = Stream.getHashAdjusters(); 764 auto &Strings = Err(File.getStringTable()); 765 for (const auto &A : Adjusters) { 766 AutoIndent Indent2(P); 767 auto ExpectedStr = Strings.getStringForID(A.first); 768 TypeIndex TI(A.second); 769 if (ExpectedStr) 770 P.formatLine("`{0}` -> {1}", *ExpectedStr, TI); 771 else { 772 P.formatLine("unknown str id ({0}) -> {1}", A.first, TI); 773 consumeError(ExpectedStr.takeError()); 774 } 775 } 776 } 777 return Error::success(); 778 } 779 780 Expected<codeview::LazyRandomTypeCollection &> 781 DumpOutputStyle::initializeTypes(uint32_t SN) { 782 auto &TypeCollection = (SN == StreamTPI) ? TpiTypes : IpiTypes; 783 auto Tpi = 784 (SN == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream(); 785 if (!Tpi) 786 return Tpi.takeError(); 787 788 if (!TypeCollection) { 789 auto &Types = Tpi->typeArray(); 790 uint32_t Count = Tpi->getNumTypeRecords(); 791 auto Offsets = Tpi->getTypeIndexOffsets(); 792 TypeCollection = 793 llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets); 794 } 795 796 return *TypeCollection; 797 } 798 799 Error DumpOutputStyle::dumpModuleSyms() { 800 printHeader(P, "Symbols"); 801 802 AutoIndent Indent(P); 803 if (!File.hasPDBDbiStream()) { 804 P.formatLine("DBI Stream not present"); 805 return Error::success(); 806 } 807 808 ExitOnError Err("Unexpected error processing symbols"); 809 810 auto &Stream = Err(File.getPDBDbiStream()); 811 812 auto &Types = Err(initializeTypes(StreamTPI)); 813 814 const DbiModuleList &Modules = Stream.modules(); 815 uint32_t Count = Modules.getModuleCount(); 816 uint32_t Digits = NumDigits(Count); 817 for (uint32_t I = 0; I < Count; ++I) { 818 auto Modi = Modules.getModuleDescriptor(I); 819 P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, Digits), 820 Modi.getModuleName()); 821 uint16_t ModiStream = Modi.getModuleStreamIndex(); 822 if (ModiStream == kInvalidStreamIndex) { 823 P.formatLine(" <symbols not present>"); 824 continue; 825 } 826 auto ModStreamData = MappedBlockStream::createIndexedStream( 827 File.getMsfLayout(), File.getMsfBuffer(), ModiStream, 828 File.getAllocator()); 829 830 ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); 831 if (auto EC = ModS.reload()) { 832 P.formatLine("Error loading module stream {0}. {1}", I, 833 toString(std::move(EC))); 834 continue; 835 } 836 837 SymbolVisitorCallbackPipeline Pipeline; 838 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); 839 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types); 840 841 Pipeline.addCallbackToPipeline(Deserializer); 842 Pipeline.addCallbackToPipeline(Dumper); 843 CVSymbolVisitor Visitor(Pipeline); 844 auto SS = ModS.getSymbolsSubstream(); 845 if (auto EC = Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) { 846 P.formatLine("Error while processing symbol records. {0}", 847 toString(std::move(EC))); 848 continue; 849 } 850 } 851 return Error::success(); 852 } 853 854 Error DumpOutputStyle::dumpPublics() { 855 printHeader(P, "Public Symbols"); 856 857 AutoIndent Indent(P); 858 if (!File.hasPDBPublicsStream()) { 859 P.formatLine("Publics stream not present"); 860 return Error::success(); 861 } 862 863 ExitOnError Err("Error dumping publics stream"); 864 865 auto &Types = Err(initializeTypes(StreamTPI)); 866 auto &Publics = Err(File.getPDBPublicsStream()); 867 SymbolVisitorCallbackPipeline Pipeline; 868 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); 869 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types); 870 871 Pipeline.addCallbackToPipeline(Deserializer); 872 Pipeline.addCallbackToPipeline(Dumper); 873 CVSymbolVisitor Visitor(Pipeline); 874 875 auto ExpectedSymbols = Publics.getSymbolArray(); 876 if (!ExpectedSymbols) { 877 P.formatLine("Could not read public symbol record stream"); 878 return Error::success(); 879 } 880 881 if (auto EC = Visitor.visitSymbolStream(*ExpectedSymbols, 0)) 882 P.formatLine("Error while processing public symbol records. {0}", 883 toString(std::move(EC))); 884 885 return Error::success(); 886 } 887 888 static std::string formatSectionCharacteristics(uint32_t IndentLevel, 889 uint32_t C) { 890 using SC = COFF::SectionCharacteristics; 891 std::vector<std::string> Opts; 892 if (C == COFF::SC_Invalid) 893 return "invalid"; 894 if (C == 0) 895 return "none"; 896 897 PUSH_FLAG(SC, IMAGE_SCN_TYPE_NOLOAD, C, "IMAGE_SCN_TYPE_NOLOAD"); 898 PUSH_FLAG(SC, IMAGE_SCN_TYPE_NO_PAD, C, "IMAGE_SCN_TYPE_NO_PAD"); 899 PUSH_FLAG(SC, IMAGE_SCN_CNT_CODE, C, "IMAGE_SCN_CNT_CODE"); 900 PUSH_FLAG(SC, IMAGE_SCN_CNT_INITIALIZED_DATA, C, 901 "IMAGE_SCN_CNT_INITIALIZED_DATA"); 902 PUSH_FLAG(SC, IMAGE_SCN_CNT_UNINITIALIZED_DATA, C, 903 "IMAGE_SCN_CNT_UNINITIALIZED_DATA"); 904 PUSH_FLAG(SC, IMAGE_SCN_LNK_OTHER, C, "IMAGE_SCN_LNK_OTHER"); 905 PUSH_FLAG(SC, IMAGE_SCN_LNK_INFO, C, "IMAGE_SCN_LNK_INFO"); 906 PUSH_FLAG(SC, IMAGE_SCN_LNK_REMOVE, C, "IMAGE_SCN_LNK_REMOVE"); 907 PUSH_FLAG(SC, IMAGE_SCN_LNK_COMDAT, C, "IMAGE_SCN_LNK_COMDAT"); 908 PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); 909 PUSH_FLAG(SC, IMAGE_SCN_MEM_PURGEABLE, C, "IMAGE_SCN_MEM_PURGEABLE"); 910 PUSH_FLAG(SC, IMAGE_SCN_MEM_16BIT, C, "IMAGE_SCN_MEM_16BIT"); 911 PUSH_FLAG(SC, IMAGE_SCN_MEM_LOCKED, C, "IMAGE_SCN_MEM_LOCKED"); 912 PUSH_FLAG(SC, IMAGE_SCN_MEM_PRELOAD, C, "IMAGE_SCN_MEM_PRELOAD"); 913 PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); 914 PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); 915 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1BYTES, C, 916 "IMAGE_SCN_ALIGN_1BYTES"); 917 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2BYTES, C, 918 "IMAGE_SCN_ALIGN_2BYTES"); 919 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4BYTES, C, 920 "IMAGE_SCN_ALIGN_4BYTES"); 921 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8BYTES, C, 922 "IMAGE_SCN_ALIGN_8BYTES"); 923 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_16BYTES, C, 924 "IMAGE_SCN_ALIGN_16BYTES"); 925 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_32BYTES, C, 926 "IMAGE_SCN_ALIGN_32BYTES"); 927 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_64BYTES, C, 928 "IMAGE_SCN_ALIGN_64BYTES"); 929 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_128BYTES, C, 930 "IMAGE_SCN_ALIGN_128BYTES"); 931 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_256BYTES, C, 932 "IMAGE_SCN_ALIGN_256BYTES"); 933 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_512BYTES, C, 934 "IMAGE_SCN_ALIGN_512BYTES"); 935 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1024BYTES, C, 936 "IMAGE_SCN_ALIGN_1024BYTES"); 937 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2048BYTES, C, 938 "IMAGE_SCN_ALIGN_2048BYTES"); 939 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4096BYTES, C, 940 "IMAGE_SCN_ALIGN_4096BYTES"); 941 PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8192BYTES, C, 942 "IMAGE_SCN_ALIGN_8192BYTES"); 943 PUSH_FLAG(SC, IMAGE_SCN_LNK_NRELOC_OVFL, C, "IMAGE_SCN_LNK_NRELOC_OVFL"); 944 PUSH_FLAG(SC, IMAGE_SCN_MEM_DISCARDABLE, C, "IMAGE_SCN_MEM_DISCARDABLE"); 945 PUSH_FLAG(SC, IMAGE_SCN_MEM_NOT_CACHED, C, "IMAGE_SCN_MEM_NOT_CACHED"); 946 PUSH_FLAG(SC, IMAGE_SCN_MEM_NOT_PAGED, C, "IMAGE_SCN_MEM_NOT_PAGED"); 947 PUSH_FLAG(SC, IMAGE_SCN_MEM_SHARED, C, "IMAGE_SCN_MEM_SHARED"); 948 PUSH_FLAG(SC, IMAGE_SCN_MEM_EXECUTE, C, "IMAGE_SCN_MEM_EXECUTE"); 949 PUSH_FLAG(SC, IMAGE_SCN_MEM_READ, C, "IMAGE_SCN_MEM_READ"); 950 PUSH_FLAG(SC, IMAGE_SCN_MEM_WRITE, C, "IMAGE_SCN_MEM_WRITE"); 951 return typesetItemList(Opts, IndentLevel, 3, " | "); 952 } 953 954 static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel, 955 OMFSegDescFlags Flags) { 956 std::vector<std::string> Opts; 957 if (Flags == OMFSegDescFlags::None) 958 return "none"; 959 960 PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read"); 961 PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write"); 962 PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute"); 963 PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr"); 964 PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector"); 965 PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr"); 966 PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group"); 967 return typesetItemList(Opts, IndentLevel, 4, " | "); 968 } 969 970 Error DumpOutputStyle::dumpSectionContribs() { 971 printHeader(P, "Section Contributions"); 972 ExitOnError Err("Error dumping publics stream"); 973 974 AutoIndent Indent(P); 975 if (!File.hasPDBDbiStream()) { 976 P.formatLine( 977 "Section contribs require a DBI Stream, which could not be loaded"); 978 return Error::success(); 979 } 980 981 auto &Dbi = Err(File.getPDBDbiStream()); 982 983 class Visitor : public ISectionContribVisitor { 984 public: 985 Visitor(LinePrinter &P) : P(P) {} 986 void visit(const SectionContrib &SC) override { 987 P.formatLine( 988 "SC | mod = {2}, {0}, size = {1}, data crc = {3}, reloc crc = {4}", 989 formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size), fmtle(SC.Imod), 990 fmtle(SC.DataCrc), fmtle(SC.RelocCrc)); 991 P.formatLine(" {0}", 992 formatSectionCharacteristics(P.getIndentLevel() + 6, 993 SC.Characteristics)); 994 } 995 void visit(const SectionContrib2 &SC) override { 996 P.formatLine("SC2 | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " 997 "crc = {4}, coff section = {5}", 998 formatSegmentOffset(SC.Base.ISect, SC.Base.Off), 999 fmtle(SC.Base.Size), fmtle(SC.Base.Imod), 1000 fmtle(SC.Base.DataCrc), fmtle(SC.Base.RelocCrc), 1001 fmtle(SC.ISectCoff)); 1002 P.formatLine(" {0}", 1003 formatSectionCharacteristics(P.getIndentLevel() + 6, 1004 SC.Base.Characteristics)); 1005 } 1006 1007 private: 1008 LinePrinter &P; 1009 }; 1010 1011 Visitor V(P); 1012 Dbi.visitSectionContributions(V); 1013 return Error::success(); 1014 } 1015 1016 Error DumpOutputStyle::dumpSectionMap() { 1017 printHeader(P, "Section Map"); 1018 ExitOnError Err("Error dumping section map"); 1019 1020 AutoIndent Indent(P); 1021 if (!File.hasPDBDbiStream()) { 1022 P.formatLine("Dumping the section map requires a DBI Stream, which could " 1023 "not be loaded"); 1024 return Error::success(); 1025 } 1026 1027 auto &Dbi = Err(File.getPDBDbiStream()); 1028 1029 uint32_t I = 0; 1030 for (auto &M : Dbi.getSectionMap()) { 1031 P.formatLine( 1032 "Section {0:4} | ovl = {0}, group = {1}, frame = {2}, name = {3}", I, 1033 fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName)); 1034 P.formatLine(" class = {0}, offset = {1}, size = {2}", 1035 fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength)); 1036 P.formatLine(" flags = {0}", 1037 formatSegMapDescriptorFlag( 1038 P.getIndentLevel() + 13, 1039 static_cast<OMFSegDescFlags>(uint16_t(M.Flags)))); 1040 ++I; 1041 } 1042 return Error::success(); 1043 } 1044