From 9cc2afc6593e3316af5409f1461464fc7fce201c Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Wed, 27 May 2026 13:10:56 -0700 Subject: [PATCH] eliminate backend-specific module parsing from debug engine ctrl thread; just pass that info back via the demon event --- project.4coder | 2 +- project.raddbg | 2 +- src/base/base_processes.h | 4 +- src/dbg_engine/dbg_engine_ctrl.c | 426 +++++-------------------- src/dbg_engine/dbg_engine_ctrl.h | 6 +- src/demon/demon_core.c | 1 + src/demon/demon_core.h | 59 +++- src/demon/demon_core.mdesk | 1 + src/demon/generated/demon.meta.c | 3 +- src/demon/generated/demon.meta.h | 3 +- src/linux/base/linux_base.c | 30 +- src/linux/demon/linux_demon.c | 256 +++++++++++++-- src/linux/demon/linux_demon.h | 5 + src/raddbg/raddbg_core.c | 27 +- src/raddbg/raddbg_eval.c | 2 +- src/raddbg/raddbg_main.c | 8 +- src/raddbg/raddbg_views.c | 2 +- src/win32/base/win32_base.c | 3 +- src/win32/demon/win32_demon.c | 524 +++++++++++++++++++++++-------- src/win32/demon/win32_demon.h | 5 +- 20 files changed, 825 insertions(+), 544 deletions(-) diff --git a/project.4coder b/project.4coder index 24737b61..14cc2147 100644 --- a/project.4coder +++ b/project.4coder @@ -49,7 +49,7 @@ commands = // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, - .f1 = { .win = "raddbg_stable --ipc kill_all && build torture", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, + .f1 = { .win = "build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, //- rjf: [raddbg wsl] // .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, diff --git a/project.raddbg b/project.raddbg index d33469ef..59545fc2 100644 --- a/project.raddbg +++ b/project.raddbg @@ -7,6 +7,7 @@ target: working_directory: build arguments: "--user:raddbg_test.user --project:raddbg_test.project" debug_subprocesses: 0 + enabled: 1 } target: { @@ -42,5 +43,4 @@ target: executable: "build/torture.exe" working_directory: build arguments: "rdi_from_pdb/* --gui" - enabled: 1 } diff --git a/src/base/base_processes.h b/src/base/base_processes.h index 78fc7969..21e33cf1 100644 --- a/src/base/base_processes.h +++ b/src/base/base_processes.h @@ -12,7 +12,9 @@ struct ProcessInfo String8 binary_file_path; String8 binary_path; String8 initial_path; - String8 user_program_data_path; + String8 user_program_config_data_path; + String8 user_program_cache_data_path; + String8 user_program_logs_data_path; String8 symbol_cache_path; String8List module_load_paths; String8List environment; diff --git a/src/dbg_engine/dbg_engine_ctrl.c b/src/dbg_engine/dbg_engine_ctrl.c index bfbc5e8b..dcdf9a50 100644 --- a/src/dbg_engine/dbg_engine_ctrl.c +++ b/src/dbg_engine/dbg_engine_ctrl.c @@ -3754,357 +3754,60 @@ d_ctrl_thread__append_program_defined_bp_traps(Arena *arena, D_Entity *bp, DMN_T //- rjf: module lifetime open/close work internal void -d_ctrl_thread__module_open(D_Handle process, D_Handle module, Rng1U64 vaddr_range, String8 path, Rng1U64 elf_phdr_vrange, U64 elf_phdr_entsize) +d_ctrl_thread__module_open(D_Handle process, D_Handle module, U64 base_vaddr, DMN_ModuleInfo *module_info) { Temp scratch = scratch_begin(0,0); //- rjf: set up per-module info Arena *arena = arena_alloc(); - U64 entry_point_voff = 0; - U32 rdi_dbg_time = 0; - Guid rdi_dbg_guid = {0}; - String8 exe_dbg_path = {0}; - String8 rdi_dbg_path = {0}; - String8 raddbg_data = {0}; - Rng1U64 raddbg_section_voff_range = {0}; - Rng1U64 raddbg_is_attached_section_voff_range = {0}; - PE_IntelPdata *pdatas = 0; - U64 pdatas_count = 0; - U32 pdb_dbg_time = 0; - U32 pdb_dbg_age = 0; - Guid pdb_dbg_guid = {0}; - String8 pdb_dbg_path = {0}; - U64 cfi_rebase = 0; - B32 is_unwind_eh = 0; + String8 raddbg_data = d_data_from_process_vaddr_range(arena, process, shift_1u64(module_info->raddbg_info_voff_range, base_vaddr), 0); + U64 pdatas_count = dim_1u64(module_info->pe_intel_pdatas_vaddr_range) / sizeof(PE_IntelPdata); + String8 pdatas_data = d_data_from_process_vaddr_range(arena, process, module_info->pe_intel_pdatas_vaddr_range, 0); + pdatas_count = Min(pdatas_count, pdatas_data.size / sizeof(PE_IntelPdata)); + PE_IntelPdata *pdatas = (PE_IntelPdata *)pdatas_data.str; + EH_PtrCtx eh_ptr_ctx = {.pc_vaddr = max_U64, .text_vaddr = max_U64, .data_vaddr = max_U64, .func_vaddr = max_U64, .ptr_align = 0}; EH_FrameHdr eh_frame_hdr = {0}; - EH_PtrCtx eh_ptr_ctx = { .pc_vaddr = max_U64, .text_vaddr = max_U64, .data_vaddr = max_U64, .func_vaddr = max_U64, .ptr_align = 0 }; - - //- read module's signature bytes - U64 module_sig_size = Max(elf_magic_string.size, sizeof(PE_DosMagic)); - U8 *module_sig_bytes = push_array(scratch.arena, U8, module_sig_size); - d_process_read(process, rng_1u64(vaddr_range.min, vaddr_range.min + module_sig_size), module_sig_bytes); - - ////////////////////////////// - //- parse PE module - // - PE_DosMagic dos_magic = *(PE_DosMagic *)module_sig_bytes; - if(dos_magic == PE_DOS_MAGIC) + B32 is_unwind_eh = 0; + if(module_info->eh_frame_header_vaddr_range.min != 0) { - ProfScope("unpack relevant PE info") - { - B32 is_valid = 1; - - //- rjf: read DOS header - PE_DosHeader dos_header = {0}; - if(is_valid) - { - if(!d_process_read_struct(process, vaddr_range.min, &dos_header) || - dos_header.magic != PE_DOS_MAGIC) - { - is_valid = 0; - } - } - - //- rjf: read PE magic - U32 pe_magic = 0; - if(is_valid) - { - if(!d_process_read_struct(process, vaddr_range.min + dos_header.coff_file_offset, &pe_magic) || - pe_magic != PE_MAGIC) - { - is_valid = 0; - } - } - - //- rjf: read COFF header - U64 file_header_off = dos_header.coff_file_offset + sizeof(pe_magic); - COFF_FileHeader file_header = {0}; - if(is_valid) - { - if(!d_process_read_struct(process, vaddr_range.min + file_header_off, &file_header)) - { - is_valid = 0; - } - } - - //- rjf: unpack range of optional extension header - U32 opt_ext_size = file_header.optional_header_size; - Rng1U64 opt_ext_off_range = r1u64(file_header_off + sizeof(COFF_FileHeader), - file_header_off + sizeof(COFF_FileHeader) + opt_ext_size); - - //- rjf: read optional header - U64 entry_point = 0; - U32 data_dir_count = 0; - if(opt_ext_size > 0) - { - // rjf: read magic number - U16 opt_ext_magic = 0; - d_process_read_struct(process, vaddr_range.min + opt_ext_off_range.min, &opt_ext_magic); - - // rjf: read info - U32 reported_data_dir_offset = 0; - U32 reported_data_dir_count = 0; - switch(opt_ext_magic) - { - case PE_PE32_MAGIC: - { - PE_OptionalHeader32 pe_optional = {0}; - d_process_read_struct(process, vaddr_range.min + opt_ext_off_range.min, &pe_optional); - entry_point = pe_optional.entry_point_va; - reported_data_dir_offset = sizeof(pe_optional); - reported_data_dir_count = pe_optional.data_dir_count; - }break; - case PE_PE32PLUS_MAGIC: - { - PE_OptionalHeader32Plus pe_optional = {0}; - d_process_read_struct(process, vaddr_range.min + opt_ext_off_range.min, &pe_optional); - entry_point = pe_optional.entry_point_va; - reported_data_dir_offset = sizeof(pe_optional); - reported_data_dir_count = pe_optional.data_dir_count; - }break; - } - - // rjf: find number of data directories - U32 data_dir_max = (opt_ext_size - reported_data_dir_offset) / sizeof(PE_DataDirectory); - data_dir_count = ClampTop(reported_data_dir_count, data_dir_max); - - // rjf: extract sections - U64 sec_array_off = opt_ext_off_range.max; - U64 sec_count = file_header.section_count; - COFF_SectionHeader *sec = push_array(scratch.arena, COFF_SectionHeader, sec_count); - d_process_read(process, r1u64(vaddr_range.min + sec_array_off, vaddr_range.min + sec_array_off + sec_count*sizeof(COFF_SectionHeader)), sec); - - // rjf: grab pdatas from exceptions section - if(data_dir_count > PE_DataDirectoryIndex_EXCEPTIONS) - { - PE_DataDirectory dir = {0}; - d_process_read_struct(process, vaddr_range.min + opt_ext_off_range.min + reported_data_dir_offset + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_EXCEPTIONS, &dir); - Rng1U64 pdatas_voff_range = r1u64((U64)dir.virt_off, (U64)dir.virt_off + (U64)dir.virt_size); - pdatas_count = dim_1u64(pdatas_voff_range)/sizeof(PE_IntelPdata); - pdatas = push_array(arena, PE_IntelPdata, pdatas_count); - d_process_read(process, r1u64(vaddr_range.min + pdatas_voff_range.min, vaddr_range.min + pdatas_voff_range.max), pdatas); - } - - // rjf: grab entry point vaddr - entry_point_voff = entry_point; - - // rjf: grab data about debug info - if(data_dir_count > PE_DataDirectoryIndex_DEBUG) - { - // rjf: read data dir - PE_DataDirectory dir = {0}; - d_process_read_struct(process, vaddr_range.min + opt_ext_off_range.min + reported_data_dir_offset + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_DEBUG, &dir); - - U64 dbg_dir_count = dir.virt_size / sizeof(PE_DebugDirectory); - for(U64 dbg_dir_idx = 0; dbg_dir_idx < dbg_dir_count; dbg_dir_idx += 1) - { - // rjf: read debug directory - U64 dir_addr = vaddr_range.min + dir.virt_off + dbg_dir_idx * sizeof(PE_DebugDirectory); - PE_DebugDirectory dbg_data = {0}; - d_process_read_struct(process, dir_addr, &dbg_data); - - // rjf: extract external file info from codeview header - if(dbg_data.type == PE_DebugDirectoryType_CODEVIEW) - { - U32 cv_magic = 0; - d_process_read_struct(process, vaddr_range.min + dbg_data.voff, &cv_magic); - switch(cv_magic) - { - default:break; - case PE_CODEVIEW_PDB20_MAGIC: - { - PE_CvHeaderPDB20 cv; - U64 read_size = d_process_read_struct(process, vaddr_range.min+dbg_data.voff, &cv); - if(read_size == sizeof(cv)) - { - U64 pdb_dbg_path_vaddr = vaddr_range.min + dbg_data.voff + sizeof(cv); - pdb_dbg_time = cv.time_stamp; - pdb_dbg_age = cv.age; - pdb_dbg_path = d_data_from_process_vaddr_range(arena, process, r1u64(pdb_dbg_path_vaddr, pdb_dbg_path_vaddr+512), 1); - } - }break; - case PE_CODEVIEW_PDB70_MAGIC: - { - PE_CvHeaderPDB70 cv; - U64 read_size = d_process_read_struct(process, vaddr_range.min + dbg_data.voff, &cv); - if(read_size == sizeof(cv)) - { - U64 pdb_dbg_path_vaddr = vaddr_range.min + dbg_data.voff + sizeof(cv); - pdb_dbg_guid = cv.guid; - pdb_dbg_age = cv.age; - pdb_dbg_path = d_data_from_process_vaddr_range(arena, process, r1u64(pdb_dbg_path_vaddr, pdb_dbg_path_vaddr+512), 1); - } - }break; - case PE_CODEVIEW_RDI_MAGIC: - { - PE_CvHeaderRDI cv; - U64 read_size = d_process_read_struct(process, vaddr_range.min + dbg_data.voff, &cv); - if(read_size == sizeof(cv)) - { - U64 rdi_dbg_path_vaddr = vaddr_range.min + dbg_data.voff + sizeof(cv); - rdi_dbg_guid = cv.guid; - rdi_dbg_path = d_data_from_process_vaddr_range(arena, process, r1u64(rdi_dbg_path_vaddr, rdi_dbg_path_vaddr+512), 1); - } - }break; - } - } - } - } - - // rjf: look for DWARF debug info - { - U64 symbol_array_off = file_header.symbol_table_foff; - U64 symbol_count = file_header.symbol_count; - if(symbol_array_off != 0) - { - exe_dbg_path = path; - } - } - - // rjf: extract copy of module's raddbg data - { - for EachIndex(idx, sec_count) - { - String8 section_name = str8_cstring((char *)sec[idx].name); - if(str8_match(section_name, str8_lit(".raddbg"), 0)) - { - raddbg_section_voff_range.min = sec[idx].voff; - raddbg_section_voff_range.max = sec[idx].voff + sec[idx].vsize; - } - else if(str8_match(section_name, str8_lit(".rdbgia"), 0)) - { - raddbg_is_attached_section_voff_range.min = sec[idx].voff; - raddbg_is_attached_section_voff_range.max = sec[idx].voff + sec[idx].vsize; - } - } - raddbg_data.size = dim_1u64(raddbg_section_voff_range); - raddbg_data.str = push_array(arena, U8, raddbg_data.size); - d_process_read(process, r1u64(vaddr_range.min + raddbg_section_voff_range.min, - vaddr_range.min + raddbg_section_voff_range.max), raddbg_data.str); - } - - // rjf: if we have a "raddbg is attached" section, mark the first byte as 1, to signify attachment - if(raddbg_is_attached_section_voff_range.max != raddbg_is_attached_section_voff_range.min) - { - U8 new_value = 1; - d_process_write_struct(process, vaddr_range.min + raddbg_is_attached_section_voff_range.min, &new_value); - } - } - } - } - - ////////////////////////////// - //- parse ELF module - // - else if(str8_match(str8(module_sig_bytes, elf_magic_string.size), elf_magic_string, 0)) - { - U64 e_entry = 0; - ELF_Type e_type = ELF_Type_None; - { - U8 *elf_sig = d_data_from_process_vaddr_range(scratch.arena, process, rng_1u64(vaddr_range.min, vaddr_range.min + sizeof(elf_sig[0]) * ELF_Identifier_Max), 0).str; - if(elf_sig == 0) { goto elf_exit; } - switch(elf_sig[ELF_Identifier_Class]) - { - default: - case ELF_Class_None: - case ELF_Class_32: - {}break; - case ELF_Class_64: - { - ELF_Hdr64 *ehdr = (ELF_Hdr64 *)d_data_from_process_vaddr_range(scratch.arena, process, r1u64(vaddr_range.min, vaddr_range.min + sizeof(*ehdr)), 0).str; - if(ehdr == 0) { goto elf_exit; } - e_entry = ehdr->e_entry; - e_type = ehdr->e_type; - }break; - } - } - - if(e_type == ELF_Type_Dyn) - { - cfi_rebase = vaddr_range.min; - } - - // find and parse .eh_frame_hdr - Rng1U64 eh_frame_hdr_vrange = {0}; - String8 eh_frame_hdr_data = {0}; - { - void *phdrs_raw = d_data_from_process_vaddr_range(scratch.arena, process, elf_phdr_vrange, 0).str; - if(phdrs_raw == 0) { goto elf_exit; } - if(elf_phdr_entsize == sizeof(ELF_Phdr64)) - { - U64 elf_phcount = dim_1u64(elf_phdr_vrange) / elf_phdr_entsize; - ELF_Phdr64 *phdrs64 = phdrs_raw; - for EachIndex(phdr_idx, elf_phcount) - { - ELF_Phdr64 *phdr = phdrs64 + phdr_idx; - if(phdr->p_type == ELF_PType_GnuEHFrame) - { - eh_frame_hdr_vrange = r1u64(cfi_rebase + phdr->p_vaddr, cfi_rebase + phdr->p_vaddr + phdr->p_memsz); - eh_frame_hdr_data = d_data_from_process_vaddr_range(arena, process, eh_frame_hdr_vrange, 0); - is_unwind_eh = 1; - break; - } - } - } - } - - // parse .eh_frame_hdr - D_Entity *process_entity = d_entity_from_handle(process); - Arch arch = process_entity->arch; - eh_ptr_ctx.pc_vaddr = eh_frame_hdr_vrange.min; - eh_ptr_ctx.data_vaddr = eh_frame_hdr_vrange.min; - eh_frame_hdr = eh_parse_frame_hdr(eh_frame_hdr_data, byte_size_from_arch(arch), &eh_ptr_ctx); - - // set entry point - if (e_entry != 0) - { - entry_point_voff = e_entry - vaddr_range.min; - } - - // TODO: is there a way to detect DWARF in runtime ELF? - if(1) - { - exe_dbg_path = path; - } - elf_exit:; + String8 eh_frame_hdr_data = d_data_from_process_vaddr_range(arena, process, module_info->eh_frame_header_vaddr_range, 0); + eh_ptr_ctx.pc_vaddr = module_info->eh_frame_header_vaddr_range.min; + eh_ptr_ctx.data_vaddr = module_info->eh_frame_header_vaddr_range.min; + eh_frame_hdr = eh_parse_frame_hdr(eh_frame_hdr_data, byte_size_from_arch(module_info->arch), &eh_ptr_ctx); + is_unwind_eh = 1; } ////////////////////////////// //- rjf: pick default initial debug info path // - String8 initial_debug_info_path = str8_zero(); + String8 initial_debug_info_path = {0}; { - String8 exe_folder = str8_chop_last_slash(path); - String8List dbg_path_candidates = {0}; - // - //~ TODO(rjf): @linux_port PLEASE READ RYAN vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - // - // TODO(rjf): trying "exe_folder/embedded_path" as the first option is only a valid - // heuristic on Windows, because we know that two absolute paths concatted together - // are necessarily invalid. however, on Linux, this is not the case - you could stitch - // two paths together and get a third path that is completely valid. so, in that case, - // we will need to infer if the path is relative, and then use either the embedded - // path as-is, or the exe-relative-path accordingly, depending on that. - // - if(rdi_dbg_path.size != 0) + String8List candidates = {0}; + + // rjf: push module-embedded debug info paths, try both relative-to-exe & absolute + if(module_info->debug_info_path.size != 0) { - str8_list_pushf(scratch.arena, &dbg_path_candidates, "%S/%S", exe_folder, rdi_dbg_path); - str8_list_push(scratch.arena, &dbg_path_candidates, rdi_dbg_path); + PathStyle path_style = path_style_from_str8(module_info->debug_info_path); + if(path_style == PathStyle_Relative) + { + String8 module_image_folder = str8_chop_last_slash(module_info->module_path); + str8_list_pushf(scratch.arena, &candidates, "%S/%S", module_image_folder, module_info->debug_info_path); + } + str8_list_push(scratch.arena, &candidates, module_info->debug_info_path); } - if(exe_dbg_path.size != 0) + + // rjf: push heuristic-based debug info paths { - str8_list_push(scratch.arena, &dbg_path_candidates, path); + String8 exe_path = module_info->module_path; + String8 exe_path_no_ext = str8_chop_last_dot(exe_path); + str8_list_pushf(scratch.arena, &candidates, "%S.pdb", exe_path_no_ext); + str8_list_pushf(scratch.arena, &candidates, "%S.pdb", exe_path); + str8_list_pushf(scratch.arena, &candidates, "%S.rdi", exe_path_no_ext); + str8_list_pushf(scratch.arena, &candidates, "%S.rdi", exe_path); } - if(pdb_dbg_path.size != 0) - { - str8_list_pushf(scratch.arena, &dbg_path_candidates, "%S/%S", exe_folder, pdb_dbg_path); - str8_list_push(scratch.arena, &dbg_path_candidates, pdb_dbg_path); - } - str8_list_pushf(scratch.arena, &dbg_path_candidates, "%S.pdb", str8_chop_last_dot(path)); - str8_list_pushf(scratch.arena, &dbg_path_candidates, "%S.pdb", path); - str8_list_pushf(scratch.arena, &dbg_path_candidates, "%S.rdi", str8_chop_last_dot(path)); - str8_list_pushf(scratch.arena, &dbg_path_candidates, "%S.rdi", path); - for(String8Node *n = dbg_path_candidates.first; n != 0; n = n->next) + + // rjf: pick first candidate that works + for EachNode(n, String8Node, candidates.first) { String8 candidate_path = n->string; FileProperties props = properties_from_file_path(candidate_path); @@ -4120,27 +3823,36 @@ d_ctrl_thread__module_open(D_Handle process, D_Handle module, Rng1U64 vaddr_rang //- rjf: determine debug info unique identifier // String8 debug_info_unique_identifier = {0}; - if(pdb_dbg_path.size != 0) + if(module_info->debug_info_path.size != 0) { - Guid guid = pdb_dbg_guid; + Guid guid = module_info->debug_info_guid; debug_info_unique_identifier = str8f(scratch.arena, "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", - guid.data1, guid.data2, guid.data3, guid.data4[0], guid.data4[1], guid.data4[2], guid.data4[3], guid.data4[4], guid.data4[5], guid.data4[6], guid.data4[7], pdb_dbg_age); + guid.data1, guid.data2, guid.data3, guid.data4[0], guid.data4[1], guid.data4[2], guid.data4[3], guid.data4[4], guid.data4[5], guid.data4[6], guid.data4[7], module_info->debug_info_age); } ////////////////////////////// //- rjf: build local server cache debug info path // String8 local_server_cache_debug_info_path = {0}; - if(debug_info_unique_identifier.size != 0 && pdb_dbg_path.size != 0) + if(debug_info_unique_identifier.size != 0 && module_info->debug_info_path.size != 0) { // TODO(rjf): this is not complete - assuming stripped etc. - just following pattern from ntdll.dll for now String8 symbol_cache_path = get_process_info()->symbol_cache_path; - String8 module_name = str8_skip_last_slash(path); - String8 dbg_name = str8_skip_last_slash(pdb_dbg_path); + String8 module_name = str8_skip_last_slash(module_info->module_path); + String8 dbg_name = str8_skip_last_slash(module_info->debug_info_path); String8 path = str8f(scratch.arena, "%S/%S/%S/stripped/%S", symbol_cache_path, module_name, debug_info_unique_identifier, dbg_name); local_server_cache_debug_info_path = path_normalized_from_string(arena, path); } + ////////////////////////////// + //- rjf: write 1 at attachment marker, to signify attachment + // + if(module_info->raddbg_is_attached_marker_voff != 0) + { + U8 new_value = 1; + d_process_write_struct(process, base_vaddr + module_info->raddbg_is_attached_marker_voff, &new_value); + } + ////////////////////////////// //- rjf: insert info into cache // @@ -4169,14 +3881,14 @@ d_ctrl_thread__module_open(D_Handle process, D_Handle module, Rng1U64 vaddr_rang node->arena = arena; node->pdatas = pdatas; node->pdatas_count = pdatas_count; - node->cfi_rebase = cfi_rebase; + node->cfi_rebase = module_info->cfi_rebase; node->is_unwind_eh = is_unwind_eh; node->eh_frame_hdr = eh_frame_hdr; node->eh_ptr_ctx = eh_ptr_ctx; - node->entry_point_voff = entry_point_voff; + node->entry_point_voff = module_info->entry_point_voff; node->local_debug_info_path = initial_debug_info_path; node->debug_info_unique_identifier= debug_info_unique_identifier; - node->raddbg_attached_marker_voff = raddbg_is_attached_section_voff_range.min; + node->raddbg_attached_marker_voff = module_info->raddbg_is_attached_marker_voff; node->raddbg_data = str8_copy(arena, raddbg_data); } } @@ -4186,7 +3898,7 @@ d_ctrl_thread__module_open(D_Handle process, D_Handle module, Rng1U64 vaddr_rang } internal void -d_ctrl_thread__module_close(D_Handle process, D_Handle module, Rng1U64 vaddr_range) +d_ctrl_thread__module_close(D_Handle process, D_Handle module, U64 base_vaddr) { DMN_Handle process_dmn = d_dmn_from_handle(process); @@ -4221,12 +3933,12 @@ d_ctrl_thread__module_close(D_Handle process, D_Handle module, Rng1U64 vaddr_ran } ////////////////////////////// - //- rjf: write 0 at attachment market, to signify detachment + //- rjf: write 0 at attachment marker, to signify detachment // if(raddbg_attached_marker_voff != 0) { U8 new_value = 0; - d_process_write_struct(process, vaddr_range.min + raddbg_attached_marker_voff, &new_value); + d_process_write_struct(process, base_vaddr + raddbg_attached_marker_voff, &new_value); } } @@ -4526,6 +4238,13 @@ d_ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, D_Msg *msg, D } MemoryCopyStruct(&dst_n->v, &src_n->v); dst_n->v.string = push_str8_copy(d_ctrl_state->dmn_event_arena, dst_n->v.string); + if(src_n->v.module_info != &dmn_module_info_nil) + { + dst_n->v.module_info = push_array(d_ctrl_state->dmn_event_arena, DMN_ModuleInfo, 1); + MemoryCopyStruct(dst_n->v.module_info, src_n->v.module_info); + dst_n->v.module_info->module_path = str8_copy(d_ctrl_state->dmn_event_arena, dst_n->v.module_info->module_path); + dst_n->v.module_info->debug_info_path = str8_copy(d_ctrl_state->dmn_event_arena, dst_n->v.module_info->debug_info_path); + } SLLQueuePush(d_ctrl_state->first_dmn_event_node, d_ctrl_state->last_dmn_event_node, dst_n); } } @@ -4607,7 +4326,7 @@ d_ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, D_Msg *msg, D D_Event *out_evt1 = d_event_list_push(scratch.arena, &evts); String8 module_path = path_normalized_from_string(scratch.arena, event->string); U64 exe_timestamp = properties_from_file_path(module_path).modified; - d_ctrl_thread__module_open(process_handle, module_handle, r1u64(event->address, event->address+event->size), module_path, event->elf_phdr_vrange, event->elf_phdr_entsize); + d_ctrl_thread__module_open(process_handle, module_handle, event->address, event->module_info); out_evt1->kind = D_EventKind_NewModule; out_evt1->msg_id = msg->msg_id; out_evt1->entity = module_handle; @@ -4618,8 +4337,8 @@ d_ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, D_Msg *msg, D out_evt1->rip_vaddr = event->address; out_evt1->timestamp = exe_timestamp; out_evt1->string = module_path; - out_evt1->tls_index = event->tls_index; - out_evt1->tls_offset = event->tls_offset; + out_evt1->tls_index = event->module_info->tls_index; + out_evt1->tls_offset = event->module_info->tls_offset; D_Event *out_evt2 = d_event_list_push(scratch.arena, &evts); String8 initial_debug_info_path = d_initial_debug_info_path_from_module(scratch.arena, module_handle); U64 debug_info_timestamp = properties_from_file_path(initial_debug_info_path).modified; @@ -4657,7 +4376,7 @@ d_ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, D_Msg *msg, D D_Entity *module_ent = d_entity_from_handle(module_handle); D_Entity *process_ent = d_process_from_entity(module_ent); String8 module_path = event->string; - d_ctrl_thread__module_close(process_ent->handle, module_handle, module_ent->vaddr_range); + d_ctrl_thread__module_close(process_ent->handle, module_handle, module_ent->vaddr_range.min); out_evt->kind = D_EventKind_EndModule; out_evt->msg_id = msg->msg_id; out_evt->entity = module_handle; @@ -5482,8 +5201,11 @@ d_ctrl_thread__open_crash_dump(DMN_CtrlCtx *ctrl_ctx, D_Msg *msg) // rjf: open module D_Handle module_handle = dump_modules[idx].module_handle; Rng1U64 vaddr_range = dump_modules[idx].vaddr_range; - d_ctrl_thread__module_open(process, module_handle, vaddr_range, dump_modules[idx].path, r1u64(0, 0), 0); - +#if 0 + // TODO(rjf): I don't think we can use this demon module info path here + DMN_ModuleInfo module_info = {0}; + d_ctrl_thread__module_open(process, module_handle, vaddr_range.min, dump_modules[idx].path, r1u64(0, 0), 0); +#endif // rjf: open debug info String8 initial_debug_info_path = d_initial_debug_info_path_from_module(scratch.arena, module_handle); U64 debug_info_timestamp = properties_from_file_path(initial_debug_info_path).modified; diff --git a/src/dbg_engine/dbg_engine_ctrl.h b/src/dbg_engine/dbg_engine_ctrl.h index 05d418e6..9df2083e 100644 --- a/src/dbg_engine/dbg_engine_ctrl.h +++ b/src/dbg_engine/dbg_engine_ctrl.h @@ -181,8 +181,8 @@ struct D_ModuleImageInfoCacheNode PE_IntelPdata *pdatas; U64 pdatas_count; U64 cfi_rebase; - B32 is_unwind_eh; String8 dwarf_unwind_data; + B32 is_unwind_eh; EH_FrameHdr eh_frame_hdr; EH_PtrCtx eh_ptr_ctx; U64 entry_point_voff; @@ -623,8 +623,8 @@ internal void d_ctrl_thread__append_resolved_process_user_bp_traps(Arena *arena, internal void d_ctrl_thread__append_program_defined_bp_traps(Arena *arena, D_Entity *bp, DMN_TrapChunkList *traps_out); //- rjf: module lifetime open/close work -internal void d_ctrl_thread__module_open(D_Handle process, D_Handle module, Rng1U64 vaddr_range, String8 path, Rng1U64 elf_phdr_vrange, U64 elf_phdr_entsize); -internal void d_ctrl_thread__module_close(D_Handle process, D_Handle module, Rng1U64 vaddr_range); +internal void d_ctrl_thread__module_open(D_Handle process, D_Handle module, U64 base_vaddr, DMN_ModuleInfo *module_info); +internal void d_ctrl_thread__module_close(D_Handle process, D_Handle module, U64 base_vaddr); //- rjf: dump process closing work internal void d_ctrl_thread__close_dump_process(D_MsgID msg_id, D_Handle process); diff --git a/src/demon/demon_core.c b/src/demon/demon_core.c index 4fd59c89..a136ad07 100644 --- a/src/demon/demon_core.c +++ b/src/demon/demon_core.c @@ -119,5 +119,6 @@ dmn_event_list_push(Arena *arena, DMN_EventList *list) SLLQueuePush(list->first, list->last, n); list->count += 1; DMN_Event *result = &n->v; + result->module_info = &dmn_module_info_nil; return result; } diff --git a/src/demon/demon_core.h b/src/demon/demon_core.h index 802766c4..76a16ab8 100644 --- a/src/demon/demon_core.h +++ b/src/demon/demon_core.h @@ -69,33 +69,72 @@ struct DMN_HandleArray //////////////////////////////// //~ rjf: Event Types +typedef struct DMN_ModuleInfo DMN_ModuleInfo; +struct DMN_ModuleInfo +{ + // rjf: image architecture + Arch arch; + + // rjf: image virtual size + U64 vsize; + + // rjf: entry point + U64 entry_point_voff; + + // rjf: module path + String8 module_path; + + // rjf: debug info key + String8 debug_info_path; + Guid debug_info_guid; + U64 debug_info_timestamp; + U64 debug_info_age; + + // rjf: thread-local storage info + U64 tls_index; + U64 tls_offset; + + // rjf: unwinding info + Rng1U64 pe_intel_pdatas_vaddr_range; + Rng1U64 eh_frame_header_vaddr_range; + U64 cfi_rebase; + + // rjf: special raddbg data + Rng1U64 raddbg_info_voff_range; + U64 raddbg_is_attached_marker_voff; +}; + typedef struct DMN_Event DMN_Event; struct DMN_Event { + // rjf: categories DMN_EventKind kind; DMN_ErrorKind error_kind; DMN_MemoryEventKind memory_kind; DMN_ExceptionKind exception_kind; + + // rjf: entities DMN_Handle process; DMN_Handle thread; DMN_Handle module; + + // rjf: general event properties Arch arch; U64 address; U64 size; String8 string; - U32 code; // code gives pid & tid on CreateProcess and CreateThread (respectfully) + U32 code; // code -> exception code (signo on Linux), pid/tid U32 flags; // DMN_TrapFlags, if `DMN_EventKind_SetBreakpoint` - S32 signo; - S32 sigcode; - Rng1U64 elf_phdr_vrange; - U64 elf_phdr_entsize; U64 instruction_pointer; U64 stack_pointer; U64 user_data; B32 exception_repeated; - U64 tls_index; - U64 tls_offset; + + // rjf: process info DMN_TlsModel tls_model; + + // rjf: module info + DMN_ModuleInfo *module_info; }; typedef struct DMN_EventNode DMN_EventNode; @@ -181,6 +220,11 @@ struct DMN_ProcessInfo U32 pid; }; +//////////////////////////////// +//~ rjf: Globals + +read_only global DMN_ModuleInfo dmn_module_info_nil = {0}; + //////////////////////////////// //~ rjf: Basic Type Functions (Helpers, Implemented Once) @@ -247,7 +291,6 @@ internal U64 dmn_process_read(DMN_Handle process, Rng1U64 range, void *dst); internal B32 dmn_process_write(DMN_Handle process, Rng1U64 range, void *src); //- rjf: threads -internal Arch dmn_arch_from_thread(DMN_Handle handle); internal U64 dmn_stack_base_vaddr_from_thread(DMN_Handle handle); internal U64 dmn_tls_root_vaddr_from_thread(DMN_Handle handle); internal B32 dmn_thread_read_reg_block(DMN_Handle handle, void *reg_block); diff --git a/src/demon/demon_core.mdesk b/src/demon/demon_core.mdesk index 78e49001..50acceef 100644 --- a/src/demon/demon_core.mdesk +++ b/src/demon/demon_core.mdesk @@ -13,6 +13,7 @@ DMN_EventKindTable: {ExitThread} {LoadModule} {UnloadModule} + {ModuleDebugInfo} {Breakpoint} {Trap} {SingleStep} diff --git a/src/demon/generated/demon.meta.c b/src/demon/generated/demon.meta.c index 41c98976..66404aeb 100644 --- a/src/demon/generated/demon.meta.c +++ b/src/demon/generated/demon.meta.c @@ -4,7 +4,7 @@ //- GENERATED CODE C_LINKAGE_BEGIN -String8 dmn_event_kind_string_table[21] = +String8 dmn_event_kind_string_table[22] = { str8_lit_comp("Null"), str8_lit_comp("Error"), @@ -15,6 +15,7 @@ str8_lit_comp("CreateThread"), str8_lit_comp("ExitThread"), str8_lit_comp("LoadModule"), str8_lit_comp("UnloadModule"), +str8_lit_comp("ModuleDebugInfo"), str8_lit_comp("Breakpoint"), str8_lit_comp("Trap"), str8_lit_comp("SingleStep"), diff --git a/src/demon/generated/demon.meta.h b/src/demon/generated/demon.meta.h index 24ee3bf3..bbbfc943 100644 --- a/src/demon/generated/demon.meta.h +++ b/src/demon/generated/demon.meta.h @@ -17,6 +17,7 @@ DMN_EventKind_CreateThread, DMN_EventKind_ExitThread, DMN_EventKind_LoadModule, DMN_EventKind_UnloadModule, +DMN_EventKind_ModuleDebugInfo, DMN_EventKind_Breakpoint, DMN_EventKind_Trap, DMN_EventKind_SingleStep, @@ -62,7 +63,7 @@ DMN_ExceptionKind_COUNT, } DMN_ExceptionKind; C_LINKAGE_BEGIN -extern String8 dmn_event_kind_string_table[21]; +extern String8 dmn_event_kind_string_table[22]; extern String8 dmn_exception_kind_string_table[5]; C_LINKAGE_END diff --git a/src/linux/base/linux_base.c b/src/linux/base/linux_base.c index aaaf71bf..9545350d 100644 --- a/src/linux/base/linux_base.c +++ b/src/linux/base/linux_base.c @@ -1571,10 +1571,36 @@ main(int argc, char **argv) info->initial_path = get_current_path(lnx_state.arena); } - // rjf: grab home directory + // rjf: grab program/user data paths { char *home = getenv("HOME"); - info->user_program_data_path = str8_cstring(home); + char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + char *xdg_cache_home = getenv("XDG_CACHE_HOME"); + char *xdg_state_home = getenv("XDG_STATE_HOME"); + if(xdg_config_home != 0) + { + info->user_program_config_data_path = str8_cstring(xdg_config_home); + } + else + { + info->user_program_config_data_path = str8f(lnx_state.arena, "%s/.config", home); + } + if(xdg_cache_home != 0) + { + info->user_program_cache_data_path = str8_cstring(xdg_cache_home); + } + else + { + info->user_program_cache_data_path = str8f(lnx_state.arena, "%s/.cache", home); + } + if(xdg_state_home != 0) + { + info->user_program_logs_data_path = str8_cstring(xdg_state_home); + } + else + { + info->user_program_logs_data_path = str8f(lnx_state.arena, "%s/.local/state", home); + } } scratch_end(scratch); diff --git a/src/linux/demon/linux_demon.c b/src/linux/demon/linux_demon.c index d3124366..e7571235 100644 --- a/src/linux/demon/linux_demon.c +++ b/src/linux/demon/linux_demon.c @@ -292,6 +292,237 @@ lnx_dmn_process_from_pid(pid_t pid) return hash_table_search_u64_raw(lnx_dmn_state->pid_ht, pid); } +//////////////////////////////// +//~ rjf: Module Info Parsing + +internal DMN_ModuleInfo * +lnx_dmn_module_info_from_process_module(Arena *arena, pid_t pid, int memory_fd, U64 base_vaddr, U64 name_vaddr, B32 is_main) +{ + DMN_ModuleInfo *info = push_array(arena, DMN_ModuleInfo, 1); + Temp scratch = scratch_begin(&arena, 1); + { + //- rjf: read ELF signature + U8 elf_sig_maybe[ELF_Identifier_Max] = {0}; + lnx_dmn_read(memory_fd, r1u64(base_vaddr, base_vaddr + sizeof(elf_sig_maybe)), elf_sig_maybe); + + //- rjf: unpack elf header info + Arch arch = Arch_Null; + U64 entry_point_voff = 0; + ELF_Type elf_type = ELF_Type_None; + U64 phdrs_off = 0; + U64 phdrs_entry_size = 0; + U64 phdrs_count = 0; + U64 shdrs_off = 0; + U64 shdrs_entry_size = 0; + U64 shdrs_count = 0; + U64 shdrs_strings_idx = 0; + { + // rjf: read ehdr + ELF_Hdr64 ehdr = {0}; + switch(elf_sig_maybe[ELF_Identifier_Class]) + { + default: + case ELF_Class_None: + {}break; + case ELF_Class_32: + { + ELF_Hdr32 ehdr32 = {0}; + lnx_dmn_read_struct(memory_fd, base_vaddr, &ehdr32); + MemoryCopy(ehdr.e_ident, ehdr32.e_ident, sizeof(ehdr.e_ident)); + ehdr.e_type = ehdr32.e_type; + ehdr.e_machine = ehdr32.e_machine; + ehdr.e_version = ehdr32.e_version; + ehdr.e_entry = (U64)ehdr32.e_entry; + ehdr.e_phoff = (U64)ehdr32.e_phoff; + ehdr.e_shoff = (U64)ehdr32.e_shoff; + ehdr.e_flags = ehdr32.e_flags; + ehdr.e_ehsize = ehdr32.e_ehsize; + ehdr.e_phentsize = ehdr32.e_phentsize; + ehdr.e_phnum = ehdr32.e_phnum; + ehdr.e_shentsize = ehdr32.e_shentsize; + ehdr.e_shnum = ehdr32.e_shnum; + ehdr.e_shstrndx = ehdr32.e_shstrndx; + }break; + case ELF_Class_64: + { + lnx_dmn_read_struct(memory_fd, base_vaddr, &ehdr); + }break; + } + + // rjf: unpack ehdr + arch = arch_from_elf_machine(ehdr.e_machine); + entry_point_voff = ehdr.e_entry - base_vaddr; + elf_type = ehdr.e_type; + phdrs_off = ehdr.e_phoff; + phdrs_entry_size = ehdr.e_phentsize; + phdrs_count = ehdr.e_phnum; + shdrs_off = ehdr.e_shoff; + shdrs_entry_size = ehdr.e_shentsize; + shdrs_count = ehdr.e_shnum; + shdrs_strings_idx = ehdr.e_shstrndx; + } + + //- rjf: decide on rebase for this module + U64 module_rebase = 0; + if(elf_type == ELF_Type_Dyn) + { + module_rebase = base_vaddr; + } + + //- rjf: determine shdrs/phdrs addresses + U64 phdrs_vaddr = module_rebase + phdrs_off; + U64 phdrs_vaddr_opl = phdrs_vaddr + phdrs_count*phdrs_entry_size; + U64 shdrs_vaddr = module_rebase + shdrs_off; + + //- rjf: scan phdrs, unpack info + U64 image_vsize = 0; + Rng1U64 eh_frame_header_vaddr_range = {0}; + { + for EachIndex(phdr_idx, phdrs_count) + { + U64 phdr_vaddr = phdrs_vaddr + phdr_idx*phdrs_entry_size; + ELF_Phdr64 phdr = {0}; + switch(elf_sig_maybe[ELF_Identifier_Class]) + { + default:{}break; + case ELF_Class_32: + { + ELF_Phdr32 phdr32 = {0}; + lnx_dmn_read_struct(memory_fd, phdr_vaddr, &phdr32); + phdr.p_type = phdr32.p_type; + phdr.p_flags = phdr32.p_flags; + phdr.p_offset = (U64)phdr32.p_offset; + phdr.p_vaddr = (U64)phdr32.p_vaddr; + phdr.p_paddr = (U64)phdr32.p_paddr; + phdr.p_filesz = (U64)phdr32.p_filesz; + phdr.p_memsz = (U64)phdr32.p_memsz; + phdr.p_align = (U64)phdr32.p_align; + }break; + case ELF_Class_64: + { + lnx_dmn_read_struct(memory_fd, phdr_vaddr, &phdr); + }break; + } + if(phdr.p_type == ELF_PType_Load) + { + image_vsize = (module_rebase + phdr.p_vaddr + phdr.p_memsz) - base_vaddr; + } + if(phdr.p_type == ELF_PType_GnuEHFrame) + { + eh_frame_header_vaddr_range = r1u64(module_rebase + phdr.p_vaddr, module_rebase + phdr.p_vaddr + phdr.p_memsz); + } + } + } + + //- rjf: scan shdrs, unpack info + Rng1U64 raddbg_info_voff_range = {0}; + U64 raddbg_is_attached_marker_voff = 0; + { + // rjf: read all shdrs + ELF_Shdr64 *shdrs = push_array(scratch.arena, ELF_Shdr64, shdrs_count); + for EachIndex(shdr_idx, shdrs_count) + { + U64 shdr_vaddr = shdrs_vaddr + shdr_idx*shdrs_entry_size; + switch(elf_sig_maybe[ELF_Identifier_Class]) + { + case ELF_Class_32: + { + ELF_Shdr32 shdr32 = {0}; + lnx_dmn_read_struct(memory_fd, shdr_vaddr, &shdr32); + shdrs[shdr_idx].sh_name = shdr32.sh_name; + shdrs[shdr_idx].sh_type = shdr32.sh_type; + shdrs[shdr_idx].sh_flags = (U64)shdr32.sh_flags; + shdrs[shdr_idx].sh_addr = (U64)shdr32.sh_addr; + shdrs[shdr_idx].sh_offset = (U64)shdr32.sh_offset; + shdrs[shdr_idx].sh_size = (U64)shdr32.sh_size; + shdrs[shdr_idx].sh_link = shdr32.sh_link; + shdrs[shdr_idx].sh_info = shdr32.sh_info; + shdrs[shdr_idx].sh_addralign= (U64)shdr32.sh_addralign; + shdrs[shdr_idx].sh_entsize = (U64)shdr32.sh_entsize; + }break; + case ELF_Class_64: + { + lnx_dmn_read_struct(memory_fd, shdr_vaddr, &shdrs[shdr_idx]); + }break; + } + } + + // rjf: get string shdr + ELF_Shdr64 *string_shdr = 0; + if(shdrs_strings_idx < shdrs_count) + { + string_shdr = &shdrs[shdrs_strings_idx]; + } + + // rjf: scan shdrs, using names + if(string_shdr != 0) + { + for EachIndex(shdr_idx, shdrs_count) + { + ELF_Shdr64 *shdr = &shdrs[shdr_idx]; + U64 name_vaddr = string_shdr->sh_addr + shdr->sh_name; + String8 shdr_name = lnx_dmn_read_string(scratch.arena, memory_fd, name_vaddr); + if(str8_match(shdr_name, s(".raddbg"), 0)) + { + raddbg_info_voff_range = r1u64(shdr->sh_addr - base_vaddr, shdr->sh_addr - base_vaddr + shdr->sh_size); + } + if(str8_match(shdr_name, s(".rdbgia"), 0)) + { + raddbg_is_attached_marker_voff = (shdr->sh_addr - base_vaddr); + } + } + } + } + + //- rjf: unpack tls info + U64 tls_index = max_U64; + U64 tls_offset = max_U64; + if(is_main) + { + tls_index = 1; + tls_offset = 0; + } + else if(lnx_dmn_state->is_tls_detected) + { + Rng1U64 tls_modid_range = r1u64(lnx_dmn_state->tls_modid_desc.offset, lnx_dmn_state->tls_modid_desc.offset + lnx_dmn_state->tls_modid_desc.bit_size / 8); + Rng1U64 tls_offset_range = r1u64(lnx_dmn_state->tls_offset_desc.offset, lnx_dmn_state->tls_offset_desc.offset + lnx_dmn_state->tls_offset_desc.bit_size / 8); + tls_modid_range = shift_1u64(tls_modid_range, base_vaddr); + tls_offset_range = shift_1u64(tls_offset_range, base_vaddr); + tls_modid_range.max = Min(tls_modid_range.max, tls_modid_range.min + sizeof(tls_index)); + tls_offset_range.max = Min(tls_offset_range.max, tls_offset_range.min + sizeof(tls_offset)); + lnx_dmn_read(memory_fd, tls_modid_range, &tls_index); + lnx_dmn_read(memory_fd, tls_offset_range, &tls_offset); + } + + //- rjf: read module path + String8 module_path = lnx_dmn_read_string(arena, memory_fd, name_vaddr); + + //- rjf: decide on debug info key info + String8 debug_info_path = module_path; + Guid debug_info_guid = {0}; + U64 debug_info_timestamp = 0; + U64 debug_info_age = 0; + + //- rjf: fill result + info->arch = arch; + info->vsize = image_vsize; + info->entry_point_voff = entry_point_voff; + info->module_path = module_path; + info->debug_info_path = debug_info_path; + info->debug_info_guid = debug_info_guid; + info->debug_info_timestamp = debug_info_timestamp; + info->debug_info_age = debug_info_age; + info->tls_index = tls_index; + info->tls_offset = tls_offset; + info->eh_frame_header_vaddr_range = eh_frame_header_vaddr_range; + info->cfi_rebase = module_rebase; + info->raddbg_info_voff_range = raddbg_info_voff_range; + info->raddbg_is_attached_marker_voff = raddbg_is_attached_marker_voff; + } + scratch_end(scratch); + return info; +} + //////////////////////////////// //~ rjf: Trap Setting @@ -1681,7 +1912,9 @@ internal void lnx_dmn_push_event_load_module(Arena *arena, DMN_EventList *events, LNX_DMN_Thread *thread, LNX_DMN_Module *module) { // TODO: reporting this module breaks ctrl thread - String8 module_name = lnx_dmn_read_string(arena, thread->process->fd, module->name_vaddr); + LNX_DMN_Process *process = thread->process; + DMN_ModuleInfo *module_info = lnx_dmn_module_info_from_process_module(arena, process->pid, process->fd, module->base_vaddr, module->name_vaddr, module->is_main); + String8 module_name = module_info->module_path; if(!str8_match(module_name, str8_lit("linux-vdso.so.1"), 0)) { DMN_Event *e = dmn_event_list_push(arena, events); @@ -1689,14 +1922,11 @@ lnx_dmn_push_event_load_module(Arena *arena, DMN_EventList *events, LNX_DMN_Thre e->process = lnx_dmn_handle_from_process(thread->process); e->thread = lnx_dmn_handle_from_thread(thread); e->module = lnx_dmn_handle_from_module(module); - e->arch = thread->process->ctx->arch; + e->arch = module_info->arch; e->address = module->base_vaddr; e->size = module->size; e->string = module_name; - e->elf_phdr_vrange = r1u64(module->phvaddr, module->phvaddr + module->phentsize * module->phcount); - e->elf_phdr_entsize = module->phentsize; - e->tls_index = module->tls_index; - e->tls_offset = module->tls_offset; + e->module_info = module_info; } } @@ -1787,7 +2017,7 @@ lnx_dmn_push_event_exception(Arena *arena, DMN_EventList *events, LNX_DMN_Thread e->thread = lnx_dmn_handle_from_thread(thread); e->instruction_pointer = lnx_dmn_thread_read_ip(thread); e->address = e->instruction_pointer; - e->signo = signo; + e->code = signo; e->exception_repeated = signo < ArrayCount(is_repeatable) ? is_repeatable[signo] : 0; if(signo == SIGSEGV) @@ -3009,18 +3239,6 @@ dmn_process_write(DMN_Handle process_handle, Rng1U64 range, void *src) //- rjf: threads -internal Arch -dmn_arch_from_thread(DMN_Handle thread_handle) -{ - Arch arch = Arch_Null; - LNX_DMN_Thread *thread = lnx_dmn_thread_from_handle(thread_handle); - if(thread) - { - arch = thread->process->ctx->arch; - } - return arch; -} - internal U64 dmn_stack_base_vaddr_from_thread(DMN_Handle thread_handle) { diff --git a/src/linux/demon/linux_demon.h b/src/linux/demon/linux_demon.h index fafdc925..721a2570 100644 --- a/src/linux/demon/linux_demon.h +++ b/src/linux/demon/linux_demon.h @@ -420,6 +420,11 @@ internal LNX_DMN_Auxv lnx_dmn_auxv_from_pid(pid_t pid, ELF_Class elf_class) internal LNX_DMN_Thread * lnx_dmn_thread_from_pid(pid_t pid); internal LNX_DMN_Process * lnx_dmn_process_from_pid(pid_t pid); +//////////////////////////////// +//~ rjf: Module Info Parsing + +internal DMN_ModuleInfo *lnx_dmn_module_info_from_process_module(Arena *arena, pid_t pid, int memory_fd, U64 base_vaddr, U64 link_map_vaddr, B32 is_main); + //////////////////////////////// //~ Entity diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index ce778538..7490c613 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -8817,16 +8817,15 @@ rd_window_frame(void) Vec4F32 hover_color = ui_color_from_tags_key_name(box->tags_key, str8_lit("hover")); // rjf: brighten + if(is_hot) { Vec4F32 color = hover_color; - color.w *= 0.05f; - if(!is_hot) - { - color.w *= t; - } + color.w *= 0.015f; R_Rect2DInst *inst = dr_rect(pad_2f32(box->rect, 1.f), v4f32(0, 0, 0, 0), 0, 0, border_softness*1.f); inst->colors[Corner_00] = color; inst->colors[Corner_10] = color; + inst->colors[Corner_01] = color; + inst->colors[Corner_11] = color; MemoryCopyArray(inst->corner_radii, box_corner_radii); } @@ -8834,7 +8833,7 @@ rd_window_frame(void) if(box->hot_t > 0.01f) DR_ClipScope(intersect_2f32(box->rect, dr_top_clip())) { Vec4F32 color = hover_color; - color.w *= 0.02f; + color.w *= 0.025f; if(!is_hot) { color.w *= t; @@ -8842,7 +8841,7 @@ rd_window_frame(void) Vec2F32 center = ui_mouse(); Vec2F32 box_dim = dim_2f32(box->rect); F32 max_dim = Max(box_dim.x, box_dim.y); - F32 radius = box->font_size*12.f; + F32 radius = box->font_size*24.f; radius = Min(max_dim, radius); dr_rect(pad_2f32(r2f32(center, center), radius), color, radius, 0, radius/3.f); } @@ -9485,7 +9484,7 @@ rd_theme_tree_from_name(Arena *arena, Access *access, String8 theme_name) } if(theme_tree == &md_nil_node) { - String8 path = str8f(scratch.arena, "%S/%Sraddbg/themes/%S", program_data_folder_prefix_from_os(OperatingSystem_CURRENT), get_process_info()->user_program_data_path, theme_name); + String8 path = str8f(scratch.arena, "%S/raddbg/themes/%S", get_process_info()->user_program_config_data_path, theme_name); U64 endt_us = now_time_us()+100; if(rd_state->frame_index <= 5) { @@ -10537,8 +10536,8 @@ rd_init(CmdLine *cmdln) } } { - String8 user_program_data_path = get_process_info()->user_program_data_path; - String8 user_data_folder = push_str8f(scratch2.arena, "%S/raddbg", user_program_data_path); + String8 user_program_config_data_path = get_process_info()->user_program_config_data_path; + String8 user_data_folder = str8f(scratch2.arena, "%S/raddbg", user_program_config_data_path); make_directory(user_data_folder); if(user_path.size == 0) { @@ -10546,12 +10545,12 @@ rd_init(CmdLine *cmdln) } if(user_path.size == 0) { - String8 last_user_path = push_str8f(scratch2.arena, "%S/last_user", user_data_folder); + String8 last_user_path = str8f(scratch2.arena, "%S/last_user", user_data_folder); user_path = data_from_file_path(scratch2.arena, last_user_path); } if(user_path.size == 0) { - user_path = push_str8f(scratch2.arena, "%S/default.raddbg_user", user_data_folder); + user_path = str8f(scratch2.arena, "%S/default.raddbg_user", user_data_folder); } } if(project_path.size == 0) @@ -13211,7 +13210,7 @@ rd_frame(void) case RD_CmdKind_RecordUserAsLastOpened: { String8 file_path = rd_regs()->file_path; - String8 last_user_path = str8f(scratch.arena, "%S/%Sraddbg/last_user", get_process_info()->user_program_data_path, program_data_folder_prefix_from_os(OperatingSystem_CURRENT)); + String8 last_user_path = str8f(scratch.arena, "%S/raddbg/last_user", get_process_info()->user_program_config_data_path); write_data_to_file_path(last_user_path, file_path); }break; @@ -15777,7 +15776,7 @@ rd_frame(void) String8 name = rd_regs()->string; if(name.size != 0) { - String8 themes_folder = str8f(scratch.arena, "%S/%Sraddbg/themes", get_process_info()->user_program_data_path, program_data_folder_prefix_from_os(OperatingSystem_CURRENT)); + String8 themes_folder = str8f(scratch.arena, "%S/raddbg/themes", get_process_info()->user_program_config_data_path); if(make_directory(themes_folder)) { String8 dst_path = push_str8f(scratch.arena, "%S/%S", themes_folder, name); diff --git a/src/raddbg/raddbg_eval.c b/src/raddbg/raddbg_eval.c index ffd50cee..94dca6ff 100644 --- a/src/raddbg/raddbg_eval.c +++ b/src/raddbg/raddbg_eval.c @@ -122,7 +122,7 @@ E_TYPE_EXPAND_INFO_FUNCTION_DEF(themes) //- rjf: gather theme files { - String8 theme_folder = push_str8f(scratch.arena, "%S/%Sraddbg/themes", get_process_info()->user_program_data_path, program_data_folder_prefix_from_os(OperatingSystem_CURRENT)); + String8 theme_folder = push_str8f(scratch.arena, "%S/raddbg/themes", get_process_info()->user_program_config_data_path); FileIter *it = file_iter_begin(scratch.arena, theme_folder, FileIterFlag_SkipFolders); for(FileInfo info = {0}; file_iter_next(scratch.arena, it, &info);) { diff --git a/src/raddbg/raddbg_main.c b/src/raddbg/raddbg_main.c index c0f994cd..b40438bb 100644 --- a/src/raddbg/raddbg_main.c +++ b/src/raddbg/raddbg_main.c @@ -6,9 +6,9 @@ // // [ ] jeff stepping bug // [ ] wassim memory bug -// [ ] test concepts -> base // [ ] switch off spoofs for step-over -// [ ] debugger, d2r, p2r exemplar / determinism testing +// [x] test concepts -> base +// [x] debugger, d2r, p2r exemplar / determinism testing // // [ ] symbol server // [ ] core dump saving/loading @@ -540,8 +540,8 @@ entry_point(CmdLine *cmd_line) g_logs_folder = cmd_line_string(cmd_line, str8_lit("logs")); if(g_logs_folder.size == 0) { - String8 user_program_data_path = get_process_info()->user_program_data_path; - g_logs_folder = push_str8f(scratch.arena, "%S/%Sraddbg/logs", program_data_folder_prefix_from_os(OperatingSystem_CURRENT), user_program_data_path); + String8 user_program_logs_data_path = get_process_info()->user_program_logs_data_path; + g_logs_folder = push_str8f(scratch.arena, "%S/raddbg/logs", user_program_logs_data_path); } make_directory(g_logs_folder); diff --git a/src/raddbg/raddbg_views.c b/src/raddbg/raddbg_views.c index 0a532fc4..db0b38af 100644 --- a/src/raddbg/raddbg_views.c +++ b/src/raddbg/raddbg_views.c @@ -1865,7 +1865,7 @@ rd_info_from_watch_row_cell(Arena *arena, EV_Row *row, EV_StringFlags string_fla String8 file_name = str8_from_rdi_string_idx(rdi, path_node->name_string_idx); // rjf: push line location - if(file_name.size != 0) UI_TagF("weak") + if(line.file_idx != 0) UI_TagF("weak") { String8 loc = str8f(arena, "%S:%i", file_name, line.line_num); DR_FStrParams params = {rd_font_from_slot(RD_FontSlot_Main), rd_raster_flags_from_slot(RD_FontSlot_Main), ui_color_from_name(str8_lit("text")), font_size*0.85f, 0, 0}; diff --git a/src/win32/base/win32_base.c b/src/win32/base/win32_base.c index 1506a3e7..b830e42b 100644 --- a/src/win32/base/win32_base.c +++ b/src/win32/base/win32_base.c @@ -1922,7 +1922,8 @@ w32_entry_point_caller(int argc, WCHAR **wargv) U16 *buffer = push_array_no_zero(scratch.arena, U16, size); if(SUCCEEDED(SHGetFolderPathW(0, CSIDL_APPDATA, 0, 0, (WCHAR*)buffer))) { - info->user_program_data_path = str8_from_16(arena, str16_cstring(buffer)); + info->user_program_config_data_path = str8_from_16(arena, str16_cstring(buffer)); + info->user_program_cache_data_path = info->user_program_logs_data_path = info->user_program_config_data_path; } scratch_end(scratch); } diff --git a/src/win32/demon/win32_demon.c b/src/win32/demon/win32_demon.c index 78f7d38f..acceaa52 100644 --- a/src/win32/demon/win32_demon.c +++ b/src/win32/demon/win32_demon.c @@ -337,6 +337,62 @@ w32_dmn_process_write(HANDLE process, Rng1U64 range, void *src) return result; } +internal String8 +w32_dmn_str8_cstring_from_process_vaddr(Arena *arena, HANDLE process, U64 vaddr) +{ + Temp scratch = scratch_begin(&arena, 1); + String8List pieces = {0}; + U64 chunk_cap = 256; + U64 chunk_size = chunk_cap; + U64 vaddr_opl = vaddr + 16384; + U8 *chunk_buffer = push_array(scratch.arena, U8, chunk_cap); + for(U64 read_vaddr = vaddr; read_vaddr < vaddr_opl;) + { + // rjf: zero / read next chunk - shrink chunk size if needed + MemoryZero(chunk_buffer, chunk_size); + U64 bytes_read = 0; + for(;bytes_read == 0 && chunk_size != 0;) + { + bytes_read = w32_dmn_process_read(process, r1u64(vaddr, vaddr+chunk_size), chunk_buffer); + if(bytes_read == 0) + { + chunk_size /= 2; + } + } + + // rjf: advance + vaddr += bytes_read; + + // rjf: look for null terminator + B32 found_null_terminator = 0; + U64 read_chunk_size = bytes_read; + for(U64 idx = 0; idx < bytes_read; idx += 1) + { + if(chunk_buffer[idx] == 0) + { + read_chunk_size = idx; + found_null_terminator = 1; + break; + } + } + + // rjf: nonzero chunk size -> push to piece list + if(read_chunk_size != 0) + { + str8_list_push(scratch.arena, &pieces, str8_copy(scratch.arena, str8(chunk_buffer, read_chunk_size))); + } + + // rjf: if this chunk contained a null terminator, or if we didn't read anything -> we're done + if(found_null_terminator || bytes_read == 0) + { + break; + } + } + String8 result = str8_list_join(arena, &pieces, 0); + scratch_end(scratch); + return result; +} + internal String8 w32_dmn_read_memory_str(Arena *arena, HANDLE process_handle, U64 address) { @@ -438,126 +494,319 @@ w32_dmn_read_memory_str16(Arena *arena, HANDLE process_handle, U64 address) return(result); } -internal W32_DMN_ImageInfo -w32_dmn_image_info_from_process_base_vaddr(HANDLE process, U64 base_vaddr) +//- rjf: modules + +internal DMN_ModuleInfo * +w32_dmn_module_info_from_process_module(Arena *arena, HANDLE process, HANDLE module, U64 base_vaddr, U64 module_name_vaddr, B32 module_name_is_unicode, B32 is_main_module) { - // rjf: find PE offset - U32 pe_offset = 0; + DMN_ModuleInfo *info = push_array(arena, DMN_ModuleInfo, 1); + Temp scratch = scratch_begin(&arena, 1); { - U64 dos_magic_off = base_vaddr; - U16 dos_magic = 0; - w32_dmn_process_read_struct(process, dos_magic_off, &dos_magic); - if(dos_magic == PE_DOS_MAGIC) - { - U64 pe_offset_off = base_vaddr + OffsetOf(PE_DosHeader, coff_file_offset); - w32_dmn_process_read_struct(process, pe_offset_off, &pe_offset); - } - } - - // rjf: get COFF header - B32 got_coff_header = 0; - U64 coff_header_off = 0; - COFF_FileHeader coff_header = {0}; - if(pe_offset > 0) - { - U64 pe_magic_off = base_vaddr + pe_offset; - U32 pe_magic = 0; - w32_dmn_process_read_struct(process, pe_magic_off, &pe_magic); - if(pe_magic == PE_MAGIC) - { - coff_header_off = pe_magic_off + sizeof(pe_magic); - got_coff_header = w32_dmn_process_read_struct(process, coff_header_off, &coff_header); - } - } - - W32_DMN_ImageInfo result = zero_struct; - if(got_coff_header) - { - U64 optional_off = coff_header_off + sizeof(COFF_FileHeader); + B32 is_good = 1; - Arch arch = arch_from_coff_machine(coff_header.machine); - U64 image_size = 0; - U64 data_dir_count = 0; - U64 data_dir_off = max_U64; - U64 tls_index = max_U64; - - // parse optional header - if(pe_has_plus_header(coff_header.machine)) + //- rjf: read DOS header + PE_DosHeader dos_header = {0}; + if(is_good) { - PE_OptionalHeader32Plus opt_header = {0}; - if(w32_dmn_process_read_struct(process, optional_off, &opt_header)) + w32_dmn_process_read_struct(process, base_vaddr, &dos_header); + is_good = (dos_header.magic == PE_DOS_MAGIC); + } + + //- rjf: read PE magic + U32 pe_magic = 0; + if(is_good) + { + w32_dmn_process_read_struct(process, base_vaddr + dos_header.coff_file_offset, &pe_magic); + is_good = (pe_magic == PE_MAGIC); + } + + //- rjf: read COFF header + U64 file_header_off = dos_header.coff_file_offset + sizeof(pe_magic); + COFF_FileHeader file_header = {0}; + if(is_good) + { + w32_dmn_process_read_struct(process, base_vaddr + file_header_off, &file_header); + } + + //- rjf: get architecture + Arch arch = arch_from_coff_machine(file_header.machine); + + //- rjf: get module path + String8 module_path = {0}; + { + String8 module_path8 = {0}; + String16 module_path16 = {0}; + + // rjf: try to get module path from handle + if(module_path16.size == 0 && module != 0) { - image_size = opt_header.sizeof_image; - data_dir_count = opt_header.data_dir_count; - data_dir_off = optional_off + sizeof(opt_header); + DWORD cap16 = GetFinalPathNameByHandleW(module, 0, 0, VOLUME_NAME_DOS); + U16 *buffer16 = push_array_no_zero(scratch.arena, U16, cap16); + DWORD size16 = GetFinalPathNameByHandleW(module, (WCHAR*)buffer16, cap16, VOLUME_NAME_DOS); + module_path16 = str16(buffer16, size16); } - else { Assert(0 && "failed to read optional header"); } - } - else - { - PE_OptionalHeader32 opt_header = {0}; - if(w32_dmn_process_read_struct(process, optional_off, &opt_header)) + + // rjf: fallback (main module only): process -> full path + if(module_path16.size == 0 && is_main_module) { - image_size = opt_header.sizeof_image; - data_dir_count = opt_header.data_dir_count; - data_dir_off = optional_off + sizeof(opt_header); - } - else { Assert(0 && "failed to read optional header"); } - } - - // extract TLS index - if(PE_DataDirectoryIndex_TLS < data_dir_count) - { - U64 tls_dir_vaddr = data_dir_off + PE_DataDirectoryIndex_TLS * sizeof(PE_DataDirectory); - PE_DataDirectory tls_dir = {0}; - if(w32_dmn_process_read_struct(process, tls_dir_vaddr, &tls_dir)) - { - if(pe_has_plus_header(coff_header.machine)) + DWORD size = 4096; + U16 *buf = push_array_no_zero(scratch.arena, U16, size); + if(QueryFullProcessImageNameW(process, 0, (WCHAR*)buf, &size)) { - if(tls_dir.virt_size == sizeof(PE_TLSHeader64)) - { - PE_TLSHeader64 tls_header = {0}; - if(w32_dmn_process_read_struct(process, base_vaddr + tls_dir.virt_off, &tls_header)) - { - U64 tls_index64 = 0; - if(w32_dmn_process_read_struct(process, tls_header.index_address, &tls_index64)) - { - tls_index = tls_index64; - } - else { Assert(0 && "failed to read TLS Index 64"); } - } - else { Assert(0 && "failed to read TLS Header 64"); } - } + module_path16 = str16(buf, size); } - else + } + + // rjf: fallback (any module - no guarantee): address_of_name -> full path + if(module_path16.size == 0 && module_name_vaddr != 0) + { + U64 ptr_size = bit_size_from_arch(arch)/8; + U64 name_pointer = 0; + if(w32_dmn_process_read(process, r1u64(module_name_vaddr, module_name_vaddr+ptr_size), &name_pointer)) { - if(tls_dir.virt_size == sizeof(PE_TLSHeader32)) + if(name_pointer != 0) { - PE_TLSHeader32 tls_header = {0}; - if(w32_dmn_process_read_struct(process, base_vaddr + tls_dir.virt_off, &tls_header)) + if(module_name_is_unicode) { - U32 tls_index32 = 0; - if(w32_dmn_process_read_struct(process, tls_header.index_address, &tls_index32)) - { - tls_index = tls_index32; - } - else { Assert(0 && "failed to read TLS Index 32"); } + module_path16 = w32_dmn_read_memory_str16(scratch.arena, process, name_pointer); + } + else + { + module_path8 = w32_dmn_read_memory_str(scratch.arena, process, name_pointer); } - else { Assert(0 && "failed to read TLS Header32"); } } } } - else { Assert(0 && "failed to read TLS directory"); } + + // rjf: skip the extended path thing if necessary + { + if(module_path16.size >= 4 && + module_path16.str[0] == L'\\' && + module_path16.str[1] == L'\\' && + module_path16.str[2] == L'?' && + module_path16.str[3] == L'\\') + { + module_path16.size -= 4; + module_path16.str += 4; + } + else if(module_path8.size >= 4 && + module_path8.str[0] == L'\\' && + module_path8.str[1] == L'\\' && + module_path8.str[2] == L'?' && + module_path8.str[3] == L'\\') + { + module_path8.size -= 4; + module_path8.str += 4; + } + } + + // rjf: 16 -> 8 + if(module_path16.size != 0) + { + module_path = str8_from_16(arena, module_path16); + } + else if(module_path8.size != 0) + { + module_path = str8_copy(arena, module_path8); + } } - // fill out result - result.arch = arch; - result.size = image_size; - result.tls_index = tls_index; + //- rjf: unpack range of optional extension header + U32 opt_ext_size = file_header.optional_header_size; + Rng1U64 opt_ext_off_range = r1u64(file_header_off + sizeof(COFF_FileHeader), + file_header_off + sizeof(COFF_FileHeader) + opt_ext_size); + + //- rjf: read optional header + U64 image_vsize = 0; + U64 entry_point_voff = 0; + U64 tls_index = 0; + String8 debug_info_path = {0}; + Guid debug_info_guid = {0}; + U64 debug_info_timestamp = 0; + U64 debug_info_age = 0; + Rng1U64 pe_intel_pdatas_vaddr_range = {0}; + Rng1U64 raddbg_info_voff_range = {0}; + U64 raddbg_is_attached_marker_voff = 0; + if(opt_ext_size > 0) + { + // rjf: read magic number + U16 opt_ext_magic = 0; + w32_dmn_process_read_struct(process, base_vaddr + opt_ext_off_range.min, &opt_ext_magic); + + // rjf: read info + U32 reported_data_dir_offset = 0; + U32 reported_data_dir_count = 0; + switch(opt_ext_magic) + { + case PE_PE32_MAGIC: + { + PE_OptionalHeader32 pe_optional = {0}; + w32_dmn_process_read_struct(process, base_vaddr + opt_ext_off_range.min, &pe_optional); + image_vsize = pe_optional.sizeof_image; + entry_point_voff = pe_optional.entry_point_va; + reported_data_dir_offset = sizeof(pe_optional); + reported_data_dir_count = pe_optional.data_dir_count; + }break; + case PE_PE32PLUS_MAGIC: + { + PE_OptionalHeader32Plus pe_optional = {0}; + w32_dmn_process_read_struct(process, base_vaddr + opt_ext_off_range.min, &pe_optional); + image_vsize = pe_optional.sizeof_image; + entry_point_voff = pe_optional.entry_point_va; + reported_data_dir_offset = sizeof(pe_optional); + reported_data_dir_count = pe_optional.data_dir_count; + }break; + } + + // rjf: find number of data directories + U32 data_dir_max = (opt_ext_size - reported_data_dir_offset) / sizeof(PE_DataDirectory); + U32 data_dir_count = ClampTop(reported_data_dir_count, data_dir_max); + + // rjf: extract sections + U64 sec_array_off = opt_ext_off_range.max; + U64 sec_count = file_header.section_count; + COFF_SectionHeader *sec = push_array(scratch.arena, COFF_SectionHeader, sec_count); + w32_dmn_process_read(process, r1u64(base_vaddr + sec_array_off, base_vaddr + sec_array_off + sec_count*sizeof(COFF_SectionHeader)), sec); + + // rjf: grab pdatas range from exceptions section + if(data_dir_count > PE_DataDirectoryIndex_EXCEPTIONS) + { + PE_DataDirectory dir = {0}; + w32_dmn_process_read_struct(process, base_vaddr + opt_ext_off_range.min + reported_data_dir_offset + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_EXCEPTIONS, &dir); + Rng1U64 pdatas_voff_range = r1u64((U64)dir.virt_off, (U64)dir.virt_off + (U64)dir.virt_size); + pe_intel_pdatas_vaddr_range = r1u64(pdatas_voff_range.min + base_vaddr, pdatas_voff_range.max + base_vaddr); + } + + // rjf: grab tls info + if(data_dir_count > PE_DataDirectoryIndex_TLS) + { + PE_DataDirectory tls_dir = {0}; + w32_dmn_process_read_struct(process, base_vaddr + opt_ext_off_range.min + reported_data_dir_offset + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_TLS, &tls_dir); + if(tls_dir.virt_size == sizeof(PE_TLSHeader64)) + { + PE_TLSHeader64 tls_header = {0}; + w32_dmn_process_read_struct(process, base_vaddr + tls_dir.virt_off, &tls_header); + U64 tls_index64 = 0; + w32_dmn_process_read_struct(process, tls_header.index_address, &tls_index64); + tls_index = tls_index64; + } + else if(tls_dir.virt_size == sizeof(PE_TLSHeader32)) + { + PE_TLSHeader32 tls_header = {0}; + w32_dmn_process_read_struct(process, base_vaddr + tls_dir.virt_off, &tls_header); + U32 tls_index32 = 0; + w32_dmn_process_read_struct(process, tls_header.index_address, &tls_index32); + tls_index = (U64)tls_index32; + } + } + + // rjf: grab data about DWARF debug info, hinted at via symbol array in header + { + U64 symbol_array_off = file_header.symbol_table_foff; + U64 symbol_count = file_header.symbol_count; + if(symbol_array_off != 0) + { + debug_info_path = module_path; + } + } + + // rjf: grab data about PDB/RDI debug info, expressed via debug data directory + if(debug_info_path.size == 0 && data_dir_count > PE_DataDirectoryIndex_DEBUG) + { + // rjf: read data dir + PE_DataDirectory dir = {0}; + w32_dmn_process_read_struct(process, base_vaddr + opt_ext_off_range.min + reported_data_dir_offset + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_DEBUG, &dir); + + U64 dbg_dir_count = dir.virt_size / sizeof(PE_DebugDirectory); + for(U64 dbg_dir_idx = 0; dbg_dir_idx < dbg_dir_count; dbg_dir_idx += 1) + { + // rjf: read debug directory + U64 dir_addr = base_vaddr + dir.virt_off + dbg_dir_idx * sizeof(PE_DebugDirectory); + PE_DebugDirectory dbg_data = {0}; + w32_dmn_process_read_struct(process, dir_addr, &dbg_data); + + // rjf: extract external file info from codeview header + if(dbg_data.type == PE_DebugDirectoryType_CODEVIEW) + { + U32 cv_magic = 0; + w32_dmn_process_read_struct(process, base_vaddr + dbg_data.voff, &cv_magic); + switch(cv_magic) + { + default:break; + case PE_CODEVIEW_PDB20_MAGIC: + { + PE_CvHeaderPDB20 cv = {0}; + U64 read_size = w32_dmn_process_read_struct(process, base_vaddr+dbg_data.voff, &cv); + if(read_size == sizeof(cv)) + { + U64 pdb_dbg_path_vaddr = base_vaddr + dbg_data.voff + sizeof(cv); + debug_info_timestamp = cv.time_stamp; + debug_info_age = cv.age; + debug_info_path = w32_dmn_str8_cstring_from_process_vaddr(arena, process, pdb_dbg_path_vaddr); + } + }break; + case PE_CODEVIEW_PDB70_MAGIC: + { + PE_CvHeaderPDB70 cv = {0}; + U64 read_size = w32_dmn_process_read_struct(process, base_vaddr + dbg_data.voff, &cv); + if(read_size == sizeof(cv)) + { + U64 pdb_dbg_path_vaddr = base_vaddr + dbg_data.voff + sizeof(cv); + debug_info_guid = cv.guid; + debug_info_age = cv.age; + debug_info_path = w32_dmn_str8_cstring_from_process_vaddr(arena, process, pdb_dbg_path_vaddr); + } + }break; + case PE_CODEVIEW_RDI_MAGIC: + { + PE_CvHeaderRDI cv = {0}; + U64 read_size = w32_dmn_process_read_struct(process, base_vaddr + dbg_data.voff, &cv); + if(read_size == sizeof(cv)) + { + U64 rdi_dbg_path_vaddr = base_vaddr + dbg_data.voff + sizeof(cv); + debug_info_guid = cv.guid; + debug_info_path = w32_dmn_str8_cstring_from_process_vaddr(arena, process, rdi_dbg_path_vaddr); + } + }break; + } + } + } + } + + // rjf: find ranges of special raddbg data + { + for EachIndex(idx, sec_count) + { + String8 section_name = str8_cstring((char *)sec[idx].name); + if(str8_match(section_name, str8_lit(".raddbg"), 0)) + { + raddbg_info_voff_range.min = sec[idx].voff; + raddbg_info_voff_range.max = sec[idx].voff + sec[idx].vsize; + } + else if(str8_match(section_name, str8_lit(".rdbgia"), 0)) + { + raddbg_is_attached_marker_voff = sec[idx].voff; + } + } + } + } + + //- rjf: fill info + info->arch = arch; + info->vsize = image_vsize; + info->entry_point_voff = entry_point_voff; + info->module_path = module_path; + info->debug_info_path = debug_info_path; + info->debug_info_guid = debug_info_guid; + info->debug_info_timestamp = debug_info_timestamp; + info->debug_info_age = debug_info_age; + info->tls_index = tls_index; + info->pe_intel_pdatas_vaddr_range = pe_intel_pdatas_vaddr_range; + info->raddbg_info_voff_range = raddbg_info_voff_range; + info->raddbg_is_attached_marker_voff = raddbg_is_attached_marker_voff; } - else { Assert(0 && "failed to find COFF file header"); } - - return result; + scratch_end(scratch); + return info; } //- rjf: threads @@ -1792,25 +2041,31 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) U64 module_base = (U64)evt.u.CreateProcessInfo.lpBaseOfImage; U64 module_name_vaddr = (U64)evt.u.CreateProcessInfo.lpImageName; B32 module_name_is_unicode = (evt.u.CreateProcessInfo.fUnicode != 0); - W32_DMN_ImageInfo image_info = w32_dmn_image_info_from_process_base_vaddr(process_handle, module_base); - // rjf: create entities (thread/module are implied for initial - they are not reported by win32) + // rjf: parse module info + DMN_ModuleInfo *module_info = w32_dmn_module_info_from_process_module(arena, process_handle, module_handle, module_base, module_name_vaddr, module_name_is_unicode, 1); + + // rjf: create process/module entities (module is implied for processes - not reported directly via module events) W32_DMN_Entity *process = w32_dmn_entity_alloc(w32_dmn_shared->entities_base, W32_DMN_EntityKind_Process, evt.dwProcessId); - W32_DMN_Entity *thread = w32_dmn_entity_alloc(process, W32_DMN_EntityKind_Thread, evt.dwThreadId); W32_DMN_Entity *module = w32_dmn_entity_alloc(process, W32_DMN_EntityKind_Module, module_base); { process->handle = process_handle; - process->arch = image_info.arch; - thread->handle = thread_handle; - thread->arch = image_info.arch; - thread->thread.thread_local_base = tls_base; + process->arch = module_info->arch; module->handle = module_handle; - module->module.vaddr_range = r1u64(module_base, image_info.size); + module->module.vaddr_range = r1u64(module_base, module_base + module_info->vsize); module->module.is_main = 1; module->module.address_of_name_pointer = module_name_vaddr; module->module.name_is_unicode = module_name_is_unicode; } + // rjf: create main thread entity (implied from process event, see above) + W32_DMN_Entity *thread = w32_dmn_entity_alloc(process, W32_DMN_EntityKind_Thread, evt.dwThreadId); + { + thread->handle = thread_handle; + thread->arch = module_info->arch; + thread->thread.thread_local_base = tls_base; + } + // rjf: put thread into suspended state, so it matches expected initial state SuspendThread(thread_handle); @@ -1833,7 +2088,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) DMN_Event *e = dmn_event_list_push(arena, &events); e->kind = DMN_EventKind_CreateProcess; e->process = w32_dmn_handle_from_entity(process); - e->arch = image_info.arch; + e->arch = module_info->arch; e->code = evt.dwProcessId; e->tls_model = DMN_TlsModel_WinodwsNt; } @@ -1844,21 +2099,21 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) e->kind = DMN_EventKind_CreateThread; e->process = w32_dmn_handle_from_entity(process); e->thread = w32_dmn_handle_from_entity(thread); - e->arch = image_info.arch; + e->arch = module_info->arch; e->code = evt.dwThreadId; } // rjf: load module { DMN_Event *e = dmn_event_list_push(arena, &events); - e->kind = DMN_EventKind_LoadModule; - e->process = w32_dmn_handle_from_entity(process); - e->module = w32_dmn_handle_from_entity(module); - e->arch = image_info.arch; - e->address = module_base; - e->size = image_info.size; - e->string = w32_dmn_full_path_from_module(arena, module); - e->tls_index = image_info.tls_index; + e->kind = DMN_EventKind_LoadModule; + e->process = w32_dmn_handle_from_entity(process); + e->module = w32_dmn_handle_from_entity(module); + e->arch = module_info->arch; + e->address = module_base; + e->size = module_info->vsize; + e->string = module_info->module_path; + e->module_info = module_info; } } }break; @@ -2010,29 +2265,32 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) // rjf: extract image info U64 module_base = (U64)evt.u.LoadDll.lpBaseOfDll; - W32_DMN_ImageInfo image_info = w32_dmn_image_info_from_process_base_vaddr(process->handle, module_base); + HANDLE module_handle = evt.u.LoadDll.hFile; + U64 module_name_vaddr = (U64)evt.u.LoadDll.lpImageName; + B32 module_name_is_unicode = (evt.u.LoadDll.fUnicode != 0); + DMN_ModuleInfo *module_info = w32_dmn_module_info_from_process_module(arena, process->handle, module_handle, module_base, module_name_vaddr, module_name_is_unicode, 0); // rjf: create module entity W32_DMN_Entity *module = w32_dmn_entity_alloc(process, W32_DMN_EntityKind_Module, module_base); { - module->handle = evt.u.LoadDll.hFile; - module->arch = image_info.arch; - module->module.vaddr_range = r1u64(module_base, module_base+image_info.size); - module->module.address_of_name_pointer = (U64)evt.u.LoadDll.lpImageName; - module->module.name_is_unicode = (evt.u.LoadDll.fUnicode != 0); + module->handle = module_handle; + module->arch = module_info->arch; + module->module.vaddr_range = r1u64(module_base, module_base+module_info->vsize); + module->module.address_of_name_pointer = module_name_vaddr; + module->module.name_is_unicode = module_name_is_unicode; } // rjf: generate event { DMN_Event *e = dmn_event_list_push(arena, &events); - e->kind = DMN_EventKind_LoadModule; - e->process = w32_dmn_handle_from_entity(process); - e->module = w32_dmn_handle_from_entity(module); - e->arch = module->arch; - e->address = module_base; - e->size = image_info.size; - e->string = w32_dmn_full_path_from_module(arena, module); - e->tls_index = image_info.tls_index; + e->kind = DMN_EventKind_LoadModule; + e->process = w32_dmn_handle_from_entity(process); + e->module = w32_dmn_handle_from_entity(module); + e->arch = module_info->arch; + e->address = module_base; + e->size = module_info->vsize; + e->string = module_info->module_path; + e->module_info = module_info; } }break; diff --git a/src/win32/demon/win32_demon.h b/src/win32/demon/win32_demon.h index 3bf51ff5..dd16020f 100644 --- a/src/win32/demon/win32_demon.h +++ b/src/win32/demon/win32_demon.h @@ -337,11 +337,14 @@ internal String8 w32_dmn_full_path_from_module(Arena *arena, W32_DMN_Entity *mod //- rjf: processes internal U64 w32_dmn_process_read(HANDLE process, Rng1U64 range, void *dst); internal B32 w32_dmn_process_write(HANDLE process, Rng1U64 range, void *src); +internal String8 w32_dmn_str8_cstring_from_process_vaddr(Arena *arena, HANDLE process, U64 vaddr); internal String8 w32_dmn_read_memory_str(Arena *arena, HANDLE process_handle, U64 address); internal String16 w32_dmn_read_memory_str16(Arena *arena, HANDLE process_handle, U64 address); #define w32_dmn_process_read_struct(process, vaddr, ptr) w32_dmn_process_read((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) #define w32_dmn_process_write_struct(process, vaddr, ptr) w32_dmn_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) -internal W32_DMN_ImageInfo w32_dmn_image_info_from_process_base_vaddr(HANDLE process, U64 base_vaddr); + +//- rjf: modules +internal DMN_ModuleInfo *w32_dmn_module_info_from_process_module(Arena *arena, HANDLE process, HANDLE module, U64 base_vaddr, U64 module_name_vaddr, B32 module_name_is_unicode, B32 is_main_module); //- rjf: threads internal B32 w32_dmn_thread_read_reg_block(Arch arch, HANDLE thread, void *reg_block);