mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-22 11:44:59 -07:00
updated PE bin info to handle RDI entry, changed raddump to
convert and load RDI from memory, added helper for parsing debug directory
This commit is contained in:
+171
-114
@@ -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
|
||||
|
||||
|
||||
+42
-5
@@ -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);
|
||||
|
||||
+108
-156
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user