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