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 *
ref_to_str(uint_t name,const ctf_header_t * hp,const ctf_data_t * cd)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 *
int_encoding_to_str(uint_t encoding)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 *
fp_encoding_to_str(uint_t encoding)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
print_line(const char * s)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
print_header(const ctf_header_t * hp,const ctf_data_t * cd)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
print_labeltable(const ctf_header_t * hp,const ctf_data_t * cd)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
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 /* 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
read_funcs(const ctf_header_t * hp,const ctf_data_t * cd)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
read_types(const ctf_header_t * hp,const ctf_data_t * cd)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
read_strtab(const ctf_header_t * hp,const ctf_data_t * cd)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
long_stat(const char * name,ulong_t value)665 long_stat(const char *name, ulong_t value)
666 {
667 (void) printf(" %-36s= %lu\n", name, value);
668 }
669
670 static void
fp_stat(const char * name,float value)671 fp_stat(const char *name, float value)
672 {
673 (void) printf(" %-36s= %.2f\n", name, value);
674 }
675
676 static int
print_stats(void)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
print_usage(FILE * fp,int verbose)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 *
findelfscn(Elf * elf,GElf_Ehdr * ehdr,char * secname)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
main(int argc,char * argv[])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