xref: /illumos-gate/usr/src/cmd/ctfdump/ctfdump.c (revision a28480febf31f0e61debac062a55216a98a05a92)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2020 Joyent, Inc.
14  */
15 
16 /*
17  * Dump information about CTF containers.
18  */
19 
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <libctf.h>
23 #include <libgen.h>
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <stddef.h>
27 #include <sys/sysmacros.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/note.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <err.h>
36 
37 #define	MAX_NAMELEN (512)
38 
39 typedef enum ctfdump_arg {
40 	CTFDUMP_OBJECTS =	0x001,
41 	CTFDUMP_FUNCTIONS =	0x002,
42 	CTFDUMP_HEADER =	0x004,
43 	CTFDUMP_LABELS =	0x008,
44 	CTFDUMP_STRINGS =	0x010,
45 	CTFDUMP_STATS =		0x020,
46 	CTFDUMP_TYPES =		0x040,
47 	CTFDUMP_DEFAULT =	0x07f,
48 	CTFDUMP_OUTPUT =	0x080,
49 	CTFDUMP_SOURCE =	0x100,
50 } ctfdump_arg_t;
51 
52 typedef struct ctfdump_stat {
53 	ulong_t		cs_ndata;		/* number of data objects */
54 	ulong_t		cs_nfuncs;		/* number of functions */
55 	ulong_t		cs_nfuncargs;		/* number of function args */
56 	ulong_t		cs_nfuncmax;		/* largest number of args */
57 	ulong_t		cs_ntypes[CTF_K_MAX];	/* number of types */
58 	ulong_t		cs_nsmembs;		/* number of struct members */
59 	ulong_t		cs_nsmax;		/* largest number of members */
60 	ulong_t		cs_structsz;		/* sum of structures sizes */
61 	ulong_t		cs_sszmax;		/* largest structure */
62 	ulong_t		cs_numembs;		/* number of union members */
63 	ulong_t		cs_numax;		/* largest number of members */
64 	ulong_t		cs_unionsz;		/* sum of unions sizes */
65 	ulong_t		cs_uszmax;		/* largest union */
66 	ulong_t		cs_nemembs;		/* number of enum members */
67 	ulong_t		cs_nemax;		/* largest number of members */
68 	ulong_t		cs_nstrings;		/* number of strings */
69 	ulong_t		cs_strsz;		/* string size */
70 	ulong_t		cs_strmax;		/* longest string */
71 } ctfdump_stat_t;
72 
73 typedef struct {
74 	char ci_name[MAX_NAMELEN];
75 	ctf_id_t ci_id;
76 	ulong_t ci_symidx;
77 	ctf_funcinfo_t ci_funcinfo;
78 } ctf_idname_t;
79 
80 static ctf_idname_t *idnames;
81 static const char *g_progname;
82 static ctfdump_arg_t g_dump;
83 static ctf_file_t *g_fp;
84 static ctfdump_stat_t g_stats;
85 static ctf_id_t *g_fargc;
86 static int g_nfargc;
87 
88 static int g_exit = 0;
89 
90 static const char *ctfdump_fpenc[] = {
91 	NULL,
92 	"SINGLE",
93 	"DOUBLE",
94 	"COMPLEX",
95 	"DCOMPLEX",
96 	"LDCOMPLEX",
97 	"LDOUBLE",
98 	"INTERVAL",
99 	"DINTERVAL",
100 	"LDINTERVAL",
101 	"IMAGINARY",
102 	"DIMAGINARY",
103 	"LDIMAGINARY"
104 };
105 
106 /*
107  * When stats are requested, we have to go through everything. To make our lives
108  * easier, we'll just always allow the code to print everything out, but only
109  * output it if we have actually enabled that section.
110  */
111 static void
112 ctfdump_printf(ctfdump_arg_t arg, const char *fmt, ...)
113 {
114 	va_list ap;
115 
116 	if ((arg & g_dump) == 0)
117 		return;
118 
119 	va_start(ap, fmt);
120 	(void) vfprintf(stdout, fmt, ap);
121 	va_end(ap);
122 }
123 
124 static void __NORETURN
125 ctfdump_fatal(const char *fmt, ...)
126 {
127 	va_list ap;
128 
129 	(void) fprintf(stderr, "%s: ", g_progname);
130 	va_start(ap, fmt);
131 	(void) vfprintf(stderr, fmt, ap);
132 	va_end(ap);
133 
134 	exit(1);
135 }
136 
137 static void
138 ctfdump_usage(const char *fmt, ...)
139 {
140 	if (fmt != NULL) {
141 		va_list ap;
142 		(void) fprintf(stderr, "%s: ", g_progname);
143 		va_start(ap, fmt);
144 		(void) vfprintf(stderr, fmt, ap);
145 		va_end(ap);
146 	}
147 
148 	(void) fprintf(stderr, "Usage: %s [-cdfhlsSt] [-p parent] [-u outfile] "
149 	    "file\n"
150 	    "\n"
151 	    "\t-c  dump C-style output\n"
152 	    "\t-d  dump object data\n"
153 	    "\t-f  dump function data\n"
154 	    "\t-h  dump the CTF header\n"
155 	    "\t-l  dump the label table\n"
156 	    "\t-p  use parent to supply additional information\n"
157 	    "\t-s  dump the string table\n"
158 	    "\t-S  dump statistics about the CTF container\n"
159 	    "\t-t  dump type information\n"
160 	    "\t-u  dump uncompressed CTF data to outfile\n",
161 	    g_progname);
162 }
163 
164 static void
165 ctfdump_title(ctfdump_arg_t arg, const char *header)
166 {
167 	static const char line[] = "----------------------------------------"
168 	    "----------------------------------------";
169 	ctfdump_printf(arg, "\n- %s %.*s\n\n", header, (int)78 - strlen(header),
170 	    line);
171 }
172 
173 static int
174 ctfdump_objects_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
175 {
176 	_NOTE(ARGUNUSED(arg));
177 
178 	int len;
179 
180 	len = snprintf(NULL, 0, "  [%lu] %ld", g_stats.cs_ndata, id);
181 	ctfdump_printf(CTFDUMP_OBJECTS, "  [%lu] %ld %*s%s (%lu)\n",
182 	    g_stats.cs_ndata, id, MAX(15 - len, 0), "", name, symidx);
183 	g_stats.cs_ndata++;
184 	return (0);
185 }
186 
187 static void
188 ctfdump_objects(void)
189 {
190 	ctfdump_title(CTFDUMP_OBJECTS, "Data Objects");
191 	if (ctf_object_iter(g_fp, ctfdump_objects_cb, NULL) == CTF_ERR) {
192 		warnx("failed to dump objects: %s",
193 		    ctf_errmsg(ctf_errno(g_fp)));
194 		g_exit = 1;
195 	}
196 }
197 
198 static void
199 ctfdump_fargs_grow(int nargs)
200 {
201 	if (g_nfargc < nargs) {
202 		g_fargc = realloc(g_fargc, sizeof (ctf_id_t) * nargs);
203 		if (g_fargc == NULL)
204 			ctfdump_fatal("failed to get memory for %d "
205 			    "ctf_id_t's\n", nargs);
206 		g_nfargc = nargs;
207 	}
208 }
209 
210 static int
211 ctfdump_functions_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *ctc,
212     void *arg)
213 {
214 	_NOTE(ARGUNUSED(arg));
215 	int i;
216 
217 	if (ctc->ctc_argc != 0) {
218 		ctfdump_fargs_grow(ctc->ctc_argc);
219 		if (ctf_func_args(g_fp, symidx, g_nfargc, g_fargc) == CTF_ERR)
220 			ctfdump_fatal("failed to get arguments for function "
221 			    "%s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
222 	}
223 
224 	ctfdump_printf(CTFDUMP_FUNCTIONS,
225 	    "  [%lu] %s (%lu) returns: %ld args: (", g_stats.cs_nfuncs, name,
226 	    symidx, ctc->ctc_return);
227 	for (i = 0; i < ctc->ctc_argc; i++)
228 		ctfdump_printf(CTFDUMP_FUNCTIONS, "%ld%s", g_fargc[i],
229 		    i + 1 == ctc->ctc_argc ? "" : ", ");
230 	if (ctc->ctc_flags & CTF_FUNC_VARARG)
231 		ctfdump_printf(CTFDUMP_FUNCTIONS, "%s...",
232 		    ctc->ctc_argc == 0 ? "" : ", ");
233 	ctfdump_printf(CTFDUMP_FUNCTIONS, ")\n");
234 
235 	g_stats.cs_nfuncs++;
236 	g_stats.cs_nfuncargs += ctc->ctc_argc;
237 	g_stats.cs_nfuncmax = MAX(ctc->ctc_argc, g_stats.cs_nfuncmax);
238 
239 	return (0);
240 }
241 
242 static void
243 ctfdump_functions(void)
244 {
245 	ctfdump_title(CTFDUMP_FUNCTIONS, "Functions");
246 
247 	if (ctf_function_iter(g_fp, ctfdump_functions_cb, NULL) == CTF_ERR) {
248 		warnx("failed to dump functions: %s",
249 		    ctf_errmsg(ctf_errno(g_fp)));
250 		g_exit = 1;
251 	}
252 }
253 
254 static void
255 ctfdump_header(void)
256 {
257 	const ctf_header_t *hp;
258 	const char *parname, *parlabel;
259 
260 	ctfdump_title(CTFDUMP_HEADER, "CTF Header");
261 	ctf_dataptr(g_fp, (const void **)&hp, NULL);
262 	ctfdump_printf(CTFDUMP_HEADER, "  cth_magic    = 0x%04x\n",
263 	    hp->cth_magic);
264 	ctfdump_printf(CTFDUMP_HEADER, "  cth_version  = %u\n",
265 	    hp->cth_version);
266 	ctfdump_printf(CTFDUMP_HEADER, "  cth_flags    = 0x%02x\n",
267 	    ctf_flags(g_fp));
268 	parname = ctf_parent_name(g_fp);
269 	parlabel = ctf_parent_label(g_fp);
270 	ctfdump_printf(CTFDUMP_HEADER, "  cth_parlabel = %s\n",
271 	    parlabel == NULL ? "(anon)" : parlabel);
272 	ctfdump_printf(CTFDUMP_HEADER, "  cth_parname  = %s\n",
273 	    parname == NULL ? "(anon)" : parname);
274 	ctfdump_printf(CTFDUMP_HEADER, "  cth_lbloff   = %u\n",
275 	    hp->cth_lbloff);
276 	ctfdump_printf(CTFDUMP_HEADER, "  cth_objtoff  = %u\n",
277 	    hp->cth_objtoff);
278 	ctfdump_printf(CTFDUMP_HEADER, "  cth_funcoff  = %u\n",
279 	    hp->cth_funcoff);
280 	ctfdump_printf(CTFDUMP_HEADER, "  cth_typeoff  = %u\n",
281 	    hp->cth_typeoff);
282 	ctfdump_printf(CTFDUMP_HEADER, "  cth_stroff   = %u\n",
283 	    hp->cth_stroff);
284 	ctfdump_printf(CTFDUMP_HEADER, "  cth_strlen   = %u\n",
285 	    hp->cth_strlen);
286 }
287 
288 static int
289 ctfdump_labels_cb(const char *name, const ctf_lblinfo_t *li, void *arg)
290 {
291 	_NOTE(ARGUNUSED(arg));
292 	ctfdump_printf(CTFDUMP_LABELS, "  %5ld %s\n", li->ctb_typeidx, name);
293 	return (0);
294 }
295 
296 static void
297 ctfdump_labels(void)
298 {
299 	ctfdump_title(CTFDUMP_LABELS, "Label Table");
300 	if (ctf_label_iter(g_fp, ctfdump_labels_cb, NULL) == CTF_ERR) {
301 		warnx("failed to dump labels: %s",
302 		    ctf_errmsg(ctf_errno(g_fp)));
303 		g_exit = 1;
304 	}
305 }
306 
307 static int
308 ctfdump_strings_cb(const char *s, void *arg)
309 {
310 	size_t len = strlen(s) + 1;
311 	ulong_t *stroff = arg;
312 	ctfdump_printf(CTFDUMP_STRINGS, "  [%lu] %s\n", *stroff,
313 	    *s == '\0' ? "\\0" : s);
314 	*stroff = *stroff + len;
315 	g_stats.cs_nstrings++;
316 	g_stats.cs_strsz += len;
317 	g_stats.cs_strmax = MAX(g_stats.cs_strmax, len);
318 	return (0);
319 }
320 
321 static void
322 ctfdump_strings(void)
323 {
324 	ulong_t stroff = 0;
325 
326 	ctfdump_title(CTFDUMP_STRINGS, "String Table");
327 	if (ctf_string_iter(g_fp, ctfdump_strings_cb, &stroff) == CTF_ERR) {
328 		warnx("failed to dump strings: %s",
329 		    ctf_errmsg(ctf_errno(g_fp)));
330 		g_exit = 1;
331 	}
332 }
333 
334 static void
335 ctfdump_stat_int(const char *name, ulong_t value)
336 {
337 	ctfdump_printf(CTFDUMP_STATS, "  %-36s= %lu\n", name, value);
338 }
339 
340 static void
341 ctfdump_stat_fp(const char *name, float value)
342 {
343 	ctfdump_printf(CTFDUMP_STATS, "  %-36s= %.2f\n", name, value);
344 }
345 
346 static void
347 ctfdump_stats(void)
348 {
349 	int i;
350 	ulong_t sum;
351 
352 	ctfdump_title(CTFDUMP_STATS, "CTF Statistics");
353 
354 	ctfdump_stat_int("total number of data objects", g_stats.cs_ndata);
355 	ctfdump_printf(CTFDUMP_STATS, "\n");
356 	ctfdump_stat_int("total number of functions", g_stats.cs_nfuncs);
357 	ctfdump_stat_int("total number of function arguments",
358 	    g_stats.cs_nfuncargs);
359 	ctfdump_stat_int("maximum argument list length", g_stats.cs_nfuncmax);
360 	if (g_stats.cs_nfuncs != 0)
361 		ctfdump_stat_fp("average argument list length",
362 		    (float)g_stats.cs_nfuncargs / (float)g_stats.cs_nfuncs);
363 	ctfdump_printf(CTFDUMP_STATS, "\n");
364 
365 	sum = 0;
366 	for (i = 0; i < CTF_K_MAX; i++)
367 		sum += g_stats.cs_ntypes[i];
368 	ctfdump_stat_int("total number of types", sum);
369 	ctfdump_stat_int("total number of integers",
370 	    g_stats.cs_ntypes[CTF_K_INTEGER]);
371 	ctfdump_stat_int("total number of floats",
372 	    g_stats.cs_ntypes[CTF_K_FLOAT]);
373 	ctfdump_stat_int("total number of pointers",
374 	    g_stats.cs_ntypes[CTF_K_POINTER]);
375 	ctfdump_stat_int("total number of arrays",
376 	    g_stats.cs_ntypes[CTF_K_ARRAY]);
377 	ctfdump_stat_int("total number of func types",
378 	    g_stats.cs_ntypes[CTF_K_FUNCTION]);
379 	ctfdump_stat_int("total number of structs",
380 	    g_stats.cs_ntypes[CTF_K_STRUCT]);
381 	ctfdump_stat_int("total number of unions",
382 	    g_stats.cs_ntypes[CTF_K_UNION]);
383 	ctfdump_stat_int("total number of enums",
384 	    g_stats.cs_ntypes[CTF_K_ENUM]);
385 	ctfdump_stat_int("total number of forward tags",
386 	    g_stats.cs_ntypes[CTF_K_FORWARD]);
387 	ctfdump_stat_int("total number of typedefs",
388 	    g_stats.cs_ntypes[CTF_K_TYPEDEF]);
389 	ctfdump_stat_int("total number of volatile types",
390 	    g_stats.cs_ntypes[CTF_K_VOLATILE]);
391 	ctfdump_stat_int("total number of const types",
392 	    g_stats.cs_ntypes[CTF_K_CONST]);
393 	ctfdump_stat_int("total number of restrict types",
394 	    g_stats.cs_ntypes[CTF_K_RESTRICT]);
395 	ctfdump_stat_int("total number of unknowns (holes)",
396 	    g_stats.cs_ntypes[CTF_K_UNKNOWN]);
397 
398 	ctfdump_printf(CTFDUMP_STATS, "\n");
399 	ctfdump_stat_int("total number of struct members", g_stats.cs_nsmembs);
400 	ctfdump_stat_int("maximum number of struct members", g_stats.cs_nsmax);
401 	ctfdump_stat_int("total size of all structs", g_stats.cs_structsz);
402 	ctfdump_stat_int("maximum size of a struct", g_stats.cs_sszmax);
403 	if (g_stats.cs_ntypes[CTF_K_STRUCT] != 0) {
404 		ctfdump_stat_fp("average number of struct members",
405 		    (float)g_stats.cs_nsmembs /
406 		    (float)g_stats.cs_ntypes[CTF_K_STRUCT]);
407 		ctfdump_stat_fp("average size of a struct",
408 		    (float)g_stats.cs_structsz /
409 		    (float)g_stats.cs_ntypes[CTF_K_STRUCT]);
410 	}
411 	ctfdump_printf(CTFDUMP_STATS, "\n");
412 	ctfdump_stat_int("total number of union members", g_stats.cs_numembs);
413 	ctfdump_stat_int("maximum number of union members", g_stats.cs_numax);
414 	ctfdump_stat_int("total size of all unions", g_stats.cs_unionsz);
415 	ctfdump_stat_int("maximum size of a union", g_stats.cs_uszmax);
416 	if (g_stats.cs_ntypes[CTF_K_UNION] != 0) {
417 		ctfdump_stat_fp("average number of union members",
418 		    (float)g_stats.cs_numembs /
419 		    (float)g_stats.cs_ntypes[CTF_K_UNION]);
420 		ctfdump_stat_fp("average size of a union",
421 		    (float)g_stats.cs_unionsz /
422 		    (float)g_stats.cs_ntypes[CTF_K_UNION]);
423 	}
424 	ctfdump_printf(CTFDUMP_STATS, "\n");
425 
426 	ctfdump_stat_int("total number of enum members", g_stats.cs_nemembs);
427 	ctfdump_stat_int("maximum number of enum members", g_stats.cs_nemax);
428 	if (g_stats.cs_ntypes[CTF_K_ENUM] != 0) {
429 		ctfdump_stat_fp("average number of enum members",
430 		    (float)g_stats.cs_nemembs /
431 		    (float)g_stats.cs_ntypes[CTF_K_ENUM]);
432 	}
433 	ctfdump_printf(CTFDUMP_STATS, "\n");
434 
435 	ctfdump_stat_int("total number of strings", g_stats.cs_nstrings);
436 	ctfdump_stat_int("bytes of string data", g_stats.cs_strsz);
437 	ctfdump_stat_int("maximum string length", g_stats.cs_strmax);
438 	if (g_stats.cs_nstrings != 0)
439 		ctfdump_stat_fp("average string length",
440 		    (float)g_stats.cs_strsz / (float)g_stats.cs_nstrings);
441 	ctfdump_printf(CTFDUMP_STATS, "\n");
442 }
443 
444 static void
445 ctfdump_intenc_name(ctf_encoding_t *cte, char *buf, int len)
446 {
447 	int off = 0;
448 	boolean_t space = B_FALSE;
449 
450 	if (cte->cte_format == 0 || (cte->cte_format &
451 	    ~(CTF_INT_SIGNED | CTF_INT_CHAR | CTF_INT_BOOL |
452 	    CTF_INT_VARARGS)) != 0) {
453 		(void) snprintf(buf, len, "0x%x", cte->cte_format);
454 		return;
455 	}
456 
457 	if (cte->cte_format & CTF_INT_SIGNED) {
458 		off += snprintf(buf + off, MAX(len - off, 0), "%sSIGNED",
459 		    space == B_TRUE ? " " : "");
460 		space = B_TRUE;
461 	}
462 
463 	if (cte->cte_format & CTF_INT_CHAR) {
464 		off += snprintf(buf + off, MAX(len - off, 0), "%sCHAR",
465 		    space == B_TRUE ? " " : "");
466 		space = B_TRUE;
467 	}
468 
469 	if (cte->cte_format & CTF_INT_BOOL) {
470 		off += snprintf(buf + off, MAX(len - off, 0), "%sBOOL",
471 		    space == B_TRUE ? " " : "");
472 		space = B_TRUE;
473 	}
474 
475 	if (cte->cte_format & CTF_INT_VARARGS) {
476 		off += snprintf(buf + off, MAX(len - off, 0), "%sVARARGS",
477 		    space == B_TRUE ? " " : "");
478 		space = B_TRUE;
479 	}
480 }
481 
482 static int
483 ctfdump_member_cb(const char *member, ctf_id_t type, ulong_t off, void *arg)
484 {
485 	int *count = arg;
486 	ctfdump_printf(CTFDUMP_TYPES, "\t%s type=%ld off=%lu\n", member, type,
487 	    off);
488 	*count = *count + 1;
489 	return (0);
490 }
491 
492 static int
493 ctfdump_enum_cb(const char *name, int value, void *arg)
494 {
495 	int *count = arg;
496 	ctfdump_printf(CTFDUMP_TYPES, "\t%s = %d\n", name, value);
497 	*count = *count + 1;
498 	return (0);
499 }
500 
501 static int
502 ctfdump_types_cb(ctf_id_t id, boolean_t root, void *arg)
503 {
504 	_NOTE(ARGUNUSED(arg));
505 	int kind, i, count;
506 	ctf_id_t ref;
507 	char name[MAX_NAMELEN], ienc[128];
508 	const char *encn;
509 	ctf_funcinfo_t ctc;
510 	ctf_arinfo_t ar;
511 	ctf_encoding_t cte;
512 	ssize_t size;
513 
514 	if ((kind = ctf_type_kind(g_fp, id)) == CTF_ERR)
515 		ctfdump_fatal("encountered malformed ctf, type %s does not "
516 		    "have a kind: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
517 
518 	if (ctf_type_name(g_fp, id, name, sizeof (name)) == NULL) {
519 		if (ctf_errno(g_fp) != ECTF_NOPARENT)
520 			ctfdump_fatal("type %ld missing name: %s\n", id,
521 			    ctf_errmsg(ctf_errno(g_fp)));
522 		(void) snprintf(name, sizeof (name), "(unknown %s)",
523 		    ctf_kind_name(g_fp, kind));
524 	}
525 
526 	g_stats.cs_ntypes[kind]++;
527 	if (root == B_TRUE)
528 		ctfdump_printf(CTFDUMP_TYPES, "  <%ld> ", id);
529 	else
530 		ctfdump_printf(CTFDUMP_TYPES, "  [%ld] ", id);
531 
532 	switch (kind) {
533 	case CTF_K_UNKNOWN:
534 		break;
535 	case CTF_K_INTEGER:
536 		if (ctf_type_encoding(g_fp, id, &cte) == CTF_ERR)
537 			ctfdump_fatal("failed to get encoding information "
538 			    "for %s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
539 		ctfdump_intenc_name(&cte, ienc, sizeof (ienc));
540 		ctfdump_printf(CTFDUMP_TYPES,
541 		    "%s encoding=%s offset=%u bits=%u",
542 		    name, ienc, cte.cte_offset, cte.cte_bits);
543 		break;
544 	case CTF_K_FLOAT:
545 		if (ctf_type_encoding(g_fp, id, &cte) == CTF_ERR)
546 			ctfdump_fatal("failed to get encoding information "
547 			    "for %s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
548 		if (cte.cte_format < 1 || cte.cte_format > 12)
549 			encn = "unknown";
550 		else
551 			encn = ctfdump_fpenc[cte.cte_format];
552 		ctfdump_printf(CTFDUMP_TYPES, "%s encoding=%s offset=%u "
553 		    "bits=%u", name, encn, cte.cte_offset, cte.cte_bits);
554 		break;
555 	case CTF_K_POINTER:
556 		if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
557 			ctfdump_fatal("failed to get reference type for %s: "
558 			    "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
559 		ctfdump_printf(CTFDUMP_TYPES, "%s refers to %ld", name,
560 		    ref);
561 		break;
562 	case CTF_K_ARRAY:
563 		if (ctf_array_info(g_fp, id, &ar) == CTF_ERR)
564 			ctfdump_fatal("failed to get array information for "
565 			    "%s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
566 		ctfdump_printf(CTFDUMP_TYPES, "%s contents: %ld, index: %ld",
567 		    name, ar.ctr_contents, ar.ctr_index);
568 		break;
569 	case CTF_K_FUNCTION:
570 		if (ctf_func_info_by_id(g_fp, id, &ctc) == CTF_ERR)
571 			ctfdump_fatal("failed to get function info for %s: "
572 			    "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
573 		if (ctc.ctc_argc > 0) {
574 			ctfdump_fargs_grow(ctc.ctc_argc);
575 			if (ctf_func_args_by_id(g_fp, id, g_nfargc, g_fargc) ==
576 			    CTF_ERR)
577 				ctfdump_fatal("failed to get function "
578 				    "arguments for %s: %s\n", name,
579 				    ctf_errmsg(ctf_errno(g_fp)));
580 		}
581 		ctfdump_printf(CTFDUMP_TYPES,
582 		    "%s returns: %ld args: (", name, ctc.ctc_return);
583 		for (i = 0; i < ctc.ctc_argc; i++) {
584 			ctfdump_printf(CTFDUMP_TYPES, "%ld%s", g_fargc[i],
585 			    i + 1 == ctc.ctc_argc ? "" : ", ");
586 		}
587 		if (ctc.ctc_flags & CTF_FUNC_VARARG)
588 			ctfdump_printf(CTFDUMP_TYPES, "%s...",
589 			    ctc.ctc_argc == 0 ? "" : ", ");
590 		ctfdump_printf(CTFDUMP_TYPES, ")");
591 		break;
592 	case CTF_K_STRUCT:
593 	case CTF_K_UNION:
594 		size = ctf_type_size(g_fp, id);
595 		if (size == CTF_ERR)
596 			ctfdump_fatal("failed to get size of %s: %s\n", name,
597 			    ctf_errmsg(ctf_errno(g_fp)));
598 		ctfdump_printf(CTFDUMP_TYPES, "%s (%zd bytes)\n", name, size);
599 		count = 0;
600 		if (ctf_member_iter(g_fp, id, ctfdump_member_cb, &count) != 0)
601 			ctfdump_fatal("failed to iterate members of %s: %s\n",
602 			    name, ctf_errmsg(ctf_errno(g_fp)));
603 		if (kind == CTF_K_STRUCT) {
604 			g_stats.cs_nsmembs += count;
605 			g_stats.cs_nsmax = MAX(count, g_stats.cs_nsmax);
606 			g_stats.cs_structsz += size;
607 			g_stats.cs_sszmax = MAX(size, g_stats.cs_sszmax);
608 		} else {
609 			g_stats.cs_numembs += count;
610 			g_stats.cs_numax = MAX(count, g_stats.cs_numax);
611 			g_stats.cs_unionsz += size;
612 			g_stats.cs_uszmax = MAX(count, g_stats.cs_uszmax);
613 		}
614 		break;
615 	case CTF_K_ENUM:
616 		size = ctf_type_size(g_fp, id);
617 
618 		/* Only the oddest enums are worth reporting on size. */
619 		if (size != CTF_ERR && size != sizeof (int)) {
620 			ctfdump_printf(CTFDUMP_TYPES, "%s (%zd bytes)\n",
621 			    name, size);
622 		} else {
623 			ctfdump_printf(CTFDUMP_TYPES, "%s\n", name);
624 		}
625 
626 		count = 0;
627 		if (ctf_enum_iter(g_fp, id, ctfdump_enum_cb, &count) != 0)
628 			ctfdump_fatal("failed to iterate enumerators of %s: "
629 			    "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
630 		g_stats.cs_nemembs += count;
631 		g_stats.cs_nemax = MAX(g_stats.cs_nemax, count);
632 		break;
633 	case CTF_K_FORWARD:
634 		ctfdump_printf(CTFDUMP_TYPES, "forward %s\n", name);
635 		break;
636 	case CTF_K_TYPEDEF:
637 		if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
638 			ctfdump_fatal("failed to get reference type for %s: "
639 			    "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
640 		ctfdump_printf(CTFDUMP_TYPES, "typedef %s refers to %ld", name,
641 		    ref);
642 		break;
643 	case CTF_K_VOLATILE:
644 		if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
645 			ctfdump_fatal("failed to get reference type for %s: "
646 			    "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
647 		ctfdump_printf(CTFDUMP_TYPES, "%s refers to %ld", name,
648 		    ref);
649 		break;
650 	case CTF_K_CONST:
651 		if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
652 			ctfdump_fatal("failed to get reference type for %s: "
653 			    "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
654 		ctfdump_printf(CTFDUMP_TYPES, "%s refers to %ld", name,
655 		    ref);
656 		break;
657 	case CTF_K_RESTRICT:
658 		if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
659 			ctfdump_fatal("failed to get reference type for %s: "
660 			    "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
661 		ctfdump_printf(CTFDUMP_TYPES, "%s refers to %ld", name,
662 		    ref);
663 		break;
664 	default:
665 		ctfdump_fatal("encountered unknown kind for type %s: %d\n",
666 		    name, kind);
667 	}
668 
669 	ctfdump_printf(CTFDUMP_TYPES, "\n");
670 
671 	return (0);
672 }
673 
674 static void
675 ctfdump_types(void)
676 {
677 	ctfdump_title(CTFDUMP_TYPES, "Types");
678 
679 	if (ctf_type_iter(g_fp, B_TRUE, ctfdump_types_cb, NULL) == CTF_ERR) {
680 		warnx("failed to dump types: %s",
681 		    ctf_errmsg(ctf_errno(g_fp)));
682 		g_exit = 1;
683 	}
684 }
685 
686 /*
687  * C-style output. This is designed mainly for comparison purposes, and doesn't
688  * produce directly valid C:
689  *
690  * - the declarations are sorted alphabetically not semantically
691  * - anonymous enums without other users are elided (e.g. IDCS_PROBE_SENT)
692  * - doubly-pointed-to functions are wrong (e.g. in kiconv_ops_t)
693  * - anon unions declared within SOUs aren't expanded
694  * - function arguments aren't expanded recursively
695  */
696 
697 static const char *
698 ctfsrc_refname(ctf_id_t id, char *buf, size_t bufsize)
699 {
700 	ctf_id_t ref;
701 
702 	if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR) {
703 		ctfdump_fatal("failed to get reference type for %ld: "
704 		    "%s\n", id, ctf_errmsg(ctf_errno(g_fp)));
705 	}
706 
707 	return (ctf_type_name(g_fp, ref, buf, bufsize));
708 }
709 
710 static int
711 ctfsrc_member_cb(const char *member, ctf_id_t type, ulong_t off, void *arg)
712 {
713 	_NOTE(ARGUNUSED(arg));
714 	char name[MAX_NAMELEN];
715 
716 	if (ctf_type_cname(g_fp, type, name, sizeof (name), member) == NULL) {
717 		if (ctf_errno(g_fp) != ECTF_NOPARENT) {
718 			ctfdump_fatal("type %ld missing name: %s\n", type,
719 			    ctf_errmsg(ctf_errno(g_fp)));
720 		}
721 
722 		(void) snprintf(name, sizeof (name), "unknown_t %s", member);
723 	}
724 
725 	/*
726 	 * A byte offset is friendlier, but we'll print bits too if it's not
727 	 * aligned (i.e. a bitfield).
728 	 */
729 	if (off % NBBY != 0) {
730 		printf("\t%s; /* offset: %lu bytes (%lu bits) */\n",
731 		    name, off / NBBY, off);
732 	} else {
733 		printf("\t%s; /* offset: %lu bytes */\n",
734 		    name, off / NBBY);
735 	}
736 	return (0);
737 }
738 
739 static int
740 ctfsrc_enum_cb(const char *name, int value, void *arg)
741 {
742 	_NOTE(ARGUNUSED(arg));
743 	printf("\t%s = %d,\n", name, value);
744 	return (0);
745 }
746 
747 static int
748 is_anon_refname(const char *refname)
749 {
750 	return ((strcmp(refname, "struct ") == 0 ||
751 	    strcmp(refname, "union ") == 0 ||
752 	    strcmp(refname, "enum ") == 0));
753 }
754 
755 static int
756 ctfsrc_collect_types_cb(ctf_id_t id, boolean_t root, void *arg)
757 {
758 	_NOTE(ARGUNUSED(root, arg));
759 	(void) ctf_type_name(g_fp, id, idnames[id].ci_name,
760 	    sizeof (idnames[id].ci_name));
761 	idnames[id].ci_id = id;
762 	return (0);
763 }
764 
765 static void
766 ctfsrc_type(ctf_id_t id, const char *name)
767 {
768 	char refname[MAX_NAMELEN] = "unknown_t";
769 	ctf_id_t ref;
770 	ssize_t size;
771 	int kind;
772 
773 	if ((kind = ctf_type_kind(g_fp, id)) == CTF_ERR) {
774 		ctfdump_fatal("encountered malformed ctf, type %s does not "
775 		    "have a kind: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
776 	}
777 
778 	switch (kind) {
779 	case CTF_K_STRUCT:
780 	case CTF_K_UNION:
781 		/*
782 		 * Delay printing anonymous SOUs; a later typedef will usually
783 		 * pick them up.
784 		 */
785 		if (is_anon_refname(name))
786 			break;
787 
788 		if ((size = ctf_type_size(g_fp, id)) == CTF_ERR) {
789 			ctfdump_fatal("failed to get size of %s: %s\n", name,
790 			    ctf_errmsg(ctf_errno(g_fp)));
791 		}
792 
793 		printf("%s { /* 0x%x bytes */\n", name, size);
794 
795 		if (ctf_member_iter(g_fp, id, ctfsrc_member_cb, NULL) != 0) {
796 			ctfdump_fatal("failed to iterate members of %s: %s\n",
797 			    name, ctf_errmsg(ctf_errno(g_fp)));
798 		}
799 
800 		printf("};\n\n");
801 		break;
802 	case CTF_K_ENUM:
803 		/*
804 		 * This will throw away any anon enum that isn't followed by a
805 		 * typedef...
806 		 */
807 		if (is_anon_refname(name))
808 			break;
809 
810 		printf("%s {\n", name);
811 
812 		if (ctf_enum_iter(g_fp, id, ctfsrc_enum_cb, NULL) != 0) {
813 			ctfdump_fatal("failed to iterate enumerators of %s: "
814 			    "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
815 		}
816 
817 		size = ctf_type_size(g_fp, id);
818 
819 		/* Only the oddest enums are worth reporting on size. */
820 		if (size != CTF_ERR && size != sizeof (int)) {
821 			printf("} /* 0x%x bytes */;\n\n", size);
822 		} else {
823 			printf("};\n\n");
824 		}
825 		break;
826 	case CTF_K_TYPEDEF:
827 		/*
828 		 * If this fails, it's probably because the referent type is in
829 		 * a parent container that was not supplied via -p.
830 		 */
831 		if (ctfsrc_refname(id, refname, sizeof (refname)) == NULL) {
832 			printf("typedef %s %s;\n\n", refname, name);
833 			break;
834 		}
835 
836 		if (!is_anon_refname(refname)) {
837 			(void) ctf_type_cname(g_fp,
838 			    ctf_type_reference(g_fp, id), refname,
839 			    sizeof (refname), name);
840 
841 			printf("typedef %s;\n\n", refname);
842 			break;
843 		}
844 
845 		ref = ctf_type_reference(g_fp, id);
846 
847 		if (ctf_type_kind(g_fp, ref) == CTF_K_ENUM) {
848 			printf("typedef enum {\n");
849 
850 			if (ctf_enum_iter(g_fp, ref,
851 			    ctfsrc_enum_cb, NULL) != 0) {
852 				ctfdump_fatal("failed to iterate enumerators "
853 				    "of %s: %s\n", refname,
854 				    ctf_errmsg(ctf_errno(g_fp)));
855 			}
856 
857 			printf("} %s;\n\n", name);
858 		} else {
859 			if ((size = ctf_type_size(g_fp, ref)) == CTF_ERR) {
860 				ctfdump_fatal("failed to get size of %s: %s\n",
861 				    refname, ctf_errmsg(ctf_errno(g_fp)));
862 			}
863 
864 			printf("typedef %s{ /* 0x%zx bytes */\n",
865 			    refname, size);
866 
867 			if (ctf_member_iter(g_fp, ref,
868 			    ctfsrc_member_cb, NULL) != 0) {
869 				ctfdump_fatal("failed to iterate members "
870 				    "of %s: %s\n", refname,
871 				    ctf_errmsg(ctf_errno(g_fp)));
872 			}
873 
874 			printf("} %s;\n\n", name);
875 		}
876 
877 		break;
878 	case CTF_K_FORWARD:
879 		printf("%s;\n\n", name);
880 		break;
881 	case CTF_K_UNKNOWN:
882 	case CTF_K_INTEGER:
883 	case CTF_K_FLOAT:
884 	case CTF_K_POINTER:
885 	case CTF_K_ARRAY:
886 	case CTF_K_FUNCTION:
887 	case CTF_K_VOLATILE:
888 	case CTF_K_CONST:
889 	case CTF_K_RESTRICT:
890 		break;
891 	default:
892 		ctfdump_fatal("encountered unknown kind for type %s: %d\n",
893 		    name, kind);
894 		break;
895 	}
896 }
897 
898 static int
899 ctfsrc_collect_objects_cb(const char *name, ctf_id_t id,
900     ulong_t symidx, void *arg)
901 {
902 	size_t *count = arg;
903 
904 	/* local static vars can have an unknown ID */
905 	if (id == 0)
906 		return (0);
907 
908 	(void) strlcpy(idnames[*count].ci_name, name,
909 	    sizeof (idnames[*count].ci_name));
910 	idnames[*count].ci_id = id;
911 	idnames[*count].ci_symidx = symidx;
912 	*count = *count + 1;
913 	return (0);
914 }
915 
916 static void
917 ctfsrc_object(ctf_id_t id, const char *name)
918 {
919 	char tname[MAX_NAMELEN];
920 
921 	if (ctf_type_cname(g_fp, id, tname, sizeof (tname), name) == NULL) {
922 		if (ctf_errno(g_fp) != ECTF_NOPARENT) {
923 			ctfdump_fatal("type %ld missing name: %s\n", id,
924 			    ctf_errmsg(ctf_errno(g_fp)));
925 		}
926 		(void) snprintf(tname, sizeof (tname), "unknown_t %s", name);
927 	}
928 
929 	printf("extern %s;\n", tname);
930 }
931 
932 static int
933 ctfsrc_collect_functions_cb(const char *name, ulong_t symidx,
934     ctf_funcinfo_t *ctc, void *arg)
935 {
936 	size_t *count = arg;
937 
938 	(void) strlcpy(idnames[*count].ci_name, name,
939 	    sizeof (idnames[*count].ci_name));
940 	bcopy(ctc, &idnames[*count].ci_funcinfo, sizeof (*ctc));
941 	idnames[*count].ci_id = 0;
942 	idnames[*count].ci_symidx = symidx;
943 	*count = *count + 1;
944 	return (0);
945 }
946 
947 static void
948 ctfsrc_function(ctf_idname_t *idn)
949 {
950 	ctf_funcinfo_t *cfi = &idn->ci_funcinfo;
951 	char name[MAX_NAMELEN] = "unknown_t";
952 
953 	(void) ctf_type_name(g_fp, cfi->ctc_return, name, sizeof (name));
954 
955 	printf("extern %s %s(", name, idn->ci_name);
956 
957 	if (cfi->ctc_argc != 0) {
958 		ctfdump_fargs_grow(cfi->ctc_argc);
959 		if (ctf_func_args(g_fp, idn->ci_symidx,
960 		    g_nfargc, g_fargc) == CTF_ERR) {
961 			ctfdump_fatal("failed to get arguments for function "
962 			    "%s: %s\n", idn->ci_name,
963 			    ctf_errmsg(ctf_errno(g_fp)));
964 		}
965 
966 		for (size_t i = 0; i < cfi->ctc_argc; i++) {
967 			ctf_id_t aid = g_fargc[i];
968 
969 			(void) strlcpy(name, "unknown_t", sizeof (name));
970 
971 			(void) ctf_type_name(g_fp, aid, name, sizeof (name));
972 
973 			printf("%s%s", name,
974 			    i + 1 == cfi->ctc_argc ? "" : ", ");
975 		}
976 	} else {
977 		if (!(cfi->ctc_flags & CTF_FUNC_VARARG))
978 			printf("void");
979 	}
980 
981 	if (cfi->ctc_flags & CTF_FUNC_VARARG)
982 		printf("%s...", cfi->ctc_argc == 0 ? "" : ", ");
983 
984 	printf(");\n");
985 }
986 
987 static int
988 idname_compare(const void *lhs, const void *rhs)
989 {
990 	int ret;
991 	char lname[MAX_NAMELEN] = {0};
992 	char rname[MAX_NAMELEN] = {0};
993 	const ctf_idname_t *l = lhs;
994 	const ctf_idname_t *r = rhs;
995 	uint_t arity = 0;
996 
997 	if ((ret = strcmp(l->ci_name, r->ci_name)) != 0)
998 		return (ret);
999 
1000 	/* If the names match, try arity */
1001 	if (l->ci_funcinfo.ctc_argc < r->ci_funcinfo.ctc_argc)
1002 		return (-1);
1003 	else if (l->ci_funcinfo.ctc_argc > r->ci_funcinfo.ctc_argc)
1004 		return (1);
1005 	else
1006 		arity = l->ci_funcinfo.ctc_argc;
1007 
1008 	/* If arity doesn't help, try return type */
1009 	(void) strlcpy(lname, "unknown_t", sizeof (lname));
1010 	(void) strlcpy(rname, "unknown_t", sizeof (rname));
1011 	(void) ctf_type_name(g_fp, l->ci_funcinfo.ctc_return, lname,
1012 	    sizeof (lname));
1013 	(void) ctf_type_name(g_fp, r->ci_funcinfo.ctc_return, rname,
1014 	    sizeof (rname));
1015 
1016 	if ((ret = strcmp(lname, rname)) != 0)
1017 		return (ret);
1018 
1019 	/* if return type doesn't help, try parameter types */
1020 	if (arity == 0)
1021 		return (0);
1022 
1023 	ctf_id_t *largs = calloc(arity, sizeof (ctf_id_t));
1024 	ctf_id_t *rargs = calloc(arity, sizeof (ctf_id_t));
1025 
1026 	if ((largs == NULL) || (rargs == NULL)) {
1027 		free(rargs);
1028 		free(largs);
1029 		ctfdump_fatal("failed to alloc argument ids for sorting: "
1030 		    " %s\n", strerror(errno));
1031 	}
1032 
1033 	if (ctf_func_args(g_fp, l->ci_symidx, arity, largs) == CTF_ERR) {
1034 		free(rargs);
1035 		free(largs);
1036 		ctfdump_fatal("failed to get arguments for function "
1037 		    "%s: %s\n", l->ci_name,
1038 		    ctf_errmsg(ctf_errno(g_fp)));
1039 	}
1040 
1041 	if (ctf_func_args(g_fp, r->ci_symidx, arity, rargs) == CTF_ERR) {
1042 		free(rargs);
1043 		free(largs);
1044 		ctfdump_fatal("failed to get arguments for function "
1045 		    "%s: %s\n", r->ci_name,
1046 		    ctf_errmsg(ctf_errno(g_fp)));
1047 	}
1048 
1049 	for (uint_t i = 0; i < arity; i++) {
1050 		(void) strlcpy(lname, "unknown_t", sizeof (lname));
1051 		(void) ctf_type_name(g_fp, largs[i], lname, sizeof (lname));
1052 
1053 		(void) strlcpy(rname, "unknown_t", sizeof (rname));
1054 		(void) ctf_type_name(g_fp, rargs[i], rname, sizeof (rname));
1055 
1056 		if ((ret = strcmp(lname, rname)) != 0) {
1057 			free(rargs);
1058 			free(largs);
1059 			return (ret);
1060 		}
1061 	}
1062 
1063 	free(rargs);
1064 	free(largs);
1065 	return (0);
1066 }
1067 
1068 static void
1069 ctfdump_source(void)
1070 {
1071 	ulong_t nr_syms = ctf_nr_syms(g_fp);
1072 	ctf_id_t max_id = ctf_max_id(g_fp);
1073 	size_t count = 0;
1074 
1075 	printf("/* Types */\n\n");
1076 
1077 	if ((idnames = calloc(max_id + 1, sizeof (idnames[0]))) == NULL) {
1078 		ctfdump_fatal("failed to alloc idnames: %s\n",
1079 		    strerror(errno));
1080 	}
1081 
1082 	/*
1083 	 * Prep for any unknown types (most likely, they exist in the parent,
1084 	 * but we weren't given the -p option).
1085 	 */
1086 	for (size_t i = 0; i <= max_id; i++) {
1087 		(void) strlcpy(idnames[i].ci_name, "unknown_t",
1088 		    sizeof (idnames[i].ci_name));
1089 	}
1090 
1091 	if (ctf_type_iter(g_fp, B_TRUE, ctfsrc_collect_types_cb,
1092 	    idnames) == CTF_ERR) {
1093 		warnx("failed to collect types: %s",
1094 		    ctf_errmsg(ctf_errno(g_fp)));
1095 		g_exit = 1;
1096 	}
1097 
1098 	qsort(idnames, max_id, sizeof (ctf_idname_t), idname_compare);
1099 
1100 	for (size_t i = 0; i <= max_id; i++) {
1101 		if (idnames[i].ci_id != 0)
1102 			ctfsrc_type(idnames[i].ci_id, idnames[i].ci_name);
1103 	}
1104 
1105 	free(idnames);
1106 
1107 	printf("\n\n/* Data Objects */\n\n");
1108 
1109 	if ((idnames = calloc(nr_syms, sizeof (idnames[0]))) == NULL) {
1110 		ctfdump_fatal("failed to alloc idnames: %s\n",
1111 		    strerror(errno));
1112 	}
1113 
1114 	if (ctf_object_iter(g_fp, ctfsrc_collect_objects_cb,
1115 	    &count) == CTF_ERR) {
1116 		warnx("failed to collect objects: %s",
1117 		    ctf_errmsg(ctf_errno(g_fp)));
1118 		g_exit = 1;
1119 	}
1120 
1121 	qsort(idnames, count, sizeof (ctf_idname_t), idname_compare);
1122 
1123 	for (size_t i = 0; i < count; i++)
1124 		ctfsrc_object(idnames[i].ci_id, idnames[i].ci_name);
1125 
1126 	free(idnames);
1127 
1128 	printf("\n\n/* Functions */\n\n");
1129 
1130 	if ((idnames = calloc(nr_syms, sizeof (idnames[0]))) == NULL) {
1131 		ctfdump_fatal("failed to alloc idnames: %s\n",
1132 		    strerror(errno));
1133 	}
1134 
1135 	count = 0;
1136 
1137 	if (ctf_function_iter(g_fp, ctfsrc_collect_functions_cb,
1138 	    &count) == CTF_ERR) {
1139 		warnx("failed to collect functions: %s",
1140 		    ctf_errmsg(ctf_errno(g_fp)));
1141 		g_exit = 1;
1142 	}
1143 
1144 	qsort(idnames, count, sizeof (ctf_idname_t), idname_compare);
1145 
1146 	for (size_t i = 0; i < count; i++)
1147 		ctfsrc_function(&idnames[i]);
1148 
1149 	free(idnames);
1150 }
1151 
1152 static void
1153 ctfdump_output(const char *out)
1154 {
1155 	int fd, ret;
1156 	const void *data;
1157 	size_t len;
1158 
1159 	ctf_dataptr(g_fp, &data, &len);
1160 	if ((fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
1161 		ctfdump_fatal("failed to open output file %s: %s\n", out,
1162 		    strerror(errno));
1163 
1164 	while (len > 0) {
1165 		ret = write(fd, data, len);
1166 		if (ret == -1 && errno == EINTR)
1167 			continue;
1168 		else if (ret == -1 && (errno == EFAULT || errno == EBADF))
1169 			abort();
1170 		else if (ret == -1)
1171 			ctfdump_fatal("failed to write to %s: %s\n", out,
1172 			    strerror(errno));
1173 		data = ((char *)data) + ret;
1174 		len -= ret;
1175 	}
1176 
1177 	do {
1178 		ret = close(fd);
1179 	} while (ret == -1 && errno == EINTR);
1180 	if (ret != 0 && errno == EBADF)
1181 		abort();
1182 	if (ret != 0)
1183 		ctfdump_fatal("failed to close %s: %s\n", out, strerror(errno));
1184 }
1185 
1186 int
1187 main(int argc, char *argv[])
1188 {
1189 	int c, fd, err;
1190 	const char *ufile = NULL, *parent = NULL;
1191 
1192 	g_progname = basename(argv[0]);
1193 	while ((c = getopt(argc, argv, ":cdfhlp:sStu:")) != -1) {
1194 		switch (c) {
1195 		case 'c':
1196 			g_dump |= CTFDUMP_SOURCE;
1197 			break;
1198 		case 'd':
1199 			g_dump |= CTFDUMP_OBJECTS;
1200 			break;
1201 		case 'f':
1202 			g_dump |= CTFDUMP_FUNCTIONS;
1203 			break;
1204 		case 'h':
1205 			g_dump |= CTFDUMP_HEADER;
1206 			break;
1207 		case 'l':
1208 			g_dump |= CTFDUMP_LABELS;
1209 			break;
1210 		case 'p':
1211 			parent = optarg;
1212 			break;
1213 		case 's':
1214 			g_dump |= CTFDUMP_STRINGS;
1215 			break;
1216 		case 'S':
1217 			g_dump |= CTFDUMP_STATS;
1218 			break;
1219 		case 't':
1220 			g_dump |= CTFDUMP_TYPES;
1221 			break;
1222 		case 'u':
1223 			g_dump |= CTFDUMP_OUTPUT;
1224 			ufile = optarg;
1225 			break;
1226 		case '?':
1227 			ctfdump_usage("Unknown option: -%c\n", optopt);
1228 			return (2);
1229 		case ':':
1230 			ctfdump_usage("Option -%c requires an operand\n",
1231 			    optopt);
1232 			return (2);
1233 		}
1234 	}
1235 
1236 	argc -= optind;
1237 	argv += optind;
1238 
1239 	if ((g_dump & CTFDUMP_SOURCE) && !!(g_dump & ~CTFDUMP_SOURCE)) {
1240 		ctfdump_usage("-c must be specified on its own\n");
1241 		return (2);
1242 	}
1243 
1244 	/*
1245 	 * Dump all information except C source by default.
1246 	 */
1247 	if (g_dump == 0)
1248 		g_dump = CTFDUMP_DEFAULT;
1249 
1250 	if (argc != 1) {
1251 		ctfdump_usage("no file to dump\n");
1252 		return (2);
1253 	}
1254 
1255 	if ((fd = open(argv[0], O_RDONLY)) < 0)
1256 		ctfdump_fatal("failed to open file %s: %s\n", argv[0],
1257 		    strerror(errno));
1258 
1259 	g_fp = ctf_fdopen(fd, &err);
1260 	if (g_fp == NULL)
1261 		ctfdump_fatal("failed to open file %s: %s\n", argv[0],
1262 		    ctf_errmsg(err));
1263 
1264 	/*
1265 	 * Check to see if this file needs a parent. If it does not and we were
1266 	 * given one, that should be an error. If it does need one and the
1267 	 * parent is not specified, that is fine, we just won't know how to
1268 	 * find child types. If we are given a parent, check at least that the
1269 	 * labels match.
1270 	 */
1271 	if (ctf_parent_name(g_fp) == NULL) {
1272 		if (parent != NULL)
1273 			ctfdump_fatal("cannot use %s as a parent file, %s is "
1274 			    "not a child\n", parent, argv[0]);
1275 	} else if (parent != NULL) {
1276 		const char *explabel, *label;
1277 		ctf_file_t *pfp = ctf_open(parent, &err);
1278 
1279 		if (pfp == NULL)
1280 			ctfdump_fatal("failed to open parent file %s: %s\n",
1281 			    parent, ctf_errmsg(err));
1282 
1283 		/*
1284 		 * Before we import the parent into the child, check that the
1285 		 * labels match. While there is also the notion of the parent
1286 		 * name, it's less straightforward to match that. Require that
1287 		 * labels match.
1288 		 */
1289 		explabel = ctf_parent_label(g_fp);
1290 		label = ctf_label_topmost(pfp);
1291 		if (explabel == NULL || label == NULL ||
1292 		    strcmp(explabel, label) != 0) {
1293 			if (label == NULL)
1294 				label = "<missing>";
1295 			if (explabel == NULL)
1296 				explabel = "<missing>";
1297 			ctfdump_fatal("label mismatch between parent %s and "
1298 			    "child %s, parent has %s, child expects %s\n",
1299 			    parent, argv[0], label, explabel);
1300 		}
1301 
1302 		if (ctf_import(g_fp, pfp) != 0)
1303 			ctfdump_fatal("failed to import parent %s: %s\n",
1304 			    parent, ctf_errmsg(ctf_errno(g_fp)));
1305 	} else {
1306 		if (g_dump & CTFDUMP_SOURCE) {
1307 			printf("/* Warning: parent \"%s\" not supplied: many "
1308 			    "types will be unknown. */\n\n",
1309 			    ctf_parent_name(g_fp));
1310 		} else {
1311 			fprintf(stderr, "warning: parent \"%s\" not supplied: "
1312 			    "many types will be unknown\n\n",
1313 			    ctf_parent_name(g_fp));
1314 		}
1315 	}
1316 
1317 	if (g_dump & CTFDUMP_SOURCE) {
1318 		ctfdump_source();
1319 		return (0);
1320 	}
1321 
1322 	/*
1323 	 * If stats is set, we must run through everything exect CTFDUMP_OUTPUT.
1324 	 * We also do CTFDUMP_STATS last as a result.
1325 	 */
1326 	if (g_dump & CTFDUMP_HEADER)
1327 		ctfdump_header();
1328 
1329 	if (g_dump & (CTFDUMP_LABELS | CTFDUMP_STATS))
1330 		ctfdump_labels();
1331 
1332 	if (g_dump & (CTFDUMP_OBJECTS | CTFDUMP_STATS))
1333 		ctfdump_objects();
1334 
1335 	if (g_dump & (CTFDUMP_FUNCTIONS | CTFDUMP_STATS))
1336 		ctfdump_functions();
1337 
1338 	if (g_dump & (CTFDUMP_TYPES | CTFDUMP_STATS))
1339 		ctfdump_types();
1340 
1341 	if (g_dump & (CTFDUMP_STRINGS | CTFDUMP_STATS))
1342 		ctfdump_strings();
1343 
1344 	if (g_dump & CTFDUMP_STATS)
1345 		ctfdump_stats();
1346 
1347 	if (g_dump & CTFDUMP_OUTPUT)
1348 		ctfdump_output(ufile);
1349 
1350 	return (g_exit);
1351 }
1352