diff --git a/project.4coder b/project.4coder index 2d3c8b01..db6f3af3 100644 --- a/project.4coder +++ b/project.4coder @@ -46,7 +46,10 @@ load_paths = commands = { //- rjf: [raddbg] - .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, + // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, + + //- rjf: [raddbg wsl] + .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, //- rjf: [scratch] .f2 = { .win = "raddbg_stable --ipc kill_all && build radbin && pushd build && radbin.exe mule_main.rdi --out:a.dump && popd", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, @@ -61,7 +64,8 @@ commands = // .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, //- rjf: running target - .f3 = { .win = "raddbg_stable --ipc run", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, + // .f3 = { .win = "raddbg_stable --ipc run", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, + .f3 = { .win = "wsl ./build/raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f3 = { .win = "pushd build && raddbg --user:dev.raddbg_user && popd", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f3 = { .win = "C:/devel/raddebugger/build/raddbg.exe --capture --user:C:/devel/raddebugger/build/local_dev.raddbg_user --project:C:/devel/raddebugger/build/local_dev.raddbg_project", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f3 = { .win = "wsl_launch /mnt/c/devel/raddebugger/build/raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, diff --git a/src/base/base_strings.c b/src/base/base_strings.c index 030f993b..5088f164 100644 --- a/src/base/base_strings.c +++ b/src/base/base_strings.c @@ -480,28 +480,30 @@ str8_skip_chop_slashes(String8 string) //~ rjf: String Formatting & Copying internal String8 -push_str8_cat(Arena *arena, String8 s1, String8 s2){ +str8_cat(Arena *arena, String8 s1, String8 s2) +{ String8 str; str.size = s1.size + s2.size; str.str = push_array_no_zero(arena, U8, str.size + 1); MemoryCopy(str.str, s1.str, s1.size); MemoryCopy(str.str + s1.size, s2.str, s2.size); str.str[str.size] = 0; - return(str); + return str; } internal String8 -push_str8_copy(Arena *arena, String8 s){ +str8_copy(Arena *arena, String8 s) +{ String8 str; str.size = s.size; str.str = push_array_no_zero(arena, U8, str.size + 1); MemoryCopy(str.str, s.str, s.size); str.str[str.size] = 0; - return(str); + return str; } internal String8 -push_str8fv(Arena *arena, char *fmt, va_list args){ +str8fv(Arena *arena, char *fmt, va_list args){ va_list args2; va_copy(args2, args); U32 needed_bytes = raddbg_vsnprintf(0, 0, fmt, args) + 1; @@ -510,16 +512,17 @@ push_str8fv(Arena *arena, char *fmt, va_list args){ result.size = raddbg_vsnprintf((char*)result.str, needed_bytes, fmt, args2); result.str[result.size] = 0; va_end(args2); - return(result); + return result; } internal String8 -push_str8f(Arena *arena, char *fmt, ...){ +str8f(Arena *arena, char *fmt, ...) +{ va_list args; va_start(args, fmt); String8 result = push_str8fv(arena, fmt, args); va_end(args); - return(result); + return result; } internal String8 @@ -637,14 +640,14 @@ try_u64_from_str8_c_rules(String8 string, U64 *x) { radix = 10, prefix_size = 0; } - + String8 integer = str8_skip(string, prefix_size); B32 is_integer = str8_is_integer(integer, radix); if(is_integer) { *x = u64_from_str8(integer, radix); } - + return is_integer; } diff --git a/src/base/base_strings.h b/src/base/base_strings.h index 14e71c74..1c3a5d63 100644 --- a/src/base/base_strings.h +++ b/src/base/base_strings.h @@ -228,11 +228,16 @@ internal String8 str8_skip_chop_slashes(String8 string); //////////////////////////////// //~ rjf: String Formatting & Copying -internal String8 push_str8_cat(Arena *arena, String8 s1, String8 s2); -internal String8 push_str8_copy(Arena *arena, String8 s); -internal String8 push_str8fv(Arena *arena, char *fmt, va_list args); -internal String8 push_str8f(Arena *arena, char *fmt, ...); -internal String8 push_cstr(Arena *arena, String8 str); +internal String8 str8_cat(Arena *arena, String8 s1, String8 s2); +internal String8 str8_copy(Arena *arena, String8 s); +internal String8 str8fv(Arena *arena, char *fmt, va_list args); +internal String8 str8f(Arena *arena, char *fmt, ...); +// TODO(rjf): remove these once we're ready to convert all usages: +#define push_str8_cat(arena, s1, s2) str8_cat((arena), (s1), (s2)) +#define push_str8_copy(arena, s) str8_copy((arena), (s)) +#define push_str8fv(arena, fmt, args) str8fv((arena), (fmt), (args)) +#define push_str8f(arena, ...) str8f((arena), __VA_ARGS__) +internal String8 push_cstr(Arena *arena, String8 str); // TODO(rjf): this is unnecessary - this is implied by `push_str8_copy`. need to remove. //////////////////////////////// //~ rjf: String <=> Integer Conversions diff --git a/src/demon/linux/demon_core_linux.c b/src/demon/linux/demon_core_linux.c index d8433013..858355be 100644 --- a/src/demon/linux/demon_core_linux.c +++ b/src/demon/linux/demon_core_linux.c @@ -1,12 +1,484 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) +//////////////////////////////// +//~ rjf: Helpers + +//- rjf: file descriptor memory reading/writing helpers + +internal U64 +dmn_lnx_read(int memory_fd, Rng1U64 range, void *dst) +{ + U64 bytes_read = 0; + U8 *ptr = (U8 *)dst; + U8 *opl = ptr + dim_1u64(range); + U64 cursor = range.min; + 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 +dmn_lnx_write(int memory_fd, Rng1U64 range, void *src) +{ + B32 result = 1; + U8 *ptr = (U8 *)src; + U8 *opl = ptr + dim_1u64(range); + U64 cursor = range.min; + 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 = 0; + break; + } + ptr += actual_write; + cursor += actual_write; + } + return result; +} + +//- rjf: pid => info extraction + +internal String8 +dmn_lnx_exe_path_from_pid(Arena *arena, pid_t pid) +{ + Temp scratch = scratch_begin(&arena, 1); + + //- rjf: get exe link path + String8 exe_link_path = str8f(scratch.arena, "/proc/%d/exe", pid); + + //- rjf: read the link + Temp restore_point = temp_begin(arena); + B32 good = 0; + U8 *buffer = 0; + int readlink_result = 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); + readlink_result = readlink((char *)exe_link_path.str, (char *)buffer, cap); + if(readlink_result < cap) + { + good = 1; + break; + } + } + + //- rjf: package result + String8 result = {0}; + if(!good || readlink_result == -1) + { + temp_end(restore_point); + } + else + { + arena_pop(arena, (cap - readlink_result - 1)); + result = str8(buffer, readlink_result + 1); + } + + scratch_end(scratch); + return result; +} + +internal Arch +dmn_lnx_arch_from_pid(pid_t pid) +{ + Arch result = Arch_Null; + { + Temp scratch = scratch_begin(0, 0); + String8 exe_path = dmn_lnx_exe_path_from_pid(scratch.arena, pid); + + // rjf: unpack exe handle + int exe_fd = -1; + if(exe_path.size != 0) + { + exe_fd = open((char*)exe_path.str, O_RDONLY); + } + + // rjf: unpack elf identifier + U8 e_ident[ELF_Identifier_Max] = {0}; + B32 is_elf = 0; + U8 elf_class = 0; + if(exe_fd >= 0 && + pread(exe_fd, e_ident, sizeof(e_ident), 0) == sizeof(e_ident)) + { + is_elf = (e_ident[ELF_Identifier_Mag0] == 0x7f && + e_ident[ELF_Identifier_Mag1] == 'E' && + e_ident[ELF_Identifier_Mag2] == 'L' && + e_ident[ELF_Identifier_Mag3] == 'F'); + elf_class = e_ident[ELF_Identifier_Class]; + } + + // rjf: read elf header + ELF_Hdr64 hdr = {0}; + switch(elf_class) + { + case 1: + { + ELF_Hdr32 hdr32 = {0}; + if(pread(exe_fd, &hdr32, sizeof(hdr32), 0) == sizeof(hdr32)) + { + hdr = elf_hdr64_from_hdr32(hdr32); + } + }break; + case 2: + { + pread(exe_fd, &hdr, sizeof(hdr), 0); + }break; + } + + // rjf: determine arch from elf machine kind + result = arch_from_elf_machine(hdr.e_machine); + + scratch_end(scratch); + } + return result; +} + +internal DMN_LNX_ProcessAux +dmn_lnx_aux_from_pid(pid_t pid, Arch arch) +{ + Temp scratch = scratch_begin(0, 0); + DMN_LNX_ProcessAux result = {0}; + + // rjf: open aux data + String8 auxv_path = push_str8f(scratch.arena, "/proc/%d/auxv", pid); + int aux_fd = open((char*)auxv_path.str, O_RDONLY); + + // rjf: scan aux data + if(aux_fd >= 0) + { + B32 addr_32bit = (arch == Arch_x86 || arch == Arch_arm32); + for(;;) + { + result.filled = 1; + + // rjf: read next aux + U64 type = 0; + U64 val = 0; + if(addr_32bit) + { + ELF_Auxv32 aux = {0}; + if(read(aux_fd, &aux, sizeof(aux)) != sizeof(aux)) + { + goto brkloop; + } + type = aux.a_type; + val = aux.a_val; + } + else + { + ELF_Auxv64 aux = {0}; + if(read(aux_fd, &aux, sizeof(aux)) != sizeof(aux)) + { + goto brkloop; + } + type = aux.a_type; + val = aux.a_val; + } + + // rjf: fill result + switch(type) + { + default:{}break; + case ELF_AuxType_Null: goto brkloop; break; + case ELF_AuxType_Phnum: result.phnum = val; break; + case ELF_AuxType_Phent: result.phent = val; break; + case ELF_AuxType_Phdr: result.phdr = val; break; + case ELF_AuxType_ExecFn: result.execfn = val; break; + } + } + brkloop:; + close(aux_fd); + } + + scratch_end(scratch); + return result; +} + +//- rjf: phdr info extraction + +internal DMN_LNX_PhdrInfo +dmn_lnx_phdr_info_from_memory(int memory_fd, B32 is_32bit, U64 phvaddr, U64 phsize, U64 phcount) +{ + DMN_LNX_PhdrInfo result = {0}; + + // rjf: determine how much phdr we'll read + U64 phdr_size_expected = (is_32bit ? sizeof(ELF_Phdr32) : sizeof(ELF_Phdr64)); + U64 phdr_stride = (phsize ? phsize : phdr_size_expected); + U64 phdr_read_size = ClampTop(phsize, phdr_size_expected); + + // rjf: scan table + U64 va = phvaddr; + for(U64 i = 0; i < phcount; i += 1, va += phdr_stride) + { + // rjf: read type and range + ELF_PType p_type = 0; + U64 p_vaddr = 0; + U64 p_memsz = 0; + if(is_32bit) + { + ELF_Phdr32 phdr32 = {0}; + dmn_lnx_read_struct(memory_fd, va, &phdr32); + p_type = phdr32.p_type; + p_vaddr = phdr32.p_vaddr; + p_memsz = phdr32.p_memsz; + } + else + { + ELF_Phdr64 phdr64 = {0}; + dmn_lnx_read_struct(memory_fd, va, &phdr64); + p_type = phdr64.p_type; + p_vaddr = phdr64.p_vaddr; + p_memsz = phdr64.p_memsz; + } + + // rjf: save + switch(p_type) + { + case ELF_PType_Dynamic: + { + result.dynamic = p_vaddr; + }break; + case ELF_PType_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; +} + +//- rjf: process entity => info extraction + +internal DMN_LNX_ModuleInfoList +dmn_lnx_module_info_list_from_process(Arena *arena, DMN_LNX_Entity *process) +{ + Arch arch = process->arch; + B32 is_32bit = (arch == Arch_x86 || arch == Arch_arm32); + int memory_fd = (int)process->fd; + + //- rjf: pid => aux + DMN_LNX_ProcessAux aux = dmn_lnx_aux_from_pid((pid_t)process->id, arch); + + //- rjf: memory => phdr info + DMN_LNX_PhdrInfo phdr_info = dmn_lnx_phdr_info_from_memory(memory_fd, is_32bit, + aux.phdr, aux.phent, aux.phnum); + + //- rjf: memory space & vaddr => linkmap first + U64 first_linkmap_vaddr = 0; + if(phdr_info.dynamic != 0) + { + U64 off = phdr_info.dynamic; + for(;;) + { + // rjf: read next dyn entry + ELF_Dyn64 dyn = {0}; + if(is_32bit) + { + ELF_Dyn32 dyn32 = {0}; + dmn_lnx_read_struct(memory_fd, off, &dyn32); + dyn.tag = dyn32.tag; + dyn.val = dyn32.val; + off += sizeof(dyn32); + } + else + { + dmn_lnx_read_struct(memory_fd, off, &dyn); + off += sizeof(dyn); + } + + // rjf: break on zero + if(dyn.tag == ELF_DynTag_Null) + { + break; + } + + // rjf: pltgot => grab first linkmap address + if(dyn.tag == ELF_DynTag_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}; + dmn_lnx_read(memory_fd, r1u64(vas_off, vas_off+sizeof(vas)), vas); + first_linkmap_vaddr = vas[1]; + break; + } + } + } + + //- rjf: push main module + DMN_LNX_ModuleInfoList list = {0}; + { + DMN_LNX_ModuleInfoNode *n = push_array(arena, DMN_LNX_ModuleInfoNode, 1); + SLLQueuePush(list.first, list.last, n); + list.count += 1; + n->v.vaddr_range = phdr_info.range; + n->v.name = aux.execfn; + } + + //- rjf: iterate link maps + if(first_linkmap_vaddr != 0) + { + U64 linkmap_vaddr = first_linkmap_vaddr; + for(;linkmap_vaddr != 0;) + { + // rjf: read next linkmap entry + ELF_LinkMap64 linkmap = {0}; + if(is_32bit) + { + // TODO(rjf): endianness + ELF_LinkMap32 linkmap32 = {0}; + dmn_lnx_read_struct(memory_fd, linkmap_vaddr, &linkmap32); + linkmap.base = linkmap32.base; + linkmap.name = linkmap32.name; + linkmap.ld = linkmap32.ld; + linkmap.next = linkmap32.next; + } + else + { + dmn_lnx_read_struct(memory_fd, linkmap_vaddr, &linkmap); + } + + // rjf: push module for next link map + if(linkmap.base != 0) + { + // rjf: find phdr info for this module + U64 phvaddr = 0; + U64 phentsize = 0; + U64 phcount = 0; + if(is_32bit) + { + ELF_Hdr32 ehdr = {0}; + dmn_lnx_read_struct(memory_fd, linkmap.base, &ehdr); + phvaddr = ehdr.e_phoff + linkmap.base; + phentsize = ehdr.e_phentsize; + phcount = ehdr.e_phnum; + } + else + { + ELF_Hdr64 ehdr = {0}; + dmn_lnx_read_struct(memory_fd, linkmap.base, &ehdr); + phvaddr = ehdr.e_phoff + linkmap.base; + phentsize = ehdr.e_phentsize; + phcount = ehdr.e_phnum; + } + + // rjf: extract info from module's phdrs + DMN_LNX_PhdrInfo module_phdr_info = dmn_lnx_phdr_info_from_memory(memory_fd, is_32bit, phvaddr, phentsize, phcount); + + // rjf: push + DMN_LNX_ModuleInfoNode *n = push_array(arena, DMN_LNX_ModuleInfoNode, 1); + SLLQueuePush(list.first, list.last, n); + list.count += 1; + n->v.vaddr_range = r1u64(linkmap.base, linkmap.base + dim_1u64(module_phdr_info.range)); + n->v.name = linkmap.name; + } + + // rjf: iterate + linkmap_vaddr = linkmap.next; + } + } + + return list; +} + +//////////////////////////////// +//~ rjf: Entity Functions + +internal DMN_LNX_Entity * +dmn_lnx_entity_alloc(DMN_LNX_Entity *parent, DMN_LNX_EntityKind kind) +{ + DMN_LNX_Entity *entity = dmn_lnx_state->free_entity; + if(entity != 0) + { + SLLStackPop(dmn_lnx_state->free_entity); + } + else + { + entity = push_array(dmn_lnx_state->entities_arena, DMN_LNX_Entity, 1); + dmn_lnx_state->entities_count += 1; + } + U32 gen = entity->gen; + MemoryCopyStruct(entity, &dmn_lnx_nil_entity); + entity->gen += 1; + if(parent != &dmn_lnx_nil_entity) + { + DLLPushBack_NPZ(&dmn_lnx_nil_entity, parent->first, parent->last, entity, next, prev); + entity->parent = parent; + } + entity->kind = kind; + return entity; +} + +internal void +dmn_lnx_entity_release(DMN_LNX_Entity *entity) +{ + SLLStackPush(dmn_lnx_state->free_entity, entity); +} + +internal DMN_Handle +dmn_lnx_handle_from_entity(DMN_LNX_Entity *entity) +{ + DMN_Handle handle = {0}; + U64 index = (U64)(entity - dmn_lnx_state->entities_base); + if(index <= 0xffffffffu) + { + handle.u32[0] = index; + handle.u32[1] = entity->gen; + } + return handle; +} + +internal DMN_LNX_Entity * +dmn_lnx_entity_from_handle(DMN_Handle handle) +{ + DMN_LNX_Entity *result = &dmn_lnx_nil_entity; + U64 index = (U64)handle.u32[0]; + if(index < dmn_lnx_state->entities_count && + dmn_lnx_state->entities_base[index].gen == handle.u32[1]) + { + result = &dmn_lnx_state->entities_base[index]; + } + return result; +} + //////////////////////////////// //~ rjf: @dmn_os_hooks Main Layer Initialization (Implemented Per-OS) internal void dmn_init(void) { + Arena *arena = arena_alloc(); + dmn_lnx_state = push_array(arena, DMN_LNX_State, 1); + dmn_lnx_state->arena = arena; + dmn_lnx_state->deferred_events_arena = arena_alloc(); + dmn_lnx_state->entities_arena = arena_alloc(.reserve_size = GB(32), .commit_size = KB(64), .flags = ArenaFlag_NoChain); + dmn_lnx_state->entities_base = push_array(dmn_lnx_state->entities_arena, DMN_LNX_Entity, 0); + dmn_lnx_state->root_entity = dmn_lnx_entity_alloc(&dmn_lnx_nil_entity, DMN_LNX_EntityKind_Root); } //////////////////////////////// @@ -32,6 +504,165 @@ dmn_ctrl_exclusive_access_end(void) internal U32 dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_ProcessLaunchParams *params) { + Temp scratch = scratch_begin(0, 0); + + //- rjf: unpack command line + char **argv = 0; + int argc = 0; + { + argc = (int)(params->cmd_line.node_count); + argv = push_array(scratch.arena, char *, argc+1); + { + U64 idx = 0; + for(String8Node *n = params->cmd_line.first; n != 0; n = n->next, idx += 1) + { + argv[idx] = (char *)push_str8_copy(scratch.arena, n->string).str; + } + } + } + + //- rjf: unpack path + char *path = (char *)push_str8_copy(scratch.arena, params->path).str; + + //- rjf: unpack environment + char **env = 0; + { + env = push_array(scratch.arena, char *, params->env.node_count+1); + { + U64 idx = 0; + for(String8Node *n = params->env.first; n != 0; n = n->next, idx += 1) + { + env[idx] = (char *)push_str8_copy(scratch.arena, n->string).str; + } + } + } + + //- rjf: create & set up new process + if(argv != 0 && argv[0] != 0) + { + pid_t pid = 0; + int ptrace_result = 0; + int chdir_result = 0; + int setoptions_result = 0; + B32 error__need_child_kill = 0; + + //- rjf: fork + pid = fork(); + if(pid == -1) { goto error; } + + //- rjf: child process -> execute actual target + if(pid == 0) + { + ptrace_result = ptrace(PTRACE_TRACEME, 0, 0, 0); + if(ptrace_result == -1) { goto error; } + chdir_result = chdir(path); + if(chdir_result == -1) { goto error; } + execve(argv[0], argv, env); + abort(); + } + + //- rjf: parent process + if(pid != 0) + { + //- rjf: wait for child + int status = 0; + pid_t wait_id = waitpid(pid, &status, __WALL); + if(wait_id != pid) + { + // NOTE(rjf): we do not know what this means - needs study if this actually arises. + goto error; + } + + //- rjf: determine child launch status + typedef enum LaunchStatus + { + LaunchStatus_Null, + LaunchStatus_FailBeforePtrace, + LaunchStatus_FailAfterPtrace, + LaunchStatus_Success, + } + LaunchStatus; + LaunchStatus launch_status = LaunchStatus_Null; + { + B32 wifstopped = WIFSTOPPED(status); + int wstopsig = WSTOPSIG(status); + if(0){} + else if(wifstopped && wstopsig == SIGTRAP) { launch_status = LaunchStatus_Success; } + else if(wifstopped && wstopsig != SIGTRAP) { launch_status = LaunchStatus_FailAfterPtrace; } + else { launch_status = LaunchStatus_FailBeforePtrace; } + } + + //- rjf: respond to launch status appropriately + switch(launch_status) + { + //- rjf: no understood handling path + default:{}break; + + //- rjf: failure, after ptrace => we need to explicitly obtain the + // result code & exit the process, otherwise it will become a zombie, + // since it is ptrace'd. + case LaunchStatus_FailAfterPtrace: + { + B32 cleanup_good = 0; + 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 = 1; + } + } + if(cleanup_good) + { + // TODO(rjf): child initialization failed, but we at least cleaned it up. + } + else + { + // TODO(rjf): child initialization failed, *and* we couldn't clean it up, so we've created + // yet-another zombie. + } + }break; + + //- rjf: successful launch + case LaunchStatus_Success: + { + setoptions_result = ptrace(PTRACE_SETOPTIONS, pid, 0, PtrFromInt(DMN_LNX_PTRACE_OPTIONS)); + if(setoptions_result == -1) { error__need_child_kill = 1; goto error; } + + //- rjf: build initial process/thread/modules entities + DMN_LNX_Entity *process = &dmn_lnx_nil_entity; + { + // rjf: build process + process = dmn_lnx_entity_alloc(dmn_lnx_state->root_entity, DMN_LNX_EntityKind_Process); + + // rjf: build thread + { + DMN_LNX_Entity *thread = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Thread); + } + + + } + }break; + } + } + + //- rjf: error case + goto success; + error:; + { + if(error__need_child_kill) + { + // TODO(rjf) + } + } + + //- rjf: success + success:; + } + + scratch_end(scratch); return 0; } diff --git a/src/demon/linux/demon_core_linux.h b/src/demon/linux/demon_core_linux.h index e11072f7..96128143 100644 --- a/src/demon/linux/demon_core_linux.h +++ b/src/demon/linux/demon_core_linux.h @@ -4,4 +4,127 @@ #ifndef DEMON_CORE_LINUX_H #define DEMON_CORE_LINUX_H +#include +#include +#include +#include +#include +#include +#include + +#define DMN_LNX_PTRACE_OPTIONS (PTRACE_O_TRACEEXIT|\ +PTRACE_O_EXITKILL|\ +PTRACE_O_TRACEFORK|\ +PTRACE_O_TRACEVFORK|\ +PTRACE_O_TRACECLONE) + +typedef struct DMN_LNX_ProcessAux DMN_LNX_ProcessAux; +struct DMN_LNX_ProcessAux +{ + B32 filled; + U64 phnum; + U64 phent; + U64 phdr; + U64 execfn; +}; + +typedef struct DMN_LNX_PhdrInfo DMN_LNX_PhdrInfo; +struct DMN_LNX_PhdrInfo +{ + Rng1U64 range; + U64 dynamic; +}; + +typedef struct DMN_LNX_ModuleInfo DMN_LNX_ModuleInfo; +struct DMN_LNX_ModuleInfo +{ + Rng1U64 vaddr_range; + U64 name; +}; + +typedef struct DMN_LNX_ModuleInfoNode DMN_LNX_ModuleInfoNode; +struct DMN_LNX_ModuleInfoNode +{ + DMN_LNX_ModuleInfoNode *next; + DMN_LNX_ModuleInfo v; +}; + +typedef struct DMN_LNX_ModuleInfoList DMN_LNX_ModuleInfoList; +struct DMN_LNX_ModuleInfoList +{ + DMN_LNX_ModuleInfoNode *first; + DMN_LNX_ModuleInfoNode *last; + U64 count; +}; + +typedef enum DMN_LNX_EntityKind +{ + DMN_LNX_EntityKind_Null, + DMN_LNX_EntityKind_Root, + DMN_LNX_EntityKind_Process, + DMN_LNX_EntityKind_Thread, + DMN_LNX_EntityKind_Module, + DMN_LNX_EntityKind_COUNT +} +DMN_LNX_EntityKind; + +typedef struct DMN_LNX_Entity DMN_LNX_Entity; +struct DMN_LNX_Entity +{ + DMN_LNX_Entity *first; + DMN_LNX_Entity *last; + DMN_LNX_Entity *next; + DMN_LNX_Entity *prev; + DMN_LNX_Entity *parent; + DMN_LNX_EntityKind kind; + U32 gen; + Arch arch; + U64 id; + int fd; +}; + +typedef struct DMN_LNX_State DMN_LNX_State; +struct DMN_LNX_State +{ + Arena *arena; + Arena *deferred_events_arena; + DMN_EventList deferred_events; + Arena *entities_arena; + DMN_LNX_Entity *entities_base; + U64 entities_count; + DMN_LNX_Entity *root_entity; + DMN_LNX_Entity *free_entity; +}; + +read_only global DMN_LNX_Entity dmn_lnx_nil_entity = {&dmn_lnx_nil_entity, &dmn_lnx_nil_entity, &dmn_lnx_nil_entity, &dmn_lnx_nil_entity, &dmn_lnx_nil_entity}; +global DMN_LNX_State *dmn_lnx_state = 0; + +//////////////////////////////// +//~ rjf: Helpers + +//- rjf: file descriptor memory reading/writing helpers +internal U64 dmn_lnx_read(int memory_fd, Rng1U64 range, void *dst); +internal B32 dmn_lnx_write(int memory_fd, Rng1U64 range, void *src); +#define dmn_lnx_read_struct(fd, vaddr, ptr) dmn_lnx_read((fd), r1u64((vaddr), (vaddr)+sizeof(*(ptr))), (ptr)) +#define dmn_lnx_write_struct(fd, vaddr, ptr) dmn_lnx_write((fd), r1u64((vaddr), (vaddr)+sizeof(*(ptr))), (ptr)) + +//- rjf: pid => info extraction +internal String8 dmn_lnx_exe_path_from_pid(Arena *arena, pid_t pid); +internal Arch dmn_lnx_arch_from_pid(pid_t pid); +internal DMN_LNX_ProcessAux dmn_lnx_aux_from_pid(pid_t pid, Arch arch); + +//- rjf: phdr info extraction +internal DMN_LNX_PhdrInfo dmn_lnx_phdr_info_from_memory(int memory_fd, B32 is_32bit, U64 phvaddr, U64 phsize, U64 phcount); + +//- rjf: process entity => info extraction +internal DMN_LNX_ModuleInfoList dmn_lnx_module_info_list_from_process(Arena *arena, DMN_LNX_Entity *process); + +//////////////////////////////// +//~ rjf: Entity Functions + +internal DMN_LNX_Entity *dmn_lnx_entity_alloc(DMN_LNX_Entity *parent, DMN_LNX_EntityKind kind); +internal void dmn_lnx_entity_release(DMN_LNX_Entity *entity); +internal DMN_Handle dmn_lnx_handle_from_entity(DMN_LNX_Entity *entity); +internal DMN_LNX_Entity *dmn_lnx_entity_from_handle(DMN_Handle handle); + #endif // DEMON_CORE_LINUX_H diff --git a/src/elf/elf.h b/src/elf/elf.h index 791b8351..d5c648b6 100644 --- a/src/elf/elf.h +++ b/src/elf/elf.h @@ -119,10 +119,10 @@ enum ELF_PType_Tls = 7, ELF_PType_LoOs = 0x60000000, ELF_PType_HiOs = 0x6fffffff, - + ELF_PType_LowProc = 0x70000000, ELF_PType_HighProc = 0x7fffffff, - + // specific to Sun ELF_PType_LowSunW = 0x6ffffffa, ELF_PType_SunWBSS = 0x6ffffffb, @@ -169,7 +169,7 @@ enum ELF_SectionCode_SUNW_verdef = 0x6ffffffd, ELF_SectionCode_SUNW_verneed = 0x6ffffffe, // Versions defined by file ELF_SectionCode_SUNW_versym = 0x6fffffff, // Versions needed by file - + // Symbol versions ELF_SectionCode_GNU_verdef = ELF_SectionCode_SUNW_verdef, ELF_SectionCode_GNU_verneed = ELF_SectionCode_SUNW_verneed, @@ -185,22 +185,22 @@ enum ELF_SectionIndex_Undef = 0, // Symbol with section index is undefined and must be resolved by the link editor ELF_SectionIndex_Abs = 0xfff1, // Symbol has absolute value and wont change after relocations ELF_SectionIndex_Common = 0xfff2, // This symbol indicates to linker to allocate the storage at address multiple of st_value - + ELF_SectionIndex_LoReserve = 0xff00, ELF_SectionIndex_HiReserve = 0xffff, - + // Processor specific ELF_SectionIndex_LoProc = ELF_SectionIndex_LoReserve, ELF_SectionIndex_HiProc = 0xff1f, - + // Reserved for OS ELF_SectionIndex_LoOs = 0xff20, ELF_SectionIndex_HiOs = 0xff3f, - + ELF_SectionIndex_IA64_ASNI_Common = ELF_SectionIndex_LoProc, ELF_SectionIndex_X8664_LCommon = 0xff02, ELF_SectionIndex_MIPS_SCommon = 0xff03, - + ELF_SectionIndex_TIC6X_Common = ELF_SectionIndex_LoReserve, ELF_SectionIndex_MIPS_SUndefined = 0xff04, }; @@ -271,7 +271,7 @@ typedef U32 ELF_DynTag; enum { ELF_DynTag_Null = 0, - + ELF_DynTag_Needed = 1, ELF_DynTag_PltRelsz = 2, ELF_DynTag_PltGot = 3, @@ -305,10 +305,10 @@ enum ELF_DynTag_PreInitArray = 32, ELF_DynTag_PreInitArraysz = 33, ELF_DynTag_SymtabShndx = 34, - + ELF_DynTag_LoOs = 0x6000000D, ELF_DynTag_HiOs = 0x6ffff000, - + ELF_DynTag_ValRngLo = 0x6ffffd00, ELF_DynTag_GNU_PreLinked = 0x6ffffdf5, ELF_DynTag_GNU_Conflictsz = 0x6ffffdf6, @@ -322,7 +322,7 @@ enum ELF_DynTag_SymInSz = 0x6ffffdfe, ELF_DynTag_SymInEnt = 0x6ffffdff, ELF_DynTag_ValRngHi = ELF_DynTag_SymInEnt, - + ELF_DynTag_AddrRngLo = 0x6ffffe00, ELF_DynTag_GNU_Hash = 0x6ffffef5, ELF_DynTag_TlsDescPlt = 0x6ffffef6, @@ -336,7 +336,7 @@ enum ELF_DynTag_MoveTab = 0x6ffffefe, ELF_DynTag_SymInfo = 0x6ffffeff, ELF_DynTag_AddrRngHi = ELF_DynTag_SymInfo, - + ELF_DynTag_RelaCount = 0x6ffffff9, ELF_DynTag_RelCount = 0x6ffffffa, ELF_DynTag_Flags_1 = 0x6ffffffb, @@ -951,10 +951,10 @@ enum ELF_CompressTypeEnum ELF_CompressType_None = 0, ELF_CompressType_ZLib = 1, ELF_CompressType_ZStd = 2, - + ELF_CompressType_LoOs = 0x60000000, ELF_CompressType_HiOs = 0x6fffffff, - + ELF_CompressType_LoProc = 0x70000000, ELF_CompressType_HiProc = 0x7fffffff, }; @@ -975,7 +975,7 @@ typedef struct ELF_Chdr64 //////////////////////////////// -internal ELF_Hdr64 elf_hdr64_from_ehdr32(ELF_Hdr32 h32); +internal ELF_Hdr64 elf_hdr64_from_hdr32(ELF_Hdr32 h32); internal ELF_Shdr64 elf_shdr64_from_shdr32(ELF_Shdr32 h32); internal ELF_Phdr64 elf_phdr64_from_phdr32(ELF_Phdr32 h32); internal ELF_Dyn64 elf_dyn64_from_dyn32 (ELF_Dyn32 h32);