feat: PDB import via RawPDB, no msdia140.dll dependency

Replace DIA SDK COM-based PDB importer with RawPDB (MolecularMatters)
which reads PDB files directly via memory-mapped I/O. Adds File menu
"Import PDB..." dialog with type filtering, selection, and progress.

- Vendor raw_pdb into third_party/
- Two-phase API: enumeratePdbTypes() + importPdbSelected()
- Full recursive import of structs/unions/arrays/pointers/bitfields
- PDB import dialog with name filter, select-all, type count
- Benchmark: 1654 types from ntkrnlmp.pdb in 16ms
- Reorganize import/export files into src/imports/
This commit is contained in:
IChooseYou
2026-02-21 17:18:24 -07:00
parent 3a76b03c85
commit 1d7d384b93
100 changed files with 11627 additions and 17 deletions

View File

@@ -0,0 +1,39 @@
project(Examples)
set(SOURCES
ExampleContributions.cpp
ExampleFunctionSymbols.cpp
ExampleFunctionVariables.cpp
ExampleIPI.cpp
ExampleLines.cpp
ExampleMain.cpp
ExampleMemoryMappedFile.cpp
ExampleMemoryMappedFile.h
ExamplePDBSize.cpp
Examples_PCH.cpp
Examples_PCH.h
ExampleSymbols.cpp
ExampleTimedScope.cpp
ExampleTimedScope.h
ExampleTypes.cpp
ExampleTypeTable.cpp
ExampleTypeTable.h
)
source_group(src FILES
${SOURCES}
)
add_executable(Examples
${SOURCES}
)
target_link_libraries(Examples
PUBLIC
raw_pdb
)
target_precompile_headers(Examples
PUBLIC
Examples_PCH.h
)

View File

@@ -0,0 +1,96 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
namespace
{
// we don't have to store std::string in the contributions, since all the data is memory-mapped anyway.
// we do it in this example to ensure that we don't "cheat" when reading the PDB file. memory-mapped data will only
// be faulted into the process once it's touched, so actually copying the string data makes us touch the needed data,
// giving us a real performance measurement.
struct Contribution
{
std::string objectFile;
uint32_t rva;
uint32_t size;
};
}
void ExampleContributions(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream);
void ExampleContributions(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream)
{
TimedScope total("\nRunning example \"Contributions\"");
// in order to keep the example easy to understand, we load the PDB data serially.
// note that this can be improved a lot by reading streams concurrently.
// prepare the image section stream first. it is needed for converting section + offset into an RVA
TimedScope sectionScope("Reading image section stream");
const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile);
sectionScope.Done();
// prepare the module info stream for matching contributions against files
TimedScope moduleScope("Reading module info stream");
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
moduleScope.Done();
// read contribution stream
TimedScope contributionScope("Reading section contribution stream");
const PDB::SectionContributionStream sectionContributionStream = dbiStream.CreateSectionContributionStream(rawPdbFile);
contributionScope.Done();
std::vector<Contribution> contributions;
{
TimedScope scope("Storing contributions");
const PDB::ArrayView<PDB::DBI::SectionContribution> sectionContributions = sectionContributionStream.GetContributions();
const size_t count = sectionContributions.GetLength();
contributions.reserve(count);
for (const PDB::DBI::SectionContribution& contribution : sectionContributions)
{
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(contribution.section, contribution.offset);
if (rva == 0u)
{
printf("Contribution has invalid RVA\n");
continue;
}
const PDB::ModuleInfoStream::Module& module = moduleInfoStream.GetModule(contribution.moduleIndex);
contributions.push_back(Contribution { module.GetName().Decay(), rva, contribution.size });
}
scope.Done(count);
}
TimedScope sortScope("std::sort contributions");
std::sort(contributions.begin(), contributions.end(), [](const Contribution& lhs, const Contribution& rhs)
{
return lhs.size > rhs.size;
});
sortScope.Done();
total.Done();
// log the 20 largest contributions
{
printf("20 largest contributions:\n");
const size_t countToShow = std::min<size_t>(20ul, contributions.size());
for (size_t i = 0u; i < countToShow; ++i)
{
const Contribution& contribution = contributions[i];
printf("%zu: %u bytes from %s\n", i + 1u, contribution.size, contribution.objectFile.c_str());
}
}
}

View File

@@ -0,0 +1,262 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
namespace
{
// in this example, we are only interested in function symbols: function name, RVA, and size.
// this is what most profilers need, they aren't interested in any other data.
struct FunctionSymbol
{
std::string name;
uint32_t rva;
uint32_t size;
const PDB::CodeView::DBI::Record* frameProc;
};
}
void ExampleFunctionSymbols(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream);
void ExampleFunctionSymbols(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream)
{
TimedScope total("\nRunning example \"Function symbols\"");
// in order to keep the example easy to understand, we load the PDB data serially.
// note that this can be improved a lot by reading streams concurrently.
// prepare the image section stream first. it is needed for converting section + offset into an RVA
TimedScope sectionScope("Reading image section stream");
const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile);
sectionScope.Done();
// prepare the module info stream for grabbing function symbols from modules
TimedScope moduleScope("Reading module info stream");
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
moduleScope.Done();
// prepare symbol record stream needed by the public stream
TimedScope symbolStreamScope("Reading symbol record stream");
const PDB::CoalescedMSFStream symbolRecordStream = dbiStream.CreateSymbolRecordStream(rawPdbFile);
symbolStreamScope.Done();
// note that we only use unordered_set in order to keep the example code easy to understand.
// using other hash set implementations like e.g. abseil's Swiss Tables (https://abseil.io/about/design/swisstables) is *much* faster.
std::vector<FunctionSymbol> functionSymbols;
std::unordered_set<uint32_t> seenFunctionRVAs;
// start by reading the module stream, grabbing every function symbol we can find.
// in most cases, this gives us ~90% of all function symbols already, along with their size.
{
TimedScope scope("Storing function symbols from modules");
const PDB::ArrayView<PDB::ModuleInfoStream::Module> modules = moduleInfoStream.GetModules();
for (const PDB::ModuleInfoStream::Module& module : modules)
{
if (!module.HasSymbolStream())
{
continue;
}
const PDB::ModuleSymbolStream moduleSymbolStream = module.CreateSymbolStream(rawPdbFile);
moduleSymbolStream.ForEachSymbol([&functionSymbols, &seenFunctionRVAs, &imageSectionStream](const PDB::CodeView::DBI::Record* record)
{
// only grab function symbols from the module streams
const char* name = nullptr;
uint32_t rva = 0u;
uint32_t size = 0u;
if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_FRAMEPROC)
{
functionSymbols[functionSymbols.size() - 1].frameProc = record;
return;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_THUNK32)
{
if (record->data.S_THUNK32.thunk == PDB::CodeView::DBI::ThunkOrdinal::TrampolineIncremental)
{
// we have never seen incremental linking thunks stored inside a S_THUNK32 symbol, but better safe than sorry
name = "ILT";
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_THUNK32.section, record->data.S_THUNK32.offset);
size = 5u;
}
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_TRAMPOLINE)
{
// incremental linking thunks are stored in the linker module
name = "ILT";
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_TRAMPOLINE.thunkSection, record->data.S_TRAMPOLINE.thunkOffset);
size = 5u;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32)
{
name = record->data.S_LPROC32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32.section, record->data.S_LPROC32.offset);
size = record->data.S_LPROC32.codeSize;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32)
{
name = record->data.S_GPROC32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32.section, record->data.S_GPROC32.offset);
size = record->data.S_GPROC32.codeSize;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32_ID)
{
name = record->data.S_LPROC32_ID.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32_ID.section, record->data.S_LPROC32_ID.offset);
size = record->data.S_LPROC32_ID.codeSize;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32_ID)
{
name = record->data.S_GPROC32_ID.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32_ID.section, record->data.S_GPROC32_ID.offset);
size = record->data.S_GPROC32_ID.codeSize;
}
if (rva == 0u)
{
return;
}
functionSymbols.push_back(FunctionSymbol { name, rva, size, nullptr });
seenFunctionRVAs.emplace(rva);
});
}
scope.Done(modules.GetLength());
}
// we don't need to touch global symbols in this case.
// most of the data we need can be obtained from the module symbol streams, and the global symbol stream only offers data symbols on top of that, which we are not interested in.
// however, there can still be public function symbols we haven't seen yet in any of the modules, especially for PDBs that don't provide module-specific information.
// read public symbols
TimedScope publicScope("Reading public symbol stream");
const PDB::PublicSymbolStream publicSymbolStream = dbiStream.CreatePublicSymbolStream(rawPdbFile);
publicScope.Done();
{
TimedScope scope("Storing public function symbols");
const PDB::ArrayView<PDB::HashRecord> hashRecords = publicSymbolStream.GetRecords();
const size_t count = hashRecords.GetLength();
for (const PDB::HashRecord& hashRecord : hashRecords)
{
const PDB::CodeView::DBI::Record* record = publicSymbolStream.GetRecord(symbolRecordStream, hashRecord);
if (record->header.kind != PDB::CodeView::DBI::SymbolRecordKind::S_PUB32)
{
// 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.
// ignore these.
continue;
}
if ((PDB_AS_UNDERLYING(record->data.S_PUB32.flags) & PDB_AS_UNDERLYING(PDB::CodeView::DBI::PublicSymbolFlags::Function)) == 0u)
{
// ignore everything that is not a function
continue;
}
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_PUB32.section, record->data.S_PUB32.offset);
if (rva == 0u)
{
// certain symbols (e.g. control-flow guard symbols) don't have a valid RVA, ignore those
continue;
}
// check whether we already know this symbol from one of the module streams
const auto it = seenFunctionRVAs.find(rva);
if (it != seenFunctionRVAs.end())
{
// we know this symbol already, ignore it
continue;
}
// this is a new function symbol, so store it.
// note that we don't know its size yet.
functionSymbols.push_back(FunctionSymbol { record->data.S_PUB32.name, rva, 0u, nullptr });
}
scope.Done(count);
}
// we still need to find the size of the public function symbols.
// this can be deduced by sorting the symbols by their RVA, and then computing the distance between the current and the next symbol.
// this works since functions are always mapped to executable pages, so they aren't interleaved by any data symbols.
TimedScope sortScope("std::sort function symbols");
std::sort(functionSymbols.begin(), functionSymbols.end(), [](const FunctionSymbol& lhs, const FunctionSymbol& rhs)
{
return lhs.rva < rhs.rva;
});
sortScope.Done();
const size_t symbolCount = functionSymbols.size();
if (symbolCount != 0u)
{
TimedScope computeScope("Computing function symbol sizes");
size_t foundCount = 0u;
// we have at least 1 symbol.
// compute missing symbol sizes by computing the distance from this symbol to the next.
// note that this includes "int 3" padding after the end of a function. if you don't want that, but the actual number of bytes of
// the function's code, your best bet is to use a disassembler instead.
for (size_t i = 0u; i < symbolCount - 1u; ++i)
{
FunctionSymbol& currentSymbol = functionSymbols[i];
if (currentSymbol.size != 0u)
{
// the symbol's size is already known
continue;
}
const FunctionSymbol& nextSymbol = functionSymbols[i + 1u];
const size_t size = nextSymbol.rva - currentSymbol.rva;
(void)size; // unused
++foundCount;
}
// we know have the sizes of all symbols, except the last.
// this can be found by going through the contributions, if needed.
FunctionSymbol& lastSymbol = functionSymbols[symbolCount - 1u];
if (lastSymbol.size == 0u)
{
// bad luck, we can't deduce the last symbol's size, so have to consult the contributions instead.
// we do a linear search in this case to keep the code simple.
const PDB::SectionContributionStream sectionContributionStream = dbiStream.CreateSectionContributionStream(rawPdbFile);
const PDB::ArrayView<PDB::DBI::SectionContribution> sectionContributions = sectionContributionStream.GetContributions();
for (const PDB::DBI::SectionContribution& contribution : sectionContributions)
{
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(contribution.section, contribution.offset);
if (rva == 0u)
{
printf("Contribution has invalid RVA\n");
continue;
}
if (rva == lastSymbol.rva)
{
lastSymbol.size = contribution.size;
break;
}
if (rva > lastSymbol.rva)
{
// should have found the contribution by now
printf("Unknown contribution for symbol %s at RVA 0x%X", lastSymbol.name.c_str(), lastSymbol.rva);
break;
}
}
}
computeScope.Done(foundCount);
}
total.Done(functionSymbols.size());
}

View File

@@ -0,0 +1,382 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "ExampleTypeTable.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
#include "PDB_TPIStream.h"
using SymbolRecordKind = PDB::CodeView::DBI::SymbolRecordKind;
static std::string GetVariableTypeName(const TypeTable& typeTable, uint32_t typeIndex)
{
// Defined in ExampleTypes.cpp
extern std::string GetTypeName(const TypeTable & typeTable, uint32_t typeIndex);
std::string typeName = GetTypeName(typeTable, typeIndex);
// Remove any '%s' substring used to insert a variable/field name.
const uint64_t markerPos = typeName.find("%s");
if (markerPos != typeName.npos)
{
typeName.erase(markerPos, 2);
}
return typeName;
}
static void Printf(uint32_t indent, const char* format, ...)
{
va_list args;
va_start(args, format);
printf("%*s", indent * 4, "");
vprintf(format, args);
va_end(args);
}
void ExampleFunctionVariables(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::TPIStream& tpiStream);
void ExampleFunctionVariables(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::TPIStream& tpiStream)
{
TimedScope total("\nRunning example \"Function variables\"");
TimedScope typeTableScope("Create TypeTable");
TypeTable typeTable(tpiStream);
typeTableScope.Done();
// in order to keep the example easy to understand, we load the PDB data serially.
// note that this can be improved a lot by reading streams concurrently.
// prepare the image section stream first. it is needed for converting section + offset into an RVA
TimedScope sectionScope("Reading image section stream");
const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile);
sectionScope.Done();
// prepare the module info stream for grabbing function symbols from modules
TimedScope moduleScope("Reading module info stream");
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
moduleScope.Done();
// prepare symbol record stream needed by the public stream
TimedScope symbolStreamScope("Reading symbol record stream");
const PDB::CoalescedMSFStream symbolRecordStream = dbiStream.CreateSymbolRecordStream(rawPdbFile);
symbolStreamScope.Done();
{
TimedScope scope("Printing function variable records from modules\n");
const PDB::ArrayView<PDB::ModuleInfoStream::Module> modules = moduleInfoStream.GetModules();
uint32_t blockLevel = 0;
uint32_t recordCount = 0;
for (const PDB::ModuleInfoStream::Module& module : modules)
{
if (!module.HasSymbolStream())
{
continue;
}
const PDB::ModuleSymbolStream moduleSymbolStream = module.CreateSymbolStream(rawPdbFile);
moduleSymbolStream.ForEachSymbol([&typeTable, &imageSectionStream, &blockLevel, &recordCount](const PDB::CodeView::DBI::Record* record)
{
const SymbolRecordKind kind = record->header.kind;
const PDB::CodeView::DBI::Record::Data& data = record->data;
if (kind == SymbolRecordKind::S_END)
{
PDB_ASSERT(blockLevel > 0, "Block level for S_END is 0");
blockLevel--;
Printf(blockLevel, "S_END\n");
if (blockLevel == 0)
{
Printf(0, "\n");
}
}
else if(kind == SymbolRecordKind::S_SKIP)
{
Printf(blockLevel, "S_SKIP\n");
}
else if (kind == SymbolRecordKind::S_BLOCK32)
{
const uint32_t offset = imageSectionStream.ConvertSectionOffsetToRVA(data.S_BLOCK32.section, data.S_BLOCK32.offset);
Printf(blockLevel, "S_BLOCK32: '%s' | Code Offset 0x%X\n", data.S_BLOCK32.name, offset);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_LABEL32)
{
Printf(blockLevel, "S_LABEL32: '%s' | Offset 0x%X\n", data.S_LABEL32.name, data.S_LABEL32.offset);
}
else if(kind == SymbolRecordKind::S_CONSTANT)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_CONSTANT.typeIndex);
Printf(blockLevel, "S_CONSTANT: '%s' -> '%s' | Value 0x%X\n", typeName.c_str(), data.S_CONSTANT.name, data.S_CONSTANT.value);
}
else if(kind == SymbolRecordKind::S_LOCAL)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_LOCAL.typeIndex);
Printf(blockLevel, "S_LOCAL: '%s' -> '%s' | Param: %s | Optimized Out: %s\n", typeName.c_str(), data.S_LOCAL.name, data.S_LOCAL.flags.fIsParam ? "True" : "False", data.S_LOCAL.flags.fIsOptimizedOut ? "True" : "False");
}
else if (kind == SymbolRecordKind::S_DEFRANGE_REGISTER)
{
Printf(blockLevel, "S_DEFRANGE_REGISTER: Register 0x%X\n", data.S_DEFRANGE_REGISTER.reg);
}
else if(kind == SymbolRecordKind::S_DEFRANGE_FRAMEPOINTER_REL)
{
Printf(blockLevel, "S_DEFRANGE_FRAMEPOINTER_REL: Frame Pointer Offset 0x%X | Range Start 0x%X | Range Section Start 0x%X | Range Length %u\n",
data.S_DEFRANGE_FRAMEPOINTER_REL.offsetFramePointer,
data.S_DEFRANGE_FRAMEPOINTER_REL.range.offsetStart,
data.S_DEFRANGE_FRAMEPOINTER_REL.range.isectionStart,
data.S_DEFRANGE_FRAMEPOINTER_REL.range.length);
}
else if(kind == SymbolRecordKind::S_DEFRANGE_SUBFIELD_REGISTER)
{
Printf(blockLevel, "S_DEFRANGE_SUBFIELD_REGISTER: Register %u | Parent offset 0x%X | Range Start 0x%X | Range Section Start 0x%X | Range Length %u\n",
data.S_DEFRANGE_SUBFIELD_REGISTER.reg,
data.S_DEFRANGE_SUBFIELD_REGISTER.offsetParent,
data.S_DEFRANGE_SUBFIELD_REGISTER.range.offsetStart,
data.S_DEFRANGE_SUBFIELD_REGISTER.range.isectionStart,
data.S_DEFRANGE_SUBFIELD_REGISTER.range.length);
}
else if (kind == SymbolRecordKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE)
{
Printf(blockLevel, "S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: Offset 0x%X\n", data.S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE.offsetFramePointer);
}
else if (kind == SymbolRecordKind::S_DEFRANGE_REGISTER_REL)
{
Printf(blockLevel, "S_DEFRANGE_REGISTER_REL: Base Register %u | Parent offset 0x%X | Base Register Offset 0x%X | Range Start 0x%X | Range Section Start 0x%X | Range Length %u\n",
data.S_DEFRANGE_REGISTER_REL.baseRegister,
data.S_DEFRANGE_REGISTER_REL.offsetParent,
data.S_DEFRANGE_REGISTER_REL.offsetBasePointer,
data.S_DEFRANGE_REGISTER_REL.offsetParent,
data.S_DEFRANGE_REGISTER_REL.range.offsetStart,
data.S_DEFRANGE_REGISTER_REL.range.isectionStart,
data.S_DEFRANGE_REGISTER_REL.range.length);
}
else if(kind == SymbolRecordKind::S_FILESTATIC)
{
Printf(blockLevel, "S_FILESTATIC: '%s'\n", data.S_FILESTATIC.name);
}
else if (kind == SymbolRecordKind::S_INLINESITE)
{
Printf(blockLevel, "S_INLINESITE: Parent 0x%X\n", data.S_INLINESITE.parent);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_INLINESITE_END)
{
PDB_ASSERT(blockLevel > 0, "Block level for S_INLINESITE_END is 0");
blockLevel--;
Printf(blockLevel, "S_INLINESITE_END:\n");
}
else if (kind == SymbolRecordKind::S_CALLEES)
{
Printf(blockLevel, "S_CALLEES: Count %u\n", data.S_CALLEES.count);
}
else if (kind == SymbolRecordKind::S_CALLERS)
{
Printf(blockLevel, "S_CALLERS: Count %u\n", data.S_CALLERS.count);
}
else if (kind == SymbolRecordKind::S_INLINEES)
{
Printf(blockLevel, "S_INLINEES: Count %u\n", data.S_INLINEES.count);
}
else if (kind == SymbolRecordKind::S_LDATA32)
{
if (blockLevel > 0)
{
// Not sure why some type index 0 (T_NO_TYPE) are included in some PDBs.
if (data.S_LDATA32.typeIndex != 0) // PDB::CodeView::TPI::TypeIndexKind::T_NOTYPE)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_LDATA32.typeIndex);
Printf(blockLevel, "S_LDATA32: '%s' -> '%s'\n", data.S_LDATA32.name, typeName.c_str());
}
}
}
else if (kind == SymbolRecordKind::S_LTHREAD32)
{
if (blockLevel > 0)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_LTHREAD32.typeIndex);
Printf(blockLevel, "S_LTHREAD32: '%s' -> '%s'\n", data.S_LTHREAD32.name, typeName.c_str());
}
}
else if (kind == SymbolRecordKind::S_UDT)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_UDT.typeIndex);
Printf(blockLevel, "S_UDT: '%s' -> '%s'\n", data.S_UDT.name, typeName.c_str());
}
else if (kind == PDB::CodeView::DBI::SymbolRecordKind::S_REGISTER)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_REGSYM.typeIndex);
Printf(blockLevel, "S_REGSYM: '%s' -> '%s' | Register %i\n",
data.S_REGSYM.name, typeName.c_str(),
data.S_REGSYM.reg);
}
else if (kind == PDB::CodeView::DBI::SymbolRecordKind::S_BPREL32)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_BPRELSYM32.typeIndex);
Printf(blockLevel, "S_BPRELSYM32: '%s' -> '%s' | BP register Offset 0x%X\n",
data.S_BPRELSYM32.name, typeName.c_str(),
data.S_BPRELSYM32.offset);
}
else if (kind == PDB::CodeView::DBI::SymbolRecordKind::S_REGREL32)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_REGREL32.typeIndex);
Printf(blockLevel, "S_REGREL32: '%s' -> '%s' | Register %i | Register Offset 0x%X\n",
data.S_REGREL32.name, typeName.c_str(),
data.S_REGREL32.reg,
data.S_REGREL32.offset);
}
else if(kind == SymbolRecordKind::S_FRAMECOOKIE)
{
Printf(blockLevel, "S_FRAMECOOKIE: Offset 0x%X | Register %u | Type %u\n",
data.S_FRAMECOOKIE.offset,
data.S_FRAMECOOKIE.reg,
data.S_FRAMECOOKIE.cookietype);
}
else if(kind == SymbolRecordKind::S_CALLSITEINFO)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_CALLSITEINFO.typeIndex);
Printf(blockLevel, "S_CALLSITEINFO: '%s' | Offset 0x%X | Section %u\n", typeName.c_str(), data.S_CALLSITEINFO.offset, data.S_CALLSITEINFO.section);
}
else if(kind == SymbolRecordKind::S_HEAPALLOCSITE)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_HEAPALLOCSITE.typeIndex);
Printf(blockLevel, "S_HEAPALLOCSITE: '%s' | Offset 0x%X | Section %u | Instruction Length %u\n", typeName.c_str(),
data.S_HEAPALLOCSITE.offset,
data.S_HEAPALLOCSITE.section,
data.S_HEAPALLOCSITE.instructionLength);
}
else if (kind == SymbolRecordKind::S_FRAMEPROC)
{
Printf(blockLevel, "S_FRAMEPROC: Size %u | Padding %u | Padding Offset 0x%X | Callee Registers Size %u\n",
data.S_FRAMEPROC.cbFrame,
data.S_FRAMEPROC.cbPad,
data.S_FRAMEPROC.offPad,
data.S_FRAMEPROC.cbSaveRegs);
}
else if (kind == SymbolRecordKind::S_ANNOTATION)
{
Printf(blockLevel, "S_ANNOTATION: Offset 0x%X | Count %u\n", data.S_ANNOTATIONSYM.offset, data.S_ANNOTATIONSYM.annotationsCount);
// print N null-terminated annotation strings, skipping their null-terminators to get to the next string
const char* annotation = data.S_ANNOTATIONSYM.annotations;
for (int i = 0; i < data.S_ANNOTATIONSYM.annotationsCount; ++i, annotation += strlen(annotation) + 1)
Printf(blockLevel + 1, "S_ANNOTATION.%u: %s\n", i, annotation);
PDB_ASSERT(annotation <= (const char*)record + record->header.size + sizeof(record->header.size),
"Annotation strings end beyond the record size %X; annotaions count: %u", record->header.size, data.S_ANNOTATIONSYM.annotationsCount);
}
else if (kind == SymbolRecordKind::S_THUNK32)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
if (data.S_THUNK32.thunk == PDB::CodeView::DBI::ThunkOrdinal::TrampolineIncremental)
{
// we have never seen incremental linking thunks stored inside a S_THUNK32 symbol, but better safe than sorry
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_THUNK32.section, data.S_THUNK32.offset);
Printf(blockLevel, "Function: 'ILT/Thunk' | RVA 0x%X\n", rva);
}
else
{
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_THUNK32.section, data.S_THUNK32.offset);
Printf(blockLevel, "S_THUNK32 Function '%s' | RVA 0x%X\n", data.S_THUNK32.name, rva);
blockLevel++;
}
}
else if (kind == SymbolRecordKind::S_TRAMPOLINE)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
// incremental linking thunks are stored in the linker module
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_TRAMPOLINE.thunkSection, data.S_TRAMPOLINE.thunkOffset);
Printf(blockLevel, "Function 'ILT/Trampoline' | RVA 0x%X\n", rva);
}
else if (kind == SymbolRecordKind::S_LPROC32)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_LPROC32.section, data.S_LPROC32.offset);
Printf(blockLevel, "S_LPROC32 Function '%s' | RVA 0x%X\n", data.S_LPROC32.name, rva);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_GPROC32)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_GPROC32.section, data.S_GPROC32.offset);
Printf(blockLevel, "S_GPROC32 Function '%s' | RVA 0x%X\n", data.S_GPROC32.name, rva);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_LPROC32_ID)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_LPROC32_ID.section, data.S_LPROC32_ID.offset);
Printf(blockLevel, "S_LPROC32_ID Function '%s' | RVA 0x%X\n", data.S_LPROC32_ID.name, rva);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_GPROC32_ID)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_GPROC32_ID.section, data.S_GPROC32_ID.offset);
Printf(blockLevel, "S_GPROC32_ID Function '%s' | RVA 0x%X\n", data.S_GPROC32_ID.name, rva);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_REGREL32_INDIR)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_REGREL32_INDIR.typeIndex);
Printf(blockLevel, "S_REGREL32_INDIR: '%s' -> '%s' | Register %i | Unknown1 0x%X | Unknown2 0x%X\n",
data.S_REGREL32_INDIR.name, typeName.c_str(),
data.S_REGREL32_INDIR.unknown1,
data.S_REGREL32_INDIR.unknown1);
}
else if (kind == SymbolRecordKind::S_REGREL32_ENCTMP)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_REGREL32.typeIndex);
Printf(blockLevel, "S_REGREL32_ENCTMP: '%s' -> '%s' | Register %i | Register Offset 0x%X\n",
data.S_REGREL32.name, typeName.c_str(),
data.S_REGREL32.reg,
data.S_REGREL32.offset);
}
else if (kind == SymbolRecordKind::S_UNAMESPACE)
{
Printf(blockLevel, "S_UNAMESPACE: '%s'\n", data.S_UNAMESPACE.name);
}
else if (kind == SymbolRecordKind::S_ARMSWITCHTABLE)
{
Printf(blockLevel, "S_ARMSWITCHTABLE: "
"Switch Type: %u | Num Entries: %u | Base Section: %u | Base Offset: 0x%X | "
"Branch Section: %u | Branch Offset: 0x%X | Table Section: %u | Table Offset: 0x%X\n",
data.S_ARMSWITCHTABLE.switchType,
data.S_ARMSWITCHTABLE.numEntries,
data.S_ARMSWITCHTABLE.sectionBase,
data.S_ARMSWITCHTABLE.offsetBase,
data.S_ARMSWITCHTABLE.sectionBranch,
data.S_ARMSWITCHTABLE.offsetBranch,
data.S_ARMSWITCHTABLE.sectionTable,
data.S_ARMSWITCHTABLE.offsetTable);
}
else
{
// We only care about records inside functions.
if (blockLevel > 0)
{
PDB_ASSERT(false, "Unhandled record kind 0x%X with block level %u\n", static_cast<uint16_t>(kind), blockLevel);
}
}
recordCount++;
});
}
scope.Done(recordCount);
}
}

View File

@@ -0,0 +1,198 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "ExampleTypeTable.h"
#include "PDB_RawFile.h"
#include "PDB_InfoStream.h"
#include "PDB_IPIStream.h"
#include "PDB_TPIStream.h"
static std::string GetTypeNameIPI(const TypeTable& typeTable, uint32_t typeIndex)
{
// Defined in ExampleTypes.cpp
extern std::string GetTypeName(const TypeTable & typeTable, uint32_t typeIndex);
std::string typeName = GetTypeName(typeTable, typeIndex);
// Remove any '%s' substring used to insert a variable/field name.
const uint64_t markerPos = typeName.find("%s");
if (markerPos != typeName.npos)
{
typeName.erase(markerPos, 2);
}
return typeName;
}
void ExampleIPI(const PDB::RawFile& rawPdbFile, const PDB::InfoStream& infoStream, const PDB::TPIStream& tpiStream, const PDB::IPIStream& ipiStream);
void ExampleIPI(const PDB::RawFile& rawPdbFile, const PDB::InfoStream& infoStream, const PDB::TPIStream& tpiStream, const PDB::IPIStream& ipiStream)
{
if (!infoStream.HasIPIStream())
{
return;
}
TimedScope total("\nRunning example \"IPI\"");
TimedScope typeTableScope("Create TypeTable");
TypeTable typeTable(tpiStream);
typeTableScope.Done();
// prepare names stream for grabbing file paths from lines
TimedScope namesScope("Reading names stream");
const PDB::NamesStream namesStream = infoStream.CreateNamesStream(rawPdbFile);
namesScope.Done();
const uint32_t firstTypeIndex = ipiStream.GetFirstTypeIndex();
PDB::ArrayView<const PDB::CodeView::IPI::Record*> records = ipiStream.GetTypeRecords();
std::vector<const char*> strings;
strings.resize(records.GetLength(), nullptr);
size_t index = 0;
for (const PDB::CodeView::IPI::Record* record : records)
{
const PDB::CodeView::IPI::RecordHeader& header = record->header;
if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_STRING_ID)
{
strings[index] = record->data.LF_STRING_ID.name;
}
index++;
}
uint32_t identifier = firstTypeIndex;
std::string typeName, parentTypeName;
printf("\n --- IPI Records ---\n\n");
for(const PDB::CodeView::IPI::Record* record : records)
{
const PDB::CodeView::IPI::RecordHeader& header = record->header;
if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_FUNC_ID)
{
typeName = GetTypeNameIPI(typeTable, record->data.LF_FUNC_ID.typeIndex);
printf("Kind: 'LF_FUNC_ID' Size: %i ID: %u\n", header.size, identifier);
printf(" Scope ID: %u\n Type: '%s'\n Name: '%s'\n\n",
record->data.LF_FUNC_ID.scopeId,
typeName.c_str(),
record->data.LF_FUNC_ID.name);
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_MFUNC_ID)
{
typeName = GetTypeNameIPI(typeTable, record->data.LF_MFUNC_ID.typeIndex);
parentTypeName = GetTypeNameIPI(typeTable, record->data.LF_MFUNC_ID.parentTypeIndex);
printf("Kind: 'LF_MFUNC_ID' Size: %i ID: %u\n", header.size, identifier);
printf(" Parent Type: '%s'\n Type: '%s'\n Name: '%s'\n\n",
parentTypeName.c_str(),
typeName.c_str(),
record->data.LF_MFUNC_ID.name);
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_BUILDINFO)
{
printf("Kind: 'LF_BUILDINFO' Size: %u ID: %u\n", header.size, identifier);
if (record->data.LF_BUILDINFO.count == 0)
{
continue;
}
printf("Strings: '%s'", strings[record->data.LF_BUILDINFO.typeIndices[0] - firstTypeIndex]);
for (uint32_t i = 1, size = record->data.LF_BUILDINFO.count; i < size; ++i)
{
const uint32_t stringIndex = record->data.LF_BUILDINFO.typeIndices[i];
if (stringIndex == 0)
{
printf(", ''");
}
else
{
printf(", '%s'", strings[stringIndex - firstTypeIndex]);
}
}
printf("\n\n");
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_SUBSTR_LIST)
{
printf("Kind: 'LF_SUBSTR_LIST' Size: %u ID: %u\n", header.size, identifier);
if (record->data.LF_SUBSTR_LIST.count == 0)
{
continue;
}
printf(" Strings: '%s'", strings[record->data.LF_SUBSTR_LIST.typeIndices[0] - firstTypeIndex]);
for (uint32_t i = 1, size = record->data.LF_SUBSTR_LIST.count; i < size; ++i)
{
const uint32_t stringIndex = record->data.LF_SUBSTR_LIST.typeIndices[i];
if (stringIndex == 0)
{
printf(", ''");
}
else
{
printf(", '%s'", strings[stringIndex - firstTypeIndex]);
}
}
printf("\n\n");
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_STRING_ID)
{
printf("Kind: 'LF_STRING_ID' Size: %u ID: %u\n", header.size, identifier);
printf(" Substring ID: %u\n Name: '%s'\n\n", record->data.LF_STRING_ID.id, record->data.LF_STRING_ID.name);
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_UDT_SRC_LINE)
{
typeName = GetTypeNameIPI(typeTable, record->data.LF_UDT_SRC_LINE.typeIndex);
const uint32_t stringIndex = record->data.LF_UDT_SRC_LINE.stringIndex;
printf("Kind: 'LF_UDT_SRC_LINE' Size: %u ID: %u\n", header.size, identifier);
printf(" Type: '%s'\n Source Path: %s\n Line: %u\n\n",
typeName.c_str(),
strings[stringIndex - firstTypeIndex],
record->data.LF_UDT_SRC_LINE.line);
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_UDT_MOD_SRC_LINE)
{
typeName = GetTypeNameIPI(typeTable, record->data.LF_UDT_MOD_SRC_LINE.typeIndex);
const char* string = namesStream.GetFilename(record->data.LF_UDT_MOD_SRC_LINE.stringIndex);
printf("Kind: 'LF_UDT_SRC_LINE' Size: %u ID: %u\n", header.size, identifier);
printf(" Type: '%s'\n Source Path: %s\n Line: %u\n Module Index: %u\n\n",
typeName.c_str(),
string,
record->data.LF_UDT_MOD_SRC_LINE.line,
record->data.LF_UDT_MOD_SRC_LINE.moduleIndex);
}
else
{
printf("Kind: 0x%X Size: %u ID: %u\n\n", static_cast<uint32_t>(header.kind), header.size, identifier);
}
identifier++;
}
}

View File

@@ -0,0 +1,268 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "Foundation/PDB_PointerUtil.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
#include "PDB_InfoStream.h"
#include <cstring>
namespace
{
struct Section
{
uint16_t index;
uint32_t offset;
size_t lineIndex;
};
struct Filename
{
uint32_t fileChecksumOffset;
uint32_t namesFilenameOffset;
PDB::CodeView::DBI::ChecksumKind checksumKind;
uint8_t checksumSize;
uint8_t checksum[32];
};
struct Line
{
uint32_t lineNumber;
uint32_t codeSize;
size_t filenameIndex;
};
}
void ExampleLines(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::InfoStream& infoStream);
void ExampleLines(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::InfoStream& infoStream)
{
if (!infoStream.HasNamesStream())
{
printf("PDB has no '/names' stream for looking up filenames for lines, skipping \"Lines\" example.");
return;
}
TimedScope total("\nRunning example \"Lines\"");
// prepare the image section stream first. it is needed for converting section + offset into an RVA
TimedScope sectionScope("Reading image section stream");
const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile);
sectionScope.Done();
// prepare the module info stream for grabbing function symbols from modules
TimedScope moduleScope("Reading module info stream");
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
moduleScope.Done();
// prepare names stream for grabbing file paths from lines
TimedScope namesScope("Reading names stream");
const PDB::NamesStream namesStream = infoStream.CreateNamesStream(rawPdbFile);
namesScope.Done();
// keeping sections and lines separate, as sorting the smaller Section struct is 2x faster in release builds
// than having all the fields in one big Line struct and sorting those.
std::vector<Section> sections;
std::vector<Filename> filenames;
std::vector<Line> lines;
{
TimedScope scope("Storing lines from modules");
const PDB::ArrayView<PDB::ModuleInfoStream::Module> modules = moduleInfoStream.GetModules();
for (const PDB::ModuleInfoStream::Module& module : modules)
{
if (!module.HasLineStream())
{
continue;
}
const PDB::ModuleLineStream moduleLineStream = module.CreateLineStream(rawPdbFile);
const size_t moduleFilenamesStartIndex = filenames.size();
const PDB::CodeView::DBI::FileChecksumHeader* moduleFileChecksumHeader = nullptr;
moduleLineStream.ForEachSection([&moduleLineStream, &namesStream, &moduleFileChecksumHeader, &sections, &filenames, &lines](const PDB::CodeView::DBI::LineSection* lineSection)
{
if (lineSection->header.kind == PDB::CodeView::DBI::DebugSubsectionKind::S_LINES)
{
moduleLineStream.ForEachLinesBlock(lineSection,
[&lineSection, &sections, &filenames, &lines](const PDB::CodeView::DBI::LinesFileBlockHeader* linesBlockHeader, const PDB::CodeView::DBI::Line* blocklines, const PDB::CodeView::DBI::Column* blockColumns)
{
if (linesBlockHeader->numLines == 0)
{
return;
}
const PDB::CodeView::DBI::Line& firstLine = blocklines[0];
const uint16_t sectionIndex = lineSection->linesHeader.sectionIndex;
const uint32_t sectionOffset = lineSection->linesHeader.sectionOffset;
const uint32_t fileChecksumOffset = linesBlockHeader->fileChecksumOffset;
const size_t filenameIndex = filenames.size();
// there will be duplicate filenames for any real world pdb.
// ideally the filenames would be stored in a map with the filename or checksum as the key.
// but that would complicate the logic in this example and therefore just use a vector to make it easier to understand.
filenames.push_back({ fileChecksumOffset, 0, PDB::CodeView::DBI::ChecksumKind::None, 0, {0} });
sections.push_back({ sectionIndex, sectionOffset, lines.size() });
// initially set code size of first line to 0, will be updated in loop below.
lines.push_back({ firstLine.linenumStart, 0, filenameIndex });
for(uint32_t i = 1, size = linesBlockHeader->numLines; i < size; ++i)
{
const PDB::CodeView::DBI::Line& line = blocklines[i];
// calculate code size of previous line by using the current line offset.
lines.back().codeSize = line.offset - blocklines[i-1].offset;
sections.push_back({ sectionIndex, sectionOffset + line.offset, lines.size() });
lines.push_back({ line.linenumStart, 0, filenameIndex });
}
// calc code size of last line
lines.back().codeSize = lineSection->linesHeader.codeSize - blocklines[linesBlockHeader->numLines-1].offset;
// columns are optional
if (blockColumns == nullptr)
{
return;
}
for (uint32_t i = 0, size = linesBlockHeader->numLines; i < size; ++i)
{
const PDB::CodeView::DBI::Column& column = blockColumns[i];
(void)column;
}
});
}
else if (lineSection->header.kind == PDB::CodeView::DBI::DebugSubsectionKind::S_FILECHECKSUMS)
{
// how to read checksums and their filenames from the Names Stream
moduleLineStream.ForEachFileChecksum(lineSection, [&namesStream](const PDB::CodeView::DBI::FileChecksumHeader* fileChecksumHeader)
{
const char* filename = namesStream.GetFilename(fileChecksumHeader->filenameOffset);
(void)filename;
});
// store the checksum header for the module, as there might be more lines after the checksums.
// so lines will get their checksum header values assigned after processing all line sections in the module.
PDB_ASSERT(moduleFileChecksumHeader == nullptr, "Module File Checksum Header already set");
moduleFileChecksumHeader = &lineSection->checksumHeader;
}
else if (lineSection->header.kind == PDB::CodeView::DBI::DebugSubsectionKind::S_INLINEELINES)
{
if (lineSection->inlineeHeader.kind == PDB::CodeView::DBI::InlineeSourceLineKind::Signature)
{
moduleLineStream.ForEachInlineeSourceLine(lineSection, [](const PDB::CodeView::DBI::InlineeSourceLine* inlineeSourceLine)
{
(void)inlineeSourceLine;
});
}
else
{
moduleLineStream.ForEachInlineeSourceLineEx(lineSection, [](const PDB::CodeView::DBI::InlineeSourceLineEx* inlineeSourceLineEx)
{
for (uint32_t i = 0; i < inlineeSourceLineEx->extraLines; ++i)
{
const uint32_t checksumOffset = inlineeSourceLineEx->extrafileChecksumOffsets[i];
(void)checksumOffset;
}
});
}
}
else
{
PDB_ASSERT(false, "Line Section kind 0x%X not handled", static_cast<uint32_t>(lineSection->header.kind));
}
});
// assign checksum values for each filename added in this module
for (size_t i = moduleFilenamesStartIndex, size = filenames.size(); i < size; ++i)
{
Filename& filename = filenames[i];
// look up the filename's checksum header in the module's checksums section
const PDB::CodeView::DBI::FileChecksumHeader* checksumHeader = PDB::Pointer::Offset<const PDB::CodeView::DBI::FileChecksumHeader*>(moduleFileChecksumHeader, filename.fileChecksumOffset);
PDB_ASSERT(checksumHeader->checksumKind >= PDB::CodeView::DBI::ChecksumKind::None &&
checksumHeader->checksumKind <= PDB::CodeView::DBI::ChecksumKind::SHA256,
"Invalid checksum kind %u", static_cast<uint16_t>(checksumHeader->checksumKind));
// store checksum values in filname struct
filename.namesFilenameOffset = checksumHeader->filenameOffset;
filename.checksumKind = checksumHeader->checksumKind;
filename.checksumSize = checksumHeader->checksumSize;
std::memcpy(filename.checksum, checksumHeader->checksum, checksumHeader->checksumSize);
}
}
scope.Done(modules.GetLength());
TimedScope sortScope("std::sort sections");
// sort sections, so we can iterate over lines by address order.
std::sort(sections.begin(), sections.end(), [](const Section& lhs, const Section& rhs)
{
if (lhs.index == rhs.index)
{
return lhs.offset < rhs.offset;
}
return lhs.index < rhs.index;
});
sortScope.Done(sections.size());
// Disabled by default, as it will print a lot of lines for large PDBs :-)
#if 0
// DIA2Dump style lines output
static const char hexChars[17] = "0123456789ABCDEF";
char checksumString[128];
printf("*** LINES RAW PDB\n");
const char* prevFilename = nullptr;
for (const Section& section : sections)
{
const Line& line = lines[section.lineIndex];
const Filename& lineFilename = filenames[line.filenameIndex];
const char* filename = namesStream.GetFilename(lineFilename.namesFilenameOffset);
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(section.index, section.offset);
// only print filename for a line if it is different from the previous one.
if (filename != prevFilename)
{
for (size_t i = 0, j = 0; i < lineFilename.checksumSize; i++, j+=2)
{
checksumString[j] = hexChars[lineFilename.checksum[i] >> 4];
checksumString[j+1] = hexChars[lineFilename.checksum[i] & 0xF];
}
checksumString[lineFilename.checksumSize * 2] = '\0';
printf(" line %u at [0x%08X][0x%04X:0x%08X], len = 0x%X %s (0x%02X: %s)\n",
line.lineNumber, rva, section.index, section.offset, line.codeSize,
filename, static_cast<uint32_t>(lineFilename.checksumKind), checksumString);
prevFilename = filename;
}
else
{
printf(" line %u at [0x%08X][0x%04X:0x%08X], len = 0x%X\n",
line.lineNumber, rva, section.index, section.offset, line.codeSize);
}
}
#endif
}
}

View File

@@ -0,0 +1,200 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleMemoryMappedFile.h"
#include "PDB.h"
#include "PDB_RawFile.h"
#include "PDB_InfoStream.h"
#include "PDB_DBIStream.h"
#include "PDB_TPIStream.h"
#include "PDB_IPIStream.h"
#include "PDB_NamesStream.h"
namespace
{
PDB_NO_DISCARD static bool IsError(PDB::ErrorCode errorCode)
{
switch (errorCode)
{
case PDB::ErrorCode::Success:
return false;
case PDB::ErrorCode::InvalidSuperBlock:
printf("Invalid Superblock\n");
return true;
case PDB::ErrorCode::InvalidFreeBlockMap:
printf("Invalid free block map\n");
return true;
case PDB::ErrorCode::InvalidStream:
printf("Invalid stream\n");
return true;
case PDB::ErrorCode::InvalidSignature:
printf("Invalid stream signature\n");
return true;
case PDB::ErrorCode::InvalidStreamIndex:
printf("Invalid stream index\n");
return true;
case PDB::ErrorCode::InvalidDataSize:
printf("Invalid data size\n");
return true;
case PDB::ErrorCode::UnknownVersion:
printf("Unknown version\n");
return true;
}
// only ErrorCode::Success means there wasn't an error, so all other paths have to assume there was an error
return true;
}
PDB_NO_DISCARD static bool HasValidDBIStreams(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream)
{
// check whether the DBI stream offers all sub-streams we need
if (IsError(dbiStream.HasValidSymbolRecordStream(rawPdbFile)))
{
return false;
}
if (IsError(dbiStream.HasValidPublicSymbolStream(rawPdbFile)))
{
return false;
}
if (IsError(dbiStream.HasValidGlobalSymbolStream(rawPdbFile)))
{
return false;
}
if (IsError(dbiStream.HasValidSectionContributionStream(rawPdbFile)))
{
return false;
}
if (IsError(dbiStream.HasValidImageSectionStream(rawPdbFile)))
{
return false;
}
return true;
}
}
// declare all examples
extern void ExamplePDBSize(const PDB::RawFile&, const PDB::DBIStream&);
extern void ExampleTPISize(const PDB::TPIStream& tpiStream, const char* outPath);
extern void ExampleContributions(const PDB::RawFile&, const PDB::DBIStream&);
extern void ExampleSymbols(const PDB::RawFile&, const PDB::DBIStream&);
extern void ExampleFunctionSymbols(const PDB::RawFile&, const PDB::DBIStream&);
extern void ExampleFunctionVariables(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::TPIStream&);
extern void ExampleLines(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::InfoStream& infoStream);
extern void ExampleTypes(const PDB::TPIStream&);
extern void ExampleIPI(const PDB::RawFile& rawPdbFile, const PDB::InfoStream& infoStream, const PDB::TPIStream& tpiStream, const PDB::IPIStream& ipiStream);
int main(int argc, char** argv)
{
if (argc != 2)
{
printf("Usage: Examples <PDB path>\nError: Incorrect usage\n");
return 1;
}
printf("Opening PDB file %s\n", argv[1]);
// try to open the PDB file and check whether all the data we need is available
MemoryMappedFile::Handle pdbFile = MemoryMappedFile::Open(argv[1]);
if (!pdbFile.baseAddress)
{
printf("Cannot memory-map file %s\n", argv[1]);
return 1;
}
if (IsError(PDB::ValidateFile(pdbFile.baseAddress, pdbFile.len)))
{
MemoryMappedFile::Close(pdbFile);
return 2;
}
const PDB::RawFile rawPdbFile = PDB::CreateRawFile(pdbFile.baseAddress);
if (IsError(PDB::HasValidDBIStream(rawPdbFile)))
{
MemoryMappedFile::Close(pdbFile);
return 3;
}
const PDB::InfoStream infoStream(rawPdbFile);
if (infoStream.UsesDebugFastLink())
{
printf("PDB was linked using unsupported option /DEBUG:FASTLINK\n");
MemoryMappedFile::Close(pdbFile);
return 4;
}
const auto h = infoStream.GetHeader();
printf("Version %u, signature %u, age %u, GUID %08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x\n",
static_cast<uint32_t>(h->version), h->signature, h->age,
h->guid.Data1, h->guid.Data2, h->guid.Data3,
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]);
const PDB::DBIStream dbiStream = PDB::CreateDBIStream(rawPdbFile);
if (!HasValidDBIStreams(rawPdbFile, dbiStream))
{
MemoryMappedFile::Close(pdbFile);
return 5;
}
if (IsError(PDB::HasValidTPIStream(rawPdbFile)))
{
MemoryMappedFile::Close(pdbFile);
return 5;
}
const PDB::TPIStream tpiStream = PDB::CreateTPIStream(rawPdbFile);
PDB::IPIStream ipiStream;
// It's perfectly possible that an old PDB does not have an IPI stream.
if(infoStream.HasIPIStream())
{
PDB::ErrorCode error = PDB::HasValidIPIStream(rawPdbFile);
if (error != PDB::ErrorCode::InvalidStream && IsError(error))
{
MemoryMappedFile::Close(pdbFile);
return 5;
}
ipiStream = PDB::CreateIPIStream(rawPdbFile);
}
// run all examples
ExamplePDBSize(rawPdbFile, dbiStream);
ExampleContributions(rawPdbFile, dbiStream);
ExampleSymbols(rawPdbFile, dbiStream);
ExampleFunctionSymbols(rawPdbFile, dbiStream);
ExampleFunctionVariables(rawPdbFile, dbiStream, tpiStream);
ExampleLines(rawPdbFile, dbiStream, infoStream);
ExampleTypes(tpiStream);
ExampleIPI(rawPdbFile, infoStream, tpiStream, ipiStream);
// uncomment to dump type sizes to a CSV
// ExampleTPISize(tpiStream, "output.csv");
MemoryMappedFile::Close(pdbFile);
return 0;
}

View File

@@ -0,0 +1,100 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleMemoryMappedFile.h"
MemoryMappedFile::Handle MemoryMappedFile::Open(const char* path)
{
#ifdef _WIN32
void* file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, nullptr);
if (file == INVALID_HANDLE_VALUE)
{
return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 };
}
void* fileMapping = CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (fileMapping == nullptr)
{
CloseHandle(file);
return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 };
}
void* baseAddress = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0);
if (baseAddress == nullptr)
{
CloseHandle(fileMapping);
CloseHandle(file);
return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 };
}
BY_HANDLE_FILE_INFORMATION fileInformation;
const bool getInformationResult = GetFileInformationByHandle(file, &fileInformation);
if (!getInformationResult)
{
UnmapViewOfFile(baseAddress);
CloseHandle(fileMapping);
CloseHandle(file);
return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 };
}
const size_t fileSizeHighBytes = static_cast<size_t>(fileInformation.nFileSizeHigh) << 32;
const size_t fileSizeLowBytes = fileInformation.nFileSizeLow;
const size_t fileSize = fileSizeHighBytes | fileSizeLowBytes;
return Handle { file, fileMapping, baseAddress, fileSize };
#else
struct stat fileSb;
int file = open(path, O_RDONLY);
if (file == INVALID_HANDLE_VALUE)
{
return Handle { INVALID_HANDLE_VALUE, nullptr, 0 };
}
if (fstat(file, &fileSb) == -1)
{
close(file);
return Handle { INVALID_HANDLE_VALUE, nullptr, 0 };
}
void* baseAddress = mmap(nullptr, fileSb.st_size, PROT_READ, MAP_PRIVATE, file, 0);
if (baseAddress == MAP_FAILED)
{
close(file);
return Handle { INVALID_HANDLE_VALUE, nullptr, 0 };
}
return Handle { file, baseAddress, static_cast<size_t>(fileSb.st_size) };
#endif
}
void MemoryMappedFile::Close(Handle& handle)
{
#ifdef _WIN32
UnmapViewOfFile(handle.baseAddress);
CloseHandle(handle.fileMapping);
CloseHandle(handle.file);
handle.file = nullptr;
handle.fileMapping = nullptr;
#else
munmap(handle.baseAddress, handle.len);
close(handle.file);
handle.file = 0;
#endif
handle.baseAddress = nullptr;
}

View File

@@ -0,0 +1,29 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#ifndef _WIN32
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define INVALID_HANDLE_VALUE ((long)-1)
#endif
namespace MemoryMappedFile
{
struct Handle
{
#ifdef _WIN32
void* file;
void* fileMapping;
#else
int file;
#endif
void* baseAddress;
size_t len;
};
Handle Open(const char* path);
void Close(Handle& handle);
}

View File

@@ -0,0 +1,124 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
namespace
{
struct Stream
{
std::string name;
uint32_t size;
};
}
void ExamplePDBSize(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream);
void ExamplePDBSize(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream)
{
TimedScope total("\nRunning example \"PDBSize\"");
std::vector<Stream> streams;
// print show general statistics
printf("General\n");
printf("-------\n");
{
const PDB::SuperBlock* superBlock = rawPdbFile.GetSuperBlock();
printf("PDB page size (block size): %u\n", superBlock->blockSize);
printf("PDB block count: %u\n", superBlock->blockCount);
const size_t rawSize = static_cast<size_t>(superBlock->blockSize) * static_cast<size_t>(superBlock->blockCount);
printf("PDB raw size: %zu MiB (%zu GiB)\n", rawSize >> 20u, rawSize >> 30u);
}
// print the sizes of all known streams
printf("\n");
printf("Sizes of known streams\n");
printf("----------------------\n");
{
const uint32_t streamCount = rawPdbFile.GetStreamCount();
const uint32_t tpiStreamSize = (streamCount > 2u) ? rawPdbFile.GetStreamSize(2u) : 0u;
const uint32_t dbiStreamSize = (streamCount > 3u) ? rawPdbFile.GetStreamSize(3u) : 0u;
const uint32_t ipiStreamSize = (streamCount > 4u) ? rawPdbFile.GetStreamSize(4u) : 0u;
printf("TPI stream size: %u KiB (%u MiB)\n", tpiStreamSize >> 10u, tpiStreamSize >> 20u);
printf("DBI stream size: %u KiB (%u MiB)\n", dbiStreamSize >> 10u, dbiStreamSize >> 20u);
printf("IPI stream size: %u KiB (%u MiB)\n", ipiStreamSize >> 10u, ipiStreamSize >> 20u);
streams.push_back(Stream { "TPI", tpiStreamSize });
streams.push_back(Stream { "DBI", dbiStreamSize });
streams.push_back(Stream { "IPI", ipiStreamSize });
const uint32_t globalSymbolStreamSize = rawPdbFile.GetStreamSize(dbiStream.GetHeader().globalStreamIndex);
const uint32_t publicSymbolStreamSize = rawPdbFile.GetStreamSize(dbiStream.GetHeader().publicStreamIndex);
const uint32_t symbolRecordStreamSize = rawPdbFile.GetStreamSize(dbiStream.GetHeader().symbolRecordStreamIndex);
printf("Global symbol stream size: %u KiB (%u MiB)\n", globalSymbolStreamSize >> 10u, globalSymbolStreamSize >> 20u);
printf("Public symbol stream size: %u KiB (%u MiB)\n", publicSymbolStreamSize >> 10u, publicSymbolStreamSize >> 20u);
printf("Symbol record stream size: %u KiB (%u MiB)\n", symbolRecordStreamSize >> 10u, symbolRecordStreamSize >> 20u);
streams.emplace_back(Stream { "Global", globalSymbolStreamSize });
streams.emplace_back(Stream { "Public", publicSymbolStreamSize });
streams.emplace_back(Stream { "Symbol", symbolRecordStreamSize });
}
// print the sizes of all module streams
printf("\n");
printf("Sizes of module streams\n");
printf("-----------------------\n");
{
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
const PDB::ArrayView<PDB::ModuleInfoStream::Module> modules = moduleInfoStream.GetModules();
for (const PDB::ModuleInfoStream::Module& module : modules)
{
const PDB::DBI::ModuleInfo* moduleInfo = module.GetInfo();
const char* name = module.GetName().Decay();
const char* objectName = module.GetObjectName().Decay();
const uint16_t streamIndex = module.HasSymbolStream() ? moduleInfo->moduleSymbolStreamIndex : 0u;
const uint32_t moduleStreamSize = (streamIndex != 0u) ? rawPdbFile.GetStreamSize(streamIndex) : 0u;
printf("Module %s (%s) stream size: %u KiB (%u MiB)\n", name, objectName, moduleStreamSize >> 10u, moduleStreamSize >> 20u);
streams.push_back(Stream { name, moduleStreamSize });
}
}
// sort the streams by their size
std::sort(streams.begin(), streams.end(), [](const Stream& lhs, const Stream& rhs)
{
return lhs.size > rhs.size;
});
// log the 20 largest stream
{
printf("\n");
printf("Sizes of 20 largest streams:\n");
const size_t countToShow = std::min<size_t>(20ul, streams.size());
for (size_t i = 0u; i < countToShow; ++i)
{
const Stream& stream = streams[i];
printf("%zu: %u KiB (%u MiB) from stream %s\n", i + 1u, stream.size >> 10u, stream.size >> 20u, stream.name.c_str());
}
}
// print the raw stream sizes
printf("\n");
printf("Raw sizes of all streams\n");
printf("------------------------\n");
{
const uint32_t streamCount = rawPdbFile.GetStreamCount();
for (uint32_t i = 0u; i < streamCount; ++i)
{
const uint32_t streamSize = rawPdbFile.GetStreamSize(i);
printf("Stream %u size: %u KiB (%u MiB)\n", i, streamSize >> 10u, streamSize >> 20u);
}
}
}

View File

@@ -0,0 +1,238 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
namespace
{
// we don't have to store std::string in the symbols, since all the data is memory-mapped anyway.
// we do it in this example to ensure that we don't "cheat" when reading the PDB file. memory-mapped data will only
// be faulted into the process once it's touched, so actually copying the string data makes us touch the needed data,
// giving us a real performance measurement.
struct Symbol
{
std::string name;
uint32_t rva;
};
}
void ExampleSymbols(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream);
void ExampleSymbols(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream)
{
TimedScope total("\nRunning example \"Symbols\"");
// in order to keep the example easy to understand, we load the PDB data serially.
// note that this can be improved a lot by reading streams concurrently.
// prepare the image section stream first. it is needed for converting section + offset into an RVA
TimedScope sectionScope("Reading image section stream");
const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile);
sectionScope.Done();
// prepare the module info stream for matching contributions against files
TimedScope moduleScope("Reading module info stream");
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
moduleScope.Done();
// prepare symbol record stream needed by both public and global streams
TimedScope symbolStreamScope("Reading symbol record stream");
const PDB::CoalescedMSFStream symbolRecordStream = dbiStream.CreateSymbolRecordStream(rawPdbFile);
symbolStreamScope.Done();
std::vector<Symbol> symbols;
// read public symbols
TimedScope publicScope("Reading public symbol stream");
const PDB::PublicSymbolStream publicSymbolStream = dbiStream.CreatePublicSymbolStream(rawPdbFile);
publicScope.Done();
{
TimedScope scope("Storing public symbols");
const PDB::ArrayView<PDB::HashRecord> hashRecords = publicSymbolStream.GetRecords();
const size_t count = hashRecords.GetLength();
symbols.reserve(count);
for (const PDB::HashRecord& hashRecord : hashRecords)
{
const PDB::CodeView::DBI::Record* record = publicSymbolStream.GetRecord(symbolRecordStream, hashRecord);
if (record->header.kind != PDB::CodeView::DBI::SymbolRecordKind::S_PUB32)
{
// 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.
// ignore these.
continue;
}
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_PUB32.section, record->data.S_PUB32.offset);
if (rva == 0u)
{
// certain symbols (e.g. control-flow guard symbols) don't have a valid RVA, ignore those
continue;
}
symbols.push_back(Symbol { record->data.S_PUB32.name, rva });
}
scope.Done(count);
}
// read global symbols
TimedScope globalScope("Reading global symbol stream");
const PDB::GlobalSymbolStream globalSymbolStream = dbiStream.CreateGlobalSymbolStream(rawPdbFile);
globalScope.Done();
{
TimedScope scope("Storing global symbols");
const PDB::ArrayView<PDB::HashRecord> hashRecords = globalSymbolStream.GetRecords();
const size_t count = hashRecords.GetLength();
symbols.reserve(symbols.size() + count);
for (const PDB::HashRecord& hashRecord : hashRecords)
{
const PDB::CodeView::DBI::Record* record = globalSymbolStream.GetRecord(symbolRecordStream, hashRecord);
const char* name = nullptr;
uint32_t rva = 0u;
if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GDATA32)
{
name = record->data.S_GDATA32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GDATA32.section, record->data.S_GDATA32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GTHREAD32)
{
name = record->data.S_GTHREAD32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GTHREAD32.section, record->data.S_GTHREAD32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LDATA32)
{
name = record->data.S_LDATA32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LDATA32.section, record->data.S_LDATA32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LTHREAD32)
{
name = record->data.S_LTHREAD32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LTHREAD32.section, record->data.S_LTHREAD32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_UDT)
{
name = record->data.S_UDT.name;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_UDT_ST)
{
name = record->data.S_UDT_ST.name;
}
if (rva == 0u)
{
// certain symbols (e.g. control-flow guard symbols) don't have a valid RVA, ignore those
continue;
}
symbols.push_back(Symbol { name, rva });
}
scope.Done(count);
}
// read module symbols
{
TimedScope scope("Storing symbols from modules");
const PDB::ArrayView<PDB::ModuleInfoStream::Module> modules = moduleInfoStream.GetModules();
for (const PDB::ModuleInfoStream::Module& module : modules)
{
if (!module.HasSymbolStream())
{
continue;
}
const PDB::ModuleSymbolStream moduleSymbolStream = module.CreateSymbolStream(rawPdbFile);
moduleSymbolStream.ForEachSymbol([&symbols, &imageSectionStream](const PDB::CodeView::DBI::Record* record)
{
const char* name = nullptr;
uint32_t rva = 0u;
if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_THUNK32)
{
if (record->data.S_THUNK32.thunk == PDB::CodeView::DBI::ThunkOrdinal::TrampolineIncremental)
{
// we have never seen incremental linking thunks stored inside a S_THUNK32 symbol, but better be safe than sorry
name = "ILT";
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_THUNK32.section, record->data.S_THUNK32.offset);
}
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_TRAMPOLINE)
{
// incremental linking thunks are stored in the linker module
name = "ILT";
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_TRAMPOLINE.thunkSection, record->data.S_TRAMPOLINE.thunkOffset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_BLOCK32)
{
// blocks never store a name and are only stored for indicating whether other symbols are children of this block
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LABEL32)
{
// labels don't have a name
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32)
{
name = record->data.S_LPROC32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32.section, record->data.S_LPROC32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32)
{
name = record->data.S_GPROC32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32.section, record->data.S_GPROC32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32_ID)
{
name = record->data.S_LPROC32_ID.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32_ID.section, record->data.S_LPROC32_ID.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32_ID)
{
name = record->data.S_GPROC32_ID.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32_ID.section, record->data.S_GPROC32_ID.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_REGREL32)
{
name = record->data.S_REGREL32.name;
// You can only get the address while running the program by checking the register value and adding the offset
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LDATA32)
{
name = record->data.S_LDATA32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LDATA32.section, record->data.S_LDATA32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LTHREAD32)
{
name = record->data.S_LTHREAD32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LTHREAD32.section, record->data.S_LTHREAD32.offset);
}
if (rva == 0u)
{
// certain symbols (e.g. control-flow guard symbols) don't have a valid RVA, ignore those
return;
}
symbols.push_back(Symbol { name, rva });
});
}
scope.Done(modules.GetLength());
}
total.Done(symbols.size());
}

View File

@@ -0,0 +1,54 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
namespace
{
static unsigned int g_indent = 0u;
static void PrintIndent(void)
{
printf("%.*s", g_indent * 2u, "| | | | | | | | ");
}
}
TimedScope::TimedScope(const char* message)
: m_begin(std::chrono::high_resolution_clock::now())
{
PrintIndent();
++g_indent;
printf("%s\n", message);
}
void TimedScope::Done(void) const
{
--g_indent;
PrintIndent();
const double milliSeconds = ReadMilliseconds();
printf("---> done in %.3fms\n", milliSeconds);
}
void TimedScope::Done(size_t count) const
{
--g_indent;
PrintIndent();
const double milliSeconds = ReadMilliseconds();
printf("---> done in %.3fms (%zu elements)\n", milliSeconds, count);
}
double TimedScope::ReadMilliseconds(void) const
{
const std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> seconds = now - m_begin;
return seconds.count() * 1000.0;
}

View File

@@ -0,0 +1,22 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Foundation/PDB_Macros.h"
#include <chrono>
class TimedScope
{
public:
explicit TimedScope(const char* message);
void Done(void) const;
void Done(size_t count) const;
private:
double ReadMilliseconds(void) const;
const std::chrono::high_resolution_clock::time_point m_begin;
PDB_DISABLE_COPY_MOVE(TimedScope);
};

View File

@@ -0,0 +1,41 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTypeTable.h"
#include "Foundation/PDB_Memory.h"
TypeTable::TypeTable(const PDB::TPIStream& tpiStream) PDB_NO_EXCEPT
: typeIndexBegin(tpiStream.GetFirstTypeIndex()), typeIndexEnd(tpiStream.GetLastTypeIndex()),
m_recordCount(tpiStream.GetTypeRecordCount())
{
// Create coalesced stream from TPI stream, so the records can be referenced directly using pointers.
const PDB::DirectMSFStream& directStream = tpiStream.GetDirectMSFStream();
m_stream = PDB::CoalescedMSFStream(directStream, directStream.GetSize(), 0);
// types in the TPI stream are accessed by their index from other streams.
// however, the index is not stored with types in the TPI stream directly, but has to be built while walking the stream.
// similarly, because types are variable-length records, there are no direct offsets to access individual types.
// we therefore walk the TPI stream once, and store pointers to the records for trivial O(1) array lookup by index later.
m_records = PDB_NEW_ARRAY(const PDB::CodeView::TPI::Record*, m_recordCount);
// parse the CodeView records
uint32_t typeIndex = 0u;
tpiStream.ForEachTypeRecordHeaderAndOffset([this, &typeIndex](const PDB::CodeView::TPI::RecordHeader& header, size_t offset)
{
// The header includes the record kind and size, which can be stored along with offset
// to allow for lazy loading of the types on-demand directly from the TPIStream::GetDirectMSFStream()
// using DirectMSFStream::ReadAtOffset(...). Thus not needing a CoalescedMSFStream to look up the types.
(void)header;
const PDB::CodeView::TPI::Record* record = m_stream.GetDataAtOffset<const PDB::CodeView::TPI::Record>(offset);
m_records[typeIndex] = record;
++typeIndex;
});
}
TypeTable::~TypeTable() PDB_NO_EXCEPT
{
PDB_DELETE_ARRAY(m_records);
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include "PDB_TPIStream.h"
#include "PDB_CoalescedMSFStream.h"
class TypeTable
{
public:
explicit TypeTable(const PDB::TPIStream& tpiStream) PDB_NO_EXCEPT;
~TypeTable() PDB_NO_EXCEPT;
// Returns the index of the first type, which is not necessarily zero.
PDB_NO_DISCARD inline uint32_t GetFirstTypeIndex(void) const PDB_NO_EXCEPT
{
return typeIndexBegin;
}
// Returns the index of the last type.
PDB_NO_DISCARD inline uint32_t GetLastTypeIndex(void) const PDB_NO_EXCEPT
{
return typeIndexEnd;
}
PDB_NO_DISCARD inline const PDB::CodeView::TPI::Record* GetTypeRecord(uint32_t typeIndex) const PDB_NO_EXCEPT
{
if (typeIndex < typeIndexBegin || typeIndex > typeIndexEnd)
return nullptr;
return m_records[typeIndex - typeIndexBegin];
}
// Returns a view of all type records.
// Records identified by a type index can be accessed via "allRecords[typeIndex - firstTypeIndex]".
PDB_NO_DISCARD inline PDB::ArrayView<const PDB::CodeView::TPI::Record*> GetTypeRecords(void) const PDB_NO_EXCEPT
{
return PDB::ArrayView<const PDB::CodeView::TPI::Record*>(m_records, m_recordCount);
}
private:
uint32_t typeIndexBegin;
uint32_t typeIndexEnd;
size_t m_recordCount;
const PDB::CodeView::TPI::Record **m_records;
PDB::CoalescedMSFStream m_stream;
PDB_DISABLE_COPY(TypeTable);
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"

View File

@@ -0,0 +1,53 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Warnings.h"
// The following clang warnings must be disabled for the examples to build with 0 warnings
#if PDB_COMPILER_CLANG
# pragma clang diagnostic ignored "-Wformat-nonliteral" // format string is not a string literal
# pragma clang diagnostic ignored "-Wswitch-default" // switch' missing 'default' label
# pragma clang diagnostic ignored "-Wcast-align" // increases required alignment from X to Y
# pragma clang diagnostic ignored "-Wold-style-cast" // use of old-style cast
#endif
#if PDB_COMPILER_MSVC
# pragma warning(push, 0)
#elif PDB_COMPILER_CLANG
# pragma clang diagnostic push
#endif
#if PDB_COMPILER_MSVC
// we compile without exceptions
# define _ALLOW_RTCc_IN_STL
// triggered by Windows.h
# pragma warning (disable : 4668)
// triggered by xlocale in VS 2017
# pragma warning (disable : 4625) // copy constructor was implicitly defined as deleted
# pragma warning (disable : 4626) // assignment operator was implicitly defined as deleted
# pragma warning (disable : 5026) // move constructor was implicitly defined as deleted
# pragma warning (disable : 5027) // move assignment operator was implicitly defined as deleted
# pragma warning (disable : 4774) // format string expected in argument 1 is not a string literal
#endif
#ifdef _WIN32
# define NOMINMAX
# include <Windows.h>
# undef cdecl
#endif
# include <vector>
# include <unordered_set>
# include <chrono>
# include <string>
# include <algorithm>
# include <cstdarg>
#if PDB_COMPILER_MSVC
# pragma warning(pop)
#elif PDB_COMPILER_CLANG
# pragma clang diagnostic pop
#endif