diff options
author | Mattias Andrée <maandree@kth.se> | 2020-05-30 17:34:29 +0200 |
---|---|---|
committer | Mattias Andrée <maandree@kth.se> | 2020-05-30 17:34:29 +0200 |
commit | 712f56fe3369c59d32b9000830b4ed7b25ed24b5 (patch) | |
tree | 27ddf931fd2d980d0c4dcdfb025f26f5380fb5d6 | |
parent | Print errno names and strings (diff) | |
download | sctrace-712f56fe3369c59d32b9000830b4ed7b25ed24b5.tar.gz sctrace-712f56fe3369c59d32b9000830b4ed7b25ed24b5.tar.bz2 sctrace-712f56fe3369c59d32b9000830b4ed7b25ed24b5.tar.xz |
Add support for tracing fork children
Signed-off-by: Mattias Andrée <maandree@kth.se>
-rw-r--r-- | Makefile | 24 | ||||
-rw-r--r-- | common.h | 90 | ||||
-rw-r--r-- | consts.c | 16 | ||||
-rw-r--r-- | memory.c | 189 | ||||
-rw-r--r-- | print.c | 560 | ||||
-rw-r--r-- | process.c | 64 | ||||
-rw-r--r-- | sctrace.c | 951 | ||||
-rw-r--r-- | util.c | 56 |
8 files changed, 1094 insertions, 856 deletions
@@ -3,10 +3,27 @@ CONFIGFILE = config.mk include $(CONFIGFILE) +OBJ =\ + consts.o\ + memory.o\ + print.o\ + process.o\ + sctrace.o\ + util.o + +HDR =\ + arg.h\ + common.h\ + list-errnos.h + all: sctrace +$(OBJ): $(@:.o=.c) $(HDR) + +sctrace: $(OBJ) + $(CC) -o $@ $(OBJ) $(LDFLAGS) -sctrace: sctrace.c arg.h list-errnos.h - $(CC) -o $@ $@.c $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) +.c.o: + $(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS) list-errnos.h: printf '#define LIST_ERRNOS(_)\\\n\t' > $@ @@ -25,4 +42,7 @@ uninstall: clean: -rm -f -- *.o list-errnos.h sctrace +.SUFFIXES: +.SUFFIXES: .c .o + .PHONY: all install uninstall clean diff --git a/common.h b/common.h new file mode 100644 index 0000000..f027327 --- /dev/null +++ b/common.h @@ -0,0 +1,90 @@ +/* See LICENSE file for copyright and license details. */ +#if !defined __x86_64__ || defined __IPL32__ +# error "This program is only implemented for x86-64" +#endif + +#include <sys/ptrace.h> +#include <sys/syscall.h> +#include <sys/uio.h> +#include <sys/user.h> +#include <sys/wait.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* Constants used in system calls */ +#include <sys/epoll.h> +#include <sys/socket.h> +#include <fcntl.h> + +#include "arg.h" +#include "list-errnos.h" + + +enum type { + Unknown, + Void, + Int, + UInt, + OInt, + XInt, + Long, + ULong, + OLong, + XLong, + LLong, + ULLong, + OLLong, + XLLong, + Ptr +}; + +enum state { + Normal, + Syscall +}; + +struct process { + pid_t pid; + struct process *next; + struct process *prev; + enum state state; + + /* Syscall data */ + unsigned long long int scall; + unsigned long long int args[6]; + unsigned long long int ret; + enum type ret_type; +}; + + +/* consts.c */ +const char *get_errno_name(int err); + +/* memory.c */ +char *get_string(pid_t pid, unsigned long int addr, size_t *lenp, const char **errorp); +int get_struct(pid_t pid, unsigned long int addr, void *out, size_t size, const char **errorp); +char *get_memory(pid_t pid, unsigned long int addr, size_t n, const char **errorp); +char *escape_memory(char *str, size_t m); + +/* print.c */ +void print_systemcall(struct process *proc); +void print_systemcall_exit(struct process *proc); + +/* process.c */ +void init_process_list(void); +struct process *find_process(pid_t pid); +struct process *add_process(pid_t pid, int trace_options); +void remove_process(struct process *proc); + +/* util.c */ +void set_trace_output(FILE *fp); +void tprintf(struct process *proc, const char *fmt, ...); +_Noreturn void eprintf(const char *fmt, ...); diff --git a/consts.c b/consts.c new file mode 100644 index 0000000..9a7e3fb --- /dev/null +++ b/consts.c @@ -0,0 +1,16 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +const char * +get_errno_name(int err) +{ + static char buf[3 * sizeof(err) + 2]; + +#define X(N) if (err == N) return #N; + LIST_ERRNOS(X) +#undef X + + sprintf(buf, "%i", err); + return buf; +} diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..1af854f --- /dev/null +++ b/memory.c @@ -0,0 +1,189 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +char * +get_string(pid_t pid, unsigned long int addr, size_t *lenp, const char **errorp) +{ + struct iovec inv, outv; + size_t off = 0, size = 0, page_off, read_size; + char *out = NULL, *in = (char *)addr, *p; + page_off = (size_t)addr % sizeof(PAGE_SIZE); + read_size = PAGE_SIZE - page_off; + *errorp = NULL; + for (;; read_size = PAGE_SIZE) { + out = realloc(out, size + PAGE_SIZE); + if (!out) + eprintf("realloc:"); + inv.iov_base = &in[off]; + inv.iov_len = read_size; + outv.iov_base = &out[off]; + outv.iov_len = read_size; + if (process_vm_readv(pid, &outv, 1, &inv, 1, 0) != (ssize_t)read_size) { + *errorp = errno == EFAULT ? "<invalid address>" : "<an error occured during reading of string>"; + *lenp = 0; + return 0; + } + p = memchr(&out[off], 0, read_size); + if (p) { + *lenp = (size_t)(p - out); + return out; + } + off += read_size; + } +} + + +int +get_struct(pid_t pid, unsigned long int addr, void *out, size_t size, const char **errorp) +{ + struct iovec inv, outv; + *errorp = NULL; + inv.iov_base = (void *)addr; + inv.iov_len = size; + outv.iov_base = out; + outv.iov_len = size; + if (process_vm_readv(pid, &outv, 1, &inv, 1, 0) == (ssize_t)size) + return 0; + *errorp = errno == EFAULT ? "<invalid address>" : "<an error occured during reading of string>"; + return -1; +} + + +char * +get_memory(pid_t pid, unsigned long int addr, size_t n, const char **errorp) +{ + char *out = malloc(n + (size_t)!n); + if (!out) + eprintf("malloc:"); + if (get_struct(pid, addr, out, n, errorp)) { + free(out); + return NULL; + } + return out; +} + + + +static void +add_char(char **strp, size_t *sizep, size_t *lenp, char c) +{ + if (*lenp == *sizep) { + *strp = realloc(*strp, *sizep += 128); + if (!*strp) + eprintf("realloc:"); + } + (*strp)[(*lenp)++] = c; +} + + +static size_t +utf8len(char *str) +{ + size_t ext, i, len; + uint32_t code; + uint8_t *s = (uint8_t *)str; + + struct { + uint8_t lower; + uint8_t upper; + uint8_t mask; + uint32_t lowest; + } lookup[] = { + { 0x00, 0x7F, 0x7F, UINT32_C(0x000000) }, + { 0xC0, 0xDF, 0x1F, UINT32_C(0x000080) }, + { 0xE0, 0xEF, 0x0F, UINT32_C(0x000800) }, + { 0xF0, 0xF7, 0x07, UINT32_C(0x010000) } + }; + + for (ext = 0; ext < sizeof(lookup) / sizeof(*lookup); ext++) + if (lookup[ext].lower <= s[0] && s[0] <= lookup[ext].upper) + goto found; + return 0; + +found: + code = s[0] & lookup[ext].mask; + len = ext + 1; + for (i = 1; i < len; i++) { + if ((s[i] & 0xC0) != 0x80) + return 0; + code = (code << 6) | (s[i] ^ 0x80); + } + + if (code < lookup[ext].lowest || (0xD800 <= code && code <= 0xDFFF) || code > UINT32_C(0x10FFFF)) + return 0; + return len; +} + + +char * +escape_memory(char *str, size_t m) +{ + char *ret = NULL, *s, *end; + size_t size = 0; + size_t len = 0; + size_t n = 0; + int need_new_string = 0; + if (!str) { + str = strdup("NULL"); + if (!str) + eprintf("strdup:"); + return str; + } + add_char(&ret, &size, &len, '"'); + for (s = str, end = &str[m]; s != end; s++) { + if (n) { + add_char(&ret, &size, &len, *s); + n -= 1; + } else if (*s == '\r') { + add_char(&ret, &size, &len, '\\'); + add_char(&ret, &size, &len, 'r'); + } else if (*s == '\t') { + add_char(&ret, &size, &len, '\\'); + add_char(&ret, &size, &len, 't'); + } else if (*s == '\a') { + add_char(&ret, &size, &len, '\\'); + add_char(&ret, &size, &len, 'a'); + } else if (*s == '\f') { + add_char(&ret, &size, &len, '\\'); + add_char(&ret, &size, &len, 'f'); + } else if (*s == '\v') { + add_char(&ret, &size, &len, '\\'); + add_char(&ret, &size, &len, 'v'); + } else if (*s == '\b') { + add_char(&ret, &size, &len, '\\'); + add_char(&ret, &size, &len, 'b'); + } else if (*s == '\n') { + add_char(&ret, &size, &len, '\\'); + add_char(&ret, &size, &len, 'n'); + } else if (*s == '\"') { + add_char(&ret, &size, &len, '\\'); + add_char(&ret, &size, &len, '"'); + } else if (*s < ' ' || *s >= 127) { + n = utf8len(s); + if (n > 1) { + add_char(&ret, &size, &len, *s); + n -= 1; + } else { + n = 0; + add_char(&ret, &size, &len, '\\'); + add_char(&ret, &size, &len, 'x'); + add_char(&ret, &size, &len, "0123456789abcdef"[(int)*(unsigned char *)s >> 4]); + add_char(&ret, &size, &len, "0123456789abcdef"[(int)*(unsigned char *)s & 15]); + need_new_string = 1; + continue; + } + } else { + if (need_new_string && isxdigit(*s)) { + add_char(&ret, &size, &len, '"'); + add_char(&ret, &size, &len, '"'); + } + add_char(&ret, &size, &len, *s); + } + need_new_string = 0; + } + add_char(&ret, &size, &len, '"'); + add_char(&ret, &size, &len, '\0'); + free(str); + return ret; +} @@ -0,0 +1,560 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static void +print_clockid(struct process *proc, size_t arg_index) /* TODO */ +{ + tprintf(proc, "%li", (long int)proc->args[arg_index]); +} + + +static void +print_timespec(struct process *proc, size_t arg_index) +{ + struct timespec ts; + const char *err; + if (get_struct(proc->pid, proc->args[arg_index], &ts, sizeof(ts), &err)) { + tprintf(proc, "%s", err); + return; + } + tprintf(proc, "{.tv_sec = %ji, .tv_nsec = %li}", (intmax_t)ts.tv_sec, ts.tv_nsec); +} + + +static void +printf_systemcall(struct process *proc, const char *scall, const char *fmt, ...) +{ + typedef char *(*Function)(struct process *proc, size_t arg_index); + Function funcs[6]; + size_t i, nfuncs = 0, func, len; + unsigned long long int *args = proc->args; + int ells = 0; + char *str; + const char *err; + va_list ap; + va_start(ap, fmt); + tprintf(proc, "%s(", scall); + for (i = 0; *fmt; fmt++) { + if (*fmt == ' ') + continue; + if (*fmt == 'l') { + ells += 1; + continue; + } else if (*fmt == 'L') { + ells += 2; + continue; + } else if (*fmt == 'h') { + ells -= 1; + continue; + } else if (*fmt == 'H') { + ells -= 2; + continue; + } + if (i) + tprintf(proc, ", "); + if (*fmt == 'p') { + if (args[i]) + tprintf(proc, "%p", (void *)args[i]); + else + tprintf(proc, "NULL"); + } else if (*fmt >= '1' && *fmt <= '6') { + func = (size_t)(*fmt - '0'); + while (nfuncs < func) + funcs[nfuncs++] = va_arg(ap, Function); + funcs[func - 1](proc, i); + } else if (*fmt == 's') { + str = get_string(proc->pid, args[i], &len, &err); + str = escape_memory(str, len); + tprintf(proc, "%s", str ? str : err); + free(str); + } else if (*fmt == 'm') { + str = escape_memory(get_memory(proc->pid, args[i], (size_t)args[i + 1], &err), (size_t)args[i + 1]); + tprintf(proc, "%s", str ? str : err); + free(str); + } else if (*fmt == 'F') { + if ((int)args[i] == AT_FDCWD) + tprintf(proc, "AT_FDCWD"); + else + tprintf(proc, "%i", (int)args[i]); + } else if (*fmt == 'u') { + if (ells == 1) + tprintf(proc, "%lu", (unsigned long int)args[i]); + else if (ells > 1) + tprintf(proc, "%llu", (unsigned long long int)args[i]); + else + tprintf(proc, "%u", (unsigned int)args[i]); + } else if (*fmt == 'x') { + if (ells == 1) + tprintf(proc, "%#lx", (unsigned long int)args[i]); + else if (ells > 1) + tprintf(proc, "%#llx", (unsigned long long int)args[i]); + else + tprintf(proc, "%#x", (unsigned int)args[i]); + } else if (*fmt == 'o') { + if (ells == 1) + tprintf(proc, "%#lo", (unsigned long int)args[i]); + else if (ells > 1) + tprintf(proc, "%#llo", (unsigned long long int)args[i]); + else + tprintf(proc, "%#o", (unsigned int)args[i]); + } else { + if (ells == 1) + tprintf(proc, "%li", (long int)args[i]); + else if (ells > 1) + tprintf(proc, "%lli", (long long int)args[i]); + else + tprintf(proc, "%i", (int)args[i]); + } + ells = 0; + i += 1; + } + tprintf(proc, ") "); + va_end(ap); +} + + +void +print_systemcall(struct process *proc) +{ + char buf1[128], *p, *buf; + unsigned long int flags; + unsigned long long int *args = proc->args; + +#define FLAGS_BEGIN(BUF, ARG)\ + p = buf = (BUF);\ + flags = (ARG) +#define FLAGS_ADD(FLAG)\ + if (flags & (unsigned long int)(FLAG))\ + p = stpcpy(p, "|"#FLAG) +#define FLAGS_END(FMT, TYPE)\ + if (flags || p == buf)\ + sprintf(p, "|"FMT, (TYPE)flags); + +#define UNDOCUMENTED(NAME) GENERIC_HANDLER(NAME) +#define UNIMPLEMENTED(NAME) GENERIC_HANDLER(NAME) +#define GENERIC_HANDLER(NAME)\ + case SYS_##NAME:\ + tprintf(proc, "%s(raw: %llu, %llu, %llu, %llu, %llu, %llu) ", #NAME,\ + args[0], args[1], args[2], args[3], args[4], args[5]);\ + break +#define SIMPLE(NAME, FMT, RET_TYPE)\ + case SYS_##NAME:\ + printf_systemcall(proc, #NAME, (FMT), args);\ + proc->ret_type = (RET_TYPE);\ + break +#define FORMATTERS(NAME, FMT, RET_TYPE, ...)\ + case SYS_##NAME:\ + printf_systemcall(proc, #NAME, (FMT), args, __VA_ARGS__);\ + proc->ret_type = (RET_TYPE);\ + break + + proc->ret_type = Unknown; + + /* TODO replace GENERIC_HANDLER with specific handlers */ + switch (proc->scall) { + GENERIC_HANDLER(_sysctl); + SIMPLE(accept, "ipp", Int); /* TODO output */ + case SYS_accept4: /* TODO output */ + FLAGS_BEGIN(buf1, args[3]); + FLAGS_ADD(SOCK_NONBLOCK); + FLAGS_ADD(SOCK_CLOEXEC); + FLAGS_END("%#x", unsigned int); + tprintf(proc, "accept4(%i, %p, %p, %s) ", (int)args[0], (void *)args[1], (void *)args[2], &buf1[1]); + proc->ret_type = Int; + break; + SIMPLE(access, "si", Int); /* TODO flags */ + SIMPLE(acct, "s", Int); + GENERIC_HANDLER(add_key); + GENERIC_HANDLER(adjtimex); + UNIMPLEMENTED(afs_syscall); + SIMPLE(alarm, "", UInt); + GENERIC_HANDLER(arch_prctl); + GENERIC_HANDLER(bind); + GENERIC_HANDLER(bpf); + SIMPLE(brk, "p", Int); + GENERIC_HANDLER(capget); + GENERIC_HANDLER(capset); + SIMPLE(chdir, "s", Int); + SIMPLE(chmod, "so", Int); + SIMPLE(chown, "sii", Int); + SIMPLE(chroot, "s", Int); + UNDOCUMENTED(clock_adjtime); + FORMATTERS(clock_getres, "1p", Int, print_clockid); /* TODO output */ + FORMATTERS(clock_gettime, "1p", Int, print_clockid); /* TODO output */ + FORMATTERS(clock_nanosleep, "1i2p", Int, print_clockid, print_timespec); /* TODO output, flags */ + FORMATTERS(clock_settime, "12", Int, print_clockid, print_timespec); + GENERIC_HANDLER(clone); + GENERIC_HANDLER(clone3); + SIMPLE(close, "i", Int); + GENERIC_HANDLER(connect); + GENERIC_HANDLER(copy_file_range); + SIMPLE(creat, "so", Int); /* TODO flags */ + SIMPLE(create_module, "slu", Ptr); + SIMPLE(delete_module, "si", Int); /* TODO flags */ + SIMPLE(dup, "i", Int); + SIMPLE(dup2, "ii", Int); + SIMPLE(dup3, "iii", Int); + SIMPLE(epoll_create, "i", Int); + case SYS_epoll_create1:\ + FLAGS_BEGIN(buf1, args[0]); + FLAGS_ADD(EPOLL_CLOEXEC); + FLAGS_END("%#x", unsigned int); + tprintf(proc, "epoll_create1(%s) ", &buf1[1]); + proc->ret_type = Int; + break; + GENERIC_HANDLER(epoll_ctl); + GENERIC_HANDLER(epoll_ctl_old); + GENERIC_HANDLER(epoll_pwait); + GENERIC_HANDLER(epoll_wait); + GENERIC_HANDLER(epoll_wait_old); + GENERIC_HANDLER(eventfd); + GENERIC_HANDLER(eventfd2); + GENERIC_HANDLER(execve); + GENERIC_HANDLER(execveat); + SIMPLE(exit, "i", Int); + SIMPLE(exit_group, "i", Int); + SIMPLE(faccessat, "Fsii", Int); /* TODO flags */ + GENERIC_HANDLER(fadvise64); + GENERIC_HANDLER(fallocate); + GENERIC_HANDLER(fanotify_init); + GENERIC_HANDLER(fanotify_mark); + SIMPLE(fchdir, "i", Int); + SIMPLE(fchmod, "io", Int); + SIMPLE(fchmodat, "Fsoi", Int); /* TODO flags */ + SIMPLE(fchown, "iii", Int); + SIMPLE(fchownat, "Fsiii", Int); /* TODO flags */ + GENERIC_HANDLER(fcntl); + SIMPLE(fdatasync, "i", Int); + SIMPLE(fgetxattr, "isplu", Long); /* TODO output */ + GENERIC_HANDLER(finit_module); + GENERIC_HANDLER(flistxattr); + GENERIC_HANDLER(flock); + SIMPLE(fork, "", Int); /* TODO fork */ + SIMPLE(fremovexattr, "is", Int); + UNDOCUMENTED(fsconfig); + SIMPLE(fsetxattr, "ismlui", Int); /* TODO flags */ + UNDOCUMENTED(fsmount); + UNDOCUMENTED(fsopen); + UNDOCUMENTED(fspick); + SIMPLE(fstat, "ip", Int); /* TODO output */ + SIMPLE(fstatfs, "ip", Int); /* TODO output */ + SIMPLE(fsync, "i", Int); + SIMPLE(ftruncate, "illi", Int); + GENERIC_HANDLER(futex); + GENERIC_HANDLER(futimesat); + GENERIC_HANDLER(get_kernel_syms); + GENERIC_HANDLER(get_mempolicy); + GENERIC_HANDLER(get_robust_list); + GENERIC_HANDLER(get_thread_area); + GENERIC_HANDLER(getcpu); + GENERIC_HANDLER(getcwd); + GENERIC_HANDLER(getdents); + GENERIC_HANDLER(getdents64); + SIMPLE(getegid, "", Int); + SIMPLE(geteuid, "", Int); + SIMPLE(getgid, "", Int); + GENERIC_HANDLER(getgroups); + GENERIC_HANDLER(getitimer); + GENERIC_HANDLER(getpeername); + SIMPLE(getpgid, "i", Int); + GENERIC_HANDLER(getpgrp); + SIMPLE(getpid, "", Int); + UNIMPLEMENTED(getpmsg); + SIMPLE(getppid, "", Int); + SIMPLE(getpriority, "ii", Int); + SIMPLE(getrandom, "pluu", Long); /* TODO output, flags */ + SIMPLE(getresgid, "ppp", Int); /* TODO output */ + SIMPLE(getresuid, "ppp", Int); /* TODO output */ + GENERIC_HANDLER(getrlimit); + GENERIC_HANDLER(getrusage); + SIMPLE(getsid, "i", Int); + GENERIC_HANDLER(getsockname); + GENERIC_HANDLER(getsockopt); + SIMPLE(gettid, "", Int); + GENERIC_HANDLER(gettimeofday); + SIMPLE(getuid, "", Int); + SIMPLE(getxattr, "ssplu", Long); /* TODO output */ + GENERIC_HANDLER(init_module); + SIMPLE(inotify_add_watch, "isx", Int); /* TODO flags */ + SIMPLE(inotify_init, "", Int); + SIMPLE(inotify_init1, "i", Int); /* TODO flags */ + SIMPLE(inotify_rm_watch, "ii", Int); + GENERIC_HANDLER(io_cancel); + GENERIC_HANDLER(io_destroy); + GENERIC_HANDLER(io_getevents); + GENERIC_HANDLER(io_pgetevents); + GENERIC_HANDLER(io_setup); + GENERIC_HANDLER(io_submit); + UNDOCUMENTED(io_uring_enter); + UNDOCUMENTED(io_uring_register); + UNDOCUMENTED(io_uring_setup); + GENERIC_HANDLER(ioctl); + SIMPLE(ioperm, "lului", Int); + SIMPLE(iopl, "i", Int); + GENERIC_HANDLER(ioprio_get); + GENERIC_HANDLER(ioprio_set); + GENERIC_HANDLER(kcmp); + GENERIC_HANDLER(kexec_file_load); + GENERIC_HANDLER(kexec_load); + GENERIC_HANDLER(keyctl); + SIMPLE(kill, "ii", Int); /* TODO flags */ + SIMPLE(lchown, "sii", Int); + SIMPLE(lgetxattr, "ssplu", Long); /* TODO output */ + SIMPLE(link, "ss", Int); + SIMPLE(linkat, "FsFsi", Int); /* TODO flags */ + SIMPLE(listen, "ii", Int); + GENERIC_HANDLER(listxattr); + GENERIC_HANDLER(llistxattr); + GENERIC_HANDLER(lookup_dcookie); + SIMPLE(lremovexattr, "ss", Int); + SIMPLE(lseek, "illii", LLong); /* TODO flags */ + SIMPLE(lsetxattr, "ssmlui", Int); /* TODO flags */ + SIMPLE(lstat, "sp", Int); /* TODO output */ + SIMPLE(madvise, "plui", Int); /* TODO flags */ + GENERIC_HANDLER(mbind); + SIMPLE(membarrier, "ii", Int); /* TODO flags */ + SIMPLE(memfd_create, "su", Int); /* TODO flags */ + GENERIC_HANDLER(migrate_pages); + GENERIC_HANDLER(mincore); + SIMPLE(mkdir, "so", Int); + SIMPLE(mkdirat, "Fso", Int); + GENERIC_HANDLER(mknod); + GENERIC_HANDLER(mknodat); + SIMPLE(mlock, "plu", Int); + SIMPLE(mlock2, "plui", Int); /* TODO flags */ + SIMPLE(mlockall, "i", Int); /* TODO flags */ + SIMPLE(mmap, "pluiiilli", Ptr); /* TODO flags */ + GENERIC_HANDLER(modify_ldt); + GENERIC_HANDLER(mount); + UNDOCUMENTED(move_mount); + GENERIC_HANDLER(move_pages); + SIMPLE(mprotect, "plui", Int); /* TODO flags */ + GENERIC_HANDLER(mq_getsetattr); + GENERIC_HANDLER(mq_notify); + GENERIC_HANDLER(mq_open); + GENERIC_HANDLER(mq_timedreceive); + GENERIC_HANDLER(mq_timedsend); + GENERIC_HANDLER(mq_unlink); + GENERIC_HANDLER(mremap); + GENERIC_HANDLER(msgctl); + GENERIC_HANDLER(msgget); + GENERIC_HANDLER(msgrcv); + GENERIC_HANDLER(msgsnd); + SIMPLE(msync, "plui", Int); /* TODO flags */ + SIMPLE(munlock, "plu", Int); + SIMPLE(munlockall, "", Int); + SIMPLE(munmap, "plu", Int); + GENERIC_HANDLER(name_to_handle_at); + FORMATTERS(nanosleep, "1p", Int, print_timespec); /* TODO output */ + SIMPLE(newfstatat, "Fspi", Int); /* TODO output, flags */ + SIMPLE(nfsservctl, "ipp", Long); /* TODO flags, struct, output */ + GENERIC_HANDLER(open); + GENERIC_HANDLER(open_by_handle_at); + UNDOCUMENTED(open_tree); + GENERIC_HANDLER(openat); + SIMPLE(pause, "", Int); + GENERIC_HANDLER(perf_event_open); + GENERIC_HANDLER(personality); + SIMPLE(pidfd_open, "iu", Int); + GENERIC_HANDLER(pidfd_send_signal); + SIMPLE(pipe, "p", Int); /* TODO output */ + SIMPLE(pipe2, "pi", Int); /* TODO output, flags */ + SIMPLE(pivot_root, "ss", Int); + SIMPLE(pkey_alloc, "lulu", Int); /* TODO flags */ + SIMPLE(pkey_free, "i", Int); + SIMPLE(pkey_mprotect, "pluii", Int); /* TODO flags */ + GENERIC_HANDLER(poll); + GENERIC_HANDLER(ppoll); + GENERIC_HANDLER(prctl); + GENERIC_HANDLER(pread64); + GENERIC_HANDLER(preadv); + GENERIC_HANDLER(preadv2); + GENERIC_HANDLER(prlimit64); + GENERIC_HANDLER(process_vm_readv); + GENERIC_HANDLER(process_vm_writev); + GENERIC_HANDLER(pselect6); + GENERIC_HANDLER(ptrace); + UNIMPLEMENTED(putpmsg); + GENERIC_HANDLER(pwrite64); + GENERIC_HANDLER(pwritev); + GENERIC_HANDLER(pwritev2); + GENERIC_HANDLER(query_module); + GENERIC_HANDLER(quotactl); + SIMPLE(read, "iplu", Long); /* TODO output */ + SIMPLE(readahead, "illilu", Long); + SIMPLE(readlink, "splu", Long); /* TODO output */ + SIMPLE(readlinkat, "Fsplu", Long); /* TODO output */ + GENERIC_HANDLER(readv); + GENERIC_HANDLER(reboot); + GENERIC_HANDLER(recvfrom); + GENERIC_HANDLER(recvmmsg); + GENERIC_HANDLER(recvmsg); + GENERIC_HANDLER(remap_file_pages); + SIMPLE(removexattr, "ss", Int); + SIMPLE(rename, "ss", Int); + SIMPLE(renameat, "FsFs", Int); + SIMPLE(renameat2, "FsFsu", Int); /* TODO flags */ + GENERIC_HANDLER(request_key); + SIMPLE(restart_syscall, "", Int); + SIMPLE(rmdir, "s", Int); + UNDOCUMENTED(rseq); + GENERIC_HANDLER(rt_sigaction); + GENERIC_HANDLER(rt_sigpending); + GENERIC_HANDLER(rt_sigprocmask); + GENERIC_HANDLER(rt_sigqueueinfo); + GENERIC_HANDLER(rt_sigreturn); + GENERIC_HANDLER(rt_sigsuspend); + GENERIC_HANDLER(rt_sigtimedwait); + GENERIC_HANDLER(rt_tgsigqueueinfo); + SIMPLE(sched_get_priority_max, "i", Int); + SIMPLE(sched_get_priority_min, "i", Int); + GENERIC_HANDLER(sched_getaffinity); + GENERIC_HANDLER(sched_getattr); + GENERIC_HANDLER(sched_getparam); + GENERIC_HANDLER(sched_getscheduler); + GENERIC_HANDLER(sched_rr_get_interval); + GENERIC_HANDLER(sched_setaffinity); + GENERIC_HANDLER(sched_setattr); + GENERIC_HANDLER(sched_setparam); + GENERIC_HANDLER(sched_setscheduler); + SIMPLE(sched_yield, "", Int); + GENERIC_HANDLER(seccomp); + UNIMPLEMENTED(security); + GENERIC_HANDLER(select); + GENERIC_HANDLER(semctl); + GENERIC_HANDLER(semget); + GENERIC_HANDLER(semop); + GENERIC_HANDLER(semtimedop); + GENERIC_HANDLER(sendfile); + GENERIC_HANDLER(sendmmsg); + GENERIC_HANDLER(sendmsg); + GENERIC_HANDLER(sendto); + GENERIC_HANDLER(set_mempolicy); + GENERIC_HANDLER(set_robust_list); + GENERIC_HANDLER(set_thread_area); + SIMPLE(set_tid_address, "p", Long); + GENERIC_HANDLER(setdomainname); + SIMPLE(setfsgid, "i", Int); + SIMPLE(setfsuid, "i", Int); + SIMPLE(setgid, "i", Int); + GENERIC_HANDLER(setgroups); + GENERIC_HANDLER(sethostname); + GENERIC_HANDLER(setitimer); + GENERIC_HANDLER(setns); + SIMPLE(setpgid, "ii", Int); + SIMPLE(setpriority, "iii", Int); + SIMPLE(setregid, "ii", Int); + SIMPLE(setresgid, "iii", Int); + SIMPLE(setresuid, "iii", Int); + SIMPLE(setreuid, "ii", Int); + GENERIC_HANDLER(setrlimit); + SIMPLE(setsid, "", Int); + GENERIC_HANDLER(setsockopt); + GENERIC_HANDLER(settimeofday); + SIMPLE(setuid, "i", Int); + SIMPLE(setxattr, "ssmlui", Int); /* TODO flags */ + GENERIC_HANDLER(shmat); + GENERIC_HANDLER(shmctl); + GENERIC_HANDLER(shmdt); + GENERIC_HANDLER(shmget); + SIMPLE(shutdown, "ii", Int); /* TODO flags */ + GENERIC_HANDLER(sigaltstack); + GENERIC_HANDLER(signalfd); + GENERIC_HANDLER(signalfd4); + SIMPLE(socket, "iii", Int); /* TODO flags */ + SIMPLE(socketpair, "iiip", Int); /* TODO output, flags */ + GENERIC_HANDLER(splice); + GENERIC_HANDLER(stat); + GENERIC_HANDLER(statfs); + GENERIC_HANDLER(statx); + SIMPLE(swapoff, "s", Int); + SIMPLE(swapon, "si", Int); /* TODO flags */ + SIMPLE(symlink, "ss", Int); + SIMPLE(symlinkat, "sFs", Int); + SIMPLE(sync, "", Void); + SIMPLE(sync_file_range, "illilliu", Int); /* TODO flags */ + SIMPLE(syncfs, "i", Int); + GENERIC_HANDLER(sysfs); + GENERIC_HANDLER(sysinfo); + GENERIC_HANDLER(syslog); + GENERIC_HANDLER(tee); + SIMPLE(tgkill, "iii", Int); /* TODO flags */ + SIMPLE(time, "p", LLong); /* TODO output */ + GENERIC_HANDLER(timer_create); + GENERIC_HANDLER(timer_delete); + GENERIC_HANDLER(timer_getoverrun); + GENERIC_HANDLER(timer_gettime); + GENERIC_HANDLER(timer_settime); + GENERIC_HANDLER(timerfd_create); + GENERIC_HANDLER(timerfd_gettime); + GENERIC_HANDLER(timerfd_settime); + GENERIC_HANDLER(times); + SIMPLE(tkill, "ii", Int); /* TODO flags */ + SIMPLE(truncate, "slli", Int); + UNIMPLEMENTED(tuxcall); + SIMPLE(umask, "o", OInt); + SIMPLE(umount2, "si", Int); /* TODO flags */ + SIMPLE(uname, "p", Int); /* TODO output */ + SIMPLE(unlink, "s", Int); + SIMPLE(unlinkat, "Fsi", Int); /* TODO flags */ + SIMPLE(unshare, "i", Int); /* TODO flags */ + SIMPLE(uselib, "s", Int); + SIMPLE(userfaultfd, "i", Int); /* TODO flags */ + GENERIC_HANDLER(ustat); + GENERIC_HANDLER(utime); + GENERIC_HANDLER(utimensat); + GENERIC_HANDLER(utimes); + SIMPLE(vfork, "", Int); /* TODO fork */ + SIMPLE(vhangup, "", Int); + GENERIC_HANDLER(vmsplice); + UNIMPLEMENTED(vserver); + GENERIC_HANDLER(wait4); + GENERIC_HANDLER(waitid); + SIMPLE(write, "imlu", Long); + GENERIC_HANDLER(writev); + default: + tprintf(proc, "syscall_0x%lx(raw: %llu, %llu, %llu, %llu, %llu, %llu) ", + (unsigned long int)proc->scall, args[0], args[1], args[2], args[3], args[4], args[5]); + break; + } +} + + +void +print_systemcall_exit(struct process *proc) +{ + if (proc->ret_type == Int) + tprintf(proc, "= %i", (int)proc->ret); + else if (proc->ret_type == UInt) + tprintf(proc, "= %u", (unsigned int)proc->ret); + else if (proc->ret_type == OInt) + tprintf(proc, "= %#o", (unsigned int)proc->ret); + else if (proc->ret_type == XInt) + tprintf(proc, "= %#x", (unsigned int)proc->ret); + else if (proc->ret_type == Long) + tprintf(proc, "= %li", (long int)proc->ret); + else if (proc->ret_type == ULong) + tprintf(proc, "= %lu", (unsigned long int)proc->ret); + else if (proc->ret_type == OLong) + tprintf(proc, "= %#lo", (unsigned long int)proc->ret); + else if (proc->ret_type == XLong) + tprintf(proc, "= %#lx", (unsigned long int)proc->ret); + else if (proc->ret_type == LLong) + tprintf(proc, "= %lli", (long long int)proc->ret); + else if (proc->ret_type == ULLong) + tprintf(proc, "= %llu", (unsigned long long int)proc->ret); + else if (proc->ret_type == OLLong) + tprintf(proc, "= %#llo", (unsigned long long int)proc->ret); + else if (proc->ret_type == XLLong) + tprintf(proc, "= %#llx", (unsigned long long int)proc->ret); + else if (proc->ret_type == Ptr && (long long int)proc->ret >= 0) + tprintf(proc, "= %p", (void *)proc->ret); + else + tprintf(proc, "= %li", (long int)proc->ret); + + if ((unsigned long long int)proc->ret > -(unsigned long long int)PAGE_SIZE) + tprintf(proc, " (%s: %s)", get_errno_name(-(int)proc->ret), strerror(-(int)proc->ret)); + + tprintf(proc, "\n"); +} diff --git a/process.c b/process.c new file mode 100644 index 0000000..ea0dfd1 --- /dev/null +++ b/process.c @@ -0,0 +1,64 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static struct process head; +static struct process tail; + + +void +init_process_list(void) +{ + head.next = &tail; + tail.prev = &head; +} + + +struct process * +find_process(pid_t pid) +{ + struct process *p; + for (p = head.next; p->next; p = p->next) + if (p->pid == pid) + return p; + return NULL; +} + + +struct process * +add_process(pid_t pid, int trace_options) +{ + struct process *proc; + int saved_errno; + proc = calloc(1, sizeof(*proc)); + if (!proc) + eprintf("calloc: %s\n"); + proc->pid = pid; + if (waitpid(pid, NULL, 0) < 0) { + eprintf("waitpid <child> NULL 0:"); + kill(pid, SIGKILL); + exit(1); + } + if (ptrace(PTRACE_SETOPTIONS, pid, 0, trace_options)) { + saved_errno = errno; + kill(pid, SIGKILL); + errno = saved_errno; + eprintf("ptrace PTRACE_SETOPTIONS %ju 0 ...:", (uintmax_t)pid); + } + if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) + eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)pid); + proc->next = &tail; + proc->prev = tail.prev; + proc->prev->next = proc; + tail.prev = proc; + return proc; +} + + +void +remove_process(struct process *proc) +{ + proc->prev->next = proc->next; + proc->next->prev = proc->prev; + free(proc); +} @@ -1,784 +1,60 @@ /* See LICENSE file for copyright and license details. */ -#if !defined __x86_64__ || defined __IPL32__ -# error "This program is only implemented for x86-64" -#endif - -#include <sys/ptrace.h> -#include <sys/syscall.h> -#include <sys/uio.h> -#include <sys/user.h> -#include <sys/wait.h> -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <signal.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -/* Constants used in system calls */ -#include <sys/epoll.h> -#include <sys/socket.h> -#include <fcntl.h> - -#include "arg.h" -#include "list-errnos.h" +#include "common.h" char *argv0; -enum Type { - Unknown, - Void, - Int, - UInt, - OInt, - XInt, - Long, - ULong, - OLong, - XLong, - LLong, - ULLong, - OLLong, - XLLong, - Ptr -}; - - static void usage(void) { - fprintf(stderr, "usage: %s [-f trace-output-file] command ...\n", argv0); + fprintf(stderr, "usage: %s [-f trace-output-file] [-CFV] command ...\n", argv0); exit(1); } -static const char * -get_errno_name(int err) -{ - static char buf[3 * sizeof(err) + 2]; - -#define X(N) if (err == N) return #N; - LIST_ERRNOS(X) -#undef X - - sprintf(buf, "%i", err); - return buf; -} - - -static char * -get_string(pid_t pid, unsigned long int addr, size_t *lenp, const char **errorp) -{ - struct iovec inv, outv; - size_t off = 0, size = 0, page_off, read_size; - char *out = NULL, *in = (char *)addr, *p; - page_off = (size_t)addr % sizeof(PAGE_SIZE); - read_size = PAGE_SIZE - page_off; - *errorp = NULL; - for (;; read_size = PAGE_SIZE) { - out = realloc(out, size + PAGE_SIZE); - if (!out) { - fprintf(stderr, "%s: realloc: %s\n", argv0, strerror(errno)); - exit(1); - } - inv.iov_base = &in[off]; - inv.iov_len = read_size; - outv.iov_base = &out[off]; - outv.iov_len = read_size; - if (process_vm_readv(pid, &outv, 1, &inv, 1, 0) != (ssize_t)read_size) { - *errorp = errno == EFAULT ? "<invalid address>" : "<an error occured during reading of string>"; - *lenp = 0; - return 0; - } - p = memchr(&out[off], 0, read_size); - if (p) { - *lenp = (size_t)(p - out); - return out; - } - off += read_size; - } -} - - -static int -get_struct(pid_t pid, unsigned long int addr, void *out, size_t size, const char **errorp) -{ - struct iovec inv, outv; - *errorp = NULL; - inv.iov_base = (void *)addr; - inv.iov_len = size; - outv.iov_base = out; - outv.iov_len = size; - if (process_vm_readv(pid, &outv, 1, &inv, 1, 0) == (ssize_t)size) - return 0; - *errorp = errno == EFAULT ? "<invalid address>" : "<an error occured during reading of string>"; - return -1; -} - -static char * -get_memory(pid_t pid, unsigned long int addr, size_t n, const char **errorp) -{ - char *out = malloc(n + (size_t)!n); - if (!out) { - fprintf(stderr, "%s: malloc: %s\n", argv0, strerror(errno)); - exit(1); - } - if (get_struct(pid, addr, out, n, errorp)) { - free(out); - return NULL; - } - return out; -} - - - static void -add_char(char **strp, size_t *sizep, size_t *lenp, char c) +handle_syscall(struct process *proc) { - if (*lenp == *sizep) { - *strp = realloc(*strp, *sizep += 128); - if (!*strp) { - fprintf(stderr, "%s: realloc: %s\n", argv0, strerror(errno)); - exit(1); - } - } - (*strp)[(*lenp)++] = c; -} - - -static size_t -utf8len(char *str) -{ - size_t ext, i, len; - uint32_t code; - uint8_t *s = (uint8_t *)str; - - struct { - uint8_t lower; - uint8_t upper; - uint8_t mask; - uint32_t lowest; - } lookup[] = { - { 0x00, 0x7F, 0x7F, UINT32_C(0x000000) }, - { 0xC0, 0xDF, 0x1F, UINT32_C(0x000080) }, - { 0xE0, 0xEF, 0x0F, UINT32_C(0x000800) }, - { 0xF0, 0xF7, 0x07, UINT32_C(0x010000) } - }; - - for (ext = 0; ext < sizeof(lookup) / sizeof(*lookup); ext++) - if (lookup[ext].lower <= s[0] && s[0] <= lookup[ext].upper) - goto found; - return 0; - -found: - code = s[0] & lookup[ext].mask; - len = ext + 1; - for (i = 1; i < len; i++) { - if ((s[i] & 0xC0) != 0x80) - return 0; - code = (code << 6) | (s[i] ^ 0x80); - } - - if (code < lookup[ext].lowest || (0xD800 <= code && code <= 0xDFFF) || code > UINT32_C(0x10FFFF)) - return 0; - return len; -} - - -static char * -escape_memory(char *str, size_t m) -{ - char *ret = NULL, *s, *end; - size_t size = 0; - size_t len = 0; - size_t n = 0; - int need_new_string = 0; - if (!str) { - str = strdup("NULL"); - if (!str) { - fprintf(stderr, "%s: strdup: %s\n", argv0, strerror(errno)); - exit(1); - } - return str; - } - add_char(&ret, &size, &len, '"'); - for (s = str, end = &str[m]; s != end; s++) { - if (n) { - add_char(&ret, &size, &len, *s); - n -= 1; - } else if (*s == '\r') { - add_char(&ret, &size, &len, '\\'); - add_char(&ret, &size, &len, 'r'); - } else if (*s == '\t') { - add_char(&ret, &size, &len, '\\'); - add_char(&ret, &size, &len, 't'); - } else if (*s == '\a') { - add_char(&ret, &size, &len, '\\'); - add_char(&ret, &size, &len, 'a'); - } else if (*s == '\f') { - add_char(&ret, &size, &len, '\\'); - add_char(&ret, &size, &len, 'f'); - } else if (*s == '\v') { - add_char(&ret, &size, &len, '\\'); - add_char(&ret, &size, &len, 'v'); - } else if (*s == '\b') { - add_char(&ret, &size, &len, '\\'); - add_char(&ret, &size, &len, 'b'); - } else if (*s == '\n') { - add_char(&ret, &size, &len, '\\'); - add_char(&ret, &size, &len, 'n'); - } else if (*s == '\"') { - add_char(&ret, &size, &len, '\\'); - add_char(&ret, &size, &len, '"'); - } else if (*s < ' ' || *s >= 127) { - n = utf8len(s); - if (n > 1) { - add_char(&ret, &size, &len, *s); - n -= 1; - } else { - n = 0; - add_char(&ret, &size, &len, '\\'); - add_char(&ret, &size, &len, 'x'); - add_char(&ret, &size, &len, "0123456789abcdef"[(int)*(unsigned char *)s >> 4]); - add_char(&ret, &size, &len, "0123456789abcdef"[(int)*(unsigned char *)s & 15]); - need_new_string = 1; - continue; - } - } else { - if (need_new_string && isxdigit(*s)) { - add_char(&ret, &size, &len, '"'); - add_char(&ret, &size, &len, '"'); - } - add_char(&ret, &size, &len, *s); - } - need_new_string = 0; - } - add_char(&ret, &size, &len, '"'); - add_char(&ret, &size, &len, '\0'); - free(str); - return ret; -} - - -static void -fprint_clockid(FILE *fp, pid_t pid, unsigned long int args[6], size_t arg_index) /* TODO */ -{ - (void) pid; - fprintf(fp, "%li", (long int)args[arg_index]); -} - - -static void -fprint_timespec(FILE *fp, pid_t pid, unsigned long int args[6], size_t arg_index) -{ - struct timespec ts; - const char *err; - if (get_struct(pid, args[arg_index], &ts, sizeof(ts), &err)) { - fprintf(fp, "%s", err); - return; - } - fprintf(fp, "{.tv_sec = %ji, .tv_nsec = %li}", (intmax_t)ts.tv_sec, ts.tv_nsec); -} + struct user_regs_struct regs; + switch (proc->state) { + default: + /* Get systemcall arguments */ + if (ptrace(PTRACE_GETREGS, proc->pid, NULL, ®s)) + eprintf("ptrace PTRACE_GETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid); + proc->scall = regs.orig_rax; + proc->args[0] = regs.rdi; + proc->args[1] = regs.rsi; + proc->args[2] = regs.rdx; + proc->args[3] = regs.r10; + proc->args[4] = regs.r8; + proc->args[5] = regs.r9; -static void -fprint_systemcall(FILE *fp, pid_t pid, const char *scall, const char *fmt, unsigned long int args[6], ...) -{ - typedef char *(*Function)(FILE *fp, pid_t pid, unsigned long int args[6], size_t arg_index); - Function funcs[6]; - size_t i, nfuncs = 0, func, len; - int ells = 0; - char *str; - const char *err; - va_list ap; - va_start(ap, args); - fprintf(fp, "%s(", scall); - for (i = 0; *fmt; fmt++) { - if (*fmt == ' ') - continue; - if (*fmt == 'l') { - ells += 1; - continue; - } else if (*fmt == 'L') { - ells += 2; - continue; - } else if (*fmt == 'h') { - ells -= 1; - continue; - } else if (*fmt == 'H') { - ells -= 2; - continue; - } - if (i) - fprintf(fp, ", "); - if (*fmt == 'p') { - if (args[i]) - fprintf(fp, "%p", (void *)args[i]); - else - fprintf(fp, "NULL"); - } else if (*fmt >= '1' && *fmt <= '6') { - func = (size_t)(*fmt - '0'); - while (nfuncs < func) - funcs[nfuncs++] = va_arg(ap, Function); - funcs[func - 1](fp, pid, args, i); - } else if (*fmt == 's') { - str = get_string(pid, args[i], &len, &err); - str = escape_memory(str, len); - fprintf(fp, "%s", str ? str : err); - free(str); - } else if (*fmt == 'm') { - str = escape_memory(get_memory(pid, args[i], (size_t)args[i + 1], &err), (size_t)args[i + 1]); - fprintf(fp, "%s", str ? str : err); - free(str); - } else if (*fmt == 'F') { - if ((int)args[i] == AT_FDCWD) - fprintf(fp, "AT_FDCWD"); - else - fprintf(fp, "%i", (int)args[i]); - } else if (*fmt == 'u') { - if (ells == 1) - fprintf(fp, "%lu", (unsigned long int)args[i]); - else if (ells > 1) - fprintf(fp, "%llu", (unsigned long long int)args[i]); - else - fprintf(fp, "%u", (unsigned int)args[i]); - } else if (*fmt == 'x') { - if (ells == 1) - fprintf(fp, "%#lx", (unsigned long int)args[i]); - else if (ells > 1) - fprintf(fp, "%#llx", (unsigned long long int)args[i]); - else - fprintf(fp, "%#x", (unsigned int)args[i]); - } else if (*fmt == 'o') { - if (ells == 1) - fprintf(fp, "%#lo", (unsigned long int)args[i]); - else if (ells > 1) - fprintf(fp, "%#llo", (unsigned long long int)args[i]); - else - fprintf(fp, "%#o", (unsigned int)args[i]); - } else { - if (ells == 1) - fprintf(fp, "%li", (long int)args[i]); - else if (ells > 1) - fprintf(fp, "%lli", (long long int)args[i]); - else - fprintf(fp, "%i", (int)args[i]); - } - ells = 0; - i += 1; - } - fprintf(fp, ")"); - va_end(ap); -} + /* Print system call */ + print_systemcall(proc); + /* Run system call */ + if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) + eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid); -static void -print_systemcall(FILE *fp, pid_t pid, unsigned long long int scall, unsigned long int args[6], enum Type *ret_type) -{ - char buf1[128], *p, *buf; - unsigned long int flags; + proc->state = Syscall; + break; -#define FLAGS_BEGIN(BUF, ARG)\ - p = buf = (BUF);\ - flags = (ARG) -#define FLAGS_ADD(FLAG)\ - if (flags & (unsigned long int)(FLAG))\ - p = stpcpy(p, "|"#FLAG) -#define FLAGS_END(FMT, TYPE)\ - if (flags || p == buf)\ - sprintf(p, "|"FMT, (TYPE)flags); + case Syscall: + /* Get system call result */ + if (ptrace(PTRACE_GETREGS, proc->pid, NULL, ®s)) + eprintf("ptrace PTRACE_GETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid); + proc->ret = regs.rax; -#define UNDOCUMENTED(NAME) GENERIC_HANDLER(NAME) -#define UNIMPLEMENTED(NAME) GENERIC_HANDLER(NAME) -#define GENERIC_HANDLER(NAME)\ - case SYS_##NAME:\ - fprintf(fp, "%s(raw: %lu, %lu, %lu, %lu, %lu, %lu)", #NAME,\ - args[0], args[1], args[2], args[3], args[4], args[5]);\ - break -#define SIMPLE(NAME, FMT, RET_TYPE)\ - case SYS_##NAME:\ - fprint_systemcall(fp, pid, #NAME, (FMT), args);\ - *ret_type = (RET_TYPE);\ - break -#define FORMATTERS(NAME, FMT, RET_TYPE, ...)\ - case SYS_##NAME:\ - fprint_systemcall(fp, pid, #NAME, (FMT), args, __VA_ARGS__);\ - *ret_type = (RET_TYPE);\ - break + /* Print system call result */ + print_systemcall_exit(proc); - *ret_type = Unknown; + /* Make process continue and stop at next syscall */ + if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) + eprintf("ptrace PTRACE_SYSCALL %ju NULL 0", (uintmax_t)proc->pid); - /* TODO replace GENERIC_HANDLER with specific handlers */ - switch (scall) { - GENERIC_HANDLER(_sysctl); - SIMPLE(accept, "ipp", Int); /* TODO output */ - case SYS_accept4: /* TODO output */ - FLAGS_BEGIN(buf1, args[3]); - FLAGS_ADD(SOCK_NONBLOCK); - FLAGS_ADD(SOCK_CLOEXEC); - FLAGS_END("%#x", unsigned int); - fprintf(fp, "accept4(%i, %p, %p, %s)", (int)args[0], (void *)args[1], (void *)args[2], &buf1[1]); - *ret_type = Int; - break; - SIMPLE(access, "si", Int); /* TODO flags */ - SIMPLE(acct, "s", Int); - GENERIC_HANDLER(add_key); - GENERIC_HANDLER(adjtimex); - UNIMPLEMENTED(afs_syscall); - SIMPLE(alarm, "", UInt); - GENERIC_HANDLER(arch_prctl); - GENERIC_HANDLER(bind); - GENERIC_HANDLER(bpf); - SIMPLE(brk, "p", Int); - GENERIC_HANDLER(capget); - GENERIC_HANDLER(capset); - SIMPLE(chdir, "s", Int); - SIMPLE(chmod, "so", Int); - SIMPLE(chown, "sii", Int); - SIMPLE(chroot, "s", Int); - UNDOCUMENTED(clock_adjtime); - FORMATTERS(clock_getres, "1p", Int, fprint_clockid); /* TODO output */ - FORMATTERS(clock_gettime, "1p", Int, fprint_clockid); /* TODO output */ - FORMATTERS(clock_nanosleep, "1i2p", Int, fprint_clockid, fprint_timespec); /* TODO output, flags */ - FORMATTERS(clock_settime, "12", Int, fprint_clockid, fprint_timespec); - GENERIC_HANDLER(clone); - GENERIC_HANDLER(clone3); - SIMPLE(close, "i", Int); - GENERIC_HANDLER(connect); - GENERIC_HANDLER(copy_file_range); - SIMPLE(creat, "so", Int); /* TODO flags */ - SIMPLE(create_module, "slu", Ptr); - SIMPLE(delete_module, "si", Int); /* TODO flags */ - SIMPLE(dup, "i", Int); - SIMPLE(dup2, "ii", Int); - SIMPLE(dup3, "iii", Int); - SIMPLE(epoll_create, "i", Int); - case SYS_epoll_create1:\ - FLAGS_BEGIN(buf1, args[0]); - FLAGS_ADD(EPOLL_CLOEXEC); - FLAGS_END("%#x", unsigned int); - fprintf(fp, "epoll_create1(%s)", &buf1[1]); - *ret_type = Int; - break; - GENERIC_HANDLER(epoll_ctl); - GENERIC_HANDLER(epoll_ctl_old); - GENERIC_HANDLER(epoll_pwait); - GENERIC_HANDLER(epoll_wait); - GENERIC_HANDLER(epoll_wait_old); - GENERIC_HANDLER(eventfd); - GENERIC_HANDLER(eventfd2); - GENERIC_HANDLER(execve); - GENERIC_HANDLER(execveat); - SIMPLE(exit, "i", Int); - SIMPLE(exit_group, "i", Int); - SIMPLE(faccessat, "Fsii", Int); /* TODO flags */ - GENERIC_HANDLER(fadvise64); - GENERIC_HANDLER(fallocate); - GENERIC_HANDLER(fanotify_init); - GENERIC_HANDLER(fanotify_mark); - SIMPLE(fchdir, "i", Int); - SIMPLE(fchmod, "io", Int); - SIMPLE(fchmodat, "Fsoi", Int); /* TODO flags */ - SIMPLE(fchown, "iii", Int); - SIMPLE(fchownat, "Fsiii", Int); /* TODO flags */ - GENERIC_HANDLER(fcntl); - SIMPLE(fdatasync, "i", Int); - SIMPLE(fgetxattr, "isplu", Long); /* TODO output */ - GENERIC_HANDLER(finit_module); - GENERIC_HANDLER(flistxattr); - GENERIC_HANDLER(flock); - SIMPLE(fork, "", Int); /* TODO fork */ - SIMPLE(fremovexattr, "is", Int); - UNDOCUMENTED(fsconfig); - SIMPLE(fsetxattr, "ismlui", Int); /* TODO flags */ - UNDOCUMENTED(fsmount); - UNDOCUMENTED(fsopen); - UNDOCUMENTED(fspick); - SIMPLE(fstat, "ip", Int); /* TODO output */ - SIMPLE(fstatfs, "ip", Int); /* TODO output */ - SIMPLE(fsync, "i", Int); - SIMPLE(ftruncate, "illi", Int); - GENERIC_HANDLER(futex); - GENERIC_HANDLER(futimesat); - GENERIC_HANDLER(get_kernel_syms); - GENERIC_HANDLER(get_mempolicy); - GENERIC_HANDLER(get_robust_list); - GENERIC_HANDLER(get_thread_area); - GENERIC_HANDLER(getcpu); - GENERIC_HANDLER(getcwd); - GENERIC_HANDLER(getdents); - GENERIC_HANDLER(getdents64); - SIMPLE(getegid, "", Int); - SIMPLE(geteuid, "", Int); - SIMPLE(getgid, "", Int); - GENERIC_HANDLER(getgroups); - GENERIC_HANDLER(getitimer); - GENERIC_HANDLER(getpeername); - SIMPLE(getpgid, "i", Int); - GENERIC_HANDLER(getpgrp); - SIMPLE(getpid, "", Int); - UNIMPLEMENTED(getpmsg); - SIMPLE(getppid, "", Int); - SIMPLE(getpriority, "ii", Int); - SIMPLE(getrandom, "pluu", Long); /* TODO output, flags */ - SIMPLE(getresgid, "ppp", Int); /* TODO output */ - SIMPLE(getresuid, "ppp", Int); /* TODO output */ - GENERIC_HANDLER(getrlimit); - GENERIC_HANDLER(getrusage); - SIMPLE(getsid, "i", Int); - GENERIC_HANDLER(getsockname); - GENERIC_HANDLER(getsockopt); - SIMPLE(gettid, "", Int); - GENERIC_HANDLER(gettimeofday); - SIMPLE(getuid, "", Int); - SIMPLE(getxattr, "ssplu", Long); /* TODO output */ - GENERIC_HANDLER(init_module); - SIMPLE(inotify_add_watch, "isx", Int); /* TODO flags */ - SIMPLE(inotify_init, "", Int); - SIMPLE(inotify_init1, "i", Int); /* TODO flags */ - SIMPLE(inotify_rm_watch, "ii", Int); - GENERIC_HANDLER(io_cancel); - GENERIC_HANDLER(io_destroy); - GENERIC_HANDLER(io_getevents); - GENERIC_HANDLER(io_pgetevents); - GENERIC_HANDLER(io_setup); - GENERIC_HANDLER(io_submit); - UNDOCUMENTED(io_uring_enter); - UNDOCUMENTED(io_uring_register); - UNDOCUMENTED(io_uring_setup); - GENERIC_HANDLER(ioctl); - SIMPLE(ioperm, "lului", Int); - SIMPLE(iopl, "i", Int); - GENERIC_HANDLER(ioprio_get); - GENERIC_HANDLER(ioprio_set); - GENERIC_HANDLER(kcmp); - GENERIC_HANDLER(kexec_file_load); - GENERIC_HANDLER(kexec_load); - GENERIC_HANDLER(keyctl); - SIMPLE(kill, "ii", Int); /* TODO flags */ - SIMPLE(lchown, "sii", Int); - SIMPLE(lgetxattr, "ssplu", Long); /* TODO output */ - SIMPLE(link, "ss", Int); - SIMPLE(linkat, "FsFsi", Int); /* TODO flags */ - SIMPLE(listen, "ii", Int); - GENERIC_HANDLER(listxattr); - GENERIC_HANDLER(llistxattr); - GENERIC_HANDLER(lookup_dcookie); - SIMPLE(lremovexattr, "ss", Int); - SIMPLE(lseek, "illii", LLong); /* TODO flags */ - SIMPLE(lsetxattr, "ssmlui", Int); /* TODO flags */ - SIMPLE(lstat, "sp", Int); /* TODO output */ - SIMPLE(madvise, "plui", Int); /* TODO flags */ - GENERIC_HANDLER(mbind); - SIMPLE(membarrier, "ii", Int); /* TODO flags */ - SIMPLE(memfd_create, "su", Int); /* TODO flags */ - GENERIC_HANDLER(migrate_pages); - GENERIC_HANDLER(mincore); - SIMPLE(mkdir, "so", Int); - SIMPLE(mkdirat, "Fso", Int); - GENERIC_HANDLER(mknod); - GENERIC_HANDLER(mknodat); - SIMPLE(mlock, "plu", Int); - SIMPLE(mlock2, "plui", Int); /* TODO flags */ - SIMPLE(mlockall, "i", Int); /* TODO flags */ - SIMPLE(mmap, "pluiiilli", Ptr); /* TODO flags */ - GENERIC_HANDLER(modify_ldt); - GENERIC_HANDLER(mount); - UNDOCUMENTED(move_mount); - GENERIC_HANDLER(move_pages); - SIMPLE(mprotect, "plui", Int); /* TODO flags */ - GENERIC_HANDLER(mq_getsetattr); - GENERIC_HANDLER(mq_notify); - GENERIC_HANDLER(mq_open); - GENERIC_HANDLER(mq_timedreceive); - GENERIC_HANDLER(mq_timedsend); - GENERIC_HANDLER(mq_unlink); - GENERIC_HANDLER(mremap); - GENERIC_HANDLER(msgctl); - GENERIC_HANDLER(msgget); - GENERIC_HANDLER(msgrcv); - GENERIC_HANDLER(msgsnd); - SIMPLE(msync, "plui", Int); /* TODO flags */ - SIMPLE(munlock, "plu", Int); - SIMPLE(munlockall, "", Int); - SIMPLE(munmap, "plu", Int); - GENERIC_HANDLER(name_to_handle_at); - FORMATTERS(nanosleep, "1p", Int, fprint_timespec); /* TODO output */ - SIMPLE(newfstatat, "Fspi", Int); /* TODO output, flags */ - SIMPLE(nfsservctl, "ipp", Long); /* TODO flags, struct, output */ - GENERIC_HANDLER(open); - GENERIC_HANDLER(open_by_handle_at); - UNDOCUMENTED(open_tree); - GENERIC_HANDLER(openat); - SIMPLE(pause, "", Int); - GENERIC_HANDLER(perf_event_open); - GENERIC_HANDLER(personality); - SIMPLE(pidfd_open, "iu", Int); - GENERIC_HANDLER(pidfd_send_signal); - SIMPLE(pipe, "p", Int); /* TODO output */ - SIMPLE(pipe2, "pi", Int); /* TODO output, flags */ - SIMPLE(pivot_root, "ss", Int); - SIMPLE(pkey_alloc, "lulu", Int); /* TODO flags */ - SIMPLE(pkey_free, "i", Int); - SIMPLE(pkey_mprotect, "pluii", Int); /* TODO flags */ - GENERIC_HANDLER(poll); - GENERIC_HANDLER(ppoll); - GENERIC_HANDLER(prctl); - GENERIC_HANDLER(pread64); - GENERIC_HANDLER(preadv); - GENERIC_HANDLER(preadv2); - GENERIC_HANDLER(prlimit64); - GENERIC_HANDLER(process_vm_readv); - GENERIC_HANDLER(process_vm_writev); - GENERIC_HANDLER(pselect6); - GENERIC_HANDLER(ptrace); - UNIMPLEMENTED(putpmsg); - GENERIC_HANDLER(pwrite64); - GENERIC_HANDLER(pwritev); - GENERIC_HANDLER(pwritev2); - GENERIC_HANDLER(query_module); - GENERIC_HANDLER(quotactl); - SIMPLE(read, "iplu", Long); /* TODO output */ - SIMPLE(readahead, "illilu", Long); - SIMPLE(readlink, "splu", Long); /* TODO output */ - SIMPLE(readlinkat, "Fsplu", Long); /* TODO output */ - GENERIC_HANDLER(readv); - GENERIC_HANDLER(reboot); - GENERIC_HANDLER(recvfrom); - GENERIC_HANDLER(recvmmsg); - GENERIC_HANDLER(recvmsg); - GENERIC_HANDLER(remap_file_pages); - SIMPLE(removexattr, "ss", Int); - SIMPLE(rename, "ss", Int); - SIMPLE(renameat, "FsFs", Int); - SIMPLE(renameat2, "FsFsu", Int); /* TODO flags */ - GENERIC_HANDLER(request_key); - SIMPLE(restart_syscall, "", Int); - SIMPLE(rmdir, "s", Int); - UNDOCUMENTED(rseq); - GENERIC_HANDLER(rt_sigaction); - GENERIC_HANDLER(rt_sigpending); - GENERIC_HANDLER(rt_sigprocmask); - GENERIC_HANDLER(rt_sigqueueinfo); - GENERIC_HANDLER(rt_sigreturn); - GENERIC_HANDLER(rt_sigsuspend); - GENERIC_HANDLER(rt_sigtimedwait); - GENERIC_HANDLER(rt_tgsigqueueinfo); - SIMPLE(sched_get_priority_max, "i", Int); - SIMPLE(sched_get_priority_min, "i", Int); - GENERIC_HANDLER(sched_getaffinity); - GENERIC_HANDLER(sched_getattr); - GENERIC_HANDLER(sched_getparam); - GENERIC_HANDLER(sched_getscheduler); - GENERIC_HANDLER(sched_rr_get_interval); - GENERIC_HANDLER(sched_setaffinity); - GENERIC_HANDLER(sched_setattr); - GENERIC_HANDLER(sched_setparam); - GENERIC_HANDLER(sched_setscheduler); - SIMPLE(sched_yield, "", Int); - GENERIC_HANDLER(seccomp); - UNIMPLEMENTED(security); - GENERIC_HANDLER(select); - GENERIC_HANDLER(semctl); - GENERIC_HANDLER(semget); - GENERIC_HANDLER(semop); - GENERIC_HANDLER(semtimedop); - GENERIC_HANDLER(sendfile); - GENERIC_HANDLER(sendmmsg); - GENERIC_HANDLER(sendmsg); - GENERIC_HANDLER(sendto); - GENERIC_HANDLER(set_mempolicy); - GENERIC_HANDLER(set_robust_list); - GENERIC_HANDLER(set_thread_area); - SIMPLE(set_tid_address, "p", Long); - GENERIC_HANDLER(setdomainname); - SIMPLE(setfsgid, "i", Int); - SIMPLE(setfsuid, "i", Int); - SIMPLE(setgid, "i", Int); - GENERIC_HANDLER(setgroups); - GENERIC_HANDLER(sethostname); - GENERIC_HANDLER(setitimer); - GENERIC_HANDLER(setns); - SIMPLE(setpgid, "ii", Int); - SIMPLE(setpriority, "iii", Int); - SIMPLE(setregid, "ii", Int); - SIMPLE(setresgid, "iii", Int); - SIMPLE(setresuid, "iii", Int); - SIMPLE(setreuid, "ii", Int); - GENERIC_HANDLER(setrlimit); - SIMPLE(setsid, "", Int); - GENERIC_HANDLER(setsockopt); - GENERIC_HANDLER(settimeofday); - SIMPLE(setuid, "i", Int); - SIMPLE(setxattr, "ssmlui", Int); /* TODO flags */ - GENERIC_HANDLER(shmat); - GENERIC_HANDLER(shmctl); - GENERIC_HANDLER(shmdt); - GENERIC_HANDLER(shmget); - SIMPLE(shutdown, "ii", Int); /* TODO flags */ - GENERIC_HANDLER(sigaltstack); - GENERIC_HANDLER(signalfd); - GENERIC_HANDLER(signalfd4); - SIMPLE(socket, "iii", Int); /* TODO flags */ - SIMPLE(socketpair, "iiip", Int); /* TODO output, flags */ - GENERIC_HANDLER(splice); - GENERIC_HANDLER(stat); - GENERIC_HANDLER(statfs); - GENERIC_HANDLER(statx); - SIMPLE(swapoff, "s", Int); - SIMPLE(swapon, "si", Int); /* TODO flags */ - SIMPLE(symlink, "ss", Int); - SIMPLE(symlinkat, "sFs", Int); - SIMPLE(sync, "", Void); - SIMPLE(sync_file_range, "illilliu", Int); /* TODO flags */ - SIMPLE(syncfs, "i", Int); - GENERIC_HANDLER(sysfs); - GENERIC_HANDLER(sysinfo); - GENERIC_HANDLER(syslog); - GENERIC_HANDLER(tee); - SIMPLE(tgkill, "iii", Int); /* TODO flags */ - SIMPLE(time, "p", LLong); /* TODO output */ - GENERIC_HANDLER(timer_create); - GENERIC_HANDLER(timer_delete); - GENERIC_HANDLER(timer_getoverrun); - GENERIC_HANDLER(timer_gettime); - GENERIC_HANDLER(timer_settime); - GENERIC_HANDLER(timerfd_create); - GENERIC_HANDLER(timerfd_gettime); - GENERIC_HANDLER(timerfd_settime); - GENERIC_HANDLER(times); - SIMPLE(tkill, "ii", Int); /* TODO flags */ - SIMPLE(truncate, "slli", Int); - UNIMPLEMENTED(tuxcall); - SIMPLE(umask, "o", OInt); - SIMPLE(umount2, "si", Int); /* TODO flags */ - SIMPLE(uname, "p", Int); /* TODO output */ - SIMPLE(unlink, "s", Int); - SIMPLE(unlinkat, "Fsi", Int); /* TODO flags */ - SIMPLE(unshare, "i", Int); /* TODO flags */ - SIMPLE(uselib, "s", Int); - SIMPLE(userfaultfd, "i", Int); /* TODO flags */ - GENERIC_HANDLER(ustat); - GENERIC_HANDLER(utime); - GENERIC_HANDLER(utimensat); - GENERIC_HANDLER(utimes); - SIMPLE(vfork, "", Int); /* TODO fork */ - SIMPLE(vhangup, "", Int); - GENERIC_HANDLER(vmsplice); - UNIMPLEMENTED(vserver); - GENERIC_HANDLER(wait4); - GENERIC_HANDLER(waitid); - SIMPLE(write, "imlu", Long); - GENERIC_HANDLER(writev); - default: - fprintf(fp, "syscall_0x%lx(raw: %lu, %lu, %lu, %lu, %lu, %lu)", (unsigned long int)scall, - args[0], args[1], args[2], args[3], args[4], args[5]); + proc->state = Normal; break; } } @@ -787,19 +63,20 @@ print_systemcall(FILE *fp, pid_t pid, unsigned long long int scall, unsigned lon int main(int argc, char **argv) { - pid_t pid; - struct user_regs_struct regs; - unsigned long long int scall; + pid_t pid, orig_pid; long int tmp; - unsigned long int args[6]; - enum Type ret_type; char *outfile = NULL; FILE *outfp = stderr; const char *num = NULL; - int err; + int status, exit_value = 0; + unsigned long int trace_options = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD; + struct process *proc; + unsigned long int event; - /* TODO add option to trace children */ + /* TODO add support for exec */ /* TODO add option to trace threads */ + /* TODO add option to trace vforks */ + /* TODO add option to trace signals */ /* TODO add option to specify argv[0] */ ARGBEGIN { case 'f': @@ -807,39 +84,34 @@ main(int argc, char **argv) usage(); outfile = EARGF(usage()); break; + case 'F': + trace_options |= PTRACE_O_TRACEFORK; + break; default: usage(); } ARGEND; if (!argc) usage(); + init_process_list(); + /* Start program to trace */ pid = fork(); switch (pid) { case -1: - fprintf(stderr, "%s: fork: %s\n", argv0, strerror(errno)); + eprintf("fork:"); return 1; case 0: if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) { - fprintf(stderr, "%s: ptrace PTRACE_TRACEME 0 NULL 0: %s\n", argv0, strerror(errno)); + eprintf("ptrace PTRACE_TRACEME 0 NULL 0:"); return 1; } /* exec will block until parent attaches */ execvp(*argv, argv); - fprintf(stderr, "%s: execvp %s: %s\n", argv0, *argv, strerror(errno)); - exit(1); + eprintf("execvp %s:", *argv); default: - if (waitpid(pid, NULL, 0) < 0) { /* Wait for exec */ - fprintf(stderr, "%s: waitpid <tracee> NULL 0: %s\n", argv0, strerror(errno)); - kill(pid, SIGKILL); - return 1; - } - if (ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL)) { - fprintf(stderr, "%s: waitpid <tracee> NULL 0: %s\n", argv0, strerror(errno)); - kill(pid, SIGKILL); - return 1; - } - /* TODO check that tracee is x86-64 */ + orig_pid = pid; + add_process(pid, trace_options); break; } @@ -865,7 +137,7 @@ main(int argc, char **argv) !*num) { outfp = fdopen((int)tmp, "wb"); if (!outfp) { - fprintf(stderr, "%s: fdopen %li wb: %s\n", argv0, tmp, strerror(errno)); + eprintf("fdopen %li wb:", tmp); return 1; } goto have_outfp; @@ -873,91 +145,62 @@ main(int argc, char **argv) } outfp = fopen(outfile, "wb"); if (!outfp) { - fprintf(stderr, "%s: fopen %s wb: %s\n", argv0, outfile, strerror(errno)); + eprintf("fopen %s wb:", outfile); return 1; } } + have_outfp: + set_trace_output(outfp); for (;;) { - /* Wait for next syscall */ - if (ptrace(PTRACE_SYSCALL, pid, NULL, 0)) { - fprintf(stderr, "%s: ptrace PTRACE_SYSCALL <tracee> NULL 0: %s\n", argv0, strerror(errno)); - return 1; - } - if (waitpid(pid, 0, 0) < 0) { - fprintf(stderr, "%s: waitpid <tracee> NULL 0: %s\n", argv0, strerror(errno)); - return 1; - } - - /* Get systemcall arguments */ - if (ptrace(PTRACE_GETREGS, pid, NULL, ®s)) { - fprintf(stderr, "%s: ptrace PTRACE_GETREGS <tracee> NULL <buffer>: %s\n", argv0, strerror(errno)); + /* Wait for next syscall enter/exit */ + pid = wait(&status); + if (pid < 0) { + if (errno == ECHILD) + return exit_value; + eprintf("wait <buffer>:"); return 1; } - scall = regs.orig_rax; - args[0] = regs.rdi; - args[1] = regs.rsi; - args[2] = regs.rdx; - args[3] = regs.r10; - args[4] = regs.r8; - args[5] = regs.r9; - /* Print system call */ - print_systemcall(outfp, pid, scall, args, &ret_type); - - /* Run system call */ - if (ptrace(PTRACE_SYSCALL, pid, NULL, 0)) { - fprintf(stderr, "%s: ptrace PTRACE_SYSCALL <tracee> NULL 0: %s\n", argv0, strerror(errno)); - return 1; - } - if (waitpid(pid, 0, 0) == -1) { - fprintf(stderr, "%s: waitpid <tracee> NULL 0: %s\n", argv0, strerror(errno)); - return 1; - } + proc = find_process(pid); + if (!proc) + continue; - /* Get system call result */ - if (ptrace(PTRACE_GETREGS, pid, NULL, ®s)) { - fprintf(outfp, " = ?\n"); - if (errno == ESRCH) - exit((int)regs.rdi); - fprintf(stderr, "%s: ptrace PTRACE_GETREGS <tracee> NULL <buffer>: %s\n", argv0, strerror(errno)); - } + if (WIFEXITED(status)) { + if (pid == orig_pid) + exit_value = WEXITSTATUS(status); + tprintf(proc, "\nProcess exited with value %i\n", WEXITSTATUS(status)); + + } else if (WIFSIGNALED(status)) { + tprintf(proc, "\nProcess terminated by signal %i (%s)\n", WTERMSIG(status), strsignal(WTERMSIG(status))); + /* TODO print signal name */ + + } else if (WIFSTOPPED(status)) { + if (WSTOPSIG(status) == (SIGTRAP | 0x80)) { + handle_syscall(proc); + } else if (WSTOPSIG(status) == SIGTRAP) { + switch (((status >> 8) ^ SIGTRAP) >> 8) { + + case PTRACE_EVENT_FORK: + if (ptrace(PTRACE_GETEVENTMSG, proc->pid, NULL, &event)) + eprintf("ptrace PTRACE_GETEVENTMSG %ju NULL <buffer>:", (uintmax_t)proc->pid); + add_process((pid_t)event, trace_options); + handle_syscall(proc); + break; + + default: + goto print_signal; + } + } else { + print_signal: + tprintf(proc, "\nProcess stopped by signal %i (%s)\n", WSTOPSIG(status), strsignal(WSTOPSIG(status))); + /* TODO print signal name */ + } - /* Print system call result */ - if (ret_type == Int) - fprintf(outfp, " = %i", (int)regs.rax); - else if (ret_type == UInt) - fprintf(outfp, " = %u", (unsigned int)regs.rax); - else if (ret_type == OInt) - fprintf(outfp, " = %#o", (unsigned int)regs.rax); - else if (ret_type == XInt) - fprintf(outfp, " = %#x", (unsigned int)regs.rax); - else if (ret_type == Long) - fprintf(outfp, " = %li", (long int)regs.rax); - else if (ret_type == ULong) - fprintf(outfp, " = %lu", (unsigned long int)regs.rax); - else if (ret_type == OLong) - fprintf(outfp, " = %#lo", (unsigned long int)regs.rax); - else if (ret_type == XLong) - fprintf(outfp, " = %#lx", (unsigned long int)regs.rax); - else if (ret_type == LLong) - fprintf(outfp, " = %lli", (long long int)regs.rax); - else if (ret_type == ULLong) - fprintf(outfp, " = %llu", (unsigned long long int)regs.rax); - else if (ret_type == OLLong) - fprintf(outfp, " = %#llo", (unsigned long long int)regs.rax); - else if (ret_type == XLLong) - fprintf(outfp, " = %#llx", (unsigned long long int)regs.rax); - else if (ret_type == Ptr && (long long int)regs.rax >= 0) - fprintf(outfp, " = %p", (void *)regs.rax); - else - fprintf(outfp, " = %li", (long int)regs.rax); - if ((unsigned long long int)regs.rax > -(unsigned long long int)PAGE_SIZE) { - err = -(int)regs.rax; - fprintf(outfp, " (%s: %s)", get_errno_name(err), strerror(err)); + } else if (WIFCONTINUED(status)) { + tprintf(proc, "\nProcess continued\n", (uintmax_t)pid); } - fprintf(outfp, "\n"); } fclose(outfp); @@ -0,0 +1,56 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static FILE *trace_fp; +static char last_char = '\n'; +static pid_t last_pid = 0; + + +void +set_trace_output(FILE *fp) +{ + trace_fp = fp; +} + + +void +tprintf(struct process *proc, const char *fmt, ...) +{ + va_list ap; + if (fmt[0] == '\n' && fmt[1]) { + last_pid = 0; + fmt = &fmt[1]; + } + if (last_char == '\n') + fprintf(trace_fp, "[%ju] ", (uintmax_t)proc->pid); + else if (proc->pid != last_pid) + fprintf(trace_fp, "\n[%ju] ", (uintmax_t)proc->pid); + va_start(ap, fmt); + vfprintf(trace_fp, fmt, ap); + last_pid = proc->pid; + last_char = strchr(fmt, '\0')[-1]; + va_end(ap); +} + + +_Noreturn void +eprintf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s%s: ", last_char == '\n' ? "" : "\n", argv0); + vfprintf(stderr, fmt, ap); + switch (strchr(fmt, '\0')[-1]) { + case ':': + fprintf(stderr, " %s\n", strerror(errno)); + break; + case '\n': + break; + default: + fprintf(stderr, "\n"); + break; + } + va_end(ap); + exit(1); +} |