diff options
author | Matt Hunter <m@lfurio.us> | 2025-08-13 01:04:57 -0400 |
---|---|---|
committer | Matt Hunter <m@lfurio.us> | 2025-09-07 06:41:16 -0400 |
commit | 4ea8ea650a1d81cf6362e1485d2fdce2617d8d8e (patch) | |
tree | 65cddd4d36a4264141f6c8dca1a8a9e91076cc17 /debugger.c | |
parent | f9c7b14383a99ecc0a1e8266467804647acfaa3e (diff) | |
download | misplays-4ea8ea650a1d81cf6362e1485d2fdce2617d8d8e.tar.gz misplays-4ea8ea650a1d81cf6362e1485d2fdce2617d8d8e.zip |
Add architecture-specific single step support
ARM 32-bit is the first platform added to misplays which lacks
underlying hardware support for single step traps - so the kernel does
not implement PTRACE_SINGLESTEP in this case.
We will work around this in a similar way as gdb does and how the kernel
used to do it until 2011. arm_singlestep() implements logic which
disassembles the program's current instruction and analyzes it to
determine all possible next locations - eg: the next instruction in
memory, or the jump target of a branch instruction, etc. This logic is
dynamically dispatched by the debugger core if an ARM build is running
in 32-bit mode.
arm_singlestep() uses breakpoints to stop execution at it's computed
next locations. However, misplays is currently very careful about
controling the use of breakpoints in order to avoid issues with thread
single steps - so a new flag (called "step") is added to breakpoints to
enable the debugger to selectively install this subset of breakpoints
for each thread's single step action, and more or less keep treating
thread free-run as normal. install_breakpoints() is updated to take a
"step" parameter to control which set of breakpoints is installed at any
given time.
resume_threads() is updated to perform this new single step dynamic
dispatch, and manage the installation of step breakpoints.
add_breakpoint() is also given a "step" parameter. This initializes the
flag for the new breakpoint, but crucially is used to sort the new
breakpoint into the process breakpoint list. Since step breakpoints
will always be installed first, prioritize them in the list so that
uninstall_breakpoints() doesn't corrupt memory when it runs the list
backward to remove them.
Signed-off-by: Matt Hunter <m@lfurio.us>
Diffstat (limited to 'debugger.c')
-rw-r--r-- | debugger.c | 50 |
1 files changed, 43 insertions, 7 deletions
@@ -10,6 +10,7 @@ #include <capstone/capstone.h> +#include "arch/arm-singlestep.h" #include "debugger.h" #include "helpers.h" @@ -67,14 +68,14 @@ static void free_breakpoints(struct process *proc) { } } -static void install_breakpoints(struct thread *th) { +static void install_breakpoints(struct thread *th, int step) { struct archinfo archinfo; struct iovec regs = { &th->state->regs, th->state->regsize }; architecture_info(&archinfo, ®s); struct list *breaks = &th->proc->breakpoints; for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) { - if (!b->installed) { + if (!b->installed && (b->step == step)) { unsigned long word; word = ptrace(PTRACE_PEEKTEXT, th->id, b->address, NULL); b->text = word; @@ -272,13 +273,34 @@ static void continue_all_threads(struct process *proc) { } } +static void compute_step_breakpoints(struct thread *th) { +#ifdef ARCH_AARCH64 + if (th->state->regsize == sizeof(struct user_regs_32)) { + arm_singlestep(th); + } +#endif +} + +static void do_singlestep(struct thread *th) { +#ifdef ARCH_AARCH64 + if (th->state->regsize == sizeof(struct user_regs_32)) { + ptrace(PTRACE_CONT, th->id, NULL, th->signal); + return; + } +#endif + + ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal); +} + static void resume_threads(struct process *proc) { struct list *threads = &proc->threads; int once = 0; for (struct thread *th = threads->head; th != threads->end; th = th->next) { if (th->stopped && th->doing == PTRACE_SINGLESTEP) { - ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal); + compute_step_breakpoints(th); + install_breakpoints(th, 1); + do_singlestep(th); th->stopped = 0; th->signal = 0; strcpy(th->status, "RUNNING"); @@ -291,7 +313,7 @@ static void resume_threads(struct process *proc) { usleep(SCHEDULER_DELAY); once = 1; } - install_breakpoints(th); + install_breakpoints(th, 0); ptrace(th->doing, th->id, NULL, th->signal); th->stopped = 0; th->signal = 0; @@ -446,7 +468,7 @@ static int wait_thread(struct thread *th) { return -1; } -struct breakpoint *add_breakpoint(struct process *proc, unsigned long address) { +struct breakpoint *add_breakpoint(struct process *proc, unsigned long address, int step) { struct breakpoint *b = xmalloc(sizeof(*b)); b->address = address; b->text = 0; @@ -454,10 +476,24 @@ struct breakpoint *add_breakpoint(struct process *proc, unsigned long address) { b->previously_installed = 0; b->hits = 0; b->user = 1; + b->step = step; b->stack = 0; b->tid = 0; b->enabled = 1; - list_insert(proc->breakpoints.end, b); + + if (step) { + struct breakpoint *p; + struct list *breaks = &proc->breakpoints; + for (p = breaks->head; p != breaks->end; p = p->next) { + if (p->step == 0) { + break; + } + } + list_insert(p, b); + } else { + list_insert(proc->breakpoints.end, b); + } + return b; } @@ -656,7 +692,7 @@ int dbg_stepover(struct thread *th) { cs_disasm_iter(handle, &code, &size, &address, insn); if (insn->id == archinfo.cs_call) { - struct breakpoint *b = add_breakpoint(th->proc, address); + struct breakpoint *b = add_breakpoint(th->proc, address, 0); b->user = 0; b->stack = archinfo.stackptr; b->tid = th->id; |