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