#include "PDBReader.h" #include "MemoryMappedFile.h" #include #include #include "raw_pdb/PDB.h" #include "raw_pdb/PDB_RawFile.h" #include "raw_pdb/PDB_InfoStream.h" #include "raw_pdb/PDB_TPIStream.h" #include "raw_pdb/PDB_DBIStream.h" enum class EModifierFlags : uint16_t { None = 0x0, Const = 0x1, Volatile = 0x2, Unaligned = 0x4, }; struct FRecordBasicInfo { const char* Name = nullptr; uint64_t Size = 0; uint32_t RelatedRecord = 0; EModifierFlags Modifiers = EModifierFlags::None; PDB::CodeView::TPI::TypeRecordKind Kind = (PDB::CodeView::TPI::TypeRecordKind)0; }; namespace PrimitiveTypes { const FRecordBasicInfo Unknown = {"UNKNOWN", 0, 0}; const FRecordBasicInfo None = { "NONE", 0, 0}; const FRecordBasicInfo Void[] = { {"void", 0, 0}, {"void*", 2, 0}, {"void*", 4, 0}, {"void*", 4, 0}, {"void*", 4, 0}, {"void*", 4, 0}, {"void*", 8, 0}, }; const FRecordBasicInfo Hresult[] = { {"HRESULT", 4, 0}, {"HRESULT*", 2, 0}, {"HRESULT*", 4, 0}, {"HRESULT*", 4, 0}, {"HRESULT*", 4, 0}, {"HRESULT*", 4, 0}, {"HRESULT*", 8, 0}, }; const FRecordBasicInfo Int8[] = { {"int8", 1, 0}, {"int8*", 2, 0}, {"int8*", 4, 0}, {"int8*", 4, 0}, {"int8*", 4, 0}, {"int8*", 4, 0}, {"int8*", 8, 0}, }; const FRecordBasicInfo Uint8[] = { {"uint8", 1, 0}, {"uint8*", 2, 0}, {"uint8*", 4, 0}, {"uint8*", 4, 0}, {"uint8*", 4, 0}, {"uint8*", 4, 0}, {"uint8*", 8, 0}, }; const FRecordBasicInfo Int16[] = { {"int16", 2, 0}, {"int16*", 2, 0}, {"int16*", 4, 0}, {"int16*", 4, 0}, {"int16*", 4, 0}, {"int16*", 4, 0}, {"int16*", 8, 0}, }; const FRecordBasicInfo Uint16[] = { {"uint16", 2, 0}, {"uint16*", 2, 0}, {"uint16*", 4, 0}, {"uint16*", 4, 0}, {"uint16*", 4, 0}, {"uint16*", 4, 0}, {"uint16*", 8, 0}, }; const FRecordBasicInfo Int32[] = { {"int32", 4, 0}, {"int32*", 2, 0}, {"int32*", 4, 0}, {"int32*", 4, 0}, {"int32*", 4, 0}, {"int32*", 4, 0}, {"int32*", 8, 0}, }; const FRecordBasicInfo Uint32[] = { {"uint32", 4, 0}, {"uint32*", 2, 0}, {"uint32*", 4, 0}, {"uint32*", 4, 0}, {"uint32*", 4, 0}, {"uint32*", 4, 0}, {"uint32*", 8, 0}, }; const FRecordBasicInfo Int64[] = { {"int64", 8, 0}, {"int64*", 2, 0}, {"int64*", 4, 0}, {"int64*", 4, 0}, {"int64*", 4, 0}, {"int64*", 4, 0}, {"int64*", 8, 0}, }; const FRecordBasicInfo Uint64[] = { {"uint64", 8, 0}, {"uint64*", 2, 0}, {"uint64*", 4, 0}, {"uint64*", 4, 0}, {"uint64*", 4, 0}, {"uint64*", 4, 0}, {"uint64*", 8, 0}, }; const FRecordBasicInfo Bool8[] = { {"bool8", 1, 0}, {"bool8*", 2, 0}, {"bool8*", 4, 0}, {"bool8*", 4, 0}, {"bool8*", 4, 0}, {"bool8*", 4, 0}, {"bool8*", 8, 0}, }; const FRecordBasicInfo Bool16[] = { {"bool16", 2, 0}, {"bool16*", 2, 0}, {"bool16*", 4, 0}, {"bool16*", 4, 0}, {"bool16*", 4, 0}, {"bool16*", 4, 0}, {"bool16*", 8, 0}, }; const FRecordBasicInfo Bool32[] = { {"bool32", 4, 0}, {"bool32*", 2, 0}, {"bool32*", 4, 0}, {"bool32*", 4, 0}, {"bool32*", 4, 0}, {"bool32*", 4, 0}, {"bool32*", 8, 0}, }; const FRecordBasicInfo Bool64[] = { {"bool64", 8, 0}, {"bool64*", 2, 0}, {"bool64*", 4, 0}, {"bool64*", 4, 0}, {"bool64*", 4, 0}, {"bool64*", 4, 0}, {"bool64*", 8, 0}, }; const FRecordBasicInfo Float[] = { {"float", 4, 0}, {"float*", 2, 0}, {"float*", 4, 0}, {"float*", 4, 0}, {"float*", 4, 0}, {"float*", 4, 0}, {"float*", 8, 0}, }; const FRecordBasicInfo Double[] = { {"double", 8, 0}, {"double*", 2, 0}, {"double*", 4, 0}, {"double*", 4, 0}, {"double*", 4, 0}, {"double*", 4, 0}, {"double*", 8, 0}, }; const FRecordBasicInfo Char[] = { {"char", 1, 0}, {"char*", 2, 0}, {"char*", 4, 0}, {"char*", 4, 0}, {"char*", 4, 0}, {"char*", 4, 0}, {"char*", 8, 0}, }; const FRecordBasicInfo Wchar[] = { {"wchar", 2, 0}, {"wchar*", 2, 0}, {"wchar*", 4, 0}, {"wchar*", 4, 0}, {"wchar*", 4, 0}, {"wchar*", 4, 0}, {"wchar*", 8, 0}, }; } namespace TPIHelpers { FRecordBasicInfo GetPrimitiveTypeBasicInfo(uint32_t typeIndex) { uint64_t sizeField = typeIndex & 0x7; uint64_t typeField = (typeIndex >> 4) & 0xf; uint64_t modeField = (typeIndex >> 8) & 0x7; // For pointers only bool isPointer = true; uint32_t pointerSize = 0; // Pointers switch (modeField) { case 1: pointerSize = 2; break; case 2: case 3: case 4: case 5: pointerSize = 4; break; case 6: pointerSize = 8; break; case 0: default: isPointer = false; break; } switch (typeField) { default: return PrimitiveTypes::Unknown; // "special" case 0: { switch (sizeField) { case 0: return PrimitiveTypes::None; case 1: // Absolute symbol case 2: // Segment return PrimitiveTypes::Unknown; case 3: return PrimitiveTypes::Void[modeField]; case 4: // 8 byte currency valye case 5: // near basic string case 6: // far basic string case 7: // untranslated legacy type default: return PrimitiveTypes::Unknown; case 8: return PrimitiveTypes::Hresult[modeField]; } } // Signed integer case 1: switch (sizeField) { default: return PrimitiveTypes::Unknown; case 0: return PrimitiveTypes::Int8[modeField]; case 1: return PrimitiveTypes::Int16[modeField]; case 2: return PrimitiveTypes::Int32[modeField]; case 3: return PrimitiveTypes::Int64[modeField]; } // Unsigned integer case 2: switch (sizeField) { default: return PrimitiveTypes::Unknown; case 0: return PrimitiveTypes::Uint8[modeField]; case 1: return PrimitiveTypes::Uint16[modeField]; case 2: return PrimitiveTypes::Uint32[modeField]; case 3: return PrimitiveTypes::Uint64[modeField]; } // Boolean case 3: switch (sizeField) { default: return PrimitiveTypes::Unknown; case 0: return PrimitiveTypes::Bool8[modeField]; case 1: return PrimitiveTypes::Bool16[modeField]; case 2: return PrimitiveTypes::Bool32[modeField]; case 3: return PrimitiveTypes::Bool64[modeField]; } // real (float?) case 4: switch (sizeField) { default: return PrimitiveTypes::Unknown; case 0: return PrimitiveTypes::Float[modeField]; case 1: return PrimitiveTypes::Double[modeField]; case 2: // 80 bit case 3: // 128 bit case 4: // 28 bit return PrimitiveTypes::Unknown; } // complex case 5: return PrimitiveTypes::Unknown; // special 2 case 6: switch (sizeField) { default: case 0: // Bit case 1: // Pascal CHAR return PrimitiveTypes::Unknown; } break; // real int case 7: switch (sizeField) { default: return PrimitiveTypes::Unknown; case 0: return PrimitiveTypes::Char[modeField]; case 1: return PrimitiveTypes::Wchar[modeField]; case 2: return PrimitiveTypes::Int16[modeField]; case 3: return PrimitiveTypes::Uint16[modeField]; case 4: return PrimitiveTypes::Int32[modeField]; case 5: return PrimitiveTypes::Uint32[modeField]; case 6: return PrimitiveTypes::Int64[modeField]; case 7: return PrimitiveTypes::Uint64[modeField]; } } } uint64_t GetPrimitiveTypeSize(uint32_t typeIndex) { return GetPrimitiveTypeBasicInfo(typeIndex).Size; } } struct PDBHandle { PDBHandle(MemoryMappedFile::Handle handle, PDB::TPIStream tpiStream); ~PDBHandle(); MemoryMappedFile::Handle FileHandle; PDB::TPIStream TPIStream; }; PDBHandle::PDBHandle(MemoryMappedFile::Handle handle, PDB::TPIStream tpiStream) : FileHandle(handle) , TPIStream(std::move(tpiStream)) { } PDBHandle::~PDBHandle() { MemoryMappedFile::Close(FileHandle); } extern "C" PDBREADER_API PDBHandle* LoadPDB(const wchar_t* path) { MemoryMappedFile::Handle pdbFile = MemoryMappedFile::Open(path); if (!pdbFile.baseAddress) { return nullptr; } if (PDB::ValidateFile(pdbFile.baseAddress) != PDB::ErrorCode::Success) { MemoryMappedFile::Close(pdbFile); return nullptr; } const PDB::RawFile rawPdbFile = PDB::CreateRawFile(pdbFile.baseAddress); if (PDB::HasValidDBIStream(rawPdbFile) != PDB::ErrorCode::Success) { MemoryMappedFile::Close(pdbFile); return nullptr; } const PDB::InfoStream infoStream(rawPdbFile); if (infoStream.UsesDebugFastLink()) { MemoryMappedFile::Close(pdbFile); return nullptr; } PDB::TPIStream tpiStream = PDB::CreateTPIStream(rawPdbFile); if (PDB::HasValidTPIStream(rawPdbFile) != PDB::ErrorCode::Success) { MemoryMappedFile::Close(pdbFile); return nullptr; } return new PDBHandle(pdbFile, std::move(tpiStream)); } extern "C" PDBREADER_API void ClosePDB(PDBHandle* handle) { delete handle; } extern "C" PDBREADER_API int64_t CountStructures(PDBHandle* handle) { int64_t count = 0; for (const PDB::CodeView::TPI::Record* record : handle->TPIStream.GetTypeRecords()) { if (record->header.kind == PDB::CodeView::TPI::TypeRecordKind::LF_STRUCTURE) { if (record->data.LF_CLASS.property.fwdref) continue; const PDB::CodeView::TPI::Record* typeRecord = handle->TPIStream.GetTypeRecord(record->data.LF_CLASS.field); if (!typeRecord) continue; ++count; } } return count; } static uint8_t GetLeafSize(PDB::CodeView::TPI::TypeRecordKind kind) { if (kind < PDB::CodeView::TPI::TypeRecordKind::LF_NUMERIC) { // No leaf can have an index less than LF_NUMERIC (0x8000) // so word is the value... return sizeof(PDB::CodeView::TPI::TypeRecordKind); } switch (kind) { case PDB::CodeView::TPI::TypeRecordKind::LF_CHAR: return sizeof(PDB::CodeView::TPI::TypeRecordKind) + sizeof(uint8_t); case PDB::CodeView::TPI::TypeRecordKind::LF_USHORT: case PDB::CodeView::TPI::TypeRecordKind::LF_SHORT: return sizeof(PDB::CodeView::TPI::TypeRecordKind) + sizeof(uint16_t); case PDB::CodeView::TPI::TypeRecordKind::LF_LONG: case PDB::CodeView::TPI::TypeRecordKind::LF_ULONG: return sizeof(PDB::CodeView::TPI::TypeRecordKind) + sizeof(uint32_t); case PDB::CodeView::TPI::TypeRecordKind::LF_QUADWORD: case PDB::CodeView::TPI::TypeRecordKind::LF_UQUADWORD: return sizeof(PDB::CodeView::TPI::TypeRecordKind) + sizeof(uint64_t); default: printf("Error! 0x%04x bogus type encountered, aborting...\n", PDB_AS_UNDERLYING(kind)); } return 0; } static size_t GetStructureSizeSize(PDB::CodeView::TPI::TypeRecordKind kind) { if (kind < PDB::CodeView::TPI::TypeRecordKind::LF_NUMERIC) { // No leaf can have an index less than LF_NUMERIC (0x8000) // so word is the value... return 0; } switch (kind) { case PDB::CodeView::TPI::TypeRecordKind::LF_CHAR: return sizeof(uint8_t); case PDB::CodeView::TPI::TypeRecordKind::LF_USHORT: case PDB::CodeView::TPI::TypeRecordKind::LF_SHORT: return sizeof(uint16_t); case PDB::CodeView::TPI::TypeRecordKind::LF_LONG: case PDB::CodeView::TPI::TypeRecordKind::LF_ULONG: return sizeof(uint32_t); case PDB::CodeView::TPI::TypeRecordKind::LF_QUADWORD: case PDB::CodeView::TPI::TypeRecordKind::LF_UQUADWORD: return sizeof(uint64_t); default: printf("Error! 0x%04x bogus type encountered, aborting...\n", PDB_AS_UNDERLYING(kind)); } return 0; } static uint64_t GetStructureSize(const char* data, PDB::CodeView::TPI::TypeRecordKind kind) { size_t sizeSize = GetStructureSizeSize(kind); if (sizeSize == 0) { return *reinterpret_cast(data); } const char* sizePtr = &data[GetLeafSize(kind) - sizeSize]; switch (sizeSize) { case 1: return *reinterpret_cast(sizePtr); case 2: return *reinterpret_cast(sizePtr); case 4: return *reinterpret_cast(sizePtr); case 8: return *reinterpret_cast(sizePtr); default: return 0; } } static const char* GetLeafName(const char* data, PDB::CodeView::TPI::TypeRecordKind kind) { return &data[GetLeafSize(kind)]; } struct FNumericLeaf { PDB::CodeView::TPI::TypeRecordKind Kind; union Value { uint64_t UnsignedInt; int64_t SignedInt; } Value; FNumericLeaf(PDB::CodeView::TPI::TypeRecordKind kind, uint64_t value) : Kind(kind) { Value.UnsignedInt = value; } size_t GetSkipSize() const { switch (Kind) { case PDB::CodeView::TPI::TypeRecordKind::LF_CHAR: return sizeof(uint8_t); case PDB::CodeView::TPI::TypeRecordKind::LF_USHORT: case PDB::CodeView::TPI::TypeRecordKind::LF_SHORT: return sizeof(uint16_t); case PDB::CodeView::TPI::TypeRecordKind::LF_LONG: case PDB::CodeView::TPI::TypeRecordKind::LF_ULONG: return sizeof(uint32_t); case PDB::CodeView::TPI::TypeRecordKind::LF_QUADWORD: case PDB::CodeView::TPI::TypeRecordKind::LF_UQUADWORD: return sizeof(uint64_t); } return 0; } }; FNumericLeaf ReadNumericLeaf(const uint8_t*& address) { using PDB::CodeView::TPI::TypeRecordKind; uint16_t size = *reinterpret_cast(address); if (size < (uint16_t)TypeRecordKind::LF_NUMERIC) { address = reinterpret_cast(address) + sizeof(uint16_t); return FNumericLeaf(TypeRecordKind::LF_NUMERIC, size); } address += sizeof(uint16_t); FNumericLeaf leaf(TypeRecordKind::LF_NUMERIC, size); TypeRecordKind kind = (TypeRecordKind)size; switch (kind) { default: PDB_ASSERT(false, "Unrecognized numeric leaf kind %X", size); return FNumericLeaf(TypeRecordKind::LF_NUMERIC, 0); case TypeRecordKind::LF_CHAR: leaf = FNumericLeaf(kind, *reinterpret_cast(address)); break; case TypeRecordKind::LF_SHORT: leaf = FNumericLeaf(kind, *reinterpret_cast(address)); break; case TypeRecordKind::LF_USHORT: leaf = FNumericLeaf(kind, *reinterpret_cast(address)); break; case TypeRecordKind::LF_LONG: leaf = FNumericLeaf(kind, *reinterpret_cast(address)); break; case TypeRecordKind::LF_ULONG: leaf = FNumericLeaf(kind, *reinterpret_cast(address)); break; case TypeRecordKind::LF_QUADWORD: leaf = FNumericLeaf(kind, *reinterpret_cast(address)); break; case TypeRecordKind::LF_UQUADWORD: leaf = FNumericLeaf(kind, *reinterpret_cast(address)); break; case TypeRecordKind::LF_REAL32: case TypeRecordKind::LF_REAL64: case TypeRecordKind::LF_REAL48: case TypeRecordKind::LF_REAL80: case TypeRecordKind::LF_REAL128: PDB_ASSERT(false, "Floating point leaves unimplemented"); break; case TypeRecordKind::LF_COMPLEX32: case TypeRecordKind::LF_COMPLEX64: case TypeRecordKind::LF_COMPLEX80: case TypeRecordKind::LF_COMPLEX128: PDB_ASSERT(false, "Complex leaves unsupported"); break; case TypeRecordKind::LF_VARSTRING: PDB_ASSERT(false, "Varstring leaves unsupported"); break; } address += leaf.GetSkipSize(); return leaf; } FNumericLeaf ReadNumericLeaf(const void*& address) { return ReadNumericLeaf(reinterpret_cast(address)); } extern "C" void GetTypeRecordsInfo( PDBHandle* handle, const void*** OutRecords, uint64_t* OutRecordCount, uint32_t* OutTypeIndexBegin, uint32_t* OutTypeIndexEnd ) { PDB::TPIStream& stream = handle->TPIStream; *OutRecords = (const void**)stream.GetTypeRecords().begin(); *OutRecordCount = stream.GetTypeRecords().GetLength(); *OutTypeIndexBegin = stream.GetFirstTypeIndex(); *OutTypeIndexEnd = stream.GetLastTypeIndex(); } // Recursion to other referenced types can be problematic here as they may be forward declarations // e.g. an LF_MODIFIER may point to the forward decl version of an LF_CLASS extern "C" void GetBasicTypeInfo(PDBHandle* handle, uint32_t index, FRecordBasicInfo* outInfo) { using PDB::CodeView::TPI::TypeRecordKind; using namespace TPIHelpers; const PDB::CodeView::TPI::Record* record = handle->TPIStream.GetTypeRecord(index); if (!record) { *outInfo = GetPrimitiveTypeBasicInfo(index); return; } const PDB::CodeView::TPI::Record* underlyingRecord = nullptr; FRecordBasicInfo underlyingInfo; const uint8_t* data = nullptr; outInfo->Kind = record->header.kind; switch (record->header.kind) { default: PDB_ASSERT(false, "Unknown record kind %X", static_cast(record->header.kind)); return; case TypeRecordKind::LF_MFUNCTION: outInfo->Name = "MEMBER FUNCTION"; return; case TypeRecordKind::LF_PROCEDURE: outInfo->Name = "FUNCTION"; // TODO: proper names for function ptrs return; case TypeRecordKind::LF_VTSHAPE: return; case TypeRecordKind::LF_STRUCTURE: case TypeRecordKind::LF_CLASS: outInfo->Name = GetLeafName(record->data.LF_CLASS.data, record->data.LF_CLASS.lfEasy.kind); outInfo->Size = GetStructureSize(record->data.LF_CLASS.data, record->data.LF_CLASS.lfEasy.kind); outInfo->RelatedRecord = record->data.LF_CLASS.field; return; case TypeRecordKind::LF_POINTER: outInfo->RelatedRecord = record->data.LF_POINTER.utype; outInfo->Size = record->data.LF_POINTER.attr.size; return; case TypeRecordKind::LF_MODIFIER: outInfo->RelatedRecord = record->data.LF_MODIFIER.type; outInfo->Modifiers = *reinterpret_cast(&record->data.LF_MODIFIER.attr); return; case TypeRecordKind::LF_ENUM: outInfo->Name = record->data.LF_ENUM.name; outInfo->RelatedRecord = record->data.LF_ENUM.utype; return; case TypeRecordKind::LF_ARRAY: outInfo->RelatedRecord = record->data.LF_ARRAY.elemtype; data = reinterpret_cast(record->data.LF_ARRAY.data); outInfo->Size = ReadNumericLeaf(data).Value.UnsignedInt; outInfo->Name = reinterpret_cast(data); return; case TypeRecordKind::LF_UNION: outInfo->Name = GetLeafName(record->data.LF_CLASS.data, record->data.LF_CLASS.lfEasy.kind); outInfo->Size = GetStructureSize(record->data.LF_CLASS.data, record->data.LF_CLASS.lfEasy.kind); outInfo->RelatedRecord = record->data.LF_CLASS.field; return; } } template const uint8_t* AlignUp(const uint8_t* address) { uintptr_t ptr = (uintptr_t)address; ptr = (ptr + (sizeof(uint32_t) - 1)) & (0 - sizeof(uint32_t)); return reinterpret_cast(ptr); } const uint8_t* SkipFieldListEntry(const PDB::CodeView::TPI::FieldList& entry) { using PDB::CodeView::TPI::TypeRecordKind; const uint8_t* Addr = nullptr; volatile static bool bLog = false; switch (entry.kind) { default: PDB_ASSERT(false, "Unknown record kind %X", static_cast(entry.kind)); return 0; case TypeRecordKind::LF_MEMBER: Addr = reinterpret_cast(&entry.data.LF_MEMBER.offset); ReadNumericLeaf(Addr); Addr += strlen((const char*)Addr) + 1; // ? Name should be length prefixed return AlignUp(Addr); case TypeRecordKind::LF_NESTTYPE: Addr = reinterpret_cast(entry.data.LF_NESTTYPE.name); Addr += strlen((const char*)Addr) + 1; return AlignUp(Addr); case TypeRecordKind::LF_STMEMBER: Addr = reinterpret_cast(entry.data.LF_STMEMBER.name); Addr += strlen((const char*)Addr) + 1; return AlignUp(Addr); case TypeRecordKind::LF_METHOD: Addr = reinterpret_cast(entry.data.LF_METHOD.name); Addr += strlen((const char*)Addr) + 1; return AlignUp(Addr); case TypeRecordKind::LF_ONEMETHOD: Addr = reinterpret_cast(&entry.data.LF_ONEMETHOD.vbaseoff); switch (static_cast(entry.data.LF_ONEMETHOD.attributes.mprop)) { case PDB::CodeView::TPI::MethodProperty::Intro: case PDB::CodeView::TPI::MethodProperty::PureIntro: Addr += sizeof(uint32_t); } Addr += strlen((const char*)Addr) + 1; return AlignUp(Addr); case TypeRecordKind::LF_BCLASS: Addr = reinterpret_cast(&entry.data.LF_BCLASS.offset); ReadNumericLeaf(Addr); return AlignUp(Addr); case TypeRecordKind::LF_VFUNCTAB: Addr = reinterpret_cast(&entry.data.LF_VFUNCTAB) + sizeof(entry.data.LF_VFUNCTAB); return AlignUp(Addr); case TypeRecordKind::LF_IVBCLASS: case TypeRecordKind::LF_VBCLASS: Addr = reinterpret_cast(&entry.data.LF_VBCLASS.lfEasy); ReadNumericLeaf(Addr); ReadNumericLeaf(Addr); return AlignUp(Addr); } } struct FWalkFieldListOperations { template void Visit(const T&){} }; template void WalkFieldList(PDBHandle* handle, const PDB::CodeView::TPI::Record* record, Operations& op) { const PDB::TPIStream& tpiStream = handle->TPIStream; using PDB::CodeView::TPI::TypeRecordKind; const uint8_t* fieldData = reinterpret_cast(&record->data.LF_FIELD.list); const uint8_t* dataEnd = fieldData + (record->header.size - sizeof(uint16_t)); while(fieldData < dataEnd) { const auto* fieldRecord = reinterpret_cast(fieldData); if (fieldRecord->kind == TypeRecordKind::LF_INDEX) { const PDB::CodeView::TPI::Record* newRecord = handle->TPIStream.GetTypeRecord(fieldRecord->data.LF_INDEX.index); WalkFieldList(handle, newRecord, op); return; } fieldData = SkipFieldListEntry(*fieldRecord); switch (fieldRecord->kind) { default: PDB_ASSERT(false, "Unknown record kind %X", static_cast(fieldRecord->kind)); return; case TypeRecordKind::LF_MEMBER: op.Visit(fieldRecord, fieldRecord->data.LF_MEMBER); break; case TypeRecordKind::LF_NESTTYPE: op.Visit(fieldRecord, fieldRecord->data.LF_NESTTYPE); break; case TypeRecordKind::LF_STMEMBER: op.Visit(fieldRecord, fieldRecord->data.LF_STMEMBER); break; case TypeRecordKind::LF_METHOD: op.Visit(fieldRecord, fieldRecord->data.LF_METHOD); break; case TypeRecordKind::LF_ONEMETHOD: op.Visit(fieldRecord, fieldRecord->data.LF_ONEMETHOD); break; case TypeRecordKind::LF_BCLASS: op.Visit(fieldRecord, fieldRecord->data.LF_BCLASS); break; case TypeRecordKind::LF_IVBCLASS: case TypeRecordKind::LF_VBCLASS: op.Visit(fieldRecord, fieldRecord->data.LF_VBCLASS); break; case TypeRecordKind::LF_VFUNCTAB: op.Visit(fieldRecord, fieldRecord->data.LF_VFUNCTAB); break; } } } struct FStructFieldCounts { uint32_t NumDataMembers = 0; }; struct FFieldCountingOperations { using LF_MEMBER = decltype(std::declval().data.LF_MEMBER); using LF_NESTTYPE = decltype(std::declval().data.LF_NESTTYPE); using LF_STMEMBER = decltype(std::declval().data.LF_STMEMBER); using LF_METHOD = decltype(std::declval().data.LF_METHOD); using LF_ONEMETHOD = decltype(std::declval().data.LF_ONEMETHOD); using LF_BCLASS = decltype(std::declval().data.LF_BCLASS); using LF_VBCLASS = decltype(std::declval().data.LF_VBCLASS); using LF_VFUNCTAB = decltype(std::declval().data.LF_VFUNCTAB); void Visit(const PDB::CodeView::TPI::FieldList*, const LF_MEMBER&) { ++Count.NumDataMembers; } void Visit(const PDB::CodeView::TPI::FieldList*, const LF_NESTTYPE&) {} void Visit(const PDB::CodeView::TPI::FieldList*, const LF_STMEMBER&) {} void Visit(const PDB::CodeView::TPI::FieldList*, const LF_METHOD&) {} void Visit(const PDB::CodeView::TPI::FieldList*, const LF_ONEMETHOD&) {} void Visit(const PDB::CodeView::TPI::FieldList*, const LF_BCLASS&) { ++Count.NumDataMembers; } void Visit(const PDB::CodeView::TPI::FieldList*, const LF_VBCLASS&) { } void Visit(const PDB::CodeView::TPI::FieldList*, const LF_VFUNCTAB&) { ++Count.NumDataMembers; } FStructFieldCounts Count; }; extern "C" PDBREADER_API void CountNumFields( PDBHandle* handle, const void* fieldListRecord, FStructFieldCounts* outNumFields ) { const PDB::CodeView::TPI::Record* record = reinterpret_cast(fieldListRecord); FFieldCountingOperations op; WalkFieldList(handle, record, op); *outNumFields = op.Count; } struct FFieldAddressOperations { using LF_MEMBER = decltype(std::declval().data.LF_MEMBER); using LF_NESTTYPE = decltype(std::declval().data.LF_NESTTYPE); using LF_STMEMBER = decltype(std::declval().data.LF_STMEMBER); using LF_METHOD = decltype(std::declval().data.LF_METHOD); using LF_ONEMETHOD = decltype(std::declval().data.LF_ONEMETHOD); using LF_BCLASS = decltype(std::declval().data.LF_BCLASS); using LF_VBCLASS = decltype(std::declval().data.LF_VBCLASS); using LF_VFUNCTAB = decltype(std::declval().data.LF_VFUNCTAB); void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_MEMBER&) { *(Pointers++) = Entry; } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_NESTTYPE&) { *(Pointers++) = Entry; } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_STMEMBER&) { *(Pointers++) = Entry; } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_METHOD&) { *(Pointers++) = Entry; } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_ONEMETHOD&) { *(Pointers++) = Entry; } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_BCLASS&) { *(Pointers++) = Entry; } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_VBCLASS&) { *(Pointers++) = Entry; } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_VFUNCTAB&) { *(Pointers++) = Entry; } const void** Pointers; FFieldAddressOperations(const void** inOutPointers) : Pointers(inOutPointers) { } }; extern "C" PDBREADER_API void GetFieldRecords( PDBHandle* handle, const void* fieldListRecord, const void** inOutPointers, int32_t numPointers ) { const PDB::CodeView::TPI::Record* record = reinterpret_cast(fieldListRecord); FFieldAddressOperations op(inOutPointers); WalkFieldList(handle, record, op); } enum class DataMemberFlags { None = 0x0, IsVtable = 0x1, IsBitfield = 0x2, IsBaseClass = 0x4, }; static DataMemberFlags operator|(DataMemberFlags a, DataMemberFlags b) { return (DataMemberFlags)((int32_t)a | (int32_t)b); } struct FDataMemberInfo { const char* Name; uint64_t Offset; uint32_t TypeIndex; DataMemberFlags Flags; uint32_t BitPosition; uint32_t BitSize; }; struct FReadDataMemberOperations { using LF_MEMBER = decltype(std::declval().data.LF_MEMBER); using LF_NESTTYPE = decltype(std::declval().data.LF_NESTTYPE); using LF_STMEMBER = decltype(std::declval().data.LF_STMEMBER); using LF_METHOD = decltype(std::declval().data.LF_METHOD); using LF_ONEMETHOD = decltype(std::declval().data.LF_ONEMETHOD); using LF_BCLASS = decltype(std::declval().data.LF_BCLASS); using LF_VBCLASS = decltype(std::declval().data.LF_VBCLASS); using LF_VFUNCTAB = decltype(std::declval().data.LF_VFUNCTAB); void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_NESTTYPE&) { } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_STMEMBER&) { } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_METHOD&) { } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_ONEMETHOD&) { } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_VBCLASS&) { } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_MEMBER& data) { FDataMemberInfo& outInfo = *(MemberInfos++); const uint8_t* addr = reinterpret_cast(data.offset); FNumericLeaf offset = ReadNumericLeaf(addr); outInfo.Offset = offset.Value.UnsignedInt; // Shouldn't need to deal with signed int64 offset? outInfo.Name = reinterpret_cast(addr); outInfo.TypeIndex = data.index; outInfo.Flags = DataMemberFlags::None; if (data.index >= PDB->TPIStream.GetFirstTypeIndex()) { using PDB::CodeView::TPI::TypeRecordKind; const PDB::CodeView::TPI::Record* typeRecord = PDB->TPIStream.GetTypeRecord(data.index); switch (typeRecord->header.kind) { // TODO: other type sizes case TypeRecordKind::LF_BITFIELD: outInfo.Flags = outInfo.Flags | DataMemberFlags::IsBitfield; outInfo.BitPosition = typeRecord->data.LF_BITFIELD.position; outInfo.BitSize = typeRecord->data.LF_BITFIELD.length; outInfo.TypeIndex = typeRecord->data.LF_BITFIELD.type; break; } } } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_BCLASS& data) { FDataMemberInfo& outInfo = *(MemberInfos++); const uint8_t* addr = reinterpret_cast(data.offset); FNumericLeaf offset = ReadNumericLeaf(addr); outInfo.Offset = offset.Value.UnsignedInt; // Shouldn't need to deal with signed int64 offset? outInfo.Name = nullptr; outInfo.TypeIndex = data.index; outInfo.Flags = DataMemberFlags::IsBaseClass; } void Visit(const PDB::CodeView::TPI::FieldList* Entry, const LF_VFUNCTAB& data) { FDataMemberInfo& outInfo = *(MemberInfos++); outInfo.Offset = 0; outInfo.Name = nullptr; outInfo.TypeIndex = data.type; outInfo.Flags = DataMemberFlags::IsVtable; } PDBHandle* PDB; FDataMemberInfo* MemberInfos; FReadDataMemberOperations(PDBHandle* pdbHandle, FDataMemberInfo* inOutMemberInfos) : PDB(pdbHandle) , MemberInfos(inOutMemberInfos) { } }; extern "C" PDBREADER_API void ReadDataMembers( PDBHandle* handle, const void* fieldListRecord, FDataMemberInfo* outMembers ) { const PDB::CodeView::TPI::Record* record = reinterpret_cast(fieldListRecord); FReadDataMemberOperations op(handle, outMembers); WalkFieldList(handle, record, op); }