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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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