diff --git a/src/demon/linux/demon_core_linux.c b/src/demon/linux/demon_core_linux.c index 9d027151..6b6b7d2a 100644 --- a/src/demon/linux/demon_core_linux.c +++ b/src/demon/linux/demon_core_linux.c @@ -532,6 +532,174 @@ dmn_lnx_thread_from_pid(pid_t pid) return result; } +internal B32 +dmn_lnx_thread_read_reg_block(DMN_LNX_Entity *thread, void *reg_block) +{ + B32 result = 0; + { + REGS_RegBlockX64 *dst = (REGS_RegBlockX64 *)reg_block; + pid_t tid = (pid_t)thread->id; + + //- rjf: read GPR + B32 got_gpr = 0; + { + DMN_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) + { + got_gpr = 1; + DMN_LNX_UserRegsX64 *src = &ctx.regs; + 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; + } + } + + //- rjf: read FPR + B32 got_fpr = 0; + if(got_gpr) + { + Temp scratch = scratch_begin(0, 0); + DMN_LNX_XSave *xsave = 0; + DMN_LNX_XSaveLegacy *xsave_legacy = 0; + + // rjf: try xsave + if(!xsave_legacy) + { + 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) + { + xsave = push_array_no_zero(scratch.arena, DMN_LNX_XSave, 1); + MemoryCopy(xsave, xsave_buffer, sizeof(*xsave)); + xsave_legacy = &xsave->legacy; + } + } + + // rjf: try fxsave + if(!xsave_legacy) + { + DMN_LNX_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) + { + xsave_legacy = push_array_no_zero(scratch.arena, DMN_LNX_XSaveLegacy, 1); + MemoryCopy(xsave_legacy, &fxsave, sizeof(*xsave_legacy)); + } + } + + // rjf: fill from xsave legacy + if(xsave_legacy) + { + DMN_LNX_XSaveLegacy *src = xsave_legacy; + dst->fcw.u16 = src->fcw; + dst->fsw.u16 = src->fsw; + dst->ftw.u16 = src->ftw; // TODO(rjf): old: fix tag word (?) + dst->fop.u16 = src->fop; + dst->fip.u64 = src->b64.fip; + // TODO(rjf): these 16-bit registers do not belong in x64 + dst->fcs.u16 = 0; + dst->fdp.u64 = src->b64.fdp; + dst->fds.u16 = 0; + dst->mxcsr.u32 = src->mxcsr; + dst->mxcsr_mask.u32 = src->mxcsr_mask; + { + U8 *float_s = src->st_space.u8; + REGS_Reg80 *float_d = &dst->st0; + for(U32 n = 0; n < 8; n += 1, float_s += 16, float_d += 1) + { + MemoryCopy(float_d, float_s, sizeof(*float_d)); + } + } + { + U8 *xmm_s = src->xmm_space.u8; + REGS_Reg512 *xmm_d = &dst->zmm0; + for(U32 n = 0; n < 16; n += 1, xmm_s += 16, xmm_d += 1) + { + MemoryCopy(xmm_d, xmm_s, 16); + } + } + } + + // rjf: fill from ymm registers + // TODO(rjf): this is a lie; ymm can technically move around. study & fix. + if(xsave) + { + B32 has_ymm_registers = ((xsave->header.xstate_bv & 4) != 0); + if(has_ymm_registers) + { + U8 *ymm_s = (U8 *)xsave->ymmh; + REGS_Reg512 *ymm_d = &dst->zmm0; + for(U32 n = 0; n < 16; n += 1, ymm_s += 16, ymm_d += 1) + { + MemoryCopy(((U8*)ymm_d) + 16, ymm_s, 16); + } + } + } + + got_fpr = (xsave || xsave_legacy); + scratch_end(scratch); + } + + //- rjf: read debug registers + B32 got_debug = 0; + if(got_fpr) + { + got_debug = 1; + REGS_Reg64 *dr_d = &dst->dr0; + for(U32 i = 0; i < 8; i += 1, dr_d += 1) + { + if(i != 4 && i != 5) + { + U64 offset = OffsetOf(DMN_LNX_UserX64, u_debugreg[i]); + errno = 0; + int peek_result = ptrace(PTRACE_PEEKUSER, tid, PtrFromInt(offset), 0); + if(errno == 0) + { + dr_d->u64 = (U64)peek_result; + } + else + { + got_debug = 0; + } + } + } + } + + result = got_debug; + } + return result; +} + //////////////////////////////// //~ rjf: @dmn_os_hooks Main Layer Initialization (Implemented Per-OS) @@ -828,6 +996,11 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) { Temp scratch = scratch_begin(&arena, 1); + //////////////////////////// + //- rjf: unpack controls + // + DMN_LNX_Entity *single_step_thread = dmn_lnx_entity_from_handle(ctrls->single_step_thread); + //////////////////////////// //- rjf: push any deferred events // @@ -853,12 +1026,17 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) e->error_kind = DMN_ErrorKind_NotAttached; } + //////////////////////////// + //- rjf: determine if we need to wait for new events + // + B32 need_wait_on_events = (evts.count == 0); + //////////////////////////// //- rjf: gather all threads which we should run // DMN_LNX_EntityNode *first_run_thread = 0; DMN_LNX_EntityNode *last_run_thread = 0; - ProfScope("gather all threads which we should run") + if(need_wait_on_events) ProfScope("gather all threads which we should run") { //- rjf: scan all processes for(DMN_LNX_Entity *process = dmn_lnx_state->entities_base->first; @@ -956,7 +1134,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) //- rjf: loop: wait for next stop, produce debug events // pid_t final_wait_pid = 0; - for(B32 done = 0; !done;) + if(need_wait_on_events) for(B32 done = 0; !done;) { //- rjf: wait for next event int status = 0; @@ -974,6 +1152,17 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) DMN_LNX_Entity *process = thread->parent; B32 thread_is_process_root = (thread->id == process->id); + //- rjf: unpack thread's registers + U64 rip = 0; + void *regs_block = 0; + if(thread != &dmn_lnx_nil_entity) + { + U64 regs_block_size = regs_block_size_from_arch(thread->arch); + regs_block = push_array(scratch.arena, U8, regs_block_size); + dmn_lnx_thread_read_reg_block(thread, regs_block); + rip = regs_rip_from_arch_block(thread->arch, regs_block); + } + //- rjf: WIFEXITED(status) -> thread exit B32 thread_exit = 0; U64 exit_code = 0; @@ -1003,11 +1192,40 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) } //- rjf: SIGTRAP:PTRACE_EVENT_FORK, or SIGTRAP:PTRACE_EVENT_VFORK - if(wifstopped && wstopsig == SIGTRAP && + B32 sigtrap_handled = 0; + if(!sigtrap_handled && wifstopped && wstopsig == SIGTRAP && (ptrace_event_code == PTRACE_EVENT_FORK || ptrace_event_code == PTRACE_EVENT_VFORK)) { - // TODO(rjf) + sigtrap_handled = 1; + } + + //- rjf: SIGTRAP + if(!sigtrap_handled && wifstopped && wstopsig == SIGTRAP) + { + // rjf: this is the single step thread => this is a single step completion + DMN_EventKind e_kind = DMN_EventKind_Trap; + if(thread == single_step_thread) + { + e_kind = DMN_EventKind_SingleStep; + } + + // rjf: this matches a specified trap => breakpoint + { + // TODO(rjf) + } + + // rjf: after breakpoint -> rollback + if(e_kind == DMN_EventKind_Breakpoint) + { + // TODO(rjf) + } + + // rjf: push event + DMN_Event *e = dmn_event_list_push(arena, &evts); + e->kind = e_kind; + e->process = dmn_lnx_handle_from_entity(process); + e->thread = dmn_lnx_handle_from_entity(thread); } //- rjf: WSTOPSIG(status) is SIGSTOP @@ -1243,7 +1461,13 @@ dmn_tls_root_vaddr_from_thread(DMN_Handle handle) internal B32 dmn_thread_read_reg_block(DMN_Handle handle, void *reg_block) { - return 0; + B32 result = 0; + DMN_AccessScope + { + DMN_LNX_Entity *thread = dmn_lnx_entity_from_handle(handle); + result = dmn_lnx_thread_read_reg_block(thread, reg_block); + } + return result; } internal B32 diff --git a/src/demon/linux/demon_core_linux.h b/src/demon/linux/demon_core_linux.h index 9a399d22..a1433116 100644 --- a/src/demon/linux/demon_core_linux.h +++ b/src/demon/linux/demon_core_linux.h @@ -4,20 +4,195 @@ #ifndef DEMON_CORE_LINUX_H #define DEMON_CORE_LINUX_H +//////////////////////////////// +//~ rjf: Includes + #include #include #include +#include #include #include #include #include +//////////////////////////////// +//~ rjf: ptrace options + #define DMN_LNX_PTRACE_OPTIONS (PTRACE_O_TRACEEXIT|\ PTRACE_O_EXITKILL|\ PTRACE_O_TRACEFORK|\ PTRACE_O_TRACEVFORK|\ PTRACE_O_TRACECLONE) +//////////////////////////////// +//~ rjf: Register Layouts +// +// These are defined in , but only for one architecture at a time + +#pragma pack(push, 1) + +typedef struct DMN_LNX_UserRegsX64 DMN_LNX_UserRegsX64; +struct DMN_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; +}; + +typedef struct DMN_LNX_XSaveLegacy DMN_LNX_XSaveLegacy; +struct DMN_LNX_XSaveLegacy +{ + U16 fcw; + U16 fsw; + U16 ftw; + U16 fop; + union + { + struct + { + U64 fip; + U64 fdp; + } b64; + struct + { + U32 fip; + U16 fcs, _pad0; + U32 fdp; + U16 fds, _pad1; + } b32; + }; + U32 mxcsr; + U32 mxcsr_mask; + U128 st_space; + U256 xmm_space; + U8 padding[96]; +}; + +typedef struct DMN_LNX_XSaveHeader DMN_LNX_XSaveHeader; +struct DMN_LNX_XSaveHeader +{ + U64 xstate_bv; + U64 xcomp_bv; + U8 reserved[48]; +}; + +// TODO(rjf): +// +// this one is hacked; ymmh is not gauranteed to be at a fixed location +// and there can be more after that. Requires CPUID to be totally compliant to the standard. +// See intel's manual on the xsave format for more info. +// +typedef struct DMN_LNX_XSave DMN_LNX_XSave; +struct DMN_LNX_XSave +{ + DMN_LNX_XSaveLegacy legacy; + DMN_LNX_XSaveHeader header; + U8 ymmh[256]; +}; + +typedef struct DMN_LNX_UserX64 DMN_LNX_UserX64; +struct DMN_LNX_UserX64 +{ + DMN_LNX_UserRegsX64 regs; + S32 u_fpvalid, _pad0; + DMN_LNX_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]; +}; + +typedef struct DMN_LNX_UserRegsX86 DMN_LNX_UserRegsX86; +struct DMN_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; +}; + +// NOTE(rjf): (32-Bit Protected Mode Format) +typedef struct DMN_LNX_FSave DMN_LNX_FSave; +struct DMN_LNX_FSave +{ + // control registers + U16 fcw; + U16 _pad0; + U16 fsw; + U16 _pad1; + U16 ftw; + U16 _pad2; + U32 fip; + U16 fips; + U16 fop; + U32 fdp; + U16 fds; + U16 _pad3; + + // data registers + U8 st[80]; +}; + +typedef struct DMN_LNX_UserX86 DMN_LNX_UserX86; +struct DMN_LNX_UserX86 +{ + DMN_LNX_UserRegsX86 regs; + S32 u_fpvalid; + DMN_LNX_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]; +}; + +#pragma pack(pop) + +//////////////////////////////// +//~ rjf: Process Info Extraction Types + typedef struct DMN_LNX_ProcessAux DMN_LNX_ProcessAux; struct DMN_LNX_ProcessAux { @@ -57,6 +232,9 @@ struct DMN_LNX_ModuleInfoList U64 count; }; +//////////////////////////////// +//~ rjf: Entity Types + typedef enum DMN_LNX_EntityKind { DMN_LNX_EntityKind_Null, @@ -91,6 +269,9 @@ struct DMN_LNX_EntityNode DMN_LNX_Entity *v; }; +//////////////////////////////// +//~ rjf: Main State Bundle + typedef struct DMN_LNX_State DMN_LNX_State; struct DMN_LNX_State { @@ -149,5 +330,6 @@ 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); internal DMN_LNX_Entity *dmn_lnx_thread_from_pid(pid_t pid); +internal B32 dmn_lnx_thread_read_reg_block(DMN_LNX_Entity *thread, void *reg_block); #endif // DEMON_CORE_LINUX_H diff --git a/src/os/core/linux/os_core_linux.c b/src/os/core/linux/os_core_linux.c index be10e519..d8ded967 100644 --- a/src/os/core/linux/os_core_linux.c +++ b/src/os/core/linux/os_core_linux.c @@ -1012,8 +1012,8 @@ os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) endt_timespec.tv_sec = endt_us/Million(1); endt_timespec.tv_nsec = Thousand(1) * (endt_us - (endt_us/Million(1))*Million(1)); B32 result = 0; - pthread_rwlock_unlock(&rw_mutex_entity->rwmutex_handle); pthread_mutex_lock(&cv_entity->cv.rwlock_mutex_handle); + pthread_rwlock_unlock(&rw_mutex_entity->rwmutex_handle); for(;;) { int wait_result = pthread_cond_timedwait(&cv_entity->cv.cond_handle, &cv_entity->cv.rwlock_mutex_handle, &endt_timespec); @@ -1048,8 +1048,8 @@ os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) endt_timespec.tv_sec = endt_us/Million(1); endt_timespec.tv_nsec = Thousand(1) * (endt_us - (endt_us/Million(1))*Million(1)); B32 result = 0; - pthread_rwlock_unlock(&rw_mutex_entity->rwmutex_handle); pthread_mutex_lock(&cv_entity->cv.rwlock_mutex_handle); + pthread_rwlock_unlock(&rw_mutex_entity->rwmutex_handle); for(;;) { int wait_result = pthread_cond_timedwait(&cv_entity->cv.cond_handle, &cv_entity->cv.rwlock_mutex_handle, &endt_timespec); diff --git a/src/regs/generated/regs.meta.c b/src/regs/generated/regs.meta.c index 0805b542..61b2632f 100644 --- a/src/regs/generated/regs.meta.c +++ b/src/regs/generated/regs.meta.c @@ -566,8 +566,8 @@ REGS_Rng regs_g_reg_code_x64_rng_table[101] = {(U16)OffsetOf(REGS_RegBlockX64, fop), 2}, {(U16)OffsetOf(REGS_RegBlockX64, fcs), 2}, {(U16)OffsetOf(REGS_RegBlockX64, fds), 2}, -{(U16)OffsetOf(REGS_RegBlockX64, fip), 4}, -{(U16)OffsetOf(REGS_RegBlockX64, fdp), 4}, +{(U16)OffsetOf(REGS_RegBlockX64, fip), 8}, +{(U16)OffsetOf(REGS_RegBlockX64, fdp), 8}, {(U16)OffsetOf(REGS_RegBlockX64, mxcsr), 4}, {(U16)OffsetOf(REGS_RegBlockX64, mxcsr_mask), 4}, {(U16)OffsetOf(REGS_RegBlockX64, ss), 2}, diff --git a/src/regs/generated/regs.meta.h b/src/regs/generated/regs.meta.h index 0f6f75a3..4adc08d4 100644 --- a/src/regs/generated/regs.meta.h +++ b/src/regs/generated/regs.meta.h @@ -373,8 +373,8 @@ REGS_Reg16 ftw; REGS_Reg16 fop; REGS_Reg16 fcs; REGS_Reg16 fds; -REGS_Reg32 fip; -REGS_Reg32 fdp; +REGS_Reg64 fip; +REGS_Reg64 fdp; REGS_Reg32 mxcsr; REGS_Reg32 mxcsr_mask; REGS_Reg16 ss; diff --git a/src/regs/regs.mdesk b/src/regs/regs.mdesk index 2d7bc1ba..56076b33 100644 --- a/src/regs/regs.mdesk +++ b/src/regs/regs.mdesk @@ -57,8 +57,8 @@ REGS_RegTableX64: {fop 16 Normal} {fcs 16 Normal} {fds 16 Normal} - {fip 32 Normal} - {fdp 32 Normal} + {fip 64 Normal} + {fdp 64 Normal} {mxcsr 32 Normal} {mxcsr_mask 32 Normal} {ss 16 Normal}