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