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