diff options
author | Mattias Andrée <maandree@kth.se> | 2020-05-27 20:01:51 +0200 |
---|---|---|
committer | Mattias Andrée <maandree@kth.se> | 2020-05-27 20:01:51 +0200 |
commit | ec04bfbd248c58ccda8cda3df785b86252efb707 (patch) | |
tree | f4a8b88fad6f7bcd44f9e952f3163a95f0fb1604 | |
download | sctrace-ec04bfbd248c58ccda8cda3df785b86252efb707.tar.gz sctrace-ec04bfbd248c58ccda8cda3df785b86252efb707.tar.bz2 sctrace-ec04bfbd248c58ccda8cda3df785b86252efb707.tar.xz |
First commit
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to '')
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | LICENSE | 15 | ||||
-rw-r--r-- | Makefile | 21 | ||||
-rw-r--r-- | arg.h | 79 | ||||
-rw-r--r-- | config.mk | 6 | ||||
-rw-r--r-- | sctrace.c | 966 |
6 files changed, 1092 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..511671e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*\#* +*~ +*.o +*.su +/sctrace @@ -0,0 +1,15 @@ +ISC License + +© 2020 Mattias Andrée <maandree@kth.se> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1a3ac27 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +all: sctrace + +sctrace: sctrace.c + $(CC) -o $@ $@.c $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) + +install: sctrace + mkdir -p -- "$(DESTDIR)$(PREFIX)/bin" + cp -- sctrace "$(DESTDIR)$(PREFIX)/bin" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/bin/sctrace" + +clean: + -rm -f -- *.o sctrace + +.PHONY: all install uninstall clean @@ -0,0 +1,79 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][0] == '-') {\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGALT(SYMBOL) }\ + } else if (argv[0][0] == SYMBOL) {\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +#define ARGEND }\ + } else {\ + break;\ + }\ + } + +#define ARGC() argc_ + +#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define LNGARG() &argv[0][0] + + +#endif diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..f4ac0df --- /dev/null +++ b/config.mk @@ -0,0 +1,6 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CPPFLAGS = -D_XOPEN_SOURCE=700 +CFLAGS = -std=c99 -Wall -O2 +LDFLAGS = -s diff --git a/sctrace.c b/sctrace.c new file mode 100644 index 0000000..8f3fd46 --- /dev/null +++ b/sctrace.c @@ -0,0 +1,966 @@ +/* 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/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" + + +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); + exit(1); +} + + +static char * +get_string_or_memory(pid_t pid, unsigned long int addr, size_t m, int string, const char **errorp) +{ + size_t orig_addr = (size_t)addr; + size_t off; + char *ret = NULL; + size_t size = 0; + size_t len = 0; + size_t i; + union { + long int value; + char bytes[sizeof(long int)]; + } u; + + *errorp = NULL; + + if (!addr) + return NULL; + + addr &= -sizeof(long int); + off = orig_addr - (size_t)addr; + + for (;;) { + errno = 0; + u.value = ptrace(PTRACE_PEEKDATA, pid, addr, 0); + switch (errno) { + case 0: + break; + case EFAULT: + *errorp = "<invalid address>"; + return NULL; + default: + *errorp = "<an error occured during reading of string>"; + return NULL; + } + + addr += sizeof(long int); + if (size - len < sizeof(long int)) { + ret = realloc(ret, size += sizeof(long int) * 32); + if (!ret) { + fprintf(stderr, "%s: realloc: %s\n", argv0, strerror(errno)); + exit(1); + } + } + if (string) { + for (i = off; i < sizeof(u.bytes); i++, len++) { + ret[len] = u.bytes[i]; + if (!ret[len]) + return ret; + } + } else { + for (i = off; i < sizeof(u.bytes) && len < m; i++, len++) + ret[len] = u.bytes[i]; + if (len == m) + return ret; + } + off = 0; + } +} + + +static char * +get_memory(pid_t pid, unsigned long int addr, size_t m, const char **errorp) +{ + return get_string_or_memory(pid, addr, m, 0, errorp); +} + + +static char * +get_string(pid_t pid, unsigned long int addr, const char **errorp) +{ + return get_string_or_memory(pid, addr, 0, 1, errorp); +} + + +static int +get_struct(pid_t pid, unsigned long int addr, void *out, size_t size, const char **errorp) +{ + char *data = get_string_or_memory(pid, addr, size, 0, errorp); + if (!data) + return -1; + memcpy(out, data, size); + free(data); + return 0; +} + + +static void +add_char(char **strp, size_t *sizep, size_t *lenp, char c) +{ + 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 *s) +{ + size_t rank, i, len; + uint32_t code; + + 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 (rank = 0; rank < sizeof(lookup) / sizeof(*lookup); rank++) + if (lookup[rank].lower <= *(uint8_t *)s && *(uint8_t *)s <= lookup[rank].upper) + goto found; + return 0; + +found: + code = (uint32_t)(*(uint8_t *)s & lookup[rank].mask); + len = rank + 1; + for (i = 1; i < len; i++) { + if ((s[i] & 0xC0) != 0x80) + return 0; + code = (code << 6) | (uint32_t)(s[i] & 0x3F); + } + + if (code < lookup[rank].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 char * +escape_string(char *str) +{ + return escape_memory(str, str ? strlen(str) : 0); +} + + +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); +} + + +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; + 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 = escape_string(get_string(pid, args[i], &err)); + 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); +} + + +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; + +#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:\ + 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 + + *ret_type = Unknown; + + 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]); + break; + } +} + + +int +main(int argc, char **argv) +{ + pid_t pid; + struct user_regs_struct regs; + unsigned long long int scall; + long int tmp; + unsigned long int args[6]; + enum Type ret_type; + char *outfile = NULL; + FILE *outfp = stderr; + const char *num = NULL; + + ARGBEGIN { + case 'f': + if (outfile) + usage(); + outfile = EARGF(usage()); + break; + default: + usage(); + } ARGEND; + if (!argc) + usage(); + + /* Start program to trace */ + pid = fork(); + switch (pid) { + case -1: + fprintf(stderr, "%s: fork: %s\n", argv0, strerror(errno)); + 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)); + return 1; + } + /* exec will block until parent attaches */ + execvp(*argv, argv); + fprintf(stderr, "%s: execvp %s: %s\n", argv0, *argv, strerror(errno)); + exit(1); + 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 */ + break; + } + + /* Open trace output file */ + if (outfile) { + if (!strncmp(outfile, "/dev/fd/", sizeof("/dev/fd/") - 1)) + num = &outfile[sizeof("/dev/fd/") - 1]; + else if (!strncmp(outfile, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1)) + num = &outfile[sizeof("/proc/self/fd/") - 1]; + else if (!strcmp(outfile, "/dev/stdin")) + num = "0"; + else if (!strcmp(outfile, "/dev/stdout")) + num = "1"; + else if (!strcmp(outfile, "/dev/stderr")) + num = "2"; + if (num && isdigit(*num)) { + errno = 0; + tmp = strtol(num, (void *)&num, 10); + if (!errno && tmp >= 0 && +#if INT_MAX < LONG_MAX + tmp < INT_MAX && +#endif + !*num) { + outfp = fdopen((int)tmp, "wb"); + if (!outfp) { + fprintf(stderr, "%s: fdopen %li wb: %s\n", argv0, tmp, strerror(errno)); + return 1; + } + goto have_outfp; + } + } + outfp = fopen(outfile, "wb"); + if (!outfp) { + fprintf(stderr, "%s: fopen %s wb: %s\n", argv0, outfile, strerror(errno)); + return 1; + } + } +have_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)); + 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; + } + + /* 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)); + } + + /* Print system call result */ + /* TODO print error name (not one all system calls) */ + if (ret_type == Int) + fprintf(outfp, " = %i\n", (int)regs.rax); + else if (ret_type == UInt) + fprintf(outfp, " = %u\n", (unsigned int)regs.rax); + else if (ret_type == OInt) + fprintf(outfp, " = %#o\n", (unsigned int)regs.rax); + else if (ret_type == XInt) + fprintf(outfp, " = %#x\n", (unsigned int)regs.rax); + else if (ret_type == Long) + fprintf(outfp, " = %li\n", (long int)regs.rax); + else if (ret_type == ULong) + fprintf(outfp, " = %lu\n", (unsigned long int)regs.rax); + else if (ret_type == OLong) + fprintf(outfp, " = %#lo\n", (unsigned long int)regs.rax); + else if (ret_type == XLong) + fprintf(outfp, " = %#lx\n", (unsigned long int)regs.rax); + else if (ret_type == LLong) + fprintf(outfp, " = %lli\n", (long long int)regs.rax); + else if (ret_type == ULLong) + fprintf(outfp, " = %llu\n", (unsigned long long int)regs.rax); + else if (ret_type == OLLong) + fprintf(outfp, " = %#llo\n", (unsigned long long int)regs.rax); + else if (ret_type == XLLong) + fprintf(outfp, " = %#llx\n", (unsigned long long int)regs.rax); + else if (ret_type == Ptr && (long long int)regs.rax >= 0) + fprintf(outfp, " = %p\n", (void *)regs.rax); + else + fprintf(outfp, " = %li\n", (long int)regs.rax); + } + + fclose(outfp); + return 0; +} |