mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 15:03:34 +00:00
072e1d2910
- Fix sigsuspend() on XNU - Fix strsignal() on non-Linux - Add unit tests for strsignal() - Add unit tests for setitimer() - Add unit tests for sigsuspend() - Rewrite setitimer() for New Technology - Rewrite nanosleep() for New Technology - Polyfill SIGALRM on the New Technology - select(0,0,0,0) on NT now calls pause() - Remove some NTDLL calls that aren't needed - Polyfill SA_NOCLDWAIT on the New Technology - Polyfill SA_RESETHAND on the New Technology - Polyfill sigprocmask() on the New Technology - Polyfill SIGCHLD+SIG_IGN on the New Technology - Polyfill SA_RESTART masking on the New Technology - Deliver console signals from main thread on New Technology - Document SA_RESTART behavior w/ @sarestartable / @norestart - System call trace in MODE=dbg now prints inherited FDs and signal mask
1056 lines
49 KiB
C
1056 lines
49 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ 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. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/assert.h"
|
|
#include "libc/bits/bits.h"
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/sigbits.h"
|
|
#include "libc/calls/struct/iovec.h"
|
|
#include "libc/calls/struct/sigaction.h"
|
|
#include "libc/calls/struct/sigset.h"
|
|
#include "libc/calls/struct/stat.h"
|
|
#include "libc/calls/struct/user_regs_struct.h"
|
|
#include "libc/dce.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/fmt/fmt.h"
|
|
#include "libc/fmt/itoa.h"
|
|
#include "libc/intrin/kprintf.h"
|
|
#include "libc/log/check.h"
|
|
#include "libc/log/log.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/stdio/append.internal.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/sysv/consts/nr.h"
|
|
#include "libc/sysv/consts/ptrace.h"
|
|
#include "libc/sysv/consts/sig.h"
|
|
#include "libc/sysv/consts/w.h"
|
|
|
|
/**
|
|
* @fileoverview ptrace() tutorial
|
|
*/
|
|
|
|
#define PROLOGUE "%r%8d %'18T "
|
|
|
|
#undef __NR_execve
|
|
#define __WALL 0x40000000
|
|
|
|
#define PTR 0
|
|
#define ULONG 0
|
|
#define INT 1
|
|
#define LONG 2
|
|
#define STR 3
|
|
#define BUF 4
|
|
#define IOV 5
|
|
#define STRLIST 6
|
|
#define INTPTR 7
|
|
#define STAT 8
|
|
#define SIG 9
|
|
#define SIGSET 10
|
|
#define OCTAL 0x80
|
|
|
|
static const struct Syscall {
|
|
long *number;
|
|
const char *name;
|
|
unsigned char arity;
|
|
unsigned char ret;
|
|
unsigned char arg[6];
|
|
} kSyscalls[] = {
|
|
// clang-format off
|
|
{&__NR_exit, "exit", 1, INT, {INT}},
|
|
{&__NR_exit_group, "exit_group", 1, INT, {INT}},
|
|
{&__NR_read, "read", 3, LONG, {INT, BUF, ULONG}},
|
|
{&__NR_write, "write", 3, LONG, {INT, BUF, ULONG}},
|
|
{&__NR_open, "open", 3, INT, {STR, INT, OCTAL|INT}},
|
|
{&__NR_close, "close", 1, INT, {INT}},
|
|
{&__NR_stat, "stat", 2, INT, {STR, STAT}},
|
|
{&__NR_fstat, "fstat", 2, INT, {INT, STAT}},
|
|
{&__NR_lstat, "lstat", 2, INT, {INT, STAT}},
|
|
{&__NR_poll, "poll", 3, INT, {PTR, INT, INT}},
|
|
{&__NR_ppoll, "ppoll", 4, INT},
|
|
{&__NR_lseek, "lseek", 3, LONG, {INT, LONG, INT}},
|
|
{&__NR_mmap, "mmap", 6, ULONG, {PTR, ULONG, INT, INT, INT, ULONG}},
|
|
{&__NR_msync, "msync", 3, INT, {PTR, ULONG, INT}},
|
|
{&__NR_mprotect, "mprotect", 3, INT, {PTR, ULONG, INT}},
|
|
{&__NR_munmap, "munmap", 2, INT, {PTR, ULONG}},
|
|
{&__NR_sigaction, "rt_sigaction", 4, INT, {SIG}},
|
|
{&__NR_sigprocmask, "rt_sigprocmask", 4, INT, {INT, SIGSET, SIGSET, LONG}},
|
|
{&__NR_sigpending, "rt_sigpending", 2, INT, {SIGSET, LONG}},
|
|
{&__NR_sigsuspend, "rt_sigsuspend", 2, INT, {SIGSET, LONG}},
|
|
{&__NR_rt_sigqueueinfo, "rt_sigqueueinfo", 6},
|
|
{&__NR_ioctl, "ioctl", 3, INT, {INT, ULONG, ULONG}},
|
|
{&__NR_pread, "pread64", 4, LONG, {INT, BUF, ULONG, ULONG}},
|
|
{&__NR_pwrite, "pwrite64", 4, LONG, {INT, BUF, ULONG, ULONG}},
|
|
{&__NR_readv, "readv", 3, LONG, {INT, IOV, INT}},
|
|
{&__NR_writev, "writev", 3, LONG, {INT, IOV, INT}},
|
|
{&__NR_access, "access", 2, INT, {STR, OCTAL|INT}},
|
|
{&__NR_pipe, "pipe", 1, INT},
|
|
{&__NR_pipe2, "pipe2", 2, INT},
|
|
{&__NR_select, "select", 5},
|
|
{&__NR_pselect, "pselect", 6},
|
|
{&__NR_pselect6, "pselect6", 6},
|
|
{&__NR_sched_yield, "sched_yield", 0, INT},
|
|
{&__NR_mremap, "mremap", 5},
|
|
{&__NR_mincore, "mincore", 6},
|
|
{&__NR_madvise, "madvise", 6},
|
|
{&__NR_shmget, "shmget", 6},
|
|
{&__NR_shmat, "shmat", 6},
|
|
{&__NR_shmctl, "shmctl", 6},
|
|
{&__NR_dup, "dup", 1, INT, {INT}},
|
|
{&__NR_dup2, "dup2", 2, INT, {INT, INT}},
|
|
{&__NR_pause, "pause", 0, INT},
|
|
{&__NR_nanosleep, "nanosleep", 2},
|
|
{&__NR_getitimer, "getitimer", 2},
|
|
{&__NR_setitimer, "setitimer", 3},
|
|
{&__NR_alarm, "alarm", 1},
|
|
{&__NR_getpid, "getpid", 0, INT},
|
|
{&__NR_sendfile, "sendfile", 6},
|
|
{&__NR_socket, "socket", 3, INT, {INT, INT, INT}},
|
|
{&__NR_connect, "connect", 3},
|
|
{&__NR_accept, "accept", 3},
|
|
{&__NR_sendto, "sendto", 6},
|
|
{&__NR_recvfrom, "recvfrom", 6},
|
|
{&__NR_sendmsg, "sendmsg", 6},
|
|
{&__NR_recvmsg, "recvmsg", 6},
|
|
{&__NR_shutdown, "shutdown", 6},
|
|
{&__NR_bind, "bind", 6},
|
|
{&__NR_listen, "listen", 6},
|
|
{&__NR_getsockname, "getsockname", 6},
|
|
{&__NR_getpeername, "getpeername", 6},
|
|
{&__NR_socketpair, "socketpair", 6},
|
|
{&__NR_setsockopt, "setsockopt", 6},
|
|
{&__NR_getsockopt, "getsockopt", 6},
|
|
{&__NR_fork, "fork", 0, INT},
|
|
{&__NR_vfork, "vfork", 0, INT},
|
|
{&__NR_posix_spawn, "posix_spawn", 6},
|
|
{&__NR_execve, "execve", 3, INT, {STR, STRLIST, STRLIST}},
|
|
{&__NR_wait4, "wait4", 4, INT, {INT, INTPTR, INT, PTR}},
|
|
{&__NR_kill, "kill", 2, INT, {INT, SIG}},
|
|
{&__NR_killpg, "killpg", 2, INT, {INT, SIG}},
|
|
{&__NR_clone, "clone", 5, INT, {PTR, PTR, INTPTR, INTPTR, ULONG}},
|
|
{&__NR_tkill, "tkill", 2, INT, {INT, SIG}},
|
|
{&__NR_futex, "futex", 6},
|
|
{&__NR_set_robust_list, "set_robust_list", 6},
|
|
{&__NR_get_robust_list, "get_robust_list", 6},
|
|
{&__NR_uname, "uname", 6},
|
|
{&__NR_semget, "semget", 6},
|
|
{&__NR_semop, "semop", 6},
|
|
{&__NR_semctl, "semctl", 6},
|
|
{&__NR_shmdt, "shmdt", 6},
|
|
{&__NR_msgget, "msgget", 6},
|
|
{&__NR_msgsnd, "msgsnd", 6},
|
|
{&__NR_msgrcv, "msgrcv", 6},
|
|
{&__NR_msgctl, "msgctl", 6},
|
|
{&__NR_fcntl, "fcntl", 3, INT, {INT, INT, ULONG}},
|
|
{&__NR_flock, "flock", 6},
|
|
{&__NR_fsync, "fsync", 6},
|
|
{&__NR_fdatasync, "fdatasync", 6},
|
|
{&__NR_truncate, "truncate", 2, INT, {STR, ULONG}},
|
|
{&__NR_ftruncate, "ftruncate", 6, INT, {INT, ULONG}},
|
|
{&__NR_getcwd, "getcwd", 2, INT, {BUF, ULONG}},
|
|
{&__NR_chdir, "chdir", 1, INT, {STR}},
|
|
{&__NR_fchdir, "fchdir", 1, INT, {INT}},
|
|
{&__NR_rename, "rename", 2, INT, {STR, STR}},
|
|
{&__NR_mkdir, "mkdir", 2, INT, {STR, OCTAL|INT}},
|
|
{&__NR_rmdir, "rmdir", 1, INT, {STR}},
|
|
{&__NR_creat, "creat", 2, INT, {STR, OCTAL|INT}},
|
|
{&__NR_link, "link", 2, INT, {STR, STR}},
|
|
{&__NR_unlink, "unlink", 1, INT, {STR}},
|
|
{&__NR_symlink, "symlink", 6},
|
|
{&__NR_readlink, "readlink", 6},
|
|
{&__NR_chmod, "chmod", 6},
|
|
{&__NR_fchmod, "fchmod", 6},
|
|
{&__NR_chown, "chown", 6},
|
|
{&__NR_fchown, "fchown", 6},
|
|
{&__NR_lchown, "lchown", 6},
|
|
{&__NR_umask, "umask", 1, OCTAL|INT, {OCTAL|INT}},
|
|
{&__NR_gettimeofday, "gettimeofday", 6},
|
|
{&__NR_getrlimit, "getrlimit", 6},
|
|
{&__NR_getrusage, "getrusage", 6},
|
|
{&__NR_sysinfo, "sysinfo", 6},
|
|
{&__NR_times, "times", 6},
|
|
{&__NR_ptrace, "ptrace", 6},
|
|
{&__NR_syslog, "syslog", 6},
|
|
{&__NR_getuid, "getuid", 0, INT},
|
|
{&__NR_getgid, "getgid", 0, INT},
|
|
{&__NR_getppid, "getppid", 0, INT},
|
|
{&__NR_getpgrp, "getpgrp", 0, INT},
|
|
{&__NR_setsid, "setsid", 6},
|
|
{&__NR_getsid, "getsid", 0},
|
|
{&__NR_getpgid, "getpgid", 0},
|
|
{&__NR_setpgid, "setpgid", 6},
|
|
{&__NR_geteuid, "geteuid", 0, INT},
|
|
{&__NR_getegid, "getegid", 0, INT},
|
|
{&__NR_getgroups, "getgroups", 6},
|
|
{&__NR_setgroups, "setgroups", 6},
|
|
{&__NR_setreuid, "setreuid", 6},
|
|
{&__NR_setregid, "setregid", 6},
|
|
{&__NR_setuid, "setuid", 6},
|
|
{&__NR_setgid, "setgid", 6},
|
|
{&__NR_setresuid, "setresuid", 6},
|
|
{&__NR_setresgid, "setresgid", 6},
|
|
{&__NR_getresuid, "getresuid", 6},
|
|
{&__NR_getresgid, "getresgid", 6},
|
|
{&__NR_sigaltstack, "sigaltstack", 6},
|
|
{&__NR_mknod, "mknod", 6},
|
|
{&__NR_mknodat, "mknodat", 6},
|
|
{&__NR_mkfifo, "mkfifo", 6},
|
|
{&__NR_mkfifoat, "mkfifoat", 6},
|
|
{&__NR_statfs, "statfs", 6},
|
|
{&__NR_fstatfs, "fstatfs", 6},
|
|
{&__NR_getpriority, "getpriority", 6},
|
|
{&__NR_setpriority, "setpriority", 6},
|
|
{&__NR_mlock, "mlock", 6},
|
|
{&__NR_munlock, "munlock", 6},
|
|
{&__NR_mlockall, "mlockall", 6},
|
|
{&__NR_munlockall, "munlockall", 6},
|
|
{&__NR_setrlimit, "setrlimit", 6},
|
|
{&__NR_chroot, "chroot", 6},
|
|
{&__NR_sync, "sync", 6},
|
|
{&__NR_acct, "acct", 6},
|
|
{&__NR_settimeofday, "settimeofday", 6},
|
|
{&__NR_mount, "mount", 6},
|
|
{&__NR_reboot, "reboot", 6},
|
|
{&__NR_quotactl, "quotactl", 6},
|
|
{&__NR_setfsuid, "setfsuid", 6},
|
|
{&__NR_setfsgid, "setfsgid", 6},
|
|
{&__NR_capget, "capget", 6},
|
|
{&__NR_capset, "capset", 6},
|
|
{&__NR_sigtimedwait, "sigtimedwait", 6},
|
|
{&__NR_personality, "personality", 6},
|
|
{&__NR_ustat, "ustat", 6},
|
|
{&__NR_sysfs, "sysfs", 6},
|
|
{&__NR_sched_setparam, "sched_setparam", 6},
|
|
{&__NR_sched_getparam, "sched_getparam", 6},
|
|
{&__NR_sched_setscheduler, "sched_setscheduler", 6},
|
|
{&__NR_sched_getscheduler, "sched_getscheduler", 6},
|
|
{&__NR_sched_get_priority_max, "sched_get_priority_max", 6},
|
|
{&__NR_sched_get_priority_min, "sched_get_priority_min", 6},
|
|
{&__NR_sched_rr_get_interval, "sched_rr_get_interval", 6},
|
|
{&__NR_vhangup, "vhangup", 6},
|
|
{&__NR_modify_ldt, "modify_ldt", 6},
|
|
{&__NR_pivot_root, "pivot_root", 6},
|
|
{&__NR__sysctl, "_sysctl", 6},
|
|
{&__NR_prctl, "prctl", 6},
|
|
{&__NR_arch_prctl, "arch_prctl", 2, INT, {INT, ULONG}},
|
|
{&__NR_adjtimex, "adjtimex", 6},
|
|
{&__NR_umount2, "umount2", 6},
|
|
{&__NR_swapon, "swapon", 6},
|
|
{&__NR_swapoff, "swapoff", 6},
|
|
{&__NR_sethostname, "sethostname", 6},
|
|
{&__NR_setdomainname, "setdomainname", 6},
|
|
{&__NR_iopl, "iopl", 6},
|
|
{&__NR_ioperm, "ioperm", 6},
|
|
{&__NR_init_module, "init_module", 6},
|
|
{&__NR_delete_module, "delete_module", 6},
|
|
{&__NR_gettid, "gettid", 6},
|
|
{&__NR_readahead, "readahead", 6},
|
|
{&__NR_setxattr, "setxattr", 6},
|
|
{&__NR_fsetxattr, "fsetxattr", 6},
|
|
{&__NR_getxattr, "getxattr", 6},
|
|
{&__NR_fgetxattr, "fgetxattr", 6},
|
|
{&__NR_listxattr, "listxattr", 6},
|
|
{&__NR_flistxattr, "flistxattr", 6},
|
|
{&__NR_removexattr, "removexattr", 6},
|
|
{&__NR_fremovexattr, "fremovexattr", 6},
|
|
{&__NR_lsetxattr, "lsetxattr", 6},
|
|
{&__NR_lgetxattr, "lgetxattr", 6},
|
|
{&__NR_llistxattr, "llistxattr", 6},
|
|
{&__NR_lremovexattr, "lremovexattr", 6},
|
|
{&__NR_sched_setaffinity, "sched_setaffinity", 6},
|
|
{&__NR_sched_getaffinity, "sched_getaffinity", 6},
|
|
{&__NR_cpuset_getaffinity, "cpuset_getaffinity", 6},
|
|
{&__NR_cpuset_setaffinity, "cpuset_setaffinity", 6},
|
|
{&__NR_io_setup, "io_setup", 6},
|
|
{&__NR_io_destroy, "io_destroy", 6},
|
|
{&__NR_io_getevents, "io_getevents", 6},
|
|
{&__NR_io_submit, "io_submit", 6},
|
|
{&__NR_io_cancel, "io_cancel", 6},
|
|
{&__NR_lookup_dcookie, "lookup_dcookie", 6},
|
|
{&__NR_epoll_create, "epoll_create", 6},
|
|
{&__NR_epoll_wait, "epoll_wait", 6},
|
|
{&__NR_epoll_ctl, "epoll_ctl", 6},
|
|
{&__NR_getdents, "getdents64", 6},
|
|
{&__NR_set_tid_address, "set_tid_address", 1},
|
|
{&__NR_restart_syscall, "restart_syscall", 6},
|
|
{&__NR_semtimedop, "semtimedop", 6},
|
|
{&__NR_fadvise, "fadvise", 6},
|
|
{&__NR_timer_create, "timer_create", 6},
|
|
{&__NR_timer_settime, "timer_settime", 6},
|
|
{&__NR_timer_gettime, "timer_gettime", 6},
|
|
{&__NR_timer_getoverrun, "timer_getoverrun", 6},
|
|
{&__NR_timer_delete, "timer_delete", 6},
|
|
{&__NR_clock_settime, "clock_settime", 6},
|
|
{&__NR_clock_gettime, "clock_gettime", 6},
|
|
{&__NR_clock_getres, "clock_getres", 6},
|
|
{&__NR_clock_nanosleep, "clock_nanosleep", 6},
|
|
{&__NR_tgkill, "tgkill", 6},
|
|
{&__NR_mbind, "mbind", 6},
|
|
{&__NR_set_mempolicy, "set_mempolicy", 6},
|
|
{&__NR_get_mempolicy, "get_mempolicy", 6},
|
|
{&__NR_mq_open, "mq_open", 6},
|
|
{&__NR_mq_unlink, "mq_unlink", 6},
|
|
{&__NR_mq_timedsend, "mq_timedsend", 6},
|
|
{&__NR_mq_timedreceive, "mq_timedreceive", 6},
|
|
{&__NR_mq_notify, "mq_notify", 6},
|
|
{&__NR_mq_getsetattr, "mq_getsetattr", 6},
|
|
{&__NR_kexec_load, "kexec_load", 6},
|
|
{&__NR_waitid, "waitid", 6},
|
|
{&__NR_add_key, "add_key", 6},
|
|
{&__NR_request_key, "request_key", 6},
|
|
{&__NR_keyctl, "keyctl", 6},
|
|
{&__NR_ioprio_set, "ioprio_set", 6},
|
|
{&__NR_ioprio_get, "ioprio_get", 6},
|
|
{&__NR_inotify_init, "inotify_init", 6},
|
|
{&__NR_inotify_add_watch, "inotify_add_watch", 6},
|
|
{&__NR_inotify_rm_watch, "inotify_rm_watch", 6},
|
|
{&__NR_openat, "openat", 4, INT, {INT, STR, INT, OCTAL|INT}},
|
|
{&__NR_mkdirat, "mkdirat", 3, INT, {INT, STR, OCTAL|INT}},
|
|
{&__NR_fchownat, "fchownat", 6},
|
|
{&__NR_utime, "utime", 6},
|
|
{&__NR_utimes, "utimes", 6},
|
|
{&__NR_futimesat, "futimesat", 6},
|
|
{&__NR_futimes, "futimes", 6},
|
|
{&__NR_futimens, "futimens", 6},
|
|
{&__NR_fstatat, "newfstatat", 4, INT, {INT, STR, STAT, INT}},
|
|
{&__NR_unlinkat, "unlinkat", 3, INT, {INT, STR, INT}},
|
|
{&__NR_renameat, "renameat", 4, INT, {INT, STR, INT, STR}},
|
|
{&__NR_linkat, "linkat", 6},
|
|
{&__NR_symlinkat, "symlinkat", 6},
|
|
{&__NR_readlinkat, "readlinkat", 6},
|
|
{&__NR_fchmodat, "fchmodat", 6},
|
|
{&__NR_faccessat, "faccessat", 4, INT, {INT, STR, INT, INT}},
|
|
{&__NR_unshare, "unshare", 6},
|
|
{&__NR_splice, "splice", 6},
|
|
{&__NR_tee, "tee", 6},
|
|
{&__NR_sync_file_range, "sync_file_range", 4},
|
|
{&__NR_vmsplice, "vmsplice", 6},
|
|
{&__NR_migrate_pages, "migrate_pages", 6},
|
|
{&__NR_move_pages, "move_pages", 6},
|
|
{&__NR_preadv, "preadv", 4, LONG, {INT, IOV, ULONG, ULONG}},
|
|
{&__NR_pwritev, "pwritev", 6, LONG, {INT, IOV, ULONG, ULONG}},
|
|
{&__NR_utimensat, "utimensat", 6},
|
|
{&__NR_fallocate, "fallocate", 6},
|
|
{&__NR_posix_fallocate, "posix_fallocate", 6},
|
|
{&__NR_accept4, "accept4", 4},
|
|
{&__NR_dup3, "dup3", 3, INT},
|
|
{&__NR_epoll_pwait, "epoll_pwait", 6},
|
|
{&__NR_epoll_create1, "epoll_create1", 6},
|
|
{&__NR_perf_event_open, "perf_event_open", 6},
|
|
{&__NR_inotify_init1, "inotify_init1", 6},
|
|
{&__NR_rt_tgsigqueueinfo, "rt_tgsigqueueinfo", 6},
|
|
{&__NR_signalfd, "signalfd", 6},
|
|
{&__NR_signalfd4, "signalfd4", 6},
|
|
{&__NR_eventfd, "eventfd", 6},
|
|
{&__NR_eventfd2, "eventfd2", 6},
|
|
{&__NR_timerfd_create, "timerfd_create", 6},
|
|
{&__NR_timerfd_settime, "timerfd_settime", 6},
|
|
{&__NR_timerfd_gettime, "timerfd_gettime", 6},
|
|
{&__NR_recvmmsg, "recvmmsg", 6},
|
|
{&__NR_fanotify_init, "fanotify_init", 6},
|
|
{&__NR_fanotify_mark, "fanotify_mark", 6},
|
|
{&__NR_prlimit, "prlimit", 6},
|
|
{&__NR_name_to_handle_at, "name_to_handle_at", 6},
|
|
{&__NR_open_by_handle_at, "open_by_handle_at", 6},
|
|
{&__NR_clock_adjtime, "clock_adjtime", 6},
|
|
{&__NR_syncfs, "syncfs", 6},
|
|
{&__NR_sendmmsg, "sendmmsg", 6},
|
|
{&__NR_setns, "setns", 6},
|
|
{&__NR_getcpu, "getcpu", 6},
|
|
{&__NR_process_vm_readv, "process_vm_readv", 6},
|
|
{&__NR_process_vm_writev, "process_vm_writev", 6},
|
|
{&__NR_kcmp, "kcmp", 6},
|
|
{&__NR_finit_module, "finit_module", 6},
|
|
{&__NR_sched_setattr, "sched_setattr", 6},
|
|
{&__NR_sched_getattr, "sched_getattr", 6},
|
|
{&__NR_renameat2, "renameat2", 6},
|
|
{&__NR_seccomp, "seccomp", 6},
|
|
{&__NR_getrandom, "getrandom", 6},
|
|
{&__NR_memfd_create, "memfd_create", 6},
|
|
{&__NR_kexec_file_load, "kexec_file_load", 6},
|
|
{&__NR_bpf, "bpf", 6},
|
|
{&__NR_execveat, "execveat", 6},
|
|
{&__NR_userfaultfd, "userfaultfd", 6},
|
|
{&__NR_membarrier, "membarrier", 6},
|
|
{&__NR_mlock2, "mlock2", 6},
|
|
{&__NR_copy_file_range, "copy_file_range", 6},
|
|
{&__NR_preadv2, "preadv2", 6},
|
|
{&__NR_pwritev2, "pwritev2", 6},
|
|
{&__NR_pkey_mprotect, "pkey_mprotect", 6},
|
|
{&__NR_pkey_alloc, "pkey_alloc", 6},
|
|
{&__NR_pkey_free, "pkey_free", 6},
|
|
{&__NR_statx, "statx", 6},
|
|
{&__NR_io_pgetevents, "io_pgetevents", 6},
|
|
{&__NR_rseq, "rseq", 6},
|
|
{&__NR_pidfd_send_signal, "pidfd_send_signal", 6},
|
|
{&__NR_io_uring_setup, "io_uring_setup", 6},
|
|
{&__NR_io_uring_enter, "io_uring_enter", 6},
|
|
{&__NR_io_uring_register, "io_uring_register", 6},
|
|
// clang-format on
|
|
};
|
|
|
|
static const struct Errno {
|
|
long *number;
|
|
const char *name;
|
|
} kErrnos[] = {
|
|
{&ENOSYS, "ENOSYS"}, //
|
|
{&EPERM, "EPERM"}, //
|
|
{&ENOENT, "ENOENT"}, //
|
|
{&ESRCH, "ESRCH"}, //
|
|
{&EINTR, "EINTR"}, //
|
|
{&EIO, "EIO"}, //
|
|
{&ENXIO, "ENXIO"}, //
|
|
{&E2BIG, "E2BIG"}, //
|
|
{&ENOEXEC, "ENOEXEC"}, //
|
|
{&EBADF, "EBADF"}, //
|
|
{&ECHILD, "ECHILD"}, //
|
|
{&EAGAIN, "EAGAIN"}, //
|
|
{&ENOMEM, "ENOMEM"}, //
|
|
{&EACCES, "EACCES"}, //
|
|
{&EFAULT, "EFAULT"}, //
|
|
{&ENOTBLK, "ENOTBLK"}, //
|
|
{&EBUSY, "EBUSY"}, //
|
|
{&EEXIST, "EEXIST"}, //
|
|
{&EXDEV, "EXDEV"}, //
|
|
{&ENODEV, "ENODEV"}, //
|
|
{&ENOTDIR, "ENOTDIR"}, //
|
|
{&EISDIR, "EISDIR"}, //
|
|
{&EINVAL, "EINVAL"}, //
|
|
{&ENFILE, "ENFILE"}, //
|
|
{&EMFILE, "EMFILE"}, //
|
|
{&ENOTTY, "ENOTTY"}, //
|
|
{&ETXTBSY, "ETXTBSY"}, //
|
|
{&EFBIG, "EFBIG"}, //
|
|
{&ENOSPC, "ENOSPC"}, //
|
|
{&EDQUOT, "EDQUOT"}, //
|
|
{&ESPIPE, "ESPIPE"}, //
|
|
{&EROFS, "EROFS"}, //
|
|
{&EMLINK, "EMLINK"}, //
|
|
{&EPIPE, "EPIPE"}, //
|
|
{&EDOM, "EDOM"}, //
|
|
{&ERANGE, "ERANGE"}, //
|
|
{&EDEADLK, "EDEADLK"}, //
|
|
{&ENAMETOOLONG, "ENAMETOOLONG"}, //
|
|
{&ENOLCK, "ENOLCK"}, //
|
|
{&ENOTEMPTY, "ENOTEMPTY"}, //
|
|
{&ELOOP, "ELOOP"}, //
|
|
{&ENOMSG, "ENOMSG"}, //
|
|
{&EIDRM, "EIDRM"}, //
|
|
{&ETIME, "ETIME"}, //
|
|
{&EPROTO, "EPROTO"}, //
|
|
{&EOVERFLOW, "EOVERFLOW"}, //
|
|
{&EILSEQ, "EILSEQ"}, //
|
|
{&EUSERS, "EUSERS"}, //
|
|
{&ENOTSOCK, "ENOTSOCK"}, //
|
|
{&EDESTADDRREQ, "EDESTADDRREQ"}, //
|
|
{&EMSGSIZE, "EMSGSIZE"}, //
|
|
{&EPROTOTYPE, "EPROTOTYPE"}, //
|
|
{&ENOPROTOOPT, "ENOPROTOOPT"}, //
|
|
{&EPROTONOSUPPORT, "EPROTONOSUPPORT"}, //
|
|
{&ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT"}, //
|
|
{&ENOTSUP, "ENOTSUP"}, //
|
|
{&EOPNOTSUPP, "EOPNOTSUPP"}, //
|
|
{&EPFNOSUPPORT, "EPFNOSUPPORT"}, //
|
|
{&EAFNOSUPPORT, "EAFNOSUPPORT"}, //
|
|
{&EADDRINUSE, "EADDRINUSE"}, //
|
|
{&EADDRNOTAVAIL, "EADDRNOTAVAIL"}, //
|
|
{&ENETDOWN, "ENETDOWN"}, //
|
|
{&ENETUNREACH, "ENETUNREACH"}, //
|
|
{&ENETRESET, "ENETRESET"}, //
|
|
{&ECONNABORTED, "ECONNABORTED"}, //
|
|
{&ECONNRESET, "ECONNRESET"}, //
|
|
{&ENOBUFS, "ENOBUFS"}, //
|
|
{&EISCONN, "EISCONN"}, //
|
|
{&ENOTCONN, "ENOTCONN"}, //
|
|
{&ESHUTDOWN, "ESHUTDOWN"}, //
|
|
{&ETOOMANYREFS, "ETOOMANYREFS"}, //
|
|
{&ETIMEDOUT, "ETIMEDOUT"}, //
|
|
{&ECONNREFUSED, "ECONNREFUSED"}, //
|
|
{&EHOSTDOWN, "EHOSTDOWN"}, //
|
|
{&EHOSTUNREACH, "EHOSTUNREACH"}, //
|
|
{&EALREADY, "EALREADY"}, //
|
|
{&EINPROGRESS, "EINPROGRESS"}, //
|
|
{&ESTALE, "ESTALE"}, //
|
|
{&EREMOTE, "EREMOTE"}, //
|
|
{&EBADRPC, "EBADRPC"}, //
|
|
{&ERPCMISMATCH, "ERPCMISMATCH"}, //
|
|
{&EPROGUNAVAIL, "EPROGUNAVAIL"}, //
|
|
{&EPROGMISMATCH, "EPROGMISMATCH"}, //
|
|
{&EPROCUNAVAIL, "EPROCUNAVAIL"}, //
|
|
{&EFTYPE, "EFTYPE"}, //
|
|
{&EAUTH, "EAUTH"}, //
|
|
{&ENEEDAUTH, "ENEEDAUTH"}, //
|
|
{&EPROCLIM, "EPROCLIM"}, //
|
|
{&ENOATTR, "ENOATTR"}, //
|
|
{&EPWROFF, "EPWROFF"}, //
|
|
{&EDEVERR, "EDEVERR"}, //
|
|
{&EBADEXEC, "EBADEXEC"}, //
|
|
{&EBADARCH, "EBADARCH"}, //
|
|
{&ESHLIBVERS, "ESHLIBVERS"}, //
|
|
{&EBADMACHO, "EBADMACHO"}, //
|
|
{&ENOPOLICY, "ENOPOLICY"}, //
|
|
{&EBADMSG, "EBADMSG"}, //
|
|
{&ECANCELED, "ECANCELED"}, //
|
|
{&EOWNERDEAD, "EOWNERDEAD"}, //
|
|
{&ENOTRECOVERABLE, "ENOTRECOVERABLE"}, //
|
|
{&ENONET, "ENONET"}, //
|
|
{&ERESTART, "ERESTART"}, //
|
|
{&ENOSR, "ENOSR"}, //
|
|
{&ENOSTR, "ENOSTR"}, //
|
|
{&ENODATA, "ENODATA"}, //
|
|
{&EMULTIHOP, "EMULTIHOP"}, //
|
|
{&ENOLINK, "ENOLINK"}, //
|
|
{&ENOMEDIUM, "ENOMEDIUM"}, //
|
|
{&EMEDIUMTYPE, "EMEDIUMTYPE"}, //
|
|
{&EWOULDBLOCK, "EWOULDBLOCK"}, //
|
|
};
|
|
|
|
struct Pid {
|
|
struct Pid *next;
|
|
struct Pid *prev;
|
|
int pid;
|
|
bool insyscall;
|
|
struct Syscall *call;
|
|
struct user_regs_struct args;
|
|
struct user_regs_struct res;
|
|
};
|
|
|
|
char *ob; // output buffer
|
|
struct Pid *sp; // active subprocess
|
|
|
|
static ssize_t WriteAll(int fd, const char *p, size_t n) {
|
|
ssize_t rc;
|
|
size_t i, got;
|
|
for (i = 0; i < n;) {
|
|
rc = write(fd, p + i, n - i);
|
|
if (rc != -1) {
|
|
got = rc;
|
|
i += got;
|
|
} else if (errno != EINTR) {
|
|
return -1;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static void Flush(void) {
|
|
WriteAll(2, ob, appendz(ob).i);
|
|
appendr(&ob, 0);
|
|
}
|
|
|
|
static const char *GetErrnoName(int x) {
|
|
int i;
|
|
static char buf[16];
|
|
if (x > 0) {
|
|
for (i = 0; i < ARRAYLEN(kErrnos); ++i) {
|
|
if (x == *kErrnos[i].number) {
|
|
return kErrnos[i].name;
|
|
}
|
|
}
|
|
}
|
|
int64toarray_radix10(x, buf);
|
|
return buf;
|
|
}
|
|
|
|
static struct Syscall *GetSyscall(int x) {
|
|
int i;
|
|
if (x > 0) {
|
|
for (i = 0; i < ARRAYLEN(kSyscalls); ++i) {
|
|
if (x == *kSyscalls[i].number) {
|
|
return kSyscalls + i;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static char *PeekString(unsigned long x) {
|
|
union {
|
|
char buf[8];
|
|
long word;
|
|
} u;
|
|
char *p;
|
|
unsigned offset;
|
|
unsigned long addr, i, n;
|
|
if (!x) return NULL;
|
|
addr = ROUNDDOWN(x, 8);
|
|
offset = x - addr;
|
|
u.word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr);
|
|
n = strnlen(u.buf + offset, 8 - offset);
|
|
p = malloc(n);
|
|
memcpy(p, u.buf + offset, n);
|
|
if (n == 8 - offset) {
|
|
do {
|
|
addr += 8;
|
|
u.word = ptrace(PTRACE_PEEKDATA, sp->pid, addr);
|
|
i = strnlen(u.buf, 8);
|
|
p = realloc(p, n + i);
|
|
memcpy(p + n, u.buf, i);
|
|
n += i;
|
|
} while (i == 8);
|
|
}
|
|
p = realloc(p, n + 1);
|
|
p[n] = 0;
|
|
return p;
|
|
}
|
|
|
|
static char *PrintString(char *s) {
|
|
kappendf(&ob, "%#s", s);
|
|
return s;
|
|
}
|
|
|
|
static void *PeekData(unsigned long x, size_t size) {
|
|
union {
|
|
char buf[8];
|
|
long word;
|
|
} u;
|
|
char *p;
|
|
unsigned offset;
|
|
unsigned long addr, i, n;
|
|
if (!x) return NULL;
|
|
addr = ROUNDDOWN(x, 8);
|
|
offset = x - addr;
|
|
u.word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr);
|
|
p = malloc(size);
|
|
memcpy(p, u.buf + offset, 8 - offset);
|
|
for (i = 8 - offset; i < size; i += MIN(8, size - i)) {
|
|
addr += 8;
|
|
u.word = ptrace(PTRACE_PEEKDATA, sp->pid, addr);
|
|
memcpy(p + i, u.buf, MIN(8, size - i));
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static char *PrintData(char *data, size_t size) {
|
|
kappendf(&ob, "%#.*hhs%s", MIN(40, size), data, size > 40 ? "..." : "", data);
|
|
return data;
|
|
}
|
|
|
|
static struct iovec *PeekIov(unsigned long addr, int len) {
|
|
int i;
|
|
char *p;
|
|
long word;
|
|
struct iovec *iov;
|
|
if (!addr) return NULL;
|
|
p = malloc(len * 16);
|
|
for (i = 0; i < len * 2; ++i, addr += sizeof(word)) {
|
|
word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr);
|
|
memcpy(p + i * sizeof(word), &word, sizeof(word));
|
|
}
|
|
iov = (struct iovec *)p;
|
|
for (i = 0; i < len; ++i) {
|
|
iov[i].iov_base = PeekData((long)iov[i].iov_base, iov[i].iov_len);
|
|
}
|
|
return iov;
|
|
}
|
|
|
|
static struct iovec *PrintIov(struct iovec *iov, int iovlen) {
|
|
int i;
|
|
if (iov) {
|
|
appendw(&ob, '{');
|
|
for (i = 0; i < iovlen; ++i) {
|
|
if (i) appendw(&ob, READ16LE(", "));
|
|
appendw(&ob, '{');
|
|
PrintData(iov[i].iov_base, iov[i].iov_len);
|
|
kappendf(&ob, ", %#lx}", iov[i].iov_len);
|
|
}
|
|
appendw(&ob, '}');
|
|
} else {
|
|
appendw(&ob, READ32LE("NULL"));
|
|
}
|
|
return iov;
|
|
}
|
|
|
|
static void FreeIov(struct iovec *iov, int iovlen) {
|
|
int i;
|
|
if (iov) {
|
|
for (i = 0; i < iovlen; ++i) {
|
|
free(iov[i].iov_base);
|
|
}
|
|
free(iov);
|
|
}
|
|
}
|
|
|
|
static char **PeekStringList(unsigned long addr) {
|
|
char *p;
|
|
long word;
|
|
size_t i, n;
|
|
char **list;
|
|
if (!addr) return NULL;
|
|
for (p = NULL, n = 0;; ++n) {
|
|
p = realloc(p, (n + 1) * sizeof(word));
|
|
word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr + n * sizeof(word));
|
|
memcpy(p + n * sizeof(word), &word, sizeof(word));
|
|
if (!word) break;
|
|
}
|
|
list = (char **)p;
|
|
for (i = 0; i < n; ++i) {
|
|
list[i] = PeekString((long)list[i]);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
static char **PrintStringList(char **list) {
|
|
int i;
|
|
if (list) {
|
|
appendw(&ob, '{');
|
|
for (i = 0;; ++i) {
|
|
if (i) appendw(&ob, READ16LE(", "));
|
|
PrintString(list[i]);
|
|
if (!list[i]) break;
|
|
}
|
|
appendw(&ob, '}');
|
|
} else {
|
|
appendw(&ob, READ32LE("NULL"));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
static void FreeStringList(char **list) {
|
|
int i;
|
|
if (list) {
|
|
for (i = 0; list[i]; ++i) {
|
|
free(list[i]);
|
|
}
|
|
free(list);
|
|
}
|
|
}
|
|
|
|
static struct stat *PeekStat(unsigned long addr) {
|
|
int i;
|
|
char *p;
|
|
long word;
|
|
if (!addr) return NULL;
|
|
p = malloc(sizeof(struct stat));
|
|
for (i = 0; i < sizeof(struct stat); i += sizeof(word)) {
|
|
word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr + i);
|
|
memcpy(p + i, &word, sizeof(word));
|
|
}
|
|
return (struct stat *)p;
|
|
}
|
|
|
|
static struct stat *PrintStat(struct stat *st) {
|
|
bool printed;
|
|
printed = false;
|
|
appendw(&ob, '{');
|
|
if (st->st_size) {
|
|
kappendf(&ob, ".st_size = %#lx", st->st_size);
|
|
printed = true;
|
|
}
|
|
if (st->st_mode) {
|
|
if (printed) appendw(&ob, READ16LE(", "));
|
|
kappendf(&ob, ".st_mode = %#o", st->st_mode);
|
|
printed = true;
|
|
}
|
|
appendw(&ob, '}');
|
|
return st;
|
|
}
|
|
|
|
static void PrintSigset(unsigned long p) {
|
|
kappendf(&ob, "{%#lx}", ptrace(PTRACE_PEEKTEXT, sp->pid, p));
|
|
}
|
|
|
|
static void PrintSyscallArg(int type, unsigned long x, unsigned long y) {
|
|
char *s;
|
|
switch (type & 31) {
|
|
case ULONG:
|
|
kappendf(&ob, "%#lx", x);
|
|
break;
|
|
case INT:
|
|
if (type & OCTAL) {
|
|
kappendf(&ob, "%#o", x);
|
|
} else {
|
|
kappendf(&ob, "%d", x);
|
|
}
|
|
break;
|
|
case LONG:
|
|
kappendf(&ob, "%ld", x);
|
|
break;
|
|
case STR:
|
|
free(PrintString(PeekString(x)));
|
|
break;
|
|
case BUF:
|
|
free(PrintData(PeekData(x, y), y));
|
|
break;
|
|
case IOV:
|
|
FreeIov(PrintIov(PeekIov(x, y), y), y);
|
|
break;
|
|
case STAT:
|
|
free(PrintStat(PeekStat(x)));
|
|
break;
|
|
case SIG:
|
|
appends(&ob, strsignal(x));
|
|
break;
|
|
case SIGSET:
|
|
PrintSigset(x);
|
|
break;
|
|
case STRLIST:
|
|
FreeStringList(PrintStringList(PeekStringList(x)));
|
|
break;
|
|
case INTPTR:
|
|
if (x) {
|
|
s = PeekData(x, 4);
|
|
kappendf(&ob, "{%d}", READ32LE(s));
|
|
free(s);
|
|
}
|
|
break;
|
|
default:
|
|
kappendf(&ob, "wut[%'ld]", x);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void PrintSyscall(void) {
|
|
if ((sp->call = GetSyscall(sp->args.orig_rax))) {
|
|
kappendf(&ob, PROLOGUE " %s(", sp->pid, sp->call->name);
|
|
if (0 < sp->call->arity) {
|
|
PrintSyscallArg(sp->call->arg[0], sp->args.rdi, sp->args.rsi);
|
|
if (1 < sp->call->arity) {
|
|
appendw(&ob, READ16LE(", "));
|
|
PrintSyscallArg(sp->call->arg[1], sp->args.rsi, sp->args.rdx);
|
|
if (2 < sp->call->arity) {
|
|
appendw(&ob, READ16LE(", "));
|
|
PrintSyscallArg(sp->call->arg[2], sp->args.rdx, sp->args.r10);
|
|
if (3 < sp->call->arity) {
|
|
appendw(&ob, READ16LE(", "));
|
|
PrintSyscallArg(sp->call->arg[3], sp->args.r10, sp->args.r8);
|
|
if (4 < sp->call->arity) {
|
|
appendw(&ob, READ16LE(", "));
|
|
PrintSyscallArg(sp->call->arg[4], sp->args.r8, sp->args.r9);
|
|
if (5 < sp->call->arity) {
|
|
appendw(&ob, READ16LE(", "));
|
|
PrintSyscallArg(sp->call->arg[5], sp->args.r9, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
appendw(&ob, ')');
|
|
}
|
|
}
|
|
|
|
static void PrintSyscallResult(void) {
|
|
if (sp->call) {
|
|
appends(&ob, " → ");
|
|
if (sp->res.rax > (unsigned long)-4096) {
|
|
kappendf(&ob, "-1 %s", GetErrnoName(-sp->res.rax));
|
|
} else {
|
|
PrintSyscallArg(sp->call->ret, sp->res.rax, 0);
|
|
}
|
|
kappendf(&ob, "%n");
|
|
Flush();
|
|
}
|
|
}
|
|
|
|
wontreturn void PropagateExit(int wstatus) {
|
|
exit(WEXITSTATUS(wstatus));
|
|
}
|
|
|
|
wontreturn void PropagateTermination(int wstatus) {
|
|
sigset_t mask;
|
|
// if signal that terminated program was sent to our whole
|
|
// process group, then that signal should be delivered and kill
|
|
// this process the moment it's unblocked here. we only unblock
|
|
// here because if the child process ignores the signal and does
|
|
// exit(0) then we want to propagate that intent too.
|
|
sigemptyset(&mask);
|
|
sigaddset(&mask, WTERMSIG(wstatus));
|
|
sigprocmask(SIG_UNBLOCK, &mask, 0);
|
|
// otherwise we can propagate it by the exit code convention
|
|
// commonly used with shell scripts
|
|
exit(128 + WTERMSIG(wstatus));
|
|
}
|
|
|
|
wontreturn void StraceMain(int argc, char *argv[]) {
|
|
bool islast;
|
|
unsigned long msg;
|
|
struct Pid *child;
|
|
struct Pid pidlist;
|
|
sigset_t mask, truemask;
|
|
struct sigaction sigdfl;
|
|
int rc, root, wstatus, resume, signal, injectsignal;
|
|
|
|
if (!IsLinux()) {
|
|
kprintf("error: ptrace() is only supported on linux right now%n");
|
|
exit(1);
|
|
}
|
|
|
|
if (IsModeDbg()) ShowCrashReports();
|
|
|
|
if (argc < 2) {
|
|
kprintf("Usage: %s PROGRAM [ARGS...]%n", argv[0]);
|
|
exit(1);
|
|
}
|
|
|
|
pidlist.next = sp = calloc(1, sizeof(struct Pid));
|
|
sp->prev = &pidlist;
|
|
|
|
sigdfl.sa_flags = 0;
|
|
sigdfl.sa_handler = SIG_DFL;
|
|
sigemptyset(&sigdfl.sa_mask);
|
|
|
|
sigemptyset(&mask);
|
|
sigaddset(&mask, SIGCHLD);
|
|
sigaddset(&mask, SIGINT);
|
|
sigaddset(&mask, SIGQUIT);
|
|
sigaddset(&mask, SIGTERM);
|
|
sigprocmask(SIG_BLOCK, &mask, &truemask);
|
|
|
|
CHECK_NE(-1, (sp->pid = root = fork()));
|
|
if (!sp->pid) {
|
|
sigprocmask(SIG_SETMASK, &truemask, 0);
|
|
ptrace(PTRACE_TRACEME);
|
|
execvp(argv[1], argv + 1);
|
|
_Exit(127);
|
|
}
|
|
|
|
// wait for ptrace(PTRACE_TRACEME) to be called
|
|
CHECK_EQ(sp->pid, waitpid(sp->pid, &wstatus, 0));
|
|
|
|
// configure linux process tracing
|
|
CHECK_NE(-1, ptrace(PTRACE_SETOPTIONS, sp->pid, 0,
|
|
PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK |
|
|
PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC |
|
|
PTRACE_O_TRACEEXIT));
|
|
|
|
// continue child process setting breakpoint at next system call
|
|
CHECK_NE(-1, ptrace(PTRACE_SYSCALL, sp->pid, 0, 0));
|
|
|
|
for (;;) {
|
|
// wait for something to happen
|
|
CHECK_NE(-1, (rc = waitpid(-1, &wstatus, WUNTRACED | __WALL)));
|
|
|
|
// iterate through linked list to find signalled process
|
|
for (sp = pidlist.next;; sp = sp->next) {
|
|
CHECK_NE(NULL, sp);
|
|
if (sp->pid == rc) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// handle actual exit
|
|
if (WIFEXITED(wstatus)) {
|
|
kprintf(PROLOGUE " exited with %d%n", sp->pid, WEXITSTATUS(wstatus));
|
|
sp->prev->next = sp->next;
|
|
if (sp->next) sp->next->prev = sp->prev;
|
|
islast = sp->pid == root || !pidlist.next;
|
|
free(sp);
|
|
sp = 0;
|
|
// we exit when the last process being monitored exits
|
|
if (islast) {
|
|
PropagateExit(wstatus);
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// handle actual kill
|
|
if (WIFSIGNALED(wstatus)) {
|
|
kprintf(PROLOGUE " exited with signal %s%n", sp->pid,
|
|
strsignal(WTERMSIG(wstatus)));
|
|
sp->prev->next = sp->next;
|
|
if (sp->next) sp->next->prev = sp->prev;
|
|
islast = sp->pid == root || !pidlist.next;
|
|
free(sp);
|
|
sp = 0;
|
|
// we die when the last process being monitored dies
|
|
if (islast) {
|
|
PropagateTermination(wstatus);
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// handle trace events
|
|
assert(WIFSTOPPED(wstatus));
|
|
injectsignal = 0;
|
|
resume = PTRACE_SYSCALL;
|
|
signal = (wstatus >> 8) & 0xffff;
|
|
if (signal == SIGTRAP) {
|
|
// trace system call
|
|
if (!sp->insyscall) {
|
|
CHECK_NE(-1, ptrace(PTRACE_GETREGS, sp->pid, 0, &sp->args));
|
|
if (sp->args.orig_rax == __NR_execve) {
|
|
PrintSyscall();
|
|
}
|
|
sp->insyscall = true;
|
|
} else if (sp->insyscall) {
|
|
CHECK_NE(-1, ptrace(PTRACE_GETREGS, sp->pid, 0, &sp->res));
|
|
if (sp->args.orig_rax != __NR_execve) {
|
|
PrintSyscall();
|
|
}
|
|
PrintSyscallResult();
|
|
sp->insyscall = false;
|
|
}
|
|
Flush();
|
|
|
|
} else if (signal == (SIGTRAP | (PTRACE_EVENT_FORK << 8)) ||
|
|
signal == (SIGTRAP | (PTRACE_EVENT_VFORK << 8)) ||
|
|
signal == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {
|
|
// trace multiple processes
|
|
// we can track multiple processes at the same time
|
|
CHECK_NE(-1, ptrace(PTRACE_GETEVENTMSG, sp->pid, 0, &msg));
|
|
if (signal == (SIGTRAP | (PTRACE_EVENT_FORK << 8))) {
|
|
kappendf(&ob, PROLOGUE " fork() → 0%n", msg);
|
|
} else if (signal == (SIGTRAP | (PTRACE_EVENT_VFORK << 8))) {
|
|
kappendf(&ob, PROLOGUE " vfork() → 0%n", msg);
|
|
} else {
|
|
kappendf(&ob, PROLOGUE " clone() → 0%n", msg);
|
|
}
|
|
child = calloc(1, sizeof(struct Pid));
|
|
child->next = pidlist.next;
|
|
child->prev = &pidlist;
|
|
child->pid = msg;
|
|
pidlist.next = child;
|
|
ptrace(PTRACE_SYSCALL, sp->pid, 0, 0);
|
|
ptrace(PTRACE_SYSCALL, child->pid, 0, 0);
|
|
Flush();
|
|
continue;
|
|
|
|
} else if (signal == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) {
|
|
// trace exit system call
|
|
// this gives us an opportunity to read the process memory
|
|
// we need to restart this process, for it to actually die
|
|
PrintSyscall();
|
|
kappendf(&ob, "%n");
|
|
Flush();
|
|
resume = PTRACE_CONT;
|
|
injectsignal = WSTOPSIG(wstatus);
|
|
|
|
} else if (signal == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) {
|
|
// handle execve() process replacement
|
|
// do nothing
|
|
|
|
} else {
|
|
// trace signal delivery
|
|
kappendf(&ob, PROLOGUE " %s (%#x)%n", sp->pid, strsignal(signal & 0x7f),
|
|
signal);
|
|
Flush();
|
|
injectsignal = signal;
|
|
}
|
|
|
|
// trace events always freeze the traced process
|
|
// this call will help it to start running again
|
|
ptrace(resume, sp->pid, 0, injectsignal);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
StraceMain(argc, argv);
|
|
}
|