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