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 */
62static 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
74static 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
96typedef 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
110static const char *
111ref_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
131static const char *
132int_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
154static const char *
155fp_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
173static void
174print_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
181static int
182print_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
203static int
204print_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 */
235static int
236next_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
266static int
267read_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
310static int
311read_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
387static int
388read_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
633static int
634read_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
664static void
665long_stat(const char *name, ulong_t value)
666{
667	(void) printf("  %-36s= %lu\n", name, value);
668}
669
670static void
671fp_stat(const char *name, float value)
672{
673	(void) printf("  %-36s= %.2f\n", name, value);
674}
675
676static int
677print_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
767static int
768print_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
787static Elf_Scn *
788findelfscn(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
804int
805main(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}
1029