1 /* 2 * Copyright 1997 Sean Eric Fagan 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. All advertising materials mentioning features or use of this software 13 * must display the following acknowledgement: 14 * This product includes software developed by Sean Eric Fagan 15 * 4. Neither the name of the author may be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 /* 36 * This file has routines used to print out system calls and their 37 * arguments. 38 */ 39 40 #include <sys/types.h> 41 #include <sys/event.h> 42 #include <sys/ioccom.h> 43 #include <sys/mount.h> 44 #include <sys/ptrace.h> 45 #include <sys/resource.h> 46 #include <sys/socket.h> 47 #include <sys/stat.h> 48 #include <sys/un.h> 49 #include <sys/wait.h> 50 #include <machine/sysarch.h> 51 #include <netinet/in.h> 52 #include <arpa/inet.h> 53 54 #include <assert.h> 55 #include <ctype.h> 56 #include <err.h> 57 #include <fcntl.h> 58 #include <poll.h> 59 #include <signal.h> 60 #include <stdbool.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <sysdecode.h> 65 #include <unistd.h> 66 #include <vis.h> 67 68 #include <contrib/cloudabi/cloudabi_types_common.h> 69 70 #include "truss.h" 71 #include "extern.h" 72 #include "syscall.h" 73 74 /* 75 * This should probably be in its own file, sorted alphabetically. 76 */ 77 static struct syscall decoded_syscalls[] = { 78 /* Native ABI */ 79 { .name = "__getcwd", .ret_type = 1, .nargs = 2, 80 .args = { { Name | OUT, 0 }, { Int, 1 } } }, 81 { .name = "_umtx_op", .ret_type = 1, .nargs = 5, 82 .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 }, 83 { Ptr, 4 } } }, 84 { .name = "accept", .ret_type = 1, .nargs = 3, 85 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 86 { .name = "access", .ret_type = 1, .nargs = 2, 87 .args = { { Name | IN, 0 }, { Accessmode, 1 } } }, 88 { .name = "bind", .ret_type = 1, .nargs = 3, 89 .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 90 { .name = "bindat", .ret_type = 1, .nargs = 4, 91 .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 }, 92 { Int, 3 } } }, 93 { .name = "break", .ret_type = 1, .nargs = 1, 94 .args = { { Ptr, 0 } } }, 95 { .name = "cap_fcntls_get", .ret_type = 1, .nargs = 2, 96 .args = { { Int, 0 }, { CapFcntlRights | OUT, 1 } } }, 97 { .name = "cap_fcntls_limit", .ret_type = 1, .nargs = 2, 98 .args = { { Int, 0 }, { CapFcntlRights, 1 } } }, 99 { .name = "chdir", .ret_type = 1, .nargs = 1, 100 .args = { { Name, 0 } } }, 101 { .name = "chflags", .ret_type = 1, .nargs = 2, 102 .args = { { Name | IN, 0 }, { FileFlags, 1 } } }, 103 { .name = "chflagsat", .ret_type = 1, .nargs = 4, 104 .args = { { Atfd, 0 }, { Name | IN, 1 }, { FileFlags, 2 }, 105 { Atflags, 3 } } }, 106 { .name = "chmod", .ret_type = 1, .nargs = 2, 107 .args = { { Name, 0 }, { Octal, 1 } } }, 108 { .name = "chown", .ret_type = 1, .nargs = 3, 109 .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, 110 { .name = "chroot", .ret_type = 1, .nargs = 1, 111 .args = { { Name, 0 } } }, 112 { .name = "clock_gettime", .ret_type = 1, .nargs = 2, 113 .args = { { Int, 0 }, { Timespec | OUT, 1 } } }, 114 { .name = "close", .ret_type = 1, .nargs = 1, 115 .args = { { Int, 0 } } }, 116 { .name = "connect", .ret_type = 1, .nargs = 3, 117 .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 118 { .name = "connectat", .ret_type = 1, .nargs = 4, 119 .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 }, 120 { Int, 3 } } }, 121 { .name = "eaccess", .ret_type = 1, .nargs = 2, 122 .args = { { Name | IN, 0 }, { Accessmode, 1 } } }, 123 { .name = "execve", .ret_type = 1, .nargs = 3, 124 .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 }, 125 { ExecEnv | IN, 2 } } }, 126 { .name = "exit", .ret_type = 0, .nargs = 1, 127 .args = { { Hex, 0 } } }, 128 { .name = "faccessat", .ret_type = 1, .nargs = 4, 129 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Accessmode, 2 }, 130 { Atflags, 3 } } }, 131 { .name = "fchflags", .ret_type = 1, .nargs = 2, 132 .args = { { Int, 0 }, { FileFlags, 1 } } }, 133 { .name = "fchmod", .ret_type = 1, .nargs = 2, 134 .args = { { Int, 0 }, { Octal, 1 } } }, 135 { .name = "fchmodat", .ret_type = 1, .nargs = 4, 136 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } }, 137 { .name = "fchown", .ret_type = 1, .nargs = 3, 138 .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } }, 139 { .name = "fchownat", .ret_type = 1, .nargs = 5, 140 .args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 }, 141 { Atflags, 4 } } }, 142 { .name = "fcntl", .ret_type = 1, .nargs = 3, 143 .args = { { Int, 0 }, { Fcntl, 1 }, { Fcntlflag, 2 } } }, 144 { .name = "fstat", .ret_type = 1, .nargs = 2, 145 .args = { { Int, 0 }, { Stat | OUT, 1 } } }, 146 { .name = "fstatat", .ret_type = 1, .nargs = 4, 147 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Stat | OUT, 2 }, 148 { Atflags, 3 } } }, 149 { .name = "fstatfs", .ret_type = 1, .nargs = 2, 150 .args = { { Int, 0 }, { StatFs | OUT, 1 } } }, 151 { .name = "ftruncate", .ret_type = 1, .nargs = 2, 152 .args = { { Int | IN, 0 }, { QuadHex | IN, 1 } } }, 153 { .name = "futimens", .ret_type = 1, .nargs = 2, 154 .args = { { Int, 0 }, { Timespec2 | IN, 1 } } }, 155 { .name = "futimes", .ret_type = 1, .nargs = 2, 156 .args = { { Int, 0 }, { Timeval2 | IN, 1 } } }, 157 { .name = "futimesat", .ret_type = 1, .nargs = 3, 158 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timeval2 | IN, 2 } } }, 159 { .name = "getitimer", .ret_type = 1, .nargs = 2, 160 .args = { { Int, 0 }, { Itimerval | OUT, 2 } } }, 161 { .name = "getpeername", .ret_type = 1, .nargs = 3, 162 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 163 { .name = "getpgid", .ret_type = 1, .nargs = 1, 164 .args = { { Int, 0 } } }, 165 { .name = "getrlimit", .ret_type = 1, .nargs = 2, 166 .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } }, 167 { .name = "getrusage", .ret_type = 1, .nargs = 2, 168 .args = { { Int, 0 }, { Rusage | OUT, 1 } } }, 169 { .name = "getsid", .ret_type = 1, .nargs = 1, 170 .args = { { Int, 0 } } }, 171 { .name = "getsockname", .ret_type = 1, .nargs = 3, 172 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 173 { .name = "gettimeofday", .ret_type = 1, .nargs = 2, 174 .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } }, 175 { .name = "ioctl", .ret_type = 1, .nargs = 3, 176 .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } }, 177 { .name = "kevent", .ret_type = 1, .nargs = 6, 178 .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, 179 { Int, 4 }, { Timespec, 5 } } }, 180 { .name = "kill", .ret_type = 1, .nargs = 2, 181 .args = { { Int | IN, 0 }, { Signal | IN, 1 } } }, 182 { .name = "kldfind", .ret_type = 1, .nargs = 1, 183 .args = { { Name | IN, 0 } } }, 184 { .name = "kldfirstmod", .ret_type = 1, .nargs = 1, 185 .args = { { Int, 0 } } }, 186 { .name = "kldload", .ret_type = 1, .nargs = 1, 187 .args = { { Name | IN, 0 } } }, 188 { .name = "kldnext", .ret_type = 1, .nargs = 1, 189 .args = { { Int, 0 } } }, 190 { .name = "kldstat", .ret_type = 1, .nargs = 2, 191 .args = { { Int, 0 }, { Ptr, 1 } } }, 192 { .name = "kldunload", .ret_type = 1, .nargs = 1, 193 .args = { { Int, 0 } } }, 194 { .name = "kse_release", .ret_type = 0, .nargs = 1, 195 .args = { { Timespec, 0 } } }, 196 { .name = "lchflags", .ret_type = 1, .nargs = 2, 197 .args = { { Name | IN, 0 }, { FileFlags, 1 } } }, 198 { .name = "lchmod", .ret_type = 1, .nargs = 2, 199 .args = { { Name, 0 }, { Octal, 1 } } }, 200 { .name = "lchown", .ret_type = 1, .nargs = 3, 201 .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, 202 { .name = "link", .ret_type = 1, .nargs = 2, 203 .args = { { Name, 0 }, { Name, 1 } } }, 204 { .name = "linkat", .ret_type = 1, .nargs = 5, 205 .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 }, 206 { Atflags, 4 } } }, 207 { .name = "lseek", .ret_type = 2, .nargs = 3, 208 .args = { { Int, 0 }, { QuadHex, 1 }, { Whence, 2 } } }, 209 { .name = "lstat", .ret_type = 1, .nargs = 2, 210 .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, 211 { .name = "lutimes", .ret_type = 1, .nargs = 2, 212 .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, 213 { .name = "mkdir", .ret_type = 1, .nargs = 2, 214 .args = { { Name, 0 }, { Octal, 1 } } }, 215 { .name = "mkdirat", .ret_type = 1, .nargs = 3, 216 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } }, 217 { .name = "mkfifo", .ret_type = 1, .nargs = 2, 218 .args = { { Name, 0 }, { Octal, 1 } } }, 219 { .name = "mkfifoat", .ret_type = 1, .nargs = 3, 220 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } }, 221 { .name = "mknod", .ret_type = 1, .nargs = 3, 222 .args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } }, 223 { .name = "mknodat", .ret_type = 1, .nargs = 4, 224 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } }, 225 { .name = "mmap", .ret_type = 1, .nargs = 6, 226 .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, 227 { Int, 4 }, { QuadHex, 5 } } }, 228 { .name = "modfind", .ret_type = 1, .nargs = 1, 229 .args = { { Name | IN, 0 } } }, 230 { .name = "mount", .ret_type = 1, .nargs = 4, 231 .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } }, 232 { .name = "mprotect", .ret_type = 1, .nargs = 3, 233 .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } }, 234 { .name = "munmap", .ret_type = 1, .nargs = 2, 235 .args = { { Ptr, 0 }, { Int, 1 } } }, 236 { .name = "nanosleep", .ret_type = 1, .nargs = 1, 237 .args = { { Timespec, 0 } } }, 238 { .name = "open", .ret_type = 1, .nargs = 3, 239 .args = { { Name | IN, 0 }, { Open, 1 }, { Octal, 2 } } }, 240 { .name = "openat", .ret_type = 1, .nargs = 4, 241 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Open, 2 }, 242 { Octal, 3 } } }, 243 { .name = "pathconf", .ret_type = 1, .nargs = 2, 244 .args = { { Name | IN, 0 }, { Pathconf, 1 } } }, 245 { .name = "pipe", .ret_type = 1, .nargs = 1, 246 .args = { { PipeFds | OUT, 0 } } }, 247 { .name = "pipe2", .ret_type = 1, .nargs = 2, 248 .args = { { Ptr, 0 }, { Pipe2, 1 } } }, 249 { .name = "poll", .ret_type = 1, .nargs = 3, 250 .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } }, 251 { .name = "posix_fadvise", .ret_type = 1, .nargs = 4, 252 .args = { { Int, 0 }, { QuadHex, 1 }, { QuadHex, 2 }, 253 { Fadvice, 3 } } }, 254 { .name = "posix_openpt", .ret_type = 1, .nargs = 1, 255 .args = { { Open, 0 } } }, 256 { .name = "procctl", .ret_type = 1, .nargs = 4, 257 .args = { { Idtype, 0 }, { Quad, 1 }, { Procctl, 2 }, { Ptr, 3 } } }, 258 { .name = "read", .ret_type = 1, .nargs = 3, 259 .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } }, 260 { .name = "readlink", .ret_type = 1, .nargs = 3, 261 .args = { { Name, 0 }, { Readlinkres | OUT, 1 }, { Int, 2 } } }, 262 { .name = "readlinkat", .ret_type = 1, .nargs = 4, 263 .args = { { Atfd, 0 }, { Name, 1 }, { Readlinkres | OUT, 2 }, 264 { Int, 3 } } }, 265 { .name = "recvfrom", .ret_type = 1, .nargs = 6, 266 .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, 267 { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } }, 268 { .name = "rename", .ret_type = 1, .nargs = 2, 269 .args = { { Name, 0 }, { Name, 1 } } }, 270 { .name = "renameat", .ret_type = 1, .nargs = 4, 271 .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 } } }, 272 { .name = "rfork", .ret_type = 1, .nargs = 1, 273 .args = { { Rforkflags, 0 } } }, 274 { .name = "rmdir", .ret_type = 1, .nargs = 1, 275 .args = { { Name, 0 } } }, 276 { .name = "select", .ret_type = 1, .nargs = 5, 277 .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, 278 { Timeval, 4 } } }, 279 { .name = "sendto", .ret_type = 1, .nargs = 6, 280 .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, 281 { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } }, 282 { .name = "setitimer", .ret_type = 1, .nargs = 3, 283 .args = { { Int, 0 }, { Itimerval, 1 }, { Itimerval | OUT, 2 } } }, 284 { .name = "setrlimit", .ret_type = 1, .nargs = 2, 285 .args = { { Resource, 0 }, { Rlimit | IN, 1 } } }, 286 { .name = "shutdown", .ret_type = 1, .nargs = 2, 287 .args = { { Int, 0 }, { Shutdown, 1 } } }, 288 { .name = "sigaction", .ret_type = 1, .nargs = 3, 289 .args = { { Signal, 0 }, { Sigaction | IN, 1 }, 290 { Sigaction | OUT, 2 } } }, 291 { .name = "sigpending", .ret_type = 1, .nargs = 1, 292 .args = { { Sigset | OUT, 0 } } }, 293 { .name = "sigprocmask", .ret_type = 1, .nargs = 3, 294 .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } }, 295 { .name = "sigqueue", .ret_type = 1, .nargs = 3, 296 .args = { { Int, 0 }, { Signal, 1 }, { LongHex, 2 } } }, 297 { .name = "sigreturn", .ret_type = 1, .nargs = 1, 298 .args = { { Ptr, 0 } } }, 299 { .name = "sigsuspend", .ret_type = 1, .nargs = 1, 300 .args = { { Sigset | IN, 0 } } }, 301 { .name = "sigtimedwait", .ret_type = 1, .nargs = 3, 302 .args = { { Sigset | IN, 0 }, { Ptr, 1 }, { Timespec | IN, 2 } } }, 303 { .name = "sigwait", .ret_type = 1, .nargs = 2, 304 .args = { { Sigset | IN, 0 }, { Ptr, 1 } } }, 305 { .name = "sigwaitinfo", .ret_type = 1, .nargs = 2, 306 .args = { { Sigset | IN, 0 }, { Ptr, 1 } } }, 307 { .name = "socket", .ret_type = 1, .nargs = 3, 308 .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } }, 309 { .name = "stat", .ret_type = 1, .nargs = 2, 310 .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, 311 { .name = "statfs", .ret_type = 1, .nargs = 2, 312 .args = { { Name | IN, 0 }, { StatFs | OUT, 1 } } }, 313 { .name = "symlink", .ret_type = 1, .nargs = 2, 314 .args = { { Name, 0 }, { Name, 1 } } }, 315 { .name = "symlinkat", .ret_type = 1, .nargs = 3, 316 .args = { { Name, 0 }, { Atfd, 1 }, { Name, 2 } } }, 317 { .name = "sysarch", .ret_type = 1, .nargs = 2, 318 .args = { { Sysarch, 0 }, { Ptr, 1 } } }, 319 { .name = "thr_kill", .ret_type = 1, .nargs = 2, 320 .args = { { Long, 0 }, { Signal, 1 } } }, 321 { .name = "thr_self", .ret_type = 1, .nargs = 1, 322 .args = { { Ptr, 0 } } }, 323 { .name = "truncate", .ret_type = 1, .nargs = 2, 324 .args = { { Name | IN, 0 }, { QuadHex | IN, 1 } } }, 325 #if 0 326 /* Does not exist */ 327 { .name = "umount", .ret_type = 1, .nargs = 2, 328 .args = { { Name, 0 }, { Int, 2 } } }, 329 #endif 330 { .name = "unlink", .ret_type = 1, .nargs = 1, 331 .args = { { Name, 0 } } }, 332 { .name = "unlinkat", .ret_type = 1, .nargs = 3, 333 .args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } }, 334 { .name = "unmount", .ret_type = 1, .nargs = 2, 335 .args = { { Name, 0 }, { Int, 1 } } }, 336 { .name = "utimensat", .ret_type = 1, .nargs = 4, 337 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timespec2 | IN, 2 }, 338 { Atflags, 3 } } }, 339 { .name = "utimes", .ret_type = 1, .nargs = 2, 340 .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, 341 { .name = "utrace", .ret_type = 1, .nargs = 1, 342 .args = { { Utrace, 0 } } }, 343 { .name = "wait4", .ret_type = 1, .nargs = 4, 344 .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 }, 345 { Rusage | OUT, 3 } } }, 346 { .name = "wait6", .ret_type = 1, .nargs = 6, 347 .args = { { Idtype, 0 }, { Quad, 1 }, { ExitStatus | OUT, 2 }, 348 { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } }, 349 { .name = "write", .ret_type = 1, .nargs = 3, 350 .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } }, 351 352 /* Linux ABI */ 353 { .name = "linux_access", .ret_type = 1, .nargs = 2, 354 .args = { { Name, 0 }, { Accessmode, 1 } } }, 355 { .name = "linux_execve", .ret_type = 1, .nargs = 3, 356 .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 }, 357 { ExecEnv | IN, 2 } } }, 358 { .name = "linux_lseek", .ret_type = 2, .nargs = 3, 359 .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } }, 360 { .name = "linux_mkdir", .ret_type = 1, .nargs = 2, 361 .args = { { Name | IN, 0 }, { Int, 1 } } }, 362 { .name = "linux_newfstat", .ret_type = 1, .nargs = 2, 363 .args = { { Int, 0 }, { Ptr | OUT, 1 } } }, 364 { .name = "linux_newstat", .ret_type = 1, .nargs = 2, 365 .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } }, 366 { .name = "linux_open", .ret_type = 1, .nargs = 3, 367 .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } }, 368 { .name = "linux_readlink", .ret_type = 1, .nargs = 3, 369 .args = { { Name, 0 }, { Name | OUT, 1 }, { Int, 2 } } }, 370 { .name = "linux_socketcall", .ret_type = 1, .nargs = 2, 371 .args = { { Int, 0 }, { LinuxSockArgs, 1 } } }, 372 { .name = "linux_stat64", .ret_type = 1, .nargs = 2, 373 .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } }, 374 375 /* CloudABI system calls. */ 376 { .name = "cloudabi_sys_clock_res_get", .ret_type = 1, .nargs = 1, 377 .args = { { CloudABIClockID, 0 } } }, 378 { .name = "cloudabi_sys_clock_time_get", .ret_type = 1, .nargs = 2, 379 .args = { { CloudABIClockID, 0 }, { CloudABITimestamp, 1 } } }, 380 { .name = "cloudabi_sys_condvar_signal", .ret_type = 1, .nargs = 3, 381 .args = { { Ptr, 0 }, { CloudABIMFlags, 1 }, { UInt, 2 } } }, 382 { .name = "cloudabi_sys_fd_close", .ret_type = 1, .nargs = 1, 383 .args = { { Int, 0 } } }, 384 { .name = "cloudabi_sys_fd_create1", .ret_type = 1, .nargs = 1, 385 .args = { { CloudABIFileType, 0 } } }, 386 { .name = "cloudabi_sys_fd_create2", .ret_type = 1, .nargs = 2, 387 .args = { { CloudABIFileType, 0 }, { PipeFds | OUT, 0 } } }, 388 { .name = "cloudabi_sys_fd_datasync", .ret_type = 1, .nargs = 1, 389 .args = { { Int, 0 } } }, 390 { .name = "cloudabi_sys_fd_dup", .ret_type = 1, .nargs = 1, 391 .args = { { Int, 0 } } }, 392 { .name = "cloudabi_sys_fd_replace", .ret_type = 1, .nargs = 2, 393 .args = { { Int, 0 }, { Int, 1 } } }, 394 { .name = "cloudabi_sys_fd_seek", .ret_type = 1, .nargs = 3, 395 .args = { { Int, 0 }, { Int, 1 }, { CloudABIWhence, 2 } } }, 396 { .name = "cloudabi_sys_fd_stat_get", .ret_type = 1, .nargs = 2, 397 .args = { { Int, 0 }, { CloudABIFDStat | OUT, 1 } } }, 398 { .name = "cloudabi_sys_fd_stat_put", .ret_type = 1, .nargs = 3, 399 .args = { { Int, 0 }, { CloudABIFDStat | IN, 1 }, 400 { ClouduABIFDSFlags, 2 } } }, 401 { .name = "cloudabi_sys_fd_sync", .ret_type = 1, .nargs = 1, 402 .args = { { Int, 0 } } }, 403 { .name = "cloudabi_sys_file_advise", .ret_type = 1, .nargs = 4, 404 .args = { { Int, 0 }, { Int, 1 }, { Int, 2 }, 405 { CloudABIAdvice, 3 } } }, 406 { .name = "cloudabi_sys_file_allocate", .ret_type = 1, .nargs = 3, 407 .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } }, 408 { .name = "cloudabi_sys_file_create", .ret_type = 1, .nargs = 3, 409 .args = { { Int, 0 }, { BinString | IN, 1 }, 410 { CloudABIFileType, 3 } } }, 411 { .name = "cloudabi_sys_file_link", .ret_type = 1, .nargs = 4, 412 .args = { { CloudABILookup, 0 }, { BinString | IN, 1 }, 413 { Int, 3 }, { BinString | IN, 4 } } }, 414 { .name = "cloudabi_sys_file_open", .ret_type = 1, .nargs = 4, 415 .args = { { Int, 0 }, { BinString | IN, 1 }, 416 { CloudABIOFlags, 3 }, { CloudABIFDStat | IN, 4 } } }, 417 { .name = "cloudabi_sys_file_readdir", .ret_type = 1, .nargs = 4, 418 .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, 419 { Int, 3 } } }, 420 { .name = "cloudabi_sys_file_readlink", .ret_type = 1, .nargs = 4, 421 .args = { { Int, 0 }, { BinString | IN, 1 }, 422 { BinString | OUT, 3 }, { Int, 4 } } }, 423 { .name = "cloudabi_sys_file_rename", .ret_type = 1, .nargs = 4, 424 .args = { { Int, 0 }, { BinString | IN, 1 }, 425 { Int, 3 }, { BinString | IN, 4 } } }, 426 { .name = "cloudabi_sys_file_stat_fget", .ret_type = 1, .nargs = 2, 427 .args = { { Int, 0 }, { CloudABIFileStat | OUT, 1 } } }, 428 { .name = "cloudabi_sys_file_stat_fput", .ret_type = 1, .nargs = 3, 429 .args = { { Int, 0 }, { CloudABIFileStat | IN, 1 }, 430 { CloudABIFSFlags, 2 } } }, 431 { .name = "cloudabi_sys_file_stat_get", .ret_type = 1, .nargs = 3, 432 .args = { { CloudABILookup, 0 }, { BinString | IN, 1 }, 433 { CloudABIFileStat | OUT, 3 } } }, 434 { .name = "cloudabi_sys_file_stat_put", .ret_type = 1, .nargs = 4, 435 .args = { { CloudABILookup, 0 }, { BinString | IN, 1 }, 436 { CloudABIFileStat | IN, 3 }, { CloudABIFSFlags, 4 } } }, 437 { .name = "cloudabi_sys_file_symlink", .ret_type = 1, .nargs = 3, 438 .args = { { BinString | IN, 0 }, 439 { Int, 2 }, { BinString | IN, 3 } } }, 440 { .name = "cloudabi_sys_file_unlink", .ret_type = 1, .nargs = 3, 441 .args = { { Int, 0 }, { BinString | IN, 1 }, 442 { CloudABIULFlags, 3 } } }, 443 { .name = "cloudabi_sys_lock_unlock", .ret_type = 1, .nargs = 2, 444 .args = { { Ptr, 0 }, { CloudABIMFlags, 1 } } }, 445 { .name = "cloudabi_sys_mem_advise", .ret_type = 1, .nargs = 3, 446 .args = { { Ptr, 0 }, { Int, 1 }, { CloudABIAdvice, 2 } } }, 447 { .name = "cloudabi_sys_mem_lock", .ret_type = 1, .nargs = 2, 448 .args = { { Ptr, 0 }, { Int, 1 } } }, 449 { .name = "cloudabi_sys_mem_map", .ret_type = 1, .nargs = 6, 450 .args = { { Ptr, 0 }, { Int, 1 }, { CloudABIMProt, 2 }, 451 { CloudABIMFlags, 3 }, { Int, 4 }, { Int, 5 } } }, 452 { .name = "cloudabi_sys_mem_protect", .ret_type = 1, .nargs = 3, 453 .args = { { Ptr, 0 }, { Int, 1 }, { CloudABIMProt, 2 } } }, 454 { .name = "cloudabi_sys_mem_sync", .ret_type = 1, .nargs = 3, 455 .args = { { Ptr, 0 }, { Int, 1 }, { CloudABIMSFlags, 2 } } }, 456 { .name = "cloudabi_sys_mem_unlock", .ret_type = 1, .nargs = 2, 457 .args = { { Ptr, 0 }, { Int, 1 } } }, 458 { .name = "cloudabi_sys_mem_unmap", .ret_type = 1, .nargs = 2, 459 .args = { { Ptr, 0 }, { Int, 1 } } }, 460 { .name = "cloudabi_sys_proc_exec", .ret_type = 1, .nargs = 5, 461 .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, 462 { IntArray, 3 }, { Int, 4 } } }, 463 { .name = "cloudabi_sys_proc_exit", .ret_type = 1, .nargs = 1, 464 .args = { { Int, 0 } } }, 465 { .name = "cloudabi_sys_proc_fork", .ret_type = 1, .nargs = 0 }, 466 { .name = "cloudabi_sys_proc_raise", .ret_type = 1, .nargs = 1, 467 .args = { { CloudABISignal, 0 } } }, 468 { .name = "cloudabi_sys_random_get", .ret_type = 1, .nargs = 2, 469 .args = { { BinString | OUT, 0 }, { Int, 1 } } }, 470 { .name = "cloudabi_sys_sock_accept", .ret_type = 1, .nargs = 2, 471 .args = { { Int, 0 }, { CloudABISockStat | OUT, 1 } } }, 472 { .name = "cloudabi_sys_sock_bind", .ret_type = 1, .nargs = 3, 473 .args = { { Int, 0 }, { Int, 1 }, { BinString | IN, 2 } } }, 474 { .name = "cloudabi_sys_sock_connect", .ret_type = 1, .nargs = 3, 475 .args = { { Int, 0 }, { Int, 1 }, { BinString | IN, 2 } } }, 476 { .name = "cloudabi_sys_sock_listen", .ret_type = 1, .nargs = 2, 477 .args = { { Int, 0 }, { Int, 1 } } }, 478 { .name = "cloudabi_sys_sock_shutdown", .ret_type = 1, .nargs = 2, 479 .args = { { Int, 0 }, { CloudABISDFlags, 1 } } }, 480 { .name = "cloudabi_sys_sock_stat_get", .ret_type = 1, .nargs = 3, 481 .args = { { Int, 0 }, { CloudABISockStat | OUT, 1 }, 482 { CloudABISSFlags, 2 } } }, 483 { .name = "cloudabi_sys_thread_exit", .ret_type = 1, .nargs = 2, 484 .args = { { Ptr, 0 }, { CloudABIMFlags, 1 } } }, 485 { .name = "cloudabi_sys_thread_yield", .ret_type = 1, .nargs = 0 }, 486 487 { .name = 0 }, 488 }; 489 static STAILQ_HEAD(, syscall) syscalls; 490 491 /* Xlat idea taken from strace */ 492 struct xlat { 493 int val; 494 const char *str; 495 }; 496 497 #define X(a) { a, #a }, 498 #define XEND { 0, NULL } 499 500 static struct xlat kevent_filters[] = { 501 X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE) 502 X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER) 503 X(EVFILT_PROCDESC) X(EVFILT_FS) X(EVFILT_LIO) X(EVFILT_USER) 504 X(EVFILT_SENDFILE) XEND 505 }; 506 507 static struct xlat kevent_flags[] = { 508 X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT) 509 X(EV_CLEAR) X(EV_RECEIPT) X(EV_DISPATCH) X(EV_FORCEONESHOT) 510 X(EV_DROP) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND 511 }; 512 513 static struct xlat kevent_user_ffctrl[] = { 514 X(NOTE_FFNOP) X(NOTE_FFAND) X(NOTE_FFOR) X(NOTE_FFCOPY) 515 XEND 516 }; 517 518 static struct xlat kevent_rdwr_fflags[] = { 519 X(NOTE_LOWAT) X(NOTE_FILE_POLL) XEND 520 }; 521 522 static struct xlat kevent_vnode_fflags[] = { 523 X(NOTE_DELETE) X(NOTE_WRITE) X(NOTE_EXTEND) X(NOTE_ATTRIB) 524 X(NOTE_LINK) X(NOTE_RENAME) X(NOTE_REVOKE) XEND 525 }; 526 527 static struct xlat kevent_proc_fflags[] = { 528 X(NOTE_EXIT) X(NOTE_FORK) X(NOTE_EXEC) X(NOTE_TRACK) X(NOTE_TRACKERR) 529 X(NOTE_CHILD) XEND 530 }; 531 532 static struct xlat kevent_timer_fflags[] = { 533 X(NOTE_SECONDS) X(NOTE_MSECONDS) X(NOTE_USECONDS) X(NOTE_NSECONDS) 534 XEND 535 }; 536 537 static struct xlat poll_flags[] = { 538 X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR) 539 X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND) 540 X(POLLWRBAND) X(POLLINIGNEOF) XEND 541 }; 542 543 static struct xlat sigaction_flags[] = { 544 X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP) 545 X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND 546 }; 547 548 static struct xlat pathconf_arg[] = { 549 X(_PC_LINK_MAX) X(_PC_MAX_CANON) X(_PC_MAX_INPUT) 550 X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF) 551 X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE) 552 X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO) 553 X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS) 554 X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE) 555 X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN) 556 X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX) 557 X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT) 558 X(_PC_ACL_NFS4) X(_PC_MIN_HOLE_SIZE) XEND 559 }; 560 561 static struct xlat at_flags[] = { 562 X(AT_EACCESS) X(AT_SYMLINK_NOFOLLOW) X(AT_SYMLINK_FOLLOW) 563 X(AT_REMOVEDIR) XEND 564 }; 565 566 static struct xlat sysarch_ops[] = { 567 #if defined(__i386__) || defined(__amd64__) 568 X(I386_GET_LDT) X(I386_SET_LDT) X(I386_GET_IOPERM) X(I386_SET_IOPERM) 569 X(I386_VM86) X(I386_GET_FSBASE) X(I386_SET_FSBASE) X(I386_GET_GSBASE) 570 X(I386_SET_GSBASE) X(I386_GET_XFPUSTATE) X(AMD64_GET_FSBASE) 571 X(AMD64_SET_FSBASE) X(AMD64_GET_GSBASE) X(AMD64_SET_GSBASE) 572 X(AMD64_GET_XFPUSTATE) 573 #endif 574 XEND 575 }; 576 577 static struct xlat linux_socketcall_ops[] = { 578 X(LINUX_SOCKET) X(LINUX_BIND) X(LINUX_CONNECT) X(LINUX_LISTEN) 579 X(LINUX_ACCEPT) X(LINUX_GETSOCKNAME) X(LINUX_GETPEERNAME) 580 X(LINUX_SOCKETPAIR) X(LINUX_SEND) X(LINUX_RECV) X(LINUX_SENDTO) 581 X(LINUX_RECVFROM) X(LINUX_SHUTDOWN) X(LINUX_SETSOCKOPT) 582 X(LINUX_GETSOCKOPT) X(LINUX_SENDMSG) X(LINUX_RECVMSG) 583 XEND 584 }; 585 586 #undef X 587 #define X(a) { CLOUDABI_##a, #a }, 588 589 static struct xlat cloudabi_advice[] = { 590 X(ADVICE_DONTNEED) X(ADVICE_NOREUSE) X(ADVICE_NORMAL) 591 X(ADVICE_RANDOM) X(ADVICE_SEQUENTIAL) X(ADVICE_WILLNEED) 592 XEND 593 }; 594 595 static struct xlat cloudabi_clockid[] = { 596 X(CLOCK_MONOTONIC) X(CLOCK_PROCESS_CPUTIME_ID) 597 X(CLOCK_REALTIME) X(CLOCK_THREAD_CPUTIME_ID) 598 XEND 599 }; 600 601 static struct xlat cloudabi_errno[] = { 602 X(E2BIG) X(EACCES) X(EADDRINUSE) X(EADDRNOTAVAIL) 603 X(EAFNOSUPPORT) X(EAGAIN) X(EALREADY) X(EBADF) X(EBADMSG) 604 X(EBUSY) X(ECANCELED) X(ECHILD) X(ECONNABORTED) X(ECONNREFUSED) 605 X(ECONNRESET) X(EDEADLK) X(EDESTADDRREQ) X(EDOM) X(EDQUOT) 606 X(EEXIST) X(EFAULT) X(EFBIG) X(EHOSTUNREACH) X(EIDRM) X(EILSEQ) 607 X(EINPROGRESS) X(EINTR) X(EINVAL) X(EIO) X(EISCONN) X(EISDIR) 608 X(ELOOP) X(EMFILE) X(EMLINK) X(EMSGSIZE) X(EMULTIHOP) 609 X(ENAMETOOLONG) X(ENETDOWN) X(ENETRESET) X(ENETUNREACH) 610 X(ENFILE) X(ENOBUFS) X(ENODEV) X(ENOENT) X(ENOEXEC) X(ENOLCK) 611 X(ENOLINK) X(ENOMEM) X(ENOMSG) X(ENOPROTOOPT) X(ENOSPC) 612 X(ENOSYS) X(ENOTCONN) X(ENOTDIR) X(ENOTEMPTY) X(ENOTRECOVERABLE) 613 X(ENOTSOCK) X(ENOTSUP) X(ENOTTY) X(ENXIO) X(EOVERFLOW) 614 X(EOWNERDEAD) X(EPERM) X(EPIPE) X(EPROTO) X(EPROTONOSUPPORT) 615 X(EPROTOTYPE) X(ERANGE) X(EROFS) X(ESPIPE) X(ESRCH) X(ESTALE) 616 X(ETIMEDOUT) X(ETXTBSY) X(EXDEV) X(ENOTCAPABLE) 617 XEND 618 }; 619 620 static struct xlat cloudabi_fdflags[] = { 621 X(FDFLAG_APPEND) X(FDFLAG_DSYNC) X(FDFLAG_NONBLOCK) 622 X(FDFLAG_RSYNC) X(FDFLAG_SYNC) 623 XEND 624 }; 625 626 static struct xlat cloudabi_fdsflags[] = { 627 X(FDSTAT_FLAGS) X(FDSTAT_RIGHTS) 628 XEND 629 }; 630 631 static struct xlat cloudabi_filetype[] = { 632 X(FILETYPE_UNKNOWN) X(FILETYPE_BLOCK_DEVICE) 633 X(FILETYPE_CHARACTER_DEVICE) X(FILETYPE_DIRECTORY) 634 X(FILETYPE_FIFO) X(FILETYPE_POLL) X(FILETYPE_PROCESS) 635 X(FILETYPE_REGULAR_FILE) X(FILETYPE_SHARED_MEMORY) 636 X(FILETYPE_SOCKET_DGRAM) X(FILETYPE_SOCKET_SEQPACKET) 637 X(FILETYPE_SOCKET_STREAM) X(FILETYPE_SYMBOLIC_LINK) 638 XEND 639 }; 640 641 static struct xlat cloudabi_fsflags[] = { 642 X(FILESTAT_ATIM) X(FILESTAT_ATIM_NOW) X(FILESTAT_MTIM) 643 X(FILESTAT_MTIM_NOW) X(FILESTAT_SIZE) 644 XEND 645 }; 646 647 static struct xlat cloudabi_mflags[] = { 648 X(MAP_ANON) X(MAP_FIXED) X(MAP_PRIVATE) X(MAP_SHARED) 649 XEND 650 }; 651 652 static struct xlat cloudabi_mprot[] = { 653 X(PROT_EXEC) X(PROT_WRITE) X(PROT_READ) 654 XEND 655 }; 656 657 static struct xlat cloudabi_msflags[] = { 658 X(MS_ASYNC) X(MS_INVALIDATE) X(MS_SYNC) 659 XEND 660 }; 661 662 static struct xlat cloudabi_oflags[] = { 663 X(O_CREAT) X(O_DIRECTORY) X(O_EXCL) X(O_TRUNC) 664 XEND 665 }; 666 667 static struct xlat cloudabi_sa_family[] = { 668 X(AF_UNSPEC) X(AF_INET) X(AF_INET6) X(AF_UNIX) 669 XEND 670 }; 671 672 static struct xlat cloudabi_sdflags[] = { 673 X(SHUT_RD) X(SHUT_WR) 674 XEND 675 }; 676 677 static struct xlat cloudabi_signal[] = { 678 X(SIGABRT) X(SIGALRM) X(SIGBUS) X(SIGCHLD) X(SIGCONT) X(SIGFPE) 679 X(SIGHUP) X(SIGILL) X(SIGINT) X(SIGKILL) X(SIGPIPE) X(SIGQUIT) 680 X(SIGSEGV) X(SIGSTOP) X(SIGSYS) X(SIGTERM) X(SIGTRAP) X(SIGTSTP) 681 X(SIGTTIN) X(SIGTTOU) X(SIGURG) X(SIGUSR1) X(SIGUSR2) 682 X(SIGVTALRM) X(SIGXCPU) X(SIGXFSZ) 683 XEND 684 }; 685 686 static struct xlat cloudabi_ssflags[] = { 687 X(SOCKSTAT_CLEAR_ERROR) 688 XEND 689 }; 690 691 static struct xlat cloudabi_ssstate[] = { 692 X(SOCKSTATE_ACCEPTCONN) 693 XEND 694 }; 695 696 static struct xlat cloudabi_ulflags[] = { 697 X(UNLINK_REMOVEDIR) 698 XEND 699 }; 700 701 static struct xlat cloudabi_whence[] = { 702 X(WHENCE_CUR) X(WHENCE_END) X(WHENCE_SET) 703 XEND 704 }; 705 706 #undef X 707 #undef XEND 708 709 /* 710 * Searches an xlat array for a value, and returns it if found. Otherwise 711 * return a string representation. 712 */ 713 static const char * 714 lookup(struct xlat *xlat, int val, int base) 715 { 716 static char tmp[16]; 717 718 for (; xlat->str != NULL; xlat++) 719 if (xlat->val == val) 720 return (xlat->str); 721 switch (base) { 722 case 8: 723 sprintf(tmp, "0%o", val); 724 break; 725 case 16: 726 sprintf(tmp, "0x%x", val); 727 break; 728 case 10: 729 sprintf(tmp, "%u", val); 730 break; 731 default: 732 errx(1,"Unknown lookup base"); 733 break; 734 } 735 return (tmp); 736 } 737 738 static const char * 739 xlookup(struct xlat *xlat, int val) 740 { 741 742 return (lookup(xlat, val, 16)); 743 } 744 745 /* 746 * Searches an xlat array containing bitfield values. Remaining bits 747 * set after removing the known ones are printed at the end: 748 * IN|0x400. 749 */ 750 static char * 751 xlookup_bits(struct xlat *xlat, int val) 752 { 753 int len, rem; 754 static char str[512]; 755 756 len = 0; 757 rem = val; 758 for (; xlat->str != NULL; xlat++) { 759 if ((xlat->val & rem) == xlat->val) { 760 /* 761 * Don't print the "all-bits-zero" string unless all 762 * bits are really zero. 763 */ 764 if (xlat->val == 0 && val != 0) 765 continue; 766 len += sprintf(str + len, "%s|", xlat->str); 767 rem &= ~(xlat->val); 768 } 769 } 770 771 /* 772 * If we have leftover bits or didn't match anything, print 773 * the remainder. 774 */ 775 if (rem || len == 0) 776 len += sprintf(str + len, "0x%x", rem); 777 if (len && str[len - 1] == '|') 778 len--; 779 str[len] = 0; 780 return (str); 781 } 782 783 static void 784 print_integer_arg(const char *(*decoder)(int), FILE *fp, int value) 785 { 786 const char *str; 787 788 str = decoder(value); 789 if (str != NULL) 790 fputs(str, fp); 791 else 792 fprintf(fp, "%d", value); 793 } 794 795 static void 796 print_mask_arg(bool (*decoder)(FILE *, int, int *), FILE *fp, int value) 797 { 798 int rem; 799 800 if (!decoder(fp, value, &rem)) 801 fprintf(fp, "0x%x", rem); 802 else if (rem != 0) 803 fprintf(fp, "|0x%x", rem); 804 } 805 806 static void 807 print_mask_arg32(bool (*decoder)(FILE *, uint32_t, uint32_t *), FILE *fp, 808 uint32_t value) 809 { 810 uint32_t rem; 811 812 if (!decoder(fp, value, &rem)) 813 fprintf(fp, "0x%x", rem); 814 else if (rem != 0) 815 fprintf(fp, "|0x%x", rem); 816 } 817 818 #ifndef __LP64__ 819 /* 820 * Add argument padding to subsequent system calls afater a Quad 821 * syscall arguments as needed. This used to be done by hand in the 822 * decoded_syscalls table which was ugly and error prone. It is 823 * simpler to do the fixup of offsets at initalization time than when 824 * decoding arguments. 825 */ 826 static void 827 quad_fixup(struct syscall *sc) 828 { 829 int offset, prev; 830 u_int i; 831 832 offset = 0; 833 prev = -1; 834 for (i = 0; i < sc->nargs; i++) { 835 /* This arg type is a dummy that doesn't use offset. */ 836 if ((sc->args[i].type & ARG_MASK) == PipeFds) 837 continue; 838 839 assert(prev < sc->args[i].offset); 840 prev = sc->args[i].offset; 841 sc->args[i].offset += offset; 842 switch (sc->args[i].type & ARG_MASK) { 843 case Quad: 844 case QuadHex: 845 #ifdef __powerpc__ 846 /* 847 * 64-bit arguments on 32-bit powerpc must be 848 * 64-bit aligned. If the current offset is 849 * not aligned, the calling convention inserts 850 * a 32-bit pad argument that should be skipped. 851 */ 852 if (sc->args[i].offset % 2 == 1) { 853 sc->args[i].offset++; 854 offset++; 855 } 856 #endif 857 offset++; 858 default: 859 break; 860 } 861 } 862 } 863 #endif 864 865 void 866 init_syscalls(void) 867 { 868 struct syscall *sc; 869 870 STAILQ_INIT(&syscalls); 871 for (sc = decoded_syscalls; sc->name != NULL; sc++) { 872 #ifndef __LP64__ 873 quad_fixup(sc); 874 #endif 875 STAILQ_INSERT_HEAD(&syscalls, sc, entries); 876 } 877 } 878 879 static struct syscall * 880 find_syscall(struct procabi *abi, u_int number) 881 { 882 struct extra_syscall *es; 883 884 if (number < nitems(abi->syscalls)) 885 return (abi->syscalls[number]); 886 STAILQ_FOREACH(es, &abi->extra_syscalls, entries) { 887 if (es->number == number) 888 return (es->sc); 889 } 890 return (NULL); 891 } 892 893 static void 894 add_syscall(struct procabi *abi, u_int number, struct syscall *sc) 895 { 896 struct extra_syscall *es; 897 898 if (number < nitems(abi->syscalls)) { 899 assert(abi->syscalls[number] == NULL); 900 abi->syscalls[number] = sc; 901 } else { 902 es = malloc(sizeof(*es)); 903 es->sc = sc; 904 es->number = number; 905 STAILQ_INSERT_TAIL(&abi->extra_syscalls, es, entries); 906 } 907 } 908 909 /* 910 * If/when the list gets big, it might be desirable to do it 911 * as a hash table or binary search. 912 */ 913 struct syscall * 914 get_syscall(struct threadinfo *t, u_int number, u_int nargs) 915 { 916 struct syscall *sc; 917 const char *name; 918 char *new_name; 919 u_int i; 920 921 sc = find_syscall(t->proc->abi, number); 922 if (sc != NULL) 923 return (sc); 924 925 name = sysdecode_syscallname(t->proc->abi->abi, number); 926 if (name == NULL) { 927 asprintf(&new_name, "#%d", number); 928 name = new_name; 929 } else 930 new_name = NULL; 931 STAILQ_FOREACH(sc, &syscalls, entries) { 932 if (strcmp(name, sc->name) == 0) { 933 add_syscall(t->proc->abi, number, sc); 934 free(new_name); 935 return (sc); 936 } 937 } 938 939 /* It is unknown. Add it into the list. */ 940 #if DEBUG 941 fprintf(stderr, "unknown syscall %s -- setting args to %d\n", name, 942 nargs); 943 #endif 944 945 sc = calloc(1, sizeof(struct syscall)); 946 sc->name = name; 947 if (new_name != NULL) 948 sc->unknown = true; 949 sc->ret_type = 1; 950 sc->nargs = nargs; 951 for (i = 0; i < nargs; i++) { 952 sc->args[i].offset = i; 953 /* Treat all unknown arguments as LongHex. */ 954 sc->args[i].type = LongHex; 955 } 956 STAILQ_INSERT_HEAD(&syscalls, sc, entries); 957 add_syscall(t->proc->abi, number, sc); 958 959 return (sc); 960 } 961 962 /* 963 * Copy a fixed amount of bytes from the process. 964 */ 965 static int 966 get_struct(pid_t pid, void *offset, void *buf, int len) 967 { 968 struct ptrace_io_desc iorequest; 969 970 iorequest.piod_op = PIOD_READ_D; 971 iorequest.piod_offs = offset; 972 iorequest.piod_addr = buf; 973 iorequest.piod_len = len; 974 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) 975 return (-1); 976 return (0); 977 } 978 979 #define MAXSIZE 4096 980 981 /* 982 * Copy a string from the process. Note that it is 983 * expected to be a C string, but if max is set, it will 984 * only get that much. 985 */ 986 static char * 987 get_string(pid_t pid, void *addr, int max) 988 { 989 struct ptrace_io_desc iorequest; 990 char *buf, *nbuf; 991 size_t offset, size, totalsize; 992 993 offset = 0; 994 if (max) 995 size = max + 1; 996 else { 997 /* Read up to the end of the current page. */ 998 size = PAGE_SIZE - ((uintptr_t)addr % PAGE_SIZE); 999 if (size > MAXSIZE) 1000 size = MAXSIZE; 1001 } 1002 totalsize = size; 1003 buf = malloc(totalsize); 1004 if (buf == NULL) 1005 return (NULL); 1006 for (;;) { 1007 iorequest.piod_op = PIOD_READ_D; 1008 iorequest.piod_offs = (char *)addr + offset; 1009 iorequest.piod_addr = buf + offset; 1010 iorequest.piod_len = size; 1011 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) { 1012 free(buf); 1013 return (NULL); 1014 } 1015 if (memchr(buf + offset, '\0', size) != NULL) 1016 return (buf); 1017 offset += size; 1018 if (totalsize < MAXSIZE && max == 0) { 1019 size = MAXSIZE - totalsize; 1020 if (size > PAGE_SIZE) 1021 size = PAGE_SIZE; 1022 nbuf = realloc(buf, totalsize + size); 1023 if (nbuf == NULL) { 1024 buf[totalsize - 1] = '\0'; 1025 return (buf); 1026 } 1027 buf = nbuf; 1028 totalsize += size; 1029 } else { 1030 buf[totalsize - 1] = '\0'; 1031 return (buf); 1032 } 1033 } 1034 } 1035 1036 static const char * 1037 strsig2(int sig) 1038 { 1039 static char tmp[32]; 1040 const char *signame; 1041 1042 signame = sysdecode_signal(sig); 1043 if (signame == NULL) { 1044 snprintf(tmp, sizeof(tmp), "%d", sig); 1045 signame = tmp; 1046 } 1047 return (signame); 1048 } 1049 1050 static void 1051 print_kevent(FILE *fp, struct kevent *ke, int input) 1052 { 1053 1054 switch (ke->filter) { 1055 case EVFILT_READ: 1056 case EVFILT_WRITE: 1057 case EVFILT_VNODE: 1058 case EVFILT_PROC: 1059 case EVFILT_TIMER: 1060 case EVFILT_PROCDESC: 1061 fprintf(fp, "%ju", (uintmax_t)ke->ident); 1062 break; 1063 case EVFILT_SIGNAL: 1064 fputs(strsig2(ke->ident), fp); 1065 break; 1066 default: 1067 fprintf(fp, "%p", (void *)ke->ident); 1068 } 1069 fprintf(fp, ",%s,%s,", xlookup(kevent_filters, ke->filter), 1070 xlookup_bits(kevent_flags, ke->flags)); 1071 switch (ke->filter) { 1072 case EVFILT_READ: 1073 case EVFILT_WRITE: 1074 fputs(xlookup_bits(kevent_rdwr_fflags, ke->fflags), fp); 1075 break; 1076 case EVFILT_VNODE: 1077 fputs(xlookup_bits(kevent_vnode_fflags, ke->fflags), fp); 1078 break; 1079 case EVFILT_PROC: 1080 case EVFILT_PROCDESC: 1081 fputs(xlookup_bits(kevent_proc_fflags, ke->fflags), fp); 1082 break; 1083 case EVFILT_TIMER: 1084 fputs(xlookup_bits(kevent_timer_fflags, ke->fflags), fp); 1085 break; 1086 case EVFILT_USER: { 1087 int ctrl, data; 1088 1089 ctrl = ke->fflags & NOTE_FFCTRLMASK; 1090 data = ke->fflags & NOTE_FFLAGSMASK; 1091 if (input) { 1092 fputs(xlookup(kevent_user_ffctrl, ctrl), fp); 1093 if (ke->fflags & NOTE_TRIGGER) 1094 fputs("|NOTE_TRIGGER", fp); 1095 if (data != 0) 1096 fprintf(fp, "|%#x", data); 1097 } else { 1098 fprintf(fp, "%#x", data); 1099 } 1100 break; 1101 } 1102 default: 1103 fprintf(fp, "%#x", ke->fflags); 1104 } 1105 fprintf(fp, ",%p,%p", (void *)ke->data, (void *)ke->udata); 1106 } 1107 1108 static void 1109 print_utrace(FILE *fp, void *utrace_addr, size_t len) 1110 { 1111 unsigned char *utrace_buffer; 1112 1113 fprintf(fp, "{ "); 1114 if (sysdecode_utrace(fp, utrace_addr, len)) { 1115 fprintf(fp, " }"); 1116 return; 1117 } 1118 1119 utrace_buffer = utrace_addr; 1120 fprintf(fp, "%zu:", len); 1121 while (len--) 1122 fprintf(fp, " %02x", *utrace_buffer++); 1123 fprintf(fp, " }"); 1124 } 1125 1126 /* 1127 * Converts a syscall argument into a string. Said string is 1128 * allocated via malloc(), so needs to be free()'d. sc is 1129 * a pointer to the syscall description (see above); args is 1130 * an array of all of the system call arguments. 1131 */ 1132 char * 1133 print_arg(struct syscall_args *sc, unsigned long *args, long *retval, 1134 struct trussinfo *trussinfo) 1135 { 1136 FILE *fp; 1137 char *tmp; 1138 size_t tmplen; 1139 pid_t pid; 1140 1141 fp = open_memstream(&tmp, &tmplen); 1142 pid = trussinfo->curthread->proc->pid; 1143 switch (sc->type & ARG_MASK) { 1144 case Hex: 1145 fprintf(fp, "0x%x", (int)args[sc->offset]); 1146 break; 1147 case Octal: 1148 fprintf(fp, "0%o", (int)args[sc->offset]); 1149 break; 1150 case Int: 1151 fprintf(fp, "%d", (int)args[sc->offset]); 1152 break; 1153 case UInt: 1154 fprintf(fp, "%u", (unsigned int)args[sc->offset]); 1155 break; 1156 case LongHex: 1157 fprintf(fp, "0x%lx", args[sc->offset]); 1158 break; 1159 case Long: 1160 fprintf(fp, "%ld", args[sc->offset]); 1161 break; 1162 case Name: { 1163 /* NULL-terminated string. */ 1164 char *tmp2; 1165 1166 tmp2 = get_string(pid, (void*)args[sc->offset], 0); 1167 fprintf(fp, "\"%s\"", tmp2); 1168 free(tmp2); 1169 break; 1170 } 1171 case BinString: { 1172 /* 1173 * Binary block of data that might have printable characters. 1174 * XXX If type|OUT, assume that the length is the syscall's 1175 * return value. Otherwise, assume that the length of the block 1176 * is in the next syscall argument. 1177 */ 1178 int max_string = trussinfo->strsize; 1179 char tmp2[max_string + 1], *tmp3; 1180 int len; 1181 int truncated = 0; 1182 1183 if (sc->type & OUT) 1184 len = retval[0]; 1185 else 1186 len = args[sc->offset + 1]; 1187 1188 /* 1189 * Don't print more than max_string characters, to avoid word 1190 * wrap. If we have to truncate put some ... after the string. 1191 */ 1192 if (len > max_string) { 1193 len = max_string; 1194 truncated = 1; 1195 } 1196 if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len) 1197 != -1) { 1198 tmp3 = malloc(len * 4 + 1); 1199 while (len) { 1200 if (strvisx(tmp3, tmp2, len, 1201 VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string) 1202 break; 1203 len--; 1204 truncated = 1; 1205 } 1206 fprintf(fp, "\"%s\"%s", tmp3, truncated ? 1207 "..." : ""); 1208 free(tmp3); 1209 } else { 1210 fprintf(fp, "0x%lx", args[sc->offset]); 1211 } 1212 break; 1213 } 1214 case ExecArgs: 1215 case ExecEnv: 1216 case StringArray: { 1217 uintptr_t addr; 1218 union { 1219 char *strarray[0]; 1220 char buf[PAGE_SIZE]; 1221 } u; 1222 char *string; 1223 size_t len; 1224 u_int first, i; 1225 1226 /* 1227 * Only parse argv[] and environment arrays from exec calls 1228 * if requested. 1229 */ 1230 if (((sc->type & ARG_MASK) == ExecArgs && 1231 (trussinfo->flags & EXECVEARGS) == 0) || 1232 ((sc->type & ARG_MASK) == ExecEnv && 1233 (trussinfo->flags & EXECVEENVS) == 0)) { 1234 fprintf(fp, "0x%lx", args[sc->offset]); 1235 break; 1236 } 1237 1238 /* 1239 * Read a page of pointers at a time. Punt if the top-level 1240 * pointer is not aligned. Note that the first read is of 1241 * a partial page. 1242 */ 1243 addr = args[sc->offset]; 1244 if (addr % sizeof(char *) != 0) { 1245 fprintf(fp, "0x%lx", args[sc->offset]); 1246 break; 1247 } 1248 1249 len = PAGE_SIZE - (addr & PAGE_MASK); 1250 if (get_struct(pid, (void *)addr, u.buf, len) == -1) { 1251 fprintf(fp, "0x%lx", args[sc->offset]); 1252 break; 1253 } 1254 1255 fputc('[', fp); 1256 first = 1; 1257 i = 0; 1258 while (u.strarray[i] != NULL) { 1259 string = get_string(pid, u.strarray[i], 0); 1260 fprintf(fp, "%s \"%s\"", first ? "" : ",", string); 1261 free(string); 1262 first = 0; 1263 1264 i++; 1265 if (i == len / sizeof(char *)) { 1266 addr += len; 1267 len = PAGE_SIZE; 1268 if (get_struct(pid, (void *)addr, u.buf, len) == 1269 -1) { 1270 fprintf(fp, ", <inval>"); 1271 break; 1272 } 1273 i = 0; 1274 } 1275 } 1276 fputs(" ]", fp); 1277 break; 1278 } 1279 #ifdef __LP64__ 1280 case Quad: 1281 fprintf(fp, "%ld", args[sc->offset]); 1282 break; 1283 case QuadHex: 1284 fprintf(fp, "0x%lx", args[sc->offset]); 1285 break; 1286 #else 1287 case Quad: 1288 case QuadHex: { 1289 unsigned long long ll; 1290 1291 #if _BYTE_ORDER == _LITTLE_ENDIAN 1292 ll = (unsigned long long)args[sc->offset + 1] << 32 | 1293 args[sc->offset]; 1294 #else 1295 ll = (unsigned long long)args[sc->offset] << 32 | 1296 args[sc->offset + 1]; 1297 #endif 1298 if ((sc->type & ARG_MASK) == Quad) 1299 fprintf(fp, "%lld", ll); 1300 else 1301 fprintf(fp, "0x%llx", ll); 1302 break; 1303 } 1304 #endif 1305 case Ptr: 1306 fprintf(fp, "0x%lx", args[sc->offset]); 1307 break; 1308 case Readlinkres: { 1309 char *tmp2; 1310 1311 if (retval[0] == -1) 1312 break; 1313 tmp2 = get_string(pid, (void*)args[sc->offset], retval[0]); 1314 fprintf(fp, "\"%s\"", tmp2); 1315 free(tmp2); 1316 break; 1317 } 1318 case Ioctl: { 1319 const char *temp; 1320 unsigned long cmd; 1321 1322 cmd = args[sc->offset]; 1323 temp = sysdecode_ioctlname(cmd); 1324 if (temp) 1325 fputs(temp, fp); 1326 else { 1327 fprintf(fp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }", 1328 cmd, cmd & IOC_OUT ? "R" : "", 1329 cmd & IOC_IN ? "W" : "", IOCGROUP(cmd), 1330 isprint(IOCGROUP(cmd)) ? (char)IOCGROUP(cmd) : '?', 1331 cmd & 0xFF, IOCPARM_LEN(cmd)); 1332 } 1333 break; 1334 } 1335 case Timespec: { 1336 struct timespec ts; 1337 1338 if (get_struct(pid, (void *)args[sc->offset], &ts, 1339 sizeof(ts)) != -1) 1340 fprintf(fp, "{ %jd.%09ld }", (intmax_t)ts.tv_sec, 1341 ts.tv_nsec); 1342 else 1343 fprintf(fp, "0x%lx", args[sc->offset]); 1344 break; 1345 } 1346 case Timespec2: { 1347 struct timespec ts[2]; 1348 const char *sep; 1349 unsigned int i; 1350 1351 if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts)) 1352 != -1) { 1353 fputs("{ ", fp); 1354 sep = ""; 1355 for (i = 0; i < nitems(ts); i++) { 1356 fputs(sep, fp); 1357 sep = ", "; 1358 switch (ts[i].tv_nsec) { 1359 case UTIME_NOW: 1360 fprintf(fp, "UTIME_NOW"); 1361 break; 1362 case UTIME_OMIT: 1363 fprintf(fp, "UTIME_OMIT"); 1364 break; 1365 default: 1366 fprintf(fp, "%jd.%09ld", 1367 (intmax_t)ts[i].tv_sec, 1368 ts[i].tv_nsec); 1369 break; 1370 } 1371 } 1372 fputs(" }", fp); 1373 } else 1374 fprintf(fp, "0x%lx", args[sc->offset]); 1375 break; 1376 } 1377 case Timeval: { 1378 struct timeval tv; 1379 1380 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) 1381 != -1) 1382 fprintf(fp, "{ %jd.%06ld }", (intmax_t)tv.tv_sec, 1383 tv.tv_usec); 1384 else 1385 fprintf(fp, "0x%lx", args[sc->offset]); 1386 break; 1387 } 1388 case Timeval2: { 1389 struct timeval tv[2]; 1390 1391 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) 1392 != -1) 1393 fprintf(fp, "{ %jd.%06ld, %jd.%06ld }", 1394 (intmax_t)tv[0].tv_sec, tv[0].tv_usec, 1395 (intmax_t)tv[1].tv_sec, tv[1].tv_usec); 1396 else 1397 fprintf(fp, "0x%lx", args[sc->offset]); 1398 break; 1399 } 1400 case Itimerval: { 1401 struct itimerval itv; 1402 1403 if (get_struct(pid, (void *)args[sc->offset], &itv, 1404 sizeof(itv)) != -1) 1405 fprintf(fp, "{ %jd.%06ld, %jd.%06ld }", 1406 (intmax_t)itv.it_interval.tv_sec, 1407 itv.it_interval.tv_usec, 1408 (intmax_t)itv.it_value.tv_sec, 1409 itv.it_value.tv_usec); 1410 else 1411 fprintf(fp, "0x%lx", args[sc->offset]); 1412 break; 1413 } 1414 case LinuxSockArgs: 1415 { 1416 struct linux_socketcall_args largs; 1417 1418 if (get_struct(pid, (void *)args[sc->offset], (void *)&largs, 1419 sizeof(largs)) != -1) 1420 fprintf(fp, "{ %s, 0x%lx }", 1421 lookup(linux_socketcall_ops, largs.what, 10), 1422 (long unsigned int)largs.args); 1423 else 1424 fprintf(fp, "0x%lx", args[sc->offset]); 1425 break; 1426 } 1427 case Pollfd: { 1428 /* 1429 * XXX: A Pollfd argument expects the /next/ syscall argument 1430 * to be the number of fds in the array. This matches the poll 1431 * syscall. 1432 */ 1433 struct pollfd *pfd; 1434 int numfds = args[sc->offset + 1]; 1435 size_t bytes = sizeof(struct pollfd) * numfds; 1436 int i; 1437 1438 if ((pfd = malloc(bytes)) == NULL) 1439 err(1, "Cannot malloc %zu bytes for pollfd array", 1440 bytes); 1441 if (get_struct(pid, (void *)args[sc->offset], pfd, bytes) 1442 != -1) { 1443 fputs("{", fp); 1444 for (i = 0; i < numfds; i++) { 1445 fprintf(fp, " %d/%s", pfd[i].fd, 1446 xlookup_bits(poll_flags, pfd[i].events)); 1447 } 1448 fputs(" }", fp); 1449 } else { 1450 fprintf(fp, "0x%lx", args[sc->offset]); 1451 } 1452 free(pfd); 1453 break; 1454 } 1455 case Fd_set: { 1456 /* 1457 * XXX: A Fd_set argument expects the /first/ syscall argument 1458 * to be the number of fds in the array. This matches the 1459 * select syscall. 1460 */ 1461 fd_set *fds; 1462 int numfds = args[0]; 1463 size_t bytes = _howmany(numfds, _NFDBITS) * _NFDBITS; 1464 int i; 1465 1466 if ((fds = malloc(bytes)) == NULL) 1467 err(1, "Cannot malloc %zu bytes for fd_set array", 1468 bytes); 1469 if (get_struct(pid, (void *)args[sc->offset], fds, bytes) 1470 != -1) { 1471 fputs("{", fp); 1472 for (i = 0; i < numfds; i++) { 1473 if (FD_ISSET(i, fds)) 1474 fprintf(fp, " %d", i); 1475 } 1476 fputs(" }", fp); 1477 } else 1478 fprintf(fp, "0x%lx", args[sc->offset]); 1479 free(fds); 1480 break; 1481 } 1482 case Signal: 1483 fputs(strsig2(args[sc->offset]), fp); 1484 break; 1485 case Sigset: { 1486 long sig; 1487 sigset_t ss; 1488 int i, first; 1489 1490 sig = args[sc->offset]; 1491 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 1492 sizeof(ss)) == -1) { 1493 fprintf(fp, "0x%lx", args[sc->offset]); 1494 break; 1495 } 1496 fputs("{ ", fp); 1497 first = 1; 1498 for (i = 1; i < sys_nsig; i++) { 1499 if (sigismember(&ss, i)) { 1500 fprintf(fp, "%s%s", !first ? "|" : "", 1501 strsig2(i)); 1502 first = 0; 1503 } 1504 } 1505 if (!first) 1506 fputc(' ', fp); 1507 fputc('}', fp); 1508 break; 1509 } 1510 case Sigprocmask: 1511 print_integer_arg(sysdecode_sigprocmask_how, fp, 1512 args[sc->offset]); 1513 break; 1514 case Fcntlflag: 1515 /* XXX: Output depends on the value of the previous argument. */ 1516 if (sysdecode_fcntl_arg_p(args[sc->offset - 1])) 1517 sysdecode_fcntl_arg(fp, args[sc->offset - 1], 1518 args[sc->offset], 16); 1519 break; 1520 case Open: 1521 print_mask_arg(sysdecode_open_flags, fp, args[sc->offset]); 1522 break; 1523 case Fcntl: 1524 print_integer_arg(sysdecode_fcntl_cmd, fp, args[sc->offset]); 1525 break; 1526 case Mprot: 1527 print_mask_arg(sysdecode_mmap_prot, fp, args[sc->offset]); 1528 break; 1529 case Mmapflags: 1530 print_mask_arg(sysdecode_mmap_flags, fp, args[sc->offset]); 1531 break; 1532 case Whence: 1533 print_integer_arg(sysdecode_whence, fp, args[sc->offset]); 1534 break; 1535 case Sockdomain: 1536 print_integer_arg(sysdecode_socketdomain, fp, args[sc->offset]); 1537 break; 1538 case Socktype: 1539 print_mask_arg(sysdecode_socket_type, fp, args[sc->offset]); 1540 break; 1541 case Shutdown: 1542 print_integer_arg(sysdecode_shutdown_how, fp, args[sc->offset]); 1543 break; 1544 case Resource: 1545 print_integer_arg(sysdecode_rlimit, fp, args[sc->offset]); 1546 break; 1547 case Pathconf: 1548 fputs(xlookup(pathconf_arg, args[sc->offset]), fp); 1549 break; 1550 case Rforkflags: 1551 print_mask_arg(sysdecode_rfork_flags, fp, args[sc->offset]); 1552 break; 1553 case Sockaddr: { 1554 char addr[64]; 1555 struct sockaddr_in *lsin; 1556 struct sockaddr_in6 *lsin6; 1557 struct sockaddr_un *sun; 1558 struct sockaddr *sa; 1559 socklen_t len; 1560 u_char *q; 1561 1562 if (args[sc->offset] == 0) { 1563 fputs("NULL", fp); 1564 break; 1565 } 1566 1567 /* 1568 * Extract the address length from the next argument. If 1569 * this is an output sockaddr (OUT is set), then the 1570 * next argument is a pointer to a socklen_t. Otherwise 1571 * the next argument contains a socklen_t by value. 1572 */ 1573 if (sc->type & OUT) { 1574 if (get_struct(pid, (void *)args[sc->offset + 1], 1575 &len, sizeof(len)) == -1) { 1576 fprintf(fp, "0x%lx", args[sc->offset]); 1577 break; 1578 } 1579 } else 1580 len = args[sc->offset + 1]; 1581 1582 /* If the length is too small, just bail. */ 1583 if (len < sizeof(*sa)) { 1584 fprintf(fp, "0x%lx", args[sc->offset]); 1585 break; 1586 } 1587 1588 sa = calloc(1, len); 1589 if (get_struct(pid, (void *)args[sc->offset], sa, len) == -1) { 1590 free(sa); 1591 fprintf(fp, "0x%lx", args[sc->offset]); 1592 break; 1593 } 1594 1595 switch (sa->sa_family) { 1596 case AF_INET: 1597 if (len < sizeof(*lsin)) 1598 goto sockaddr_short; 1599 lsin = (struct sockaddr_in *)(void *)sa; 1600 inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof(addr)); 1601 fprintf(fp, "{ AF_INET %s:%d }", addr, 1602 htons(lsin->sin_port)); 1603 break; 1604 case AF_INET6: 1605 if (len < sizeof(*lsin6)) 1606 goto sockaddr_short; 1607 lsin6 = (struct sockaddr_in6 *)(void *)sa; 1608 inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, 1609 sizeof(addr)); 1610 fprintf(fp, "{ AF_INET6 [%s]:%d }", addr, 1611 htons(lsin6->sin6_port)); 1612 break; 1613 case AF_UNIX: 1614 sun = (struct sockaddr_un *)sa; 1615 fprintf(fp, "{ AF_UNIX \"%.*s\" }", 1616 (int)(len - offsetof(struct sockaddr_un, sun_path)), 1617 sun->sun_path); 1618 break; 1619 default: 1620 sockaddr_short: 1621 fprintf(fp, 1622 "{ sa_len = %d, sa_family = %d, sa_data = {", 1623 (int)sa->sa_len, (int)sa->sa_family); 1624 for (q = (u_char *)sa->sa_data; 1625 q < (u_char *)sa + len; q++) 1626 fprintf(fp, "%s 0x%02x", 1627 q == (u_char *)sa->sa_data ? "" : ",", 1628 *q); 1629 fputs(" } }", fp); 1630 } 1631 free(sa); 1632 break; 1633 } 1634 case Sigaction: { 1635 struct sigaction sa; 1636 1637 if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa)) 1638 != -1) { 1639 fputs("{ ", fp); 1640 if (sa.sa_handler == SIG_DFL) 1641 fputs("SIG_DFL", fp); 1642 else if (sa.sa_handler == SIG_IGN) 1643 fputs("SIG_IGN", fp); 1644 else 1645 fprintf(fp, "%p", sa.sa_handler); 1646 fprintf(fp, " %s ss_t }", 1647 xlookup_bits(sigaction_flags, sa.sa_flags)); 1648 } else 1649 fprintf(fp, "0x%lx", args[sc->offset]); 1650 break; 1651 } 1652 case Kevent: { 1653 /* 1654 * XXX XXX: The size of the array is determined by either the 1655 * next syscall argument, or by the syscall return value, 1656 * depending on which argument number we are. This matches the 1657 * kevent syscall, but luckily that's the only syscall that uses 1658 * them. 1659 */ 1660 struct kevent *ke; 1661 int numevents = -1; 1662 size_t bytes; 1663 int i; 1664 1665 if (sc->offset == 1) 1666 numevents = args[sc->offset+1]; 1667 else if (sc->offset == 3 && retval[0] != -1) 1668 numevents = retval[0]; 1669 1670 if (numevents >= 0) { 1671 bytes = sizeof(struct kevent) * numevents; 1672 if ((ke = malloc(bytes)) == NULL) 1673 err(1, 1674 "Cannot malloc %zu bytes for kevent array", 1675 bytes); 1676 } else 1677 ke = NULL; 1678 if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset], 1679 ke, bytes) != -1) { 1680 fputc('{', fp); 1681 for (i = 0; i < numevents; i++) { 1682 fputc(' ', fp); 1683 print_kevent(fp, &ke[i], sc->offset == 1); 1684 } 1685 fputs(" }", fp); 1686 } else { 1687 fprintf(fp, "0x%lx", args[sc->offset]); 1688 } 1689 free(ke); 1690 break; 1691 } 1692 case Stat: { 1693 struct stat st; 1694 1695 if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st)) 1696 != -1) { 1697 char mode[12]; 1698 1699 strmode(st.st_mode, mode); 1700 fprintf(fp, 1701 "{ mode=%s,inode=%ju,size=%jd,blksize=%ld }", mode, 1702 (uintmax_t)st.st_ino, (intmax_t)st.st_size, 1703 (long)st.st_blksize); 1704 } else { 1705 fprintf(fp, "0x%lx", args[sc->offset]); 1706 } 1707 break; 1708 } 1709 case StatFs: { 1710 unsigned int i; 1711 struct statfs buf; 1712 1713 if (get_struct(pid, (void *)args[sc->offset], &buf, 1714 sizeof(buf)) != -1) { 1715 char fsid[17]; 1716 1717 bzero(fsid, sizeof(fsid)); 1718 if (buf.f_fsid.val[0] != 0 || buf.f_fsid.val[1] != 0) { 1719 for (i = 0; i < sizeof(buf.f_fsid); i++) 1720 snprintf(&fsid[i*2], 1721 sizeof(fsid) - (i*2), "%02x", 1722 ((u_char *)&buf.f_fsid)[i]); 1723 } 1724 fprintf(fp, 1725 "{ fstypename=%s,mntonname=%s,mntfromname=%s," 1726 "fsid=%s }", buf.f_fstypename, buf.f_mntonname, 1727 buf.f_mntfromname, fsid); 1728 } else 1729 fprintf(fp, "0x%lx", args[sc->offset]); 1730 break; 1731 } 1732 1733 case Rusage: { 1734 struct rusage ru; 1735 1736 if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru)) 1737 != -1) { 1738 fprintf(fp, 1739 "{ u=%jd.%06ld,s=%jd.%06ld,in=%ld,out=%ld }", 1740 (intmax_t)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec, 1741 (intmax_t)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec, 1742 ru.ru_inblock, ru.ru_oublock); 1743 } else 1744 fprintf(fp, "0x%lx", args[sc->offset]); 1745 break; 1746 } 1747 case Rlimit: { 1748 struct rlimit rl; 1749 1750 if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl)) 1751 != -1) { 1752 fprintf(fp, "{ cur=%ju,max=%ju }", 1753 rl.rlim_cur, rl.rlim_max); 1754 } else 1755 fprintf(fp, "0x%lx", args[sc->offset]); 1756 break; 1757 } 1758 case ExitStatus: { 1759 int status; 1760 1761 if (get_struct(pid, (void *)args[sc->offset], &status, 1762 sizeof(status)) != -1) { 1763 fputs("{ ", fp); 1764 if (WIFCONTINUED(status)) 1765 fputs("CONTINUED", fp); 1766 else if (WIFEXITED(status)) 1767 fprintf(fp, "EXITED,val=%d", 1768 WEXITSTATUS(status)); 1769 else if (WIFSIGNALED(status)) 1770 fprintf(fp, "SIGNALED,sig=%s%s", 1771 strsig2(WTERMSIG(status)), 1772 WCOREDUMP(status) ? ",cored" : ""); 1773 else 1774 fprintf(fp, "STOPPED,sig=%s", 1775 strsig2(WTERMSIG(status))); 1776 fputs(" }", fp); 1777 } else 1778 fprintf(fp, "0x%lx", args[sc->offset]); 1779 break; 1780 } 1781 case Waitoptions: 1782 print_mask_arg(sysdecode_wait6_options, fp, args[sc->offset]); 1783 break; 1784 case Idtype: 1785 print_integer_arg(sysdecode_idtype, fp, args[sc->offset]); 1786 break; 1787 case Procctl: 1788 print_integer_arg(sysdecode_procctl_cmd, fp, args[sc->offset]); 1789 break; 1790 case Umtxop: 1791 print_integer_arg(sysdecode_umtx_op, fp, args[sc->offset]); 1792 break; 1793 case Atfd: 1794 print_integer_arg(sysdecode_atfd, fp, args[sc->offset]); 1795 break; 1796 case Atflags: 1797 fputs(xlookup_bits(at_flags, args[sc->offset]), fp); 1798 break; 1799 case Accessmode: 1800 print_mask_arg(sysdecode_access_mode, fp, args[sc->offset]); 1801 break; 1802 case Sysarch: 1803 fputs(xlookup(sysarch_ops, args[sc->offset]), fp); 1804 break; 1805 case PipeFds: 1806 /* 1807 * The pipe() system call in the kernel returns its 1808 * two file descriptors via return values. However, 1809 * the interface exposed by libc is that pipe() 1810 * accepts a pointer to an array of descriptors. 1811 * Format the output to match the libc API by printing 1812 * the returned file descriptors as a fake argument. 1813 * 1814 * Overwrite the first retval to signal a successful 1815 * return as well. 1816 */ 1817 fprintf(fp, "{ %ld, %ld }", retval[0], retval[1]); 1818 retval[0] = 0; 1819 break; 1820 case Utrace: { 1821 size_t len; 1822 void *utrace_addr; 1823 1824 len = args[sc->offset + 1]; 1825 utrace_addr = calloc(1, len); 1826 if (get_struct(pid, (void *)args[sc->offset], 1827 (void *)utrace_addr, len) != -1) 1828 print_utrace(fp, utrace_addr, len); 1829 else 1830 fprintf(fp, "0x%lx", args[sc->offset]); 1831 free(utrace_addr); 1832 break; 1833 } 1834 case IntArray: { 1835 int descriptors[16]; 1836 unsigned long i, ndescriptors; 1837 bool truncated; 1838 1839 ndescriptors = args[sc->offset + 1]; 1840 truncated = false; 1841 if (ndescriptors > nitems(descriptors)) { 1842 ndescriptors = nitems(descriptors); 1843 truncated = true; 1844 } 1845 if (get_struct(pid, (void *)args[sc->offset], 1846 descriptors, ndescriptors * sizeof(descriptors[0])) != -1) { 1847 fprintf(fp, "{"); 1848 for (i = 0; i < ndescriptors; i++) 1849 fprintf(fp, i == 0 ? " %d" : ", %d", 1850 descriptors[i]); 1851 fprintf(fp, truncated ? ", ... }" : " }"); 1852 } else 1853 fprintf(fp, "0x%lx", args[sc->offset]); 1854 break; 1855 } 1856 case Pipe2: 1857 print_mask_arg(sysdecode_pipe2_flags, fp, args[sc->offset]); 1858 break; 1859 case CapFcntlRights: { 1860 uint32_t rights; 1861 1862 if (sc->type & OUT) { 1863 if (get_struct(pid, (void *)args[sc->offset], &rights, 1864 sizeof(rights)) == -1) { 1865 fprintf(fp, "0x%lx", args[sc->offset]); 1866 break; 1867 } 1868 } else 1869 rights = args[sc->offset]; 1870 print_mask_arg32(sysdecode_cap_fcntlrights, fp, rights); 1871 break; 1872 } 1873 case Fadvice: 1874 print_integer_arg(sysdecode_fadvice, fp, args[sc->offset]); 1875 break; 1876 case FileFlags: { 1877 fflags_t rem; 1878 1879 if (!sysdecode_fileflags(fp, args[sc->offset], &rem)) 1880 fprintf(fp, "0x%x", rem); 1881 else if (rem != 0) 1882 fprintf(fp, "|0x%x", rem); 1883 break; 1884 } 1885 1886 case CloudABIAdvice: 1887 fputs(xlookup(cloudabi_advice, args[sc->offset]), fp); 1888 break; 1889 case CloudABIClockID: 1890 fputs(xlookup(cloudabi_clockid, args[sc->offset]), fp); 1891 break; 1892 case ClouduABIFDSFlags: 1893 fputs(xlookup_bits(cloudabi_fdsflags, args[sc->offset]), fp); 1894 break; 1895 case CloudABIFDStat: { 1896 cloudabi_fdstat_t fds; 1897 if (get_struct(pid, (void *)args[sc->offset], &fds, sizeof(fds)) 1898 != -1) { 1899 fprintf(fp, "{ %s, ", 1900 xlookup(cloudabi_filetype, fds.fs_filetype)); 1901 fprintf(fp, "%s, ... }", 1902 xlookup_bits(cloudabi_fdflags, fds.fs_flags)); 1903 } else 1904 fprintf(fp, "0x%lx", args[sc->offset]); 1905 break; 1906 } 1907 case CloudABIFileStat: { 1908 cloudabi_filestat_t fsb; 1909 if (get_struct(pid, (void *)args[sc->offset], &fsb, sizeof(fsb)) 1910 != -1) 1911 fprintf(fp, "{ %s, %ju }", 1912 xlookup(cloudabi_filetype, fsb.st_filetype), 1913 (uintmax_t)fsb.st_size); 1914 else 1915 fprintf(fp, "0x%lx", args[sc->offset]); 1916 break; 1917 } 1918 case CloudABIFileType: 1919 fputs(xlookup(cloudabi_filetype, args[sc->offset]), fp); 1920 break; 1921 case CloudABIFSFlags: 1922 fputs(xlookup_bits(cloudabi_fsflags, args[sc->offset]), fp); 1923 break; 1924 case CloudABILookup: 1925 if ((args[sc->offset] & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0) 1926 fprintf(fp, "%d|LOOKUP_SYMLINK_FOLLOW", 1927 (int)args[sc->offset]); 1928 else 1929 fprintf(fp, "%d", (int)args[sc->offset]); 1930 break; 1931 case CloudABIMFlags: 1932 fputs(xlookup_bits(cloudabi_mflags, args[sc->offset]), fp); 1933 break; 1934 case CloudABIMProt: 1935 fputs(xlookup_bits(cloudabi_mprot, args[sc->offset]), fp); 1936 break; 1937 case CloudABIMSFlags: 1938 fputs(xlookup_bits(cloudabi_msflags, args[sc->offset]), fp); 1939 break; 1940 case CloudABIOFlags: 1941 fputs(xlookup_bits(cloudabi_oflags, args[sc->offset]), fp); 1942 break; 1943 case CloudABISDFlags: 1944 fputs(xlookup_bits(cloudabi_sdflags, args[sc->offset]), fp); 1945 break; 1946 case CloudABISignal: 1947 fputs(xlookup(cloudabi_signal, args[sc->offset]), fp); 1948 break; 1949 case CloudABISockStat: { 1950 cloudabi_sockstat_t ss; 1951 if (get_struct(pid, (void *)args[sc->offset], &ss, sizeof(ss)) 1952 != -1) { 1953 fprintf(fp, "{ %s, ", xlookup( 1954 cloudabi_sa_family, ss.ss_sockname.sa_family)); 1955 fprintf(fp, "%s, ", xlookup( 1956 cloudabi_sa_family, ss.ss_peername.sa_family)); 1957 fprintf(fp, "%s, ", xlookup( 1958 cloudabi_errno, ss.ss_error)); 1959 fprintf(fp, "%s }", xlookup_bits( 1960 cloudabi_ssstate, ss.ss_state)); 1961 } else 1962 fprintf(fp, "0x%lx", args[sc->offset]); 1963 break; 1964 } 1965 case CloudABISSFlags: 1966 fputs(xlookup_bits(cloudabi_ssflags, args[sc->offset]), fp); 1967 break; 1968 case CloudABITimestamp: 1969 fprintf(fp, "%lu.%09lus", args[sc->offset] / 1000000000, 1970 args[sc->offset] % 1000000000); 1971 break; 1972 case CloudABIULFlags: 1973 fputs(xlookup_bits(cloudabi_ulflags, args[sc->offset]), fp); 1974 break; 1975 case CloudABIWhence: 1976 fputs(xlookup(cloudabi_whence, args[sc->offset]), fp); 1977 break; 1978 1979 default: 1980 errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK); 1981 } 1982 fclose(fp); 1983 return (tmp); 1984 } 1985 1986 /* 1987 * Print (to outfile) the system call and its arguments. 1988 */ 1989 void 1990 print_syscall(struct trussinfo *trussinfo) 1991 { 1992 struct threadinfo *t; 1993 const char *name; 1994 char **s_args; 1995 int i, len, nargs; 1996 1997 t = trussinfo->curthread; 1998 1999 name = t->cs.sc->name; 2000 nargs = t->cs.nargs; 2001 s_args = t->cs.s_args; 2002 2003 len = print_line_prefix(trussinfo); 2004 len += fprintf(trussinfo->outfile, "%s(", name); 2005 2006 for (i = 0; i < nargs; i++) { 2007 if (s_args[i] != NULL) 2008 len += fprintf(trussinfo->outfile, "%s", s_args[i]); 2009 else 2010 len += fprintf(trussinfo->outfile, 2011 "<missing argument>"); 2012 len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? 2013 "," : ""); 2014 } 2015 len += fprintf(trussinfo->outfile, ")"); 2016 for (i = 0; i < 6 - (len / 8); i++) 2017 fprintf(trussinfo->outfile, "\t"); 2018 } 2019 2020 void 2021 print_syscall_ret(struct trussinfo *trussinfo, int errorp, long *retval) 2022 { 2023 struct timespec timediff; 2024 struct threadinfo *t; 2025 struct syscall *sc; 2026 int error; 2027 2028 t = trussinfo->curthread; 2029 sc = t->cs.sc; 2030 if (trussinfo->flags & COUNTONLY) { 2031 timespecsubt(&t->after, &t->before, &timediff); 2032 timespecadd(&sc->time, &timediff, &sc->time); 2033 sc->ncalls++; 2034 if (errorp) 2035 sc->nerror++; 2036 return; 2037 } 2038 2039 print_syscall(trussinfo); 2040 fflush(trussinfo->outfile); 2041 2042 if (retval == NULL) { 2043 /* 2044 * This system call resulted in the current thread's exit, 2045 * so there is no return value or error to display. 2046 */ 2047 fprintf(trussinfo->outfile, "\n"); 2048 return; 2049 } 2050 2051 if (errorp) { 2052 error = sysdecode_abi_to_freebsd_errno(t->proc->abi->abi, 2053 retval[0]); 2054 fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval[0], 2055 error == INT_MAX ? "Unknown error" : strerror(error)); 2056 } 2057 #ifndef __LP64__ 2058 else if (sc->ret_type == 2) { 2059 off_t off; 2060 2061 #if _BYTE_ORDER == _LITTLE_ENDIAN 2062 off = (off_t)retval[1] << 32 | retval[0]; 2063 #else 2064 off = (off_t)retval[0] << 32 | retval[1]; 2065 #endif 2066 fprintf(trussinfo->outfile, " = %jd (0x%jx)\n", (intmax_t)off, 2067 (intmax_t)off); 2068 } 2069 #endif 2070 else 2071 fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval[0], 2072 retval[0]); 2073 } 2074 2075 void 2076 print_summary(struct trussinfo *trussinfo) 2077 { 2078 struct timespec total = {0, 0}; 2079 struct syscall *sc; 2080 int ncall, nerror; 2081 2082 fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n", 2083 "syscall", "seconds", "calls", "errors"); 2084 ncall = nerror = 0; 2085 STAILQ_FOREACH(sc, &syscalls, entries) 2086 if (sc->ncalls) { 2087 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", 2088 sc->name, (intmax_t)sc->time.tv_sec, 2089 sc->time.tv_nsec, sc->ncalls, sc->nerror); 2090 timespecadd(&total, &sc->time, &total); 2091 ncall += sc->ncalls; 2092 nerror += sc->nerror; 2093 } 2094 fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n", 2095 "", "-------------", "-------", "-------"); 2096 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", 2097 "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror); 2098 } 2099