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