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 /* 101 * cd_symdata will be non-NULL if the CTF data is being retrieved from 102 * an ELF file with a symbol table. cd_strdata and cd_nsyms should be 103 * used only if cd_symdata is non-NULL. 104 */ 105 Elf_Data *cd_symdata; /* Symbol table */ 106 Elf_Data *cd_strdata; /* Symbol table strings */ 107 int cd_nsyms; /* Number of symbol table entries */ 108 } ctf_data_t; 109 110 static const char * 111 ref_to_str(uint_t name, const ctf_header_t *hp, const ctf_data_t *cd) 112 { 113 size_t offset = CTF_NAME_OFFSET(name); 114 const char *s = cd->cd_ctfdata + hp->cth_stroff + offset; 115 116 if (CTF_NAME_STID(name) != CTF_STRTAB_0) 117 return ("<< ??? - name in external strtab >>"); 118 119 if (offset >= hp->cth_strlen) 120 return ("<< ??? - name exceeds strlab len >>"); 121 122 if (hp->cth_stroff + offset >= cd->cd_ctflen) 123 return ("<< ??? - file truncated >>"); 124 125 if (s[0] == '\0') 126 return ("(anon)"); 127 128 return (s); 129 } 130 131 static const char * 132 int_encoding_to_str(uint_t encoding) 133 { 134 static char buf[32]; 135 136 if (encoding == 0 || (encoding & ~(CTF_INT_SIGNED | CTF_INT_CHAR | 137 CTF_INT_BOOL | CTF_INT_VARARGS)) != 0) 138 (void) snprintf(buf, sizeof (buf), " 0x%x", encoding); 139 else { 140 buf[0] = '\0'; 141 if (encoding & CTF_INT_SIGNED) 142 (void) strcat(buf, " SIGNED"); 143 if (encoding & CTF_INT_CHAR) 144 (void) strcat(buf, " CHAR"); 145 if (encoding & CTF_INT_BOOL) 146 (void) strcat(buf, " BOOL"); 147 if (encoding & CTF_INT_VARARGS) 148 (void) strcat(buf, " VARARGS"); 149 } 150 151 return (buf + 1); 152 } 153 154 static const char * 155 fp_encoding_to_str(uint_t encoding) 156 { 157 static const char *const encs[] = { 158 NULL, "SINGLE", "DOUBLE", "COMPLEX", "DCOMPLEX", "LDCOMPLEX", 159 "LDOUBLE", "INTERVAL", "DINTERVAL", "LDINTERVAL", "IMAGINARY", 160 "DIMAGINARY", "LDIMAGINARY" 161 }; 162 163 static char buf[16]; 164 165 if (encoding < 1 || encoding >= (sizeof (encs) / sizeof (char *))) { 166 (void) snprintf(buf, sizeof (buf), "%u", encoding); 167 return (buf); 168 } 169 170 return (encs[encoding]); 171 } 172 173 static void 174 print_line(const char *s) 175 { 176 static const char line[] = "----------------------------------------" 177 "----------------------------------------"; 178 (void) printf("\n%s%.*s\n\n", s, (int)(78 - strlen(s)), line); 179 } 180 181 static int 182 print_header(const ctf_header_t *hp, const ctf_data_t *cd) 183 { 184 print_line("- CTF Header "); 185 186 (void) printf(" cth_magic = 0x%04x\n", hp->cth_magic); 187 (void) printf(" cth_version = %u\n", hp->cth_version); 188 (void) printf(" cth_flags = 0x%02x\n", hp->cth_flags); 189 (void) printf(" cth_parlabel = %s\n", 190 ref_to_str(hp->cth_parlabel, hp, cd)); 191 (void) printf(" cth_parname = %s\n", 192 ref_to_str(hp->cth_parname, hp, cd)); 193 (void) printf(" cth_lbloff = %u\n", hp->cth_lbloff); 194 (void) printf(" cth_objtoff = %u\n", hp->cth_objtoff); 195 (void) printf(" cth_funcoff = %u\n", hp->cth_funcoff); 196 (void) printf(" cth_typeoff = %u\n", hp->cth_typeoff); 197 (void) printf(" cth_stroff = %u\n", hp->cth_stroff); 198 (void) printf(" cth_strlen = %u\n", hp->cth_strlen); 199 200 return (E_SUCCESS); 201 } 202 203 static int 204 print_labeltable(const ctf_header_t *hp, const ctf_data_t *cd) 205 { 206 /* LINTED - pointer alignment */ 207 const ctf_lblent_t *ctl = (ctf_lblent_t *)(cd->cd_ctfdata + 208 hp->cth_lbloff); 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 /* LINTED - pointer alignment */ 271 const ushort_t *idp = (ushort_t *)(cd->cd_ctfdata + hp->cth_objtoff); 272 ulong_t n = (hp->cth_funcoff - hp->cth_objtoff) / sizeof (ushort_t); 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 < n; i++) { 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 len = printf(" [%u] %u", i, *idp++); 300 if (name != NULL) 301 (void) printf("%*s%s (%u)", (15 - len), "", 302 name, symidx); 303 (void) putchar('\n'); 304 } 305 } 306 307 stats.s_ndata = n; 308 return (E_SUCCESS); 309 } 310 311 static int 312 read_funcs(const ctf_header_t *hp, const ctf_data_t *cd) 313 { 314 /* LINTED - pointer alignment */ 315 const ushort_t *fp = (ushort_t *)(cd->cd_ctfdata + hp->cth_funcoff); 316 317 /* LINTED - pointer alignment */ 318 const ushort_t *end = (ushort_t *)(cd->cd_ctfdata + hp->cth_typeoff); 319 320 ulong_t id; 321 int symidx; 322 323 if (flags != F_STATS) 324 print_line("- Functions "); 325 326 if (hp->cth_funcoff & 1) 327 WARN("cth_funcoff is not aligned properly\n"); 328 if (hp->cth_funcoff >= cd->cd_ctflen) 329 WARN("file is truncated or cth_funcoff is corrupt\n"); 330 if (hp->cth_typeoff >= cd->cd_ctflen) 331 WARN("file is truncated or cth_typeoff is corrupt\n"); 332 if (hp->cth_funcoff > hp->cth_typeoff) 333 WARN("file is corrupt -- cth_funcoff > cth_typeoff\n"); 334 335 for (symidx = -1, id = 0; fp < end; id++) { 336 ushort_t info = *fp++; 337 ushort_t kind = CTF_INFO_KIND(info); 338 ushort_t n = CTF_INFO_VLEN(info); 339 ushort_t i; 340 int nextsym; 341 char *name; 342 343 if (cd->cd_symdata == NULL || (nextsym = next_sym(cd, symidx, 344 STT_FUNC, &name)) < 0) 345 name = NULL; 346 else 347 symidx = nextsym; 348 349 if (kind == CTF_K_UNKNOWN && n == 0) 350 continue; /* skip padding */ 351 352 if (kind != CTF_K_FUNCTION) { 353 (void) printf(" [%lu] unexpected kind -- %u\n", 354 id, kind); 355 return (E_ERROR); 356 } 357 358 if (fp + n > end) { 359 (void) printf(" [%lu] vlen %u extends past section " 360 "boundary\n", id, n); 361 return (E_ERROR); 362 } 363 364 if (flags != F_STATS) { 365 (void) printf(" [%lu] FUNC ", id); 366 if (name != NULL) 367 (void) printf("(%s) ", name); 368 (void) printf("returns: %u args: (", *fp++); 369 370 if (n != 0) { 371 (void) printf("%u", *fp++); 372 for (i = 1; i < n; i++) 373 (void) printf(", %u", *fp++); 374 } 375 376 (void) printf(")\n"); 377 } else 378 fp += n + 1; /* skip to next function definition */ 379 380 stats.s_nfunc++; 381 stats.s_nargs += n; 382 stats.s_argmax = MAX(stats.s_argmax, n); 383 } 384 385 return (E_SUCCESS); 386 } 387 388 static int 389 read_types(const ctf_header_t *hp, const ctf_data_t *cd) 390 { 391 /* LINTED - pointer alignment */ 392 const ctf_type_t *tp = (ctf_type_t *)(cd->cd_ctfdata + hp->cth_typeoff); 393 394 /* LINTED - pointer alignment */ 395 const ctf_type_t *end = (ctf_type_t *)(cd->cd_ctfdata + hp->cth_stroff); 396 397 ulong_t id; 398 399 if (flags != F_STATS) 400 print_line("- Types "); 401 402 if (hp->cth_typeoff & 3) 403 WARN("cth_typeoff is not aligned properly\n"); 404 if (hp->cth_typeoff >= cd->cd_ctflen) 405 WARN("file is truncated or cth_typeoff is corrupt\n"); 406 if (hp->cth_stroff >= cd->cd_ctflen) 407 WARN("file is truncated or cth_stroff is corrupt\n"); 408 if (hp->cth_typeoff > hp->cth_stroff) 409 WARN("file is corrupt -- cth_typeoff > cth_stroff\n"); 410 411 id = 1; 412 if (hp->cth_parlabel || hp->cth_parname) 413 id += 1 << CTF_PARENT_SHIFT; 414 415 for (/* */; tp < end; id++) { 416 ulong_t i, n = CTF_INFO_VLEN(tp->ctt_info); 417 size_t size, increment, vlen = 0; 418 int kind = CTF_INFO_KIND(tp->ctt_info); 419 420 union { 421 const void *ptr; 422 const ctf_array_t *ap; 423 const ctf_member_t *mp; 424 const ctf_lmember_t *lmp; 425 const ctf_enum_t *ep; 426 const ushort_t *argp; 427 } u; 428 429 if (flags != F_STATS) { 430 (void) printf(" %c%lu%c ", 431 "[<"[CTF_INFO_ISROOT(tp->ctt_info)], id, 432 "]>"[CTF_INFO_ISROOT(tp->ctt_info)]); 433 } 434 435 if (tp->ctt_size == CTF_LSIZE_SENT) { 436 increment = sizeof (ctf_type_t); 437 size = (size_t)CTF_TYPE_LSIZE(tp); 438 } else { 439 increment = sizeof (ctf_stype_t); 440 size = tp->ctt_size; 441 } 442 u.ptr = (caddr_t)tp + increment; 443 444 switch (kind) { 445 case CTF_K_INTEGER: 446 if (flags != F_STATS) { 447 uint_t encoding = *((const uint_t *)u.ptr); 448 449 (void) printf("INTEGER %s encoding=%s offset=%u" 450 " bits=%u", ref_to_str(tp->ctt_name, hp, 451 cd), int_encoding_to_str( 452 CTF_INT_ENCODING(encoding)), 453 CTF_INT_OFFSET(encoding), 454 CTF_INT_BITS(encoding)); 455 } 456 vlen = sizeof (uint_t); 457 break; 458 459 case CTF_K_FLOAT: 460 if (flags != F_STATS) { 461 uint_t encoding = *((const uint_t *)u.ptr); 462 463 (void) printf("FLOAT %s encoding=%s offset=%u " 464 "bits=%u", ref_to_str(tp->ctt_name, hp, 465 cd), fp_encoding_to_str( 466 CTF_FP_ENCODING(encoding)), 467 CTF_FP_OFFSET(encoding), 468 CTF_FP_BITS(encoding)); 469 } 470 vlen = sizeof (uint_t); 471 break; 472 473 case CTF_K_POINTER: 474 if (flags != F_STATS) { 475 (void) printf("POINTER %s refers to %u", 476 ref_to_str(tp->ctt_name, hp, cd), 477 tp->ctt_type); 478 } 479 break; 480 481 case CTF_K_ARRAY: 482 if (flags != F_STATS) { 483 (void) printf("ARRAY %s content: %u index: %u " 484 "nelems: %u\n", ref_to_str(tp->ctt_name, 485 hp, cd), u.ap->cta_contents, 486 u.ap->cta_index, u.ap->cta_nelems); 487 } 488 vlen = sizeof (ctf_array_t); 489 break; 490 491 case CTF_K_FUNCTION: 492 if (flags != F_STATS) { 493 (void) printf("FUNCTION %s returns: %u args: (", 494 ref_to_str(tp->ctt_name, hp, cd), 495 tp->ctt_type); 496 497 if (n != 0) { 498 (void) printf("%u", *u.argp++); 499 for (i = 1; i < n; i++, u.argp++) 500 (void) printf(", %u", *u.argp); 501 } 502 503 (void) printf(")"); 504 } 505 506 vlen = sizeof (ushort_t) * (n + (n & 1)); 507 break; 508 509 case CTF_K_STRUCT: 510 case CTF_K_UNION: 511 if (kind == CTF_K_STRUCT) { 512 stats.s_nsmem += n; 513 stats.s_smmax = MAX(stats.s_smmax, n); 514 stats.s_nsbytes += size; 515 stats.s_sbmax = MAX(stats.s_sbmax, size); 516 517 if (flags != F_STATS) 518 (void) printf("STRUCT"); 519 } else { 520 stats.s_numem += n; 521 stats.s_ummax = MAX(stats.s_ummax, n); 522 stats.s_nubytes += size; 523 stats.s_ubmax = MAX(stats.s_ubmax, size); 524 525 if (flags != F_STATS) 526 (void) printf("UNION"); 527 } 528 529 if (flags != F_STATS) { 530 (void) printf(" %s (%d bytes)\n", 531 ref_to_str(tp->ctt_name, hp, cd), size); 532 533 if (size >= CTF_LSTRUCT_THRESH) { 534 for (i = 0; i < n; i++, u.lmp++) { 535 (void) printf( 536 "\t%s type=%u off=%llu\n", 537 ref_to_str(u.lmp->ctlm_name, 538 hp, cd), u.lmp->ctlm_type, 539 CTF_LMEM_OFFSET(u.lmp)); 540 } 541 } else { 542 for (i = 0; i < n; i++, u.mp++) { 543 (void) printf( 544 "\t%s type=%u off=%u\n", 545 ref_to_str(u.mp->ctm_name, 546 hp, cd), u.mp->ctm_type, 547 u.mp->ctm_offset); 548 } 549 } 550 } 551 552 vlen = n * (size >= CTF_LSTRUCT_THRESH ? 553 sizeof (ctf_lmember_t) : sizeof (ctf_member_t)); 554 break; 555 556 case CTF_K_ENUM: 557 if (flags != F_STATS) { 558 (void) printf("ENUM %s\n", 559 ref_to_str(tp->ctt_name, hp, cd)); 560 561 for (i = 0; i < n; i++, u.ep++) { 562 (void) printf("\t%s = %d\n", 563 ref_to_str(u.ep->cte_name, hp, cd), 564 u.ep->cte_value); 565 } 566 } 567 568 stats.s_nemem += n; 569 stats.s_emmax = MAX(stats.s_emmax, n); 570 571 vlen = sizeof (ctf_enum_t) * n; 572 break; 573 574 case CTF_K_FORWARD: 575 if (flags != F_STATS) { 576 (void) printf("FORWARD %s", 577 ref_to_str(tp->ctt_name, hp, cd)); 578 } 579 break; 580 581 case CTF_K_TYPEDEF: 582 if (flags != F_STATS) { 583 (void) printf("TYPEDEF %s refers to %u", 584 ref_to_str(tp->ctt_name, hp, cd), 585 tp->ctt_type); 586 } 587 break; 588 589 case CTF_K_VOLATILE: 590 if (flags != F_STATS) { 591 (void) printf("VOLATILE %s refers to %u", 592 ref_to_str(tp->ctt_name, hp, cd), 593 tp->ctt_type); 594 } 595 break; 596 597 case CTF_K_CONST: 598 if (flags != F_STATS) { 599 (void) printf("CONST %s refers to %u", 600 ref_to_str(tp->ctt_name, hp, cd), 601 tp->ctt_type); 602 } 603 break; 604 605 case CTF_K_RESTRICT: 606 if (flags != F_STATS) { 607 (void) printf("RESTRICT %s refers to %u", 608 ref_to_str(tp->ctt_name, hp, cd), 609 tp->ctt_type); 610 } 611 break; 612 613 case CTF_K_UNKNOWN: 614 break; /* hole in type id space */ 615 616 default: 617 (void) printf("unexpected kind %u\n", kind); 618 return (E_ERROR); 619 } 620 621 if (flags != F_STATS) 622 (void) printf("\n"); 623 624 stats.s_ntypes++; 625 stats.s_types[kind]++; 626 627 tp = (ctf_type_t *)((uintptr_t)tp + increment + vlen); 628 } 629 630 return (E_SUCCESS); 631 } 632 633 static int 634 read_strtab(const ctf_header_t *hp, const ctf_data_t *cd) 635 { 636 size_t n, off, len = hp->cth_strlen; 637 const char *s = cd->cd_ctfdata + hp->cth_stroff; 638 639 if (flags != F_STATS) 640 print_line("- String Table "); 641 642 if (hp->cth_stroff >= cd->cd_ctflen) 643 WARN("file is truncated or cth_stroff is corrupt\n"); 644 if (hp->cth_stroff + hp->cth_strlen > cd->cd_ctflen) 645 WARN("file is truncated or cth_strlen is corrupt\n"); 646 647 for (off = 0; len != 0; off += n) { 648 if (flags != F_STATS) { 649 (void) printf(" [%lu] %s\n", (ulong_t)off, 650 s[0] == '\0' ? "\\0" : s); 651 } 652 n = strlen(s) + 1; 653 len -= n; 654 s += n; 655 656 stats.s_nstr++; 657 stats.s_strlen += n; 658 stats.s_strmax = MAX(stats.s_strmax, n); 659 } 660 661 return (E_SUCCESS); 662 } 663 664 static void 665 long_stat(const char *name, ulong_t value) 666 { 667 (void) printf(" %-36s= %lu\n", name, value); 668 } 669 670 static void 671 fp_stat(const char *name, float value) 672 { 673 (void) printf(" %-36s= %.2f\n", name, value); 674 } 675 676 static int 677 print_stats(void) 678 { 679 print_line("- CTF Statistics "); 680 681 long_stat("total number of data objects", stats.s_ndata); 682 (void) printf("\n"); 683 684 long_stat("total number of functions", stats.s_nfunc); 685 long_stat("total number of function arguments", stats.s_nargs); 686 long_stat("maximum argument list length", stats.s_argmax); 687 688 if (stats.s_nfunc != 0) { 689 fp_stat("average argument list length", 690 (float)stats.s_nargs / (float)stats.s_nfunc); 691 } 692 693 (void) printf("\n"); 694 695 long_stat("total number of types", stats.s_ntypes); 696 long_stat("total number of integers", stats.s_types[CTF_K_INTEGER]); 697 long_stat("total number of floats", stats.s_types[CTF_K_FLOAT]); 698 long_stat("total number of pointers", stats.s_types[CTF_K_POINTER]); 699 long_stat("total number of arrays", stats.s_types[CTF_K_ARRAY]); 700 long_stat("total number of func types", stats.s_types[CTF_K_FUNCTION]); 701 long_stat("total number of structs", stats.s_types[CTF_K_STRUCT]); 702 long_stat("total number of unions", stats.s_types[CTF_K_UNION]); 703 long_stat("total number of enums", stats.s_types[CTF_K_ENUM]); 704 long_stat("total number of forward tags", stats.s_types[CTF_K_FORWARD]); 705 long_stat("total number of typedefs", stats.s_types[CTF_K_TYPEDEF]); 706 long_stat("total number of volatile types", 707 stats.s_types[CTF_K_VOLATILE]); 708 long_stat("total number of const types", stats.s_types[CTF_K_CONST]); 709 long_stat("total number of restrict types", 710 stats.s_types[CTF_K_RESTRICT]); 711 long_stat("total number of unknowns (holes)", 712 stats.s_types[CTF_K_UNKNOWN]); 713 714 (void) printf("\n"); 715 716 long_stat("total number of struct members", stats.s_nsmem); 717 long_stat("maximum number of struct members", stats.s_smmax); 718 long_stat("total size of all structs", stats.s_nsbytes); 719 long_stat("maximum size of a struct", stats.s_sbmax); 720 721 if (stats.s_types[CTF_K_STRUCT] != 0) { 722 fp_stat("average number of struct members", 723 (float)stats.s_nsmem / (float)stats.s_types[CTF_K_STRUCT]); 724 fp_stat("average size of a struct", (float)stats.s_nsbytes / 725 (float)stats.s_types[CTF_K_STRUCT]); 726 } 727 728 (void) printf("\n"); 729 730 long_stat("total number of union members", stats.s_numem); 731 long_stat("maximum number of union members", stats.s_ummax); 732 long_stat("total size of all unions", stats.s_nubytes); 733 long_stat("maximum size of a union", stats.s_ubmax); 734 735 if (stats.s_types[CTF_K_UNION] != 0) { 736 fp_stat("average number of union members", 737 (float)stats.s_numem / (float)stats.s_types[CTF_K_UNION]); 738 fp_stat("average size of a union", (float)stats.s_nubytes / 739 (float)stats.s_types[CTF_K_UNION]); 740 } 741 742 (void) printf("\n"); 743 744 long_stat("total number of enum members", stats.s_nemem); 745 long_stat("maximum number of enum members", stats.s_emmax); 746 747 if (stats.s_types[CTF_K_ENUM] != 0) { 748 fp_stat("average number of enum members", 749 (float)stats.s_nemem / (float)stats.s_types[CTF_K_ENUM]); 750 } 751 752 (void) printf("\n"); 753 754 long_stat("total number of unique strings", stats.s_nstr); 755 long_stat("bytes of string data", stats.s_strlen); 756 long_stat("maximum string length", stats.s_strmax); 757 758 if (stats.s_nstr != 0) { 759 fp_stat("average string length", 760 (float)stats.s_strlen / (float)stats.s_nstr); 761 } 762 763 (void) printf("\n"); 764 return (E_SUCCESS); 765 } 766 767 static int 768 print_usage(FILE *fp, int verbose) 769 { 770 (void) fprintf(fp, "Usage: %s [-dfhlsSt] [-u file] file\n", getpname()); 771 772 if (verbose) { 773 (void) fprintf(fp, 774 "\t-d dump data object section\n" 775 "\t-f dump function section\n" 776 "\t-h dump file header\n" 777 "\t-l dump label table\n" 778 "\t-s dump string table\n" 779 "\t-S dump statistics\n" 780 "\t-t dump type section\n" 781 "\t-u save uncompressed CTF to a file\n"); 782 } 783 784 return (E_USAGE); 785 } 786 787 static Elf_Scn * 788 findelfscn(Elf *elf, GElf_Ehdr *ehdr, char *secname) 789 { 790 GElf_Shdr shdr; 791 Elf_Scn *scn; 792 char *name; 793 794 for (scn = NULL; (scn = elf_nextscn(elf, scn)) != NULL; ) { 795 if (gelf_getshdr(scn, &shdr) != NULL && (name = 796 elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name)) != NULL && 797 strcmp(name, secname) == 0) 798 return (scn); 799 } 800 801 return (NULL); 802 } 803 804 int 805 main(int argc, char *argv[]) 806 { 807 const char *filename = NULL; 808 const char *ufile = NULL; 809 int error = 0; 810 int c, fd, ufd; 811 812 ctf_data_t cd; 813 const ctf_preamble_t *pp; 814 ctf_header_t *hp; 815 Elf *elf; 816 GElf_Ehdr ehdr; 817 818 (void) elf_version(EV_CURRENT); 819 820 for (opterr = 0; optind < argc; optind++) { 821 while ((c = getopt(argc, argv, "dfhlsStu:")) != (int)EOF) { 822 switch (c) { 823 case 'd': 824 flags |= F_DATA; 825 break; 826 case 'f': 827 flags |= F_FUNC; 828 break; 829 case 'h': 830 flags |= F_HDR; 831 break; 832 case 'l': 833 flags |= F_LABEL; 834 break; 835 case 's': 836 flags |= F_STR; 837 break; 838 case 'S': 839 flags |= F_STATS; 840 break; 841 case 't': 842 flags |= F_TYPES; 843 break; 844 case 'u': 845 ufile = optarg; 846 break; 847 default: 848 if (optopt == '?') 849 return (print_usage(stdout, 1)); 850 warn("illegal option -- %c\n", optopt); 851 return (print_usage(stderr, 0)); 852 } 853 } 854 855 if (optind < argc) { 856 if (filename != NULL) 857 return (print_usage(stderr, 0)); 858 filename = argv[optind]; 859 } 860 } 861 862 if (filename == NULL) 863 return (print_usage(stderr, 0)); 864 865 if (flags == 0 && ufile == NULL) 866 flags = F_ALLMSK; 867 868 if ((fd = open(filename, O_RDONLY)) == -1) 869 die("failed to open %s", filename); 870 871 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) != NULL && 872 gelf_getehdr(elf, &ehdr) != NULL) { 873 874 Elf_Data *dp; 875 Elf_Scn *ctfscn = findelfscn(elf, &ehdr, ".SUNW_ctf"); 876 Elf_Scn *symscn; 877 GElf_Shdr ctfshdr; 878 879 if (ctfscn == NULL || (dp = elf_getdata(ctfscn, NULL)) == NULL) 880 die("%s does not contain .SUNW_ctf data\n", filename); 881 882 cd.cd_ctfdata = dp->d_buf; 883 cd.cd_ctflen = dp->d_size; 884 885 /* 886 * If the sh_link field of the CTF section header is non-zero 887 * it indicates which section contains the symbol table that 888 * should be used. We default to the .symtab section if sh_link 889 * is zero or if there's an error reading the section header. 890 */ 891 if (gelf_getshdr(ctfscn, &ctfshdr) != NULL && 892 ctfshdr.sh_link != 0) { 893 symscn = elf_getscn(elf, ctfshdr.sh_link); 894 } else { 895 symscn = findelfscn(elf, &ehdr, ".symtab"); 896 } 897 898 /* If we found a symbol table, find the corresponding strings */ 899 if (symscn != NULL) { 900 GElf_Shdr shdr; 901 Elf_Scn *symstrscn; 902 903 if (gelf_getshdr(symscn, &shdr) != NULL) { 904 symstrscn = elf_getscn(elf, shdr.sh_link); 905 906 cd.cd_nsyms = shdr.sh_size / shdr.sh_entsize; 907 cd.cd_symdata = elf_getdata(symscn, NULL); 908 cd.cd_strdata = elf_getdata(symstrscn, NULL); 909 } 910 } 911 } else { 912 struct stat st; 913 914 if (fstat(fd, &st) == -1) 915 die("failed to fstat %s", filename); 916 917 cd.cd_ctflen = st.st_size; 918 cd.cd_ctfdata = mmap(NULL, cd.cd_ctflen, PROT_READ, 919 MAP_PRIVATE, fd, 0); 920 if (cd.cd_ctfdata == MAP_FAILED) 921 die("failed to mmap %s", filename); 922 } 923 924 /* 925 * Get a pointer to the CTF data buffer and interpret the first portion 926 * as a ctf_header_t. Validate the magic number and size. 927 */ 928 929 if (cd.cd_ctflen < sizeof (ctf_preamble_t)) 930 die("%s does not contain a CTF preamble\n", filename); 931 932 /* LINTED - pointer alignment */ 933 pp = (const ctf_preamble_t *)cd.cd_ctfdata; 934 935 if (pp->ctp_magic != CTF_MAGIC) 936 die("%s does not appear to contain CTF data\n", filename); 937 938 if (pp->ctp_version == CTF_VERSION) { 939 /* LINTED - pointer alignment */ 940 hp = (ctf_header_t *)cd.cd_ctfdata; 941 cd.cd_ctfdata = (caddr_t)cd.cd_ctfdata + sizeof (ctf_header_t); 942 943 if (cd.cd_ctflen < sizeof (ctf_header_t)) { 944 die("%s does not contain a v%d CTF header\n", filename, 945 CTF_VERSION); 946 } 947 948 } else { 949 die("%s contains unsupported CTF version %d\n", filename, 950 pp->ctp_version); 951 } 952 953 /* 954 * If the data buffer is compressed, then malloc a buffer large enough 955 * to hold the decompressed data, and use zlib to decompress it. 956 */ 957 if (hp->cth_flags & CTF_F_COMPRESS) { 958 z_stream zstr; 959 void *buf; 960 int rc; 961 962 if ((buf = malloc(hp->cth_stroff + hp->cth_strlen)) == NULL) 963 die("failed to allocate decompression buffer"); 964 965 bzero(&zstr, sizeof (z_stream)); 966 zstr.next_in = (void *)cd.cd_ctfdata; 967 zstr.avail_in = cd.cd_ctflen; 968 zstr.next_out = buf; 969 zstr.avail_out = hp->cth_stroff + hp->cth_strlen; 970 971 if ((rc = inflateInit(&zstr)) != Z_OK) 972 die("failed to initialize zlib: %s\n", zError(rc)); 973 974 if ((rc = inflate(&zstr, Z_FINISH)) != Z_STREAM_END) 975 die("failed to decompress CTF data: %s\n", zError(rc)); 976 977 if ((rc = inflateEnd(&zstr)) != Z_OK) 978 die("failed to finish decompression: %s\n", zError(rc)); 979 980 if (zstr.total_out != hp->cth_stroff + hp->cth_strlen) 981 die("CTF data is corrupt -- short decompression\n"); 982 983 cd.cd_ctfdata = buf; 984 cd.cd_ctflen = hp->cth_stroff + hp->cth_strlen; 985 } 986 987 if (flags & F_HDR) 988 error |= print_header(hp, &cd); 989 if (flags & (F_LABEL)) 990 error |= print_labeltable(hp, &cd); 991 if (flags & (F_DATA | F_STATS)) 992 error |= read_data(hp, &cd); 993 if (flags & (F_FUNC | F_STATS)) 994 error |= read_funcs(hp, &cd); 995 if (flags & (F_TYPES | F_STATS)) 996 error |= read_types(hp, &cd); 997 if (flags & (F_STR | F_STATS)) 998 error |= read_strtab(hp, &cd); 999 if (flags & F_STATS) 1000 error |= print_stats(); 1001 1002 /* 1003 * If the -u option is specified, write the uncompressed CTF data to a 1004 * raw CTF file. CTF data can already be extracted compressed by 1005 * applying elfdump -w -N .SUNW_ctf to an ELF file, so we don't bother. 1006 */ 1007 if (ufile != NULL) { 1008 ctf_header_t h; 1009 1010 bcopy(hp, &h, sizeof (h)); 1011 h.cth_flags &= ~CTF_F_COMPRESS; 1012 1013 if ((ufd = open(ufile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0 || 1014 write(ufd, &h, sizeof (h)) != sizeof (h) || 1015 write(ufd, cd.cd_ctfdata, cd.cd_ctflen) != cd.cd_ctflen) { 1016 warn("failed to write CTF data to '%s'", ufile); 1017 error |= E_ERROR; 1018 } 1019 1020 (void) close(ufd); 1021 } 1022 1023 if (elf != NULL) 1024 (void) elf_end(elf); 1025 1026 (void) close(fd); 1027 return (error); 1028 } 1029