xref: /freebsd/usr.bin/truss/syscalls.c (revision 27459358ed4d9060525ff02148eba0939ab4dc04)
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