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