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