summaryrefslogtreecommitdiffstats
path: root/debugger.c
diff options
context:
space:
mode:
Diffstat (limited to 'debugger.c')
-rw-r--r--debugger.c744
1 files changed, 565 insertions, 179 deletions
diff --git a/debugger.c b/debugger.c
index 19d9edc..5fc3978 100644
--- a/debugger.c
+++ b/debugger.c
@@ -1,5 +1,8 @@
+#include <dirent.h>
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/wait.h>
@@ -10,18 +13,117 @@
#include "debugger.h"
#include "helpers.h"
-static const int PTRACE_OPTIONS = PTRACE_O_TRACESYSGOOD;
+static const int PTRACE_OPTIONS = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE;
+static const int INTERRUPT_ON_SEIZE = 1;
+
+static int detect_breakpoint(struct thread *th) {
+ struct user_regs_struct regs;
+ int check = 0;
+ int restart = 0;
+
+ ptrace(PTRACE_GETREGS, th->id, NULL, &regs);
+
+ struct list *breaks = &th->proc->breakpoints;
+ for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) {
+ /* should be if (b->active) ?
+ * but there is a potential other bug that can happen
+ * with the order of events in dbg_wait() */
+ if (b->enabled) {
+ if (!check) {
+ if (regs.rip - 1 == b->address) {
+ regs.rip--;
+ ptrace(PTRACE_SETREGS, th->id, NULL, &regs);
+ check = 1;
+
+ if (b->stack != 0 && b->stack != regs.rsp) {
+ restart = 1;
+ }
+
+ if (b->tid != 0 && b->tid != th->id) {
+ restart = 1;
+ }
+ }
+ }
+
+ if (b->enabled < 0 && !restart) {
+ struct breakpoint *del = b;
+ b = b->prev;
+ list_remove(del);
+ free(del);
+ }
+ }
+ }
+
+ return restart;
+}
+
+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->enabled && !b->active) {
+ unsigned long data;
+ data = ptrace(PTRACE_PEEKTEXT, th->id, b->address, NULL);
+ b->orig = data;
+
+ data = (data & ~0xff) | BREAKPOINT_INSN;
+ ptrace(PTRACE_POKETEXT, th->id, b->address, data);
+ b->active = 1;
+ }
+ }
+}
+
+static void uninstall_breakpoints(struct process *proc) {
+ struct list *breaks = &proc->breakpoints;
+ for (struct breakpoint *b = breaks->tail; b != breaks->end; b = b->prev) {
+ if (b->active) {
+ ptrace(PTRACE_POKETEXT, proc->id, b->address, b->orig);
+ b->active = 0;
+ }
+ }
+}
+
+static void clear_breakpoints(struct process *proc) {
+ while (proc->breakpoints.head != proc->breakpoints.end) {
+ struct breakpoint *b = proc->breakpoints.head;
+ list_remove(b);
+ free(b);
+ }
+}
+
+static void clear_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);
+ }
+
+ th->state = NULL;
+ th->clearstates = 0;
+}
+
+static void capture_state(struct thread *th) {
+ if (th->clearstates) {
+ clear_states(th);
+ }
-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);
+ ptrace(PTRACE_GETREGS, th->id, NULL, &s->regs);
+ ptrace(PTRACE_GETFPREGS, th->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);
+ 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) {
@@ -33,7 +135,7 @@ static void capture_state(struct tracee *dbg) {
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) {
+ if (process_vm_readv(th->id, &loc, 1, &rem, 1, 0) < 0) {
free(m->data);
free(m);
continue;
@@ -46,232 +148,304 @@ static void capture_state(struct tracee *dbg) {
}
}
-static void clear_states(struct tracee *dbg) {
- while (dbg->states.head != dbg->states.end) {
- struct state *s = dbg->states.head;
- while (s->maps.head != s->maps.end) {
- struct map *m = s->maps.head;
- list_remove(m);
- free(m->data);
- free(m);
+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->cont = 0;
+ th->status = "RUNNING";
+ return 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);
+ while (!dbg_wait(th, 0)) {}
}
- list_remove(s);
- free(s);
}
- dbg->state = NULL;
}
-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;
+void add_breakpoint(struct process *proc, unsigned long address, unsigned long stack, pid_t tid, int enabled) {
+ struct breakpoint *b = xmalloc(sizeof(*b));
+ b->orig = 0;
+ b->address = address;
+ b->stack = stack;
+ b->tid = tid;
+ b->enabled = enabled;
+ b->active = 0;
+ list_insert(proc->breakpoints.end, b);
+}
- data = (data & ~0xff) | BREAKPOINT_INSN;
- ptrace(PTRACE_POKETEXT, dbg->id, b->address, data);
- b->active = 1;
+int is_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 1;
}
}
+ return 0;
}
-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, &regs);
- }
+int dbg_process(struct process *proc, pid_t pid, int child) {
+ proc->id = pid;
+ proc->child = child;
+ list_init(&proc->breakpoints);
+ list_init(&proc->threads);
- 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;
+ char taskpath[32];
+ snprintf(taskpath, sizeof(taskpath), "/proc/%li/task", (long)pid);
- /* detect stop at breakpoint */
- if (stop && !ch) {
- if (regs.rip - 1 == b->address) {
- regs.rip--;
- ptrace(PTRACE_SETREGS, dbg->id, NULL, &regs);
- ch = 1;
+ DIR *taskdir = opendir(taskpath);
+ if (!taskdir) {
+ return -1;
+ }
- if (b->stack != 0 && b->stack != regs.rsp) {
- reenter = 1;
- }
- }
- }
+ struct dirent *task;
+ while ((task = readdir(taskdir))) {
+ pid_t id = strtoul(task->d_name, NULL, 0);
- if (b->enabled < 0 && !reenter) {
- struct breakpoint *del = b;
- b = b->prev;
- list_remove(del);
- free(del);
+ if (id != 0) {
+ if (ptrace(PTRACE_SEIZE, id, NULL, PTRACE_OPTIONS) < 0) {
+ closedir(taskdir);
+ return -1;
}
+
+ struct thread *th = new_thread(proc, id);
+ list_insert(proc->threads.end, th);
}
}
- return reenter;
-}
-
-int dbg_process(struct tracee *dbg, pid_t pid) {
- if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
- return -1;
+ if (INTERRUPT_ON_SEIZE) {
+ interrupt_all_threads(proc);
}
- 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);
+ closedir(taskdir);
return 0;
}
-int dbg_new_process(struct tracee *dbg, char **argv, struct console *cons) {
- pid_t pid = fork();
- if (pid < 0) {
- return -1;
- }
+int dbg_detach(struct process *proc) {
+ interrupt_all_threads(proc);
+ uninstall_breakpoints(proc);
+ clear_breakpoints(proc);
- 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);
+ while (proc->threads.head != proc->threads.end) {
+ struct thread *th = proc->threads.head;
+ if (th->id >= 0) {
+ ptrace(PTRACE_DETACH, th->id, NULL, th->signal);
+ }
+ clear_states(th);
+ list_remove(th);
+ free(th);
}
- 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);
return 0;
}
-int dbg_wait(struct tracee *dbg) {
- if (dbg->stopped) {
- return 1;
+int dbg_wait(struct thread *th, int dostops) {
+ if (th->id < 0) {
+ return -1;
}
- if (waitpid(dbg->id, &dbg->status, WNOHANG | __WALL) <= 0) {
+ int status;
+ if (waitpid(th->id, &status, __WALL | WNOHANG) <= 0) {
return 0;
}
- if (dbg->cont != 0) {
- install_breakpoints(dbg);
- ptrace(dbg->cont, dbg->id, NULL, NULL);
- dbg->cont = 0;
- return 0;
+ if (WIFEXITED(status)) {
+ th->id = -1;
+ th->stopped = 1;
+ th->signal = WEXITSTATUS(status);
+ th->cont = 0;
+ th->status = "EXITED";
+ return 1;
}
- if (uninstall_breakpoints(dbg, 1)) {
- ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
- dbg->cont = PTRACE_CONT;
- return 0;
+ if (WIFSIGNALED(status)) {
+ th->id = -2;
+ th->stopped = 1;
+ th->signal = WTERMSIG(status);
+ th->cont = 0;
+ th->status = "TERMINATED";
+ return 1;
}
- capture_state(dbg);
- dbg->stopped = 1;
- return 1;
-}
+ unsigned long eventmsg;
+ struct thread *newth;
+ ptrace(PTRACE_GETEVENTMSG, th->id, NULL, &eventmsg);
+
+ if (WIFSTOPPED(status)) {
+ switch (status >> 8) {
+ case SIGTRAP | (PTRACE_EVENT_CLONE << 8):
+ newth = new_thread(th->proc, eventmsg);
+ list_insert(th->proc->threads.end, newth);
+ while (!dbg_wait(newth, 0)) {}
+
+ th->stopped = 1;
+ th->signal = 0;
+ th->cont = 0;
+ th->status = "CLONE EVENT";
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
-/* note: move head < state < tail checks from main into these function
- * add a dbg_stepback function */
+ capture_state(th);
+ return 1;
-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;
+ case SIGTRAP | (PTRACE_EVENT_EXIT << 8):
+ th->stopped = 1;
+ th->signal = 0; //eventmsg;
+ th->cont = 0;
+ th->status = "EXIT EVENT";
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
+
+ capture_state(th);
+ return 1;
+
+ case SIGTRAP | (PTRACE_EVENT_STOP << 8):
+ th->stopped = 1;
+ th->signal = 0;
+ th->cont = 0;
+ th->status = "STOP EVENT";
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
+
+ capture_state(th);
+ return 1;
+
+ case SIGTRAP | 0x80:
+ th->stopped = 1;
+ th->signal = 0;
+ th->cont = 0;
+ th->status = "SYSCALL EVENT";
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
+
+ capture_state(th);
+ return 1;
+
+ case SIGTRAP:
+ th->stopped = 1;
+ th->signal = 0;
+
+ if (th->cont != 0) {
+ install_breakpoints(th);
+ ptrace(th->cont, th->id, NULL, NULL);
+ th->cont = 0;
+ th->stopped = 0;
+ th->status = "RUNNING";
+ return 0;
+ }
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
+
+ if (detect_breakpoint(th)) {
+ dbg_cont(th, PTRACE_CONT);
+ return 0;
+ }
+
+ th->status = "STEP/BREAKPOINT";
+ capture_state(th);
+ return 1;
+
+ default:
+ th->stopped = 1;
+ th->signal = WSTOPSIG(status);
+ th->cont = 0;
+ th->status = "SIGNAL DELIVERY";
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
+
+ capture_state(th);
+ return 1;
+ }
+ }
+
+ return -1;
}
-int dbg_stepover(struct tracee *dbg) {
- csh handle;
- cs_open(CS_ARCH_X86, CS_MODE_64, &handle);
-
- uint64_t address = dbg->state->regs.rip;
- size_t size = 128;
- const uint8_t *code = deref(dbg, address, size);
- cs_insn *insn = cs_malloc(handle);
-
- 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;
- b->enabled = -1;
- b->active = 0;
- list_insert(dbg->breaks.end, b);
- dbg->cont = PTRACE_CONT;
- //dbg_cont(dbg, PTRACE_CONT);
- } else {
- dbg->cont = 0;
- //dbg_stepin(dbg);
+int dbg_cont(struct thread *th, int cont) {
+ if (th->id < 0 || !th->stopped) {
+ return -1;
}
- ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
- dbg->stopped = 0;
+ struct list *threads = &th->proc->threads;
+ for (struct thread *t = threads->head; t != threads->end; t = t->next) {
+ ptrace(PTRACE_SINGLESTEP, t->id, NULL, t->signal);
+
+ t->stopped = 0;
+ t->signal = 0;
+ t->cont = cont;
+ t->status = "RUNNING";
+ t->clearstates = 1;
+ }
- cs_free(insn, 1);
- cs_close(&handle);
+ /* are there timing concerns here? Critically, we
+ * are doing this before the event loop has a chance to
+ * wait on any thread and actually start it in the cont */
+ /* cant do it here, since no thread is stopped (ptrace
+ * returns ESRCH). */
+ //install_breakpoints(th->proc);
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_stepin(struct thread *th) {
+ if (th->id < 0 || !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);
+ if (ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal) < 0) {
+ return -1;
+ }
- ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
- //clear_states(dbg); // keep this?
- dbg->stopped = 0;
- dbg->cont = PTRACE_CONT;
+ th->stopped = 0;
+ th->signal = 0;
+ th->cont = 0;
+ th->status = "RUNNING";
+ th->clearstates = 0;
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;
+int dbg_intr(struct thread *th) {
+ if (th->id < 0 || th->stopped) {
+ return -1;
+ }
+
+ ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL);
return 0;
}
-void *deref(struct tracee *dbg, unsigned long addr, size_t size) {
- (void)size; // todo
+void *deref(struct thread *th, unsigned long addr, size_t size) {
+ (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);
@@ -280,3 +454,215 @@ void *deref(struct tracee *dbg, unsigned long addr, size_t size) {
return NULL;
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+///*
+//static const char *strstatus(int status) {
+// static char buff[128];
+//
+// if (WIFEXITED(status)) {
+// snprintf(buff, sizeof(buff), "exited: %i", WEXITSTATUS(status));
+// } else if (WIFSIGNALED(status)) {
+// snprintf(buff, sizeof(buff), "terminated: %s", strsignal(WTERMSIG(status)));
+// } else if (WIFSTOPPED(status)) {
+// snprintf(buff, sizeof(buff), "stopped: %s", strsignal(WSTOPSIG(status)));
+// } else {
+// snprintf(buff, sizeof(buff), "UNKNOWN STATUS VALUE");
+// }
+//
+// return buff;
+//}
+//*/
+//
+//static int all_cont(struct process *proc) {
+// /* install breakpoints */
+// struct list *breaks = &proc->breakpoints;
+// for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) {
+// if (b->enabled) {
+// unsigned long data;
+// data = ptrace(PTRACE_PEEKTEXT, proc->id, b->address, NULL);
+// b->orig = data;
+//
+// data = (data & ~0xff) | BREAKPOINT_INSN;
+// ptrace(PTRACE_POKETEXT, proc->id, b->address, data);
+// b->active = 1;
+// }
+// }
+//
+// /* continue all threads */
+// struct list *threads = &proc->threads;
+// for (struct thread *th = threads->head; th != threads->end; th = th->next) {
+// ptrace(PTRACE_);
+// }
+// return -1;
+//}
+//
+///* current problem to solve:
+// * if a SINGLESTEP is interrupted (it was over a blocking syscall for example),
+// * a CONT out of that interruption will stop AS IF the SINGLESTEP had completed.
+// */
+///*
+// * new:
+// * next step is to trigger all threads starting on continue.
+// */
+
+
+
+
+
+
+
+
+/* PTRACE_GETSIGINFO ! */
+/* handle ptrace() -> ESRCH as unexpectedly dead tracee */
+
+
+
+
+
+
+
+
+
+
+//
+//int dbg_new_process(struct tracee *dbg, char **argv, struct console *cons) {
+// pid_t pid = fork();
+// if (pid < 0) {
+// 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->gid = 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, NULL)) {}
+// ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_EXITKILL | PTRACE_OPTIONS);
+// return 0;
+//}
+//
+///* note: move head < state < tail checks from main into these function
+// * add a dbg_stepback function */
+//
+//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;
+//}
+//
+//int dbg_stepover(struct tracee *dbg) {
+// csh handle;
+// cs_open(CS_ARCH_X86, CS_MODE_64, &handle);
+//
+// uint64_t address = dbg->state->regs.rip;
+// size_t size = 128;
+// const uint8_t *code = deref(dbg, address, size);
+// cs_insn *insn = cs_malloc(handle);
+//
+// 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;
+// b->enabled = -1;
+// b->active = 0;
+// list_insert(dbg->breaks.end, b);
+// dbg->cont = PTRACE_CONT;
+// //dbg_cont(dbg, PTRACE_CONT);
+// } else {
+// dbg->cont = 0;
+// //dbg_stepin(dbg);
+// }
+//
+// 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));
+//
+// 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;
+//}
+//*/
+//
+//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;
+//}
+//
+//void *deref(struct tracee *dbg, unsigned long addr, size_t size) {
+// (void)size; // todo
+//
+// struct list *maps = &dbg->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);
+// }
+// }
+//
+// return NULL;
+//}
+//
+//void dbg_free(struct tracee *dbg) {
+// while (dbg->breaks.head != dbg->breaks.end) {
+// struct breakpoint *b = dbg->breaks.head;
+// list_remove(b);
+// free(b);
+// }
+// clear_states(dbg);
+// free(dbg->buff);
+//}