├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── build ├── Examples.vcxproj ├── Examples.vcxproj.filters ├── RawPDB.sln ├── RawPDB.vcxproj └── RawPDB.vcxproj.filters ├── raw_pdb.natvis └── src ├── CMakeLists.txt ├── Examples ├── CMakeLists.txt ├── ExampleContributions.cpp ├── ExampleFunctionSymbols.cpp ├── ExampleFunctionVariables.cpp ├── ExampleLines.cpp ├── ExampleMain.cpp ├── ExampleMemoryMappedFile.cpp ├── ExampleMemoryMappedFile.h ├── ExamplePDBSize.cpp ├── ExampleSymbols.cpp ├── ExampleTimedScope.cpp ├── ExampleTimedScope.h ├── ExampleTypeTable.cpp ├── ExampleTypeTable.h ├── ExampleTypes.cpp ├── Examples_PCH.cpp └── Examples_PCH.h ├── Foundation ├── PDB_ArrayView.h ├── PDB_Assert.h ├── PDB_BitOperators.h ├── PDB_BitUtil.h ├── PDB_DisableWarningsPop.h ├── PDB_DisableWarningsPush.h ├── PDB_Forward.h ├── PDB_Log.h ├── PDB_Macros.h ├── PDB_Memory.h ├── PDB_Move.h ├── PDB_Platform.h ├── PDB_PointerUtil.h └── PDB_Warnings.h ├── PDB.cpp ├── PDB.h ├── PDB_CoalescedMSFStream.cpp ├── PDB_CoalescedMSFStream.h ├── PDB_DBIStream.cpp ├── PDB_DBIStream.h ├── PDB_DBITypes.cpp ├── PDB_DBITypes.h ├── PDB_DirectMSFStream.cpp ├── PDB_DirectMSFStream.h ├── PDB_ErrorCodes.h ├── PDB_GlobalSymbolStream.cpp ├── PDB_GlobalSymbolStream.h ├── PDB_IPIStream.cpp ├── PDB_IPIStream.h ├── PDB_IPITypes.h ├── PDB_ImageSectionStream.cpp ├── PDB_ImageSectionStream.h ├── PDB_InfoStream.cpp ├── PDB_InfoStream.h ├── PDB_ModuleInfoStream.cpp ├── PDB_ModuleInfoStream.h ├── PDB_ModuleLineStream.cpp ├── PDB_ModuleLineStream.h ├── PDB_ModuleSymbolStream.cpp ├── PDB_ModuleSymbolStream.h ├── PDB_NamesStream.cpp ├── PDB_NamesStream.h ├── PDB_PCH.cpp ├── PDB_PCH.h ├── PDB_PublicSymbolStream.cpp ├── PDB_PublicSymbolStream.h ├── PDB_RawFile.cpp ├── PDB_RawFile.h ├── PDB_SectionContributionStream.cpp ├── PDB_SectionContributionStream.h ├── PDB_SourceFileStream.cpp ├── PDB_SourceFileStream.h ├── PDB_TPIStream.cpp ├── PDB_TPIStream.h ├── PDB_TPITypes.h ├── PDB_Types.cpp ├── PDB_Types.h └── PDB_Util.h /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.vspscc 94 | *.vssscc 95 | .builds 96 | *.pidb 97 | *.svclog 98 | *.scc 99 | 100 | # Chutzpah Test files 101 | _Chutzpah* 102 | 103 | # Visual C++ cache files 104 | ipch/ 105 | *.aps 106 | *.ncb 107 | *.opendb 108 | *.opensdf 109 | *.sdf 110 | *.cachefile 111 | *.VC.db 112 | *.VC.VC.opendb 113 | 114 | # Visual Studio profiler 115 | *.psess 116 | *.vsp 117 | *.vspx 118 | *.sap 119 | 120 | # Visual Studio Trace Files 121 | *.e2e 122 | 123 | # TFS 2012 Local Workspace 124 | $tf/ 125 | 126 | # Guidance Automation Toolkit 127 | *.gpState 128 | 129 | # ReSharper is a .NET coding add-in 130 | _ReSharper*/ 131 | *.[Rr]e[Ss]harper 132 | *.DotSettings.user 133 | 134 | # TeamCity is a build add-in 135 | _TeamCity* 136 | 137 | # DotCover is a Code Coverage Tool 138 | *.dotCover 139 | 140 | # AxoCover is a Code Coverage Tool 141 | .axoCover/* 142 | !.axoCover/settings.json 143 | 144 | # Coverlet is a free, cross platform Code Coverage Tool 145 | coverage*[.json, .xml, .info] 146 | 147 | # Visual Studio code coverage results 148 | *.coverage 149 | *.coveragexml 150 | 151 | # NCrunch 152 | _NCrunch_* 153 | .*crunch*.local.xml 154 | nCrunchTemp_* 155 | 156 | # MightyMoose 157 | *.mm.* 158 | AutoTest.Net/ 159 | 160 | # Web workbench (sass) 161 | .sass-cache/ 162 | 163 | # Installshield output folder 164 | [Ee]xpress/ 165 | 166 | # DocProject is a documentation generator add-in 167 | DocProject/buildhelp/ 168 | DocProject/Help/*.HxT 169 | DocProject/Help/*.HxC 170 | DocProject/Help/*.hhc 171 | DocProject/Help/*.hhk 172 | DocProject/Help/*.hhp 173 | DocProject/Help/Html2 174 | DocProject/Help/html 175 | 176 | # Click-Once directory 177 | publish/ 178 | 179 | # Publish Web Output 180 | *.[Pp]ublish.xml 181 | *.azurePubxml 182 | # Note: Comment the next line if you want to checkin your web deploy settings, 183 | # but database connection strings (with potential passwords) will be unencrypted 184 | *.pubxml 185 | *.publishproj 186 | 187 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 188 | # checkin your Azure Web App publish settings, but sensitive information contained 189 | # in these scripts will be unencrypted 190 | PublishScripts/ 191 | 192 | # NuGet Packages 193 | *.nupkg 194 | # NuGet Symbol Packages 195 | *.snupkg 196 | # The packages folder can be ignored because of Package Restore 197 | **/[Pp]ackages/* 198 | # except build/, which is used as an MSBuild target. 199 | !**/[Pp]ackages/build/ 200 | # Uncomment if necessary however generally it will be regenerated when needed 201 | #!**/[Pp]ackages/repositories.config 202 | # NuGet v3's project.json files produces more ignorable files 203 | *.nuget.props 204 | *.nuget.targets 205 | 206 | # Microsoft Azure Build Output 207 | csx/ 208 | *.build.csdef 209 | 210 | # Microsoft Azure Emulator 211 | ecf/ 212 | rcf/ 213 | 214 | # Windows Store app package directories and files 215 | AppPackages/ 216 | BundleArtifacts/ 217 | Package.StoreAssociation.xml 218 | _pkginfo.txt 219 | *.appx 220 | *.appxbundle 221 | *.appxupload 222 | 223 | # Visual Studio cache files 224 | # files ending in .cache can be ignored 225 | *.[Cc]ache 226 | # but keep track of directories ending in .cache 227 | !?*.[Cc]ache/ 228 | 229 | # Others 230 | ClientBin/ 231 | ~$* 232 | *~ 233 | *.dbmdl 234 | *.dbproj.schemaview 235 | *.jfm 236 | *.pfx 237 | *.publishsettings 238 | orleans.codegen.cs 239 | 240 | # Including strong name files can present a security risk 241 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 242 | #*.snk 243 | 244 | # Since there are multiple workflows, uncomment next line to ignore bower_components 245 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 246 | #bower_components/ 247 | 248 | # RIA/Silverlight projects 249 | Generated_Code/ 250 | 251 | # Backup & report files from converting an old project file 252 | # to a newer Visual Studio version. Backup files are not needed, 253 | # because we have git ;-) 254 | _UpgradeReport_Files/ 255 | Backup*/ 256 | UpgradeLog*.XML 257 | UpgradeLog*.htm 258 | ServiceFabricBackup/ 259 | *.rptproj.bak 260 | 261 | # SQL Server files 262 | *.mdf 263 | *.ldf 264 | *.ndf 265 | 266 | # Business Intelligence projects 267 | *.rdl.data 268 | *.bim.layout 269 | *.bim_*.settings 270 | *.rptproj.rsuser 271 | *- [Bb]ackup.rdl 272 | *- [Bb]ackup ([0-9]).rdl 273 | *- [Bb]ackup ([0-9][0-9]).rdl 274 | 275 | # Microsoft Fakes 276 | FakesAssemblies/ 277 | 278 | # GhostDoc plugin setting file 279 | *.GhostDoc.xml 280 | 281 | # Node.js Tools for Visual Studio 282 | .ntvs_analysis.dat 283 | node_modules/ 284 | 285 | # Visual Studio 6 build log 286 | *.plg 287 | 288 | # Visual Studio 6 workspace options file 289 | *.opt 290 | 291 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 292 | *.vbw 293 | 294 | # Visual Studio LightSwitch build output 295 | **/*.HTMLClient/GeneratedArtifacts 296 | **/*.DesktopClient/GeneratedArtifacts 297 | **/*.DesktopClient/ModelManifest.xml 298 | **/*.Server/GeneratedArtifacts 299 | **/*.Server/ModelManifest.xml 300 | _Pvt_Extensions 301 | 302 | # Paket dependency manager 303 | .paket/paket.exe 304 | paket-files/ 305 | 306 | # FAKE - F# Make 307 | .fake/ 308 | 309 | # CodeRush personal settings 310 | .cr/personal 311 | 312 | # Python Tools for Visual Studio (PTVS) 313 | __pycache__/ 314 | *.pyc 315 | 316 | # Cake - Uncomment if you are using it 317 | # tools/** 318 | # !tools/packages.config 319 | 320 | # Tabs Studio 321 | *.tss 322 | 323 | # Telerik's JustMock configuration file 324 | *.jmconfig 325 | 326 | # BizTalk build output 327 | *.btp.cs 328 | *.btm.cs 329 | *.odx.cs 330 | *.xsd.cs 331 | 332 | # OpenCover UI analysis results 333 | OpenCover/ 334 | 335 | # Azure Stream Analytics local run output 336 | ASALocalRun/ 337 | 338 | # MSBuild Binary and Structured Log 339 | *.binlog 340 | 341 | # NVidia Nsight GPU debugger configuration file 342 | *.nvuser 343 | 344 | # MFractors (Xamarin productivity tool) working folder 345 | .mfractor/ 346 | 347 | # Local History for Visual Studio 348 | .localhistory/ 349 | 350 | # BeatPulse healthcheck temp database 351 | healthchecksdb 352 | 353 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 354 | MigrationBackup/ 355 | 356 | # Ionide (cross platform F# VS Code tools) working folder 357 | .ionide/ 358 | 359 | 360 | # Xcode 361 | # 362 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 363 | 364 | .DS_Store 365 | 366 | ## User settings 367 | xcuserdata/ 368 | 369 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 370 | *.xcscmblueprint 371 | *.xccheckout 372 | 373 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 374 | build/Xcode/build/* 375 | DerivedData/ 376 | *.moved-aside 377 | *.pbxuser 378 | !default.pbxuser 379 | *.mode1v3 380 | !default.mode1v3 381 | *.mode2v3 382 | !default.mode2v3 383 | *.perspectivev3 384 | !default.perspectivev3 385 | 386 | ## Obj-C/Swift specific 387 | *.hmap 388 | 389 | ## App packaging 390 | *.ipa 391 | *.dSYM.zip 392 | *.dSYM 393 | 394 | # CocoaPods 395 | # 396 | # We recommend against adding the Pods directory to your .gitignore. However 397 | # you should judge for yourself, the pros and cons are mentioned at: 398 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 399 | # 400 | # Pods/ 401 | # 402 | # Add this line if you want to avoid checking in source code from the Xcode workspace 403 | # *.xcworkspace 404 | 405 | # Carthage 406 | # 407 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 408 | # Carthage/Checkouts 409 | 410 | Carthage/Build/ 411 | 412 | # fastlane 413 | # 414 | # It is recommended to not store the screenshots in the git repo. 415 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 416 | # For more information about the recommended setup visit: 417 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 418 | 419 | fastlane/report.xml 420 | fastlane/Preview.html 421 | fastlane/screenshots/**/*.png 422 | fastlane/test_output 423 | 424 | # Code Injection 425 | # 426 | # After new code Injection tools there's a generated folder /iOSInjectionProject 427 | # https://github.com/johnno1962/injectionforxcode 428 | 429 | iOSInjectionProject/ 430 | 431 | # Scons 432 | *.o 433 | .sconsign.dblite -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project(raw_pdb) 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 8 | 9 | if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_SIMULATE_ID MATCHES "MSVC") 10 | add_compile_options(-fdeclspec 11 | -fms-extensions) 12 | endif() 13 | 14 | add_subdirectory(src) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright 2011-2022, Molecular Matters GmbH 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RawPDB 2 | 3 | **RawPDB** is a C++11 library that directly reads Microsoft Program DataBase PDB files. The code is extracted almost directly from Live++ 2, a battle-tested hot-reload tool for C++. 4 | 5 | ## Design 6 | 7 | **RawPDB** gives you direct access to the stream data contained in a PDB file. It does not attempt to offer abstractions for iterating symbols, translation units, contributions, etc. 8 | 9 | Building a high-level abstraction over the provided low-level data is an ill-fated attempt that can never really be performant for everybody, because different tools like debuggers, hot-reload tools (e.g. Live++), profilers (e.g. Superluminal), need to perform different queries against the stored data. 10 | 11 | We therefore believe the best solution is to offer direct access to the underlying data, with applications bringing that data into their own structures. 12 | 13 | ## Goal 14 | 15 | Eventually, we want **RawPDB** to become the de-facto replacement of Microsoft's DIA SDK that most C++ developers (have to) use. 16 | 17 | ## Features 18 | 19 | * Fast - **RawPDB** works directly with memory-mapped data, so only the data from the streams you touch affect performance. It is orders of magnitudes faster than the DIA SDK, and faster than comparable LLVM code 20 | * Scalable - **RawPDB's** API gives you access to individual streams that can all be read concurrently in a trivial fashion, since all returned data structures are immutable. There are no locks or waits anywhere inside the library 21 | * Lightweight - **RawPDB** is small and compiles in roughly 1 second 22 | * Allocation-friendly - **RawPDB** performs only a few allocations, and those can be overridden easily by changing the underlying macro 23 | * No STL - **RawPDB** does not need any STL containers or algorithms 24 | * No exceptions - **RawPDB** does not use exceptions 25 | * No RTTI - **RawPDB** does not need RTTI or use class hierarchies 26 | * High-quality code - **RawPDB** compiles clean under -Wall 27 | 28 | ## Building 29 | 30 | The code compiles clean under Visual Studio 2015, 2017, 2019, or 2022. A solution for Visual Studio 2019 is included. 31 | 32 | ## Performance 33 | 34 | Running the **Symbols** and **Contributions** examples on a 1GiB PDB yields the following output: 35 | 36 |
 37 | Opening PDB file C:\Development\llvm-project\build\tools\clang\unittests\Tooling\RelWithDebInfo\ToolingTests.pdb
 38 | 
 39 | Running example "Symbols"
 40 | | Reading image section stream
 41 | | ---> done in 0.066ms
 42 | | Reading module info stream
 43 | | ---> done in 0.562ms
 44 | | Reading symbol record stream
 45 | | ---> done in 25.185ms
 46 | | Reading public symbol stream
 47 | | ---> done in 1.133ms
 48 | | Storing public symbols
 49 | | ---> done in 46.171ms (212023 elements)
 50 | | Reading global symbol stream
 51 | | ---> done in 1.381ms
 52 | | Storing global symbols
 53 | | ---> done in 12.769ms (448957 elements)
 54 | | Storing symbols from modules
 55 | | ---> done in 145.849ms (2243 elements)
 56 | ---> done in 233.694ms (539611 elements)
 57 | 
58 | 59 |
 60 | Opening PDB file C:\Development\llvm-project\build\tools\clang\unittests\Tooling\RelWithDebInfo\ToolingTests.pdb
 61 | 
 62 | Running example "Contributions"
 63 | | Reading image section stream
 64 | | ---> done in 0.066ms
 65 | | Reading module info stream
 66 | | ---> done in 0.594ms
 67 | | Reading section contribution stream
 68 | | ---> done in 9.839ms
 69 | | Storing contributions
 70 | | ---> done in 67.346ms (630924 elements)
 71 | | std::sort contributions
 72 | | ---> done in 19.218ms
 73 | ---> done in 97.283ms
 74 | 20 largest contributions:
 75 | 1: 1896496 bytes from LLVMAMDGPUCodeGen.dir\RelWithDebInfo\AMDGPUInstructionSelector.obj
 76 | 2: 1700720 bytes from LLVMHexagonCodeGen.dir\RelWithDebInfo\HexagonInstrInfo.obj
 77 | 3: 1536470 bytes from LLVMRISCVCodeGen.dir\RelWithDebInfo\RISCVISelDAGToDAG.obj
 78 | 4: 1441408 bytes from LLVMAArch64CodeGen.dir\RelWithDebInfo\AArch64InstructionSelector.obj
 79 | 5: 1187048 bytes from LLVMRISCVCodeGen.dir\RelWithDebInfo\RISCVInstructionSelector.obj
 80 | 6: 1026504 bytes from LLVMARMCodeGen.dir\RelWithDebInfo\ARMInstructionSelector.obj
 81 | 7: 952080 bytes from LLVMAMDGPUDesc.dir\RelWithDebInfo\AMDGPUMCTargetDesc.obj
 82 | 8: 849888 bytes from LLVMX86Desc.dir\RelWithDebInfo\X86MCTargetDesc.obj
 83 | 9: 712176 bytes from LLVMHexagonCodeGen.dir\RelWithDebInfo\HexagonInstrInfo.obj
 84 | 10: 679035 bytes from LLVMX86CodeGen.dir\RelWithDebInfo\X86ISelDAGToDAG.obj
 85 | 11: 525174 bytes from LLVMAMDGPUDesc.dir\RelWithDebInfo\AMDGPUMCTargetDesc.obj
 86 | 12: 523035 bytes from * Linker *
 87 | 13: 519312 bytes from LLVMRISCVDesc.dir\RelWithDebInfo\RISCVMCTargetDesc.obj
 88 | 14: 512496 bytes from LLVMVEDesc.dir\RelWithDebInfo\VEMCTargetDesc.obj
 89 | 15: 498768 bytes from LLVMX86CodeGen.dir\RelWithDebInfo\X86InstructionSelector.obj
 90 | 16: 483528 bytes from LLVMMipsCodeGen.dir\RelWithDebInfo\MipsInstructionSelector.obj
 91 | 17: 449472 bytes from LLVMAMDGPUCodeGen.dir\RelWithDebInfo\AMDGPUISelDAGToDAG.obj
 92 | 18: 444246 bytes from C:\Development\llvm-project\build\tools\clang\lib\Basic\obj.clangBasic.dir\RelWithDebInfo\DiagnosticIDs.obj
 93 | 19: 371584 bytes from LLVMAArch64CodeGen.dir\RelWithDebInfo\AArch64ISelDAGToDAG.obj
 94 | 20: 370272 bytes from LLVMNVPTXDesc.dir\RelWithDebInfo\NVPTXMCTargetDesc.obj
 95 | 
96 | 97 | This is at least an order of magnitude faster than DIA, even though the example code is completely serial and uses std::vector, std::string, and std::sort, which are used for illustration purposes only. 98 | 99 | When reading streams in a concurrent fashion, you will most likely be limited by the speed at which the OS can bring the data into your process. 100 | 101 | Running the **Lines** example on a 1.37 GiB PDB yields the following output: 102 | 103 |
104 | 
105 | Opening PDB file C:\pdb-test-files\clang-debug.pdb
106 | Version 20000404, signature 1658696914, age 1, GUID 563dd8f1-f32b-459b-8c2beae0e70bc19b
107 | 
108 | Running example "Lines"
109 | | Reading image section stream
110 | | ---> done in 0.313ms
111 | | Reading module info stream
112 | | ---> done in 0.403ms
113 | | Reading names stream
114 | | ---> done in 0.126ms
115 | | Storing lines from modules
116 | | ---> done in 306.720ms (1847 elements)
117 | | std::sort sections
118 | | ---> done in 103.090ms (4023680 elements)
119 | 
120 | 
121 | 122 | ## Supported streams 123 | 124 | **RawPDB** gives you access to the following PDB stream data: 125 | 126 | * DBI stream data 127 | * Public symbols 128 | * Global symbols 129 | * Modules 130 | * Module symbols 131 | * Module lines (C13 line information) 132 | * Image sections 133 | * Info stream 134 | * "/names" stream 135 | * Section contributions 136 | * Source files 137 | 138 | * IPI stream data 139 | 140 | * TPI stream data 141 | 142 | Furthermore, PDBs linked using /DEBUG:FASTLINK are not supported. These PDBs do not contain much information, since private symbol information is distributed among object files and library files. 143 | 144 | ## Documentation 145 | 146 | If you are unfamiliar with the basic structure of a PDB file, the LLVM documentation serves as a good introduction. 147 | 148 | Consult the example code to see how to read and parse the PDB streams. 149 | 150 | ## Directory structure 151 | 152 | * bin: contains final binary output files (.exe and .pdb) 153 | * build: contains Visual Studio 2019 solution and project files 154 | * lib: contains the RawPDB library output files (.lib and .pdb) 155 | * src: contains the RawPDB source code, as well as example code 156 | * temp: contains intermediate build artefacts 157 | 158 | ## Examples 159 | 160 | ### Symbols (ExampleSymbols.cpp) 161 | 162 | A basic example that shows how to load symbols from public, global, and module streams. 163 | 164 | ### Contributions (ExampleContributions.cpp) 165 | 166 | A basic example that shows how to load contributions, sort them by size, and output the 20 largest ones along with the object file they originated from. 167 | 168 | ### Function symbols (ExampleFunctionSymbols.cpp) 169 | 170 | An example intended for profiler developers that shows how to enumerate all function symbols and retrieve or compute their code size. 171 | 172 | ### Function variables (ExampleFunctionVariables.cpp) 173 | 174 | An example intended for debugger developers that shows how to enumerate all function records needed for displaying function variables. 175 | 176 | ### Lines (ExampleLines.cpp) 177 | 178 | An example that shows to how to load line information for all modules. 179 | 180 | ### Types (ExampleTypes.cpp) 181 | 182 | An example that prints all type records. 183 | 184 | ### PDBSize (ExamplePDBSize.cpp) 185 | 186 | An example that could serve as a starting point for people wanting to investigate and optimize the size of their PDBs. 187 | 188 | ## Sponsoring or supporting RawPDB 189 | 190 | We have chosen a very liberal license to let **RawPDB** be used in as many scenarios as possible, including commercial applications. If you would like to support its development, consider licensing Live++ instead. Not only do you give something back, but get a great productivity enhancement on top! 191 | -------------------------------------------------------------------------------- /build/Examples.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Create 24 | Examples_PCH.h 25 | Create 26 | Examples_PCH.h 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 15.0 40 | {A2570A81-BAD3-4E84-9180-FC4900D67E52} 41 | ExampleSymbols 42 | 10.0 43 | Examples 44 | 45 | 46 | 47 | Application 48 | true 49 | v142 50 | Unicode 51 | 52 | 53 | Application 54 | false 55 | v142 56 | true 57 | Unicode 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | $(ProjectDir)..\bin\$(PlatformName)\$(Configuration)\ 72 | $(ProjectDir)..\temp\$(PlatformName)\$(Configuration)\$(ProjectName)\ 73 | Examples 74 | NativeRecommendedRules.ruleset 75 | 76 | 77 | false 78 | $(ProjectDir)..\bin\$(PlatformName)\$(Configuration)\ 79 | $(ProjectDir)..\temp\$(PlatformName)\$(Configuration)\$(ProjectName)\ 80 | Examples 81 | NativeRecommendedRules.ruleset 82 | 83 | 84 | 85 | EnableAllWarnings 86 | Disabled 87 | _DEBUG;_WINDOWS;%(PreprocessorDefinitions) 88 | ProgramDatabase 89 | false 90 | MultiThreadedDebug 91 | true 92 | true 93 | false 94 | false 95 | false 96 | true 97 | false 98 | EnableFastChecks 99 | ..\src 100 | true 101 | stdcpp14 102 | /Gw /Zc:__cplusplus /wd4577 %(AdditionalOptions) 103 | true 104 | false 105 | Guard 106 | false 107 | true 108 | Use 109 | Examples_PCH.h 110 | 111 | 112 | Console 113 | DebugFull 114 | RawPDB.lib;%(AdditionalDependencies) 115 | true 116 | $(OutDir)$(TargetName).pdb 117 | NotSet 118 | true 119 | ..\lib\x64\Debug 120 | 121 | 122 | 123 | 124 | EnableAllWarnings 125 | MaxSpeed 126 | true 127 | true 128 | NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 129 | AnySuitable 130 | true 131 | Fast 132 | false 133 | true 134 | false 135 | false 136 | false 137 | false 138 | false 139 | ..\src 140 | /Gw /Zc:__cplusplus /wd4577 %(AdditionalOptions) 141 | MultiThreaded 142 | true 143 | true 144 | true 145 | stdcpp14 146 | false 147 | false 148 | true 149 | Use 150 | Examples_PCH.h 151 | 152 | 153 | Console 154 | true 155 | true 156 | DebugFull 157 | ..\lib\x64\Release 158 | RawPDB.lib;%(AdditionalDependencies) 159 | true 160 | $(OutDir)$(TargetName).pdb 161 | NotSet 162 | true 163 | UseLinkTimeCodeGeneration 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /build/Examples.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {f1ba9862-aac4-4f1d-9094-4a0936820acd} 6 | 7 | 8 | 9 | 10 | Source Files 11 | 12 | 13 | Source Files 14 | 15 | 16 | Source Files 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | -------------------------------------------------------------------------------- /build/RawPDB.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32126.315 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Library", "Library", "{9DA9F6DF-458B-4921-82BE-F0B6CCCB2A65}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RawPDB", "RawPDB.vcxproj", "{FBE3DBFA-20A7-4F99-9326-ED82C8B7B910}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Examples", "Examples.vcxproj", "{A2570A81-BAD3-4E84-9180-FC4900D67E52}" 11 | ProjectSection(ProjectDependencies) = postProject 12 | {FBE3DBFA-20A7-4F99-9326-ED82C8B7B910} = {FBE3DBFA-20A7-4F99-9326-ED82C8B7B910} 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|x64 = Debug|x64 18 | Release|x64 = Release|x64 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {FBE3DBFA-20A7-4F99-9326-ED82C8B7B910}.Debug|x64.ActiveCfg = Debug|x64 22 | {FBE3DBFA-20A7-4F99-9326-ED82C8B7B910}.Debug|x64.Build.0 = Debug|x64 23 | {FBE3DBFA-20A7-4F99-9326-ED82C8B7B910}.Release|x64.ActiveCfg = Release|x64 24 | {FBE3DBFA-20A7-4F99-9326-ED82C8B7B910}.Release|x64.Build.0 = Release|x64 25 | {A2570A81-BAD3-4E84-9180-FC4900D67E52}.Debug|x64.ActiveCfg = Debug|x64 26 | {A2570A81-BAD3-4E84-9180-FC4900D67E52}.Debug|x64.Build.0 = Debug|x64 27 | {A2570A81-BAD3-4E84-9180-FC4900D67E52}.Release|x64.ActiveCfg = Release|x64 28 | {A2570A81-BAD3-4E84-9180-FC4900D67E52}.Release|x64.Build.0 = Release|x64 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | GlobalSection(NestedProjects) = preSolution 34 | {FBE3DBFA-20A7-4F99-9326-ED82C8B7B910} = {9DA9F6DF-458B-4921-82BE-F0B6CCCB2A65} 35 | EndGlobalSection 36 | GlobalSection(ExtensibilityGlobals) = postSolution 37 | SolutionGuid = {CBA7451C-D19B-44B8-A568-D76727048631} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /build/RawPDB.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {230ed13c-dce7-4e9f-be4d-7f677b87f1e0} 6 | 7 | 8 | {57dcde6f-1d23-4eec-b0a4-8e232ef2cc64} 9 | 10 | 11 | 12 | 13 | Source Files 14 | 15 | 16 | Source Files 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files 53 | 54 | 55 | Source Files 56 | 57 | 58 | Source Files 59 | 60 | 61 | Source Files 62 | 63 | 64 | Source Files 65 | 66 | 67 | Source Files 68 | 69 | 70 | Source Files 71 | 72 | 73 | 74 | 75 | Source Files 76 | 77 | 78 | Source Files 79 | 80 | 81 | Source Files 82 | 83 | 84 | Source Files 85 | 86 | 87 | Source Files 88 | 89 | 90 | Source Files 91 | 92 | 93 | Source Files 94 | 95 | 96 | Source Files 97 | 98 | 99 | Source Files 100 | 101 | 102 | Source Files 103 | 104 | 105 | Source Files 106 | 107 | 108 | Source Files 109 | 110 | 111 | Source Files 112 | 113 | 114 | Source Files 115 | 116 | 117 | Source Files 118 | 119 | 120 | Source Files 121 | 122 | 123 | Source Files 124 | 125 | 126 | Source Files 127 | 128 | 129 | Source Files 130 | 131 | 132 | Source Files\Foundation 133 | 134 | 135 | Source Files\Foundation 136 | 137 | 138 | Source Files\Foundation 139 | 140 | 141 | Source Files\Foundation 142 | 143 | 144 | Source Files\Foundation 145 | 146 | 147 | Source Files\Foundation 148 | 149 | 150 | Source Files\Foundation 151 | 152 | 153 | Source Files\Foundation 154 | 155 | 156 | Source Files\Foundation 157 | 158 | 159 | Source Files\Foundation 160 | 161 | 162 | Source Files\Foundation 163 | 164 | 165 | Source Files\Foundation 166 | 167 | 168 | Source Files\Foundation 169 | 170 | 171 | Source Files\Foundation 172 | 173 | 174 | Source Files 175 | 176 | 177 | Source Files 178 | 179 | 180 | Source Files 181 | 182 | 183 | Source Files 184 | 185 | 186 | Source Files 187 | 188 | 189 | -------------------------------------------------------------------------------- /raw_pdb.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ size={m_length} }} 5 | 6 | 7 | m_length 8 | m_data 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES 2 | Foundation/PDB_ArrayView.h 3 | Foundation/PDB_Assert.h 4 | Foundation/PDB_BitOperators.h 5 | Foundation/PDB_BitUtil.h 6 | Foundation/PDB_DisableWarningsPop.h 7 | Foundation/PDB_DisableWarningsPush.h 8 | Foundation/PDB_Forward.h 9 | Foundation/PDB_Log.h 10 | Foundation/PDB_Macros.h 11 | Foundation/PDB_Memory.h 12 | Foundation/PDB_Move.h 13 | Foundation/PDB_Platform.h 14 | Foundation/PDB_PointerUtil.h 15 | Foundation/PDB_Warnings.h 16 | 17 | PDB.cpp 18 | PDB.h 19 | PDB_CoalescedMSFStream.cpp 20 | PDB_CoalescedMSFStream.h 21 | PDB_DBIStream.cpp 22 | PDB_DBIStream.h 23 | PDB_DBITypes.cpp 24 | PDB_DBITypes.h 25 | PDB_DirectMSFStream.cpp 26 | PDB_DirectMSFStream.h 27 | PDB_ErrorCodes.h 28 | PDB_GlobalSymbolStream.cpp 29 | PDB_GlobalSymbolStream.h 30 | PDB_ImageSectionStream.cpp 31 | PDB_ImageSectionStream.h 32 | PDB_InfoStream.cpp 33 | PDB_InfoStream.h 34 | PDB_IPIStream.cpp 35 | PDB_IPIStream.h 36 | PDB_IPITypes.h 37 | PDB_ModuleInfoStream.cpp 38 | PDB_ModuleInfoStream.h 39 | PDB_ModuleLineStream.cpp 40 | PDB_ModuleLineStream.h 41 | PDB_ModuleSymbolStream.cpp 42 | PDB_ModuleSymbolStream.h 43 | PDB_NamesStream.cpp 44 | PDB_NamesStream.h 45 | PDB_PCH.cpp 46 | PDB_PCH.h 47 | PDB_PublicSymbolStream.cpp 48 | PDB_PublicSymbolStream.h 49 | PDB_RawFile.cpp 50 | PDB_RawFile.h 51 | PDB_SectionContributionStream.cpp 52 | PDB_SectionContributionStream.h 53 | PDB_SourceFileStream.cpp 54 | PDB_SourceFileStream.h 55 | PDB_TPIStream.cpp 56 | PDB_TPIStream.h 57 | PDB_TPITypes.h 58 | PDB_Types.cpp 59 | PDB_Types.h 60 | PDB_Util.h 61 | ) 62 | 63 | source_group(src FILES 64 | ${SOURCES} 65 | ) 66 | 67 | add_library(raw_pdb 68 | ${SOURCES} 69 | ) 70 | 71 | target_include_directories(raw_pdb 72 | PUBLIC 73 | . 74 | ) 75 | 76 | target_precompile_headers(raw_pdb 77 | PRIVATE 78 | PDB_PCH.h 79 | ) 80 | 81 | option(RAWPDB_BUILD_EXAMPLES "Build Examples" ON) 82 | 83 | if (RAWPDB_BUILD_EXAMPLES) 84 | add_subdirectory(Examples) 85 | endif() 86 | 87 | if (UNIX) 88 | include(GNUInstallDirs) 89 | 90 | install( 91 | TARGETS raw_pdb 92 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 93 | ) 94 | 95 | file(GLOB_RECURSE HEADER_FILES 96 | "${CMAKE_CURRENT_SOURCE_DIR}/*.h" 97 | ) 98 | 99 | file(GLOB_RECURSE HEADER_FILES_FOUNDATION 100 | "${CMAKE_CURRENT_SOURCE_DIR}/Foundation/*.h" 101 | ) 102 | 103 | install( 104 | FILES ${HEADER_FILES} 105 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/raw_pdb/" 106 | ) 107 | 108 | install( 109 | FILES ${HEADER_FILES_FOUNDATION} 110 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/raw_pdb/Foundation" 111 | ) 112 | endif (UNIX) 113 | -------------------------------------------------------------------------------- /src/Examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Examples) 2 | 3 | set(SOURCES 4 | ExampleContributions.cpp 5 | ExampleFunctionSymbols.cpp 6 | ExampleFunctionVariables.cpp 7 | ExampleLines.cpp 8 | ExampleMain.cpp 9 | ExampleMemoryMappedFile.cpp 10 | ExampleMemoryMappedFile.h 11 | ExamplePDBSize.cpp 12 | Examples_PCH.cpp 13 | Examples_PCH.h 14 | ExampleSymbols.cpp 15 | ExampleTypes.cpp 16 | ExampleTimedScope.cpp 17 | ExampleTimedScope.h 18 | ExampleTypeTable.cpp 19 | ExampleTypeTable.h 20 | ) 21 | 22 | source_group(src FILES 23 | ${SOURCES} 24 | ) 25 | 26 | add_executable(Examples 27 | ${SOURCES} 28 | ) 29 | 30 | target_link_libraries(Examples 31 | PUBLIC 32 | raw_pdb 33 | ) 34 | 35 | target_precompile_headers(Examples 36 | PUBLIC 37 | Examples_PCH.h 38 | ) -------------------------------------------------------------------------------- /src/Examples/ExampleContributions.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "Examples_PCH.h" 5 | #include "ExampleTimedScope.h" 6 | #include "PDB_RawFile.h" 7 | #include "PDB_DBIStream.h" 8 | 9 | 10 | namespace 11 | { 12 | // we don't have to store std::string in the contributions, since all the data is memory-mapped anyway. 13 | // we do it in this example to ensure that we don't "cheat" when reading the PDB file. memory-mapped data will only 14 | // be faulted into the process once it's touched, so actually copying the string data makes us touch the needed data, 15 | // giving us a real performance measurement. 16 | struct Contribution 17 | { 18 | std::string objectFile; 19 | uint32_t rva; 20 | uint32_t size; 21 | }; 22 | } 23 | 24 | 25 | void ExampleContributions(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream); 26 | void ExampleContributions(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream) 27 | { 28 | TimedScope total("\nRunning example \"Contributions\""); 29 | 30 | // in order to keep the example easy to understand, we load the PDB data serially. 31 | // note that this can be improved a lot by reading streams concurrently. 32 | 33 | // prepare the image section stream first. it is needed for converting section + offset into an RVA 34 | TimedScope sectionScope("Reading image section stream"); 35 | const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile); 36 | sectionScope.Done(); 37 | 38 | 39 | // prepare the module info stream for matching contributions against files 40 | TimedScope moduleScope("Reading module info stream"); 41 | const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile); 42 | moduleScope.Done(); 43 | 44 | 45 | // read contribution stream 46 | TimedScope contributionScope("Reading section contribution stream"); 47 | const PDB::SectionContributionStream sectionContributionStream = dbiStream.CreateSectionContributionStream(rawPdbFile); 48 | contributionScope.Done(); 49 | 50 | std::vector contributions; 51 | { 52 | TimedScope scope("Storing contributions"); 53 | 54 | const PDB::ArrayView sectionContributions = sectionContributionStream.GetContributions(); 55 | const size_t count = sectionContributions.GetLength(); 56 | 57 | contributions.reserve(count); 58 | 59 | for (const PDB::DBI::SectionContribution& contribution : sectionContributions) 60 | { 61 | const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(contribution.section, contribution.offset); 62 | if (rva == 0u) 63 | { 64 | printf("Contribution has invalid RVA\n"); 65 | continue; 66 | } 67 | 68 | const PDB::ModuleInfoStream::Module& module = moduleInfoStream.GetModule(contribution.moduleIndex); 69 | 70 | contributions.push_back(Contribution { module.GetName().Decay(), rva, contribution.size }); 71 | } 72 | 73 | scope.Done(count); 74 | } 75 | 76 | TimedScope sortScope("std::sort contributions"); 77 | std::sort(contributions.begin(), contributions.end(), [](const Contribution& lhs, const Contribution& rhs) 78 | { 79 | return lhs.size > rhs.size; 80 | }); 81 | sortScope.Done(); 82 | 83 | total.Done(); 84 | 85 | // log the 20 largest contributions 86 | { 87 | printf("20 largest contributions:\n"); 88 | 89 | const size_t countToShow = std::min(20ul, contributions.size()); 90 | for (size_t i = 0u; i < countToShow; ++i) 91 | { 92 | const Contribution& contribution = contributions[i]; 93 | printf("%zu: %u bytes from %s\n", i + 1u, contribution.size, contribution.objectFile.c_str()); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Examples/ExampleMain.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "Examples_PCH.h" 5 | #include "ExampleMemoryMappedFile.h" 6 | #include "PDB.h" 7 | #include "PDB_RawFile.h" 8 | #include "PDB_InfoStream.h" 9 | #include "PDB_DBIStream.h" 10 | #include "PDB_TPIStream.h" 11 | #include "PDB_IPIStream.h" 12 | #include "PDB_NamesStream.h" 13 | 14 | namespace 15 | { 16 | PDB_NO_DISCARD static bool IsError(PDB::ErrorCode errorCode) 17 | { 18 | switch (errorCode) 19 | { 20 | case PDB::ErrorCode::Success: 21 | return false; 22 | 23 | case PDB::ErrorCode::InvalidSuperBlock: 24 | printf("Invalid Superblock\n"); 25 | return true; 26 | 27 | case PDB::ErrorCode::InvalidFreeBlockMap: 28 | printf("Invalid free block map\n"); 29 | return true; 30 | 31 | case PDB::ErrorCode::InvalidStream: 32 | printf("Invalid stream\n"); 33 | return true; 34 | 35 | case PDB::ErrorCode::InvalidSignature: 36 | printf("Invalid stream signature\n"); 37 | return true; 38 | 39 | case PDB::ErrorCode::InvalidStreamIndex: 40 | printf("Invalid stream index\n"); 41 | return true; 42 | 43 | case PDB::ErrorCode::InvalidDataSize: 44 | printf("Invalid data size\n"); 45 | return true; 46 | 47 | case PDB::ErrorCode::UnknownVersion: 48 | printf("Unknown version\n"); 49 | return true; 50 | } 51 | 52 | // only ErrorCode::Success means there wasn't an error, so all other paths have to assume there was an error 53 | return true; 54 | } 55 | 56 | PDB_NO_DISCARD static bool HasValidDBIStreams(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream) 57 | { 58 | // check whether the DBI stream offers all sub-streams we need 59 | if (IsError(dbiStream.HasValidSymbolRecordStream(rawPdbFile))) 60 | { 61 | return false; 62 | } 63 | 64 | if (IsError(dbiStream.HasValidPublicSymbolStream(rawPdbFile))) 65 | { 66 | return false; 67 | } 68 | 69 | if (IsError(dbiStream.HasValidGlobalSymbolStream(rawPdbFile))) 70 | { 71 | return false; 72 | } 73 | 74 | if (IsError(dbiStream.HasValidSectionContributionStream(rawPdbFile))) 75 | { 76 | return false; 77 | } 78 | 79 | if (IsError(dbiStream.HasValidImageSectionStream(rawPdbFile))) 80 | { 81 | return false; 82 | } 83 | 84 | return true; 85 | } 86 | } 87 | 88 | 89 | // declare all examples 90 | extern void ExamplePDBSize(const PDB::RawFile&, const PDB::DBIStream&); 91 | extern void ExampleTPISize(const PDB::TPIStream& tpiStream, const char* outPath); 92 | extern void ExampleContributions(const PDB::RawFile&, const PDB::DBIStream&); 93 | extern void ExampleSymbols(const PDB::RawFile&, const PDB::DBIStream&); 94 | extern void ExampleFunctionSymbols(const PDB::RawFile&, const PDB::DBIStream&); 95 | extern void ExampleFunctionVariables(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::TPIStream&); 96 | extern void ExampleLines(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::InfoStream& infoStream); 97 | extern void ExampleTypes(const PDB::TPIStream&); 98 | 99 | int main(int argc, char** argv) 100 | { 101 | if (argc != 2) 102 | { 103 | printf("Usage: Examples \nError: Incorrect usage\n"); 104 | 105 | return 1; 106 | } 107 | 108 | printf("Opening PDB file %s\n", argv[1]); 109 | 110 | // try to open the PDB file and check whether all the data we need is available 111 | MemoryMappedFile::Handle pdbFile = MemoryMappedFile::Open(argv[1]); 112 | if (!pdbFile.baseAddress) 113 | { 114 | printf("Cannot memory-map file %s\n", argv[1]); 115 | 116 | return 1; 117 | } 118 | 119 | if (IsError(PDB::ValidateFile(pdbFile.baseAddress, pdbFile.len))) 120 | { 121 | MemoryMappedFile::Close(pdbFile); 122 | 123 | return 2; 124 | } 125 | 126 | const PDB::RawFile rawPdbFile = PDB::CreateRawFile(pdbFile.baseAddress); 127 | if (IsError(PDB::HasValidDBIStream(rawPdbFile))) 128 | { 129 | MemoryMappedFile::Close(pdbFile); 130 | 131 | return 3; 132 | } 133 | 134 | const PDB::InfoStream infoStream(rawPdbFile); 135 | if (infoStream.UsesDebugFastLink()) 136 | { 137 | printf("PDB was linked using unsupported option /DEBUG:FASTLINK\n"); 138 | 139 | MemoryMappedFile::Close(pdbFile); 140 | 141 | return 4; 142 | } 143 | 144 | const auto h = infoStream.GetHeader(); 145 | printf("Version %u, signature %u, age %u, GUID %08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x\n", 146 | static_cast(h->version), h->signature, h->age, 147 | h->guid.Data1, h->guid.Data2, h->guid.Data3, 148 | h->guid.Data4[0], h->guid.Data4[1], h->guid.Data4[2], h->guid.Data4[3], h->guid.Data4[4], h->guid.Data4[5], h->guid.Data4[6], h->guid.Data4[7]); 149 | 150 | const PDB::DBIStream dbiStream = PDB::CreateDBIStream(rawPdbFile); 151 | if (!HasValidDBIStreams(rawPdbFile, dbiStream)) 152 | { 153 | MemoryMappedFile::Close(pdbFile); 154 | 155 | return 5; 156 | } 157 | 158 | if (IsError(PDB::HasValidTPIStream(rawPdbFile))) 159 | { 160 | MemoryMappedFile::Close(pdbFile); 161 | 162 | return 5; 163 | } 164 | const PDB::TPIStream tpiStream = PDB::CreateTPIStream(rawPdbFile); 165 | 166 | { 167 | // It's perfectly possible that an old PDB does not have an IPI stream. 168 | // It's not necessarily an error. You can also check the InfoStream for whether 169 | // the PDB should have an IPI stream at all. 170 | PDB::ErrorCode error = PDB::HasValidIPIStream(rawPdbFile); 171 | if (error != PDB::ErrorCode::InvalidStream && IsError(error)) 172 | { 173 | MemoryMappedFile::Close(pdbFile); 174 | 175 | return 5; 176 | } 177 | } 178 | 179 | // run all examples 180 | ExamplePDBSize(rawPdbFile, dbiStream); 181 | ExampleContributions(rawPdbFile, dbiStream); 182 | ExampleSymbols(rawPdbFile, dbiStream); 183 | ExampleFunctionSymbols(rawPdbFile, dbiStream); 184 | ExampleFunctionVariables(rawPdbFile, dbiStream, tpiStream); 185 | ExampleLines(rawPdbFile, dbiStream, infoStream); 186 | ExampleTypes(tpiStream); 187 | // uncomment to dump type sizes to a CSV 188 | // ExampleTPISize(tpiStream, "output.csv"); 189 | 190 | MemoryMappedFile::Close(pdbFile); 191 | 192 | return 0; 193 | } 194 | -------------------------------------------------------------------------------- /src/Examples/ExampleMemoryMappedFile.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "Examples_PCH.h" 5 | #include "ExampleMemoryMappedFile.h" 6 | 7 | 8 | MemoryMappedFile::Handle MemoryMappedFile::Open(const char* path) 9 | { 10 | #ifdef _WIN32 11 | void* file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, nullptr); 12 | 13 | if (file == INVALID_HANDLE_VALUE) 14 | { 15 | return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 }; 16 | } 17 | 18 | void* fileMapping = CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, nullptr); 19 | 20 | if (fileMapping == nullptr) 21 | { 22 | CloseHandle(file); 23 | 24 | return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 }; 25 | } 26 | 27 | void* baseAddress = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0); 28 | 29 | if (baseAddress == nullptr) 30 | { 31 | CloseHandle(fileMapping); 32 | CloseHandle(file); 33 | 34 | return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 }; 35 | } 36 | 37 | BY_HANDLE_FILE_INFORMATION fileInformation; 38 | const bool getInformationResult = GetFileInformationByHandle(file, &fileInformation); 39 | if (!getInformationResult) 40 | { 41 | UnmapViewOfFile(baseAddress); 42 | CloseHandle(fileMapping); 43 | CloseHandle(file); 44 | 45 | return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 }; 46 | } 47 | 48 | const size_t fileSizeHighBytes = static_cast(fileInformation.nFileSizeHigh) << 32; 49 | const size_t fileSizeLowBytes = fileInformation.nFileSizeLow; 50 | const size_t fileSize = fileSizeHighBytes | fileSizeLowBytes; 51 | return Handle { file, fileMapping, baseAddress, fileSize }; 52 | #else 53 | struct stat fileSb; 54 | 55 | int file = open(path, O_RDONLY); 56 | 57 | if (file == INVALID_HANDLE_VALUE) 58 | { 59 | return Handle { INVALID_HANDLE_VALUE, nullptr, 0 }; 60 | } 61 | 62 | if (fstat(file, &fileSb) == -1) 63 | { 64 | close(file); 65 | 66 | return Handle { INVALID_HANDLE_VALUE, nullptr, 0 }; 67 | } 68 | 69 | void* baseAddress = mmap(nullptr, fileSb.st_size, PROT_READ, MAP_PRIVATE, file, 0); 70 | 71 | if (baseAddress == MAP_FAILED) 72 | { 73 | close(file); 74 | 75 | return Handle { INVALID_HANDLE_VALUE, nullptr, 0 }; 76 | } 77 | 78 | return Handle { file, baseAddress, static_cast(fileSb.st_size) }; 79 | #endif 80 | } 81 | 82 | 83 | void MemoryMappedFile::Close(Handle& handle) 84 | { 85 | #ifdef _WIN32 86 | UnmapViewOfFile(handle.baseAddress); 87 | CloseHandle(handle.fileMapping); 88 | CloseHandle(handle.file); 89 | 90 | handle.file = nullptr; 91 | handle.fileMapping = nullptr; 92 | #else 93 | munmap(handle.baseAddress, handle.len); 94 | close(handle.file); 95 | 96 | handle.file = 0; 97 | #endif 98 | 99 | handle.baseAddress = nullptr; 100 | } 101 | -------------------------------------------------------------------------------- /src/Examples/ExampleMemoryMappedFile.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #ifndef _WIN32 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define INVALID_HANDLE_VALUE ((long)-1) 11 | #endif 12 | 13 | namespace MemoryMappedFile 14 | { 15 | struct Handle 16 | { 17 | #ifdef _WIN32 18 | void* file; 19 | void* fileMapping; 20 | #else 21 | int file; 22 | #endif 23 | void* baseAddress; 24 | size_t len; 25 | }; 26 | 27 | Handle Open(const char* path); 28 | void Close(Handle& handle); 29 | } 30 | -------------------------------------------------------------------------------- /src/Examples/ExamplePDBSize.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "Examples_PCH.h" 5 | #include "ExampleTimedScope.h" 6 | #include "PDB_RawFile.h" 7 | #include "PDB_DBIStream.h" 8 | 9 | 10 | namespace 11 | { 12 | struct Stream 13 | { 14 | std::string name; 15 | uint32_t size; 16 | }; 17 | } 18 | 19 | 20 | void ExamplePDBSize(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream); 21 | void ExamplePDBSize(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream) 22 | { 23 | TimedScope total("\nRunning example \"PDBSize\""); 24 | 25 | std::vector streams; 26 | 27 | // print show general statistics 28 | printf("General\n"); 29 | printf("-------\n"); 30 | { 31 | const PDB::SuperBlock* superBlock = rawPdbFile.GetSuperBlock(); 32 | printf("PDB page size (block size): %u\n", superBlock->blockSize); 33 | printf("PDB block count: %u\n", superBlock->blockCount); 34 | 35 | const size_t rawSize = static_cast(superBlock->blockSize) * static_cast(superBlock->blockCount); 36 | printf("PDB raw size: %zu MiB (%zu GiB)\n", rawSize >> 20u, rawSize >> 30u); 37 | } 38 | 39 | // print the sizes of all known streams 40 | printf("\n"); 41 | printf("Sizes of known streams\n"); 42 | printf("----------------------\n"); 43 | { 44 | const uint32_t streamCount = rawPdbFile.GetStreamCount(); 45 | const uint32_t tpiStreamSize = (streamCount > 2u) ? rawPdbFile.GetStreamSize(2u) : 0u; 46 | const uint32_t dbiStreamSize = (streamCount > 3u) ? rawPdbFile.GetStreamSize(3u) : 0u; 47 | const uint32_t ipiStreamSize = (streamCount > 4u) ? rawPdbFile.GetStreamSize(4u) : 0u; 48 | 49 | printf("TPI stream size: %u KiB (%u MiB)\n", tpiStreamSize >> 10u, tpiStreamSize >> 20u); 50 | printf("DBI stream size: %u KiB (%u MiB)\n", dbiStreamSize >> 10u, dbiStreamSize >> 20u); 51 | printf("IPI stream size: %u KiB (%u MiB)\n", ipiStreamSize >> 10u, ipiStreamSize >> 20u); 52 | 53 | streams.push_back(Stream { "TPI", tpiStreamSize }); 54 | streams.push_back(Stream { "DBI", dbiStreamSize }); 55 | streams.push_back(Stream { "IPI", ipiStreamSize }); 56 | 57 | const uint32_t globalSymbolStreamSize = rawPdbFile.GetStreamSize(dbiStream.GetHeader().globalStreamIndex); 58 | const uint32_t publicSymbolStreamSize = rawPdbFile.GetStreamSize(dbiStream.GetHeader().publicStreamIndex); 59 | const uint32_t symbolRecordStreamSize = rawPdbFile.GetStreamSize(dbiStream.GetHeader().symbolRecordStreamIndex); 60 | 61 | printf("Global symbol stream size: %u KiB (%u MiB)\n", globalSymbolStreamSize >> 10u, globalSymbolStreamSize >> 20u); 62 | printf("Public symbol stream size: %u KiB (%u MiB)\n", publicSymbolStreamSize >> 10u, publicSymbolStreamSize >> 20u); 63 | printf("Symbol record stream size: %u KiB (%u MiB)\n", symbolRecordStreamSize >> 10u, symbolRecordStreamSize >> 20u); 64 | 65 | streams.emplace_back(Stream { "Global", globalSymbolStreamSize }); 66 | streams.emplace_back(Stream { "Public", publicSymbolStreamSize }); 67 | streams.emplace_back(Stream { "Symbol", symbolRecordStreamSize }); 68 | } 69 | 70 | // print the sizes of all module streams 71 | printf("\n"); 72 | printf("Sizes of module streams\n"); 73 | printf("-----------------------\n"); 74 | { 75 | const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile); 76 | const PDB::ArrayView modules = moduleInfoStream.GetModules(); 77 | 78 | for (const PDB::ModuleInfoStream::Module& module : modules) 79 | { 80 | const PDB::DBI::ModuleInfo* moduleInfo = module.GetInfo(); 81 | const char* name = module.GetName().Decay(); 82 | const char* objectName = module.GetObjectName().Decay(); 83 | 84 | const uint16_t streamIndex = module.HasSymbolStream() ? moduleInfo->moduleSymbolStreamIndex : 0u; 85 | const uint32_t moduleStreamSize = (streamIndex != 0u) ? rawPdbFile.GetStreamSize(streamIndex) : 0u; 86 | 87 | printf("Module %s (%s) stream size: %u KiB (%u MiB)\n", name, objectName, moduleStreamSize >> 10u, moduleStreamSize >> 20u); 88 | 89 | streams.push_back(Stream { name, moduleStreamSize }); 90 | } 91 | } 92 | 93 | // sort the streams by their size 94 | std::sort(streams.begin(), streams.end(), [](const Stream& lhs, const Stream& rhs) 95 | { 96 | return lhs.size > rhs.size; 97 | }); 98 | 99 | // log the 20 largest stream 100 | { 101 | printf("\n"); 102 | printf("Sizes of 20 largest streams:\n"); 103 | 104 | const size_t countToShow = std::min(20ul, streams.size()); 105 | for (size_t i = 0u; i < countToShow; ++i) 106 | { 107 | const Stream& stream = streams[i]; 108 | printf("%zu: %u KiB (%u MiB) from stream %s\n", i + 1u, stream.size >> 10u, stream.size >> 20u, stream.name.c_str()); 109 | } 110 | } 111 | 112 | // print the raw stream sizes 113 | printf("\n"); 114 | printf("Raw sizes of all streams\n"); 115 | printf("------------------------\n"); 116 | { 117 | const uint32_t streamCount = rawPdbFile.GetStreamCount(); 118 | for (uint32_t i = 0u; i < streamCount; ++i) 119 | { 120 | const uint32_t streamSize = rawPdbFile.GetStreamSize(i); 121 | printf("Stream %u size: %u KiB (%u MiB)\n", i, streamSize >> 10u, streamSize >> 20u); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Examples/ExampleSymbols.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "Examples_PCH.h" 5 | #include "ExampleTimedScope.h" 6 | #include "PDB_RawFile.h" 7 | #include "PDB_DBIStream.h" 8 | 9 | 10 | namespace 11 | { 12 | // we don't have to store std::string in the symbols, since all the data is memory-mapped anyway. 13 | // we do it in this example to ensure that we don't "cheat" when reading the PDB file. memory-mapped data will only 14 | // be faulted into the process once it's touched, so actually copying the string data makes us touch the needed data, 15 | // giving us a real performance measurement. 16 | struct Symbol 17 | { 18 | std::string name; 19 | uint32_t rva; 20 | }; 21 | } 22 | 23 | 24 | void ExampleSymbols(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream); 25 | void ExampleSymbols(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream) 26 | { 27 | TimedScope total("\nRunning example \"Symbols\""); 28 | 29 | // in order to keep the example easy to understand, we load the PDB data serially. 30 | // note that this can be improved a lot by reading streams concurrently. 31 | 32 | // prepare the image section stream first. it is needed for converting section + offset into an RVA 33 | TimedScope sectionScope("Reading image section stream"); 34 | const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile); 35 | sectionScope.Done(); 36 | 37 | 38 | // prepare the module info stream for matching contributions against files 39 | TimedScope moduleScope("Reading module info stream"); 40 | const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile); 41 | moduleScope.Done(); 42 | 43 | 44 | // prepare symbol record stream needed by both public and global streams 45 | TimedScope symbolStreamScope("Reading symbol record stream"); 46 | const PDB::CoalescedMSFStream symbolRecordStream = dbiStream.CreateSymbolRecordStream(rawPdbFile); 47 | symbolStreamScope.Done(); 48 | 49 | std::vector symbols; 50 | 51 | // read public symbols 52 | TimedScope publicScope("Reading public symbol stream"); 53 | const PDB::PublicSymbolStream publicSymbolStream = dbiStream.CreatePublicSymbolStream(rawPdbFile); 54 | publicScope.Done(); 55 | { 56 | TimedScope scope("Storing public symbols"); 57 | 58 | const PDB::ArrayView hashRecords = publicSymbolStream.GetRecords(); 59 | const size_t count = hashRecords.GetLength(); 60 | 61 | symbols.reserve(count); 62 | 63 | for (const PDB::HashRecord& hashRecord : hashRecords) 64 | { 65 | const PDB::CodeView::DBI::Record* record = publicSymbolStream.GetRecord(symbolRecordStream, hashRecord); 66 | if (record->header.kind != PDB::CodeView::DBI::SymbolRecordKind::S_PUB32) 67 | { 68 | // normally, a PDB only contains S_PUB32 symbols in the public symbol stream, but we have seen PDBs that also store S_CONSTANT as public symbols. 69 | // ignore these. 70 | continue; 71 | } 72 | 73 | const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_PUB32.section, record->data.S_PUB32.offset); 74 | if (rva == 0u) 75 | { 76 | // certain symbols (e.g. control-flow guard symbols) don't have a valid RVA, ignore those 77 | continue; 78 | } 79 | 80 | symbols.push_back(Symbol { record->data.S_PUB32.name, rva }); 81 | } 82 | 83 | scope.Done(count); 84 | } 85 | 86 | 87 | // read global symbols 88 | TimedScope globalScope("Reading global symbol stream"); 89 | const PDB::GlobalSymbolStream globalSymbolStream = dbiStream.CreateGlobalSymbolStream(rawPdbFile); 90 | globalScope.Done(); 91 | { 92 | TimedScope scope("Storing global symbols"); 93 | 94 | const PDB::ArrayView hashRecords = globalSymbolStream.GetRecords(); 95 | const size_t count = hashRecords.GetLength(); 96 | 97 | symbols.reserve(symbols.size() + count); 98 | 99 | for (const PDB::HashRecord& hashRecord : hashRecords) 100 | { 101 | const PDB::CodeView::DBI::Record* record = globalSymbolStream.GetRecord(symbolRecordStream, hashRecord); 102 | 103 | const char* name = nullptr; 104 | uint32_t rva = 0u; 105 | if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GDATA32) 106 | { 107 | name = record->data.S_GDATA32.name; 108 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GDATA32.section, record->data.S_GDATA32.offset); 109 | } 110 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GTHREAD32) 111 | { 112 | name = record->data.S_GTHREAD32.name; 113 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GTHREAD32.section, record->data.S_GTHREAD32.offset); 114 | } 115 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LDATA32) 116 | { 117 | name = record->data.S_LDATA32.name; 118 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LDATA32.section, record->data.S_LDATA32.offset); 119 | } 120 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LTHREAD32) 121 | { 122 | name = record->data.S_LTHREAD32.name; 123 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LTHREAD32.section, record->data.S_LTHREAD32.offset); 124 | } 125 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_UDT) 126 | { 127 | name = record->data.S_UDT.name; 128 | } 129 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_UDT_ST) 130 | { 131 | name = record->data.S_UDT_ST.name; 132 | } 133 | 134 | if (rva == 0u) 135 | { 136 | // certain symbols (e.g. control-flow guard symbols) don't have a valid RVA, ignore those 137 | continue; 138 | } 139 | 140 | symbols.push_back(Symbol { name, rva }); 141 | } 142 | 143 | scope.Done(count); 144 | } 145 | 146 | 147 | // read module symbols 148 | { 149 | TimedScope scope("Storing symbols from modules"); 150 | 151 | const PDB::ArrayView modules = moduleInfoStream.GetModules(); 152 | 153 | for (const PDB::ModuleInfoStream::Module& module : modules) 154 | { 155 | if (!module.HasSymbolStream()) 156 | { 157 | continue; 158 | } 159 | 160 | const PDB::ModuleSymbolStream moduleSymbolStream = module.CreateSymbolStream(rawPdbFile); 161 | moduleSymbolStream.ForEachSymbol([&symbols, &imageSectionStream](const PDB::CodeView::DBI::Record* record) 162 | { 163 | const char* name = nullptr; 164 | uint32_t rva = 0u; 165 | if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_THUNK32) 166 | { 167 | if (record->data.S_THUNK32.thunk == PDB::CodeView::DBI::ThunkOrdinal::TrampolineIncremental) 168 | { 169 | // we have never seen incremental linking thunks stored inside a S_THUNK32 symbol, but better be safe than sorry 170 | name = "ILT"; 171 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_THUNK32.section, record->data.S_THUNK32.offset); 172 | } 173 | } 174 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_TRAMPOLINE) 175 | { 176 | // incremental linking thunks are stored in the linker module 177 | name = "ILT"; 178 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_TRAMPOLINE.thunkSection, record->data.S_TRAMPOLINE.thunkOffset); 179 | } 180 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_BLOCK32) 181 | { 182 | // blocks never store a name and are only stored for indicating whether other symbols are children of this block 183 | } 184 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LABEL32) 185 | { 186 | // labels don't have a name 187 | } 188 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32) 189 | { 190 | name = record->data.S_LPROC32.name; 191 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32.section, record->data.S_LPROC32.offset); 192 | } 193 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32) 194 | { 195 | name = record->data.S_GPROC32.name; 196 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32.section, record->data.S_GPROC32.offset); 197 | } 198 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32_ID) 199 | { 200 | name = record->data.S_LPROC32_ID.name; 201 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32_ID.section, record->data.S_LPROC32_ID.offset); 202 | } 203 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32_ID) 204 | { 205 | name = record->data.S_GPROC32_ID.name; 206 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32_ID.section, record->data.S_GPROC32_ID.offset); 207 | } 208 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_REGREL32) 209 | { 210 | name = record->data.S_REGREL32.name; 211 | // You can only get the address while running the program by checking the register value and adding the offset 212 | } 213 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LDATA32) 214 | { 215 | name = record->data.S_LDATA32.name; 216 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LDATA32.section, record->data.S_LDATA32.offset); 217 | } 218 | else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LTHREAD32) 219 | { 220 | name = record->data.S_LTHREAD32.name; 221 | rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LTHREAD32.section, record->data.S_LTHREAD32.offset); 222 | } 223 | 224 | if (rva == 0u) 225 | { 226 | // certain symbols (e.g. control-flow guard symbols) don't have a valid RVA, ignore those 227 | return; 228 | } 229 | 230 | symbols.push_back(Symbol { name, rva }); 231 | }); 232 | } 233 | 234 | scope.Done(modules.GetLength()); 235 | } 236 | 237 | total.Done(symbols.size()); 238 | } 239 | -------------------------------------------------------------------------------- /src/Examples/ExampleTimedScope.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "Examples_PCH.h" 5 | #include "ExampleTimedScope.h" 6 | 7 | namespace 8 | { 9 | static unsigned int g_indent = 0u; 10 | 11 | static void PrintIndent(void) 12 | { 13 | printf("%.*s", g_indent * 2u, "| | | | | | | | "); 14 | } 15 | } 16 | 17 | 18 | TimedScope::TimedScope(const char* message) 19 | : m_begin(std::chrono::high_resolution_clock::now()) 20 | { 21 | PrintIndent(); 22 | ++g_indent; 23 | 24 | printf("%s\n", message); 25 | } 26 | 27 | 28 | void TimedScope::Done(void) const 29 | { 30 | --g_indent; 31 | PrintIndent(); 32 | 33 | const double milliSeconds = ReadMilliseconds(); 34 | printf("---> done in %.3fms\n", milliSeconds); 35 | } 36 | 37 | 38 | void TimedScope::Done(size_t count) const 39 | { 40 | --g_indent; 41 | PrintIndent(); 42 | 43 | const double milliSeconds = ReadMilliseconds(); 44 | printf("---> done in %.3fms (%zu elements)\n", milliSeconds, count); 45 | } 46 | 47 | 48 | double TimedScope::ReadMilliseconds(void) const 49 | { 50 | const std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now(); 51 | const std::chrono::duration seconds = now - m_begin; 52 | 53 | return seconds.count() * 1000.0; 54 | } 55 | -------------------------------------------------------------------------------- /src/Examples/ExampleTimedScope.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "Foundation/PDB_Macros.h" 5 | #include 6 | 7 | 8 | class TimedScope 9 | { 10 | public: 11 | explicit TimedScope(const char* message); 12 | 13 | void Done(void) const; 14 | void Done(size_t count) const; 15 | 16 | private: 17 | double ReadMilliseconds(void) const; 18 | 19 | const std::chrono::high_resolution_clock::time_point m_begin; 20 | 21 | PDB_DISABLE_COPY_MOVE(TimedScope); 22 | }; 23 | -------------------------------------------------------------------------------- /src/Examples/ExampleTypeTable.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "Examples_PCH.h" 5 | #include "ExampleTypeTable.h" 6 | #include "Foundation/PDB_Memory.h" 7 | 8 | TypeTable::TypeTable(const PDB::TPIStream& tpiStream) PDB_NO_EXCEPT 9 | : typeIndexBegin(tpiStream.GetFirstTypeIndex()), typeIndexEnd(tpiStream.GetLastTypeIndex()), 10 | m_recordCount(tpiStream.GetTypeRecordCount()) 11 | { 12 | // Create coalesced stream from TPI stream, so the records can be referenced directly using pointers. 13 | const PDB::DirectMSFStream& directStream = tpiStream.GetDirectMSFStream(); 14 | m_stream = PDB::CoalescedMSFStream(directStream, directStream.GetSize(), 0); 15 | 16 | // types in the TPI stream are accessed by their index from other streams. 17 | // however, the index is not stored with types in the TPI stream directly, but has to be built while walking the stream. 18 | // similarly, because types are variable-length records, there are no direct offsets to access individual types. 19 | // we therefore walk the TPI stream once, and store pointers to the records for trivial O(1) array lookup by index later. 20 | m_records = PDB_NEW_ARRAY(const PDB::CodeView::TPI::Record*, m_recordCount); 21 | 22 | // parse the CodeView records 23 | uint32_t typeIndex = 0u; 24 | 25 | tpiStream.ForEachTypeRecordHeaderAndOffset([this, &typeIndex](const PDB::CodeView::TPI::RecordHeader& header, size_t offset) 26 | { 27 | // The header includes the record kind and size, which can be stored along with offset 28 | // to allow for lazy loading of the types on-demand directly from the TPIStream::GetDirectMSFStream() 29 | // using DirectMSFStream::ReadAtOffset(...). Thus not needing a CoalescedMSFStream to look up the types. 30 | (void)header; 31 | 32 | const PDB::CodeView::TPI::Record* record = m_stream.GetDataAtOffset(offset); 33 | m_records[typeIndex] = record; 34 | ++typeIndex; 35 | }); 36 | } 37 | 38 | TypeTable::~TypeTable() PDB_NO_EXCEPT 39 | { 40 | PDB_DELETE_ARRAY(m_records); 41 | } 42 | -------------------------------------------------------------------------------- /src/Examples/ExampleTypeTable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "PDB_TPIStream.h" 4 | #include "PDB_CoalescedMSFStream.h" 5 | 6 | class TypeTable 7 | { 8 | public: 9 | explicit TypeTable(const PDB::TPIStream& tpiStream) PDB_NO_EXCEPT; 10 | ~TypeTable() PDB_NO_EXCEPT; 11 | 12 | // Returns the index of the first type, which is not necessarily zero. 13 | PDB_NO_DISCARD inline uint32_t GetFirstTypeIndex(void) const PDB_NO_EXCEPT 14 | { 15 | return typeIndexBegin; 16 | } 17 | 18 | // Returns the index of the last type. 19 | PDB_NO_DISCARD inline uint32_t GetLastTypeIndex(void) const PDB_NO_EXCEPT 20 | { 21 | return typeIndexEnd; 22 | } 23 | 24 | PDB_NO_DISCARD inline const PDB::CodeView::TPI::Record* GetTypeRecord(uint32_t typeIndex) const PDB_NO_EXCEPT 25 | { 26 | if (typeIndex < typeIndexBegin || typeIndex > typeIndexEnd) 27 | return nullptr; 28 | 29 | return m_records[typeIndex - typeIndexBegin]; 30 | } 31 | 32 | // Returns a view of all type records. 33 | // Records identified by a type index can be accessed via "allRecords[typeIndex - firstTypeIndex]". 34 | PDB_NO_DISCARD inline PDB::ArrayView GetTypeRecords(void) const PDB_NO_EXCEPT 35 | { 36 | return PDB::ArrayView(m_records, m_recordCount); 37 | } 38 | 39 | private: 40 | uint32_t typeIndexBegin; 41 | uint32_t typeIndexEnd; 42 | 43 | size_t m_recordCount; 44 | const PDB::CodeView::TPI::Record **m_records; 45 | 46 | PDB::CoalescedMSFStream m_stream; 47 | 48 | PDB_DISABLE_COPY(TypeTable); 49 | }; 50 | -------------------------------------------------------------------------------- /src/Examples/Examples_PCH.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "Examples_PCH.h" 5 | -------------------------------------------------------------------------------- /src/Examples/Examples_PCH.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Warnings.h" 7 | #include "Foundation/PDB_DisableWarningsPush.h" 8 | 9 | // we compile without exceptions 10 | # define _ALLOW_RTCc_IN_STL 11 | 12 | // triggered by Windows.h 13 | # pragma warning (disable : 4668) 14 | 15 | // triggered by xlocale in VS 2017 16 | # pragma warning (disable : 4625) // copy constructor was implicitly defined as deleted 17 | # pragma warning (disable : 4626) // assignment operator was implicitly defined as deleted 18 | # pragma warning (disable : 5026) // move constructor was implicitly defined as deleted 19 | # pragma warning (disable : 5027) // move assignment operator was implicitly defined as deleted 20 | # pragma warning (disable : 4774) // format string expected in argument 1 is not a string literal 21 | 22 | #ifdef _WIN32 23 | # define NOMINMAX 24 | # include 25 | # undef cdecl 26 | #endif 27 | # include 28 | # include 29 | # include 30 | # include 31 | # include 32 | # include 33 | # include "Foundation/PDB_DisableWarningsPop.h" 34 | -------------------------------------------------------------------------------- /src/Foundation/PDB_ArrayView.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "PDB_Macros.h" 7 | #include "PDB_Assert.h" 8 | 9 | 10 | namespace PDB 11 | { 12 | // A read-only view into arrays of any type and length. 13 | template 14 | class PDB_NO_DISCARD ArrayView 15 | { 16 | public: 17 | // Constructs an array view from a C array with explicit length. 18 | inline constexpr explicit ArrayView(const T* const array, size_t length) PDB_NO_EXCEPT 19 | : m_data(array) 20 | , m_length(length) 21 | { 22 | } 23 | 24 | PDB_DEFAULT_COPY_CONSTRUCTOR(ArrayView); 25 | PDB_DEFAULT_MOVE_CONSTRUCTOR(ArrayView); 26 | 27 | // Provides read-only access to the underlying array. 28 | PDB_NO_DISCARD inline constexpr const T* Decay(void) const PDB_NO_EXCEPT 29 | { 30 | return m_data; 31 | } 32 | 33 | // Returns the length of the view. 34 | PDB_NO_DISCARD inline constexpr size_t GetLength(void) const PDB_NO_EXCEPT 35 | { 36 | return m_length; 37 | } 38 | 39 | // Returns the i-th element. 40 | PDB_NO_DISCARD inline const T& operator[](size_t i) const PDB_NO_EXCEPT 41 | { 42 | PDB_ASSERT(i < GetLength(), "Index %zu out of bounds [0, %zu).", i, GetLength()); 43 | return m_data[i]; 44 | } 45 | 46 | 47 | // ------------------------------------------------------------------------------------------------ 48 | // Range-based for-loop support 49 | // ------------------------------------------------------------------------------------------------ 50 | 51 | PDB_NO_DISCARD inline const T* begin(void) const PDB_NO_EXCEPT 52 | { 53 | return m_data; 54 | } 55 | 56 | PDB_NO_DISCARD inline const T* end(void) const PDB_NO_EXCEPT 57 | { 58 | return m_data + m_length; 59 | } 60 | 61 | private: 62 | const T* const m_data; 63 | const size_t m_length; 64 | 65 | PDB_DISABLE_MOVE_ASSIGNMENT(ArrayView); 66 | PDB_DISABLE_COPY_ASSIGNMENT(ArrayView); 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /src/Foundation/PDB_Assert.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "PDB_Macros.h" 7 | #include "PDB_Log.h" 8 | 9 | 10 | PDB_PUSH_WARNING_CLANG 11 | PDB_DISABLE_WARNING_CLANG("-Wgnu-zero-variadic-macro-arguments") 12 | 13 | #ifdef _DEBUG 14 | # define PDB_ASSERT(_condition, _msg, ...) (_condition) ? (void)true : (PDB_LOG_ERROR(_msg, ##__VA_ARGS__), __debugbreak()) 15 | #else 16 | # define PDB_ASSERT(_condition, _msg, ...) __noop((void)(_condition), (void)(_msg), ##__VA_ARGS__) 17 | #endif 18 | 19 | PDB_POP_WARNING_CLANG 20 | -------------------------------------------------------------------------------- /src/Foundation/PDB_BitOperators.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "PDB_Macros.h" 7 | #include "PDB_DisableWarningsPush.h" 8 | #include 9 | #include "PDB_DisableWarningsPop.h" 10 | 11 | 12 | #define PDB_DEFINE_BIT_OPERATORS(_type) \ 13 | PDB_NO_DISCARD inline constexpr _type operator|(_type lhs, _type rhs) PDB_NO_EXCEPT \ 14 | { \ 15 | return static_cast<_type>(PDB_AS_UNDERLYING(lhs) | PDB_AS_UNDERLYING(rhs)); \ 16 | } \ 17 | \ 18 | PDB_NO_DISCARD inline constexpr _type operator&(_type lhs, _type rhs) PDB_NO_EXCEPT \ 19 | { \ 20 | return static_cast<_type>(PDB_AS_UNDERLYING(lhs) & PDB_AS_UNDERLYING(rhs)); \ 21 | } \ 22 | \ 23 | PDB_NO_DISCARD inline constexpr _type operator~(_type value) PDB_NO_EXCEPT \ 24 | { \ 25 | return static_cast<_type>(~PDB_AS_UNDERLYING(value)); \ 26 | } 27 | -------------------------------------------------------------------------------- /src/Foundation/PDB_BitUtil.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "PDB_Assert.h" 7 | #include "PDB_DisableWarningsPush.h" 8 | #include 9 | #include "PDB_DisableWarningsPop.h" 10 | 11 | #ifdef _WIN32 12 | #include 13 | # pragma intrinsic(_BitScanForward) 14 | #endif 15 | 16 | 17 | namespace PDB 18 | { 19 | namespace BitUtil 20 | { 21 | template 22 | PDB_NO_DISCARD inline constexpr bool IsPowerOfTwo(T value) PDB_NO_EXCEPT 23 | { 24 | static_assert(std::is_unsigned::value == true, "T must be an unsigned type."); 25 | 26 | PDB_ASSERT(value != 0u, "Invalid value."); 27 | 28 | return (value & (value - 1u)) == 0u; 29 | } 30 | 31 | 32 | template 33 | PDB_NO_DISCARD inline constexpr T RoundUpToMultiple(T numToRound, T multipleOf) PDB_NO_EXCEPT 34 | { 35 | static_assert(std::is_unsigned::value == true, "T must be an unsigned type."); 36 | 37 | PDB_ASSERT(IsPowerOfTwo(multipleOf), "Multiple must be a power-of-two."); 38 | 39 | return (numToRound + (multipleOf - 1u)) & ~(multipleOf - 1u); 40 | } 41 | 42 | 43 | // Finds the position of the first set bit in the given value starting from the LSB, e.g. FindFirstSetBit(0b00000010) == 1. 44 | // This operation is also known as CTZ (Count Trailing Zeros). 45 | template 46 | PDB_NO_DISCARD inline uint32_t FindFirstSetBit(T value) PDB_NO_EXCEPT; 47 | 48 | template <> 49 | PDB_NO_DISCARD inline uint32_t FindFirstSetBit(uint32_t value) PDB_NO_EXCEPT 50 | { 51 | PDB_ASSERT(value != 0u, "Invalid value."); 52 | 53 | #ifdef _WIN32 54 | unsigned long result = 0ul; 55 | 56 | _BitScanForward(&result, value); 57 | #else 58 | unsigned int result = 0u; 59 | 60 | result = static_cast(__builtin_ffs(static_cast(value))); 61 | if (result) 62 | --result; 63 | #endif 64 | 65 | return result; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Foundation/PDB_DisableWarningsPop.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_Platform.h" 5 | 6 | 7 | #if PDB_COMPILER_MSVC 8 | # pragma warning(pop) 9 | #elif PDB_COMPILER_CLANG 10 | # pragma clang diagnostic pop 11 | #endif 12 | -------------------------------------------------------------------------------- /src/Foundation/PDB_DisableWarningsPush.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_Platform.h" 5 | 6 | 7 | #if PDB_COMPILER_MSVC 8 | # pragma warning(push, 0) 9 | #elif PDB_COMPILER_CLANG 10 | # pragma clang diagnostic push 11 | # pragma clang diagnostic ignored "-Weverything" 12 | #endif 13 | -------------------------------------------------------------------------------- /src/Foundation/PDB_Forward.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | 7 | // See Jonathan Mueller's blog for replacing std::move and std::forward: 8 | // https://foonathan.net/2021/09/move-forward/ 9 | #define PDB_FORWARD(...) static_cast(__VA_ARGS__) 10 | -------------------------------------------------------------------------------- /src/Foundation/PDB_Log.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "PDB_Macros.h" 7 | #include "PDB_DisableWarningsPush.h" 8 | #include 9 | #include "PDB_DisableWarningsPop.h" 10 | 11 | 12 | PDB_PUSH_WARNING_CLANG 13 | PDB_DISABLE_WARNING_CLANG("-Wgnu-zero-variadic-macro-arguments") 14 | 15 | #define PDB_LOG_ERROR(_format, ...) printf(_format, ##__VA_ARGS__) 16 | 17 | PDB_POP_WARNING_CLANG 18 | -------------------------------------------------------------------------------- /src/Foundation/PDB_Macros.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "PDB_Platform.h" 7 | 8 | 9 | // ------------------------------------------------------------------------------------------------ 10 | // ATTRIBUTES 11 | // ------------------------------------------------------------------------------------------------ 12 | 13 | // Indicates to the compiler that the return value of a function or class should not be ignored. 14 | #if PDB_CPP_17 15 | # define PDB_NO_DISCARD [[nodiscard]] 16 | #else 17 | # define PDB_NO_DISCARD 18 | #endif 19 | 20 | // Indicates to the compiler that a function does not throw an exception. 21 | #define PDB_NO_EXCEPT noexcept 22 | 23 | 24 | // ------------------------------------------------------------------------------------------------ 25 | // SPECIAL MEMBER FUNCTIONS 26 | // ------------------------------------------------------------------------------------------------ 27 | 28 | // Default special member functions. 29 | #define PDB_DEFAULT_COPY_CONSTRUCTOR(_name) _name(const _name&) PDB_NO_EXCEPT = default 30 | #define PDB_DEFAULT_COPY_ASSIGNMENT(_name) _name& operator=(const _name&) PDB_NO_EXCEPT = default 31 | #define PDB_DEFAULT_MOVE_CONSTRUCTOR(_name) _name(_name&&) PDB_NO_EXCEPT = default 32 | #define PDB_DEFAULT_MOVE_ASSIGNMENT(_name) _name& operator=(_name&&) PDB_NO_EXCEPT = default 33 | 34 | // Default copy member functions. 35 | #define PDB_DEFAULT_COPY(_name) PDB_DEFAULT_COPY_CONSTRUCTOR(_name); PDB_DEFAULT_COPY_ASSIGNMENT(_name) 36 | 37 | // Default move member functions. 38 | #define PDB_DEFAULT_MOVE(_name) PDB_DEFAULT_MOVE_CONSTRUCTOR(_name); PDB_DEFAULT_MOVE_ASSIGNMENT(_name) 39 | 40 | // Single macro to default all copy and move member functions. 41 | #define PDB_DEFAULT_COPY_MOVE(_name) PDB_DEFAULT_COPY(_name); PDB_DEFAULT_MOVE(_name) 42 | 43 | // Disable special member functions. 44 | #define PDB_DISABLE_COPY_CONSTRUCTOR(_name) _name(const _name&) PDB_NO_EXCEPT = delete 45 | #define PDB_DISABLE_COPY_ASSIGNMENT(_name) _name& operator=(const _name&) PDB_NO_EXCEPT = delete 46 | #define PDB_DISABLE_MOVE_CONSTRUCTOR(_name) _name(_name&&) PDB_NO_EXCEPT = delete 47 | #define PDB_DISABLE_MOVE_ASSIGNMENT(_name) _name& operator=(_name&&) PDB_NO_EXCEPT = delete 48 | 49 | // Disable copy member functions. 50 | #define PDB_DISABLE_COPY(_name) PDB_DISABLE_COPY_CONSTRUCTOR(_name); PDB_DISABLE_COPY_ASSIGNMENT(_name) 51 | 52 | // Disable move member functions. 53 | #define PDB_DISABLE_MOVE(_name) PDB_DISABLE_MOVE_CONSTRUCTOR(_name); PDB_DISABLE_MOVE_ASSIGNMENT(_name) 54 | 55 | // Single macro to disable all copy and move member functions. 56 | #define PDB_DISABLE_COPY_MOVE(_name) PDB_DISABLE_COPY(_name); PDB_DISABLE_MOVE(_name) 57 | 58 | 59 | // ------------------------------------------------------------------------------------------------ 60 | // COMPILER WARNINGS 61 | // ------------------------------------------------------------------------------------------------ 62 | 63 | #if PDB_COMPILER_MSVC 64 | # define PDB_PRAGMA(_x) __pragma(_x) 65 | 66 | # define PDB_PUSH_WARNING_MSVC PDB_PRAGMA(warning(push)) 67 | # define PDB_SUPPRESS_WARNING_MSVC(_number) PDB_PRAGMA(warning(suppress : _number)) 68 | # define PDB_DISABLE_WARNING_MSVC(_number) PDB_PRAGMA(warning(disable : _number)) 69 | # define PDB_POP_WARNING_MSVC PDB_PRAGMA(warning(pop)) 70 | 71 | # define PDB_PUSH_WARNING_CLANG 72 | # define PDB_DISABLE_WARNING_CLANG(_diagnostic) 73 | # define PDB_POP_WARNING_CLANG 74 | #elif PDB_COMPILER_CLANG 75 | # define PDB_PRAGMA(_x) _Pragma(#_x) 76 | 77 | # define PDB_PUSH_WARNING_MSVC 78 | # define PDB_SUPPRESS_WARNING_MSVC(_number) 79 | # define PDB_DISABLE_WARNING_MSVC(_number) 80 | # define PDB_POP_WARNING_MSVC 81 | 82 | # define PDB_PUSH_WARNING_CLANG PDB_PRAGMA(clang diagnostic push) 83 | # define PDB_DISABLE_WARNING_CLANG(_diagnostic) PDB_PRAGMA(clang diagnostic ignored _diagnostic) 84 | # define PDB_POP_WARNING_CLANG PDB_PRAGMA(clang diagnostic pop) 85 | 86 | # define PDB_PUSH_WARNING_CLANG PDB_PRAGMA(clang diagnostic push) 87 | # define PDB_DISABLE_WARNING_CLANG(_diagnostic) PDB_PRAGMA(clang diagnostic ignored _diagnostic) 88 | # define PDB_POP_WARNING_CLANG PDB_PRAGMA(clang diagnostic pop) 89 | #elif PDB_COMPILER_GCC 90 | # define PDB_PRAGMA(_x) _Pragma(#_x) 91 | 92 | # define PDB_PUSH_WARNING_MSVC 93 | # define PDB_SUPPRESS_WARNING_MSVC(_number) 94 | # define PDB_DISABLE_WARNING_MSVC(_number) 95 | # define PDB_POP_WARNING_MSVC 96 | 97 | # define PDB_PUSH_WARNING_CLANG 98 | # define PDB_DISABLE_WARNING_CLANG(_diagnostic) 99 | # define PDB_POP_WARNING_CLANG 100 | 101 | # define __noop(...) 102 | #endif 103 | 104 | 105 | // ------------------------------------------------------------------------------------------------ 106 | // MISCELLANEOUS 107 | // ------------------------------------------------------------------------------------------------ 108 | 109 | // Trick to make other macros require a semicolon at the end. 110 | #define PDB_REQUIRE_SEMICOLON static_assert(true, "") 111 | 112 | // Defines a C-like flexible array member. 113 | #define PDB_FLEXIBLE_ARRAY_MEMBER(_type, _name) \ 114 | PDB_PUSH_WARNING_MSVC \ 115 | PDB_PUSH_WARNING_CLANG \ 116 | PDB_DISABLE_WARNING_MSVC(4200) \ 117 | PDB_DISABLE_WARNING_CLANG("-Wzero-length-array") \ 118 | _type _name[0]; \ 119 | PDB_POP_WARNING_MSVC \ 120 | PDB_POP_WARNING_CLANG \ 121 | PDB_REQUIRE_SEMICOLON 122 | 123 | // Casts any value to the value of the underlying type. 124 | #define PDB_AS_UNDERLYING(_value) static_cast::type>(_value) 125 | -------------------------------------------------------------------------------- /src/Foundation/PDB_Memory.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | 7 | #define PDB_NEW(_type) new _type 8 | #define PDB_NEW_ARRAY(_type, _length) new _type[_length] 9 | 10 | #define PDB_DELETE(_ptr) delete _ptr 11 | #define PDB_DELETE_ARRAY(_ptr) delete[] _ptr 12 | -------------------------------------------------------------------------------- /src/Foundation/PDB_Move.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "PDB_DisableWarningsPush.h" 7 | #include 8 | #include "PDB_DisableWarningsPop.h" 9 | 10 | 11 | // See Jonathan Mueller's blog for replacing std::move and std::forward: 12 | // https://foonathan.net/2020/09/move-forward/ 13 | #define PDB_MOVE(...) static_cast::type&&>(__VA_ARGS__) 14 | -------------------------------------------------------------------------------- /src/Foundation/PDB_Platform.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | 7 | // determine the compiler/toolchain used 8 | #if defined(__clang__) 9 | # define PDB_COMPILER_MSVC 0 10 | # define PDB_COMPILER_CLANG 1 11 | # define PDB_COMPILER_GCC 0 12 | #elif defined(_MSC_VER) 13 | # define PDB_COMPILER_MSVC 1 14 | # define PDB_COMPILER_CLANG 0 15 | # define PDB_COMPILER_GCC 0 16 | #elif defined(__GNUC__) 17 | # define PDB_COMPILER_MSVC 0 18 | # define PDB_COMPILER_CLANG 0 19 | # define PDB_COMPILER_GCC 1 20 | #else 21 | # error("Unknown compiler."); 22 | #endif 23 | 24 | // check whether C++17 is available 25 | #if __cplusplus >= 201703L 26 | # define PDB_CPP_17 1 27 | #else 28 | # define PDB_CPP_17 0 29 | #endif 30 | -------------------------------------------------------------------------------- /src/Foundation/PDB_PointerUtil.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "PDB_Macros.h" 7 | #include "PDB_DisableWarningsPush.h" 8 | #include 9 | #include "PDB_DisableWarningsPop.h" 10 | 11 | 12 | namespace PDB 13 | { 14 | namespace Pointer 15 | { 16 | // Offsets any pointer by a given number of bytes. 17 | template 18 | PDB_NO_DISCARD inline T Offset(U* anyPointer, V howManyBytes) PDB_NO_EXCEPT 19 | { 20 | static_assert(std::is_pointer::value == true, "Type T must be a pointer type."); 21 | static_assert(std::is_const::type>::value == std::is_const::value, "Wrong constness."); 22 | 23 | union 24 | { 25 | T as_T; 26 | U* as_U_ptr; 27 | char* as_char_ptr; 28 | }; 29 | 30 | as_U_ptr = anyPointer; 31 | as_char_ptr += howManyBytes; 32 | 33 | return as_T; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Foundation/PDB_Warnings.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "PDB_Platform.h" 7 | 8 | 9 | #if PDB_COMPILER_MSVC 10 | // some warnings were introduced with different versions of Visual Studio, so we disable this warning instead of using a bunch of #if/#endif 11 | # pragma warning (disable : 4619) // there is no warning number N 12 | 13 | // we compile with exceptions disabled 14 | # pragma warning (disable : 4530) // C++ exception handler used, but unwind semantics are not enabled.Specify / EHsc 15 | # pragma warning (disable : 4577) // 'noexcept' used with no exception handling mode specified; termination on exception is not guaranteed. Specify /EHsc 16 | 17 | // ignore purely informational warnings 18 | # pragma warning (disable : 4514) // unreferenced inline function has been removed 19 | # pragma warning (disable : 4710) // function not inlined 20 | # pragma warning (disable : 4711) // function selected for automatic inline expansion 21 | # pragma warning (disable : 4820) // 'N' bytes padding added after data member 'm_member' 22 | # pragma warning (disable : 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified 23 | #elif PDB_COMPILER_CLANG 24 | // turn on absolutely all available Clang warnings 25 | # pragma clang diagnostic warning "-Wall" 26 | # pragma clang diagnostic warning "-Wextra" 27 | # pragma clang diagnostic warning "-Weverything" 28 | # pragma clang diagnostic warning "-Wpedantic" 29 | 30 | // these warnings contradict -Weverything 31 | # pragma clang diagnostic ignored "-Wc++98-compat" 32 | # pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 33 | 34 | // this warning is triggered for templates which are explicitly instantiated. 35 | // forgetting to instantiate the template would trigger a linker error anyway, so we disable this warning. 36 | # pragma clang diagnostic ignored "-Wundefined-func-template" 37 | 38 | // we don't strive for C++20 compatibility 39 | # pragma clang diagnostic ignored "-Wc++20-compat" 40 | 41 | // some structures will have to be padded 42 | # pragma clang diagnostic ignored "-Wpadded" 43 | #endif 44 | -------------------------------------------------------------------------------- /src/PDB.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB.h" 6 | #include "PDB_Types.h" 7 | #include "PDB_Util.h" 8 | #include "PDB_RawFile.h" 9 | #include "Foundation/PDB_PointerUtil.h" 10 | #include "Foundation/PDB_DisableWarningsPush.h" 11 | #include 12 | #include "Foundation/PDB_DisableWarningsPop.h" 13 | 14 | 15 | // ------------------------------------------------------------------------------------------------ 16 | // ------------------------------------------------------------------------------------------------ 17 | PDB_NO_DISCARD PDB::ErrorCode PDB::ValidateFile(const void* data, size_t size) PDB_NO_EXCEPT 18 | { 19 | // validate whether there is enough size for the super block 20 | if (size < sizeof(SuperBlock)) 21 | { 22 | return ErrorCode::InvalidDataSize; 23 | } 24 | // validate the super block 25 | const SuperBlock* superBlock = Pointer::Offset(data, 0u); 26 | { 27 | // validate header magic 28 | if (std::memcmp(superBlock->fileMagic, SuperBlock::MAGIC, sizeof(SuperBlock::MAGIC)) != 0) 29 | { 30 | return ErrorCode::InvalidSuperBlock; 31 | } 32 | 33 | // validate whether enough size is provided for the PDB file 34 | // blockCount * blockSize is the size of the PDB file on disk 35 | if (size < superBlock->blockCount * superBlock->blockSize) 36 | { 37 | return ErrorCode::InvalidDataSize; 38 | } 39 | 40 | // validate free block map. 41 | // the free block map should always reside at either index 1 or 2. 42 | if (superBlock->freeBlockMapIndex != 1u && superBlock->freeBlockMapIndex != 2u) 43 | { 44 | return ErrorCode::InvalidFreeBlockMap; 45 | } 46 | } 47 | 48 | return ErrorCode::Success; 49 | } 50 | 51 | 52 | // ------------------------------------------------------------------------------------------------ 53 | // ------------------------------------------------------------------------------------------------ 54 | PDB_NO_DISCARD PDB::RawFile PDB::CreateRawFile(const void* data) PDB_NO_EXCEPT 55 | { 56 | return RawFile(data); 57 | } 58 | -------------------------------------------------------------------------------- /src/PDB.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "PDB_ErrorCodes.h" 8 | 9 | 10 | // https://llvm.org/docs/PDB/index.html 11 | namespace PDB 12 | { 13 | class RawFile; 14 | 15 | 16 | // Validates whether a PDB file is valid. 17 | PDB_NO_DISCARD ErrorCode ValidateFile(const void* data, size_t size) PDB_NO_EXCEPT; 18 | 19 | // Creates a raw PDB file that must have been validated. 20 | PDB_NO_DISCARD RawFile CreateRawFile(const void* data) PDB_NO_EXCEPT; 21 | } 22 | -------------------------------------------------------------------------------- /src/PDB_CoalescedMSFStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_CoalescedMSFStream.h" 6 | #include "PDB_Util.h" 7 | #include "PDB_DirectMSFStream.h" 8 | #include "Foundation/PDB_PointerUtil.h" 9 | #include "Foundation/PDB_Memory.h" 10 | #include "Foundation/PDB_DisableWarningsPush.h" 11 | #include 12 | #include "Foundation/PDB_DisableWarningsPop.h" 13 | 14 | 15 | namespace 16 | { 17 | // ------------------------------------------------------------------------------------------------ 18 | // ------------------------------------------------------------------------------------------------ 19 | PDB_NO_DISCARD static bool AreBlockIndicesContiguous(const uint32_t* blockIndices, uint32_t blockSize, uint32_t streamSize) PDB_NO_EXCEPT 20 | { 21 | const uint32_t blockCount = PDB::ConvertSizeToBlockCount(streamSize, blockSize); 22 | 23 | // start with the first index, checking if all following indices are contiguous (N, N+1, N+2, ...) 24 | uint32_t expectedIndex = blockIndices[0]; 25 | for (uint32_t i = 1u; i < blockCount; ++i) 26 | { 27 | ++expectedIndex; 28 | if (blockIndices[i] != expectedIndex) 29 | { 30 | return false; 31 | } 32 | } 33 | 34 | return true; 35 | } 36 | } 37 | 38 | 39 | // ------------------------------------------------------------------------------------------------ 40 | // ------------------------------------------------------------------------------------------------ 41 | PDB::CoalescedMSFStream::CoalescedMSFStream(void) PDB_NO_EXCEPT 42 | : m_ownedData(nullptr) 43 | , m_data(nullptr) 44 | , m_size(0u) 45 | { 46 | } 47 | 48 | 49 | // ------------------------------------------------------------------------------------------------ 50 | // ------------------------------------------------------------------------------------------------ 51 | PDB::CoalescedMSFStream::CoalescedMSFStream(CoalescedMSFStream&& other) PDB_NO_EXCEPT 52 | : m_ownedData(PDB_MOVE(other.m_ownedData)) 53 | , m_data(PDB_MOVE(other.m_data)) 54 | , m_size(PDB_MOVE(other.m_size)) 55 | { 56 | other.m_ownedData = nullptr; 57 | other.m_data = nullptr; 58 | other.m_size = 0u; 59 | } 60 | 61 | 62 | // ------------------------------------------------------------------------------------------------ 63 | // ------------------------------------------------------------------------------------------------ 64 | PDB::CoalescedMSFStream& PDB::CoalescedMSFStream::operator=(CoalescedMSFStream&& other) PDB_NO_EXCEPT 65 | { 66 | if (this != &other) 67 | { 68 | PDB_DELETE_ARRAY(m_ownedData); 69 | 70 | m_ownedData = PDB_MOVE(other.m_ownedData); 71 | m_data = PDB_MOVE(other.m_data); 72 | m_size = PDB_MOVE(other.m_size); 73 | 74 | other.m_ownedData = nullptr; 75 | other.m_data = nullptr; 76 | other.m_size = 0u; 77 | } 78 | 79 | return *this; 80 | } 81 | 82 | 83 | // ------------------------------------------------------------------------------------------------ 84 | // ------------------------------------------------------------------------------------------------ 85 | PDB::CoalescedMSFStream::CoalescedMSFStream(const void* data, uint32_t blockSize, const uint32_t* blockIndices, uint32_t streamSize) PDB_NO_EXCEPT 86 | : m_ownedData(nullptr) 87 | , m_data(nullptr) 88 | , m_size(streamSize) 89 | { 90 | if (AreBlockIndicesContiguous(blockIndices, blockSize, streamSize)) 91 | { 92 | // fast path, all block indices are contiguous, so we don't have to copy any data at all. 93 | // instead, we directly point into the memory-mapped file at the correct offset. 94 | const uint32_t index = blockIndices[0]; 95 | const size_t fileOffset = PDB::ConvertBlockIndexToFileOffset(index, blockSize); 96 | m_data = Pointer::Offset(data, fileOffset); 97 | } 98 | else 99 | { 100 | // slower path, we need to copy disjunct blocks into our own data array, block by block 101 | m_ownedData = PDB_NEW_ARRAY(Byte, streamSize); 102 | m_data = m_ownedData; 103 | 104 | Byte* destination = m_ownedData; 105 | 106 | // copy full blocks first 107 | const uint32_t fullBlockCount = streamSize / blockSize; 108 | for (uint32_t i = 0u; i < fullBlockCount; ++i) 109 | { 110 | const uint32_t index = blockIndices[i]; 111 | 112 | // read one single block at the correct offset in the stream 113 | const size_t fileOffset = PDB::ConvertBlockIndexToFileOffset(index, blockSize); 114 | const void* sourceData = Pointer::Offset(data, fileOffset); 115 | std::memcpy(destination, sourceData, blockSize); 116 | 117 | destination += blockSize; 118 | } 119 | 120 | // account for non-full blocks 121 | const uint32_t remainingBytes = streamSize - (fullBlockCount * blockSize); 122 | if (remainingBytes != 0u) 123 | { 124 | const uint32_t index = blockIndices[fullBlockCount]; 125 | 126 | // read remaining bytes at correct offset in the stream 127 | const size_t fileOffset = PDB::ConvertBlockIndexToFileOffset(index, blockSize); 128 | const void* sourceData = Pointer::Offset(data, fileOffset); 129 | std::memcpy(destination, sourceData, remainingBytes); 130 | } 131 | } 132 | } 133 | 134 | 135 | // ------------------------------------------------------------------------------------------------ 136 | // ------------------------------------------------------------------------------------------------ 137 | PDB::CoalescedMSFStream::CoalescedMSFStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT 138 | : m_ownedData(nullptr) 139 | , m_data(nullptr) 140 | , m_size(size) 141 | { 142 | const DirectMSFStream::IndexAndOffset indexAndOffset = directStream.GetBlockIndexForOffset(offset); 143 | 144 | // Note: we need to add the offset within the block to the size of the stream to determine if the block 145 | // indices are contiguous. This is needed to deal with the case where reading the requested number of bytes 146 | // from the specified offset would cross a block boundary. For example, if the offset within the block is 147 | // 64 and we want to read 4096 bytes with a block size of 4096, we need to consider *two* block indices, 148 | // not *one*, even though 4096 / 4096 = 1. 149 | if (AreBlockIndicesContiguous(directStream.GetBlockIndices() + indexAndOffset.index, directStream.GetBlockSize(), indexAndOffset.offsetWithinBlock + size)) 150 | { 151 | // fast path, all block indices inside the direct stream from (data + offset) to (data + offset + size) are contiguous 152 | const size_t offsetWithinData = directStream.GetDataOffsetForIndexAndOffset(indexAndOffset); 153 | m_data = Pointer::Offset(directStream.GetData(), offsetWithinData); 154 | } 155 | else 156 | { 157 | // slower path, we need to copy from disjunct blocks, which is performed by the direct stream 158 | m_ownedData = PDB_NEW_ARRAY(Byte, size); 159 | m_data = m_ownedData; 160 | 161 | directStream.ReadAtOffset(m_ownedData, size, offset); 162 | } 163 | } 164 | 165 | 166 | // ------------------------------------------------------------------------------------------------ 167 | // ------------------------------------------------------------------------------------------------ 168 | PDB::CoalescedMSFStream::~CoalescedMSFStream(void) PDB_NO_EXCEPT 169 | { 170 | PDB_DELETE_ARRAY(m_ownedData); 171 | } 172 | -------------------------------------------------------------------------------- /src/PDB_CoalescedMSFStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Assert.h" 7 | #include "Foundation/PDB_Macros.h" 8 | #include "PDB_Types.h" 9 | #include 10 | 11 | // https://llvm.org/docs/PDB/index.html#the-msf-container 12 | // https://llvm.org/docs/PDB/MsfFile.html 13 | namespace PDB 14 | { 15 | class PDB_NO_DISCARD DirectMSFStream; 16 | 17 | 18 | // provides access to a coalesced version of an MSF stream. 19 | // inherently thread-safe, the stream doesn't carry any internal offset or similar. 20 | // coalesces all blocks into a contiguous stream of data upon construction. 21 | // very fast individual reads, useful when almost all data of a stream is needed anyway. 22 | class PDB_NO_DISCARD CoalescedMSFStream 23 | { 24 | public: 25 | CoalescedMSFStream(void) PDB_NO_EXCEPT; 26 | CoalescedMSFStream(CoalescedMSFStream&& other) PDB_NO_EXCEPT; 27 | CoalescedMSFStream& operator=(CoalescedMSFStream&& other) PDB_NO_EXCEPT; 28 | 29 | explicit CoalescedMSFStream(const void* data, uint32_t blockSize, const uint32_t* blockIndices, uint32_t streamSize) PDB_NO_EXCEPT; 30 | 31 | // Creates a coalesced stream from a direct stream at any offset. 32 | explicit CoalescedMSFStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT; 33 | 34 | ~CoalescedMSFStream(void) PDB_NO_EXCEPT; 35 | 36 | // Returns the size of the stream. 37 | PDB_NO_DISCARD inline size_t GetSize(void) const PDB_NO_EXCEPT 38 | { 39 | return m_size; 40 | } 41 | 42 | // Provides read-only access to the data. 43 | template 44 | PDB_NO_DISCARD inline const T* GetDataAtOffset(size_t offset) const PDB_NO_EXCEPT 45 | { 46 | return reinterpret_cast(m_data + offset); 47 | } 48 | 49 | template 50 | PDB_NO_DISCARD inline size_t GetPointerOffset(const T* pointer) const PDB_NO_EXCEPT 51 | { 52 | const Byte* bytePointer = reinterpret_cast(pointer); 53 | const Byte* dataEnd = m_data + m_size; 54 | 55 | PDB_ASSERT(bytePointer >= m_data && bytePointer <= dataEnd, 56 | "Pointer 0x%016" PRIXPTR " not within stream range [0x%016" PRIXPTR ":0x%016" PRIXPTR "]", 57 | reinterpret_cast(bytePointer), reinterpret_cast(m_data), reinterpret_cast(dataEnd)); 58 | 59 | return static_cast(bytePointer - m_data); 60 | } 61 | 62 | private: 63 | // contiguous, coalesced data, can be null 64 | Byte* m_ownedData; 65 | 66 | // either points to the owned data that has been copied from disjunct blocks, or points to the 67 | // memory-mapped data directly in case all stream blocks are contiguous. 68 | const Byte* m_data; 69 | size_t m_size; 70 | 71 | PDB_DISABLE_COPY(CoalescedMSFStream); 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /src/PDB_DBIStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "PDB_ErrorCodes.h" 8 | #include "PDB_DBITypes.h" 9 | #include "PDB_CoalescedMSFStream.h" 10 | #include "PDB_DirectMSFStream.h" 11 | #include "PDB_ImageSectionStream.h" 12 | #include "PDB_PublicSymbolStream.h" 13 | #include "PDB_GlobalSymbolStream.h" 14 | #include "PDB_SourceFileStream.h" 15 | #include "PDB_SectionContributionStream.h" 16 | #include "PDB_ModuleInfoStream.h" 17 | 18 | 19 | // PDB DBI Stream 20 | // https://llvm.org/docs/PDB/DbiStream.html 21 | namespace PDB 22 | { 23 | class RawFile; 24 | 25 | 26 | class PDB_NO_DISCARD DBIStream 27 | { 28 | public: 29 | DBIStream(void) PDB_NO_EXCEPT; 30 | explicit DBIStream(const RawFile& file, const DBI::StreamHeader& header) PDB_NO_EXCEPT; 31 | 32 | PDB_DEFAULT_MOVE(DBIStream); 33 | 34 | PDB_NO_DISCARD ErrorCode HasValidSymbolRecordStream(const RawFile& file) const PDB_NO_EXCEPT; 35 | PDB_NO_DISCARD ErrorCode HasValidImageSectionStream(const RawFile& file) const PDB_NO_EXCEPT; 36 | PDB_NO_DISCARD ErrorCode HasValidPublicSymbolStream(const RawFile& file) const PDB_NO_EXCEPT; 37 | PDB_NO_DISCARD ErrorCode HasValidGlobalSymbolStream(const RawFile& file) const PDB_NO_EXCEPT; 38 | PDB_NO_DISCARD ErrorCode HasValidSectionContributionStream(const RawFile& file) const PDB_NO_EXCEPT; 39 | 40 | PDB_NO_DISCARD CoalescedMSFStream CreateSymbolRecordStream(const RawFile& file) const PDB_NO_EXCEPT; 41 | PDB_NO_DISCARD ImageSectionStream CreateImageSectionStream(const RawFile& file) const PDB_NO_EXCEPT; 42 | PDB_NO_DISCARD PublicSymbolStream CreatePublicSymbolStream(const RawFile& file) const PDB_NO_EXCEPT; 43 | PDB_NO_DISCARD GlobalSymbolStream CreateGlobalSymbolStream(const RawFile& file) const PDB_NO_EXCEPT; 44 | PDB_NO_DISCARD SourceFileStream CreateSourceFileStream(const RawFile& file) const PDB_NO_EXCEPT; 45 | PDB_NO_DISCARD SectionContributionStream CreateSectionContributionStream(const RawFile& file) const PDB_NO_EXCEPT; 46 | PDB_NO_DISCARD ModuleInfoStream CreateModuleInfoStream(const RawFile& file) const PDB_NO_EXCEPT; 47 | 48 | PDB_NO_DISCARD const DBI::StreamHeader& GetHeader(void) const PDB_NO_EXCEPT 49 | { 50 | return m_header; 51 | } 52 | 53 | private: 54 | DBI::StreamHeader m_header; 55 | DirectMSFStream m_stream; 56 | 57 | PDB_DISABLE_COPY(DBIStream); 58 | }; 59 | 60 | // Returns whether the given raw file provides a valid DBI stream. 61 | PDB_NO_DISCARD ErrorCode HasValidDBIStream(const RawFile& file) PDB_NO_EXCEPT; 62 | 63 | // Creates the DBI stream from a raw file. 64 | PDB_NO_DISCARD DBIStream CreateDBIStream(const RawFile& file) PDB_NO_EXCEPT; 65 | } 66 | -------------------------------------------------------------------------------- /src/PDB_DBITypes.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_DBITypes.h" 6 | 7 | 8 | const uint32_t PDB::DBI::StreamHeader::Signature = 0xffffffffu; 9 | const uint16_t PDB::DBI::DebugHeader::InvalidStreamIndex = 0xFFFFu; 10 | -------------------------------------------------------------------------------- /src/PDB_DirectMSFStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_DirectMSFStream.h" 6 | #include "Foundation/PDB_PointerUtil.h" 7 | #include "Foundation/PDB_BitUtil.h" 8 | #include "Foundation/PDB_Assert.h" 9 | #include "Foundation/PDB_DisableWarningsPush.h" 10 | #include 11 | #include "Foundation/PDB_DisableWarningsPop.h" 12 | 13 | 14 | // ------------------------------------------------------------------------------------------------ 15 | // ------------------------------------------------------------------------------------------------ 16 | PDB::DirectMSFStream::DirectMSFStream(void) PDB_NO_EXCEPT 17 | : m_data(nullptr) 18 | , m_blockIndices(nullptr) 19 | , m_blockSize(0u) 20 | , m_size(0u) 21 | , m_blockSizeLog2(0u) 22 | { 23 | } 24 | 25 | 26 | // ------------------------------------------------------------------------------------------------ 27 | // ------------------------------------------------------------------------------------------------ 28 | PDB::DirectMSFStream::DirectMSFStream(const void* data, uint32_t blockSize, const uint32_t* blockIndices, uint32_t streamSize) PDB_NO_EXCEPT 29 | : m_data(data) 30 | , m_blockIndices(blockIndices) 31 | , m_blockSize(blockSize) 32 | , m_size(streamSize) 33 | , m_blockSizeLog2(BitUtil::FindFirstSetBit(blockSize)) 34 | { 35 | PDB_ASSERT(BitUtil::IsPowerOfTwo(blockSize), "MSF block size must be a power of two."); 36 | } 37 | 38 | 39 | // ------------------------------------------------------------------------------------------------ 40 | // ------------------------------------------------------------------------------------------------ 41 | void PDB::DirectMSFStream::ReadAtOffset(void* destination, size_t size, size_t offset) const PDB_NO_EXCEPT 42 | { 43 | PDB_ASSERT(destination != nullptr, "Destination buffer not set"); 44 | PDB_ASSERT(offset + size <= m_size, "Not enough data left to read."); 45 | 46 | // work out which block and offset within the block the read offset corresponds to 47 | size_t blockIndex = offset >> m_blockSizeLog2; 48 | const size_t offsetWithinBlock = offset & (m_blockSize - 1u); 49 | 50 | // work out the offset within the data based on the block indices 51 | size_t offsetWithinData = (static_cast(m_blockIndices[blockIndex]) << m_blockSizeLog2) + offsetWithinBlock; 52 | const size_t bytesLeftInBlock = m_blockSize - offsetWithinBlock; 53 | 54 | if (bytesLeftInBlock >= size) 55 | { 56 | // fast path, all the data can be read in one go 57 | const void* const sourceData = Pointer::Offset(m_data, offsetWithinData); 58 | std::memcpy(destination, sourceData, size); 59 | } 60 | else 61 | { 62 | // slower path, data is scattered across several blocks. 63 | // read remaining bytes in current block first. 64 | { 65 | const void* const sourceData = Pointer::Offset(m_data, offsetWithinData); 66 | std::memcpy(destination, sourceData, bytesLeftInBlock); 67 | } 68 | 69 | // read remaining bytes from blocks 70 | size_t bytesLeftToRead = size - bytesLeftInBlock; 71 | while (bytesLeftToRead != 0u) 72 | { 73 | // advance to the next block 74 | ++blockIndex; 75 | offsetWithinData = static_cast(m_blockIndices[blockIndex]) << m_blockSizeLog2; 76 | 77 | void* const destinationData = Pointer::Offset(destination, size - bytesLeftToRead); 78 | const void* const sourceData = Pointer::Offset(m_data, offsetWithinData); 79 | 80 | if (bytesLeftToRead > m_blockSize) 81 | { 82 | // copy a whole block at once 83 | std::memcpy(destinationData, sourceData, m_blockSize); 84 | bytesLeftToRead -= m_blockSize; 85 | } 86 | else 87 | { 88 | // copy remaining bytes 89 | std::memcpy(destinationData, sourceData, bytesLeftToRead); 90 | bytesLeftToRead -= bytesLeftToRead; 91 | } 92 | } 93 | } 94 | } 95 | 96 | 97 | // ------------------------------------------------------------------------------------------------ 98 | // ------------------------------------------------------------------------------------------------ 99 | PDB_NO_DISCARD PDB::DirectMSFStream::IndexAndOffset PDB::DirectMSFStream::GetBlockIndexForOffset(uint32_t offset) const PDB_NO_EXCEPT 100 | { 101 | // work out which block and offset within the block the offset corresponds to 102 | const uint32_t blockIndex = offset >> m_blockSizeLog2; 103 | const uint32_t offsetWithinBlock = offset & (m_blockSize - 1u); 104 | 105 | return IndexAndOffset { blockIndex, offsetWithinBlock }; 106 | } 107 | 108 | 109 | // ------------------------------------------------------------------------------------------------ 110 | // ------------------------------------------------------------------------------------------------ 111 | PDB_NO_DISCARD size_t PDB::DirectMSFStream::GetDataOffsetForIndexAndOffset(const IndexAndOffset& indexAndOffset) const PDB_NO_EXCEPT 112 | { 113 | // work out the offset within the data based on the block indices 114 | const size_t offsetWithinData = (static_cast(m_blockIndices[indexAndOffset.index]) << m_blockSizeLog2) + indexAndOffset.offsetWithinBlock; 115 | 116 | return offsetWithinData; 117 | } 118 | -------------------------------------------------------------------------------- /src/PDB_DirectMSFStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_DisableWarningsPush.h" 8 | #include 9 | #include "Foundation/PDB_DisableWarningsPop.h" 10 | 11 | 12 | // https://llvm.org/docs/PDB/index.html#the-msf-container 13 | // https://llvm.org/docs/PDB/MsfFile.html 14 | namespace PDB 15 | { 16 | // provides direct access to the data of an MSF stream. 17 | // inherently thread-safe, the stream doesn't carry any internal offset or similar. 18 | // trivial to construct. 19 | // slower individual reads, but pays off when not all data of a stream is needed. 20 | class PDB_NO_DISCARD DirectMSFStream 21 | { 22 | public: 23 | DirectMSFStream(void) PDB_NO_EXCEPT; 24 | explicit DirectMSFStream(const void* data, uint32_t blockSize, const uint32_t* blockIndices, uint32_t streamSize) PDB_NO_EXCEPT; 25 | 26 | PDB_DEFAULT_MOVE(DirectMSFStream); 27 | 28 | // Reads a number of bytes from the stream. 29 | void ReadAtOffset(void* destination, size_t size, size_t offset) const PDB_NO_EXCEPT; 30 | 31 | // Reads from the stream. 32 | template 33 | PDB_NO_DISCARD inline T ReadAtOffset(size_t offset) const PDB_NO_EXCEPT 34 | { 35 | T data; 36 | ReadAtOffset(&data, sizeof(T), offset); 37 | return data; 38 | } 39 | 40 | // Returns the block size of the stream. 41 | PDB_NO_DISCARD inline uint32_t GetBlockSize(void) const PDB_NO_EXCEPT 42 | { 43 | return m_blockSize; 44 | } 45 | 46 | // Returns the size of the stream. 47 | PDB_NO_DISCARD inline uint32_t GetSize(void) const PDB_NO_EXCEPT 48 | { 49 | return m_size; 50 | } 51 | 52 | private: 53 | friend class CoalescedMSFStream; 54 | 55 | struct IndexAndOffset 56 | { 57 | uint32_t index; 58 | uint32_t offsetWithinBlock; 59 | }; 60 | 61 | // Returns the block index and offset within the block that correspond to the given offset. 62 | PDB_NO_DISCARD IndexAndOffset GetBlockIndexForOffset(uint32_t offset) const PDB_NO_EXCEPT; 63 | 64 | // Returns the offset into the data that corresponds to the given indices and offset within a block. 65 | PDB_NO_DISCARD size_t GetDataOffsetForIndexAndOffset(const IndexAndOffset& indexAndOffset) const PDB_NO_EXCEPT; 66 | 67 | // Provides read-only access to the memory-mapped data. 68 | PDB_NO_DISCARD inline const void* GetData(void) const PDB_NO_EXCEPT 69 | { 70 | return m_data; 71 | } 72 | 73 | // Provides read-only access to the block indices. 74 | PDB_NO_DISCARD inline const uint32_t* GetBlockIndices(void) const PDB_NO_EXCEPT 75 | { 76 | return m_blockIndices; 77 | } 78 | 79 | const void* m_data; 80 | const uint32_t* m_blockIndices; 81 | uint32_t m_blockSize; 82 | uint32_t m_size; 83 | uint32_t m_blockSizeLog2; 84 | 85 | PDB_DISABLE_COPY(DirectMSFStream); 86 | }; 87 | } 88 | -------------------------------------------------------------------------------- /src/PDB_ErrorCodes.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | 8 | 9 | namespace PDB 10 | { 11 | enum class PDB_NO_DISCARD ErrorCode : unsigned int 12 | { 13 | Success = 0u, 14 | 15 | // main PDB validation 16 | InvalidDataSize, 17 | InvalidSuperBlock, 18 | InvalidFreeBlockMap, 19 | 20 | // stream validation 21 | InvalidStream, 22 | InvalidSignature, 23 | InvalidStreamIndex, 24 | UnknownVersion 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /src/PDB_GlobalSymbolStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_GlobalSymbolStream.h" 6 | #include "PDB_RawFile.h" 7 | #include "PDB_Types.h" 8 | #include "PDB_DBITypes.h" 9 | 10 | 11 | // ------------------------------------------------------------------------------------------------ 12 | // ------------------------------------------------------------------------------------------------ 13 | PDB::GlobalSymbolStream::GlobalSymbolStream(void) PDB_NO_EXCEPT 14 | : m_stream() 15 | , m_hashRecords(nullptr) 16 | , m_count(0u) 17 | { 18 | } 19 | 20 | 21 | // ------------------------------------------------------------------------------------------------ 22 | // ------------------------------------------------------------------------------------------------ 23 | PDB::GlobalSymbolStream::GlobalSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t count) PDB_NO_EXCEPT 24 | : m_stream(file.CreateMSFStream(streamIndex)) 25 | , m_hashRecords(m_stream.GetDataAtOffset(sizeof(HashTableHeader))) 26 | , m_count(count) 27 | { 28 | } 29 | 30 | 31 | // ------------------------------------------------------------------------------------------------ 32 | // ------------------------------------------------------------------------------------------------ 33 | PDB_NO_DISCARD const PDB::CodeView::DBI::Record* PDB::GlobalSymbolStream::GetRecord(const CoalescedMSFStream& symbolRecordStream, const HashRecord& hashRecord) const PDB_NO_EXCEPT 34 | { 35 | // hash record offsets start at 1, not at 0 36 | const uint32_t headerOffset = hashRecord.offset - 1u; 37 | 38 | // the offset doesn't point to the global symbol directly, but to the CodeView record: 39 | // https://llvm.org/docs/PDB/CodeViewSymbols.html 40 | const CodeView::DBI::Record* record = symbolRecordStream.GetDataAtOffset(headerOffset); 41 | 42 | return record; 43 | } 44 | -------------------------------------------------------------------------------- /src/PDB_GlobalSymbolStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_ArrayView.h" 8 | #include "PDB_CoalescedMSFStream.h" 9 | 10 | 11 | namespace PDB 12 | { 13 | class RawFile; 14 | struct HashRecord; 15 | 16 | namespace CodeView 17 | { 18 | namespace DBI 19 | { 20 | struct Record; 21 | } 22 | } 23 | 24 | 25 | class PDB_NO_DISCARD GlobalSymbolStream 26 | { 27 | public: 28 | GlobalSymbolStream(void) PDB_NO_EXCEPT; 29 | explicit GlobalSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t count) PDB_NO_EXCEPT; 30 | 31 | PDB_DEFAULT_MOVE(GlobalSymbolStream); 32 | 33 | // Turns a given hash record into a DBI record using the given symbol stream. 34 | PDB_NO_DISCARD const CodeView::DBI::Record* GetRecord(const CoalescedMSFStream& symbolRecordStream, const HashRecord& hashRecord) const PDB_NO_EXCEPT; 35 | 36 | // Returns a view of all the records in the stream. 37 | PDB_NO_DISCARD inline ArrayView GetRecords(void) const PDB_NO_EXCEPT 38 | { 39 | return ArrayView(m_hashRecords, m_count); 40 | } 41 | 42 | private: 43 | CoalescedMSFStream m_stream; 44 | const HashRecord* m_hashRecords; 45 | uint32_t m_count; 46 | 47 | PDB_DISABLE_COPY(GlobalSymbolStream); 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /src/PDB_IPIStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_IPIStream.h" 6 | #include "PDB_RawFile.h" 7 | #include "PDB_Util.h" 8 | #include "PDB_DirectMSFStream.h" 9 | #include "PDB_InfoStream.h" 10 | #include "Foundation/PDB_Memory.h" 11 | 12 | namespace 13 | { 14 | // the IPI stream always resides at index 4 15 | static constexpr const uint32_t IPIStreamIndex = 4u; 16 | } 17 | 18 | 19 | // ------------------------------------------------------------------------------------------------ 20 | // ------------------------------------------------------------------------------------------------ 21 | PDB::IPIStream::IPIStream(void) PDB_NO_EXCEPT 22 | : m_header() 23 | , m_stream() 24 | , m_records(nullptr) 25 | , m_recordCount(0u) 26 | { 27 | } 28 | 29 | 30 | // ------------------------------------------------------------------------------------------------ 31 | // ------------------------------------------------------------------------------------------------ 32 | PDB::IPIStream::IPIStream(IPIStream&& other) PDB_NO_EXCEPT 33 | : m_header(PDB_MOVE(other.m_header)) 34 | , m_stream(PDB_MOVE(other.m_stream)) 35 | , m_records(PDB_MOVE(other.m_records)) 36 | , m_recordCount(PDB_MOVE(other.m_recordCount)) 37 | { 38 | other.m_records = nullptr; 39 | other.m_recordCount = 0u; 40 | } 41 | 42 | 43 | // ------------------------------------------------------------------------------------------------ 44 | // ------------------------------------------------------------------------------------------------ 45 | PDB::IPIStream& PDB::IPIStream::operator=(IPIStream&& other) PDB_NO_EXCEPT 46 | { 47 | if (this != &other) 48 | { 49 | PDB_DELETE_ARRAY(m_records); 50 | 51 | m_header = PDB_MOVE(other.m_header); 52 | m_stream = PDB_MOVE(other.m_stream); 53 | m_records = PDB_MOVE(other.m_records); 54 | m_recordCount = PDB_MOVE(other.m_recordCount); 55 | 56 | other.m_records = nullptr; 57 | other.m_recordCount = 0u; 58 | } 59 | 60 | return *this; 61 | } 62 | 63 | 64 | // ------------------------------------------------------------------------------------------------ 65 | // ------------------------------------------------------------------------------------------------ 66 | PDB::IPIStream::IPIStream(const RawFile& file, const IPI::StreamHeader& header) PDB_NO_EXCEPT 67 | : m_header(header) 68 | , m_stream(file.CreateMSFStream(IPIStreamIndex)) 69 | , m_records(nullptr) 70 | , m_recordCount(GetLastTypeIndex() - GetFirstTypeIndex()) 71 | { 72 | // types in the IPI stream are accessed by their index from other streams. 73 | // however, the index is not stored with types in the IPI stream directly, but has to be built while walking the stream. 74 | // similarly, because types are variable-length records, there are no direct offsets to access individual types. 75 | // we therefore walk the IPI stream once, and store pointers to the records for trivial O(N) array lookup by index later. 76 | m_records = PDB_NEW_ARRAY(const CodeView::IPI::Record*, m_recordCount); 77 | 78 | // ignore the stream's header 79 | size_t offset = sizeof(IPI::StreamHeader); 80 | 81 | // parse the CodeView records 82 | uint32_t typeIndex = 0u; 83 | while (offset < m_stream.GetSize()) 84 | { 85 | // https://llvm.org/docs/PDB/CodeViewTypes.html 86 | const CodeView::IPI::Record* record = m_stream.GetDataAtOffset(offset); 87 | const uint32_t recordSize = GetCodeViewRecordSize(record); 88 | m_records[typeIndex] = record; 89 | 90 | // position the stream offset at the next record 91 | offset += sizeof(CodeView::IPI::RecordHeader) + recordSize; 92 | 93 | ++typeIndex; 94 | } 95 | } 96 | 97 | 98 | // ------------------------------------------------------------------------------------------------ 99 | // ------------------------------------------------------------------------------------------------ 100 | PDB::IPIStream::~IPIStream(void) PDB_NO_EXCEPT 101 | { 102 | PDB_DELETE_ARRAY(m_records); 103 | } 104 | 105 | 106 | // ------------------------------------------------------------------------------------------------ 107 | // ------------------------------------------------------------------------------------------------ 108 | PDB_NO_DISCARD PDB::ErrorCode PDB::HasValidIPIStream(const RawFile& file) PDB_NO_EXCEPT 109 | { 110 | const PDB::InfoStream infoStream(file); 111 | if (!infoStream.HasIPIStream()) 112 | { 113 | return ErrorCode::InvalidStream; 114 | } 115 | 116 | DirectMSFStream stream = file.CreateMSFStream(IPIStreamIndex); 117 | if (stream.GetSize() < sizeof(IPI::StreamHeader)) 118 | { 119 | return ErrorCode::InvalidStream; 120 | } 121 | 122 | const IPI::StreamHeader header = stream.ReadAtOffset(0u); 123 | if (header.version != IPI::StreamHeader::Version::V80) 124 | { 125 | return ErrorCode::UnknownVersion; 126 | } 127 | 128 | return ErrorCode::Success; 129 | } 130 | 131 | 132 | // ------------------------------------------------------------------------------------------------ 133 | // ------------------------------------------------------------------------------------------------ 134 | PDB_NO_DISCARD PDB::IPIStream PDB::CreateIPIStream(const RawFile& file) PDB_NO_EXCEPT 135 | { 136 | DirectMSFStream stream = file.CreateMSFStream(IPIStreamIndex); 137 | 138 | const IPI::StreamHeader header = stream.ReadAtOffset(0u); 139 | return IPIStream { file, header }; 140 | } 141 | -------------------------------------------------------------------------------- /src/PDB_IPIStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_ArrayView.h" 8 | #include "PDB_ErrorCodes.h" 9 | #include "PDB_IPITypes.h" 10 | #include "PDB_CoalescedMSFStream.h" 11 | 12 | 13 | // PDB IPI stream 14 | // https://llvm.org/docs/PDB/TpiStream.html 15 | namespace PDB 16 | { 17 | class RawFile; 18 | 19 | 20 | class PDB_NO_DISCARD IPIStream 21 | { 22 | public: 23 | IPIStream(void) PDB_NO_EXCEPT; 24 | IPIStream(IPIStream&& other) PDB_NO_EXCEPT; 25 | IPIStream& operator=(IPIStream&& other) PDB_NO_EXCEPT; 26 | 27 | explicit IPIStream(const RawFile& file, const IPI::StreamHeader& header) PDB_NO_EXCEPT; 28 | ~IPIStream(void) PDB_NO_EXCEPT; 29 | 30 | // Returns the index of the first type, which is not necessarily zero. 31 | PDB_NO_DISCARD inline uint32_t GetFirstTypeIndex(void) const PDB_NO_EXCEPT 32 | { 33 | return m_header.typeIndexBegin; 34 | } 35 | 36 | // Returns the index of the last type. 37 | PDB_NO_DISCARD inline uint32_t GetLastTypeIndex(void) const PDB_NO_EXCEPT 38 | { 39 | return m_header.typeIndexEnd; 40 | } 41 | 42 | // Returns a view of all type records. 43 | // Records identified by a type index can be accessed via "allRecords[typeIndex - firstTypeIndex]". 44 | PDB_NO_DISCARD inline ArrayView GetTypeRecords(void) const PDB_NO_EXCEPT 45 | { 46 | return ArrayView(m_records, m_recordCount); 47 | } 48 | 49 | private: 50 | IPI::StreamHeader m_header; 51 | CoalescedMSFStream m_stream; 52 | const CodeView::IPI::Record** m_records; 53 | size_t m_recordCount; 54 | 55 | PDB_DISABLE_COPY(IPIStream); 56 | }; 57 | 58 | 59 | // ------------------------------------------------------------------------------------------------ 60 | // General 61 | // ------------------------------------------------------------------------------------------------ 62 | 63 | PDB_NO_DISCARD ErrorCode HasValidIPIStream(const RawFile& file) PDB_NO_EXCEPT; 64 | 65 | PDB_NO_DISCARD IPIStream CreateIPIStream(const RawFile& file) PDB_NO_EXCEPT; 66 | } 67 | -------------------------------------------------------------------------------- /src/PDB_IPITypes.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_DisableWarningsPush.h" 8 | #include 9 | #include "Foundation/PDB_DisableWarningsPop.h" 10 | 11 | 12 | namespace PDB 13 | { 14 | namespace IPI 15 | { 16 | // https://llvm.org/docs/PDB/TpiStream.html#tpi-header 17 | struct StreamHeader 18 | { 19 | enum class PDB_NO_DISCARD Version : uint32_t 20 | { 21 | V40 = 19950410u, 22 | V41 = 19951122u, 23 | V50 = 19961031u, 24 | V70 = 19990903u, 25 | V80 = 20040203u 26 | }; 27 | 28 | Version version; 29 | uint32_t headerSize; 30 | uint32_t typeIndexBegin; 31 | uint32_t typeIndexEnd; 32 | uint32_t typeRecordBytes; 33 | uint16_t hashStreamIndex; 34 | uint16_t hashAuxStreamIndex; 35 | uint32_t hashKeySize; 36 | uint32_t hashBucketCount; 37 | uint32_t hashValueBufferOffset; 38 | uint32_t hashValueBufferLength; 39 | uint32_t indexOffsetBufferOffset; 40 | uint32_t indexOffsetBufferLength; 41 | uint32_t hashAdjBufferOffset; 42 | uint32_t hashAdjBufferLength; 43 | }; 44 | } 45 | 46 | 47 | namespace CodeView 48 | { 49 | namespace IPI 50 | { 51 | // code view type records that can appear in an IPI stream 52 | // https://llvm.org/docs/PDB/CodeViewTypes.html 53 | // https://llvm.org/docs/PDB/TpiStream.html#tpi-vs-ipi-stream 54 | enum class PDB_NO_DISCARD TypeRecordKind : uint16_t 55 | { 56 | LF_FUNC_ID = 0x1601u, // global function ID 57 | LF_MFUNC_ID = 0x1602u, // member function ID 58 | LF_BUILDINFO = 0x1603u, // build information 59 | LF_SUBSTR_LIST = 0x1604u, // similar to LF_ARGLIST for a list of substrings 60 | LF_STRING_ID = 0x1605u, // string ID 61 | LF_UDT_SRC_LINE = 0x1606u, // source and line on where an UDT is defined, generated by the compiler 62 | LF_UDT_MOD_SRC_LINE = 0x1607u // module, source and line on where an UDT is defined, generated by the linker 63 | }; 64 | 65 | // https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1715 66 | enum class PDB_NO_DISCARD BuildInfoType : uint8_t 67 | { 68 | CurrentDirectory, // compiler working directory 69 | BuildTool, // tool path 70 | SourceFile, // path to source file, relative or absolute 71 | TypeServerPDB, // path to PDB file 72 | CommandLine // command-line used to build the source file 73 | }; 74 | 75 | struct RecordHeader 76 | { 77 | uint16_t size; // record length, not including this 2-byte field 78 | TypeRecordKind kind; // record kind 79 | }; 80 | 81 | // all CodeView records are stored as a header, followed by variable-length data. 82 | // internal Record structs such as S_PUB32, S_GDATA32, etc. correspond to the data layout of a CodeView record of that kind. 83 | struct Record 84 | { 85 | RecordHeader header; 86 | union Data 87 | { 88 | #pragma pack(push, 1) 89 | // https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1694 90 | struct 91 | { 92 | uint32_t id; // ID to list of sub-string IDs 93 | PDB_FLEXIBLE_ARRAY_MEMBER(char, name); 94 | } LF_STRING_ID; 95 | 96 | // https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L2043 97 | struct 98 | { 99 | uint32_t count; 100 | PDB_FLEXIBLE_ARRAY_MEMBER(uint32_t, typeIndices); 101 | } LF_SUBSTR_LIST; 102 | 103 | // https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1726 104 | 105 | struct 106 | { 107 | uint16_t count; 108 | PDB_FLEXIBLE_ARRAY_MEMBER(uint32_t, typeIndices); 109 | } LF_BUILDINFO; 110 | #pragma pack(pop) 111 | } data; 112 | }; 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/PDB_ImageSectionStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_ImageSectionStream.h" 6 | #include "PDB_RawFile.h" 7 | 8 | 9 | // ------------------------------------------------------------------------------------------------ 10 | // ------------------------------------------------------------------------------------------------ 11 | PDB::ImageSectionStream::ImageSectionStream(void) PDB_NO_EXCEPT 12 | : m_stream() 13 | , m_headers(nullptr) 14 | , m_count(0u) 15 | { 16 | } 17 | 18 | 19 | // ------------------------------------------------------------------------------------------------ 20 | // ------------------------------------------------------------------------------------------------ 21 | PDB::ImageSectionStream::ImageSectionStream(const RawFile& file, uint16_t streamIndex) PDB_NO_EXCEPT 22 | : m_stream(file.CreateMSFStream(streamIndex)) 23 | , m_headers(m_stream.GetDataAtOffset(0u)) 24 | , m_count(m_stream.GetSize() / sizeof(IMAGE_SECTION_HEADER)) 25 | { 26 | } 27 | 28 | 29 | // ------------------------------------------------------------------------------------------------ 30 | // ------------------------------------------------------------------------------------------------ 31 | PDB_NO_DISCARD uint32_t PDB::ImageSectionStream::ConvertSectionOffsetToRVA(uint16_t oneBasedSectionIndex, uint32_t offsetInSection) const PDB_NO_EXCEPT 32 | { 33 | if (oneBasedSectionIndex == 0u) 34 | { 35 | // should never happen, but prevent underflow 36 | return 0u; 37 | } 38 | else if (oneBasedSectionIndex > m_count) 39 | { 40 | // this symbol is "contained" in a section that is neither part of the PDB, nor the EXE. 41 | // it is a special compiler-generated or linker-generated symbol such as CFG symbols (e.g. __guard_fids_count, __guard_flags). 42 | // we can safely ignore those symbols. 43 | return 0u; 44 | } 45 | 46 | return m_headers[oneBasedSectionIndex - 1u].VirtualAddress + offsetInSection; 47 | } 48 | -------------------------------------------------------------------------------- /src/PDB_ImageSectionStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_ArrayView.h" 8 | #include "PDB_Types.h" 9 | #include "PDB_CoalescedMSFStream.h" 10 | 11 | 12 | namespace PDB 13 | { 14 | class RawFile; 15 | struct IMAGE_SECTION_HEADER; 16 | 17 | 18 | class PDB_NO_DISCARD ImageSectionStream 19 | { 20 | public: 21 | ImageSectionStream(void) PDB_NO_EXCEPT; 22 | explicit ImageSectionStream(const RawFile& file, uint16_t streamIndex) PDB_NO_EXCEPT; 23 | 24 | PDB_DEFAULT_MOVE(ImageSectionStream); 25 | 26 | // Converts a one-based section offset into an RVA. 27 | PDB_NO_DISCARD uint32_t ConvertSectionOffsetToRVA(uint16_t oneBasedSectionIndex, uint32_t offsetInSection) const PDB_NO_EXCEPT; 28 | 29 | // Returns a view of all the sections in the stream. 30 | PDB_NO_DISCARD inline ArrayView GetImageSections(void) const PDB_NO_EXCEPT 31 | { 32 | return ArrayView(m_headers, m_count); 33 | } 34 | 35 | private: 36 | CoalescedMSFStream m_stream; 37 | const IMAGE_SECTION_HEADER* m_headers; 38 | size_t m_count; 39 | 40 | PDB_DISABLE_COPY(ImageSectionStream); 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /src/PDB_InfoStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_InfoStream.h" 6 | #include "PDB_RawFile.h" 7 | 8 | 9 | namespace 10 | { 11 | // the PDB info stream always resides at index 1 12 | static constexpr const uint32_t InfoStreamIndex = 1u; 13 | } 14 | 15 | 16 | // ------------------------------------------------------------------------------------------------ 17 | // ------------------------------------------------------------------------------------------------ 18 | PDB::InfoStream::InfoStream(void) PDB_NO_EXCEPT 19 | : m_stream() 20 | , m_header(nullptr) 21 | , m_namesStreamIndex(0) 22 | , m_usesDebugFastlink(false) 23 | , m_hasIPIStream(false) 24 | { 25 | } 26 | 27 | 28 | // ------------------------------------------------------------------------------------------------ 29 | // ------------------------------------------------------------------------------------------------ 30 | PDB::InfoStream::InfoStream(const RawFile& file) PDB_NO_EXCEPT 31 | : m_stream(file.CreateMSFStream(InfoStreamIndex)) 32 | , m_header(m_stream.GetDataAtOffset(0u)) 33 | , m_namesStreamIndex(0) 34 | , m_usesDebugFastlink(false) 35 | , m_hasIPIStream(false) 36 | { 37 | // the info stream starts with the header, followed by the named stream map, followed by the feature codes 38 | // https://llvm.org/docs/PDB/PdbStream.html#named-stream-map 39 | size_t streamOffset = sizeof(Header); 40 | 41 | const NamedStreamMap* namedStreamMap = m_stream.GetDataAtOffset(streamOffset); 42 | streamOffset += sizeof(NamedStreamMap) + namedStreamMap->length; 43 | 44 | const SerializedHashTable::Header* hashTableHeader = m_stream.GetDataAtOffset(streamOffset); 45 | streamOffset += sizeof(SerializedHashTable::Header); 46 | 47 | const SerializedHashTable::BitVector* presentBitVector = m_stream.GetDataAtOffset(streamOffset); 48 | streamOffset += sizeof(SerializedHashTable::BitVector) + sizeof(uint32_t) * presentBitVector->wordCount; 49 | 50 | const SerializedHashTable::BitVector* deletedBitVector = m_stream.GetDataAtOffset(streamOffset); 51 | streamOffset += sizeof(SerializedHashTable::BitVector) + sizeof(uint32_t) * deletedBitVector->wordCount; 52 | 53 | // the hash table entries can be used to identify the indices of certain common streams like: 54 | // "/UDTSRCLINEUNDONE" 55 | // "/src/headerblock" 56 | // "/LinkInfo" 57 | // "/TMCache" 58 | // "/names" 59 | 60 | const NamedStreamMap::HashTableEntry* namedStreamMapHashEntries = m_stream.GetDataAtOffset(streamOffset); 61 | 62 | // Find "/names" stream, used to look up filenames for lines. 63 | for (uint32_t i = 0, size = hashTableHeader->size; i < size; ++i) 64 | { 65 | const NamedStreamMap::HashTableEntry& entry = namedStreamMapHashEntries[i]; 66 | const char* streamName = &namedStreamMap->stringTable[entry.stringTableOffset]; 67 | 68 | if (std::strcmp("/names", streamName) == 0) 69 | { 70 | m_namesStreamIndex = entry.streamIndex; 71 | } 72 | } 73 | 74 | streamOffset += sizeof(NamedStreamMap::HashTableEntry) * hashTableHeader->size; 75 | 76 | // read feature codes by consuming remaining bytes 77 | // https://llvm.org/docs/PDB/PdbStream.html#pdb-feature-codes 78 | const FeatureCode* featureCodes = m_stream.GetDataAtOffset(streamOffset); 79 | const size_t remainingBytes = m_stream.GetSize() - streamOffset; 80 | const size_t count = remainingBytes / sizeof(FeatureCode); 81 | 82 | for (size_t i=0u; i < count; ++i) 83 | { 84 | FeatureCode code = featureCodes[i]; 85 | if (code == PDB::FeatureCode::MinimalDebugInfo) 86 | { 87 | m_usesDebugFastlink = true; 88 | } 89 | else if (code == PDB::FeatureCode::VC110 || code == PDB::FeatureCode::VC140) 90 | { 91 | m_hasIPIStream = true; 92 | } 93 | } 94 | } 95 | 96 | 97 | // ------------------------------------------------------------------------------------------------ 98 | // ------------------------------------------------------------------------------------------------ 99 | PDB::NamesStream PDB::InfoStream::CreateNamesStream(const RawFile& file) const PDB_NO_EXCEPT 100 | { 101 | return NamesStream(file, m_namesStreamIndex); 102 | } 103 | -------------------------------------------------------------------------------- /src/PDB_InfoStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "PDB_Types.h" 8 | #include "PDB_CoalescedMSFStream.h" 9 | #include "PDB_NamesStream.h" 10 | 11 | namespace PDB 12 | { 13 | class RawFile; 14 | 15 | 16 | // PDB Info Stream 17 | // https://llvm.org/docs/PDB/PdbStream.html 18 | class PDB_NO_DISCARD InfoStream 19 | { 20 | public: 21 | InfoStream(void) PDB_NO_EXCEPT; 22 | explicit InfoStream(const RawFile& file) PDB_NO_EXCEPT; 23 | 24 | PDB_DEFAULT_MOVE(InfoStream); 25 | 26 | // Returns the header of the stream. 27 | PDB_NO_DISCARD inline const Header* GetHeader(void) const PDB_NO_EXCEPT 28 | { 29 | return m_header; 30 | } 31 | 32 | // Returns whether the module has a names stream. 33 | PDB_NO_DISCARD inline bool HasNamesStream(void) const PDB_NO_EXCEPT 34 | { 35 | return (m_namesStreamIndex != 0u); 36 | } 37 | 38 | // Returns whether the PDB file was linked using /DEBUG:FASTLINK. 39 | PDB_NO_DISCARD inline bool UsesDebugFastLink(void) const PDB_NO_EXCEPT 40 | { 41 | return m_usesDebugFastlink; 42 | } 43 | 44 | // Returns whether the PDB file has an IPI stream. 45 | PDB_NO_DISCARD inline bool HasIPIStream(void) const PDB_NO_EXCEPT 46 | { 47 | return m_hasIPIStream; 48 | } 49 | 50 | // Create names stream 51 | PDB_NO_DISCARD NamesStream CreateNamesStream(const RawFile& file) const PDB_NO_EXCEPT; 52 | 53 | private: 54 | CoalescedMSFStream m_stream; 55 | const Header* m_header; 56 | uint32_t m_namesStreamIndex; 57 | bool m_usesDebugFastlink; 58 | bool m_hasIPIStream; 59 | 60 | PDB_DISABLE_COPY(InfoStream); 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /src/PDB_ModuleInfoStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_ModuleInfoStream.h" 6 | #include "Foundation/PDB_Memory.h" 7 | #include "Foundation/PDB_DisableWarningsPush.h" 8 | #include 9 | #include "Foundation/PDB_DisableWarningsPop.h" 10 | 11 | 12 | namespace 13 | { 14 | static constexpr const char* LinkerSymbolName("* Linker *"); 15 | 16 | 17 | // ------------------------------------------------------------------------------------------------ 18 | // ------------------------------------------------------------------------------------------------ 19 | PDB_NO_DISCARD static inline size_t EstimateModuleCount(size_t streamSize) PDB_NO_EXCEPT 20 | { 21 | // work out how many modules are stored in the stream at most. 22 | // the module info is stored in variable-length records, so we can't determine the exact number without walking the stream. 23 | return streamSize / sizeof(PDB::DBI::ModuleInfo); 24 | } 25 | } 26 | 27 | 28 | // ------------------------------------------------------------------------------------------------ 29 | // ------------------------------------------------------------------------------------------------ 30 | PDB::ModuleInfoStream::Module::Module(void) PDB_NO_EXCEPT 31 | : m_info(nullptr) 32 | , m_name(nullptr) 33 | , m_nameLength(0u) 34 | , m_objectName(nullptr) 35 | , m_objectNameLength(0u) 36 | { 37 | } 38 | 39 | 40 | // ------------------------------------------------------------------------------------------------ 41 | // ------------------------------------------------------------------------------------------------ 42 | PDB::ModuleInfoStream::Module::Module(const DBI::ModuleInfo* info, const char* name, size_t nameLength, const char* objectName, size_t objectNameLength) PDB_NO_EXCEPT 43 | : m_info(info) 44 | , m_name(name) 45 | , m_nameLength(nameLength) 46 | , m_objectName(objectName) 47 | , m_objectNameLength(objectNameLength) 48 | { 49 | } 50 | 51 | 52 | // ------------------------------------------------------------------------------------------------ 53 | // ------------------------------------------------------------------------------------------------ 54 | PDB_NO_DISCARD bool PDB::ModuleInfoStream::Module::HasSymbolStream(void) const PDB_NO_EXCEPT 55 | { 56 | const uint16_t streamIndex = m_info->moduleSymbolStreamIndex; 57 | 58 | // some modules don't have a symbol stream, i.e. no additional debug information is present. 59 | // this usually happens when private symbols are stripped from a PDB. 60 | return (streamIndex != 0xFFFFu); 61 | } 62 | 63 | 64 | // ------------------------------------------------------------------------------------------------ 65 | // ------------------------------------------------------------------------------------------------ 66 | PDB_NO_DISCARD bool PDB::ModuleInfoStream::Module::HasLineStream(void) const PDB_NO_EXCEPT 67 | { 68 | return (m_info->c13Size > 0); 69 | } 70 | 71 | // ------------------------------------------------------------------------------------------------ 72 | // ------------------------------------------------------------------------------------------------ 73 | PDB_NO_DISCARD PDB::ModuleSymbolStream PDB::ModuleInfoStream::Module::CreateSymbolStream(const RawFile& file) const PDB_NO_EXCEPT 74 | { 75 | PDB_ASSERT(HasSymbolStream(), "Module symbol stream index is invalid."); 76 | 77 | return ModuleSymbolStream(file, m_info->moduleSymbolStreamIndex, m_info->symbolSize); 78 | } 79 | 80 | PDB_NO_DISCARD PDB::ModuleLineStream PDB::ModuleInfoStream::Module::CreateLineStream(const RawFile& file) const PDB_NO_EXCEPT 81 | { 82 | PDB_ASSERT(HasLineStream(), "Module line stream is not present."); 83 | 84 | return ModuleLineStream(file, m_info->moduleSymbolStreamIndex, m_info->symbolSize + m_info->c11Size + m_info->c13Size, m_info->symbolSize + m_info->c11Size); 85 | } 86 | 87 | // ------------------------------------------------------------------------------------------------ 88 | // ------------------------------------------------------------------------------------------------ 89 | PDB::ModuleInfoStream::ModuleInfoStream(void) PDB_NO_EXCEPT 90 | : m_stream() 91 | , m_modules(nullptr) 92 | , m_moduleCount(0u) 93 | { 94 | } 95 | 96 | 97 | // ------------------------------------------------------------------------------------------------ 98 | // ------------------------------------------------------------------------------------------------ 99 | PDB::ModuleInfoStream::ModuleInfoStream(ModuleInfoStream&& other) PDB_NO_EXCEPT 100 | : m_stream(PDB_MOVE(other.m_stream)) 101 | , m_modules(PDB_MOVE(other.m_modules)) 102 | , m_moduleCount(PDB_MOVE(other.m_moduleCount)) 103 | { 104 | other.m_modules = nullptr; 105 | other.m_moduleCount = 0u; 106 | } 107 | 108 | 109 | // ------------------------------------------------------------------------------------------------ 110 | // ------------------------------------------------------------------------------------------------ 111 | PDB::ModuleInfoStream& PDB::ModuleInfoStream::operator=(ModuleInfoStream&& other) PDB_NO_EXCEPT 112 | { 113 | if (this != &other) 114 | { 115 | PDB_DELETE_ARRAY(m_modules); 116 | 117 | m_stream = PDB_MOVE(other.m_stream); 118 | m_modules = PDB_MOVE(other.m_modules); 119 | m_moduleCount = PDB_MOVE(other.m_moduleCount); 120 | 121 | other.m_modules = nullptr; 122 | other.m_moduleCount = 0u; 123 | } 124 | 125 | return *this; 126 | } 127 | 128 | 129 | // ------------------------------------------------------------------------------------------------ 130 | // ------------------------------------------------------------------------------------------------ 131 | PDB::ModuleInfoStream::ModuleInfoStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT 132 | : m_stream(directStream, size, offset) 133 | , m_modules(nullptr) 134 | , m_moduleCount(0u) 135 | { 136 | m_modules = PDB_NEW_ARRAY(Module, EstimateModuleCount(size)); 137 | 138 | size_t streamOffset = 0u; 139 | while (streamOffset < size) 140 | { 141 | const DBI::ModuleInfo* moduleInfo = m_stream.GetDataAtOffset(streamOffset); 142 | streamOffset += sizeof(DBI::ModuleInfo); 143 | 144 | const char* name = m_stream.GetDataAtOffset(streamOffset); 145 | const size_t nameLength = std::strlen(name); 146 | streamOffset += nameLength + 1u; 147 | 148 | const char* objectName = m_stream.GetDataAtOffset(streamOffset); 149 | const size_t objectNameLength = std::strlen(objectName); 150 | streamOffset += objectNameLength + 1u; 151 | 152 | // the stream is aligned to 4 bytes 153 | streamOffset = BitUtil::RoundUpToMultiple(streamOffset, 4ul); 154 | 155 | m_modules[m_moduleCount] = Module(moduleInfo, name, nameLength, objectName, objectNameLength); 156 | ++m_moduleCount; 157 | } 158 | } 159 | 160 | 161 | // ------------------------------------------------------------------------------------------------ 162 | // ------------------------------------------------------------------------------------------------ 163 | PDB::ModuleInfoStream::~ModuleInfoStream(void) PDB_NO_EXCEPT 164 | { 165 | PDB_DELETE_ARRAY(m_modules); 166 | } 167 | 168 | 169 | // ------------------------------------------------------------------------------------------------ 170 | // ------------------------------------------------------------------------------------------------ 171 | PDB_NO_DISCARD const PDB::ModuleInfoStream::Module* PDB::ModuleInfoStream::FindLinkerModule(void) const PDB_NO_EXCEPT 172 | { 173 | const size_t count = m_moduleCount; 174 | for (size_t i = 0u; i < count; ++i) 175 | { 176 | // with both MSVC cl.exe and Clang, the linker symbol is the last one to be stored, so start searching from the end 177 | const Module& module = m_modules[count - i - 1u]; 178 | 179 | // check if this is the linker symbol 180 | if (std::strcmp(module.GetName().Decay(), LinkerSymbolName) == 0) 181 | { 182 | return &module; 183 | } 184 | } 185 | 186 | return nullptr; 187 | } 188 | -------------------------------------------------------------------------------- /src/PDB_ModuleInfoStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_ArrayView.h" 8 | #include "PDB_CoalescedMSFStream.h" 9 | #include "PDB_ModuleSymbolStream.h" 10 | #include "PDB_ModuleLineStream.h" 11 | 12 | namespace PDB 13 | { 14 | class PDB_NO_DISCARD DirectMSFStream; 15 | 16 | class PDB_NO_DISCARD ModuleInfoStream 17 | { 18 | public: 19 | class PDB_NO_DISCARD Module 20 | { 21 | public: 22 | Module(void) PDB_NO_EXCEPT; 23 | explicit Module(const DBI::ModuleInfo* info, const char* name, size_t nameLength, const char* objectName, size_t objectNameLength) PDB_NO_EXCEPT; 24 | 25 | PDB_DEFAULT_MOVE(Module); 26 | 27 | // Returns whether the module has a symbol stream. 28 | PDB_NO_DISCARD bool HasSymbolStream(void) const PDB_NO_EXCEPT; 29 | 30 | // Returns whether the module has a line stream. 31 | PDB_NO_DISCARD bool HasLineStream(void) const PDB_NO_EXCEPT; 32 | 33 | // Creates a symbol stream for the module. 34 | PDB_NO_DISCARD ModuleSymbolStream CreateSymbolStream(const RawFile& file) const PDB_NO_EXCEPT; 35 | 36 | // Create a line stream for the module 37 | PDB_NO_DISCARD ModuleLineStream CreateLineStream(const RawFile& file) const PDB_NO_EXCEPT; 38 | 39 | 40 | // Returns the PDB module info. 41 | PDB_NO_DISCARD inline const DBI::ModuleInfo* GetInfo(void) const PDB_NO_EXCEPT 42 | { 43 | return m_info; 44 | } 45 | 46 | // Returns the name of the module. 47 | PDB_NO_DISCARD inline ArrayView GetName(void) const PDB_NO_EXCEPT 48 | { 49 | return ArrayView(m_name, m_nameLength); 50 | } 51 | 52 | // Returns the name of the object file of the module. 53 | PDB_NO_DISCARD inline ArrayView GetObjectName(void) const PDB_NO_EXCEPT 54 | { 55 | return ArrayView(m_objectName, m_objectNameLength); 56 | } 57 | 58 | private: 59 | // the module info is stored in variable-length arrays inside the stream, so rather than store an array directly, 60 | // we need to store pointers to the individual data items inside the stream. 61 | const DBI::ModuleInfo* m_info; 62 | 63 | // the module name, e.g. the path to an object file or import library such as "Import:kernel32.dll" 64 | const char* m_name; 65 | size_t m_nameLength; 66 | 67 | // the name of the object file. either the same as the module name, or the path to the archive that contained the module 68 | const char* m_objectName; 69 | size_t m_objectNameLength; 70 | 71 | PDB_DISABLE_COPY(Module); 72 | }; 73 | 74 | ModuleInfoStream(void) PDB_NO_EXCEPT; 75 | ModuleInfoStream(ModuleInfoStream&& other) PDB_NO_EXCEPT; 76 | ModuleInfoStream& operator=(ModuleInfoStream&& other) PDB_NO_EXCEPT; 77 | 78 | explicit ModuleInfoStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT; 79 | 80 | ~ModuleInfoStream(void) PDB_NO_EXCEPT; 81 | 82 | // Tries to find the linker module corresponding to the linker, i.e. the module named "* Linker *". 83 | PDB_NO_DISCARD const Module* FindLinkerModule(void) const PDB_NO_EXCEPT; 84 | 85 | // Returns the module with the given index. 86 | PDB_NO_DISCARD inline const Module& GetModule(uint32_t index) const PDB_NO_EXCEPT 87 | { 88 | return m_modules[index]; 89 | } 90 | 91 | // Returns a view of all modules in the info stream. 92 | PDB_NO_DISCARD inline ArrayView GetModules(void) const PDB_NO_EXCEPT 93 | { 94 | return ArrayView(m_modules, m_moduleCount); 95 | } 96 | 97 | private: 98 | CoalescedMSFStream m_stream; 99 | Module* m_modules; 100 | size_t m_moduleCount; 101 | 102 | PDB_DISABLE_COPY(ModuleInfoStream); 103 | }; 104 | } 105 | -------------------------------------------------------------------------------- /src/PDB_ModuleLineStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_ModuleLineStream.h" 6 | #include "PDB_RawFile.h" 7 | 8 | 9 | // ------------------------------------------------------------------------------------------------ 10 | // ------------------------------------------------------------------------------------------------ 11 | PDB::ModuleLineStream::ModuleLineStream(void) PDB_NO_EXCEPT 12 | : m_stream(), m_c13LineInfoOffset(0) 13 | { 14 | } 15 | 16 | 17 | // ------------------------------------------------------------------------------------------------ 18 | // ------------------------------------------------------------------------------------------------ 19 | PDB::ModuleLineStream::ModuleLineStream(const RawFile& file, uint16_t streamIndex, uint32_t streamSize, size_t c13LineInfoOffset) PDB_NO_EXCEPT 20 | : m_stream(file.CreateMSFStream(streamIndex, streamSize)), m_c13LineInfoOffset(c13LineInfoOffset) 21 | { 22 | // https://llvm.org/docs/PDB/ModiStream.html 23 | // struct ModiStream { 24 | // uint32_t Signature; 25 | // uint8_t Symbols[SymbolSize - 4]; 26 | // uint8_t C11LineInfo[C11Size]; 27 | // uint8_t C13LineInfo[C13Size]; 28 | // uint32_t GlobalRefsSize; 29 | // uint8_t GlobalRefs[GlobalRefsSize]; 30 | // }; 31 | } 32 | -------------------------------------------------------------------------------- /src/PDB_ModuleLineStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_BitUtil.h" 8 | #include "PDB_DBITypes.h" 9 | #include "PDB_Util.h" 10 | #include "PDB_CoalescedMSFStream.h" 11 | 12 | 13 | namespace PDB 14 | { 15 | class RawFile; 16 | 17 | class PDB_NO_DISCARD ModuleLineStream 18 | { 19 | public: 20 | ModuleLineStream(void) PDB_NO_EXCEPT; 21 | explicit ModuleLineStream(const RawFile& file, uint16_t streamIndex, uint32_t streamSize, size_t c13LineInfoOffset) PDB_NO_EXCEPT; 22 | 23 | PDB_DEFAULT_MOVE(ModuleLineStream); 24 | 25 | template 26 | void ForEachSection(F&& functor) const PDB_NO_EXCEPT 27 | { 28 | size_t offset = m_c13LineInfoOffset; 29 | 30 | // read the line stream sections 31 | while (offset < m_stream.GetSize()) 32 | { 33 | const CodeView::DBI::LineSection* section = m_stream.GetDataAtOffset(offset); 34 | 35 | functor(section); 36 | 37 | offset = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + section->header.size, 4u); 38 | } 39 | } 40 | 41 | template 42 | void ForEachLinesBlock(const CodeView::DBI::LineSection* section, F&& functor) const PDB_NO_EXCEPT 43 | { 44 | PDB_ASSERT(section->header.kind == CodeView::DBI::DebugSubsectionKind::S_LINES, 45 | "DebugSubsectionHeader::Kind %X != S_LINES (%X)", 46 | static_cast(section->header.kind), static_cast(CodeView::DBI::DebugSubsectionKind::S_LINES)); 47 | 48 | size_t offset = m_stream.GetPointerOffset(section); 49 | const size_t headerEnd = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + section->header.size, 4u); 50 | 51 | offset = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + sizeof(CodeView::DBI::LinesHeader), 4u); 52 | 53 | // read all blocks of lines 54 | while (offset < headerEnd) 55 | { 56 | const CodeView::DBI::LinesFileBlockHeader* linesBlockHeader = m_stream.GetDataAtOffset(offset); 57 | const CodeView::DBI::Line* blockLines = m_stream.GetDataAtOffset(offset + sizeof(CodeView::DBI::LinesFileBlockHeader)); 58 | 59 | const size_t blockColumnsOffset = sizeof(CodeView::DBI::LinesFileBlockHeader) + (linesBlockHeader->numLines * (sizeof(CodeView::DBI::Line))); 60 | const CodeView::DBI::Column* blockColumns = blockColumnsOffset < linesBlockHeader->size ? m_stream.GetDataAtOffset(offset) : nullptr; 61 | 62 | functor(linesBlockHeader, blockLines, blockColumns); 63 | 64 | offset = BitUtil::RoundUpToMultiple(offset + linesBlockHeader->size, 4u); 65 | } 66 | 67 | PDB_ASSERT(offset == headerEnd, "Mismatch between offset %zu and header end %zu when reading lines blocks", offset, headerEnd); 68 | } 69 | 70 | template 71 | void ForEachFileChecksum(const CodeView::DBI::LineSection* section, F&& functor) const PDB_NO_EXCEPT 72 | { 73 | PDB_ASSERT(section->header.kind == CodeView::DBI::DebugSubsectionKind::S_FILECHECKSUMS, 74 | "DebugSubsectionHeader::Kind %X != S_FILECHECKSUMS (%X)", 75 | static_cast(section->header.kind), static_cast(CodeView::DBI::DebugSubsectionKind::S_FILECHECKSUMS)); 76 | 77 | size_t offset = m_stream.GetPointerOffset(section); 78 | const size_t headerEnd = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + section->header.size, 4u); 79 | 80 | offset = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::DebugSubsectionHeader), 4u); 81 | 82 | // read all file checksums 83 | while (offset < headerEnd) 84 | { 85 | const CodeView::DBI::FileChecksumHeader* fileChecksumHeader = m_stream.GetDataAtOffset(offset); 86 | 87 | functor(fileChecksumHeader); 88 | 89 | offset = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::FileChecksumHeader) + fileChecksumHeader->checksumSize, 4u); 90 | } 91 | 92 | PDB_ASSERT(offset == headerEnd, "Mismatch between offset %zu and header end %zu when reading file checksums", offset, headerEnd); 93 | } 94 | 95 | template 96 | void ForEachInlineeSourceLine(const CodeView::DBI::LineSection* section, F&& functor) const PDB_NO_EXCEPT 97 | { 98 | PDB_ASSERT(section->header.kind == CodeView::DBI::DebugSubsectionKind::S_INLINEELINES, 99 | "DebugSubsectionHeader::Kind %X != S_INLINEELINES (%X)", 100 | static_cast(section->header.kind), static_cast(CodeView::DBI::DebugSubsectionKind::S_INLINEELINES)); 101 | 102 | PDB_ASSERT(section->inlineeHeader.kind == CodeView::DBI::InlineeSourceLineKind::Signature, 103 | "InlineeSourceLineKind %X != :InlineeSourceLineKind::Signature (%X)", static_cast(section->header.kind), static_cast(CodeView::DBI::InlineeSourceLineKind::Signature)); 104 | 105 | size_t offset = m_stream.GetPointerOffset(section); 106 | const size_t headerEnd = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + section->header.size, 4u); 107 | 108 | offset = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + sizeof(CodeView::DBI::InlineeSourceLineHeader), 4u); 109 | 110 | // read all file checksums 111 | while (offset < headerEnd) 112 | { 113 | const CodeView::DBI::InlineeSourceLine* inlineeSourceLine = m_stream.GetDataAtOffset(offset); 114 | 115 | functor(inlineeSourceLine); 116 | 117 | offset = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::InlineeSourceLine), 4u); 118 | } 119 | } 120 | 121 | template 122 | void ForEachInlineeSourceLineEx(const CodeView::DBI::LineSection* section, F&& functor) const PDB_NO_EXCEPT 123 | { 124 | PDB_ASSERT(section->header.kind == CodeView::DBI::DebugSubsectionKind::S_INLINEELINES, 125 | "DebugSubsectionHeader::Kind %X != S_INLINEELINES (%X)", static_cast(section->header.kind), static_cast(CodeView::DBI::DebugSubsectionKind::S_INLINEELINES)); 126 | 127 | PDB_ASSERT(section->inlineeHeader.kind == CodeView::DBI::InlineeSourceLineKind::SignatureEx, 128 | "InlineeSourceLineKind %X != :InlineeSourceLineKind::SignatureEx (%X)", static_cast(section->header.kind), static_cast(CodeView::DBI::InlineeSourceLineKind::SignatureEx)); 129 | 130 | size_t offset = m_stream.GetPointerOffset(section); 131 | const size_t headerEnd = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + section->header.size, 4u); 132 | 133 | offset = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + sizeof(CodeView::DBI::InlineeSourceLineHeader), 4u); 134 | 135 | // read all file checksums 136 | while (offset < headerEnd) 137 | { 138 | const CodeView::DBI::InlineeSourceLineEx* inlineeSourceLineEx = m_stream.GetDataAtOffset(offset); 139 | 140 | functor(inlineeSourceLineEx); 141 | 142 | offset = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::InlineeSourceLineEx) + (inlineeSourceLineEx->extraLines * sizeof(uint32_t)), 4u); 143 | } 144 | } 145 | private: 146 | CoalescedMSFStream m_stream; 147 | size_t m_c13LineInfoOffset; 148 | 149 | PDB_DISABLE_COPY(ModuleLineStream); 150 | }; 151 | } 152 | -------------------------------------------------------------------------------- /src/PDB_ModuleSymbolStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_ModuleSymbolStream.h" 6 | #include "PDB_RawFile.h" 7 | 8 | 9 | // ------------------------------------------------------------------------------------------------ 10 | // ------------------------------------------------------------------------------------------------ 11 | PDB::ModuleSymbolStream::ModuleSymbolStream(void) PDB_NO_EXCEPT 12 | : m_stream() 13 | { 14 | } 15 | 16 | 17 | // ------------------------------------------------------------------------------------------------ 18 | // ------------------------------------------------------------------------------------------------ 19 | PDB::ModuleSymbolStream::ModuleSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t symbolStreamSize) PDB_NO_EXCEPT 20 | : m_stream(file.CreateMSFStream(streamIndex, symbolStreamSize)) 21 | { 22 | // https://llvm.org/docs/PDB/ModiStream.html 23 | // struct ModiStream { 24 | // uint32_t Signature; 25 | // uint8_t Symbols[SymbolSize - 4]; 26 | // uint8_t C11LineInfo[C11Size]; 27 | // uint8_t C13LineInfo[C13Size]; 28 | // uint32_t GlobalRefsSize; 29 | // uint8_t GlobalRefs[GlobalRefsSize]; 30 | // }; 31 | // we are only interested in the symbols, but not the line information or global refs. 32 | // the coalesced stream is therefore only built for the symbols, not all the data in the stream. 33 | // this potentially saves a lot of memory and performance on large PDBs. 34 | } 35 | 36 | 37 | // ------------------------------------------------------------------------------------------------ 38 | // ------------------------------------------------------------------------------------------------ 39 | PDB_NO_DISCARD const PDB::CodeView::DBI::Record* PDB::ModuleSymbolStream::FindRecord(CodeView::DBI::SymbolRecordKind kind) const PDB_NO_EXCEPT 40 | { 41 | // ignore the stream's 4-byte signature 42 | size_t offset = sizeof(uint32_t); 43 | 44 | // parse the CodeView records 45 | while (offset < m_stream.GetSize()) 46 | { 47 | // https://llvm.org/docs/PDB/CodeViewTypes.html 48 | const CodeView::DBI::Record* record = m_stream.GetDataAtOffset(offset); 49 | if (record->header.kind == kind) 50 | { 51 | return record; 52 | } 53 | 54 | const uint32_t recordSize = GetCodeViewRecordSize(record); 55 | 56 | // position the module stream offset at the next record 57 | offset = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::RecordHeader) + recordSize, 4u); 58 | } 59 | 60 | return nullptr; 61 | } 62 | -------------------------------------------------------------------------------- /src/PDB_ModuleSymbolStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_BitUtil.h" 8 | #include "PDB_DBITypes.h" 9 | #include "PDB_Util.h" 10 | #include "PDB_CoalescedMSFStream.h" 11 | 12 | 13 | namespace PDB 14 | { 15 | class RawFile; 16 | 17 | 18 | class PDB_NO_DISCARD ModuleSymbolStream 19 | { 20 | public: 21 | ModuleSymbolStream(void) PDB_NO_EXCEPT; 22 | explicit ModuleSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t symbolStreamSize) PDB_NO_EXCEPT; 23 | 24 | PDB_DEFAULT_MOVE(ModuleSymbolStream); 25 | 26 | // Returns a record's parent record. 27 | template 28 | PDB_NO_DISCARD inline const CodeView::DBI::Record* GetParentRecord(const T& record) const PDB_NO_EXCEPT 29 | { 30 | return m_stream.GetDataAtOffset(record.parent); 31 | } 32 | 33 | // Returns a record's end record. 34 | template 35 | PDB_NO_DISCARD inline const CodeView::DBI::Record* GetEndRecord(const T& record) const PDB_NO_EXCEPT 36 | { 37 | return m_stream.GetDataAtOffset(record.end); 38 | } 39 | 40 | // Finds a record of a certain kind. 41 | PDB_NO_DISCARD const CodeView::DBI::Record* FindRecord(CodeView::DBI::SymbolRecordKind Kind) const PDB_NO_EXCEPT; 42 | 43 | 44 | // Iterates all records in the stream. 45 | template 46 | void ForEachSymbol(F&& functor) const PDB_NO_EXCEPT 47 | { 48 | // ignore the stream's 4-byte signature 49 | size_t offset = sizeof(uint32_t); 50 | 51 | // parse the CodeView records 52 | while (offset < m_stream.GetSize()) 53 | { 54 | // https://llvm.org/docs/PDB/CodeViewTypes.html 55 | const CodeView::DBI::Record* record = m_stream.GetDataAtOffset(offset); 56 | const uint32_t recordSize = GetCodeViewRecordSize(record); 57 | 58 | functor(record); 59 | 60 | // position the module stream offset at the next record 61 | offset = BitUtil::RoundUpToMultiple(offset + sizeof(CodeView::DBI::RecordHeader) + recordSize, 4u); 62 | } 63 | } 64 | 65 | private: 66 | CoalescedMSFStream m_stream; 67 | 68 | PDB_DISABLE_COPY(ModuleSymbolStream); 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /src/PDB_NamesStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_NamesStream.h" 6 | #include "PDB_RawFile.h" 7 | 8 | 9 | // ------------------------------------------------------------------------------------------------ 10 | // ------------------------------------------------------------------------------------------------ 11 | PDB::NamesStream::NamesStream(void) PDB_NO_EXCEPT 12 | : m_stream() 13 | , m_header(nullptr) 14 | , m_stringTable(nullptr) 15 | { 16 | } 17 | 18 | 19 | // ------------------------------------------------------------------------------------------------ 20 | // ------------------------------------------------------------------------------------------------ 21 | PDB::NamesStream::NamesStream(const RawFile& file, uint32_t streamIndex) PDB_NO_EXCEPT 22 | : m_stream(file.CreateMSFStream(streamIndex)) 23 | , m_header(m_stream.GetDataAtOffset(0u)) 24 | , m_stringTable(nullptr) 25 | { 26 | // grab a pointer into the string table 27 | m_stringTable = m_stream.GetDataAtOffset(sizeof(NamesHeader)); 28 | } 29 | -------------------------------------------------------------------------------- /src/PDB_NamesStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "PDB_Types.h" 8 | #include "PDB_CoalescedMSFStream.h" 9 | 10 | 11 | namespace PDB 12 | { 13 | class RawFile; 14 | 15 | struct NamesHeader 16 | { 17 | uint32_t magic; 18 | uint32_t hashVersion; 19 | uint32_t size; 20 | }; 21 | 22 | class PDB_NO_DISCARD NamesStream 23 | { 24 | public: 25 | NamesStream(void) PDB_NO_EXCEPT; 26 | explicit NamesStream(const RawFile& file, uint32_t streamIndex) PDB_NO_EXCEPT; 27 | 28 | PDB_DEFAULT_MOVE(NamesStream); 29 | 30 | // Returns the header of the stream. 31 | PDB_NO_DISCARD inline const NamesHeader* GetHeader(void) const PDB_NO_EXCEPT 32 | { 33 | return m_header; 34 | } 35 | 36 | PDB_NO_DISCARD inline const char* GetFilename(uint32_t filenameOffset) const PDB_NO_EXCEPT 37 | { 38 | return m_stringTable + filenameOffset; 39 | } 40 | 41 | private: 42 | CoalescedMSFStream m_stream; 43 | const NamesHeader* m_header; 44 | const char* m_stringTable; 45 | 46 | PDB_DISABLE_COPY(NamesStream); 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /src/PDB_PCH.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | -------------------------------------------------------------------------------- /src/PDB_PCH.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | // this needs to be the first include, since it determines the platform/toolchain we're compiling for 7 | #include "Foundation/PDB_Platform.h" 8 | #include "Foundation/PDB_Macros.h" 9 | #include "Foundation/PDB_Warnings.h" 10 | 11 | // third-party includes 12 | #include "Foundation/PDB_DisableWarningsPush.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "Foundation/PDB_DisableWarningsPop.h" 18 | 19 | // library includes 20 | #include "Foundation/PDB_Log.h" 21 | #include "Foundation/PDB_Assert.h" 22 | #include "Foundation/PDB_Move.h" 23 | #include "Foundation/PDB_Forward.h" 24 | #include "Foundation/PDB_Memory.h" 25 | #include "Foundation/PDB_ArrayView.h" 26 | #include "Foundation/PDB_BitUtil.h" 27 | #include "Foundation/PDB_BitOperators.h" 28 | #include "Foundation/PDB_PointerUtil.h" 29 | -------------------------------------------------------------------------------- /src/PDB_PublicSymbolStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_PublicSymbolStream.h" 6 | #include "PDB_RawFile.h" 7 | #include "PDB_Types.h" 8 | #include "PDB_DBITypes.h" 9 | 10 | 11 | // ------------------------------------------------------------------------------------------------ 12 | // ------------------------------------------------------------------------------------------------ 13 | PDB::PublicSymbolStream::PublicSymbolStream(void) PDB_NO_EXCEPT 14 | : m_stream() 15 | , m_hashRecords(nullptr) 16 | , m_count(0u) 17 | { 18 | } 19 | 20 | 21 | // ------------------------------------------------------------------------------------------------ 22 | // ------------------------------------------------------------------------------------------------ 23 | PDB::PublicSymbolStream::PublicSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t count) PDB_NO_EXCEPT 24 | : m_stream(file.CreateMSFStream(streamIndex)) 25 | , m_hashRecords(m_stream.GetDataAtOffset(sizeof(PublicStreamHeader) + sizeof(HashTableHeader))) 26 | , m_count(count) 27 | { 28 | } 29 | 30 | 31 | // ------------------------------------------------------------------------------------------------ 32 | // ------------------------------------------------------------------------------------------------ 33 | PDB_NO_DISCARD const PDB::CodeView::DBI::Record* PDB::PublicSymbolStream::GetRecord(const CoalescedMSFStream& symbolRecordStream, const HashRecord& hashRecord) const PDB_NO_EXCEPT 34 | { 35 | // hash record offsets start at 1, not at 0 36 | const uint32_t headerOffset = hashRecord.offset - 1u; 37 | 38 | // the offset doesn't point to the public symbol directly, but to the CodeView record: 39 | // https://llvm.org/docs/PDB/CodeViewSymbols.html 40 | const CodeView::DBI::Record* record = symbolRecordStream.GetDataAtOffset(headerOffset); 41 | 42 | return record; 43 | } 44 | -------------------------------------------------------------------------------- /src/PDB_PublicSymbolStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_ArrayView.h" 8 | #include "PDB_CoalescedMSFStream.h" 9 | 10 | 11 | namespace PDB 12 | { 13 | class RawFile; 14 | struct HashRecord; 15 | 16 | namespace CodeView 17 | { 18 | namespace DBI 19 | { 20 | struct Record; 21 | } 22 | } 23 | 24 | 25 | class PDB_NO_DISCARD PublicSymbolStream 26 | { 27 | public: 28 | PublicSymbolStream(void) PDB_NO_EXCEPT; 29 | explicit PublicSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t count) PDB_NO_EXCEPT; 30 | 31 | PDB_DEFAULT_MOVE(PublicSymbolStream); 32 | 33 | // Turns a given hash record into a DBI record using the given symbol stream. 34 | PDB_NO_DISCARD const CodeView::DBI::Record* GetRecord(const CoalescedMSFStream& symbolRecordStream, const HashRecord& hashRecord) const PDB_NO_EXCEPT; 35 | 36 | // Returns a view of all the records in the stream. 37 | PDB_NO_DISCARD inline ArrayView GetRecords(void) const PDB_NO_EXCEPT 38 | { 39 | return ArrayView(m_hashRecords, m_count); 40 | } 41 | 42 | private: 43 | CoalescedMSFStream m_stream; 44 | const HashRecord* m_hashRecords; 45 | uint32_t m_count; 46 | 47 | PDB_DISABLE_COPY(PublicSymbolStream); 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /src/PDB_RawFile.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_RawFile.h" 6 | #include "PDB_Types.h" 7 | #include "PDB_Util.h" 8 | #include "PDB_DirectMSFStream.h" 9 | #include "Foundation/PDB_PointerUtil.h" 10 | #include "Foundation/PDB_Memory.h" 11 | #include "Foundation/PDB_Assert.h" 12 | 13 | 14 | // ------------------------------------------------------------------------------------------------ 15 | // ------------------------------------------------------------------------------------------------ 16 | PDB::RawFile::RawFile(RawFile&& other) PDB_NO_EXCEPT 17 | : m_data(PDB_MOVE(other.m_data)) 18 | , m_superBlock(PDB_MOVE(other.m_superBlock)) 19 | , m_directoryStream(PDB_MOVE(other.m_directoryStream)) 20 | , m_streamCount(PDB_MOVE(other.m_streamCount)) 21 | , m_streamSizes(PDB_MOVE(other.m_streamSizes)) 22 | , m_streamBlocks(PDB_MOVE(other.m_streamBlocks)) 23 | { 24 | other.m_data = nullptr; 25 | other.m_superBlock = nullptr; 26 | other.m_streamCount = 0u; 27 | other.m_streamSizes = nullptr; 28 | other.m_streamBlocks = nullptr; 29 | } 30 | 31 | 32 | // ------------------------------------------------------------------------------------------------ 33 | // ------------------------------------------------------------------------------------------------ 34 | PDB::RawFile& PDB::RawFile::operator=(RawFile&& other) PDB_NO_EXCEPT 35 | { 36 | if (this != &other) 37 | { 38 | PDB_DELETE_ARRAY(m_streamBlocks); 39 | 40 | m_data = PDB_MOVE(other.m_data); 41 | m_superBlock = PDB_MOVE(other.m_superBlock); 42 | m_directoryStream = PDB_MOVE(other.m_directoryStream); 43 | m_streamCount = PDB_MOVE(other.m_streamCount); 44 | m_streamSizes = PDB_MOVE(other.m_streamSizes); 45 | m_streamBlocks = PDB_MOVE(other.m_streamBlocks); 46 | 47 | other.m_data = nullptr; 48 | other.m_superBlock = nullptr; 49 | other.m_streamCount = 0u; 50 | other.m_streamSizes = nullptr; 51 | other.m_streamBlocks = nullptr; 52 | } 53 | 54 | return *this; 55 | } 56 | 57 | 58 | // ------------------------------------------------------------------------------------------------ 59 | // ------------------------------------------------------------------------------------------------ 60 | PDB::RawFile::RawFile(const void* data) PDB_NO_EXCEPT 61 | : m_data(data) 62 | , m_superBlock(Pointer::Offset(data, 0u)) 63 | , m_directoryStream() 64 | , m_streamCount(0u) 65 | , m_streamSizes(nullptr) 66 | , m_streamBlocks(nullptr) 67 | { 68 | // the SuperBlock stores an array of indices of blocks that make up the indices of directory blocks, which need to be stitched together to form the directory. 69 | // the blocks holding the indices of directory blocks are not necessarily contiguous, so they need to be coalesced first. 70 | const uint32_t directoryBlockCount = PDB::ConvertSizeToBlockCount(m_superBlock->directorySize, m_superBlock->blockSize); 71 | 72 | // the directory is made up of directoryBlockCount blocks, so we need that many indices to be read from the blocks that make up the indices 73 | CoalescedMSFStream directoryIndicesStream(data, m_superBlock->blockSize, m_superBlock->directoryBlockIndices, directoryBlockCount * sizeof(uint32_t)); 74 | 75 | // these are the indices of blocks making up the directory stream, now guaranteed to be contiguous 76 | const uint32_t* directoryIndices = directoryIndicesStream.GetDataAtOffset(0u); 77 | 78 | m_directoryStream = CoalescedMSFStream(data, m_superBlock->blockSize, directoryIndices, m_superBlock->directorySize); 79 | 80 | // https://llvm.org/docs/PDB/MsfFile.html#the-stream-directory 81 | // parse the directory from its contiguous version. the directory matches the following struct: 82 | // struct StreamDirectory 83 | // { 84 | // uint32_t streamCount; 85 | // uint32_t streamSizes[streamCount]; 86 | // uint32_t streamBlocks[streamCount][]; 87 | // }; 88 | m_streamCount = *m_directoryStream.GetDataAtOffset(0u); 89 | 90 | // we can assign pointers into the stream directly, since the RawFile keeps ownership of the directory stream 91 | m_streamSizes = m_directoryStream.GetDataAtOffset(sizeof(uint32_t)); 92 | const uint32_t* directoryStreamBlocks = m_directoryStream.GetDataAtOffset(sizeof(uint32_t) + sizeof(uint32_t) * m_streamCount); 93 | 94 | // prepare indices for directly accessing individual streams 95 | m_streamBlocks = PDB_NEW_ARRAY(const uint32_t*, m_streamCount); 96 | 97 | const uint32_t* indicesForCurrentBlock = directoryStreamBlocks; 98 | for (uint32_t i = 0u; i < m_streamCount; ++i) 99 | { 100 | const uint32_t sizeInBytes = GetStreamSize(i); 101 | const uint32_t blockCount = ConvertSizeToBlockCount(sizeInBytes, m_superBlock->blockSize); 102 | m_streamBlocks[i] = indicesForCurrentBlock; 103 | 104 | indicesForCurrentBlock += blockCount; 105 | } 106 | } 107 | 108 | 109 | // ------------------------------------------------------------------------------------------------ 110 | // ------------------------------------------------------------------------------------------------ 111 | PDB::RawFile::~RawFile(void) PDB_NO_EXCEPT 112 | { 113 | PDB_DELETE_ARRAY(m_streamBlocks); 114 | } 115 | 116 | 117 | // ------------------------------------------------------------------------------------------------ 118 | // ------------------------------------------------------------------------------------------------ 119 | template 120 | PDB_NO_DISCARD T PDB::RawFile::CreateMSFStream(uint32_t streamIndex) const PDB_NO_EXCEPT 121 | { 122 | PDB_ASSERT(streamIndex != PDB::NilStreamIndex, "Invalid stream index."); 123 | PDB_ASSERT(streamIndex < m_streamCount, "Invalid stream index."); 124 | 125 | return T(m_data, m_superBlock->blockSize, m_streamBlocks[streamIndex], GetStreamSize(streamIndex)); 126 | } 127 | 128 | 129 | // ------------------------------------------------------------------------------------------------ 130 | // ------------------------------------------------------------------------------------------------ 131 | template 132 | PDB_NO_DISCARD T PDB::RawFile::CreateMSFStream(uint32_t streamIndex, uint32_t streamSize) const PDB_NO_EXCEPT 133 | { 134 | PDB_ASSERT(streamIndex != PDB::NilStreamIndex, "Invalid stream index."); 135 | PDB_ASSERT(streamIndex < m_streamCount, "Invalid stream index."); 136 | PDB_ASSERT(streamSize <= GetStreamSize(streamIndex), "Invalid stream size."); 137 | 138 | return T(m_data, m_superBlock->blockSize, m_streamBlocks[streamIndex], streamSize); 139 | } 140 | 141 | 142 | // explicit template instantiation 143 | template PDB::CoalescedMSFStream PDB::RawFile::CreateMSFStream(uint32_t streamIndex) const PDB_NO_EXCEPT; 144 | template PDB::DirectMSFStream PDB::RawFile::CreateMSFStream(uint32_t streamIndex) const PDB_NO_EXCEPT; 145 | 146 | template PDB::CoalescedMSFStream PDB::RawFile::CreateMSFStream(uint32_t streamIndex, uint32_t streamSize) const PDB_NO_EXCEPT; 147 | template PDB::DirectMSFStream PDB::RawFile::CreateMSFStream(uint32_t streamIndex, uint32_t streamSize) const PDB_NO_EXCEPT; 148 | -------------------------------------------------------------------------------- /src/PDB_RawFile.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_DisableWarningsPush.h" 8 | #include 9 | #include "Foundation/PDB_DisableWarningsPop.h" 10 | #include "PDB_CoalescedMSFStream.h" 11 | 12 | 13 | // https://llvm.org/docs/PDB/index.html 14 | namespace PDB 15 | { 16 | struct SuperBlock; 17 | 18 | 19 | class PDB_NO_DISCARD RawFile 20 | { 21 | public: 22 | RawFile(RawFile&& other) PDB_NO_EXCEPT; 23 | RawFile& operator=(RawFile&& other) PDB_NO_EXCEPT; 24 | 25 | explicit RawFile(const void* data) PDB_NO_EXCEPT; 26 | ~RawFile(void) PDB_NO_EXCEPT; 27 | 28 | // Creates any type of MSF stream. 29 | template 30 | PDB_NO_DISCARD T CreateMSFStream(uint32_t streamIndex) const PDB_NO_EXCEPT; 31 | 32 | // Creates any type of MSF stream with the given size. 33 | template 34 | PDB_NO_DISCARD T CreateMSFStream(uint32_t streamIndex, uint32_t streamSize) const PDB_NO_EXCEPT; 35 | 36 | 37 | // Returns the SuperBlock. 38 | PDB_NO_DISCARD inline const SuperBlock* GetSuperBlock(void) const PDB_NO_EXCEPT 39 | { 40 | return m_superBlock; 41 | } 42 | 43 | // Returns the number of streams in the PDB file. 44 | PDB_NO_DISCARD inline uint32_t GetStreamCount(void) const PDB_NO_EXCEPT 45 | { 46 | return m_streamCount; 47 | } 48 | 49 | // Returns the size of the stream with the given index, taking into account nil page sizes. 50 | PDB_NO_DISCARD inline uint32_t GetStreamSize(uint32_t streamIndex) const PDB_NO_EXCEPT 51 | { 52 | const uint32_t streamSize = m_streamSizes[streamIndex]; 53 | 54 | return (streamSize == NilPageSize) ? 0u : streamSize; 55 | } 56 | 57 | private: 58 | const void* m_data; 59 | const SuperBlock* m_superBlock; 60 | CoalescedMSFStream m_directoryStream; 61 | 62 | // stream directory 63 | uint32_t m_streamCount; 64 | const uint32_t* m_streamSizes; 65 | const uint32_t** m_streamBlocks; 66 | 67 | PDB_DISABLE_COPY(RawFile); 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /src/PDB_SectionContributionStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_SectionContributionStream.h" 6 | 7 | 8 | // ------------------------------------------------------------------------------------------------ 9 | // ------------------------------------------------------------------------------------------------ 10 | PDB::SectionContributionStream::SectionContributionStream(void) PDB_NO_EXCEPT 11 | : m_stream() 12 | , m_contributions(nullptr) 13 | , m_count(0u) 14 | { 15 | } 16 | 17 | 18 | // ------------------------------------------------------------------------------------------------ 19 | // ------------------------------------------------------------------------------------------------ 20 | PDB::SectionContributionStream::SectionContributionStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT 21 | : m_stream(directStream, size, offset) 22 | , m_contributions(m_stream.GetDataAtOffset(0u)) 23 | , m_count(size / sizeof(DBI::SectionContribution)) 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/PDB_SectionContributionStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_ArrayView.h" 8 | #include "PDB_DBITypes.h" 9 | #include "PDB_CoalescedMSFStream.h" 10 | 11 | 12 | namespace PDB 13 | { 14 | class PDB_NO_DISCARD DirectMSFStream; 15 | 16 | 17 | class PDB_NO_DISCARD SectionContributionStream 18 | { 19 | public: 20 | SectionContributionStream(void) PDB_NO_EXCEPT; 21 | explicit SectionContributionStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT; 22 | 23 | PDB_DEFAULT_MOVE(SectionContributionStream); 24 | 25 | // Returns a view of all section contributions in the stream. 26 | PDB_NO_DISCARD inline ArrayView GetContributions(void) const PDB_NO_EXCEPT 27 | { 28 | return ArrayView(m_contributions, m_count); 29 | } 30 | 31 | private: 32 | CoalescedMSFStream m_stream; 33 | const DBI::SectionContribution* m_contributions; 34 | size_t m_count; 35 | 36 | PDB_DISABLE_COPY(SectionContributionStream); 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /src/PDB_SourceFileStream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_SourceFileStream.h" 6 | 7 | 8 | // ------------------------------------------------------------------------------------------------ 9 | // ------------------------------------------------------------------------------------------------ 10 | PDB::SourceFileStream::SourceFileStream(void) PDB_NO_EXCEPT 11 | : m_stream() 12 | , m_moduleCount(0u) 13 | , m_moduleIndices(nullptr) 14 | , m_moduleFileCounts(nullptr) 15 | , m_fileNameOffsets(nullptr) 16 | , m_stringTable(nullptr) 17 | { 18 | } 19 | 20 | 21 | // ------------------------------------------------------------------------------------------------ 22 | // ------------------------------------------------------------------------------------------------ 23 | PDB::SourceFileStream::SourceFileStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT 24 | : m_stream(directStream, size, offset) 25 | , m_moduleCount(0u) 26 | , m_moduleIndices(nullptr) 27 | , m_moduleFileCounts(nullptr) 28 | , m_fileNameOffsets(nullptr) 29 | , m_stringTable(nullptr) 30 | { 31 | // we are going to consume the whole source info sub-stream, so create a coalesced stream for faster read operations and direct access. 32 | // the sub-stream has the following layout: 33 | // struct SourceInfoSubstream 34 | // { 35 | // uint16_t moduleCount; 36 | // uint16_t sourceFileCount; 37 | // uint16_t moduleIndices[moduleCount]; 38 | // uint16_t moduleFileCounts[moduleCount]; 39 | // uint32_t fileNameOffsets[realSourceFileCount]; 40 | // char stringTable[][realSourceFileCount]; 41 | // }; 42 | m_moduleCount = *m_stream.GetDataAtOffset(0u); 43 | size_t readOffset = sizeof(uint16_t); 44 | 45 | // skip number of source files. this would only support 64k unique files and is no longer used. 46 | // the number of source files is computed dynamically instead. 47 | readOffset += sizeof(uint16_t); 48 | 49 | // grab direct pointers into the stream data 50 | m_moduleIndices = m_stream.GetDataAtOffset(readOffset); 51 | readOffset += sizeof(uint16_t) * m_moduleCount; 52 | 53 | m_moduleFileCounts = m_stream.GetDataAtOffset(readOffset); 54 | readOffset += sizeof(uint16_t) * m_moduleCount; 55 | 56 | // count the actual number of source files 57 | size_t sourceFileCount = 0u; 58 | for (unsigned int i = 0u; i < m_moduleCount; ++i) 59 | { 60 | sourceFileCount += m_moduleFileCounts[i]; 61 | } 62 | 63 | m_fileNameOffsets = m_stream.GetDataAtOffset(readOffset); 64 | readOffset += sizeof(uint32_t) * sourceFileCount; 65 | 66 | // grab a pointer into the string table 67 | m_stringTable = m_stream.GetDataAtOffset(readOffset); 68 | } 69 | -------------------------------------------------------------------------------- /src/PDB_SourceFileStream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_ArrayView.h" 8 | #include "PDB_CoalescedMSFStream.h" 9 | 10 | 11 | namespace PDB 12 | { 13 | class PDB_NO_DISCARD DirectMSFStream; 14 | 15 | 16 | class PDB_NO_DISCARD SourceFileStream 17 | { 18 | public: 19 | SourceFileStream(void) PDB_NO_EXCEPT; 20 | explicit SourceFileStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT; 21 | 22 | PDB_DEFAULT_MOVE(SourceFileStream); 23 | 24 | // Returns the number of modules. 25 | PDB_NO_DISCARD inline uint32_t GetModuleCount(void) const PDB_NO_EXCEPT 26 | { 27 | return m_moduleCount; 28 | } 29 | 30 | // Returns a view of all the filename offsets for the module with the given index. 31 | PDB_NO_DISCARD inline ArrayView GetModuleFilenameOffsets(size_t moduleIndex) const PDB_NO_EXCEPT 32 | { 33 | const uint16_t moduleStartIndex = m_moduleIndices[moduleIndex]; 34 | const uint16_t moduleFileCount = m_moduleFileCounts[moduleIndex]; 35 | 36 | return ArrayView(m_fileNameOffsets + moduleStartIndex, moduleFileCount); 37 | } 38 | 39 | // Returns a filename for the given filename offset. 40 | PDB_NO_DISCARD inline const char* GetFilename(uint32_t filenameOffset) const PDB_NO_EXCEPT 41 | { 42 | return m_stringTable + filenameOffset; 43 | } 44 | 45 | private: 46 | CoalescedMSFStream m_stream; 47 | 48 | // the number of modules 49 | uint32_t m_moduleCount; 50 | 51 | // the indices into the file name offsets, for each module 52 | const uint16_t* m_moduleIndices; 53 | 54 | // the number of files, for each module 55 | const uint16_t* m_moduleFileCounts; 56 | 57 | // the filename offsets into the string table, for all modules 58 | const uint32_t* m_fileNameOffsets; 59 | 60 | // the string table storing all filenames 61 | const char* m_stringTable; 62 | 63 | PDB_DISABLE_COPY(SourceFileStream); 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /src/PDB_TPIStream.cpp: -------------------------------------------------------------------------------- 1 | #include "PDB_PCH.h" 2 | #include "PDB_TPIStream.h" 3 | #include "PDB_RawFile.h" 4 | #include "PDB_DirectMSFStream.h" 5 | #include "Foundation/PDB_Memory.h" 6 | 7 | namespace 8 | { 9 | // the TPI stream always resides at index 2 10 | static constexpr const uint32_t TPIStreamIndex = 2u; 11 | } 12 | 13 | 14 | // ------------------------------------------------------------------------------------------------ 15 | // ------------------------------------------------------------------------------------------------ 16 | PDB::TPIStream::TPIStream(void) PDB_NO_EXCEPT 17 | : m_stream() 18 | , m_header() 19 | , m_recordCount(0u) 20 | { 21 | } 22 | 23 | 24 | // ------------------------------------------------------------------------------------------------ 25 | // ------------------------------------------------------------------------------------------------ 26 | PDB::TPIStream::TPIStream(TPIStream&& other) PDB_NO_EXCEPT 27 | : m_stream(PDB_MOVE(other.m_stream)) 28 | , m_header(PDB_MOVE(other.m_header)) 29 | , m_recordCount(PDB_MOVE(other.m_recordCount)) 30 | { 31 | other.m_recordCount = 0u; 32 | } 33 | 34 | 35 | // ------------------------------------------------------------------------------------------------ 36 | // ------------------------------------------------------------------------------------------------ 37 | PDB::TPIStream& PDB::TPIStream::operator=(TPIStream&& other) PDB_NO_EXCEPT 38 | { 39 | if (this != &other) 40 | { 41 | m_stream = PDB_MOVE(other.m_stream); 42 | m_header = PDB_MOVE(other.m_header); 43 | m_recordCount = PDB_MOVE(other.m_recordCount); 44 | 45 | other.m_recordCount = 0u; 46 | } 47 | 48 | return *this; 49 | } 50 | 51 | 52 | // ------------------------------------------------------------------------------------------------ 53 | // ------------------------------------------------------------------------------------------------ 54 | PDB::TPIStream::TPIStream(const RawFile& file) PDB_NO_EXCEPT 55 | : m_stream(file.CreateMSFStream(TPIStreamIndex)), 56 | m_header(m_stream.ReadAtOffset(0u)), 57 | m_recordCount(GetLastTypeIndex() - GetFirstTypeIndex()) 58 | { 59 | } 60 | 61 | // ------------------------------------------------------------------------------------------------ 62 | // ------------------------------------------------------------------------------------------------ 63 | PDB_NO_DISCARD PDB::ErrorCode PDB::HasValidTPIStream(const RawFile& file) PDB_NO_EXCEPT 64 | { 65 | DirectMSFStream stream = file.CreateMSFStream(TPIStreamIndex); 66 | if (stream.GetSize() < sizeof(TPI::StreamHeader)) 67 | { 68 | return ErrorCode::InvalidStream; 69 | } 70 | 71 | const TPI::StreamHeader header = stream.ReadAtOffset(0u); 72 | if (header.version != TPI::StreamHeader::Version::V80) 73 | { 74 | return ErrorCode::UnknownVersion; 75 | } 76 | 77 | return ErrorCode::Success; 78 | } 79 | 80 | 81 | // ------------------------------------------------------------------------------------------------ 82 | // ------------------------------------------------------------------------------------------------ 83 | PDB_NO_DISCARD PDB::TPIStream PDB::CreateTPIStream(const RawFile& file) PDB_NO_EXCEPT 84 | { 85 | return TPIStream { file }; 86 | } 87 | -------------------------------------------------------------------------------- /src/PDB_TPIStream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Foundation/PDB_Macros.h" 4 | #include "Foundation/PDB_ArrayView.h" 5 | #include "PDB_ErrorCodes.h" 6 | #include "PDB_TPITypes.h" 7 | #include "PDB_DirectMSFStream.h" 8 | #include "PDB_Util.h" 9 | 10 | // PDB TPI stream 11 | // https://llvm.org/docs/PDB/TpiStream.html 12 | namespace PDB 13 | { 14 | class RawFile; 15 | 16 | 17 | class PDB_NO_DISCARD TPIStream 18 | { 19 | public: 20 | TPIStream(void) PDB_NO_EXCEPT; 21 | TPIStream(TPIStream&& other) PDB_NO_EXCEPT; 22 | TPIStream& operator=(TPIStream&& other) PDB_NO_EXCEPT; 23 | 24 | explicit TPIStream(const RawFile& file) PDB_NO_EXCEPT; 25 | 26 | PDB_NO_DISCARD inline const DirectMSFStream& GetDirectMSFStream(void) const PDB_NO_EXCEPT 27 | { 28 | return m_stream; 29 | } 30 | 31 | // Returns the index of the first type, which is not necessarily zero. 32 | PDB_NO_DISCARD inline uint32_t GetFirstTypeIndex(void) const PDB_NO_EXCEPT 33 | { 34 | return m_header.typeIndexBegin; 35 | } 36 | 37 | // Returns the index of the last type. 38 | PDB_NO_DISCARD inline uint32_t GetLastTypeIndex(void) const PDB_NO_EXCEPT 39 | { 40 | return m_header.typeIndexEnd; 41 | } 42 | 43 | // Returns the number of type records. 44 | PDB_NO_DISCARD inline size_t GetTypeRecordCount(void) const PDB_NO_EXCEPT 45 | { 46 | return m_recordCount; 47 | } 48 | 49 | CodeView::TPI::RecordHeader ReadTypeRecordHeader(size_t offset) const PDB_NO_EXCEPT 50 | { 51 | const CodeView::TPI::RecordHeader header = m_stream.ReadAtOffset(offset); 52 | return header; 53 | } 54 | 55 | template 56 | void ForEachTypeRecordHeaderAndOffset(F&& functor) const PDB_NO_EXCEPT 57 | { 58 | // ignore the stream's header 59 | size_t offset = sizeof(TPI::StreamHeader); 60 | 61 | while (offset < m_stream.GetSize()) 62 | { 63 | const CodeView::TPI::RecordHeader header = ReadTypeRecordHeader(offset); 64 | 65 | functor(header, offset); 66 | 67 | // position the stream offset at the next record 68 | offset += sizeof(CodeView::TPI::RecordHeader) + header.size - sizeof(uint16_t); 69 | } 70 | } 71 | 72 | private: 73 | DirectMSFStream m_stream; 74 | TPI::StreamHeader m_header; 75 | size_t m_recordCount; 76 | 77 | PDB_DISABLE_COPY(TPIStream); 78 | }; 79 | 80 | // Returns whether the given raw file provides a valid TPI stream. 81 | PDB_NO_DISCARD ErrorCode HasValidTPIStream(const RawFile& file) PDB_NO_EXCEPT; 82 | 83 | // Creates the TPI stream from a raw file. 84 | PDB_NO_DISCARD TPIStream CreateTPIStream(const RawFile& file) PDB_NO_EXCEPT; 85 | } 86 | -------------------------------------------------------------------------------- /src/PDB_Types.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #include "PDB_PCH.h" 5 | #include "PDB_Types.h" 6 | 7 | 8 | // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp#L962 9 | const char PDB::SuperBlock::MAGIC[30u] = "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53"; 10 | 11 | const uint32_t PDB::HashTableHeader::Signature = 0xffffffffu; 12 | const uint32_t PDB::HashTableHeader::Version = 0xeffe0000u + 19990810u; 13 | -------------------------------------------------------------------------------- /src/PDB_Types.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_DisableWarningsPush.h" 8 | #include 9 | #include "Foundation/PDB_DisableWarningsPop.h" 10 | 11 | 12 | namespace PDB 13 | { 14 | // emulating std::byte from C++17 to make the intention clear that we're dealing with untyped data in certain cases, without actually requiring C++17 15 | enum class Byte : unsigned char {}; 16 | 17 | // PDB files have the notion of "nil" pages, denoted by a special size 18 | // https://github.com/microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp#L177 19 | const uint32_t NilPageSize = 0xffffffffu; 20 | 21 | // PDB files have the notion of a "nil" stream index 22 | // https://github.com/microsoft/microsoft-pdb/blob/master/PDB/include/msf.h#L45 23 | const uint16_t NilStreamIndex = 0xffffu; 24 | 25 | // this matches the definition in guiddef.h, but we don't want to pull that in 26 | struct GUID 27 | { 28 | uint32_t Data1; 29 | uint16_t Data2; 30 | uint16_t Data3; 31 | uint8_t Data4[8]; 32 | }; 33 | 34 | static_assert(sizeof(GUID) == 16u, "Size mismatch."); 35 | 36 | // this matches the definition in winnt.h, but we don't want to pull that in 37 | struct IMAGE_SECTION_HEADER 38 | { 39 | uint8_t Name[8]; 40 | union 41 | { 42 | uint32_t PhysicalAddress; 43 | uint32_t VirtualSize; 44 | } Misc; 45 | uint32_t VirtualAddress; 46 | uint32_t SizeOfRawData; 47 | uint32_t PointerToRawData; 48 | uint32_t PointerToRelocations; 49 | uint32_t PointerToLinenumbers; 50 | uint16_t NumberOfRelocations; 51 | uint16_t NumberOfLinenumbers; 52 | uint32_t Characteristics; 53 | }; 54 | 55 | static_assert(sizeof(IMAGE_SECTION_HEADER) == 40u, "Size mismatch."); 56 | 57 | // https://llvm.org/docs/PDB/MsfFile.html#msf-superblock 58 | struct PDB_NO_DISCARD SuperBlock 59 | { 60 | static const char MAGIC[30u]; 61 | 62 | char fileMagic[30u]; 63 | char padding[2u]; 64 | uint32_t blockSize; 65 | uint32_t freeBlockMapIndex; // index of the free block map 66 | uint32_t blockCount; // number of blocks in the file 67 | uint32_t directorySize; // size of the stream directory in bytes 68 | uint32_t unknown; 69 | PDB_FLEXIBLE_ARRAY_MEMBER(uint32_t, directoryBlockIndices); // indices of the blocks that make up the directory indices 70 | }; 71 | 72 | // https://llvm.org/docs/PDB/PdbStream.html#stream-header 73 | struct Header 74 | { 75 | enum class PDB_NO_DISCARD Version : uint32_t 76 | { 77 | VC2 = 19941610u, 78 | VC4 = 19950623u, 79 | VC41 = 19950814u, 80 | VC50 = 19960307u, 81 | VC98 = 19970604u, 82 | VC70Dep = 19990604u, 83 | VC70 = 20000404u, 84 | VC80 = 20030901u, 85 | VC110 = 20091201u, 86 | VC140 = 20140508u 87 | }; 88 | 89 | Version version; 90 | uint32_t signature; 91 | uint32_t age; 92 | GUID guid; 93 | }; 94 | 95 | // https://llvm.org/docs/PDB/PdbStream.html 96 | struct NamedStreamMap 97 | { 98 | uint32_t length; 99 | PDB_FLEXIBLE_ARRAY_MEMBER(char, stringTable); 100 | 101 | struct HashTableEntry 102 | { 103 | uint32_t stringTableOffset; 104 | uint32_t streamIndex; 105 | }; 106 | }; 107 | 108 | // https://llvm.org/docs/PDB/HashTable.html 109 | struct SerializedHashTable 110 | { 111 | struct Header 112 | { 113 | uint32_t size; 114 | uint32_t capacity; 115 | }; 116 | 117 | struct BitVector 118 | { 119 | uint32_t wordCount; 120 | PDB_FLEXIBLE_ARRAY_MEMBER(uint32_t, words); 121 | }; 122 | }; 123 | 124 | // https://llvm.org/docs/PDB/PdbStream.html#pdb-feature-codes 125 | enum class PDB_NO_DISCARD FeatureCode : uint32_t 126 | { 127 | VC110 = 20091201, 128 | VC140 = 20140508, 129 | 130 | // https://github.com/microsoft/microsoft-pdb/blob/master/PDB/include/pdbcommon.h#L23 131 | NoTypeMerge = 0x4D544F4E, // "NOTM" 132 | MinimalDebugInfo = 0x494E494D // "MINI", i.e. executable was linked with /DEBUG:FASTLINK 133 | }; 134 | 135 | // header of the public stream, based on PSGSIHDR defined here: 136 | // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h#L240 137 | struct PublicStreamHeader 138 | { 139 | uint32_t symHash; 140 | uint32_t addrMap; 141 | uint32_t thunkCount; 142 | uint32_t sizeOfThunk; 143 | uint16_t isectThunkTable; 144 | uint16_t padding; 145 | uint32_t offsetThunkTable; 146 | uint16_t sectionCount; 147 | uint16_t padding2; 148 | }; 149 | 150 | // header of the hash tables used by the public and global symbol stream, based on GSIHashHdr defined here: 151 | // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h#L62 152 | struct HashTableHeader 153 | { 154 | static const uint32_t Signature; 155 | static const uint32_t Version; 156 | 157 | uint32_t signature; 158 | uint32_t version; 159 | uint32_t size; 160 | uint32_t bucketCount; 161 | }; 162 | 163 | // hash record, based on HRFile defined here: 164 | // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h#L8 165 | struct HashRecord 166 | { 167 | uint32_t offset; // offset into the symbol record stream 168 | uint32_t cref; 169 | }; 170 | } 171 | -------------------------------------------------------------------------------- /src/PDB_Util.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2022, Molecular Matters GmbH 2 | // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) 3 | 4 | #pragma once 5 | 6 | #include "Foundation/PDB_Macros.h" 7 | #include "Foundation/PDB_DisableWarningsPush.h" 8 | #include 9 | #include "Foundation/PDB_DisableWarningsPop.h" 10 | 11 | 12 | namespace PDB 13 | { 14 | // Converts a block index into a file offset, based on the block size of the PDB file 15 | PDB_NO_DISCARD inline size_t ConvertBlockIndexToFileOffset(uint32_t blockIndex, uint32_t blockSize) PDB_NO_EXCEPT 16 | { 17 | // cast to size_t to avoid potential overflow in 64-bit 18 | return static_cast(blockIndex) * static_cast(blockSize); 19 | } 20 | 21 | // Calculates how many blocks are needed for a certain number of bytes 22 | PDB_NO_DISCARD inline uint32_t ConvertSizeToBlockCount(uint32_t sizeInBytes, uint32_t blockSize) PDB_NO_EXCEPT 23 | { 24 | // integer ceil to account for non-full blocks 25 | return static_cast((static_cast(sizeInBytes) + blockSize - 1u) / blockSize); 26 | }; 27 | 28 | // Returns the actual size of the data associated with a CodeView record, not including the size of the header 29 | template 30 | PDB_NO_DISCARD inline uint32_t GetCodeViewRecordSize(const T* record) PDB_NO_EXCEPT 31 | { 32 | // the stored size includes the size of the 'kind' field, but not the size of the 'size' field itself 33 | return record->header.size - sizeof(uint16_t); 34 | } 35 | 36 | template 37 | PDB_NO_DISCARD inline size_t GetNameLength(const Header& header, const T& record) PDB_NO_EXCEPT 38 | { 39 | // we can estimate the length of the string from the size of the record 40 | const size_t estimatedLength = header.size - sizeof(uint16_t) - sizeof(T); 41 | if (estimatedLength == 0u) 42 | { 43 | return estimatedLength; 44 | } 45 | 46 | // we still need to account for padding after the string to find the real length 47 | size_t nullTerminatorCount = 0u; 48 | for (/* nothing */; nullTerminatorCount < estimatedLength; ++nullTerminatorCount) 49 | { 50 | if (record.name[estimatedLength - nullTerminatorCount - 1u] != '\0') 51 | { 52 | break; 53 | } 54 | } 55 | 56 | const size_t length = estimatedLength - nullTerminatorCount; 57 | return length; 58 | } 59 | } 60 | --------------------------------------------------------------------------------