mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
1198 lines
54 KiB
C
1198 lines
54 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/intrin/bits.h"
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/struct/iovec.h"
|
|
#include "libc/calls/struct/sigaction.h"
|
|
#include "libc/calls/struct/siginfo.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/intrin/nomultics.internal.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 PIPE 11
|
|
#define OCTAL 0x80
|
|
|
|
static const long __NR_brk = 12;
|
|
static const long __NR_sigreturn = 15;
|
|
|
|
static const struct Syscall {
|
|
long *number;
|
|
const char *name;
|
|
char arity;
|
|
char eager;
|
|
unsigned char ret;
|
|
unsigned char arg[6];
|
|
} kSyscalls[] = {
|
|
// clang-format off
|
|
{&__NR_exit, "exit", 1, 1, INT, {INT}},
|
|
{&__NR_exit_group, "exit_group", 1, 1, INT, {INT}},
|
|
{&__NR_read, "read", 3, 1, LONG, {INT, BUF, ULONG}},
|
|
{&__NR_write, "write", 3, 3, LONG, {INT, BUF, ULONG}},
|
|
{&__NR_open, "open", 3, 3, INT, {STR, INT, OCTAL|INT}},
|
|
{&__NR_close, "close", 1, 1, INT, {INT}},
|
|
{&__NR_brk, "brk", 1, 1, ULONG, {ULONG}},
|
|
{&__NR_stat, "stat", 2, 1, INT, {STR, STAT}},
|
|
{&__NR_fstat, "fstat", 2, 1, INT, {INT, STAT}},
|
|
{&__NR_lstat, "lstat", 2, 1, INT, {INT, STAT}},
|
|
{&__NR_poll, "poll", 3, 3, INT, {PTR, INT, INT}},
|
|
{&__NR_ppoll, "ppoll", 4, 4, INT},
|
|
{&__NR_lseek, "lseek", 3, 3, LONG, {INT, LONG, INT}},
|
|
{&__NR_mmap, "mmap", 6, 6, ULONG, {PTR, ULONG, INT, INT, INT, ULONG}},
|
|
{&__NR_msync, "msync", 3, 3, INT, {PTR, ULONG, INT}},
|
|
{&__NR_mprotect, "mprotect", 3, 3, INT, {PTR, ULONG, INT}},
|
|
{&__NR_munmap, "munmap", 2, 2, INT, {PTR, ULONG}},
|
|
{&__NR_sigreturn, "rt_sigreturn", 6, 6, LONG},
|
|
{&__NR_sigaction, "rt_sigaction", 4, 4, INT, {SIG}},
|
|
{&__NR_sigprocmask, "rt_sigprocmask", 4, 4, INT, {INT, SIGSET, SIGSET, LONG}},
|
|
{&__NR_sigpending, "rt_sigpending", 2, 2, INT, {SIGSET, LONG}},
|
|
{&__NR_sigsuspend, "rt_sigsuspend", 2, 2, INT, {SIGSET, LONG}},
|
|
{&__NR_rt_sigqueueinfo, "rt_sigqueueinfo", 6, 6},
|
|
{&__NR_ioctl, "ioctl", 3, 3, INT, {INT, ULONG, ULONG}},
|
|
{&__NR_pread, "pread64", 4, 1, LONG, {INT, BUF, ULONG, ULONG}},
|
|
{&__NR_pwrite, "pwrite64", 4, 4, LONG, {INT, BUF, ULONG, ULONG}},
|
|
{&__NR_readv, "readv", 3, 1, LONG, {INT, IOV, INT}},
|
|
{&__NR_writev, "writev", 3, 3, LONG, {INT, IOV, INT}},
|
|
{&__NR_access, "access", 2, 2, INT, {STR, OCTAL|INT}},
|
|
{&__NR_pipe, "pipe", 1, 0, INT, {PIPE}},
|
|
{&__NR_pipe2, "pipe2", 2, 0, INT, {PIPE, INT}},
|
|
{&__NR_select, "select", 5, 5},
|
|
{&__NR_pselect, "pselect", 6, 6},
|
|
{&__NR_pselect6, "pselect6", 6, 6},
|
|
{&__NR_sched_yield, "sched_yield", 0, 0, INT},
|
|
{&__NR_mremap, "mremap", 5, 5},
|
|
{&__NR_mincore, "mincore", 6, 6},
|
|
{&__NR_madvise, "madvise", 6, 6},
|
|
{&__NR_shmget, "shmget", 6, 6},
|
|
{&__NR_shmat, "shmat", 6, 6},
|
|
{&__NR_shmctl, "shmctl", 6, 6},
|
|
{&__NR_dup, "dup", 1, 1, INT, {INT}},
|
|
{&__NR_dup2, "dup2", 2, 2, INT, {INT, INT}},
|
|
{&__NR_pause, "pause", 0, 0, INT},
|
|
{&__NR_nanosleep, "nanosleep", 2, 1},
|
|
{&__NR_getitimer, "getitimer", 2, 2},
|
|
{&__NR_setitimer, "setitimer", 3, 3},
|
|
{&__NR_alarm, "alarm", 1, 1},
|
|
{&__NR_getpid, "getpid", 0, 0, INT},
|
|
{&__NR_sendfile, "sendfile", 6, 6},
|
|
{&__NR_socket, "socket", 3, 3, INT, {INT, INT, INT}},
|
|
{&__NR_connect, "connect", 3, 3},
|
|
{&__NR_accept, "accept", 3, 3},
|
|
{&__NR_sendto, "sendto", 6, 6},
|
|
{&__NR_recvfrom, "recvfrom", 6, 6},
|
|
{&__NR_sendmsg, "sendmsg", 6, 6},
|
|
{&__NR_recvmsg, "recvmsg", 6, 6},
|
|
{&__NR_shutdown, "shutdown", 6, 6},
|
|
{&__NR_bind, "bind", 6, 6},
|
|
{&__NR_listen, "listen", 6, 6},
|
|
{&__NR_getsockname, "getsockname", 6, 6},
|
|
{&__NR_getpeername, "getpeername", 6, 6},
|
|
{&__NR_socketpair, "socketpair", 6, 6},
|
|
{&__NR_setsockopt, "setsockopt", 6, 6},
|
|
{&__NR_getsockopt, "getsockopt", 6, 6},
|
|
{&__NR_fork, "fork", 0, 0, INT},
|
|
{&__NR_vfork, "vfork", 0, 0, INT},
|
|
{&__NR_posix_spawn, "posix_spawn", 6, 6},
|
|
{&__NR_execve, "execve", 3, 3, INT, {STR, STRLIST, STRLIST}},
|
|
{&__NR_wait4, "wait4", 4, 4, INT, {INT, INTPTR, INT, PTR}},
|
|
{&__NR_kill, "kill", 2, 2, INT, {INT, SIG}},
|
|
{&__NR_killpg, "killpg", 2, 2, INT, {INT, SIG}},
|
|
{&__NR_clone, "clone", 5, 5, INT, {PTR, PTR, INTPTR, INTPTR, ULONG}},
|
|
{&__NR_tkill, "tkill", 2, 2, INT, {INT, SIG}},
|
|
{&__NR_futex, "futex", 6, 6},
|
|
{&__NR_set_robust_list, "set_robust_list", 6, 6},
|
|
{&__NR_get_robust_list, "get_robust_list", 6, 6},
|
|
{&__NR_uname, "uname", 6, 6},
|
|
{&__NR_semget, "semget", 6, 6},
|
|
{&__NR_semop, "semop", 6, 6},
|
|
{&__NR_semctl, "semctl", 6, 6},
|
|
{&__NR_shmdt, "shmdt", 6, 6},
|
|
{&__NR_msgget, "msgget", 6, 6},
|
|
{&__NR_msgsnd, "msgsnd", 6, 6},
|
|
{&__NR_msgrcv, "msgrcv", 6, 6},
|
|
{&__NR_msgctl, "msgctl", 6, 6},
|
|
{&__NR_fcntl, "fcntl", 3, 3, INT, {INT, INT, ULONG}},
|
|
{&__NR_flock, "flock", 6, 6},
|
|
{&__NR_fsync, "fsync", 6, 6},
|
|
{&__NR_fdatasync, "fdatasync", 6, 6},
|
|
{&__NR_truncate, "truncate", 2, 2, INT, {STR, ULONG}},
|
|
{&__NR_ftruncate, "ftruncate", 6, 6, INT, {INT, ULONG}},
|
|
{&__NR_getcwd, "getcwd", 2, 2, INT, {BUF, ULONG}},
|
|
{&__NR_chdir, "chdir", 1, 1, INT, {STR}},
|
|
{&__NR_fchdir, "fchdir", 1, 1, INT, {INT}},
|
|
{&__NR_rename, "rename", 2, 2, INT, {STR, STR}},
|
|
{&__NR_mkdir, "mkdir", 2, 2, INT, {STR, OCTAL|INT}},
|
|
{&__NR_rmdir, "rmdir", 1, 1, INT, {STR}},
|
|
{&__NR_creat, "creat", 2, 2, INT, {STR, OCTAL|INT}},
|
|
{&__NR_link, "link", 2, 2, INT, {STR, STR}},
|
|
{&__NR_unlink, "unlink", 1, 1, INT, {STR}},
|
|
{&__NR_symlink, "symlink", 6, 6},
|
|
{&__NR_readlink, "readlink", 3, 1, INT, {STR, BUF, ULONG}},
|
|
{&__NR_chmod, "chmod", 6, 6},
|
|
{&__NR_fchmod, "fchmod", 6, 6},
|
|
{&__NR_chown, "chown", 6, 6},
|
|
{&__NR_fchown, "fchown", 6, 6},
|
|
{&__NR_lchown, "lchown", 6, 6},
|
|
{&__NR_umask, "umask", 1, 1, OCTAL|INT, {OCTAL|INT}},
|
|
{&__NR_gettimeofday, "gettimeofday", 6, 6},
|
|
{&__NR_getrlimit, "getrlimit", 6, 6},
|
|
{&__NR_getrusage, "getrusage", 6, 6},
|
|
{&__NR_sysinfo, "sysinfo", 6, 6},
|
|
{&__NR_times, "times", 6, 6},
|
|
{&__NR_ptrace, "ptrace", 6, 6},
|
|
{&__NR_syslog, "syslog", 6, 6},
|
|
{&__NR_getuid, "getuid", 0, 0, INT},
|
|
{&__NR_getgid, "getgid", 0, 0, INT},
|
|
{&__NR_getppid, "getppid", 0, 0, INT},
|
|
{&__NR_getpgrp, "getpgrp", 0, 0, INT},
|
|
{&__NR_setsid, "setsid", 1, 1, INT, {INT}},
|
|
{&__NR_getsid, "getsid", 0, 0, INT},
|
|
{&__NR_getpgid, "getpgid", 0, 0, INT},
|
|
{&__NR_setpgid, "setpgid", 2, 2, INT, {INT, INT}},
|
|
{&__NR_geteuid, "geteuid", 0, 0, INT},
|
|
{&__NR_getegid, "getegid", 0, 0, INT},
|
|
{&__NR_getgroups, "getgroups", 6, 6},
|
|
{&__NR_setgroups, "setgroups", 6, 6},
|
|
{&__NR_setreuid, "setreuid", 6, 6},
|
|
{&__NR_setregid, "setregid", 6, 6},
|
|
{&__NR_setuid, "setuid", 1, 1, INT, {INT}},
|
|
{&__NR_setgid, "setgid", 1, 1, INT, {INT}},
|
|
{&__NR_setresuid, "setresuid", 6, 6},
|
|
{&__NR_setresgid, "setresgid", 6, 6},
|
|
{&__NR_getresuid, "getresuid", 6, 6},
|
|
{&__NR_getresgid, "getresgid", 6, 6},
|
|
{&__NR_sigaltstack, "sigaltstack", 6, 6},
|
|
{&__NR_mknod, "mknod", 6, 6},
|
|
{&__NR_mknodat, "mknodat", 6, 6},
|
|
{&__NR_mkfifo, "mkfifo", 6, 6},
|
|
{&__NR_mkfifoat, "mkfifoat", 6, 6},
|
|
{&__NR_statfs, "statfs", 6, 6},
|
|
{&__NR_fstatfs, "fstatfs", 6, 6},
|
|
{&__NR_getpriority, "getpriority", 6, 6},
|
|
{&__NR_setpriority, "setpriority", 6, 6},
|
|
{&__NR_mlock, "mlock", 6, 6},
|
|
{&__NR_munlock, "munlock", 6, 6},
|
|
{&__NR_mlockall, "mlockall", 6, 6},
|
|
{&__NR_munlockall, "munlockall", 6, 6},
|
|
{&__NR_setrlimit, "setrlimit", 6, 6},
|
|
{&__NR_chroot, "chroot", 6, 6},
|
|
{&__NR_sync, "sync", 6, 6},
|
|
{&__NR_acct, "acct", 6, 6},
|
|
{&__NR_settimeofday, "settimeofday", 6, 6},
|
|
{&__NR_mount, "mount", 6, 6},
|
|
{&__NR_reboot, "reboot", 6, 6},
|
|
{&__NR_quotactl, "quotactl", 6, 6},
|
|
{&__NR_setfsuid, "setfsuid", 6, 6},
|
|
{&__NR_setfsgid, "setfsgid", 6, 6},
|
|
{&__NR_capget, "capget", 6, 6},
|
|
{&__NR_capset, "capset", 6, 6},
|
|
{&__NR_sigtimedwait, "sigtimedwait", 6, 6},
|
|
{&__NR_personality, "personality", 6, 6},
|
|
{&__NR_ustat, "ustat", 6, 6},
|
|
{&__NR_sysfs, "sysfs", 6, 6},
|
|
{&__NR_sched_setparam, "sched_setparam", 6, 6},
|
|
{&__NR_sched_getparam, "sched_getparam", 6, 6},
|
|
{&__NR_sched_setscheduler, "sched_setscheduler", 6, 6},
|
|
{&__NR_sched_getscheduler, "sched_getscheduler", 6, 6},
|
|
{&__NR_sched_get_priority_max, "sched_get_priority_max", 6, 6},
|
|
{&__NR_sched_get_priority_min, "sched_get_priority_min", 6, 6},
|
|
{&__NR_sched_rr_get_interval, "sched_rr_get_interval", 6, 6},
|
|
{&__NR_vhangup, "vhangup", 6, 6},
|
|
{&__NR_modify_ldt, "modify_ldt", 6, 6},
|
|
{&__NR_pivot_root, "pivot_root", 6, 6},
|
|
{&__NR__sysctl, "_sysctl", 6, 6},
|
|
{&__NR_prctl, "prctl", 6, 6},
|
|
{&__NR_arch_prctl, "arch_prctl", 2, 2, INT, {INT, ULONG}},
|
|
{&__NR_adjtimex, "adjtimex", 6, 6},
|
|
{&__NR_umount2, "umount2", 6, 6},
|
|
{&__NR_swapon, "swapon", 6, 6},
|
|
{&__NR_swapoff, "swapoff", 6, 6},
|
|
{&__NR_sethostname, "sethostname", 6, 6},
|
|
{&__NR_setdomainname, "setdomainname", 6, 6},
|
|
{&__NR_iopl, "iopl", 6, 6},
|
|
{&__NR_ioperm, "ioperm", 6, 6},
|
|
{&__NR_init_module, "init_module", 6, 6},
|
|
{&__NR_delete_module, "delete_module", 6, 6},
|
|
{&__NR_gettid, "gettid", 6, 6},
|
|
{&__NR_readahead, "readahead", 6, 6},
|
|
{&__NR_setxattr, "setxattr", 6, 6},
|
|
{&__NR_fsetxattr, "fsetxattr", 6, 6},
|
|
{&__NR_getxattr, "getxattr", 6, 6},
|
|
{&__NR_fgetxattr, "fgetxattr", 6, 6},
|
|
{&__NR_listxattr, "listxattr", 6, 6},
|
|
{&__NR_flistxattr, "flistxattr", 6, 6},
|
|
{&__NR_removexattr, "removexattr", 6, 6},
|
|
{&__NR_fremovexattr, "fremovexattr", 6, 6},
|
|
{&__NR_lsetxattr, "lsetxattr", 6, 6},
|
|
{&__NR_lgetxattr, "lgetxattr", 6, 6},
|
|
{&__NR_llistxattr, "llistxattr", 6, 6},
|
|
{&__NR_lremovexattr, "lremovexattr", 6, 6},
|
|
{&__NR_sched_setaffinity, "sched_setaffinity", 6, 6},
|
|
{&__NR_sched_getaffinity, "sched_getaffinity", 6, 6},
|
|
{&__NR_cpuset_getaffinity, "cpuset_getaffinity", 6, 6},
|
|
{&__NR_cpuset_setaffinity, "cpuset_setaffinity", 6, 6},
|
|
{&__NR_io_setup, "io_setup", 6, 6},
|
|
{&__NR_io_destroy, "io_destroy", 6, 6},
|
|
{&__NR_io_getevents, "io_getevents", 6, 6},
|
|
{&__NR_io_submit, "io_submit", 6, 6},
|
|
{&__NR_io_cancel, "io_cancel", 6, 6},
|
|
{&__NR_lookup_dcookie, "lookup_dcookie", 6, 6},
|
|
{&__NR_epoll_create, "epoll_create", 6, 6},
|
|
{&__NR_epoll_wait, "epoll_wait", 6, 6},
|
|
{&__NR_epoll_ctl, "epoll_ctl", 6, 6},
|
|
{&__NR_getdents, "getdents64", 6, 6},
|
|
{&__NR_set_tid_address, "set_tid_address", 1, 1},
|
|
{&__NR_restart_syscall, "restart_syscall", 6, 6},
|
|
{&__NR_semtimedop, "semtimedop", 6, 6},
|
|
{&__NR_fadvise, "fadvise", 6, 6},
|
|
{&__NR_timer_create, "timer_create", 6, 6},
|
|
{&__NR_timer_settime, "timer_settime", 6, 6},
|
|
{&__NR_timer_gettime, "timer_gettime", 6, 6},
|
|
{&__NR_timer_getoverrun, "timer_getoverrun", 6, 6},
|
|
{&__NR_timer_delete, "timer_delete", 6, 6},
|
|
{&__NR_clock_settime, "clock_settime", 6, 6},
|
|
{&__NR_clock_gettime, "clock_gettime", 6, 6},
|
|
{&__NR_clock_getres, "clock_getres", 6, 6},
|
|
{&__NR_clock_nanosleep, "clock_nanosleep", 6, 6},
|
|
{&__NR_tgkill, "tgkill", 6, 6},
|
|
{&__NR_mbind, "mbind", 6, 6},
|
|
{&__NR_set_mempolicy, "set_mempolicy", 6, 6},
|
|
{&__NR_get_mempolicy, "get_mempolicy", 6, 6},
|
|
{&__NR_mq_open, "mq_open", 6, 6},
|
|
{&__NR_mq_unlink, "mq_unlink", 6, 6},
|
|
{&__NR_mq_timedsend, "mq_timedsend", 6, 6},
|
|
{&__NR_mq_timedreceive, "mq_timedreceive", 6, 6},
|
|
{&__NR_mq_notify, "mq_notify", 6, 6},
|
|
{&__NR_mq_getsetattr, "mq_getsetattr", 6, 6},
|
|
{&__NR_kexec_load, "kexec_load", 6, 6},
|
|
{&__NR_waitid, "waitid", 6, 6},
|
|
{&__NR_add_key, "add_key", 6, 6},
|
|
{&__NR_request_key, "request_key", 6, 6},
|
|
{&__NR_keyctl, "keyctl", 6, 6},
|
|
{&__NR_ioprio_set, "ioprio_set", 6, 6},
|
|
{&__NR_ioprio_get, "ioprio_get", 6, 6},
|
|
{&__NR_inotify_init, "inotify_init", 6, 6},
|
|
{&__NR_inotify_add_watch, "inotify_add_watch", 6, 6},
|
|
{&__NR_inotify_rm_watch, "inotify_rm_watch", 6, 6},
|
|
{&__NR_openat, "openat", 4, 4, INT, {INT, STR, INT, OCTAL|INT}},
|
|
{&__NR_mkdirat, "mkdirat", 3, 3, INT, {INT, STR, OCTAL|INT}},
|
|
{&__NR_fchownat, "fchownat", 6, 6},
|
|
{&__NR_utime, "utime", 6, 6},
|
|
{&__NR_utimes, "utimes", 6, 6},
|
|
{&__NR_futimesat, "futimesat", 6, 6},
|
|
{&__NR_futimes, "futimes", 6, 6},
|
|
{&__NR_futimens, "futimens", 6, 6},
|
|
{&__NR_fstatat, "newfstatat", 4, 2, INT, {INT, STR, STAT, INT}},
|
|
{&__NR_unlinkat, "unlinkat", 3, 3, INT, {INT, STR, INT}},
|
|
{&__NR_renameat, "renameat", 4, 4, INT, {INT, STR, INT, STR}},
|
|
{&__NR_linkat, "linkat", 6, 6},
|
|
{&__NR_symlinkat, "symlinkat", 6, 6},
|
|
{&__NR_readlinkat, "readlinkat", 6, 6},
|
|
{&__NR_fchmodat, "fchmodat", 6, 6},
|
|
{&__NR_faccessat, "faccessat", 4, 4, INT, {INT, STR, INT, INT}},
|
|
{&__NR_unshare, "unshare", 6, 6},
|
|
{&__NR_splice, "splice", 6, 6},
|
|
{&__NR_tee, "tee", 6, 6},
|
|
{&__NR_sync_file_range, "sync_file_range", 4, 4},
|
|
{&__NR_vmsplice, "vmsplice", 6, 6},
|
|
{&__NR_migrate_pages, "migrate_pages", 6, 6},
|
|
{&__NR_move_pages, "move_pages", 6, 6},
|
|
{&__NR_preadv, "preadv", 4, 1, LONG, {INT, IOV, ULONG, ULONG}},
|
|
{&__NR_pwritev, "pwritev", 6, 6, LONG, {INT, IOV, ULONG, ULONG}},
|
|
{&__NR_utimensat, "utimensat", 6, 6},
|
|
{&__NR_fallocate, "fallocate", 6, 6},
|
|
{&__NR_posix_fallocate, "posix_fallocate", 6, 6},
|
|
{&__NR_accept4, "accept4", 4, 4},
|
|
{&__NR_dup3, "dup3", 3, 3, INT},
|
|
{&__NR_epoll_pwait, "epoll_pwait", 6, 6},
|
|
{&__NR_epoll_create1, "epoll_create1", 6, 6},
|
|
{&__NR_perf_event_open, "perf_event_open", 6, 6},
|
|
{&__NR_inotify_init1, "inotify_init1", 6, 6},
|
|
{&__NR_rt_tgsigqueueinfo, "rt_tgsigqueueinfo", 6, 6},
|
|
{&__NR_signalfd, "signalfd", 6, 6},
|
|
{&__NR_signalfd4, "signalfd4", 6, 6},
|
|
{&__NR_eventfd, "eventfd", 6, 6},
|
|
{&__NR_eventfd2, "eventfd2", 6, 6},
|
|
{&__NR_timerfd_create, "timerfd_create", 6, 6},
|
|
{&__NR_timerfd_settime, "timerfd_settime", 6, 6},
|
|
{&__NR_timerfd_gettime, "timerfd_gettime", 6, 6},
|
|
{&__NR_recvmmsg, "recvmmsg", 6, 6},
|
|
{&__NR_fanotify_init, "fanotify_init", 6, 6},
|
|
{&__NR_fanotify_mark, "fanotify_mark", 6, 6},
|
|
{&__NR_prlimit, "prlimit", 6, 6},
|
|
{&__NR_name_to_handle_at, "name_to_handle_at", 6, 6},
|
|
{&__NR_open_by_handle_at, "open_by_handle_at", 6, 6},
|
|
{&__NR_clock_adjtime, "clock_adjtime", 6, 6},
|
|
{&__NR_syncfs, "syncfs", 6, 6},
|
|
{&__NR_sendmmsg, "sendmmsg", 6, 6},
|
|
{&__NR_setns, "setns", 6, 6},
|
|
{&__NR_getcpu, "getcpu", 6, 6},
|
|
{&__NR_process_vm_readv, "process_vm_readv", 6, 6},
|
|
{&__NR_process_vm_writev, "process_vm_writev", 6, 6},
|
|
{&__NR_kcmp, "kcmp", 6, 6},
|
|
{&__NR_finit_module, "finit_module", 6, 6},
|
|
{&__NR_sched_setattr, "sched_setattr", 6, 6},
|
|
{&__NR_sched_getattr, "sched_getattr", 6, 6},
|
|
{&__NR_renameat2, "renameat2", 6, 6},
|
|
{&__NR_seccomp, "seccomp", 6, 6},
|
|
{&__NR_getrandom, "getrandom", 6, 6},
|
|
{&__NR_memfd_create, "memfd_create", 6, 6},
|
|
{&__NR_kexec_file_load, "kexec_file_load", 6, 6},
|
|
{&__NR_bpf, "bpf", 6, 6},
|
|
{&__NR_execveat, "execveat", 6, 6},
|
|
{&__NR_userfaultfd, "userfaultfd", 6, 6},
|
|
{&__NR_membarrier, "membarrier", 6, 6},
|
|
{&__NR_mlock2, "mlock2", 6, 6},
|
|
{&__NR_copy_file_range, "copy_file_range", 6, 6},
|
|
{&__NR_preadv2, "preadv2", 6, 6},
|
|
{&__NR_pwritev2, "pwritev2", 6, 6},
|
|
{&__NR_pkey_mprotect, "pkey_mprotect", 6, 6},
|
|
{&__NR_pkey_alloc, "pkey_alloc", 6, 6},
|
|
{&__NR_pkey_free, "pkey_free", 6, 6},
|
|
{&__NR_statx, "statx", 6, 6},
|
|
{&__NR_io_pgetevents, "io_pgetevents", 6, 6},
|
|
{&__NR_rseq, "rseq", 6, 6},
|
|
{&__NR_pidfd_send_signal, "pidfd_send_signal", 6, 6},
|
|
{&__NR_io_uring_setup, "io_uring_setup", 6, 6},
|
|
{&__NR_io_uring_enter, "io_uring_enter", 6, 6},
|
|
{&__NR_io_uring_register, "io_uring_register", 6, 6},
|
|
// clang-format on
|
|
};
|
|
|
|
static const struct Errno {
|
|
errno_t *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 {
|
|
int pid;
|
|
uint32_t hash;
|
|
bool insyscall;
|
|
struct Syscall *call;
|
|
struct Syscall fakecall;
|
|
struct user_regs_struct args;
|
|
char fakename[12];
|
|
};
|
|
|
|
struct PidList {
|
|
uint32_t i, n;
|
|
struct Pid *p;
|
|
};
|
|
|
|
char *ob; // output buffer
|
|
struct Pid *sp; // active subprocess
|
|
|
|
static uint32_t Hash(uint64_t pid) {
|
|
uint32_t hash;
|
|
hash = (pid * 6364136223846793005 + 1442695040888963407) >> 32;
|
|
if (!hash) hash = 1;
|
|
return hash;
|
|
}
|
|
|
|
static struct Pid *GetPid(struct PidList *list, int pid) {
|
|
uint32_t i, hash, step;
|
|
DCHECK_NE(0, pid);
|
|
DCHECK_LE(list->i, list->n >> 1);
|
|
if (list->n) {
|
|
i = 0;
|
|
step = 0;
|
|
hash = Hash(pid);
|
|
do {
|
|
i = (hash + step * (step + 1) / 2) & (list->n - 1);
|
|
if (list->p[i].pid == pid) {
|
|
return list->p + i;
|
|
}
|
|
step++;
|
|
} while (list->p[i].hash);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void ReservePid(struct PidList *list, int count) {
|
|
size_t i, j, step;
|
|
struct PidList old;
|
|
DCHECK_LE(list->i, list->n >> 1);
|
|
while (list->i + count >= (list->n >> 1)) {
|
|
memcpy(&old, list, sizeof(*list));
|
|
list->n = list->n ? list->n << 1 : 16;
|
|
list->p = calloc(list->n, sizeof(*list->p));
|
|
for (i = 0; i < old.n; ++i) {
|
|
if (!old.p[i].hash) continue;
|
|
step = 0;
|
|
do {
|
|
j = (old.p[i].hash + step * (step + 1) / 2) & (list->n - 1);
|
|
step++;
|
|
} while (list->p[j].hash);
|
|
list->p[j] = old.p[i];
|
|
}
|
|
free(old.p);
|
|
}
|
|
}
|
|
|
|
static struct Pid *AddPid(struct PidList *list, int pid) {
|
|
struct Pid *item;
|
|
uint32_t i, hash, step;
|
|
DCHECK_NE(0, pid);
|
|
DCHECK_LE(list->i, list->n >> 1);
|
|
if (!(item = GetPid(list, pid))) {
|
|
ReservePid(list, 1);
|
|
hash = Hash(pid);
|
|
++list->i;
|
|
i = 0;
|
|
step = 0;
|
|
do {
|
|
i = (hash + step * (step + 1) / 2) & (list->n - 1);
|
|
step++;
|
|
} while (list->p[i].hash);
|
|
list->p[i].hash = hash;
|
|
list->p[i].pid = pid;
|
|
item = list->p + i;
|
|
}
|
|
return item;
|
|
}
|
|
|
|
static void RemovePid(struct PidList *list, int pid) {
|
|
uint32_t i, hash, step;
|
|
DCHECK_NE(0, pid);
|
|
DCHECK_LE(list->i, list->n >> 1);
|
|
if (list->n) {
|
|
i = 0;
|
|
step = 0;
|
|
hash = Hash(pid);
|
|
do {
|
|
i = (hash + step * (step + 1) / 2) & (list->n - 1);
|
|
if (list->p[i].pid == pid) {
|
|
bzero(list->p + i, sizeof(*list->p));
|
|
list->i--;
|
|
assert(list->i < 99999);
|
|
return;
|
|
}
|
|
step++;
|
|
} while (list->p[i].hash);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
const char *s;
|
|
static char buf[16];
|
|
if ((s = strerrno(x))) return s;
|
|
FormatInt64(buf, x);
|
|
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;
|
|
errno = 0;
|
|
u.word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr);
|
|
if (errno) return 0;
|
|
n = strnlen(u.buf + offset, 8 - offset);
|
|
p = calloc(1, n);
|
|
memcpy(p, u.buf + offset, n);
|
|
if (n == 8 - offset) {
|
|
do {
|
|
addr += 8;
|
|
errno = 0;
|
|
u.word = ptrace(PTRACE_PEEKDATA, sp->pid, addr);
|
|
if (errno) break;
|
|
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;
|
|
errno = 0;
|
|
u.word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr);
|
|
if (errno) return 0;
|
|
p = calloc(1, size);
|
|
memcpy(p, u.buf + offset, MIN(size, 8 - offset));
|
|
for (i = 8 - offset; i < size; i += MIN(8, size - i)) {
|
|
addr += 8;
|
|
errno = 0;
|
|
u.word = ptrace(PTRACE_PEEKDATA, sp->pid, addr);
|
|
if (errno) break;
|
|
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 = calloc(1, len * 16);
|
|
for (i = 0; i < len * 2; ++i, addr += sizeof(word)) {
|
|
errno = 0;
|
|
word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr);
|
|
if (errno) break;
|
|
memcpy(p + i * sizeof(word), &word, sizeof(word));
|
|
}
|
|
iov = (struct iovec *)p;
|
|
for (i = 0; i < len; ++i) {
|
|
errno = 0;
|
|
iov[i].iov_base = PeekData((long)iov[i].iov_base, iov[i].iov_len);
|
|
if (errno) break;
|
|
}
|
|
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 void PrintPipe(unsigned long addr) {
|
|
unsigned long word;
|
|
word = ptrace(PTRACE_PEEKDATA, sp->pid, addr);
|
|
kappendf(&ob, "[{%d, %d}]", (int)word, (int)(word >> 32));
|
|
}
|
|
|
|
static struct stat *PeekStat(unsigned long addr) {
|
|
int i;
|
|
char *p;
|
|
long word;
|
|
if (!addr) return NULL;
|
|
p = calloc(1, sizeof(struct stat));
|
|
for (i = 0; i < sizeof(struct stat); i += sizeof(word)) {
|
|
errno = 0;
|
|
word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr + i);
|
|
if (errno) break;
|
|
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, MIN(32, y)), MIN(32, y)));
|
|
break;
|
|
case IOV:
|
|
FreeIov(PrintIov(PeekIov(x, y), y), y);
|
|
break;
|
|
case STAT:
|
|
free(PrintStat(PeekStat(x)));
|
|
break;
|
|
case PIPE:
|
|
PrintPipe(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(bool issecond) {
|
|
int a, b, eager, arity;
|
|
bool gotsome, isresuming, isunfinished;
|
|
gotsome = false;
|
|
if (issecond) {
|
|
if (!sp->call) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!(sp->call = GetSyscall(sp->args.orig_rax))) {
|
|
ksnprintf(sp->fakename, sizeof(sp->fakename), "%d", sp->args.orig_rax);
|
|
bzero(&sp->fakecall, sizeof(sp->fakecall));
|
|
sp->call = &sp->fakecall;
|
|
sp->call->name = sp->fakename;
|
|
sp->call->arity = 6;
|
|
sp->call->eager = 6;
|
|
}
|
|
if (sp->args.orig_rax == __NR_execve) {
|
|
if (sp->args.rax == -ENOSYS) {
|
|
sp->args.rax = 0;
|
|
}
|
|
} else if (sp->args.rax == -ENOSYS) {
|
|
return;
|
|
}
|
|
}
|
|
isresuming = false;
|
|
isunfinished = false;
|
|
if (!isunfinished) {
|
|
a = 0;
|
|
b = sp->call->arity;
|
|
} else if (!isresuming) {
|
|
a = 0;
|
|
b = sp->call->eager;
|
|
} else {
|
|
a = sp->call->eager;
|
|
b = sp->call->arity;
|
|
}
|
|
kappendf(&ob, PROLOGUE " %s%s(", sp->pid, isresuming ? "<resuming> " : "",
|
|
sp->call->name);
|
|
if (a <= 0 && 0 < b) {
|
|
if (gotsome) appendw(&ob, READ16LE(", "));
|
|
PrintSyscallArg(sp->call->arg[0], sp->args.rdi, sp->args.rsi);
|
|
gotsome = true;
|
|
}
|
|
if (a <= 1 && 1 < b) {
|
|
if (gotsome) appendw(&ob, READ16LE(", "));
|
|
PrintSyscallArg(sp->call->arg[1], sp->args.rsi, sp->args.rdx);
|
|
gotsome = true;
|
|
}
|
|
if (a <= 2 && 2 < b) {
|
|
if (gotsome) appendw(&ob, READ16LE(", "));
|
|
PrintSyscallArg(sp->call->arg[2], sp->args.rdx, sp->args.r10);
|
|
gotsome = true;
|
|
}
|
|
if (a <= 3 && 3 < b) {
|
|
if (gotsome) appendw(&ob, READ16LE(", "));
|
|
PrintSyscallArg(sp->call->arg[3], sp->args.r10, sp->args.r8);
|
|
gotsome = true;
|
|
}
|
|
if (a <= 4 && 4 < b) {
|
|
if (gotsome) appendw(&ob, READ16LE(", "));
|
|
PrintSyscallArg(sp->call->arg[4], sp->args.r8, sp->args.r9);
|
|
gotsome = true;
|
|
}
|
|
if (a <= 5 && 5 < b) {
|
|
if (gotsome) appendw(&ob, READ16LE(", "));
|
|
PrintSyscallArg(sp->call->arg[5], sp->args.r9, 0);
|
|
gotsome = true;
|
|
}
|
|
if (isunfinished) {
|
|
appends(&ob, ") → <unfinished>");
|
|
} else {
|
|
appends(&ob, ") → ");
|
|
if (sp->args.rax > (unsigned long)-4096) {
|
|
kappendf(&ob, "-1 %s", GetErrnoName(-sp->args.rax));
|
|
} else {
|
|
PrintSyscallArg(sp->call->ret, sp->args.rax, 0);
|
|
}
|
|
}
|
|
kappendf(&ob, "%n");
|
|
Flush();
|
|
sp->call = 0;
|
|
}
|
|
|
|
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[]) {
|
|
unsigned long msg;
|
|
struct siginfo si;
|
|
struct Pid *s, *child;
|
|
struct PidList pidlist;
|
|
sigset_t mask, origmask;
|
|
int i, sig, evpid, root, wstatus, signal;
|
|
struct sigaction sigign, saveint, savequit;
|
|
|
|
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.i = 0;
|
|
pidlist.n = 0;
|
|
pidlist.p = 0;
|
|
|
|
sigign.sa_flags = 0;
|
|
sigign.sa_handler = SIG_IGN;
|
|
sigemptyset(&sigign.sa_mask);
|
|
|
|
sigaction(SIGINT, &sigign, &saveint);
|
|
sigaction(SIGQUIT, &sigign, &savequit);
|
|
|
|
sigemptyset(&mask);
|
|
/* sigaddset(&mask, SIGCHLD); */
|
|
sigprocmask(SIG_BLOCK, &mask, &origmask);
|
|
|
|
CHECK_NE(-1, (root = fork()));
|
|
if (!root) {
|
|
sigaction(SIGINT, &saveint, 0);
|
|
sigaction(SIGQUIT, &savequit, 0);
|
|
sigprocmask(SIG_SETMASK, &origmask, 0);
|
|
ptrace(PTRACE_TRACEME);
|
|
execvp(argv[1], argv + 1);
|
|
_Exit(127);
|
|
}
|
|
sp = AddPid(&pidlist, root);
|
|
|
|
// 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 | PTRACE_O_TRACESYSGOOD)));
|
|
|
|
// continue child process setting breakpoint at next system call
|
|
CHECK_NE(-1, ptrace(PTRACE_SYSCALL, sp->pid, 0, 0));
|
|
|
|
for (;;) {
|
|
CHECK_NE(-1, (evpid = waitpid(-1, &wstatus, __WALL)));
|
|
|
|
// prevent rehash in second addpid call
|
|
ReservePid(&pidlist, 2);
|
|
|
|
// we can get wait notifications before fork/vfork/etc. events
|
|
sp = AddPid(&pidlist, evpid);
|
|
|
|
// handle actual exit
|
|
if (WIFEXITED(wstatus)) {
|
|
kprintf(PROLOGUE " exited with %d%n", sp->pid, WEXITSTATUS(wstatus));
|
|
RemovePid(&pidlist, sp->pid);
|
|
sp = 0;
|
|
// we exit when the last process being monitored exits
|
|
if (!pidlist.i) {
|
|
PropagateExit(wstatus);
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// handle actual kill
|
|
if (WIFSIGNALED(wstatus)) {
|
|
kprintf(PROLOGUE " exited with signal %G%n", sp->pid, WTERMSIG(wstatus));
|
|
RemovePid(&pidlist, sp->pid);
|
|
sp = 0;
|
|
// we die when the last process being monitored dies
|
|
if (!pidlist.i) {
|
|
PropagateTermination(wstatus);
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// handle core dump
|
|
if (WCOREDUMP(wstatus)) {
|
|
kprintf(PROLOGUE " exited with core%n", sp->pid);
|
|
RemovePid(&pidlist, sp->pid);
|
|
sp = 0;
|
|
// we die when the last process being monitored dies
|
|
if (!pidlist.i) {
|
|
PropagateTermination(wstatus);
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// handle trace events
|
|
sig = 0;
|
|
signal = (wstatus >> 8) & 0xffff;
|
|
assert(WIFSTOPPED(wstatus));
|
|
if (signal == SIGTRAP | PTRACE_EVENT_STOP) {
|
|
CHECK_NE(-1, ptrace(PTRACE_GETSIGINFO, sp->pid, 0, &si));
|
|
if (si.si_code == SIGTRAP || si.si_code == SIGTRAP | 0x80) {
|
|
CHECK_NE(-1, ptrace(PTRACE_GETREGS, sp->pid, 0, &sp->args));
|
|
PrintSyscall(sp->insyscall);
|
|
sp->insyscall = !sp->insyscall;
|
|
ptrace(PTRACE_SYSCALL, sp->pid, 0, 0);
|
|
} else {
|
|
sig = signal & 127;
|
|
kappendf(&ob, PROLOGUE " got signal %G%n", sp->pid, sig);
|
|
Flush();
|
|
ptrace(PTRACE_SYSCALL, sp->pid, 0, sig);
|
|
}
|
|
} else if (signal == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) {
|
|
CHECK_NE(-1, ptrace(PTRACE_GETEVENTMSG, sp->pid, 0, &msg));
|
|
sig = WSTOPSIG(wstatus);
|
|
ptrace(PTRACE_SYSCALL, sp->pid, 0, 0);
|
|
} else if (signal == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) {
|
|
CHECK_NE(-1, ptrace(PTRACE_GETEVENTMSG, sp->pid, 0, &msg));
|
|
ptrace(PTRACE_SYSCALL, sp->pid, 0, 0);
|
|
} else if (signal == (SIGTRAP | (PTRACE_EVENT_FORK << 8)) ||
|
|
signal == (SIGTRAP | (PTRACE_EVENT_VFORK << 8)) ||
|
|
signal == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {
|
|
CHECK_NE(-1, ptrace(PTRACE_GETEVENTMSG, evpid, 0, &msg));
|
|
child = AddPid(&pidlist, msg);
|
|
child->pid = msg;
|
|
if (signal == (SIGTRAP | (PTRACE_EVENT_FORK << 8))) {
|
|
kappendf(&ob, PROLOGUE " fork() → %d%n", sp->pid, child->pid);
|
|
} else if (signal == (SIGTRAP | (PTRACE_EVENT_VFORK << 8))) {
|
|
kappendf(&ob, PROLOGUE " vfork() → %d%n", sp->pid, child->pid);
|
|
} else {
|
|
kappendf(&ob, PROLOGUE " clone() → %d%n", sp->pid, child->pid);
|
|
}
|
|
Flush();
|
|
ptrace(PTRACE_SYSCALL, child->pid, 0, 0);
|
|
ptrace(PTRACE_SYSCALL, sp->pid, 0, 0);
|
|
} else {
|
|
kappendf(&ob, PROLOGUE " gottish signal %G%n", sp->pid, sig);
|
|
Flush();
|
|
ptrace(PTRACE_SYSCALL, sp->pid, 0, signal & 127);
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
StraceMain(argc, argv);
|
|
}
|