diff options
Diffstat (limited to 'debugger.c')
-rw-r--r-- | debugger.c | 795 |
1 files changed, 609 insertions, 186 deletions
@@ -1,282 +1,705 @@ +#include <dirent.h> +#include <elf.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <sys/ptrace.h> #include <sys/uio.h> #include <sys/wait.h> #include <capstone/capstone.h> -/* #include "config.h" */ #include "debugger.h" #include "helpers.h" -static const int PTRACE_OPTIONS = PTRACE_O_TRACESYSGOOD; - -static void capture_state(struct tracee *dbg) { - struct state *s = xmalloc(sizeof(*s)); - ptrace(PTRACE_GETREGS, dbg->id, NULL, &s->regs); - ptrace(PTRACE_GETFPREGS, dbg->id, NULL, &s->fpregs); - list_init(&s->maps); - list_insert(dbg->states.end, s); - dbg->state = s; - - char mapspath[128], entry[512]; - snprintf(mapspath, sizeof(mapspath), "/proc/%li/maps", (long)dbg->id); - - FILE *maps = fopen(mapspath, "r"); - if (maps) { - while (fgets(entry, sizeof(entry), maps)) { - struct map *m = xmalloc(sizeof(*m)); - sscanf(entry, "%lx-%lx ", &m->start, &m->end); - size_t size = m->end - m->start; - m->data = xmalloc(size); - - struct iovec loc = { m->data, size }; - struct iovec rem = { (void *)m->start, size }; - if (process_vm_readv(dbg->id, &loc, 1, &rem, 1, 0) < 0) { - free(m->data); - free(m); - continue; - } +static const int PTRACE_OPTIONS = + PTRACE_O_TRACECLONE | + PTRACE_O_TRACEFORK | + PTRACE_O_TRACEEXEC | + PTRACE_O_TRACEEXIT | + PTRACE_O_TRACESYSGOOD; + +static const int PTRACE_CHILD_OPTIONS = PTRACE_OPTIONS | PTRACE_O_EXITKILL; +//static const int STOP_ALL_ON_EVENT = 1; +static const useconds_t SCHEDULER_DELAY = 10000; +static const unsigned int BREAKPOINT_INSN = 0xcc; + +static struct process *new_process(pid_t id, int child) { + struct process *proc = xmalloc(sizeof(*proc)); + proc->id = id; + proc->child = child; + list_init(&proc->breakpoints); + list_init(&proc->threads); + memset(proc->status, 0, sizeof(proc->status)); + return proc; +} + +static struct thread *new_thread(struct process *proc, pid_t id) { + struct thread *th = xmalloc(sizeof(*th)); + th->proc = proc; + list_init(&th->states); + th->state = NULL; + th->clearstates = 0; + th->id = id; + th->stopped = 0; + th->signal = 0; + th->doing = 0; + th->donext = 0; + strcpy(th->status, "RUNNING"); + return th; +} + +static struct thread *thread_by_id(struct process *proc, pid_t id) { + struct list *threads = &proc->threads; + for (struct thread *th = threads->head; th != threads->end; th = th->next) { + if (th->id == id) { + return th; + } + } + return NULL; +} + +static void free_breakpoints(struct process *proc) { + while (proc->breakpoints.head != proc->breakpoints.end) { + struct breakpoint *b = proc->breakpoints.head; + list_remove(b); + free(b); + } +} + +static void install_breakpoints(struct thread *th) { + struct list *breaks = &th->proc->breakpoints; + for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) { + if (!b->installed) { + unsigned long word; + word = ptrace(PTRACE_PEEKTEXT, th->id, b->address, NULL); + b->text = word; + + word = (word & ~0xff) | BREAKPOINT_INSN; + ptrace(PTRACE_POKETEXT, th->id, b->address, word); + b->installed = 1; + } + } +} - list_insert(s->maps.end, m); +static void uninstall_breakpoints(struct thread *th) { + struct list *breaks = &th->proc->breakpoints; + for (struct breakpoint *b = breaks->tail; b != breaks->end; b = b->prev) { + if (b->installed) { + ptrace(PTRACE_POKETEXT, th->id, b->address, b->text); + b->installed = 0; + + if (b->enabled < 0) { + struct thread *t; + if (b->tid == 0 || + ((t = thread_by_id(th->proc, b->tid)) && !t->doing)) { + struct breakpoint *del = b; + b = b->next; + list_remove(del); + free(del); + } + } } + } +} + +static int detect_breakpoint(struct thread *th, int *restart) { + int ret = 0; + *restart = 0; - fclose(maps); + struct user_regs_struct regs; + struct iovec ivregs = { ®s, sizeof(regs) }; + ptrace(PTRACE_GETREGSET, th->id, NT_PRSTATUS, &ivregs); + + struct breakpoint *b = get_breakpoint(th->proc, regs.rip - 1); + if (b && b->installed && th->doing != PTRACE_SINGLESTEP) { + regs.rip--; + 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 != regs.rsp) { + *restart = 1; + } else if (b->tid != 0 && b->tid != th->id) { + *restart = 1; + } else if (!b->enabled) { + *restart = 1; + } } + + return ret; } -static void clear_states(struct tracee *dbg) { - while (dbg->states.head != dbg->states.end) { - struct state *s = dbg->states.head; +static void free_states(struct thread *th) { + while (th->states.head != th->states.end) { + struct state *s = th->states.head; + while (s->maps.head != s->maps.end) { struct map *m = s->maps.head; list_remove(m); free(m->data); free(m); } + list_remove(s); free(s); } - dbg->state = NULL; + + th->state = NULL; + th->clearstates = 0; } -static void install_breakpoints(struct tracee *dbg) { - struct list *breaks = &dbg->breaks; - for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) { - if (b->enabled) { - unsigned long data; - data = ptrace(PTRACE_PEEKTEXT, dbg->id, b->address, NULL); - b->orig = data; - - data = (data & ~0xff) | BREAKPOINT_INSN; - ptrace(PTRACE_POKETEXT, dbg->id, b->address, data); - b->active = 1; +static void capture_state(struct process *proc) { + struct list *threads = &proc->threads; + for (struct thread *th = threads->head; th != threads->end; th = th->next) { + if (!th->state) { + if (th->clearstates) { + free_states(th); + } + + struct state *s = xmalloc(sizeof(*s)); + struct iovec regs = { &s->regs, sizeof(s->regs) }; + ptrace(PTRACE_GETREGSET, th->id, NT_PRSTATUS, ®s); + list_init(&s->maps); + + list_insert(th->states.end, s); + th->state = s; + + char mapspath[32], entry[512]; + snprintf(mapspath, sizeof(mapspath), "/proc/%li/maps", (long)th->id); + + FILE *maps = fopen(mapspath, "r"); + if (maps) { + while (fgets(entry, sizeof(entry), maps)) { + struct map *m = xmalloc(sizeof(*m)); + sscanf(entry, "%lx-%lx ", &m->start, &m->end); + size_t size = m->end - m->start; + m->data = xmalloc(size); + + struct iovec loc = { m->data, size }; + struct iovec rem = { (void *)m->start, size }; + if (process_vm_readv(th->id, &loc, 1, &rem, 1, 0) < 0) { + free(m->data); + free(m); + continue; + } + + list_insert(s->maps.end, m); + } + + fclose(maps); + } } } } -static int uninstall_breakpoints(struct tracee *dbg, int stop) { - struct user_regs_struct regs; - int ch = 0; - int reenter = 0; - if (stop) { - ptrace(PTRACE_GETREGS, dbg->id, NULL, ®s); +static char thread_state(struct thread *th) { + char statpath[32], stat[512]; + snprintf(statpath, sizeof(statpath), "/proc/%li/stat", (long)th->id); + + FILE *f = fopen(statpath, "r"); + if (f) { + fread(stat, 1, sizeof(stat), f); + fclose(f); + + char *c = strchr(stat, ' '); + c = strchr(c+1, ' ') + 1; + return *c; } - struct list *breaks = &dbg->breaks; - for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) { - if (b->active) { - ptrace(PTRACE_POKETEXT, dbg->id, b->address, b->orig); - b->active = 0; - - /* detect stop at breakpoint */ - if (stop && !ch) { - if (regs.rip - 1 == b->address) { - regs.rip--; - ptrace(PTRACE_SETREGS, dbg->id, NULL, ®s); - ch = 1; - - if (b->stack != 0 && b->stack != regs.rsp) { - reenter = 1; - } + return 0; +} + +static int wait_thread(struct thread *th); +static void interrupt_all_threads(struct process *proc) { + struct list *threads = &proc->threads; + for (struct thread *th = threads->head; th != threads->end; th = th->next) { + if (!th->stopped) { + ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL); + + char state; + do { + state = thread_state(th); + } while (state != 't' && state != 'D' && state != 'Z'); + + wait_thread(th); + + //if (STOP_ALL_ON_EVENT) { + // th->cont = 0; + // th->shouldcont = 0; + //} + } + } +} + +static void continue_all_threads(struct process *proc) { + struct list *threads = &proc->threads; + for (struct thread *th = threads->head; th != threads->end; th = th->next) { + if (th->id > 0) { + char state; + do { + wait_thread(th); + + ptrace(PTRACE_CONT, th->id, NULL, th->signal); + th->stopped = 0; + th->signal = 0; + th->donext = 0; + th->doing = PTRACE_CONT; + strcpy(th->status, "RUNNING"); + th->clearstates = 1; + + state = thread_state(th); + } while (state == 't'); + } + } +} + +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); + th->stopped = 0; + th->signal = 0; + strcpy(th->status, "RUNNING"); + } + } + + for (struct thread *th = threads->head; th != threads->end; th = th->next) { + if (th->stopped && th->doing && th->doing != PTRACE_SINGLESTEP) { + if (!once) { + usleep(SCHEDULER_DELAY); + once = 1; + } + install_breakpoints(th); + ptrace(th->doing, th->id, NULL, th->signal); + th->stopped = 0; + th->signal = 0; + strcpy(th->status, "RUNNING"); + } + } +} + +static int wait_thread(struct thread *th) { + if (th->id <= 0) { + return -1; + } + + int status; + if (waitpid(th->id, &status, __WALL | WNOHANG) <= 0) { + return 0; + } + + if (WIFEXITED(status)) { + th->id = 0; + th->stopped = 1; + th->signal = WEXITSTATUS(status); + th->doing = 0; + th->donext = 0; + strcpy(th->status, "EXITED"); + return 1; + } else if (WIFSIGNALED(status)) { + th->id = 0; + th->stopped = 1; + th->signal = WTERMSIG(status); + th->doing = 0; + th->donext = 0; + strcpy(th->status, "TERMINATED"); + return 1; + } else if (WIFSTOPPED(status)) { + struct process *eventproc; + struct thread *eventth; + unsigned long eventmsg; + ptrace(PTRACE_GETEVENTMSG, th->id, NULL, &eventmsg); + + /* todo: process status messages */ + switch (status >> 8) { + /* todo: other ptrace event stops */ + case SIGTRAP | (PTRACE_EVENT_CLONE << 8): + eventth = new_thread(th->proc, eventmsg); + list_insert(th->proc->threads.end, eventth); + while (!wait_thread(eventth)) {} + + th->stopped = 1; + th->signal = 0; + th->doing = 0; + th->donext = 0; + strcpy(th->status, "CLONE"); + th->state = NULL; + return 1; + + case SIGTRAP | (PTRACE_EVENT_FORK << 8): + eventproc = new_process(eventmsg, th->proc->child); + eventth = new_thread(eventproc, eventmsg); + list_insert(eventproc->threads.end, eventth); + list_insert(th->proc->next, eventproc); + //while (!wait_thread(eventth)) {} + dbg_sync(eventproc); + + th->stopped = 1; + th->signal = 0; + th->doing = 0; + th->donext = 0; + strcpy(th->status, "FORK"); + th->state = NULL; + return 1; + + case SIGTRAP | (PTRACE_EVENT_EXEC << 8): + eventth = thread_by_id(th->proc, eventmsg); + if (eventth->id != th->id) { + eventth->id = 0; + eventth->stopped = 1; + eventth->signal = 0; + eventth->doing = 0; + eventth->donext = 0; + strcpy(eventth->status, "EXITED"); + } + + th->stopped = 1; + th->signal = 0; + th->doing = 0; + th->donext = 0; + strcpy(th->status, "EXECVE"); + th->state = NULL; + return 1; + + case SIGTRAP | (PTRACE_EVENT_EXIT << 8): + th->stopped = 1; + th->signal = 0; + th->doing = PTRACE_CONT; + th->donext = 0; + strcpy(th->status, "EXITING"); + th->state = NULL; + return 1; + + case SIGTRAP | (PTRACE_EVENT_STOP << 8): + th->stopped = 1; + th->signal = 0; + strcpy(th->status, "STOPPED"); + if (!th->doing) { + th->state = NULL; + } + return 1; + + case SIGTRAP | 0x80: + th->stopped = 1; + th->signal = 0; + th->doing = 0; + th->donext = 0; + strcpy(th->status, "SYSCALL"); + th->state = NULL; + return 1; + + case SIGTRAP: + th->stopped = 1; + th->signal = 0; + + int restart; + int bp = detect_breakpoint(th, &restart); + strcpy(th->status, (bp ? "BREAKPOINT" : "STEP")); + + if (restart) { + th->donext = th->doing; + th->doing = (th->doing ? PTRACE_SINGLESTEP : 0); + } else { + th->doing = th->donext; + th->donext = 0; } + + if (!th->doing) { + th->state = NULL; + } + + return 1; + + default: + th->stopped = 1; + th->signal = WSTOPSIG(status); + th->doing = 0; + th->donext = 0; + strcpy(th->status, "SIGNAL DELIVERY"); + th->state = NULL; + return 1; + } + } + + return -1; +} + +struct breakpoint *add_breakpoint(struct process *proc, unsigned long address) { + struct breakpoint *b = xmalloc(sizeof(*b)); + b->address = address; + b->text = 0; + b->installed = 0; + b->hits = 0; + b->user = 1; + b->stack = 0; + b->tid = 0; + b->enabled = 1; + list_insert(proc->breakpoints.end, b); + return b; +} + +struct breakpoint *get_breakpoint(struct process *proc, unsigned long address) { + struct list *breaks = &proc->breakpoints; + for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) { + if (b->address == address) { + return b; + } + } + return NULL; +} + +struct process *dbg_attach(pid_t pid, int child) { + const int OPTIONS = (child ? PTRACE_CHILD_OPTIONS : PTRACE_OPTIONS); + + char taskpath[32]; + snprintf(taskpath, sizeof(taskpath), "/proc/%li/task", (long)pid); + + DIR *taskdir = opendir(taskpath); + if (!taskdir) { + return NULL; + } + + struct dirent *task; + struct process *proc = new_process(pid, child); + + while ((task = readdir(taskdir))) { + pid_t id = strict_strtoul(task->d_name, 0); + + if (id != 0) { + if (ptrace(PTRACE_SEIZE, id, NULL, OPTIONS) < 0) { + closedir(taskdir); + dbg_detach(proc); + return NULL; } + struct thread *th = new_thread(proc, id); + list_insert(proc->threads.end, th); + } + } + + closedir(taskdir); + + interrupt_all_threads(proc); + capture_state(proc); + return proc; +} + +void dbg_detach(struct process *proc) { + /* ptrace requires that threads be stopped before calling PTRACE_DETACH. + * In some cases, a blocked thread can possibly have a pending trap which + * would crash the process if encountered after ptracing. We cycle an extra + * continue/interrupt to consume this trap event ourselves before + * detaching. */ + interrupt_all_threads(proc); + continue_all_threads(proc); + interrupt_all_threads(proc); + + /* Supplement to PTRACE_O_EXITKILL */ + if (proc->child) { + kill(proc->id, SIGKILL); + } + + while (proc->threads.head != proc->threads.end) { + struct thread *th = proc->threads.head; + if (th->id > 0) { + uninstall_breakpoints(th); + ptrace(PTRACE_DETACH, th->id, NULL, th->signal); + th->id = 0; + } + dbg_free(th); + } + + free_breakpoints(proc); + list_remove(proc); + free(proc); +} + +int dbg_free(struct thread *th) { + if (th->id <= 0) { + free_states(th); + list_remove(th); + free(th); + return 0; + } + return -1; +} + +void dbg_sync(struct process *proc) { + struct thread *acted = NULL; - if (b->enabled < 0 && !reenter) { - struct breakpoint *del = b; - b = b->prev; - list_remove(del); - free(del); + struct list *threads = &proc->threads; + for (struct thread *th = threads->head; th != threads->end; th = th->next) { + if (th->id > 0) { + if (/*!th->stopped &&*/ wait_thread(th)) { + acted = th; + } else if (th->stopped && th->doing) { + acted = th; } } } - return reenter; + if (acted) { + interrupt_all_threads(proc); + uninstall_breakpoints(acted); + capture_state(proc); + resume_threads(proc); + } } -int dbg_process(struct tracee *dbg, pid_t pid) { - if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) { +int dbg_intr(struct thread *th) { + if (th->id <= 0 || th->stopped) { return -1; } - list_init(&dbg->breaks); - list_init(&dbg->states); - dbg->state = NULL; - dbg->id = pid; - dbg->child = 0; - dbg->stopped = 0; - dbg->status = 0; - dbg->signal = 0; - dbg->cont = 0; - dbg->buff = NULL; - dbg->buffsize = 0; - - while (!dbg_wait(dbg)) {} - ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_OPTIONS); + /* todo: move this into dbg_sync? */ + /* probably not */ + ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL); + th->doing = 0; + th->donext = 0; return 0; } -int dbg_new_process(struct tracee *dbg, char **argv, struct console *cons) { - pid_t pid = fork(); - if (pid < 0) { +int dbg_cont(struct thread *th) { + if (th->id <= 0 || !th->stopped) { return -1; } - if (pid == 0) { - console_configslave(cons); - setpgid(0, 0); - ptrace(PTRACE_TRACEME, 0, NULL, NULL); - close_range(STDERR_FILENO+1, ~0U, CLOSE_RANGE_UNSHARE); - execvp(argv[0], argv); - exit(-1); - } - - list_init(&dbg->breaks); - list_init(&dbg->states); - dbg->state = NULL; - dbg->id = pid; - dbg->child = 1; - dbg->stopped = 0; - dbg->status = 0; - dbg->signal = 0; - dbg->cont = 0; - dbg->buff = NULL; - dbg->buffsize = 0; - - while (!dbg_wait(dbg)) {} - ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_EXITKILL | PTRACE_OPTIONS); + th->doing = PTRACE_SINGLESTEP; + th->donext = PTRACE_CONT; + th->clearstates = 1; return 0; } -int dbg_wait(struct tracee *dbg) { - if (dbg->stopped) { - return 1; +int dbg_syscall(struct thread *th) { + if (th->id <= 0 || !th->stopped) { + return -1; } - if (waitpid(dbg->id, &dbg->status, WNOHANG | __WALL) <= 0) { - return 0; + th->doing = PTRACE_SINGLESTEP; + th->donext = PTRACE_SYSCALL; + th->clearstates = 1; + return 0; +} + +int dbg_stepin(struct thread *th) { + if (!th->stopped) { + return -1; } - if (dbg->cont != 0) { - install_breakpoints(dbg); - ptrace(dbg->cont, dbg->id, NULL, NULL); - dbg->cont = 0; + if (th->state != th->states.tail) { + th->state = th->state->next; return 0; } - if (uninstall_breakpoints(dbg, 1)) { - ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL); - dbg->cont = PTRACE_CONT; - return 0; + if (th->id <= 0) { + return -1; } - capture_state(dbg); - dbg->stopped = 1; - return 1; + th->doing = PTRACE_SINGLESTEP; + th->donext = 0; + th->clearstates = 0; + return 0; } -/* note: move head < state < tail checks from main into these function - * add a dbg_stepback function */ +int dbg_stepover(struct thread *th) { + if (!th->stopped) { + return -1; + } + + if (th->state != th->states.tail) { + th->state = th->state->next; + return 0; + } -int dbg_stepin(struct tracee *dbg) { - /* consider making this a no-op if we are not stopped on a call */ - ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL); - dbg->stopped = 0; - dbg->cont = 0; - return 0; -} + if (th->id <= 0) { + return -1; + } -int dbg_stepover(struct tracee *dbg) { csh handle; cs_open(CS_ARCH_X86, CS_MODE_64, &handle); + cs_insn *insn = cs_malloc(handle); - uint64_t address = dbg->state->regs.rip; + uint64_t address = th->state->regs.rip; size_t size = 128; - const uint8_t *code = deref(dbg, address, size); - cs_insn *insn = cs_malloc(handle); + const uint8_t *code = deref(th, address, size); cs_disasm_iter(handle, &code, &size, &address, insn); if (insn->id == X86_INS_CALL) { - struct breakpoint *b = xmalloc(sizeof(*b)); - b->address = address; - b->stack = dbg->state->regs.rsp; + struct breakpoint *b = add_breakpoint(th->proc, address); + b->user = 0; + b->stack = th->state->regs.rsp; + b->tid = th->id; b->enabled = -1; - b->active = 0; - list_insert(dbg->breaks.end, b); - dbg->cont = PTRACE_CONT; - //dbg_cont(dbg, PTRACE_CONT); + + th->doing = PTRACE_SINGLESTEP; + th->donext = PTRACE_CONT; + th->clearstates = 0; } else { - dbg->cont = 0; - //dbg_stepin(dbg); + th->doing = PTRACE_SINGLESTEP; + th->donext = 0; + th->clearstates = 0; } - ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL); - dbg->stopped = 0; - cs_free(insn, 1); cs_close(&handle); return 0; } -/* -int dbg_stepout(struct tracee *dbg) { - unsigned long bp = dbg->state->regs.rbp; - unsigned long ra = *(unsigned long *)deref(dbg, bp+8, sizeof(unsigned long)); +int dbg_stepback(struct thread *th) { + if (!th->stopped) { + return -1; + } - struct breakpoint *b = xmalloc(sizeof(*b)); - b->address = ra; - b->stack = 0; - b->enabled = -1; - b->active = 0; - list_insert(dbg->breaks.end, b); - - ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL); - //clear_states(dbg); // keep this? - dbg->stopped = 0; - dbg->cont = PTRACE_CONT; - return 0; -} -*/ + if (th->state != th->states.head) { + th->state = th->state->prev; + return 0; + } -int dbg_cont(struct tracee *dbg, int cont) { - ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL); - clear_states(dbg); - dbg->stopped = 0; - dbg->cont = cont; - return 0; + return -1; } -void *deref(struct tracee *dbg, unsigned long addr, size_t size) { - (void)size; // todo +void *deref(struct thread *th, unsigned long address, size_t size) { + /* todo: size, and mapping breaks */ + (void)size; + + if (!th->state) { + return NULL; + } - struct list *maps = &dbg->state->maps; + struct list *maps = &th->state->maps; for (struct map *m = maps->head; m != maps->end; m = m->next) { - if (m->start <= addr && addr < m->end) { - return (unsigned char *)m->data + (addr - m->start); + if (m->start <= address && address < m->end) { + return (unsigned char *)m->data + (address - m->start); } } return NULL; } + + + + + + +//int dbg_stepout(struct tracee *dbg) { +// unsigned long bp = dbg->state->regs.rbp; +// unsigned long ra = *(unsigned long *)deref(dbg, bp+8, sizeof(unsigned long)); +// +// struct breakpoint *b = xmalloc(sizeof(*b)); +// b->address = ra; +// b->stack = 0; +// b->enabled = -1; +// b->active = 0; +// list_insert(dbg->breaks.end, b); +// +// ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL); +// //clear_states(dbg); // keep this? +// dbg->stopped = 0; +// dbg->cont = PTRACE_CONT; +// return 0; +//} |