mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-12 23:31:38 -07:00
demon/linux: pid extraction, module extraction, most of launching path
This commit is contained in:
+6
-2
@@ -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, },
|
||||
|
||||
+13
-10
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+10
-5
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,4 +4,127 @@
|
||||
#ifndef DEMON_CORE_LINUX_H
|
||||
#define DEMON_CORE_LINUX_H
|
||||
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <elf.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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
|
||||
|
||||
+16
-16
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user