diff --git a/18-ptrace/systrace.c b/18-ptrace/systrace.c
index 9c5b260..c200a29 100644
--- a/18-ptrace/systrace.c
+++ b/18-ptrace/systrace.c
@@ -21,9 +21,37 @@
// which we got from PTRACE_GET_SYSCALL_INFO. This function also uses
// the system call table to print pretty syscall names.
void print_syscall(struct ptrace_syscall_info *info) {
- printf("%s\n", names[__NR_write].name);
- // FIXME: Handle info->op == PTRACE_SYSCALL_INFO_ENTRY
- // FIXME: Handle info->op == PTRACE_SYSCALL_INFO_EXIT
+ char buf[128];
+ if (info->op == PTRACE_SYSCALL_INFO_ENTRY) {
+ char *p = buf;
+ int args = 6;
+ if (info->entry.nr < (sizeof(names) / sizeof(*names))) {
+ // Get the system call name
+ p+= sprintf(p, "%s(", names[info->entry.nr].name);
+ // Less than 6 arguments?
+ if (names[info->entry.nr].argc < args)
+ args = names[info->entry.nr].argc;
+ } else {
+ p+= sprintf(p, "syscall(%lld, ", info->entry.nr);
+ }
+ // Format the arguments
+ for (unsigned i = 0; i < args; i++) {
+ p += sprintf(p, "%lld", info->entry.args[i]);
+ if ((i + 1) < args)
+ p += sprintf(p, ", ");
+ }
+ p += sprintf(p, ")");
+ fprintf(stderr, "%-70s", buf);
+ } else if (info->op == PTRACE_SYSCALL_INFO_EXIT) {
+ // The system call did complete
+ if (info->exit.is_error) {
+ fprintf(stderr, " = %lld (%s)\n",
+ info->exit.rval,
+ strerror(-1* info->exit.rval));
+ } else {
+ fprintf(stderr, " = %lld\n", info->exit.rval);
+ }
+ }
}
int main(int argc, char *argv[]) {
@@ -31,10 +59,67 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "usage: %s CMD [ARGS...]\n", argv[0]);
return -1;
}
- // FIXME/child: Issue PTRACE_TRACEME
- // FIXME/child: execvp(argv[1], &argv[1])
- // FIXME/parent: Wait for the first SIGTRAP
- // FIXME/parent: set PTRACE_O_TRACESYSGOOD option
- // FIXME/parent: PTRACE_SYSCALL to the next syscall
- // FIXME/parent: Use PTRACE_GET_SYSCALL_INFO to get details
+ pid_t child = fork();
+ if (child < 0)
+ die("fork");
+
+ // The child execs to the specified argv[1:]
+ if(child == 0) {
+ // The child allows its parent process to trace it.
+ if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0)
+ die("ptrace/TRACEME");
+
+ // This will yield to a SIGTRAP in the parent and stop the
+ // process before the program is loaded.
+ if (execvp(argv[1], &argv[1]) < 0)
+ die("execvp");
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // The parent traces its child.
+
+ // Wait for the execvp SIGTRAP
+ int wstatus;
+ if (waitpid(child, &wstatus, 0) < 0)
+ die("waitpid");
+
+ // Might already have died
+ if (WIFEXITED(wstatus))
+ exit(WEXITSTATUS(wstatus));
+
+ // We also configure the tracing of the child.
+ unsigned flags =
+ // We want that the child is also killed if we die
+ PTRACE_O_EXITKILL
+ // We want to have the good and nice syscall tracing interface
+ | PTRACE_O_TRACESYSGOOD;
+
+ if (ptrace(PTRACE_SETOPTIONS, child, 0, flags) < 0)
+ die("ptrace/SETOPTIONS");
+
+ while(1) {
+ // Continue the child to the next system call
+ if (ptrace(PTRACE_SYSCALL, child, NULL, NULL) < 0)
+ die("ptrace");
+
+ // Wait for the child to reach the next trace point
+ if (waitpid(child, &wstatus, 0) < 0)
+ die("waitpid");
+
+ // If it has exited, we exit with the same exit code
+ if (WIFEXITED(wstatus))
+ exit(WEXITSTATUS(wstatus));
+
+ // We use PTRACE_GET_SYSCALL_INFO to extract information about
+ // the system call. This is called twice:
+ // - on syscall entry => (op == PTRACE_SYSCALL_INFO_ENTRY)
+ // - on syscall exit => (op == PTRACE_SYSCALL_INFO_EXIT)
+ struct ptrace_syscall_info info;
+ size_t size = sizeof(info);
+ if (ptrace(PTRACE_GET_SYSCALL_INFO, child, &size, &info) < 0)
+ die("ptrace/GET_SYSCALL_INFO");
+
+ // Use the helper function to produce an output
+ print_syscall(&info);
+ }
}