initial upload

This commit is contained in:
Ryan Fleury
2024-01-10 19:57:50 -08:00
commit a42ec6aeff
308 changed files with 162362 additions and 0 deletions
+488
View File
@@ -0,0 +1,488 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ CodeView Common Functions
static CV_NumericParsed
cv_numeric_from_data_range(U8 *first, U8 *opl){
CV_NumericParsed result = {0};
if (first + 2 <= opl){
U16 x = *(U16*)first;
if (x < 0x8000){
result.kind = CV_NumericKind_USHORT;
result.val = first;
result.encoded_size = 2;
}
else{
U64 val_size = 0;
switch (x){
case CV_NumericKind_CHAR: val_size = 1; break;
case CV_NumericKind_SHORT:
case CV_NumericKind_USHORT: val_size = 2; break;
case CV_NumericKind_LONG:
case CV_NumericKind_ULONG: val_size = 4; break;
case CV_NumericKind_FLOAT32: val_size = 4; break;
case CV_NumericKind_FLOAT64: val_size = 8; break;
case CV_NumericKind_FLOAT80: val_size = 10; break;
case CV_NumericKind_FLOAT128: val_size = 16; break;
case CV_NumericKind_QUADWORD:
case CV_NumericKind_UQUADWORD: val_size = 8; break;
case CV_NumericKind_FLOAT48: val_size = 6; break;
case CV_NumericKind_COMPLEX32: val_size = 8; break;
case CV_NumericKind_COMPLEX64: val_size = 16; break;
case CV_NumericKind_COMPLEX80: val_size = 20; break;
case CV_NumericKind_COMPLEX128:val_size = 32; break;
case CV_NumericKind_VARSTRING: val_size = 0; break; // TODO: ???
case CV_NumericKind_OCTWORD:
case CV_NumericKind_UOCTWORD: val_size = 16; break;
case CV_NumericKind_DECIMAL: val_size = 0; break; // TODO: ???
case CV_NumericKind_DATE: val_size = 0; break; // TODO: ???
case CV_NumericKind_UTF8STRING:val_size = 0; break; // TODO: ???
case CV_NumericKind_FLOAT16: val_size = 2; break;
}
if (first + 2 + val_size <= opl){
result.kind = x;
result.val = (first + 2);
result.encoded_size = 2 + val_size;
}
}
}
return(result);
}
static B32
cv_numeric_fits_in_u64(CV_NumericParsed *num){
B32 result = 0;
switch (num->kind){
case CV_NumericKind_USHORT:
case CV_NumericKind_ULONG:
case CV_NumericKind_UQUADWORD:
{
result = 1;
}break;
}
return(result);
}
static B32
cv_numeric_fits_in_s64(CV_NumericParsed *num){
B32 result = 0;
switch (num->kind){
case CV_NumericKind_CHAR:
case CV_NumericKind_SHORT:
case CV_NumericKind_LONG:
case CV_NumericKind_QUADWORD:
{
result = 1;
}break;
}
return(result);
}
static B32
cv_numeric_fits_in_f64(CV_NumericParsed *num){
B32 result = 0;
switch (num->kind){
case CV_NumericKind_FLOAT32:
case CV_NumericKind_FLOAT64:
{
result = 1;
}break;
}
return(result);
}
static U64
cv_u64_from_numeric(CV_NumericParsed *num){
U64 result = 0;
switch (num->kind){
case CV_NumericKind_USHORT:
{
result = *(U16*)num->val;
}break;
case CV_NumericKind_ULONG:
{
result = *(U32*)num->val;
}break;
case CV_NumericKind_UQUADWORD:
{
result = *(U64*)num->val;
}break;
}
return(result);
}
static S64
cv_s64_from_numeric(CV_NumericParsed *num){
S64 result = 0;
switch (num->kind){
case CV_NumericKind_CHAR:
{
result = *(S8*)num->val;
}break;
case CV_NumericKind_SHORT:
{
result = *(S16*)num->val;
}break;
case CV_NumericKind_LONG:
{
result = *(S32*)num->val;
}break;
case CV_NumericKind_QUADWORD:
{
result = *(S64*)num->val;
}break;
}
return(result);
}
static F64
cv_f64_from_numeric(CV_NumericParsed *num){
F64 result = 0;
switch (num->kind){
case CV_NumericKind_FLOAT32:
{
result = *(F32*)num->val;
}break;
case CV_NumericKind_FLOAT64:
{
result = *(F64*)num->val;
}break;
}
return(result);
}
////////////////////////////////
//~ CodeView Sym Parser Functions
static CV_SymParsed*
cv_sym_from_data(Arena *arena, String8 sym_data, U64 sym_align){
Assert(1 <= sym_align && IsPow2OrZero(sym_align));
ProfBegin("cv_sym_from_data");
Temp scratch = scratch_begin(&arena, 1);
// gather up symbols
CV_RecRangeStream *stream = cv_rec_range_stream_from_data(scratch.arena, sym_data, sym_align);
// convert to result
CV_SymParsed *result = push_array(arena, CV_SymParsed, 1);
result->data = sym_data;
result->sym_align = sym_align;
result->sym_ranges = cv_rec_range_array_from_stream(arena, stream);
cv_sym_top_level_info_from_syms(arena, sym_data, &result->sym_ranges, &result->info);
scratch_end(scratch);
ProfEnd();
return(result);
}
static CV_LeafParsed*
cv_leaf_from_data(Arena *arena, String8 leaf_data, CV_TypeId itype_first){
ProfBegin("cv_leaf_from_data");
Temp scratch = scratch_begin(&arena, 1);
// gather up symbols
CV_RecRangeStream *stream = cv_rec_range_stream_from_data(scratch.arena, leaf_data, 1);
// convert to result
CV_LeafParsed *result = push_array(arena, CV_LeafParsed, 1);
result->data = leaf_data;
result->itype_first = itype_first;
result->itype_opl = itype_first + stream->total_count;
result->leaf_ranges = cv_rec_range_array_from_stream(arena, stream);
scratch_end(scratch);
ProfEnd();
return(result);
}
static CV_RecRangeStream*
cv_rec_range_stream_from_data(Arena *arena, String8 sym_data, U64 sym_align){
Assert(1 <= sym_align && IsPow2OrZero(sym_align));
CV_RecRangeStream *result = push_array(arena, CV_RecRangeStream, 1);
U8 *data = sym_data.str;
U64 cursor = 0;
U64 cap = sym_data.size;
for (;cursor + sizeof(CV_RecHeader) <= cap;){
// setup a new chunk
arena_push_align(arena, 64);
CV_RecRangeChunk *cur_chunk = cv_rec_range_stream_push_chunk(arena, result);
U64 partial_count = 0;
for (;partial_count < CV_REC_RANGE_CHUNK_SIZE && cursor + sizeof(CV_RecHeader) <= cap;
partial_count += 1){
// compute cap
CV_RecHeader *hdr = (CV_RecHeader*)(data + cursor);
U64 symbol_cap_unclamped = cursor + 2 + hdr->size;
U64 symbol_cap = ClampTop(symbol_cap_unclamped, cap);
// push on range
cur_chunk->ranges[partial_count].off = cursor + 2;
cur_chunk->ranges[partial_count].hdr = *hdr;
// update cursor
U32 next_pos = AlignPow2(symbol_cap, sym_align);
cursor = next_pos;
}
result->total_count += partial_count;
}
return(result);
}
static void
cv_sym_top_level_info_from_syms(Arena *arena, String8 sym_data,
CV_RecRangeArray *ranges,
CV_SymTopLevelInfo *info_out){
MemoryZeroStruct(info_out);
CV_RecRange *range = ranges->ranges;
CV_RecRange *opl = range + ranges->count;
for (; range < opl; range += 1){
U8 *first = sym_data.str + range->off + 2;
U64 cap = range->hdr.size - 2;
switch (range->hdr.kind){
case CV_SymKind_COMPILE:
{
if (sizeof(CV_SymCompile) <= cap){
CV_SymCompile *compile = (CV_SymCompile*)first;
String8 ver_str = str8_cstring_capped((char*)(compile + 1), (char *)(first + cap));
info_out->arch = compile->machine;
info_out->language = CV_CompileFlags_ExtractLanguage(compile->flags);;
info_out->compiler_name = ver_str;
}
}break;
case CV_SymKind_COMPILE2:
{
if (sizeof(CV_SymCompile2) <= cap){
CV_SymCompile2 *compile2 = (CV_SymCompile2*)first;
String8 ver_str = str8_cstring_capped((char*)(compile2 + 1), (char*)(first + cap));
String8 compiler_name = push_str8f(arena, "%.*s %u.%u.%u",
str8_varg(ver_str),
compile2->ver_major,
compile2->ver_minor,
compile2->ver_build);
info_out->arch = compile2->machine;
info_out->language = CV_Compile2Flags_ExtractLanguage(compile2->flags);;
info_out->compiler_name = compiler_name;
}
}break;
case CV_SymKind_COMPILE3:
{
if (sizeof(CV_SymCompile3) <= cap){
CV_SymCompile3 *compile3 = (CV_SymCompile3*)first;
String8 ver_str = str8_cstring_capped((char*)(compile3 + 1), (char *)(first + cap));
String8 compiler_name = push_str8f(arena, "%.*s %u.%u.%u",
str8_varg(ver_str),
compile3->ver_major,
compile3->ver_minor,
compile3->ver_build);
info_out->arch = compile3->machine;
info_out->language = CV_Compile3Flags_ExtractLanguage(compile3->flags);;
info_out->compiler_name = compiler_name;
}
}break;
}
}
}
//- range streams
static CV_RecRangeChunk*
cv_rec_range_stream_push_chunk(Arena *arena, CV_RecRangeStream *stream){
CV_RecRangeChunk *result = push_array_no_zero(arena, CV_RecRangeChunk, 1);
SLLQueuePush(stream->first_chunk, stream->last_chunk, result);
return(result);
}
static CV_RecRangeArray
cv_rec_range_array_from_stream(Arena *arena, CV_RecRangeStream *stream){
U64 total_count = stream->total_count;
CV_RecRange *ranges = push_array_no_zero(arena, CV_RecRange, total_count);
U64 idx = 0;
for (CV_RecRangeChunk *chunk = stream->first_chunk;
chunk != 0;
chunk = chunk->next){
U64 copy_count_raw = total_count - idx;
U64 copy_count = ClampTop(copy_count_raw, CV_REC_RANGE_CHUNK_SIZE);
MemoryCopy(ranges + idx, chunk->ranges, copy_count*sizeof(CV_RecRange));
idx += copy_count;
}
CV_RecRangeArray result = {0};
result.ranges = ranges;
result.count = total_count;
return(result);
}
////////////////////////////////
//~ CodeView C13 Parser Functions
static CV_C13Parsed*
cv_c13_from_data(Arena *arena, String8 c13_data,
PDB_Strtbl *strtbl, PDB_CoffSectionArray *sections){
ProfBegin("cv_c13_from_data");
// gather c13 data
CV_C13SubSectionNode *file_chksms = 0;
CV_C13SubSectionNode *first = 0;
CV_C13SubSectionNode *last = 0;
U64 count = 0;
{
U32 cursor = 0;
for (; cursor + sizeof(CV_C13_SubSectionHeader) <= c13_data.size;){
// read header
CV_C13_SubSectionHeader *hdr = (CV_C13_SubSectionHeader*)(c13_data.str + cursor);
// get sub section info
U32 sub_section_off = cursor + sizeof(*hdr);
U32 sub_section_size_raw = hdr->size;
U32 after_sub_section_off_unclamped = sub_section_off + sub_section_size_raw;
U32 after_sub_section_off = ClampTop(after_sub_section_off_unclamped, c13_data.size);
U32 sub_section_size = after_sub_section_off - sub_section_off;
// emit sub section
if (!(hdr->kind & CV_C13_SubSectionKind_IgnoreFlag)){
CV_C13SubSectionNode *node = push_array(arena, CV_C13SubSectionNode, 1);
SLLQueuePush(first, last, node);
count += 1;
node->kind = hdr->kind;
node->off = sub_section_off;
node->size = sub_section_size;
if (hdr->kind == CV_C13_SubSectionKind_FileChksms){
file_chksms = node;
}
}
// move cursor
cursor = AlignPow2(after_sub_section_off, 4);
}
}
// parse each section
for (CV_C13SubSectionNode *node = first;
node != 0;
node = node->next){
U8 *first = c13_data.str + node->off;
U32 cap = node->size;
switch (node->kind){
case CV_C13_SubSectionKind_Lines:
{
// read header
if (sizeof(CV_C13_SubSecLinesHeader) <= cap){
CV_C13_SubSecLinesHeader *hdr = (CV_C13_SubSecLinesHeader*)first;
// read file
U32 file_info_off = sizeof(*hdr);
if (file_info_off + sizeof(CV_C13_File) <= cap){
CV_C13_File *file = (CV_C13_File*)(first + file_info_off);
// extract top level info
U32 sec_idx = hdr->sec;
if (1 <= sec_idx && sec_idx <= sections->count){
B32 has_cols = !!(hdr->flags & CV_C13_SubSecLinesFlag_HasColumns);
U64 secrel_off = hdr->sec_off;
U64 secrel_opl = secrel_off + hdr->len;
U64 sec_base_off = sections->sections[sec_idx - 1].voff;
U32 file_off = file->file_off;
U32 line_count_unclamped = file->num_lines;
U32 block_size = file->block_size;
// file_name from file_off
String8 file_name = {0};
if (file_off + sizeof(CV_C13_Checksum) <= file_chksms->size){
CV_C13_Checksum *checksum = (CV_C13_Checksum*)(c13_data.str + file_chksms->off + file_off);
U32 name_off = checksum->name_off;
file_name = pdb_strtbl_string_from_off(strtbl, name_off);
}
// array layouts
U32 line_item_size = sizeof(CV_C13_Line);
if (has_cols){
line_item_size += sizeof(CV_C13_Column);
}
U32 line_array_off = file_info_off + sizeof(*file);
U32 line_count_max = (cap - line_array_off)/line_item_size;
U32 line_count = ClampTop(line_count_unclamped, line_count_max);
U32 col_array_off = line_array_off + line_count*sizeof(CV_C13_Line);
// parse lines
U64 *voffs = push_array_no_zero(arena, U64, line_count + 1);
U32 *line_nums = push_array_no_zero(arena, U32, line_count);
{
CV_C13_Line *line_ptr = (CV_C13_Line*)(first + line_array_off);
CV_C13_Line *line_opl = line_ptr + line_count;
// TODO(allen): check order correctness here
U32 i = 0;
for (; line_ptr < line_opl; line_ptr += 1, i += 1){
voffs[i] = line_ptr->off + secrel_off + sec_base_off;
line_nums[i] = CV_C13_LineFlags_ExtractLineNumber(line_ptr->flags);
}
voffs[i] = secrel_opl + sec_base_off;
}
// emit parsed lines
CV_C13LinesParsed *lines_parsed = push_array(arena, CV_C13LinesParsed, 1);
lines_parsed->sec_idx = sec_idx;
lines_parsed->file_off = file_off;
lines_parsed->secrel_base_off = secrel_off;
lines_parsed->file_name = file_name;
lines_parsed->voffs = voffs;
lines_parsed->line_nums = line_nums;
lines_parsed->line_count = line_count;
node->lines = lines_parsed;
}
}
}
}break;
}
}
// convert to result
CV_C13Parsed *result = push_array(arena, CV_C13Parsed, 1);
result->first_sub_section = first;
result->last_sub_section = last;
result->sub_section_count = count;
result->file_chksms_sub_section = file_chksms;
ProfEnd();
return(result);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,124 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ CodeView Conversion Functions
static RADDBG_Arch
raddbg_arch_from_cv_arch(CV_Arch cv_arch){
RADDBG_Arch result = 0;
switch (cv_arch){
case CV_Arch_8086: result = RADDBG_Arch_X86; break;
case CV_Arch_X64: result = RADDBG_Arch_X64; break;
//case CV_Arch_8080: break;
//case CV_Arch_80286: break;
//case CV_Arch_80386: break;
//case CV_Arch_80486: break;
//case CV_Arch_PENTIUM: break;
//case CV_Arch_PENTIUMII: break;
//case CV_Arch_PENTIUMIII: break;
//case CV_Arch_MIPS: break;
//case CV_Arch_MIPS16: break;
//case CV_Arch_MIPS32: break;
//case CV_Arch_MIPS64: break;
//case CV_Arch_MIPSI: break;
//case CV_Arch_MIPSII: break;
//case CV_Arch_MIPSIII: break;
//case CV_Arch_MIPSIV: break;
//case CV_Arch_MIPSV: break;
//case CV_Arch_M68000: break;
//case CV_Arch_M68010: break;
//case CV_Arch_M68020: break;
//case CV_Arch_M68030: break;
//case CV_Arch_M68040: break;
//case CV_Arch_ALPHA: break;
//case CV_Arch_ALPHA_21164: break;
//case CV_Arch_ALPHA_21164A: break;
//case CV_Arch_ALPHA_21264: break;
//case CV_Arch_ALPHA_21364: break;
//case CV_Arch_PPC601: break;
//case CV_Arch_PPC603: break;
//case CV_Arch_PPC604: break;
//case CV_Arch_PPC620: break;
//case CV_Arch_PPCFP: break;
//case CV_Arch_PPCBE: break;
//case CV_Arch_SH3: break;
//case CV_Arch_SH3E: break;
//case CV_Arch_SH3DSP: break;
//case CV_Arch_SH4: break;
//case CV_Arch_SHMEDIA: break;
//case CV_Arch_ARM3: break;
//case CV_Arch_ARM4: break;
//case CV_Arch_ARM4T: break;
//case CV_Arch_ARM5: break;
//case CV_Arch_ARM5T: break;
//case CV_Arch_ARM6: break;
//case CV_Arch_ARM_XMAC: break;
//case CV_Arch_ARM_WMMX: break;
//case CV_Arch_ARM7: break;
//case CV_Arch_OMNI: break;
//case CV_Arch_IA64_1: break;
//case CV_Arch_IA64_2: break;
//case CV_Arch_CEE: break;
//case CV_Arch_AM33: break;
//case CV_Arch_M32R: break;
//case CV_Arch_TRICORE: break;
//case CV_Arch_EBC: break;
//case CV_Arch_THUMB: break;
//case CV_Arch_ARMNT: break;
//case CV_Arch_ARM64: break;
//case CV_Arch_D3D11_SHADER: break;
}
return(result);
}
static RADDBG_RegisterCode
raddbg_reg_code_from_cv_reg_code(RADDBG_Arch arch, CV_Reg reg_code){
RADDBG_RegisterCode result = 0;
switch (arch){
case RADDBG_Arch_X86:
{
switch (reg_code){
#define X(CVN,C,RDN,BP,BZ) case C: result = RADDBG_RegisterCode_X86_##RDN; break;
CV_Reg_X86_XList(X)
#undef X
}
}break;
case RADDBG_Arch_X64:
{
switch (reg_code){
#define X(CVN,C,RDN,BP,BZ) case C: result = RADDBG_RegisterCode_X64_##RDN; break;
CV_Reg_X64_XList(X)
#undef X
}
}break;
}
return(result);
}
static RADDBG_Language
raddbg_language_from_cv_language(CV_Language cv_language){
RADDBG_Language result = 0;
switch (cv_language){
case CV_Language_C: result = RADDBG_Language_C; break;
case CV_Language_CXX: result = RADDBG_Language_CPlusPlus; break;
//case CV_Language_FORTRAN: result = ; break;
//case CV_Language_MASM: result = ; break;
//case CV_Language_PASCAL: result = ; break;
//case CV_Language_BASIC: result = ; break;
//case CV_Language_COBOL: result = ; break;
//case CV_Language_LINK: result = ; break;
//case CV_Language_CVTRES: result = ; break;
//case CV_Language_CVTPGD: result = ; break;
//case CV_Language_CSHARP: result = ; break;
//case CV_Language_VB: result = ; break;
//case CV_Language_ILASM: result = ; break;
//case CV_Language_JAVA: result = ; break;
//case CV_Language_JSCRIPT: result = ; break;
//case CV_Language_MSIL: result = ; break;
//case CV_Language_HLSL: result = ; break;
}
return(result);
}
@@ -0,0 +1,14 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_CODEVIEW_CONVERSION_H
#define RADDBG_CODEVIEW_CONVERSION_H
////////////////////////////////
//~ CodeView Conversion Functions
static RADDBG_Arch raddbg_arch_from_cv_arch(CV_Arch arch);
static RADDBG_RegisterCode raddbg_reg_code_from_cv_reg_code(RADDBG_Arch arch, CV_Reg reg_code);
static RADDBG_Language raddbg_language_from_cv_language(CV_Language language);
#endif //RADDBG_CODEVIEW_CONVERSION_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,86 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_CODEVIEW_STRINGIZE_H
#define RADDBG_CODEVIEW_STRINGIZE_H
////////////////////////////////
//~ CodeView Stringize Helper Types
typedef struct CV_StringizeSymParams{
CV_Arch arch;
} CV_StringizeSymParams;
typedef struct CV_StringizeLeafParams{
U32 dummy;
} CV_StringizeLeafParams;
////////////////////////////////
//~ CodeView Common Stringize Functions
static void cv_stringize_numeric(Arena *arena, String8List *out, CV_NumericParsed *num);
static void cv_stringize_lvar_addr_range(Arena *arena, String8List *out,
CV_LvarAddrRange *range);
static void cv_stringize_lvar_addr_gap(Arena *arena, String8List *out, CV_LvarAddrGap *gap);
static void cv_stringize_lvar_addr_gap_list(Arena *arena, String8List *out,
void *first, void *opl);
static String8 cv_string_from_sym_kind(CV_SymKind kind);
static String8 cv_string_from_basic_type(CV_BasicType basic_type);
static String8 cv_string_from_leaf_kind(CV_LeafKind kind);
static String8 cv_string_from_numeric_kind(CV_NumericKind kind);
static String8 cv_string_from_c13_sub_section_kind(CV_C13_SubSectionKind kind);
static String8 cv_string_from_machine(CV_Arch arch);
static String8 cv_string_from_reg(CV_Arch arch, CV_Reg reg);
static String8 cv_string_from_pointer_kind(CV_PointerKind ptr_kind);
static String8 cv_string_from_pointer_mode(CV_PointerMode ptr_mode);
static String8 cv_string_from_hfa_kind(CV_HFAKind hfa_kind);
static String8 cv_string_from_mo_com_udt_kind(CV_MoComUDTKind mo_com_udt_kind);
////////////////////////////////
//~ CodeView Flags Stringize Functions
static void cv_stringize_modifier_flags(Arena *arena, String8List *out,
U32 indent, CV_ModifierFlags flags);
static void cv_stringize_type_props(Arena *arena, String8List *out,
U32 indent, CV_TypeProps props);
static void cv_stringize_pointer_attribs(Arena *arena, String8List *out,
U32 indent, CV_PointerAttribs attribs);
static void cv_stringize_local_flags(Arena *arena, String8List *out,
U32 indent, CV_LocalFlags flags);
////////////////////////////////
//~ CodeView Sym Stringize Functions
static void cv_stringize_sym_parsed(Arena *arena, String8List *out, CV_SymParsed *sym);
static void cv_stringize_sym_range(Arena *arena, String8List *out,
CV_RecRange *range, String8 data,
CV_StringizeSymParams *p);
static void cv_stringize_sym_array(Arena *arena, String8List *out,
CV_RecRangeArray *ranges, String8 data,
CV_StringizeSymParams *p);
////////////////////////////////
//~ CodeView Leaf Stringize Functions
static void cv_stringize_leaf_parsed(Arena *arena, String8List *out, CV_LeafParsed *leaf);
static void cv_stringize_leaf_range(Arena *arena, String8List *out,
CV_RecRange *range, CV_TypeId itype, String8 data,
CV_StringizeLeafParams *p);
static void cv_stringize_leaf_array(Arena *arena, String8List *out,
CV_RecRangeArray *ranges, CV_TypeId itype_first,
String8 data,
CV_StringizeLeafParams *p);
////////////////////////////////
//~ CodeView C13 Stringize Functions
static void cv_stringize_c13_parsed(Arena *arena, String8List *out, CV_C13Parsed *c13);
#endif //RADDBG_CODEVIEW_STRINGIZE_H
+52
View File
@@ -0,0 +1,52 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_COFF_H
#define RADDBG_COFF_H
////////////////////////////////
//~ COFF Format Types
typedef struct COFF_Guid{
U32 data1;
U16 data2;
U16 data3;
U32 data4;
U32 data5;
} COFF_Guid;
#define COFF_ArchXList(X)\
X(UNKNOWN, 0x0)\
X(X86, 0x14c)\
X(X64, 0x8664)\
X(ARM33, 0x1d3)\
X(ARM, 0x1c0)\
X(ARM64, 0xaa64)\
X(ARMNT, 0x1c4)\
X(EBC, 0xebc)\
X(IA64, 0x200)\
X(M32R, 0x9041)\
X(MIPS16, 0x266)\
X(MIPSFPU, 0x366)\
X(MIPSFPU16, 0x466)\
X(POWERPC, 0x1f0)\
X(POWERPCFP, 0x1f1)\
X(R4000, 0x166)\
X(RISCV32, 0x5032)\
X(RISCV64, 0x5064)\
X(RISCV128, 0x5128)\
X(SH3, 0x1a2)\
X(SH3DSP, 0x1a3)\
X(SH4, 0x1a6)\
X(SH5, 0x1a8)\
X(THUMB, 0x1c2)\
X(WCEMIPSV2, 0x169)
typedef U16 COFF_Arch;
enum{
#define X(N,c) COFF_Arch_##N = c,
COFF_ArchXList(X)
#undef X
};
#endif //COFF_H
@@ -0,0 +1,22 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ COFF Conversion Functions
static RADDBG_BinarySectionFlags
raddbg_binary_section_flags_from_coff_section_flags(COFF_SectionFlags flags){
RADDBG_BinarySectionFlags result = 0;
if (flags & COFF_SectionFlag_MEM_READ){
result |= RADDBG_BinarySectionFlag_Read;
}
if (flags & COFF_SectionFlag_MEM_WRITE){
result |= RADDBG_BinarySectionFlag_Write;
}
if (flags & COFF_SectionFlag_MEM_EXECUTE){
result |= RADDBG_BinarySectionFlag_Execute;
}
return(result);
}
@@ -0,0 +1,13 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_COFF_CONVERSION_H
#define RADDBG_COFF_CONVERSION_H
////////////////////////////////
//~ COFF Conversion Functions
static RADDBG_BinarySectionFlags
raddbg_binary_section_flags_from_coff_section_flags(COFF_SectionFlags flags);
#endif //RADDBG_COFF_CONVERSION_H
File diff suppressed because it is too large Load Diff
+262
View File
@@ -0,0 +1,262 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_FROM_PDB_H
#define RADDBG_FROM_PDB_H
////////////////////////////////
//~ Program Parameters Type
typedef struct PDBCONV_Params{
String8 input_pdb_name;
String8 input_pdb_data;
String8 input_exe_name;
String8 input_exe_data;
String8 output_name;
struct{
B8 input;
B8 output;
B8 parsing;
B8 converting;
} hide_errors;
B8 dump;
B8 dump__first;
B8 dump_coff_sections;
B8 dump_msf;
B8 dump_sym;
B8 dump_tpi_hash;
B8 dump_leaf;
B8 dump_c13;
B8 dump_contributions;
B8 dump__last;
String8List errors;
} PDBCONV_Params;
////////////////////////////////
//~ Program Parameters Parser
static PDBCONV_Params *pdb_convert_params_from_cmd_line(Arena *arena, CmdLine *cmdline);
////////////////////////////////
//~ PDB Type & Symbol Info Translation Helpers
//- translation helper types
typedef struct PDBCONV_FwdNode{
struct PDBCONV_FwdNode *next;
CV_TypeId key;
CV_TypeId val;
} PDBCONV_FwdNode;
typedef struct PDBCONV_FwdMap{
PDBCONV_FwdNode *buckets[1<<24];
} PDBCONV_FwdMap;
typedef struct PDBCONV_TypeRev{
struct PDBCONV_TypeRev *next;
CONS_Type *owner_type;
CV_TypeId field_itype;
} PDBCONV_TypeRev;
typedef struct PDBCONV_FrameProcData{
U32 frame_size;
CV_FrameprocFlags flags;
} PDBCONV_FrameProcData;
typedef struct PDBCONV_FrameProcNode{
struct PDBCONV_FrameProcNode *next;
CONS_Symbol *key;
PDBCONV_FrameProcData data;
} PDBCONV_FrameProcNode;
typedef struct PDBCONV_FrameProcMap{
PDBCONV_FrameProcNode *buckets[1<<24];
} PDBCONV_FrameProcMap;
typedef struct PDBCONV_ScopeNode{
struct PDBCONV_ScopeNode *next;
CONS_Scope *scope;
CONS_Symbol *symbol;
} PDBCONV_ScopeNode;
typedef struct PDBCONV_KnownGlobalNode{
struct PDBCONV_KnownGlobalNode *next;
String8 key_name;
U64 key_voff;
U64 hash;
} PDBCONV_KnownGlobalNode;
typedef struct PDBCONV_KnownGlobalSet{
PDBCONV_KnownGlobalNode *buckets[1<<24];
} PDBCONV_KnownGlobalSet;
typedef struct PDBCONV_TypesSymbolsParams{
RADDBG_Arch architecture;
CV_SymParsed *sym;
CV_SymParsed **sym_for_unit;
U64 unit_count;
PDB_TpiHashParsed *tpi_hash;
CV_LeafParsed *tpi_leaf;
PDB_CoffSectionArray *sections;
} PDBCONV_TypesSymbolsParams;
typedef struct PDBCONV_LinkNameNode{
struct PDBCONV_LinkNameNode *next;
U64 voff;
String8 name;
} PDBCONV_LinkNameNode;
typedef struct PDBCONV_LinkNameMap{
PDBCONV_LinkNameNode *buckets[1<<24];
} PDBCONV_LinkNameMap;
typedef struct PDBCONV_Ctx{
// INPUT data
RADDBG_Arch arch;
U64 addr_size;
PDB_TpiHashParsed *hash;
CV_LeafParsed *leaf;
COFF_SectionHeader *sections;
U64 section_count;
// OUTPUT data
CONS_Root *root;
// TEMPORARY STATE
Arena *temp_arena;
PDBCONV_FwdMap fwd_map;
PDBCONV_TypeRev *member_revisit_first;
PDBCONV_TypeRev *member_revisit_last;
PDBCONV_TypeRev *enum_revisit_first;
PDBCONV_TypeRev *enum_revisit_last;
PDBCONV_FrameProcMap frame_proc_map;
PDBCONV_ScopeNode *scope_stack;
PDBCONV_ScopeNode *scope_node_free;
PDBCONV_KnownGlobalSet known_globals;
PDBCONV_LinkNameMap link_names;
} PDBCONV_Ctx;
//- pdb types and symbols
static void pdbconv_types_and_symbols(PDBCONV_TypesSymbolsParams *params, CONS_Root *out_root);
//- decoding helpers
static U32 pdbconv_u32_from_numeric(PDBCONV_Ctx *ctx, CV_NumericParsed *num);
static COFF_SectionHeader* pdbconv_sec_header_from_sec_num(PDBCONV_Ctx *ctx, U32 sec_num);
//- type info
// TODO(allen): explain the overarching pattern of PDB type info translation here
// 1. main passes (out of order necessity) & after
// 2. resolve forward
// 3. cons type info
// 4. "resolve itype"
// 5. equipping members & enumerates
// 6. equipping source coordinates
// type info construction passes
static void pdbconv_type_cons_main_passes(PDBCONV_Ctx *ctx);
static CV_TypeId pdbconv_type_resolve_fwd(PDBCONV_Ctx *ctx, CV_TypeId itype);
static CONS_Type* pdbconv_type_resolve_itype(PDBCONV_Ctx *ctx, CV_TypeId itype);
static void pdbconv_type_equip_members(PDBCONV_Ctx *ctx, CONS_Type *owern_type,
CV_TypeId field_itype);
static void pdbconv_type_equip_enumerates(PDBCONV_Ctx *ctx, CONS_Type *owner_type,
CV_TypeId field_itype);
// type info construction helpers
static CONS_Type* pdbconv_type_cons_basic(PDBCONV_Ctx *ctx, CV_TypeId itype);
static CONS_Type* pdbconv_type_cons_leaf_record(PDBCONV_Ctx *ctx, CV_TypeId itype);
static CONS_Type* pdbconv_type_resolve_and_check(PDBCONV_Ctx *ctx, CV_TypeId itype);
static void pdbconv_type_resolve_arglist(Arena *arena, CONS_TypeList *out,
PDBCONV_Ctx *ctx, CV_TypeId arglist_itype);
// type info resolution helpers
static CONS_Type* pdbconv_type_from_name(PDBCONV_Ctx *ctx, String8 name);
// type fwd map
static void pdbconv_type_fwd_map_set(Arena *arena, PDBCONV_FwdMap *map,
CV_TypeId key, CV_TypeId val);
static CV_TypeId pdbconv_type_fwd_map_get(PDBCONV_FwdMap *map, CV_TypeId key);
//- symbol info
// symbol info construction
static void pdbconv_symbol_cons(PDBCONV_Ctx *ctx, CV_SymParsed *sym, U32 sym_unique_id);
static void pdbconv_gather_link_names(PDBCONV_Ctx *ctx, CV_SymParsed *sym);
// "frameproc" map
static void pdbconv_symbol_frame_proc_write(PDBCONV_Ctx *ctx,CONS_Symbol *key,
PDBCONV_FrameProcData *data);
static PDBCONV_FrameProcData* pdbconv_symbol_frame_proc_read(PDBCONV_Ctx *ctx, CONS_Symbol *key);
// scope stack
static void pdbconv_symbol_push_scope(PDBCONV_Ctx *ctx, CONS_Scope *scope, CONS_Symbol *symbol);
static void pdbconv_symbol_pop_scope(PDBCONV_Ctx *ctx);
static void pdbconv_symbol_clear_scope_stack(PDBCONV_Ctx *ctx);
#define pdbconv_symbol_current_scope(ctx) \
((ctx)->scope_stack == 0)?0:((ctx)->scope_stack->scope)
#define pdbconv_symbol_current_symbol(ctx) \
((ctx)->scope_stack == 0)?0:((ctx)->scope_stack->symbol)
// PDB/C++ name parsing helper
static U64 pdbconv_end_of_cplusplus_container_name(String8 str);
// global deduplication
static U64 pdbconv_known_global_hash(String8 name, U64 voff);
static B32 pdbconv_known_global_lookup(PDBCONV_KnownGlobalSet *set, String8 name, U64 voff);
static void pdbconv_known_global_insert(Arena *arena, PDBCONV_KnownGlobalSet *set,
String8 name, U64 voff);
// location info helpers
static CONS_Location* pdbconv_location_from_addr_reg_off(PDBCONV_Ctx *ctx,
RADDBG_RegisterCode reg_code,
U32 reg_byte_pos,
U32 reg_byte_size,
U64 offset,
B32 extra_indirection);
static CV_EncodedFramePtrReg pdbconv_cv_encoded_fp_reg_from_proc(PDBCONV_Ctx *ctx,
CONS_Symbol *proc,
B32 param_base);
static RADDBG_RegisterCode pdbconv_reg_code_from_arch_encoded_fp_reg(RADDBG_Arch arch,
CV_EncodedFramePtrReg encoded_reg);
static void pdbconv_location_over_lvar_addr_range(PDBCONV_Ctx *ctx,
CONS_LocationSet *locset,
CONS_Location *location,
CV_LvarAddrRange *range,
CV_LvarAddrGap *gaps, U64 gap_count);
// link names
static void pdbconv_link_name_save(Arena *arena, PDBCONV_LinkNameMap *map,
U64 voff, String8 name);
static String8 pdbconv_link_name_find(PDBCONV_LinkNameMap *map, U64 voff);
////////////////////////////////
//~ Conversion Output Type
typedef struct PDBCONV_Out PDBCONV_Out;
struct PDBCONV_Out
{
B32 good_parse;
CONS_Root *root;
String8List dump;
String8List errors;
};
////////////////////////////////
//~ Conversion Path
static PDBCONV_Out *pdbconv_convert(Arena *arena, PDBCONV_Params *params);
#endif //RADDBG_FROM_PDB_H
@@ -0,0 +1,116 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "base/base_inc.h"
#include "os/os_inc.h"
#include "coff/coff.h"
#include "raddbg_format/raddbg_format.h"
#include "raddbg_cons/raddbg_cons.h"
#include "raddbg_coff.h"
#include "raddbg_codeview.h"
#include "raddbg_msf.h"
#include "raddbg_pdb.h"
#include "raddbg_coff_conversion.h"
#include "raddbg_codeview_conversion.h"
#include "raddbg_codeview_stringize.h"
#include "raddbg_pdb_stringize.h"
#include "raddbg_from_pdb.h"
#include "base/base_inc.c"
#include "coff/coff.c"
#include "os/os_inc.c"
#include "raddbg_format/raddbg_format.c"
#include "raddbg_cons/raddbg_cons.c"
#include "raddbg_msf.c"
#include "raddbg_codeview.c"
#include "raddbg_pdb.c"
#include "raddbg_coff_conversion.c"
#include "raddbg_codeview_conversion.c"
#include "raddbg_codeview_stringize.c"
#include "raddbg_pdb_stringize.c"
#include "raddbg_from_pdb.c"
int
main(int argc, char **argv){
local_persist TCTX main_thread_tctx = {0};
tctx_init_and_equip(&main_thread_tctx);
#if PROFILE_TELEMETRY
U64 tm_data_size = GB(1);
U8 *tm_data = os_reserve(tm_data_size);
os_commit(tm_data, tm_data_size);
tmLoadLibrary(TM_RELEASE);
tmSetMaxThreadCount(1024);
tmInitialize(tm_data_size, tm_data);
#endif
ThreadName("[main]");
Arena *arena = arena_alloc();
String8List args = os_string_list_from_argcv(arena, argc, argv);
CmdLine cmdline = cmd_line_from_string_list(arena, args);
ProfBeginCapture("raddbg_from_pdb");
//- rjf: parse arguments
PDBCONV_Params *params = pdb_convert_params_from_cmd_line(arena, &cmdline);
//- rjf: show input errors
if (params->errors.node_count > 0 &&
!params->hide_errors.input){
for (String8Node *node = params->errors.first;
node != 0;
node = node->next){
fprintf(stderr, "error(input): %.*s\n", str8_varg(node->string));
}
}
//- rjf: open output file
String8 output_name = push_str8_copy(arena, params->output_name);
FILE *out_file = fopen((char*)output_name.str, "wb");
if(out_file == 0 && !params->hide_errors.output)
{
fprintf(stderr, "error(output): could not open output file\n");
}
//- rjf: convert
PDBCONV_Out *out = 0;
if(out_file != 0)
{
out = pdbconv_convert(arena, params);
}
//- rjf: print dump
if(out != 0)
{
for(String8Node *node = out->dump.first; node != 0; node = node->next)
{
fwrite(node->string.str, 1, node->string.size, stdout);
}
}
//- rjf: bake file
if(out != 0 && out->good_parse && params->output_name.size > 0 && out->good_parse)
{
String8List baked = {0};
cons_bake_file(arena, out->root, &baked);
for(String8Node *node = baked.first; node != 0; node = node->next)
{
fwrite(node->string.str, node->string.size, 1, out_file);
}
}
//- rjf: close output file
if(out_file != 0)
{
fclose(out_file);
}
ProfEndCapture();
return(0);
}
+284
View File
@@ -0,0 +1,284 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ MSF Parser Function
static MSF_Parsed*
msf_parsed_from_data(Arena *arena, String8 msf_data){
ProfBegin("msf_parsed_from_data");
Temp scratch = scratch_begin(&arena, 1);
MSF_Parsed *result = 0;
//- determine msf type
U32 index_size = 0;
if (msf_data.size >= MSF_MIN_SIZE){
if (str8_match(msf_data, str8_lit(msf_msf20_magic),
StringMatchFlag_RightSideSloppy)){
index_size = 2;
}
else if (str8_match(msf_data, str8_lit(msf_msf70_magic),
StringMatchFlag_RightSideSloppy)){
index_size = 4;
}
}
if (index_size == 2 || index_size == 4){
//- extract info from header
U32 block_size_raw = 0;
U32 whole_file_block_count_raw = 0;
U32 directory_size_raw = 0;
U32 directory_super_map_raw = 0;
if (index_size == 2){
MSF_Header20 *header = (MSF_Header20*)(msf_data.str + MSF_MSF20_MAGIC_SIZE);
block_size_raw = header->block_size;
whole_file_block_count_raw = header->block_count;
directory_size_raw = header->directory_size;
}
else if (index_size == 4){
MSF_Header70 *header = (MSF_Header70*)(msf_data.str + MSF_MSF70_MAGIC_SIZE);
block_size_raw = header->block_size;
whole_file_block_count_raw = header->block_count;
directory_size_raw = header->directory_size;
directory_super_map_raw = header->directory_super_map;
}
//- setup important sizes & counts
// (blocks)
U32 block_size = ClampTop(block_size_raw, msf_data.size);
// (whole file block count)
U32 whole_file_block_count_max = CeilIntegerDiv(msf_data.size, block_size);
U32 whole_file_block_count = ClampTop(whole_file_block_count_raw, whole_file_block_count_max);
// (directory)
U32 directory_size = ClampTop(directory_size_raw, msf_data.size);
U32 block_count_in_directory = CeilIntegerDiv(directory_size, block_size);
// (map)
U32 directory_map_size = block_count_in_directory*index_size;
U32 block_count_in_directory_map = CeilIntegerDiv(directory_map_size, block_size);
// Layout of the "directory":
//
// super map: [s1, s2, s3, ...]
// map: s1 -> [i1, i2, i3, ...]; s2 -> [...]; s3 -> [...]; ...
// directory: i1 -> [data]; i2 -> [data]; i3 -> [data]; ... i1 -> [data]; ...
//
// The "data" in the directory describes streams:
// PDB20:
// struct Pdb20StreamSize{
// U32 size;
// U32 unknown; // looks like kind codes or revision counters or something
// }
// struct{
// U32 stream_count;
// Pdb20StreamSize stream_sizes[stream_count];
// U16 stream_indices[stream_count][...];
// }
//
// PDB70:
// struct{
// U32 stream_count;
// U32 stream_sizes[stream_count];
// U32 stream_indices[stream_count][...];
// }
//- parse stream directory
U8 *directory_buf = push_array(scratch.arena, U8, directory_size);
B32 got_directory = 1;
{
U32 directory_super_map_dummy = 0;
U32 *directory_super_map = 0;
U32 directory_map_block_skip_size = 0;
if (index_size == 2){
directory_super_map = &directory_super_map_dummy;
directory_map_block_skip_size =
MSF_MSF20_MAGIC_SIZE + OffsetOf(MSF_Header20, directory_map);
}
else{
U64 super_map_off =
MSF_MSF70_MAGIC_SIZE + OffsetOf(MSF_Header70, directory_super_map);
directory_super_map = (U32*)(msf_data.str + super_map_off);
}
U32 max_index_count_in_map_block = (block_size - directory_map_block_skip_size)/index_size;
// for each index in super map ...
U8 *out_ptr = directory_buf;
U32 *super_map_ptr = directory_super_map;
for (U32 i = 0; i < block_count_in_directory_map; i += 1, super_map_ptr += 1){
U32 directory_map_block_index = *super_map_ptr;
if (directory_map_block_index >= whole_file_block_count){
got_directory = 0;
goto parse_directory_done;
}
U64 directory_map_block_off = (U64)(directory_map_block_index)*block_size;
U8 *directory_map_block_base = (msf_data.str + directory_map_block_off);
// clamp index count by end of directory
U32 index_count = 0;
{
U32 directory_pos = (U32)(out_ptr - directory_buf);
U32 remaining_size = directory_size - directory_pos;
U32 remaining_map_block_count = CeilIntegerDiv(remaining_size, block_size);
index_count = ClampTop(max_index_count_in_map_block, remaining_map_block_count);
}
// for each index in map ...
U8 *map_ptr = directory_map_block_base + directory_map_block_skip_size;
for (U32 j = 0; j < index_count; j += 1, map_ptr += index_size){
// read index
U32 directory_block_index = 0;
if (index_size == 4){
directory_block_index = *(U32*)(map_ptr);
}
else{
directory_block_index = *(U16*)(map_ptr);
}
if (directory_block_index >= whole_file_block_count){
got_directory = 0;
goto parse_directory_done;
}
U64 directory_block_off = (U64)(directory_block_index)*block_size;
U8 *directory_block_base = (msf_data.str + directory_block_off);
// clamp copy size by end of directory
U32 copy_size = 0;
{
U32 directory_pos = (U32)(out_ptr - directory_buf);
U32 remaining_size = directory_size - directory_pos;
copy_size = ClampTop(block_size, remaining_size);
}
// copy block data
MemoryCopy(out_ptr, directory_block_base, copy_size);
out_ptr += copy_size;
}
}
parse_directory_done:;
}
//- parse streams from directory
U32 stream_count = 0;
B32 got_streams = 0;
String8 *streams = 0;
if (got_directory){
got_streams = 1;
// read stream count
U32 stream_count_raw = *(U32*)(directory_buf);
// setup counts, sizes, and offsets
U32 size_of_stream_entry = 4;
if (index_size == 2){
size_of_stream_entry = 8;
}
U32 stream_count_max = (directory_size - 4)/size_of_stream_entry;
U32 stream_count__inner = ClampTop(stream_count_raw, stream_count_max);
U32 all_stream_entries_off = 4;
U32 all_indices_off = all_stream_entries_off + stream_count__inner*size_of_stream_entry;
// set output buffer and count
stream_count = stream_count__inner;
streams = push_array(arena, String8, stream_count);
// iterate sizes and indices in lock step
U32 entry_cursor = all_stream_entries_off;
U32 index_cursor = all_indices_off;
String8 *stream_ptr = streams;
for (U32 i = 0; i < stream_count; i += 1){
// read stream size
U32 stream_size_raw = *(U32*)(directory_buf + entry_cursor);
if (stream_size_raw == 0xffffffff){
stream_size_raw = 0;
}
// compute block count
U32 stream_block_count_raw = CeilIntegerDiv(stream_size_raw, block_size);
U32 stream_block_count_max = (directory_size - index_cursor)/index_size;;
U32 stream_block_count = ClampTop(stream_block_count_raw, stream_block_count_max);
U32 stream_size = ClampTop(stream_size_raw, stream_block_count*block_size);
// copy stream data
U8 *stream_buf = push_array(arena, U8, stream_size);
stream_ptr->str = stream_buf;
stream_ptr->size = stream_size;
U32 sub_index_cursor = index_cursor;
U8 *stream_out_ptr = stream_buf;
for (U32 i = 0; i < stream_block_count; i += 1, sub_index_cursor += index_size){
// read index
U32 stream_block_index = 0;
if (index_size == 4){
stream_block_index = *(U32*)(directory_buf + sub_index_cursor);
}
else{
stream_block_index = *(U16*)(directory_buf + sub_index_cursor);
}
if (stream_block_index >= whole_file_block_count){
got_streams = 0;
goto parse_streams_done;
}
U64 stream_block_off = (U64)(stream_block_index)*block_size;
U8 *stream_block_base = (msf_data.str + stream_block_off);
// clamp copy size by end of stream
U32 copy_size = 0;
{
U32 stream_pos = (U32)(stream_out_ptr - stream_buf);
U32 remaining_size = stream_size - stream_pos;
copy_size = ClampTop(block_size, remaining_size);
}
// copy block data
MemoryCopy(stream_out_ptr, stream_block_base, copy_size);
stream_out_ptr += copy_size;
}
// advance cursors
entry_cursor += size_of_stream_entry;
index_cursor = sub_index_cursor;
stream_ptr += 1;
}
parse_streams_done:;
}
if (got_streams){
result = push_array(arena, MSF_Parsed, 1);
result->streams = streams;
result->stream_count = stream_count;
result->block_size = block_size;
result->block_count = whole_file_block_count;
}
}
scratch_end(scratch);
ProfEnd();
return(result);
}
static String8
msf_data_from_stream(MSF_Parsed *msf, MSF_StreamNumber sn){
String8 result = {0};
if (sn < msf->stream_count){
result = msf->streams[sn];
}
return(result);
}
+60
View File
@@ -0,0 +1,60 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_MSF_H
#define RADDBG_MSF_H
////////////////////////////////
//~ MSF Format Types
#define MSF_INVALID_STREAM_NUMBER 0xFFFF
typedef U16 MSF_StreamNumber;
static char msf_msf20_magic[] = "Microsoft C/C++ program database 2.00\r\n\x1aJG\0\0";
static char msf_msf70_magic[] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0";
#define MSF_MSF20_MAGIC_SIZE 44
#define MSF_MSF70_MAGIC_SIZE 32
#define MSF_MAX_MAGIC_SIZE 44
typedef struct MSF_Header20{
U32 block_size;
U16 free_block_map_block;
U16 block_count;
U32 directory_size;
U32 unknown;
U16 directory_map;
} MSF_Header20;
typedef struct MSF_Header70{
U32 block_size;
U32 free_block_map_block;
U32 block_count;
U32 directory_size;
U32 unknown;
U32 directory_super_map;
} MSF_Header70;
// magic(20) + header(20) = 44 + 20 = 64
// magic(70) + header(70) = 32 + 24 = 56
#define MSF_MIN_SIZE 64
////////////////////////////////
//~ MSF Parser Helper Types
typedef struct MSF_Parsed{
String8 *streams;
U64 stream_count;
U64 block_size;
U64 block_count;
} MSF_Parsed;
////////////////////////////////
//~ MSF Parser Function
static MSF_Parsed* msf_parsed_from_data(Arena *arena, String8 msf_data);
static String8 msf_data_from_stream(MSF_Parsed *msf, MSF_StreamNumber sn);
#endif //RADDBG_MSF_H
+908
View File
@@ -0,0 +1,908 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ PDB Parser Functions
static PDB_Info*
pdb_info_from_data(Arena *arena, String8 data){
ProfBegin("pdb_info_from_data");
// get header
PDB_InfoHeader *header = 0;
if (data.size >= sizeof(*header)){
header = (PDB_InfoHeader*)data.str;
}
PDB_Info *result = 0;
if (header != 0){
// read guid
COFF_Guid *auth_guid = 0;
U32 after_auth_guid_off = sizeof(*header);
switch (header->version){
case PDB_Version_VC70_DEP:
case PDB_Version_VC70:
case PDB_Version_VC80:
case PDB_Version_VC110:
case PDB_Version_VC140:
{
auth_guid = (COFF_Guid*)(data.str + after_auth_guid_off);
after_auth_guid_off = sizeof(*header) + sizeof(*auth_guid);
}break;
default:
{}break;
}
if (header->version != 0){
// table layout: names
U32 names_len_off = after_auth_guid_off;
U32 names_len = 0;
if (names_len_off + 4 <= data.size){
names_len = *(U32*)(data.str + names_len_off);
}
U32 names_base_off = names_len_off + 4;
U32 names_base_opl = names_base_off + names_len;
// table layout: hash table
U32 hash_table_count_off = names_base_opl;
U32 hash_table_max_off = hash_table_count_off + 4;
U32 hash_table_count = 0;
U32 hash_table_max = 0;
if (hash_table_max_off + 4 <= data.size){
hash_table_count = *(U32*)(data.str + hash_table_count_off);
hash_table_max = *(U32*)(data.str + hash_table_max_off);
}
// table layout: words
U32 num_present_words_off = hash_table_max_off + 4;
U32 num_present_words = 0;
if (hash_table_max_off + 4 <= data.size){
num_present_words = *(U32*)(data.str + num_present_words_off);
}
U32 present_words_array_off = num_present_words_off + 4;
U32 num_deleted_words_off = present_words_array_off + num_present_words*sizeof(U32);
U32 num_deleted_words = 0;
if (num_deleted_words_off + 4 <= data.size){
num_deleted_words = *(U32*)(data.str + num_deleted_words_off);
}
U32 deleted_words_array_off = num_deleted_words_off + 4;
// table layout: epilogue
U32 epilogue_base_off = deleted_words_array_off + num_deleted_words*sizeof(U32);
// read table
if (hash_table_count > 0 && epilogue_base_off <= data.size){
PDB_InfoNode *first = 0;
PDB_InfoNode *last = 0;
U32 record_off = epilogue_base_off;
for (U32 i = 0; i < hash_table_count; i += 1, record_off += 8){
U32 *record = (U32*)(data.str + record_off);
U32 relative_name_off = record[0];
MSF_StreamNumber sn = (MSF_StreamNumber)record[1];
U32 name_off = names_base_off + relative_name_off;
String8 name = str8_cstring_capped((char*)(data.str + name_off),
(char*)(data.str + names_base_opl));
// push info node
PDB_InfoNode *node = push_array(arena, PDB_InfoNode, 1);
SLLQueuePush(first, last, node);
node->string = name;
node->sn = sn;
}
result = push_array(arena, PDB_Info, 1);
result->first = first;
result->last = last;
result->auth_guid = *auth_guid;
}
}
}
ProfEnd();
return(result);
}
static PDB_NamedStreamTable*
pdb_named_stream_table_from_info(Arena *arena, PDB_Info *info){
ProfBegin("pdb_named_stream_table_from_info");
// mapping "NamedStream" indexes to strings
struct StreamNameIndexPair{
PDB_NamedStream index;
String8 name;
};
struct StreamNameIndexPair pairs[] = {
{PDB_NamedStream_HEADER_BLOCK, str8_lit("/src/headerblock")},
{PDB_NamedStream_STRTABLE , str8_lit("/names")},
{PDB_NamedStream_LINK_INFO , str8_lit("/LinkInfo")},
};
// build baked table
PDB_NamedStreamTable *result = push_array(arena, PDB_NamedStreamTable, 1);
struct StreamNameIndexPair *p = pairs;
for (U64 i = 0; i < ArrayCount(pairs); i += 1, p += 1){
String8 name = p->name;
// get info node with this name
PDB_InfoNode *match = 0;
for (PDB_InfoNode *node = info->first;
node != 0;
node = node->next){
if (str8_match(name, node->string, 0)){
match = node;
break;
}
}
// if match found save stream number
if (match != 0){
result->sn[p->index] = match->sn;
}
else{
result->sn[p->index] = 0xFFFF;
}
}
ProfEnd();
return(result);
}
static PDB_Strtbl*
pdb_strtbl_from_data(Arena *arena, String8 data){
ProfBegin("pdb_strtbl_from_data");
// get header
PDB_StrtblHeader *header = 0;
if (sizeof(*header) <= data.size){
header = (PDB_StrtblHeader*)data.str;
}
PDB_Strtbl *result = 0;
if (header != 0 && header->magic == PDB_StrtblHeader_MAGIC && header->version == 1){
U32 strblock_size_off = sizeof(*header);
U32 strblock_size = 0;
if (strblock_size_off + 4 <= data.size){
strblock_size = *(U32*)(data.str + strblock_size_off);
}
U32 strblock_off = strblock_size_off + 4;
U32 bucket_count_off = strblock_off + strblock_size;
U32 bucket_count = 0;
if (bucket_count_off + 4 <= data.size){
bucket_count = *(U32*)(data.str + bucket_count_off);
}
U32 bucket_array_off = bucket_count_off + 4;
U32 bucket_array_size = bucket_count*sizeof(PDB_StringIndex);
if (bucket_array_off + bucket_array_size <= data.size){
result = push_array(arena, PDB_Strtbl, 1);
result->data = data;
result->bucket_count = bucket_count;
result->strblock_min = strblock_off;
result->strblock_max = strblock_off + strblock_size;
result->buckets_min = bucket_array_off;
result->buckets_min = bucket_array_off + bucket_array_size;
}
}
ProfEnd();
return(result);
}
static PDB_DbiParsed*
pdb_dbi_from_data(Arena *arena, String8 data){
ProfBegin("pdb_dbi_from_data");
// get header
PDB_DbiHeader *header = 0;
if (sizeof(*header) <= data.size){
header = (PDB_DbiHeader*)data.str;
}
PDB_DbiParsed *result = 0;
if (header != 0 && header->sig == PDB_DbiHeaderSignature_V1){
// extract range sizes
U64 range_size[PDB_DbiRange_COUNT];
range_size[PDB_DbiRange_ModuleInfo] = header->module_info_size;
range_size[PDB_DbiRange_SecCon] = header->sec_con_size;
range_size[PDB_DbiRange_SecMap] = header->sec_map_size;
range_size[PDB_DbiRange_FileInfo] = header->file_info_size;
range_size[PDB_DbiRange_TSM] = header->tsm_size;
range_size[PDB_DbiRange_EcInfo] = header->ec_info_size;
range_size[PDB_DbiRange_DbgHeader] = header->dbg_header_size;
// fill result
result = push_array(arena, PDB_DbiParsed, 1);
result->data = data;
result->arch = header->machine;
result->gsi_sn = header->gsi_sn;
result->psi_sn = header->psi_sn;
result->sym_sn = header->sym_sn;
// fill result's range offsets
{
U64 cursor = sizeof(*header);
for (U64 i = 0; i < (U64)(PDB_DbiRange_COUNT); i += 1){
result->range_off[i] = cursor;
cursor += range_size[i];
cursor = ClampTop(cursor, data.size);
}
result->range_off[PDB_DbiRange_COUNT] = cursor;
}
// fill result's debug streams
U64 dbg_streams_min = result->range_off[PDB_DbiRange_DbgHeader];
U64 dbg_streams_max = result->range_off[PDB_DbiRange_DbgHeader + 1];
U64 dbg_streams_size_raw = dbg_streams_max - dbg_streams_min;
U64 dbg_streams_size = ClampTop(dbg_streams_size_raw, sizeof(result->dbg_streams));
MemoryCopy(result->dbg_streams, data.str + dbg_streams_min, dbg_streams_size);
if (dbg_streams_size < sizeof(result->dbg_streams)){
U64 filled_count = dbg_streams_size/sizeof(MSF_StreamNumber);
MemorySet(result->dbg_streams + filled_count, 0xff,
(ArrayCount(result->dbg_streams) - filled_count)*sizeof(MSF_StreamNumber));
}
}
ProfEnd();
return(result);
}
static PDB_TpiParsed*
pdb_tpi_from_data(Arena *arena, String8 data){
ProfBegin("pdb_tpi_from_data");
// get header
PDB_TpiHeader *header = 0;
if (sizeof(*header) <= data.size){
header = (PDB_TpiHeader*)data.str;
}
PDB_TpiParsed *result = 0;
if (header != 0 && header->version == PDB_TpiVersion_IMPV80){
U64 leaf_first_raw = header->header_size;
U64 leaf_first = ClampTop(leaf_first_raw, data.size);
U64 leaf_opl_raw = leaf_first + header->leaf_data_size;
U64 leaf_opl = ClampTop(leaf_opl_raw, data.size);
result = push_array(arena, PDB_TpiParsed, 1);
result->data = data;
result->leaf_first = leaf_first;
result->leaf_opl = leaf_opl;
result->itype_first = header->ti_lo;
result->itype_opl = header->ti_hi;
result->hash_sn = header->hash_sn;
result->hash_sn_aux = header->hash_sn_aux;
result->hash_key_size = header->hash_key_size;
result->hash_bucket_count = header->hash_bucket_count;
result->hash_vals_off = header->hash_vals_off;
result->hash_vals_size = header->hash_vals_size;
result->itype_off = header->itype_off;
result->itype_size = header->itype_size;
result->hash_adj_off = header->hash_adj_off;
result->hash_adj_size = header->hash_adj_size;
}
ProfEnd();
return(result);
}
static PDB_TpiHashParsed*
pdb_tpi_hash_from_data(Arena *arena, PDB_TpiParsed *tpi, String8 data, String8 aux_data){
ProfBegin("pdb_tpi_hash_from_data");
PDB_TpiHashParsed *result = 0;
U32 stride = tpi->hash_key_size;
U32 bucket_count = tpi->hash_bucket_count;
if (1 <= stride && stride <= 8 && bucket_count > 0){
// allocate buckets
PDB_TpiHashBlock **buckets = push_array(arena, PDB_TpiHashBlock*, bucket_count);
// extract "hash" array
U8 *hashes = data.str + tpi->hash_vals_off;
U8 *hash_opl = hashes + tpi->hash_vals_size;
// for each index in the array...
CV_TypeId itype = tpi->itype_first;
U8 *hash_cursor = hashes;
for (;hash_cursor + stride <= hash_opl;){
// read index
U64 bucket_idx = 0;
MemoryCopy(&bucket_idx, hash_cursor, stride);
// save to map
if (bucket_idx < bucket_count){
PDB_TpiHashBlock *block = buckets[bucket_idx];
if (block == 0 || block->local_count == ArrayCount(block->itypes)){
block = push_array(arena, PDB_TpiHashBlock, 1);
SLLStackPush(buckets[bucket_idx], block);
}
block->itypes[block->local_count] = itype;
block->local_count += 1;
}
// advance cursor
hash_cursor += stride;
itype += 1;
}
// fill result
result = push_array(arena, PDB_TpiHashParsed, 1);
result->data = data;
result->aux_data = aux_data;
result->buckets = buckets;
result->bucket_count = bucket_count;
if (IsPow2OrZero(bucket_count)){
result->bucket_mask = bucket_count - 1;
}
}
ProfEnd();
return(result);
}
static PDB_GsiParsed*
pdb_gsi_from_data(Arena *arena, String8 data){
ProfBegin("pdb_gsi_from_data");
// get header
PDB_GsiHeader *header = 0;
if (sizeof(*header) <= data.size){
header = (PDB_GsiHeader*)data.str;
}
PDB_GsiParsed *result = 0;
if (header != 0 && header->signature == PDB_GsiSignature_Basic &&
header->version == PDB_GsiVersion_V70 && header->num_buckets != 0){
Temp scratch = scratch_begin(&arena, 1);
// hash offset
U32 hash_record_array_off = sizeof(*header);
// bucket count
U32 slot_count = 4097;
// array offsets
U32 bitmask_u32_count = CeilIntegerDiv(slot_count, 32);
U32 bitmask_byte_size = bitmask_u32_count*4;
U32 bitmask_off = hash_record_array_off + header->hr_len;
U32 offsets_off = bitmask_off + bitmask_byte_size;
// get bitmask & packed offset arrays
U8 *bitmasks = 0;
U8 *packed_offsets = 0;
if (bitmask_off + bitmask_byte_size <= data.size){
bitmasks = (data.str + bitmask_off);
packed_offsets = (data.str + offsets_off);
}
U32 packed_offset_count = (data.size - offsets_off)/4;
// unpack
U32 *unpacked_offsets = 0;
if (packed_offsets != 0){
unpacked_offsets = push_array(scratch.arena, U32, slot_count);
U32 *bitmask_ptr = (U32*)bitmasks;
U32 *bitmask_opl = bitmask_ptr + bitmask_u32_count;
U32 *src_ptr = (U32*)packed_offsets;
U32 *src_opl = src_ptr + packed_offset_count;
U32 *dst_ptr = unpacked_offsets;
U32 *dst_opl = dst_ptr + slot_count;
for (; bitmask_ptr < bitmask_opl && src_ptr < src_opl; bitmask_ptr += 1){
U32 bits = *bitmask_ptr;
U32 src_max = (U32)(src_opl - src_ptr);
U32 dst_max = (U32)(dst_opl - dst_ptr);
U32 k_max0 = ClampTop(32, dst_max);
U32 k_max = ClampTop(k_max0, src_max);
for (U32 k = 0; k < k_max; k += 1){
if ((bits & 1) == 1){
*dst_ptr = *src_ptr;
src_ptr += 1;
}
else{
*dst_ptr = 0xFFFFFFFF;
}
dst_ptr += 1;
bits >>= 1;
}
}
for (; dst_ptr < dst_opl; dst_ptr += 1){
*dst_ptr = 0xFFFFFFFF;
}
}
// construct table
B32 bad_table = 0;
if (unpacked_offsets != 0){
result = push_array(arena, PDB_GsiParsed, 1);
// hash records
PDB_GsiHashRecord *hash_records = (PDB_GsiHashRecord*)(data.str + hash_record_array_off);
U32 hash_record_count = header->hr_len/sizeof(PDB_GsiHashRecord);
// * We unpack hash records into the the table by scanning backwards through the
// * hash records. Neighboring values in unpacked_offsets *sort of* form counts, but we
// * have to skip the max-U32s (sloppy PDB nonsense).
// * PDBs put one extra slot at the beginning of the encoded buckets that is mean
// * to be padding for modifying the buffer in place. After decoding there are 4096 buckets,
// * in the encoded buckets there are 4097. We are meant to drop the first one.
// build table
PDB_GsiHashRecord *hash_record_ptr = hash_records + hash_record_count - 1;
U32 prev_n = hash_record_count;
for (U32 i = slot_count; i > 1;){
i -= 1;
if (unpacked_offsets[i] != 0xFFFFFFFF){
// determine hash record range to use
// * The "12" here is the result of some really sloppy PDB magic.
U32 n = unpacked_offsets[i]/12;
if (n > prev_n){
bad_table = 1;
break;
}
U32 num_steps = prev_n - n;
// fill this bucket
arena_push_align(arena, 4);
U32 *bucket_offs = push_array_no_zero(arena, U32, num_steps);
for (U32 j = num_steps; j > 0;){
j -= 1;
// * The "- 1" is more sloppy PDB magic.
bucket_offs[j] = hash_record_ptr->symbol_off - 1;
hash_record_ptr -= 1;
}
PDB_GsiBucket *bucket = &result->buckets[i - 1];
bucket->count = num_steps;
bucket->offs = bucket_offs;
// update prev_n
prev_n = n;
}
}
}
scratch_end(scratch);
}
ProfEnd();
return(result);
}
static PDB_CoffSectionArray*
pdb_coff_section_array_from_data(Arena *arena, String8 data){
U64 count = data.size/sizeof(COFF_SectionHeader);
PDB_CoffSectionArray *result = push_array(arena, PDB_CoffSectionArray, 1);
result->sections = (COFF_SectionHeader*)data.str;
result->count = count;
return(result);
}
static PDB_CompUnitArray*
pdb_comp_unit_array_from_data(Arena *arena, String8 data){
PDB_CompUnitNode *first = 0;
PDB_CompUnitNode *last = 0;
U64 count = 0;
U64 cursor = 0;
for (;cursor + sizeof(PDB_DbiCompUnitHeader) <= data.size;){
// get header
PDB_DbiCompUnitHeader *header = (PDB_DbiCompUnitHeader*)(data.str + cursor);
// get names
U64 name_off = cursor + sizeof(*header);
String8 name = str8_cstring_capped((char *)(data.str + name_off), (char *)(data.str + data.size));
U64 name2_off = name_off + name.size + 1;
String8 name2 = str8_cstring_capped((char *)(data.str + name2_off), (char *)(data.str + data.size));
U64 after_name2_off = name2_off + name2.size + 1;
// save mod info
PDB_CompUnitNode *node = push_array_no_zero(arena, PDB_CompUnitNode, 1);
SLLQueuePush(first, last, node);
count += 1;
node->unit.sn = header->sn;
node->unit.obj_name = name;
node->unit.group_name = name2;
// fill range offsets
U32 *range_buf = node->unit.range_off;
{
// fill the buffer with size of each range
range_buf[PDB_DbiCompUnitRange_Symbols] = header->symbols_size;
range_buf[PDB_DbiCompUnitRange_C11] = header->c11_lines_size;
range_buf[PDB_DbiCompUnitRange_C13] = header->c13_lines_size;
Assert(PDB_DbiCompUnitRange_C13 + 1 == PDB_DbiCompUnitRange_COUNT);
// in-place sizes -> offs conversion
U64 i = 0;
U32 range_cursor = 0;
for (; i < (U64)(PDB_DbiCompUnitRange_COUNT); i += 1){
U64 adv = range_buf[i];
range_buf[i] = range_cursor;
range_cursor += adv;
}
range_buf[i] = range_cursor;
// skip 4 byte signature in symbols range
if (range_buf[1] >= 4){
range_buf[0] += 4;
}
}
// update cursor
cursor = AlignPow2(after_name2_off, 4);
}
// fill result
PDB_CompUnit **units = push_array_no_zero(arena, PDB_CompUnit*, count);
{
U64 idx = 0;
for (PDB_CompUnitNode *node = first;
node != 0;
node = node->next, idx += 1){
units[idx] = &node->unit;
}
}
PDB_CompUnitArray *result = push_array(arena, PDB_CompUnitArray, 1);
result->units = units;
result->count = count;
return(result);
}
static PDB_CompUnitContributionArray*
pdb_comp_unit_contribution_array_from_data(Arena *arena, String8 data,
PDB_CoffSectionArray *sections){
PDB_CompUnitContribution *contributions = 0;
U64 count = 0;
if (data.size >= sizeof(PDB_DbiSectionContribVersion)){
PDB_DbiSectionContribVersion *version = (PDB_DbiSectionContribVersion*)data.str;
// determine array layout from version
U32 item_size = 0;
U32 array_off = 0;
switch (*version){
default:
{
// TODO(allen): do we have a test case for this?
item_size = sizeof(PDB_DbiSectionContrib40);
}break;
case PDB_DbiSectionContribVersion_1:
{
item_size = sizeof(PDB_DbiSectionContrib);
array_off = sizeof(*version);
}break;
case PDB_DbiSectionContribVersion_2:
{
item_size = sizeof(PDB_DbiSectionContrib2);
array_off = sizeof(*version);
}break;
}
// allocate ranges
U64 max_count = (data.size - array_off)/item_size;
contributions = push_array_no_zero(arena, PDB_CompUnitContribution, max_count);
// binary section info
U64 section_count = sections->count;
COFF_SectionHeader* section_headers = sections->sections;
// fill array
PDB_CompUnitContribution *contribution_ptr = contributions;
U64 cursor = array_off;
for (; cursor + item_size <= data.size; cursor += item_size){
PDB_DbiSectionContrib40 *sc = (PDB_DbiSectionContrib40*)(data.str + cursor);
if (sc->size > 0 && 1 <= sc->sec && sc->sec <= section_count){
U64 voff = section_headers[sc->sec - 1].voff + sc->sec_off;
contribution_ptr->mod = sc->mod;
contribution_ptr->voff_first = voff;
contribution_ptr->voff_opl = voff + sc->size;
contribution_ptr += 1;
}
}
count = (U64)(contribution_ptr - contributions);
}
// fill result
PDB_CompUnitContributionArray *result = push_array(arena, PDB_CompUnitContributionArray, 1);
result->contributions = contributions;
result->count = count;
return(result);
}
////////////////////////////////
//~ PDB Definition Functions
static U32
pdb_string_hash1(String8 string){
U32 result = 0;
U8 *ptr = string.str;
U8 *opl = ptr + (string.size&(~3));
for (; ptr < opl; ptr += 4){ result ^= *(U32*)ptr; }
if ((string.size&2) != 0){ result ^= *(U16*)ptr; ptr += 2; }
if ((string.size&1) != 0){ result ^= *ptr; }
result |= 0x20202020;
result ^= (result >> 11);
result ^= (result >> 16);
return(result);
}
////////////////////////////////
//~ PDB Dbi Functions
static String8
pdb_data_from_dbi_range(PDB_DbiParsed *dbi, PDB_DbiRange range){
String8 result = {0};
if (range < PDB_DbiRange_COUNT){
U64 first = dbi->range_off[range];
U64 opl = dbi->range_off[range + 1];
result.str = dbi->data.str + first;
result.size = opl - first;
}
return(result);
}
static String8
pdb_data_from_unit_range(MSF_Parsed *msf, PDB_CompUnit *unit, PDB_DbiCompUnitRange range){
String8 result = {0};
if (range < PDB_DbiCompUnitRange_COUNT){
String8 full_stream_data = msf_data_from_stream(msf, unit->sn);
U64 first_raw = unit->range_off[range];
U64 opl_raw = unit->range_off[range + 1];
U64 opl = ClampTop(opl_raw, full_stream_data.size);
U64 first = ClampTop(first_raw, opl);
result.str = full_stream_data.str + first;
result.size = opl - first;
}
return(result);
}
////////////////////////////////
//~ PDB Tpi Functions
static String8
pdb_leaf_data_from_tpi(PDB_TpiParsed *tpi){
String8 data = tpi->data;
U8 *first = data.str + tpi->leaf_first;
U8 *opl = data.str + tpi->leaf_opl;
String8 result = str8_range(first, opl);
return(result);
}
static CV_TypeIdArray
pdb_tpi_itypes_from_name(Arena *arena, PDB_TpiHashParsed *tpi_hash, CV_LeafParsed *leaf,
String8 name, B32 compare_unique_name, U32 output_cap){
ProfBegin("pdb_tpi_itypes_from_name");
U32 hash = pdb_string_hash1(name);
U32 bucket_idx = ((tpi_hash->bucket_mask != 0) ?
hash&tpi_hash->bucket_mask :
hash%tpi_hash->bucket_count);
CV_TypeId itype_first = leaf->itype_first;
CV_TypeId itype_opl = leaf->itype_opl;
String8 data = leaf->data;
Temp scratch = scratch_begin(&arena, 1);
struct Chain{
struct Chain *next;
CV_TypeId itype;
};
struct Chain *first = 0;
struct Chain *last = 0;
U32 count = 0;
for (PDB_TpiHashBlock *block = tpi_hash->buckets[bucket_idx];
block != 0;
block = block->next){
U32 local_count = block->local_count;
CV_TypeId *itype_ptr = block->itypes;
for (U32 i = 0; i < local_count; i += 1, itype_ptr += 1){
String8 extracted_name = {0};
CV_TypeId itype = *itype_ptr;
if (itype_first <= itype && itype < itype_opl){
CV_RecRange *range = &leaf->leaf_ranges.ranges[itype - leaf->itype_first];
if (range->off + range->hdr.size <= data.size){
U8 *first = data.str + range->off + 2;
U64 cap = range->hdr.size - 2;
switch (range->hdr.kind){
default:break;
case CV_LeafKind_CLASS:
case CV_LeafKind_STRUCTURE:
{
if (sizeof(CV_LeafStruct) <= cap){
CV_LeafStruct *lf_struct = (CV_LeafStruct*)first;
if (!(lf_struct->props & CV_TypeProp_FwdRef)){
// size
U8 *numeric_ptr = (U8*)(lf_struct + 1);
CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap);
// name
U8 *name_ptr = numeric_ptr + size.encoded_size;
String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap));
// unique name
if (compare_unique_name){
if (lf_struct->props & CV_TypeProp_HasUniqueName) {
U8 *unique_name_ptr = name_ptr + name.size + 1;
String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap));
extracted_name = unique_name;
}
}
else{
extracted_name = name;
}
}
}
}break;
case CV_LeafKind_UNION:
{
if (sizeof(CV_LeafUnion) <= cap){
CV_LeafUnion *lf_union = (CV_LeafUnion*)first;
if (!(lf_union->props & CV_TypeProp_FwdRef)){
// size
U8 *numeric_ptr = (U8*)(lf_union + 1);
CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap);
// name
U8 *name_ptr = numeric_ptr + size.encoded_size;
String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap));
// unique name
if (compare_unique_name){
if (lf_union->props & CV_TypeProp_HasUniqueName) {
U8 *unique_name_ptr = name_ptr + name.size + 1;
String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap));
extracted_name = unique_name;
}
}
else{
extracted_name = name;
}
}
}
}break;
case CV_LeafKind_ENUM:
{
if (sizeof(CV_LeafEnum) <= cap){
CV_LeafEnum *lf_enum = (CV_LeafEnum*)first;
if (!(lf_enum->props & CV_TypeProp_FwdRef)){
// name
U8 *name_ptr = (U8*)(lf_enum + 1);
String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap));
// unique name
if (compare_unique_name){
if (lf_enum->props & CV_TypeProp_HasUniqueName) {
U8 *unique_name_ptr = name_ptr + name.size + 1;
String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap));
extracted_name = unique_name;
}
}
else{
extracted_name = name;
}
}
}
}break;
}
}
}
if (str8_match(extracted_name, name, 0)){
struct Chain *chain = push_array(scratch.arena, struct Chain, 1);
SLLQueuePush(first, last, chain);
count += 1;
chain->itype = itype;
if (count == output_cap){
goto dblbreak;
}
}
}
}
dblbreak:;
// assemble result
CV_TypeId *itypes = push_array(arena, CV_TypeId, count);
{
CV_TypeId *itype_ptr = itypes;
for (struct Chain *node = first;
node != 0;
node = node->next, itype_ptr += 1){
*itype_ptr = node->itype;
}
}
CV_TypeIdArray result = {0};
result.itypes = itypes;
result.count = count;
scratch_end(scratch);
ProfEnd();
return(result);
}
static CV_TypeId
pdb_tpi_first_itype_from_name(PDB_TpiHashParsed *tpi_hash, CV_LeafParsed *tpi_leaf,
String8 name, B32 compare_unique_name){
ProfBegin("pdb_tpi_first_itype_from_name");
Temp scratch = scratch_begin(0, 0);
CV_TypeIdArray array = pdb_tpi_itypes_from_name(scratch.arena, tpi_hash, tpi_leaf,
name, compare_unique_name, 1);
CV_TypeId result = 0;
if (array.count > 0){
result = array.itypes[0];
}
scratch_end(scratch);
ProfEnd();
return(result);
}
////////////////////////////////
//~ PDB Strtbl Functions
static String8
pdb_strtbl_string_from_off(PDB_Strtbl *strtbl, U32 off){
U32 strblock_max = strtbl->strblock_max;
U32 full_off_raw = strtbl->strblock_min + off;
U32 full_off = ClampTop(full_off_raw, strblock_max);
String8 result = str8_cstring_capped((char*)(strtbl->data.str + full_off),
(char*)(strtbl->data.str + strblock_max));
return(result);
}
static String8
pdb_strtbl_string_from_index(PDB_Strtbl *strtbl, PDB_StringIndex idx){
String8 result = {0};
if (idx < strtbl->bucket_count){
U32 off = *(U32*)(strtbl->data.str + strtbl->buckets_min + idx*4);
result = pdb_strtbl_string_from_off(strtbl, off);
}
return(result);
}
+461
View File
@@ -0,0 +1,461 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_PDB_H
#define RADDBG_PDB_H
// https://github.com/microsoft/microsoft-pdb/tree/master/PDB
////////////////////////////////
//~ PDB Format Types
typedef U32 PDB_Version;
enum{
PDB_Version_VC2 = 19941610,
PDB_Version_VC4 = 19950623,
PDB_Version_VC41 = 19950814,
PDB_Version_VC50 = 19960307,
PDB_Version_VC98 = 19970604,
PDB_Version_VC70_DEP = 19990604,
PDB_Version_VC70 = 20000404,
PDB_Version_VC80 = 20030901,
PDB_Version_VC110 = 20091201,
PDB_Version_VC140 = 20140508
};
typedef U16 PDB_ModIndex;
typedef U32 PDB_StringIndex;
typedef enum PDB_FixedStream{
PDB_FixedStream_PdbInfo = 1,
PDB_FixedStream_Tpi = 2,
PDB_FixedStream_Dbi = 3,
PDB_FixedStream_Ipi = 4
} PDB_FixedStream;
typedef enum PDB_NamedStream{
PDB_NamedStream_HEADER_BLOCK,
PDB_NamedStream_STRTABLE,
PDB_NamedStream_LINK_INFO,
PDB_NamedStream_COUNT
} PDB_NamedStream;
typedef struct PDB_InfoHeader{
PDB_Version version;
U32 time;
U32 age;
} PDB_InfoHeader;
enum{
PDB_StrtblHeader_MAGIC = 0xEFFEEFFE
};
typedef struct PDB_StrtblHeader{
U32 magic;
U32 version;
} PDB_StrtblHeader;
////////////////////////////////
//~ PDB Format DBI Types
typedef U32 PDB_DbiStream;
enum{
PDB_DbiStream_FPO,
PDB_DbiStream_EXCEPTION,
PDB_DbiStream_FIXUP,
PDB_DbiStream_OMAP_TO_SRC,
PDB_DbiStream_OMAP_FROM_SRC,
PDB_DbiStream_SECTION_HEADER,
PDB_DbiStream_TOKEN_RDI_MAP,
PDB_DbiStream_XDATA,
PDB_DbiStream_PDATA,
PDB_DbiStream_NEW_FPO,
PDB_DbiStream_SECTION_HEADER_ORIG,
PDB_DbiStream_COUNT
};
typedef U32 PDB_DbiHeaderSignature;
enum{
PDB_DbiHeaderSignature_V1 = 0xFFFFFFFF
};
typedef U32 PDB_DbiVersion;
enum{
PDB_DbiVersion_41 = 930803,
PDB_DbiVersion_50 = 19960307,
PDB_DbiVersion_60 = 19970606,
PDB_DbiVersion_70 = 19990903,
PDB_DbiVersion_110 = 20091201,
};
typedef U16 PDB_DbiBuildNumber;
#define PDB_DbiBuildNumberNewFormatFlag 0x8000
#define PDB_DbiBuildNumberMinor(bn) ((bn)&0xFF)
#define PDB_DbiBuildNumberMajor(bn) (((bn) >> 8)&0x7F)
#define PDB_DbiBuildNumberNewFormat(bn) (!!((bn)&PDB_DbiBuildNumberNewFormatFlag))
#define PDB_DbiBuildNumber(maj, min) \
(PDB_DbiBuildNumberNewFormatFlag | ((min)&0xFF) | (((maj)&0x7F) << 16))
typedef U16 PDB_DbiHeaderFlags;
enum{
PDB_DbiHeaderFlag_Incremental = 0x1,
PDB_DbiHeaderFlag_Stripped = 0x2,
PDB_DbiHeaderFlag_CTypes = 0x4
};
typedef struct PDB_DbiHeader{
PDB_DbiHeaderSignature sig;
PDB_DbiVersion version;
U32 age;
MSF_StreamNumber gsi_sn;
PDB_DbiBuildNumber build_number;
MSF_StreamNumber psi_sn;
U16 pdb_version;
MSF_StreamNumber sym_sn;
U16 pdb_version2;
U32 module_info_size;
U32 sec_con_size;
U32 sec_map_size;
U32 file_info_size;
U32 tsm_size;
U32 mfc_index;
U32 dbg_header_size;
U32 ec_info_size;
PDB_DbiHeaderFlags flags;
COFF_Arch machine;
U32 reserved;
} PDB_DbiHeader;
// (this is not "literally" defined by the format - but helpful to have)
typedef enum PDB_DbiRange{
PDB_DbiRange_ModuleInfo,
PDB_DbiRange_SecCon,
PDB_DbiRange_SecMap,
PDB_DbiRange_FileInfo,
PDB_DbiRange_TSM,
PDB_DbiRange_EcInfo,
PDB_DbiRange_DbgHeader,
PDB_DbiRange_COUNT
} PDB_DbiRange;
// "ModuleInfo" DBI range
typedef U32 PDB_DbiSectionContribVersion;
#define PDB_DbiSectionContribVersion_1 (0xeffe0000u + 19970605u)
#define PDB_DbiSectionContribVersion_2 (0xeffe0000u + 20140516u)
typedef struct PDB_DbiSectionContrib40{
CV_SectionIndex sec;
U32 sec_off;
U32 size;
U32 flags;
PDB_ModIndex mod;
} PDB_DbiSectionContrib40;
typedef struct PDB_DbiSectionContrib{
PDB_DbiSectionContrib40 base;
U32 data_crc;
U32 reloc_crc;
} PDB_DbiSectionContrib;
typedef struct PDB_DbiSectionContrib2{
PDB_DbiSectionContrib40 base;
U32 data_crc;
U32 reloc_crc;
U32 sec_coff;
} PDB_DbiSectionContrib2;
typedef struct PDB_DbiCompUnitHeader{
U32 unused;
PDB_DbiSectionContrib contribution;
U16 flags; // unknown
MSF_StreamNumber sn;
U32 symbols_size;
U32 c11_lines_size;
U32 c13_lines_size;
U16 num_contrib_files;
U16 unused2;
U32 file_names_offset;
PDB_StringIndex src_file;
PDB_StringIndex pdb_file;
// U8[] module_name (null terminated)
// U8[] obj_name (null terminated)
} PDB_DbiCompUnitHeader;
// (this is not "literally" defined by the format - but helpful to have)
typedef enum{
PDB_DbiCompUnitRange_Symbols,
PDB_DbiCompUnitRange_C11,
PDB_DbiCompUnitRange_C13,
PDB_DbiCompUnitRange_COUNT
} PDB_DbiCompUnitRange;
////////////////////////////////
//~ PDB Format TPI Types
typedef U32 PDB_TpiVersion;
enum{
PDB_TpiVersion_INTV_VC2 = 920924,
PDB_TpiVersion_IMPV40 = 19950410,
PDB_TpiVersion_IMPV41 = 19951122,
PDB_TpiVersion_IMPV50_INTERIM = 19960307,
PDB_TpiVersion_IMPV50 = 19961031,
PDB_TpiVersion_IMPV70 = 19990903,
PDB_TpiVersion_IMPV80 = 20040203,
};
typedef struct PDB_TpiHeader{
// (HDR)
PDB_TpiVersion version;
U32 header_size;
U32 ti_lo;
U32 ti_hi;
U32 leaf_data_size;
// (PdbTpiHash)
MSF_StreamNumber hash_sn;
MSF_StreamNumber hash_sn_aux;
U32 hash_key_size;
U32 hash_bucket_count;
U32 hash_vals_off;
U32 hash_vals_size;
U32 itype_off;
U32 itype_size;
U32 hash_adj_off;
U32 hash_adj_size;
} PDB_TpiHeader;
typedef struct PDB_TpiOffHint{
CV_TypeId itype;
U32 off;
} PDB_TpiOffHint;
////////////////////////////////
//~ PDB Format GSI Types
typedef U32 PDB_GsiSignature;
enum{
PDB_GsiSignature_Basic = 0xffffffff,
};
typedef U32 PDB_GsiVersion;
enum{
PDB_GsiVersion_V70 = 0xeffe0000 + 19990810,
};
typedef struct PDB_GsiHeader{
PDB_GsiSignature signature;
PDB_GsiVersion version;
U32 hr_len;
U32 num_buckets;
} PDB_GsiHeader;
typedef struct PDB_GsiHashRecord{
U32 symbol_off;
U32 cref;
} PDB_GsiHashRecord;
typedef struct PDB_PsiHeader{
U32 sym_hash_size;
U32 addr_map_size;
U32 thunk_count;
U32 thunk_size;
CV_SectionIndex isec_thunk_table;
U16 padding;
U32 sec_thunk_table_off;
U32 sec_count;
} PDB_PsiHeader;
////////////////////////////////
//~ PDB Parser Types
typedef struct PDB_InfoNode{
struct PDB_InfoNode *next;
String8 string;
MSF_StreamNumber sn;
} PDB_InfoNode;
typedef struct PDB_Info{
PDB_InfoNode *first;
PDB_InfoNode *last;
COFF_Guid auth_guid;
} PDB_Info;
typedef struct PDB_NamedStreamTable{
MSF_StreamNumber sn[PDB_NamedStream_COUNT];
} PDB_NamedStreamTable;
typedef struct PDB_Strtbl{
String8 data;
U32 bucket_count;
U32 strblock_min;
U32 strblock_max;
U32 buckets_min;
U32 buckets_max;
} PDB_Strtbl;
typedef struct PDB_DbiParsed{
String8 data;
COFF_Arch arch;
MSF_StreamNumber gsi_sn;
MSF_StreamNumber psi_sn;
MSF_StreamNumber sym_sn;
U64 range_off[(U64)(PDB_DbiRange_COUNT) + 1];
MSF_StreamNumber dbg_streams[PDB_DbiStream_COUNT];
} PDB_DbiParsed;
typedef struct PDB_TpiParsed{
String8 data;
// leaf info
U64 leaf_first;
U64 leaf_opl;
U32 itype_first;
U32 itype_opl;
// hash info
MSF_StreamNumber hash_sn;
MSF_StreamNumber hash_sn_aux;
U32 hash_key_size;
U32 hash_bucket_count;
U32 hash_vals_off;
U32 hash_vals_size;
U32 itype_off;
U32 itype_size;
U32 hash_adj_off;
U32 hash_adj_size;
} PDB_TpiParsed;
typedef struct PDB_TpiHashBlock{
struct PDB_TpiHashBlock *next;
U32 local_count;
CV_TypeId itypes[13]; // 13 = (64 - 12)/4
} PDB_TpiHashBlock;
typedef struct PDB_TpiHashParsed{
String8 data;
String8 aux_data;
PDB_TpiHashBlock **buckets;
U32 bucket_count;
U32 bucket_mask;
} PDB_TpiHashParsed;
typedef struct PDB_GsiBucket{
U32 *offs;
U64 count;
} PDB_GsiBucket;
typedef struct PDB_GsiParsed{
PDB_GsiBucket buckets[4096];
} PDB_GsiParsed;
typedef struct PDB_CompUnit{
MSF_StreamNumber sn;
U32 range_off[(U32)(PDB_DbiCompUnitRange_COUNT) + 1];
String8 obj_name;
String8 group_name;
} PDB_CompUnit;
typedef struct PDB_CoffSectionArray{
COFF_SectionHeader *sections;
U64 count;
} PDB_CoffSectionArray;
typedef struct PDB_CompUnitNode{
struct PDB_CompUnitNode *next;
PDB_CompUnit unit;
} PDB_CompUnitNode;
typedef struct PDB_CompUnitArray{
PDB_CompUnit **units;
U64 count;
} PDB_CompUnitArray;
typedef struct PDB_CompUnitContribution{
U32 mod;
U64 voff_first;
U64 voff_opl;
} PDB_CompUnitContribution;
typedef struct PDB_CompUnitContributionArray{
PDB_CompUnitContribution *contributions;
U64 count;
} PDB_CompUnitContributionArray;
////////////////////////////////
//~ PDB Parser Functions
static PDB_Info* pdb_info_from_data(Arena *arena, String8 pdb_info_data);
static PDB_NamedStreamTable*pdb_named_stream_table_from_info(Arena *arena, PDB_Info *info);
static PDB_Strtbl* pdb_strtbl_from_data(Arena *arena, String8 strtbl_data);
static PDB_DbiParsed* pdb_dbi_from_data(Arena *arena, String8 dbi_data);
static PDB_TpiParsed* pdb_tpi_from_data(Arena *arena, String8 tpi_data);
static PDB_TpiHashParsed* pdb_tpi_hash_from_data(Arena *arena,
PDB_TpiParsed *tpi,
String8 tpi_hash_data,
String8 tpi_hash_aux_data);
static PDB_GsiParsed* pdb_gsi_from_data(Arena *arena, String8 gsi_data);
static PDB_CoffSectionArray*pdb_coff_section_array_from_data(Arena *arena,
String8 section_data);
static PDB_CompUnitArray* pdb_comp_unit_array_from_data(Arena *arena,
String8 module_info_data);
static PDB_CompUnitContributionArray*
pdb_comp_unit_contribution_array_from_data(Arena *arena, String8 seccontrib_data,
PDB_CoffSectionArray *sections);
////////////////////////////////
//~ PDB Definition Functions
static U32 pdb_string_hash1(String8 string);
////////////////////////////////
//~ PDB Dbi Functions
static String8 pdb_data_from_dbi_range(PDB_DbiParsed *dbi, PDB_DbiRange range);
static String8 pdb_data_from_unit_range(MSF_Parsed *msf, PDB_CompUnit *unit,
PDB_DbiCompUnitRange range);
////////////////////////////////
//~ PDB Tpi Functions
static String8 pdb_leaf_data_from_tpi(PDB_TpiParsed *tpi);
static CV_TypeIdArray pdb_tpi_itypes_from_name(Arena *arena,
PDB_TpiHashParsed *tpi_hash,
CV_LeafParsed *tpi_leaf,
String8 name,
B32 compare_unique_name,
U32 output_cap);
static CV_TypeId pdb_tpi_first_itype_from_name(PDB_TpiHashParsed *tpi_hash,
CV_LeafParsed *tpi_leaf,
String8 name,
B32 compare_unique_name);
////////////////////////////////
//~ PDB Strtbl Functions
static String8 pdb_strtbl_string_from_off(PDB_Strtbl *strtbl, U32 off);
static String8 pdb_strtbl_string_from_index(PDB_Strtbl *strtbl,
PDB_StringIndex idx);
#endif //RADDBG_PDB_H
@@ -0,0 +1,26 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ PDB Stringize Functions
static void
pdb_stringize_tpi_hash(Arena *arena, String8List *out, PDB_TpiHashParsed *hash){
U32 bucket_count = hash->bucket_count;
str8_list_pushf(arena, out, "bucket_count=%u\n\n", bucket_count);
for (U32 i = 0; i < bucket_count; i += 1){
if (hash->buckets[i] != 0){
str8_list_pushf(arena, out, "bucket[%u]:\n", i);
for (PDB_TpiHashBlock *block = hash->buckets[i];
block != 0;
block = block->next){
U32 local_count = block->local_count;
CV_TypeId *itype_ptr = block->itypes;
for (U32 j = 0; j < local_count; j += 1, itype_ptr += 1){
str8_list_pushf(arena, out, " %u\n", *itype_ptr);
}
}
str8_list_push(arena, out, str8_lit("\n"));
}
}
}
@@ -0,0 +1,12 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBG_PDB_STRINGIZE_H
#define RADDBG_PDB_STRINGIZE_H
////////////////////////////////
//~ PDB Stringize Functions
static void pdb_stringize_tpi_hash(Arena *arena, String8List *out, PDB_TpiHashParsed *hash);
#endif //RADDBG_PDB_STRINGIZE_H