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