diff --git a/src/base/base_entry_point.c b/src/base/base_entry_point.c index f42cfc3d..cb6219a0 100644 --- a/src/base/base_entry_point.c +++ b/src/base/base_entry_point.c @@ -40,9 +40,6 @@ main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **argum txti_init(); #endif #if defined(DEMON_CORE_H) - demon_init(); -#endif -#if defined(DEMON2_CORE_H) dmn_init(); #endif #if defined(CTRL_CORE_H) diff --git a/src/demon/demon_accel.c b/src/demon/demon_accel.c deleted file mode 100644 index 156fb9f1..00000000 --- a/src/demon/demon_accel.c +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//- allen: Acceleration Layer Functions - -//- accel helpers -internal DEMON_AccelModule* -demon_accel_module_alloc(void){ - DEMON_AccelModule *result = demon_free_module_accel; - if (result != 0){ - SLLStackPop(demon_free_module_accel); - } - else{ - result = push_array_no_zero(demon_ent_arena, DEMON_AccelModule, 1); - } - MemoryZeroStruct(result); - return(result); -} - -internal void -demon_accel_module_free(DEMON_AccelModule *module){ - SLLStackPush(demon_free_module_accel, module); -} - -internal DEMON_AccelThread* -demon_accel_thread_alloc(void){ - DEMON_AccelThread *result = demon_free_thread_accel; - if (result != 0){ - SLLStackPop(demon_free_thread_accel); - } - else{ - result = push_array_no_zero(demon_ent_arena, DEMON_AccelThread, 1); - } - MemoryZeroStruct(result); - return(result); -} - -internal void -demon_accel_thread_free(DEMON_AccelThread *thread){ - SLLStackPush(demon_free_thread_accel, thread); -} - -internal DEMON_AccelThread* -demon_accel_from_thread(DEMON_Entity *thread){ - DEMON_AccelThread *accel = (DEMON_AccelThread*)thread->accel; - if (accel == 0){ - accel = demon_accel_thread_alloc(); - thread->accel = accel; - } - return(accel); -} - -//- operations on demon objects -internal String8 -demon_accel_full_path_from_module(Arena *arena, DEMON_Entity *module){ - DEMON_AccelModule *accel = (DEMON_AccelModule*)module->accel; - - String8 result = {0}; - - // first time - if (accel == 0){ - result = demon_os_full_path_from_module(arena, module); - - // build chain - DEMON_AccelModule *last_accel = 0; - - U8 *ptr = result.str; - U8 *opl = result.str + result.size; - for (;ptr < opl;){ - U64 size = (U64)(ptr - opl); - U64 clamped_size = ClampTop(result.size, sizeof(Member(DEMON_AccelModule, buf))); - - DEMON_AccelModule *node = demon_accel_module_alloc(); - SLLQueuePush(accel, last_accel, node); - node->total_size = result.size; - MemoryCopy(node->buf, ptr, clamped_size); - - ptr += clamped_size; - } - - // store in module - module->accel = accel; - } - - // read from accel - else{ - U64 size = accel->total_size; - U8 *str = push_array_no_zero(arena, U8, size + 1); - - // copy chain contents to buffer - U8 *ptr = str; - for (DEMON_AccelModule *node = accel; - node != 0; - node = node->next){ - U64 total_size = node->total_size; - U64 clamped_size = ClampTop(total_size, sizeof(node->buf)); - MemoryCopy(ptr, node->buf, clamped_size); - ptr += clamped_size; - } - *ptr = 0; - - // fill result - result.str = str; - result.size = size; - } - - return(result); -} - -internal U64 -demon_accel_stack_base_vaddr_from_thread(DEMON_Entity *thread){ - // get accel data - DEMON_AccelThread *accel = demon_accel_from_thread(thread); - - // fill stack base - if (!accel->has_stack_base){ - accel->has_stack_base = 1; - accel->stack_base = demon_os_stack_base_vaddr_from_thread(thread); - } - - return(accel->stack_base); -} - -internal U64 -demon_accel_tls_root_vaddr_from_thread(DEMON_Entity *thread){ - // get accel data - DEMON_AccelThread *accel = demon_accel_from_thread(thread); - - // fill tls root - if (!accel->has_tls_root){ - accel->has_tls_root = 1; - accel->tls_root = demon_os_tls_root_vaddr_from_thread(thread); - } - - return(accel->tls_root); -} - -internal void* -demon_accel_read_regs(DEMON_Entity *thread){ - // get accel data - DEMON_AccelThread *accel = demon_accel_from_thread(thread); - - // update reg cache - if (accel->reg_cache_time != demon_time){ - accel->reg_cache_time = demon_time; - B32 success = demon_os_read_regs(thread, &accel->regs); - if (!success){ - MemoryZeroStruct(&accel->regs); - } - } - - return(&accel->regs); -} - -internal void -demon_accel_write_regs(DEMON_Entity *thread, void *data){ - // get accel data - DEMON_AccelThread *accel = demon_accel_from_thread(thread); - - // write - U64 data_size = regs_block_size_from_architecture(thread->arch); - B32 success = demon_os_write_regs(thread, data); - - // update cache - if(success) - { - accel->reg_cache_time = demon_time; - MemoryCopy(&accel->regs, data, data_size); - } -} - -//- entity accel free -internal void -demon_accel_free(DEMON_Entity *entity){ - switch (entity->kind){ - default:{}break; - - case DEMON_EntityKind_Module: - { - if (entity->accel != 0){ - for (DEMON_AccelModule *node = (DEMON_AccelModule*)entity->accel, *next = 0; - node != 0; - node = next){ - next = node->next; - demon_accel_module_free(node); - } - } - }break; - - case DEMON_EntityKind_Thread: - { - if (entity->accel != 0){ - demon_accel_thread_free((DEMON_AccelThread*)entity->accel); - } - }break; - } -} diff --git a/src/demon/demon_accel.h b/src/demon/demon_accel.h deleted file mode 100644 index 0b6f8288..00000000 --- a/src/demon/demon_accel.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DEMON_ACCEL_H -#define DEMON_ACCEL_H - -//////////////////////////////// -//~ allen: Acceleration Data - -typedef struct DEMON_AccelModule DEMON_AccelModule; -struct DEMON_AccelModule -{ - DEMON_AccelModule *next; - U64 total_size; - U8 buf[240]; -}; - -typedef union DEMON_AccelThread DEMON_AccelThread; -union DEMON_AccelThread -{ - DEMON_AccelThread *next; - struct{ - B32 has_stack_base; - B32 has_tls_root; - U64 stack_base; - U64 tls_root; - - U64 reg_cache_time; - union{ - REGS_RegBlockX64 x64; - REGS_RegBlockX86 x86; - } regs; - }; -}; - -//////////////////////////////// -//~ allen: Acceleration Globals - -global DEMON_AccelModule *demon_free_module_accel = 0; -global DEMON_AccelThread *demon_free_thread_accel = 0; - -//////////////////////////////// -//~ allen: Acceleration Layer Functions - -//- accel helpers -internal DEMON_AccelModule *demon_accel_module_alloc(void); -internal void demon_accel_module_free(DEMON_AccelModule *module); - -internal DEMON_AccelThread *demon_accel_thread_alloc(void); -internal void demon_accel_thread_free(DEMON_AccelThread *thread); -internal DEMON_AccelThread *demon_accel_from_thread(DEMON_Entity *thread); - -//- operations on demon objects -internal String8 demon_accel_full_path_from_module(Arena *arena, DEMON_Entity *module); -internal U64 demon_accel_stack_base_vaddr_from_thread(DEMON_Entity *thread); -internal U64 demon_accel_tls_root_vaddr_from_thread(DEMON_Entity *thread); - -internal void* demon_accel_read_regs(DEMON_Entity *thread); -internal void demon_accel_write_regs(DEMON_Entity *thread, void *data); - -//- entity accel free -internal void demon_accel_free(DEMON_Entity *entity); - -#endif //DEMON_ACCEL_H diff --git a/src/demon/demon_common.c b/src/demon/demon_common.c deleted file mode 100644 index 1ae895d0..00000000 --- a/src/demon/demon_common.c +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -// NOTE(allen): State Safety Helper - -internal B32 -demon_access_begin(void){ - B32 result = 0; - if (demon_primary_thread){ - Assert(demon_run_state); - result = 1; - } - else{ - os_mutex_take(demon_state_mutex); - if (demon_run_state){ - os_mutex_drop(demon_state_mutex); - } - else{ - result = 1; - } - } - return(result); -} - -internal void -demon_access_end(void){ - if (!demon_primary_thread){ - os_mutex_drop(demon_state_mutex); - } -} - -//////////////////////////////// -// NOTE(allen): Entity System - -internal void -demon_common_init(void){ - // access control mechanism - demon_state_mutex = os_mutex_alloc(); - - // time - demon_time = 1; - - // setup arena - demon_ent_arena = arena_alloc(); - - // setup map - demon_ent_map = push_array(demon_ent_arena, DEMON_Map, 1); - demon_ent_map->bucket_count = 4093; - demon_ent_map->buckets = push_array(demon_ent_arena, DEMON_MapSlot*, demon_ent_map->bucket_count); - - // setup entity memory - U64 reserve_size_unaligned = (DEMON_ENTITY_CAP)*sizeof(DEMON_Entity); - U64 reserve_size = AlignPow2(reserve_size_unaligned, DEMON_ENTITY_CMT_SIZE); - demon_ent_cmt = demon_ent_pos = demon_ent_base = (DEMON_Entity*)os_reserve(reserve_size); - demon_ent_opl = demon_ent_base + (reserve_size/sizeof(DEMON_Entity)); - - Assert(demon_ent_base != 0); - - // setup root - demon_ent_root = demon_ent_alloc(); - demon_ent_root->kind = DEMON_EntityKind_Root; -} - -internal DEMON_Entity* -demon_ent_alloc(void){ - DEMON_Entity *result = demon_ent_free; - if (result != 0){ - SLLStackPop(demon_ent_free); - } - else{ - if (demon_ent_pos < demon_ent_opl){ - if (ensure_commit(&demon_ent_cmt, demon_ent_pos + 1, DEMON_ENTITY_CMT_SIZE)){ - result = demon_ent_pos; - demon_ent_pos += 1; - } - } - } - if (result != 0){ - U32 gen = result->gen; - MemoryZeroStruct(result); - result->gen = gen; - } - return(result); -} - -//- handle <-> entity pointer - -internal DEMON_Entity* -demon_ent_ptr_from_handle(DEMON_Handle handle){ - Assert(demon_ent_base != 0); - DEMON_Entity *result = 0; - U32 index = (U32)(handle & 0xFFFFFFFF); - U64 count = (U64)(demon_ent_pos - demon_ent_base); - if (0 < index && index < count){ - DEMON_Entity *entity = demon_ent_base + index; - U32 gen = (U32)(handle >> 32); - if (gen == entity->gen){ - result = entity; - } - } - return(result); -} - -internal DEMON_Handle -demon_ent_handle_from_ptr(DEMON_Entity *entity){ - Assert(demon_ent_base != 0); - DEMON_Handle result = {0}; - if (demon_ent_base < entity && entity < demon_ent_pos){ - U32 index = (U32)(entity - demon_ent_base); - U64 gen = entity->gen; - result = (gen << 32) | index; - } - return(result); -} - -//- high level entity alloc,init,release - -internal DEMON_Entity* -demon_ent_new(DEMON_Entity *parent, DEMON_EntityKind kind, U64 id){ - Assert(demon_ent_base != 0); - DEMON_Entity *result = demon_ent_alloc(); - if (result != 0){ - result->kind = kind; - result->id = id; - result->arch = parent->arch; - result->parent = parent; - DLLPushBack(parent->first, parent->last, result); - demon_ent_map_save(kind, id, result); - } - return(result); -} - -internal void -demon_ent_release_single(DEMON_Entity *entity){ - switch (entity->kind){ - default:{}break; - case DEMON_EntityKind_Process: demon_proc_count -= 1; break; - case DEMON_EntityKind_Thread: demon_thread_count -= 1; break; - case DEMON_EntityKind_Module: demon_module_count -= 1; break; - } - demon_accel_free(entity); - demon_os_entity_cleanup(entity); - DEMON_MapRef ref = demon_ent_map_find(entity->kind, entity->id); - demon_ent_map_erase(ref); - entity->gen += 1; -} - -internal void -demon_ent_release_children(DEMON_Entity *root){ - Assert(demon_ent_base != 0); - if (root->first != 0){ - for (DEMON_Entity *node = root->first; - node != 0; - node = node->next){ - demon_ent_release_children(node); - demon_ent_release_single(node); - } - root->last->next = demon_ent_free; - demon_ent_free = root->first; - root->first = 0; - root->last = 0; - } -} - -internal void -demon_ent_release_root_and_children(DEMON_Entity *root){ - Assert(demon_ent_base != 0); - Assert(root->parent != 0); - - // release children - demon_ent_release_children(root); - - // release root - DEMON_Entity *parent = root->parent; - demon_ent_release_single(root); - DLLRemove(parent->first, parent->last, root); - SLLStackPush(demon_ent_free, root); -} - -//- entity map - -internal U64 -demon_ent_map_hash(U16 kind, U64 id){ - U64 result = ((U64)kind << 32) ^ id; - return(result); -} - -internal void -demon_ent_map_save(U16 kind, U64 id, DEMON_Entity *entity){ - Assert(demon_ent_base != 0); - DEMON_Map *map = demon_ent_map; - - // allocate a new slot - DEMON_MapSlot *slot = map->free_slots; - if (slot != 0){ - SLLStackPop(map->free_slots); - } - else{ - slot = push_array_no_zero(demon_ent_arena, DEMON_MapSlot, 1); - } - - // fill slot - slot->kind = kind; - slot->id = id; - slot->entity = entity; - - // insert into bucket - U64 hash = demon_ent_map_hash(kind, id); - U64 bucket_index = hash%map->bucket_count; - SLLStackPush(map->buckets[bucket_index], slot); -} - -internal DEMON_MapRef -demon_ent_map_find(U16 kind, U64 id){ - Assert(demon_ent_base != 0); - DEMON_Map *map = demon_ent_map; - - // scan bucket - DEMON_MapRef result = {0}; - U64 hash = demon_ent_map_hash(kind, id); - U64 bucket_index = hash%map->bucket_count; - for (DEMON_MapSlot **ptr = &map->buckets[bucket_index], *slot = 0; - *ptr != 0; - ptr = &slot->next){ - slot = *ptr; - if (slot->kind == kind && slot->id == id){ - result.slot = slot; - result.ptr_to_slot = ptr; - break; - } - } - - return(result); -} - -internal DEMON_Entity* -demon_ent_map_entity_from_id(U16 kind, U64 id){ - DEMON_Entity *result = 0; - DEMON_MapRef ref = demon_ent_map_find(kind, id); - if (ref.slot != 0){ - result = ref.slot->entity; - } - return(result); -} - -internal void -demon_ent_map_erase(DEMON_MapRef ref){ - Assert(demon_ent_base != 0); - DEMON_Map *map = demon_ent_map; - - // move slot to free list - if (ref.slot != 0){ - *ref.ptr_to_slot = ref.slot->next; - SLLStackPush(map->free_slots, ref.slot); - } -} - -//////////////////////////////// -// NOTE(allen): Event Helpers - -internal DEMON_Event* -demon_push_event(Arena *arena, DEMON_EventList *list, DEMON_EventKind kind){ - DEMON_EventNode *n = push_array(arena, DEMON_EventNode, 1); - DEMON_Event *result = &n->v; - SLLQueuePush(list->first, list->last, n); - list->count += 1; - result->kind = kind; - return(result); -} - diff --git a/src/demon/demon_common.h b/src/demon/demon_common.h deleted file mode 100644 index 07ec4765..00000000 --- a/src/demon/demon_common.h +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DEMON_COMMON_H -#define DEMON_COMMON_H - -//////////////////////////////// -//~ allen: DEMON Entity System - -typedef enum DEMON_EntityKind -{ - DEMON_EntityKind_NULL, - - DEMON_EntityKind_Root, - DEMON_EntityKind_Process, - DEMON_EntityKind_Thread, - DEMON_EntityKind_Module, - - DEMON_EntityKind_COUNT -} -DEMON_EntityKind; - -typedef struct DEMON_Entity DEMON_Entity; -struct DEMON_Entity -{ - // TODO(allen): these could be U32s - DEMON_Entity *next; - DEMON_Entity *prev; - DEMON_Entity *parent; - DEMON_Entity *first; - DEMON_Entity *last; - - DEMON_EntityKind kind; - Architecture arch; - U32 gen; - U64 id; - U64 addr_range_dim; - - // each OS backend decides how to use `ext` for each entity kind - union{ - void *ext; - U64 ext_u64; - }; - - // the accel layer attaches some extra information to some entities - void *accel; -}; - -//- id -> entity map -typedef struct DEMON_MapSlot DEMON_MapSlot; -struct DEMON_MapSlot -{ - DEMON_MapSlot *next; - U16 kind; - U64 id; - DEMON_Entity *entity; -}; - -typedef struct DEMON_Map DEMON_Map; -struct DEMON_Map -{ - DEMON_MapSlot **buckets; - U64 bucket_count; - DEMON_MapSlot *free_slots; -}; - -typedef struct DEMON_MapRef DEMON_MapRef; -struct DEMON_MapRef -{ - DEMON_MapSlot *slot; - DEMON_MapSlot **ptr_to_slot; -}; - -//- rjf: entity extrusive list - -typedef struct DEMON_EntityNode DEMON_EntityNode; -struct DEMON_EntityNode -{ - DEMON_EntityNode *next; - DEMON_Entity *entity; -}; - -//////////////////////////////// -//~ allen: Demon Globals - -thread_static B32 demon_primary_thread = 0; -global B32 demon_run_state = 0; -global OS_Handle demon_state_mutex = {0}; - -global U64 demon_time = 0; - -global Arena *demon_ent_arena = 0; -global DEMON_Map *demon_ent_map = 0; - -global DEMON_Entity *demon_ent_free = 0; -global DEMON_Entity *demon_ent_root = 0; - -global DEMON_Entity *demon_ent_base = 0; -global DEMON_Entity *demon_ent_pos = 0; -global DEMON_Entity *demon_ent_opl = 0; -global void *demon_ent_cmt = 0; - -global U64 demon_proc_count = 0; -global U64 demon_thread_count = 0; -global U64 demon_module_count = 0; - -#if !defined(DEMON_ENTITY_CMT_SIZE) -# define DEMON_ENTITY_CMT_SIZE KB(64) -#endif -#if !defined(DEMON_ENTITY_CAP) -# define DEMON_ENTITY_CAP 65536 -#endif - -StaticAssert(IsPow2(DEMON_ENTITY_CMT_SIZE), check_demon_entity_cmt_size); - -//////////////////////////////// -//~ allen: State Safety Helper - -internal B32 demon_access_begin(void); -internal void demon_access_end(void); - -//////////////////////////////// -//~ allen: Entity System - -internal void demon_common_init(void); -internal DEMON_Entity* demon_ent_alloc(void); - -//- handle <-> entity pointer -internal DEMON_Entity* demon_ent_ptr_from_handle(DEMON_Handle handle); -internal DEMON_Handle demon_ent_handle_from_ptr(DEMON_Entity *entity); - -//- high level entity alloc,init,release -internal DEMON_Entity* demon_ent_new(DEMON_Entity *parent, DEMON_EntityKind kind, U64 id); -internal void demon_ent_release_single(DEMON_Entity *entity); -internal void demon_ent_release_children(DEMON_Entity *root); -internal void demon_ent_release_root_and_children(DEMON_Entity *root); - -//- entity map -internal U64 demon_ent_map_hash(U16 kind, U64 id); -internal void demon_ent_map_save(U16 kind, U64 id, DEMON_Entity *entity); -internal DEMON_MapRef demon_ent_map_find(U16 kind, U64 id); -internal DEMON_Entity* demon_ent_map_entity_from_id(U16 kind, U64 id); -internal void demon_ent_map_erase(DEMON_MapRef map_ref); - -//////////////////////////////// -//~ allen: Event Helpers - -internal DEMON_Event* demon_push_event(Arena *arena, DEMON_EventList *list, DEMON_EventKind kind); - -#endif //DEMON_COMMON_H diff --git a/src/demon/demon_core.c b/src/demon/demon_core.c index 43077629..86c1c3c6 100644 --- a/src/demon/demon_core.c +++ b/src/demon/demon_core.c @@ -2,148 +2,34 @@ // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// -//~ rjf: Main Layer Initialization +//~ rjf: Basic Type Functions (Helpers, Implemented Once) -internal void -demon_init(void){ - demon_common_init(); - demon_os_init(); +//- rjf: handles + +internal DMN_Handle +dmn_handle_zero(void) +{ + DMN_Handle h = {0}; + return h; } -//////////////////////////////// -//~ 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, "}"); +internal B32 +dmn_handle_match(DMN_Handle a, DMN_Handle b) +{ + return a.u32[0] == b.u32[0] && a.u32[1] == b.u32[1]; } //- rjf: trap chunk lists internal void -demon_trap_chunk_list_push(Arena *arena, DEMON_TrapChunkList *list, U64 cap, DEMON_Trap *trap) +dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap) { - DEMON_TrapChunkNode *node = list->last; + DMN_TrapChunkNode *node = list->last; if(node == 0 || node->count >= node->cap) { - node = push_array(arena, DEMON_TrapChunkNode, 1); + node = push_array(arena, DMN_TrapChunkNode, 1); node->cap = cap; - node->v = push_array_no_zero(arena, DEMON_Trap, node->cap); + node->v = push_array_no_zero(arena, DMN_Trap, node->cap); SLLQueuePush(list->first, list->last, node); list->node_count += 1; } @@ -153,7 +39,7 @@ demon_trap_chunk_list_push(Arena *arena, DEMON_TrapChunkList *list, U64 cap, DEM } internal void -demon_trap_chunk_list_concat_in_place(DEMON_TrapChunkList *dst, DEMON_TrapChunkList *to_push) +dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push) { if(dst->last == 0) { @@ -170,11 +56,11 @@ demon_trap_chunk_list_concat_in_place(DEMON_TrapChunkList *dst, DEMON_TrapChunkL } internal void -demon_trap_chunk_list_concat_shallow_copy(Arena *arena, DEMON_TrapChunkList *dst, DEMON_TrapChunkList *to_push) +dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push) { - for(DEMON_TrapChunkNode *src_n = to_push->first; src_n != 0; src_n = src_n->next) + for(DMN_TrapChunkNode *src_n = to_push->first; src_n != 0; src_n = src_n->next) { - DEMON_TrapChunkNode *dst_n = push_array(arena, DEMON_TrapChunkNode, 1); + DMN_TrapChunkNode *dst_n = push_array(arena, DMN_TrapChunkNode, 1); dst_n->v = src_n->v; dst_n->cap = src_n->cap; dst_n->count = src_n->count; @@ -187,685 +73,81 @@ demon_trap_chunk_list_concat_shallow_copy(Arena *arena, DEMON_TrapChunkList *dst //- rjf: handle lists internal void -demon_handle_list_push(Arena *arena, DEMON_HandleList *list, DEMON_Handle handle) +dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle) { - DEMON_HandleNode *node = push_array(arena, DEMON_HandleNode, 1); + DMN_HandleNode *node = push_array(arena, DMN_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) +internal DMN_HandleArray +dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list) { - DEMON_HandleArray array = {0}; + DMN_HandleArray array = {0}; array.count = list->count; - array.handles = push_array_no_zero(arena, DEMON_Handle, array.count); + array.handles = push_array_no_zero(arena, DMN_Handle, array.count); U64 idx = 0; - for(DEMON_HandleNode *n = list->first; n != 0; n = n->next, idx += 1) + for(DMN_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) +internal DMN_HandleArray +dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src) { - DEMON_HandleArray dst = {0}; + DMN_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); + dst.handles = push_array_no_zero(arena, DMN_Handle, dst.count); + MemoryCopy(dst.handles, src->handles, sizeof(DMN_Handle)*dst.count); return dst; } -//////////////////////////////// -//~ rjf: Primary Thread & Exclusive Mode Controls +//- rjf: event list building -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) +internal DMN_Event * +dmn_event_list_push(Arena *arena, DMN_EventList *list) { - 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}; + DMN_EventNode *n = push_array(arena, DMN_EventNode, 1); + SLLQueuePush(list->first, list->last, n); + list->count += 1; + DMN_Event *result = &n->v; + return result; +} + +//////////////////////////////// +//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once) + +internal U64 +dmn_rip_from_thread(DMN_Handle thread) +{ + U64 result = 0; + Temp scratch = scratch_begin(0, 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:; + Architecture arch = dmn_arch_from_thread(thread); + U64 reg_block_size = regs_block_size_from_architecture(arch); + void *reg_block = push_array(scratch.arena, U8, reg_block_size); + dmn_thread_read_reg_block(thread, reg_block); + result = regs_rip_from_arch_block(arch, reg_block); } - - // 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); + return result; } 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) +dmn_rsp_from_thread(DMN_Handle thread) { - Rng1U64 result = {0}; - if(demon_access_begin()) + U64 result = 0; + Temp scratch = scratch_begin(0, 0); { - 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(); + Architecture arch = dmn_arch_from_thread(thread); + U64 reg_block_size = regs_block_size_from_architecture(arch); + void *reg_block = push_array(scratch.arena, U8, reg_block_size); + dmn_thread_read_reg_block(thread, reg_block); + result = regs_rsp_from_arch_block(arch, reg_block); } - 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); + scratch_end(scratch); + return result; } diff --git a/src/demon/demon_core.h b/src/demon/demon_core.h index f164fcd1..48a2cb63 100644 --- a/src/demon/demon_core.h +++ b/src/demon/demon_core.h @@ -5,111 +5,70 @@ #define DEMON_CORE_H //////////////////////////////// -//~ allen: Demon Low Level Entities +//~ rjf: Control-Thread-Only Context +// +// An instance of this struct must ONLY be returned by dmn_ctrl_begin, and only +// used by the thread which called it. All APIs which can ONLY run on the +// control thread, which blocks to control & receive events, will take this +// parameter. All other APIs can be called from any thread. -typedef U64 DEMON_Handle; - -typedef struct DEMON_HandleNode DEMON_HandleNode; -struct DEMON_HandleNode +typedef struct DMN_CtrlCtx DMN_CtrlCtx; +struct DMN_CtrlCtx { - DEMON_HandleNode *next; - DEMON_Handle v; + U64 u64 [1]; }; -typedef struct DEMON_HandleList DEMON_HandleList; -struct DEMON_HandleList +//////////////////////////////// +//~ rjf: Handle Types + +typedef union DMN_Handle DMN_Handle; +union DMN_Handle { - DEMON_HandleNode *first; - DEMON_HandleNode *last; + U32 u32[2]; + U64 u64[1]; +}; + +typedef struct DMN_HandleNode DMN_HandleNode; +struct DMN_HandleNode +{ + DMN_HandleNode *next; + DMN_Handle v; +}; + +typedef struct DMN_HandleList DMN_HandleList; +struct DMN_HandleList +{ + DMN_HandleNode *first; + DMN_HandleNode *last; U64 count; }; -typedef struct DEMON_HandleArray DEMON_HandleArray; -struct DEMON_HandleArray +typedef struct DMN_HandleArray DMN_HandleArray; +struct DMN_HandleArray { - DEMON_Handle *handles; + DMN_Handle *handles; U64 count; }; //////////////////////////////// -//~ rjf: Memory Protection Flags +//~ rjf: Generated Code -typedef U32 DEMON_MemoryProtectFlags; -enum{ - DEMON_MemoryProtectFlag_Read = (1<<0), - DEMON_MemoryProtectFlag_Write = (1<<1), - DEMON_MemoryProtectFlag_Execute = (1<<2), -}; +#include "generated/demon.meta.h" //////////////////////////////// -//~ allen: Demon Event Types +//~ rjf: Event Types -typedef enum DEMON_EventKind +typedef struct DMN_Event DMN_Event; +struct DMN_Event { - DEMON_EventKind_Null, - DEMON_EventKind_Error, - DEMON_EventKind_HandshakeComplete, - DEMON_EventKind_CreateProcess, - DEMON_EventKind_ExitProcess, - DEMON_EventKind_CreateThread, - DEMON_EventKind_ExitThread, - DEMON_EventKind_LoadModule, - DEMON_EventKind_UnloadModule, - DEMON_EventKind_Breakpoint, - DEMON_EventKind_Trap, - DEMON_EventKind_SingleStep, - DEMON_EventKind_Exception, - DEMON_EventKind_Halt, - DEMON_EventKind_Memory, - DEMON_EventKind_DebugString, - DEMON_EventKind_SetThreadName, - DEMON_EventKind_COUNT -} -DEMON_EventKind; - -typedef enum DEMON_ErrorKind -{ - DEMON_ErrorKind_Null, - DEMON_ErrorKind_NotInitialized, - DEMON_ErrorKind_NotAttached, - DEMON_ErrorKind_UnexpectedFailure, - DEMON_ErrorKind_InvalidHandle, -} -DEMON_ErrorKind; - -typedef enum DEMON_MemoryEventKind -{ - DEMON_MemoryEventKind_Null, - DEMON_MemoryEventKind_Commit, - DEMON_MemoryEventKind_Reserve, - DEMON_MemoryEventKind_Decommit, - DEMON_MemoryEventKind_Release, - DEMON_MemoryEventKind_COUNT -} -DEMON_MemoryEventKind; - -typedef enum DEMON_ExceptionKind -{ - DEMON_ExceptionKind_Null, - DEMON_ExceptionKind_MemoryRead, - DEMON_ExceptionKind_MemoryWrite, - DEMON_ExceptionKind_MemoryExecute, - DEMON_ExceptionKind_CppThrow, - DEMON_ExceptionKind_COUNT -} -DEMON_ExceptionKind; - -typedef struct DEMON_Event DEMON_Event; -struct DEMON_Event -{ - // TODO(allen): condense - DEMON_EventKind kind; - DEMON_ErrorKind error_kind; - DEMON_MemoryEventKind memory_kind; - DEMON_ExceptionKind exception_kind; - DEMON_Handle process; - DEMON_Handle thread; - DEMON_Handle module; + DMN_EventKind kind; + DMN_ErrorKind error_kind; + DMN_MemoryEventKind memory_kind; + DMN_ExceptionKind exception_kind; + DMN_Handle process; + DMN_Handle thread; + DMN_Handle module; + Architecture arch; U64 address; U64 size; String8 string; @@ -123,171 +82,156 @@ struct DEMON_Event B32 exception_repeated; }; -typedef struct DEMON_EventNode DEMON_EventNode; -struct DEMON_EventNode +typedef struct DMN_EventNode DMN_EventNode; +struct DMN_EventNode { - DEMON_EventNode *next; - DEMON_Event v; + DMN_EventNode *next; + DMN_Event v; }; -typedef struct DEMON_EventList DEMON_EventList; -struct DEMON_EventList +typedef struct DMN_EventList DMN_EventList; +struct DMN_EventList { - DEMON_EventNode *first; - DEMON_EventNode *last; + DMN_EventNode *first; + DMN_EventNode *last; U64 count; }; //////////////////////////////// -//~ allen: Demon Run Control Types +//~ rjf: Run Control Types -typedef struct DEMON_Trap DEMON_Trap; -struct DEMON_Trap +typedef struct DMN_Trap DMN_Trap; +struct DMN_Trap { - DEMON_Handle process; - U64 address; + DMN_Handle process; + U64 vaddr; U64 id; }; -typedef struct DEMON_TrapChunkNode DEMON_TrapChunkNode; -struct DEMON_TrapChunkNode +typedef struct DMN_TrapChunkNode DMN_TrapChunkNode; +struct DMN_TrapChunkNode { - DEMON_TrapChunkNode *next; - DEMON_Trap *v; + DMN_TrapChunkNode *next; + DMN_Trap *v; U64 cap; U64 count; }; -typedef struct DEMON_TrapChunkList DEMON_TrapChunkList; -struct DEMON_TrapChunkList +typedef struct DMN_TrapChunkList DMN_TrapChunkList; +struct DMN_TrapChunkList { - DEMON_TrapChunkNode *first; - DEMON_TrapChunkNode *last; + DMN_TrapChunkNode *first; + DMN_TrapChunkNode *last; U64 node_count; U64 trap_count; }; -typedef struct DEMON_RunCtrls DEMON_RunCtrls; -struct DEMON_RunCtrls +typedef struct DMN_RunCtrls DMN_RunCtrls; +struct DMN_RunCtrls { - DEMON_Handle single_step_thread; + DMN_Handle single_step_thread; B8 ignore_previous_exception; B8 run_entities_are_unfrozen; B8 run_entities_are_processes; - DEMON_Handle *run_entities; + DMN_Handle *run_entities; U64 run_entity_count; - DEMON_TrapChunkList traps; + DMN_TrapChunkList traps; }; //////////////////////////////// -//~ allen: Demon Process Listing +//~ rjf: System Process Listing Types -typedef struct DEMON_ProcessIter DEMON_ProcessIter; -struct DEMON_ProcessIter +typedef struct DMN_ProcessIter DMN_ProcessIter; +struct DMN_ProcessIter { U64 v[2]; }; -typedef struct DEMON_ProcessInfo DEMON_ProcessInfo; -struct DEMON_ProcessInfo +typedef struct DMN_ProcessInfo DMN_ProcessInfo; +struct DMN_ProcessInfo { String8 name; U32 pid; }; //////////////////////////////// -//~ rjf: Main Layer Initialization +//~ rjf: Basic Type Functions (Helpers, Implemented Once) -internal void demon_init(void); - -//////////////////////////////// -//~ rjf: Basic Type Functions - -//- rjf: stringizing -internal String8 demon_string_from_event_kind(DEMON_EventKind kind); -internal String8 demon_string_from_memory_event_kind(DEMON_MemoryEventKind kind); -internal String8 demon_string_from_exception_kind(DEMON_ExceptionKind kind); -internal void demon_string_list_from_event(Arena *arena, String8List *out, DEMON_Event *event); +//- rjf: handles +internal DMN_Handle dmn_handle_zero(void); +internal B32 dmn_handle_match(DMN_Handle a, DMN_Handle b); //- rjf: trap chunk lists -internal void demon_trap_chunk_list_push(Arena *arena, DEMON_TrapChunkList *list, U64 cap, DEMON_Trap *trap); -internal void demon_trap_chunk_list_concat_in_place(DEMON_TrapChunkList *dst, DEMON_TrapChunkList *to_push); -internal void demon_trap_chunk_list_concat_shallow_copy(Arena *arena, DEMON_TrapChunkList *dst, DEMON_TrapChunkList *to_push); +internal void dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap); +internal void dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push); +internal void dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push); //- rjf: handle lists -internal void demon_handle_list_push(Arena *arena, DEMON_HandleList *list, DEMON_Handle handle); -internal DEMON_HandleArray demon_handle_array_from_list(Arena *arena, DEMON_HandleList *list); -internal DEMON_HandleArray demon_handle_array_copy(Arena *arena, DEMON_HandleArray *src); +internal void dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle); +internal DMN_HandleArray dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list); +internal DMN_HandleArray dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src); + +//- rjf: event list building +internal DMN_Event *dmn_event_list_push(Arena *arena, DMN_EventList *list); //////////////////////////////// -//~ rjf: Primary Thread & Exclusive Mode Controls +//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once) -internal void demon_primary_thread_begin(void); -internal void demon_exclusive_mode_begin(void); -internal void demon_exclusive_mode_end(void); +internal U64 dmn_rip_from_thread(DMN_Handle thread); +internal U64 dmn_rsp_from_thread(DMN_Handle thread); //////////////////////////////// -//~ rjf: Running/Halting +//~ rjf: @dmn_os_hooks Main Layer Initialization (Implemented Per-OS) -internal DEMON_EventList demon_run(Arena *arena, DEMON_RunCtrls *ctrls); -internal void demon_halt(U64 code, U64 user_data); -internal U64 demon_get_time_counter(void); +internal void dmn_init(void); //////////////////////////////// -//~ rjf: Target Process Launching/Attaching/Killing/Detaching/Halting +//~ rjf: @dmn_os_hooks Blocking Control Thread Operations (Implemented Per-OS) -internal U32 demon_launch_process(OS_LaunchOptions *options); -internal B32 demon_attach_process(U32 pid); -internal B32 demon_kill_process(DEMON_Handle process, U32 exit_code); -internal B32 demon_detach_process(DEMON_Handle process); +internal DMN_CtrlCtx *dmn_ctrl_begin(void); +internal void dmn_ctrl_exclusive_access_begin(void); +internal void dmn_ctrl_exclusive_access_end(void); +#define DMN_CtrlExclusiveAccessScope DeferLoop(dmn_ctrl_exclusive_access_begin(), dmn_ctrl_exclusive_access_end()) +internal U32 dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_LaunchOptions *options); +internal B32 dmn_ctrl_attach(DMN_CtrlCtx *ctx, U32 pid); +internal B32 dmn_ctrl_kill(DMN_CtrlCtx *ctx, DMN_Handle process, U32 exit_code); +internal B32 dmn_ctrl_detach(DMN_CtrlCtx *ctx, DMN_Handle process); +internal DMN_EventList dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls); //////////////////////////////// -//~ rjf: Entity Functions +//~ rjf: @dmn_os_hooks Halting (Implemented Per-OS) -//- rjf: basics -internal B32 demon_object_exists(DEMON_Handle object); - -//- rjf: introspection -internal Architecture demon_arch_from_object(DEMON_Handle object); -internal U64 demon_base_vaddr_from_module(DEMON_Handle module); -internal Rng1U64 demon_vaddr_range_from_module(DEMON_Handle module); -internal String8 demon_full_path_from_module(Arena *arena, DEMON_Handle module); -internal U64 demon_stack_base_vaddr_from_thread(DEMON_Handle thread); -internal U64 demon_tls_root_vaddr_from_thread(DEMON_Handle thread); -internal DEMON_HandleArray demon_all_processes(Arena *arena); -internal DEMON_HandleArray demon_threads_from_process(Arena *arena, DEMON_Handle process); -internal DEMON_HandleArray demon_modules_from_process(Arena *arena, DEMON_Handle process); - -//- rjf: target process memory allocation/protection -internal U64 demon_reserve_memory(DEMON_Handle process, U64 size); -internal B32 demon_set_memory_protect_flags(DEMON_Handle process, U64 page_vaddr, U64 size, DEMON_MemoryProtectFlags flags); -internal B32 demon_release_memory(DEMON_Handle process, U64 vaddr, U64 size); - -//- rjf: target process memory reading/writing -internal U64 demon_read_memory(DEMON_Handle process, void *dst, U64 src_address, U64 size); -internal B32 demon_write_memory(DEMON_Handle process, U64 dst_address, void *src, U64 size); -internal U64 demon_read_memory_amap_aligned(DEMON_Handle process, void *dst, U64 src_address, U64 size); -internal U64 demon_read_memory_amap(DEMON_Handle process, void *dst, U64 src_address, U64 size); - -//- rjf: thread registers reading/writing -// IMPORTANT(allen): This API is _trusting_ you. You should never modify the data pointed -// at by that void pointer! It is pointing to the internal cache of the registers, so it -// will become invalid after a call to demon_write_regs, or demon_run. Use it to read -// what you need and be done ASAP and we can avoid an extra copy baked into the API. -internal void *demon_read_regs(DEMON_Handle thread); -internal B32 demon_write_regs(DEMON_Handle thread, void *data); -// TODO(allen): These might be a bad idea when we try to extend to ARM -// They make sense for x86/x64 abstraction, which often needs identical -// code paths except for these parts. Revisit this when ARM is integrated. -internal U64 demon_read_ip(DEMON_Handle thread); -internal U64 demon_read_sp(DEMON_Handle thread); -internal void demon_write_ip(DEMON_Handle thread, U64 ip); +internal void dmn_halt(U64 code, U64 user_data); //////////////////////////////// -//~ rjf: Process Listing +//~ rjf: @dmn_os_hooks Introspection Functions (Implemented Per-OS) -internal void demon_proc_iter_begin(DEMON_ProcessIter *iter); -internal B32 demon_proc_iter_next(Arena *arena, DEMON_ProcessIter *iter, DEMON_ProcessInfo *info_out); -internal void demon_proc_iter_end(DEMON_ProcessIter *iter); +//- rjf: run/memory/register counters +internal U64 dmn_run_gen(void); +internal U64 dmn_mem_gen(void); +internal U64 dmn_reg_gen(void); -#endif //DEMON_CORE_H +//- rjf: non-blocking-control-thread access barriers +internal B32 dmn_access_open(void); +internal void dmn_access_close(void); +#define DMN_AccessScope DeferLoopChecked(dmn_access_open(), dmn_access_close()) + +//- rjf: processes +internal U64 dmn_process_read(DMN_Handle process, Rng1U64 range, void *dst); +internal B32 dmn_process_write(DMN_Handle process, Rng1U64 range, void *src); +#define dmn_process_read_struct(process, vaddr, ptr) dmn_process_read((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) +#define dmn_process_write_struct(process, vaddr, ptr) dmn_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) + +//- rjf: threads +internal Architecture 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); +internal B32 dmn_thread_write_reg_block(DMN_Handle handle, void *reg_block); + +//- rjf: system process listing +internal void dmn_process_iter_begin(DMN_ProcessIter *iter); +internal B32 dmn_process_iter_next(Arena *arena, DMN_ProcessIter *iter, DMN_ProcessInfo *info_out); +internal void dmn_process_iter_end(DMN_ProcessIter *iter); + +#endif // DEMON_CORE_H diff --git a/src/demon2/demon2_core.mdesk b/src/demon/demon_core.mdesk similarity index 100% rename from src/demon2/demon2_core.mdesk rename to src/demon/demon_core.mdesk diff --git a/src/demon/demon_inc.c b/src/demon/demon_inc.c index 64a45e09..daab75e2 100644 --- a/src/demon/demon_inc.c +++ b/src/demon/demon_inc.c @@ -2,14 +2,9 @@ // Licensed under the MIT license (https://opensource.org/license/mit/) #include "demon_core.c" -#include "demon_common.c" -#include "demon_accel.c" -#include "demon_os.c" #if OS_WINDOWS -# include "win32/demon_os_win32.c" -#elif OS_LINUX -# include "linux/demon_os_linux.c" +# include "win32/demon_core_win32.c" #else -# error No Demon Implementation for This OS +# error Demon layer backend not defined for this operating system. #endif diff --git a/src/demon/demon_inc.h b/src/demon/demon_inc.h index daf2f65d..3328f9b6 100644 --- a/src/demon/demon_inc.h +++ b/src/demon/demon_inc.h @@ -5,16 +5,11 @@ #define DEMON_INC_H #include "demon_core.h" -#include "demon_common.h" -#include "demon_accel.h" -#include "demon_os.h" #if OS_WINDOWS -# include "win32/demon_os_win32.h" -#elif OS_LINUX -# include "linux/demon_os_linux.h" +# include "win32/demon_core_win32.h" #else -# error No Demon Implementation for This OS +# error Demon layer backend not defined for this operating system. #endif -#endif //DEMON_INC_H +#endif // DEMON_INC_H diff --git a/src/demon/demon_os.c b/src/demon/demon_os.c deleted file mode 100644 index 76c55a9b..00000000 --- a/src/demon/demon_os.c +++ /dev/null @@ -1,28 +0,0 @@ -//////////////////////////////// -//~ rjf: Helpers - -internal B32 -demon_os_read_regs(DEMON_Entity *thread, void *dst) -{ - B32 result = 0; - switch(thread->arch) - { - default:{}break; - case Architecture_x86:{result = demon_os_read_regs_x86(thread, (REGS_RegBlockX86 *)dst);}break; - case Architecture_x64:{result = demon_os_read_regs_x64(thread, (REGS_RegBlockX64 *)dst);}break; - } - return result; -} - -internal B32 -demon_os_write_regs(DEMON_Entity *thread, void *src) -{ - B32 result = 0; - switch(thread->arch) - { - default:{}break; - case Architecture_x86:{result = demon_os_write_regs_x86(thread, (REGS_RegBlockX86 *)src);}break; - case Architecture_x64:{result = demon_os_write_regs_x64(thread, (REGS_RegBlockX64 *)src);}break; - } - return result; -} diff --git a/src/demon/demon_os.h b/src/demon/demon_os.h deleted file mode 100644 index fd6aac59..00000000 --- a/src/demon/demon_os.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DEMON_OS_H -#define DEMON_OS_H - -// NOTE(allen): -// These are the functions that the OS backends actually implement. -// Demon objects go through a handle validation layer but it is a lot more -// convenient in the OS backends to implement these versions which take the -// already validated DEMON_Entity*. These are also more convenient to call from -// the backend layer, which lets us avoid converting back and forth between -// handles and pointers a lot. - -//////////////////////////////// -//~ NOTE(allen): Demon OS Run Control Types - -typedef struct DEMON_OS_Trap DEMON_OS_Trap; -struct DEMON_OS_Trap -{ - DEMON_Entity *process; - U64 address; -}; - -typedef struct DEMON_OS_RunCtrls DEMON_OS_RunCtrls; -struct DEMON_OS_RunCtrls -{ - DEMON_Entity *single_step_thread; - B8 ignore_previous_exception; - B8 run_entities_are_unfrozen; - B8 run_entities_are_processes; - DEMON_Entity **run_entities; - U64 run_entity_count; - DEMON_OS_Trap *traps; - U64 trap_count; -}; - -//////////////////////////////// -//~ rjf: Helpers - -internal B32 demon_os_read_regs(DEMON_Entity *thread, void *dst); -internal B32 demon_os_write_regs(DEMON_Entity *thread, void *src); - -//////////////////////////////// -//~ rjf: @demon_os_hooks Main Layer Initialization - -internal void demon_os_init(void); - -//////////////////////////////// -//~ rjf: @demon_os_hooks Running/Halting - -internal DEMON_EventList demon_os_run(Arena *arena, DEMON_OS_RunCtrls *controls); -internal void demon_os_halt(U64 code, U64 user_data); - -//////////////////////////////// -//~ rjf: @demon_os_hooks Target Process Launching/Attaching/Killing/Detaching/Halting - -internal U32 demon_os_launch_process(OS_LaunchOptions *options); -internal B32 demon_os_attach_process(U32 pid); -internal B32 demon_os_kill_process(DEMON_Entity *process, U32 exit_code); -internal B32 demon_os_detach_process(DEMON_Entity *process); - -//////////////////////////////// -//~ rjf: @demon_os_hooks Entity Functions - -//- rjf: cleanup -internal void demon_os_entity_cleanup(DEMON_Entity *entity); - -//- rjf: introspection -internal String8 demon_os_full_path_from_module(Arena *arena, DEMON_Entity *module); -internal U64 demon_os_stack_base_vaddr_from_thread(DEMON_Entity *thread); -internal U64 demon_os_tls_root_vaddr_from_thread(DEMON_Entity *thread); - -//- rjf: target process memory allocation/protection -internal U64 demon_os_reserve_memory(DEMON_Entity *process, U64 size); -internal void demon_os_set_memory_protect_flags(DEMON_Entity *process, U64 page_vaddr, U64 size, DEMON_MemoryProtectFlags flags); -internal void demon_os_release_memory(DEMON_Entity *process, U64 vaddr, U64 size); - -//- rjf: target process memory reading/writing -internal U64 demon_os_read_memory(DEMON_Entity *process, void *dst, U64 src_address, U64 size); -internal B32 demon_os_write_memory(DEMON_Entity *process, U64 dst_address, void *src, U64 size); -#define demon_os_read_struct(p,dst,src) demon_os_read_memory((p), (dst), (src), sizeof(*(dst))) -#define demon_os_write_struct(p,dst,src) demon_os_write_memory((p), (dst), (src), sizeof(*(src))) - -//- rjf: thread registers reading/writing -internal B32 demon_os_read_regs_x86(DEMON_Entity *thread, REGS_RegBlockX86 *dst); -internal B32 demon_os_write_regs_x86(DEMON_Entity *thread, REGS_RegBlockX86 *src); -internal B32 demon_os_read_regs_x64(DEMON_Entity *thread, REGS_RegBlockX64 *dst); -internal B32 demon_os_write_regs_x64(DEMON_Entity *thread, REGS_RegBlockX64 *src); - -//////////////////////////////// -//~ rjf: @demon_os_hooks Process Listing - -internal void demon_os_proc_iter_begin(DEMON_ProcessIter *iter); -internal B32 demon_os_proc_iter_next(Arena *arena, DEMON_ProcessIter *iter, DEMON_ProcessInfo *info_out); -internal void demon_os_proc_iter_end(DEMON_ProcessIter *iter); - -#endif //DEMON_OS_H diff --git a/src/demon2/generated/demon2.meta.c b/src/demon/generated/demon.meta.c similarity index 100% rename from src/demon2/generated/demon2.meta.c rename to src/demon/generated/demon.meta.c diff --git a/src/demon2/generated/demon2.meta.h b/src/demon/generated/demon.meta.h similarity index 95% rename from src/demon2/generated/demon2.meta.h rename to src/demon/generated/demon.meta.h index 0cb8af87..1db2313d 100644 --- a/src/demon2/generated/demon2.meta.h +++ b/src/demon/generated/demon.meta.h @@ -3,8 +3,8 @@ //- GENERATED CODE -#ifndef DEMON2_META_H -#define DEMON2_META_H +#ifndef DEMON_META_H +#define DEMON_META_H typedef enum DMN_EventKind { @@ -60,4 +60,4 @@ DMN_ExceptionKind_COUNT, C_LINKAGE_BEGIN C_LINKAGE_END -#endif // DEMON2_META_H +#endif // DEMON_META_H diff --git a/src/demon/linux/demon_os_linux.c b/src/demon/linux/demon_os_linux.c deleted file mode 100644 index 0c92f461..00000000 --- a/src/demon/linux/demon_os_linux.c +++ /dev/null @@ -1,2106 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -// TODO(allen): run controls: ignore_previous_exception - -//////////////////////////////// -//~ allen: Elf Parsing Code - -#include "syms/syms_elf_inc.c" - -//////////////////////////////// -//~ rjf: Globals - -global B32 demon_lnx_already_has_halt_injection = false; -global U64 demon_lnx_halt_code = 0; -global U64 demon_lnx_halt_user_data = 0; - -global B32 demon_lnx_new_process_pending = false; - -global Arena *demon_lnx_event_arena = 0; -global DEMON_EventList demon_lnx_queued_events = {0}; - -global U32 demon_lnx_ptrace_options = (PTRACE_O_TRACEEXIT| - PTRACE_O_EXITKILL| - PTRACE_O_TRACEFORK| - PTRACE_O_TRACEVFORK| - PTRACE_O_TRACECLONE); - -//////////////////////////////// -//~ rjf: Helpers - -internal DEMON_LNX_ThreadExt* -demon_lnx_thread_ext(DEMON_Entity *entity){ - DEMON_LNX_ThreadExt *result = (DEMON_LNX_ThreadExt*)&entity->ext; - return(result); -} - -internal B32 -demon_lnx_attach_pid(Arena *arena, pid_t pid, DEMON_LNX_AttachNode **new_node){ - B32 result = false; - - int attach_result = ptrace(PTRACE_ATTACH, pid, 0, 0); - if (attach_result == -1){ - // TODO(allen): attach denied - } - else{ - // return a new attachment node as soon as the ptrace exists. we use these nodes - // for cleanup on failure *and* for initializing on success. either way we need - // to see all new attachments whether or not they fully initialized correctly. - DEMON_LNX_AttachNode *proc_attachment = push_array_no_zero(arena, DEMON_LNX_AttachNode, 1); - proc_attachment->next = 0; - proc_attachment->pid = pid; - *new_node = proc_attachment; - - int status = 0; - pid_t wait_id = waitpid(pid, &status, __WALL); - // NOTE(allen): if wait_id != pid we don't know what that means; study that case before - // deciding how error handling around it works. - if (wait_id == pid){ - int setoptions_result = ptrace(PTRACE_SETOPTIONS, pid, 0, PtrFromInt(demon_lnx_ptrace_options)); - if (setoptions_result == -1){ - // TODO(allen): setup failed - } - else{ - result = true; - } - } - } - - return(result); -} - -internal String8 -demon_lnx_executable_path_from_pid(Arena *arena, pid_t pid){ - // get symbolic path - Temp scratch = scratch_begin(&arena, 1); - String8 exe_symbol_path = push_str8f(scratch.arena, "/proc/%d/exe", pid); - - // try to read the link for a bit - Temp restore_point = temp_begin(arena); - B32 got_final_result = false; - U8 *buffer = 0; - int size = 0; - S64 cap = PATH_MAX; - for (S64 r = 0; r < 4; cap *= 2, r += 1){ - temp_end(restore_point); - buffer = push_array_no_zero(arena, U8, cap); - size = readlink((char*)exe_symbol_path.str, (char*)buffer, cap); - if (size < cap){ - got_final_result = true; - break; - } - } - - // finalize result - String8 result = {0}; - if (!got_final_result || size == -1){ - temp_end(restore_point); - } - else{ - arena_put_back(arena, (cap - size - 1)); - result = str8(buffer, size + 1); - } - - scratch_end(scratch); - return(result); -} - -internal int -demon_lnx_open_memory_fd_for_pid(pid_t pid){ - Temp scratch = scratch_begin(0, 0); - String8 memory_path = push_str8f(scratch.arena, "/proc/%i/mem", pid); - int result = open((char*)memory_path.str, O_RDWR); - scratch_end(scratch); - return(result); -} - -internal Architecture -demon_lnx_arch_from_pid(pid_t pid){ - Temp scratch = scratch_begin(0, 0); - Architecture result = Architecture_Null; - - // exe path - String8 exe_path = demon_lnx_executable_path_from_pid(scratch.arena, pid); - - // handle to exe - int exe_fd = -1; - if (exe_path.size != 0){ - exe_fd = open((char*)exe_path.str, O_RDONLY); - } - - // elf identification - B32 is_elf = false; - U8 e_ident[SYMS_ElfIdentifier_NIDENT] = {0}; - if (exe_fd >= 0){ - if (pread(exe_fd, e_ident, sizeof(e_ident), 0) == sizeof(e_ident)){ - is_elf = (e_ident[SYMS_ElfIdentifier_MAG0] == 0x7f && - e_ident[SYMS_ElfIdentifier_MAG1] == 'E' && - e_ident[SYMS_ElfIdentifier_MAG2] == 'L' && - e_ident[SYMS_ElfIdentifier_MAG3] == 'F'); - } - } - - // elf class - U8 elf_class = 0; - if (is_elf){ - elf_class = e_ident[SYMS_ElfIdentifier_CLASS]; - } - - // exe header data - SYMS_ElfEhdr64 ehdr = {0}; - switch (elf_class){ - case 1: - { - SYMS_ElfEhdr32 ehdr32 = {0}; - if (pread(exe_fd, &ehdr32, sizeof(ehdr32), 0) == sizeof(ehdr32)){ - ehdr = syms_elf_ehdr64_from_ehdr32(ehdr32); - } - }break; - - case 2: - { - pread(exe_fd, &ehdr, sizeof(ehdr), 0); - }break; - } - - // determine machine type - switch (ehdr.e_machine){ - case SYMS_ElfMachineKind_386: - { - result = Architecture_x86; - }break; - - case SYMS_ElfMachineKind_ARM: - { - result = Architecture_arm32; - }break; - - case SYMS_ElfMachineKind_X86_64: - { - result = Architecture_x64; - }break; - - case SYMS_ElfMachineKind_AARCH64: - { - result = Architecture_arm64; - }break; - } - - scratch_end(scratch); - return(result); -} - -internal DEMON_LNX_ProcessAux -demon_lnx_aux_from_pid(pid_t pid, Architecture arch){ - DEMON_LNX_ProcessAux result = {0}; - B32 addr_32bit = (arch == Architecture_x86 || arch == Architecture_arm32); - - // open aux data - Temp scratch = scratch_begin(0, 0); - String8 auxv_symbol_path = push_str8f(scratch.arena, "/proc/%d/auxv", pid); - int aux_fd = open((char*)auxv_symbol_path.str, O_RDONLY); - - // scan aux data - if (aux_fd >= 0){ - for (;;){ - result.filled = true; - - // read next aux - U64 type = 0; - U64 val = 0; - if (addr_32bit){ - SYMS_ElfAuxv32 aux; - if (read(aux_fd, &aux, sizeof(aux)) != sizeof(aux)){ - goto brkloop; - } - type = aux.a_type; - val = aux.a_val; - } - else{ - SYMS_ElfAuxv64 aux; - if (read(aux_fd, &aux, sizeof(aux)) != sizeof(aux)){ - goto brkloop; - } - type = aux.a_type; - val = aux.a_val; - } - - // place value in result - switch (type){ - default:break; - case SYMS_ElfAuxType_NULL: goto brkloop; break; - case SYMS_ElfAuxType_PHNUM: result.phnum = val; break; - case SYMS_ElfAuxType_PHENT: result.phent = val; break; - case SYMS_ElfAuxType_PHDR: result.phdr = val; break; - case SYMS_ElfAuxType_EXECFN: result.execfn = val; break; - } - } - brkloop:; - - close(aux_fd); - } - - scratch_end(scratch); - return(result); -} - -internal DEMON_LNX_PhdrInfo -demon_lnx_phdr_info_from_memory(int memory_fd, B32 is_32bit, U64 phvaddr, U64 phentsize, U64 phcount){ - DEMON_LNX_PhdrInfo result = {0}; - result.range.min = max_U64; - - // how much phdr will we read? - U64 phdr_size_expected = (is_32bit?sizeof(SYMS_ElfPhdr32):sizeof(SYMS_ElfPhdr64)); - U64 phdr_stride = (phentsize?phentsize:phdr_size_expected); - U64 phdr_read_size = ClampTop(phdr_stride, phdr_size_expected); - - // scan table - U64 va = phvaddr; - for (U64 i = 0; i < phcount; i += 1, va += phdr_stride){ - - // get type and range - SYMS_ElfPKind p_type = 0; - U64 p_vaddr = 0; - U64 p_memsz = 0; - - if (is_32bit){ - SYMS_ElfPhdr32 phdr32 = {0}; - demon_lnx_read_memory(memory_fd, &phdr32, va, phdr_read_size); - p_type = phdr32.p_type; - p_vaddr = phdr32.p_vaddr; - p_memsz = phdr32.p_memsz; - } - else{ - SYMS_ElfPhdr64 phdr64 = {0}; - demon_lnx_read_memory(memory_fd, &phdr64, va, phdr_read_size); - p_type = phdr64.p_type; - p_vaddr = phdr64.p_vaddr; - p_memsz = phdr64.p_memsz; - } - - // save useful info - switch (p_type){ - case SYMS_ElfPKind_Dynamic: - { - result.dynamic = p_vaddr; - }break; - case SYMS_ElfPKind_Load: - { - U64 min = p_vaddr; - U64 max = p_vaddr + p_memsz; - result.range.min = Min(result.range.min, min); - result.range.max = Max(result.range.max, max); - }break; - } - } - - return(result); -} - -internal DEMON_LNX_ModuleNode* -demon_lnx_module_list_from_process(Arena *arena, DEMON_Entity *process){ - Architecture arch = (Architecture)process->arch; - B32 is_32bit = (arch == Architecture_x86 || arch == Architecture_arm32); - int memory_fd = (int)process->ext_u64; - - // aux from pid - DEMON_LNX_ProcessAux aux = demon_lnx_aux_from_pid((pid_t)process->id, arch); - - // extract info from program headers - DEMON_LNX_PhdrInfo phdr_info = demon_lnx_phdr_info_from_memory(memory_fd, is_32bit, - aux.phdr, aux.phent, aux.phnum); - - // linkmap first from memory space & dyn address - U64 first_linkmap_va = 0; - if (phdr_info.dynamic != 0){ - U64 off = phdr_info.dynamic; - for (;;){ - SYMS_ElfDyn64 dyn = {0}; - if (is_32bit){ - SYMS_ElfDyn32 dyn32 = {0}; - demon_lnx_read_memory(memory_fd, &dyn32, off, sizeof(dyn32)); - dyn.tag = dyn32.tag; - dyn.val = dyn32.val; - off += sizeof(dyn32); - } - else{ - demon_lnx_read_memory(memory_fd, &dyn, off, sizeof(dyn)); - off += sizeof(dyn); - } - - if (dyn.tag == SYMS_ElfDynTag_NULL){ - break; - } - - if (dyn.tag == SYMS_ElfDynTag_PLTGOT){ - // True for x86 and x64 - // vas[0] virtual address of .dynamic - // vas[2] callback for resolving function address of relocation and if successful jumps to it. - // - // Code that sets up PLTGOT is in glibc/sysdeps/x86_64/dl_machine.h -> elf_machine_runtime_setup - U64 vas_off = dyn.val; - U64 vas[3] = {0}; - demon_lnx_read_memory(memory_fd, vas, vas_off, sizeof(vas)); - first_linkmap_va = vas[1]; - break; - } - } - } - - // setup output list - DEMON_LNX_ModuleNode *first = 0; - DEMON_LNX_ModuleNode *last = 0; - - // main module - { - DEMON_LNX_ModuleNode *node = push_array(arena, DEMON_LNX_ModuleNode, 1); - SLLQueuePush(first, last, node); - node->vaddr = phdr_info.range.min; - node->size = phdr_info.range.max - phdr_info.range.min; - node->name = aux.execfn; - } - - // iterate link maps - if (first_linkmap_va != 0){ - U64 linkmap_va = first_linkmap_va; - - for (;;){ - SYMS_ElfLinkMap64 linkmap = {0}; - if (is_32bit){ - // TOOD(nick): endian awarness - SYMS_ElfLinkMap32 linkmap32 = {0}; - demon_lnx_read_memory(memory_fd, &linkmap32, linkmap_va, sizeof(linkmap32)); - linkmap.base = linkmap32.base; - linkmap.name = linkmap32.name; - linkmap.ld = linkmap32.ld; - linkmap.next = linkmap32.next; - } - else{ - demon_lnx_read_memory(memory_fd, &linkmap, linkmap_va, sizeof(linkmap)); - } - - if (linkmap.base != 0){ - // find phdrs for this module - SYMS_U64 phvaddr = 0; - SYMS_U64 phentsize = 0; - SYMS_U64 phcount = 0; - - if (is_32bit){ - SYMS_ElfEhdr32 ehdr = {0}; - demon_lnx_read_memory(memory_fd, &ehdr, linkmap.base, sizeof(ehdr)); - phvaddr = ehdr.e_phoff + linkmap.base; - phentsize = ehdr.e_phentsize; - phcount = ehdr.e_phnum; - } - else{ - SYMS_ElfEhdr64 ehdr = {0}; - demon_lnx_read_memory(memory_fd, &ehdr, linkmap.base, sizeof(ehdr)); - phvaddr = ehdr.e_phoff + linkmap.base; - phentsize = ehdr.e_phentsize; - phcount = ehdr.e_phnum; - } - - // extract info from phdrs - DEMON_LNX_PhdrInfo module_phdr_info = demon_lnx_phdr_info_from_memory(memory_fd, is_32bit, - phvaddr, phentsize, phcount); - - // save module node - DEMON_LNX_ModuleNode *node = push_array(arena, DEMON_LNX_ModuleNode, 1); - SLLQueuePush(first, last, node); - node->vaddr = linkmap.base; - node->size = module_phdr_info.range.max - module_phdr_info.range.min; - node->name = linkmap.name; - } - - linkmap_va = linkmap.next; - if (linkmap_va == 0){ - break; - } - } - } - - return(first); -} - -internal U64 -demon_lnx_read_memory(int memory_fd, void *dst, U64 src, U64 size){ - U64 bytes_read = 0; - U8 *ptr = (U8*)dst; - U8 *opl = ptr + size; - U64 cursor = src; - for (;ptr < opl;){ - size_t to_read = (size_t)(opl - ptr); - ssize_t actual_read = pread(memory_fd, ptr, to_read, cursor); - if (actual_read == -1){ - break; - } - ptr += actual_read; - cursor += actual_read; - bytes_read += actual_read; - } - return(bytes_read); -} - -internal B32 -demon_lnx_write_memory(int memory_fd, U64 dst, void *src, U64 size){ - B32 result = true; - U8 *ptr = (U8*)src; - U8 *opl = ptr + size; - U64 cursor = dst; - for (;ptr < opl;){ - size_t to_write = (size_t)(opl - ptr); - ssize_t actual_write = pwrite(memory_fd, ptr, to_write, cursor); - if (actual_write == -1){ - result = false; - break; - } - ptr += actual_write; - cursor += actual_write; - } - return(result); -} - -internal String8 -demon_lnx_read_memory_str(Arena *arena, int memory_fd, U64 address){ - // TODO(allen): this could be done better with a demon_lnx_read_memory - // that returns a read amount instead of a success/fail. - - // scan piece by piece - Temp scratch = scratch_begin(&arena, 1); - String8List list = {0}; - - U64 max_cap = 256; - U64 cap = max_cap; - U64 read_p = address; - for (;;){ - U8 *block = push_array(scratch.arena, U8, cap); - for (;cap > 0;){ - if (demon_lnx_read_memory(memory_fd, block, read_p, cap)){ - break; - } - cap /= 2; - } - read_p += cap; - - U64 block_opl = 0; - for (;block_opl < cap; block_opl += 1){ - if (block[block_opl] == 0){ - break; - } - } - - if (block_opl > 0){ - str8_list_push(scratch.arena, &list, str8(block, block_opl)); - } - - if (block_opl < cap || cap == 0){ - break; - } - } - - // assemble results - String8 result = str8_list_join(arena, &list, 0); - scratch_end(scratch); - return(result); -} - -internal void -demon_lnx_regs_x64_from_usr_regs_x64(SYMS_RegX64 *dst, DEMON_LNX_UserRegsX64 *src){ - dst->rax.u64 = src->rax; - dst->rcx.u64 = src->rcx; - dst->rdx.u64 = src->rdx; - dst->rbx.u64 = src->rbx; - dst->rsp.u64 = src->rsp; - dst->rbp.u64 = src->rbp; - dst->rsi.u64 = src->rsi; - dst->rdi.u64 = src->rdi; - dst->r8.u64 = src->r8; - dst->r9.u64 = src->r9; - dst->r10.u64 = src->r10; - dst->r11.u64 = src->r11; - dst->r12.u64 = src->r12; - dst->r13.u64 = src->r13; - dst->r14.u64 = src->r14; - dst->r15.u64 = src->r15; - dst->cs.u16 = src->cs; - dst->ds.u16 = src->ds; - dst->es.u16 = src->es; - dst->fs.u16 = src->fs; - dst->gs.u16 = src->gs; - dst->ss.u16 = src->ss; - dst->fsbase.u64 = src->fsbase; - dst->gsbase.u64 = src->gsbase; - dst->rip.u64 = src->rip; - dst->rflags.u64 = src->rflags; -} - -internal void -demon_lnx_usr_regs_x64_from_regs_x64(DEMON_LNX_UserRegsX64 *dst, SYMS_RegX64 *src){ - dst->rax = src->rax.u64; - dst->rcx = src->rcx.u64; - dst->rdx = src->rdx.u64; - dst->rbx = src->rbx.u64; - dst->rsp = src->rsp.u64; - dst->rbp = src->rbp.u64; - dst->rsi = src->rsi.u64; - dst->rdi = src->rdi.u64; - dst->r8 = src->r8.u64; - dst->r9 = src->r9.u64; - dst->r10 = src->r10.u64; - dst->r11 = src->r11.u64; - dst->r12 = src->r12.u64; - dst->r13 = src->r13.u64; - dst->r14 = src->r14.u64; - dst->r15 = src->r15.u64; - dst->cs = src->cs.u16; - dst->ds = src->ds.u16; - dst->es = src->es.u16; - dst->fs = src->fs.u16; - dst->gs = src->gs.u16; - dst->ss = src->ss.u16; - dst->fsbase = src->fsbase.u64; - dst->gsbase = src->gsbase.u64; - dst->rip = src->rip.u64; - dst->rflags = src->rflags.u64; -} - -//////////////////////////////// - -internal String8 -demon_lnx_read_int_string(Arena *arena, int fd, int radix){ - String8 integer = str8(0,0); - - int to_read = 0; - int to_seek = 0; - for (;;){ - char b = 0; - if (read(fd, &b, sizeof(b)) == 0){ - break; - } - to_seek += 1; - if ( ! char_is_digit(b, radix)){ - break; - } - to_read += 1; - } - - if (lseek(fd, -to_seek, SEEK_CUR) != -1) { - char *buf = push_array_no_zero(arena, char, to_read + 1); - read(fd, buf, to_read); - buf[to_read] = '\0'; - integer = str8((U8*)buf, (U64)to_read); - } - - return(integer); -} - -internal U64 -demon_lnx_read_u64(int fd, int radix){ - Temp scratch = scratch_begin(0, 0); - String8 integer = demon_lnx_read_int_string(scratch.arena, fd, radix); - U64 result = u64_from_str8(integer, radix); - scratch_end(scratch); - return(result); -} - -internal S64 -demon_lnx_read_s64(int fd, int radix){ - Temp scratch = scratch_begin(0, 0); - String8 integer = demon_lnx_read_int_string(scratch.arena, fd, radix); - S64 result = s64_from_str8(integer, radix); - scratch_end(scratch); - return(result); -} - -internal B32 -demon_lnx_read_expect(int fd, char expect){ - char got = 0; - read(fd, &got, sizeof(got)); - B32 result = (got == expect); - if (!result){ - lseek(fd, -1, SEEK_CUR); - } - return(result); -} - -internal int -demon_lnx_read_whitespace(int fd){ - int whitespace_size = 0; - for (;;){ - if (!demon_lnx_read_expect(fd, ' ')){ - if (!demon_lnx_read_expect(fd, '\t')){ - break; - } - } - whitespace_size += 1; - } - return whitespace_size; -} - -internal String8 -demon_lnx_read_string(Arena *arena, int fd){ - String8 result = str8(0,0); - - int to_read = 0; - int to_seek = 0; - for (;;){ - char b = 0; - if (read(fd, &b, sizeof(b)) == 0) { - break; - } - to_seek += 1; - if (b == '\0' || b == '\n'){ - break; - } - to_read += 1; - } - - if (to_seek > 0 && lseek(fd, -to_seek, SEEK_CUR) != -1){ - char *buf = push_array_no_zero(arena, char, to_read + 1); - read(fd, buf, to_read); - buf[to_read] = '\0'; - result = str8((U8*)buf, to_read); - } - - return(result); -} - -internal int -demon_lnx_open_maps(pid_t pid){ - Temp scratch = scratch_begin(0, 0); - String8 path = push_str8f(scratch.arena, "/proc/%d/maps", pid); - int maps = open((char*)path.str, O_RDONLY); - scratch_end(scratch); - return(maps); -} - -internal B32 -demon_lnx_next_map(Arena *arena, int maps, DEMON_LNX_MapsEntry *entry_out){ - B32 is_parsed = false; - MemoryZeroStruct(entry_out); - do{ - U64 address_lo = 0; - U64 address_hi = 0; - DEMON_LNX_PermFlags perms = 0; - U64 offset = 0; - U64 dev_major = 0; - U64 dev_minor = 0; - U64 inode = 0; - String8 pathname = str8(0,0); - - // address range - address_lo = demon_lnx_read_u64(maps, 16); - if (!demon_lnx_read_expect(maps, '-')){ - break; - } - address_hi = demon_lnx_read_u64(maps, 16); - if (demon_lnx_read_whitespace(maps) == 0){ - break; - } - - // permission flags - char b; - if (read(maps, &b, sizeof(b)) == 0){ - break; - } - if (b=='r'){ - perms |= DEMON_LNX_PermFlags_Read; - } - if (read(maps, &b, sizeof(b)) == 0){ - break; - } - if (b=='w'){ - perms |= DEMON_LNX_PermFlags_Write; - } - if (read(maps, &b, sizeof(b)) == 0){ - break; - } - if (b=='x'){ - perms |= DEMON_LNX_PermFlags_Exec; - } - if (read(maps, &b, sizeof(b)) == 0){ - break; - } - if (b == 'p'){ - perms |= DEMON_LNX_PermFlags_Private; - } - if (demon_lnx_read_whitespace(maps) == 0){ - break; - } - - // offset - offset = demon_lnx_read_u64(maps, 16); - if (demon_lnx_read_whitespace(maps) == 0){ - break; - } - - // dev - dev_major = demon_lnx_read_u64(maps, 10); - if (!demon_lnx_read_expect(maps, ':')){ - break; - } - dev_minor = demon_lnx_read_u64(maps, 10); - if (demon_lnx_read_whitespace(maps) == 0){ - break; - } - - // inode - inode = demon_lnx_read_u64(maps, 10); - if (demon_lnx_read_whitespace(maps) == 10){ - break; - } - - // pathname - pathname = demon_lnx_read_string(arena, maps); - - // emit entry if en - b = 0; - read(maps, &b, sizeof(b)); - if (b != '\n' && b != '\0') { - break; - } - - // fill result - entry_out->address_lo = address_lo; - entry_out->address_hi = address_hi; - entry_out->perms = perms; - entry_out->offset = offset; - entry_out->dev_major = (U32)dev_major; - entry_out->dev_minor = (U32)dev_minor; - entry_out->inode = inode; - entry_out->pathname = pathname; - entry_out->type = DEMON_LNX_MapsEntryType_Null; - entry_out->stack_tid = 0; - - if (str8_match(pathname, str8_lit("/"), StringMatchFlag_RightSideSloppy)){ - entry_out->type = DEMON_LNX_MapsEntryType_Path; - } else if (str8_match(pathname, str8_lit("[heap]"), 0)){ - entry_out->type = DEMON_LNX_MapsEntryType_Heap; - } else if (str8_match(pathname, str8_lit("[stack]"), 0)){ - entry_out->type = DEMON_LNX_MapsEntryType_Stack; - } else if (str8_match(pathname, str8_lit("[stack:"), StringMatchFlag_RightSideSloppy)){ - entry_out->type = DEMON_LNX_MapsEntryType_Stack; - String8 tid = str8_substr(pathname, r1u64(7, pathname.size - 8)); - entry_out->stack_tid = (pid_t)u64_from_str8(tid, 10); - } - - is_parsed = true; - }while(0); - return(is_parsed); -} - -//////////////////////////////// -//~ rjf: @demon_os_hooks Main Layer Initialization - -internal void -demon_os_init(void){ - demon_lnx_event_arena = arena_alloc(); -} - -//////////////////////////////// -//~ rjf: @demon_os_hooks Running/Halting - -internal DEMON_EventList -demon_os_run(Arena *arena, DEMON_OS_RunCtrls *controls){ - DEMON_EventList result = {0}; - - if (demon_ent_root == 0){ - demon_push_event(arena, &result, DEMON_EventKind_NotInitialized); - } - else if (demon_ent_root->first == 0 && !demon_lnx_new_process_pending){ - demon_push_event(arena, &result, DEMON_EventKind_NotAttached); - } - else{ - Temp scratch = scratch_begin(&arena, 1); - - // use queued events if there are any - if (demon_lnx_queued_events.first != 0){ - // copy event queue - for (DEMON_Event *node = demon_lnx_queued_events.first; - node != 0; - node = node->next){ - DEMON_Event *copy = push_array_no_zero(arena, DEMON_Event, 1); - MemoryCopyStruct(copy, node); - SLLQueuePush(result.first, result.last, copy); - } - result.count = demon_lnx_queued_events.count; - - // zero stored queue - MemoryZeroStruct(&demon_lnx_queued_events); - arena_clear(demon_lnx_event_arena); - } - - // get the single step thread (if any) - DEMON_Entity *single_step_thread = controls->single_step_thread; - - // do setup - B32 did_setup = false; - U8 *trap_swap_bytes = 0; - - if (result.first == 0){ - // TODO(allen): per-Architecture implementation of single steps - // set single step bit - if (single_step_thread != 0){ - switch (single_step_thread->arch){ - case Architecture_x86: - { - // TODO(allen): possibly buggy - SYMS_RegX86 regs = {0}; - demon_os_read_regs_x86(single_step_thread, ®s); - regs.eflags.u32 |= 0x100; - demon_os_write_regs_x86(single_step_thread, ®s); - }break; - - case Architecture_x64: - { - // TODO(allen): possibly buggy - SYMS_RegX64 regs = {0}; - demon_os_read_regs_x64(single_step_thread, ®s); - regs.rflags.u64 |= 0x100; - demon_os_write_regs_x64(single_step_thread, ®s); - }break; - } - } - - // TODO(allen): per-Architecture implementation of traps - trap_swap_bytes = push_array_no_zero(scratch.arena, U8, controls->trap_count); - - { - DEMON_OS_Trap *trap = controls->traps; - for (U64 i = 0; i < controls->trap_count; i += 1, trap += 1){ - if (demon_os_read_memory(trap->process, trap_swap_bytes + i, trap->address, 1)){ - U8 int3 = 0xCC; - demon_os_write_memory(trap->process, trap->address, &int3, 1); - } - else{ - trap_swap_bytes[i] = 0xCC; - } - } - } - - did_setup = true; - } - - // do run - B32 did_run = false; - if (did_setup){ - // continue non-frozen threads - DEMON_LNX_EntityNode *resume_threads = 0; - for (DEMON_Entity *process = demon_ent_root->first; - process != 0; - process = process->next){ - if (process->kind == DEMON_EntityKind_Process){ - - // determine if this process is frozen - B32 process_is_frozen = false; - if (controls->run_entities_are_processes){ - for (U64 i = 0; i < controls->run_entity_count; i += 1){ - if (controls->run_entities[i] == process){ - process_is_frozen = true; - break; - } - } - } - - for (DEMON_Entity *thread = process->first; - thread != 0; - thread = thread->next){ - if (thread->kind == DEMON_EntityKind_Thread){ - // determine if this thread is frozen - B32 is_frozen = false; - - if (controls->single_step_thread != 0 && - controls->single_step_thread != thread){ - is_frozen = true; - } - else{ - - if (controls->run_entities_are_processes){ - is_frozen = process_is_frozen; - } - else{ - for (U64 i = 0; i < controls->run_entity_count; i += 1){ - if (controls->run_entities[i] == thread){ - is_frozen = true; - break; - } - } - } - - if (controls->run_entities_are_unfrozen){ - is_frozen = !is_frozen; - } - } - - // continue if not frozen - if (!is_frozen){ - errno = 0; - ptrace(PTRACE_CONT, (pid_t)thread->id, 0, 0); - DEMON_LNX_EntityNode *thread_node = push_array_no_zero(scratch.arena, DEMON_LNX_EntityNode, 1); - SLLStackPush(resume_threads, thread_node); - thread_node->entity = thread; - } - } - } - } - } - - // get next stop - wait_for_stop: - B32 did_dummy_stop = false; - int status = 0; - pid_t wait_id = waitpid(-1, &status, __WALL); - - // increment demon time - demon_time += 1; - - // handle devent - DEMON_Entity *thread = demon_ent_map_entity_from_id(DEMON_EntityKind_Thread, wait_id); - if (thread == 0){ - if (wait_id >= 0){ - // TODO(allen): this isn't a great situation! From what I can tell there's no - // options that I am super happy with for going from unknown tid -> pid. - // We can parse it out of /proc//status; but I don't want to do that until - // I'm forced to, because it seems like this shouldn't happen if the ptrace - // API works correctly and we don't have any bugs in our demon entity system. - } - } - else{ - B32 thread_exit = false; - U64 exit_code = 0; - - DEMON_Entity *process = thread->parent; - // NOTE(allen): hitting this assert should never ever be possible, if our entities - // are wired up correctly. it doesn't matter what ptrace or waitpid are doing. - Assert(process != 0); - - // read register info - U64 instruction_pointer = 0; - union{ SYMS_RegX86 x86; SYMS_RegX64 x64; } regs = {0}; - - switch (thread->arch){ - case Architecture_x86: - { - demon_os_read_regs_x86(thread, ®s.x86); - instruction_pointer = regs.x86.eip.u32; - }break; - - case Architecture_x64: - { - demon_os_read_regs_x64(thread, ®s.x64); - instruction_pointer = regs.x64.rip.u64; - }break; - } - - // check stop status - if (WIFEXITED(status)){ - thread_exit = true; - } - if (WIFSIGNALED(status)){ - exit_code = WTERMSIG(status); - thread_exit = true; - } - - // extra event list - DEMON_EventList stop_events = {0}; - - if (WIFSTOPPED(status)){ - switch (WSTOPSIG(status)){ - case SIGTRAP: - { - switch (status >> 8){ - case (SIGTRAP | (PTRACE_EVENT_EXIT << 8)): - { - // TODO(allen): (not sure actually, study this part) - thread_exit = true; - }break; - - case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)): - { - // new thread coming - unsigned long new_tid = 0; - int get_message_result = ptrace(PTRACE_GETEVENTMSG, wait_id, 0, &new_tid); - if (get_message_result == -1){ - // TODO(allen): this isn't right, time to give up on getting this process. - // this will likely lead to getting unrecognized wait_id s later. So we need - // this stuff in the log to make sense of it still. - } - else{ - // thread entity - DEMON_Entity *new_thread = demon_ent_new(process, DEMON_EntityKind_Thread, new_tid); - demon_thread_count += 1; - DEMON_LNX_ThreadExt *thread_ext = demon_lnx_thread_ext(new_thread); - thread_ext->expecting_dummy_sigstop = true; - - // thread event - DEMON_Event *e = demon_push_event(arena, &stop_events, DEMON_EventKind_CreateThread); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(new_thread); - } - }break; - - case (SIGTRAP | (PTRACE_EVENT_FORK << 8)): - case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)): - { - // new process coming - unsigned long new_pid = 0; - int get_message_result = ptrace(PTRACE_GETEVENTMSG, wait_id, 0, &new_pid); - if (get_message_result == -1){ - // TODO(allen): this isn't right, time to give up on getting this process. - // this will likely lead to getting unrecognized wait_id s later. So we need - // this stuff in the log to make sense of it still. - } - else{ - Architecture arch = demon_lnx_arch_from_pid(new_pid); - - // process entity - DEMON_Entity *new_process = demon_ent_new(demon_ent_root, DEMON_EntityKind_Process, new_pid); - new_process->arch = arch; - new_process->ext_u64 = demon_lnx_open_memory_fd_for_pid(new_pid); - - demon_lnx_new_process_pending = false; - - // thread entity - DEMON_Entity *new_thread = demon_ent_new(new_process, DEMON_EntityKind_Thread, new_pid); - demon_thread_count += 1; - DEMON_LNX_ThreadExt *thread_ext = demon_lnx_thread_ext(new_thread); - thread_ext->expecting_dummy_sigstop = true; - - // process event - { - DEMON_Event *e = demon_push_event(arena, &stop_events, DEMON_EventKind_CreateProcess); - e->process = demon_ent_handle_from_ptr(new_process); - } - - // thread event - { - DEMON_Event *e = demon_push_event(arena, &stop_events, DEMON_EventKind_CreateThread); - e->process = demon_ent_handle_from_ptr(new_process); - e->thread = demon_ent_handle_from_ptr(new_thread); - } - } - }break; - - default: - { - // check single step - DEMON_EventKind e_kind = DEMON_EventKind_Trap; - if (thread == single_step_thread){ - e_kind = DEMON_EventKind_SingleStep; - } - - // check bp - if (e_kind == DEMON_EventKind_Trap){ - DEMON_OS_Trap *trap = controls->traps; - for (U64 i = 0; i < controls->trap_count; i += 1, trap += 1){ - if (trap->process == process && trap->address == instruction_pointer - 1){ - e_kind = DEMON_EventKind_Breakpoint; - break; - } - } - } - - // adjust ip after breakpoint - if (e_kind == DEMON_EventKind_Breakpoint){ - // TODO(allen): possibly buggy - switch (thread->arch){ - case Architecture_x86: - { - instruction_pointer -= 1; - regs.x86.eip.u32 = instruction_pointer; - demon_os_write_regs_x86(thread, ®s.x86); - }break; - - case Architecture_x64: - { - instruction_pointer -= 1; - regs.x64.rip.u64 = instruction_pointer; - demon_os_write_regs_x64(thread, ®s.x64); - }break; - } - } - - // event - DEMON_Event *e = demon_push_event(arena, &stop_events, e_kind); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - e->instruction_pointer = instruction_pointer; - }break; - } - }break; - - case SIGSTOP: - { - // TODO(allen): we need to figure out how we want to tell apart: - // SIGSTOP All-Stop, SIGSTOP Halt, SIGSTOP "User" - // what we're doing right now == big-time race conditions - - DEMON_LNX_ThreadExt *thread_ext = demon_lnx_thread_ext(thread); - - if (thread_ext->expecting_dummy_sigstop){ - thread_ext->expecting_dummy_sigstop = false; - did_dummy_stop = true; - } - else if (demon_lnx_already_has_halt_injection){ - DEMON_Event *e = demon_push_event(arena, &stop_events, DEMON_EventKind_Halt); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - e->instruction_pointer = instruction_pointer; - } - else{ - // TODO(allen): a signal we don't want to mess with (except to record that it happened maybe) - // we should "hand it back" - } - }break; - - default: - { -#if 0 - // these are a little special. the program cannot continue after these - // unless the user first does something to change the state (move the IP, change a variable, w/e) - case SIGABRT:case SIGFPE:case SIGSEGV: -#endif - - // event - DEMON_Event *e = demon_push_event(arena, &stop_events, DEMON_EventKind_Exception); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - e->instruction_pointer = instruction_pointer; - e->signo = WSTOPSIG(status); - }break; - } - } - - // entity cleanup - if (thread_exit){ - if (thread->id == process->id){ - // generate events for threads & modules - for (DEMON_Entity *entity = process->first; - entity != 0; - entity = entity->next){ - if (entity->kind == DEMON_EntityKind_Thread){ - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_ExitThread); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(entity); - } - else{ - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_UnloadModule); - e->process = demon_ent_handle_from_ptr(process); - e->module = demon_ent_handle_from_ptr(entity); - } - } - - // exit event - DEMON_Event *e = demon_push_event(arena, &stop_events, DEMON_EventKind_ExitProcess); - e->process = demon_ent_handle_from_ptr(process); - e->code = exit_code; - - // free entity - demon_ent_release_root_and_children(process); - } - else{ - // exit event - DEMON_Event *e = demon_push_event(arena, &stop_events, DEMON_EventKind_ExitThread); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - e->code = exit_code; - - // free entity - demon_ent_release_root_and_children(thread); - } - } - - // update all module lists (for each process ...) - DEMON_EventList module_change_events = {0}; - - for (DEMON_Entity *proc_node = demon_ent_root->first; - proc_node != 0; - proc_node = proc_node->next){ - DEMON_LNX_ModuleNode *first_module = demon_lnx_module_list_from_process(scratch.arena, proc_node); - - DEMON_LNX_EntityNode *first_unloaded = 0; - DEMON_LNX_EntityNode *last_unloaded = 0; - - // compute the delta (mark known modules, save list of unloaded modules) - for (DEMON_Entity *entity = proc_node->first; - entity != 0; - entity = entity->next){ - if (entity->kind == DEMON_EntityKind_Module){ - U64 base = entity->id; - U64 name = entity->ext_u64; - B32 still_exists = false; - for (DEMON_LNX_ModuleNode *module_node = first_module; - module_node != 0; - module_node = module_node->next){ - if (module_node->vaddr == base && module_node->name == name){ - module_node->already_known = true; - still_exists = true; - break; - } - } - if (!still_exists){ - DEMON_LNX_EntityNode *node = push_array_no_zero(scratch.arena, DEMON_LNX_EntityNode, 1); - SLLQueuePush(first_unloaded, last_unloaded, node); - node->entity = entity; - } - } - } - - // handle unloads - for (DEMON_LNX_EntityNode *unloaded_node = first_unloaded; - unloaded_node != 0; - unloaded_node = unloaded_node->next){ - DEMON_Entity *module = unloaded_node->entity; - - // event - { - DEMON_Event *e = demon_push_event(arena, &module_change_events, DEMON_EventKind_UnloadModule); - e->process = demon_ent_handle_from_ptr(proc_node); - e->module = demon_ent_handle_from_ptr(module); - } - - // free entity - demon_ent_release_root_and_children(module); - } - - // handle loads - for (DEMON_LNX_ModuleNode *module_node = first_module; - module_node != 0; - module_node = module_node->next){ - if (!module_node->already_known){ - // entity - DEMON_Entity *module = demon_ent_new(proc_node, DEMON_EntityKind_Module, module_node->vaddr); - demon_module_count += 1; - module->ext_u64 = module_node->name; - - // event - { - DEMON_Event *e = demon_push_event(arena, &module_change_events, DEMON_EventKind_LoadModule); - e->process = demon_ent_handle_from_ptr(proc_node); - e->module = demon_ent_handle_from_ptr(module); - e->address = module_node->vaddr; - e->size = module_node->size; - } - } - } - } - - // concat the events list (with module changes first) - result.count = module_change_events.count + stop_events.count; - result.first = module_change_events.first; - result.last = module_change_events.last; - if (stop_events.first != 0){ - if (result.first != 0){ - result.last->next = stop_events.first; - result.last = stop_events.last; - } - else{ - result.first = stop_events.first; - result.last = stop_events.last; - } - } - } - - // do we have a reason to keep going? - B32 skip_this_stop = false; - if (did_dummy_stop && result.count == 0){ - skip_this_stop = true; - } - - // ignore this stop, resume and wait again - if (skip_this_stop){ - if (wait_id != 0){ - ptrace(PTRACE_CONT, (pid_t)wait_id, 0, 0); - } - goto wait_for_stop; - } - - // stop all running threads - for (DEMON_LNX_EntityNode *node = resume_threads; - node != 0; - node = node->next){ - DEMON_Entity *thread = node->entity; - pid_t thread_id = (pid_t)thread->id; - if (thread_id != wait_id){ - union sigval sv = {0}; - sigqueue(thread_id, SIGSTOP, sv); - - DEMON_LNX_ThreadExt *thread_ext = demon_lnx_thread_ext(thread); - thread_ext->expecting_dummy_sigstop = true; - } - } - - did_run = true; - } - - // cleanup - if (did_run){ - // TODO(allen): per-Architecture - // unset traps - { - DEMON_OS_Trap *trap = controls->traps; - for (U64 i = 0; i < controls->trap_count; i += 1, trap += 1){ - U8 og_byte = trap_swap_bytes[i]; - if (og_byte != 0xCC){ - demon_os_write_memory(trap->process, trap->address, &og_byte, 1); - } - } - } - - // TODO(allen): per-Architecture - // unset single step bit - // the single step bit is automatically unset whenever we single step - // but if *something else* happened, it will still be there ready to - // confound us later; so here we're just being sure it's taken out. - if (single_step_thread != 0){ - // TODO(allen): possibly buggy - switch (single_step_thread->arch){ - case Architecture_x86: - { - SYMS_RegX86 regs = {0}; - demon_os_read_regs_x86(single_step_thread, ®s); - regs.eflags.u32 &= ~0x100; - demon_os_write_regs_x86(single_step_thread, ®s); - }break; - - case Architecture_x64: - { - SYMS_RegX64 regs = {0}; - demon_os_read_regs_x64(single_step_thread, ®s); - regs.rflags.u64 &= ~0x100; - demon_os_write_regs_x64(single_step_thread, ®s); - }break; - } - } - } - - scratch_end(scratch); - } - - return(result); -} - -internal void -demon_os_halt(U64 code, U64 user_data){ - if (demon_ent_root != 0 && !demon_lnx_already_has_halt_injection){ - DEMON_Entity *process = demon_ent_root->first; - if (process != 0){ - demon_lnx_already_has_halt_injection = true; - demon_lnx_halt_code = code; - demon_lnx_halt_user_data = user_data; - union sigval sv = {0}; - if (sigqueue(process->id, SIGSTOP, sv) == -1){ - demon_lnx_already_has_halt_injection = false; - } - } - } -} - -// NOTE(allen): siginfo hint from old code: -#if 0 -{ - switch (siginfo.si_code){ - // SI_KERNEL (hit int3; 0xCC) - case 0x80: - { - // TODO(allen): breakpoint event - }break; - - // TRAP_UNK, TRAP_HWBKPT, TRAP_BRKPT, TRAP_TRACE - case 0x5: case 0x4: case 0x1: case 0x2: - { - // TODO(allen): breakpoint event (?) - }break; - - case 0x3: case 0x0: - { - // TODO(allen): do nothing I guess? - }break; - } -} -#endif - -//////////////////////////////// -//~ rjf: @demon_os_hooks Target Process Launching/Attaching/Killing/Detaching/Halting - -internal U32 -demon_os_launch_process(OS_LaunchOptions *options){ - U32 result = 0; - Temp scratch = scratch_begin(0, 0); - - // arrange options - char *binary = 0; - char **args = 0; - if (options->cmd_line.node_count > 0){ - args = push_array_no_zero(scratch.arena, char*, options->cmd_line.node_count + 1); - char **arg_ptr = args; - for (String8Node *node = options->cmd_line.first; - node != 0; - node = node->next, arg_ptr += 1){ - String8 string = push_str8_copy(scratch.arena, node->string); - *arg_ptr = (char*)string.str; - } - *arg_ptr = 0; - binary = args[0]; - } - - char *path = 0; - { - String8 string = push_str8_copy(scratch.arena, options->path); - path = (char*)string.str; - } - - char **env = 0; - if (options->env.node_count > 0){ - env = push_array_no_zero(scratch.arena, char*, options->env.node_count + 1); - char **env_ptr = env; - for (String8Node *node = options->env.first; - node != 0; - node = node->next, env_ptr += 1){ - String8 string = push_str8_copy(scratch.arena, node->string); - *env_ptr = (char*)string.str; - } - *env_ptr = 0; - } - - // fork - if (binary != 0){ - pid_t pid = fork(); - if (pid == -1){ - // TODO(allen): fork error - } - else if (pid == 0){ - // NOTE(allen): child process - int ptrace_result = ptrace(PTRACE_TRACEME, 0, 0, 0); - if (ptrace_result != -1){ - int chdir_result = chdir(path); - if (chdir_result != -1){ - execve(binary, args, env); - } - } - // failed to init fully; abort so the parent can clean up the child - abort(); - } - else{ - // NOTE(allen): parent process - - // wait for child - int status = 0; - pid_t wait_id = waitpid(pid, &status, __WALL); - - // determine child launch status - enum{ - LaunchCode_Null, - LaunchCode_FailBeforePtrace, - LaunchCode_FailAfterPtrace, - LaunchCode_Success, - }; - U32 launch_result = LaunchCode_Null; - // NOTE(allen): if wait_id != pid we don't know what that means; study that case before - // deciding how error handling around it works. - if (wait_id == pid){ - if (WIFSTOPPED(status)){ - if (WSTOPSIG(status) == SIGTRAP){ - launch_result = LaunchCode_Success; - } - else{ - launch_result = LaunchCode_FailAfterPtrace; - } - } - else{ - launch_result = LaunchCode_FailBeforePtrace; - } - } - - // handle launch result - switch (launch_result){ - default: - { - // TODO(allen): error that we do not understand - }break; - - case LaunchCode_FailBeforePtrace: - { - // TODO(allen): child ptrace init failed - }break; - - case LaunchCode_FailAfterPtrace: - { - // need to specifically pull the exit status out of the child - // or it will sit around as a zombie forever since it is ptraced. - B32 cleanup_good = false; - int detach_result = ptrace(PTRACE_DETACH, pid, 0, (void*)SIGCONT); - if (detach_result != -1){ - int status_cleanup = 0; - pid_t wait_id_cleanup = waitpid(pid, &status_cleanup, __WALL); - if (wait_id_cleanup == pid){ - cleanup_good = true; - } - } - if (cleanup_good){ - // TODO(allen): child init failed - } - else{ - // TODO(allen): child init failed; something went wrong and a process may have leaked - } - }break; - - case LaunchCode_Success: - { - int setoptions_result = ptrace(PTRACE_SETOPTIONS, pid, 0, PtrFromInt(demon_lnx_ptrace_options)); - if (setoptions_result == -1){ - // TODO(allen): ptrace setup failed; need to kill the child and clean it up - } - else{ - result = pid; - - Architecture arch = demon_lnx_arch_from_pid(pid); - - // process entity - DEMON_Entity *process = demon_ent_new(demon_ent_root, DEMON_EntityKind_Process, pid); - demon_proc_count += 1; - process->arch = arch; - process->ext_u64 = demon_lnx_open_memory_fd_for_pid(pid); - - // thread entity - DEMON_Entity *thread = demon_ent_new(process, DEMON_EntityKind_Thread, pid); - demon_thread_count += 1; - - // process event - { - DEMON_Event *e = demon_push_event(demon_lnx_event_arena, &demon_lnx_queued_events, - DEMON_EventKind_CreateProcess); - e->process = demon_ent_handle_from_ptr(process); - } - - // thread event - { - DEMON_Event *e = demon_push_event(demon_lnx_event_arena, &demon_lnx_queued_events, - DEMON_EventKind_CreateThread); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - } - - // get module list - DEMON_LNX_ModuleNode *module_list = demon_lnx_module_list_from_process(scratch.arena, process); - - // for each module ... - for (DEMON_LNX_ModuleNode *node = module_list; - node != 0; - node = node->next){ - // module entity - DEMON_Entity *module = demon_ent_new(process, DEMON_EntityKind_Module, node->vaddr); - demon_module_count += 1; - module->ext_u64 = node->name; - - // event - { - DEMON_Event *e = demon_push_event(demon_lnx_event_arena, &demon_lnx_queued_events, - DEMON_EventKind_LoadModule); - e->process = demon_ent_handle_from_ptr(process); - e->module = demon_ent_handle_from_ptr(module); - e->address = node->vaddr; - e->size = node->size; - } - } - - // handshake event - { - DEMON_Event *e = demon_push_event(demon_lnx_event_arena, &demon_lnx_queued_events, - DEMON_EventKind_HandshakeComplete); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - } - } - }break; - } - } - } - - scratch_end(scratch); - return(result); -} - -internal B32 -demon_os_attach_process(U32 pid){ - B32 result = false; - - Temp scratch = scratch_begin(0, 0); - DEMON_LNX_AttachNode *attachments = 0; - DEMON_LNX_AttachNode *the_process = 0; - - // TODO(allen): double check that this logic only lets us - // "attach" when pid is the id of the main thread of a process. - - // attach this process - B32 attached_proc = false; - if (kill(pid, 0) == -1){ - // TODO(allen): process does not exist - } - else{ - attached_proc = demon_lnx_attach_pid(scratch.arena, pid, &the_process); - if (the_process != 0){ - SLLStackPush(attachments, the_process); - } - } - - // open thread list - if (attached_proc){ - String8 threads_path = push_str8f(scratch.arena, "/proc/%d/task", pid); - DIR *proc_dir = opendir((char*)threads_path.str); - if (proc_dir == 0){ - // TODO(allen): could not read proc threads somehow; no good! - } - else{ - - // attach all threads - B32 attached_all_threads = true; - for (;;){ - struct dirent *entry = readdir(proc_dir); - if (entry == 0){ - break; - } - - String8 name = str8_cstring(entry->d_name); - if (str8_is_integer(name, 10)){ - pid_t tid = u64_from_str8(name, 10); - if (tid != pid){ - DEMON_LNX_AttachNode *new_attachment = 0; - B32 attached_this_thread = demon_lnx_attach_pid(scratch.arena, tid, &new_attachment); - if (new_attachment != 0){ - SLLStackPush(attachments, new_attachment); - } - if (!attached_this_thread){ - attached_all_threads = false; - break; - } - } - } - } - closedir(proc_dir); - - if (attached_all_threads){ - result = true; - } - } - } - - // initialize new entities on success - if (result){ - Architecture arch = demon_lnx_arch_from_pid(the_process->pid); - - // process entity - DEMON_Entity *process = demon_ent_new(demon_ent_root, DEMON_EntityKind_Process, the_process->pid); - demon_proc_count += 1; - process->arch = arch; - process->ext_u64 = demon_lnx_open_memory_fd_for_pid(the_process->pid); - - // process event - { - DEMON_Event *e = demon_push_event(demon_lnx_event_arena, &demon_lnx_queued_events, - DEMON_EventKind_CreateProcess); - e->process = demon_ent_handle_from_ptr(process); - } - - // TODO(allen): happens on windows here? - - for (DEMON_LNX_AttachNode *node = attachments; - node != 0; - node = node->next){ - DEMON_Entity *thread = demon_ent_new(process, DEMON_EntityKind_Thread, node->pid); - demon_thread_count += 1; - - // thread event - { - DEMON_Event *e = demon_push_event(demon_lnx_event_arena, &demon_lnx_queued_events, - DEMON_EventKind_CreateThread); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - } - } - - // TODO(allen): sync modules in process - } - - // cleanup on failure - else{ - for (DEMON_LNX_AttachNode *node = attachments; - node != 0; - node = node->next){ - ptrace(PTRACE_DETACH, node->pid, 0, (void*)SIGCONT); - } - } - - scratch_end(scratch); - return(result); -} - -internal B32 -demon_os_kill_process(DEMON_Entity *process, U32 exit_code){ - B32 result = false; - if (process != 0){ - if (kill(process->id, SIGKILL) != -1){ - result = true; - } - } - return(result); -} - -internal B32 -demon_os_detach_process(DEMON_Entity *process){ - B32 result = false; - if (process != 0){ - int detach_result = ptrace(PTRACE_DETACH, process->id, 0, 0); - result = (detach_result != -1); - } - return(0); -} - -//////////////////////////////// -//~ rjf: @demon_os_hooks Entity Functions - -//- rjf: cleanup - -internal void -demon_os_entity_cleanup(DEMON_Entity *entity) -{ - // NOTE(rjf): no-op -} - -//- rjf: introspection - -internal String8 -demon_os_full_path_from_module(Arena *arena, DEMON_Entity *module){ - DEMON_Entity *process = module->parent; - int memory_fd = (int)process->ext_u64; - U64 name_va = module->ext_u64; - String8 result = demon_lnx_read_memory_str(arena, memory_fd, name_va); - return(result); -} - -internal U64 -demon_os_stack_base_vaddr_from_thread(DEMON_Entity *thread){ - Temp scratch = scratch_begin(0, 0); - - U64 stack_base = 0; - - DEMON_Entity *process = thread->parent; - - // id for main thread is zero - B32 is_main_thread = (thread->id == process->id); - pid_t match_tid = is_main_thread ? 0 : thread->id; - - // open /proc/$pid/maps - int maps = demon_lnx_open_maps(process->id); - - // look for entry with stack markings and matching thread id - for (;;){ - DEMON_LNX_MapsEntry e; - Temp temp = temp_begin(scratch.arena); - if (!demon_lnx_next_map(temp.arena, maps, &e)){ - break; - } - if (e.type == DEMON_LNX_MapsEntryType_Stack && e.stack_tid == match_tid){ - stack_base = e.address_lo; - break; - } - temp_end(temp); - } - - scratch_end(scratch); - return(stack_base); -} - -internal U64 -demon_os_tls_root_vaddr_from_thread(DEMON_Entity *thread){ - U64 result = 0; - switch (thread->arch){ - case Architecture_x64: - case Architecture_x86: - { - U32 fsbase = 0; - pid_t tid = (pid_t)thread->id; - if (ptrace(PT_GETFSBASE, tid, (void*)&fsbase, 0) != -1){ - result = (U64)fsbase; - } - if (thread->arch == Architecture_x64){ - result += 8; - } - else{ - result += 4; - } - }break; - } - return(result); -} - -//- rjf: target process memory allocation/protection - -internal U64 -demon_os_reserve_memory(DEMON_Entity *process, U64 size){ - U64 result = 0; - NotImplemented; - return(result); -} - -internal void -demon_os_set_memory_protect_flags(DEMON_Entity *process, U64 page_vaddr, U64 size, DEMON_MemoryProtectFlags flags){ - NotImplemented; -} - -internal void -demon_os_release_memory(DEMON_Entity *process, U64 vaddr, U64 size){ - NotImplemented; -} - -//- rjf: target process memory reading/writing - -internal U64 -demon_os_read_memory(DEMON_Entity *process, void *dst, U64 src_address, U64 size){ - int memory_fd = (int)process->ext_u64; - U64 result = demon_lnx_read_memory(memory_fd, dst, src_address, size); - return(result); -} - -internal B32 -demon_os_write_memory(DEMON_Entity *process, U64 dst_address, void *src, U64 size){ - int memory_fd = (int)process->ext_u64; - B32 result = demon_lnx_write_memory(memory_fd, dst_address, src, size); - return(result); -} - -//- rjf: thread registers reading/writing - -internal B32 -demon_os_read_regs_x86(DEMON_Entity *thread, SYMS_RegX86 *dst){ - B32 result = false; - NotImplemented; - return(result); -} - -internal B32 -demon_os_write_regs_x86(DEMON_Entity *thread, SYMS_RegX86 *src){ - B32 result = false; - NotImplemented; - return(result); -} - -internal B32 -demon_os_read_regs_x64(DEMON_Entity *thread, SYMS_RegX64 *dst){ - pid_t tid = (pid_t)thread->id; - - // gpr - B32 got_gpr = false; - DEMON_LNX_UserX64 ctx = {0}; - struct iovec iov_gpr = {0}; - iov_gpr.iov_len = sizeof(ctx); - iov_gpr.iov_base = &ctx; - if (ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, &iov_gpr) != -1){ - demon_lnx_regs_x64_from_usr_regs_x64(dst, &ctx.regs); - got_gpr = true; - } - - // fpr - B32 got_fpr = false; - if (got_gpr){ - B32 got_xsave = false; - { - U8 xsave_buffer[KB(4)]; - struct iovec iov_xsave = {0}; - iov_xsave.iov_len = sizeof(xsave_buffer); - iov_xsave.iov_base = xsave_buffer; - if (ptrace(PTRACE_GETREGSET, tid, (void*)NT_X86_XSTATE, &iov_xsave) != -1){ - SYMS_XSave *xsave = (SYMS_XSave*)xsave_buffer; - syms_x64_regs__set_full_regs_from_xsave_legacy(dst, &xsave->legacy); - - // TODO(allen): this is a lie; ymm can technically move around - // we need some more low-level-assembly-fu to do this hardcore. - B32 has_ymm_registers = ((xsave->header.xstate_bv & 4) != 0); - if (has_ymm_registers){ - syms_x64_regs__set_full_regs_from_xsave_avx_extension(dst, xsave->ymmh); - } - - got_xsave = true; - } - } - - B32 got_fxsave = false; - if (!got_xsave){ - SYMS_XSaveLegacy fxsave = {0}; - struct iovec iov_fxsave = {0}; - iov_fxsave.iov_len = sizeof(fxsave); - iov_fxsave.iov_base = &fxsave; - if (ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, &iov_fxsave) != -1){ - syms_x64_regs__set_full_regs_from_xsave_legacy(dst, &fxsave); - got_fxsave = true; - } - } - - if (got_xsave || got_fxsave){ - got_fpr = true; - } - } - - // debug - B32 got_debug = false; - if (got_fpr){ - got_debug = true; - SYMS_Reg32 *dr_d = &dst->dr0; - for (U32 i = 0; i < 8; i += 1, dr_d += 1){ - if (i != 4 && i != 5){ - U64 offset = OffsetOf(DEMON_LNX_UserX64, u_debugreg[i]); - errno = 0; - int peek_result = ptrace(PTRACE_PEEKUSER, tid, PtrFromInt(offset), 0); - if (errno == 0){ - dr_d->u32 = (U32)peek_result; - } - else{ - got_debug = false; - } - } - } - } - - // got everything - B32 result = got_debug; - return(result); -} - -internal B32 -demon_os_write_regs_x64(DEMON_Entity *thread, SYMS_RegX64 *src){ - pid_t tid = (pid_t)thread->id; - - // gpr - DEMON_LNX_UserX64 ctx = {0}; - demon_lnx_usr_regs_x64_from_regs_x64(&ctx.regs, src); - - struct iovec iov_gpr = {0}; - iov_gpr.iov_base = &ctx; - iov_gpr.iov_len = sizeof(ctx); - int gpr_result = ptrace(PTRACE_SETREGSET, tid, (void*)NT_PRSTATUS, &iov_gpr); - B32 gpr_success = (gpr_result != -1); - - // fpr - int xsave_result = 0; - int fxsave_result = 0; - - { - U8 xsave_buffer[KB(4)] = {0}; - SYMS_XSave *xsave = (SYMS_XSave*)xsave_buffer; - syms_x64_regs__set_xsave_legacy_from_full_regs(&xsave->legacy, src); - - xsave->header.xstate_bv = 7; - - // TODO(allen): this is a lie; ymm can technically move around - // we need some more low-level-assembly-fu to do this hardcore. - syms_x64_regs__set_xsave_avx_extension_from_full_regs(xsave->ymmh, src); - - { - struct iovec iov_xsave = {0}; - iov_xsave.iov_base = &xsave; - iov_xsave.iov_len = sizeof(xsave); - xsave_result = ptrace(PTRACE_SETREGSET, tid, (void*)NT_X86_XSTATE, &iov_xsave); - } - - if (xsave_result == -1){ - struct iovec iov_fxsave = {0}; - iov_fxsave.iov_base = &xsave->legacy; - iov_fxsave.iov_len = sizeof(xsave->legacy); - fxsave_result = ptrace(PTRACE_SETREGSET, tid, (void*)NT_FPREGSET, &iov_fxsave); - } - } - - B32 fpr_success = (xsave_result != -1 || fxsave_result != -1); - - // debug - B32 dr_success = true; - { - SYMS_Reg32 *dr_s = &src->dr0; - for (U32 i = 0; i < 8; i += 1, dr_s += 1){ - if (i != 4 && i != 5){ - U64 offset = OffsetOf(DEMON_LNX_UserX64, u_debugreg[i]); - errno = 0; - int poke_result = ptrace(PTRACE_POKEUSER, tid, PtrFromInt(offset), dr_s->u32); - if (poke_result == -1){ - dr_success = false; - } - } - } - } - - // assemble result - B32 result = (gpr_success && fpr_success && dr_success); - - return(result); -} - -//////////////////////////////// -//~ rjf: @demon_os_hooks Process Listing - -internal void -demon_os_proc_iter_begin(DEMON_ProcessIter *iter){ - DIR *dir = opendir("/proc"); - MemoryZeroStruct(iter); - iter->v[0] = IntFromPtr(dir); -} - -internal B32 -demon_os_proc_iter_next(Arena *arena, DEMON_ProcessIter *iter, DEMON_ProcessInfo *info_out){ - // scan for a process id - B32 got_pid = false; - String8 pid_string = {0}; - - DIR *dir = (DIR*)PtrFromInt(iter->v[0]); - if (dir != 0 && iter->v[1] == 0){ - for (;;){ - struct dirent *d = readdir(dir); - if (d == 0){ - break; - } - - // check file name is integer - String8 file_name = str8_cstring((char*)d->d_name); - B32 is_integer = str8_is_integer(file_name, 10); - - // break on integers (which represent processes) - if (is_integer){ - got_pid = true; - pid_string = file_name; - break; - } - } - } - - // mark iterator dead if nothing found - if (!got_pid){ - iter->v[1] = 1; - } - - // if got process id convert pid -> process info - B32 result = false; - if (got_pid){ - // determine the name we will report - pid_t pid = u64_from_str8(pid_string, 10); - String8 name = demon_lnx_executable_path_from_pid(arena, pid); - if (name.size == 0){ - name = str8_lit(""); - } - - // finish conversion - info_out->name = name; - info_out->pid = pid; - result = true; - } - - return(result); -} - -internal void -demon_os_proc_iter_end(DEMON_ProcessIter *iter){ - DIR *dir = (DIR*)PtrFromInt(iter->v[0]); - if (dir != 0){ - closedir(dir); - } - MemoryZeroStruct(iter); -} diff --git a/src/demon/linux/demon_os_linux.h b/src/demon/linux/demon_os_linux.h deleted file mode 100644 index 4e039e35..00000000 --- a/src/demon/linux/demon_os_linux.h +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DEMON_OS_LINUX_H -#define DEMON_OS_LINUX_H - -// TODO(allen): Potential Upgrades: -// -// memory fd upgrade - Right now for each process we hold open a file -// descriptor for the process's memory (/proc/%d/mem) for the entire lifetime -// of the process; it could be opened and closed with some kind of LRU cache -// to put a finite cap on the number of handles the demon holds -// - -//////////////////////////////// -//~ NOTE(allen): Get The Linux Includes - -#include -#include -#include -#include -#include -#include -#include - -//////////////////////////////// -//~ NOTE(allen): Linux Demon Types - -//- entities - -// Demon Linux Entity Extensions -// Process: ext_u64 set to memory file descriptor -// Thread : ext_u64 cast to DEMON_LNX_ThreadExt -// Module : ext_u64 set to U64 (address of name) - -struct DEMON_LNX_ThreadExt{ - B32 expecting_dummy_sigstop; -}; -StaticAssert(sizeof(DEMON_LNX_ThreadExt) <= sizeof(Member(DEMON_Entity, ext_u64)), check_demon_lnx_thread_ext); - -//- helpers - -struct DEMON_LNX_AttachNode{ - DEMON_LNX_AttachNode *next; - pid_t pid; -}; - -struct DEMON_LNX_ProcessAux{ - B32 filled; - U64 phnum; - U64 phent; - U64 phdr; - U64 execfn; -}; - -struct DEMON_LNX_PhdrInfo{ - Rng1U64 range; - U64 dynamic; -}; - -struct DEMON_LNX_ModuleNode{ - DEMON_LNX_ModuleNode *next; - U64 vaddr; - U64 size; - U64 name; - U64 already_known; -}; - -struct DEMON_LNX_EntityNode{ - DEMON_LNX_EntityNode *next; - DEMON_Entity *entity; -}; - -//////////////////////////////// -//~ NOTE(allen): Linux Demon Register Layouts - -// these are defined in but only for one architecture at a time -// (and we can't really trick it into giving us both in any obvious way) -// we define them here so that we have them all "at once" - -struct DEMON_LNX_UserRegsX64{ - U64 r15; - U64 r14; - U64 r13; - U64 r12; - U64 rbp; - U64 rbx; - U64 r11; - U64 r10; - U64 r9; - U64 r8; - U64 rax; - U64 rcx; - U64 rdx; - U64 rsi; - U64 rdi; - U64 orig_rax; - U64 rip; - U64 cs; - U64 rflags; - U64 rsp; - U64 ss; - U64 fsbase; - U64 gsbase; - U64 ds; - U64 es; - U64 fs; - U64 gs; -}; - -struct DEMON_LNX_UserX64{ - DEMON_LNX_UserRegsX64 regs; - S32 u_fpvalid, _pad0; - SYMS_XSaveLegacy i387; - U64 u_tsize, u_dsize, u_ssize, start_code, start_stack; - U64 signal; - S32 reserved, _pad1; - U64 u_ar0, u_fpstate; - U64 magic; - U8 u_comm[32]; - U64 u_debugreg[8]; -}; - -struct DEMON_LNX_UserRegsX86{ - U32 ebx; - U32 ecx; - U32 edx; - U32 esi; - U32 edi; - U32 ebp; - U32 eax; - U32 ds; - U32 es; - U32 fs; - U32 gs; - U32 orig_eax; - U32 eip; - U32 cs; - U32 eflags; - U32 sp; - U32 ss; -}; - -struct DEMON_LNX_UserX86{ - DEMON_LNX_UserRegsX86 regs; - S32 u_fpvalid; - SYMS_FSave i387; - U32 u_tsize, u_dsize, u_ssize, start_code, start_stack; - S32 signal, reserved; - U32 u_ar0, u_fpstate; - U32 magic; - U8 u_comm[32]; - U32 u_debugreg[8]; -}; - -//////////////////////////////// - -enum -{ - DEMON_LNX_PermFlags_Read = (1 << 0), - DEMON_LNX_PermFlags_Write = (1 << 1), - DEMON_LNX_PermFlags_Exec = (1 << 2), - DEMON_LNX_PermFlags_Private = (1 << 3) -}; -typedef int DEMON_LNX_PermFlags; - -enum -{ - DEMON_LNX_MapsEntryType_Null, - DEMON_LNX_MapsEntryType_Path, - DEMON_LNX_MapsEntryType_Heap, - DEMON_LNX_MapsEntryType_Stack, - DEMON_LNX_MapsEntryType_VDSO, -}; -typedef int DEMON_LNX_MapsEntryType; - -struct DEMON_LNX_MapsEntry -{ - U64 address_lo; - U64 address_hi; - DEMON_LNX_PermFlags perms; - U64 offset; - U32 dev_major; - U32 dev_minor; - U64 inode; - String8 pathname; - DEMON_LNX_MapsEntryType type; - pid_t stack_tid; -}; - -//////////////////////////////// -//~ rjf: Helpers - -internal DEMON_LNX_ThreadExt* demon_lnx_thread_ext(DEMON_Entity *entity); - -internal B32 demon_lnx_attach_pid(Arena *arena, pid_t pid, DEMON_LNX_AttachNode **new_node); - -internal String8 demon_lnx_executable_path_from_pid(Arena *arena, pid_t pid); -internal int demon_lnx_open_memory_fd_for_pid(pid_t pid); - -internal Architecture demon_lnx_arch_from_pid(pid_t pid); -internal DEMON_LNX_ProcessAux demon_lnx_aux_from_pid(pid_t pid, Architecture arch); -internal DEMON_LNX_PhdrInfo demon_lnx_phdr_info_from_memory(int memory_fd, B32 is_32bit, - U64 phvaddr, U64 phstride, U64 phcount); -internal DEMON_LNX_ModuleNode* demon_lnx_module_list_from_process(Arena *arena, DEMON_Entity *process); - -internal U64 demon_lnx_read_memory(int memory_fd, void *dst, U64 src, U64 size); -internal B32 demon_lnx_write_memory(int memory_fd, U64 dst, void *src, U64 size); -internal String8 demon_lnx_read_memory_str(Arena *arena, int memory_fd, U64 address); - -internal void demon_lnx_regs_x64_from_usr_regs_x64(SYMS_RegX64 *dst, DEMON_LNX_UserRegsX64 *src); -internal void demon_lnx_usr_regs_x64_from_regs_x64(DEMON_LNX_UserRegsX64 *dst, SYMS_RegX64 *src); - -internal String8 demon_lnx_read_int_string(int fd); -internal B32 demon_lnx_read_expect(int fd, char expect); -internal int demon_lnx_read_whitespace(int fd); -internal String8 demon_lnx_read_string(Arena *arena, int fd); - -internal int demon_lnx_open_maps(pid_t pid); -internal B32 demon_lnx_next_map(Arena *arena, int maps, DEMON_LNX_MapsEntry *entry_out); - -#endif //DEMON_OS_LINUX_H diff --git a/src/demon2/win32/demon2_core_win32.c b/src/demon/win32/demon_core_win32.c similarity index 100% rename from src/demon2/win32/demon2_core_win32.c rename to src/demon/win32/demon_core_win32.c diff --git a/src/demon2/win32/demon2_core_win32.h b/src/demon/win32/demon_core_win32.h similarity index 99% rename from src/demon2/win32/demon2_core_win32.h rename to src/demon/win32/demon_core_win32.h index 15e2e3ac..02dd05d1 100644 --- a/src/demon2/win32/demon2_core_win32.h +++ b/src/demon/win32/demon_core_win32.h @@ -1,8 +1,8 @@ // Copyright (c) 2024 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -#ifndef DEMON2_CORE_WIN32_H -#define DEMON2_CORE_WIN32_H +#ifndef DEMON_CORE_WIN32_H +#define DEMON_CORE_WIN32_H //////////////////////////////// //~ rjf: Windows Includes @@ -284,4 +284,4 @@ internal B32 dmn_w32_thread_write_reg_block(Architecture arch, HANDLE thread, vo //- rjf: remote thread injection internal DWORD dmn_w32_inject_thread(HANDLE process, U64 start_address); -#endif // DEMON2_CORE_WIN32_H +#endif // DEMON_CORE_WIN32_H diff --git a/src/demon/win32/demon_os_win32.c b/src/demon/win32/demon_os_win32.c deleted file mode 100644 index a27f816d..00000000 --- a/src/demon/win32/demon_os_win32.c +++ /dev/null @@ -1,2154 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Globals - -global GetThreadDescriptionFunctionType *demon_w32_GetThreadDescription = 0; - -global B32 demon_w32_resume_needed = 0; -global DWORD demon_w32_resume_pid = 0; -global DWORD demon_w32_resume_tid = 0; - -global B32 demon_w32_exception_not_handled = 0; -global DEMON_Entity* demon_w32_halter_process = 0; -global DWORD demon_w32_halter_thread_id = 0; - -global B32 demon_w32_new_process_pending = 0; - -global Arena *demon_w32_ext_arena = 0 ; -global DEMON_W32_Ext *demon_w32_proc_ext_free = 0; - -global Arena *demon_w32_detach_proc_arena = 0; -global DEMON_EntityNode *demon_w32_first_detached_proc = 0; -global DEMON_EntityNode *demon_w32_last_detached_proc = 0; - -global String8List demon_w32_environment = {0}; - -//////////////////////////////// -//~ rjf: Helpers - -internal U64 -demon_w32_hash_from_string(String8 string) -{ - U64 result = 5381; - for(U64 i = 0; i < string.size; i += 1) - { - result = ((result << 5) + result) + string.str[i]; - } - return result; -} - -internal DEMON_W32_Ext* -demon_w32_ext_alloc(void){ - DEMON_W32_Ext *result = demon_w32_proc_ext_free; - if (result != 0){ - SLLStackPop(demon_w32_proc_ext_free); - } - else{ - result = push_array_no_zero(demon_w32_ext_arena, DEMON_W32_Ext, 1); - } - MemoryZeroStruct(result); - return(result); -} - -internal DEMON_W32_Ext* -demon_w32_ext(DEMON_Entity *entity){ - DEMON_W32_Ext *result = (DEMON_W32_Ext*)entity->ext; - return(result); -} - -internal U64 -demon_w32_read_memory(HANDLE process_handle, void *dst, U64 src_address, U64 size){ - U64 bytes_read = 0; - U8 *ptr = (U8*)dst; - U8 *opl = ptr + size; - U64 cursor = src_address; - for (;ptr < opl;){ - SIZE_T to_read = (SIZE_T)(opl - ptr); - SIZE_T actual_read = 0; - if (!ReadProcessMemory(process_handle, (LPCVOID)cursor, ptr, to_read, &actual_read)){ - bytes_read += actual_read; - break; - } - ptr += actual_read; - cursor += actual_read; - bytes_read += actual_read; - } - return bytes_read; -} - -internal B32 -demon_w32_write_memory(HANDLE process_handle, U64 dst_address, void *src, U64 size){ - B32 result = 1; - U8 *ptr = (U8*)src; - U8 *opl = ptr + size; - U64 cursor = dst_address; - for (;ptr < opl;){ - SIZE_T to_write = (SIZE_T)(opl - ptr); - SIZE_T actual_write = 0; - if (!WriteProcessMemory(process_handle, (LPVOID)cursor, ptr, to_write, &actual_write)){ - result = 0; - break; - } - ptr += actual_write; - cursor += actual_write; - } - return(result); -} - -internal String8 -demon_w32_read_memory_str(Arena *arena, HANDLE process_handle, U64 address){ - // TODO(allen): this could be done better with a demon_w32_read_memory - // that returns a read amount instead of a success/fail. - - // scan piece by piece - Temp scratch = scratch_begin(&arena, 1); - String8List list = {0}; - - U64 max_cap = 256; - U64 cap = max_cap; - U64 read_p = address; - for (;;){ - U8 *block = push_array(scratch.arena, U8, cap); - for (;cap > 0;){ - if (demon_w32_read_memory(process_handle, block, read_p, cap)){ - break; - } - cap /= 2; - } - read_p += cap; - - U64 block_opl = 0; - for (;block_opl < cap; block_opl += 1){ - if (block[block_opl] == 0){ - break; - } - } - - if (block_opl > 0){ - str8_list_push(scratch.arena, &list, str8(block, block_opl)); - } - - if (block_opl < cap || cap == 0){ - break; - } - } - - // assemble results - String8 result = str8_list_join(arena, &list, 0); - scratch_end(scratch); - return(result); -} - -internal String16 -demon_w32_read_memory_str16(Arena *arena, HANDLE process_handle, U64 address){ - // TODO(allen): this could be done better with a demon_w32_read_memory - // that returns a read amount instead of a success/fail. - - // scan piece by piece - Temp scratch = scratch_begin(&arena, 1); - String8List list = {0}; - - U64 max_cap = 256; - U64 cap = max_cap; - U64 read_p = address; - for (;;){ - U8 *block = push_array(scratch.arena, U8, cap); - for (;cap > 1;){ - if (demon_w32_read_memory(process_handle, block, read_p, cap)){ - break; - } - cap /= 2; - } - read_p += cap; - - U16 *block16 = (U16*)block; - (void)block16; - U64 block_opl = 0; - for (;block_opl < cap; block_opl += 2){ - if (*(U16*)(block + block_opl) == 0){ - break; - } - } - - if (block_opl > 0){ - str8_list_push(scratch.arena, &list, str8(block, block_opl)); - } - - if (block_opl < cap || cap == 0){ - break; - } - } - - // assemble results - String8 joined = str8_list_join(arena, &list, 0); - String16 result = {(U16*)joined.str, joined.size/2}; - scratch_end(scratch); - return(result); -} - -internal DEMON_W32_ImageInfo -demon_w32_image_info_from_base(HANDLE process_handle, U64 base){ - // find pe offset in dos header - U32 pe_offset = 0; - - { - U64 dos_magic_off = base; - U16 dos_magic = 0; - demon_w32_read_struct(process_handle, &dos_magic, dos_magic_off); - if (dos_magic == DEMON_DOS_MAGIC){ - U64 pe_offset_off = base + OffsetOf(DEMON_DosHeader, coff_file_offset); - demon_w32_read_struct(process_handle, &pe_offset, pe_offset_off); - } - } - - // get coff header - B32 got_coff_header = 0; - U64 coff_header_off = 0; - DEMON_CoffHeader coff_header = {0}; - - if (pe_offset > 0){ - U64 pe_magic_off = base + pe_offset; - U32 pe_magic = 0; - demon_w32_read_struct(process_handle, &pe_magic, pe_magic_off); - if (pe_magic == DEMON_PE_MAGIC){ - coff_header_off = pe_magic_off + sizeof(pe_magic); - if (demon_w32_read_struct(process_handle, &coff_header, coff_header_off)){ - got_coff_header = 1; - } - } - } - - // get arch and size - DEMON_W32_ImageInfo result = zero_struct; - if (got_coff_header){ - U64 optional_size_off = 0; - - Architecture arch = Architecture_Null; - switch (coff_header.machine){ - case DEMON_CoffMachineType_X86: - { - arch = Architecture_x86; - optional_size_off = OffsetOf(DEMON_PeOptionalHeader32, sizeof_image); - }break; - - case DEMON_CoffMachineType_X64: - { - arch = Architecture_x64; - optional_size_off = OffsetOf(DEMON_PeOptionalHeader32Plus, sizeof_image); - }break; - - default: - {}break; - } - - if (arch != Architecture_Null){ - U64 optional_off = coff_header_off + sizeof(coff_header); - U32 size = 0; - if (demon_w32_read_struct(process_handle, &size, optional_off + optional_size_off)){ - result.arch = arch; - result.size = size; - } - } - } - - return(result); -} - -internal DWORD -demon_w32_inject_thread(DEMON_Entity *process, U64 start_address){ - LPTHREAD_START_ROUTINE start = (LPTHREAD_START_ROUTINE)start_address; - DEMON_Entity *entity = process; - DEMON_W32_Ext *process_ext = demon_w32_ext(entity); - DWORD thread_id = 0; - HANDLE thread = CreateRemoteThread(process_ext->proc.handle, 0, 0, start, 0, 0, &thread_id); - if (thread != 0){ - CloseHandle(thread); - } - return(thread_id); -} - -internal U16 -demon_w32_real_tag_word_from_xsave(XSAVE_FORMAT *fxsave){ - U16 result = 0; - U32 top = (fxsave->StatusWord >> 11) & 7; - for (U32 fpr = 0; fpr < 8; fpr += 1){ - U32 tag = 3; - if (fxsave->TagWord & (1 << fpr)){ - U32 st = (fpr - top)&7; - - REGS_Reg80 *fp = (REGS_Reg80*)&fxsave->FloatRegisters[st*16]; - U16 exponent = fp->sign1_exp15 & bitmask15; - U64 integer_part = fp->int1_frac63 >> 63; - U64 fraction_part = fp->int1_frac63 & bitmask63; - - // tag: 0 - normal; 1 - zero; 2 - special - tag = 2; - if (exponent == 0){ - if (integer_part == 0 && fraction_part == 0){ - tag = 1; - } - } - else if (exponent != bitmask15 && integer_part != 0){ - tag = 0; - } - } - result |= tag << (2 * fpr); - } - return(result); -} - -internal U16 -demon_w32_xsave_tag_word_from_real_tag_word(U16 ftw){ - U16 compact = 0; - for (U32 fpr = 0; fpr < 8; fpr++){ - U32 tag = (ftw >> (fpr * 2)) & 3; - if (tag != 3){ - compact |= (1 << fpr); - } - } - return(compact); -} - -internal DWORD -demon_w32_win32_from_memory_protect_flags(DEMON_MemoryProtectFlags flags){ - DWORD result = 0; - switch (flags){ - default: - case DEMON_MemoryProtectFlag_Read|DEMON_MemoryProtectFlag_Write: - case DEMON_MemoryProtectFlag_Write: - { - result = PAGE_READWRITE; - }break; - case DEMON_MemoryProtectFlag_Read|DEMON_MemoryProtectFlag_Write|DEMON_MemoryProtectFlag_Execute: - { - result = PAGE_EXECUTE_READWRITE; - }break; - case DEMON_MemoryProtectFlag_Execute: - { - result = PAGE_EXECUTE; - }break; - case DEMON_MemoryProtectFlag_Read: - { - result = PAGE_READONLY; - }break; - } - return(result); -} - -//////////////////////////////// -//~ rjf: Experiments - -internal void -demon_w32_peak_at_tls(DEMON_Handle thread_handle){ - DEMON_Entity *thread = demon_ent_ptr_from_handle(thread_handle); - if (thread != 0 && thread->kind == DEMON_EntityKind_Thread){ - - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - U64 tlb = thread_ext->thread.thread_local_base; - - U8 buffer[0x1000]; - demon_os_read_memory(thread->parent, buffer, tlb, 0x1000); - - int x = 0; - (void)x; - } -} - -//////////////////////////////// -//~ rjf: @demon_os_hooks Main Layer Initialization - -internal void -demon_os_init(void){ - demon_w32_ext_arena = arena_alloc(); - demon_w32_detach_proc_arena = arena_alloc(); - - // rjf: load Windows 10+ GetThreadDescription API - { - demon_w32_GetThreadDescription = (GetThreadDescriptionFunctionType *)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "GetThreadDescription"); - } - - // rjf: setup environment variables - { - CHAR *this_proc_env = GetEnvironmentStrings(); - U64 start_idx = 0; - for(U64 idx = 0;; idx += 1) - { - if(this_proc_env[idx] == 0) - { - if(start_idx == idx) - { - break; - } - else - { - String8 string = str8((U8 *)this_proc_env + start_idx, idx - start_idx); - str8_list_push(demon_w32_ext_arena, &demon_w32_environment, string); - start_idx = idx+1; - } - } - } - } -} - -//////////////////////////////// -//~ rjf: @demon_os_hooks Running/Halting - -internal DEMON_EventList -demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){ - DEMON_EventList result = {0}; - - if (demon_ent_root == 0){ - DEMON_Event *event = demon_push_event(arena, &result, DEMON_EventKind_Error); - event->error_kind = DEMON_ErrorKind_NotInitialized; - } - else if (demon_ent_root->first == 0 && !demon_w32_new_process_pending){ - DEMON_Event *event = demon_push_event(arena, &result, DEMON_EventKind_Error); - event->error_kind = DEMON_ErrorKind_NotAttached; - } - else if(demon_w32_first_detached_proc != 0) - { - for(DEMON_EntityNode *n = demon_w32_first_detached_proc; n != 0; n = n->next) - { - DEMON_Entity *process = n->entity; - - // rjf: push exit thread events - for(DEMON_Entity *child = process->first; child != 0; child = child->next) - { - if(child->kind == DEMON_EntityKind_Thread) - { - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_ExitThread); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(child); - } - } - - // rjf: push unload module events - for(DEMON_Entity *child = process->first; child != 0; child = child->next) - { - if(child->kind == DEMON_EntityKind_Module) - { - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_UnloadModule); - e->process = demon_ent_handle_from_ptr(process); - e->module = demon_ent_handle_from_ptr(child); - e->string = demon_os_full_path_from_module(arena, child); - } - } - - // rjf: push exit process event - { - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_ExitProcess); - e->process = demon_ent_handle_from_ptr(process); - e->code = 0; - } - - // rjf: free process - demon_ent_release_root_and_children(process); - } - demon_w32_first_detached_proc = 0; - demon_w32_last_detached_proc = 0; - arena_clear(demon_w32_detach_proc_arena); - } - else{ - Temp scratch = scratch_begin(&arena, 1); - - // get the single step thread (if any) - DEMON_Entity *single_step_thread = ctrls->single_step_thread; - - // TODO(allen): dedup per architecture? - // set single step bit - if (single_step_thread != 0){ - // TODO(allen): possibly buggy - switch(single_step_thread->arch) - { - default:{NotImplemented;}break; - case Architecture_x86: - { - REGS_RegBlockX86 regs = {0}; - demon_os_read_regs_x86(single_step_thread, ®s); - regs.eflags.u32 |= 0x100; - demon_os_write_regs_x86(single_step_thread, ®s); - }break; - - case Architecture_x64: - { - REGS_RegBlockX64 regs = {0}; - demon_os_read_regs_x64(single_step_thread, ®s); - regs.rflags.u64 |= 0x100; - demon_os_write_regs_x64(single_step_thread, ®s); - }break; - } - } - - // TODO(allen): per-Architecture implementation of traps - // set traps - U8 *trap_swap_bytes = push_array_no_zero(scratch.arena, U8, ctrls->trap_count); - - { - DEMON_OS_Trap *trap = ctrls->traps; - for (U64 i = 0; i < ctrls->trap_count; i += 1, trap += 1){ - if (demon_os_read_memory(trap->process, trap_swap_bytes + i, trap->address, 1)){ - U8 int3 = 0xCC; - demon_os_write_memory(trap->process, trap->address, &int3, 1); - } - else{ - trap_swap_bytes[i] = 0xCC; - } - } - } - - // determine how to resume from the last event - DWORD resume_code = DBG_CONTINUE; - if (demon_w32_exception_not_handled){ - if (!ctrls->ignore_previous_exception){ - resume_code = DBG_EXCEPTION_NOT_HANDLED; - } - } - demon_w32_exception_not_handled = 0; - - // list threads that run this time - DEMON_W32_EntityNode *first_run_thread = 0; - DEMON_W32_EntityNode *last_run_thread = 0; - - for(DEMON_Entity *process = demon_ent_root->first; - process != 0; - process = process->next){ - if (process->kind == DEMON_EntityKind_Process){ - - // determine if this process is frozen - B32 process_is_frozen = 0; - if (ctrls->run_entities_are_processes){ - for (U64 i = 0; i < ctrls->run_entity_count; i += 1){ - if (ctrls->run_entities[i] == process){ - process_is_frozen = 1; - break; - } - } - } - - for (DEMON_Entity *thread = process->first; - thread != 0; - thread = thread->next){ - if (thread->kind == DEMON_EntityKind_Thread){ - // determine if this thread is frozen - B32 is_frozen = 0; - - if (ctrls->single_step_thread != 0 && - ctrls->single_step_thread != thread){ - is_frozen = 1; - } - else{ - - if (ctrls->run_entities_are_processes){ - is_frozen = process_is_frozen; - } - else{ - for (U64 i = 0; i < ctrls->run_entity_count; i += 1){ - if (ctrls->run_entities[i] == thread){ - is_frozen = 1; - break; - } - } - } - - if (ctrls->run_entities_are_unfrozen){ - is_frozen = !is_frozen; - } - } - - // rjf: disregard all other rules if this is the halter thread - if(demon_w32_halter_thread_id == thread->id) - { - is_frozen = 0; - } - - // add this thread to the list - if (!is_frozen){ - DEMON_W32_EntityNode *node = push_array_no_zero(scratch.arena, DEMON_W32_EntityNode, 1); - SLLQueuePush(first_run_thread, last_run_thread, node); - node->entity = thread; - } - } - } - } - } - - // prep suspension state of threads that will be allowed to run - for(DEMON_W32_EntityNode *node = first_run_thread; - node != 0; - node = node->next) - { - DEMON_Entity *thread = node->entity; - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - DWORD resume_result = ResumeThread(thread_ext->thread.handle); - if(resume_result == max_U32) - { - // TODO(allen): Error. Unknown cause (do GetLastError, FromatMessage) - } - else - { - DWORD desired_counter = 0; - DWORD current_counter = resume_result - 1; - if(current_counter != desired_counter) - { - // NOTE(rjf): Warning. The user has manually suspended this thread, - // so even though from Demon's perspective it thinks this thread - // should run, it will not, because the user has manually called - // SuspendThread or used CREATE_SUSPENDED or whatever. - } - } - } - - // rjf: if run threads are marked as having reported an explicit trap - // on their last run, shift their RIPs past that trap instruction, so - // that they may continue - for(DEMON_W32_EntityNode *node = first_run_thread; - node != 0; - node = node->next) - { - DEMON_Entity *thread = node->entity; - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - if(thread_ext->thread.last_run_reported_trap) - { - Temp temp = temp_begin(scratch.arena); - U64 regs_block_size = regs_block_size_from_architecture(thread->arch); - void *regs_block = push_array(temp.arena, U8, regs_block_size); - B32 good = demon_os_read_regs(thread, regs_block); - U64 pre_rip = regs_rip_from_arch_block(thread->arch, regs_block); - if(good && pre_rip == thread_ext->thread.last_run_reported_trap_pre_rip) - { - regs_arch_block_write_rip(thread->arch, regs_block, thread_ext->thread.last_run_reported_trap_post_rip); - demon_os_write_regs(thread, regs_block); - } - temp_end(temp); - thread_ext->thread.last_run_reported_trap = 0; - thread_ext->thread.last_run_reported_trap_post_rip = 0; - } - } - - // send last saved continue signal - B32 good_state = 1; - if (demon_w32_resume_needed != 0){ - if (!ContinueDebugEvent(demon_w32_resume_pid, demon_w32_resume_tid, resume_code)){ - good_state = 0; - } - - demon_w32_resume_needed = 0; - demon_w32_resume_pid = 0; - demon_w32_resume_tid = 0; - } - - // wait for a new event from targets - DEBUG_EVENT evt = {0}; - B32 got_new_event = 0; - if (good_state){ - got_new_event = WaitForDebugEvent(&evt, INFINITE); - } - if (got_new_event){ - demon_w32_resume_needed = 1; - demon_w32_resume_pid = evt.dwProcessId; - demon_w32_resume_tid = evt.dwThreadId; - } - - // increment demon time - demon_time += 1; - - // reset all threads to paused state - if (got_new_event){ - for (DEMON_W32_EntityNode *node = first_run_thread; - node != 0; - node = node->next){ - DEMON_Entity *thread = node->entity; - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - DWORD suspend_result = SuspendThread(thread_ext->thread.handle); - if (suspend_result == max_U32){ - // TODO(allen): Error. Unknown cause (do GetLastError, FromatMessage) - // NOTE(allen): This can happen when the event is EXIT_THREAD_DEBUG_EVENT - // or EXIT_PROCESS_DEBUG_EVENT. After such an event SuspendThread - // gives error code 5 (access denied). This has no adverse effects, but if - // want to start reporting errors we should take care to avoid calling - // SuspendThread in that case. - } - else{ - DWORD desired_counter = 1; - DWORD current_counter = suspend_result + 1; - if (current_counter != desired_counter){ - // NOTE(rjf): Warning. We've suspended to something higher than 1. - // In this case, it means the user probably created the thread in - // a suspended state, or they called SuspendThread. - } - } - } - } - - // rjf: process event - if (got_new_event){ - got_new_event = 1; - - switch (evt.dwDebugEventCode){ - case CREATE_PROCESS_DEBUG_EVENT: - { - // pull outs - HANDLE process_handle = evt.u.CreateProcessInfo.hProcess; - HANDLE thread_handle = evt.u.CreateProcessInfo.hThread; - U64 module_base = (U64)evt.u.CreateProcessInfo.lpBaseOfImage; - DEMON_W32_ImageInfo image_info = demon_w32_image_info_from_base(process_handle, module_base); - - // init process entity - DEMON_Entity *process = demon_ent_new(demon_ent_root, DEMON_EntityKind_Process, evt.dwProcessId); - demon_proc_count += 1; - process->arch = image_info.arch; - DEMON_W32_Ext *process_ext = demon_w32_ext_alloc(); - process->ext = process_ext; - process_ext->proc.handle = process_handle; - - demon_w32_new_process_pending = 0; - - // init new associated entities - DEMON_Entity *thread = demon_ent_new(process, DEMON_EntityKind_Thread, evt.dwThreadId); - demon_thread_count += 1; - DEMON_W32_Ext *thread_ext = demon_w32_ext_alloc(); - thread->ext = thread_ext; - thread_ext->thread.handle = thread_handle; - thread_ext->thread.thread_local_base = (U64)evt.u.CreateProcessInfo.lpThreadLocalBase; - SuspendThread(thread_ext->thread.handle); - - DEMON_Entity *module = demon_ent_new(process, DEMON_EntityKind_Module, module_base); - demon_module_count += 1; - module->addr_range_dim = image_info.size; - DEMON_W32_Ext *module_ext = demon_w32_ext_alloc(); - module->ext = module_ext; - module_ext->module.handle = evt.u.CreateProcessInfo.hFile; - module_ext->module.address_of_name_pointer = (U64)evt.u.CreateProcessInfo.lpImageName; - module_ext->module.is_main = 1; - module_ext->module.name_is_unicode = (evt.u.CreateProcessInfo.fUnicode != 0); - - // injection memory - { - U8 injection_code[DEMON_W32_INJECTED_CODE_SIZE]; - injection_code[0] = 0xC3; - for (U64 i = 1; i < DEMON_W32_INJECTED_CODE_SIZE; i += 1){ - injection_code[i] = 0xCC; - } - - U64 injection_size = DEMON_W32_INJECTED_CODE_SIZE + sizeof(DEMON_W32_InjectedBreak); - U64 injection_address = (U64)VirtualAllocEx(process_ext->proc.handle, 0, injection_size, - MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE); - demon_os_write_memory(process, injection_address, injection_code, sizeof(injection_code)); - process_ext->proc.injection_address = injection_address; - } - - // generate events - { - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_CreateProcess); - e->process = demon_ent_handle_from_ptr(process); - e->code = evt.dwProcessId; - } - - { - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_CreateThread); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - e->code = evt.dwThreadId; - } - - { - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_LoadModule); - e->process = demon_ent_handle_from_ptr(process); - e->module = demon_ent_handle_from_ptr(module); - e->address = module_base; - e->size = image_info.size; - e->string = demon_os_full_path_from_module(arena, module); - } - }break; - - case EXIT_PROCESS_DEBUG_EVENT: - { - // get process entity - DEMON_Entity *process = demon_ent_map_entity_from_id(DEMON_EntityKind_Process, evt.dwProcessId); - - if (process != 0){ - // update halter process pointer - if (process == demon_w32_halter_process){ - demon_w32_halter_process = 0; - } - - // generate events for threads & modules - for (DEMON_Entity *entity = process->first; - entity != 0; - entity = entity->next){ - if (entity->kind == DEMON_EntityKind_Thread){ - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_ExitThread); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(entity); - } - else{ - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_UnloadModule); - e->process = demon_ent_handle_from_ptr(process); - e->module = demon_ent_handle_from_ptr(entity); - e->string = demon_os_full_path_from_module(arena, entity); - } - } - - // generate event for self - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_ExitProcess); - e->process = demon_ent_handle_from_ptr(process); - e->code = evt.u.ExitProcess.dwExitCode; - - // free entity - demon_ent_release_root_and_children(process); - - // rjf: detach - { - DWORD pid = evt.dwProcessId; - DebugActiveProcessStop(pid); - } - } - }break; - - case CREATE_THREAD_DEBUG_EVENT: - { - // get process entity - DEMON_Entity *process = demon_ent_map_entity_from_id(DEMON_EntityKind_Process, evt.dwProcessId); - - if (process != 0){ - // init new entity - DEMON_Entity *thread = demon_ent_new(process, DEMON_EntityKind_Thread, evt.dwThreadId); - demon_thread_count += 1; - DEMON_W32_Ext *thread_ext = demon_w32_ext_alloc(); - thread->ext = thread_ext; - thread_ext->thread.handle = evt.u.CreateThread.hThread; - thread_ext->thread.thread_local_base = (U64)evt.u.CreateThread.lpThreadLocalBase; - DWORD sus_result = SuspendThread(thread_ext->thread.handle); - (void)sus_result; - - // rjf: unpack thread name - String8 thread_name = {0}; - if(demon_w32_GetThreadDescription != 0) - { - WCHAR *thread_name_w = 0; - HRESULT hr = demon_w32_GetThreadDescription(evt.u.CreateThread.hThread, &thread_name_w); - if(SUCCEEDED(hr)) - { - thread_name = str8_from_16(arena, str16_cstring((U16 *)thread_name_w)); - LocalFree(thread_name_w); - } - } - - // rjf: determine if this is the halter thread - B32 is_halter = (evt.dwThreadId == demon_w32_halter_thread_id); - - // generate event for non-halters - if (is_halter == 0){ - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_CreateThread); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - e->code = evt.dwThreadId; - e->string = thread_name; - } - } - }break; - - case EXIT_THREAD_DEBUG_EVENT: - { - // get thread and process entity - DEMON_Entity *thread = demon_ent_map_entity_from_id(DEMON_EntityKind_Thread, evt.dwThreadId); - - if (thread != 0){ - DEMON_Entity *process = thread->parent; - - // rjf: determine if this is the halter thread - B32 is_halter = (evt.dwThreadId == demon_w32_halter_thread_id); - - // rjf: generate a halt event if we're the halter - if (is_halter){ - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_Halt); - demon_w32_halter_process = 0; - (void)e; - } - - // rjf: generate normal thread exit event on non-halter-thread exist - if (!is_halter){ - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_ExitThread); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - e->code = evt.u.ExitThread.dwExitCode; - } - - // free entity - demon_ent_release_root_and_children(thread); - } - }break; - - case LOAD_DLL_DEBUG_EVENT: - { - // get process entity - DEMON_Entity *process = demon_ent_map_entity_from_id(DEMON_EntityKind_Process, evt.dwProcessId); - - if (process != 0){ - // get image info - DEMON_W32_Ext *process_ext = demon_w32_ext(process); - HANDLE process_handle = process_ext->proc.handle; - U64 module_base = (U64)evt.u.LoadDll.lpBaseOfDll; - DEMON_W32_ImageInfo image_info = demon_w32_image_info_from_base(process_handle, module_base); - - // init new entity - DEMON_Entity *module = demon_ent_new(process, DEMON_EntityKind_Module, module_base); - module->addr_range_dim = image_info.size; - demon_module_count += 1; - DEMON_W32_Ext *module_ext = demon_w32_ext_alloc(); - module->ext = module_ext; - module_ext->module.handle = evt.u.LoadDll.hFile; - module_ext->module.address_of_name_pointer = (U64)evt.u.LoadDll.lpImageName; - module_ext->module.is_main = 0; - module_ext->module.name_is_unicode = (evt.u.LoadDll.fUnicode != 0); - - // generate events - { - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_LoadModule); - e->process = demon_ent_handle_from_ptr(process); - e->module = demon_ent_handle_from_ptr(module); - e->address = module_base; - e->size = image_info.size; - e->string = demon_os_full_path_from_module(arena, module); - } - } - }break; - - case UNLOAD_DLL_DEBUG_EVENT: - { - // get module and process entity - U64 module_base = (U64)evt.u.UnloadDll.lpBaseOfDll; - DEMON_Entity *module = demon_ent_map_entity_from_id(DEMON_EntityKind_Module, module_base); - - if (module != 0){ - DEMON_Entity *process = module->parent; - - // generate events - { - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_UnloadModule); - e->process = demon_ent_handle_from_ptr(process); - e->module = demon_ent_handle_from_ptr(module); - e->string = demon_os_full_path_from_module(arena, module); - } - - // free entity - demon_ent_release_root_and_children(module); - } - }break; - - case EXCEPTION_DEBUG_EVENT: - { - // NOTE(rjf): Notes on multithreaded breakpoint events - // (2021/11/1): - // - // When many threads are simultaneously running, multiple threads - // may hit a trap "at the same time". When this happens there will be - // multiple events in an internal queue that we cannot see. If there - // is another event in the queue we will not see it until we call - // ContinueDebugEvent again, in a subsequent call to demon_os_run. - // - // When we get a trap event, the instruction pointer stored - // in the event will have the address of the int 3 instruction that - // was hit. Our RIP register, however, will be one byte past that. - // So, to get the behavior we want, we need to set the RIP register - // back to the address of the int 3. - // - // To deal with the fact that we may get breakpoint events later that - // were actually from this run what we do is: - // - // #1. If we get a trap event, and it corresponds to a user submitted - // trap, then we treat it is a breakpoint event. - // #2. If we get a trap event, and it does NOT correspond to a user - // trap in this call: - // #A. If the actual unmodified instruction byte is NOT an int 3, - // then this is a queued event from a previous run that is no - // longer applicable and we skip it. - // #B. If the actual unmodified instruction is an int 3, then this - // becomes a trap event and we do not reset RIP. - - // get thread and process entity - DEMON_Entity *thread = demon_ent_map_entity_from_id(DEMON_EntityKind_Thread, evt.dwThreadId); - - if (thread != 0){ - DEMON_Entity *process = thread->parent; - Assert(process->kind == DEMON_EntityKind_Process); - - DEMON_W32_Ext *process_ext = demon_w32_ext(process); - - // TODO(allen): the exception record has two forms, one for 32 bit and one for 64 bit mode. - - EXCEPTION_DEBUG_INFO *edi = &evt.u.Exception; - EXCEPTION_RECORD *exception = &edi->ExceptionRecord; - U64 instruction_pointer = (U64)exception->ExceptionAddress; - - // check if first BP - B32 first_bp = 0; - if (exception->ExceptionCode == DEMON_W32_EXCEPTION_BREAKPOINT && - !process_ext->proc.did_first_bp){ - process_ext->proc.did_first_bp = 1; - first_bp = 1; - } - - // rjf: check if trap - B32 is_trap = (!first_bp && - (exception->ExceptionCode == DEMON_W32_EXCEPTION_BREAKPOINT || - exception->ExceptionCode == DEMON_W32_EXCEPTION_STACK_BUFFER_OVERRUN)); - - // rjf: check if this trap is currently registered - B32 hit_user_trap = 0; - if (is_trap){ - DEMON_OS_Trap *trap = ctrls->traps; - for (U64 i = 0; i < ctrls->trap_count; i += 1, trap += 1){ - if (trap->process == process && trap->address == instruction_pointer){ - hit_user_trap = 1; - break; - } - } - } - - // rjf: check if trap is explicit in the actual code memory - B32 hit_explicit_trap = 0; - if (is_trap && !hit_user_trap){ - U8 instruction_byte = 0; - if (demon_os_read_memory(process, &instruction_byte, instruction_pointer, 1)){ - // TODO(rjf): x86/x64 specific check - // TODO(rjf): do we need to check to make sure the instruction - // pointer has not changed? - hit_explicit_trap = (instruction_byte == 0xCC || - instruction_byte == 0xCD); - } - } - - // rjf: determine whether to roll back instruction pointer - B32 rollback = (is_trap); - - // rjf: roll back - U64 post_trap_rip = 0; - if(rollback) - { - Temp temp = temp_begin(scratch.arena); - U64 regs_block_size = regs_block_size_from_architecture(thread->arch); - void *regs_block = push_array(scratch.arena, U8, regs_block_size); - if(demon_os_read_regs(thread, regs_block)) - { - post_trap_rip = regs_rip_from_arch_block(thread->arch, regs_block); - regs_arch_block_write_rip(thread->arch, regs_block, instruction_pointer); - demon_os_write_regs(thread, regs_block); - } - temp_end(temp); - } - - // allen: if this is not a user trap or explicit trap it's a previous trap - B32 hit_previous_trap = (is_trap && !hit_user_trap && !hit_explicit_trap); - - // determine whether to skip this event - B32 skip_event = (hit_previous_trap); - - // emit event - if(!skip_event) - { - // rjf: fill top-level info - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_Exception); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - e->code = exception->ExceptionCode; - e->flags = exception->ExceptionFlags; - e->instruction_pointer = (U64)exception->ExceptionAddress; - - // rjf: explicit trap -> mark this thread as having reported this trap - if(hit_explicit_trap) - { - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - thread_ext->thread.last_run_reported_trap = 1; - thread_ext->thread.last_run_reported_trap_pre_rip = instruction_pointer; - thread_ext->thread.last_run_reported_trap_post_rip = post_trap_rip; - } - - // rjf: fill by exception code - switch (exception->ExceptionCode){ - case DEMON_W32_EXCEPTION_BREAKPOINT: - { - // rjf: determine event kind - DEMON_EventKind report_event_kind = DEMON_EventKind_Trap; - if (first_bp){ - report_event_kind = DEMON_EventKind_HandshakeComplete; - } - else if (hit_user_trap){ - report_event_kind = DEMON_EventKind_Breakpoint; - } - - // set event kind - e->kind = report_event_kind; - }break; - - case DEMON_W32_EXCEPTION_STACK_BUFFER_OVERRUN: - { - e->kind = DEMON_EventKind_Trap; - }break; - - case DEMON_W32_EXCEPTION_SINGLE_STEP: - { - e->kind = DEMON_EventKind_SingleStep; - }break; - - case DEMON_W32_EXCEPTION_THROW: - { - U64 exception_sp = 0; - U64 exception_ip = 0; - if (exception->NumberParameters >= 3){ - exception_sp = (U64)exception->ExceptionInformation[1]; - exception_ip = (U64)exception->ExceptionInformation[2]; - } - e->stack_pointer = exception_sp; - e->exception_kind = DEMON_ExceptionKind_CppThrow; - e->exception_repeated = (edi->dwFirstChance == 0); - demon_w32_exception_not_handled = (edi->dwFirstChance != 0); - }break; - - case DEMON_W32_EXCEPTION_ACCESS_VIOLATION: - case DEMON_W32_EXCEPTION_IN_PAGE_ERROR: - { - U64 exception_address = 0; - DEMON_ExceptionKind exception_kind = DEMON_ExceptionKind_Null; - if (exception->NumberParameters >= 2){ - switch (exception->ExceptionInformation[0]){ - case 0: exception_kind = DEMON_ExceptionKind_MemoryRead; break; - case 1: exception_kind = DEMON_ExceptionKind_MemoryWrite; break; - case 8: exception_kind = DEMON_ExceptionKind_MemoryExecute; break; - } - exception_address = exception->ExceptionInformation[1]; - } - - e->address = exception_address; - e->exception_kind = exception_kind; - e->exception_repeated = (edi->dwFirstChance == 0); - demon_w32_exception_not_handled = (edi->dwFirstChance != 0); - }break; - - case DEMON_W32_EXCEPTION_SET_THREAD_NAME: - { - if(exception->NumberParameters >= 2) - { - U64 thread_name_address = exception->ExceptionInformation[1]; - DEMON_Entity *process = demon_ent_map_entity_from_id(DEMON_EntityKind_Process, evt.dwProcessId); - String8List thread_name_strings = {0}; - { - U64 read_addr = thread_name_address; - U64 total_string_size = 0; - for(;total_string_size < KB(4);) - { - U8 *buffer = push_array_no_zero(scratch.arena, U8, 256); - B32 good_read = demon_os_read_memory(process, buffer, read_addr, 256); - if(good_read) - { - U64 size = 256; - for(U64 idx = 0; idx < 256; idx += 1) - { - if(buffer[idx] == 0) - { - size = idx; - break; - } - } - String8 string_part = str8(buffer, size); - str8_list_push(scratch.arena, &thread_name_strings, string_part); - total_string_size += size; - read_addr += size; - if(size < 256) - { - break; - } - } - } - } - e->kind = DEMON_EventKind_SetThreadName; - e->string = str8_list_join(arena, &thread_name_strings, 0); - if(exception->NumberParameters > 2) - { - e->code = exception->ExceptionInformation[2]; - } - } - }break; - - default: - { - demon_w32_exception_not_handled = (edi->dwFirstChance != 0); - }break; - } - } - } - }break; - - case OUTPUT_DEBUG_STRING_EVENT: - { - // get process entity - DEMON_Entity *process = demon_ent_map_entity_from_id(DEMON_EntityKind_Process, evt.dwProcessId); - DEMON_Entity *thread = demon_ent_map_entity_from_id(DEMON_EntityKind_Thread, evt.dwThreadId); - - U64 string_address = (U64)evt.u.DebugString.lpDebugStringData; - U64 string_size = (U64)evt.u.DebugString.nDebugStringLength; - - // TODO(allen): is the string in UTF-8 or UTF-16? - - U8 *buffer = push_array_no_zero(arena, U8, string_size + 1); - demon_os_read_memory(process, buffer, string_address, string_size); - buffer[string_size] = 0; - - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_DebugString); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - e->string = str8(buffer, string_size); - if(string_size != 0 && buffer[string_size-1] == 0) - { - e->string.size -= 1; - } - }break; - - case RIP_EVENT: - { - DEMON_Entity *process = demon_ent_map_entity_from_id(DEMON_EntityKind_Process, evt.dwProcessId); - DEMON_Entity *thread = demon_ent_map_entity_from_id(DEMON_EntityKind_Thread, evt.dwThreadId); - - // TODO(allen): this is not the right way to handle this event - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_Exception); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - e->flags = 0; - e->address = 0; - }break; - - default: - { - got_new_event = 0; - }break; - } - } - - //- rjf: gather new thread-names - if(demon_w32_GetThreadDescription != 0) - { - for(DEMON_Entity *process = demon_ent_root->first; - process != 0; - process = process->next) - { - if(process->kind != DEMON_EntityKind_Process) { continue; } - for(DEMON_Entity *thread = process->first; - thread != 0; - thread = thread->next) - { - if(thread->kind != DEMON_EntityKind_Thread) { continue; } - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - if(thread_ext->thread.last_name_hash == 0 || - thread_ext->thread.name_gather_time_us+1000000 <= os_now_microseconds()) - { - String8 name = {0}; - { - WCHAR *thread_name_w = 0; - HRESULT hr = demon_w32_GetThreadDescription(thread_ext->thread.handle, &thread_name_w); - if(SUCCEEDED(hr)) - { - name = str8_from_16(scratch.arena, str16_cstring((U16 *)thread_name_w)); - LocalFree(thread_name_w); - } - } - U64 name_hash = demon_w32_hash_from_string(name); - if(name.size != 0 && name_hash != thread_ext->thread.last_name_hash) - { - DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_SetThreadName); - e->process = demon_ent_handle_from_ptr(process); - e->thread = demon_ent_handle_from_ptr(thread); - e->string = push_str8_copy(arena, name); - } - thread_ext->thread.name_gather_time_us = os_now_microseconds(); - thread_ext->thread.last_name_hash = name_hash; - } - } - } - } - - // TODO(allen): handle errors? if (!got_new_event) ? if (!good_state) ? - - // TODO(allen): per-Architecture - // unset traps - { - DEMON_OS_Trap *trap = ctrls->traps; - for (U64 i = 0; i < ctrls->trap_count; i += 1, trap += 1){ - U8 og_byte = trap_swap_bytes[i]; - if (og_byte != 0xCC){ - demon_os_write_memory(trap->process, trap->address, &og_byte, 1); - } - } - } - - // TODO(allen): per-Architecture - // unset single step bit - // the single step bit is automatically unset whenever we single step - // but if *something else* happened, it will still be there ready to - // confound us later; so here we're just being sure it's taken out. - if (single_step_thread != 0){ - // TODO(allen): possibly buggy - switch(single_step_thread->arch) - { - default:{NotImplemented;}break; - case Architecture_x86: - { - REGS_RegBlockX86 regs = {0}; - demon_os_read_regs_x86(single_step_thread, ®s); - regs.eflags.u32 &= ~0x100; - demon_os_write_regs_x86(single_step_thread, ®s); - }break; - - case Architecture_x64: - { - REGS_RegBlockX64 regs = {0}; - demon_os_read_regs_x64(single_step_thread, ®s); - regs.rflags.u64 &= ~0x100; - demon_os_write_regs_x64(single_step_thread, ®s); - }break; - } - } - - scratch_end(scratch); - } - - return(result); -} - -internal void -demon_os_halt(U64 code, U64 user_data){ - if (demon_ent_root != 0 && demon_w32_halter_process == 0){ - DEMON_Entity *process = demon_ent_root->first; - if (process != 0){ - DEMON_W32_Ext *process_ext = demon_w32_ext(process); - - demon_w32_halter_process = process; - DEMON_W32_InjectedBreak injection = {code, user_data}; - U64 data_injection_address = process_ext->proc.injection_address + DEMON_W32_INJECTED_CODE_SIZE; - demon_os_write_struct(process, data_injection_address, &injection); - demon_w32_halter_thread_id = demon_w32_inject_thread(process, process_ext->proc.injection_address); - } - } -} - -//////////////////////////////// -//~ rjf: @demon_os_hooks Target Process Launching/Attaching/Killing/Detaching/Halting - -internal U32 -demon_os_launch_process(OS_LaunchOptions *options){ - Temp scratch = scratch_begin(0, 0); - - // TODO(allen): maybe a good command line escaper/parser function pair? - String8 cmd = {0}; - if(options->cmd_line.first != 0) - { - String8List args = {0}; - String8 exe_path = options->cmd_line.first->string; - str8_list_pushf(scratch.arena, &args, "\"%S\"", exe_path); - for(String8Node *n = options->cmd_line.first->next; n != 0; n = n->next) - { - str8_list_push(scratch.arena, &args, n->string); - } - StringJoin join_params = {0}; - join_params.sep = str8_lit(" "); - cmd = str8_list_join(scratch.arena, &args, &join_params); - } - - StringJoin join_params2 = {0}; - join_params2.sep = str8_lit("\0"); - join_params2.post = str8_lit("\0"); - String8List all_opts = options->env; - if(options->inherit_env != 0) - { - MemoryZeroStruct(&all_opts); - for(String8Node *n = options->env.first; n != 0; n = n->next) - { - str8_list_push(scratch.arena, &all_opts, n->string); - } - for(String8Node *n = demon_w32_environment.first; n != 0; n = n->next) - { - str8_list_push(scratch.arena, &all_opts, n->string); - } - } - String8 env = str8_list_join(scratch.arena, &all_opts, &join_params2); - - String16 cmd16 = str16_from_8(scratch.arena, cmd); - String16 dir16 = str16_from_8(scratch.arena, options->path); - String16 env16 = str16_from_8(scratch.arena, env); - - U32 result = 0; - - //- rjf: launch - DWORD access_flags = CREATE_UNICODE_ENVIRONMENT|DEBUG_PROCESS; - STARTUPINFOW startup_info = {sizeof(startup_info)}; - PROCESS_INFORMATION process_info = {0}; - AllocConsole(); - if (CreateProcessW(0, (WCHAR*)cmd16.str, 0, 0, 1, access_flags, (WCHAR*)env16.str, (WCHAR*)dir16.str, - &startup_info, &process_info)){ - // check if we are 32-bit app, and just close it immediately - BOOL is_wow = 0; - IsWow64Process(process_info.hProcess, &is_wow); - if ( is_wow ){ - MessageBox(0,"Sorry, The RAD Debugger only debugs 64-bit applications currently.","Process error",MB_OK|MB_ICONSTOP); - DebugActiveProcessStop(process_info.dwProcessId); - TerminateProcess(process_info.hProcess,0xffffffff); - CloseHandle(process_info.hProcess); - CloseHandle(process_info.hThread); - } - else{ - CloseHandle(process_info.hProcess); - CloseHandle(process_info.hThread); - result = process_info.dwProcessId; - demon_w32_new_process_pending = 1; - } - } - else{ - MessageBox(0,"Error starting process.","Process error",MB_OK|MB_ICONSTOP); - } - FreeConsole(); - - //- rjf: eliminate all handles which have stuck around from the AllocConsole - { - SetStdHandle(STD_INPUT_HANDLE, 0); - SetStdHandle(STD_OUTPUT_HANDLE, 0); - SetStdHandle(STD_ERROR_HANDLE, 0); - } - - scratch_end(scratch); - return(result); -} - -internal B32 -demon_os_attach_process(U32 pid){ - B32 result = 0; - if (DebugActiveProcess((DWORD)pid)){ - result = 1; - demon_w32_new_process_pending = 1; - } - return(result); -} - -internal B32 -demon_os_kill_process(DEMON_Entity *process, U32 exit_code){ - B32 result = 0; - DEMON_W32_Ext *ext = demon_w32_ext(process); - if (TerminateProcess(ext->proc.handle, exit_code)){ - result = 1; - } - return(result); -} - -internal B32 -demon_os_detach_process(DEMON_Entity *process){ - B32 result = 0; - - // rjf: resume threads - for(DEMON_Entity *child = process->first; child != 0; child = child->next) - { - if(child->kind == DEMON_EntityKind_Thread) - { - DEMON_W32_Ext *thread_ext = demon_w32_ext(child); - DWORD resume_result = ResumeThread(thread_ext->thread.handle); - } - } - - // rjf: detach - { - DWORD pid = (DWORD)process->id; - if(DebugActiveProcessStop(pid)) - { - result = 1; - } - } - - // rjf: push into list - if(result != 0) - { - DEMON_EntityNode *n = push_array(demon_w32_detach_proc_arena, DEMON_EntityNode, 1); - n->entity = process; - SLLQueuePush(demon_w32_first_detached_proc, demon_w32_last_detached_proc, n); - } - - return(result); -} - -//////////////////////////////// -//~ rjf: @demon_os_hooks Entity Functions - -//- rjf: cleanup - -internal void -demon_os_entity_cleanup(DEMON_Entity *entity) -{ - if (entity->kind == DEMON_EntityKind_Process){ - DEMON_W32_Ext *ext = demon_w32_ext(entity); - SLLStackPush(demon_w32_proc_ext_free, ext); - } - else if(entity->kind == DEMON_EntityKind_Module) - { - DEMON_W32_Ext *ext = demon_w32_ext(entity); - CloseHandle(ext->module.handle); - } -} - -//- rjf: introspection - -internal String8 -demon_os_full_path_from_module(Arena *arena, DEMON_Entity *module){ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - - // object -> handle - DEMON_W32_Ext *module_ext = (DEMON_W32_Ext*)module->ext; - HANDLE handle = module_ext->module.handle; - - String16 path16 = {0}; - String8 path8 = {0}; - - // handle -> full path - if (handle != 0){ - DWORD cap16 = GetFinalPathNameByHandleW(handle, 0, 0, VOLUME_NAME_DOS); - U16 *buffer16 = push_array_no_zero(scratch.arena, U16, cap16); - DWORD size16 = GetFinalPathNameByHandleW(handle, (WCHAR*)buffer16, cap16, VOLUME_NAME_DOS); - path16 = str16(buffer16, size16); - } - - // fallback (main module only): process -> full path - if (path16.size == 0 && module_ext->module.is_main){ - // process handle - DEMON_Entity *process = module->parent; - DEMON_W32_Ext *process_ext = (DEMON_W32_Ext*)process->ext; - HANDLE process_handle = process_ext->proc.handle; - - DWORD size = KB(4); - U16 *buf = push_array_no_zero(scratch.arena, U16, size); - if (QueryFullProcessImageNameW(process_handle, 0, (WCHAR*)buf, &size)){ - path16 = str16(buf, size); - } - } - - // fallback (any module - no gaurantee): address_of_name -> full path - if (path16.size == 0 && module_ext->module.address_of_name_pointer != 0){ - // process handle - DEMON_Entity *process = module->parent; - DEMON_W32_Ext *process_ext = (DEMON_W32_Ext*)process->ext; - HANDLE process_handle = process_ext->proc.handle; - - // TODO(allen): address size independence - U64 ptr_size = 8; - - U64 name_pointer = 0; - if (demon_w32_read_memory(process_handle, &name_pointer, module_ext->module.address_of_name_pointer, ptr_size)){ - if (name_pointer != 0){ - if (module_ext->module.name_is_unicode){ - path16 = demon_w32_read_memory_str16(scratch.arena, process_handle, name_pointer); - } - else{ - path8 = demon_w32_read_memory_str(scratch.arena, process_handle, name_pointer); - } - } - } - } - - // finalize the result - String8 result = {0}; - - if (path16.size > 0){ - // skip the extended path thing if necessary - if (path16.size >= 4 && - path16.str[0] == L'\\' && - path16.str[1] == L'\\' && - path16.str[2] == L'?' && - path16.str[3] == L'\\'){ - path16.size -= 4; - path16.str += 4; - } - - // convert result - result = str8_from_16(arena, path16); - } - else{ - // skip the extended path thing if necessary - if (path8.size >= 4 && - path8.str[0] == L'\\' && - path8.str[1] == L'\\' && - path8.str[2] == L'?' && - path8.str[3] == L'\\'){ - path8.size -= 4; - path8.str += 4; - } - - // copy the result - result = push_str8_copy(arena, path8); - } - - scratch_end(scratch); - ProfEnd(); - return(result); -} - -internal U64 -demon_os_stack_base_vaddr_from_thread(DEMON_Entity *thread) -{ - DEMON_Entity *process = thread->parent; - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - U64 tlb = thread_ext->thread.thread_local_base; - U64 result = 0; - switch (thread->arch) - { - default:{NotImplemented;}break; - case Architecture_x64: - { - U64 stack_base_addr = tlb + 0x8; - demon_os_read_memory(process, &result, stack_base_addr, 8); - }break; - case Architecture_x86: - { - U64 stack_base_addr = tlb + 0x4; - demon_os_read_memory(process, &result, stack_base_addr, 4); - }break; - } - return(result); -} - -internal U64 -demon_os_tls_root_vaddr_from_thread(DEMON_Entity *thread) -{ - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - U64 result = thread_ext->thread.thread_local_base; - switch(thread->arch) - { - default:{NotImplemented;}break; - case Architecture_x64: - { - result += 88; - }break; - case Architecture_x86: - { - result += 44; - }break; - } - return(result); -} - -//- rjf: target process memory allocation/protection - -internal U64 -demon_os_reserve_memory(DEMON_Entity *process, U64 size){ - DEMON_W32_Ext *ext = demon_w32_ext(process); - void *ptr = VirtualAllocEx(ext->proc.handle, 0, size, MEM_RESERVE, PAGE_NOACCESS); - U64 result = (U64)ptr; - return(result); -} - -internal void -demon_os_set_memory_protect_flags(DEMON_Entity *process, U64 page_vaddr, U64 size, DEMON_MemoryProtectFlags flags){ - DEMON_W32_Ext *ext = demon_w32_ext(process); - DWORD w32_flags = demon_w32_win32_from_memory_protect_flags(flags); - DWORD old_flags = 0; - DWORD alloc_type = (flags == 0 ? MEM_DECOMMIT : MEM_COMMIT); - VirtualAllocEx(ext->proc.handle, (void *)page_vaddr, size, alloc_type, w32_flags); - (void)old_flags; -} - -internal void -demon_os_release_memory(DEMON_Entity *process, U64 vaddr, U64 size){ - DEMON_W32_Ext *ext = demon_w32_ext(process); - VirtualFreeEx(ext->proc.handle, (void *)vaddr, 0, MEM_RELEASE); -} - -//- rjf: target process memory reading/writing - -internal U64 -demon_os_read_memory(DEMON_Entity *process, void *dst, U64 src_address, U64 size){ - DEMON_W32_Ext *process_ext = demon_w32_ext(process); - HANDLE handle = process_ext->proc.handle; - U64 result = demon_w32_read_memory(handle, dst, src_address, size); - return(result); -} - -internal B32 -demon_os_write_memory(DEMON_Entity *process, U64 dst_address, void *src, U64 size){ - DEMON_W32_Ext *process_ext = demon_w32_ext(process); - HANDLE handle = process_ext->proc.handle; - B32 result = demon_w32_write_memory(handle, dst_address, src, size); - return(result); -} - -//- rjf: thread registers reading/writing - -internal B32 -demon_os_read_regs_x86(DEMON_Entity *thread, REGS_RegBlockX86 *dst){ - B32 result = 0; - - // NOTE(allen): Get Thread Context - WOW64_CONTEXT ctx = {0}; - ctx.ContextFlags = DEMON_W32_CTX_X86_ALL; - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - HANDLE handle = thread_ext->thread.handle; - if (Wow64GetThreadContext(handle, (WOW64_CONTEXT *)&ctx)){ - result = 1; - - // NOTE(allen): Convert WOW64_CONTEXT -> REGS_RegBlockX86 - dst->eax.u32 = ctx.Eax; - dst->ebx.u32 = ctx.Ebx; - dst->ecx.u32 = ctx.Ecx; - dst->edx.u32 = ctx.Edx; - dst->esi.u32 = ctx.Esi; - dst->edi.u32 = ctx.Edi; - dst->esp.u32 = ctx.Esp; - dst->ebp.u32 = ctx.Ebp; - dst->eip.u32 = ctx.Eip; - dst->cs.u16 = ctx.SegCs; - dst->ds.u16 = ctx.SegDs; - dst->es.u16 = ctx.SegEs; - dst->fs.u16 = ctx.SegFs; - dst->gs.u16 = ctx.SegGs; - dst->ss.u16 = ctx.SegSs; - dst->dr0.u32 = ctx.Dr0; - dst->dr1.u32 = ctx.Dr1; - dst->dr2.u32 = ctx.Dr2; - dst->dr3.u32 = ctx.Dr3; - dst->dr6.u32 = ctx.Dr6; - dst->dr7.u32 = ctx.Dr7; - - // NOTE(allen): This bit is "supposed to always be 1" I guess. - // TODO(allen): Not sure what this is all about but I haven't investigated it yet. - // This might be totally not necessary or something. - dst->eflags.u32 = ctx.EFlags | 0x2; - - XSAVE_FORMAT *fxsave = (XSAVE_FORMAT*)ctx.ExtendedRegisters; - dst->fcw.u16 = fxsave->ControlWord; - dst->fsw.u16 = fxsave->StatusWord; - dst->ftw.u16 = demon_w32_real_tag_word_from_xsave(fxsave); - dst->fop.u16 = fxsave->ErrorOpcode; - dst->fip.u32 = fxsave->ErrorOffset; - dst->fcs.u16 = fxsave->ErrorSelector; - dst->fdp.u32 = fxsave->DataOffset; - dst->fds.u16 = fxsave->DataSelector; - dst->mxcsr.u32 = fxsave->MxCsr; - dst->mxcsr_mask.u32 = fxsave->MxCsr_Mask; - - M128A *float_s = fxsave->FloatRegisters; - REGS_Reg80 *float_d = &dst->fpr0; - for (U32 n = 0; n < 8; n += 1, float_s += 1, float_d += 1){ - MemoryCopy(float_d, float_s, sizeof(*float_d)); - } - - M128A *xmm_s = fxsave->XmmRegisters; - REGS_Reg256 *xmm_d = &dst->ymm0; - for (U32 n = 0; n < 8; n += 1, xmm_s += 1, xmm_d += 1){ - MemoryCopy(xmm_d, xmm_s, sizeof(*xmm_s)); - } - - // read FS/GS base - WOW64_LDT_ENTRY ldt = {0}; - - if (Wow64GetThreadSelectorEntry(handle, ctx.SegFs, &ldt)){ - U32 base = (ldt.BaseLow) | (ldt.HighWord.Bytes.BaseMid << 16) | (ldt.HighWord.Bytes.BaseHi << 24); - dst->fsbase.u32 = base; - } - if (Wow64GetThreadSelectorEntry(handle, ctx.SegGs, &ldt)){ - U32 base = (ldt.BaseLow) | (ldt.HighWord.Bytes.BaseMid << 16) | (ldt.HighWord.Bytes.BaseHi << 24); - dst->gsbase.u32 = base; - } - } - - return(result); -} - -internal B32 -demon_os_write_regs_x86(DEMON_Entity *thread, REGS_RegBlockX86 *src){ - // NOTE(allen): Convert REGS_RegBlockX86 -> WOW64_CONTEXT - WOW64_CONTEXT ctx = {0}; - ctx.ContextFlags = DEMON_W32_CTX_X86_ALL; - ctx.Eax = src->eax.u32; - ctx.Ebx = src->ebx.u32; - ctx.Ecx = src->ecx.u32; - ctx.Edx = src->edx.u32; - ctx.Esi = src->esi.u32; - ctx.Edi = src->edi.u32; - ctx.Esp = src->esp.u32; - ctx.Ebp = src->ebp.u32; - ctx.Eip = src->eip.u32; - ctx.SegCs = src->cs.u16; - ctx.SegDs = src->ds.u16; - ctx.SegEs = src->es.u16; - ctx.SegFs = src->fs.u16; - ctx.SegGs = src->gs.u16; - ctx.SegSs = src->ss.u16; - ctx.Dr0 = src->dr0.u32; - ctx.Dr1 = src->dr1.u32; - ctx.Dr2 = src->dr2.u32; - ctx.Dr3 = src->dr3.u32; - ctx.Dr6 = src->dr6.u32; - ctx.Dr7 = src->dr7.u32; - ctx.EFlags = src->eflags.u32; - - XSAVE_FORMAT *fxsave = (XSAVE_FORMAT*)ctx.ExtendedRegisters; - fxsave->ControlWord = src->fcw.u16; - fxsave->StatusWord = src->fsw.u16; - fxsave->TagWord = demon_w32_xsave_tag_word_from_real_tag_word(src->ftw.u16); - fxsave->ErrorOpcode = src->fop.u16; - fxsave->ErrorSelector = src->fcs.u16; - fxsave->DataSelector = src->fds.u16; - fxsave->ErrorOffset = src->fip.u32; - fxsave->DataOffset = src->fdp.u32; - fxsave->MxCsr = src->mxcsr.u32 & src->mxcsr_mask.u32; - fxsave->MxCsr_Mask = src->mxcsr_mask.u32; - - M128A *float_d = fxsave->FloatRegisters; - REGS_Reg80 *float_s = &src->fpr0; - for (U32 n = 0; - n < 8; - n += 1, float_s += 1, float_d += 1){ - MemoryCopy(float_d, float_s, 10); - } - - M128A *xmm_d = fxsave->XmmRegisters; - REGS_Reg256 *xmm_s = &src->ymm0; - for (U32 n = 0; - n < 8; - n += 1, xmm_d += 1, xmm_s += 1){ - MemoryCopy(xmm_d, xmm_s, sizeof(*xmm_d)); - } - - // set thread context - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - HANDLE handle = thread_ext->thread.handle; - B32 result = 0; - if (Wow64SetThreadContext(handle, &ctx)){ - result = 1; - } - - return(result); -} - -internal B32 -demon_os_read_regs_x64(DEMON_Entity *thread, REGS_RegBlockX64 *dst){ - Temp scratch = scratch_begin(0, 0); - - // NOTE(allen): Check available features - U32 feature_mask = GetEnabledXStateFeatures(); - B32 avx_enabled = !!(feature_mask & XSTATE_MASK_AVX); - - // NOTE(allen): Setup the context - CONTEXT *ctx = 0; - U32 ctx_flags = DEMON_W32_CTX_X64_ALL; - if (avx_enabled){ - ctx_flags |= DEMON_W32_CTX_INTEL_XSTATE; - } - DWORD size = 0; - InitializeContext(0, ctx_flags, 0, &size); - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER){ - void *ctx_memory = push_array(scratch.arena, U8, size); - if (!InitializeContext(ctx_memory, ctx_flags, &ctx, &size)){ - ctx = 0; - } - } - - B32 avx_available = 0; - - if (ctx != 0){ - // NOTE(allen): Finish Context Setup - if (avx_enabled){ - SetXStateFeaturesMask(ctx, XSTATE_MASK_AVX); - } - - // NOTE(allen): Determine what features are available on this particular ctx - // TODO(allen): Experiment carefully with this nonsense. - // Does avx_enabled = avx_available in all circumstances or not? - DWORD64 xstate_flags = 0; - if (GetXStateFeaturesMask(ctx, &xstate_flags)){ - if (xstate_flags & XSTATE_MASK_AVX){ - avx_available = 1; - } - } - } - - // get thread context - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - HANDLE thread_handle = thread_ext->thread.handle; - if (!GetThreadContext(thread_handle, ctx)){ - ctx = 0; - } - - B32 result = 0; - if (ctx != 0){ - result = 1; - - // NOTE(allen): Convert CONTEXT -> REGS_RegBlockX64 - dst->rax.u64 = ctx->Rax; - dst->rcx.u64 = ctx->Rcx; - dst->rdx.u64 = ctx->Rdx; - dst->rbx.u64 = ctx->Rbx; - dst->rsp.u64 = ctx->Rsp; - dst->rbp.u64 = ctx->Rbp; - dst->rsi.u64 = ctx->Rsi; - dst->rdi.u64 = ctx->Rdi; - dst->r8.u64 = ctx->R8; - dst->r9.u64 = ctx->R9; - dst->r10.u64 = ctx->R10; - dst->r11.u64 = ctx->R11; - dst->r12.u64 = ctx->R12; - dst->r13.u64 = ctx->R13; - dst->r14.u64 = ctx->R14; - dst->r15.u64 = ctx->R15; - dst->rip.u64 = ctx->Rip; - dst->cs.u16 = ctx->SegCs; - dst->ds.u16 = ctx->SegDs; - dst->es.u16 = ctx->SegEs; - dst->fs.u16 = ctx->SegFs; - dst->gs.u16 = ctx->SegGs; - dst->ss.u16 = ctx->SegSs; - dst->dr0.u32 = ctx->Dr0; - dst->dr1.u32 = ctx->Dr1; - dst->dr2.u32 = ctx->Dr2; - dst->dr3.u32 = ctx->Dr3; - dst->dr6.u32 = ctx->Dr6; - dst->dr7.u32 = ctx->Dr7; - - // NOTE(allen): This bit is "supposed to always be 1" I guess. - // TODO(allen): Not sure what this is all about but I haven't investigated it yet. - // This might be totally not necessary or something. - dst->rflags.u64 = ctx->EFlags | 0x2; - - XSAVE_FORMAT *xsave = &ctx->FltSave; - dst->fcw.u16 = xsave->ControlWord; - dst->fsw.u16 = xsave->StatusWord; - dst->ftw.u16 = demon_w32_real_tag_word_from_xsave(xsave); - dst->fop.u16 = xsave->ErrorOpcode; - dst->fcs.u16 = xsave->ErrorSelector; - dst->fds.u16 = xsave->DataSelector; - dst->fip.u32 = xsave->ErrorOffset; - dst->fdp.u32 = xsave->DataOffset; - dst->mxcsr.u32 = xsave->MxCsr; - dst->mxcsr_mask.u32 = xsave->MxCsr_Mask; - - M128A *float_s = xsave->FloatRegisters; - REGS_Reg80 *float_d = &dst->fpr0; - for (U32 n = 0; n < 8; n += 1, float_s += 1, float_d += 1){ - MemoryCopy(float_d, float_s, sizeof(*float_d)); - } - - if (!avx_available){ - M128A *xmm_s = xsave->XmmRegisters; - REGS_Reg256 *xmm_d = &dst->ymm0; - for (U32 n = 0; n < 16; n += 1, xmm_s += 1, xmm_d += 1){ - MemoryCopy(xmm_d, xmm_s, sizeof(*xmm_s)); - } - } - - if (avx_available){ - DWORD part0_length = 0; - M128A *part0 = (M128A*)LocateXStateFeature(ctx, XSTATE_LEGACY_SSE, &part0_length); - DWORD part1_length = 0; - M128A *part1 = (M128A*)LocateXStateFeature(ctx, XSTATE_AVX, &part1_length); - Assert(part0_length == part1_length); - - DWORD count = part0_length/sizeof(part0[0]); - count = ClampTop(count, 16); - REGS_Reg256 *ymm_d = &dst->ymm0; - for (DWORD i = 0; - i < count; - i += 1, part0 += 1, part1 += 1, ymm_d += 1){ - // TODO(allen): Are we writing these out in the right order? Seems weird right? - ymm_d->u64[3] = part0->Low; - ymm_d->u64[2] = part0->High; - ymm_d->u64[1] = part1->Low; - ymm_d->u64[0] = part1->High; - } - } - - } - - scratch_end(scratch); - return(result); -} - -internal B32 -demon_os_write_regs_x64(DEMON_Entity *thread, REGS_RegBlockX64 *src){ - Temp scratch = scratch_begin(0, 0); - - // NOTE(allen): Check available features - U32 feature_mask = GetEnabledXStateFeatures(); - B32 avx_enabled = !!(feature_mask & XSTATE_MASK_AVX); - - // NOTE(allen): Setup the context - CONTEXT *ctx = 0; - U32 ctx_flags = DEMON_W32_CTX_X64_ALL; - if (avx_enabled){ - ctx_flags |= DEMON_W32_CTX_INTEL_XSTATE; - } - DWORD size = 0; - InitializeContext(0, ctx_flags, 0, &size); - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER){ - void *ctx_memory = push_array(scratch.arena, U8, size); - if (!InitializeContext(ctx_memory, ctx_flags, &ctx, &size)){ - ctx = 0; - } - } - - B32 avx_available = 0; - - if (ctx != 0){ - // NOTE(allen): Finish Context Setup - if (avx_enabled){ - SetXStateFeaturesMask(ctx, XSTATE_MASK_AVX); - } - - // NOTE(allen): Determine what features are available on this particular ctx - // TODO(allen): Experiment carefully with this nonsense. - // Does avx_enabled = avx_available in all circumstances or not? - DWORD64 xstate_flags = 0; - if (GetXStateFeaturesMask(ctx, &xstate_flags)){ - if (xstate_flags & XSTATE_MASK_AVX){ - avx_available = 1; - } - } - } - - B32 result = 0; - if (ctx != 0){ - // NOTE(allen): Convert REGS_RegBlockX64 -> CONTEXT - ctx->ContextFlags = ctx_flags; - - ctx->MxCsr = src->mxcsr.u32 & src->mxcsr_mask.u32; - - ctx->Rax = src->rax.u64; - ctx->Rcx = src->rcx.u64; - ctx->Rdx = src->rdx.u64; - ctx->Rbx = src->rbx.u64; - ctx->Rsp = src->rsp.u64; - ctx->Rbp = src->rbp.u64; - ctx->Rsi = src->rsi.u64; - ctx->Rdi = src->rdi.u64; - ctx->R8 = src->r8.u64; - ctx->R9 = src->r9.u64; - ctx->R10 = src->r10.u64; - ctx->R11 = src->r11.u64; - ctx->R12 = src->r12.u64; - ctx->R13 = src->r13.u64; - ctx->R14 = src->r14.u64; - ctx->R15 = src->r15.u64; - ctx->Rip = src->rip.u64; - ctx->SegCs = src->cs.u16; - ctx->SegDs = src->ds.u16; - ctx->SegEs = src->es.u16; - ctx->SegFs = src->fs.u16; - ctx->SegGs = src->gs.u16; - ctx->SegSs = src->ss.u16; - ctx->Dr0 = src->dr0.u32; - ctx->Dr1 = src->dr1.u32; - ctx->Dr2 = src->dr2.u32; - ctx->Dr3 = src->dr3.u32; - ctx->Dr6 = src->dr6.u32; - ctx->Dr7 = src->dr7.u32; - - ctx->EFlags = src->rflags.u64; - - XSAVE_FORMAT *fxsave = &ctx->FltSave; - fxsave->ControlWord = src->fcw.u16; - fxsave->StatusWord = src->fsw.u16; - fxsave->TagWord = demon_w32_xsave_tag_word_from_real_tag_word(src->ftw.u16); - fxsave->ErrorOpcode = src->fop.u16; - fxsave->ErrorSelector = src->fcs.u16; - fxsave->DataSelector = src->fds.u16; - fxsave->ErrorOffset = src->fip.u32; - fxsave->DataOffset = src->fdp.u32; - - M128A *float_d = fxsave->FloatRegisters; - REGS_Reg80 *float_s = &src->fpr0; - for (U32 n = 0; - n < 8; - n += 1, float_s += 1, float_d += 1){ - MemoryCopy(float_d, float_s, 10); - } - - if (!avx_available){ - M128A *xmm_d = fxsave->XmmRegisters; - REGS_Reg256 *xmm_s = &src->ymm0; - for (U32 n = 0; - n < 8; - n += 1, xmm_d += 1, xmm_s += 1){ - MemoryCopy(xmm_d, xmm_s, sizeof(*xmm_d)); - } - } - - if (avx_available){ - DWORD part0_length = 0; - M128A *part0 = (M128A*)LocateXStateFeature(ctx, XSTATE_LEGACY_SSE, &part0_length); - DWORD part1_length = 0; - M128A *part1 = (M128A*)LocateXStateFeature(ctx, XSTATE_AVX, &part1_length); - Assert(part0_length == part1_length); - - DWORD count = part0_length/sizeof(part0[0]); - count = ClampTop(count, 16); - REGS_Reg256 *ymm_d = &src->ymm0; - for (DWORD i = 0; - i < count; - i += 1, part0 += 1, part1 += 1, ymm_d += 1){ - // TODO(allen): Are we writing these out in the right order? Seems weird right? - part0->Low = ymm_d->u64[3]; - part0->High = ymm_d->u64[2]; - part1->Low = ymm_d->u64[1]; - part1->High = ymm_d->u64[0]; - } - } - - //- set thread context - DEMON_W32_Ext *thread_ext = demon_w32_ext(thread); - HANDLE thread_handle = thread_ext->thread.handle; - if (SetThreadContext(thread_handle, ctx)){ - result = 1; - } - } - - scratch_end(scratch); - return(result); -} - -//////////////////////////////// -//~ rjf: @demon_os_hooks Process Listing - -internal void -demon_os_proc_iter_begin(DEMON_ProcessIter *iter){ - MemoryZeroStruct(iter); - iter->v[0] = (U64)CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); -} - -internal B32 -demon_os_proc_iter_next(Arena *arena, DEMON_ProcessIter *iter, DEMON_ProcessInfo *info_out){ - // get the next process entry - B32 result = 0; - PROCESSENTRY32W process_entry = {sizeof(process_entry)}; - HANDLE snapshot = (HANDLE)iter->v[0]; - if (iter->v[1] == 0){ - if (Process32FirstW(snapshot, &process_entry)){ - result = 1; - } - } - else{ - if (Process32NextW(snapshot, &process_entry)){ - result = 1; - } - } - - // increment counter - iter->v[1] += 1; - - // convert to process info - if (result){ - info_out->name = str8_from_16(arena, str16_cstring((U16*)process_entry.szExeFile)); - info_out->pid = (U32)process_entry.th32ProcessID; - } - - return(result); -} - -internal void -demon_os_proc_iter_end(DEMON_ProcessIter *iter){ - CloseHandle((HANDLE)iter->v[0]); - MemoryZeroStruct(iter); -} diff --git a/src/demon/win32/demon_os_win32.h b/src/demon/win32/demon_os_win32.h deleted file mode 100644 index 7233e0d0..00000000 --- a/src/demon/win32/demon_os_win32.h +++ /dev/null @@ -1,385 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DEMON_OS_WIN32_H -#define DEMON_OS_WIN32_H - -//////////////////////////////// -//~ NOTE(allen): Win32 Demon Headers Negotation - -// windows headers -#define WIN32_LEAN_AND_MEAN -#include -#include -#include - -//////////////////////////////// -//~ NOTE(allen): Win32 Demon Types - -//- entities - -// Demon Win32 Entity Extensions -// Process: ext points to independently allocated DEMON_W32_Ext -// Thread : ext points to independently allocated DEMON_W32_Ext -// Module : ext set to HANDLE - -typedef union DEMON_W32_Ext DEMON_W32_Ext; -union DEMON_W32_Ext -{ - DEMON_W32_Ext *next; - struct{ - HANDLE handle; - U64 injection_address; - B32 did_first_bp; - } proc; - struct{ - HANDLE handle; - U64 thread_local_base; - U64 last_name_hash; - U64 name_gather_time_us; - B32 last_run_reported_trap; - U64 last_run_reported_trap_pre_rip; - U64 last_run_reported_trap_post_rip; - } thread; - struct{ - HANDLE handle; - U64 address_of_name_pointer; - B32 is_main; - B32 name_is_unicode; - } module; -}; - -//- helpers - -typedef struct DEMON_W32_InjectedBreak DEMON_W32_InjectedBreak; -struct DEMON_W32_InjectedBreak -{ - U64 code; - U64 user_data; -}; -#define DEMON_W32_INJECTED_CODE_SIZE 32 - -typedef struct DEMON_W32_ImageInfo DEMON_W32_ImageInfo; -struct DEMON_W32_ImageInfo -{ - Architecture arch; - U32 size; -}; - -typedef struct DEMON_W32_EntityNode DEMON_W32_EntityNode; -struct DEMON_W32_EntityNode -{ - DEMON_W32_EntityNode *next; - DEMON_Entity *entity; -}; - -typedef HRESULT GetThreadDescriptionFunctionType(HANDLE hThread, WCHAR **ppszThreadDescription); - -//////////////////////////////// -//~ NOTE(allen): Win32 Demon Exceptions - -#define DEMON_W32_EXCEPTION_BREAKPOINT 0x80000003u -#define DEMON_W32_EXCEPTION_SINGLE_STEP 0x80000004u -#define DEMON_W32_EXCEPTION_LONG_JUMP 0x80000026u -#define DEMON_W32_EXCEPTION_ACCESS_VIOLATION 0xC0000005u -#define DEMON_W32_EXCEPTION_ARRAY_BOUNDS_EXCEEDED 0xC000008Cu -#define DEMON_W32_EXCEPTION_DATA_TYPE_MISALIGNMENT 0x80000002u -#define DEMON_W32_EXCEPTION_GUARD_PAGE_VIOLATION 0x80000001u -#define DEMON_W32_EXCEPTION_FLT_DENORMAL_OPERAND 0xC000008Du -#define DEMON_W32_EXCEPTION_FLT_DEVIDE_BY_ZERO 0xC000008Eu -#define DEMON_W32_EXCEPTION_FLT_INEXACT_RESULT 0xC000008Fu -#define DEMON_W32_EXCEPTION_FLT_INVALID_OPERATION 0xC0000090u -#define DEMON_W32_EXCEPTION_FLT_OVERFLOW 0xC0000091u -#define DEMON_W32_EXCEPTION_FLT_STACK_CHECK 0xC0000092u -#define DEMON_W32_EXCEPTION_FLT_UNDERFLOW 0xC0000093u -#define DEMON_W32_EXCEPTION_INT_DIVIDE_BY_ZERO 0xC0000094u -#define DEMON_W32_EXCEPTION_INT_OVERFLOW 0xC0000095u -#define DEMON_W32_EXCEPTION_PRIVILEGED_INSTRUCTION 0xC0000096u -#define DEMON_W32_EXCEPTION_ILLEGAL_INSTRUCTION 0xC000001Du -#define DEMON_W32_EXCEPTION_IN_PAGE_ERROR 0xC0000006u -#define DEMON_W32_EXCEPTION_INVALID_DISPOSITION 0xC0000026u -#define DEMON_W32_EXCEPTION_NONCONTINUABLE 0xC0000025u -#define DEMON_W32_EXCEPTION_STACK_OVERFLOW 0xC00000FDu -#define DEMON_W32_EXCEPTION_INVALID_HANDLE 0xC0000008u -#define DEMON_W32_EXCEPTION_UNWIND_CONSOLIDATE 0x80000029u -#define DEMON_W32_EXCEPTION_DLL_NOT_FOUND 0xC0000135u -#define DEMON_W32_EXCEPTION_ORDINAL_NOT_FOUND 0xC0000138u -#define DEMON_W32_EXCEPTION_ENTRY_POINT_NOT_FOUND 0xC0000139u -#define DEMON_W32_EXCEPTION_DLL_INIT_FAILED 0xC0000142u -#define DEMON_W32_EXCEPTION_CONTROL_C_EXIT 0xC000013Au -#define DEMON_W32_EXCEPTION_FLT_MULTIPLE_FAULTS 0xC00002B4u -#define DEMON_W32_EXCEPTION_FLT_MULTIPLE_TRAPS 0xC00002B5u -#define DEMON_W32_EXCEPTION_NAT_CONSUMPTION 0xC00002C9u -#define DEMON_W32_EXCEPTION_HEAP_CORRUPTION 0xC0000374u -#define DEMON_W32_EXCEPTION_STACK_BUFFER_OVERRUN 0xC0000409u -#define DEMON_W32_EXCEPTION_INVALID_CRUNTIME_PARAM 0xC0000417u -#define DEMON_W32_EXCEPTION_ASSERT_FAILURE 0xC0000420u -#define DEMON_W32_EXCEPTION_NO_MEMORY 0xC0000017u -#define DEMON_W32_EXCEPTION_THROW 0xE06D7363u -#define DEMON_W32_EXCEPTION_SET_THREAD_NAME 0x406d1388u - -//////////////////////////////// -//~ NOTE(allen): Win32 Demon Register API Codes - -#define DEMON_W32_CTX_X86 0x00010000 -#define DEMON_W32_CTX_X64 0x00100000 - -#define DEMON_W32_CTX_INTEL_CONTROL 0x0001 -#define DEMON_W32_CTX_INTEL_INTEGER 0x0002 -#define DEMON_W32_CTX_INTEL_SEGMENTS 0x0004 -#define DEMON_W32_CTX_INTEL_FLOATS 0x0008 -#define DEMON_W32_CTX_INTEL_DEBUG 0x0010 -#define DEMON_W32_CTX_INTEL_EXTENDED 0x0020 -#define DEMON_W32_CTX_INTEL_XSTATE 0x0040 - -#define DEMON_W32_CTX_X86_ALL (DEMON_W32_CTX_X86 | \ -DEMON_W32_CTX_INTEL_CONTROL | DEMON_W32_CTX_INTEL_INTEGER | \ -DEMON_W32_CTX_INTEL_SEGMENTS | DEMON_W32_CTX_INTEL_DEBUG | \ -DEMON_W32_CTX_INTEL_EXTENDED) -#define DEMON_W32_CTX_X64_ALL (DEMON_W32_CTX_X64 | \ -DEMON_W32_CTX_INTEL_CONTROL | DEMON_W32_CTX_INTEL_INTEGER | \ -DEMON_W32_CTX_INTEL_SEGMENTS | DEMON_W32_CTX_INTEL_FLOATS | \ -DEMON_W32_CTX_INTEL_DEBUG) - -//////////////////////////////// -//~ rjf: DOS Header Types - -// this is the "MZ" as a 16-bit short -#define DEMON_DOS_MAGIC 0x5a4d - -#pragma pack(push,1) -typedef struct DEMON_DosHeader DEMON_DosHeader; -struct DEMON_DosHeader -{ - U16 magic; - U16 last_page_size; - U16 page_count; - U16 reloc_count; - U16 paragraph_header_size; - U16 min_paragraph; - U16 max_paragraph; - U16 init_ss; - U16 init_sp; - U16 checksum; - U16 init_ip; - U16 init_cs; - U16 reloc_table_file_off; - U16 overlay_number; - U16 reserved[4]; - U16 oem_id; - U16 oem_info; - U16 reserved2[10]; - U32 coff_file_offset; -}; -#pragma pack(pop) - -//////////////////////////////// -//~ rjf: Coff Header Types - -#define DEMON_PE_MAGIC 0x00004550u - -typedef U16 DEMON_CoffMachineType; -enum{ - DEMON_CoffMachineType_UNKNOWN = 0x0, - DEMON_CoffMachineType_X86 = 0x14c, - DEMON_CoffMachineType_X64 = 0x8664, - DEMON_CoffMachineType_ARM33 = 0x1d3, - DEMON_CoffMachineType_ARM = 0x1c0, - DEMON_CoffMachineType_ARM64 = 0xaa64, - DEMON_CoffMachineType_ARMNT = 0x1c4, - DEMON_CoffMachineType_EBC = 0xebc, - DEMON_CoffMachineType_IA64 = 0x200, - DEMON_CoffMachineType_M32R = 0x9041, - DEMON_CoffMachineType_MIPS16 = 0x266, - DEMON_CoffMachineType_MIPSFPU = 0x366, - DEMON_CoffMachineType_MIPSFPU16 = 0x466, - DEMON_CoffMachineType_POWERPC = 0x1f0, - DEMON_CoffMachineType_POWERPCFP = 0x1f1, - DEMON_CoffMachineType_R4000 = 0x166, - DEMON_CoffMachineType_RISCV32 = 0x5032, - DEMON_CoffMachineType_RISCV64 = 0x5064, - DEMON_CoffMachineType_RISCV128 = 0x5128, - DEMON_CoffMachineType_SH3 = 0x1a2, - DEMON_CoffMachineType_SH3DSP = 0x1a3, - DEMON_CoffMachineType_SH4 = 0x1a6, - DEMON_CoffMachineType_SH5 = 0x1a8, - DEMON_CoffMachineType_THUMB = 0x1c2, - DEMON_CoffMachineType_WCEMIPSV2 = 0x169, - DEMON_CoffMachineType_COUNT = 25 -}; - -typedef U16 DEMON_CoffFlags; -enum{ - DEMON_CoffFlag_RELOC_STRIPPED = (1 << 0), - DEMON_CoffFlag_EXECUTABLE_IMAGE = (1 << 1), - DEMON_CoffFlag_LINE_NUMS_STRIPPED = (1 << 2), - DEMON_CoffFlag_SYM_STRIPPED = (1 << 3), - DEMON_CoffFlag_RESERVED_0 = (1 << 4), - DEMON_CoffFlag_LARGE_ADDRESS_AWARE = (1 << 5), - DEMON_CoffFlag_RESERVED_1 = (1 << 6), - DEMON_CoffFlag_RESERVED_2 = (1 << 7), - DEMON_CoffFlag_32BIT_MACHINE = (1 << 8), - DEMON_CoffFlag_DEBUG_STRIPPED = (1 << 9), - DEMON_CoffFlag_REMOVABLE_RUN_FROM_SWAP = (1 << 10), - DEMON_CoffFlag_NET_RUN_FROM_SWAP = (1 << 11), - DEMON_CoffFlag_SYSTEM = (1 << 12), - DEMON_CoffFlag_DLL = (1 << 13), - DEMON_CoffFlag_UP_SYSTEM_ONLY = (1 << 14), - DEMON_CoffFlag_BYTES_RESERVED_HI = (1 << 15), -}; - -#pragma pack(push,1) -typedef struct DEMON_CoffHeader DEMON_CoffHeader; -struct DEMON_CoffHeader -{ - DEMON_CoffMachineType machine; - U16 section_count; - U32 time_date_stamp; - // TODO: rename to "unix_timestamp" - U32 pointer_to_symbol_table; - U32 number_of_symbols; - // TODO: rename to "symbol_count" - U16 size_of_optional_header; - // TODO: rename to "optional_header_size" - DEMON_CoffFlags flags; -}; -#pragma pack(pop) - -//////////////////////////////// -//~ rjf: PE Header Types - -#pragma pack(push, 1) - -typedef U16 DEMON_PeWindowsSubsystem; -enum{ - DEMON_PeWindowsSubsystem_UNKNOWN = 0, - DEMON_PeWindowsSubsystem_NATIVE = 1, - DEMON_PeWindowsSubsystem_WINDOWS_GUI = 2, - DEMON_PeWindowsSubsystem_WINDOWS_CUI = 3, - DEMON_PeWindowsSubsystem_OS2_CUI = 5, - DEMON_PeWindowsSubsystem_POSIX_CUI = 7, - DEMON_PeWindowsSubsystem_NATIVE_WINDOWS = 8, - DEMON_PeWindowsSubsystem_WINDOWS_CE_GUI = 9, - DEMON_PeWindowsSubsystem_EFI_APPLICATION = 10, - DEMON_PeWindowsSubsystem_EFI_BOOT_SERVICE_DRIVER = 11, - DEMON_PeWindowsSubsystem_EFI_RUNTIME_DRIVER = 12, - DEMON_PeWindowsSubsystem_EFI_ROM = 13, - DEMON_PeWindowsSubsystem_XBOX = 14, - DEMON_PeWindowsSubsystem_WINDOWS_BOOT_APPLICATION = 16, - DEMON_PeWindowsSubsystem_COUNT = 14 -}; - -typedef U16 DEMON_DllCharacteristics; -enum{ - DEMON_DllCharacteristic_HIGH_ENTROPY_VA = (1 << 5), - DEMON_DllCharacteristic_DYNAMIC_BASE = (1 << 6), - DEMON_DllCharacteristic_FORCE_INTEGRITY = (1 << 7), - DEMON_DllCharacteristic_NX_COMPAT = (1 << 8), - DEMON_DllCharacteristic_NO_ISOLATION = (1 << 9), - DEMON_DllCharacteristic_NO_SEH = (1 << 10), - DEMON_DllCharacteristic_NO_BIND = (1 << 11), - DEMON_DllCharacteristic_APPCONTAINER = (1 << 12), - DEMON_DllCharacteristic_WDM_DRIVER = (1 << 13), - DEMON_DllCharacteristic_GUARD_CF = (1 << 14), - DEMON_DllCharacteristic_TERMINAL_SERVER_AWARE = (1 << 15), -}; - -typedef struct DEMON_PeOptionalHeader32 DEMON_PeOptionalHeader32; -struct DEMON_PeOptionalHeader32 -{ - U16 magic; - U8 major_linker_version; - U8 minor_linker_version; - U32 sizeof_code; - U32 sizeof_inited_data; - U32 sizeof_uninited_data; - U32 entry_point_va; - U32 code_base; - U32 data_base; - U32 image_base; - U32 section_alignment; - U32 file_alignment; - U16 major_os_ver; - U16 minor_os_ver; - U16 major_img_ver; - U16 minor_img_ver; - U16 major_subsystem_ver; - U16 minor_subsystem_ver; - U32 win32_version_value; - U32 sizeof_image; - U32 sizeof_headers; - U32 check_sum; - DEMON_PeWindowsSubsystem subsystem; - DEMON_DllCharacteristics dll_characteristics; - U32 sizeof_stack_reserve; - U32 sizeof_stack_commit; - U32 sizeof_heap_reserve; - U32 sizeof_heap_commit; - U32 loader_flags; - U32 data_dir_count; -}; - -typedef struct DEMON_PeOptionalHeader32Plus DEMON_PeOptionalHeader32Plus; -struct DEMON_PeOptionalHeader32Plus -{ - U16 magic; - U8 major_linker_version; - U8 minor_linker_version; - U32 sizeof_code; - U32 sizeof_inited_data; - U32 sizeof_uninited_data; - U32 entry_point_va; - U32 code_base; - U64 image_base; - U32 section_alignment; - U32 file_alignment; - U16 major_os_ver; - U16 minor_os_ver; - U16 major_img_ver; - U16 minor_img_ver; - U16 major_subsystem_ver; - U16 minor_subsystem_ver; - U32 win32_version_value; - U32 sizeof_image; - U32 sizeof_headers; - U32 check_sum; - DEMON_PeWindowsSubsystem subsystem; - DEMON_DllCharacteristics dll_characteristics; - U64 sizeof_stack_reserve; - U64 sizeof_stack_commit; - U64 sizeof_heap_reserve; - U64 sizeof_heap_commit; - U32 loader_flags; - U32 data_dir_count; -}; - -#pragma pack(pop) - -//////////////////////////////// -//~ rjf: Helpers - -internal U64 demon_w32_hash_from_string(String8 string); -internal DEMON_W32_Ext* demon_w32_ext_alloc(void); -internal DEMON_W32_Ext* demon_w32_ext(DEMON_Entity *entity); - -internal U64 demon_w32_read_memory(HANDLE process_handle, void *dst, U64 src_address, U64 size); -internal B32 demon_w32_write_memory(HANDLE process_handle, U64 dst_address, void *src, U64 size); -internal String8 demon_w32_read_memory_str(Arena *arena, HANDLE process_handle, U64 address); -internal String16 demon_w32_read_memory_str16(Arena *arena, HANDLE process_handle, U64 address); - -#define demon_w32_read_struct(h,dst,src) demon_w32_read_memory((h), (dst), (src), sizeof(*(dst))) - -internal DEMON_W32_ImageInfo demon_w32_image_info_from_base(HANDLE process_handle, U64 base); -internal DWORD demon_w32_inject_thread(DEMON_Entity *process, U64 start_address); - -internal U16 demon_w32_real_tag_word_from_xsave(XSAVE_FORMAT *fxsave); -internal U16 demon_w32_xsave_tag_word_from_real_tag_word(U16 ftw); - -internal DWORD demon_w32_win32_from_memory_protect_flags(DEMON_MemoryProtectFlags flags); - -//////////////////////////////// -//~ rjf: Experiments - -internal void demon_w32_peak_at_tls(DEMON_Handle handle); - -#endif //DEMON_OS_WIN32_H diff --git a/src/demon2/demon2_core.c b/src/demon2/demon2_core.c deleted file mode 100644 index 86c1c3c6..00000000 --- a/src/demon2/demon2_core.c +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Basic Type Functions (Helpers, Implemented Once) - -//- rjf: handles - -internal DMN_Handle -dmn_handle_zero(void) -{ - DMN_Handle h = {0}; - return h; -} - -internal B32 -dmn_handle_match(DMN_Handle a, DMN_Handle b) -{ - return a.u32[0] == b.u32[0] && a.u32[1] == b.u32[1]; -} - -//- rjf: trap chunk lists - -internal void -dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap) -{ - DMN_TrapChunkNode *node = list->last; - if(node == 0 || node->count >= node->cap) - { - node = push_array(arena, DMN_TrapChunkNode, 1); - node->cap = cap; - node->v = push_array_no_zero(arena, DMN_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 -dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_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 -dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push) -{ - for(DMN_TrapChunkNode *src_n = to_push->first; src_n != 0; src_n = src_n->next) - { - DMN_TrapChunkNode *dst_n = push_array(arena, DMN_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 -dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle) -{ - DMN_HandleNode *node = push_array(arena, DMN_HandleNode, 1); - SLLQueuePush(list->first, list->last, node); - node->v = handle; - list->count += 1; -} - -internal DMN_HandleArray -dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list) -{ - DMN_HandleArray array = {0}; - array.count = list->count; - array.handles = push_array_no_zero(arena, DMN_Handle, array.count); - U64 idx = 0; - for(DMN_HandleNode *n = list->first; n != 0; n = n->next, idx += 1) - { - array.handles[idx] = n->v; - } - return array; -} - -internal DMN_HandleArray -dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src) -{ - DMN_HandleArray dst = {0}; - dst.count = src->count; - dst.handles = push_array_no_zero(arena, DMN_Handle, dst.count); - MemoryCopy(dst.handles, src->handles, sizeof(DMN_Handle)*dst.count); - return dst; -} - -//- rjf: event list building - -internal DMN_Event * -dmn_event_list_push(Arena *arena, DMN_EventList *list) -{ - DMN_EventNode *n = push_array(arena, DMN_EventNode, 1); - SLLQueuePush(list->first, list->last, n); - list->count += 1; - DMN_Event *result = &n->v; - return result; -} - -//////////////////////////////// -//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once) - -internal U64 -dmn_rip_from_thread(DMN_Handle thread) -{ - U64 result = 0; - Temp scratch = scratch_begin(0, 0); - { - Architecture arch = dmn_arch_from_thread(thread); - U64 reg_block_size = regs_block_size_from_architecture(arch); - void *reg_block = push_array(scratch.arena, U8, reg_block_size); - dmn_thread_read_reg_block(thread, reg_block); - result = regs_rip_from_arch_block(arch, reg_block); - } - scratch_end(scratch); - return result; -} - -internal U64 -dmn_rsp_from_thread(DMN_Handle thread) -{ - U64 result = 0; - Temp scratch = scratch_begin(0, 0); - { - Architecture arch = dmn_arch_from_thread(thread); - U64 reg_block_size = regs_block_size_from_architecture(arch); - void *reg_block = push_array(scratch.arena, U8, reg_block_size); - dmn_thread_read_reg_block(thread, reg_block); - result = regs_rsp_from_arch_block(arch, reg_block); - } - scratch_end(scratch); - return result; -} diff --git a/src/demon2/demon2_core.h b/src/demon2/demon2_core.h deleted file mode 100644 index e0e3860e..00000000 --- a/src/demon2/demon2_core.h +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DEMON2_CORE_H -#define DEMON2_CORE_H - -//////////////////////////////// -//~ rjf: Control-Thread-Only Context -// -// An instance of this struct must ONLY be returned by dmn_ctrl_begin, and only -// used by the thread which called it. All APIs which can ONLY run on the -// control thread, which blocks to control & receive events, will take this -// parameter. All other APIs can be called from any thread. - -typedef struct DMN_CtrlCtx DMN_CtrlCtx; -struct DMN_CtrlCtx -{ - U64 u64 [1]; -}; - -//////////////////////////////// -//~ rjf: Handle Types - -typedef union DMN_Handle DMN_Handle; -union DMN_Handle -{ - U32 u32[2]; - U64 u64[1]; -}; - -typedef struct DMN_HandleNode DMN_HandleNode; -struct DMN_HandleNode -{ - DMN_HandleNode *next; - DMN_Handle v; -}; - -typedef struct DMN_HandleList DMN_HandleList; -struct DMN_HandleList -{ - DMN_HandleNode *first; - DMN_HandleNode *last; - U64 count; -}; - -typedef struct DMN_HandleArray DMN_HandleArray; -struct DMN_HandleArray -{ - DMN_Handle *handles; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Generated Code - -#include "generated/demon2.meta.h" - -//////////////////////////////// -//~ rjf: Event Types - -typedef struct DMN_Event DMN_Event; -struct DMN_Event -{ - DMN_EventKind kind; - DMN_ErrorKind error_kind; - DMN_MemoryEventKind memory_kind; - DMN_ExceptionKind exception_kind; - DMN_Handle process; - DMN_Handle thread; - DMN_Handle module; - Architecture arch; - U64 address; - U64 size; - String8 string; - U32 code; // code gives pid & tid on CreateProcess and CreateThread (respectfully) - U32 flags; - S32 signo; - S32 sigcode; - U64 instruction_pointer; - U64 stack_pointer; - U64 user_data; - B32 exception_repeated; -}; - -typedef struct DMN_EventNode DMN_EventNode; -struct DMN_EventNode -{ - DMN_EventNode *next; - DMN_Event v; -}; - -typedef struct DMN_EventList DMN_EventList; -struct DMN_EventList -{ - DMN_EventNode *first; - DMN_EventNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Run Control Types - -typedef struct DMN_Trap DMN_Trap; -struct DMN_Trap -{ - DMN_Handle process; - U64 vaddr; - U64 id; -}; - -typedef struct DMN_TrapChunkNode DMN_TrapChunkNode; -struct DMN_TrapChunkNode -{ - DMN_TrapChunkNode *next; - DMN_Trap *v; - U64 cap; - U64 count; -}; - -typedef struct DMN_TrapChunkList DMN_TrapChunkList; -struct DMN_TrapChunkList -{ - DMN_TrapChunkNode *first; - DMN_TrapChunkNode *last; - U64 node_count; - U64 trap_count; -}; - -typedef struct DMN_RunCtrls DMN_RunCtrls; -struct DMN_RunCtrls -{ - DMN_Handle single_step_thread; - B8 ignore_previous_exception; - B8 run_entities_are_unfrozen; - B8 run_entities_are_processes; - DMN_Handle *run_entities; - U64 run_entity_count; - DMN_TrapChunkList traps; -}; - -//////////////////////////////// -//~ rjf: System Process Listing Types - -typedef struct DMN_ProcessIter DMN_ProcessIter; -struct DMN_ProcessIter -{ - U64 v[2]; -}; - -typedef struct DMN_ProcessInfo DMN_ProcessInfo; -struct DMN_ProcessInfo -{ - String8 name; - U32 pid; -}; - -//////////////////////////////// -//~ rjf: Basic Type Functions (Helpers, Implemented Once) - -//- rjf: handles -internal DMN_Handle dmn_handle_zero(void); -internal B32 dmn_handle_match(DMN_Handle a, DMN_Handle b); - -//- rjf: trap chunk lists -internal void dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap); -internal void dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push); -internal void dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push); - -//- rjf: handle lists -internal void dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle); -internal DMN_HandleArray dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list); -internal DMN_HandleArray dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src); - -//- rjf: event list building -internal DMN_Event *dmn_event_list_push(Arena *arena, DMN_EventList *list); - -//////////////////////////////// -//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once) - -internal U64 dmn_rip_from_thread(DMN_Handle thread); -internal U64 dmn_rsp_from_thread(DMN_Handle thread); - -//////////////////////////////// -//~ rjf: @dmn_os_hooks Main Layer Initialization (Implemented Per-OS) - -internal void dmn_init(void); - -//////////////////////////////// -//~ rjf: @dmn_os_hooks Blocking Control Thread Operations (Implemented Per-OS) - -internal DMN_CtrlCtx *dmn_ctrl_begin(void); -internal void dmn_ctrl_exclusive_access_begin(void); -internal void dmn_ctrl_exclusive_access_end(void); -#define DMN_CtrlExclusiveAccessScope DeferLoop(dmn_ctrl_exclusive_access_begin(), dmn_ctrl_exclusive_access_end()) -internal U32 dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_LaunchOptions *options); -internal B32 dmn_ctrl_attach(DMN_CtrlCtx *ctx, U32 pid); -internal B32 dmn_ctrl_kill(DMN_CtrlCtx *ctx, DMN_Handle process, U32 exit_code); -internal B32 dmn_ctrl_detach(DMN_CtrlCtx *ctx, DMN_Handle process); -internal DMN_EventList dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls); - -//////////////////////////////// -//~ rjf: @dmn_os_hooks Halting (Implemented Per-OS) - -internal void dmn_halt(U64 code, U64 user_data); - -//////////////////////////////// -//~ rjf: @dmn_os_hooks Introspection Functions (Implemented Per-OS) - -//- rjf: run/memory/register counters -internal U64 dmn_run_gen(void); -internal U64 dmn_mem_gen(void); -internal U64 dmn_reg_gen(void); - -//- rjf: non-blocking-control-thread access barriers -internal B32 dmn_access_open(void); -internal void dmn_access_close(void); -#define DMN_AccessScope DeferLoopChecked(dmn_access_open(), dmn_access_close()) - -//- rjf: processes -internal U64 dmn_process_read(DMN_Handle process, Rng1U64 range, void *dst); -internal B32 dmn_process_write(DMN_Handle process, Rng1U64 range, void *src); -#define dmn_process_read_struct(process, vaddr, ptr) dmn_process_read((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) -#define dmn_process_write_struct(process, vaddr, ptr) dmn_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) - -//- rjf: threads -internal Architecture 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); -internal B32 dmn_thread_write_reg_block(DMN_Handle handle, void *reg_block); - -//- rjf: system process listing -internal void dmn_process_iter_begin(DMN_ProcessIter *iter); -internal B32 dmn_process_iter_next(Arena *arena, DMN_ProcessIter *iter, DMN_ProcessInfo *info_out); -internal void dmn_process_iter_end(DMN_ProcessIter *iter); - -#endif // DEMON2_CORE_H diff --git a/src/demon2/demon2_inc.c b/src/demon2/demon2_inc.c deleted file mode 100644 index f6bb4837..00000000 --- a/src/demon2/demon2_inc.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#include "demon2_core.c" - -#if OS_WINDOWS -# include "win32/demon2_core_win32.c" -#else -# error Demon layer backend not defined for this operating system. -#endif diff --git a/src/demon2/demon2_inc.h b/src/demon2/demon2_inc.h deleted file mode 100644 index e724c098..00000000 --- a/src/demon2/demon2_inc.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DEMON2_INC_H -#define DEMON2_INC_H - -#include "demon2_core.h" - -#if OS_WINDOWS -# include "win32/demon2_core_win32.h" -#else -# error Demon layer backend not defined for this operating system. -#endif - -#endif // DEMON2_INC_H diff --git a/src/raddbg/raddbg.h b/src/raddbg/raddbg.h index eb035e73..9e4442c2 100644 --- a/src/raddbg/raddbg.h +++ b/src/raddbg/raddbg.h @@ -2,11 +2,11 @@ // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// -//~ rjf: Demon2 Pass Tasks +//~ rjf: Demon Pass Tasks // // [ ] TLS eval -> in-process-memory EXE info // [ ] unwinding -> in-process-memory EXE info -// [x] solidify synchronization mechanisms for usage of demon2 layer +// [x] solidify synchronization mechanisms for usage of demon layer // [x] TLS eval correctness // [x] freezing thread while running -> soft-halt diff --git a/src/raddbg/raddbg_main.cpp b/src/raddbg/raddbg_main.cpp index 4fc4e296..888aea56 100644 --- a/src/raddbg/raddbg_main.cpp +++ b/src/raddbg/raddbg_main.cpp @@ -43,7 +43,7 @@ #include "regs/raddbgi/regs_raddbgi.h" #include "type_graph/type_graph.h" #include "dbgi/dbgi.h" -#include "demon2/demon2_inc.h" +#include "demon/demon_inc.h" #include "eval/eval_inc.h" #include "unwind/unwind.h" #include "ctrl/ctrl_inc.h" @@ -81,7 +81,7 @@ #include "regs/raddbgi/regs_raddbgi.c" #include "type_graph/type_graph.c" #include "dbgi/dbgi.c" -#include "demon2/demon2_inc.c" +#include "demon/demon_inc.c" #include "eval/eval_inc.c" #include "unwind/unwind.c" #include "ctrl/ctrl_inc.c" diff --git a/src/scratch/ryan_scratch.c b/src/scratch/ryan_scratch.c index d21ad724..6616ce90 100644 --- a/src/scratch/ryan_scratch.c +++ b/src/scratch/ryan_scratch.c @@ -43,7 +43,7 @@ #include "regs/raddbgi/regs_raddbgi.h" #include "type_graph/type_graph.h" #include "dbgi/dbgi.h" -#include "demon2/demon2_inc.h" +#include "demon/demon_inc.h" #include "eval/eval_inc.h" #include "unwind/unwind.h" #include "ctrl/ctrl_inc.h" @@ -71,7 +71,7 @@ #include "regs/raddbgi/regs_raddbgi.c" #include "type_graph/type_graph.c" #include "dbgi/dbgi.c" -#include "demon2/demon2_inc.c" +#include "demon/demon_inc.c" #include "eval/eval_inc.c" #include "unwind/unwind.c" #include "ctrl/ctrl_inc.c"