// Copyright (c) 2024 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// //~ rjf: Main Layer Initialization internal void demon_init(void){ demon_common_init(); demon_os_init(); } //////////////////////////////// //~ rjf: Basic Type Functions //- rjf: stringizing internal String8 demon_string_from_event_kind(DEMON_EventKind kind){ String8 result = str8_lit("unknown"); switch (kind){ default: break; case DEMON_EventKind_Error: result = str8_lit("Error"); break; case DEMON_EventKind_HandshakeComplete: result = str8_lit("HandshakeComplete"); break; case DEMON_EventKind_CreateProcess: result = str8_lit("CreateProcess"); break; case DEMON_EventKind_ExitProcess: result = str8_lit("ExitProcess"); break; case DEMON_EventKind_CreateThread: result = str8_lit("CreateThread"); break; case DEMON_EventKind_ExitThread: result = str8_lit("ExitThread"); break; case DEMON_EventKind_LoadModule: result = str8_lit("LoadModule"); break; case DEMON_EventKind_UnloadModule: result = str8_lit("UnloadModule"); break; case DEMON_EventKind_Breakpoint: result = str8_lit("Breakpoint"); break; case DEMON_EventKind_Trap: result = str8_lit("Trap"); break; case DEMON_EventKind_SingleStep: result = str8_lit("SingleStep"); break; case DEMON_EventKind_Exception: result = str8_lit("Exception"); break; case DEMON_EventKind_Halt: result = str8_lit("Halt"); break; case DEMON_EventKind_Memory: result = str8_lit("Memory"); break; case DEMON_EventKind_DebugString: result = str8_lit("DebugString"); break; case DEMON_EventKind_SetThreadName: result = str8_lit("SetThreadName"); break; } return(result); } internal String8 demon_string_from_memory_event_kind(DEMON_MemoryEventKind kind){ String8 result = str8_lit("unknown"); switch (kind){ default: break; case DEMON_MemoryEventKind_Commit: result = str8_lit("Commit"); break; case DEMON_MemoryEventKind_Reserve: result = str8_lit("Reserve"); break; case DEMON_MemoryEventKind_Decommit: result = str8_lit("Decommit"); break; case DEMON_MemoryEventKind_Release: result = str8_lit("Release"); break; } return(result); } internal String8 demon_string_from_exception_kind(DEMON_ExceptionKind kind){ String8 result = str8_lit("unknown"); switch (kind){ default: break; case DEMON_ExceptionKind_MemoryRead: result = str8_lit("MemoryRead"); break; case DEMON_ExceptionKind_MemoryWrite: result = str8_lit("MemoryWrite"); break; case DEMON_ExceptionKind_MemoryExecute: result = str8_lit("MemoryExecute"); break; case DEMON_ExceptionKind_CppThrow: result = str8_lit("CppThrow"); break; } return(result); } internal void demon_string_list_from_event(Arena *arena, String8List *out, DEMON_Event *event){ B32 need_exception_info = (event->kind == DEMON_EventKind_Exception || event->kind == DEMON_EventKind_Breakpoint || event->kind == DEMON_EventKind_Halt || event->kind == DEMON_EventKind_SingleStep); // allen: kind String8 kind_string = demon_string_from_event_kind(event->kind); str8_list_pushf(arena, out, "%S: { (%i)", kind_string, event->kind); // rjf: basics { str8_list_pushf(arena, out, " process: (%I64x)", event->process); str8_list_pushf(arena, out, " thread: (%I64x)", event->thread); str8_list_pushf(arena, out, " module: (%I64x)", event->module); str8_list_pushf(arena, out, " address: (%I64x)", event->address, event->address); str8_list_pushf(arena, out, " size: (0x%I64x, %I64u)", event->size, event->size); } // rjf: string if (event->string.size != 0){ str8_list_pushf(arena, out, " string: \"%S\"", event->string); } // rjf: exception info if (need_exception_info){ str8_list_pushf(arena, out, " code: (0x%x, %i)", event->code, event->code); str8_list_pushf(arena, out, " flags: (0x%x, %i)", event->flags, event->flags); str8_list_pushf(arena, out, " signo: (0x%x, %i)", event->signo, event->signo); str8_list_pushf(arena, out, " sigcode: (0x%x, %i)", event->sigcode, event->sigcode); } // rjf: need error info if (event->kind == DEMON_EventKind_Error){ str8_list_pushf(arena, out, " error_kind: (0x%x, %i)", event->error_kind, event->error_kind); } // rjf: memory event kind info if (event->memory_kind != DEMON_MemoryEventKind_Null){ String8 memory_kind_string = demon_string_from_memory_event_kind(event->memory_kind); str8_list_pushf(arena, out, " memory_kind: (%S, %i)", memory_kind_string, event->memory_kind); } // rjf: exception kind if (need_exception_info){ String8 exception_kind_string = demon_string_from_exception_kind(event->exception_kind); str8_list_pushf(arena, out, " exception_kind: (%S, %i)", exception_kind_string, event->exception_kind); } // rjf: instruction ptr if (event->instruction_pointer != 0){ str8_list_pushf(arena, out, " instruction_pointer: (%I64x)", event->instruction_pointer); } // rjf: stack ptr if (event->stack_pointer != 0){ str8_list_pushf(arena, out, " stack_pointer: (%I64x)", event->stack_pointer); } str8_list_pushf(arena, out, " user_data: (0x%I64x, %I64u)", event->user_data, event->user_data); str8_list_pushf(arena, out, "}"); } //- rjf: trap chunk lists internal void demon_trap_chunk_list_push(Arena *arena, DEMON_TrapChunkList *list, U64 cap, DEMON_Trap *trap) { DEMON_TrapChunkNode *node = list->last; if(node == 0 || node->count >= node->cap) { node = push_array(arena, DEMON_TrapChunkNode, 1); node->cap = cap; node->v = push_array_no_zero(arena, DEMON_Trap, node->cap); SLLQueuePush(list->first, list->last, node); list->node_count += 1; } MemoryCopyStruct(&node->v[node->count], trap); node->count += 1; list->trap_count += 1; } internal void demon_trap_chunk_list_concat_in_place(DEMON_TrapChunkList *dst, DEMON_TrapChunkList *to_push) { if(dst->last == 0) { MemoryCopyStruct(dst, to_push); } else if(to_push->first != 0) { dst->last->next = to_push->first; dst->last = to_push->last; dst->node_count += to_push->node_count; dst->trap_count += to_push->trap_count; } MemoryZeroStruct(to_push); } internal void demon_trap_chunk_list_concat_shallow_copy(Arena *arena, DEMON_TrapChunkList *dst, DEMON_TrapChunkList *to_push) { for(DEMON_TrapChunkNode *src_n = to_push->first; src_n != 0; src_n = src_n->next) { DEMON_TrapChunkNode *dst_n = push_array(arena, DEMON_TrapChunkNode, 1); dst_n->v = src_n->v; dst_n->cap = src_n->cap; dst_n->count = src_n->count; SLLQueuePush(dst->first, dst->last, dst_n); dst->node_count += 1; dst->trap_count += dst_n->count; } } //- rjf: handle lists internal void demon_handle_list_push(Arena *arena, DEMON_HandleList *list, DEMON_Handle handle) { DEMON_HandleNode *node = push_array(arena, DEMON_HandleNode, 1); SLLQueuePush(list->first, list->last, node); node->v = handle; list->count += 1; } internal DEMON_HandleArray demon_handle_array_from_list(Arena *arena, DEMON_HandleList *list) { DEMON_HandleArray array = {0}; array.count = list->count; array.handles = push_array_no_zero(arena, DEMON_Handle, array.count); U64 idx = 0; for(DEMON_HandleNode *n = list->first; n != 0; n = n->next, idx += 1) { array.handles[idx] = n->v; } return array; } internal DEMON_HandleArray demon_handle_array_copy(Arena *arena, DEMON_HandleArray *src) { DEMON_HandleArray dst = {0}; dst.count = src->count; dst.handles = push_array_no_zero(arena, DEMON_Handle, dst.count); MemoryCopy(dst.handles, src->handles, sizeof(DEMON_Handle)*dst.count); return dst; } //////////////////////////////// //~ rjf: Primary Thread & Exclusive Mode Controls internal void demon_primary_thread_begin(void){ demon_primary_thread = 1; } internal void demon_exclusive_mode_begin(void){ Assert(demon_primary_thread); os_mutex_take(demon_state_mutex); demon_run_state = 1; os_mutex_drop(demon_state_mutex); } internal void demon_exclusive_mode_end(void){ Assert(demon_primary_thread); os_mutex_take(demon_state_mutex); demon_run_state = 0; os_mutex_drop(demon_state_mutex); } //////////////////////////////// //~ rjf: Running/Halting internal DEMON_EventList demon_run(Arena *arena, DEMON_RunCtrls *ctrls) { Assert(demon_primary_thread); Temp scratch = scratch_begin(&arena, 1); // convert controls to os controls B32 full_conversion = 1; DEMON_OS_RunCtrls os_ctrls = {0}; { // convert single_step_thread if (ctrls->single_step_thread != 0){ DEMON_Entity *sst_entity = demon_ent_ptr_from_handle(ctrls->single_step_thread); if (sst_entity != 0 && sst_entity->kind == DEMON_EntityKind_Thread){ os_ctrls.single_step_thread = sst_entity; } else{ full_conversion = 0; goto finish_conversion; } } // convert exception handling flag os_ctrls.ignore_previous_exception = ctrls->ignore_previous_exception; // convert fronzen threads os_ctrls.run_entities_are_unfrozen = ctrls->run_entities_are_unfrozen; os_ctrls.run_entities_are_processes = ctrls->run_entities_are_processes; os_ctrls.run_entity_count = ctrls->run_entity_count; os_ctrls.run_entities = push_array_no_zero(scratch.arena, DEMON_Entity*, ctrls->run_entity_count); { DEMON_EntityKind expected_entity_kind = DEMON_EntityKind_Thread; if (os_ctrls.run_entities_are_processes){ expected_entity_kind = DEMON_EntityKind_Process; } DEMON_Handle *src = ctrls->run_entities; DEMON_Entity **dst = os_ctrls.run_entities; for (U64 i = 0; i < ctrls->run_entity_count; i += 1, src += 1, dst += 1){ DEMON_Entity *frozen_thread = demon_ent_ptr_from_handle(*src); if (frozen_thread != 0 && frozen_thread->kind == expected_entity_kind){ *dst = frozen_thread; } else{ full_conversion = 0; goto finish_conversion; } } } // convert traps os_ctrls.traps = push_array_no_zero(scratch.arena, DEMON_OS_Trap, ctrls->traps.trap_count); { DEMON_OS_Trap *dst = os_ctrls.traps; for (DEMON_TrapChunkNode *node = ctrls->traps.first; node != 0; node = node->next){ DEMON_Trap *src = node->v; U64 node_trap_count = node->count; for (U64 i = 0; i < node_trap_count; i += 1, src += 1){ if (src->process != 0){ DEMON_Entity *trap_process = demon_ent_ptr_from_handle(src->process); if (trap_process != 0 && trap_process->kind == DEMON_EntityKind_Process){ dst->process = trap_process; dst->address = src->address; dst += 1; } else{ full_conversion = 0; goto finish_conversion; } } } } os_ctrls.trap_count = (U64)(dst - os_ctrls.traps); } finish_conversion:; } // call the OS implementation of run DEMON_EventList result = {0}; if (full_conversion){ result = demon_os_run(arena, &os_ctrls); } else{ DEMON_Event *event = demon_push_event(arena, &result, DEMON_EventKind_Error); event->error_kind = DEMON_ErrorKind_InvalidHandle; } scratch_end(scratch); return(result); } internal void demon_halt(U64 code, U64 user_data){ demon_os_halt(code, user_data); } internal U64 demon_get_time_counter(void){ return(demon_time); } //////////////////////////////// //~ rjf: Target Process Launching/Attaching/Killing/Detaching/Halting internal U32 demon_launch_process(OS_LaunchOptions *options){ Assert(demon_primary_thread); U32 result = demon_os_launch_process(options); return(result); } internal B32 demon_attach_process(U32 pid){ Assert(demon_primary_thread); B32 result = demon_os_attach_process(pid); return(result); } internal B32 demon_kill_process(DEMON_Handle process, U32 exit_code){ Assert(demon_primary_thread); B32 result = 0; DEMON_Entity *entity = demon_ent_ptr_from_handle(process); if (entity != 0 && entity->kind == DEMON_EntityKind_Process){ result = demon_os_kill_process(entity, exit_code); } return(result); } internal B32 demon_detach_process(DEMON_Handle process){ Assert(demon_primary_thread); B32 result = 0; DEMON_Entity *entity = demon_ent_ptr_from_handle(process); if (entity != 0 && entity->kind == DEMON_EntityKind_Process){ result = demon_os_detach_process(entity); } return(result); } //////////////////////////////// //~ rjf: Entity Functions //- rjf: basics internal B32 demon_object_exists(DEMON_Handle object){ B32 result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(object); result = (entity != 0); demon_access_end(); } return(result); } //- rjf: introspection internal Architecture demon_arch_from_object(DEMON_Handle object){ Architecture result = Architecture_Null; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(object); if (entity != 0){ result = (Architecture)entity->arch; } demon_access_end(); } return(result); } internal U64 demon_base_vaddr_from_module(DEMON_Handle module){ U64 result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(module); if (entity != 0 && entity->kind == DEMON_EntityKind_Module){ result = entity->id; } demon_access_end(); } return(result); } internal Rng1U64 demon_vaddr_range_from_module(DEMON_Handle module) { Rng1U64 result = {0}; if(demon_access_begin()) { DEMON_Entity *entity = demon_ent_ptr_from_handle(module); if(entity != 0 && entity->kind == DEMON_EntityKind_Module) { result = r1u64(entity->id, entity->id+entity->addr_range_dim); } demon_access_end(); } return(result); } internal String8 demon_full_path_from_module(Arena *arena, DEMON_Handle module){ String8 result = {0}; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(module); if (entity != 0 && entity->kind == DEMON_EntityKind_Module){ result = demon_accel_full_path_from_module(arena, entity); } demon_access_end(); } return(result); } internal U64 demon_stack_base_vaddr_from_thread(DEMON_Handle thread){ U64 result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(thread); if (entity != 0 && entity->kind == DEMON_EntityKind_Thread){ result = demon_accel_stack_base_vaddr_from_thread(entity); } demon_access_end(); } return(result); } internal U64 demon_tls_root_vaddr_from_thread(DEMON_Handle handle){ U64 result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(handle); if (entity != 0 && entity->kind == DEMON_EntityKind_Thread){ result = demon_accel_tls_root_vaddr_from_thread(entity); } demon_access_end(); } return(result); } internal DEMON_HandleArray demon_all_processes(Arena *arena){ DEMON_HandleArray result = {0}; if (demon_access_begin()){ DEMON_Handle *handles = push_array_no_zero(arena, DEMON_Handle, demon_proc_count); DEMON_Handle *handle_opl = handles + demon_proc_count; DEMON_Handle *handle_ptr = handles; for (DEMON_Entity *process = demon_ent_root->first; process != 0 && handle_ptr < handle_opl; process = process->next){ if (process->kind == DEMON_EntityKind_Process){ *handle_ptr = demon_ent_handle_from_ptr(process); handle_ptr += 1; } } result.handles = handles; result.count = (U64)(handle_ptr - handles); U64 unused_count = demon_proc_count - result.count; arena_put_back(arena, sizeof(DEMON_Handle)*unused_count); demon_access_end(); } return(result); } internal DEMON_HandleArray demon_threads_from_process(Arena *arena, DEMON_Handle process){ DEMON_HandleArray result = {0}; if (demon_access_begin()){ DEMON_Handle *handles = push_array_no_zero(arena, DEMON_Handle, demon_thread_count); DEMON_Handle *handle_opl = handles + demon_thread_count; DEMON_Handle *handle_ptr = handles; DEMON_Entity *process_ptr = demon_ent_ptr_from_handle(process); if (process_ptr != 0 && process_ptr->kind == DEMON_EntityKind_Process){ for (DEMON_Entity *thread = process_ptr->first; thread != 0 && handle_ptr < handle_opl; thread = thread->next){ if (thread->kind == DEMON_EntityKind_Thread){ *handle_ptr = demon_ent_handle_from_ptr(thread); handle_ptr += 1; } } } result.handles = handles; result.count = (U64)(handle_ptr - handles); U64 unused_count = demon_thread_count - result.count; arena_put_back(arena, sizeof(DEMON_Handle)*unused_count); demon_access_end(); } return(result); } internal DEMON_HandleArray demon_modules_from_process(Arena *arena, DEMON_Handle process){ DEMON_HandleArray result = {0}; if (demon_access_begin()){ DEMON_Handle *handles = push_array_no_zero(arena, DEMON_Handle, demon_module_count); DEMON_Handle *handle_opl = handles + demon_module_count; DEMON_Handle *handle_ptr = handles; DEMON_Entity *process_ptr = demon_ent_ptr_from_handle(process); if (process_ptr != 0 && process_ptr->kind == DEMON_EntityKind_Process){ for (DEMON_Entity *module = process_ptr->first; module != 0 && handle_ptr < handle_opl; module = module->next){ if (module->kind == DEMON_EntityKind_Module){ *handle_ptr = demon_ent_handle_from_ptr(module); handle_ptr += 1; } } } result.handles = handles; result.count = (U64)(handle_ptr - handles); U64 unused_count = demon_module_count - result.count; arena_put_back(arena, sizeof(DEMON_Handle)*unused_count); demon_access_end(); } return(result); } //- rjf: target process memory allocation/protection internal U64 demon_reserve_memory(DEMON_Handle process, U64 size){ U64 result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(process); if (entity != 0 && entity->kind == DEMON_EntityKind_Process){ result = demon_os_reserve_memory(entity, size); } demon_access_end(); } return(result); } internal B32 demon_set_memory_protect_flags(DEMON_Handle process, U64 page_vaddr, U64 size, DEMON_MemoryProtectFlags flags){ B32 result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(process); if (entity != 0 && entity->kind == DEMON_EntityKind_Process){ demon_os_set_memory_protect_flags(entity, page_vaddr, size, flags); result = 1; } demon_access_end(); } return(result); } internal B32 demon_release_memory(DEMON_Handle process, U64 vaddr, U64 size){ B32 result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(process); if (entity != 0 && entity->kind == DEMON_EntityKind_Process){ demon_os_release_memory(entity, vaddr, size); result = 1; } demon_access_end(); } return(result); } //- rjf: target process memory reading/writing internal U64 demon_read_memory(DEMON_Handle process, void *dst, U64 src_address, U64 size){ U64 bytes_read = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(process); if (entity != 0 && entity->kind == DEMON_EntityKind_Process){ bytes_read = demon_os_read_memory(entity, dst, src_address, size); } demon_access_end(); } return(bytes_read); } internal B32 demon_write_memory(DEMON_Handle process, U64 dst_address, void *src, U64 size){ B32 result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(process); if (entity != 0 && entity->kind == DEMON_EntityKind_Process){ result = demon_os_write_memory(entity, dst_address, src, size); } demon_access_end(); } return(result); } #define READ_BLOCK_SIZE 4096 internal U64 demon_read_memory_amap_aligned(DEMON_Handle process, void *dst, U64 src_address, U64 size){ // Algorithm: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ^ ^ ^ // MIN MAX SMAX // [MIN,MAX) - range attempting to read // [MAX,SMAX) - range not yet proven to be impossible to read Assert(src_address%READ_BLOCK_SIZE == 0); Assert(size%READ_BLOCK_SIZE == 0); U64 read_size = 0; U64 min = 0; U64 max = size; U64 smax = max; for (;;){ if (max <= min){ break; } // attempt to read range U64 attempt_size = max - min; B32 success = demon_read_memory(process, (U8*)dst + min, src_address + min, attempt_size); if (success){ // increase successful read size read_size += attempt_size; // adjust range up min = max; max = smax; } else{ // mark this point as too far smax = max - READ_BLOCK_SIZE; // bisect the range for the next read attempt U64 mid = (min + max)/2; U64 aligned_mid = AlignDownPow2(mid, READ_BLOCK_SIZE); max = aligned_mid; } } U64 result = read_size; return(result); } internal U64 demon_read_memory_amap(DEMON_Handle process, void *dst, U64 src_address, U64 size){ U64 read_size = 0; if (demon_access_begin()){ B32 done = 0; U64 read_opl = src_address + size; // pre-aligned part -- [SRC,PRE_OPL) U64 src_block_opl = AlignPow2(src_address, READ_BLOCK_SIZE); U64 pre_opl = Min(src_block_opl, read_opl); if(src_address < pre_opl) { U64 attempt_size = pre_opl - src_address; if(!demon_read_memory(process, dst, src_address, attempt_size)) { done = 1; } else { read_size += attempt_size; } } // aligned part -- [PRE_OPL,POST_FIRST) U64 read_opl_block_base = AlignDownPow2(read_opl, READ_BLOCK_SIZE); U64 post_first = Max(read_opl_block_base, pre_opl); if (!done && pre_opl < post_first){ U64 off = pre_opl - src_address; U64 attempt_size = post_first - pre_opl; U64 actual_size = demon_read_memory_amap_aligned(process, (U8*)dst + off, pre_opl, attempt_size); read_size += actual_size; if (actual_size < attempt_size){ done = 1; } } // post-aligned part -- [POST_FIRST,READ_OPL) if (!done && post_first < read_opl){ U64 off = post_first - src_address; U64 attempt_size = read_opl - post_first; if (!demon_read_memory(process, (U8*)dst + off, post_first, attempt_size)){ done = 1; } else { read_size += attempt_size; } } demon_access_end(); } U64 result = read_size; return(result); } #undef READ_BLOCK_SIZE //- rjf: thread registers reading/writing internal void* demon_read_regs(DEMON_Handle thread){ void *result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(thread); if (entity != 0 && entity->kind == DEMON_EntityKind_Thread){ result = demon_accel_read_regs(entity); } demon_access_end(); } return(result); } internal B32 demon_write_regs(DEMON_Handle thread, void *data){ B32 result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(thread); if (entity != 0 && entity->kind == DEMON_EntityKind_Thread){ demon_accel_write_regs(entity, data); result = 1; } demon_access_end(); } return(result); } internal U64 demon_read_ip(DEMON_Handle thread){ U64 result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(thread); if (entity != 0 && entity->kind == DEMON_EntityKind_Thread){ void *regs = demon_accel_read_regs(entity); result = regs_rip_from_arch_block((Architecture)entity->arch, regs); } demon_access_end(); } return(result); } internal U64 demon_read_sp(DEMON_Handle thread){ U64 result = 0; if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(thread); if (entity != 0 && entity->kind == DEMON_EntityKind_Thread){ void *regs = demon_accel_read_regs(entity); result = regs_rsp_from_arch_block((Architecture)entity->arch, regs); } demon_access_end(); } return(result); } internal void demon_write_ip(DEMON_Handle thread, U64 ip){ if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(thread); if (entity != 0 && entity->kind == DEMON_EntityKind_Thread){ void *regs = demon_accel_read_regs(entity); regs_arch_block_write_rip((Architecture)entity->arch, regs, ip); demon_accel_write_regs(entity, regs); } demon_access_end(); } } //////////////////////////////// //~ rjf: Process Listing internal void demon_proc_iter_begin(DEMON_ProcessIter *iter){ demon_os_proc_iter_begin(iter); } internal B32 demon_proc_iter_next(Arena *arena, DEMON_ProcessIter *iter, DEMON_ProcessInfo *info_out){ return(demon_os_proc_iter_next(arena, iter, info_out)); } internal void demon_proc_iter_end(DEMON_ProcessIter *iter){ demon_os_proc_iter_end(iter); }