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