mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-14 17:28:07 +00:00
WIP fork
This commit is contained in:
+17
-12
@@ -1323,7 +1323,7 @@ ctrl_entity_store_apply_events(CTRL_EntityCtxRWStore *store, CTRL_EventList *lis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
thread->stack_base = event->stack_base;
|
thread->stack_base = event->stack_base;
|
||||||
ctrl_rip_from_thread(&store->ctx, event->entity);
|
//ctrl_rip_from_thread(&store->ctx, event->entity);
|
||||||
}break;
|
}break;
|
||||||
case CTRL_EventKind_EndThread:
|
case CTRL_EventKind_EndThread:
|
||||||
{
|
{
|
||||||
@@ -4451,6 +4451,20 @@ ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg,
|
|||||||
dmn_process_write(spoof->process, r1u64(spoof->vaddr, spoof->vaddr+size_of_spoof), &spoof->new_ip_value);
|
dmn_process_write(spoof->process, r1u64(spoof->vaddr, spoof->vaddr+size_of_spoof), &spoof->new_ip_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy spoof info
|
||||||
|
if(do_spoof)
|
||||||
|
{
|
||||||
|
run_ctrls->spoof.process = spoof->process;
|
||||||
|
run_ctrls->spoof.thread = spoof->thread;
|
||||||
|
run_ctrls->spoof.vaddr = spoof->vaddr;
|
||||||
|
run_ctrls->spoof.old_ip = spoof_old_ip_value;
|
||||||
|
run_ctrls->spoof.size = size_of_spoof;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MemoryZeroStruct(&run_ctrls->spoof);
|
||||||
|
}
|
||||||
|
|
||||||
// rjf: run for new events
|
// rjf: run for new events
|
||||||
ProfScope("run for new events")
|
ProfScope("run for new events")
|
||||||
{
|
{
|
||||||
@@ -4474,6 +4488,7 @@ ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DMN_EventList events = dmn_ctrl_run(scratch.arena, ctrl_ctx, run_ctrls);
|
DMN_EventList events = dmn_ctrl_run(scratch.arena, ctrl_ctx, run_ctrls);
|
||||||
ins_atomic_u64_inc_eval(&ctrl_state->mem_gen);
|
ins_atomic_u64_inc_eval(&ctrl_state->mem_gen);
|
||||||
ins_atomic_u64_inc_eval(&ctrl_state->reg_gen);
|
ins_atomic_u64_inc_eval(&ctrl_state->reg_gen);
|
||||||
@@ -5956,17 +5971,7 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg)
|
|||||||
CTRL_Entity *process = ctrl_entity_from_handle(entity_ctx, ctrl_handle_make(CTRL_MachineID_Local, event->process));
|
CTRL_Entity *process = ctrl_entity_from_handle(entity_ctx, ctrl_handle_make(CTRL_MachineID_Local, event->process));
|
||||||
Arch arch = thread->arch;
|
Arch arch = thread->arch;
|
||||||
U64 thread_rip_vaddr = dmn_rip_from_thread(event->thread);
|
U64 thread_rip_vaddr = dmn_rip_from_thread(event->thread);
|
||||||
CTRL_Entity *module = &ctrl_entity_nil;
|
CTRL_Entity *module = ctrl_module_from_process_vaddr(process, thread_rip_vaddr);
|
||||||
{
|
|
||||||
for(CTRL_Entity *m = process->first; m != &ctrl_entity_nil; m = m->next)
|
|
||||||
{
|
|
||||||
if(m->kind == CTRL_EntityKind_Module && contains_1u64(m->vaddr_range, thread_rip_vaddr))
|
|
||||||
{
|
|
||||||
module = m;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
//- rjf: extract module-dependent info
|
//- rjf: extract module-dependent info
|
||||||
|
|||||||
@@ -218,3 +218,37 @@ dmn_get_trap_inst(void)
|
|||||||
return str8_array_fixed(inst);
|
return str8_array_fixed(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal DMN_ActiveTrap *
|
||||||
|
dmn_set_trap(Arena *arena, DMN_Trap *trap)
|
||||||
|
{
|
||||||
|
DMN_ActiveTrap *result = 0;
|
||||||
|
String8 trap_inst = dmn_get_trap_inst();
|
||||||
|
U8 *swap_bytes = push_array(arena, U8, trap_inst.size);
|
||||||
|
if(dmn_process_read(trap->process, r1u64(trap->vaddr, trap->vaddr + trap_inst.size), swap_bytes) == trap_inst.size)
|
||||||
|
{
|
||||||
|
if(dmn_process_write(trap->process, r1u64(trap->vaddr, trap->vaddr + trap_inst.size), trap_inst.str) == trap_inst.size)
|
||||||
|
{
|
||||||
|
result = push_array(arena, DMN_ActiveTrap, 1);
|
||||||
|
result->trap = trap;
|
||||||
|
result->swap_bytes = str8(swap_bytes, trap_inst.size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert(0 && "failed to write trap instruction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert(0 && "failed to read original byte");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
dmn_remove_trap(DMN_ActiveTrap *active_trap)
|
||||||
|
{
|
||||||
|
B32 is_removed = dmn_process_write(active_trap->trap->process, r1u64(active_trap->trap->vaddr, active_trap->trap->vaddr + active_trap->swap_bytes.size), active_trap->swap_bytes.str);
|
||||||
|
Assert(is_removed);
|
||||||
|
return is_removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-3
@@ -165,12 +165,20 @@ struct DMN_RunCtrls
|
|||||||
{
|
{
|
||||||
DMN_Handle priority_thread;
|
DMN_Handle priority_thread;
|
||||||
DMN_Handle single_step_thread;
|
DMN_Handle single_step_thread;
|
||||||
B8 ignore_previous_exception;
|
|
||||||
B8 run_entities_are_unfrozen;
|
|
||||||
B8 run_entities_are_processes;
|
|
||||||
DMN_Handle *run_entities;
|
DMN_Handle *run_entities;
|
||||||
U64 run_entity_count;
|
U64 run_entity_count;
|
||||||
DMN_TrapChunkList traps;
|
DMN_TrapChunkList traps;
|
||||||
|
B8 ignore_previous_exception;
|
||||||
|
B8 run_entities_are_unfrozen;
|
||||||
|
B8 run_entities_are_processes;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
DMN_Handle process;
|
||||||
|
DMN_Handle thread;
|
||||||
|
U64 vaddr;
|
||||||
|
U64 old_ip;
|
||||||
|
U64 size;
|
||||||
|
} spoof;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
@@ -279,4 +287,7 @@ internal void dmn_process_iter_end(DMN_ProcessIter *iter);
|
|||||||
//- arch trap
|
//- arch trap
|
||||||
internal String8 dmn_get_trap_inst(void);
|
internal String8 dmn_get_trap_inst(void);
|
||||||
|
|
||||||
|
internal DMN_ActiveTrap * dmn_set_trap(Arena *arena, DMN_Trap *trap);
|
||||||
|
internal B32 dmn_remove_trap(DMN_ActiveTrap *active_trap);
|
||||||
|
|
||||||
#endif // DEMON_CORE_H
|
#endif // DEMON_CORE_H
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ dmn_lnx_ptrace_seize(pid_t pid)
|
|||||||
AssertAlways(dmn_lnx_state->tracer_tid == gettid());
|
AssertAlways(dmn_lnx_state->tracer_tid == gettid());
|
||||||
|
|
||||||
// TODO: PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE
|
// TODO: PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE
|
||||||
return OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACEEXEC | PTRACE_O_EXITKILL | PTRACE_O_TRACECLONE));
|
return OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACEEXEC | PTRACE_O_EXITKILL | PTRACE_O_TRACEFORK | PTRACE_O_TRACECLONE));
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
@@ -925,6 +925,39 @@ dmn_lnx_process_from_pid(pid_t pid)
|
|||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
dmn_lnx_process_trap_probes(DMN_LNX_Entity *process)
|
||||||
|
{
|
||||||
|
Assert(process->kind == DMN_LNX_EntityKind_Process);
|
||||||
|
for EachIndex(i, DMN_LNX_ProbeType_Count)
|
||||||
|
{
|
||||||
|
if(process->probes[i] == 0) { continue; }
|
||||||
|
|
||||||
|
DMN_Trap *trap = push_array(process->arena, DMN_Trap, 1);
|
||||||
|
trap->process = dmn_lnx_handle_from_entity(process);
|
||||||
|
trap->vaddr = process->probes[i]->pc;
|
||||||
|
trap->id = i;
|
||||||
|
|
||||||
|
DMN_ActiveTrap *active_trap = dmn_set_trap(process->arena, trap);
|
||||||
|
SLLQueuePush(process->first_probe_trap, process->last_probe_trap, active_trap);
|
||||||
|
|
||||||
|
if(BUILD_DEBUG && process->arch == Arch_x64)
|
||||||
|
{
|
||||||
|
Assert(active_trap->swap_bytes.size == 1 && active_trap->swap_bytes.str[0] == 0x90);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
dmn_lnx_process_untrap_probes(DMN_LNX_Entity *process)
|
||||||
|
{
|
||||||
|
Assert(process->kind == DMN_LNX_EntityKind_Process);
|
||||||
|
for EachNode(active_trap, DMN_ActiveTrap, process->first_probe_trap)
|
||||||
|
{
|
||||||
|
dmn_remove_trap(active_trap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal U64
|
internal U64
|
||||||
dmn_lnx_thread_read_ip(DMN_LNX_Entity *thread)
|
dmn_lnx_thread_read_ip(DMN_LNX_Entity *thread)
|
||||||
{
|
{
|
||||||
@@ -1419,6 +1452,8 @@ dmn_lnx_set_single_step_flag(DMN_LNX_Entity *thread, B32 is_on)
|
|||||||
return is_flag_set;
|
return is_flag_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
dmn_lnx_handle_not_attached(Arena *arena, DMN_EventList *events)
|
dmn_lnx_handle_not_attached(Arena *arena, DMN_EventList *events)
|
||||||
{
|
{
|
||||||
@@ -1427,8 +1462,6 @@ dmn_lnx_handle_not_attached(Arena *arena, DMN_EventList *events)
|
|||||||
e->error_kind = DMN_ErrorKind_NotAttached;
|
e->error_kind = DMN_ErrorKind_NotAttached;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
|
||||||
|
|
||||||
internal DMN_LNX_Entity *
|
internal DMN_LNX_Entity *
|
||||||
dmn_lnx_handle_create_thread(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, pid_t tid)
|
dmn_lnx_handle_create_thread(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, pid_t tid)
|
||||||
{
|
{
|
||||||
@@ -1537,24 +1570,6 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, B32 debug_sub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// install DL probes
|
|
||||||
//
|
|
||||||
U64 probe_vaddrs[DMN_LNX_ProbeType_Count] = {0};
|
|
||||||
for EachIndex(i, DMN_LNX_ProbeType_Count)
|
|
||||||
{
|
|
||||||
if(known_probes[i] == 0) { continue; }
|
|
||||||
|
|
||||||
U8 og_byte = 0;
|
|
||||||
if(!dmn_lnx_read_struct(memory_fd, known_probes[i]->pc, &og_byte)) { Assert(0 && "failed to read original byte"); }
|
|
||||||
Assert(og_byte == 0x90);
|
|
||||||
|
|
||||||
U8 trap = 0xcc;
|
|
||||||
if(!dmn_lnx_write_struct(memory_fd, known_probes[i]->pc, &trap)) { Assert(0 && "failed to install probe"); }
|
|
||||||
|
|
||||||
probe_vaddrs[i] = known_probes[i]->pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// init process
|
// init process
|
||||||
//
|
//
|
||||||
@@ -1574,17 +1589,15 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, B32 debug_sub
|
|||||||
process->xcr0 = xcr0;
|
process->xcr0 = xcr0;
|
||||||
process->xsave_size = Max(xsave_size, sizeof(X64_XSave));
|
process->xsave_size = Max(xsave_size, sizeof(X64_XSave));
|
||||||
process->xsave_layout = xsave_layout;
|
process->xsave_layout = xsave_layout;
|
||||||
MemoryCopyTyped(&process->probe_vaddrs[0], &probe_vaddrs[0], DMN_LNX_ProbeType_Count);
|
|
||||||
hash_table_push_u64_raw(dmn_lnx_state->arena, dmn_lnx_state->pid_ht, pid, process);
|
hash_table_push_u64_raw(dmn_lnx_state->arena, dmn_lnx_state->pid_ht, pid, process);
|
||||||
|
|
||||||
// push create process event
|
// push create process event
|
||||||
{
|
|
||||||
DMN_Event *e = dmn_event_list_push(arena, events);
|
DMN_Event *e = dmn_event_list_push(arena, events);
|
||||||
e->kind = DMN_EventKind_CreateProcess;
|
e->kind = DMN_EventKind_CreateProcess;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
e->arch = process->arch;
|
e->arch = process->arch;
|
||||||
e->code = pid;
|
e->code = pid;
|
||||||
e->tls_model = DMN_TlsModel_Gnu; // TODO: use dynamic linker path to figure out correct enum here
|
e->tls_model = DMN_TlsModel_Gnu; // TODO: use dynamic linker path to figure out correct enum here
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// init main thread
|
// init main thread
|
||||||
@@ -1598,6 +1611,8 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, B32 debug_sub
|
|||||||
DMN_LNX_Entity *module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module);
|
DMN_LNX_Entity *module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module);
|
||||||
module->id = auxv.execfn;
|
module->id = auxv.execfn;
|
||||||
module->base_vaddr = base_vaddr;
|
module->base_vaddr = base_vaddr;
|
||||||
|
module->module_name_vaddr = auxv.execfn;
|
||||||
|
module->is_main = 1;
|
||||||
|
|
||||||
hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, 0, module);
|
hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, 0, module);
|
||||||
hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, base_vaddr, module);
|
hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, base_vaddr, module);
|
||||||
@@ -1707,28 +1722,20 @@ dmn_lnx_handle_exit_thread(Arena *arena, DMN_EventList *events, pid_t tid, U64 e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
internal DMN_LNX_Entity *
|
||||||
dmn_lnx_handle_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, U64 name_space_id, U64 new_link_map_vaddr)
|
dmn_lnx_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, U64 name_space_id, U64 module_name_vaddr, U64 base_vaddr)
|
||||||
{
|
{
|
||||||
GNU_LinkMap64 map = {0};
|
DMN_LNX_Entity *module = 0;
|
||||||
for(U64 map_vaddr = new_link_map_vaddr; map_vaddr != 0; map_vaddr = map.next_vaddr)
|
|
||||||
{
|
|
||||||
// read out new link map item
|
|
||||||
if(!dmn_lnx_read_linkmap(process->fd, map_vaddr, process->dl_class, &map)) { goto exit; }
|
|
||||||
|
|
||||||
// was module with this base already registered?
|
|
||||||
DMN_LNX_Entity *module = hash_table_search_u64_raw(process->loaded_modules_ht, map.addr_vaddr);
|
|
||||||
if(module) { continue; }
|
|
||||||
|
|
||||||
// parse out module's ELF header
|
// parse out module's ELF header
|
||||||
ELF_Hdr64 module_ehdr = {0};
|
ELF_Hdr64 module_ehdr = {0};
|
||||||
if(!dmn_lnx_read_ehdr(process->fd, map.addr_vaddr, &module_ehdr)) { goto exit; }
|
if(!dmn_lnx_read_ehdr(process->fd, base_vaddr, &module_ehdr)) { goto exit; }
|
||||||
|
|
||||||
// gather info about module
|
// gather info about module
|
||||||
U64 module_rebase = module_ehdr.e_type == ELF_Type_Dyn ? map.addr_vaddr : 0;
|
U64 module_rebase = module_ehdr.e_type == ELF_Type_Dyn ? base_vaddr : 0;
|
||||||
U64 module_phdr_vaddr = module_rebase + module_ehdr.e_phoff;
|
U64 module_phdr_vaddr = module_rebase + module_ehdr.e_phoff;
|
||||||
DMN_LNX_PhdrInfo module_phdr_info = dmn_lnx_phdr_info_from_memory(process->fd, module_ehdr.e_ident[ELF_Identifier_Class], module_rebase, module_phdr_vaddr, module_ehdr.e_phentsize, module_ehdr.e_phnum);
|
DMN_LNX_PhdrInfo module_phdr_info = dmn_lnx_phdr_info_from_memory(process->fd, module_ehdr.e_ident[ELF_Identifier_Class], module_rebase, module_phdr_vaddr, module_ehdr.e_phentsize, module_ehdr.e_phnum);
|
||||||
String8 module_name = dmn_lnx_read_string(process->arena, process->fd, map.name_vaddr);
|
String8 module_name = dmn_lnx_read_string(process->arena, process->fd, module_name_vaddr);
|
||||||
|
|
||||||
// read TLS index and TLS offset
|
// read TLS index and TLS offset
|
||||||
U64 tls_index = max_U64;
|
U64 tls_index = max_U64;
|
||||||
@@ -1737,39 +1744,58 @@ dmn_lnx_handle_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *
|
|||||||
{
|
{
|
||||||
Rng1U64 tls_modid_range = r1u64(dmn_lnx_state->tls_modid_desc.offset, dmn_lnx_state->tls_modid_desc.offset + dmn_lnx_state->tls_modid_desc.bit_size / 8);
|
Rng1U64 tls_modid_range = r1u64(dmn_lnx_state->tls_modid_desc.offset, dmn_lnx_state->tls_modid_desc.offset + dmn_lnx_state->tls_modid_desc.bit_size / 8);
|
||||||
Rng1U64 tls_offset_range = r1u64(dmn_lnx_state->tls_offset_desc.offset, dmn_lnx_state->tls_offset_desc.offset + dmn_lnx_state->tls_offset_desc.bit_size / 8);
|
Rng1U64 tls_offset_range = r1u64(dmn_lnx_state->tls_offset_desc.offset, dmn_lnx_state->tls_offset_desc.offset + dmn_lnx_state->tls_offset_desc.bit_size / 8);
|
||||||
tls_modid_range = shift_1u64(tls_modid_range, map_vaddr);
|
tls_modid_range = shift_1u64(tls_modid_range, base_vaddr);
|
||||||
tls_offset_range = shift_1u64(tls_offset_range, map_vaddr);
|
tls_offset_range = shift_1u64(tls_offset_range, base_vaddr);
|
||||||
if(!dmn_lnx_read(process->fd, tls_modid_range, &tls_index)) { Assert(0 && "failed to read TLS index"); }
|
if(!dmn_lnx_read(process->fd, tls_modid_range, &tls_index)) { Assert(0 && "failed to read TLS index"); }
|
||||||
if(!dmn_lnx_read(process->fd, tls_offset_range, &tls_offset)) { Assert(0 && "failed to read TLS offset"); }
|
if(!dmn_lnx_read(process->fd, tls_offset_range, &tls_offset)) { Assert(0 && "failed to read TLS offset"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill out module
|
// fill out module
|
||||||
module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module);
|
module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module);
|
||||||
module->id = map.name_vaddr;
|
module->id = module_name_vaddr;
|
||||||
module->base_vaddr = map.addr_vaddr;
|
module->base_vaddr = base_vaddr;
|
||||||
|
module->module_name_vaddr = module_name_vaddr;
|
||||||
|
|
||||||
|
if(str8_match(module_name, str8_lit("linux-vdso.so.1"), 0)) { goto exit; }
|
||||||
|
|
||||||
if(!str8_match(module_name, str8_lit("linux-vdso.so.1"), 0))
|
|
||||||
{
|
|
||||||
// push load event
|
// push load event
|
||||||
DMN_Event *e = dmn_event_list_push(arena, events);
|
DMN_Event *e = dmn_event_list_push(arena, events);
|
||||||
e->kind = DMN_EventKind_LoadModule;
|
e->kind = DMN_EventKind_LoadModule;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
e->module = dmn_lnx_handle_from_entity(module);
|
e->module = dmn_lnx_handle_from_entity(module);
|
||||||
e->arch = arch_from_elf_machine(module_ehdr.e_machine);
|
e->arch = arch_from_elf_machine(module_ehdr.e_machine);
|
||||||
e->address = map.addr_vaddr;
|
e->address = base_vaddr;
|
||||||
e->size = dim_1u64(module_phdr_info.range);
|
e->size = dim_1u64(module_phdr_info.range);
|
||||||
e->string = module_name;
|
e->string = module_name;
|
||||||
e->elf_phdr_vrange = r1u64(module_phdr_vaddr, module_phdr_vaddr + module_ehdr.e_phentsize * module_ehdr.e_phnum);
|
e->elf_phdr_vrange = r1u64(module_phdr_vaddr, module_phdr_vaddr + module_ehdr.e_phentsize * module_ehdr.e_phnum);
|
||||||
e->elf_phdr_entsize = module_ehdr.e_phentsize;
|
e->elf_phdr_entsize = module_ehdr.e_phentsize;
|
||||||
e->tls_index = tls_index;
|
e->tls_index = tls_index;
|
||||||
e->tls_offset = tls_offset;
|
e->tls_offset = tls_offset;
|
||||||
}
|
|
||||||
|
exit:;
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
dmn_lnx_handle_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, U64 name_space_id, U64 new_link_map_vaddr)
|
||||||
|
{
|
||||||
|
GNU_LinkMap64 map = {0};
|
||||||
|
for(U64 map_vaddr = new_link_map_vaddr; map_vaddr != 0; map_vaddr = map.next_vaddr)
|
||||||
|
{
|
||||||
|
// read out new link map item
|
||||||
|
if(!dmn_lnx_read_linkmap(process->fd, map_vaddr, process->dl_class, &map)) { break; }
|
||||||
|
|
||||||
|
// was module with this base already registered?
|
||||||
|
DMN_LNX_Entity *module = hash_table_search_u64_raw(process->loaded_modules_ht, map.addr_vaddr);
|
||||||
|
if(module == 0)
|
||||||
|
{
|
||||||
|
// load module
|
||||||
|
module = dmn_lnx_load_module(arena, events, process, name_space_id, map.name_vaddr, map.addr_vaddr);
|
||||||
|
|
||||||
// create mapping for base -> module
|
// create mapping for base -> module
|
||||||
hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, map.addr_vaddr, module);
|
hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, map.addr_vaddr, module);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
exit:;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
@@ -1835,11 +1861,19 @@ dmn_lnx_handle_breakpoint(Arena *arena, DMN_EventList *events, HashTable *active
|
|||||||
|
|
||||||
// is this a probe trap?
|
// is this a probe trap?
|
||||||
DMN_LNX_ProbeType probe_type = DMN_LNX_ProbeType_Null;
|
DMN_LNX_ProbeType probe_type = DMN_LNX_ProbeType_Null;
|
||||||
for EachIndex(i, ArrayCount(process->probe_vaddrs))
|
for EachNode(active_trap, DMN_ActiveTrap, process->first_probe_trap)
|
||||||
{
|
{
|
||||||
if(process->probe_vaddrs[i] == ip-1)
|
if(active_trap->trap->vaddr == ip-1)
|
||||||
|
{
|
||||||
|
probe_type = active_trap->trap->id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(U64 i = 0; i < DMN_LNX_ProbeType_Count; ++i)
|
||||||
|
{
|
||||||
|
if(process->probes[i] != 0 && process->probes[i]->pc == ip-1)
|
||||||
{
|
{
|
||||||
probe_type = i;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2029,6 +2063,7 @@ dmn_lnx_handle_attach(Arena *arena, DMN_EventList *events, pid_t pid)
|
|||||||
{
|
{
|
||||||
// create process
|
// create process
|
||||||
process = dmn_lnx_handle_create_process(arena, events, 1, 1, pid);
|
process = dmn_lnx_handle_create_process(arena, events, 1, 1, pid);
|
||||||
|
dmn_lnx_process_trap_probes(process);
|
||||||
|
|
||||||
// extract threads from /proc/pid/task
|
// extract threads from /proc/pid/task
|
||||||
for(;;)
|
for(;;)
|
||||||
@@ -2313,26 +2348,10 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
DMN_LNX_Entity *process = dmn_lnx_entity_from_handle(trap->process);
|
DMN_LNX_Entity *process = dmn_lnx_entity_from_handle(trap->process);
|
||||||
if(process == dmn_lnx_nil_entity) { continue; }
|
if(process == dmn_lnx_nil_entity) { continue; }
|
||||||
|
|
||||||
// read original instruction bytes
|
// trap instruction
|
||||||
String8 trap_inst = dmn_get_trap_inst();
|
DMN_ActiveTrap *active_trap = dmn_set_trap(scratch.arena, trap);
|
||||||
U8 *swap_bytes = push_array(scratch.arena, U8, trap_inst.size);
|
|
||||||
if(dmn_process_read(trap->process, r1u64(trap->vaddr, trap->vaddr+trap_inst.size), swap_bytes) != trap_inst.size)
|
|
||||||
{
|
|
||||||
Assert(0 && "failed to read original byte");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace with trap instruction bytes
|
|
||||||
if(dmn_process_write(trap->process, r1u64(trap->vaddr, trap->vaddr+trap_inst.size), trap_inst.str) != trap_inst.size)
|
|
||||||
{
|
|
||||||
Assert(0 && "failed to write trap");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add trap to the active list
|
// add trap to the active list
|
||||||
DMN_ActiveTrap *active_trap = push_array(scratch.arena, DMN_ActiveTrap, 1);
|
|
||||||
active_trap->trap = trap;
|
|
||||||
active_trap->swap_bytes = str8(swap_bytes, trap_inst.size);
|
|
||||||
SLLQueuePush(active_trap_first, active_trap_last, active_trap);
|
SLLQueuePush(active_trap_first, active_trap_last, active_trap);
|
||||||
|
|
||||||
// add (address -> trap)
|
// add (address -> trap)
|
||||||
@@ -2464,6 +2483,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
|
|
||||||
// wait for signals from the running threads
|
// wait for signals from the running threads
|
||||||
{
|
{
|
||||||
|
printf("----------------------------------------------\n");
|
||||||
B32 is_halt_done = 0;
|
B32 is_halt_done = 0;
|
||||||
for(U64 stopped_threads = 0;;)
|
for(U64 stopped_threads = 0;;)
|
||||||
{
|
{
|
||||||
@@ -2495,6 +2515,8 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
int wstopsig = WSTOPSIG(status);
|
int wstopsig = WSTOPSIG(status);
|
||||||
int event_code = (status >> 16);
|
int event_code = (status >> 16);
|
||||||
|
|
||||||
|
printf("wstopsig %d, event_code %d\n", wstopsig, event_code);
|
||||||
|
|
||||||
// intercept signals meant for the process launch sequence
|
// intercept signals meant for the process launch sequence
|
||||||
{
|
{
|
||||||
// pid -> pending process launch
|
// pid -> pending process launch
|
||||||
@@ -2515,7 +2537,8 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
dmn_lnx_process_launch_list_push_node(&dmn_lnx_state->free_pids, pending_proc);
|
dmn_lnx_process_launch_list_push_node(&dmn_lnx_state->free_pids, pending_proc);
|
||||||
|
|
||||||
// push create process events
|
// push create process events
|
||||||
dmn_lnx_handle_create_process(arena, &events, pending_proc->debug_subprocesses, 0, wait_id);
|
DMN_LNX_Entity *process = dmn_lnx_handle_create_process(arena, &events, pending_proc->debug_subprocesses, 0, wait_id);
|
||||||
|
dmn_lnx_process_trap_probes(process);
|
||||||
|
|
||||||
// override event code so the main code path does not create another process
|
// override event code so the main code path does not create another process
|
||||||
event_code = PTRACE_EVENT_STOP;
|
event_code = PTRACE_EVENT_STOP;
|
||||||
@@ -2637,7 +2660,72 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
}break;
|
}break;
|
||||||
case PTRACE_EVENT_FORK:
|
case PTRACE_EVENT_FORK:
|
||||||
{
|
{
|
||||||
NotImplemented;
|
// grab child pid
|
||||||
|
pid_t child_pid;
|
||||||
|
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_GETEVENTMSG, wait_id, 0, &child_pid)) < 0)
|
||||||
|
{
|
||||||
|
AssertAlways(0 && "failed to grab child pid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point kernel cloned memory pages and writes in the child process trigger COW
|
||||||
|
Temp temp = temp_begin(scratch.arena);
|
||||||
|
int child_memory_fd = open((char*)str8f(temp.arena, "/proc/%d/mem", child_pid).str, O_RDWR);
|
||||||
|
|
||||||
|
// grab parent process
|
||||||
|
DMN_LNX_Entity *parent_thread = dmn_lnx_thread_from_pid(wait_id);
|
||||||
|
DMN_LNX_Entity *parent_process = parent_thread->parent;
|
||||||
|
DMN_Handle parent_process_handle = dmn_lnx_handle_from_entity(parent_process);
|
||||||
|
DMN_Handle parent_thread_handle = dmn_lnx_handle_from_entity(parent_thread);
|
||||||
|
|
||||||
|
// if debugger is steps over a line with fork we have to undo the return address spoof
|
||||||
|
if(MemoryCompare(&parent_process_handle, &ctrls->spoof.process, sizeof(DMN_Handle)) == 0 &&
|
||||||
|
MemoryCompare(&parent_thread_handle, &ctrls->spoof.thread, sizeof(DMN_Handle)) == 0)
|
||||||
|
{
|
||||||
|
if(!dmn_lnx_write(child_memory_fd, r1u64(ctrls->spoof.vaddr, ctrls->spoof.vaddr + ctrls->spoof.size), &ctrls->spoof.old_ip))
|
||||||
|
{
|
||||||
|
AssertAlways(0 && "failed to undo spoof");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parent_process->debug_subprocesses)
|
||||||
|
{
|
||||||
|
// create child process entity
|
||||||
|
dmn_lnx_process_untrap_probes(parent_process);
|
||||||
|
DMN_LNX_Entity *child_process = dmn_lnx_handle_create_process(arena, &events, 1, 1, child_pid);
|
||||||
|
dmn_lnx_process_trap_probes(child_process);
|
||||||
|
|
||||||
|
// copy modules from the parent process
|
||||||
|
for(DMN_LNX_Entity *module = parent_process->first; module != dmn_lnx_nil_entity; module = module->next)
|
||||||
|
{
|
||||||
|
if(module->kind != DMN_LNX_EntityKind_Module) { continue; }
|
||||||
|
if(module->is_main) { continue; }
|
||||||
|
dmn_lnx_load_module(arena, &events, child_process, module->name_space_id, module->module_name_vaddr, module->base_vaddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// take out probes traps from the child probess
|
||||||
|
dmn_lnx_process_untrap_probes(parent_process);
|
||||||
|
|
||||||
|
// take out traps from the child process
|
||||||
|
for EachNode(n, DMN_ActiveTrap, active_trap_first)
|
||||||
|
{
|
||||||
|
if(MemoryCompare(&n->trap->process, &parent_process_handle, sizeof(DMN_Handle)))
|
||||||
|
{
|
||||||
|
B32 is_written = dmn_lnx_write(child_memory_fd, r1u64(n->trap->vaddr, n->trap->vaddr + n->swap_bytes.size), n->swap_bytes.str);
|
||||||
|
AssertAlways(is_written);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// detach and continue the child process
|
||||||
|
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_DETACH, child_pid, 0, 0) < 0))
|
||||||
|
{
|
||||||
|
Assert(0 && "unsuccessful detach");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(child_memory_fd);
|
||||||
|
temp_end(temp);
|
||||||
}break;
|
}break;
|
||||||
case PTRACE_EVENT_VFORK:
|
case PTRACE_EVENT_VFORK:
|
||||||
{
|
{
|
||||||
@@ -2661,7 +2749,8 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
if(process->debug_subprocesses)
|
if(process->debug_subprocesses)
|
||||||
{
|
{
|
||||||
dmn_lnx_handle_exit_thread(arena, &events, wait_id, 0);
|
dmn_lnx_handle_exit_thread(arena, &events, wait_id, 0);
|
||||||
dmn_lnx_handle_create_process(arena, &events, 1, 0, wait_id);
|
DMN_LNX_Entity *process = dmn_lnx_handle_create_process(arena, &events, 1, 0, wait_id);
|
||||||
|
dmn_lnx_process_trap_probes(process);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -203,7 +203,8 @@ struct DMN_LNX_Entity
|
|||||||
ELF_Class dl_class;
|
ELF_Class dl_class;
|
||||||
HashTable *loaded_modules_ht;
|
HashTable *loaded_modules_ht;
|
||||||
DMN_LNX_Probe **probes;
|
DMN_LNX_Probe **probes;
|
||||||
U64 probe_vaddrs[DMN_LNX_ProbeType_Count];
|
DMN_ActiveTrap *first_probe_trap;
|
||||||
|
DMN_ActiveTrap *last_probe_trap;
|
||||||
U64 main_thread_exit_code;
|
U64 main_thread_exit_code;
|
||||||
U64 thread_count;
|
U64 thread_count;
|
||||||
|
|
||||||
@@ -222,11 +223,14 @@ struct DMN_LNX_Entity
|
|||||||
U64 thread_local_base;
|
U64 thread_local_base;
|
||||||
|
|
||||||
// module
|
// module
|
||||||
|
U64 module_name_vaddr;
|
||||||
U64 base_vaddr;
|
U64 base_vaddr;
|
||||||
U64 phvaddr;
|
U64 phvaddr;
|
||||||
U64 phentsize;
|
U64 phentsize;
|
||||||
U64 phcount;
|
U64 phcount;
|
||||||
|
U64 name_space_id;
|
||||||
B8 is_live;
|
B8 is_live;
|
||||||
|
B8 is_main;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct DMN_LNX_EntityNode DMN_LNX_EntityNode;
|
typedef struct DMN_LNX_EntityNode DMN_LNX_EntityNode;
|
||||||
@@ -372,6 +376,10 @@ internal DMN_Handle dmn_lnx_handle_from_entity(DMN_LNX_Entity *entity);
|
|||||||
internal DMN_LNX_Entity *dmn_lnx_entity_from_handle(DMN_Handle handle);
|
internal DMN_LNX_Entity *dmn_lnx_entity_from_handle(DMN_Handle handle);
|
||||||
internal DMN_LNX_Entity *dmn_lnx_thread_from_pid(pid_t pid);
|
internal DMN_LNX_Entity *dmn_lnx_thread_from_pid(pid_t pid);
|
||||||
|
|
||||||
|
//- Process
|
||||||
|
internal void dmn_lnx_process_install_probes();
|
||||||
|
|
||||||
|
//- Thread
|
||||||
internal U64 dmn_lnx_thread_read_ip(DMN_LNX_Entity *thread);
|
internal U64 dmn_lnx_thread_read_ip(DMN_LNX_Entity *thread);
|
||||||
internal U64 dmn_lnx_thread_read_sp(DMN_LNX_Entity *thread);
|
internal U64 dmn_lnx_thread_read_sp(DMN_LNX_Entity *thread);
|
||||||
internal void dmn_lnx_thread_write_ip(DMN_LNX_Entity *thread, U64 ip);
|
internal void dmn_lnx_thread_write_ip(DMN_LNX_Entity *thread, U64 ip);
|
||||||
|
|||||||
Reference in New Issue
Block a user