1 /// Copyright: Copyright (c) 2017-2019 Andrey Penechko. 2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 3 /// Authors: Andrey Penechko. 4 5 /// .pdb reader and writer 6 /// PDB docs http://llvm.org/docs/PDB/index.html 7 /// MSF docs http://llvm.org/docs/PDB/MsfFile.html 8 /// CodeView http://pierrelib.pagesperso-orange.fr/exec_formats/MS_Symbol_Type_v1.0.pdf 9 /// https://github.com/mountainstorm/pdbfile 10 /// https://github.com/willglynn/pdb 11 /// https://github.com/smx-smx/PDBSharp 12 13 /// todo: 14 /// - S_BPREL32 15 /// - C13 DEBUG SUBSECTIONS: fileChecksums, line numbers, GlobalRefs 16 /// - TPI hash stream, IPI hash stream 17 module vox.be.debug_info.pdb; 18 19 public import vox.be.debug_info.pdb.symbol; 20 public import vox.be.debug_info.pdb.codeview; 21 22 import std.bitmanip : bitfields; 23 import std.file; 24 import std.format : formattedWrite; 25 import std.path; 26 import std.range : repeat; 27 import std.string : fromStringz; 28 29 import vox.utils; 30 31 /* 32 pdb format 33 34 MSF - Multistream File 35 36 file consists of pages of size 512, 1024, 2048 or 4096 37 Meta-stream stores information about all streams in .pdb file. It has following format: 38 uint numStreams 39 uint[numStreams] streamSizes 40 uint[][numStreams] streamPages 41 Note: some streams produced by MSVC have uint.max size. We interpret that as 0 size. 42 It is stored in a list of pages. 43 Array of indices to those pages are stored in a page `metaStreamHeaderPage` 44 Free page bitmap is a page that stores a bit per page. 1 if page is free, 0 if not. 45 Page map... 46 47 Page 0 48 MSF magic 49 MSF header 50 Page 1 \ freePageBitmapIndex is 1 or 2 51 Page 2 / 52 Page metaStreamHeaderPage 53 ... 54 Page 55 */ 56 57 struct PdbInfo 58 { 59 string filename; 60 ubyte[] fileData; 61 MsfHeader msfHeader; 62 63 // Those are valid if != 0 64 uint linkInfoStreamIndex; // /LinkInfo 65 uint headerblockStreamIndex; // /src/headerblock 66 uint namesStreamIndex; // /names 67 uint globalStreamIndex; 68 uint publicStreamIndex; 69 uint symRecordStream; 70 71 StreamInfo[] streamInfos; 72 void setStreamName(uint index, string name) 73 { 74 if (index == ushort.max) return; // this index is not valid 75 enforce(index < streamInfos.length); 76 enforce(streamInfos[index].name is null, 77 format("stream %s, already has name '%s', trying to set to '%s'", 78 index, streamInfos[index].name, name)); 79 streamInfos[index].name = name; 80 } 81 82 void printStreamInfos() 83 { 84 foreach(i, ref info; streamInfos) 85 { 86 writefln("%s %s %s bytes", i, info.name, info.streamBytes); 87 } 88 } 89 } 90 91 struct StreamInfo 92 { 93 string name; 94 uint streamBytes; 95 } 96 97 struct PdbReader 98 { 99 PdbInfo pdb; 100 string filename; 101 ubyte[] fileData; 102 103 MsfHeader* msfHeader; 104 string[100] longestNames; 105 106 void visitString(string str) 107 { 108 foreach(ref longStr; longestNames) 109 if (str.length > longStr.length) 110 swap(longStr, str); 111 } 112 113 static PdbReader fromFile(string filename) 114 { 115 enforce(exists(filename), format("%s does not exist", filename)); 116 enforce(extension(filename) == ".pdb", format("%s must have .pdb extension", filename)); 117 118 ubyte[] fileData = cast(ubyte[])std.file.read(filename); 119 return fromBytes(fileData, filename); 120 } 121 122 static PdbReader fromBytes(ubyte[] fileData, string filename = null) 123 { 124 PdbReader reader; 125 reader.filename = filename; 126 reader.fileData = fileData; 127 reader.load(); 128 return reader; 129 } 130 131 /// Performs actual processing of .pdb data stored in fileData 132 void load() 133 { 134 writefln("Load %s %s bytes", filename, fileData.length); 135 136 auto slicer = FileDataSlicer(fileData); 137 138 // MSF Magic 139 enforce(slicer.fileData.length >= MsfMagic.length, format("%s: Error: Invalid PDB file, MSF magic is truncated", filename)); 140 ubyte[] msfMagic = slicer.getArrayOf!ubyte(MsfMagic.length); 141 enforce(msfMagic == MsfMagic, "Invalid magic"); 142 143 // MSF Header 144 pdb.msfHeader = *slicer.getPtrTo!MsfHeader; 145 pdb.msfHeader.validate; 146 enforce(pdb.msfHeader.numPages * pdb.msfHeader.pageSize == fileData.length, 147 format("Invalid file size (%s) != num pages (%s) * page size (%s)", 148 fileData.length, pdb.msfHeader.numPages, pdb.msfHeader.pageSize)); 149 150 writeln(pdb.msfHeader); 151 152 // Meta stream contains info about all streams 153 slicer.fileCursor = pdb.msfHeader.metaStreamHeaderPage * pdb.msfHeader.pageSize; 154 writefln("meta stream header page %s, offset 0x%X", pdb.msfHeader.metaStreamHeaderPage, slicer.fileCursor); 155 156 uint[] metaStreamPagesIndices = slicer.getArrayOf!uint(pdb.msfHeader.numMetaStreamPages); 157 writefln("meta stream pages %s", metaStreamPagesIndices); 158 159 StreamReader metaStream; 160 metaStream.fileData = fileData; 161 metaStream.streamBytes = pdb.msfHeader.metaStreamBytes; 162 metaStream.pageSize = pdb.msfHeader.pageSize; 163 metaStream.pages = metaStreamPagesIndices; 164 165 uint numStreams = metaStream.read!uint; 166 writefln("Number of streams %s", numStreams); 167 168 pdb.streamInfos = new StreamInfo[numStreams]; 169 pdb.setStreamName(FixedStream.old_directory, "Old directory"); 170 pdb.setStreamName(FixedStream.pdb_stream, "PDB stream"); 171 pdb.setStreamName(FixedStream.tpi_stream, "TPI stream"); 172 pdb.setStreamName(FixedStream.dbi_stream, "DBI stream"); 173 pdb.setStreamName(FixedStream.ipi_stream, "IPI stream"); 174 175 StreamReader[] streams = new StreamReader[numStreams]; 176 foreach(i, ref stream; streams) 177 { 178 stream.fileData = fileData; 179 stream.streamBytes = metaStream.read!uint; 180 pdb.streamInfos[i].streamBytes = stream.streamBytes; 181 if (stream.streamBytes == uint.max) stream.streamBytes = 0; 182 183 stream.pageSize = pdb.msfHeader.pageSize; 184 } 185 186 foreach(streamIndex, ref stream; streams) 187 { 188 uint numPages = divCeil(stream.streamBytes, stream.pageSize); 189 stream.pages = new uint[numPages]; 190 metaStream.readIntoArray(stream.pages); 191 foreach(page; stream.pages) 192 { 193 enforce(page < pdb.msfHeader.numPages, 194 format("Stream %s contains page %s. Number of pages is %s", 195 streamIndex, page, pdb.msfHeader.numPages)); 196 } 197 } 198 writeln; 199 200 201 writefln("Streams:"); 202 foreach(i, ref stream; streams) 203 { 204 writefln(" %s %s", i, stream); 205 } 206 writeln; 207 208 209 writefln("# PDB stream %s", cast(uint)FixedStream.pdb_stream); 210 StreamReader* pdbStream = &streams[FixedStream.pdb_stream]; 211 auto pdbStreamHeader = pdbStream.read!PdbStreamHeader; 212 writefln("PDB stream header %s", pdbStreamHeader); 213 214 writefln(" Named streams hashmap"); 215 uint stringBufLength = pdbStream.read!uint; 216 string namedStreamStringBuf = cast(string)pdbStream.readArray!char(stringBufLength); 217 writefln(" string buffer %s", namedStreamStringBuf); 218 219 // size == number of present keys == number of present values 220 uint hashmapSize = pdbStream.read!uint; 221 // capacity == number of keys + number of values 222 uint hashmapCapacity = pdbStream.read!uint; 223 writefln(" size %s", hashmapSize); 224 writefln(" capacity %s", hashmapCapacity); 225 226 uint presentBitmapWords = pdbStream.read!uint; 227 uint[] presentBitmap = pdbStream.readArray!uint(presentBitmapWords); 228 writefln(" presentBitmap %s", presentBitmap); 229 230 uint deletedBitmapWords = pdbStream.read!uint; 231 uint[] deletedBitmap = pdbStream.readArray!uint(deletedBitmapWords); 232 writefln(" deletedBitmap %s", deletedBitmap); 233 234 writefln(" buckets:"); 235 236 // gather named streams 237 // iterate set bits in uint 238 // for each bit set an entry follows 239 foreach(size_t bucketIndex; presentBitmap.bitsSet) 240 { 241 uint nameBufOffset = pdbStream.read!uint; 242 uint streamIndex = pdbStream.read!uint; 243 244 string name = namedStreamStringBuf[nameBufOffset..$].ptr.fromStringz; 245 writefln(" % 4s %s", streamIndex, name); 246 247 switch(name) 248 { 249 case "/LinkInfo": pdb.linkInfoStreamIndex = streamIndex; break; 250 case "/src/headerblock": pdb.headerblockStreamIndex = streamIndex; break; 251 case "/names": pdb.namesStreamIndex = streamIndex; break; 252 default: break; // ignore unknown 253 } 254 255 pdb.setStreamName(streamIndex, name); 256 } 257 258 uint pdbFeatures; 259 write(" PDB features:"); 260 while(!pdbStream.empty) 261 { 262 uint featureCode = pdbStream.read!uint; 263 switch(featureCode) with(Pdb_FeatureCodeMagic) 264 { 265 case vc110: pdbFeatures |= Pdb_FeatureFlags.vc110; write(" vc110"); break; 266 case vc140: pdbFeatures |= Pdb_FeatureFlags.vc140; write(" vc140"); break; 267 case noTypeMerge: pdbFeatures |= Pdb_FeatureFlags.noTypeMerge; write(" noTypeMerge"); break; 268 case minimalDebugInfo: pdbFeatures |= Pdb_FeatureFlags.minimalDebugInfo; write(" minimalDebugInfo"); break; 269 default: 270 writef(" feature(0x%x)", featureCode); 271 break; // ignore unknown feature 272 } 273 } 274 writeln; 275 writeln; 276 277 278 StreamReader* tpiStream = &streams[FixedStream.tpi_stream]; 279 writefln("# TPI stream %s, %s bytes", cast(uint)FixedStream.tpi_stream, tpiStream.remainingBytes); 280 auto tpiStreamHeader = tpiStream.read!TpiStreamHeader; 281 tpiStreamHeader.print; 282 pdb.setStreamName(tpiStreamHeader.hashStreamIndex, "TPI Hash stream"); 283 pdb.setStreamName(tpiStreamHeader.hashAuxStreamIndex, "TPI Hash aux stream"); 284 285 size_t typeIndex; 286 while(tpiStream.remainingBytes) 287 { 288 auto len = tpiStream.read!ushort; 289 auto start = tpiStream.streamCursor; 290 auto end = start + len; 291 292 auto kind = tpiStream.read!ushort; 293 writefln("- TPI:%s %s %s bytes", typeIndex, cast(TypeRecordKind)kind, len); 294 295 switch(kind) with(TypeRecordKind) 296 { 297 case LF_ARGLIST: 298 auto argcount = tpiStream.read!uint; 299 writefln(" number of parameters %s", argcount); 300 foreach(index; 0..argcount) 301 { 302 auto arg = tpiStream.read!TypeIndex; 303 writefln(" arg %s", arg); 304 } 305 break; 306 case LF_PROCEDURE: 307 auto proc = tpiStream.read!CVType_PROCEDURE; 308 writefln(" return type %s", proc.returnType); 309 writefln(" calling convention %s", proc.callConv); 310 writefln(" number of parameters %s", proc.numParams); 311 writefln(" arguments %s", proc.argList); 312 break; 313 default: 314 tpiStream.drop(len - 2); 315 break; 316 } 317 318 auto padding = end - tpiStream.streamCursor; 319 assert(padding < 4); 320 if (padding) writefln(" padding %s bytes", padding); 321 322 // skip padding 323 tpiStream.streamCursor = end; 324 325 ++typeIndex; 326 } 327 assert(typeIndex + 0x1000 == tpiStreamHeader.typeIndexEnd); 328 writeln; 329 330 331 StreamReader* ipiStream = &streams[FixedStream.ipi_stream]; 332 writefln("# IPI stream header %s, %s bytes", cast(uint)FixedStream.ipi_stream, ipiStream.remainingBytes); 333 auto ipiStreamHeader = ipiStream.read!TpiStreamHeader; 334 ipiStreamHeader.print; 335 pdb.setStreamName(ipiStreamHeader.hashStreamIndex, "IPI Hash stream"); 336 pdb.setStreamName(ipiStreamHeader.hashAuxStreamIndex, "IPI Hash aux stream"); 337 338 while(ipiStream.remainingBytes) 339 { 340 auto start = ipiStream.streamCursor; 341 auto len = ipiStream.read!ushort; 342 auto end = start + len + 2; 343 344 auto kind = ipiStream.read!ushort; 345 writefln("- IPI:%s (%06X) length %s kind %s", typeIndex + 0x1000, start, len, cast(TypeRecordKind)kind); 346 347 switch(kind) with(TypeRecordKind) 348 { 349 case LF_FUNC_ID: 350 auto funcId = ipiStream.read!CVType_FUNC_ID; 351 writefln(" scope id %s", funcId.scopeId); 352 writefln(" type %s, size %s", funcId.type, CVType_FUNC_ID.sizeof); 353 string name = ipiStream.readNameBefore(end); 354 visitString(name); 355 writefln(" name `%s`", name); 356 break; 357 358 case LF_MFUNC_ID: 359 auto funcId = ipiStream.read!CVType_MFUNC_ID; 360 writefln(" type %s", funcId.type); 361 writefln(" parent type %s", funcId.parentType); 362 string name = ipiStream.readNameBefore(end); 363 visitString(name); 364 writefln(" name `%s`", name); 365 break; 366 367 case LF_STRING_ID: 368 auto stringId = ipiStream.read!CVType_STRING_ID; 369 writefln(" id 0x%X", stringId.id); 370 string name = ipiStream.readNameBefore(end); 371 visitString(name); 372 writefln(" name `%s`", name); 373 break; 374 375 case LF_UDT_SRC_LINE: 376 auto udtSrcLine = ipiStream.read!UdtSrcLine; 377 writefln(" type = %s, source file = %s, line = %s", udtSrcLine.udt, udtSrcLine.sourceFile, udtSrcLine.lineNumber); 378 break; 379 380 case LF_SUBSTR_LIST: 381 auto argcount = ipiStream.read!uint; 382 writefln(" number of substrings %s", argcount); 383 foreach(index; 0..argcount) 384 { 385 auto arg = ipiStream.read!TypeIndex; 386 writefln(" substr %s", arg); 387 } 388 break; 389 390 case LF_BUILDINFO: 391 auto buildInfo = ipiStream.read!CVType_BUILDINFO; 392 writefln(" number of args %s", buildInfo.count); 393 writefln(" CurrentDirectory %s", buildInfo.args[CV_BuildInfo.CurrentDirectory]); 394 writefln(" BuildTool %s", buildInfo.args[CV_BuildInfo.BuildTool]); 395 writefln(" SourceFile %s", buildInfo.args[CV_BuildInfo.SourceFile]); 396 writefln(" ProgramDatabaseFile %s", buildInfo.args[CV_BuildInfo.ProgramDatabaseFile]); 397 writefln(" CommandArguments %s", buildInfo.args[CV_BuildInfo.CommandArguments]); 398 break; 399 400 default: 401 string stringBuf2 = new char[len - 2]; 402 ipiStream.readIntoArray(cast(char[])stringBuf2); 403 writefln(" buf `%s`", stringBuf2); 404 break; 405 } 406 407 auto padding = end - ipiStream.streamCursor; 408 assert(padding < 4); 409 if (padding) writefln(" padding: %s bytes", padding); 410 411 // skip padding 412 ipiStream.streamCursor = end; 413 414 ++typeIndex; 415 } 416 writeln; 417 418 419 StreamReader* dbiStream = &streams[FixedStream.dbi_stream]; 420 auto dbiStreamHeader = dbiStream.read!DbiStreamHeader; 421 writefln("# DBI stream %s, %s bytes", cast(uint)FixedStream.dbi_stream, dbiStream.remainingBytes); 422 writefln(" DbiStreamHeader %s", dbiStreamHeader); 423 424 pdb.globalStreamIndex = dbiStreamHeader.GlobalStreamIndex; 425 pdb.publicStreamIndex = dbiStreamHeader.PublicStreamIndex; 426 pdb.symRecordStream = dbiStreamHeader.SymRecordStream; 427 pdb.setStreamName(pdb.globalStreamIndex, "Global Symbol stream"); 428 pdb.setStreamName(pdb.publicStreamIndex, "Public Symbol stream"); 429 pdb.setStreamName(pdb.symRecordStream, "Symbol record"); 430 431 uint substreamsSize = dbiStreamHeader.ModInfoSize 432 + dbiStreamHeader.SectionContributionSize 433 + dbiStreamHeader.SectionMapSize 434 + dbiStreamHeader.SourceInfoSize 435 + dbiStreamHeader.TypeServerMapSize 436 + dbiStreamHeader.OptionalDbgHeaderSize 437 + dbiStreamHeader.ECSubstreamSize; 438 439 enforce(substreamsSize == dbiStream.remainingBytes, 440 format("substream size sum (%s) != remainingBytes (%s)", 441 substreamsSize, dbiStream.remainingBytes)); 442 443 StreamReader dbiModInfoStream = dbiStream.substream(dbiStreamHeader.ModInfoSize); 444 StreamReader dbiSectionContributionStream = dbiStream.substream(dbiStreamHeader.SectionContributionSize); 445 StreamReader dbiSectionMapStream = dbiStream.substream(dbiStreamHeader.SectionMapSize); 446 StreamReader dbiSourceInfoStream = dbiStream.substream(dbiStreamHeader.SourceInfoSize); 447 StreamReader dbiTypeServerMapStream = dbiStream.substream(dbiStreamHeader.TypeServerMapSize); 448 StreamReader dbiECSubstreamStream = dbiStream.substream(dbiStreamHeader.ECSubstreamSize); 449 StreamReader dbiOptionalDbgHeaderStream = dbiStream.substream(dbiStreamHeader.OptionalDbgHeaderSize); 450 enforce(dbiStream.remainingBytes == 0); 451 writeln; 452 453 454 writefln("## dbiModInfo substream: %s bytes", dbiModInfoStream.remainingBytes); 455 while(dbiModInfoStream.remainingBytes) 456 { 457 auto modInfoStart = dbiModInfoStream.streamCursor; 458 auto modInfo = dbiModInfoStream.read!ModInfo; 459 writefln("(%06X) ModInfo", modInfoStart); 460 string moduleName = dbiModInfoStream.readZString; 461 pdb.setStreamName(modInfo.ModuleSymStream, format("module sym stream: %s", moduleName)); 462 writefln(" moduleName %s", moduleName); 463 string objFileName = dbiModInfoStream.readZString; 464 writefln(" objFileName %s", objFileName); 465 dbiModInfoStream.dropPadding(4); 466 writefln(" % 8s module sym stream", modInfo.ModuleSymStream); 467 writefln(" %04X Flags", modInfo.Flags); 468 writefln(" SectionContr %s", modInfo.SectionContr); 469 writefln(" % 8s SymByteSize", modInfo.SymByteSize); 470 writefln(" % 8s C11ByteSize", modInfo.C11ByteSize); 471 writefln(" % 8s C13ByteSize", modInfo.C13ByteSize); 472 writefln(" % 8s SourceFileCount", modInfo.SourceFileCount); 473 writefln(" % 8s SourceFileNameIndex", modInfo.SourceFileNameIndex); 474 writefln(" % 8s PdbFilePathNameIndex", modInfo.PdbFilePathNameIndex); 475 476 if (modInfo.ModuleSymStream != ushort.max) 477 { 478 StreamReader modSymStream = streams[modInfo.ModuleSymStream]; 479 writefln("# Module symbol stream %s: %s bytes", modInfo.ModuleSymStream, modSymStream.remainingBytes); 480 481 // SYMBOLS 482 StreamReader symSubstream = modSymStream.substream(modInfo.SymByteSize); 483 auto debugMagic = symSubstream.read!uint; 484 enforce(debugMagic == COFF_DEBUG_SECTION_MAGIC, 485 format("Invalid magic (%s), expected %s", 486 debugMagic, COFF_DEBUG_SECTION_MAGIC)); 487 parseSymbols(symSubstream); 488 enforce(symSubstream.remainingBytes == 0); 489 writeln; 490 491 // C11, legacy 492 StreamReader c11Substream = modSymStream.substream(modInfo.C11ByteSize); 493 if (c11Substream.remainingBytes) { 494 writefln("--- C11 DEBUG SUBSECTIONS: %s bytes ---", modInfo.C11ByteSize); 495 printHex(c11Substream.readArray!ubyte(c11Substream.remainingBytes), 16, PrintAscii.yes); 496 } 497 498 // C13 499 StreamReader c13Substream = modSymStream.substream(modInfo.C13ByteSize); 500 if (c13Substream.remainingBytes) { 501 writefln("--- C13 DEBUG SUBSECTIONS: %s bytes ---", modInfo.C13ByteSize); 502 // read C13 debug subsections 503 while(c13Substream.remainingBytes) 504 { 505 // read DebugSubsectionHeader 506 auto kind = c13Substream.read!DebugSubsectionKind; 507 if (kind == DebugSubsectionKind.none) break; // last entry 508 auto length = c13Substream.read!uint; 509 writefln(" - %s: %s bytes", kind, length); 510 printHex(c13Substream.readArray!ubyte(length), 16, PrintAscii.yes); 511 } 512 enforce(c13Substream.remainingBytes == 0); 513 } 514 515 // GlobalRefs, unknown purpose 516 uint GlobalRefsSize = modSymStream.read!uint; 517 if (GlobalRefsSize) { 518 writefln("--- GlobalRefs: %s bytes ---", GlobalRefsSize); 519 printHex(modSymStream.readArray!ubyte(GlobalRefsSize), 16, PrintAscii.yes); 520 } 521 522 enforce(modSymStream.remainingBytes == 0); 523 } 524 writeln; 525 } 526 enforce(dbiModInfoStream.remainingBytes == 0); 527 writeln; 528 529 530 writefln("## dbiSectionContribution substream: %s bytes", dbiSectionContributionStream.remainingBytes); 531 auto secContribVer = dbiSectionContributionStream.read!SectionContrSubstreamVersion; 532 writefln(" Section Contr Substream Version %s", secContribVer); 533 enforce(secContribVer == SectionContrSubstreamVersion.Ver60, "only SectionContrSubstreamVersion.Ver60 is supported"); 534 while(dbiSectionContributionStream.remainingBytes) { 535 auto entry = dbiSectionContributionStream.read!SectionContribEntry; 536 writefln(" %s", entry); 537 } 538 enforce(dbiSectionContributionStream.remainingBytes == 0); 539 writeln; 540 541 542 writefln("## dbiSectionMap substream: %s bytes", dbiSectionMapStream.remainingBytes); 543 auto sectionMapHeader = dbiSectionMapStream.read!SectionMapHeader; 544 writefln("Section map header %s", sectionMapHeader); 545 while(dbiSectionMapStream.remainingBytes) { 546 auto entry = dbiSectionMapStream.read!SectionMapEntry; 547 writefln(" %s", entry); 548 } 549 writeln; 550 551 //writefln("Longest string:"); 552 //import std.algorithm : uniq; 553 //foreach(str; longestNames[].uniq) writeln(str); 554 555 writefln("## dbiSourceInfo substream: %s bytes", dbiSourceInfoStream.remainingBytes); 556 auto sourceInfoHeader = dbiSourceInfoStream.read!SourceInfoHeader; 557 writefln("Source info %s", dbiSourceInfoStream.remainingBytes); 558 writefln(" num modules %s", sourceInfoHeader.numModules); 559 writefln(" num sources %s", sourceInfoHeader.numSourceFiles); 560 561 ushort[] modIndices = new ushort[sourceInfoHeader.numModules]; 562 dbiSourceInfoStream.readIntoArray(modIndices); 563 writefln(" mod indices %s", modIndices); 564 565 ushort[] modFileCounts = new ushort[sourceInfoHeader.numModules]; 566 dbiSourceInfoStream.readIntoArray(modFileCounts); 567 writefln(" mod file counts %s", modFileCounts); 568 569 uint numSourceFiles; 570 foreach(count; modFileCounts) 571 numSourceFiles += count; 572 uint[] fileNameOffsets = new uint[numSourceFiles]; 573 dbiSourceInfoStream.readIntoArray(fileNameOffsets); 574 writefln(" File name offsets %s", fileNameOffsets); 575 576 { 577 writefln(" File name strings:"); 578 uint numStrings; 579 uint stringsStart = dbiSourceInfoStream.streamCursor; 580 while (!dbiSourceInfoStream.empty) 581 { 582 ++numStrings; 583 uint offset = dbiSourceInfoStream.streamCursor - stringsStart; 584 writefln(" %s %s", offset, dbiSourceInfoStream.readZString); 585 } 586 writefln(" Num name strings: %s", numStrings); 587 } 588 dbiSourceInfoStream.dropPadding(4); 589 enforce(dbiSourceInfoStream.remainingBytes == 0); 590 writeln; 591 592 593 writefln("## dbiTypeServerMap substream: %s bytes", dbiTypeServerMapStream.remainingBytes); 594 // dbiTypeServerMapStream unknown purpose 595 writefln("## dbiECSubstream subtream: %s bytes", dbiECSubstreamStream.remainingBytes); 596 // dbiECSubstreamStream edit and continue in MSVC 597 598 599 writefln("## dbiOptionalDbgHeader subtream: %s bytes", dbiOptionalDbgHeaderStream.remainingBytes); 600 ushort[11] dbgStreamArray; 601 dbgStreamArray[] = ushort.max; // init with unknown stream id if less than 11 recors present 602 uint numDbgStreamIndices = min(dbiOptionalDbgHeaderStream.remainingBytes / 2, 11); // we only understand 11 records, ignore if more present 603 dbiOptionalDbgHeaderStream.readIntoArray(dbgStreamArray[0..numDbgStreamIndices]); 604 pdb.setStreamName(dbgStreamArray[0], "FPO Data"); 605 writefln(" FPO Data: %s", dbgStreamArray[0]); 606 pdb.setStreamName(dbgStreamArray[1], "Exception Data"); 607 writefln(" Exception Data: %s", dbgStreamArray[1]); 608 pdb.setStreamName(dbgStreamArray[2], "Fixup Data"); 609 writefln(" Fixup Data: %s", dbgStreamArray[2]); 610 pdb.setStreamName(dbgStreamArray[3], "Omap To Src Data"); 611 writefln(" Omap To Src Data: %s", dbgStreamArray[3]); 612 pdb.setStreamName(dbgStreamArray[4], "Omap From Src Data"); 613 writefln(" Omap From Src Data: %s", dbgStreamArray[4]); 614 pdb.setStreamName(dbgStreamArray[5], "Section Header Data"); 615 writefln(" Section Header Data: %s", dbgStreamArray[5]); 616 pdb.setStreamName(dbgStreamArray[6], "Token / RID Map"); 617 writefln(" Token / RID Map: %s", dbgStreamArray[6]); 618 pdb.setStreamName(dbgStreamArray[7], "Xdata"); 619 writefln(" Xdata: %s", dbgStreamArray[7]); 620 pdb.setStreamName(dbgStreamArray[8], "Pdata"); 621 writefln(" Pdata: %s", dbgStreamArray[8]); 622 pdb.setStreamName(dbgStreamArray[9], "New FPO Data"); 623 writefln(" New FPO Data: %s", dbgStreamArray[9]); 624 pdb.setStreamName(dbgStreamArray[10], "Original Section Header Data"); 625 writefln(" Original Section Header Data: %s", dbgStreamArray[10]); 626 writeln; 627 628 629 writefln("Stream names:"); 630 pdb.printStreamInfos; 631 writeln; 632 633 634 StreamReader globalStream = streams[pdb.globalStreamIndex]; 635 writefln("# Globals symbol stream %s: %s bytes", pdb.globalStreamIndex, globalStream.remainingBytes); 636 readGSIHashTable(globalStream); 637 enforce(globalStream.remainingBytes == 0); 638 writeln; 639 640 641 // Publics stream 642 StreamReader publicStream = streams[pdb.publicStreamIndex]; 643 writefln("# Publics sym stream %s: %s bytes", pdb.publicStreamIndex, publicStream.remainingBytes); 644 enforce(publicStream.remainingBytes >= PublicsStreamHeader.sizeof, 645 format("Cannot read PublicsStreamHeader from Publics stream, not enough bytes left (%s)", 646 publicStream.remainingBytes)); 647 auto publicsHeader = publicStream.read!PublicsStreamHeader; 648 writefln(" PublicsStreamHeader:"); 649 writefln(" symHash %s", publicsHeader.symHash); 650 writefln(" address map bytes %s", publicsHeader.addrMap); 651 writefln(" numThunks %s", publicsHeader.numThunks); 652 writefln(" sizeOfThunk %s", publicsHeader.sizeOfThunk); 653 writefln(" isectThunkTable %s", publicsHeader.isectThunkTable); 654 writefln(" offThunkTable %s", publicsHeader.offThunkTable); 655 writefln(" numSections %s", publicsHeader.numSections); 656 readGSIHashTable(publicStream); 657 uint[] addressMap = publicStream.readArray!uint(publicsHeader.addrMap / uint.sizeof); 658 writefln(" address map %(0x%X, %)", addressMap); 659 uint[] thunkMap = publicStream.readArray!uint(publicsHeader.numThunks); 660 writefln(" thunk map %(0x%X, %)", thunkMap); 661 SectionOffset[] sectionOffsets = publicStream.readArray!SectionOffset(publicsHeader.numSections); 662 writefln(" section map:"); 663 foreach(sect; sectionOffsets) writefln(" isect 0x%04X offset 0x%08X", sect.isect, sect.offset); 664 enforce(publicStream.remainingBytes == 0); 665 writeln; 666 667 668 StreamReader tpiHashStream = streams[tpiStreamHeader.hashStreamIndex]; 669 writefln("# TPI hash stream %s: %s bytes", tpiStreamHeader.hashStreamIndex, tpiHashStream.remainingBytes); 670 ubyte[] buf2 = tpiHashStream.readArray!ubyte(tpiHashStream.remainingBytes); 671 printHex(buf2, 16, PrintAscii.yes); 672 enforce(tpiHashStream.remainingBytes == 0, "Found bytes past the stream data"); 673 writeln; 674 675 676 StreamReader ipiHashStream = streams[ipiStreamHeader.hashStreamIndex]; 677 writefln("# IPI hash stream %s: %s bytes", ipiStreamHeader.hashStreamIndex, ipiHashStream.remainingBytes); 678 ubyte[] buf3 = ipiHashStream.readArray!ubyte(ipiHashStream.remainingBytes); 679 printHex(buf3, 16, PrintAscii.yes); 680 enforce(ipiHashStream.remainingBytes == 0, "Found bytes past the stream data"); 681 writeln; 682 683 684 StreamReader symRecordStream = streams[pdb.symRecordStream]; 685 writefln("# Symbol record stream %s: %s bytes", pdb.symRecordStream, symRecordStream.remainingBytes); 686 parseSymbols(symRecordStream); 687 enforce(symRecordStream.remainingBytes == 0, "Found bytes past the stream data"); 688 writeln; 689 690 StreamReader namesStream = streams[pdb.namesStreamIndex]; 691 auto stringtableHeader = namesStream.read!StringTableHeader; 692 writefln("# Names stream %s: %s bytes", pdb.namesStreamIndex, namesStream.remainingBytes); 693 writeln (" StringTableHeader:"); 694 writefln(" signature 0x%X", stringtableHeader.signature); 695 writefln(" hashVersion %s", stringtableHeader.hashVersion); 696 writefln(" byteSize %s", stringtableHeader.byteSize); 697 enforce(stringtableHeader.hashVersion == 1 || stringtableHeader.hashVersion == 2, 698 format("Unsupported hash version %s", stringtableHeader.hashVersion)); 699 700 StreamReader stringsSubstream = namesStream.substream(stringtableHeader.byteSize); 701 writeln (" String buffer:"); 702 size_t strOffset; 703 size_t strIndex; 704 while (!stringsSubstream.empty) 705 { 706 string str = stringsSubstream.readZString; 707 writefln(" % 6s %08X %s", strIndex, strOffset, str); 708 strOffset += str.length + 1; 709 ++strIndex; 710 } 711 712 uint numHashSlots = namesStream.read!uint; 713 writefln(" Num hash slots: %s", numHashSlots); 714 715 uint[] hashSlots = namesStream.readArray!uint(numHashSlots); 716 writefln(" Slots: %s", hashSlots); 717 718 uint numNames = namesStream.read!uint; 719 writefln(" Num names: %s", numNames); 720 721 enforce(namesStream.remainingBytes == 0); 722 writeln; 723 } 724 725 void readGSIHashTable(ref StreamReader stream) 726 { 727 auto gsiHashHeader = stream.read!GSIHashHeader; 728 enforce(gsiHashHeader.verSignature == GSIHashHeader.init.verSignature, "GSIHashHeader.verSignature is not valid"); 729 enforce(gsiHashHeader.verHdr == GSIHashHeader.init.verHdr, "GSIHashHeader.verHdr is not valid"); 730 writefln(" hrSize %s numBuckets %s bytes %s", gsiHashHeader.hrSize, gsiHashHeader.numBuckets, stream.remainingBytes); 731 enforce(gsiHashHeader.hrSize + gsiHashHeader.numBuckets <= stream.remainingBytes, 732 format("Not enough bytes left in the stream to read GSI hash map (needed %s + %s, while got %s)", 733 gsiHashHeader.hrSize, gsiHashHeader.numBuckets, stream.remainingBytes)); 734 735 // read hash records 736 enforce(gsiHashHeader.hrSize % PSHashRecord.sizeof == 0, format("GSIHashHeader.hrSize is not multiple of %s", PSHashRecord.sizeof)); 737 uint numHRRecords = gsiHashHeader.hrSize / PSHashRecord.sizeof; 738 PSHashRecord[] hashRecords = stream.readArray!PSHashRecord(numHRRecords); 739 writefln(" Hash records (%s items)", numHRRecords); 740 foreach(record; hashRecords) { 741 writefln(" offset: 0x%X, cref: 0x%X", record.offset, record.cref); 742 } 743 744 // read hash buckets 745 // bitmap is followed by buckets 746 // calculate bitmap size (it depends on verSignature and verHdr, but we only support the modern version) 747 enum IPHR_HASH = 4096; 748 // extra 1 slot (1 bit in bitmap) is reserved for technical reasons, and with 4 byte alignment gives extra 32 bits in addition to IPHR_HASH = 4096 749 uint bitmapBits = alignValue(IPHR_HASH + 1, 32); 750 uint bitmapBytes = bitmapBits / 8; 751 uint bitmapUints = bitmapBytes / uint.sizeof; 752 uint[] hashBitmap = stream.readArray!uint(bitmapUints); 753 enforce(stream.remainingBytes % uint.sizeof == 0, format("Space after GSI hash bitmap is not multiple of 4 (%s)", stream.remainingBytes)); 754 writefln(" Buckets: %s", stream.remainingBytes / uint.sizeof); 755 // iterate all set bits in bitmap 756 // there are 1 bucket per set bit after bitmap 757 foreach(size_t bitIndex; hashBitmap.bitsSet) 758 { 759 uint bucket = stream.read!uint; 760 // max 4096 buckets 761 writefln(" % 4s 0x%08X", bitIndex, bucket); 762 } 763 } 764 765 void parseSymbols(ref StreamReader stream) 766 { 767 char[] indentation; 768 int indentLevel = 0; 769 void ind(char[] i = indentation) { // prints indentation 770 write(i); 771 } 772 void indPush() { 773 ++indentLevel; 774 indentation ~= "| "; 775 } 776 void indPop() { 777 --indentLevel; 778 indentation = indentation[0..$-2]; 779 } 780 writefln("--- SYMBOLS %s bytes ---", stream.remainingBytes); 781 while(stream.remainingBytes) 782 { 783 auto start = stream.streamCursor; 784 auto len = stream.read!ushort; 785 auto end = start + len + 2; 786 SymbolKind kind = stream.read!SymbolKind; 787 788 switch(kind) 789 { 790 case SymbolKind.S_UDT: 791 auto udtsym = stream.read!UdtSym; 792 string name = stream.readZString; 793 visitString(name); 794 ind; writefln("(%06X) %s: Type %s, %s", start, kind, udtsym.type, name); 795 break; 796 797 case SymbolKind.S_PUB32: 798 auto pubsym = stream.read!PublicSym32; 799 string name = stream.readZString; 800 visitString(name); 801 ind; writefln("(%06X) %s: [%04X:%08X] Flags %04b, %s", start, kind, pubsym.segment, pubsym.offset, pubsym.flags, name); 802 break; 803 804 // Those start a new level of indentation 805 // then follow arguments terminated with S_ENDARG 806 // then other data terminated with S_END 807 case SymbolKind.S_GPROC32: 808 case SymbolKind.S_LPROC32: 809 case SymbolKind.S_GPROC32_ID: 810 case SymbolKind.S_LPROC32_ID: 811 case SymbolKind.S_LPROC32_DPC: 812 case SymbolKind.S_LPROC32_DPC_ID: 813 auto procsym = stream.read!ProcSym; 814 string name = stream.readZString; 815 visitString(name); 816 ind; writef("(%06X) %s: [%04X:%08X] Flags:", start, kind, procsym.segment, procsym.offset); 817 printProcSymFlags(procsym.flags); 818 writefln(", %s", name); 819 ind; writefln("| Parent %06X End %06X Next %06X", procsym.parent, procsym.end, procsym.next); 820 ind; writefln("| Length %s, Dbg Start %08X, Dbg End %08X, Type %s", 821 procsym.length, procsym.dbgStart, procsym.dbgEnd, procsym.typeIndex); 822 indPush; 823 ind; writeln; 824 break; 825 826 case SymbolKind.S_ENDARG: 827 ind(indentation[0..$-2]); writefln("+-(%06X) %s", start, kind); 828 break; 829 830 case SymbolKind.S_REGREL32: 831 // (0000C8) S_REGREL32: rsp+00000008, Type: T_64PVOID(0603), hInstance 832 auto regRelative = stream.read!RegRelativeSym; 833 string name = stream.readZString; 834 visitString(name); 835 ind; writefln("(%06X) %s: %s+%08X, Type %s, %s", start, kind, regRelative.register, regRelative.offset, regRelative.type, name); 836 break; 837 838 case SymbolKind.S_LDATA32: 839 case SymbolKind.S_GDATA32: 840 case SymbolKind.S_LMANDATA: 841 case SymbolKind.S_GMANDATA: 842 // S_GDATA32: [0002:000236E8], Type: 0x1A60, RTInfoImpl 843 auto dataSym = stream.read!DataSym; 844 string name = stream.readZString; 845 visitString(name); 846 ind; writefln("(%06X) %s: [%04X:%08X] Type %s, %s", start, kind, dataSym.segment, dataSym.dataOffset, dataSym.type, name); 847 break; 848 849 case SymbolKind.S_LTHREAD32: 850 case SymbolKind.S_GTHREAD32: 851 // (1FCD30) S_GTHREAD32: [0006:00000270], Type: 0x168D, binaryCondStrings 852 auto threadData = stream.read!ThreadDataSym; 853 string name = stream.readZString; 854 visitString(name); 855 ind; writefln("(%06X) %s: [%04X:%08X] Type %s, %s", start, kind, threadData.segment, threadData.dataOffset, threadData.type, name); 856 break; 857 858 case SymbolKind.S_BUILDINFO: 859 auto buildInfo = stream.read!BuildInfoSym; 860 ind; writefln("(%06X) %s: %s", start, kind, buildInfo.buildId); 861 break; 862 863 case SymbolKind.S_INLINESITE: 864 // (0002D8) S_INLINESITE: Parent: 000001E8, End: 00000354, Inlinee: 0x259F 865 // BinaryAnnotations: CodeLengthAndCodeOffset 29 20 866 // BinaryAnnotation Length: 4 bytes (1 bytes padding) 867 auto inlineSite = stream.read!InlineSiteSym; 868 ind; writefln("(%06X) %s: Parent %06X, End %06X, Inlinee %s", start, kind, inlineSite.parent, inlineSite.end, inlineSite.inlinee); 869 870 // binary annotations stream 871 StreamReader binAnnot = stream.substreamUntil(end); 872 uint annotationsBytes = binAnnot.remainingBytes; 873 ind; writefln("| BinaryAnnotations: %s bytes", annotationsBytes); 874 875 while (!binAnnot.empty) 876 { 877 auto instr = cast(BinaryAnnotationsOpcode)cvReadCompressedUint(binAnnot); 878 enforce(instr != uint.max, "Invalid value inside binary annotations"); 879 if (instr == BinaryAnnotationsOpcode.invalid) { 880 // only happens when first byte of an instruction is 0, which is a padding at the end of data 881 binAnnot.unread(1); 882 break; 883 } 884 885 ind; write("| "); 886 887 switch (instr) with(BinaryAnnotationsOpcode) 888 { 889 case codeOffset: 890 uint arg = cvReadCompressedUint(binAnnot); 891 writefln("code offset: %s", arg); 892 break; 893 case changeCodeOffsetBase: 894 uint arg = cvReadCompressedUint(binAnnot); 895 writefln("segment number: %s", arg); 896 break; 897 case changeCodeOffset: 898 uint arg = cvReadCompressedUint(binAnnot); 899 writefln("change code offset: %s (delta)", arg); 900 break; 901 case changeCodeLength: 902 uint arg = cvReadCompressedUint(binAnnot); 903 writefln("change code length: %s", arg); 904 break; 905 case changeFile: 906 uint arg = cvReadCompressedUint(binAnnot); 907 writefln("change file: fileId %s", arg); 908 break; 909 case changeLineOffset: 910 uint arg = cvDecodeSignedInt32(cvReadCompressedUint(binAnnot)); 911 writefln("change line offset: %s (signed)", arg); 912 break; 913 case changeLineEndDelta: 914 uint arg = cvReadCompressedUint(binAnnot); 915 writefln("change line end: %s (delta)", arg); 916 break; 917 case changeRangeKind: 918 uint arg = cvReadCompressedUint(binAnnot); 919 writef("change range kind to %s", arg); 920 switch(arg) { 921 case 0: writeln(" (expression)"); break; 922 case 1: writeln(" (statement)"); break; 923 default: writeln; break; 924 } 925 break; 926 case changeColumnStart: 927 uint arg = cvReadCompressedUint(binAnnot); 928 writef("change column start: %s", arg); 929 if (arg == 0) writeln(" (0 means no column info)"); 930 else writeln; 931 break; 932 case changeColumnEndDelta: 933 uint arg = cvDecodeSignedInt32(cvReadCompressedUint(binAnnot)); 934 writefln("end column number delta: %s (signed)", arg); 935 break; 936 case changeCodeOffsetAndLineOffset: 937 uint arg = cvReadCompressedUint(binAnnot); 938 writefln("end column number delta: %s (signed)", arg & 0b1111); 939 ind; writefln("| and change code offset: %s (delta)", arg >> 4); 940 break; 941 case changeCodeLengthAndCodeOffset: 942 uint arg = cvReadCompressedUint(binAnnot); 943 writefln("change code length: %s", arg); 944 uint arg2 = cvReadCompressedUint(binAnnot); 945 ind; writefln("| and change code offset: %s (delta)", arg2); 946 break; 947 case changeColumnEnd: 948 uint arg = cvReadCompressedUint(binAnnot); 949 writef("end column number: %s", arg); 950 break; 951 default: 952 writefln("??? 0x%04X", cast(uint)instr); break; 953 } 954 } 955 ind; writefln("| BinaryAnnotations padding: %s bytes", binAnnot.remainingBytes); 956 957 indPush; 958 break; 959 960 case SymbolKind.S_CONSTANT: 961 case SymbolKind.S_MANCONSTANT: // was not observed 962 auto con = stream.read!ConstSym; 963 ind; writef("(%06X) %s: Type %s, Value: ", start, kind, con.type); 964 void printNum() 965 { 966 if (con.value < CV_TYPE.LF_NUMERIC) 967 { 968 // `con.value` is a value 969 writef("%s", con.value); 970 return; 971 } 972 973 // `con.value` is a type of value 974 switch(cast(CV_TYPE)con.value) 975 { 976 case CV_TYPE.LF_CHAR: writef("(LF_CHAR) 0x%02X", stream.read!ubyte); break; 977 case CV_TYPE.LF_SHORT: writef("(LF_SHORT) 0x%04X", stream.read!short); break; 978 case CV_TYPE.LF_USHORT: writef("(LF_USHORT) 0x%04X", stream.read!ushort); break; 979 case CV_TYPE.LF_LONG: writef("(LF_LONG) 0x%08X", stream.read!int); break; 980 case CV_TYPE.LF_ULONG: writef("(LF_ULONG) 0x%08X", stream.read!uint); break; 981 case CV_TYPE.LF_QUADWORD: writef("(LF_QUADWORD) 0x%016X", stream.read!long); break; 982 case CV_TYPE.LF_UQUADWORD: writef("(LF_UQUADWORD) 0x%016X", stream.read!ulong); break; 983 case CV_TYPE.LF_OCTWORD: 984 writef("(LF_OCTWORD) [%(%02X %)]", stream.read!(ubyte[16])); 985 break; 986 case CV_TYPE.LF_UOCTWORD: 987 writef("(LF_UOCTWORD) [%(%02X %)]", stream.read!(ubyte[16])); 988 break; 989 case CV_TYPE.LF_REAL16: 990 writef("(LF_REAL16) 0x04X", stream.read!ushort); 991 break; 992 case CV_TYPE.LF_REAL32: 993 union U { 994 ubyte[4] bytes; 995 float f; 996 } 997 U u; 998 u.bytes = stream.read!(ubyte[4]); 999 writef("(LF_REAL32) [%(%02X %)] %s", u.bytes, u.f); 1000 break; 1001 case CV_TYPE.LF_REAL64: 1002 union U2 { 1003 ubyte[8] bytes; 1004 double f; 1005 } 1006 U2 u; 1007 u.bytes = stream.read!(ubyte[8]); 1008 writef("(LF_REAL64) [%(%02X %)] %s", u.bytes, u.f); 1009 break; 1010 case CV_TYPE.LF_REAL80: 1011 writef("(LF_REAL80) [%(%02X %)]", stream.read!(ubyte[10])); 1012 break; 1013 case CV_TYPE.LF_REAL128: 1014 writef("(LF_REAL128) [%(%02X %)]", stream.read!(ubyte[16])); 1015 break; 1016 case CV_TYPE.LF_REAL48: 1017 writef("(LF_REAL48) [%(%02X %)]", stream.read!(ubyte[6])); 1018 break; 1019 case CV_TYPE.LF_COMPLEX32: 1020 writef("(LF_COMPLEX32) [%(%02X %)]", stream.read!(ubyte[8])); 1021 break; 1022 case CV_TYPE.LF_COMPLEX64: 1023 writef("(LF_COMPLEX64) [%(%02X %)]", stream.read!(ubyte[16])); 1024 break; 1025 case CV_TYPE.LF_COMPLEX80: 1026 writef("(LF_COMPLEX80) [%(%02X %)]", stream.read!(ubyte[20])); 1027 break; 1028 case CV_TYPE.LF_COMPLEX128: 1029 writef("(LF_COMPLEX128) [%(%02X %)]", stream.read!(ubyte[32])); 1030 break; 1031 case CV_TYPE.LF_VARSTRING: 1032 ushort len = stream.read!ushort; 1033 writef("(LF_VARSTRING) %s", stream.readArray!char(len)); 1034 break; 1035 case CV_TYPE.LF_DATE: 1036 writef("(LF_DATE) 0x%016X", stream.read!ulong); // DATE is alias of double 1037 break; 1038 case CV_TYPE.LF_DECIMAL: 1039 writef("(LF_DECIMAL) [%(%02X %)]", stream.read!(ubyte[12])); // DECIMAL is 12 bytes 1040 break; 1041 case CV_TYPE.LF_UTF8STRING: 1042 writef("(LF_UTF8STRING) %s", stream.readZString); 1043 break; 1044 default: 1045 write("Invalid Numeric Leaf"); 1046 break; 1047 } 1048 } 1049 printNum; 1050 writefln(", %s", stream.readZString); 1051 break; 1052 1053 case SymbolKind.S_LOCAL: 1054 auto localsym = stream.read!LocalSym; 1055 string name = stream.readZString; 1056 visitString(name); 1057 ind; writef("(%06X) %s: Type %s, Flags:", start, kind, localsym.typeIndex); 1058 printLocalSymFlags(localsym.flags); 1059 writefln(", %s", name); 1060 break; 1061 1062 case SymbolKind.S_FRAMEPROC: 1063 // (0000A0) S_FRAMEPROC: 1064 // Frame size = 0x00000028 bytes 1065 // Pad size = 0x00000000 bytes 1066 // Offset of pad in frame = 0x00000000 1067 // Size of callee save registers = 0x00000000 1068 // Address of exception handler = 0000:00000000 1069 // Function info: invalid_pgo_counts opt_for_speed Local=rsp Param=rsp (0x00114000) 1070 auto frameProc = stream.read!FrameProcSym; 1071 ind; writefln("(%06X) %s:", start, kind); 1072 ind; writefln(" Frame size = 0x%08X bytes", frameProc.totalFrameBytes); 1073 ind; writefln(" Pad size = 0x%08X bytes", frameProc.paddingFrameBytes); 1074 ind; writefln(" Offset of pad in frame = 0x%08X", frameProc.offsetToPadding); 1075 ind; writefln(" Size of callee save registers = 0x%08X", frameProc.bytesOfCalleeSavedRegisters); 1076 ind; writefln(" Address of exception handler = %04X:%08X", frameProc.sectionIdOfExceptionHandler, frameProc.offsetOfExceptionHandler); 1077 ind; write(" Function info:"); 1078 if (frameProc.hasAlloca) write(" alloca"); 1079 if (frameProc.hasSetJmp) write(" setjmp"); 1080 if (frameProc.hasLongJmp) write(" longjmp"); 1081 if (frameProc.hasInlineAssembly) write(" inlasm"); 1082 if (frameProc.hasExceptionHandling) write(" EH"); 1083 if (frameProc.markedInline) write(" marked_inline"); 1084 if (frameProc.hasStructuredExceptionHandling) write(" SEH"); 1085 if (frameProc.naked) write(" naked"); 1086 if (frameProc.securityChecks) write(" gschecks"); 1087 if (frameProc.asynchronousExceptionHandling) write(" asyncEH"); 1088 if (frameProc.noStackOrderingForSecurityChecks) write(" gs_no_stack_ordering"); 1089 if (frameProc.inlined) write(" wasinlined"); 1090 if (frameProc.strictSecurityChecks) write(" strict_gs_check"); 1091 if (frameProc.safeBuffers) write(" safe_buffers"); 1092 if (frameProc.encodedLocalBasePointer) write(" Local=%s", frameProc.encodedLocalBasePointer); 1093 if (frameProc.encodedParamBasePointer) write(" Param=%s", frameProc.encodedParamBasePointer); 1094 if (frameProc.profileGuidedOptimization) write(" pgo_on"); 1095 if (frameProc.validProfileCounts) write(" valid_pgo_counts"); else write(" invalid_pgo_counts"); 1096 if (frameProc.optimizedForSpeed) write(" opt_for_speed"); 1097 if (frameProc.guardCfg) write(" guard_cfg"); 1098 if (frameProc.guardCfw) write(" guard_cfw"); 1099 writeln; 1100 break; 1101 1102 case SymbolKind.S_TRAMPOLINE: 1103 // (000184) S_TRAMPOLINE: subtype Incremental, code size = 5 bytes 1104 // Thunk address: [0001:00000005] 1105 // Thunk target: [0001:00000010] 1106 auto trampSym = stream.read!TrampolineSym; 1107 ind; writefln("(%06X) %s: Subtype %s, Code size %s bytes", start, kind, trampSym.type, trampSym.size); 1108 ind; writefln(" Thunk address [%04X:%08X]", trampSym.thunkSection, trampSym.thunkOffset); 1109 ind; writefln(" Thunk target [%04X:%08X]", trampSym.targetSection, trampSym.targetOffset); 1110 ind; writeln; 1111 break; 1112 1113 case SymbolKind.S_THUNK32: 1114 // (000048) S_THUNK32: [0001:004B4984], Cb: 00000006, CommandLineToArgvW 1115 // Parent: 00000000, End: 00000074, Next: 00000000 1116 auto thunk = stream.read!ThunkSym; 1117 string name = stream.readZString; 1118 visitString(name); 1119 //ubyte[] variantData = stream.readArrayBefore!ubyte(end); 1120 ind; writefln("(%06X) %s: [%04X:%08X] Length %08X, Type %s, %s", start, kind, thunk.segment, thunk.offset, thunk.length, thunk.type, name); 1121 ind; writefln("| Parent %06X, End %06X, Next %06X", thunk.parent, thunk.end, thunk.next); 1122 //ind; writefln(" variant data: %(%02x %)", variantData); 1123 indPush; 1124 break; 1125 1126 case SymbolKind.S_COMPILE: 1127 auto compilesym = stream.read!CompileSym; 1128 string verstring = stream.readZString; 1129 ind; writefln("(%06X) %s:", start, kind); 1130 ind; writefln(" Language: %s", compilesym.sourceLanguage); 1131 ind; writefln(" Target processor: %s", cast(CV_CPUType)compilesym.machine); 1132 ind; writefln(" Floating-point precision: %s", compilesym.floatprec); 1133 static immutable string[4] floatPackageStrings = ["hardware", "emulator", "altmath", "???"]; 1134 ind; writefln(" Floating-point package: %s", floatPackageStrings[compilesym.floatpkg]); 1135 static immutable string[4] modelStrings = ["near", "far", "huge", "???"]; 1136 ind; writefln(" Ambient data: %s", modelStrings[compilesym.ambdata]); 1137 ind; writefln(" Ambient code: %s", modelStrings[compilesym.ambcode]); 1138 ind; writefln(" PCode present: %s", compilesym.pcode); 1139 ind; writefln(" mode32: %s", compilesym.mode32); 1140 if (compilesym.pad) { 1141 ind; writefln(" pad: 0x%03X", compilesym.pad); 1142 } 1143 ind; writefln(" Compiler Version: %s", verstring); 1144 ind; writeln; 1145 break; 1146 1147 case SymbolKind.S_COMPILE2: 1148 auto compilesym = stream.read!CompileSym2; 1149 string verstring = stream.readZString; 1150 ind; writefln("(%06X) %s:", start, kind); 1151 ind; writefln(" Language: %s", compilesym.sourceLanguage); 1152 ind; writefln(" Target processor: %s", compilesym.machine); 1153 ind; writefln(" Compiled for edit and continue: %s", compilesym.EC); 1154 ind; writefln(" Compiled without debugging info: %s", compilesym.NoDbgInfo); 1155 ind; writefln(" Compiled with LTCG: %s", compilesym.LTCG); 1156 ind; writefln(" Compiled with /bzalign: %s", compilesym.NoDataAlign); 1157 ind; writefln(" Managed code present: %s", compilesym.ManagedPresent); 1158 ind; writefln(" Compiled with /GS: %s", compilesym.SecurityChecks); 1159 ind; writefln(" Compiled with /hotpatch: %s", compilesym.HotPatch); 1160 ind; writefln(" Converted by CVTCIL: %s", compilesym.CVTCIL); 1161 ind; writefln(" MSIL module: %s", compilesym.MSILModule); 1162 ind; writefln(" Pad bits = 0x%04X", compilesym.padding); 1163 ind; writefln(" Frontend Version: Major = %s, Minor = %s, Build = %s", 1164 compilesym.verFEMajor, compilesym.verFEMinor, compilesym.verFEBuild); 1165 ind; writefln(" Backend Version: Major = %s, Minor = %s, Build = %s", 1166 compilesym.verMajor, compilesym.verMinor, compilesym.verBuild); 1167 ind; writefln(" Version string: %s", verstring); 1168 ind; writefln(" Command block: %s", verstring); 1169 while(!stream.empty) 1170 { 1171 string cmdName = stream.readZString; 1172 if (cmdName is null) break; // terminated by empty string 1173 1174 string cmd = stream.readZString; 1175 ind; writefln(" %s = '%s'", cmdName, cmd); 1176 } 1177 ind; writeln; 1178 break; 1179 1180 case SymbolKind.S_COMPILE3: 1181 auto compilesym = stream.read!CompileSym3; 1182 string verstring = stream.readZString; 1183 ind; writefln("(%06X) %s:", start, kind); 1184 ind; writefln(" Language: %s", compilesym.sourceLanguage); 1185 ind; writefln(" Target processor: %s", compilesym.machine); 1186 ind; writefln(" Compiled for edit and continue: %s", compilesym.EC); 1187 ind; writefln(" Compiled without debugging info: %s", compilesym.NoDbgInfo); 1188 ind; writefln(" Compiled with LTCG: %s", compilesym.LTCG); 1189 ind; writefln(" Compiled with /bzalign: %s", compilesym.NoDataAlign); 1190 ind; writefln(" Managed code present: %s", compilesym.ManagedPresent); 1191 ind; writefln(" Compiled with /GS: %s", compilesym.SecurityChecks); 1192 ind; writefln(" Compiled with /hotpatch: %s", compilesym.HotPatch); 1193 ind; writefln(" Converted by CVTCIL: %s", compilesym.CVTCIL); 1194 ind; writefln(" MSIL module: %s", compilesym.MSILModule); 1195 ind; writefln(" Compiled with /sdl: %s", compilesym.Sdl); 1196 ind; writefln(" Compiled with pgo: %s", compilesym.PGO); 1197 ind; writefln(" .EXP module: %s", compilesym.Exp); 1198 ind; writefln(" Pad bits = 0x%04X", compilesym.padding); 1199 ind; writefln(" Frontend Version: Major = %s, Minor = %s, Build = %s, QFE = %s", 1200 compilesym.verFEMajor, compilesym.verFEMinor, compilesym.verFEBuild, compilesym.verFEQFE); 1201 ind; writefln(" Backend Version: Major = %s, Minor = %s, Build = %s, QFE = %s", 1202 compilesym.verMajor, compilesym.verMinor, compilesym.verBuild, compilesym.verQFE); 1203 ind; writefln(" Version string: %s", verstring); 1204 ind; writeln; 1205 break; 1206 1207 case SymbolKind.S_LABEL32: 1208 auto labelSym = stream.read!LabelSym; 1209 string name = stream.readZString; 1210 visitString(name); 1211 ind; writef("(%06X) %s: [%04X:%08X] Flags:", 1212 start, kind, labelSym.segment, labelSym.offset); 1213 printProcSymFlags(labelSym.flags); 1214 writefln(", %s", name); 1215 break; 1216 1217 case SymbolKind.S_BLOCK32: 1218 // (000930) S_BLOCK32: [0001:0005AE21], Cb: 00000027, 1219 // Parent: 00000118, End: 00000964 1220 auto blockSym = stream.read!BlockSym; 1221 string name = stream.readZString; 1222 visitString(name); 1223 ind; writefln("(%06X) %s: [%04X:%08X] Code size %08X bytes, %s", 1224 start, kind, blockSym.codeSegment, blockSym.codeOffset, blockSym.codeSize, name); 1225 ind; writefln("| Parent %06X, End %06X", blockSym.parent, blockSym.end); 1226 assert(len == 22); 1227 indPush; 1228 break; 1229 1230 case SymbolKind.S_OBJNAME: 1231 // (000004) S_OBJNAME: Signature: 00000000, * Linker * 1232 auto objname = stream.read!ObjNameSym; 1233 string name = stream.readZString; 1234 visitString(name); 1235 ind; writefln("(%06X) %s: Signature %08X, %s", start, kind, objname.signature, name); 1236 ind; writeln; 1237 break; 1238 1239 case SymbolKind.S_ENVBLOCK: 1240 auto env = stream.read!EnvBlockSym; 1241 ind; writefln("(%06X) %s:", start, kind); 1242 ind; writefln(" Compiled for edit and continue: %s", env.EC); 1243 ind; writefln(" Command block:"); 1244 1245 // at least 4 bytes are needed for 2 non-empty strings 1246 while(!stream.empty) 1247 { 1248 string cmdName = stream.readZString; 1249 if (cmdName is null) break; // terminated by empty string 1250 1251 string cmd = stream.readZString; 1252 ind; writefln(" %s = '%s'", cmdName, cmd); 1253 } 1254 ind; writeln; 1255 break; 1256 1257 case SymbolKind.S_SECTION: 1258 // (000198) S_SECTION: [0001], RVA = 00001000, Cb = 00001030, Align = 00001000, Characteristics = 60000020, .text 1259 auto sectionsym = stream.read!SectionSym; 1260 string name = stream.readZString; 1261 visitString(name); 1262 ind; writefln("(%06X) %s: [%04X] RVA %08X, %08X bytes, Align %08X, Char %08X, %s", start, kind, 1263 sectionsym.section, sectionsym.rva, sectionsym.length, 1264 1 << sectionsym.alignmentPower, sectionsym.characteristics, name); 1265 break; 1266 1267 case SymbolKind.S_COFFGROUP: 1268 // (0001B4) S_COFFGROUP: [0001:00000000], Cb: 00001030, Characteristics = 60000020, .text$mn 1269 auto coffGroupSym = stream.read!CoffGroupSym; 1270 string name = stream.readZString; 1271 visitString(name); 1272 ind; writefln("(%06X) %s: [%04X:%08X] %08X bytes, Char %08X, %s", start, kind, 1273 coffGroupSym.symbolSegment, coffGroupSym.symbolOffset, 1274 coffGroupSym.length, coffGroupSym.characteristics, name); 1275 break; 1276 1277 case SymbolKind.S_EXPORT: 1278 auto exportSym = stream.read!ExportSym; 1279 string name = stream.readZString; 1280 visitString(name); 1281 ind; writef("(%06X) %s: Ordinal %s, Flags:", start, kind, exportSym.ordinal); 1282 if (exportSym.isConstant) write(" isConstant"); 1283 if (exportSym.isData) write(" isData"); 1284 if (exportSym.isPrivate) write(" isPrivate"); 1285 if (exportSym.hasNoName) write(" hasNoName"); 1286 if (exportSym.hasExplicitOrdinal) write(" hasExplicitOrdinal"); 1287 if (exportSym.isForwarder) write(" isForwarder"); 1288 if (exportSym.pad) writef(" pad = 0x%X", exportSym.pad); 1289 writefln(", %s", name); 1290 break; 1291 1292 case SymbolKind.S_DEFRANGE_REGISTER: 1293 // (000230) S_DEFRANGE_REGISTER: rcx 1294 // Range: [0001:004A706C] - [0001:004A708C], 0 Gaps 1295 auto reg = stream.read!DefRangeRegisterSym; 1296 auto gaps = stream.readArrayBefore!LocalVariableAddrGap(end); 1297 ind; writefln("(%06X) %s: %s", start, kind, reg.register); 1298 ind; writef(" Range %s, %s Gaps", reg.range, gaps.length); 1299 if (gaps.length) write(" (Start offset, Length):"); 1300 foreach(gap; gaps) writef(" (%04X, %02X)", gap.startOffset, gap.length); 1301 writeln; 1302 break; 1303 1304 case SymbolKind.S_DEFRANGE_SUBFIELD_REGISTER: 1305 auto reg = stream.read!DefRangeSubfieldRegisterSym; 1306 auto gaps = stream.readArrayBefore!LocalVariableAddrGap(end); 1307 ind; writefln("(%06X) %s: offset at %04X: %s", start, kind, reg.offsetInParent, reg.register); 1308 ind; writef(" Range %s, %s Gaps", reg.range, gaps.length); 1309 if (gaps.length) write(" (Start offset, Length):"); 1310 foreach(gap; gaps) writef(" (%04X, %02X)", gap.startOffset, gap.length); 1311 writeln; 1312 break; 1313 1314 case SymbolKind.S_DEFRANGE_REGISTER_REL: 1315 auto reg = stream.read!DefRangeRegisterRelSym; 1316 ind; writefln("(%06X) %s: %s+%08X, UDT %s, Offset in parent %s", start, kind, 1317 reg.baseReg, reg.basePointerOffset, reg.spilledUdtMember, reg.offsetInParent); 1318 ind; writefln(" Range: %s", reg.range); 1319 break; 1320 1321 case SymbolKind.S_DEFRANGE_FRAMEPOINTER_REL: 1322 auto reg = stream.read!DefRangeFramePointerRelSym; 1323 auto gaps = stream.readArrayBefore!LocalVariableAddrGap(end); 1324 ind; writefln("(%06X) %s: FrameOffset: %04X ", start, kind, reg.offFramePointer); 1325 ind; writef(" Range: %s, %s Gaps", reg.range, gaps.length); 1326 if (gaps.length) write(" (Start offset, Length):"); 1327 foreach(gap; gaps) writef(" (%04X, %02X)", gap.startOffset, gap.length); 1328 writeln; 1329 break; 1330 1331 case SymbolKind.S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: 1332 auto reg = stream.read!DefRangeFramePointerRelFullScopeSym; 1333 ind; writefln("(%06X) %s: FrameOffset: %04X", start, kind, reg.offFramePointer); 1334 break; 1335 1336 case SymbolKind.S_PROCREF: 1337 case SymbolKind.S_LPROCREF: 1338 auto procref = stream.read!ProcRefSym; 1339 string name = stream.readZString; 1340 visitString(name); 1341 ind; writefln("(%06X) %s: [%04X:%08X] Sum name %s, %s", start, kind, procref.mod, 1342 procref.symOffset, procref.sumName, name); 1343 break; 1344 1345 case SymbolKind.S_CALLERS: 1346 case SymbolKind.S_CALLEES: 1347 case SymbolKind.S_INLINEES: 1348 auto funclist = stream.read!FunctionListSym; 1349 TypeIndex[] funcs = stream.readArray!TypeIndex(funclist.numFuncs); 1350 uint[] numInvocationsPerFunc = stream.readArrayBefore!uint(end); 1351 ind; writefln("(%06X) %s: count %s", start, kind, funclist.numFuncs); 1352 foreach(i, func; funcs) { 1353 uint numInvocations = 0; 1354 if (i < numInvocationsPerFunc.length) 1355 numInvocations = numInvocationsPerFunc[i]; 1356 ind; writefln(" %s (%s)", func, numInvocations); 1357 } 1358 break; 1359 1360 case SymbolKind.S_FRAMECOOKIE: 1361 auto cookie = stream.read!FrameCookieSym; 1362 ind; writefln("(%06X) %s: %s+%08X, Type %s, Flags 0x%02X", start, kind, 1363 cookie.reg, cookie.offset, cookie.type, cookie.flags); 1364 break; 1365 1366 case SymbolKind.S_HEAPALLOCSITE: 1367 auto heapAlloc = stream.read!HeapAllocationSiteSym; 1368 ind; writefln("(%06X) %s: [%04X:%08X] Instr length %08X bytes, Type %s", start, kind, 1369 heapAlloc.section, heapAlloc.offset, heapAlloc.instrBytes, heapAlloc.type); 1370 break; 1371 1372 case SymbolKind.S_CALLSITEINFO: 1373 auto callSite = stream.read!CallSiteInfoSym; 1374 ind; writefln("(%06X) %s: [%04X:%08X] Type %s", start, kind, 1375 callSite.section, callSite.offset, callSite.type); 1376 break; 1377 1378 case SymbolKind.S_UNAMESPACE: 1379 string name = stream.readZString; 1380 visitString(name); 1381 ind; writefln("(%06X) %s: %s", start, kind, name); 1382 break; 1383 1384 // terminates S_*PROC*, S_THUNK32, S_BLOCK32 1385 case SymbolKind.S_END: 1386 // terminates S_INLINESITE 1387 case SymbolKind.S_INLINESITE_END: 1388 enforce(indentLevel > 0, format("%s found when depth is 0", kind)); 1389 indPop; 1390 ind; writefln("`-(%06X) %s", start, kind); 1391 if (indentLevel == 0) { 1392 ind; writeln; 1393 } 1394 break; 1395 1396 case SymbolKind.S_FILESTATIC: 1397 auto fileStatic = stream.read!FileStaticSym; 1398 string name = stream.readZString; 1399 ind; writef("(%06X) %s: Mod name offset %08X, Type %s, Flags:", start, kind, 1400 fileStatic.modOffset, fileStatic.type); 1401 printLocalSymFlags(fileStatic.flags); 1402 writefln(", %s", name); 1403 break; 1404 1405 default: 1406 string stringBuf2 = new char[len - 2]; 1407 stream.readIntoArray(cast(char[])stringBuf2); 1408 ind; writefln("(%06X) %s: UNKNOWN `%s`", start, kind, stringBuf2); 1409 printHex(cast(ubyte[])stringBuf2, 16, PrintAscii.yes, indentation, 9); 1410 } 1411 1412 auto padding = end - stream.streamCursor; 1413 if (padding >= 4) { 1414 ind; writefln(" padding: %s bytes", padding); 1415 } 1416 1417 // skip padding 1418 stream.streamCursor = end; 1419 } 1420 enforce(indentLevel == 0, format("Wrong nesting detected, depth is %s", indentLevel)); 1421 } 1422 } 1423 1424 enum FixedStream 1425 { 1426 old_directory, 1427 pdb_stream, 1428 tpi_stream, 1429 dbi_stream, 1430 ipi_stream, 1431 } 1432 1433 struct StreamReader 1434 { 1435 ubyte[] fileData; 1436 uint[] pages; 1437 uint pageSize; 1438 uint streamBytes; 1439 uint streamCursor = 0; 1440 1441 void readIntoArray(T)(T[] buf) 1442 { 1443 ubyte[] byteBuf = cast(ubyte[])buf; 1444 while(byteBuf.length > 0) 1445 { 1446 size_t pageArrayIndex = streamCursor / pageSize; 1447 enforce(pageArrayIndex < pages.length, "Reading past last page"); 1448 size_t page = pages[pageArrayIndex]; 1449 size_t pageOffset = streamCursor % pageSize; 1450 size_t pageFrom = page * pageSize; 1451 size_t pageTo = pageFrom + pageSize; 1452 pageFrom += pageOffset; 1453 ubyte[] pageData = fileData[pageFrom..pageTo]; 1454 size_t pageBytesToRead = min(pageData.length, byteBuf.length); 1455 //writefln("cur %s page %s pageOffset %s read %s", streamCursor, page, pageOffset, pageBytesToRead); 1456 //printHex(pageData[0..pageBytesToRead], 16, PrintAscii.yes); 1457 enforce(streamCursor + pageBytesToRead <= streamBytes, 1458 format("attempt to read past the end of stream, %s %s %s", 1459 streamCursor, pageBytesToRead, streamBytes)); 1460 byteBuf[0..pageBytesToRead] = pageData[0..pageBytesToRead]; 1461 byteBuf = byteBuf[pageBytesToRead..$]; 1462 streamCursor += pageBytesToRead; 1463 } 1464 } 1465 1466 T[] readArrayBefore(T)(uint cursorAfterArray) 1467 { 1468 uint numBytes = cursorAfterArray - streamCursor; 1469 enforce(numBytes % T.sizeof == 0, 1470 format("readArrayBefore: %s is not multiple of %s.sizeof (%s)", 1471 numBytes, T.stringof, T.sizeof)); 1472 uint numItems = numBytes / T.sizeof; 1473 return readArray!T(numItems); 1474 } 1475 1476 T[] readArray(T)(uint size) 1477 { 1478 auto buf = new T[size]; 1479 readIntoArray(buf); 1480 return buf; 1481 } 1482 1483 T read(T)() 1484 { 1485 ubyte[T.sizeof] buf; 1486 readIntoArray(buf); 1487 return *cast(T*)buf.ptr; 1488 } 1489 1490 void unread(uint numBytes) 1491 { 1492 streamCursor -= numBytes; 1493 } 1494 1495 /// Creates stream reader that contains `byteSize` bytes since current cursor. 1496 /// Advances cursor of current stream. 1497 StreamReader substream(uint byteSize) 1498 { 1499 StreamReader sub = this; 1500 sub.streamBytes = streamCursor + byteSize; 1501 drop(byteSize); 1502 return sub; 1503 } 1504 1505 StreamReader substreamUntil(uint end) 1506 { 1507 StreamReader sub = this; 1508 sub.streamBytes = end; 1509 assert(end >= streamCursor); 1510 assert(end <= streamBytes); 1511 uint byteSize = end - streamCursor; 1512 drop(byteSize); 1513 return sub; 1514 } 1515 1516 void drop(size_t bytesToDrop) 1517 { 1518 enforce(streamCursor + bytesToDrop <= streamBytes, 1519 format("attempt to drop past the end of stream, (stream cursor %s, bytes to drop %s, bytes left %s)", 1520 streamCursor, bytesToDrop, remainingBytes)); 1521 streamCursor += bytesToDrop; 1522 } 1523 1524 uint remainingBytes() { 1525 return streamBytes - streamCursor; 1526 } 1527 1528 bool empty() { 1529 return remainingBytes == 0; 1530 } 1531 1532 void toString(scope void delegate(const(char)[]) sink) 1533 { 1534 sink.formattedWrite("pages %s, size %s, cursor %s", pages, streamBytes, streamCursor); 1535 } 1536 1537 // reads zero-terminated string of unknown length including zero char. Returns string without zero char. 1538 string readZString() 1539 { 1540 uint cursorCopy = streamCursor; 1541 while (true) 1542 { 1543 char[1] buf; 1544 readIntoArray(buf); 1545 char c = buf[0]; 1546 1547 if (c == '\0') 1548 { 1549 uint nameLength = streamCursor - cursorCopy - 1; 1550 streamCursor = cursorCopy; // restore cursor 1551 string str = new char[nameLength]; 1552 readIntoArray(cast(char[])str); 1553 drop(1); // skip 0 1554 return str; 1555 } 1556 } 1557 } 1558 1559 string readNameBefore(uint end) 1560 { 1561 uint nameLength = end - streamCursor; 1562 string name = new char[nameLength]; 1563 readIntoArray(cast(char[])name); 1564 while (true) 1565 { 1566 if (name.length == 0) return null; // handle empty string 1567 if (name[$-1] != '\0') break; // we found non-zero 1568 name = name[0..$-1]; // peel zero terminators 1569 } 1570 return name; 1571 } 1572 1573 string readZNameBefore(uint end) 1574 { 1575 uint nameLength = end - streamCursor; 1576 string name = new char[nameLength]; 1577 readIntoArray(cast(char[])name); 1578 //printHex(cast(ubyte[])name, 16, PrintAscii.yes); 1579 // find zero terminator first 1580 while (true) 1581 { 1582 if (name.length == 0) return null; // handle empty string 1583 if (name[$-1] == '\0') break; // we found non-zero 1584 name = name[0..$-1]; // peel padding 1585 } 1586 // find last char 1587 while (true) 1588 { 1589 if (name.length == 0) return null; // handle empty string 1590 if (name[$-1] != '\0') break; // we found non-zero 1591 name = name[0..$-1]; // peel zero terminators 1592 } 1593 return name; 1594 } 1595 1596 // alignment is PoT 1597 void dropPadding(uint alignment) { 1598 uint padding = paddingSize(streamCursor, alignment); 1599 drop(padding); 1600 } 1601 } 1602 1603 ubyte[32] MsfMagic = [ 1604 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, // Microsof 1605 0x74, 0x20, 0x43, 0x2F, 0x43, 0x2B, 0x2B, 0x20, // t C/C++ 1606 0x4D, 0x53, 0x46, 0x20, 0x37, 0x2E, 0x30, 0x30, // MSF 7.00 1607 0x0D, 0x0A, 0x1A, 0x44, 0x53, 0x00, 0x00, 0x00, // ...DS... 1608 ]; 1609 1610 bool isValidPageSize(uint pageSize) 1611 { 1612 return pageSize == 512 || 1613 pageSize == 1024 || 1614 pageSize == 2048 || 1615 pageSize == 4096; 1616 } 1617 1618 uint[4] validPageSizes = [512, 1024, 2048, 4096]; 1619 1620 struct MsfHeader 1621 { 1622 uint pageSize; 1623 uint freePageBitmapIndex; 1624 /// Total size of MSF file is numPages * pageSize 1625 uint numPages; 1626 /// Length of stream that stores stream infos 1627 uint metaStreamBytes; 1628 uint _reserved; 1629 uint metaStreamHeaderPage; 1630 1631 uint numMetaStreamPages() { 1632 return divCeil(metaStreamBytes, pageSize); 1633 } 1634 1635 void toString(scope void delegate(const(char)[]) sink) 1636 { 1637 sink.formattedWrite("MsfHeader:"); 1638 sink.formattedWrite(" page size 0x%X\n", pageSize); 1639 sink.formattedWrite(" number of pages %s\n", numPages); 1640 sink.formattedWrite(" free page bitmap index %s\n", freePageBitmapIndex); 1641 sink.formattedWrite(" page map index %s\n", metaStreamHeaderPage); 1642 sink.formattedWrite(" meta-stream bytes %s\n", metaStreamBytes); 1643 } 1644 1645 void validate() 1646 { 1647 enforce(isValidPageSize(pageSize), 1648 format("Invalid page size %s. Valid page sizes are %s", 1649 pageSize, validPageSizes)); 1650 enforce(freePageBitmapIndex < numPages, 1651 format("Free page map index (%s) out of bounds. Number of pages is %s", 1652 freePageBitmapIndex, numPages)); 1653 enforce(freePageBitmapIndex == 1 || freePageBitmapIndex == 2, 1654 format("Free page map index must be 1 or 2, not %s", freePageBitmapIndex)); 1655 enforce(metaStreamHeaderPage < numPages, 1656 format("Page map index (%s) out of bounds. Number of pages is %s", 1657 metaStreamHeaderPage, numPages)); 1658 enforce(metaStreamHeaderPage != 0, "Page map index cannot be located in page 0"); 1659 1660 uint directoryBytes = cast(uint)(numMetaStreamPages * uint.sizeof); 1661 enforce(directoryBytes <= pageSize, 1662 format("Array of directory pages (%s pages, %s bytes) cannot fit into single page (%s bytes)", 1663 numMetaStreamPages, directoryBytes, pageSize)); 1664 } 1665 } 1666 1667 struct PdbStreamHeader { 1668 uint ver; 1669 uint signature; 1670 uint age; 1671 ubyte[16] guid; 1672 } 1673 1674 enum PdbStreamVersion : uint { 1675 VC2 = 19941610, 1676 VC4 = 19950623, 1677 VC41 = 19950814, 1678 VC50 = 19960307, 1679 VC98 = 19970604, 1680 VC70Dep = 19990604, 1681 VC70 = 20000404, 1682 VC80 = 20030901, 1683 VC110 = 20091201, 1684 VC140 = 20140508, 1685 } 1686 1687 enum Pdb_FeatureCodeMagic : uint { 1688 vc110 = 20091201, 1689 vc140 = 20140508, 1690 noTypeMerge = 0x4D544F4E, 1691 minimalDebugInfo = 0x494E494D, 1692 }; 1693 1694 enum Pdb_FeatureFlags : uint { 1695 vc110 = 1 << 0, 1696 vc140 = 1 << 1, 1697 noTypeMerge = 1 << 2, 1698 minimalDebugInfo = 1 << 3, 1699 }; 1700 1701 struct TpiStreamHeader { 1702 uint ver; 1703 uint headerSize; 1704 uint typeIndexBegin; 1705 uint typeIndexEnd; 1706 uint typeRecordBytes; 1707 1708 ushort hashStreamIndex; 1709 ushort hashAuxStreamIndex; 1710 uint hashKeySize; 1711 uint numHashBuckets; 1712 1713 int hashValueBufferOffset; 1714 uint hashValueBufferLength; 1715 1716 int indexOffsetBufferOffset; 1717 uint indexOffsetBufferLength; 1718 1719 int hashAdjBufferOffset; 1720 uint hashAdjBufferLength; 1721 1722 void print() 1723 { 1724 writefln(" Stream header:"); 1725 writefln(" % 10s ver", ver); 1726 writefln(" % 10s headerSize", headerSize); 1727 writefln(" % 10s typeIndexBegin", typeIndexBegin); 1728 writefln(" % 10s typeIndexEnd", typeIndexEnd); 1729 writefln(" % 10s typeRecordBytes", typeRecordBytes); 1730 writefln(" % 10s hashStreamIndex", hashStreamIndex); 1731 writefln(" % 10s hashAuxStreamIndex", hashAuxStreamIndex); 1732 writefln(" % 10s hashKeySize", hashKeySize); 1733 writefln(" % 10s numHashBuckets", numHashBuckets); 1734 writefln(" % 10s hashValueBufferOffset", hashValueBufferOffset); 1735 writefln(" % 10s hashValueBufferLength", hashValueBufferLength); 1736 writefln(" % 10s indexOffsetBufferOffset", indexOffsetBufferOffset); 1737 writefln(" % 10s indexOffsetBufferLength", indexOffsetBufferLength); 1738 writefln(" % 10s hashAdjBufferOffset", hashAdjBufferOffset); 1739 writefln(" % 10s hashAdjBufferLength", hashAdjBufferLength); 1740 } 1741 } 1742 1743 struct DbiStreamHeader { 1744 int VersionSignature; 1745 uint VersionHeader; 1746 uint Age; 1747 ushort GlobalStreamIndex; 1748 ushort BuildNumber; 1749 ushort PublicStreamIndex; 1750 ushort PdbDllVersion; 1751 ushort SymRecordStream; 1752 ushort PdbDllRbld; 1753 int ModInfoSize; 1754 int SectionContributionSize; 1755 int SectionMapSize; 1756 int SourceInfoSize; 1757 int TypeServerMapSize; 1758 uint MFCTypeServerIndex; 1759 int OptionalDbgHeaderSize; 1760 int ECSubstreamSize; 1761 ushort Flags; 1762 ushort Machine; 1763 uint Padding; 1764 } 1765 static assert(DbiStreamHeader.sizeof == 64); 1766 1767 enum COFF_DEBUG_SECTION_MAGIC = 4; 1768 1769 enum DebugSubsectionKind : uint { 1770 none = 0, 1771 symbols = 0xf1, 1772 lines = 0xf2, 1773 stringTable = 0xf3, 1774 fileChecksums = 0xf4, 1775 frameData = 0xf5, 1776 inlineeLines = 0xf6, 1777 crossScopeImports = 0xf7, 1778 crossScopeExports = 0xf8, 1779 iLLines = 0xf9, 1780 funcMDTokenMap = 0xfa, 1781 typeMDTokenMap = 0xfb, 1782 mergedAssemblyInput = 0xfc, 1783 coffSymbolRVA = 0xfd, 1784 } 1785 1786 struct DebugSubsectionHeader 1787 { 1788 DebugSubsectionKind kind; 1789 uint length; 1790 } 1791 1792 struct ModInfo { 1793 uint Unused1; 1794 SectionContribEntry SectionContr; 1795 ushort Flags; 1796 ushort ModuleSymStream; 1797 uint SymByteSize; 1798 uint C11ByteSize; 1799 uint C13ByteSize; 1800 ushort SourceFileCount; 1801 ubyte[2] Padding; 1802 uint Unused2; 1803 uint SourceFileNameIndex; 1804 uint PdbFilePathNameIndex; 1805 /// two zero-terminated strings follow. They are padded to 4 byte alignment 1806 //char ModuleName[]; 1807 //char ObjFileName[]; 1808 } 1809 static assert(ModInfo.sizeof == 64); 1810 1811 enum SectionContrSubstreamVersion : uint { 1812 Ver60 = 0xeffe0000 + 19970605, 1813 V2 = 0xeffe0000 + 20140516 1814 } 1815 1816 struct SectionContribEntry 1817 { 1818 ushort section; 1819 int offset; 1820 int size; 1821 uint characteristics; 1822 ushort moduleIndex; 1823 uint dataCrc; 1824 uint relocCrc; 1825 } 1826 static assert(SectionContribEntry.sizeof == 28); 1827 1828 struct SectionContribution2 1829 { 1830 SectionContribEntry sc; 1831 uint CoffSectionIndex; 1832 } 1833 1834 struct SectionMapHeader 1835 { 1836 ushort count; // Number of segment descriptors 1837 ushort logCount; // Number of logical segment descriptors 1838 } 1839 1840 struct SectionMapEntry 1841 { 1842 ushort flags; // See the SectionMapEntryFlags enum below. 1843 ushort ovl; // Logical overlay number 1844 ushort group; // Group index into descriptor array. 1845 ushort frame; 1846 ushort sectionName; // Byte index of segment / group name in string table, or 0xFFFF. 1847 ushort className; // Byte index of class in string table, or 0xFFFF. 1848 uint offset; // Byte offset of the logical segment within physical segment. If group is set in flags, this is the offset of the group. 1849 uint sectionLength; // Byte count of the segment or group. 1850 } 1851 1852 enum SectionMapEntryFlags : ushort { 1853 read = 1 << 0, // Segment is readable. 1854 write = 1 << 1, // Segment is writable. 1855 execute = 1 << 2, // Segment is executable. 1856 addressIs32Bit = 1 << 3, // Descriptor describes a 32-bit linear address. 1857 isSelector = 1 << 8, // Frame represents a selector. 1858 isAbsoluteAddress = 1 << 9, // Frame represents an absolute address. 1859 isGroup = 1 << 10 // If set, descriptor represents a group. 1860 } 1861 1862 struct SourceInfoHeader { 1863 ushort numModules; 1864 ushort numSourceFiles; 1865 1866 // following are variable size arrays 1867 // ushort ModIndices[numModules]; 1868 // ushort ModFileCounts[numModules]; 1869 // uint FileNameOffsets[numSourceFiles]; 1870 // char NamesBuffer[][numSourceFiles]; 1871 }; 1872 1873 enum PointerKind : ubyte { 1874 near16 = 0x00, // 16 bit pointer 1875 far16 = 0x01, // 16:16 far pointer 1876 huge16 = 0x02, // 16:16 huge pointer 1877 basedOnSegment = 0x03, // based on segment 1878 basedOnValue = 0x04, // based on value of base 1879 basedOnSegmentValue = 0x05, // based on segment value of base 1880 basedOnAddress = 0x06, // based on address of base 1881 basedOnSegmentAddress = 0x07, // based on segment address of base 1882 basedOnType = 0x08, // based on type 1883 basedOnSelf = 0x09, // based on self 1884 near32 = 0x0a, // 32 bit pointer 1885 far32 = 0x0b, // 16:32 pointer 1886 near64 = 0x0c // 64 bit pointer 1887 } 1888 enum PointerMode : ubyte { 1889 pointer = 0x00, // "normal" pointer 1890 lValueReference = 0x01, // "old" reference 1891 pointerToDataMember = 0x02, // pointer to data member 1892 pointerToMemberFunction = 0x03, // pointer to member function 1893 rValueReference = 0x04 // r-value reference 1894 } 1895 enum PointerModifiers : ubyte { 1896 none = 0x00, // "normal" pointer 1897 flat32 = 0x01, // "flat" pointer 1898 volatile = 0x02, // pointer is marked volatile 1899 constant = 0x04, // pointer is marked const 1900 unaligned = 0x08, // pointer is marked unaligned 1901 restrict = 0x10, // pointer is marked restrict 1902 } 1903 enum PointerFlags : ubyte { 1904 winRTSmartPointer = 0x01, // pointer is a WinRT smart pointer 1905 lValueRefThisPointer = 0x02, // pointer is a 'this' pointer of a member function with ref qualifier (e.g. void X::foo() &) 1906 rValueRefThisPointer = 0x04 // pointer is a 'this' pointer of a member function with ref qualifier (e.g. void X::foo() &&) 1907 } 1908 1909 enum TypeRecordKind : ushort { 1910 // Those are found in TPI stream 1911 LF_POINTER = 0x1002, 1912 LF_MODIFIER = 0x1001, 1913 LF_PROCEDURE = 0x1008, 1914 LF_MFUNCTION = 0x1009, 1915 LF_LABEL = 0x000e, 1916 1917 LF_ARGLIST = 0x1201, 1918 LF_FIELDLIST = 0x1203, 1919 1920 LF_ARRAY = 0x1503, 1921 LF_CLASS = 0x1504, 1922 LF_STRUCTURE = 0x1505, 1923 LF_UNION = 0x1506, 1924 LF_ENUM = 0x1507, 1925 LF_TYPESERVER2 = 0x1515, 1926 1927 // Those are found in IPI stream 1928 LF_FUNC_ID = 0x1601, // global func ID 1929 LF_MFUNC_ID = 0x1602, // member func ID 1930 LF_BUILDINFO = 0x1603, // build info: tool, version, command line, src/pdb file 1931 LF_SUBSTR_LIST = 0x1604, // similar to LF_ARGLIST, for list of sub strings 1932 LF_STRING_ID = 0x1605, // string ID 1933 LF_UDT_SRC_LINE = 0x1606, // source and line on where an UDT is defined, only generated by compiler 1934 LF_UDT_MOD_SRC_LINE = 0x1607, // module, source and line on where an UDT is defined, only generated by linker 1935 } 1936 1937 /// http://llvm.org/docs/PDB/TpiStream.html#id4 1938 /// 32bit index 1939 struct TypeIndex 1940 { 1941 union { 1942 uint asUint; 1943 mixin(bitfields!( 1944 SimpleTypeKind, "kind", 8, 1945 SimpleTypeMode, "mode", 4, 1946 uint, "", 19, 1947 bool, "isInIPIStream", 1 1948 )); 1949 } 1950 1951 void toString(scope void delegate(const(char)[]) sink) 1952 { 1953 TypeIndex copy = this; 1954 copy.isInIPIStream = false; 1955 if (copy.asUint >= 0x1000) 1956 { 1957 if (isInIPIStream) 1958 { 1959 sink.formattedWrite("IPI:%s", asUint - 0x1000); 1960 } 1961 else 1962 { 1963 sink.formattedWrite("TPI:%s", asUint - 0x1000); 1964 } 1965 } 1966 else 1967 { 1968 sink.formattedWrite("%s:%s", kind, mode); 1969 } 1970 } 1971 } 1972 1973 enum SimpleTypeKind : ushort { 1974 None = 0x0000, // uncharacterized type (no type) 1975 Void = 0x0003, // void 1976 NotTranslated = 0x0007, // type not translated by cvpack 1977 HResult = 0x0008, // OLE/COM HRESULT 1978 1979 SignedCharacter = 0x0010, // 8 bit signed 1980 UnsignedCharacter = 0x0020, // 8 bit unsigned 1981 NarrowCharacter = 0x0070, // really a char 1982 WideCharacter = 0x0071, // wide char 1983 Character16 = 0x007a, // char16_t 1984 Character32 = 0x007b, // char32_t 1985 1986 SByte = 0x0068, // 8 bit signed int 1987 Byte = 0x0069, // 8 bit unsigned int 1988 Int16Short = 0x0011, // 16 bit signed 1989 UInt16Short = 0x0021, // 16 bit unsigned 1990 Int16 = 0x0072, // 16 bit signed int 1991 UInt16 = 0x0073, // 16 bit unsigned int 1992 Int32Long = 0x0012, // 32 bit signed 1993 UInt32Long = 0x0022, // 32 bit unsigned 1994 Int32 = 0x0074, // 32 bit signed int 1995 UInt32 = 0x0075, // 32 bit unsigned int 1996 Int64Quad = 0x0013, // 64 bit signed 1997 UInt64Quad = 0x0023, // 64 bit unsigned 1998 Int64 = 0x0076, // 64 bit signed int 1999 UInt64 = 0x0077, // 64 bit unsigned int 2000 Int128Oct = 0x0014, // 128 bit signed int 2001 UInt128Oct = 0x0024, // 128 bit unsigned int 2002 Int128 = 0x0078, // 128 bit signed int 2003 UInt128 = 0x0079, // 128 bit unsigned int 2004 2005 Float16 = 0x0046, // 16 bit real 2006 Float32 = 0x0040, // 32 bit real 2007 Float32PartialPrecision = 0x0045, // 32 bit PP real 2008 Float48 = 0x0044, // 48 bit real 2009 Float64 = 0x0041, // 64 bit real 2010 Float80 = 0x0042, // 80 bit real 2011 Float128 = 0x0043, // 128 bit real 2012 2013 Complex16 = 0x0056, // 16 bit complex 2014 Complex32 = 0x0050, // 32 bit complex 2015 Complex32PartialPrecision = 0x0055, // 32 bit PP complex 2016 Complex48 = 0x0054, // 48 bit complex 2017 Complex64 = 0x0051, // 64 bit complex 2018 Complex80 = 0x0052, // 80 bit complex 2019 Complex128 = 0x0053, // 128 bit complex 2020 2021 Boolean8 = 0x0030, // 8 bit boolean 2022 Boolean16 = 0x0031, // 16 bit boolean 2023 Boolean32 = 0x0032, // 32 bit boolean 2024 Boolean64 = 0x0033, // 64 bit boolean 2025 Boolean128 = 0x0034, // 128 bit boolean 2026 } 2027 2028 enum SimpleTypeMode : ubyte { 2029 Direct = 0, // Not a pointer 2030 NearPointer = 1, // Near pointer 2031 FarPointer = 2, // Far pointer 2032 HugePointer = 3, // Huge pointer 2033 NearPointer32 = 4, // 32 bit near pointer 2034 FarPointer32 = 5, // 32 bit far pointer 2035 NearPointer64 = 6, // 64 bit near pointer 2036 NearPointer128 = 7 // 128 bit near pointer 2037 }; 2038 2039 2040 2041 /// Leaf record types 2042 /// 32 bit types are the same as 16 bit but have 0x1000 bit set 2043 2044 // LF_SKIP 2045 struct CVType_SKIP 2046 { 2047 uint type; // next valid index 2048 } 2049 2050 // LF_ARGLIST, LF_SUBSTR_LIST 2051 struct CVType_ARGLIST 2052 { 2053 uint count; // number of arguments 2054 // next follow `count` type indices of type TypeIndex 2055 } 2056 2057 // LF_DERIVED 2058 2059 // LF_PROCEDURE 2060 struct CVType_PROCEDURE 2061 { 2062 TypeIndex returnType; 2063 CV_CallConv callConv; 2064 ubyte funcAttributes; 2065 ushort numParams; 2066 TypeIndex argList; 2067 } 2068 2069 2070 2071 // LF_FUNC_ID 2072 struct CVType_FUNC_ID 2073 { 2074 uint scopeId; // parent scope of the ID, 0 if global 2075 TypeIndex type; // function type 2076 // zero terminated string follows (name) 2077 } 2078 2079 // LF_STRING_ID 2080 struct CVType_STRING_ID 2081 { 2082 uint id; // ID to list of sub string IDs 2083 // zero terminated string follows (name) 2084 } 2085 2086 // struct lfUdtSrcLine 2087 // LF_UDT_SRC_LINE 2088 struct UdtSrcLine { 2089 TypeIndex udt; // UDT's type index 2090 TypeIndex sourceFile; // index to LF_STRING_ID record where source file name is saved 2091 uint lineNumber; // line number 2092 } 2093 2094 // LF_MFUNC_ID 2095 struct CVType_MFUNC_ID { 2096 TypeIndex parentType; // type index of parent 2097 TypeIndex type; // function type 2098 // zero terminated string follows (name) 2099 } 2100 2101 2102 // LF_BUILDINFO 2103 struct CVType_BUILDINFO 2104 { 2105 ushort count; // number of arguments 2106 TypeIndex[CV_BuildInfo.KNOWN] args; 2107 } 2108 2109 enum CV_BuildInfo 2110 { 2111 CurrentDirectory = 0, 2112 BuildTool = 1, // Cl.exe 2113 SourceFile = 2, // foo.cpp 2114 ProgramDatabaseFile = 3, // foo.pdb 2115 CommandArguments = 4, // -I etc 2116 KNOWN 2117 } 2118 2119 // -------------------------------------------- 2120 // Symbols 2121 2122 /// Header of the hash tables found in the globals and publics sections. 2123 // GSIHashHdr 2124 struct GSIHashHeader 2125 { 2126 uint verSignature = 0xFFFF_FFFF; 2127 uint verHdr = 0xeffe0000 + 19990810; 2128 // hrSize + numBuckets == remainingBytes of the stream 2129 uint hrSize; 2130 uint numBuckets; 2131 } 2132 2133 // Comes first in Publics stream 2134 // PSGSIHDR 2135 struct PublicsStreamHeader 2136 { 2137 uint symHash; 2138 uint addrMap; 2139 uint numThunks; 2140 uint sizeOfThunk; 2141 ushort isectThunkTable; 2142 ubyte[2] padding; 2143 uint offThunkTable; 2144 uint numSections; 2145 } 2146 2147 struct PSHashRecord 2148 { 2149 uint offset; // Offset in the symbol record stream 2150 uint cref; 2151 } 2152 2153 // struct SO in langapi/include/pdb.h 2154 struct SectionOffset 2155 { 2156 uint offset; 2157 ushort isect; 2158 ubyte[2] pad; 2159 }; 2160 2161 2162 // The header preceeding the /names stream. 2163 struct StringTableHeader 2164 { 2165 uint signature = 0xEFFEEFFE; 2166 uint hashVersion; // 1 or 2 2167 uint byteSize; // Number of bytes of names buffer. 2168 2169 uint stringTableHashString(const(char)[] str) { 2170 if (hashVersion == 1) return stringTableHashStringV1(str); 2171 if (hashVersion == 2) return stringTableHashStringV2(str); 2172 assert(false); 2173 } 2174 }; 2175 2176 // Corresponds to `Hasher::lhashPbCb` in PDB/include/misc.h. 2177 // Used for name hash table and TPI/IPI hashes. 2178 uint stringTableHashStringV1(const(char)[] str) { 2179 uint hash = 0; 2180 uint length = cast(uint)str.length; 2181 2182 uint[] uints = (cast(uint*)str.ptr)[0..str.length / uint.sizeof]; 2183 foreach (u; uints) 2184 hash ^= u; 2185 2186 const(ubyte)* remainder = cast(const(ubyte)*)(uints.ptr + uints.length); 2187 uint remainderSize = length % 4; 2188 2189 // Maximum of 3 bytes left. Hash a 2 byte word if possible, then hash the 2190 // possibly remaining 1 byte. 2191 if (remainderSize >= 2) { 2192 ushort value = *cast(ushort*)(remainder); 2193 hash ^= value; 2194 remainder += 2; 2195 remainderSize -= 2; 2196 } 2197 2198 // hash possible odd byte 2199 if (remainderSize == 1) { 2200 hash ^= *(remainder++); 2201 } 2202 2203 immutable uint toLowerMask = 0x20202020; 2204 hash |= toLowerMask; 2205 hash ^= (hash >> 11); 2206 2207 return hash ^ (hash >> 16); 2208 } 2209 2210 // Corresponds to `HasherV2::HashULONG` in PDB/include/misc.h. 2211 // Used for name hash table. 2212 uint stringTableHashStringV2(const(char)[] str) { 2213 uint hash = 0xb170a1bf; 2214 2215 // hash whole uints from the start of the string 2216 uint[] uints = (cast(uint*)str.ptr)[0..str.length / uint.sizeof]; 2217 foreach (uint item; uints) { 2218 hash += item; 2219 hash += (hash << 10); 2220 hash ^= (hash >> 6); 2221 } 2222 2223 // hash remaining bytes 2224 const(ubyte)[] remainingBytes = cast(const(ubyte)[])str[uints.length * uint.sizeof..$]; 2225 foreach (ubyte item; remainingBytes) { 2226 hash += item; 2227 hash += (hash << 10); 2228 hash ^= (hash >> 6); 2229 } 2230 2231 return hash * 1664525U + 1013904223U; 2232 }