1 //===- llvm-pdbdump.cpp - Dump debug info from a PDB file -------*- 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 // Dumps debug information present in PDB files. This utility makes use of 11 // the Microsoft Windows SDK, so will not compile or run on non-Windows 12 // platforms. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "llvm-pdbdump.h" 17 #include "CompilandDumper.h" 18 #include "ExternalSymbolDumper.h" 19 #include "FunctionDumper.h" 20 #include "LinePrinter.h" 21 #include "TypeDumper.h" 22 #include "VariableDumper.h" 23 24 #include "llvm/ADT/ArrayRef.h" 25 #include "llvm/ADT/BitVector.h" 26 #include "llvm/ADT/DenseMap.h" 27 #include "llvm/ADT/StringExtras.h" 28 #include "llvm/Config/config.h" 29 #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" 30 #include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" 31 #include "llvm/DebugInfo/PDB/IPDBSession.h" 32 #include "llvm/DebugInfo/PDB/PDB.h" 33 #include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" 34 #include "llvm/DebugInfo/PDB/PDBSymbolData.h" 35 #include "llvm/DebugInfo/PDB/PDBSymbolExe.h" 36 #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" 37 #include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" 38 #include "llvm/Support/CommandLine.h" 39 #include "llvm/Support/ConvertUTF.h" 40 #include "llvm/Support/FileSystem.h" 41 #include "llvm/Support/Format.h" 42 #include "llvm/Support/ManagedStatic.h" 43 #include "llvm/Support/MemoryBuffer.h" 44 #include "llvm/Support/PrettyStackTrace.h" 45 #include "llvm/Support/Process.h" 46 #include "llvm/Support/raw_ostream.h" 47 #include "llvm/Support/Signals.h" 48 49 #if defined(HAVE_DIA_SDK) 50 #ifndef NOMINMAX 51 #define NOMINMAX 52 #endif 53 #include <Windows.h> 54 #endif 55 56 using namespace llvm; 57 58 namespace opts { 59 60 enum class PDB_DumpType { ByType, ByObjFile, Both }; 61 62 cl::list<std::string> InputFilenames(cl::Positional, 63 cl::desc("<input PDB files>"), 64 cl::OneOrMore); 65 66 cl::OptionCategory TypeCategory("Symbol Type Options"); 67 cl::OptionCategory FilterCategory("Filtering Options"); 68 cl::OptionCategory OtherOptions("Other Options"); 69 70 cl::opt<bool> Compilands("compilands", cl::desc("Display compilands"), 71 cl::cat(TypeCategory)); 72 cl::opt<bool> Symbols("symbols", cl::desc("Display symbols for each compiland"), 73 cl::cat(TypeCategory)); 74 cl::opt<bool> Globals("globals", cl::desc("Dump global symbols"), 75 cl::cat(TypeCategory)); 76 cl::opt<bool> Externals("externals", cl::desc("Dump external symbols"), 77 cl::cat(TypeCategory)); 78 cl::opt<bool> Types("types", cl::desc("Display types"), cl::cat(TypeCategory)); 79 cl::opt<bool> 80 All("all", cl::desc("Implies all other options in 'Symbol Types' category"), 81 cl::cat(TypeCategory)); 82 83 cl::opt<uint64_t> LoadAddress( 84 "load-address", 85 cl::desc("Assume the module is loaded at the specified address"), 86 cl::cat(OtherOptions)); 87 88 cl::opt<bool> DumpHeaders("dump-headers", cl::desc("dump PDB headers"), 89 cl::cat(OtherOptions)); 90 cl::opt<bool> DumpStreamSizes("dump-stream-sizes", 91 cl::desc("dump PDB stream sizes"), 92 cl::cat(OtherOptions)); 93 cl::opt<bool> DumpStreamBlocks("dump-stream-blocks", 94 cl::desc("dump PDB stream blocks"), 95 cl::cat(OtherOptions)); 96 cl::opt<std::string> DumpStreamData("dump-stream", cl::desc("dump stream data"), 97 cl::cat(OtherOptions)); 98 99 cl::list<std::string> 100 ExcludeTypes("exclude-types", 101 cl::desc("Exclude types by regular expression"), 102 cl::ZeroOrMore, cl::cat(FilterCategory)); 103 cl::list<std::string> 104 ExcludeSymbols("exclude-symbols", 105 cl::desc("Exclude symbols by regular expression"), 106 cl::ZeroOrMore, cl::cat(FilterCategory)); 107 cl::list<std::string> 108 ExcludeCompilands("exclude-compilands", 109 cl::desc("Exclude compilands by regular expression"), 110 cl::ZeroOrMore, cl::cat(FilterCategory)); 111 112 cl::list<std::string> IncludeTypes( 113 "include-types", 114 cl::desc("Include only types which match a regular expression"), 115 cl::ZeroOrMore, cl::cat(FilterCategory)); 116 cl::list<std::string> IncludeSymbols( 117 "include-symbols", 118 cl::desc("Include only symbols which match a regular expression"), 119 cl::ZeroOrMore, cl::cat(FilterCategory)); 120 cl::list<std::string> IncludeCompilands( 121 "include-compilands", 122 cl::desc("Include only compilands those which match a regular expression"), 123 cl::ZeroOrMore, cl::cat(FilterCategory)); 124 125 cl::opt<bool> ExcludeCompilerGenerated( 126 "no-compiler-generated", 127 cl::desc("Don't show compiler generated types and symbols"), 128 cl::cat(FilterCategory)); 129 cl::opt<bool> 130 ExcludeSystemLibraries("no-system-libs", 131 cl::desc("Don't show symbols from system libraries"), 132 cl::cat(FilterCategory)); 133 cl::opt<bool> NoClassDefs("no-class-definitions", 134 cl::desc("Don't display full class definitions"), 135 cl::cat(FilterCategory)); 136 cl::opt<bool> NoEnumDefs("no-enum-definitions", 137 cl::desc("Don't display full enum definitions"), 138 cl::cat(FilterCategory)); 139 } 140 141 142 static void reportError(StringRef Input, StringRef Message) { 143 if (Input == "-") 144 Input = "<stdin>"; 145 errs() << Input << ": " << Message << "\n"; 146 errs().flush(); 147 exit(1); 148 } 149 150 static void reportError(StringRef Input, std::error_code EC) { 151 reportError(Input, EC.message()); 152 } 153 154 static std::error_code checkOffset(MemoryBufferRef M, uintptr_t Addr, 155 const uint64_t Size) { 156 if (Addr + Size < Addr || Addr + Size < Size || 157 Addr + Size > uintptr_t(M.getBufferEnd()) || 158 Addr < uintptr_t(M.getBufferStart())) { 159 return std::make_error_code(std::errc::bad_address); 160 } 161 return std::error_code(); 162 } 163 164 template <typename T> 165 static std::error_code checkOffset(MemoryBufferRef M, ArrayRef<T> AR) { 166 return checkOffset(M, uintptr_t(AR.data()), (uint64_t)AR.size() * sizeof(T)); 167 } 168 169 static std::error_code checkOffset(MemoryBufferRef M, StringRef SR) { 170 return checkOffset(M, uintptr_t(SR.data()), SR.size()); 171 } 172 173 // Sets Obj unless any bytes in [addr, addr + size) fall outsize of m. 174 // Returns unexpected_eof if error. 175 template <typename T> 176 static std::error_code getObject(const T *&Obj, MemoryBufferRef M, 177 const void *Ptr, 178 const uint64_t Size = sizeof(T)) { 179 uintptr_t Addr = uintptr_t(Ptr); 180 if (std::error_code EC = checkOffset(M, Addr, Size)) 181 return EC; 182 Obj = reinterpret_cast<const T *>(Addr); 183 return std::error_code(); 184 } 185 186 static uint64_t bytesToBlocks(uint64_t NumBytes, uint64_t BlockSize) { 187 return RoundUpToAlignment(NumBytes, BlockSize) / BlockSize; 188 } 189 190 static uint64_t blockToOffset(uint64_t BlockNumber, uint64_t BlockSize) { 191 return BlockNumber * BlockSize; 192 } 193 194 static void dumpStructure(MemoryBufferRef M) { 195 const PDB::SuperBlock *SB; 196 if (auto EC = getObject(SB, M, M.getBufferStart())) 197 reportError(M.getBufferIdentifier(), EC); 198 199 if (opts::DumpHeaders) { 200 outs() << "BlockSize: " << SB->BlockSize << '\n'; 201 outs() << "Unknown0: " << SB->Unknown0 << '\n'; 202 outs() << "NumBlocks: " << SB->NumBlocks << '\n'; 203 outs() << "NumDirectoryBytes: " << SB->NumDirectoryBytes << '\n'; 204 outs() << "Unknown1: " << SB->Unknown1 << '\n'; 205 outs() << "BlockMapAddr: " << SB->BlockMapAddr << '\n'; 206 } 207 208 // We don't support blocksizes which aren't a multiple of four bytes. 209 if (SB->BlockSize % sizeof(support::ulittle32_t) != 0) 210 reportError(M.getBufferIdentifier(), 211 std::make_error_code(std::errc::illegal_byte_sequence)); 212 213 // We don't support directories whose sizes aren't a multiple of four bytes. 214 if (SB->NumDirectoryBytes % sizeof(support::ulittle32_t) != 0) 215 reportError(M.getBufferIdentifier(), 216 std::make_error_code(std::errc::illegal_byte_sequence)); 217 218 // The number of blocks which comprise the directory is a simple function of 219 // the number of bytes it contains. 220 uint64_t NumDirectoryBlocks = 221 bytesToBlocks(SB->NumDirectoryBytes, SB->BlockSize); 222 if (opts::DumpHeaders) 223 outs() << "NumDirectoryBlocks: " << NumDirectoryBlocks << '\n'; 224 225 // The block map, as we understand it, is a block which consists of a list of 226 // block numbers. 227 // It is unclear what would happen if the number of blocks couldn't fit on a 228 // single block. 229 if (NumDirectoryBlocks > SB->BlockSize / sizeof(support::ulittle32_t)) 230 reportError(M.getBufferIdentifier(), 231 std::make_error_code(std::errc::illegal_byte_sequence)); 232 233 234 uint64_t BlockMapOffset = (uint64_t)SB->BlockMapAddr * SB->BlockSize; 235 if (opts::DumpHeaders) 236 outs() << "BlockMapOffset: " << BlockMapOffset << '\n'; 237 238 // The directory is not contiguous. Instead, the block map contains a 239 // contiguous list of block numbers whose contents, when concatenated in 240 // order, make up the directory. 241 auto DirectoryBlocks = 242 makeArrayRef(reinterpret_cast<const support::ulittle32_t *>( 243 M.getBufferStart() + BlockMapOffset), 244 NumDirectoryBlocks); 245 if (auto EC = checkOffset(M, DirectoryBlocks)) 246 reportError(M.getBufferIdentifier(), EC); 247 248 if (opts::DumpHeaders) { 249 outs() << "DirectoryBlocks: ["; 250 for (const support::ulittle32_t &DirectoryBlockAddr : DirectoryBlocks) { 251 if (&DirectoryBlockAddr != &DirectoryBlocks.front()) 252 outs() << ", "; 253 outs() << DirectoryBlockAddr; 254 } 255 outs() << "]\n"; 256 } 257 258 bool SeenNumStreams = false; 259 uint32_t NumStreams = 0; 260 std::vector<uint32_t> StreamSizes; 261 DenseMap<uint32_t, std::vector<uint32_t>> StreamMap; 262 uint32_t StreamIdx = 0; 263 uint64_t DirectoryBytesRead = 0; 264 // The structure of the directory is as follows: 265 // struct PDBDirectory { 266 // uint32_t NumStreams; 267 // uint32_t StreamSizes[NumStreams]; 268 // uint32_t StreamMap[NumStreams][]; 269 // }; 270 // 271 // Empty streams don't consume entries in the StreamMap. 272 for (uint32_t DirectoryBlockAddr : DirectoryBlocks) { 273 uint64_t DirectoryBlockOffset = 274 blockToOffset(DirectoryBlockAddr, SB->BlockSize); 275 auto DirectoryBlock = 276 makeArrayRef(reinterpret_cast<const support::ulittle32_t *>( 277 M.getBufferStart() + DirectoryBlockOffset), 278 SB->BlockSize / sizeof(support::ulittle32_t)); 279 if (auto EC = checkOffset(M, DirectoryBlock)) 280 reportError(M.getBufferIdentifier(), EC); 281 282 // We read data out of the directory four bytes at a time. Depending on 283 // where we are in the directory, the contents may be: the number of streams 284 // in the directory, a stream's size, or a block in the stream map. 285 for (uint32_t Data : DirectoryBlock) { 286 // Don't read beyond the end of the directory. 287 if (DirectoryBytesRead == SB->NumDirectoryBytes) 288 break; 289 290 DirectoryBytesRead += sizeof(Data); 291 292 // This data must be the number of streams if we haven't seen it yet. 293 if (!SeenNumStreams) { 294 NumStreams = Data; 295 SeenNumStreams = true; 296 continue; 297 } 298 // This data must be a stream size if we have not seen them all yet. 299 if (StreamSizes.size() < NumStreams) { 300 // It seems like some streams have their set to -1 when their contents 301 // are not present. Treat them like empty streams for now. 302 if (Data == UINT32_MAX) 303 StreamSizes.push_back(0); 304 else 305 StreamSizes.push_back(Data); 306 continue; 307 } 308 309 // This data must be a stream block number if we have seen all of the 310 // stream sizes. 311 std::vector<uint32_t> *StreamBlocks = nullptr; 312 // Figure out which stream this block number belongs to. 313 while (StreamIdx < NumStreams) { 314 uint64_t NumExpectedStreamBlocks = 315 bytesToBlocks(StreamSizes[StreamIdx], SB->BlockSize); 316 StreamBlocks = &StreamMap[StreamIdx]; 317 if (NumExpectedStreamBlocks > StreamBlocks->size()) 318 break; 319 ++StreamIdx; 320 } 321 // It seems this block doesn't belong to any stream? The stream is either 322 // corrupt or something more mysterious is going on. 323 if (StreamIdx == NumStreams) 324 reportError(M.getBufferIdentifier(), 325 std::make_error_code(std::errc::illegal_byte_sequence)); 326 327 StreamBlocks->push_back(Data); 328 } 329 } 330 331 // We should have read exactly SB->NumDirectoryBytes bytes. 332 assert(DirectoryBytesRead == SB->NumDirectoryBytes); 333 334 if (opts::DumpHeaders) 335 outs() << "NumStreams: " << NumStreams << '\n'; 336 if (opts::DumpStreamSizes) 337 for (uint32_t StreamIdx = 0; StreamIdx < NumStreams; ++StreamIdx) 338 outs() << "StreamSizes[" << StreamIdx << "]: " << StreamSizes[StreamIdx] 339 << '\n'; 340 341 if (opts::DumpStreamBlocks) { 342 for (uint32_t StreamIdx = 0; StreamIdx < NumStreams; ++StreamIdx) { 343 outs() << "StreamBlocks[" << StreamIdx << "]: ["; 344 std::vector<uint32_t> &StreamBlocks = StreamMap[StreamIdx]; 345 for (uint32_t &StreamBlock : StreamBlocks) { 346 if (&StreamBlock != &StreamBlocks.front()) 347 outs() << ", "; 348 outs() << StreamBlock; 349 } 350 outs() << "]\n"; 351 } 352 } 353 354 StringRef DumpStreamStr = opts::DumpStreamData; 355 uint32_t DumpStreamNum; 356 if (!DumpStreamStr.getAsInteger(/*Radix=*/0U, DumpStreamNum) && 357 DumpStreamNum < NumStreams) { 358 uint32_t StreamBytesRead = 0; 359 uint32_t StreamSize = StreamSizes[DumpStreamNum]; 360 std::vector<uint32_t> &StreamBlocks = StreamMap[DumpStreamNum]; 361 for (uint32_t &StreamBlockAddr : StreamBlocks) { 362 uint64_t StreamBlockOffset = blockToOffset(StreamBlockAddr, SB->BlockSize); 363 uint32_t BytesLeftToReadInStream = StreamSize - StreamBytesRead; 364 if (BytesLeftToReadInStream == 0) 365 break; 366 367 uint32_t BytesToReadInBlock = std::min( 368 BytesLeftToReadInStream, static_cast<uint32_t>(SB->BlockSize)); 369 auto StreamBlockData = 370 StringRef(M.getBufferStart() + StreamBlockOffset, BytesToReadInBlock); 371 if (auto EC = checkOffset(M, StreamBlockData)) 372 reportError(M.getBufferIdentifier(), EC); 373 374 outs() << StreamBlockData; 375 StreamBytesRead += StreamBlockData.size(); 376 } 377 } 378 } 379 380 static void dumpInput(StringRef Path) { 381 if (opts::DumpHeaders || !opts::DumpStreamData.empty()) { 382 ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer = 383 MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, 384 /*RequiresNullTerminator=*/false); 385 386 if (std::error_code EC = ErrorOrBuffer.getError()) 387 reportError(Path, EC); 388 389 std::unique_ptr<MemoryBuffer> &Buffer = ErrorOrBuffer.get(); 390 391 dumpStructure(Buffer->getMemBufferRef()); 392 393 outs().flush(); 394 return; 395 } 396 397 std::unique_ptr<IPDBSession> Session; 398 PDB_ErrorCode Error = loadDataForPDB(PDB_ReaderType::DIA, Path, Session); 399 switch (Error) { 400 case PDB_ErrorCode::Success: 401 break; 402 case PDB_ErrorCode::NoPdbImpl: 403 outs() << "Reading PDBs is not supported on this platform.\n"; 404 return; 405 case PDB_ErrorCode::InvalidPath: 406 outs() << "Unable to load PDB at '" << Path 407 << "'. Check that the file exists and is readable.\n"; 408 return; 409 case PDB_ErrorCode::InvalidFileFormat: 410 outs() << "Unable to load PDB at '" << Path 411 << "'. The file has an unrecognized format.\n"; 412 return; 413 default: 414 outs() << "Unable to load PDB at '" << Path 415 << "'. An unknown error occured.\n"; 416 return; 417 } 418 if (opts::LoadAddress) 419 Session->setLoadAddress(opts::LoadAddress); 420 421 LinePrinter Printer(2, outs()); 422 423 auto GlobalScope(Session->getGlobalScope()); 424 std::string FileName(GlobalScope->getSymbolsFileName()); 425 426 WithColor(Printer, PDB_ColorItem::None).get() << "Summary for "; 427 WithColor(Printer, PDB_ColorItem::Path).get() << FileName; 428 Printer.Indent(); 429 uint64_t FileSize = 0; 430 431 Printer.NewLine(); 432 WithColor(Printer, PDB_ColorItem::Identifier).get() << "Size"; 433 if (!sys::fs::file_size(FileName, FileSize)) { 434 Printer << ": " << FileSize << " bytes"; 435 } else { 436 Printer << ": (Unable to obtain file size)"; 437 } 438 439 Printer.NewLine(); 440 WithColor(Printer, PDB_ColorItem::Identifier).get() << "Guid"; 441 Printer << ": " << GlobalScope->getGuid(); 442 443 Printer.NewLine(); 444 WithColor(Printer, PDB_ColorItem::Identifier).get() << "Age"; 445 Printer << ": " << GlobalScope->getAge(); 446 447 Printer.NewLine(); 448 WithColor(Printer, PDB_ColorItem::Identifier).get() << "Attributes"; 449 Printer << ": "; 450 if (GlobalScope->hasCTypes()) 451 outs() << "HasCTypes "; 452 if (GlobalScope->hasPrivateSymbols()) 453 outs() << "HasPrivateSymbols "; 454 Printer.Unindent(); 455 456 if (opts::Compilands) { 457 Printer.NewLine(); 458 WithColor(Printer, PDB_ColorItem::SectionHeader).get() 459 << "---COMPILANDS---"; 460 Printer.Indent(); 461 auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>(); 462 CompilandDumper Dumper(Printer); 463 while (auto Compiland = Compilands->getNext()) 464 Dumper.start(*Compiland, false); 465 Printer.Unindent(); 466 } 467 468 if (opts::Types) { 469 Printer.NewLine(); 470 WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---TYPES---"; 471 Printer.Indent(); 472 TypeDumper Dumper(Printer); 473 Dumper.start(*GlobalScope); 474 Printer.Unindent(); 475 } 476 477 if (opts::Symbols) { 478 Printer.NewLine(); 479 WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---SYMBOLS---"; 480 Printer.Indent(); 481 auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>(); 482 CompilandDumper Dumper(Printer); 483 while (auto Compiland = Compilands->getNext()) 484 Dumper.start(*Compiland, true); 485 Printer.Unindent(); 486 } 487 488 if (opts::Globals) { 489 Printer.NewLine(); 490 WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---GLOBALS---"; 491 Printer.Indent(); 492 { 493 FunctionDumper Dumper(Printer); 494 auto Functions = GlobalScope->findAllChildren<PDBSymbolFunc>(); 495 while (auto Function = Functions->getNext()) { 496 Printer.NewLine(); 497 Dumper.start(*Function, FunctionDumper::PointerType::None); 498 } 499 } 500 { 501 auto Vars = GlobalScope->findAllChildren<PDBSymbolData>(); 502 VariableDumper Dumper(Printer); 503 while (auto Var = Vars->getNext()) 504 Dumper.start(*Var); 505 } 506 { 507 auto Thunks = GlobalScope->findAllChildren<PDBSymbolThunk>(); 508 CompilandDumper Dumper(Printer); 509 while (auto Thunk = Thunks->getNext()) 510 Dumper.dump(*Thunk); 511 } 512 Printer.Unindent(); 513 } 514 if (opts::Externals) { 515 Printer.NewLine(); 516 WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---EXTERNALS---"; 517 Printer.Indent(); 518 ExternalSymbolDumper Dumper(Printer); 519 Dumper.start(*GlobalScope); 520 } 521 outs().flush(); 522 } 523 524 int main(int argc_, const char *argv_[]) { 525 // Print a stack trace if we signal out. 526 sys::PrintStackTraceOnErrorSignal(); 527 PrettyStackTraceProgram X(argc_, argv_); 528 529 SmallVector<const char *, 256> argv; 530 SpecificBumpPtrAllocator<char> ArgAllocator; 531 std::error_code EC = sys::Process::GetArgumentVector( 532 argv, makeArrayRef(argv_, argc_), ArgAllocator); 533 if (EC) { 534 errs() << "error: couldn't get arguments: " << EC.message() << '\n'; 535 return 1; 536 } 537 538 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 539 540 cl::ParseCommandLineOptions(argv.size(), argv.data(), "LLVM PDB Dumper\n"); 541 if (opts::All) { 542 opts::Compilands = true; 543 opts::Symbols = true; 544 opts::Globals = true; 545 opts::Types = true; 546 opts::Externals = true; 547 } 548 if (opts::ExcludeCompilerGenerated) { 549 opts::ExcludeTypes.push_back("__vc_attributes"); 550 opts::ExcludeCompilands.push_back("* Linker *"); 551 } 552 if (opts::ExcludeSystemLibraries) { 553 opts::ExcludeCompilands.push_back( 554 "f:\\binaries\\Intermediate\\vctools\\crt_bld"); 555 } 556 557 #if defined(HAVE_DIA_SDK) 558 CoInitializeEx(nullptr, COINIT_MULTITHREADED); 559 #endif 560 561 std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(), 562 dumpInput); 563 564 #if defined(HAVE_DIA_SDK) 565 CoUninitialize(); 566 #endif 567 568 return 0; 569 } 570