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 *
ref_to_str(uint_t name,const ctf_header_t * hp,const ctf_data_t * cd)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 *
int_encoding_to_str(uint_t encoding)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 *
fp_encoding_to_str(uint_t encoding)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
print_line(const char * s)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
print_header(const ctf_header_t * hp,const ctf_data_t * cd)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
print_labeltable(const ctf_header_t * hp,const ctf_data_t * cd)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
next_sym(const ctf_data_t * cd,const int symidx,const uchar_t matchtype,char ** namep)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
read_data(const ctf_header_t * hp,const ctf_data_t * cd)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
read_funcs(const ctf_header_t * hp,const ctf_data_t * cd)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
read_types(const ctf_header_t * hp,const ctf_data_t * cd)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
read_strtab(const ctf_header_t * hp,const ctf_data_t * cd)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
long_stat(const char * name,ulong_t value)754 long_stat(const char *name, ulong_t value)
755 {
756 (void) printf(" %-36s= %lu\n", name, value);
757 }
758
759 static void
fp_stat(const char * name,float value)760 fp_stat(const char *name, float value)
761 {
762 (void) printf(" %-36s= %.2f\n", name, value);
763 }
764
765 static int
print_stats(void)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
print_usage(FILE * fp,int verbose)857 print_usage(FILE *fp, int verbose)
858 {
859 (void) fprintf(fp, "Usage: %s [-dfhlsSt] [-u file] file\n", getprogname());
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 *
findelfscn(Elf * elf,GElf_Ehdr * ehdr,const char * secname)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
main(int argc,char * argv[])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