diff options
Diffstat (limited to 'debugger.c')
-rw-r--r-- | debugger.c | 105 |
1 files changed, 79 insertions, 26 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; @@ -95,7 +96,12 @@ static void uninstall_breakpoints(struct thread *th) { b->installed = 0; } - if (b->previously_installed && b->enabled < 0) { + if (b->step) { + struct breakpoint *del = b; + b = b->next; + list_remove(del); + free(del); + } else if (b->enabled < 0 && b->previously_installed) { struct thread *t = NULL; if (b->tid == 0 || ((t = thread_by_id(th->proc, b->tid)) && !t->doing)) { struct breakpoint *del = b; @@ -108,8 +114,9 @@ static void uninstall_breakpoints(struct thread *th) { } static int detect_breakpoint(struct thread *th, int *restart) { - int ret = 0; - *restart = 0; + int is_bp = 0; /* at least 1 effective breakpoint at this PC */ + int is_user = 0; /* at least 1 user-defined bp at this PC */ + *restart = 0; /* at least 1 bp which must stop at this PC */ /* Hack: Need to manually fetch registers here, since capture_state() has * not yet run for this stop. It is not guaranteed that we even want to @@ -122,8 +129,27 @@ static int detect_breakpoint(struct thread *th, int *restart) { architecture_info(&archinfo, &ivregs); unsigned long breakpt_address = archinfo.progmctr - archinfo.bp_adjust; - struct breakpoint *b = get_breakpoint(th->proc, breakpt_address); - if (b && b->installed && th->doing != PTRACE_SINGLESTEP) { + + struct list *breaks = &th->proc->breakpoints; + for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) { + if (b->address == breakpt_address) { + if (b->installed /* && th->doing != PTRACE_SINGLESTEP*/) { + /* todo - issues with singlestep? */ + /* todo - put conditional guard around `hits++` */ + is_bp = 1; + b->hits++; + is_user |= b->user; + + if ((b->stack == 0 || b->stack == archinfo.stackptr) + && (b->tid == 0 || b->tid == th->id) + && (b->enabled)) { + *restart = 1; + } + } + } + } + + if (is_bp) { /* restore actual program counter to breakpoint address */ if (ivregs.iov_len == sizeof(struct user_regs_32)) { regs.PROGMCTR_32 = breakpt_address; @@ -131,20 +157,10 @@ static int detect_breakpoint(struct thread *th, int *restart) { regs.PROGMCTR_64 = breakpt_address; } ptrace(PTRACE_SETREGSET, th->id, NT_PRSTATUS, &ivregs); - - b->hits++; /* todo: consider whether this is firing too much */ - ret = b->user; - - if (b->stack != 0 && b->stack != archinfo.stackptr) { - *restart = 1; - } else if (b->tid != 0 && b->tid != th->id) { - *restart = 1; - } else if (!b->enabled) { - *restart = 1; - } } - return ret; + *restart = (is_bp ? !*restart : 0); + return is_user; } static void free_states(struct thread *th) { @@ -272,13 +288,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 +328,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; @@ -419,8 +456,10 @@ static int wait_thread(struct thread *th) { strcpy(th->status, (bp ? "BREAKPOINT" : "STEP")); if (restart) { - th->donext = th->doing; - th->doing = (th->doing ? PTRACE_SINGLESTEP : 0); + if (th->doing != PTRACE_SINGLESTEP) { + th->donext = th->doing; + th->doing = (th->doing ? PTRACE_SINGLESTEP : 0); + } } else { th->doing = th->donext; th->donext = 0; @@ -446,7 +485,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 +493,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 +709,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; |