summaryrefslogtreecommitdiffstats
path: root/debugger.c
diff options
context:
space:
mode:
authorMalfurious <m@lfurio.us>2023-10-02 03:18:21 -0400
committerMalfurious <m@lfurio.us>2024-04-24 13:31:08 -0400
commit46f72be263cf29688f684e90f2e149e5c911016b (patch)
tree08a36804eed9ce35470504419dd95553760b3ca7 /debugger.c
parent67a0755a248c9793a1e7a3cf73f4041b2103ebf7 (diff)
downloadmisplays-46f72be263cf29688f684e90f2e149e5c911016b.tar.gz
misplays-46f72be263cf29688f684e90f2e149e5c911016b.zip
Multithread version 3
Signed-off-by: Malfurious <m@lfurio.us>
Diffstat (limited to 'debugger.c')
-rw-r--r--debugger.c592
1 files changed, 300 insertions, 292 deletions
diff --git a/debugger.c b/debugger.c
index 46a6a15..6a1cb9d 100644
--- a/debugger.c
+++ b/debugger.c
@@ -1,6 +1,9 @@
#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>
@@ -10,60 +13,32 @@
#include "debugger.h"
#include "helpers.h"
-#define BREAKPOINT_INSN 0xcc
-
-//struct list global_processes = {0};
-//struct thread *global_thread = NULL;
-
static const int PTRACE_OPTIONS =
PTRACE_O_TRACECLONE |
- //PTRACE_O_TRACEEXIT |
PTRACE_O_TRACEEXEC |
PTRACE_O_TRACESYSGOOD;
-static int detect_breakpoint(struct thread *th) {
- int check = 0;
- int restart = 0;
-
- struct user_regs_struct regs;
- 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) {
- if (b->installed) {
- if (!check) {
- if (regs.rip - 1 == b->address) {
- regs.rip--;
- ptrace(PTRACE_SETREGS, th->id, NULL, &regs);
- check = 1;
- b->hits++;
-
- if (b->stack != 0 && b->stack != regs.rsp) {
- restart = 1;
- }
+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 = 100000;
+static const unsigned int BREAKPOINT_INSN = 0xcc;
- if (b->tid != 0 && b->tid != th->id) {
- restart = 1;
- }
-
- if (!b->enabled) {
- restart = 1;
- }
- }
- }
-
- ///* this needs moved, we have to finish the for-loop
- // * to actually know if restart is true or false... */
- //if (b->enabled < 0 && !restart) {
- // struct breakpoint *del = b;
- // b = b->prev;
- // list_remove(del);
- // free(del);
- //}
+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;
+}
- return restart;
+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) {
@@ -88,22 +63,51 @@ static void uninstall_breakpoints(struct thread *th) {
ptrace(PTRACE_POKETEXT, th->id, b->address, b->text);
b->installed = 0;
}
-
if (b->enabled < 0) {
- struct breakpoint *del = b;
- b = b->next;
- list_remove(del);
- free(del);
+ struct thread *t;
+ if (b->tid == 0 ||
+ ((t = thread_by_id(th->proc, b->tid)) && !t->shouldcont)) {
+ struct breakpoint *del = b;
+ b = b->next;
+ list_remove(del);
+ free(del);
+ }
}
}
}
-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 int detect_breakpoint(struct thread *th, int *restart) {
+ int ret = 0;
+ *restart = 0;
+
+ struct user_regs_struct regs;
+ struct iovec ivregs = { &regs, sizeof(regs) };
+ ptrace(PTRACE_GETREGSET, th->id, NT_PRSTATUS, &ivregs);
+
+ /* implement with get_breakpoint? */
+ struct list *breaks = &th->proc->breakpoints;
+ for (struct breakpoint *b = breaks->tail; b != breaks->end; b = b->prev) {
+ if (b->installed && (regs.rip - 1 == b->address)) {
+ regs.rip--;
+ ptrace(PTRACE_SETREGSET, th->id, NT_PRSTATUS, &ivregs);
+ b->hits++;
+ ret = b->user;
+
+ if (b->stack != 0 && b->stack != regs.rsp) {
+ *restart = 1;
+ }
+ if (b->tid != 0 && b->tid != th->id) {
+ *restart = 1;
+ }
+ if (!b->enabled) {
+ *restart = 1;
+ }
+
+ break;
+ }
}
+
+ return ret;
}
static void free_states(struct thread *th) {
@@ -125,155 +129,166 @@ static void free_states(struct thread *th) {
th->clearstates = 0;
}
-static void capture_state_thread(struct thread *th) {
- if (th->clearstates) {
- free_states(th);
- }
-
- struct state *s = xmalloc(sizeof(*s));
- ptrace(PTRACE_GETREGS, th->id, NULL, &s->regs);
- ptrace(PTRACE_GETFPREGS, th->id, NULL, &s->fpregs);
- list_init(&s->maps);
-
- list_insert(th->states.end, s);
- th->state = s;
+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->shouldcont) {
+ if (th->clearstates) {
+ free_states(th);
+ }
- char mapspath[32], entry[512];
- snprintf(mapspath, sizeof(mapspath), "/proc/%li/maps", (long)th->id);
+ struct state *s = xmalloc(sizeof(*s));
+ struct iovec regs = { &s->regs, sizeof(s->regs) };
+ ptrace(PTRACE_GETREGSET, th->id, NT_PRSTATUS, &regs);
+ 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;
+ }
- 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);
+ list_insert(s->maps.end, m);
+ }
- 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;
+ fclose(maps);
}
+ }
+ }
+}
- list_insert(s->maps.end, m);
+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)) {}
+ }
+ if (STOP_ALL_ON_EVENT) {
+ th->shouldcont = 0;
}
+ }
+}
- fclose(maps);
+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) {
+ ptrace(PTRACE_CONT, th->id, NULL, th->signal);
+ th->stopped = 0;
+ th->signal = 0;
+ th->cont = 0;
+ th->shouldcont = 2;
+ strcpy(th->status, "RUNNING");
+ th->clearstates = 1;
+ }
}
}
-static void capture_state(struct thread *th, int all) {
- (void)all;
- struct list *threads = &th->proc->threads;
- for (struct thread *t = threads->head; t != threads->end; t = t->next) {
- if (!t->state) {
- capture_state_thread(t);
+/* 0: stop 1: singlestep 2: cont 3: syscall */
+/* this is simplified, surely incorrect, and needs updated once testable */
+static void resume_threads(struct process *proc) {
+ struct list *threads = &proc->threads;
+ for (struct thread *th = threads->head; th != threads->end; th = th->next) {
+ if (th->shouldcont) {
+ install_breakpoints(th);
+ ptrace(PTRACE_CONT, th->id, NULL, th->signal);
+ th->stopped = 0;
+ th->signal = 0;
+ th->cont = 0;
+ strcpy(th->status, "RUNNING");
}
}
- //if (all) {
- // struct list *threads = &th->proc->threads;
- // for (struct thread *t = threads->head; t != threads->end; t = t->next) {
- // capture_state_thread(t);
- // }
- //} else {
- // capture_state_thread(th);
- //}
}
-static struct process *new_process(pid_t pid, int child) {
+static struct process *new_process(pid_t id, int child) {
struct process *proc = xmalloc(sizeof(*proc));
- proc->id = pid;
+ proc->id = id;
proc->child = child;
- list_init(&proc->threads);
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 tid) {
+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 = tid;
+ th->id = id;
th->stopped = 0;
th->signal = 0;
th->cont = 0;
- th->status = "RUNNING";
+ th->shouldcont = 0;
+ strcpy(th->status, "RUNNING");
return th;
}
-static int interrupt_all_threads(struct process *proc) {
- int stopped = 0;
- 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, 1)) {}
- stopped = 1;
- }
- }
- return stopped;
-}
-
-void add_breakpoint(struct process *proc, unsigned long address, unsigned long stack, pid_t tid, int enabled) {
+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->stack = stack;
- b->tid = tid;
- b->enabled = enabled;
+ b->user = 1;
+ b->stack = 0;
+ b->tid = 0;
+ b->enabled = 1;
list_insert(proc->breakpoints.end, b);
+ return b;
}
-int is_breakpoint(struct process *proc, unsigned long address) {
+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 1;
+ return b;
}
}
- return 0;
+ return NULL;
}
-//int dbg_attach(pid_t pid, int child) {
struct process *dbg_attach(pid_t pid, int child) {
- //if (global_processes.end == NULL) {
- // list_init(&global_processes);
- //}
-
- struct process *proc = new_process(pid, child);
- //list_insert(global_processes.end, proc);
-
- int options = PTRACE_OPTIONS;
- if (child) {
- options |= PTRACE_O_EXITKILL;
- }
+ 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) {
- dbg_detach(proc);
return NULL;
}
struct dirent *task;
+ struct process *proc = new_process(pid, child);
+
while ((task = readdir(taskdir))) {
- pid_t id = strtoul(task->d_name, NULL, 0);
+ pid_t id = strict_strtoul(task->d_name, 0);
if (id != 0) {
- if (ptrace(PTRACE_SEIZE, id, NULL, options) < 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);
}
@@ -281,137 +296,137 @@ struct process *dbg_attach(pid_t pid, int child) {
closedir(taskdir);
- //global_thread = proc->threads.head;
interrupt_all_threads(proc);
- capture_state(proc->threads.head, 0);
+ capture_state(proc);
return proc;
}
-int dbg_detach(struct process *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.
+ * Threads must actually get scheduled for this to be effective, which is
+ * the reason for the usleep. A better approach is welcome here. */
interrupt_all_threads(proc);
- dbg_realcont(proc->threads.head);
- usleep(100000);
+ continue_all_threads(proc);
+ usleep(SCHEDULER_DELAY);
interrupt_all_threads(proc);
- uninstall_breakpoints(proc->threads.head);
- free_breakpoints(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) {
+ if (th->id > 0) {
+ uninstall_breakpoints(th);
ptrace(PTRACE_DETACH, th->id, NULL, th->signal);
+ th->id = 0;
}
- free_states(th);
- list_remove(th);
- free(th);
+ dbg_free(th);
}
+ free_breakpoints(proc);
list_remove(proc);
free(proc);
- return 0;
}
-int dbg_wait(struct thread *th, int recursion) {
- if (th->id < 0) {
+int dbg_free(struct thread *th) {
+ if (th->id <= 0) {
+ free_states(th);
+ list_remove(th);
+ free(th);
+ return 0;
+ }
+ return -1;
+}
+
+int dbg_wait(struct thread *th, int primary) {
+ if (th->id <= 0) {
return -1;
+ } else if (th->stopped) {
+ return 1;
}
- /* todo: check what happens if we call on an already known
- * stopped thread? */
int status;
if (waitpid(th->id, &status, __WALL | WNOHANG) <= 0) {
return 0;
}
if (WIFEXITED(status)) {
- th->id = -1;
+ th->id = 0;
th->stopped = 1;
th->signal = WEXITSTATUS(status);
th->cont = 0;
- th->status = "EXITED";
+ th->shouldcont = 0;
+ strcpy(th->status, "EXITED");
return 1;
}
if (WIFSIGNALED(status)) {
- th->id = -2;
+ th->id = 0;
th->stopped = 1;
th->signal = WTERMSIG(status);
th->cont = 0;
- th->status = "TERMINATED";
+ th->shouldcont = 0;
+ strcpy(th->status, "TERMINATED");
return 1;
}
- int stopped;
- struct thread *newth;
- struct list *threads;
-
+ struct thread *newth; // event thread
unsigned long eventmsg;
ptrace(PTRACE_GETEVENTMSG, th->id, NULL, &eventmsg);
+ /* todo: process status messages */
if (WIFSTOPPED(status)) {
switch (status >> 8) {
/* todo: other ptrace event stops */
case SIGTRAP | (PTRACE_EVENT_CLONE << 8):
newth = new_thread(th->proc, eventmsg);
list_insert(th->proc->threads.end, newth);
- while (!dbg_wait(newth, 1)) {}
+ while (!dbg_wait(newth, 0)) {}
th->stopped = 1;
th->signal = 0;
th->cont = 0;
- th->status = "CLONE EVENT";
+ th->shouldcont = 0;
+ strcpy(th->status, "CLONE");
th->state = NULL;
- if (!recursion) {
- stopped = interrupt_all_threads(th->proc);
+ if (primary) {
+ interrupt_all_threads(th->proc);
uninstall_breakpoints(th);
- capture_state(th, stopped);
+ capture_state(th->proc);
+ resume_threads(th->proc);
}
return 1;
case SIGTRAP | (PTRACE_EVENT_EXEC << 8):
- /* eventmsg contains the tid that actually did the execve */
- threads = &th->proc->threads;
- for (struct thread *t = threads->head; t != threads->end; t = t->next) {
- if (t->id == (pid_t)eventmsg && (pid_t)eventmsg != th->id) {
- t->id = -1;
- t->stopped = 1;
- t->signal = 0;
- t->cont = 0;
- t->status = "EXITED";
- break;
- }
+ if ((pid_t)eventmsg != th->id) {
+ newth = thread_by_id(th->proc, eventmsg);
+ newth->id = 0;
+ newth->stopped = 1;
+ newth->signal = 0;
+ newth->cont = 0;
+ newth->shouldcont = 0;
+ strcpy(newth->status, "EXITED");
}
th->stopped = 1;
th->signal = 0;
th->cont = 0;
- th->status = "EXEC EVENT";
- th->state = NULL;
-
- if (!recursion) {
- stopped = interrupt_all_threads(th->proc);
- uninstall_breakpoints(th);
- capture_state(th, stopped);
- }
-
- return 1;
-
- case SIGTRAP | (PTRACE_EVENT_EXIT << 8):
- th->stopped = 1;
- th->signal = 0; /* eventmsg has exit code, but would inject sig */
- th->cont = 0;
- th->status = "EXIT EVENT";
+ th->shouldcont = 0;
+ strcpy(th->status, "EXECVE");
th->state = NULL;
- if (!recursion) {
- stopped = interrupt_all_threads(th->proc);
+ if (primary) {
+ interrupt_all_threads(th->proc);
uninstall_breakpoints(th);
- capture_state(th, stopped);
+ capture_state(th->proc);
+ resume_threads(th->proc);
}
return 1;
@@ -420,28 +435,30 @@ int dbg_wait(struct thread *th, int recursion) {
th->stopped = 1;
th->signal = 0;
th->cont = 0;
- th->status = "STOP EVENT";
- th->state = NULL;
+ strcpy(th->status, "STOPPED");
- if (!recursion) {
- stopped = interrupt_all_threads(th->proc);
+ if (primary) {
+ interrupt_all_threads(th->proc);
uninstall_breakpoints(th);
- capture_state(th, stopped);
+ capture_state(th->proc);
+ resume_threads(th->proc);
}
- return 1;
+ return th->stopped;
case SIGTRAP | 0x80:
th->stopped = 1;
th->signal = 0;
th->cont = 0;
- th->status = "SYSCALL EVENT";
+ th->shouldcont = 0;
+ strcpy(th->status, "SYSCALL");
th->state = NULL;
- if (!recursion) {
- stopped = interrupt_all_threads(th->proc);
+ if (primary) {
+ interrupt_all_threads(th->proc);
uninstall_breakpoints(th);
- capture_state(th, stopped);
+ capture_state(th->proc);
+ resume_threads(th->proc);
}
return 1;
@@ -449,52 +466,35 @@ int dbg_wait(struct thread *th, int recursion) {
case SIGTRAP:
th->stopped = 1;
th->signal = 0;
- th->status = "STEP/BREAKPOINT";
-
- if (th->cont != 0) {
- /* gdb this portion. are there race conditions
- * that matter?? */
- install_breakpoints(th);
- ptrace(th->cont, th->id, NULL, NULL);
- th->cont = 0;
- th->stopped = 0;
- th->status = "RUNNING";
- return 0;
- }
-
- /* todo: Test two threads hitting a breakpoint at
- * the same time. */
- int restart = detect_breakpoint(th);
- if (!restart) {
- th->state = NULL;
- }
- if (!recursion) {
- stopped = interrupt_all_threads(th->proc);
- if (!restart) {
- uninstall_breakpoints(th);
- capture_state(th, stopped);
- }
- }
+ int restart;
+ int bp = detect_breakpoint(th, &restart);
+ strcpy(th->status, (bp ? "BREAKPOINT" : "STEP"));
+ //th->shouldcont = (b && b->enabled == 0);
+ th->shouldcont = (restart ? 2 : 0);
- if (restart) {
- dbg_cont(th, PTRACE_CONT);
- return 0;
+ if (primary) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th);
+ capture_state(th->proc);
+ resume_threads(th->proc);
}
- return 1;
+ return th->stopped;
default:
th->stopped = 1;
th->signal = WSTOPSIG(status);
th->cont = 0;
- th->status = "SIGNAL DELIVERY";
+ th->shouldcont = 0;
+ strcpy(th->status, "SIGNAL DELIVERY");
th->state = NULL;
- if (!recursion) {
- stopped = interrupt_all_threads(th->proc);
+ if (primary) {
+ interrupt_all_threads(th->proc);
uninstall_breakpoints(th);
- capture_state(th, stopped);
+ capture_state(th->proc);
+ resume_threads(th->proc);
}
return 1;
@@ -505,58 +505,70 @@ int dbg_wait(struct thread *th, int recursion) {
}
int dbg_intr(struct thread *th) {
- if (th->id < 0 || th->stopped) {
+ if (th->id <= 0 || th->stopped) {
return -1;
}
ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL);
+ th->shouldcont = 0;
return 0;
}
-int dbg_cont(struct thread *th, int cont) {
- if (th->id < 0 || !th->stopped) {
+int dbg_cont(struct thread *th) {
+ if (th->id <= 0 || !th->stopped) {
return -1;
}
- 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);
+ ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal);
+ th->stopped = 0;
+ th->signal = 0;
+ th->cont = PTRACE_CONT;
+ th->shouldcont = 1;
+ strcpy(th->status, "RUNNING");
+ th->clearstates = 1;
+ return 0;
+}
- t->stopped = 0;
- t->signal = 0;
- t->cont = cont;
- t->status = "RUNNING";
- t->clearstates = 1;
+int dbg_syscall(struct thread *th) {
+ if (th->id <= 0 || !th->stopped) {
+ return -1;
}
+ ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal);
+ th->stopped = 0;
+ th->signal = 0;
+ th->cont = PTRACE_SYSCALL;
+ th->shouldcont = 1;
+ strcpy(th->status, "RUNNING");
+ th->clearstates = 1;
return 0;
}
-int dbg_realcont(struct thread *th) {
- if (th->id < 0 || !th->stopped) {
+int dbg_stepin(struct thread *th) {
+ if (!th->stopped) {
return -1;
}
- struct list *threads = &th->proc->threads;
- for (struct thread *t = threads->head; t != threads->end; t = t->next) {
- ptrace(PTRACE_CONT, t->id, NULL, NULL);
+ if (th->state != th->states.tail) {
+ th->state = th->state->next;
+ return 0;
+ }
- t->stopped = 0;
- t->signal = 0;
- t->cont = 0;
- t->status = "RUNNING";
- t->clearstates = 1;
+ if (th->id <= 0) {
+ return -1;
}
+ ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal);
+ th->stopped = 0;
+ th->signal = 0;
+ th->cont = PTRACE_SINGLESTEP;
+ th->shouldcont = 1;
+ strcpy(th->status, "RUNNING");
+ th->clearstates = 0;
return 0;
}
-int dbg_step(struct thread *th, int stepover) {
- // todo: support step-out
- //if (th->id < 0 || !th->stopped) {
- // return -1;
- //}
-
+int dbg_stepover(struct thread *th) {
if (!th->stopped) {
return -1;
}
@@ -566,39 +578,39 @@ int dbg_step(struct thread *th, int stepover) {
return 0;
}
- if (th->id < 0) {
+ if (th->id <= 0) {
return -1;
}
- csh cshandle;
- cs_open(CS_ARCH_X86, CS_MODE_64, &cshandle);
- cs_insn *insn = cs_malloc(cshandle);
+ csh handle;
+ cs_open(CS_ARCH_X86, CS_MODE_64, &handle);
+ cs_insn *insn = cs_malloc(handle);
uint64_t address = th->state->regs.rip;
size_t size = 128;
const uint8_t *code = deref(th, address, size);
- cs_disasm_iter(cshandle, &code, &size, &address, insn);
+ cs_disasm_iter(handle, &code, &size, &address, insn);
+
+ int ret = -1;
+ if (insn->id == X86_INS_CALL) {
+ struct breakpoint *b = add_breakpoint(th->proc, address);
+ b->user = 0;
+ b->stack = th->state->regs.rsp;
+ b->tid = th->id;
+ b->enabled = -1;
- if (insn->id == X86_INS_CALL && stepover) {
- add_breakpoint(th->proc, address, th->state->regs.rsp, th->id, -1);
- th->cont = PTRACE_CONT;
+ ret = dbg_cont(th);
} else {
- th->cont = 0;
+ ret = dbg_stepin(th);
}
- ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal);
-
- th->stopped = 0;
- th->signal = 0;
- th->status = "RUNNING";
-
cs_free(insn, 1);
- cs_close(&cshandle);
- return 0;
+ cs_close(&handle);
+ return ret;
}
-int dbg_pets(struct thread *th) {
+int dbg_stepback(struct thread *th) {
if (!th->stopped) {
return -1;
}
@@ -633,10 +645,6 @@ void *deref(struct thread *th, unsigned long address, size_t size) {
-///* 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.
-// */
//int dbg_stepout(struct tracee *dbg) {
// unsigned long bp = dbg->state->regs.rbp;