Changeset 100914 in vbox
- Timestamp:
- Aug 21, 2023 2:32:45 AM (14 months ago)
- Location:
- trunk
- Files:
-
- 5 edited
-
include/iprt/formats/pdb.h (modified) (1 diff)
-
include/iprt/formats/pecoff.h (modified) (1 diff)
-
include/iprt/fsvfs.h (modified) (1 diff)
-
src/VBox/Runtime/Makefile.kmk (modified) (3 diffs)
-
src/VBox/Runtime/common/fs/pdbvfs.cpp (modified) (28 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/iprt/formats/pdb.h
r100909 r100914 324 324 } 325 325 326 327 /** 328 * This is the old DBI stream header. 329 * 330 * @note Haven't found any examples of this yet, as it must predate VC60, quite 331 * likely VC40 was the last to use it. It may need padding adjusting as 332 * well as a reality check... 333 */ 334 typedef struct RTPDBDBIHDROLD 335 { 336 /** The global symbol records stream number. */ 337 uint16_t idxGlobalStream; 338 /** The public symbol records stream number. */ 339 uint16_t idxPublicStream; 340 /** The symbol records stream. */ 341 uint16_t idxSymRecStream; 342 /** Haven't seen the real thing yet... The struct could be misaligned by 343 * pragma pack(1)... */ 344 uint16_t uUnusedPadding; 345 /** Size of the module info substream. */ 346 uint32_t cbModInfoSubstream; 347 /** Size of the section contribution substream. */ 348 uint32_t cbSectContribSubstream; 349 /** Size of the source info substream. */ 350 uint32_t cbSrcInfoSubstream; 351 } RTPDBDBIHDROLD; 352 353 354 /** 355 * The new DBI stream header. 356 */ 357 typedef struct RTPDBDBIHDR 358 { 359 /** 0x00: Header signature, RTPDBDBIHDR_SIGNATURE. 360 * This has the value UINT32_MAX which probably to make sure it can be 361 * distinguished from the start of the old header, assuming the global and/or 362 * public stream indexes won't both be NIL (UINT16_MAX). */ 363 uint32_t uSignature; 364 /** 0x04: The header version signature, RTPDBDBIHDR_VCVER_XXX. 365 * This should be RTPDBDBIHDR_VCVER_50 or higher, even if Visual C++ 4.1 in 366 * the RTPDBDBIHDR_VCVER_XXX collection. */ 367 uint32_t uVcVersion; 368 /** 0x08: Same (copy) of RTPDB70NAMES::uAge / RTPDB20NAMES::uAge. */ 369 uint32_t uAge; 370 /** 0x0c: The global symbol records stream number. */ 371 uint16_t idxGlobalStream; 372 /** 0x0e: The MSPDB*.DLL version. 373 * This is a bitfield with two different layout. If the new format is used, 374 * the RBuild is stored separately as uPdbDllRBuild 375 * 376 * @note In the w2ksp4 PDBs this is all zeros. So, was presumably added 377 * after VC60. Ditto for the two other version/build fields. 378 * 379 * In a random 32-bit xp rtm kernel it's set to 0x8800 (8.0, new format), 380 * and with uPdbDllBuild=0xc627 (50727) and uPdbDllRBuild=0x0048 (72). 381 * 382 * For a VBoxRT.dll built with VC 2010 this is set to 0x8a00 (10.0, new 383 * format), uPdbDllBuild=0x6f76 (28534) and uPdbDllRBuild=0x0001. */ 384 union 385 { 386 uint16_t u16; 387 struct 388 { 389 uint16_t uMinor : 8; 390 uint16_t uMajor : 7; 391 uint16_t fNewVerFmt : 1; 392 } New; 393 struct 394 { 395 uint16_t uRBuild : 4; 396 uint16_t uMinor : 7; 397 uint16_t uMajor : 5; 398 } Old; 399 } PdbDllVer; 400 /** 0x10: The public symbol records stream number. */ 401 uint16_t idxPublicStream; 402 /** 0x12: The MSPDB*.DLL build number. */ 403 uint16_t uPdbDllBuild; 404 /** 0x14: The symbol records stream. */ 405 uint16_t idxSymRecStream; 406 /** 0x16: The MSPDB*.DLL rbuild number, whatever that is... (Release build 407 * number, perhaps?) */ 408 uint16_t uPdbDllRBuild; 409 /** 0x18: Size of the module info substream. */ 410 uint32_t cbModInfoSubstream; 411 /** 0x1c: Size of the section contribution substream. */ 412 uint32_t cbSectContribSubstream; 413 /** 0x20: Size of the section map substream. */ 414 uint32_t cbSectionMapSubstream; 415 /** 0x24: Size of the source info substream. */ 416 uint32_t cbSrcInfoSubstream; 417 /** 0x28: Size of the type server map substream. */ 418 uint32_t cbTypeServerMapSubstream; 419 /** 0x2c: Index of the MFC type server in the type server map substream. */ 420 uint32_t idxMFC; 421 /** 0x30: Size of the optional debug header at the end of the stream. */ 422 uint32_t cbOptDbgHdr; 423 /** 0x34: Size of the edit & continue substream. Added in VC60, can contain 424 * garbage when uVcVersion is older. */ 425 uint32_t cbEditContinueSubstream; 426 /** 0x38: Flat, combination of RTPDBDBIHDR_F_XXX. */ 427 uint16_t fFlags; 428 /** 0x3a: The machine type (IMAGE_FILE_MACHINE_XXX from pecoff). */ 429 uint16_t uMachine; 430 /** 0x3c: Currently unused field. */ 431 uint32_t uReserved; 432 } RTPDBDBIHDR; 433 AssertCompileSize(RTPDBDBIHDR,64); 434 435 /** The value of RTPDBDBIHDR::uSignature. */ 436 #define RTPDBDBIHDR_SIGNATURE UINT32_MAX 437 438 /** @name RTPDBDBIHDR_VCVER_XXX - Possible RTPDBDBIHDR::uVcVersion values. 439 * @{ */ 440 #define RTPDBDBIHDR_VCVER RTPDBDBIHDR_VCVER_70 441 #define RTPDBDBIHDR_VCVER_41 UINT32_C( 930803) /**< Apparently too old for the new header. Go figure. */ 442 #define RTPDBDBIHDR_VCVER_50 UINT32_C(19960307) 443 #define RTPDBDBIHDR_VCVER_60 UINT32_C(19970606) /**< Used by Windows 2000 SP4 PDBs.*/ 444 #define RTPDBDBIHDR_VCVER_70 UINT32_C(19990903) 445 #define RTPDBDBIHDR_VCVER_110 UINT32_C(20091201) 446 /** @} */ 447 448 /** @name RTPDBDBIHDR_F_XXX - DBI Header Flags. 449 * @{ */ 450 #define RTPDBDBIHDR_F_INCREMENTAL_LINK UINT16_C(0x0001) 451 #define RTPDBDBIHDR_F_PRIVATE_SYMS_STRIPPED UINT16_C(0x0002) 452 #define RTPDBDBIHDR_F_CONFLICTING_TYPES UINT16_C(0x0004) 453 #define RTPDBDBIHDR_F_RESERVED UINT16_C(0xfff8) 454 /** @} */ 455 456 457 /** @name RTPDBDBIOPT_IDX_XXX - DBI Optional Header Indexes. 458 * 459 * The optional DBI header follows after the edit & continue substream and 460 * consists of an array of 16-bit stream indexes (uint16_t) that helps 461 * identifying streams in the PDB. 462 * 463 * @{ */ 464 /** Frame pointer optimization sections ('.debug$F' from MASM, apparently). */ 465 #define RTPDBDBIOPT_IDX_FPO_MASM 0 466 /** Exception data - IMAGE_DEBUG_TYPE_EXCEPTION. */ 467 #define RTPDBDBIOPT_IDX_EXCEPTION 1 468 /** Fixup data - IMAGE_DEBUG_TYPE_FIXUP. */ 469 #define RTPDBDBIOPT_IDX_FIXUP 2 470 /** OMAP to source - IMAGE_DEBUG_TYPE_OMAP_TO_SRC. */ 471 #define RTPDBDBIOPT_IDX_OMAP_TO_SRC 3 472 /** OMAP from source - IMAGE_DEBUG_TYPE_OMAP_FROM_SRC. */ 473 #define RTPDBDBIOPT_IDX_OMAP_FROM_SRC 4 474 /** Section headers from the executable (array of IMAGE_SECTION_HEADER). */ 475 #define RTPDBDBIOPT_IDX_SECTION_HEADERS 5 476 /** Something related to mapping CLR tokens to CLR record IDs. */ 477 #define RTPDBDBIOPT_IDX_CLR_TOKEN_ID_MAP 6 478 /** Copy of the '.xdata' section. */ 479 #define RTPDBDBIOPT_IDX_XDATA 7 480 /** Copy of the '.pdata' section. (Same purpose as RTPDBDBIOPT_IDX_EXCEPTION?) */ 481 #define RTPDBDBIOPT_IDX_PDATA 8 482 /** Frame pointer optimization info - IMAGE_DEBUG_TYPE_FPO. */ 483 #define RTPDBDBIOPT_IDX_FPO 9 484 /** Original section headers from the executable (array of IMAGE_SECTION_HEADER). */ 485 #define RTPDBDBIOPT_IDX_ORG_SECTION_HEADERS 10 486 487 /** End of known indexes. */ 488 #define RTPDBDBIOPT_IDX_END 11 489 /** @} */ 490 326 491 /** @} */ 327 492 -
trunk/include/iprt/formats/pecoff.h
r98103 r100914 72 72 73 73 74 /** @name PE & COFF machine types.74 /** @name IMAGE_FILE_MACHINE_XXX - PE & COFF machine types. 75 75 * Used by IMAGE_FILE_HEADER::Machine and IMAGE_SEPARATE_DEBUG_HEADER::Machine. 76 76 * @{ */ -
trunk/include/iprt/fsvfs.h
r100913 r100914 230 230 * creating the PDB. Version 2 may have both 6 and 8 digit variants. 231 231 * 232 * Standard stream names: 233 * - root - stream \#0, the root stream 234 * - pdb - stream \#1, the PDB header 235 * - tpi - stream \#2, the type info 236 * - dbi - stream \#3, the DBI stream 237 * - dbi-module-info - Module info DBI substream. 238 * - dbi-section-contributions - Section contribs DBI substream. 239 * - dbi-section-map - Section map DBI substream. 240 * - dbi-source-info - Source info DBI substream. 241 * - dbi-type-server-map - Type server map DBI substream. 242 * - dbi-continue-and-edit - Continue and edit DBI substream. 243 * - dbi-optional-header - Optional DBI header (substream). 244 * - dbi-unknown - Unkown DBI substream. 245 * - global-symbol-hash - RTPDBDBIHDR::idxGlobalStream 246 * - public-symbol-hash - RTPDBDBIHDR::idxPublicStream 247 * - symbols-records - RTPDBDBIHDR::idxSymRecStream 248 * - image-fpo-masm-section - RTPDBDBIOPT_IDX_FPO_MASM 249 * - image-exception - RTPDBDBIOPT_IDX_EXCEPTION 250 * - image-fixup - RTPDBDBIOPT_IDX_FIXUP 251 * - omap-to-src - RTPDBDBIOPT_IDX_OMAP_TO_SRC 252 * - omap-from-src - RTPDBDBIOPT_IDX_OMAP_FROM_SRC 253 * - image-section-headers - RTPDBDBIOPT_IDX_SECTION_HEADERS 254 * - clr-token-id-map - RTPDBDBIOPT_IDX_CLR_TOKEN_ID_MAP 255 * - image-xdata-section - RTPDBDBIOPT_IDX_XDATA 256 * - image-pdata-section - RTPDBDBIOPT_IDX_PDATA 257 * - image-fpo - RTPDBDBIOPT_IDX_FPO 258 * - image-orginal-section-headers - RTPDBDBIOPT_IDX_ORG_SECTION_HEADERS 259 * 232 260 * @returns IPRT status code. 233 261 * @param hVfsFileIn The file or device backing the volume. -
trunk/src/VBox/Runtime/Makefile.kmk
r100910 r100914 2363 2363 common/fs/isomakerimport.cpp \ 2364 2364 common/fs/isovfs.cpp \ 2365 common/fs/pdbvfs.cpp \ 2365 2366 common/ldr/ldr.cpp \ 2366 2367 common/ldr/ldrELF.cpp \ … … 2460 2461 common/string/RTStrNLen.cpp \ 2461 2462 common/string/RTStrPrintHexBytes.cpp \ 2463 common/string/RTStrStartsWith.cpp \ 2462 2464 common/string/RTStrStr.cpp \ 2463 2465 common/string/simplepattern.cpp \ … … 2481 2483 common/string/unidata-lower.cpp \ 2482 2484 common/string/unidata-upper.cpp \ 2485 common/string/RTUtf16CopyAscii.cpp \ 2483 2486 common/string/utf-16-case.cpp \ 2484 2487 common/string/utf-16.cpp \ -
trunk/src/VBox/Runtime/common/fs/pdbvfs.cpp
r100913 r100914 67 67 68 68 /** 69 * Substream info. 70 */ 71 typedef struct RTFSPDBSUBSTREAM 72 { 73 /** The parent stream. */ 74 uint32_t idxStream; 75 /** The substream index within the parent. */ 76 uint8_t idxSubstream; 77 /** The offset of the start of the substream within the parent. */ 78 uint32_t offSubstream; 79 /** The size of the substream. */ 80 uint32_t cbSubstream; 81 /** The name of the substream (including the parent one at the start). 82 * Readonly string constant, as substreams are all hardcoded. */ 83 const char *pszName; 84 } RTFSPDBSUBSTREAM; 85 typedef RTFSPDBSUBSTREAM *PRTFSPDBSUBSTREAM; 86 typedef RTFSPDBSUBSTREAM const *PCRTFSPDBSUBSTREAM; 87 88 89 /** 69 90 * Stream info. 70 91 */ … … 87 108 PCRTPDB70PAGE pa70; 88 109 } PageMap; 110 /** Number of substreams. */ 111 uint8_t cSubstreams; 112 /** The index into RTFSPDBVOL::aSubstreams of the first one. */ 113 uint8_t idxFirstSubstream; 89 114 } RTFSPDBSTREAMINFO; 90 115 typedef RTFSPDBSTREAMINFO *PRTFSPDBSTREAMINFO; … … 101 126 /** The stream number (0 based). */ 102 127 uint32_t idxStream; 103 /** Size of the stream. */ 128 /** The start offset of the substream this file object represents. This is 129 * zero for full streams. */ 130 uint32_t offSubstream; 131 /** Size of the stream (or substream). */ 104 132 uint32_t cbStream; 105 /** Number of pages in the stream . */133 /** Number of pages in the stream (the whole stream, not just the substream). */ 106 134 uint32_t cPages; 107 /** Pointer to the page map for the stream (within RTFSPDBVOL::pbRoot)(. */ 135 /** Pointer to the page map for the stream (within RTFSPDBVOL::pbRoot) 136 * (again the whole stream, not just the substream). */ 108 137 union 109 138 { … … 124 153 /** Pointer to the PDB volume data. */ 125 154 PRTFSPDBVOL pPdb; 126 /** The next stream number to return info for when reading (0 based). */ 127 uint32_t idxNextStream; 155 /** The next stream and substream number to return info for when reading. */ 156 union 157 { 158 uint32_t idxNext; 159 struct 160 { 161 /** This is zero for the whole stream and non-zero for substreams. */ 162 uint32_t idxNextSubstream : 8; 163 /** The next stream number. */ 164 uint32_t idxNextStream : 24; 165 } s; 166 } u; 128 167 } RTFSPDBDIROBJ; 129 168 typedef RTFSPDBDIROBJ *PRTFSPDBDIROBJ; … … 186 225 /** @} */ 187 226 227 /** The DBI header from stream \#3. */ 228 RTPDBDBIHDR DbiHdr; 229 188 230 /** Extra per-stream info. We've do individual allocations here in case we 189 231 * want to add write support. */ 190 232 PRTFSPDBSTREAMINFO *papStreamInfo; 233 234 /** Number of substreams in aSubstreams. */ 235 uint8_t cSubstreams; 236 /** Substreams (only from the DBI atm.). */ 237 RTFSPDBSUBSTREAM aSubstreams[8]; 191 238 } RTFSPDBVOL; 192 239 … … 203 250 */ 204 251 static void rtFsPdbPopulateObjInfo(PRTFSPDBVOL pPdb, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr, 205 uint32_t cbStream, uint32_t idxStream, bool fIsDir)252 uint32_t cbStream, uint32_t idxStream, uint8_t idxSubstream, bool fIsDir) 206 253 { 207 254 RTTimeSpecSetNano(&pObjInfo->AccessTime, 0); … … 226 273 pObjInfo->Attr.u.Unix.cHardlinks = 1 + fIsDir; 227 274 pObjInfo->Attr.u.Unix.INodeIdDevice = 0; 228 pObjInfo->Attr.u.Unix.INodeId = idxStream ;275 pObjInfo->Attr.u.Unix.INodeId = idxStream | ((uint64_t)idxSubstream << 32); 229 276 pObjInfo->Attr.u.Unix.fFlags = 0; 230 277 pObjInfo->Attr.u.Unix.GenerationId = 0; … … 253 300 * stream \#1 containing PDB metadata. 254 301 */ 255 static void rtFsPdbPopulateFileObj(PRTFSPDBFILEOBJ pNewFile, PRTFSPDBVOL pPdb, 256 uint32_t idxStream, uint32_t cbStream, void const *pvPageMap) 257 { 258 pNewFile->pPdb = pPdb; 259 pNewFile->idxStream = idxStream; 260 pNewFile->cbStream = cbStream; 261 pNewFile->PageMap.pv = pvPageMap; 262 pNewFile->cPages = RTPdbSizeToPages(cbStream, pPdb->cbPage); 263 pNewFile->offFile = 0; 264 } 265 266 267 /** 268 * Helper methods for opening a stream. 269 */ 270 static void rtFsPdbPopulateFileObjFromInfo(PRTFSPDBFILEOBJ pNewFile, PRTFSPDBVOL pPdb, uint32_t idxStream) 302 static void rtFsPdbPopulateFileObj(PRTFSPDBFILEOBJ pNewFile, PRTFSPDBVOL pPdb, uint32_t idxStream, 303 uint32_t cbStream, void const *pvPageMap) 304 { 305 pNewFile->pPdb = pPdb; 306 pNewFile->idxStream = idxStream; 307 pNewFile->offSubstream = 0; 308 pNewFile->cbStream = cbStream; 309 pNewFile->PageMap.pv = pvPageMap; 310 pNewFile->cPages = RTPdbSizeToPages(cbStream, pPdb->cbPage); 311 pNewFile->offFile = 0; 312 } 313 314 315 /** 316 * Helper methods for opening a stream or substream. 317 */ 318 static void rtFsPdbPopulateFileObjFromInfo(PRTFSPDBFILEOBJ pNewFile, PRTFSPDBVOL pPdb, 319 uint32_t idxStream, PCRTFSPDBSUBSTREAM pSubstream) 271 320 { 272 321 PCRTFSPDBSTREAMINFO pInfo = pPdb->papStreamInfo[idxStream]; 273 322 rtFsPdbPopulateFileObj(pNewFile, pPdb, idxStream, pInfo->cbStream, pInfo->PageMap.pv); 274 323 Assert(pInfo->cPages == pNewFile->cPages); 324 if (pSubstream) 325 { 326 pNewFile->offSubstream = pSubstream->offSubstream; 327 pNewFile->cbStream = pSubstream->cbSubstream; 328 } 275 329 } 276 330 … … 294 348 { 295 349 PRTFSPDBFILEOBJ const pThis = (PRTFSPDBFILEOBJ)pvThis; 296 rtFsPdbPopulateObjInfo(pThis->pPdb, pObjInfo, enmAddAttr, pThis->cbStream, pThis->idxStream, false /*fIsDir*/);350 rtFsPdbPopulateObjInfo(pThis->pPdb, pObjInfo, enmAddAttr, pThis->cbStream, pThis->idxStream, 0, false /*fIsDir*/); 297 351 return VINF_SUCCESS; 298 352 } … … 350 404 while (cbToRead > 0) 351 405 { 352 uint32_t iPageMap = (uint32_t)( offFile/ pPdb->cbPage);353 uint32_t const offInPage = (uint32_t)( offFile% pPdb->cbPage);406 uint32_t iPageMap = (uint32_t)((offFile + pThis->offSubstream) / pPdb->cbPage); 407 uint32_t const offInPage = (uint32_t)((offFile + pThis->offSubstream) % pPdb->cbPage); 354 408 size_t cbLeftInPage = pPdb->cbPage - offInPage; 355 409 if (cbLeftInPage > cbToRead) … … 516 570 PRTFSPDBDIROBJ const pThis = (PRTFSPDBDIROBJ)pvThis; 517 571 PRTFSPDBVOL const pPdb = pThis->pPdb; 518 rtFsPdbPopulateObjInfo(pPdb, pObjInfo, enmAddAttr, pPdb->cbRoot, 0 /* root dir is stream zero */, true /*fIsDir*/); 572 rtFsPdbPopulateObjInfo(pPdb, pObjInfo, enmAddAttr, pPdb->cbRoot, 0 /* root dir is stream zero */, 573 0 /*idxSubstream*/, true /*fIsDir*/); 519 574 return VINF_SUCCESS; 575 } 576 577 578 /** 579 * Helper for looping up a substream name. 580 */ 581 DECLINLINE(PCRTFSPDBSUBSTREAM) rtFsPdbDirLookupSubstream(PRTFSPDBVOL pPdb, uint32_t idxStream, const char *pszName) 582 { 583 uint8_t const idxFirst = idxStream != UINT32_MAX ? pPdb->papStreamInfo[idxStream]->idxFirstSubstream : 0; 584 uint8_t cLeft = idxStream != UINT32_MAX ? pPdb->papStreamInfo[idxStream]->cSubstreams : pPdb->cSubstreams; 585 while (cLeft-- > 0) 586 { 587 PCRTFSPDBSUBSTREAM pSubstream = &pPdb->aSubstreams[idxFirst + cLeft]; 588 if (strcmp(pSubstream->pszName, pszName) == 0) 589 return pSubstream; 590 } 591 return NULL; 520 592 } 521 593 … … 569 641 * - or the combination of the two: "1-pdb". 570 642 */ 571 uint32_t idxStream; 643 uint32_t idxStream; 644 PCRTFSPDBSUBSTREAM pSubstream = NULL; 572 645 if (RT_C_IS_DIGIT(*pszEntry)) 573 646 { … … 584 657 && RTStrCmp(pszNext + 1, pPdb->papStreamInfo[idxStream]->pszName) != 0) 585 658 { 586 Log2(("rtFsPdbDir_Open: idxStream=%#x name mismatch '%s', expected '%s'\n", 587 idxStream, pszEntry, pPdb->papStreamInfo[idxStream]->pszName)); 588 return VERR_PATH_NOT_FOUND; 659 pSubstream = rtFsPdbDirLookupSubstream(pPdb, idxStream, pszNext + 1); 660 if (pSubstream) 661 Assert(pSubstream->idxStream == idxStream); 662 else 663 { 664 Log2(("rtFsPdbDir_Open: idxStream=%#x name mismatch '%s', expected '%s'\n", 665 idxStream, pszEntry, pPdb->papStreamInfo[idxStream]->pszName)); 666 return VERR_PATH_NOT_FOUND; 667 } 589 668 } 590 669 } … … 599 678 if (idxStream >= pPdb->cStreams) 600 679 { 601 Log2(("rtFsPdbDir_Open: '%s' not found in name table\n", pszEntry)); 602 return VERR_PATH_NOT_FOUND; 680 pSubstream = rtFsPdbDirLookupSubstream(pPdb, UINT32_MAX, pszEntry); 681 if (pSubstream) 682 idxStream = pSubstream->idxStream; 683 else 684 { 685 Log2(("rtFsPdbDir_Open: '%s' not found in name table\n", pszEntry)); 686 return VERR_PATH_NOT_FOUND; 687 } 603 688 } 604 689 } … … 615 700 if (RT_SUCCESS(rc)) 616 701 { 617 rtFsPdbPopulateFileObjFromInfo(pNewFile, pPdb, idxStream );702 rtFsPdbPopulateFileObjFromInfo(pNewFile, pPdb, idxStream, pSubstream); 618 703 619 704 /* Convert it to a file object. */ … … 637 722 { 638 723 PRTFSPDBDIROBJ pThis = (PRTFSPDBDIROBJ)pvThis; 639 pThis-> idxNextStream= 0;724 pThis->u.idxNext = 0; 640 725 return VINF_SUCCESS; 641 726 } … … 648 733 RTFSOBJATTRADD enmAddAttr) 649 734 { 650 PRTFSPDBDIROBJ const pThis = (PRTFSPDBDIROBJ)pvThis; 651 PRTFSPDBVOL const pPdb = pThis->pPdb; 652 uint32_t const idxStream = pThis->idxNextStream; 735 PRTFSPDBDIROBJ const pThis = (PRTFSPDBDIROBJ)pvThis; 736 PRTFSPDBVOL const pPdb = pThis->pPdb; 737 uint32_t const idxStream = pThis->u.s.idxNextStream; 738 uint32_t const idxSubstream = pThis->u.s.idxNextSubstream; 653 739 if (idxStream < pPdb->cStreams) 654 740 { 741 PCRTFSPDBSTREAMINFO const pStreamInfo = pPdb->papStreamInfo[idxStream]; 742 PCRTFSPDBSUBSTREAM const pSubstreamInfo = !idxSubstream ? NULL 743 : &pPdb->aSubstreams[pStreamInfo->idxFirstSubstream + idxSubstream - 1]; 744 655 745 /* 656 746 * Do names first as they may cause overflows. … … 661 751 662 752 /* Provide a more descriptive name if possible. */ 663 const char *pszOtherName = pPdb->papStreamInfo[idxStream]->pszName;753 const char *pszOtherName = !idxSubstream ? pStreamInfo->pszName : pSubstreamInfo->pszName; 664 754 size_t const cchOtherName = pszOtherName ? strlen(pszOtherName) : 0; 665 755 … … 682 772 pDirEntry->szName[cchName] = '\0'; 683 773 684 if (cchOtherName )774 if (cchOtherName && !idxSubstream) 685 775 { 686 776 Assert(cchStreamNo <= 8); … … 697 787 698 788 /* Provide the other info. */ 699 if (pPdb->enmVersion == RTFSPDBVER_2) 700 { 701 PCRTPDB20ROOT const pRoot = (PCRTPDB20ROOT)pPdb->pbRoot; 702 rtFsPdbPopulateObjInfo(pPdb, &pDirEntry->Info, enmAddAttr, 703 pRoot->aStreams[idxStream].cbStream, idxStream, false /*fIsDir*/); 789 rtFsPdbPopulateObjInfo(pPdb, &pDirEntry->Info, enmAddAttr, 790 !pSubstreamInfo ? pStreamInfo->cbStream : pSubstreamInfo->cbSubstream, 791 idxStream, idxSubstream, false /*fIsDir*/); 792 793 /* Advance the directory location and return. */ 794 if (idxSubstream >= pStreamInfo->cSubstreams) 795 { 796 pThis->u.s.idxNextSubstream = 0; 797 pThis->u.s.idxNextStream = idxStream + 1; 704 798 } 705 799 else 706 { 707 PCRTPDB70ROOT const pRoot = (PCRTPDB70ROOT)pPdb->pbRoot; 708 rtFsPdbPopulateObjInfo(pPdb, &pDirEntry->Info, enmAddAttr, 709 pRoot->aStreams[idxStream].cbStream, idxStream, false /*fIsDir*/); 710 } 711 712 /* Advance the directory location and return. */ 713 pThis->idxNextStream = idxStream + 1; 800 pThis->u.s.idxNextSubstream = idxSubstream + 1; 714 801 715 802 return VINF_SUCCESS; 716 803 } 717 804 718 Log3(("rtFsPdbDir_ReadDir9660: idxNext Stream=%#x: VERR_NO_MORE_FILES\n", pThis->idxNextStream));805 Log3(("rtFsPdbDir_ReadDir9660: idxNext=%#x: VERR_NO_MORE_FILES\n", pThis->u.idxNext)); 719 806 return VERR_NO_MORE_FILES; 720 807 } … … 775 862 pThis->pbRoot = NULL; 776 863 864 RTMemFree(pThis->pszzNames); 865 pThis->pszzNames = NULL; 866 867 if (pThis->papStreamInfo) 868 { 869 uint32_t idxStream = pThis->cStreams; 870 while (idxStream-- > 0) 871 RTMemFree(pThis->papStreamInfo[idxStream]); 872 RTMemFree(pThis->papStreamInfo); 873 pThis->papStreamInfo = NULL; 874 } 875 pThis->cStreams = 0; 876 777 877 return VINF_SUCCESS; 778 878 } … … 876 976 if (RT_SUCCESS(rc)) 877 977 { 878 pNewDir->pPdb = pThis;879 pNewDir-> idxNextStream= 0;978 pNewDir->pPdb = pThis; 979 pNewDir->u.idxNext = 0; 880 980 return VINF_SUCCESS; 881 981 } … … 912 1012 }; 913 1013 1014 1015 /** 1016 * Internal helper for reading an entire stream into a heap block. 1017 * 1018 * Use RTMemTmpFree to free the returned memory when done. 1019 */ 914 1020 static int rtFsPdbVolReadStreamToHeap(PRTFSPDBVOL pThis, uint32_t idxStream, 915 1021 uint8_t **ppbStream, uint32_t *pcbStream, PRTERRINFO pErrInfo) … … 924 1030 */ 925 1031 RTFSPDBFILEOBJ FileObj; 926 rtFsPdbPopulateFileObjFromInfo(&FileObj, pThis, idxStream );1032 rtFsPdbPopulateFileObjFromInfo(&FileObj, pThis, idxStream, NULL); 927 1033 928 1034 size_t const cbDst = (size_t)FileObj.cbStream + !FileObj.cbStream; … … 945 1051 RTMemTmpFree(pbDst); 946 1052 return rc; 1053 } 1054 1055 1056 /** 1057 * Internal helper for reading from a stream. 1058 */ 1059 static int rtFsPdbVolReadStreamAt(PRTFSPDBVOL pThis, uint32_t idxStream, uint32_t offStream, 1060 void *pvBuf, size_t cbToRead, size_t *pcbRead) 1061 { 1062 RTFSPDBFILEOBJ FileObj; 1063 rtFsPdbPopulateFileObjFromInfo(&FileObj, pThis, idxStream, NULL); 1064 1065 RTSGSEG SgSeg = { pvBuf, cbToRead }; 1066 RTSGBUF SgBuf; 1067 RTSgBufInit(&SgBuf, &SgSeg, 1); 1068 1069 return rtFsPdbFile_Read(&FileObj, offStream, &SgBuf, true /*fBlocking*/, pcbRead); 1070 } 1071 1072 1073 /** 1074 * Adds a substream. 1075 */ 1076 static void rtFsPdbVolAddSubstream(PRTFSPDBVOL pThis, uint32_t idxStream, uint8_t idxSubstream, 1077 uint32_t offSubstream, uint32_t cbSubstream, const char *pszName) 1078 { 1079 /* 1080 * Sanity checking. 1081 */ 1082 Assert(idxSubstream > 0); /* zero is used for the parent stream when enumerating directories. */ 1083 1084 AssertReturnVoid(idxStream < pThis->cStreams); 1085 PRTFSPDBSTREAMINFO const pStreamInfo = pThis->papStreamInfo[idxStream]; 1086 Assert(RTStrStartsWith(pszName, pStreamInfo->pszName)); 1087 AssertReturnVoid(offSubstream < pStreamInfo->cbStream); 1088 AssertStmt(cbSubstream <= pStreamInfo->cbStream - offSubstream, cbSubstream = pStreamInfo->cbStream - offSubstream); 1089 1090 AssertReturnVoid(pThis->cSubstreams < RT_ELEMENTS(pThis->aSubstreams)); 1091 PRTFSPDBSUBSTREAM const pSubstreamInfo = &pThis->aSubstreams[pThis->cSubstreams]; 1092 Assert( pThis->cSubstreams == 0 1093 || pSubstreamInfo[-1].idxStream < idxStream 1094 || ( pSubstreamInfo[-1].idxStream == idxStream 1095 && pSubstreamInfo[-1].idxSubstream < idxSubstream 1096 && pSubstreamInfo[-1].offSubstream <= offSubstream)); 1097 1098 /* 1099 * Add it. 1100 */ 1101 pSubstreamInfo->idxStream = idxStream; 1102 pSubstreamInfo->idxSubstream = idxSubstream; 1103 pSubstreamInfo->offSubstream = offSubstream; 1104 pSubstreamInfo->cbSubstream = cbSubstream; 1105 pSubstreamInfo->pszName = pszName; 1106 1107 if (!pStreamInfo->cSubstreams) 1108 pStreamInfo->idxFirstSubstream = pThis->cSubstreams; 1109 pStreamInfo->cSubstreams++; 1110 1111 pThis->cSubstreams++; 1112 } 1113 1114 1115 /** 1116 * Helper for rtFsPdbVolLoadStream3 to set standard stream names. 1117 */ 1118 static void rtFsPdbVolSetDefaultStreamName(PRTFSPDBVOL pThis, uint32_t idxStream, const char *pszDefaultName) 1119 { 1120 if (idxStream < pThis->cStreams) 1121 { 1122 /* We override entries from the string table, as the standard stream 1123 names must be assumed to work when the streams are present! */ 1124 if ((uintptr_t)pThis->papStreamInfo[idxStream]->pszName - (uintptr_t)pThis->pszzNames < pThis->cbNames) 1125 Log(("rtFsPdbVolSetDefaultStreamName: Overriding string table name for stream %#x: %s -> %s\n", 1126 idxStream, pThis->papStreamInfo[idxStream]->pszName, pszDefaultName)); 1127 pThis->papStreamInfo[idxStream]->pszName = pszDefaultName; 1128 } 1129 } 1130 1131 1132 /** 1133 * Worker for rtFsPdbVolTryInit that extracts info from the DBI stream. 1134 */ 1135 static int rtFsPdbVolLoadStream3(PRTFSPDBVOL pThis, PRTERRINFO pErrInfo) 1136 { 1137 /* On the offchance that the DBI stream is missing... */ 1138 if (3 >= pThis->cStreams) 1139 return VINF_SUCCESS; 1140 1141 /* 1142 * Read the header. 1143 */ 1144 union 1145 { 1146 RTPDBDBIHDR New; 1147 RTPDBDBIHDROLD Old; 1148 uint16_t aidxStream[32]; 1149 } Hdr; 1150 size_t cbRead = 0; 1151 int rc = rtFsPdbVolReadStreamAt(pThis, 3, 0, &Hdr, sizeof(Hdr.New), &cbRead); 1152 if (RT_FAILURE(rc)) 1153 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Error eeading the DBI header"); 1154 1155 if (Hdr.New.uSignature == RTPDBDBIHDR_SIGNATURE) 1156 { 1157 Log(("rtFsPdbVolLoadStream3: New DBI header\n")); 1158 if (cbRead != sizeof(Hdr.New)) 1159 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Bogus DBI header size: cbRead=%#zx, expected %#zx", 1160 cbRead, sizeof(Hdr.New)); 1161 if (Hdr.New.uVcVersion < RTPDBDBIHDR_VCVER_50) 1162 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Bogus DBI header version: %u (%#x), expected min %u", 1163 Hdr.New.uVcVersion, Hdr.New.uVcVersion, RTPDBDBIHDR_VCVER_50); 1164 if (Hdr.New.uVcVersion < RTPDBDBIHDR_VCVER_60) 1165 Hdr.New.cbEditContinueSubstream = 0; 1166 pThis->DbiHdr = Hdr.New; 1167 } 1168 else 1169 { 1170 /* Convert old header to new to new. */ 1171 if (cbRead >= sizeof(Hdr.Old)) 1172 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Bogus DBI header size: cbRead=%#zx, expected %#zx", 1173 cbRead, sizeof(Hdr.Old)); 1174 Log(("rtFsPdbVolLoadStream3: Old DBI header\n")); 1175 RT_ZERO(pThis->DbiHdr); 1176 pThis->DbiHdr.idxMFC = UINT16_MAX; 1177 pThis->DbiHdr.idxGlobalStream = Hdr.Old.idxGlobalStream; 1178 pThis->DbiHdr.idxPublicStream = Hdr.Old.idxPublicStream; 1179 pThis->DbiHdr.idxSymRecStream = Hdr.Old.idxSymRecStream; 1180 pThis->DbiHdr.cbModInfoSubstream = Hdr.Old.cbModInfoSubstream; 1181 pThis->DbiHdr.cbSectContribSubstream = Hdr.Old.cbSectContribSubstream; 1182 pThis->DbiHdr.cbSrcInfoSubstream = Hdr.Old.cbSrcInfoSubstream; 1183 pThis->DbiHdr.uAge = pThis->uAge; 1184 } 1185 1186 Log2((" DBI Hdr uSignature = %#x\n", pThis->DbiHdr.uSignature)); 1187 Log2((" DBI Hdr uVcVersion = %#x\n", pThis->DbiHdr.uVcVersion)); 1188 if (pThis->DbiHdr.uAge == pThis->uAge) 1189 Log2((" DBI Hdr uAge = %#x (same as PDB header)\n", pThis->DbiHdr.uAge)); 1190 else 1191 Log2((" DBI Hdr uAge = %#x - differs from DB.uAge %#x!\n", pThis->DbiHdr.uAge, pThis->uAge)); 1192 Log2((" DBI Hdr idxGlobalStream = %#x\n", pThis->DbiHdr.idxGlobalStream)); 1193 if (pThis->DbiHdr.PdbDllVer.New.fNewVerFmt) 1194 { 1195 Log2((" DBI Hdr PdbDllVer.u16 = %#x - %u.%u (new)\n", pThis->DbiHdr.PdbDllVer.u16, 1196 pThis->DbiHdr.PdbDllVer.New.uMajor, pThis->DbiHdr.PdbDllVer.New.uMinor)); 1197 Log2((" DBI Hdr uPdbDllBuild = %u (%#x)\n", pThis->DbiHdr.uPdbDllBuild, pThis->DbiHdr.uPdbDllBuild)); 1198 Log2((" DBI Hdr uPdbDllRBuild = %u (%#x)\n", pThis->DbiHdr.uPdbDllRBuild, pThis->DbiHdr.uPdbDllRBuild)); 1199 } 1200 else 1201 { 1202 Log2((" DBI Hdr PdbDllVer = %#x - %u.%u.%u (old)\n", pThis->DbiHdr.PdbDllVer.u16, 1203 pThis->DbiHdr.PdbDllVer.Old.uMajor, pThis->DbiHdr.PdbDllVer.Old.uMinor, 1204 pThis->DbiHdr.PdbDllVer.Old.uRBuild)); 1205 if (pThis->DbiHdr.uPdbDllBuild) 1206 Log2((" DBI Hdr uPdbDllBuild = %u (%#x)!!\n", pThis->DbiHdr.uPdbDllBuild, pThis->DbiHdr.uPdbDllBuild)); 1207 if (pThis->DbiHdr.uPdbDllRBuild) 1208 Log2((" DBI Hdr uPdbDllRBuild = %u (%#x)!!\n", pThis->DbiHdr.uPdbDllRBuild, pThis->DbiHdr.uPdbDllRBuild)); 1209 } 1210 Log2((" DBI Hdr idxPublicStream = %#x\n", pThis->DbiHdr.idxPublicStream)); 1211 Log2((" DBI Hdr idxSymRecStream = %#x\n", pThis->DbiHdr.idxSymRecStream)); 1212 Log2((" DBI Hdr cbModInfoSubstream = %#x\n", pThis->DbiHdr.cbModInfoSubstream)); 1213 Log2((" DBI Hdr cbSectContribSubstream = %#x\n", pThis->DbiHdr.cbSectContribSubstream)); 1214 Log2((" DBI Hdr cbSectionMapSubstream = %#x\n", pThis->DbiHdr.cbSectionMapSubstream)); 1215 Log2((" DBI Hdr cbSrcInfoSubstream = %#x\n", pThis->DbiHdr.cbSrcInfoSubstream)); 1216 Log2((" DBI Hdr cbTypeServerMapSubstream = %#x\n", pThis->DbiHdr.cbTypeServerMapSubstream)); 1217 Log2((" DBI Hdr idxMFC = %#x\n", pThis->DbiHdr.idxMFC)); 1218 Log2((" DBI Hdr cbOptDbgHdr = %#x\n", pThis->DbiHdr.cbOptDbgHdr)); 1219 Log2((" DBI Hdr cbEditContinueSubstream = %#x\n", pThis->DbiHdr.cbEditContinueSubstream)); 1220 Log2((" DBI Hdr fFlags = %#x%s%s%s%s%s\n", pThis->DbiHdr.fFlags, pThis->DbiHdr.fFlags ? " -" : "", 1221 pThis->DbiHdr.fFlags & RTPDBDBIHDR_F_INCREMENTAL_LINK ? " IncrmentalLink" : "", 1222 pThis->DbiHdr.fFlags & RTPDBDBIHDR_F_PRIVATE_SYMS_STRIPPED ? " PrivateSymsStripped" : "", 1223 pThis->DbiHdr.fFlags & RTPDBDBIHDR_F_CONFLICTING_TYPES ? " ConflictingTypes" : "", 1224 pThis->DbiHdr.fFlags & RTPDBDBIHDR_F_RESERVED ? " !ReservedFlagsSet!" : "")); 1225 Log2((" DBI Hdr uMachine = %#x\n", pThis->DbiHdr.uMachine)); 1226 Log2((" DBI Hdr uReserved = %#x\n", pThis->DbiHdr.uReserved)); 1227 1228 /* 1229 * Apply new standard stream names. 1230 */ 1231 rtFsPdbVolSetDefaultStreamName(pThis, pThis->DbiHdr.idxGlobalStream, "global-symbol-hash"); 1232 rtFsPdbVolSetDefaultStreamName(pThis, pThis->DbiHdr.idxPublicStream, "public-symbol-hash"); 1233 rtFsPdbVolSetDefaultStreamName(pThis, pThis->DbiHdr.idxSymRecStream, "symbol-records"); 1234 1235 /* 1236 * Fill in substream info. 1237 */ 1238 uint32_t offSubstream = sizeof(Hdr.New); 1239 if (pThis->DbiHdr.cbModInfoSubstream) 1240 { 1241 rtFsPdbVolAddSubstream(pThis, 3, 1, offSubstream, pThis->DbiHdr.cbModInfoSubstream, "dbi-module-info"); 1242 offSubstream += pThis->DbiHdr.cbModInfoSubstream; 1243 } 1244 if (pThis->DbiHdr.cbSectContribSubstream) 1245 { 1246 rtFsPdbVolAddSubstream(pThis, 3, 2, offSubstream, pThis->DbiHdr.cbSectContribSubstream, "dbi-section-contributions"); 1247 offSubstream += pThis->DbiHdr.cbSectContribSubstream; 1248 } 1249 if (pThis->DbiHdr.cbSectionMapSubstream) 1250 { 1251 rtFsPdbVolAddSubstream(pThis, 3, 3, offSubstream, pThis->DbiHdr.cbSectionMapSubstream, "dbi-section-map"); 1252 offSubstream += pThis->DbiHdr.cbSectionMapSubstream; 1253 } 1254 if (pThis->DbiHdr.cbSrcInfoSubstream) 1255 { 1256 rtFsPdbVolAddSubstream(pThis, 3, 4, offSubstream, pThis->DbiHdr.cbSrcInfoSubstream, "dbi-source-info"); 1257 offSubstream += pThis->DbiHdr.cbSrcInfoSubstream; 1258 } 1259 if (pThis->DbiHdr.cbTypeServerMapSubstream) 1260 { 1261 rtFsPdbVolAddSubstream(pThis, 3, 5, offSubstream, pThis->DbiHdr.cbTypeServerMapSubstream, "dbi-type-server-map"); 1262 offSubstream += pThis->DbiHdr.cbTypeServerMapSubstream; 1263 } 1264 if (pThis->DbiHdr.cbEditContinueSubstream) 1265 { 1266 rtFsPdbVolAddSubstream(pThis, 3, 6, offSubstream, pThis->DbiHdr.cbEditContinueSubstream, "dbi-continue-and-edit"); 1267 offSubstream += pThis->DbiHdr.cbEditContinueSubstream; 1268 } 1269 uint32_t const offOptDbgHdr = offSubstream; 1270 if (pThis->DbiHdr.cbOptDbgHdr) 1271 { 1272 rtFsPdbVolAddSubstream(pThis, 3, 7, offSubstream, pThis->DbiHdr.cbOptDbgHdr, "dbi-optional-header"); 1273 offSubstream += pThis->DbiHdr.cbOptDbgHdr; 1274 } 1275 if (offSubstream < pThis->papStreamInfo[3]->cbStream) 1276 rtFsPdbVolAddSubstream(pThis, 3, 8, offSubstream, pThis->papStreamInfo[3]->cbStream - offSubstream, "dbi-unknown"); 1277 1278 /* 1279 * Read the optional header if present, it identifies a bunch of standard streams. 1280 */ 1281 if (pThis->DbiHdr.cbOptDbgHdr > 1) 1282 { 1283 RT_ZERO(Hdr); 1284 rc = rtFsPdbVolReadStreamAt(pThis, 3, offOptDbgHdr, &Hdr, RT_MIN(sizeof(Hdr), pThis->DbiHdr.cbOptDbgHdr), &cbRead); 1285 if (RT_FAILURE(rc)) 1286 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Error eeading the DBI optional header at %#x LB %#x", 1287 offOptDbgHdr, pThis->DbiHdr.cbOptDbgHdr); 1288 static const char * const s_apszIdxNames[] = 1289 { 1290 /* [RTPDBDBIOPT_IDX_FPO_MASM] = */ "image-fpo-masm-section", 1291 /* [RTPDBDBIOPT_IDX_EXCEPTION] = */ "image-exception", 1292 /* [RTPDBDBIOPT_IDX_FIXUP] = */ "image-fixup", 1293 /* [RTPDBDBIOPT_IDX_OMAP_TO_SRC] = */ "omap-to-src", 1294 /* [RTPDBDBIOPT_IDX_OMAP_FROM_SRC] = */ "omap-from-src", 1295 /* [RTPDBDBIOPT_IDX_SECTION_HEADERS] = */ "image-section-headers", 1296 /* [RTPDBDBIOPT_IDX_CLR_TOKEN_ID_MAP] = */ "clr-token-id-map", 1297 /* [RTPDBDBIOPT_IDX_XDATA] = */ "image-xdata-section", 1298 /* [RTPDBDBIOPT_IDX_PDATA] = */ "image-pdata-section", 1299 /* [RTPDBDBIOPT_IDX_FPO] = */ "image-fpo", 1300 /* [RTPDBDBIOPT_IDX_ORG_SECTION_HEADERS] = */ "image-orginal-section-headers", 1301 }; 1302 uint32_t idxName = pThis->DbiHdr.cbOptDbgHdr / 2; 1303 if (idxName > RT_ELEMENTS(s_apszIdxNames)) 1304 { 1305 #ifdef LOG_ENABLED 1306 while (idxName-- > RT_ELEMENTS(s_apszIdxNames)) 1307 Log(("Unknown DBI optional header entry %u: %#x\n", idxName, Hdr.aidxStream[idxName])); 1308 #endif 1309 idxName = RT_ELEMENTS(s_apszIdxNames); 1310 } 1311 while (idxName-- > 0) 1312 rtFsPdbVolSetDefaultStreamName(pThis, Hdr.aidxStream[idxName], s_apszIdxNames[idxName]); 1313 } 1314 1315 return VINF_SUCCESS; 947 1316 } 948 1317 … … 1132 1501 case 5: 1133 1502 if (pThis->enmVersion == RTFSPDBVER_7) /** @todo condition? */ 1134 pThis->papStreamInfo[ 3]->pszName = "name-map";1503 pThis->papStreamInfo[4]->pszName = "name-map"; 1135 1504 RT_FALL_THRU(); 1136 1505 case 4: 1137 pThis->papStreamInfo[3]->pszName = "dbi"; /** @todo conditional? */1506 pThis->papStreamInfo[3]->pszName = "dbi"; 1138 1507 RT_FALL_THRU(); 1139 1508 case 3: … … 1218 1587 pThis->pszzNames = NULL; 1219 1588 pThis->papStreamInfo = NULL; 1589 pThis->cSubstreams = 0; 1220 1590 1221 1591 /* … … 1429 1799 1430 1800 /* 1431 * Load stream #1 if there. 1432 */ 1433 return rtFsPdbVolLoadStream1(pThis, pErrInfo); 1801 * Load info from streams #1 (PDB & names) and #3 (DBI). 1802 */ 1803 rc = rtFsPdbVolLoadStream1(pThis, pErrInfo); 1804 if (RT_SUCCESS(rc)) 1805 rc = rtFsPdbVolLoadStream3(pThis, pErrInfo); 1806 return rc; 1434 1807 } 1435 1808
Note:
See TracChangeset
for help on using the changeset viewer.

