]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - cddl/contrib/opensolaris/tools/ctf/dump/dump.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / cddl / contrib / opensolaris / tools / ctf / dump / dump.c
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         void *v = (void *) (cd->cd_ctfdata + hp->cth_lbloff);
207         const ctf_lblent_t *ctl = v;
208         ulong_t i, n = (hp->cth_objtoff - hp->cth_lbloff) / sizeof (*ctl);
209
210         print_line("- Label Table ");
211
212         if (hp->cth_lbloff & 3)
213                 WARN("cth_lbloff is not aligned properly\n");
214         if (hp->cth_lbloff >= cd->cd_ctflen)
215                 WARN("file is truncated or cth_lbloff is corrupt\n");
216         if (hp->cth_objtoff >= cd->cd_ctflen)
217                 WARN("file is truncated or cth_objtoff is corrupt\n");
218         if (hp->cth_lbloff > hp->cth_objtoff)
219                 WARN("file is corrupt -- cth_lbloff > cth_objtoff\n");
220
221         for (i = 0; i < n; i++, ctl++) {
222                 (void) printf("  %5u %s\n", ctl->ctl_typeidx,
223                     ref_to_str(ctl->ctl_label, hp, cd));
224         }
225
226         return (E_SUCCESS);
227 }
228
229 /*
230  * Given the current symbol index (-1 to start at the beginning of the symbol
231  * table) and the type of symbol to match, this function returns the index of
232  * the next matching symbol (if any), and places the name of that symbol in
233  * *namep.  If no symbol is found, -1 is returned.
234  */
235 static int
236 next_sym(const ctf_data_t *cd, const int symidx, const uchar_t matchtype,
237     char **namep)
238 {
239         int i;
240
241         for (i = symidx + 1; i < cd->cd_nsyms; i++) {
242                 GElf_Sym sym;
243                 char *name;
244                 int type;
245
246                 if (gelf_getsym(cd->cd_symdata, i, &sym) == 0)
247                         return (-1);
248
249                 name = (char *)cd->cd_strdata->d_buf + sym.st_name;
250                 type = GELF_ST_TYPE(sym.st_info);
251
252                 /*
253                  * Skip various types of symbol table entries.
254                  */
255                 if (type != matchtype || ignore_symbol(&sym, name))
256                         continue;
257
258                 /* Found one */
259                 *namep = name;
260                 return (i);
261         }
262
263         return (-1);
264 }
265
266 static int
267 read_data(const ctf_header_t *hp, const ctf_data_t *cd)
268 {
269         void *v = (void *) (cd->cd_ctfdata + hp->cth_objtoff);
270         const ushort_t *idp = v;
271         ulong_t n = (hp->cth_funcoff - hp->cth_objtoff) / sizeof (ushort_t);
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                         int nextsym;
291
292                         if (cd->cd_symdata == NULL || (nextsym = next_sym(cd,
293                             symidx, STT_OBJECT, &name)) < 0)
294                                 name = NULL;
295                         else
296                                 symidx = nextsym;
297
298                         len = printf("  [%u] %u", i, *idp++);
299                         if (name != NULL)
300                                 (void) printf("%*s%s (%u)", (15 - len), "",
301                                     name, symidx);
302                         (void) putchar('\n');
303                 }
304         }
305
306         stats.s_ndata = n;
307         return (E_SUCCESS);
308 }
309
310 static int
311 read_funcs(const ctf_header_t *hp, const ctf_data_t *cd)
312 {
313         void *v = (void *) (cd->cd_ctfdata + hp->cth_funcoff);
314         const ushort_t *fp = v;
315
316         v = (void *) (cd->cd_ctfdata + hp->cth_typeoff);
317         const ushort_t *end = v;
318
319         ulong_t id;
320         int symidx;
321
322         if (flags != F_STATS)
323                 print_line("- Functions ");
324
325         if (hp->cth_funcoff & 1)
326                 WARN("cth_funcoff is not aligned properly\n");
327         if (hp->cth_funcoff >= cd->cd_ctflen)
328                 WARN("file is truncated or cth_funcoff is corrupt\n");
329         if (hp->cth_typeoff >= cd->cd_ctflen)
330                 WARN("file is truncated or cth_typeoff is corrupt\n");
331         if (hp->cth_funcoff > hp->cth_typeoff)
332                 WARN("file is corrupt -- cth_funcoff > cth_typeoff\n");
333
334         for (symidx = -1, id = 0; fp < end; id++) {
335                 ushort_t info = *fp++;
336                 ushort_t kind = CTF_INFO_KIND(info);
337                 ushort_t n = CTF_INFO_VLEN(info);
338                 ushort_t i;
339                 int nextsym;
340                 char *name;
341
342                 if (cd->cd_symdata == NULL || (nextsym = next_sym(cd, symidx,
343                     STT_FUNC, &name)) < 0)
344                         name = NULL;
345                 else
346                         symidx = nextsym;
347
348                 if (kind == CTF_K_UNKNOWN && n == 0)
349                         continue; /* skip padding */
350
351                 if (kind != CTF_K_FUNCTION) {
352                         (void) printf("  [%lu] unexpected kind -- %u\n",
353                             id, kind);
354                         return (E_ERROR);
355                 }
356
357                 if (fp + n > end) {
358                         (void) printf("  [%lu] vlen %u extends past section "
359                             "boundary\n", id, n);
360                         return (E_ERROR);
361                 }
362
363                 if (flags != F_STATS) {
364                         (void) printf("  [%lu] FUNC ", id);
365                         if (name != NULL)
366                                 (void) printf("(%s) ", name);
367                         (void) printf("returns: %u args: (", *fp++);
368
369                         if (n != 0) {
370                                 (void) printf("%u", *fp++);
371                                 for (i = 1; i < n; i++)
372                                         (void) printf(", %u", *fp++);
373                         }
374
375                         (void) printf(")\n");
376                 } else
377                         fp += n + 1; /* skip to next function definition */
378
379                 stats.s_nfunc++;
380                 stats.s_nargs += n;
381                 stats.s_argmax = MAX(stats.s_argmax, n);
382         }
383
384         return (E_SUCCESS);
385 }
386
387 static int
388 read_types(const ctf_header_t *hp, const ctf_data_t *cd)
389 {
390         void *v = (void *) (cd->cd_ctfdata + hp->cth_typeoff);
391         const ctf_type_t *tp = v;
392
393         v = (void *) (cd->cd_ctfdata + hp->cth_stroff);
394         const ctf_type_t *end = v;
395
396         ulong_t id;
397
398         if (flags != F_STATS)
399                 print_line("- Types ");
400
401         if (hp->cth_typeoff & 3)
402                 WARN("cth_typeoff is not aligned properly\n");
403         if (hp->cth_typeoff >= cd->cd_ctflen)
404                 WARN("file is truncated or cth_typeoff is corrupt\n");
405         if (hp->cth_stroff >= cd->cd_ctflen)
406                 WARN("file is truncated or cth_stroff is corrupt\n");
407         if (hp->cth_typeoff > hp->cth_stroff)
408                 WARN("file is corrupt -- cth_typeoff > cth_stroff\n");
409
410         id = 1;
411         if (hp->cth_parlabel || hp->cth_parname)
412                 id += 1 << CTF_PARENT_SHIFT;
413
414         for (/* */; tp < end; id++) {
415                 ulong_t i, n = CTF_INFO_VLEN(tp->ctt_info);
416                 size_t size, increment, vlen = 0;
417                 int kind = CTF_INFO_KIND(tp->ctt_info);
418
419                 union {
420                         const void *ptr;
421                         ctf_array_t *ap;
422                         const ctf_member_t *mp;
423                         const ctf_lmember_t *lmp;
424                         const ctf_enum_t *ep;
425                         const ushort_t *argp;
426                 } u;
427
428                 if (flags != F_STATS) {
429                         (void) printf("  %c%lu%c ",
430                             "[<"[CTF_INFO_ISROOT(tp->ctt_info)], id,
431                             "]>"[CTF_INFO_ISROOT(tp->ctt_info)]);
432                 }
433
434                 if (tp->ctt_size == CTF_LSIZE_SENT) {
435                         increment = sizeof (ctf_type_t);
436                         size = (size_t)CTF_TYPE_LSIZE(tp);
437                 } else {
438                         increment = sizeof (ctf_stype_t);
439                         size = tp->ctt_size;
440                 }
441                 u.ptr = (const char *)tp + increment;
442
443                 switch (kind) {
444                 case CTF_K_INTEGER:
445                         if (flags != F_STATS) {
446                                 uint_t encoding = *((const uint_t *)u.ptr);
447
448                                 (void) printf("INTEGER %s encoding=%s offset=%u"
449                                     " bits=%u", ref_to_str(tp->ctt_name, hp,
450                                     cd), int_encoding_to_str(
451                                     CTF_INT_ENCODING(encoding)),
452                                     CTF_INT_OFFSET(encoding),
453                                     CTF_INT_BITS(encoding));
454                         }
455                         vlen = sizeof (uint_t);
456                         break;
457
458                 case CTF_K_FLOAT:
459                         if (flags != F_STATS) {
460                                 uint_t encoding = *((const uint_t *)u.ptr);
461
462                                 (void) printf("FLOAT %s encoding=%s offset=%u "
463                                     "bits=%u", ref_to_str(tp->ctt_name, hp,
464                                     cd), fp_encoding_to_str(
465                                     CTF_FP_ENCODING(encoding)),
466                                     CTF_FP_OFFSET(encoding),
467                                     CTF_FP_BITS(encoding));
468                         }
469                         vlen = sizeof (uint_t);
470                         break;
471
472                 case CTF_K_POINTER:
473                         if (flags != F_STATS) {
474                                 (void) printf("POINTER %s refers to %u",
475                                     ref_to_str(tp->ctt_name, hp, cd),
476                                     tp->ctt_type);
477                         }
478                         break;
479
480                 case CTF_K_ARRAY:
481                         if (flags != F_STATS) {
482                                 (void) printf("ARRAY %s content: %u index: %u "
483                                     "nelems: %u\n", ref_to_str(tp->ctt_name,
484                                     hp, cd), u.ap->cta_contents,
485                                     u.ap->cta_index, u.ap->cta_nelems);
486                         }
487                         vlen = sizeof (ctf_array_t);
488                         break;
489
490                 case CTF_K_FUNCTION:
491                         if (flags != F_STATS) {
492                                 (void) printf("FUNCTION %s returns: %u args: (",
493                                     ref_to_str(tp->ctt_name, hp, cd),
494                                     tp->ctt_type);
495
496                                 if (n != 0) {
497                                         (void) printf("%u", *u.argp++);
498                                         for (i = 1; i < n; i++, u.argp++)
499                                                 (void) printf(", %u", *u.argp);
500                                 }
501
502                                 (void) printf(")");
503                         }
504
505                         vlen = sizeof (ushort_t) * (n + (n & 1));
506                         break;
507
508                 case CTF_K_STRUCT:
509                 case CTF_K_UNION:
510                         if (kind == CTF_K_STRUCT) {
511                                 stats.s_nsmem += n;
512                                 stats.s_smmax = MAX(stats.s_smmax, n);
513                                 stats.s_nsbytes += size;
514                                 stats.s_sbmax = MAX(stats.s_sbmax, size);
515
516                                 if (flags != F_STATS)
517                                         (void) printf("STRUCT");
518                         } else {
519                                 stats.s_numem += n;
520                                 stats.s_ummax = MAX(stats.s_ummax, n);
521                                 stats.s_nubytes += size;
522                                 stats.s_ubmax = MAX(stats.s_ubmax, size);
523
524                                 if (flags != F_STATS)
525                                         (void) printf("UNION");
526                         }
527
528                         if (flags != F_STATS) {
529                                 (void) printf(" %s (%zd bytes)\n",
530                                     ref_to_str(tp->ctt_name, hp, cd), size);
531
532                                 if (size >= CTF_LSTRUCT_THRESH) {
533                                         for (i = 0; i < n; i++, u.lmp++) {
534                                                 (void) printf(
535                                                     "\t%s type=%u off=%llu\n",
536                                                     ref_to_str(u.lmp->ctlm_name,
537                                                     hp, cd), u.lmp->ctlm_type,
538                                                     (unsigned long long)
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, const 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 = NULL;
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 = NULL;
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         void *v = (void *) cd.cd_ctfdata;
933         pp = v;
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                 v = (void *) cd.cd_ctfdata;
940                 hp = v;
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) != (int) 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 }