xref: /openbsd/usr.bin/ctfdump/ctfdump.c (revision ccd92e6d)
1*ccd92e6dSuwe /*	$OpenBSD: ctfdump.c,v 1.7 2017/09/23 12:24:31 uwe Exp $ */
27047d4c7Sjasper 
326d55b61Smpi /*
426d55b61Smpi  * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
526d55b61Smpi  *
626d55b61Smpi  * Permission to use, copy, modify, and distribute this software for any
726d55b61Smpi  * purpose with or without fee is hereby granted, provided that the above
826d55b61Smpi  * copyright notice and this permission notice appear in all copies.
926d55b61Smpi  *
1026d55b61Smpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1126d55b61Smpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1226d55b61Smpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1326d55b61Smpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1426d55b61Smpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1526d55b61Smpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1626d55b61Smpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1726d55b61Smpi  */
1826d55b61Smpi 
1926d55b61Smpi #include <sys/types.h>
2026d55b61Smpi #include <sys/stat.h>
2126d55b61Smpi #include <sys/exec_elf.h>
2226d55b61Smpi #include <sys/mman.h>
2326d55b61Smpi #include <sys/ctf.h>
2426d55b61Smpi 
2526d55b61Smpi #include <err.h>
2626d55b61Smpi #include <fcntl.h>
2726d55b61Smpi #include <locale.h>
2826d55b61Smpi #include <stdio.h>
2926d55b61Smpi #include <stdint.h>
3026d55b61Smpi #include <stdlib.h>
3126d55b61Smpi #include <string.h>
3226d55b61Smpi #include <unistd.h>
3326d55b61Smpi 
3426d55b61Smpi #ifdef ZLIB
3526d55b61Smpi #include <zlib.h>
3626d55b61Smpi #endif /* ZLIB */
3726d55b61Smpi 
3826d55b61Smpi #ifndef nitems
3926d55b61Smpi #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
4026d55b61Smpi #endif
4126d55b61Smpi 
4226d55b61Smpi #define DUMP_OBJECT	(1 << 0)
4326d55b61Smpi #define DUMP_FUNCTION	(1 << 1)
4426d55b61Smpi #define DUMP_HEADER	(1 << 2)
4526d55b61Smpi #define DUMP_LABEL	(1 << 3)
4626d55b61Smpi #define DUMP_STRTAB	(1 << 4)
4726d55b61Smpi #define DUMP_STATISTIC	(1 << 5)
4826d55b61Smpi #define DUMP_TYPE	(1 << 6)
4926d55b61Smpi 
5026d55b61Smpi int		 dump(const char *, uint8_t);
5126d55b61Smpi int		 isctf(const char *, size_t);
5226d55b61Smpi __dead void	 usage(void);
5326d55b61Smpi 
5426d55b61Smpi int		 ctf_dump(const char *, size_t, uint8_t);
5526d55b61Smpi uint32_t	 ctf_dump_type(struct ctf_header *, const char *, off_t,
5626d55b61Smpi 		     uint32_t, uint32_t);
5726d55b61Smpi const char	*ctf_kind2name(uint16_t);
5826d55b61Smpi const char	*ctf_enc2name(uint16_t);
59*ccd92e6dSuwe const char	*ctf_fpenc2name(uint16_t);
6026d55b61Smpi const char	*ctf_off2name(struct ctf_header *, const char *, off_t,
6126d55b61Smpi 		     uint32_t);
6226d55b61Smpi 
6326d55b61Smpi int		 elf_dump(char *, size_t, uint8_t);
6426d55b61Smpi const char	*elf_idx2sym(size_t *, uint8_t);
6526d55b61Smpi 
6626d55b61Smpi /* elf.c */
6726d55b61Smpi int		 iself(const char *, size_t);
6826d55b61Smpi int		 elf_getshstab(const char *, size_t, const char **, size_t *);
6926d55b61Smpi ssize_t		 elf_getsymtab(const char *, const char *, size_t,
7026d55b61Smpi 		     const Elf_Sym **, size_t *);
7126d55b61Smpi ssize_t		 elf_getsection(char *, const char *, const char *,
7226d55b61Smpi 		     size_t, const char **, size_t *);
7326d55b61Smpi 
7426d55b61Smpi char		*decompress(const char *, size_t, off_t);
7526d55b61Smpi 
7626d55b61Smpi int
7726d55b61Smpi main(int argc, char *argv[])
7826d55b61Smpi {
7926d55b61Smpi 	const char *filename;
8026d55b61Smpi 	uint8_t flags = 0;
8126d55b61Smpi 	int ch, error = 0;
8226d55b61Smpi 
8326e2f886Sjasper 	setlocale(LC_ALL, "");
8426e2f886Sjasper 
85933e960dSjasper 	if (pledge("stdio rpath", NULL) == -1)
86933e960dSjasper 		err(1, "pledge");
87933e960dSjasper 
8826d55b61Smpi 	while ((ch = getopt(argc, argv, "dfhlst")) != -1) {
8926d55b61Smpi 		switch (ch) {
9026d55b61Smpi 		case 'd':
9126d55b61Smpi 			flags |= DUMP_OBJECT;
9226d55b61Smpi 			break;
9326d55b61Smpi 		case 'f':
9426d55b61Smpi 			flags |= DUMP_FUNCTION;
9526d55b61Smpi 			break;
9626d55b61Smpi 		case 'h':
9726d55b61Smpi 			flags |= DUMP_HEADER;
9826d55b61Smpi 			break;
9926d55b61Smpi 		case 'l':
10026d55b61Smpi 			flags |= DUMP_LABEL;
10126d55b61Smpi 			break;
10226d55b61Smpi 		case 's':
10326d55b61Smpi 			flags |= DUMP_STRTAB;
10426d55b61Smpi 			break;
10526d55b61Smpi 		case 't':
10626d55b61Smpi 			flags |= DUMP_TYPE;
10726d55b61Smpi 			break;
10826d55b61Smpi 		default:
10926d55b61Smpi 			usage();
11026d55b61Smpi 		}
11126d55b61Smpi 	}
11226d55b61Smpi 
11326d55b61Smpi 	argc -= optind;
11426d55b61Smpi 	argv += optind;
11526d55b61Smpi 
11626d55b61Smpi 	if (argc <= 0)
11726d55b61Smpi 		usage();
11826d55b61Smpi 
11926d55b61Smpi 	/* Dump everything by default */
12026d55b61Smpi 	if (flags == 0)
12126d55b61Smpi 		flags = 0xff;
12226d55b61Smpi 
12326d55b61Smpi 	while ((filename = *argv++) != NULL)
12426d55b61Smpi 		error |= dump(filename, flags);
12526d55b61Smpi 
12626d55b61Smpi 	return error;
12726d55b61Smpi }
12826d55b61Smpi 
12926d55b61Smpi int
13026d55b61Smpi dump(const char *path, uint8_t flags)
13126d55b61Smpi {
13226d55b61Smpi 	struct stat		 st;
13326d55b61Smpi 	int			 fd, error = 1;
13426d55b61Smpi 	char			*p;
13526d55b61Smpi 
13626d55b61Smpi 	fd = open(path, O_RDONLY);
13726d55b61Smpi 	if (fd == -1) {
13826d55b61Smpi 		warn("open");
13926d55b61Smpi 		return 1;
14026d55b61Smpi 	}
14126d55b61Smpi 	if (fstat(fd, &st) == -1) {
14226d55b61Smpi 		warn("fstat");
143c8f519c3Sjsg 		close(fd);
14426d55b61Smpi 		return 1;
14526d55b61Smpi 	}
14626d55b61Smpi 	if ((uintmax_t)st.st_size > SIZE_MAX) {
14726d55b61Smpi 		warnx("file too big to fit memory");
148c8f519c3Sjsg 		close(fd);
14926d55b61Smpi 		return 1;
15026d55b61Smpi 	}
15126d55b61Smpi 
15226d55b61Smpi 	p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
15326d55b61Smpi 	if (p == MAP_FAILED)
15426d55b61Smpi 		err(1, "mmap");
15526d55b61Smpi 
15626d55b61Smpi 	if (iself(p, st.st_size)) {
15726d55b61Smpi 		error = elf_dump(p, st.st_size, flags);
15826d55b61Smpi 	} else if (isctf(p, st.st_size)) {
15926d55b61Smpi 		error = ctf_dump(p, st.st_size, flags);
16026d55b61Smpi 	}
16126d55b61Smpi 
16226d55b61Smpi 	munmap(p, st.st_size);
16326d55b61Smpi 	close(fd);
16426d55b61Smpi 
16526d55b61Smpi 	return error;
16626d55b61Smpi }
16726d55b61Smpi 
16826d55b61Smpi const char		*strtab;
16926d55b61Smpi const Elf_Sym		*symtab;
17026d55b61Smpi size_t			 strtabsz, nsymb;
17126d55b61Smpi 
17226d55b61Smpi const char *
17326d55b61Smpi elf_idx2sym(size_t *idx, uint8_t type)
17426d55b61Smpi {
17526d55b61Smpi 	const Elf_Sym	*st;
17626d55b61Smpi 	size_t		 i;
17726d55b61Smpi 
17826d55b61Smpi 	for (i = *idx + 1; i < nsymb; i++) {
17926d55b61Smpi 		st = &symtab[i];
18026d55b61Smpi 
18126d55b61Smpi 		if (ELF_ST_TYPE(st->st_info) != type)
18226d55b61Smpi 			continue;
18326d55b61Smpi 
18426d55b61Smpi 		*idx = i;
18526d55b61Smpi 		return strtab + st->st_name;
18626d55b61Smpi 	}
18726d55b61Smpi 
18826d55b61Smpi 	return NULL;
18926d55b61Smpi }
19026d55b61Smpi 
19126d55b61Smpi int
19226d55b61Smpi elf_dump(char *p, size_t filesize, uint8_t flags)
19326d55b61Smpi {
19426d55b61Smpi 	Elf_Ehdr		*eh = (Elf_Ehdr *)p;
19526d55b61Smpi 	Elf_Shdr		*sh;
19626d55b61Smpi 	const char		*shstab;
19726d55b61Smpi 	size_t			 i, shstabsz;
19826d55b61Smpi 
19926d55b61Smpi 	/* Find section header string table location and size. */
20026d55b61Smpi 	if (elf_getshstab(p, filesize, &shstab, &shstabsz))
20126d55b61Smpi 		return 1;
20226d55b61Smpi 
20326d55b61Smpi 	/* Find symbol table location and number of symbols. */
20426d55b61Smpi 	if (elf_getsymtab(p, shstab, shstabsz, &symtab, &nsymb) == -1)
20526d55b61Smpi 		warnx("symbol table not found");
20626d55b61Smpi 
20726d55b61Smpi 	/* Find string table location and size. */
20826d55b61Smpi 	if (elf_getsection(p, ELF_STRTAB, shstab, shstabsz, &strtab,
20926d55b61Smpi 	    &strtabsz) == -1)
21026d55b61Smpi 		warnx("string table not found");
21126d55b61Smpi 
21226d55b61Smpi 	/* Find CTF section and dump it. */
21326d55b61Smpi 	for (i = 0; i < eh->e_shnum; i++) {
21426d55b61Smpi 		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
21526d55b61Smpi 
21626d55b61Smpi 		if ((sh->sh_link >= eh->e_shnum) ||
21726d55b61Smpi 		    (sh->sh_name >= shstabsz))
21826d55b61Smpi 			continue;
21926d55b61Smpi 
22026d55b61Smpi 		if (strncmp(shstab + sh->sh_name, ELF_CTF, strlen(ELF_CTF)))
22126d55b61Smpi 			continue;
22226d55b61Smpi 
22326d55b61Smpi 		if (!isctf(p + sh->sh_offset, sh->sh_size))
22426d55b61Smpi 			break;
22526d55b61Smpi 
22626d55b61Smpi 		return ctf_dump(p + sh->sh_offset, sh->sh_size, flags);
22726d55b61Smpi 	}
22826d55b61Smpi 
22926d55b61Smpi 	warnx("%s section not found", ELF_CTF);
23026d55b61Smpi 	return 1;
23126d55b61Smpi }
23226d55b61Smpi 
23326d55b61Smpi int
23426d55b61Smpi isctf(const char *p, size_t filesize)
23526d55b61Smpi {
23626d55b61Smpi 	struct ctf_header	*cth = (struct ctf_header *)p;
23726d55b61Smpi 	off_t 			 dlen;
23826d55b61Smpi 
23926d55b61Smpi 	if (filesize < sizeof(struct ctf_header)) {
24026d55b61Smpi 		warnx("file too small to be CTF");
24126d55b61Smpi 		return 0;
24226d55b61Smpi 	}
24326d55b61Smpi 
24426d55b61Smpi 	if (cth->cth_magic != CTF_MAGIC || cth->cth_version != CTF_VERSION)
24526d55b61Smpi 		return 0;
24626d55b61Smpi 
24726d55b61Smpi 	dlen = cth->cth_stroff + cth->cth_strlen;
24826d55b61Smpi 	if (dlen > (off_t)filesize && !(cth->cth_flags & CTF_F_COMPRESS)) {
24926d55b61Smpi 		warnx("bogus file size");
25026d55b61Smpi 		return 0;
25126d55b61Smpi 	}
25226d55b61Smpi 
25326d55b61Smpi 	if ((cth->cth_lbloff & 3) || (cth->cth_objtoff & 1) ||
25426d55b61Smpi 	    (cth->cth_funcoff & 1) || (cth->cth_typeoff & 3)) {
25526d55b61Smpi 		warnx("wrongly aligned offset");
25626d55b61Smpi 		return 0;
25726d55b61Smpi 	}
25826d55b61Smpi 
25926d55b61Smpi 	if ((cth->cth_lbloff >= dlen) || (cth->cth_objtoff >= dlen) ||
26026d55b61Smpi 	    (cth->cth_funcoff >= dlen) || (cth->cth_typeoff >= dlen)) {
26126d55b61Smpi 		warnx("truncated file");
26226d55b61Smpi 		return 0;
26326d55b61Smpi 	}
26426d55b61Smpi 
26526d55b61Smpi 	if ((cth->cth_lbloff > cth->cth_objtoff) ||
26626d55b61Smpi 	    (cth->cth_objtoff > cth->cth_funcoff) ||
26726d55b61Smpi 	    (cth->cth_funcoff > cth->cth_typeoff) ||
26826d55b61Smpi 	    (cth->cth_typeoff > cth->cth_stroff)) {
26926d55b61Smpi 		warnx("corrupted file");
27026d55b61Smpi 		return 0;
27126d55b61Smpi 	}
27226d55b61Smpi 
27326d55b61Smpi 	return 1;
27426d55b61Smpi }
27526d55b61Smpi 
27626d55b61Smpi int
27726d55b61Smpi ctf_dump(const char *p, size_t size, uint8_t flags)
27826d55b61Smpi {
27926d55b61Smpi 	struct ctf_header	*cth = (struct ctf_header *)p;
28026d55b61Smpi 	off_t 			 dlen = cth->cth_stroff + cth->cth_strlen;
28126d55b61Smpi 	char			*data;
28226d55b61Smpi 
28326d55b61Smpi 	if (cth->cth_flags & CTF_F_COMPRESS) {
28426d55b61Smpi 		data = decompress(p + sizeof(*cth), size - sizeof(*cth), dlen);
28526d55b61Smpi 		if (data == NULL)
28626d55b61Smpi 			return 1;
28726d55b61Smpi 	} else {
28826d55b61Smpi 		data = (char *)p + sizeof(*cth);
28926d55b61Smpi 	}
29026d55b61Smpi 
29126d55b61Smpi 	if (flags & DUMP_HEADER) {
29226d55b61Smpi 		printf("  cth_magic    = 0x%04x\n", cth->cth_magic);
29326d55b61Smpi 		printf("  cth_version  = %d\n", cth->cth_version);
29426d55b61Smpi 		printf("  cth_flags    = 0x%02x\n", cth->cth_flags);
29526d55b61Smpi 		printf("  cth_parlabel = %s\n",
29626d55b61Smpi 		    ctf_off2name(cth, data, dlen, cth->cth_parname));
29726d55b61Smpi 		printf("  cth_parname  = %s\n",
29826d55b61Smpi 		    ctf_off2name(cth, data, dlen, cth->cth_parname));
29926d55b61Smpi 		printf("  cth_lbloff   = %d\n", cth->cth_lbloff);
30026d55b61Smpi 		printf("  cth_objtoff  = %d\n", cth->cth_objtoff);
30126d55b61Smpi 		printf("  cth_funcoff  = %d\n", cth->cth_funcoff);
30226d55b61Smpi 		printf("  cth_typeoff  = %d\n", cth->cth_typeoff);
30326d55b61Smpi 		printf("  cth_stroff   = %d\n", cth->cth_stroff);
30426d55b61Smpi 		printf("  cth_strlen   = %d\n", cth->cth_strlen);
30526d55b61Smpi 		printf("\n");
30626d55b61Smpi 	}
30726d55b61Smpi 
30826d55b61Smpi 	if (flags & DUMP_LABEL) {
30926d55b61Smpi 		uint32_t		 lbloff = cth->cth_lbloff;
31026d55b61Smpi 		struct ctf_lblent	*ctl;
31126d55b61Smpi 
31226d55b61Smpi 		while (lbloff < cth->cth_objtoff) {
31326d55b61Smpi 			ctl = (struct ctf_lblent *)(data + lbloff);
31426d55b61Smpi 
31526d55b61Smpi 			printf("  %5u %s\n", ctl->ctl_typeidx,
31626d55b61Smpi 			    ctf_off2name(cth, data, dlen, ctl->ctl_label));
31726d55b61Smpi 
31826d55b61Smpi 			lbloff += sizeof(*ctl);
31926d55b61Smpi 		}
32026d55b61Smpi 		printf("\n");
32126d55b61Smpi 	}
32226d55b61Smpi 
32326d55b61Smpi 	if (flags & DUMP_OBJECT) {
32426d55b61Smpi 		uint32_t		 objtoff = cth->cth_objtoff;
32526d55b61Smpi 		size_t			 idx = 0, i = 0;
32626d55b61Smpi 		uint16_t		*dsp;
32726d55b61Smpi 		const char		*s;
32826d55b61Smpi 		int			 l;
32926d55b61Smpi 
33026d55b61Smpi 		while (objtoff < cth->cth_funcoff) {
33126d55b61Smpi 			dsp = (uint16_t *)(data + objtoff);
33226d55b61Smpi 
33326d55b61Smpi 			l = printf("  [%zu] %u", i++, *dsp);
33426d55b61Smpi 			if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL)
33526d55b61Smpi 				printf("%*s %s (%zu)\n", (14 - l), "", s, idx);
33626d55b61Smpi 			else
33726d55b61Smpi 				printf("\n");
33826d55b61Smpi 
33926d55b61Smpi 			objtoff += sizeof(*dsp);
34026d55b61Smpi 		}
34126d55b61Smpi 		printf("\n");
34226d55b61Smpi 	}
34326d55b61Smpi 
34426d55b61Smpi 	if (flags & DUMP_FUNCTION) {
34526d55b61Smpi 		uint16_t		*fsp, kind, vlen;
34626d55b61Smpi 		size_t			 idx = 0, i = -1;
34726d55b61Smpi 		const char		*s;
34826d55b61Smpi 		int			 l;
34926d55b61Smpi 
35026d55b61Smpi 		fsp = (uint16_t *)(data + cth->cth_funcoff);
35126d55b61Smpi 		while (fsp < (uint16_t *)(data + cth->cth_typeoff)) {
35226d55b61Smpi 			kind = CTF_INFO_KIND(*fsp);
35326d55b61Smpi 			vlen = CTF_INFO_VLEN(*fsp);
35426d55b61Smpi 			s = elf_idx2sym(&idx, STT_FUNC);
35526d55b61Smpi 			fsp++;
35626d55b61Smpi 			i++;
35726d55b61Smpi 
35826d55b61Smpi 			if (kind == CTF_K_UNKNOWN && vlen == 0)
35926d55b61Smpi 				continue;
36026d55b61Smpi 
36126d55b61Smpi 			l = printf("  [%zu] FUNC ", i);
36226d55b61Smpi 			if (s != NULL)
36326d55b61Smpi 				printf("(%s)", s);
36426d55b61Smpi 			printf(" returns: %u args: (", *fsp++);
36526d55b61Smpi 			while (vlen-- > 0)
36626d55b61Smpi 				printf("%u%s", *fsp++, (vlen > 0) ? ", " : "");
36726d55b61Smpi 			printf(")\n");
36826d55b61Smpi 		}
36926d55b61Smpi 		printf("\n");
37026d55b61Smpi 	}
37126d55b61Smpi 
37226d55b61Smpi 	if (flags & DUMP_TYPE) {
37326d55b61Smpi 		uint32_t		 idx = 1, offset = cth->cth_typeoff;
37426d55b61Smpi 
37526d55b61Smpi 		while (offset < cth->cth_stroff) {
37626d55b61Smpi 			offset += ctf_dump_type(cth, data, dlen, offset, idx++);
37726d55b61Smpi 		}
37826d55b61Smpi 		printf("\n");
37926d55b61Smpi 	}
38026d55b61Smpi 
38126d55b61Smpi 	if (flags & DUMP_STRTAB) {
38226d55b61Smpi 		uint32_t		 offset = 0;
38326d55b61Smpi 		const char		*str;
38426d55b61Smpi 
38526d55b61Smpi 		while (offset < cth->cth_strlen) {
38626d55b61Smpi 			str = ctf_off2name(cth, data, dlen, offset);
38726d55b61Smpi 
38826d55b61Smpi 			printf("  [%u] ", offset);
38926d55b61Smpi 			if (strcmp(str, "(anon)"))
39026d55b61Smpi 				offset += printf("%s\n", str);
39126d55b61Smpi 			else {
39226d55b61Smpi 				printf("\\0\n");
39326d55b61Smpi 				offset++;
39426d55b61Smpi 			}
39526d55b61Smpi 		}
39626d55b61Smpi 		printf("\n");
39726d55b61Smpi 	}
39826d55b61Smpi 
39926d55b61Smpi 	if (cth->cth_flags & CTF_F_COMPRESS)
40026d55b61Smpi 		free(data);
40126d55b61Smpi 
40226d55b61Smpi 	return 0;
40326d55b61Smpi }
40426d55b61Smpi 
40526d55b61Smpi uint32_t
40626d55b61Smpi ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen,
40726d55b61Smpi     uint32_t offset, uint32_t idx)
40826d55b61Smpi {
40926d55b61Smpi 	const char		*p = data + offset;
41026d55b61Smpi 	const struct ctf_type	*ctt = (struct ctf_type *)p;
41126d55b61Smpi 	const struct ctf_array	*cta;
41226d55b61Smpi 	uint16_t		*argp, i, kind, vlen, root;
41326d55b61Smpi 	uint32_t		 eob, toff;
41426d55b61Smpi 	uint64_t		 size;
41526d55b61Smpi 	const char		*name, *kname;
41626d55b61Smpi 
41726d55b61Smpi 	kind = CTF_INFO_KIND(ctt->ctt_info);
41826d55b61Smpi 	vlen = CTF_INFO_VLEN(ctt->ctt_info);
41926d55b61Smpi 	root = CTF_INFO_ISROOT(ctt->ctt_info);
42026d55b61Smpi 	name = ctf_off2name(cth, data, dlen, ctt->ctt_name);
42126d55b61Smpi 
42226d55b61Smpi 	if (root)
42326d55b61Smpi 		printf("  <%u> ", idx);
42426d55b61Smpi 	else
42526d55b61Smpi 		printf("  [%u] ", idx);
42626d55b61Smpi 
42726d55b61Smpi 	if ((kname = ctf_kind2name(kind)) != NULL)
42826d55b61Smpi 		printf("%s %s", kname, name);
42926d55b61Smpi 
43026d55b61Smpi 	if (ctt->ctt_size <= CTF_MAX_SIZE) {
43126d55b61Smpi 		size = ctt->ctt_size;
43226d55b61Smpi 		toff = sizeof(struct ctf_stype);
43326d55b61Smpi 	} else {
43426d55b61Smpi 		size = CTF_TYPE_LSIZE(ctt);
43526d55b61Smpi 		toff = sizeof(struct ctf_type);
43626d55b61Smpi 	}
43726d55b61Smpi 
43826d55b61Smpi 	switch (kind) {
43926d55b61Smpi 	case CTF_K_UNKNOWN:
44026d55b61Smpi 	case CTF_K_FORWARD:
44126d55b61Smpi 		break;
44226d55b61Smpi 	case CTF_K_INTEGER:
44326d55b61Smpi 		eob = *((uint32_t *)(p + toff));
44426d55b61Smpi 		toff += sizeof(uint32_t);
44526d55b61Smpi 		printf(" encoding=%s offset=%u bits=%u",
44626d55b61Smpi 		    ctf_enc2name(CTF_INT_ENCODING(eob)), CTF_INT_OFFSET(eob),
44726d55b61Smpi 		    CTF_INT_BITS(eob));
44826d55b61Smpi 		break;
44926d55b61Smpi 	case CTF_K_FLOAT:
45026d55b61Smpi 		eob = *((uint32_t *)(p + toff));
45126d55b61Smpi 		toff += sizeof(uint32_t);
452*ccd92e6dSuwe 		printf(" encoding=%s offset=%u bits=%u",
453*ccd92e6dSuwe 		    ctf_fpenc2name(CTF_FP_ENCODING(eob)), CTF_FP_OFFSET(eob),
454*ccd92e6dSuwe 		    CTF_FP_BITS(eob));
45526d55b61Smpi 		break;
45626d55b61Smpi 	case CTF_K_ARRAY:
45726d55b61Smpi 		cta = (struct ctf_array *)(p + toff);
45826d55b61Smpi 		printf(" content: %u index: %u nelems: %u\n", cta->cta_contents,
45926d55b61Smpi 		    cta->cta_index, cta->cta_nelems);
46026d55b61Smpi 		toff += sizeof(struct ctf_array);
46126d55b61Smpi 		break;
46226d55b61Smpi 	case CTF_K_FUNCTION:
46326d55b61Smpi 		argp = (uint16_t *)(p + toff);
46426d55b61Smpi 		printf(" returns: %u args: (%u", ctt->ctt_type, *argp);
46526d55b61Smpi 		for (i = 1; i < vlen; i++) {
46626d55b61Smpi 			argp++;
46726d55b61Smpi 			printf(", %u", *argp);
46826d55b61Smpi 		}
46926d55b61Smpi 		printf(")");
47026d55b61Smpi 		toff += (vlen + (vlen & 1)) * sizeof(uint16_t);
47126d55b61Smpi 		break;
47226d55b61Smpi 	case CTF_K_STRUCT:
47326d55b61Smpi 	case CTF_K_UNION:
47426d55b61Smpi 		printf(" (%llu bytes)\n", size);
47526d55b61Smpi 
47626d55b61Smpi 		if (size < CTF_LSTRUCT_THRESH) {
47726d55b61Smpi 			for (i = 0; i < vlen; i++) {
47826d55b61Smpi 				struct ctf_member	*ctm;
47926d55b61Smpi 
48026d55b61Smpi 				ctm = (struct ctf_member *)(p + toff);
48126d55b61Smpi 				toff += sizeof(struct ctf_member);
48226d55b61Smpi 
48326d55b61Smpi 				printf("\t%s type=%u off=%u\n",
48426d55b61Smpi 				    ctf_off2name(cth, data, dlen,
48526d55b61Smpi 					ctm->ctm_name),
48626d55b61Smpi 				    ctm->ctm_type, ctm->ctm_offset);
48726d55b61Smpi 			}
48826d55b61Smpi 		} else {
48926d55b61Smpi 			for (i = 0; i < vlen; i++) {
49026d55b61Smpi 				struct ctf_lmember	*ctlm;
49126d55b61Smpi 
49226d55b61Smpi 				ctlm = (struct ctf_lmember *)(p + toff);
49326d55b61Smpi 				toff += sizeof(struct ctf_lmember);
49426d55b61Smpi 
49526d55b61Smpi 				printf("\t%s type=%u off=%llu\n",
49626d55b61Smpi 				    ctf_off2name(cth, data, dlen,
49726d55b61Smpi 					ctlm->ctlm_name),
49826d55b61Smpi 				    ctlm->ctlm_type, CTF_LMEM_OFFSET(ctlm));
49926d55b61Smpi 			}
50026d55b61Smpi 		}
50126d55b61Smpi 		break;
50226d55b61Smpi 	case CTF_K_ENUM:
50326d55b61Smpi 		printf("\n");
50426d55b61Smpi 		for (i = 0; i < vlen; i++) {
50526d55b61Smpi 			struct ctf_enum	*cte;
50626d55b61Smpi 
50726d55b61Smpi 			cte = (struct ctf_enum *)(p + toff);
50826d55b61Smpi 			toff += sizeof(struct ctf_enum);
50926d55b61Smpi 
51026d55b61Smpi 			printf("\t%s = %d\n",
51126d55b61Smpi 			    ctf_off2name(cth, data, dlen, cte->cte_name),
51226d55b61Smpi 			    cte->cte_value);
51326d55b61Smpi 		}
51426d55b61Smpi 		break;
51526d55b61Smpi 	case CTF_K_POINTER:
51626d55b61Smpi 	case CTF_K_TYPEDEF:
51726d55b61Smpi 	case CTF_K_VOLATILE:
51826d55b61Smpi 	case CTF_K_CONST:
51926d55b61Smpi 	case CTF_K_RESTRICT:
52026d55b61Smpi 		printf(" refers to %u", ctt->ctt_type);
52126d55b61Smpi 		break;
52226d55b61Smpi 	default:
52326d55b61Smpi 		errx(1, "incorrect type %u at offset %u", kind, offset);
52426d55b61Smpi 	}
52526d55b61Smpi 
52626d55b61Smpi 	printf("\n");
52726d55b61Smpi 
52826d55b61Smpi 	return toff;
52926d55b61Smpi }
53026d55b61Smpi 
53126d55b61Smpi const char *
53226d55b61Smpi ctf_kind2name(uint16_t kind)
53326d55b61Smpi {
53426d55b61Smpi 	static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER",
53526d55b61Smpi 	   "ARRAY", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD",
53626d55b61Smpi 	   "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" };
53726d55b61Smpi 
53826d55b61Smpi 	if (kind >= nitems(kind_name))
53926d55b61Smpi 		return NULL;
54026d55b61Smpi 
54126d55b61Smpi 	return kind_name[kind];
54226d55b61Smpi }
54326d55b61Smpi 
54426d55b61Smpi const char *
54526d55b61Smpi ctf_enc2name(uint16_t enc)
54626d55b61Smpi {
54726d55b61Smpi 	static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR",
54826d55b61Smpi 	    "BOOL", "SIGNED BOOL" };
54926d55b61Smpi 	static char invalid[7];
55026d55b61Smpi 
55126d55b61Smpi 	if (enc == CTF_INT_VARARGS)
55226d55b61Smpi 		return "VARARGS";
55326d55b61Smpi 
55426d55b61Smpi 	if (enc > 0 && enc < nitems(enc_name))
55526d55b61Smpi 		return enc_name[enc - 1];
55626d55b61Smpi 
55726d55b61Smpi 	snprintf(invalid, sizeof(invalid), "0x%x", enc);
55826d55b61Smpi 	return invalid;
55926d55b61Smpi }
56026d55b61Smpi 
56126d55b61Smpi const char *
562*ccd92e6dSuwe ctf_fpenc2name(uint16_t enc)
563*ccd92e6dSuwe {
564*ccd92e6dSuwe 	static const char *enc_name[] = { "SINGLE", "DOUBLE", NULL, NULL,
565*ccd92e6dSuwe 	    NULL, "LDOUBLE" };
566*ccd92e6dSuwe 	static char invalid[7];
567*ccd92e6dSuwe 
568*ccd92e6dSuwe 	if (enc > 0 && enc <= nitems(enc_name) && enc_name[enc - 1] != NULL)
569*ccd92e6dSuwe 		return enc_name[enc - 1];
570*ccd92e6dSuwe 
571*ccd92e6dSuwe 	snprintf(invalid, sizeof(invalid), "0x%x", enc);
572*ccd92e6dSuwe 	return invalid;
573*ccd92e6dSuwe }
574*ccd92e6dSuwe 
575*ccd92e6dSuwe const char *
57626d55b61Smpi ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen,
57726d55b61Smpi     uint32_t offset)
57826d55b61Smpi {
57926d55b61Smpi 	const char		*name;
58026d55b61Smpi 
58126d55b61Smpi 	if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
58226d55b61Smpi 		return "external";
58326d55b61Smpi 
58426d55b61Smpi 	if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
58526d55b61Smpi 		return "exceeds strlab";
58626d55b61Smpi 
58726d55b61Smpi 	if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
58826d55b61Smpi 		return "invalid";
58926d55b61Smpi 
59026d55b61Smpi 	name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
59126d55b61Smpi 	if (*name == '\0')
59226d55b61Smpi 		return "(anon)";
59326d55b61Smpi 
59426d55b61Smpi 	return name;
59526d55b61Smpi }
59626d55b61Smpi 
59726d55b61Smpi char *
59826d55b61Smpi decompress(const char *buf, size_t size, off_t len)
59926d55b61Smpi {
60026d55b61Smpi #ifdef ZLIB
60126d55b61Smpi 	z_stream		 stream;
60226d55b61Smpi 	char			*data;
60326d55b61Smpi 	int			 error;
60426d55b61Smpi 
60526d55b61Smpi 	data = malloc(len);
60626d55b61Smpi 	if (data == NULL) {
60726d55b61Smpi 		warn(NULL);
60826d55b61Smpi 		return NULL;
60926d55b61Smpi 	}
61026d55b61Smpi 
61126d55b61Smpi 	memset(&stream, 0, sizeof(stream));
61226d55b61Smpi 	stream.next_in = (void *)buf;
61326d55b61Smpi 	stream.avail_in = size;
61426d55b61Smpi 	stream.next_out = (uint8_t *)data;
61526d55b61Smpi 	stream.avail_out = len;
61626d55b61Smpi 
61726d55b61Smpi 	if ((error = inflateInit(&stream)) != Z_OK) {
61826d55b61Smpi 		warnx("zlib inflateInit failed: %s", zError(error));
61926d55b61Smpi 		goto exit;
62026d55b61Smpi 	}
62126d55b61Smpi 
62226d55b61Smpi 	if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
62326d55b61Smpi 		warnx("zlib inflate failed: %s", zError(error));
62426d55b61Smpi 		inflateEnd(&stream);
62526d55b61Smpi 		goto exit;
62626d55b61Smpi 	}
62726d55b61Smpi 
62826d55b61Smpi 	if ((error = inflateEnd(&stream)) != Z_OK) {
62926d55b61Smpi 		warnx("zlib inflateEnd failed: %s", zError(error));
63026d55b61Smpi 		goto exit;
63126d55b61Smpi 	}
63226d55b61Smpi 
63326d55b61Smpi 	if (stream.total_out != len) {
63426d55b61Smpi 		warnx("decompression failed: %llu != %llu",
63526d55b61Smpi 		    stream.total_out, len);
63626d55b61Smpi 		goto exit;
63726d55b61Smpi 	}
63826d55b61Smpi 
63926d55b61Smpi 	return data;
64026d55b61Smpi 
64126d55b61Smpi exit:
64226d55b61Smpi 	free(data);
64326d55b61Smpi #endif /* ZLIB */
64426d55b61Smpi 	return NULL;
64526d55b61Smpi }
64626d55b61Smpi 
64726d55b61Smpi __dead void
64826d55b61Smpi usage(void)
64926d55b61Smpi {
65026d55b61Smpi 	fprintf(stderr, "usage: %s [-dfhlst] file ...\n",
65126d55b61Smpi 	    getprogname());
65226d55b61Smpi 	exit(1);
65326d55b61Smpi }
654