From b889765c1597d002cfe3c686ce732badfff5923d Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Tue, 14 Jan 2025 16:49:05 -0800 Subject: [PATCH] updated PE bin info to handle RDI entry, changed raddump to convert and load RDI from memory, added helper for parsing debug directory --- src/pe/pe.c | 285 +++++++++++++++++++++++++----------------- src/pe/pe.h | 47 ++++++- src/raddump/raddump.c | 264 ++++++++++++++++---------------------- 3 files changed, 321 insertions(+), 275 deletions(-) diff --git a/src/pe/pe.c b/src/pe/pe.c index 630ce5f7..352e775e 100644 --- a/src/pe/pe.c +++ b/src/pe/pe.c @@ -438,18 +438,18 @@ pe_bin_info_from_data(Arena *arena, String8 data) } // rjf: read coff header - U32 coff_header_off = dos_header.coff_file_offset + sizeof(pe_magic); - COFF_Header coff_header = {0}; + U32 coff_header_off = dos_header.coff_file_offset + sizeof(pe_magic); + COFF_Header coff_header = {0}; if(valid) { str8_deserial_read_struct(data, coff_header_off, &coff_header); } // rjf: range of optional extension header ("optional" for short) - U32 optional_size = coff_header.optional_header_size; - U64 after_coff_header_off = coff_header_off + sizeof(coff_header); - U64 after_optional_header_off = after_coff_header_off + optional_size; - Rng1U64 optional_range = {0}; + U32 optional_size = coff_header.optional_header_size; + U64 after_coff_header_off = coff_header_off + sizeof(coff_header); + U64 after_optional_header_off = after_coff_header_off + optional_size; + Rng1U64 optional_range = {0}; if(valid) { optional_range.min = ClampTop(after_coff_header_off, data.size); @@ -457,11 +457,11 @@ pe_bin_info_from_data(Arena *arena, String8 data) } // rjf: get sections - U64 sec_array_off = optional_range.max; - U64 sec_array_raw_opl = sec_array_off + coff_header.section_count*sizeof(COFF_SectionHeader); - U64 sec_array_opl = ClampTop(sec_array_raw_opl, data.size); - U64 clamped_sec_count = (sec_array_opl - sec_array_off)/sizeof(COFF_SectionHeader); - COFF_SectionHeader *sections = (COFF_SectionHeader*)(data.str + sec_array_off); + U64 sec_array_off = optional_range.max; + U64 sec_array_raw_opl = sec_array_off + coff_header.section_count*sizeof(COFF_SectionHeader); + U64 sec_array_opl = ClampTop(sec_array_raw_opl, data.size); + U64 clamped_sec_count = (sec_array_opl - sec_array_off)/sizeof(COFF_SectionHeader); + COFF_SectionHeader *sections = (COFF_SectionHeader*)(data.str + sec_array_off); // rjf: get symbols U64 symbol_array_off = coff_header.symbol_table_foff; @@ -471,13 +471,13 @@ pe_bin_info_from_data(Arena *arena, String8 data) U64 string_table_off = symbol_array_off + sizeof(COFF_Symbol16) * symbol_count; // rjf: read optional header - U16 optional_magic = 0; - U64 image_base = 0; - U64 entry_point = 0; - U32 data_dir_count = 0; - U64 virt_section_align = 0; - U64 file_section_align = 0; - Rng1U64 *data_dir_franges = 0; + U16 optional_magic = 0; + U64 image_base = 0; + U64 entry_point = 0; + U32 data_dir_count = 0; + U64 virt_section_align = 0; + U64 file_section_align = 0; + Rng1U64 *data_dir_franges = 0; if(valid && optional_size > 0) { // rjf: read magic number @@ -491,24 +491,36 @@ pe_bin_info_from_data(Arena *arena, String8 data) case PE_PE32_MAGIC: { PE_OptionalHeader32 pe_optional = {0}; - str8_deserial_read_struct(data, optional_range.min, &pe_optional); - image_base = pe_optional.image_base; - entry_point = pe_optional.entry_point_va; - virt_section_align = pe_optional.section_alignment; - file_section_align = pe_optional.file_alignment; - reported_data_dir_offset = sizeof(pe_optional); - reported_data_dir_count = pe_optional.data_dir_count; + if(str8_deserial_read_struct(data, optional_range.min, &pe_optional) == sizeof(pe_optional)) + { + image_base = pe_optional.image_base; + entry_point = pe_optional.entry_point_va; + virt_section_align = pe_optional.section_alignment; + file_section_align = pe_optional.file_alignment; + reported_data_dir_offset = sizeof(pe_optional); + reported_data_dir_count = pe_optional.data_dir_count; + } + else + { + Assert(!"unable to read PE Optional Header"); + } }break; case PE_PE32PLUS_MAGIC: { PE_OptionalHeader32Plus pe_optional = {0}; - str8_deserial_read_struct(data, optional_range.min, &pe_optional); - image_base = pe_optional.image_base; - entry_point = pe_optional.entry_point_va; - virt_section_align = pe_optional.section_alignment; - file_section_align = pe_optional.file_alignment; - reported_data_dir_offset = sizeof(pe_optional); - reported_data_dir_count = pe_optional.data_dir_count; + if(str8_deserial_read_struct(data, optional_range.min, &pe_optional) == sizeof(pe_optional)) + { + image_base = pe_optional.image_base; + entry_point = pe_optional.entry_point_va; + virt_section_align = pe_optional.section_alignment; + file_section_align = pe_optional.file_alignment; + reported_data_dir_offset = sizeof(pe_optional); + reported_data_dir_count = pe_optional.data_dir_count; + } + else + { + Assert(!"unable to read PE Optional Plus Header"); + } }break; } @@ -520,56 +532,16 @@ pe_bin_info_from_data(Arena *arena, String8 data) data_dir_franges = push_array(arena, Rng1U64, data_dir_count); for(U32 dir_idx = 0; dir_idx < data_dir_count; dir_idx += 1) { - U64 dir_offset = optional_range.min + reported_data_dir_offset + sizeof(PE_DataDirectory)*dir_idx; - PE_DataDirectory dir = {0}; - str8_deserial_read_struct(data, dir_offset, &dir); - U64 file_off = coff_foff_from_voff(sections, clamped_sec_count, dir.virt_off); - data_dir_franges[dir_idx] = r1u64(file_off, file_off+dir.virt_size); - } - } - - // rjf: read info about debug file - U32 dbg_time = 0; - U32 dbg_age = 0; - Guid dbg_guid = {0}; - U64 dbg_path_off = 0; - U64 dbg_path_size = 0; - if(valid && PE_DataDirectoryIndex_DEBUG < data_dir_count) - { - // rjf: read debug directory - PE_DebugDirectory dbg_data = {0}; - str8_deserial_read_struct(data, data_dir_franges[PE_DataDirectoryIndex_DEBUG].min, &dbg_data); - - // rjf: extract external file info from codeview header - if(dbg_data.type == PE_DebugDirectoryType_CODEVIEW) - { - U64 cv_offset = dbg_data.foff; - U32 cv_magic = 0; - str8_deserial_read_struct(data, cv_offset, &cv_magic); - switch(cv_magic) + U64 dir_offset = optional_range.min + reported_data_dir_offset + sizeof(PE_DataDirectory)*dir_idx; + PE_DataDirectory dir = {0}; + if(str8_deserial_read_struct(data, dir_offset, &dir) == sizeof(dir)) { - default:break; - case PE_CODEVIEW_PDB20_MAGIC: - { - PE_CvHeaderPDB20 cv = {0}; - str8_deserial_read_struct(data, cv_offset, &cv); - dbg_time = cv.time_stamp; - dbg_age = cv.age; - dbg_path_off = cv_offset + sizeof(cv); - }break; - case PE_CODEVIEW_PDB70_MAGIC: - { - PE_CvHeaderPDB70 cv = {0}; - str8_deserial_read_struct(data, cv_offset, &cv); - dbg_guid = cv.guid; - dbg_age = cv.age; - dbg_path_off = cv_offset + sizeof(cv); - }break; + U64 file_off = coff_foff_from_voff(sections, clamped_sec_count, dir.virt_off); + data_dir_franges[dir_idx] = r1u64(file_off, file_off+dir.virt_size); } - if(dbg_path_off > 0) + else { - U8 *dbg_path_cstring_base = data.str+dbg_path_off; - dbg_path_size = cstring8_length(dbg_path_cstring_base); + Assert(!"unable to read data directory"); } } } @@ -581,21 +553,32 @@ pe_bin_info_from_data(Arena *arena, String8 data) Rng1U64 tls_header_frng = data_dir_franges[PE_DataDirectoryIndex_TLS]; switch(coff_header.machine) { - default:{}break; + default:{ NotImplemented; }break; + case COFF_MachineType_UNKNOWN: break; case COFF_MachineType_X86: { PE_TLSHeader32 tls_header32 = {0}; - str8_deserial_read_struct(data, tls_header_frng.min, &tls_header32); - tls_header.raw_data_start = (U64)tls_header32.raw_data_start; - tls_header.raw_data_end = (U64)tls_header32.raw_data_end; - tls_header.index_address = (U64)tls_header32.index_address; - tls_header.callbacks_address = (U64)tls_header32.callbacks_address; - tls_header.zero_fill_size = (U64)tls_header32.zero_fill_size; - tls_header.characteristics = (U64)tls_header32.characteristics; + if(str8_deserial_read_struct(data, tls_header_frng.min, &tls_header32) == sizeof(tls_header32)) + { + tls_header.raw_data_start = (U64)tls_header32.raw_data_start; + tls_header.raw_data_end = (U64)tls_header32.raw_data_end; + tls_header.index_address = (U64)tls_header32.index_address; + tls_header.callbacks_address = (U64)tls_header32.callbacks_address; + tls_header.zero_fill_size = (U64)tls_header32.zero_fill_size; + tls_header.characteristics = (U64)tls_header32.characteristics; + } + else + { + Assert(!"unable to read TLS Header 32"); + } }break; case COFF_MachineType_X64: { - str8_deserial_read_struct(data, tls_header_frng.min, &tls_header); + if(str8_deserial_read_struct(data, tls_header_frng.min, &tls_header) != sizeof(tls_header)) + { + MemoryZeroStruct(&tls_header); + Assert(!"unable to read TLS Header 64"); + } }break; } } @@ -603,36 +586,110 @@ pe_bin_info_from_data(Arena *arena, String8 data) // rjf: fill info if(valid) { - info.image_base = image_base; - info.entry_point = entry_point; - info.is_pe32 = (optional_magic == PE_PE32_MAGIC); - info.virt_section_align = virt_section_align; - info.file_section_align = file_section_align; - info.section_array_off = sec_array_off; - info.section_count = clamped_sec_count; - info.symbol_array_off = symbol_array_off; - info.symbol_count = symbol_count; - info.string_table_off = string_table_off; - info.dbg_path_off = dbg_path_off; - info.dbg_path_size = dbg_path_size; - info.dbg_guid = dbg_guid; - info.dbg_age = dbg_age; - info.dbg_time = dbg_time; - info.data_dir_franges = data_dir_franges; - info.data_dir_count = data_dir_count; - switch(coff_header.machine) - { - default:{}break; - case COFF_MachineType_X86: {info.arch = Arch_x86;}break; - case COFF_MachineType_X64: {info.arch = Arch_x64;}break; - case COFF_MachineType_ARM: {info.arch = Arch_arm32;}break; - case COFF_MachineType_ARM64: {info.arch = Arch_arm64;}break; - } - MemoryCopyStruct(&info.tls_header, &tls_header); + info.image_base = image_base; + info.entry_point = entry_point; + info.is_pe32 = (optional_magic == PE_PE32_MAGIC); + info.virt_section_align = virt_section_align; + info.file_section_align = file_section_align; + info.section_array_off = sec_array_off; + info.section_count = clamped_sec_count; + info.symbol_array_off = symbol_array_off; + info.symbol_count = symbol_count; + info.string_table_off = string_table_off; + info.data_dir_franges = data_dir_franges; + info.data_dir_count = data_dir_count; + info.arch = arch_from_coff_machine(coff_header.machine); + info.tls_header = tls_header; } + return info; } +internal PE_DebugInfoList +pe_parse_debug_directory(Arena *arena, String8 raw_image, String8 raw_debug_dir) +{ + PE_DebugInfoList result = {0}; + + PE_DebugDirectory *debug_entry = str8_deserial_get_raw_ptr(raw_debug_dir, 0, sizeof(*debug_entry)); + PE_DebugDirectory *debug_entry_opl = debug_entry + raw_debug_dir.size/sizeof(*debug_entry_opl); + for (PE_DebugDirectory *entry = debug_entry; entry < debug_entry_opl; ++entry) { + switch (entry->type) { + default: { + PE_DebugInfoNode *n = push_array(arena, PE_DebugInfoNode, 1); + n->v.header = *entry; + n->v.u.raw_data = str8_substr(raw_image, rng_1u64(entry->foff, entry->foff + entry->size)); + + SLLQueuePush(result.first, result.last, n); + ++result.count; + } break; + case PE_DebugDirectoryType_CODEVIEW: { + U32 cv_magic = 0; + str8_deserial_read_struct(raw_image, entry->foff, &cv_magic); + + switch (cv_magic) { + case PE_CODEVIEW_PDB20_MAGIC: { + PE_CvHeaderPDB20 cv = {0}; + U64 cv_read_size = str8_deserial_read_struct(raw_image, entry->foff, &cv); + if (cv_read_size == sizeof(cv)) { + String8 path = {0}; + str8_deserial_read_cstr(raw_image, entry->foff+sizeof(cv), &path); + + PE_DebugInfoNode *n = push_array(arena, PE_DebugInfoNode, 1); + n->v.header = *entry; + n->v.u.codeview.pdb20.header = cv; + n->v.u.codeview.pdb20.path = path; + + SLLQueuePush(result.first, result.last, n); + ++result.count; + } else { + Assert(!"unable to read PE_CvHeaderPDB20"); + } + } break; + case PE_CODEVIEW_PDB70_MAGIC: { + PE_CvHeaderPDB70 cv = {0}; + U64 cv_read_size = str8_deserial_read_struct(raw_image, entry->foff, &cv); + if (cv_read_size == sizeof(cv)) { + String8 path = {0}; + str8_deserial_read_cstr(raw_image, entry->foff+sizeof(cv), &path); + + PE_DebugInfoNode *n = push_array(arena, PE_DebugInfoNode, 1); + n->v.header = *entry; + n->v.u.codeview.pdb70.header = cv; + n->v.u.codeview.pdb70.path = path; + + SLLQueuePush(result.first, result.last, n); + ++result.count; + } else { + Assert(!"unable to read PE_CvHeaderPDB70"); + } + } break; + case PE_CODEVIEW_RDI_MAGIC: { + PE_CvHeaderRDI cv = {0}; + U64 cv_read_size = str8_deserial_read_struct(raw_image, entry->foff, &cv); + if (cv_read_size == sizeof(cv)) { + String8 path = {0}; + str8_deserial_read_cstr(raw_image, entry->foff+sizeof(cv), &path); + + PE_DebugInfoNode *n = push_array(arena, PE_DebugInfoNode, 1); + n->v.header = *entry; + n->v.u.codeview.rdi.header = cv; + n->v.u.codeview.rdi.path = path; + + SLLQueuePush(result.first, result.last, n); + ++result.count; + } else { + Assert(!"unable to read PE_CvHeaderRDI"); + } + } break; + default: break; + } + } break; + } + } + + return result; +} + //////////////////////////////// //~ rjf: Helpers diff --git a/src/pe/pe.h b/src/pe/pe.h index 5df0890b..c6e40254 100644 --- a/src/pe/pe.h +++ b/src/pe/pe.h @@ -1003,17 +1003,53 @@ struct PE_BinInfo U64 symbol_array_off; U64 symbol_count; U64 string_table_off; - U64 dbg_path_off; - U64 dbg_path_size; - Guid dbg_guid; - U32 dbg_age; - U32 dbg_time; Arch arch; Rng1U64 *data_dir_franges; U32 data_dir_count; PE_TLSHeader64 tls_header; }; +typedef struct PE_DebugInfo +{ + PE_DebugDirectory header; + union + { + union + { + U32 magic; + struct + { + PE_CvHeaderPDB20 header; + String8 path; + } pdb20; + struct + { + PE_CvHeaderPDB70 header; + String8 path; + } pdb70; + struct + { + PE_CvHeaderRDI header; + String8 path; + } rdi; + } codeview; + String8 raw_data; + } u; +} PE_DebugInfo; + +typedef struct PE_DebugInfoNode +{ + struct PE_DebugInfoNode *next; + PE_DebugInfo v; +} PE_DebugInfoNode; + +typedef struct PE_DebugInfoList +{ + PE_DebugInfoNode *first; + PE_DebugInfoNode *last; + U64 count; +} PE_DebugInfoList; + //////////////////////////////// //~ rjf: Basic Enum Functions @@ -1038,6 +1074,7 @@ internal String8 pe_string_from_dll_characteristics(Arena *arena, PE_DllCharacte internal PE_BinInfo pe_bin_info_from_data(Arena *arena, String8 data); +internal PE_DebugInfoList pe_parse_debug_directory(Arena *arena, String8 raw_image, String8 raw_debug_dir); internal PE_ParsedStaticImportTable pe_static_imports_from_data(Arena *arena, B32 is_pe32, U64 section_count, COFF_SectionHeader *sections, String8 raw_data, Rng1U64 dir_file_range); internal PE_ParsedDelayImportTable pe_delay_imports_from_data(Arena *arena, B32 is_pe32, U64 section_count, COFF_SectionHeader *sections, String8 raw_data, Rng1U64 dir_file_range); internal PE_ParsedExportTable pe_exports_from_data(Arena *arena, U64 section_count, COFF_SectionHeader *sections, String8 raw_data, Rng1U64 dir_file_range, Rng1U64 dir_virt_range); diff --git a/src/raddump/raddump.c b/src/raddump/raddump.c index 189fcc48..9da15187 100644 --- a/src/raddump/raddump.c +++ b/src/raddump/raddump.c @@ -42,8 +42,8 @@ rd_stderr(char *fmt, ...) scratch_end(scratch); } -internal B32 -rd_invoke_rdi_converter(String8 exe_name, String8 exe_data, String8 pdb_path, String8 out_path) +internal String8 +rd_invoke_rdi_converter(Arena *arena, String8 exe_name, String8 exe_data, String8 pdb_path) { Temp scratch = scratch_begin(0,0); @@ -52,17 +52,17 @@ rd_invoke_rdi_converter(String8 exe_name, String8 exe_data, String8 pdb_path, St user2convert.input_pdb_data = os_data_from_file_path(scratch.arena, pdb_path); user2convert.input_exe_name = exe_name; user2convert.input_exe_data = exe_data; - user2convert.output_name = out_path; + user2convert.output_name = str8_zero(); user2convert.flags = P2R_ConvertFlag_All; - P2R_Convert2Bake *convert2bake = p2r_convert(scratch.arena, &user2convert); - P2R_Bake2Serialize *bake2srlz = p2r_bake(scratch.arena, convert2bake); - RDIM_SerializedSectionBundle bundle = rdim_serialized_section_bundle_from_bake_results(&bake2srlz->bake_results); - String8List rdi_blobs = rdim_file_blobs_from_section_bundle(scratch.arena, &bundle); - B32 is_rdi_write_ok = os_write_data_list_to_file_path(user2convert.output_name, rdi_blobs); + P2R_Convert2Bake *convert2bake = p2r_convert(scratch.arena, &user2convert); + P2R_Bake2Serialize *bake2srlz = p2r_bake(scratch.arena, convert2bake); + RDIM_SerializedSectionBundle bundle = rdim_serialized_section_bundle_from_bake_results(&bake2srlz->bake_results); + String8List rdi_blobs = rdim_file_blobs_from_section_bundle(scratch.arena, &bundle); + String8 raw_rdi = str8_list_join(arena, &rdi_blobs, 0); scratch_end(scratch); - return is_rdi_write_ok; + return raw_rdi; } internal RDI_Parsed * @@ -71,110 +71,72 @@ rd_rdi_from_pe(Arena *arena, String8 data_path, String8 raw_data) Temp scratch = scratch_begin(&arena, 1); RDI_Parsed *rdi = 0; - PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, raw_data); - // PDB 2.0 - COFF_TimeStamp pdb20_time_stamp = 0; - U32 pdb20_age = 0; - String8 pdb20_path = str8_zero(); - // PDB 7.0 - U32 pdb70_age = 0; - Guid pdb70_guid = {0}; - String8 pdb70_path = str8_zero(); - // RDI - String8 rdi_path = str8_zero(); - Guid rdi_guid = {0}; - if (PE_DataDirectoryIndex_DEBUG < pe.data_dir_count) { - String8 raw_debug_dir = str8_substr(raw_data, pe.data_dir_franges[PE_DataDirectoryIndex_DEBUG]); - PE_DebugDirectory *debug_entry = str8_deserial_get_raw_ptr(raw_debug_dir, 0, sizeof(*debug_entry)); - PE_DebugDirectory *debug_entry_opl = debug_entry + raw_debug_dir.size/sizeof(*debug_entry_opl); - for (PE_DebugDirectory *debug_entry_ptr = debug_entry; debug_entry_ptr < debug_entry_opl; ++debug_entry_ptr) { - if (debug_entry_ptr->type == PE_DebugDirectoryType_CODEVIEW) { - U32 cv_magic = 0; - str8_deserial_read_struct(raw_data, debug_entry_ptr->foff, &cv_magic); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, raw_data); + String8 raw_debug_dir = str8_substr(raw_data, pe.data_dir_franges[PE_DataDirectoryIndex_DEBUG]); + PE_DebugInfoList dbg_list = pe_parse_debug_directory(scratch.arena, raw_data, raw_debug_dir); - switch (cv_magic) { - case PE_CODEVIEW_PDB20_MAGIC: { - PE_CvHeaderPDB20 cv = {0}; - U64 cv_read_size = str8_deserial_read_struct(raw_data, debug_entry_ptr->foff, &cv); - if (cv_read_size == sizeof(cv)) { - pdb20_time_stamp = cv.time_stamp; - pdb20_age = cv.age; - str8_deserial_read_cstr(raw_data, debug_entry_ptr->foff+sizeof(cv), &pdb20_path); - } else { - rd_errorf("unable to read PE_CvHeaderPDB20"); - } - } break; - case PE_CODEVIEW_PDB70_MAGIC: { - PE_CvHeaderPDB70 cv = {0}; - U64 cv_read_size = str8_deserial_read_struct(raw_data, debug_entry_ptr->foff, &cv); - if (cv_read_size == sizeof(cv)) { - pdb70_age = cv.age; - pdb70_guid = cv.guid; - str8_deserial_read_cstr(raw_data, debug_entry_ptr->foff+sizeof(cv), &pdb70_path); - } else { - rd_errorf("unable to read PE_CvHeaderPDB70"); - } - } break; - case PE_CODEVIEW_RDI_MAGIC: { - PE_CvHeaderRDI cv = {0}; - U64 cv_read_size = str8_deserial_read_struct(raw_data, debug_entry_ptr->foff, &cv); - if (cv_read_size == sizeof(cv)) { - rdi_guid = cv.guid; - str8_deserial_read_cstr(raw_data, debug_entry_ptr->foff+sizeof(cv), &rdi_path); - } else { - rd_errorf("unable to read PE_CvHeaderRDI"); - } - } break; - default: break; + String8 raw_rdi = {0}; + Guid rdi_guid = {0}; + for (PE_DebugInfoNode *n = dbg_list.first; n != 0; n = n->next) { + PE_DebugInfo *v = &n->v; + if (v->header.type == PE_DebugDirectoryType_CODEVIEW) { + if (v->u.codeview.magic == PE_CODEVIEW_RDI_MAGIC) { + if (raw_rdi.size) { + rd_warningf("multiple RDI paths defined in %S"); + } else { + raw_rdi = os_data_from_file_path(arena, v->u.codeview.rdi.path); + rdi_guid = v->u.codeview.rdi.header.guid; + if (raw_rdi.size == 0) { + rd_errorf("unable to open RDI: %S", v->u.codeview.rdi.path); + } } } } } - // convert debug info to RDI - if (!rdi_path.size && (pdb20_path.size || pdb70_path.size)) { - if (pdb20_path.size && pdb70_path.size) { - rd_warningf("multiple PDB paths defined %S, %S (picking first)", pdb70_path, pdb20_path); + if (!raw_rdi.size) { + String8 pdb_path = str8_zero(); + Guid pdb_guid = {0}; + B32 convert_pdb = 0; + for (PE_DebugInfoNode *n = dbg_list.first; n != 0; n = n->next) { + PE_DebugInfo *v = &n->v; + if (v->header.type == PE_DebugDirectoryType_CODEVIEW) { + pdb_path = v->u.codeview.pdb70.path; + pdb_guid = v->u.codeview.pdb70.header.guid; + convert_pdb = 1; + break; + } } - String8 pdb_path = pdb70_path.size ? pdb70_path : pdb20_path; - String8 out_path = push_str8f(scratch.arena, "%S.rdi", str8_chop_last_dot(pdb_path)); - B32 is_rdi_ok = rd_invoke_rdi_converter(data_path, raw_data, pdb_path, out_path); - if (is_rdi_ok) { - rdi_path = out_path; - } else { - rd_errorf("unable write RDI to %S", out_path); + + if (convert_pdb) { + raw_rdi = rd_invoke_rdi_converter(scratch.arena, data_path, raw_data, pdb_path); } } - if (rdi_path.size) { - String8 raw_rdi = os_data_from_file_path(arena, rdi_path); - if (raw_rdi.size > 0) { - rdi = push_array(arena, RDI_Parsed, 1); + if (raw_rdi.size) { + rdi = push_array(arena, RDI_Parsed, 1); - // TODO: check guid - RDI_ParseStatus parse_status = rdi_parse(raw_rdi.str, raw_rdi.size, rdi); + // TODO: check guid + RDI_ParseStatus parse_status = rdi_parse(raw_rdi.str, raw_rdi.size, rdi); - String8 parse_status_string = str8_zero(); - if (parse_status == RDI_ParseStatus_Good) { - } else if (parse_status == RDI_ParseStatus_HeaderDoesNotMatch) { - parse_status_string = str8_lit("Header does not match"); - } else if (parse_status == RDI_ParseStatus_UnsupportedVersionNumber) { - parse_status_string = str8_lit("Unsupported version number"); - } else if (parse_status == RDI_ParseStatus_InvalidDataSecionLayout) { - parse_status_string = str8_lit("Invalid data section layout"); - } else if (parse_status == RDI_ParseStatus_MissingRequiredSection) { - parse_status_string = str8_lit("Missing required section"); - } else { - parse_status_string = push_str8f(scratch.arena, "unknown parse status code: %u", parse_status); - } - - if (parse_status_string.size) { - rdi = 0; - rd_errorf("RDI parse status(%u): %S", parse_status, parse_status_string); - } + String8 parse_status_string = str8_zero(); + if (parse_status == RDI_ParseStatus_Good) { + } else if (parse_status == RDI_ParseStatus_HeaderDoesNotMatch) { + parse_status_string = str8_lit("Header does not match"); + } else if (parse_status == RDI_ParseStatus_UnsupportedVersionNumber) { + parse_status_string = str8_lit("Unsupported version number"); + } else if (parse_status == RDI_ParseStatus_InvalidDataSecionLayout) { + parse_status_string = str8_lit("Invalid data section layout"); + } else if (parse_status == RDI_ParseStatus_MissingRequiredSection) { + parse_status_string = str8_lit("Missing required section"); } else { - rd_errorf("unable to open RDI: %S", rdi_path); + parse_status_string = push_str8f(scratch.arena, "unknown parse status code: %u", parse_status); + } + + if (parse_status_string.size) { + rdi = 0; + rd_errorf("RDI parse status(%u): %S", parse_status, parse_status_string); } } @@ -5657,41 +5619,34 @@ pe_print_debug_diretory(Arena *arena, String8List *out, String8 indent, String8 { Temp scratch = scratch_begin(&arena, 1); - rd_printf("# Debug"); + rd_printf("# Debug Directory"); rd_indent(); - U64 entry_count = raw_dir.size / sizeof(PE_DebugDirectory); - PE_DebugDirectory *entries = str8_deserial_get_raw_ptr(raw_dir, 0, sizeof(*entries)*entry_count); - for (U64 i = 0; i < entry_count; ++i) { - PE_DebugDirectory *de = entries+i; - if (i > 0) { - rd_newline(); - } - rd_printf("Entry[%u]", i); - rd_indent(); + PE_DebugInfoList debug_info_list = pe_parse_debug_directory(scratch.arena, raw_data, raw_dir); + U64 i = 0; + for (PE_DebugInfoNode *entry = debug_info_list.first; entry != 0; entry = entry->next, ++i) { + PE_DebugInfo *de = &entry->v; - { - String8 time_stamp = coff_string_from_time_stamp(scratch.arena, de->time_stamp); - String8 type = pe_string_from_debug_directory_type(de->type); - - rd_printf("Characteristics: %#x", de->characteristics); - rd_printf("Time Stamp: %S", time_stamp); - rd_printf("Version: %u.%u", de->major_ver, de->minor_ver); - rd_printf("Type: %S", type); - rd_printf("Size: %u", de->size); - rd_printf("Data virt off: %#x", de->voff); - rd_printf("Data file off: %#x", de->foff); + if (entry != debug_info_list.first) { rd_newline(); } - String8 raw_entry = str8_substr(raw_data, rng_1u64(de->foff, de->foff+de->size)); - if (raw_entry.size != de->size) { - rd_errorf("unable to read debug entry @ %#x", de->foff); - break; - } - + rd_printf("Entry[%llu]", i); rd_indent(); - switch (de->type) { + + // print header + rd_printf("Characteristics: %#x", de->header.characteristics); + rd_printf("Time Stamp: %S", coff_string_from_time_stamp(scratch.arena, de->header.time_stamp)); + rd_printf("Version: %u.%u", de->header.major_ver, de->header.minor_ver); + rd_printf("Type: %S", pe_string_from_debug_directory_type(de->header.type)); + rd_printf("Size: %u", de->header.size); + rd_printf("Data virt off: %#x", de->header.voff); + rd_printf("Data file off: %#x", de->header.foff); + rd_newline(); + + // print directory contents + rd_indent(); + switch (de->header.type) { case PE_DebugDirectoryType_ILTCG: case PE_DebugDirectoryType_MPX: case PE_DebugDirectoryType_EXCEPTION: @@ -5709,30 +5664,30 @@ pe_print_debug_diretory(Arena *arena, String8List *out, String8 indent, String8 // TODO: is this version? U32 unknown = 0; - off += str8_deserial_read_struct(raw_entry, off, &unknown); + off += str8_deserial_read_struct(de->u.raw_data, off, &unknown); if (unknown != 0) { rd_printf("TODO: unknown: %u", unknown); } rd_printf("%-8s %-8s %-8s", "VOFF", "Size", "Name"); - for (; off < raw_entry.size; ) { + for (; off < de->u.raw_data.size; ) { U32 voff = 0; U32 size = 0; String8 name = str8_zero(); - off += str8_deserial_read_struct(raw_entry, off, &voff); - off += str8_deserial_read_struct(raw_entry, off, &size); + off += str8_deserial_read_struct(de->u.raw_data, off, &voff); + off += str8_deserial_read_struct(de->u.raw_data, off, &size); if (voff == 0 && size == 0) { break; } - off += str8_deserial_read_cstr(raw_entry, off, &name); + off += str8_deserial_read_cstr(de->u.raw_data, off, &name); off = AlignPow2(off, 4); rd_printf("%08x %08x %S", voff, size, name); } } break; case PE_DebugDirectoryType_VC_FEATURE: { - MSCRT_VCFeatures *feat = str8_deserial_get_raw_ptr(raw_entry, 0, sizeof(*feat)); + MSCRT_VCFeatures *feat = str8_deserial_get_raw_ptr(de->u.raw_data, 0, sizeof(*feat)); if (feat) { rd_printf("Pre-VC++ 11.0: %u", feat->pre_vcpp); rd_printf("C/C++: %u", feat->c_cpp); @@ -5744,7 +5699,7 @@ pe_print_debug_diretory(Arena *arena, String8List *out, String8 indent, String8 } } break; case PE_DebugDirectoryType_FPO: { - PE_DebugFPO *fpo = str8_deserial_get_raw_ptr(raw_entry, 0, sizeof(*fpo)); + PE_DebugFPO *fpo = str8_deserial_get_raw_ptr(de->u.raw_data, 0, sizeof(*fpo)); if (fpo) { U8 prolog_size = PE_FPOEncoded_Extract_PROLOG_SIZE(fpo->flags); U8 saved_regs_size = PE_FPOEncoded_Extract_SAVED_REGS_SIZE(fpo->flags); @@ -5766,38 +5721,35 @@ pe_print_debug_diretory(Arena *arena, String8List *out, String8 indent, String8 rd_errorf("not enough bytes to read FPO"); } } break; - case PE_DebugDirectoryType_CODEVIEW: { - U32 magic = 0; - str8_deserial_read_struct(raw_entry, 0, &magic); - switch (magic) { + switch (de->u.codeview.magic) { case PE_CODEVIEW_PDB20_MAGIC: { - PE_CvHeaderPDB20 *cv20 = str8_deserial_get_raw_ptr(raw_entry, 0, sizeof(*cv20)); - String8 name; str8_deserial_read_cstr(raw_entry, sizeof(*cv20), &name); + PE_CvHeaderPDB20 *header = &de->u.codeview.pdb20.header; - String8 time_stamp = coff_string_from_time_stamp(scratch.arena, cv20->time_stamp); - - rd_printf("Time stamp: %S", time_stamp); - rd_printf("Age: %u", cv20->age); - rd_printf("Name: %S", name); + rd_printf("Time stamp: %S", coff_string_from_time_stamp(scratch.arena, header->time_stamp)); + rd_printf("Age: %u", header->age); + rd_printf("Name: %S", de->u.codeview.pdb20.path); } break; case PE_CODEVIEW_PDB70_MAGIC: { - PE_CvHeaderPDB70 *cv70 = str8_deserial_get_raw_ptr(raw_entry, 0, sizeof(*cv70)); - String8 name; str8_deserial_read_cstr(raw_entry, sizeof(*cv70), &name); - - String8 guid = string_from_guid(scratch.arena, cv70->guid); + PE_CvHeaderPDB70 *header = &de->u.codeview.pdb70.header; - rd_printf("GUID: %S", guid); - rd_printf("Age: %u", cv70->age); - rd_printf("Name: %S", name); + rd_printf("GUID: %S", string_from_guid(scratch.arena, header->guid)); + rd_printf("Age: %u", header->age); + rd_printf("Name: %S", de->u.codeview.pdb70.path); + } break; + case PE_CODEVIEW_RDI_MAGIC: { + PE_CvHeaderRDI *header = &de->u.codeview.rdi.header; + + rd_printf("GUID: %S", string_from_guid(scratch.arena, header->guid)); + rd_printf("Name: %S", de->u.codeview.rdi.path); } break; default: { - rd_errorf("unknown CodeView magic %#x", magic); + rd_errorf("unknown CodeView magic %#x", de->u.codeview.magic); } break; } } break; case PE_DebugDirectoryType_MISC: { - PE_DebugMisc *misc = str8_deserial_get_raw_ptr(raw_entry, 0, sizeof(*misc)); + PE_DebugMisc *misc = str8_deserial_get_raw_ptr(de->u.raw_data, 0, sizeof(*misc)); String8 type_string = pe_string_from_misc_type(misc->data_type); @@ -5808,7 +5760,7 @@ pe_print_debug_diretory(Arena *arena, String8List *out, String8 indent, String8 switch (misc->data_type) { case PE_DebugMiscType_EXE_NAME: { String8 name; - str8_deserial_read_cstr(raw_entry, sizeof(*misc), &name); + str8_deserial_read_cstr(de->u.raw_data, sizeof(*misc), &name); rd_printf("Name: %S", name); } break; default: { @@ -5818,12 +5770,12 @@ pe_print_debug_diretory(Arena *arena, String8List *out, String8 indent, String8 } break; } rd_unindent(); - rd_unindent(); } rd_unindent(); rd_newline(); + scratch_end(scratch); }