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