1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/sysmacros.h> 29 #include <sys/stat.h> 30 #include <sys/mman.h> 31 32 #include <err.h> 33 #include <strings.h> 34 #include <unistd.h> 35 #include <stdlib.h> 36 #include <stdio.h> 37 #include <fcntl.h> 38 #include <gelf.h> 39 #include <zlib.h> 40 41 #include "ctf_headers.h" 42 #include "utils.h" 43 #include "symbol.h" 44 45 #define WARN(x) { warn(x); return (E_ERROR); } 46 47 /* 48 * Flags that indicate what data is to be displayed. An explicit `all' value is 49 * provided to allow the code to distinguish between a request for everything 50 * (currently requested by invoking ctfdump without flags) and individual 51 * requests for all of the types of data (an invocation with all flags). In the 52 * former case, we want to be able to implicitly adjust the definition of `all' 53 * based on the CTF version of the file being dumped. For example, if a v2 file 54 * is being dumped, `all' includes F_LABEL - a request to dump the label 55 * section. If a v1 file is being dumped, `all' does not include F_LABEL, 56 * because v1 CTF doesn't support labels. We need to be able to distinguish 57 * between `ctfdump foo', which has an implicit request for labels if `foo' 58 * supports them, and `ctfdump -l foo', which has an explicity request. In the 59 * latter case, we exit with an error if `foo' is a v1 CTF file. 60 */ 61 static enum { 62 F_DATA = 0x01, /* show data object section */ 63 F_FUNC = 0x02, /* show function section */ 64 F_HDR = 0x04, /* show header */ 65 F_STR = 0x08, /* show string table */ 66 F_TYPES = 0x10, /* show type section */ 67 F_STATS = 0x20, /* show statistics */ 68 F_LABEL = 0x40, /* show label section */ 69 F_ALL = 0x80, /* explicit request for `all' */ 70 F_ALLMSK = 0xff /* show all sections and statistics */ 71 } flags = 0; 72 73 static struct { 74 ulong_t s_ndata; /* total number of data objects */ 75 ulong_t s_nfunc; /* total number of functions */ 76 ulong_t s_nargs; /* total number of function arguments */ 77 ulong_t s_argmax; /* longest argument list */ 78 ulong_t s_ntypes; /* total number of types */ 79 ulong_t s_types[16]; /* number of types by kind */ 80 ulong_t s_nsmem; /* total number of struct members */ 81 ulong_t s_nsbytes; /* total size of all structs */ 82 ulong_t s_smmax; /* largest struct in terms of members */ 83 ulong_t s_sbmax; /* largest struct in terms of bytes */ 84 ulong_t s_numem; /* total number of union members */ 85 ulong_t s_nubytes; /* total size of all unions */ 86 ulong_t s_ummax; /* largest union in terms of members */ 87 ulong_t s_ubmax; /* largest union in terms of bytes */ 88 ulong_t s_nemem; /* total number of enum members */ 89 ulong_t s_emmax; /* largest enum in terms of members */ 90 ulong_t s_nstr; /* total number of strings */ 91 size_t s_strlen; /* total length of all strings */ 92 size_t s_strmax; /* longest string length */ 93 } stats; 94 95 typedef struct ctf_data { 96 caddr_t cd_ctfdata; /* Pointer to the CTF data */ 97 size_t cd_ctflen; /* Length of CTF data */ 98 99 size_t cd_idwidth; /* Size of a type ID, in bytes */ 100 101 /* 102 * cd_symdata will be non-NULL if the CTF data is being retrieved from 103 * an ELF file with a symbol table. cd_strdata and cd_nsyms should be 104 * used only if cd_symdata is non-NULL. 105 */ 106 Elf_Data *cd_symdata; /* Symbol table */ 107 Elf_Data *cd_strdata; /* Symbol table strings */ 108 int cd_nsyms; /* Number of symbol table entries */ 109 } ctf_data_t; 110 111 static const char * 112 ref_to_str(uint_t name, const ctf_header_t *hp, const ctf_data_t *cd) 113 { 114 size_t offset = CTF_NAME_OFFSET(name); 115 const char *s = cd->cd_ctfdata + hp->cth_stroff + offset; 116 117 if (CTF_NAME_STID(name) != CTF_STRTAB_0) 118 return ("<< ??? - name in external strtab >>"); 119 120 if (offset >= hp->cth_strlen) 121 return ("<< ??? - name exceeds strlab len >>"); 122 123 if (hp->cth_stroff + offset >= cd->cd_ctflen) 124 return ("<< ??? - file truncated >>"); 125 126 if (s[0] == '\0') 127 return ("(anon)"); 128 129 return (s); 130 } 131 132 static const char * 133 int_encoding_to_str(uint_t encoding) 134 { 135 static char buf[32]; 136 137 if (encoding == 0 || (encoding & ~(CTF_INT_SIGNED | CTF_INT_CHAR | 138 CTF_INT_BOOL | CTF_INT_VARARGS)) != 0) 139 (void) snprintf(buf, sizeof (buf), " 0x%x", encoding); 140 else { 141 buf[0] = '\0'; 142 if (encoding & CTF_INT_SIGNED) 143 (void) strcat(buf, " SIGNED"); 144 if (encoding & CTF_INT_CHAR) 145 (void) strcat(buf, " CHAR"); 146 if (encoding & CTF_INT_BOOL) 147 (void) strcat(buf, " BOOL"); 148 if (encoding & CTF_INT_VARARGS) 149 (void) strcat(buf, " VARARGS"); 150 } 151 152 return (buf + 1); 153 } 154 155 static const char * 156 fp_encoding_to_str(uint_t encoding) 157 { 158 static const char *const encs[] = { 159 NULL, "SINGLE", "DOUBLE", "COMPLEX", "DCOMPLEX", "LDCOMPLEX", 160 "LDOUBLE", "INTERVAL", "DINTERVAL", "LDINTERVAL", "IMAGINARY", 161 "DIMAGINARY", "LDIMAGINARY" 162 }; 163 164 static char buf[16]; 165 166 if (encoding < 1 || encoding >= (sizeof (encs) / sizeof (char *))) { 167 (void) snprintf(buf, sizeof (buf), "%u", encoding); 168 return (buf); 169 } 170 171 return (encs[encoding]); 172 } 173 174 static void 175 print_line(const char *s) 176 { 177 static const char line[] = "----------------------------------------" 178 "----------------------------------------"; 179 (void) printf("\n%s%.*s\n\n", s, (int)(78 - strlen(s)), line); 180 } 181 182 static int 183 print_header(const ctf_header_t *hp, const ctf_data_t *cd) 184 { 185 print_line("- CTF Header "); 186 187 (void) printf(" cth_magic = 0x%04x\n", hp->cth_magic); 188 (void) printf(" cth_version = %u\n", hp->cth_version); 189 (void) printf(" cth_flags = 0x%02x\n", hp->cth_flags); 190 (void) printf(" cth_parlabel = %s\n", 191 ref_to_str(hp->cth_parlabel, hp, cd)); 192 (void) printf(" cth_parname = %s\n", 193 ref_to_str(hp->cth_parname, hp, cd)); 194 (void) printf(" cth_lbloff = %u\n", hp->cth_lbloff); 195 (void) printf(" cth_objtoff = %u\n", hp->cth_objtoff); 196 (void) printf(" cth_funcoff = %u\n", hp->cth_funcoff); 197 (void) printf(" cth_typeoff = %u\n", hp->cth_typeoff); 198 (void) printf(" cth_stroff = %u\n", hp->cth_stroff); 199 (void) printf(" cth_strlen = %u\n", hp->cth_strlen); 200 201 return (E_SUCCESS); 202 } 203 204 static int 205 print_labeltable(const ctf_header_t *hp, const ctf_data_t *cd) 206 { 207 void *v = (void *) (cd->cd_ctfdata + hp->cth_lbloff); 208 const ctf_lblent_t *ctl = v; 209 ulong_t i, n = (hp->cth_objtoff - hp->cth_lbloff) / sizeof (*ctl); 210 211 print_line("- Label Table "); 212 213 if (hp->cth_lbloff & 3) 214 WARN("cth_lbloff is not aligned properly\n"); 215 if (hp->cth_lbloff >= cd->cd_ctflen) 216 WARN("file is truncated or cth_lbloff is corrupt\n"); 217 if (hp->cth_objtoff >= cd->cd_ctflen) 218 WARN("file is truncated or cth_objtoff is corrupt\n"); 219 if (hp->cth_lbloff > hp->cth_objtoff) 220 WARN("file is corrupt -- cth_lbloff > cth_objtoff\n"); 221 222 for (i = 0; i < n; i++, ctl++) { 223 (void) printf(" %5u %s\n", ctl->ctl_typeidx, 224 ref_to_str(ctl->ctl_label, hp, cd)); 225 } 226 227 return (E_SUCCESS); 228 } 229 230 /* 231 * Given the current symbol index (-1 to start at the beginning of the symbol 232 * table) and the type of symbol to match, this function returns the index of 233 * the next matching symbol (if any), and places the name of that symbol in 234 * *namep. If no symbol is found, -1 is returned. 235 */ 236 static int 237 next_sym(const ctf_data_t *cd, const int symidx, const uchar_t matchtype, 238 char **namep) 239 { 240 int i; 241 242 for (i = symidx + 1; i < cd->cd_nsyms; i++) { 243 GElf_Sym sym; 244 char *name; 245 int type; 246 247 if (gelf_getsym(cd->cd_symdata, i, &sym) == 0) 248 return (-1); 249 250 name = (char *)cd->cd_strdata->d_buf + sym.st_name; 251 type = GELF_ST_TYPE(sym.st_info); 252 253 /* 254 * Skip various types of symbol table entries. 255 */ 256 if (type != matchtype || ignore_symbol(&sym, name)) 257 continue; 258 259 /* Found one */ 260 *namep = name; 261 return (i); 262 } 263 264 return (-1); 265 } 266 267 static int 268 read_data(const ctf_header_t *hp, const ctf_data_t *cd) 269 { 270 const char *v = (void *) (cd->cd_ctfdata + hp->cth_objtoff); 271 ulong_t n = (hp->cth_funcoff - hp->cth_objtoff) / cd->cd_idwidth; 272 273 if (flags != F_STATS) 274 print_line("- Data Objects "); 275 276 if (hp->cth_objtoff & 1) 277 WARN("cth_objtoff is not aligned properly\n"); 278 if (hp->cth_objtoff >= cd->cd_ctflen) 279 WARN("file is truncated or cth_objtoff is corrupt\n"); 280 if (hp->cth_funcoff >= cd->cd_ctflen) 281 WARN("file is truncated or cth_funcoff is corrupt\n"); 282 if (hp->cth_objtoff > hp->cth_funcoff) 283 WARN("file is corrupt -- cth_objtoff > cth_funcoff\n"); 284 285 if (flags != F_STATS) { 286 int symidx, len, i; 287 char *name = NULL; 288 289 for (symidx = -1, i = 0; i < (int) n; i++) { 290 uint32_t id = 0; 291 int nextsym; 292 293 if (cd->cd_symdata == NULL || (nextsym = next_sym(cd, 294 symidx, STT_OBJECT, &name)) < 0) 295 name = NULL; 296 else 297 symidx = nextsym; 298 299 memcpy(&id, v, cd->cd_idwidth); 300 v += cd->cd_idwidth; 301 len = printf(" [%u] %u", i, id); 302 if (name != NULL) 303 (void) printf("%*s%s (%u)", (15 - len), "", 304 name, symidx); 305 (void) putchar('\n'); 306 } 307 } 308 309 stats.s_ndata = n; 310 return (E_SUCCESS); 311 } 312 313 static int 314 read_funcs(const ctf_header_t *hp, const ctf_data_t *cd) 315 { 316 const char *v = (void *) (cd->cd_ctfdata + hp->cth_funcoff); 317 uint_t f = 0, info; 318 319 const char *end = (void *) (cd->cd_ctfdata + hp->cth_typeoff); 320 321 ulong_t id; 322 int symidx; 323 324 if (flags != F_STATS) 325 print_line("- Functions "); 326 327 if (hp->cth_funcoff & 1) 328 WARN("cth_funcoff is not aligned properly\n"); 329 if (hp->cth_funcoff >= cd->cd_ctflen) 330 WARN("file is truncated or cth_funcoff is corrupt\n"); 331 if (hp->cth_typeoff >= cd->cd_ctflen) 332 WARN("file is truncated or cth_typeoff is corrupt\n"); 333 if (hp->cth_funcoff > hp->cth_typeoff) 334 WARN("file is corrupt -- cth_funcoff > cth_typeoff\n"); 335 336 for (symidx = -1, id = 0; v < end; id++) { 337 info = 0; 338 memcpy(&info, v, cd->cd_idwidth); 339 v += cd->cd_idwidth; 340 ushort_t kind = hp->cth_version == CTF_VERSION_2 ? 341 CTF_V2_INFO_KIND(info) : CTF_V3_INFO_KIND(info); 342 ushort_t n = hp->cth_version == CTF_VERSION_2 ? 343 CTF_V2_INFO_VLEN(info) : CTF_V3_INFO_VLEN(info); 344 ushort_t i; 345 int nextsym; 346 char *name; 347 348 if (cd->cd_symdata == NULL || (nextsym = next_sym(cd, symidx, 349 STT_FUNC, &name)) < 0) 350 name = NULL; 351 else 352 symidx = nextsym; 353 354 if (kind == CTF_K_UNKNOWN && n == 0) 355 continue; /* skip padding */ 356 357 if (kind != CTF_K_FUNCTION) { 358 (void) printf(" [%lu] unexpected kind -- %u\n", 359 id, kind); 360 return (E_ERROR); 361 } 362 363 if (v + n * cd->cd_idwidth > end) { 364 (void) printf(" [%lu] vlen %u extends past section " 365 "boundary\n", id, n); 366 return (E_ERROR); 367 } 368 369 if (flags != F_STATS) { 370 (void) printf(" [%lu] FUNC ", id); 371 if (name != NULL) 372 (void) printf("(%s) ", name); 373 memcpy(&f, v, cd->cd_idwidth); 374 v += cd->cd_idwidth; 375 (void) printf("returns: %u args: (", f); 376 377 if (n != 0) { 378 memcpy(&f, v, cd->cd_idwidth); 379 v += cd->cd_idwidth; 380 (void) printf("%u", f); 381 for (i = 1; i < n; i++) { 382 memcpy(&f, v, cd->cd_idwidth); 383 v += cd->cd_idwidth; 384 (void) printf(", %u", f); 385 } 386 } 387 388 (void) printf(")\n"); 389 } else 390 v += n * cd->cd_idwidth + 1; /* skip to next function definition */ 391 392 stats.s_nfunc++; 393 stats.s_nargs += n; 394 stats.s_argmax = MAX(stats.s_argmax, n); 395 } 396 397 return (E_SUCCESS); 398 } 399 400 static int 401 read_types(const ctf_header_t *hp, const ctf_data_t *cd) 402 { 403 const char *v = (void *) (cd->cd_ctfdata + hp->cth_typeoff); 404 const char *end = (void *) (cd->cd_ctfdata + hp->cth_stroff); 405 ulong_t id; 406 uint_t version; 407 408 if (flags != F_STATS) 409 print_line("- Types "); 410 411 if (hp->cth_typeoff & 3) 412 WARN("cth_typeoff is not aligned properly\n"); 413 if (hp->cth_typeoff >= cd->cd_ctflen) 414 WARN("file is truncated or cth_typeoff is corrupt\n"); 415 if (hp->cth_stroff >= cd->cd_ctflen) 416 WARN("file is truncated or cth_stroff is corrupt\n"); 417 if (hp->cth_typeoff > hp->cth_stroff) 418 WARN("file is corrupt -- cth_typeoff > cth_stroff\n"); 419 420 version = hp->cth_version; 421 422 id = 1; 423 if (hp->cth_parlabel || hp->cth_parname) 424 id += 1ul << (hp->cth_version == CTF_VERSION_2 ? 425 CTF_V2_PARENT_SHIFT : CTF_V3_PARENT_SHIFT); 426 427 for (/* */; v < end; id++) { 428 struct ctf_type_v2 t2; 429 struct ctf_type_v3 t3; 430 ulong_t i, n; 431 size_t size, increment, vlen = 0; 432 uint_t isroot, name, type; 433 int kind; 434 435 if (version == CTF_VERSION_2) { 436 memcpy(&t2, v, sizeof(t2)); 437 name = t2.ctt_name; 438 n = CTF_V2_INFO_VLEN(t2.ctt_info); 439 isroot = CTF_V2_INFO_ISROOT(t2.ctt_info); 440 kind = CTF_V2_INFO_KIND(t2.ctt_info); 441 type = t2.ctt_type; 442 443 if (t2.ctt_size == CTF_V2_LSIZE_SENT) { 444 increment = sizeof (struct ctf_type_v2); 445 size = (size_t)CTF_TYPE_LSIZE(&t2); 446 } else { 447 increment = sizeof (struct ctf_stype_v2); 448 size = t2.ctt_size; 449 } 450 } else { 451 memcpy(&t3, v, sizeof(t3)); 452 name = t3.ctt_name; 453 n = CTF_V3_INFO_VLEN(t3.ctt_info); 454 isroot = CTF_V3_INFO_ISROOT(t3.ctt_info); 455 kind = CTF_V3_INFO_KIND(t3.ctt_info); 456 type = t3.ctt_type; 457 458 if (t3.ctt_size == CTF_V3_LSIZE_SENT) { 459 increment = sizeof (struct ctf_type_v3); 460 size = (size_t)CTF_TYPE_LSIZE(&t3); 461 } else { 462 increment = sizeof (struct ctf_stype_v3); 463 size = t3.ctt_size; 464 } 465 } 466 467 union { 468 const char *ptr; 469 struct ctf_array_v2 *ap2; 470 struct ctf_array_v3 *ap3; 471 const struct ctf_member_v2 *mp2; 472 const struct ctf_member_v3 *mp3; 473 const struct ctf_lmember_v2 *lmp2; 474 const struct ctf_lmember_v3 *lmp3; 475 const ctf_enum_t *ep; 476 } u; 477 478 u.ptr = v + increment; 479 480 if (flags != F_STATS) { 481 (void) printf(" %c%lu%c ", 482 "[<"[isroot], id, "]>"[isroot]); 483 } 484 485 switch (kind) { 486 case CTF_K_INTEGER: 487 if (flags != F_STATS) { 488 uint_t encoding = 489 *((const uint_t *)(const void *)u.ptr); 490 491 (void) printf("INTEGER %s encoding=%s offset=%u" 492 " bits=%u", ref_to_str(name, hp, cd), 493 int_encoding_to_str( 494 CTF_INT_ENCODING(encoding)), 495 CTF_INT_OFFSET(encoding), 496 CTF_INT_BITS(encoding)); 497 } 498 vlen = sizeof (uint32_t); 499 break; 500 501 case CTF_K_FLOAT: 502 if (flags != F_STATS) { 503 uint_t encoding = 504 *((const uint_t *)(const void *)u.ptr); 505 506 (void) printf("FLOAT %s encoding=%s offset=%u " 507 "bits=%u", ref_to_str(name, hp, cd), 508 fp_encoding_to_str( 509 CTF_FP_ENCODING(encoding)), 510 CTF_FP_OFFSET(encoding), 511 CTF_FP_BITS(encoding)); 512 } 513 vlen = sizeof (uint32_t); 514 break; 515 516 case CTF_K_POINTER: 517 if (flags != F_STATS) { 518 (void) printf("POINTER %s refers to %u", 519 ref_to_str(name, hp, cd), type); 520 } 521 break; 522 523 case CTF_K_ARRAY: { 524 uint_t contents, index, nelems; 525 526 if (version == CTF_VERSION_2) { 527 contents = u.ap2->cta_contents; 528 index = u.ap2->cta_index; 529 nelems = u.ap2->cta_nelems; 530 } else { 531 contents = u.ap3->cta_contents; 532 index = u.ap3->cta_index; 533 nelems = u.ap3->cta_nelems; 534 } 535 if (flags != F_STATS) { 536 (void) printf("ARRAY %s content: %u index: %u " 537 "nelems: %u\n", ref_to_str(name, hp, cd), 538 contents, index, nelems); 539 } 540 if (version == 2) 541 vlen = sizeof (struct ctf_array_v2); 542 else 543 vlen = sizeof (struct ctf_array_v3); 544 break; 545 } 546 547 case CTF_K_FUNCTION: { 548 uint_t arg = 0; 549 550 if (flags != F_STATS) { 551 (void) printf("FUNCTION %s returns: %u args: (", 552 ref_to_str(name, hp, cd), type); 553 554 if (n != 0) { 555 memcpy(&arg, u.ptr, cd->cd_idwidth); 556 u.ptr += cd->cd_idwidth; 557 (void) printf("%u", arg); 558 for (i = 1; i < n; 559 i++, u.ptr += cd->cd_idwidth) { 560 memcpy(&arg, u.ptr, 561 cd->cd_idwidth); 562 (void) printf(", %u", arg); 563 } 564 } 565 566 (void) printf(")"); 567 } 568 569 vlen = roundup2(cd->cd_idwidth * n, 4); 570 break; 571 } 572 573 case CTF_K_STRUCT: 574 case CTF_K_UNION: 575 if (kind == CTF_K_STRUCT) { 576 stats.s_nsmem += n; 577 stats.s_smmax = MAX(stats.s_smmax, n); 578 stats.s_nsbytes += size; 579 stats.s_sbmax = MAX(stats.s_sbmax, size); 580 581 if (flags != F_STATS) 582 (void) printf("STRUCT"); 583 } else { 584 stats.s_numem += n; 585 stats.s_ummax = MAX(stats.s_ummax, n); 586 stats.s_nubytes += size; 587 stats.s_ubmax = MAX(stats.s_ubmax, size); 588 589 if (flags != F_STATS) 590 (void) printf("UNION"); 591 } 592 593 if (flags != F_STATS) { 594 (void) printf(" %s (%zd bytes)\n", 595 ref_to_str(name, hp, cd), size); 596 597 if (version == CTF_VERSION_2) { 598 if (size >= CTF_V2_LSTRUCT_THRESH) { 599 for (i = 0; i < n; i++, u.lmp2++) { 600 (void) printf( 601 "\t%s type=%u off=%llu\n", 602 ref_to_str(u.lmp2->ctlm_name, 603 hp, cd), u.lmp2->ctlm_type, 604 (unsigned long long) 605 CTF_LMEM_OFFSET(u.lmp2)); 606 } 607 } else { 608 for (i = 0; i < n; i++, u.mp2++) { 609 (void) printf( 610 "\t%s type=%u off=%u\n", 611 ref_to_str(u.mp2->ctm_name, 612 hp, cd), u.mp2->ctm_type, 613 u.mp2->ctm_offset); 614 } 615 } 616 } else { 617 if (size >= CTF_V3_LSTRUCT_THRESH) { 618 for (i = 0; i < n; i++, u.lmp3++) { 619 (void) printf( 620 "\t%s type=%u off=%llu\n", 621 ref_to_str(u.lmp3->ctlm_name, 622 hp, cd), u.lmp3->ctlm_type, 623 (unsigned long long) 624 CTF_LMEM_OFFSET(u.lmp3)); 625 } 626 } else { 627 for (i = 0; i < n; i++, u.mp3++) { 628 (void) printf( 629 "\t%s type=%u off=%u\n", 630 ref_to_str(u.mp3->ctm_name, 631 hp, cd), u.mp3->ctm_type, 632 u.mp3->ctm_offset); 633 } 634 } 635 } 636 } 637 638 if (version == CTF_VERSION_2) { 639 vlen = n * (size >= CTF_V2_LSTRUCT_THRESH ? 640 sizeof (struct ctf_lmember_v2) : 641 sizeof (struct ctf_member_v2)); 642 } else { 643 vlen = n * (size >= CTF_V3_LSTRUCT_THRESH ? 644 sizeof (struct ctf_lmember_v3) : 645 sizeof (struct ctf_member_v3)); 646 } 647 break; 648 649 case CTF_K_ENUM: 650 if (flags != F_STATS) { 651 (void) printf("ENUM %s\n", 652 ref_to_str(name, hp, cd)); 653 654 for (i = 0; i < n; i++, u.ep++) { 655 (void) printf("\t%s = %d\n", 656 ref_to_str(u.ep->cte_name, hp, cd), 657 u.ep->cte_value); 658 } 659 } 660 661 stats.s_nemem += n; 662 stats.s_emmax = MAX(stats.s_emmax, n); 663 664 vlen = sizeof (ctf_enum_t) * n; 665 break; 666 667 case CTF_K_FORWARD: 668 if (flags != F_STATS) { 669 (void) printf("FORWARD %s", 670 ref_to_str(name, hp, cd)); 671 } 672 break; 673 674 case CTF_K_TYPEDEF: 675 if (flags != F_STATS) { 676 (void) printf("TYPEDEF %s refers to %u", 677 ref_to_str(name, hp, cd), type); 678 } 679 break; 680 681 case CTF_K_VOLATILE: 682 if (flags != F_STATS) { 683 (void) printf("VOLATILE %s refers to %u", 684 ref_to_str(name, hp, cd), type); 685 } 686 break; 687 688 case CTF_K_CONST: 689 if (flags != F_STATS) { 690 (void) printf("CONST %s refers to %u", 691 ref_to_str(name, hp, cd), type); 692 } 693 break; 694 695 case CTF_K_RESTRICT: 696 if (flags != F_STATS) { 697 (void) printf("RESTRICT %s refers to %u", 698 ref_to_str(name, hp, cd), type); 699 } 700 break; 701 702 case CTF_K_UNKNOWN: 703 break; /* hole in type id space */ 704 705 default: 706 (void) printf("unexpected kind %u\n", kind); 707 return (E_ERROR); 708 } 709 710 if (flags != F_STATS) 711 (void) printf("\n"); 712 713 stats.s_ntypes++; 714 stats.s_types[kind]++; 715 716 v += increment + vlen; 717 } 718 719 return (E_SUCCESS); 720 } 721 722 static int 723 read_strtab(const ctf_header_t *hp, const ctf_data_t *cd) 724 { 725 size_t n, off, len = hp->cth_strlen; 726 const char *s = cd->cd_ctfdata + hp->cth_stroff; 727 728 if (flags != F_STATS) 729 print_line("- String Table "); 730 731 if (hp->cth_stroff >= cd->cd_ctflen) 732 WARN("file is truncated or cth_stroff is corrupt\n"); 733 if (hp->cth_stroff + hp->cth_strlen > cd->cd_ctflen) 734 WARN("file is truncated or cth_strlen is corrupt\n"); 735 736 for (off = 0; len != 0; off += n) { 737 if (flags != F_STATS) { 738 (void) printf(" [%lu] %s\n", (ulong_t)off, 739 s[0] == '\0' ? "\\0" : s); 740 } 741 n = strlen(s) + 1; 742 len -= n; 743 s += n; 744 745 stats.s_nstr++; 746 stats.s_strlen += n; 747 stats.s_strmax = MAX(stats.s_strmax, n); 748 } 749 750 return (E_SUCCESS); 751 } 752 753 static void 754 long_stat(const char *name, ulong_t value) 755 { 756 (void) printf(" %-36s= %lu\n", name, value); 757 } 758 759 static void 760 fp_stat(const char *name, float value) 761 { 762 (void) printf(" %-36s= %.2f\n", name, value); 763 } 764 765 static int 766 print_stats(void) 767 { 768 print_line("- CTF Statistics "); 769 770 long_stat("total number of data objects", stats.s_ndata); 771 (void) printf("\n"); 772 773 long_stat("total number of functions", stats.s_nfunc); 774 long_stat("total number of function arguments", stats.s_nargs); 775 long_stat("maximum argument list length", stats.s_argmax); 776 777 if (stats.s_nfunc != 0) { 778 fp_stat("average argument list length", 779 (float)stats.s_nargs / (float)stats.s_nfunc); 780 } 781 782 (void) printf("\n"); 783 784 long_stat("total number of types", stats.s_ntypes); 785 long_stat("total number of integers", stats.s_types[CTF_K_INTEGER]); 786 long_stat("total number of floats", stats.s_types[CTF_K_FLOAT]); 787 long_stat("total number of pointers", stats.s_types[CTF_K_POINTER]); 788 long_stat("total number of arrays", stats.s_types[CTF_K_ARRAY]); 789 long_stat("total number of func types", stats.s_types[CTF_K_FUNCTION]); 790 long_stat("total number of structs", stats.s_types[CTF_K_STRUCT]); 791 long_stat("total number of unions", stats.s_types[CTF_K_UNION]); 792 long_stat("total number of enums", stats.s_types[CTF_K_ENUM]); 793 long_stat("total number of forward tags", stats.s_types[CTF_K_FORWARD]); 794 long_stat("total number of typedefs", stats.s_types[CTF_K_TYPEDEF]); 795 long_stat("total number of volatile types", 796 stats.s_types[CTF_K_VOLATILE]); 797 long_stat("total number of const types", stats.s_types[CTF_K_CONST]); 798 long_stat("total number of restrict types", 799 stats.s_types[CTF_K_RESTRICT]); 800 long_stat("total number of unknowns (holes)", 801 stats.s_types[CTF_K_UNKNOWN]); 802 803 (void) printf("\n"); 804 805 long_stat("total number of struct members", stats.s_nsmem); 806 long_stat("maximum number of struct members", stats.s_smmax); 807 long_stat("total size of all structs", stats.s_nsbytes); 808 long_stat("maximum size of a struct", stats.s_sbmax); 809 810 if (stats.s_types[CTF_K_STRUCT] != 0) { 811 fp_stat("average number of struct members", 812 (float)stats.s_nsmem / (float)stats.s_types[CTF_K_STRUCT]); 813 fp_stat("average size of a struct", (float)stats.s_nsbytes / 814 (float)stats.s_types[CTF_K_STRUCT]); 815 } 816 817 (void) printf("\n"); 818 819 long_stat("total number of union members", stats.s_numem); 820 long_stat("maximum number of union members", stats.s_ummax); 821 long_stat("total size of all unions", stats.s_nubytes); 822 long_stat("maximum size of a union", stats.s_ubmax); 823 824 if (stats.s_types[CTF_K_UNION] != 0) { 825 fp_stat("average number of union members", 826 (float)stats.s_numem / (float)stats.s_types[CTF_K_UNION]); 827 fp_stat("average size of a union", (float)stats.s_nubytes / 828 (float)stats.s_types[CTF_K_UNION]); 829 } 830 831 (void) printf("\n"); 832 833 long_stat("total number of enum members", stats.s_nemem); 834 long_stat("maximum number of enum members", stats.s_emmax); 835 836 if (stats.s_types[CTF_K_ENUM] != 0) { 837 fp_stat("average number of enum members", 838 (float)stats.s_nemem / (float)stats.s_types[CTF_K_ENUM]); 839 } 840 841 (void) printf("\n"); 842 843 long_stat("total number of unique strings", stats.s_nstr); 844 long_stat("bytes of string data", stats.s_strlen); 845 long_stat("maximum string length", stats.s_strmax); 846 847 if (stats.s_nstr != 0) { 848 fp_stat("average string length", 849 (float)stats.s_strlen / (float)stats.s_nstr); 850 } 851 852 (void) printf("\n"); 853 return (E_SUCCESS); 854 } 855 856 static int 857 print_usage(FILE *fp, int verbose) 858 { 859 (void) fprintf(fp, "Usage: %s [-dfhlsSt] [-u file] file\n", getpname()); 860 861 if (verbose) { 862 (void) fprintf(fp, 863 "\t-d dump data object section\n" 864 "\t-f dump function section\n" 865 "\t-h dump file header\n" 866 "\t-l dump label table\n" 867 "\t-s dump string table\n" 868 "\t-S dump statistics\n" 869 "\t-t dump type section\n" 870 "\t-u save uncompressed CTF to a file\n"); 871 } 872 873 return (E_USAGE); 874 } 875 876 static Elf_Scn * 877 findelfscn(Elf *elf, GElf_Ehdr *ehdr, const char *secname) 878 { 879 GElf_Shdr shdr; 880 Elf_Scn *scn; 881 char *name; 882 883 for (scn = NULL; (scn = elf_nextscn(elf, scn)) != NULL; ) { 884 if (gelf_getshdr(scn, &shdr) != NULL && (name = 885 elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name)) != NULL && 886 strcmp(name, secname) == 0) 887 return (scn); 888 } 889 890 return (NULL); 891 } 892 893 int 894 main(int argc, char *argv[]) 895 { 896 const char *filename = NULL; 897 const char *ufile = NULL; 898 int error = 0; 899 int c, fd, ufd; 900 901 ctf_data_t cd; 902 const ctf_preamble_t *pp; 903 ctf_header_t *hp = NULL; 904 Elf *elf; 905 GElf_Ehdr ehdr; 906 907 (void) elf_version(EV_CURRENT); 908 909 for (opterr = 0; optind < argc; optind++) { 910 while ((c = getopt(argc, argv, "dfhlsStu:")) != (int)EOF) { 911 switch (c) { 912 case 'd': 913 flags |= F_DATA; 914 break; 915 case 'f': 916 flags |= F_FUNC; 917 break; 918 case 'h': 919 flags |= F_HDR; 920 break; 921 case 'l': 922 flags |= F_LABEL; 923 break; 924 case 's': 925 flags |= F_STR; 926 break; 927 case 'S': 928 flags |= F_STATS; 929 break; 930 case 't': 931 flags |= F_TYPES; 932 break; 933 case 'u': 934 ufile = optarg; 935 break; 936 default: 937 if (optopt == '?') 938 return (print_usage(stdout, 1)); 939 warn("illegal option -- %c\n", optopt); 940 return (print_usage(stderr, 0)); 941 } 942 } 943 944 if (optind < argc) { 945 if (filename != NULL) 946 return (print_usage(stderr, 0)); 947 filename = argv[optind]; 948 } 949 } 950 951 if (filename == NULL) 952 return (print_usage(stderr, 0)); 953 954 if (flags == 0 && ufile == NULL) 955 flags = F_ALLMSK; 956 957 if ((fd = open(filename, O_RDONLY)) == -1) 958 die("failed to open %s", filename); 959 960 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) != NULL && 961 gelf_getehdr(elf, &ehdr) != NULL) { 962 963 Elf_Data *dp = NULL; 964 Elf_Scn *ctfscn = findelfscn(elf, &ehdr, ".SUNW_ctf"); 965 Elf_Scn *symscn; 966 GElf_Shdr ctfshdr; 967 968 if (ctfscn == NULL || (dp = elf_getdata(ctfscn, NULL)) == NULL) 969 die("%s does not contain .SUNW_ctf data\n", filename); 970 971 cd.cd_ctfdata = dp->d_buf; 972 cd.cd_ctflen = dp->d_size; 973 974 /* 975 * If the sh_link field of the CTF section header is non-zero 976 * it indicates which section contains the symbol table that 977 * should be used. We default to the .symtab section if sh_link 978 * is zero or if there's an error reading the section header. 979 */ 980 if (gelf_getshdr(ctfscn, &ctfshdr) != NULL && 981 ctfshdr.sh_link != 0) { 982 symscn = elf_getscn(elf, ctfshdr.sh_link); 983 } else { 984 symscn = findelfscn(elf, &ehdr, ".symtab"); 985 } 986 987 /* If we found a symbol table, find the corresponding strings */ 988 if (symscn != NULL) { 989 GElf_Shdr shdr; 990 Elf_Scn *symstrscn; 991 992 if (gelf_getshdr(symscn, &shdr) != NULL) { 993 symstrscn = elf_getscn(elf, shdr.sh_link); 994 995 cd.cd_nsyms = shdr.sh_size / shdr.sh_entsize; 996 cd.cd_symdata = elf_getdata(symscn, NULL); 997 cd.cd_strdata = elf_getdata(symstrscn, NULL); 998 } 999 } 1000 } else { 1001 struct stat st; 1002 1003 if (fstat(fd, &st) == -1) 1004 die("failed to fstat %s", filename); 1005 1006 cd.cd_ctflen = st.st_size; 1007 cd.cd_ctfdata = mmap(NULL, cd.cd_ctflen, PROT_READ, 1008 MAP_PRIVATE, fd, 0); 1009 if (cd.cd_ctfdata == MAP_FAILED) 1010 die("failed to mmap %s", filename); 1011 } 1012 1013 /* 1014 * Get a pointer to the CTF data buffer and interpret the first portion 1015 * as a ctf_header_t. Validate the magic number and size. 1016 */ 1017 1018 if (cd.cd_ctflen < sizeof (ctf_preamble_t)) 1019 die("%s does not contain a CTF preamble\n", filename); 1020 1021 void *v = (void *) cd.cd_ctfdata; 1022 pp = v; 1023 1024 if (pp->ctp_magic != CTF_MAGIC) 1025 die("%s does not appear to contain CTF data\n", filename); 1026 1027 if (pp->ctp_version >= CTF_VERSION_2) { 1028 v = (void *) cd.cd_ctfdata; 1029 hp = v; 1030 cd.cd_ctfdata = (caddr_t)cd.cd_ctfdata + sizeof (ctf_header_t); 1031 1032 cd.cd_idwidth = pp->ctp_version == CTF_VERSION_2 ? 2 : 4; 1033 1034 if (cd.cd_ctflen < sizeof (ctf_header_t)) { 1035 die("%s does not contain a v%d CTF header\n", filename, 1036 pp->ctp_version); 1037 } 1038 1039 } else { 1040 die("%s contains unsupported CTF version %d\n", filename, 1041 pp->ctp_version); 1042 } 1043 1044 /* 1045 * If the data buffer is compressed, then malloc a buffer large enough 1046 * to hold the decompressed data, and use zlib to decompress it. 1047 */ 1048 if (hp->cth_flags & CTF_F_COMPRESS) { 1049 z_stream zstr; 1050 void *buf; 1051 int rc; 1052 1053 if ((buf = malloc(hp->cth_stroff + hp->cth_strlen)) == NULL) 1054 die("failed to allocate decompression buffer"); 1055 1056 bzero(&zstr, sizeof (z_stream)); 1057 zstr.next_in = (void *)cd.cd_ctfdata; 1058 zstr.avail_in = cd.cd_ctflen; 1059 zstr.next_out = buf; 1060 zstr.avail_out = hp->cth_stroff + hp->cth_strlen; 1061 1062 if ((rc = inflateInit(&zstr)) != Z_OK) 1063 die("failed to initialize zlib: %s\n", zError(rc)); 1064 1065 if ((rc = inflate(&zstr, Z_FINISH)) != Z_STREAM_END) 1066 die("failed to decompress CTF data: %s\n", zError(rc)); 1067 1068 if ((rc = inflateEnd(&zstr)) != Z_OK) 1069 die("failed to finish decompression: %s\n", zError(rc)); 1070 1071 if (zstr.total_out != hp->cth_stroff + hp->cth_strlen) 1072 die("CTF data is corrupt -- short decompression\n"); 1073 1074 cd.cd_ctfdata = buf; 1075 cd.cd_ctflen = hp->cth_stroff + hp->cth_strlen; 1076 } 1077 1078 if (flags & F_HDR) 1079 error |= print_header(hp, &cd); 1080 if (flags & (F_LABEL)) 1081 error |= print_labeltable(hp, &cd); 1082 if (flags & (F_DATA | F_STATS)) 1083 error |= read_data(hp, &cd); 1084 if (flags & (F_FUNC | F_STATS)) 1085 error |= read_funcs(hp, &cd); 1086 if (flags & (F_TYPES | F_STATS)) 1087 error |= read_types(hp, &cd); 1088 if (flags & (F_STR | F_STATS)) 1089 error |= read_strtab(hp, &cd); 1090 if (flags & F_STATS) 1091 error |= print_stats(); 1092 1093 /* 1094 * If the -u option is specified, write the uncompressed CTF data to a 1095 * raw CTF file. CTF data can already be extracted compressed by 1096 * applying elfdump -w -N .SUNW_ctf to an ELF file, so we don't bother. 1097 */ 1098 if (ufile != NULL) { 1099 ctf_header_t h; 1100 1101 bcopy(hp, &h, sizeof (h)); 1102 h.cth_flags &= ~CTF_F_COMPRESS; 1103 1104 if ((ufd = open(ufile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0 || 1105 write(ufd, &h, sizeof (h)) != sizeof (h) || 1106 write(ufd, cd.cd_ctfdata, cd.cd_ctflen) != (int) cd.cd_ctflen) { 1107 warn("failed to write CTF data to '%s'", ufile); 1108 error |= E_ERROR; 1109 } 1110 1111 (void) close(ufd); 1112 } 1113 1114 if (elf != NULL) 1115 (void) elf_end(elf); 1116 1117 (void) close(fd); 1118 return (error); 1119 } 1120