1178481Sjb/*
2178481Sjb * CDDL HEADER START
3178481Sjb *
4178481Sjb * The contents of this file are subject to the terms of the
5178481Sjb * Common Development and Distribution License, Version 1.0 only
6178481Sjb * (the "License").  You may not use this file except in compliance
7178481Sjb * with the License.
8178481Sjb *
9178481Sjb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10178481Sjb * or http://www.opensolaris.org/os/licensing.
11178481Sjb * See the License for the specific language governing permissions
12178481Sjb * and limitations under the License.
13178481Sjb *
14178481Sjb * When distributing Covered Code, include this CDDL HEADER in each
15178481Sjb * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16178481Sjb * If applicable, add the following below this CDDL HEADER, with the
17178481Sjb * fields enclosed by brackets "[]" replaced with your own identifying
18178481Sjb * information: Portions Copyright [yyyy] [name of copyright owner]
19178481Sjb *
20178481Sjb * CDDL HEADER END
21178481Sjb */
22178481Sjb/*
23178481Sjb * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24178481Sjb * Use is subject to license terms.
25178481Sjb */
26178481Sjb
27178481Sjb#pragma ident	"%Z%%M%	%I%	%E% SMI"
28178481Sjb
29178481Sjb#include <sys/types.h>
30178481Sjb#include <sys/sysmacros.h>
31178481Sjb#include <sys/stat.h>
32178481Sjb#include <sys/mman.h>
33178481Sjb
34178481Sjb#include <strings.h>
35178481Sjb#include <unistd.h>
36178481Sjb#include <stdlib.h>
37178481Sjb#include <stdio.h>
38178481Sjb#include <fcntl.h>
39178481Sjb#include <gelf.h>
40178481Sjb#include <zlib.h>
41178481Sjb
42178481Sjb#include "ctf_headers.h"
43178481Sjb#include "utils.h"
44178481Sjb#include "symbol.h"
45178481Sjb
46178481Sjb#define	WARN(x)	{ warn(x); return (E_ERROR); }
47178481Sjb
48178481Sjb/*
49178481Sjb * Flags that indicate what data is to be displayed.  An explicit `all' value is
50178481Sjb * provided to allow the code to distinguish between a request for everything
51178481Sjb * (currently requested by invoking ctfdump without flags) and individual
52178481Sjb * requests for all of the types of data (an invocation with all flags).  In the
53178481Sjb * former case, we want to be able to implicitly adjust the definition of `all'
54178481Sjb * based on the CTF version of the file being dumped.  For example, if a v2 file
55178481Sjb * is being dumped, `all' includes F_LABEL - a request to dump the label
56178481Sjb * section.  If a v1 file is being dumped, `all' does not include F_LABEL,
57178481Sjb * because v1 CTF doesn't support labels.  We need to be able to distinguish
58178481Sjb * between `ctfdump foo', which has an implicit request for labels if `foo'
59178481Sjb * supports them, and `ctfdump -l foo', which has an explicity request.  In the
60178481Sjb * latter case, we exit with an error if `foo' is a v1 CTF file.
61178481Sjb */
62178481Sjbstatic enum {
63178481Sjb	F_DATA	= 0x01,		/* show data object section */
64178481Sjb	F_FUNC	= 0x02,		/* show function section */
65178481Sjb	F_HDR	= 0x04,		/* show header */
66178481Sjb	F_STR	= 0x08,		/* show string table */
67178481Sjb	F_TYPES	= 0x10,		/* show type section */
68178481Sjb	F_STATS = 0x20, 	/* show statistics */
69178481Sjb	F_LABEL	= 0x40,		/* show label section */
70178481Sjb	F_ALL	= 0x80,		/* explicit request for `all' */
71178481Sjb	F_ALLMSK = 0xff		/* show all sections and statistics */
72178481Sjb} flags = 0;
73178481Sjb
74178481Sjbstatic struct {
75178481Sjb	ulong_t s_ndata;	/* total number of data objects */
76178481Sjb	ulong_t s_nfunc;	/* total number of functions */
77178481Sjb	ulong_t s_nargs;	/* total number of function arguments */
78178481Sjb	ulong_t s_argmax;	/* longest argument list */
79178481Sjb	ulong_t s_ntypes;	/* total number of types */
80178481Sjb	ulong_t s_types[16];	/* number of types by kind */
81178481Sjb	ulong_t s_nsmem;	/* total number of struct members */
82178481Sjb	ulong_t s_nsbytes;	/* total size of all structs */
83178481Sjb	ulong_t s_smmax;	/* largest struct in terms of members */
84178481Sjb	ulong_t s_sbmax;	/* largest struct in terms of bytes */
85178481Sjb	ulong_t s_numem;	/* total number of union members */
86178481Sjb	ulong_t s_nubytes;	/* total size of all unions */
87178481Sjb	ulong_t s_ummax;	/* largest union in terms of members */
88178481Sjb	ulong_t s_ubmax;	/* largest union in terms of bytes */
89178481Sjb	ulong_t s_nemem;	/* total number of enum members */
90178481Sjb	ulong_t s_emmax;	/* largest enum in terms of members */
91178481Sjb	ulong_t s_nstr;		/* total number of strings */
92178481Sjb	size_t s_strlen;	/* total length of all strings */
93178481Sjb	size_t s_strmax;	/* longest string length */
94178481Sjb} stats;
95178481Sjb
96178481Sjbtypedef struct ctf_data {
97178481Sjb	caddr_t cd_ctfdata;	/* Pointer to the CTF data */
98178481Sjb	size_t cd_ctflen;	/* Length of CTF data */
99178481Sjb
100178481Sjb	/*
101178481Sjb	 * cd_symdata will be non-NULL if the CTF data is being retrieved from
102178481Sjb	 * an ELF file with a symbol table.  cd_strdata and cd_nsyms should be
103178481Sjb	 * used only if cd_symdata is non-NULL.
104178481Sjb	 */
105178481Sjb	Elf_Data *cd_symdata;	/* Symbol table */
106178481Sjb	Elf_Data *cd_strdata;	/* Symbol table strings */
107178481Sjb	int cd_nsyms;		/* Number of symbol table entries */
108178481Sjb} ctf_data_t;
109178481Sjb
110178481Sjbstatic const char *
111178481Sjbref_to_str(uint_t name, const ctf_header_t *hp, const ctf_data_t *cd)
112178481Sjb{
113178481Sjb	size_t offset = CTF_NAME_OFFSET(name);
114178481Sjb	const char *s = cd->cd_ctfdata + hp->cth_stroff + offset;
115178481Sjb
116178481Sjb	if (CTF_NAME_STID(name) != CTF_STRTAB_0)
117178481Sjb		return ("<< ??? - name in external strtab >>");
118178481Sjb
119178481Sjb	if (offset >= hp->cth_strlen)
120178481Sjb		return ("<< ??? - name exceeds strlab len >>");
121178481Sjb
122178481Sjb	if (hp->cth_stroff + offset >= cd->cd_ctflen)
123178481Sjb		return ("<< ??? - file truncated >>");
124178481Sjb
125178481Sjb	if (s[0] == '\0')
126178481Sjb		return ("(anon)");
127178481Sjb
128178481Sjb	return (s);
129178481Sjb}
130178481Sjb
131178481Sjbstatic const char *
132178481Sjbint_encoding_to_str(uint_t encoding)
133178481Sjb{
134178481Sjb	static char buf[32];
135178481Sjb
136178481Sjb	if (encoding == 0 || (encoding & ~(CTF_INT_SIGNED | CTF_INT_CHAR |
137178481Sjb	    CTF_INT_BOOL | CTF_INT_VARARGS)) != 0)
138178481Sjb		(void) snprintf(buf, sizeof (buf), " 0x%x", encoding);
139178481Sjb	else {
140178481Sjb		buf[0] = '\0';
141178481Sjb		if (encoding & CTF_INT_SIGNED)
142178481Sjb			(void) strcat(buf, " SIGNED");
143178481Sjb		if (encoding & CTF_INT_CHAR)
144178481Sjb			(void) strcat(buf, " CHAR");
145178481Sjb		if (encoding & CTF_INT_BOOL)
146178481Sjb			(void) strcat(buf, " BOOL");
147178481Sjb		if (encoding & CTF_INT_VARARGS)
148178481Sjb			(void) strcat(buf, " VARARGS");
149178481Sjb	}
150178481Sjb
151178481Sjb	return (buf + 1);
152178481Sjb}
153178481Sjb
154178481Sjbstatic const char *
155178481Sjbfp_encoding_to_str(uint_t encoding)
156178481Sjb{
157178481Sjb	static const char *const encs[] = {
158178481Sjb		NULL, "SINGLE", "DOUBLE", "COMPLEX", "DCOMPLEX", "LDCOMPLEX",
159178481Sjb		"LDOUBLE", "INTERVAL", "DINTERVAL", "LDINTERVAL", "IMAGINARY",
160178481Sjb		"DIMAGINARY", "LDIMAGINARY"
161178481Sjb	};
162178481Sjb
163178481Sjb	static char buf[16];
164178481Sjb
165178481Sjb	if (encoding < 1 || encoding >= (sizeof (encs) / sizeof (char *))) {
166178481Sjb		(void) snprintf(buf, sizeof (buf), "%u", encoding);
167178481Sjb		return (buf);
168178481Sjb	}
169178481Sjb
170178481Sjb	return (encs[encoding]);
171178481Sjb}
172178481Sjb
173178481Sjbstatic void
174178481Sjbprint_line(const char *s)
175178481Sjb{
176178481Sjb	static const char line[] = "----------------------------------------"
177178481Sjb	    "----------------------------------------";
178178481Sjb	(void) printf("\n%s%.*s\n\n", s, (int)(78 - strlen(s)), line);
179178481Sjb}
180178481Sjb
181178481Sjbstatic int
182178481Sjbprint_header(const ctf_header_t *hp, const ctf_data_t *cd)
183178481Sjb{
184178481Sjb	print_line("- CTF Header ");
185178481Sjb
186178481Sjb	(void) printf("  cth_magic    = 0x%04x\n", hp->cth_magic);
187178481Sjb	(void) printf("  cth_version  = %u\n", hp->cth_version);
188178481Sjb	(void) printf("  cth_flags    = 0x%02x\n", hp->cth_flags);
189178481Sjb	(void) printf("  cth_parlabel = %s\n",
190178481Sjb	    ref_to_str(hp->cth_parlabel, hp, cd));
191178481Sjb	(void) printf("  cth_parname  = %s\n",
192178481Sjb	    ref_to_str(hp->cth_parname, hp, cd));
193178481Sjb	(void) printf("  cth_lbloff   = %u\n", hp->cth_lbloff);
194178481Sjb	(void) printf("  cth_objtoff  = %u\n", hp->cth_objtoff);
195178481Sjb	(void) printf("  cth_funcoff  = %u\n", hp->cth_funcoff);
196178481Sjb	(void) printf("  cth_typeoff  = %u\n", hp->cth_typeoff);
197178481Sjb	(void) printf("  cth_stroff   = %u\n", hp->cth_stroff);
198178481Sjb	(void) printf("  cth_strlen   = %u\n", hp->cth_strlen);
199178481Sjb
200178481Sjb	return (E_SUCCESS);
201178481Sjb}
202178481Sjb
203178481Sjbstatic int
204178481Sjbprint_labeltable(const ctf_header_t *hp, const ctf_data_t *cd)
205178481Sjb{
206178542Sjb	void *v = (void *) (cd->cd_ctfdata + hp->cth_lbloff);
207178542Sjb	const ctf_lblent_t *ctl = v;
208178481Sjb	ulong_t i, n = (hp->cth_objtoff - hp->cth_lbloff) / sizeof (*ctl);
209178481Sjb
210178481Sjb	print_line("- Label Table ");
211178481Sjb
212178481Sjb	if (hp->cth_lbloff & 3)
213178481Sjb		WARN("cth_lbloff is not aligned properly\n");
214178481Sjb	if (hp->cth_lbloff >= cd->cd_ctflen)
215178481Sjb		WARN("file is truncated or cth_lbloff is corrupt\n");
216178481Sjb	if (hp->cth_objtoff >= cd->cd_ctflen)
217178481Sjb		WARN("file is truncated or cth_objtoff is corrupt\n");
218178481Sjb	if (hp->cth_lbloff > hp->cth_objtoff)
219178481Sjb		WARN("file is corrupt -- cth_lbloff > cth_objtoff\n");
220178481Sjb
221178481Sjb	for (i = 0; i < n; i++, ctl++) {
222178481Sjb		(void) printf("  %5u %s\n", ctl->ctl_typeidx,
223178481Sjb		    ref_to_str(ctl->ctl_label, hp, cd));
224178481Sjb	}
225178481Sjb
226178481Sjb	return (E_SUCCESS);
227178481Sjb}
228178481Sjb
229178481Sjb/*
230178481Sjb * Given the current symbol index (-1 to start at the beginning of the symbol
231178481Sjb * table) and the type of symbol to match, this function returns the index of
232178481Sjb * the next matching symbol (if any), and places the name of that symbol in
233178481Sjb * *namep.  If no symbol is found, -1 is returned.
234178481Sjb */
235178481Sjbstatic int
236178481Sjbnext_sym(const ctf_data_t *cd, const int symidx, const uchar_t matchtype,
237178481Sjb    char **namep)
238178481Sjb{
239178481Sjb	int i;
240178481Sjb
241178481Sjb	for (i = symidx + 1; i < cd->cd_nsyms; i++) {
242178481Sjb		GElf_Sym sym;
243178481Sjb		char *name;
244178481Sjb		int type;
245178481Sjb
246178481Sjb		if (gelf_getsym(cd->cd_symdata, i, &sym) == 0)
247178481Sjb			return (-1);
248178481Sjb
249178481Sjb		name = (char *)cd->cd_strdata->d_buf + sym.st_name;
250178481Sjb		type = GELF_ST_TYPE(sym.st_info);
251178481Sjb
252178481Sjb		/*
253178481Sjb		 * Skip various types of symbol table entries.
254178481Sjb		 */
255178481Sjb		if (type != matchtype || ignore_symbol(&sym, name))
256178481Sjb			continue;
257178481Sjb
258178481Sjb		/* Found one */
259178481Sjb		*namep = name;
260178481Sjb		return (i);
261178481Sjb	}
262178481Sjb
263178481Sjb	return (-1);
264178481Sjb}
265178481Sjb
266178481Sjbstatic int
267178481Sjbread_data(const ctf_header_t *hp, const ctf_data_t *cd)
268178481Sjb{
269178542Sjb	void *v = (void *) (cd->cd_ctfdata + hp->cth_objtoff);
270178542Sjb	const ushort_t *idp = v;
271178481Sjb	ulong_t n = (hp->cth_funcoff - hp->cth_objtoff) / sizeof (ushort_t);
272178481Sjb
273178481Sjb	if (flags != F_STATS)
274178481Sjb		print_line("- Data Objects ");
275178481Sjb
276178481Sjb	if (hp->cth_objtoff & 1)
277178481Sjb		WARN("cth_objtoff is not aligned properly\n");
278178481Sjb	if (hp->cth_objtoff >= cd->cd_ctflen)
279178481Sjb		WARN("file is truncated or cth_objtoff is corrupt\n");
280178481Sjb	if (hp->cth_funcoff >= cd->cd_ctflen)
281178481Sjb		WARN("file is truncated or cth_funcoff is corrupt\n");
282178481Sjb	if (hp->cth_objtoff > hp->cth_funcoff)
283178481Sjb		WARN("file is corrupt -- cth_objtoff > cth_funcoff\n");
284178481Sjb
285178481Sjb	if (flags != F_STATS) {
286178481Sjb		int symidx, len, i;
287178481Sjb		char *name = NULL;
288178481Sjb
289178542Sjb		for (symidx = -1, i = 0; i < (int) n; i++) {
290178481Sjb			int nextsym;
291178481Sjb
292178481Sjb			if (cd->cd_symdata == NULL || (nextsym = next_sym(cd,
293178481Sjb			    symidx, STT_OBJECT, &name)) < 0)
294178481Sjb				name = NULL;
295178481Sjb			else
296178481Sjb				symidx = nextsym;
297178481Sjb
298178481Sjb			len = printf("  [%u] %u", i, *idp++);
299178481Sjb			if (name != NULL)
300178481Sjb				(void) printf("%*s%s (%u)", (15 - len), "",
301178481Sjb				    name, symidx);
302178481Sjb			(void) putchar('\n');
303178481Sjb		}
304178481Sjb	}
305178481Sjb
306178481Sjb	stats.s_ndata = n;
307178481Sjb	return (E_SUCCESS);
308178481Sjb}
309178481Sjb
310178481Sjbstatic int
311178481Sjbread_funcs(const ctf_header_t *hp, const ctf_data_t *cd)
312178481Sjb{
313178542Sjb	void *v = (void *) (cd->cd_ctfdata + hp->cth_funcoff);
314178542Sjb	const ushort_t *fp = v;
315178481Sjb
316178542Sjb	v = (void *) (cd->cd_ctfdata + hp->cth_typeoff);
317178542Sjb	const ushort_t *end = v;
318178481Sjb
319178481Sjb	ulong_t id;
320178481Sjb	int symidx;
321178481Sjb
322178481Sjb	if (flags != F_STATS)
323178481Sjb		print_line("- Functions ");
324178481Sjb
325178481Sjb	if (hp->cth_funcoff & 1)
326178481Sjb		WARN("cth_funcoff is not aligned properly\n");
327178481Sjb	if (hp->cth_funcoff >= cd->cd_ctflen)
328178481Sjb		WARN("file is truncated or cth_funcoff is corrupt\n");
329178481Sjb	if (hp->cth_typeoff >= cd->cd_ctflen)
330178481Sjb		WARN("file is truncated or cth_typeoff is corrupt\n");
331178481Sjb	if (hp->cth_funcoff > hp->cth_typeoff)
332178481Sjb		WARN("file is corrupt -- cth_funcoff > cth_typeoff\n");
333178481Sjb
334178481Sjb	for (symidx = -1, id = 0; fp < end; id++) {
335178481Sjb		ushort_t info = *fp++;
336178481Sjb		ushort_t kind = CTF_INFO_KIND(info);
337178481Sjb		ushort_t n = CTF_INFO_VLEN(info);
338178481Sjb		ushort_t i;
339178481Sjb		int nextsym;
340178481Sjb		char *name;
341178481Sjb
342178481Sjb		if (cd->cd_symdata == NULL || (nextsym = next_sym(cd, symidx,
343178481Sjb		    STT_FUNC, &name)) < 0)
344178481Sjb			name = NULL;
345178481Sjb		else
346178481Sjb			symidx = nextsym;
347178481Sjb
348178481Sjb		if (kind == CTF_K_UNKNOWN && n == 0)
349178481Sjb			continue; /* skip padding */
350178481Sjb
351178481Sjb		if (kind != CTF_K_FUNCTION) {
352178481Sjb			(void) printf("  [%lu] unexpected kind -- %u\n",
353178481Sjb			    id, kind);
354178481Sjb			return (E_ERROR);
355178481Sjb		}
356178481Sjb
357178481Sjb		if (fp + n > end) {
358178481Sjb			(void) printf("  [%lu] vlen %u extends past section "
359178481Sjb			    "boundary\n", id, n);
360178481Sjb			return (E_ERROR);
361178481Sjb		}
362178481Sjb
363178481Sjb		if (flags != F_STATS) {
364178481Sjb			(void) printf("  [%lu] FUNC ", id);
365178481Sjb			if (name != NULL)
366178481Sjb				(void) printf("(%s) ", name);
367178481Sjb			(void) printf("returns: %u args: (", *fp++);
368178481Sjb
369178481Sjb			if (n != 0) {
370178481Sjb				(void) printf("%u", *fp++);
371178481Sjb				for (i = 1; i < n; i++)
372178481Sjb					(void) printf(", %u", *fp++);
373178481Sjb			}
374178481Sjb
375178481Sjb			(void) printf(")\n");
376178481Sjb		} else
377178481Sjb			fp += n + 1; /* skip to next function definition */
378178481Sjb
379178481Sjb		stats.s_nfunc++;
380178481Sjb		stats.s_nargs += n;
381178481Sjb		stats.s_argmax = MAX(stats.s_argmax, n);
382178481Sjb	}
383178481Sjb
384178481Sjb	return (E_SUCCESS);
385178481Sjb}
386178481Sjb
387178481Sjbstatic int
388178481Sjbread_types(const ctf_header_t *hp, const ctf_data_t *cd)
389178481Sjb{
390178542Sjb	void *v = (void *) (cd->cd_ctfdata + hp->cth_typeoff);
391178542Sjb	const ctf_type_t *tp = v;
392178481Sjb
393178542Sjb	v = (void *) (cd->cd_ctfdata + hp->cth_stroff);
394178542Sjb	const ctf_type_t *end = v;
395178481Sjb
396178481Sjb	ulong_t id;
397178481Sjb
398178481Sjb	if (flags != F_STATS)
399178481Sjb		print_line("- Types ");
400178481Sjb
401178481Sjb	if (hp->cth_typeoff & 3)
402178481Sjb		WARN("cth_typeoff is not aligned properly\n");
403178481Sjb	if (hp->cth_typeoff >= cd->cd_ctflen)
404178481Sjb		WARN("file is truncated or cth_typeoff is corrupt\n");
405178481Sjb	if (hp->cth_stroff >= cd->cd_ctflen)
406178481Sjb		WARN("file is truncated or cth_stroff is corrupt\n");
407178481Sjb	if (hp->cth_typeoff > hp->cth_stroff)
408178481Sjb		WARN("file is corrupt -- cth_typeoff > cth_stroff\n");
409178481Sjb
410178481Sjb	id = 1;
411178481Sjb	if (hp->cth_parlabel || hp->cth_parname)
412178481Sjb		id += 1 << CTF_PARENT_SHIFT;
413178481Sjb
414178481Sjb	for (/* */; tp < end; id++) {
415178481Sjb		ulong_t i, n = CTF_INFO_VLEN(tp->ctt_info);
416178481Sjb		size_t size, increment, vlen = 0;
417178481Sjb		int kind = CTF_INFO_KIND(tp->ctt_info);
418178481Sjb
419178481Sjb		union {
420178481Sjb			const void *ptr;
421178542Sjb			ctf_array_t *ap;
422178481Sjb			const ctf_member_t *mp;
423178481Sjb			const ctf_lmember_t *lmp;
424178481Sjb			const ctf_enum_t *ep;
425178481Sjb			const ushort_t *argp;
426178481Sjb		} u;
427178481Sjb
428178481Sjb		if (flags != F_STATS) {
429178481Sjb			(void) printf("  %c%lu%c ",
430178481Sjb			    "[<"[CTF_INFO_ISROOT(tp->ctt_info)], id,
431178481Sjb			    "]>"[CTF_INFO_ISROOT(tp->ctt_info)]);
432178481Sjb		}
433178481Sjb
434178481Sjb		if (tp->ctt_size == CTF_LSIZE_SENT) {
435178481Sjb			increment = sizeof (ctf_type_t);
436178481Sjb			size = (size_t)CTF_TYPE_LSIZE(tp);
437178481Sjb		} else {
438178481Sjb			increment = sizeof (ctf_stype_t);
439178481Sjb			size = tp->ctt_size;
440178481Sjb		}
441178542Sjb		u.ptr = (const char *)tp + increment;
442178481Sjb
443178481Sjb		switch (kind) {
444178481Sjb		case CTF_K_INTEGER:
445178481Sjb			if (flags != F_STATS) {
446178481Sjb				uint_t encoding = *((const uint_t *)u.ptr);
447178481Sjb
448178481Sjb				(void) printf("INTEGER %s encoding=%s offset=%u"
449178481Sjb				    " bits=%u", ref_to_str(tp->ctt_name, hp,
450178481Sjb				    cd), int_encoding_to_str(
451178481Sjb				    CTF_INT_ENCODING(encoding)),
452178481Sjb				    CTF_INT_OFFSET(encoding),
453178481Sjb				    CTF_INT_BITS(encoding));
454178481Sjb			}
455178481Sjb			vlen = sizeof (uint_t);
456178481Sjb			break;
457178481Sjb
458178481Sjb		case CTF_K_FLOAT:
459178481Sjb			if (flags != F_STATS) {
460178481Sjb				uint_t encoding = *((const uint_t *)u.ptr);
461178481Sjb
462178481Sjb				(void) printf("FLOAT %s encoding=%s offset=%u "
463178481Sjb				    "bits=%u", ref_to_str(tp->ctt_name, hp,
464178481Sjb				    cd), fp_encoding_to_str(
465178481Sjb				    CTF_FP_ENCODING(encoding)),
466178481Sjb				    CTF_FP_OFFSET(encoding),
467178481Sjb				    CTF_FP_BITS(encoding));
468178481Sjb			}
469178481Sjb			vlen = sizeof (uint_t);
470178481Sjb			break;
471178481Sjb
472178481Sjb		case CTF_K_POINTER:
473178481Sjb			if (flags != F_STATS) {
474178481Sjb				(void) printf("POINTER %s refers to %u",
475178481Sjb				    ref_to_str(tp->ctt_name, hp, cd),
476178481Sjb				    tp->ctt_type);
477178481Sjb			}
478178481Sjb			break;
479178481Sjb
480178481Sjb		case CTF_K_ARRAY:
481178481Sjb			if (flags != F_STATS) {
482178481Sjb				(void) printf("ARRAY %s content: %u index: %u "
483178481Sjb				    "nelems: %u\n", ref_to_str(tp->ctt_name,
484178481Sjb				    hp, cd), u.ap->cta_contents,
485178481Sjb				    u.ap->cta_index, u.ap->cta_nelems);
486178481Sjb			}
487178481Sjb			vlen = sizeof (ctf_array_t);
488178481Sjb			break;
489178481Sjb
490178481Sjb		case CTF_K_FUNCTION:
491178481Sjb			if (flags != F_STATS) {
492178481Sjb				(void) printf("FUNCTION %s returns: %u args: (",
493178481Sjb				    ref_to_str(tp->ctt_name, hp, cd),
494178481Sjb				    tp->ctt_type);
495178481Sjb
496178481Sjb				if (n != 0) {
497178481Sjb					(void) printf("%u", *u.argp++);
498178481Sjb					for (i = 1; i < n; i++, u.argp++)
499178481Sjb						(void) printf(", %u", *u.argp);
500178481Sjb				}
501178481Sjb
502178481Sjb				(void) printf(")");
503178481Sjb			}
504178481Sjb
505178481Sjb			vlen = sizeof (ushort_t) * (n + (n & 1));
506178481Sjb			break;
507178481Sjb
508178481Sjb		case CTF_K_STRUCT:
509178481Sjb		case CTF_K_UNION:
510178481Sjb			if (kind == CTF_K_STRUCT) {
511178481Sjb				stats.s_nsmem += n;
512178481Sjb				stats.s_smmax = MAX(stats.s_smmax, n);
513178481Sjb				stats.s_nsbytes += size;
514178481Sjb				stats.s_sbmax = MAX(stats.s_sbmax, size);
515178481Sjb
516178481Sjb				if (flags != F_STATS)
517178481Sjb					(void) printf("STRUCT");
518178481Sjb			} else {
519178481Sjb				stats.s_numem += n;
520178481Sjb				stats.s_ummax = MAX(stats.s_ummax, n);
521178481Sjb				stats.s_nubytes += size;
522178481Sjb				stats.s_ubmax = MAX(stats.s_ubmax, size);
523178481Sjb
524178481Sjb				if (flags != F_STATS)
525178481Sjb					(void) printf("UNION");
526178481Sjb			}
527178481Sjb
528178481Sjb			if (flags != F_STATS) {
529178542Sjb				(void) printf(" %s (%zd bytes)\n",
530178481Sjb				    ref_to_str(tp->ctt_name, hp, cd), size);
531178481Sjb
532178481Sjb				if (size >= CTF_LSTRUCT_THRESH) {
533178481Sjb					for (i = 0; i < n; i++, u.lmp++) {
534178481Sjb						(void) printf(
535178481Sjb						    "\t%s type=%u off=%llu\n",
536178481Sjb						    ref_to_str(u.lmp->ctlm_name,
537178481Sjb						    hp, cd), u.lmp->ctlm_type,
538178542Sjb						    (unsigned long long)
539178481Sjb						    CTF_LMEM_OFFSET(u.lmp));
540178481Sjb					}
541178481Sjb				} else {
542178481Sjb					for (i = 0; i < n; i++, u.mp++) {
543178481Sjb						(void) printf(
544178481Sjb						    "\t%s type=%u off=%u\n",
545178481Sjb						    ref_to_str(u.mp->ctm_name,
546178481Sjb						    hp, cd), u.mp->ctm_type,
547178481Sjb						    u.mp->ctm_offset);
548178481Sjb					}
549178481Sjb				}
550178481Sjb			}
551178481Sjb
552178481Sjb			vlen = n * (size >= CTF_LSTRUCT_THRESH ?
553178481Sjb			    sizeof (ctf_lmember_t) : sizeof (ctf_member_t));
554178481Sjb			break;
555178481Sjb
556178481Sjb		case CTF_K_ENUM:
557178481Sjb			if (flags != F_STATS) {
558178481Sjb				(void) printf("ENUM %s\n",
559178481Sjb				    ref_to_str(tp->ctt_name, hp, cd));
560178481Sjb
561178481Sjb				for (i = 0; i < n; i++, u.ep++) {
562178481Sjb					(void) printf("\t%s = %d\n",
563178481Sjb					    ref_to_str(u.ep->cte_name, hp, cd),
564178481Sjb					    u.ep->cte_value);
565178481Sjb				}
566178481Sjb			}
567178481Sjb
568178481Sjb			stats.s_nemem += n;
569178481Sjb			stats.s_emmax = MAX(stats.s_emmax, n);
570178481Sjb
571178481Sjb			vlen = sizeof (ctf_enum_t) * n;
572178481Sjb			break;
573178481Sjb
574178481Sjb		case CTF_K_FORWARD:
575178481Sjb			if (flags != F_STATS) {
576178481Sjb				(void) printf("FORWARD %s",
577178481Sjb				    ref_to_str(tp->ctt_name, hp, cd));
578178481Sjb			}
579178481Sjb			break;
580178481Sjb
581178481Sjb		case CTF_K_TYPEDEF:
582178481Sjb			if (flags != F_STATS) {
583178481Sjb				(void) printf("TYPEDEF %s refers to %u",
584178481Sjb				    ref_to_str(tp->ctt_name, hp, cd),
585178481Sjb				    tp->ctt_type);
586178481Sjb			}
587178481Sjb			break;
588178481Sjb
589178481Sjb		case CTF_K_VOLATILE:
590178481Sjb			if (flags != F_STATS) {
591178481Sjb				(void) printf("VOLATILE %s refers to %u",
592178481Sjb				    ref_to_str(tp->ctt_name, hp, cd),
593178481Sjb				    tp->ctt_type);
594178481Sjb			}
595178481Sjb			break;
596178481Sjb
597178481Sjb		case CTF_K_CONST:
598178481Sjb			if (flags != F_STATS) {
599178481Sjb				(void) printf("CONST %s refers to %u",
600178481Sjb				    ref_to_str(tp->ctt_name, hp, cd),
601178481Sjb				    tp->ctt_type);
602178481Sjb			}
603178481Sjb			break;
604178481Sjb
605178481Sjb		case CTF_K_RESTRICT:
606178481Sjb			if (flags != F_STATS) {
607178481Sjb				(void) printf("RESTRICT %s refers to %u",
608178481Sjb				    ref_to_str(tp->ctt_name, hp, cd),
609178481Sjb				    tp->ctt_type);
610178481Sjb			}
611178481Sjb			break;
612178481Sjb
613178481Sjb		case CTF_K_UNKNOWN:
614178481Sjb			break; /* hole in type id space */
615178481Sjb
616178481Sjb		default:
617178481Sjb			(void) printf("unexpected kind %u\n", kind);
618178481Sjb			return (E_ERROR);
619178481Sjb		}
620178481Sjb
621178481Sjb		if (flags != F_STATS)
622178481Sjb			(void) printf("\n");
623178481Sjb
624178481Sjb		stats.s_ntypes++;
625178481Sjb		stats.s_types[kind]++;
626178481Sjb
627178481Sjb		tp = (ctf_type_t *)((uintptr_t)tp + increment + vlen);
628178481Sjb	}
629178481Sjb
630178481Sjb	return (E_SUCCESS);
631178481Sjb}
632178481Sjb
633178481Sjbstatic int
634178481Sjbread_strtab(const ctf_header_t *hp, const ctf_data_t *cd)
635178481Sjb{
636178481Sjb	size_t n, off, len = hp->cth_strlen;
637178481Sjb	const char *s = cd->cd_ctfdata + hp->cth_stroff;
638178481Sjb
639178481Sjb	if (flags != F_STATS)
640178481Sjb		print_line("- String Table ");
641178481Sjb
642178481Sjb	if (hp->cth_stroff >= cd->cd_ctflen)
643178481Sjb		WARN("file is truncated or cth_stroff is corrupt\n");
644178481Sjb	if (hp->cth_stroff + hp->cth_strlen > cd->cd_ctflen)
645178481Sjb		WARN("file is truncated or cth_strlen is corrupt\n");
646178481Sjb
647178481Sjb	for (off = 0; len != 0; off += n) {
648178481Sjb		if (flags != F_STATS) {
649178481Sjb			(void) printf("  [%lu] %s\n", (ulong_t)off,
650178481Sjb			    s[0] == '\0' ? "\\0" : s);
651178481Sjb		}
652178481Sjb		n = strlen(s) + 1;
653178481Sjb		len -= n;
654178481Sjb		s += n;
655178481Sjb
656178481Sjb		stats.s_nstr++;
657178481Sjb		stats.s_strlen += n;
658178481Sjb		stats.s_strmax = MAX(stats.s_strmax, n);
659178481Sjb	}
660178481Sjb
661178481Sjb	return (E_SUCCESS);
662178481Sjb}
663178481Sjb
664178481Sjbstatic void
665178481Sjblong_stat(const char *name, ulong_t value)
666178481Sjb{
667178481Sjb	(void) printf("  %-36s= %lu\n", name, value);
668178481Sjb}
669178481Sjb
670178481Sjbstatic void
671178481Sjbfp_stat(const char *name, float value)
672178481Sjb{
673178481Sjb	(void) printf("  %-36s= %.2f\n", name, value);
674178481Sjb}
675178481Sjb
676178481Sjbstatic int
677178481Sjbprint_stats(void)
678178481Sjb{
679178481Sjb	print_line("- CTF Statistics ");
680178481Sjb
681178481Sjb	long_stat("total number of data objects", stats.s_ndata);
682178481Sjb	(void) printf("\n");
683178481Sjb
684178481Sjb	long_stat("total number of functions", stats.s_nfunc);
685178481Sjb	long_stat("total number of function arguments", stats.s_nargs);
686178481Sjb	long_stat("maximum argument list length", stats.s_argmax);
687178481Sjb
688178481Sjb	if (stats.s_nfunc != 0) {
689178481Sjb		fp_stat("average argument list length",
690178481Sjb		    (float)stats.s_nargs / (float)stats.s_nfunc);
691178481Sjb	}
692178481Sjb
693178481Sjb	(void) printf("\n");
694178481Sjb
695178481Sjb	long_stat("total number of types", stats.s_ntypes);
696178481Sjb	long_stat("total number of integers", stats.s_types[CTF_K_INTEGER]);
697178481Sjb	long_stat("total number of floats", stats.s_types[CTF_K_FLOAT]);
698178481Sjb	long_stat("total number of pointers", stats.s_types[CTF_K_POINTER]);
699178481Sjb	long_stat("total number of arrays", stats.s_types[CTF_K_ARRAY]);
700178481Sjb	long_stat("total number of func types", stats.s_types[CTF_K_FUNCTION]);
701178481Sjb	long_stat("total number of structs", stats.s_types[CTF_K_STRUCT]);
702178481Sjb	long_stat("total number of unions", stats.s_types[CTF_K_UNION]);
703178481Sjb	long_stat("total number of enums", stats.s_types[CTF_K_ENUM]);
704178481Sjb	long_stat("total number of forward tags", stats.s_types[CTF_K_FORWARD]);
705178481Sjb	long_stat("total number of typedefs", stats.s_types[CTF_K_TYPEDEF]);
706178481Sjb	long_stat("total number of volatile types",
707178481Sjb	    stats.s_types[CTF_K_VOLATILE]);
708178481Sjb	long_stat("total number of const types", stats.s_types[CTF_K_CONST]);
709178481Sjb	long_stat("total number of restrict types",
710178481Sjb	    stats.s_types[CTF_K_RESTRICT]);
711178481Sjb	long_stat("total number of unknowns (holes)",
712178481Sjb	    stats.s_types[CTF_K_UNKNOWN]);
713178481Sjb
714178481Sjb	(void) printf("\n");
715178481Sjb
716178481Sjb	long_stat("total number of struct members", stats.s_nsmem);
717178481Sjb	long_stat("maximum number of struct members", stats.s_smmax);
718178481Sjb	long_stat("total size of all structs", stats.s_nsbytes);
719178481Sjb	long_stat("maximum size of a struct", stats.s_sbmax);
720178481Sjb
721178481Sjb	if (stats.s_types[CTF_K_STRUCT] != 0) {
722178481Sjb		fp_stat("average number of struct members",
723178481Sjb		    (float)stats.s_nsmem / (float)stats.s_types[CTF_K_STRUCT]);
724178481Sjb		fp_stat("average size of a struct", (float)stats.s_nsbytes /
725178481Sjb		    (float)stats.s_types[CTF_K_STRUCT]);
726178481Sjb	}
727178481Sjb
728178481Sjb	(void) printf("\n");
729178481Sjb
730178481Sjb	long_stat("total number of union members", stats.s_numem);
731178481Sjb	long_stat("maximum number of union members", stats.s_ummax);
732178481Sjb	long_stat("total size of all unions", stats.s_nubytes);
733178481Sjb	long_stat("maximum size of a union", stats.s_ubmax);
734178481Sjb
735178481Sjb	if (stats.s_types[CTF_K_UNION] != 0) {
736178481Sjb		fp_stat("average number of union members",
737178481Sjb		    (float)stats.s_numem / (float)stats.s_types[CTF_K_UNION]);
738178481Sjb		fp_stat("average size of a union", (float)stats.s_nubytes /
739178481Sjb		    (float)stats.s_types[CTF_K_UNION]);
740178481Sjb	}
741178481Sjb
742178481Sjb	(void) printf("\n");
743178481Sjb
744178481Sjb	long_stat("total number of enum members", stats.s_nemem);
745178481Sjb	long_stat("maximum number of enum members", stats.s_emmax);
746178481Sjb
747178481Sjb	if (stats.s_types[CTF_K_ENUM] != 0) {
748178481Sjb		fp_stat("average number of enum members",
749178481Sjb		    (float)stats.s_nemem / (float)stats.s_types[CTF_K_ENUM]);
750178481Sjb	}
751178481Sjb
752178481Sjb	(void) printf("\n");
753178481Sjb
754178481Sjb	long_stat("total number of unique strings", stats.s_nstr);
755178481Sjb	long_stat("bytes of string data", stats.s_strlen);
756178481Sjb	long_stat("maximum string length", stats.s_strmax);
757178481Sjb
758178481Sjb	if (stats.s_nstr != 0) {
759178481Sjb		fp_stat("average string length",
760178481Sjb		    (float)stats.s_strlen / (float)stats.s_nstr);
761178481Sjb	}
762178481Sjb
763178481Sjb	(void) printf("\n");
764178481Sjb	return (E_SUCCESS);
765178481Sjb}
766178481Sjb
767178481Sjbstatic int
768178481Sjbprint_usage(FILE *fp, int verbose)
769178481Sjb{
770178481Sjb	(void) fprintf(fp, "Usage: %s [-dfhlsSt] [-u file] file\n", getpname());
771178481Sjb
772178481Sjb	if (verbose) {
773178481Sjb		(void) fprintf(fp,
774178481Sjb		    "\t-d  dump data object section\n"
775178481Sjb		    "\t-f  dump function section\n"
776178481Sjb		    "\t-h  dump file header\n"
777178481Sjb		    "\t-l  dump label table\n"
778178481Sjb		    "\t-s  dump string table\n"
779178481Sjb		    "\t-S  dump statistics\n"
780178481Sjb		    "\t-t  dump type section\n"
781178481Sjb		    "\t-u  save uncompressed CTF to a file\n");
782178481Sjb	}
783178481Sjb
784178481Sjb	return (E_USAGE);
785178481Sjb}
786178481Sjb
787178481Sjbstatic Elf_Scn *
788178542Sjbfindelfscn(Elf *elf, GElf_Ehdr *ehdr, const char *secname)
789178481Sjb{
790178481Sjb	GElf_Shdr shdr;
791178481Sjb	Elf_Scn *scn;
792178481Sjb	char *name;
793178481Sjb
794178481Sjb	for (scn = NULL; (scn = elf_nextscn(elf, scn)) != NULL; ) {
795178481Sjb		if (gelf_getshdr(scn, &shdr) != NULL && (name =
796178481Sjb		    elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name)) != NULL &&
797178481Sjb		    strcmp(name, secname) == 0)
798178481Sjb			return (scn);
799178481Sjb	}
800178481Sjb
801178481Sjb	return (NULL);
802178481Sjb}
803178481Sjb
804178481Sjbint
805178481Sjbmain(int argc, char *argv[])
806178481Sjb{
807178481Sjb	const char *filename = NULL;
808178481Sjb	const char *ufile = NULL;
809178481Sjb	int error = 0;
810178481Sjb	int c, fd, ufd;
811178481Sjb
812178481Sjb	ctf_data_t cd;
813178481Sjb	const ctf_preamble_t *pp;
814178542Sjb	ctf_header_t *hp = NULL;
815178481Sjb	Elf *elf;
816178481Sjb	GElf_Ehdr ehdr;
817178481Sjb
818178481Sjb	(void) elf_version(EV_CURRENT);
819178481Sjb
820178481Sjb	for (opterr = 0; optind < argc; optind++) {
821178481Sjb		while ((c = getopt(argc, argv, "dfhlsStu:")) != (int)EOF) {
822178481Sjb			switch (c) {
823178481Sjb			case 'd':
824178481Sjb				flags |= F_DATA;
825178481Sjb				break;
826178481Sjb			case 'f':
827178481Sjb				flags |= F_FUNC;
828178481Sjb				break;
829178481Sjb			case 'h':
830178481Sjb				flags |= F_HDR;
831178481Sjb				break;
832178481Sjb			case 'l':
833178481Sjb				flags |= F_LABEL;
834178481Sjb				break;
835178481Sjb			case 's':
836178481Sjb				flags |= F_STR;
837178481Sjb				break;
838178481Sjb			case 'S':
839178481Sjb				flags |= F_STATS;
840178481Sjb				break;
841178481Sjb			case 't':
842178481Sjb				flags |= F_TYPES;
843178481Sjb				break;
844178481Sjb			case 'u':
845178481Sjb				ufile = optarg;
846178481Sjb				break;
847178481Sjb			default:
848178481Sjb				if (optopt == '?')
849178481Sjb					return (print_usage(stdout, 1));
850178481Sjb				warn("illegal option -- %c\n", optopt);
851178481Sjb				return (print_usage(stderr, 0));
852178481Sjb			}
853178481Sjb		}
854178481Sjb
855178481Sjb		if (optind < argc) {
856178481Sjb			if (filename != NULL)
857178481Sjb				return (print_usage(stderr, 0));
858178481Sjb			filename = argv[optind];
859178481Sjb		}
860178481Sjb	}
861178481Sjb
862178481Sjb	if (filename == NULL)
863178481Sjb		return (print_usage(stderr, 0));
864178481Sjb
865178481Sjb	if (flags == 0 && ufile == NULL)
866178481Sjb		flags = F_ALLMSK;
867178481Sjb
868178481Sjb	if ((fd = open(filename, O_RDONLY)) == -1)
869178481Sjb		die("failed to open %s", filename);
870178481Sjb
871178481Sjb	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) != NULL &&
872178481Sjb	    gelf_getehdr(elf, &ehdr) != NULL) {
873178481Sjb
874178542Sjb		Elf_Data *dp = NULL;
875178481Sjb		Elf_Scn *ctfscn = findelfscn(elf, &ehdr, ".SUNW_ctf");
876178481Sjb		Elf_Scn *symscn;
877178481Sjb		GElf_Shdr ctfshdr;
878178481Sjb
879178481Sjb		if (ctfscn == NULL || (dp = elf_getdata(ctfscn, NULL)) == NULL)
880178481Sjb			die("%s does not contain .SUNW_ctf data\n", filename);
881178481Sjb
882178481Sjb		cd.cd_ctfdata = dp->d_buf;
883178481Sjb		cd.cd_ctflen = dp->d_size;
884178481Sjb
885178481Sjb		/*
886178481Sjb		 * If the sh_link field of the CTF section header is non-zero
887178481Sjb		 * it indicates which section contains the symbol table that
888178481Sjb		 * should be used. We default to the .symtab section if sh_link
889178481Sjb		 * is zero or if there's an error reading the section header.
890178481Sjb		 */
891178481Sjb		if (gelf_getshdr(ctfscn, &ctfshdr) != NULL &&
892178481Sjb		    ctfshdr.sh_link != 0) {
893178481Sjb			symscn = elf_getscn(elf, ctfshdr.sh_link);
894178481Sjb		} else {
895178481Sjb			symscn = findelfscn(elf, &ehdr, ".symtab");
896178481Sjb		}
897178481Sjb
898178481Sjb		/* If we found a symbol table, find the corresponding strings */
899178481Sjb		if (symscn != NULL) {
900178481Sjb			GElf_Shdr shdr;
901178481Sjb			Elf_Scn *symstrscn;
902178481Sjb
903178481Sjb			if (gelf_getshdr(symscn, &shdr) != NULL) {
904178481Sjb				symstrscn = elf_getscn(elf, shdr.sh_link);
905178481Sjb
906178481Sjb				cd.cd_nsyms = shdr.sh_size / shdr.sh_entsize;
907178481Sjb				cd.cd_symdata = elf_getdata(symscn, NULL);
908178481Sjb				cd.cd_strdata = elf_getdata(symstrscn, NULL);
909178481Sjb			}
910178481Sjb		}
911178481Sjb	} else {
912178481Sjb		struct stat st;
913178481Sjb
914178481Sjb		if (fstat(fd, &st) == -1)
915178481Sjb			die("failed to fstat %s", filename);
916178481Sjb
917178481Sjb		cd.cd_ctflen = st.st_size;
918178481Sjb		cd.cd_ctfdata = mmap(NULL, cd.cd_ctflen, PROT_READ,
919178481Sjb		    MAP_PRIVATE, fd, 0);
920178481Sjb		if (cd.cd_ctfdata == MAP_FAILED)
921178481Sjb			die("failed to mmap %s", filename);
922178481Sjb	}
923178481Sjb
924178481Sjb	/*
925178481Sjb	 * Get a pointer to the CTF data buffer and interpret the first portion
926178481Sjb	 * as a ctf_header_t.  Validate the magic number and size.
927178481Sjb	 */
928178481Sjb
929178481Sjb	if (cd.cd_ctflen < sizeof (ctf_preamble_t))
930178481Sjb		die("%s does not contain a CTF preamble\n", filename);
931178481Sjb
932178542Sjb	void *v = (void *) cd.cd_ctfdata;
933178542Sjb	pp = v;
934178481Sjb
935178481Sjb	if (pp->ctp_magic != CTF_MAGIC)
936178481Sjb		die("%s does not appear to contain CTF data\n", filename);
937178481Sjb
938178481Sjb	if (pp->ctp_version == CTF_VERSION) {
939178542Sjb		v = (void *) cd.cd_ctfdata;
940178542Sjb		hp = v;
941178481Sjb		cd.cd_ctfdata = (caddr_t)cd.cd_ctfdata + sizeof (ctf_header_t);
942178481Sjb
943178481Sjb		if (cd.cd_ctflen < sizeof (ctf_header_t)) {
944178481Sjb			die("%s does not contain a v%d CTF header\n", filename,
945178481Sjb			    CTF_VERSION);
946178481Sjb		}
947178481Sjb
948178481Sjb	} else {
949178481Sjb		die("%s contains unsupported CTF version %d\n", filename,
950178481Sjb		    pp->ctp_version);
951178481Sjb	}
952178481Sjb
953178481Sjb	/*
954178481Sjb	 * If the data buffer is compressed, then malloc a buffer large enough
955178481Sjb	 * to hold the decompressed data, and use zlib to decompress it.
956178481Sjb	 */
957178481Sjb	if (hp->cth_flags & CTF_F_COMPRESS) {
958178481Sjb		z_stream zstr;
959178481Sjb		void *buf;
960178481Sjb		int rc;
961178481Sjb
962178481Sjb		if ((buf = malloc(hp->cth_stroff + hp->cth_strlen)) == NULL)
963178481Sjb			die("failed to allocate decompression buffer");
964178481Sjb
965178481Sjb		bzero(&zstr, sizeof (z_stream));
966178481Sjb		zstr.next_in = (void *)cd.cd_ctfdata;
967178481Sjb		zstr.avail_in = cd.cd_ctflen;
968178481Sjb		zstr.next_out = buf;
969178481Sjb		zstr.avail_out = hp->cth_stroff + hp->cth_strlen;
970178481Sjb
971178481Sjb		if ((rc = inflateInit(&zstr)) != Z_OK)
972178481Sjb			die("failed to initialize zlib: %s\n", zError(rc));
973178481Sjb
974178481Sjb		if ((rc = inflate(&zstr, Z_FINISH)) != Z_STREAM_END)
975178481Sjb			die("failed to decompress CTF data: %s\n", zError(rc));
976178481Sjb
977178481Sjb		if ((rc = inflateEnd(&zstr)) != Z_OK)
978178481Sjb			die("failed to finish decompression: %s\n", zError(rc));
979178481Sjb
980178481Sjb		if (zstr.total_out != hp->cth_stroff + hp->cth_strlen)
981178481Sjb			die("CTF data is corrupt -- short decompression\n");
982178481Sjb
983178481Sjb		cd.cd_ctfdata = buf;
984178481Sjb		cd.cd_ctflen = hp->cth_stroff + hp->cth_strlen;
985178481Sjb	}
986178481Sjb
987178481Sjb	if (flags & F_HDR)
988178481Sjb		error |= print_header(hp, &cd);
989178481Sjb	if (flags & (F_LABEL))
990178481Sjb		error |= print_labeltable(hp, &cd);
991178481Sjb	if (flags & (F_DATA | F_STATS))
992178481Sjb		error |= read_data(hp, &cd);
993178481Sjb	if (flags & (F_FUNC | F_STATS))
994178481Sjb		error |= read_funcs(hp, &cd);
995178481Sjb	if (flags & (F_TYPES | F_STATS))
996178481Sjb		error |= read_types(hp, &cd);
997178481Sjb	if (flags & (F_STR | F_STATS))
998178481Sjb		error |= read_strtab(hp, &cd);
999178481Sjb	if (flags & F_STATS)
1000178481Sjb		error |= print_stats();
1001178481Sjb
1002178481Sjb	/*
1003178481Sjb	 * If the -u option is specified, write the uncompressed CTF data to a
1004178481Sjb	 * raw CTF file.  CTF data can already be extracted compressed by
1005178481Sjb	 * applying elfdump -w -N .SUNW_ctf to an ELF file, so we don't bother.
1006178481Sjb	 */
1007178481Sjb	if (ufile != NULL) {
1008178481Sjb		ctf_header_t h;
1009178481Sjb
1010178481Sjb		bcopy(hp, &h, sizeof (h));
1011178481Sjb		h.cth_flags &= ~CTF_F_COMPRESS;
1012178481Sjb
1013178481Sjb		if ((ufd = open(ufile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0 ||
1014178481Sjb		    write(ufd, &h, sizeof (h)) != sizeof (h) ||
1015178542Sjb		    write(ufd, cd.cd_ctfdata, cd.cd_ctflen) != (int) cd.cd_ctflen) {
1016178481Sjb			warn("failed to write CTF data to '%s'", ufile);
1017178481Sjb			error |= E_ERROR;
1018178481Sjb		}
1019178481Sjb
1020178481Sjb		(void) close(ufd);
1021178481Sjb	}
1022178481Sjb
1023178481Sjb	if (elf != NULL)
1024178481Sjb		(void) elf_end(elf);
1025178481Sjb
1026178481Sjb	(void) close(fd);
1027178481Sjb	return (error);
1028178481Sjb}
1029