xref: /openbsd/usr.bin/ctfdump/ctfdump.c (revision 4e6a2d21)
1*4e6a2d21Sclaudio /*	$OpenBSD: ctfdump.c,v 1.28 2024/02/22 13:21:03 claudio 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/mman.h>
2226d55b61Smpi #include <sys/ctf.h>
2326d55b61Smpi 
2426d55b61Smpi #include <err.h>
2526d55b61Smpi #include <fcntl.h>
269b214edaSsunil #include <gelf.h>
279b214edaSsunil #include <libelf.h>
2826d55b61Smpi #include <locale.h>
2926d55b61Smpi #include <stdio.h>
3026d55b61Smpi #include <stdint.h>
3126d55b61Smpi #include <stdlib.h>
3226d55b61Smpi #include <string.h>
3326d55b61Smpi #include <unistd.h>
3426d55b61Smpi 
3526d55b61Smpi #ifdef ZLIB
3626d55b61Smpi #include <zlib.h>
3726d55b61Smpi #endif /* ZLIB */
3826d55b61Smpi 
3926d55b61Smpi #ifndef nitems
4026d55b61Smpi #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
4126d55b61Smpi #endif
4226d55b61Smpi 
4326d55b61Smpi #define DUMP_OBJECT	(1 << 0)
4426d55b61Smpi #define DUMP_FUNCTION	(1 << 1)
4526d55b61Smpi #define DUMP_HEADER	(1 << 2)
4626d55b61Smpi #define DUMP_LABEL	(1 << 3)
4726d55b61Smpi #define DUMP_STRTAB	(1 << 4)
4826d55b61Smpi #define DUMP_STATISTIC	(1 << 5)
4926d55b61Smpi #define DUMP_TYPE	(1 << 6)
5026d55b61Smpi 
5126d55b61Smpi int		 dump(const char *, uint8_t);
5226d55b61Smpi int		 isctf(const char *, size_t);
5326d55b61Smpi __dead void	 usage(void);
5426d55b61Smpi 
5526d55b61Smpi int		 ctf_dump(const char *, size_t, uint8_t);
562f07e5c0Smillert void		 ctf_dump_type(struct ctf_header *, const char *, size_t,
571f72f5aaSmpi 		     uint32_t, uint32_t *, uint32_t);
5826d55b61Smpi const char	*ctf_kind2name(uint16_t);
5926d55b61Smpi const char	*ctf_enc2name(uint16_t);
60ccd92e6dSuwe const char	*ctf_fpenc2name(uint16_t);
612f07e5c0Smillert const char	*ctf_off2name(struct ctf_header *, const char *, size_t,
6226d55b61Smpi 		     uint32_t);
6326d55b61Smpi 
642f07e5c0Smillert char		*decompress(const char *, size_t, size_t);
659b214edaSsunil int		 elf_dump(uint8_t);
669b214edaSsunil const char	*elf_idx2sym(size_t *, uint8_t);
6726d55b61Smpi 
6826d55b61Smpi int
main(int argc,char * argv[])6926d55b61Smpi main(int argc, char *argv[])
7026d55b61Smpi {
7126d55b61Smpi 	const char *filename;
7226d55b61Smpi 	uint8_t flags = 0;
7326d55b61Smpi 	int ch, error = 0;
7426d55b61Smpi 
7526e2f886Sjasper 	setlocale(LC_ALL, "");
7626e2f886Sjasper 
77933e960dSjasper 	if (pledge("stdio rpath", NULL) == -1)
78933e960dSjasper 		err(1, "pledge");
79933e960dSjasper 
8026d55b61Smpi 	while ((ch = getopt(argc, argv, "dfhlst")) != -1) {
8126d55b61Smpi 		switch (ch) {
8226d55b61Smpi 		case 'd':
8326d55b61Smpi 			flags |= DUMP_OBJECT;
8426d55b61Smpi 			break;
8526d55b61Smpi 		case 'f':
8626d55b61Smpi 			flags |= DUMP_FUNCTION;
8726d55b61Smpi 			break;
8826d55b61Smpi 		case 'h':
8926d55b61Smpi 			flags |= DUMP_HEADER;
9026d55b61Smpi 			break;
9126d55b61Smpi 		case 'l':
9226d55b61Smpi 			flags |= DUMP_LABEL;
9326d55b61Smpi 			break;
9426d55b61Smpi 		case 's':
9526d55b61Smpi 			flags |= DUMP_STRTAB;
9626d55b61Smpi 			break;
9726d55b61Smpi 		case 't':
9826d55b61Smpi 			flags |= DUMP_TYPE;
9926d55b61Smpi 			break;
10026d55b61Smpi 		default:
10126d55b61Smpi 			usage();
10226d55b61Smpi 		}
10326d55b61Smpi 	}
10426d55b61Smpi 
10526d55b61Smpi 	argc -= optind;
10626d55b61Smpi 	argv += optind;
10726d55b61Smpi 
10826d55b61Smpi 	if (argc <= 0)
10926d55b61Smpi 		usage();
11026d55b61Smpi 
11126d55b61Smpi 	/* Dump everything by default */
11226d55b61Smpi 	if (flags == 0)
11326d55b61Smpi 		flags = 0xff;
11426d55b61Smpi 
1159b214edaSsunil 	if (elf_version(EV_CURRENT) == EV_NONE)
1169b214edaSsunil 		errx(1, "elf_version: %s", elf_errmsg(-1));
1179b214edaSsunil 
11826d55b61Smpi 	while ((filename = *argv++) != NULL)
11926d55b61Smpi 		error |= dump(filename, flags);
12026d55b61Smpi 
12126d55b61Smpi 	return error;
12226d55b61Smpi }
12326d55b61Smpi 
1249b214edaSsunil Elf	*e;
1259b214edaSsunil Elf_Scn	*scnsymtab;
1269b214edaSsunil size_t	 strtabndx, strtabsz, nsymb;
1279b214edaSsunil 
12826d55b61Smpi int
dump(const char * path,uint8_t flags)12926d55b61Smpi dump(const char *path, uint8_t flags)
13026d55b61Smpi {
13126d55b61Smpi 	struct stat	 st;
13226d55b61Smpi 	char		*p;
1339b214edaSsunil 	int		 fd, error = 1;
13426d55b61Smpi 
13526d55b61Smpi 	fd = open(path, O_RDONLY);
13626d55b61Smpi 	if (fd == -1) {
13726d55b61Smpi 		warn("open");
13826d55b61Smpi 		return 1;
13926d55b61Smpi 	}
1409b214edaSsunil 
1419b214edaSsunil 	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
1429b214edaSsunil 		warnx("elf_begin: %s", elf_errmsg(-1));
1439b214edaSsunil 		goto done;
1449b214edaSsunil 	}
1459b214edaSsunil 
1469b214edaSsunil 	if (elf_kind(e) == ELF_K_ELF) {
1479b214edaSsunil 		error = elf_dump(flags);
1489b214edaSsunil 		elf_end(e);
1499b214edaSsunil 		goto done;
1509b214edaSsunil 	}
1519b214edaSsunil 	elf_end(e);
1529b214edaSsunil 
15326d55b61Smpi 	if (fstat(fd, &st) == -1) {
15426d55b61Smpi 		warn("fstat");
1559b214edaSsunil 		goto done;
15626d55b61Smpi 	}
15726d55b61Smpi 	if ((uintmax_t)st.st_size > SIZE_MAX) {
15826d55b61Smpi 		warnx("file too big to fit memory");
1599b214edaSsunil 		goto done;
16026d55b61Smpi 	}
16126d55b61Smpi 
16226d55b61Smpi 	p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
16326d55b61Smpi 	if (p == MAP_FAILED)
16426d55b61Smpi 		err(1, "mmap");
16526d55b61Smpi 
1669b214edaSsunil 	if (isctf(p, st.st_size))
16726d55b61Smpi 		error = ctf_dump(p, st.st_size, flags);
16826d55b61Smpi 
16926d55b61Smpi 	munmap(p, st.st_size);
17026d55b61Smpi 
1719b214edaSsunil  done:
1729b214edaSsunil 	close(fd);
17326d55b61Smpi 	return error;
17426d55b61Smpi }
17526d55b61Smpi 
17626d55b61Smpi const char *
elf_idx2sym(size_t * idx,uint8_t type)17726d55b61Smpi elf_idx2sym(size_t *idx, uint8_t type)
17826d55b61Smpi {
1799b214edaSsunil 	GElf_Sym	 sym;
1809b214edaSsunil 	Elf_Data	*data;
1819b214edaSsunil 	char		*name;
18226d55b61Smpi 	size_t		 i;
18326d55b61Smpi 
1849b214edaSsunil 	if (scnsymtab == NULL || strtabndx == 0)
1853bdd1273Smpi 		return NULL;
1863bdd1273Smpi 
1879b214edaSsunil 	data = NULL;
1889b214edaSsunil 	while ((data = elf_rawdata(scnsymtab, data)) != NULL) {
18926d55b61Smpi 		for (i = *idx + 1; i < nsymb; i++) {
1909b214edaSsunil 			if (gelf_getsym(data, i, &sym) != &sym)
1919b214edaSsunil 				continue;
1929b214edaSsunil 			if (GELF_ST_TYPE(sym.st_info) != type)
1939b214edaSsunil 				continue;
1949b214edaSsunil 			if (sym.st_name >= strtabsz)
1959b214edaSsunil 				break;
1969b214edaSsunil 			if ((name = elf_strptr(e, strtabndx,
1979b214edaSsunil 			    sym.st_name)) == NULL)
19826d55b61Smpi 				continue;
19926d55b61Smpi 
20026d55b61Smpi 			*idx = i;
2019b214edaSsunil 			return name;
2029b214edaSsunil 		}
20326d55b61Smpi 	}
20426d55b61Smpi 
20526d55b61Smpi 	return NULL;
20626d55b61Smpi }
20726d55b61Smpi 
20826d55b61Smpi int
elf_dump(uint8_t flags)2099b214edaSsunil elf_dump(uint8_t flags)
21026d55b61Smpi {
2119b214edaSsunil 	GElf_Shdr	 shdr;
2129b214edaSsunil 	Elf_Scn		*scn, *scnctf;
2139b214edaSsunil 	Elf_Data	*data;
2149b214edaSsunil 	char		*name;
2159b214edaSsunil 	size_t		 shstrndx;
216e868eff2Smpi 	int		 error = 0;
21726d55b61Smpi 
2189b214edaSsunil 	if (elf_getshdrstrndx(e, &shstrndx) != 0) {
2199b214edaSsunil 		warnx("elf_getshdrstrndx: %s", elf_errmsg(-1));
220e868eff2Smpi 		return 1;
22126d55b61Smpi 	}
22226d55b61Smpi 
2239b214edaSsunil 	scn = scnctf = NULL;
2249b214edaSsunil 	while ((scn = elf_nextscn(e, scn)) != NULL) {
2259b214edaSsunil 		if (gelf_getshdr(scn, &shdr) != &shdr) {
2269b214edaSsunil 			warnx("elf_getshdr: %s", elf_errmsg(-1));
227e868eff2Smpi 			return 1;
2289b214edaSsunil 		}
2299b214edaSsunil 
2309b214edaSsunil 		if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL) {
2319b214edaSsunil 			warnx("elf_strptr: %s", elf_errmsg(-1));
232e868eff2Smpi 			return 1;
2339b214edaSsunil 		}
2349b214edaSsunil 
2359b214edaSsunil 		if (strcmp(name, ELF_CTF) == 0)
2369b214edaSsunil 			scnctf = scn;
2379b214edaSsunil 
2389b214edaSsunil 		if (strcmp(name, ELF_SYMTAB) == 0 &&
2399b214edaSsunil 		    shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
2409b214edaSsunil 			scnsymtab = scn;
2419b214edaSsunil 			nsymb = shdr.sh_size / shdr.sh_entsize;
2429b214edaSsunil 		}
2439b214edaSsunil 
2449b214edaSsunil 		if (strcmp(name, ELF_STRTAB) == 0 &&
2459b214edaSsunil 		    shdr.sh_type == SHT_STRTAB) {
2469b214edaSsunil 			strtabndx = elf_ndxscn(scn);
2479b214edaSsunil 			strtabsz = shdr.sh_size;
2489b214edaSsunil 		}
2499b214edaSsunil 	}
2509b214edaSsunil 
2519b214edaSsunil 	if (scnctf == NULL) {
25226d55b61Smpi 		warnx("%s section not found", ELF_CTF);
253e868eff2Smpi 		return 1;
2549b214edaSsunil 	}
2559b214edaSsunil 
2569b214edaSsunil 	if (scnsymtab == NULL)
2579b214edaSsunil 		warnx("symbol table not found");
2589b214edaSsunil 
2599b214edaSsunil 	data = NULL;
2609b214edaSsunil 	while ((data = elf_rawdata(scnctf, data)) != NULL) {
2619b214edaSsunil 		if (data->d_buf == NULL) {
2629b214edaSsunil 			warnx("%s section size is zero", ELF_CTF);
263e868eff2Smpi 			return 1;
2649b214edaSsunil 		}
2659b214edaSsunil 
2669b214edaSsunil 		if (isctf(data->d_buf, data->d_size))
2679b214edaSsunil 			error |= ctf_dump(data->d_buf, data->d_size, flags);
2689b214edaSsunil 	}
2699b214edaSsunil 
2709b214edaSsunil 	return error;
27126d55b61Smpi }
27226d55b61Smpi 
27326d55b61Smpi int
isctf(const char * p,size_t filesize)27426d55b61Smpi isctf(const char *p, size_t filesize)
27526d55b61Smpi {
2765cdeb2c0Sbluhm 	struct ctf_header	 cth;
2772f07e5c0Smillert 	size_t			 dlen;
27826d55b61Smpi 
27926d55b61Smpi 	if (filesize < sizeof(struct ctf_header)) {
28026d55b61Smpi 		warnx("file too small to be CTF");
28126d55b61Smpi 		return 0;
28226d55b61Smpi 	}
28326d55b61Smpi 
2845cdeb2c0Sbluhm 	memcpy(&cth, p, sizeof(struct ctf_header));
2855cdeb2c0Sbluhm 	if (cth.cth_magic != CTF_MAGIC || cth.cth_version != CTF_VERSION)
28626d55b61Smpi 		return 0;
28726d55b61Smpi 
2882f07e5c0Smillert 	dlen = cth.cth_stroff + cth.cth_strlen;
2892f07e5c0Smillert 	if (dlen > filesize && !(cth.cth_flags & CTF_F_COMPRESS)) {
29026d55b61Smpi 		warnx("bogus file size");
29126d55b61Smpi 		return 0;
29226d55b61Smpi 	}
29326d55b61Smpi 
2945cdeb2c0Sbluhm 	if ((cth.cth_lbloff & 3) || (cth.cth_objtoff & 1) ||
2955cdeb2c0Sbluhm 	    (cth.cth_funcoff & 1) || (cth.cth_typeoff & 3)) {
29626d55b61Smpi 		warnx("wrongly aligned offset");
29726d55b61Smpi 		return 0;
29826d55b61Smpi 	}
29926d55b61Smpi 
3005cdeb2c0Sbluhm 	if ((cth.cth_lbloff >= dlen) || (cth.cth_objtoff >= dlen) ||
3015cdeb2c0Sbluhm 	    (cth.cth_funcoff >= dlen) || (cth.cth_typeoff >= dlen)) {
30226d55b61Smpi 		warnx("truncated file");
30326d55b61Smpi 		return 0;
30426d55b61Smpi 	}
30526d55b61Smpi 
3065cdeb2c0Sbluhm 	if ((cth.cth_lbloff > cth.cth_objtoff) ||
3075cdeb2c0Sbluhm 	    (cth.cth_objtoff > cth.cth_funcoff) ||
3085cdeb2c0Sbluhm 	    (cth.cth_funcoff > cth.cth_typeoff) ||
3095cdeb2c0Sbluhm 	    (cth.cth_typeoff > cth.cth_stroff)) {
31026d55b61Smpi 		warnx("corrupted file");
31126d55b61Smpi 		return 0;
31226d55b61Smpi 	}
31326d55b61Smpi 
31426d55b61Smpi 	return 1;
31526d55b61Smpi }
31626d55b61Smpi 
31726d55b61Smpi int
ctf_dump(const char * p,size_t size,uint8_t flags)31826d55b61Smpi ctf_dump(const char *p, size_t size, uint8_t flags)
31926d55b61Smpi {
3205cdeb2c0Sbluhm 	struct ctf_header	 cth;
3212f07e5c0Smillert 	size_t			 dlen;
32226d55b61Smpi 	char			*data;
32326d55b61Smpi 
3245cdeb2c0Sbluhm 	memcpy(&cth, p, sizeof(struct ctf_header));
3252f07e5c0Smillert 	dlen = cth.cth_stroff + cth.cth_strlen;
3265cdeb2c0Sbluhm 	if (cth.cth_flags & CTF_F_COMPRESS) {
3275cdeb2c0Sbluhm 		data = decompress(p + sizeof(cth), size - sizeof(cth), dlen);
32826d55b61Smpi 		if (data == NULL)
32926d55b61Smpi 			return 1;
33026d55b61Smpi 	} else {
3315cdeb2c0Sbluhm 		data = (char *)p + sizeof(cth);
33226d55b61Smpi 	}
33326d55b61Smpi 
33426d55b61Smpi 	if (flags & DUMP_HEADER) {
3355cdeb2c0Sbluhm 		printf("  cth_magic    = 0x%04x\n", cth.cth_magic);
3365cdeb2c0Sbluhm 		printf("  cth_version  = %u\n", cth.cth_version);
3375cdeb2c0Sbluhm 		printf("  cth_flags    = 0x%02x\n", cth.cth_flags);
33826d55b61Smpi 		printf("  cth_parlabel = %s\n",
3395cdeb2c0Sbluhm 		    ctf_off2name(&cth, data, dlen, cth.cth_parlabel));
34026d55b61Smpi 		printf("  cth_parname  = %s\n",
3415cdeb2c0Sbluhm 		    ctf_off2name(&cth, data, dlen, cth.cth_parname));
3425cdeb2c0Sbluhm 		printf("  cth_lbloff   = %u\n", cth.cth_lbloff);
3435cdeb2c0Sbluhm 		printf("  cth_objtoff  = %u\n", cth.cth_objtoff);
3445cdeb2c0Sbluhm 		printf("  cth_funcoff  = %u\n", cth.cth_funcoff);
3455cdeb2c0Sbluhm 		printf("  cth_typeoff  = %u\n", cth.cth_typeoff);
3465cdeb2c0Sbluhm 		printf("  cth_stroff   = %u\n", cth.cth_stroff);
3475cdeb2c0Sbluhm 		printf("  cth_strlen   = %u\n", cth.cth_strlen);
34826d55b61Smpi 		printf("\n");
34926d55b61Smpi 	}
35026d55b61Smpi 
35126d55b61Smpi 	if (flags & DUMP_LABEL) {
3525cdeb2c0Sbluhm 		uint32_t		 lbloff = cth.cth_lbloff;
35326d55b61Smpi 		struct ctf_lblent	*ctl;
35426d55b61Smpi 
3555cdeb2c0Sbluhm 		while (lbloff < cth.cth_objtoff) {
35626d55b61Smpi 			ctl = (struct ctf_lblent *)(data + lbloff);
35726d55b61Smpi 
35826d55b61Smpi 			printf("  %5u %s\n", ctl->ctl_typeidx,
3595cdeb2c0Sbluhm 			    ctf_off2name(&cth, data, dlen, ctl->ctl_label));
36026d55b61Smpi 
36126d55b61Smpi 			lbloff += sizeof(*ctl);
36226d55b61Smpi 		}
36326d55b61Smpi 		printf("\n");
36426d55b61Smpi 	}
36526d55b61Smpi 
36626d55b61Smpi 	if (flags & DUMP_OBJECT) {
3675cdeb2c0Sbluhm 		uint32_t		 objtoff = cth.cth_objtoff;
36826d55b61Smpi 		size_t			 idx = 0, i = 0;
36926d55b61Smpi 		uint16_t		*dsp;
37026d55b61Smpi 		const char		*s;
37126d55b61Smpi 		int			 l;
37226d55b61Smpi 
3735cdeb2c0Sbluhm 		while (objtoff < cth.cth_funcoff) {
37426d55b61Smpi 			dsp = (uint16_t *)(data + objtoff);
37526d55b61Smpi 
37626d55b61Smpi 			l = printf("  [%zu] %u", i++, *dsp);
37726d55b61Smpi 			if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL)
37826d55b61Smpi 				printf("%*s %s (%zu)\n", (14 - l), "", s, idx);
37926d55b61Smpi 			else
38026d55b61Smpi 				printf("\n");
38126d55b61Smpi 
38226d55b61Smpi 			objtoff += sizeof(*dsp);
38326d55b61Smpi 		}
38426d55b61Smpi 		printf("\n");
38526d55b61Smpi 	}
38626d55b61Smpi 
38726d55b61Smpi 	if (flags & DUMP_FUNCTION) {
38826d55b61Smpi 		uint16_t		*fsp, kind, vlen;
389bf63fe7cSmpi 		uint16_t		*fstart, *fend;
39026d55b61Smpi 		size_t			 idx = 0, i = -1;
39126d55b61Smpi 		const char		*s;
39226d55b61Smpi 		int			 l;
39326d55b61Smpi 
3945cdeb2c0Sbluhm 		fstart = (uint16_t *)(data + cth.cth_funcoff);
3955cdeb2c0Sbluhm 		fend = (uint16_t *)(data + cth.cth_typeoff);
396bf63fe7cSmpi 
397bf63fe7cSmpi 		fsp = fstart;
398bf63fe7cSmpi 		while (fsp < fend) {
39926d55b61Smpi 			kind = CTF_INFO_KIND(*fsp);
40026d55b61Smpi 			vlen = CTF_INFO_VLEN(*fsp);
40126d55b61Smpi 			s = elf_idx2sym(&idx, STT_FUNC);
40226d55b61Smpi 			fsp++;
40326d55b61Smpi 			i++;
40426d55b61Smpi 
40526d55b61Smpi 			if (kind == CTF_K_UNKNOWN && vlen == 0)
40626d55b61Smpi 				continue;
40726d55b61Smpi 
40826d55b61Smpi 			l = printf("  [%zu] FUNC ", i);
40926d55b61Smpi 			if (s != NULL)
41026d55b61Smpi 				printf("(%s) ", s);
41126d55b61Smpi 			printf("returns: %u args: (", *fsp++);
412bf63fe7cSmpi 			while (vlen-- > 0 && fsp < fend)
41326d55b61Smpi 				printf("%u%s", *fsp++, (vlen > 0) ? ", " : "");
41426d55b61Smpi 			printf(")\n");
41526d55b61Smpi 		}
41626d55b61Smpi 		printf("\n");
41726d55b61Smpi 	}
41826d55b61Smpi 
41926d55b61Smpi 	if (flags & DUMP_TYPE) {
4205cdeb2c0Sbluhm 		uint32_t		 idx = 1, offset = cth.cth_typeoff;
4215cdeb2c0Sbluhm 		uint32_t		 stroff = cth.cth_stroff;
42226d55b61Smpi 
4231f72f5aaSmpi 		while (offset < stroff) {
4245cdeb2c0Sbluhm 			ctf_dump_type(&cth, data, dlen, stroff, &offset, idx++);
42526d55b61Smpi 		}
42626d55b61Smpi 		printf("\n");
42726d55b61Smpi 	}
42826d55b61Smpi 
42926d55b61Smpi 	if (flags & DUMP_STRTAB) {
43026d55b61Smpi 		uint32_t		 offset = 0;
43126d55b61Smpi 		const char		*str;
43226d55b61Smpi 
4335cdeb2c0Sbluhm 		while (offset < cth.cth_strlen) {
4345cdeb2c0Sbluhm 			str = ctf_off2name(&cth, data, dlen, offset);
43526d55b61Smpi 
43626d55b61Smpi 			printf("  [%u] ", offset);
43726d55b61Smpi 			if (strcmp(str, "(anon)"))
43826d55b61Smpi 				offset += printf("%s\n", str);
43926d55b61Smpi 			else {
44026d55b61Smpi 				printf("\\0\n");
44126d55b61Smpi 				offset++;
44226d55b61Smpi 			}
44326d55b61Smpi 		}
44426d55b61Smpi 		printf("\n");
44526d55b61Smpi 	}
44626d55b61Smpi 
4475cdeb2c0Sbluhm 	if (cth.cth_flags & CTF_F_COMPRESS)
44826d55b61Smpi 		free(data);
44926d55b61Smpi 
45026d55b61Smpi 	return 0;
45126d55b61Smpi }
45226d55b61Smpi 
4531f72f5aaSmpi void
ctf_dump_type(struct ctf_header * cth,const char * data,size_t dlen,uint32_t stroff,uint32_t * offset,uint32_t idx)4542f07e5c0Smillert ctf_dump_type(struct ctf_header *cth, const char *data, size_t dlen,
4551f72f5aaSmpi     uint32_t stroff, uint32_t *offset, uint32_t idx)
45626d55b61Smpi {
4571f72f5aaSmpi 	const char		*p = data + *offset;
45826d55b61Smpi 	const struct ctf_type	*ctt = (struct ctf_type *)p;
45926d55b61Smpi 	const struct ctf_array	*cta;
46026d55b61Smpi 	uint16_t		*argp, i, kind, vlen, root;
46126d55b61Smpi 	uint32_t		 eob, toff;
46226d55b61Smpi 	uint64_t		 size;
46326d55b61Smpi 	const char		*name, *kname;
46426d55b61Smpi 
46526d55b61Smpi 	kind = CTF_INFO_KIND(ctt->ctt_info);
46626d55b61Smpi 	vlen = CTF_INFO_VLEN(ctt->ctt_info);
46726d55b61Smpi 	root = CTF_INFO_ISROOT(ctt->ctt_info);
46826d55b61Smpi 	name = ctf_off2name(cth, data, dlen, ctt->ctt_name);
46926d55b61Smpi 
47026d55b61Smpi 	if (root)
47126d55b61Smpi 		printf("  <%u> ", idx);
47226d55b61Smpi 	else
47326d55b61Smpi 		printf("  [%u] ", idx);
47426d55b61Smpi 
47526d55b61Smpi 	if ((kname = ctf_kind2name(kind)) != NULL)
47626d55b61Smpi 		printf("%s %s", kname, name);
47726d55b61Smpi 
47826d55b61Smpi 	if (ctt->ctt_size <= CTF_MAX_SIZE) {
47926d55b61Smpi 		size = ctt->ctt_size;
48026d55b61Smpi 		toff = sizeof(struct ctf_stype);
48126d55b61Smpi 	} else {
48226d55b61Smpi 		size = CTF_TYPE_LSIZE(ctt);
48326d55b61Smpi 		toff = sizeof(struct ctf_type);
48426d55b61Smpi 	}
48526d55b61Smpi 
48626d55b61Smpi 	switch (kind) {
48726d55b61Smpi 	case CTF_K_UNKNOWN:
48826d55b61Smpi 	case CTF_K_FORWARD:
48926d55b61Smpi 		break;
49026d55b61Smpi 	case CTF_K_INTEGER:
49126d55b61Smpi 		eob = *((uint32_t *)(p + toff));
49226d55b61Smpi 		toff += sizeof(uint32_t);
493*4e6a2d21Sclaudio 		printf(" encoding=%s offset=%u bits=%u (%llu bytes)",
49426d55b61Smpi 		    ctf_enc2name(CTF_INT_ENCODING(eob)), CTF_INT_OFFSET(eob),
495*4e6a2d21Sclaudio 		    CTF_INT_BITS(eob), size);
49626d55b61Smpi 		break;
49726d55b61Smpi 	case CTF_K_FLOAT:
49826d55b61Smpi 		eob = *((uint32_t *)(p + toff));
49926d55b61Smpi 		toff += sizeof(uint32_t);
500*4e6a2d21Sclaudio 		printf(" encoding=%s offset=%u bits=%u (%llu bytes)",
501ccd92e6dSuwe 		    ctf_fpenc2name(CTF_FP_ENCODING(eob)), CTF_FP_OFFSET(eob),
502*4e6a2d21Sclaudio 		    CTF_FP_BITS(eob), size);
50326d55b61Smpi 		break;
50426d55b61Smpi 	case CTF_K_ARRAY:
50526d55b61Smpi 		cta = (struct ctf_array *)(p + toff);
50626d55b61Smpi 		printf(" content: %u index: %u nelems: %u\n", cta->cta_contents,
50726d55b61Smpi 		    cta->cta_index, cta->cta_nelems);
50826d55b61Smpi 		toff += sizeof(struct ctf_array);
50926d55b61Smpi 		break;
51026d55b61Smpi 	case CTF_K_FUNCTION:
51126d55b61Smpi 		argp = (uint16_t *)(p + toff);
51226d55b61Smpi 		printf(" returns: %u args: (%u", ctt->ctt_type, *argp);
51326d55b61Smpi 		for (i = 1; i < vlen; i++) {
51426d55b61Smpi 			argp++;
5157b3efaaaSsunil 			if ((const char *)argp > data + dlen)
5167b3efaaaSsunil 				errx(1, "offset exceeds CTF section");
5177b3efaaaSsunil 
51826d55b61Smpi 			printf(", %u", *argp);
51926d55b61Smpi 		}
52026d55b61Smpi 		printf(")");
52126d55b61Smpi 		toff += (vlen + (vlen & 1)) * sizeof(uint16_t);
52226d55b61Smpi 		break;
52326d55b61Smpi 	case CTF_K_STRUCT:
52426d55b61Smpi 	case CTF_K_UNION:
52526d55b61Smpi 		printf(" (%llu bytes)\n", size);
52626d55b61Smpi 
52726d55b61Smpi 		if (size < CTF_LSTRUCT_THRESH) {
52826d55b61Smpi 			for (i = 0; i < vlen; i++) {
52926d55b61Smpi 				struct ctf_member	*ctm;
53026d55b61Smpi 
531cb8e4a7cSsunil 				if (p + toff > data + dlen)
532cb8e4a7cSsunil 					errx(1, "offset exceeds CTF section");
533cb8e4a7cSsunil 
5341f72f5aaSmpi 				if (toff > (stroff - sizeof(*ctm)))
5351f72f5aaSmpi 					break;
5361f72f5aaSmpi 
53726d55b61Smpi 				ctm = (struct ctf_member *)(p + toff);
53826d55b61Smpi 				toff += sizeof(struct ctf_member);
53926d55b61Smpi 
54026d55b61Smpi 				printf("\t%s type=%u off=%u\n",
54126d55b61Smpi 				    ctf_off2name(cth, data, dlen,
54226d55b61Smpi 					ctm->ctm_name),
54326d55b61Smpi 				    ctm->ctm_type, ctm->ctm_offset);
54426d55b61Smpi 			}
54526d55b61Smpi 		} else {
54626d55b61Smpi 			for (i = 0; i < vlen; i++) {
54726d55b61Smpi 				struct ctf_lmember	*ctlm;
54826d55b61Smpi 
549cb8e4a7cSsunil 				if (p + toff > data + dlen)
550cb8e4a7cSsunil 					errx(1, "offset exceeds CTF section");
551cb8e4a7cSsunil 
5521f72f5aaSmpi 				if (toff > (stroff - sizeof(*ctlm)))
5531f72f5aaSmpi 					break;
5541f72f5aaSmpi 
55526d55b61Smpi 				ctlm = (struct ctf_lmember *)(p + toff);
55626d55b61Smpi 				toff += sizeof(struct ctf_lmember);
55726d55b61Smpi 
55826d55b61Smpi 				printf("\t%s type=%u off=%llu\n",
55926d55b61Smpi 				    ctf_off2name(cth, data, dlen,
56026d55b61Smpi 					ctlm->ctlm_name),
56126d55b61Smpi 				    ctlm->ctlm_type, CTF_LMEM_OFFSET(ctlm));
56226d55b61Smpi 			}
56326d55b61Smpi 		}
56426d55b61Smpi 		break;
56526d55b61Smpi 	case CTF_K_ENUM:
566*4e6a2d21Sclaudio 		printf(" (%llu bytes)\n", size);
567*4e6a2d21Sclaudio 
56826d55b61Smpi 		for (i = 0; i < vlen; i++) {
56926d55b61Smpi 			struct ctf_enum	*cte;
57026d55b61Smpi 
571bc20f881Ssunil 			if (p + toff > data + dlen)
572bc20f881Ssunil 				errx(1, "offset exceeds CTF section");
573bc20f881Ssunil 
5741f72f5aaSmpi 			if (toff > (stroff - sizeof(*cte)))
5751f72f5aaSmpi 				break;
5761f72f5aaSmpi 
57726d55b61Smpi 			cte = (struct ctf_enum *)(p + toff);
57826d55b61Smpi 			toff += sizeof(struct ctf_enum);
57926d55b61Smpi 
58026d55b61Smpi 			printf("\t%s = %d\n",
58126d55b61Smpi 			    ctf_off2name(cth, data, dlen, cte->cte_name),
58226d55b61Smpi 			    cte->cte_value);
58326d55b61Smpi 		}
58426d55b61Smpi 		break;
58526d55b61Smpi 	case CTF_K_POINTER:
58626d55b61Smpi 	case CTF_K_TYPEDEF:
58726d55b61Smpi 	case CTF_K_VOLATILE:
58826d55b61Smpi 	case CTF_K_CONST:
58926d55b61Smpi 	case CTF_K_RESTRICT:
59026d55b61Smpi 		printf(" refers to %u", ctt->ctt_type);
59126d55b61Smpi 		break;
59226d55b61Smpi 	default:
5931f72f5aaSmpi 		errx(1, "incorrect type %u at offset %u", kind, *offset);
59426d55b61Smpi 	}
59526d55b61Smpi 
59626d55b61Smpi 	printf("\n");
59726d55b61Smpi 
5981f72f5aaSmpi 	*offset += toff;
59926d55b61Smpi }
60026d55b61Smpi 
60126d55b61Smpi const char *
ctf_kind2name(uint16_t kind)60226d55b61Smpi ctf_kind2name(uint16_t kind)
60326d55b61Smpi {
60426d55b61Smpi 	static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER",
60526d55b61Smpi 	   "ARRAY", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD",
60626d55b61Smpi 	   "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" };
60726d55b61Smpi 
60826d55b61Smpi 	if (kind >= nitems(kind_name))
60926d55b61Smpi 		return NULL;
61026d55b61Smpi 
61126d55b61Smpi 	return kind_name[kind];
61226d55b61Smpi }
61326d55b61Smpi 
61426d55b61Smpi const char *
ctf_enc2name(uint16_t enc)61526d55b61Smpi ctf_enc2name(uint16_t enc)
61626d55b61Smpi {
61726d55b61Smpi 	static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR",
61826d55b61Smpi 	    "BOOL", "SIGNED BOOL" };
61926d55b61Smpi 	static char invalid[7];
62026d55b61Smpi 
62126d55b61Smpi 	if (enc == CTF_INT_VARARGS)
62226d55b61Smpi 		return "VARARGS";
62326d55b61Smpi 
62460eaf76fSuwe 	if (enc > 0 && enc <= nitems(enc_name))
62526d55b61Smpi 		return enc_name[enc - 1];
62626d55b61Smpi 
62726d55b61Smpi 	snprintf(invalid, sizeof(invalid), "0x%x", enc);
62826d55b61Smpi 	return invalid;
62926d55b61Smpi }
63026d55b61Smpi 
63126d55b61Smpi const char *
ctf_fpenc2name(uint16_t enc)632ccd92e6dSuwe ctf_fpenc2name(uint16_t enc)
633ccd92e6dSuwe {
634ccd92e6dSuwe 	static const char *enc_name[] = { "SINGLE", "DOUBLE", NULL, NULL,
635ccd92e6dSuwe 	    NULL, "LDOUBLE" };
636ccd92e6dSuwe 	static char invalid[7];
637ccd92e6dSuwe 
638ccd92e6dSuwe 	if (enc > 0 && enc <= nitems(enc_name) && enc_name[enc - 1] != NULL)
639ccd92e6dSuwe 		return enc_name[enc - 1];
640ccd92e6dSuwe 
641ccd92e6dSuwe 	snprintf(invalid, sizeof(invalid), "0x%x", enc);
642ccd92e6dSuwe 	return invalid;
643ccd92e6dSuwe }
644ccd92e6dSuwe 
645ccd92e6dSuwe const char *
ctf_off2name(struct ctf_header * cth,const char * data,size_t dlen,uint32_t offset)6462f07e5c0Smillert ctf_off2name(struct ctf_header *cth, const char *data, size_t dlen,
64726d55b61Smpi     uint32_t offset)
64826d55b61Smpi {
64926d55b61Smpi 	const char		*name;
65026d55b61Smpi 
65126d55b61Smpi 	if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
65226d55b61Smpi 		return "external";
65326d55b61Smpi 
65426d55b61Smpi 	if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
65526d55b61Smpi 		return "exceeds strlab";
65626d55b61Smpi 
65726d55b61Smpi 	if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
65826d55b61Smpi 		return "invalid";
65926d55b61Smpi 
66026d55b61Smpi 	name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
66126d55b61Smpi 	if (*name == '\0')
66226d55b61Smpi 		return "(anon)";
66326d55b61Smpi 
66426d55b61Smpi 	return name;
66526d55b61Smpi }
66626d55b61Smpi 
66726d55b61Smpi char *
decompress(const char * buf,size_t size,size_t len)6682f07e5c0Smillert decompress(const char *buf, size_t size, size_t len)
66926d55b61Smpi {
67026d55b61Smpi #ifdef ZLIB
67126d55b61Smpi 	z_stream		 stream;
67226d55b61Smpi 	char			*data;
67326d55b61Smpi 	int			 error;
67426d55b61Smpi 
67526d55b61Smpi 	data = malloc(len);
67626d55b61Smpi 	if (data == NULL) {
67726d55b61Smpi 		warn(NULL);
67826d55b61Smpi 		return NULL;
67926d55b61Smpi 	}
68026d55b61Smpi 
68126d55b61Smpi 	memset(&stream, 0, sizeof(stream));
68226d55b61Smpi 	stream.next_in = (void *)buf;
68326d55b61Smpi 	stream.avail_in = size;
68426d55b61Smpi 	stream.next_out = (uint8_t *)data;
68526d55b61Smpi 	stream.avail_out = len;
68626d55b61Smpi 
68726d55b61Smpi 	if ((error = inflateInit(&stream)) != Z_OK) {
68826d55b61Smpi 		warnx("zlib inflateInit failed: %s", zError(error));
68926d55b61Smpi 		goto exit;
69026d55b61Smpi 	}
69126d55b61Smpi 
69226d55b61Smpi 	if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
69326d55b61Smpi 		warnx("zlib inflate failed: %s", zError(error));
69426d55b61Smpi 		inflateEnd(&stream);
69526d55b61Smpi 		goto exit;
69626d55b61Smpi 	}
69726d55b61Smpi 
69826d55b61Smpi 	if ((error = inflateEnd(&stream)) != Z_OK) {
69926d55b61Smpi 		warnx("zlib inflateEnd failed: %s", zError(error));
70026d55b61Smpi 		goto exit;
70126d55b61Smpi 	}
70226d55b61Smpi 
7032f07e5c0Smillert 	if (stream.total_out != len) {
7042f07e5c0Smillert 		warnx("decompression failed: %lu != %zu",
70526d55b61Smpi 		    stream.total_out, len);
70626d55b61Smpi 		goto exit;
70726d55b61Smpi 	}
70826d55b61Smpi 
70926d55b61Smpi 	return data;
71026d55b61Smpi 
71126d55b61Smpi exit:
71226d55b61Smpi 	free(data);
71326d55b61Smpi #endif /* ZLIB */
71426d55b61Smpi 	return NULL;
71526d55b61Smpi }
71626d55b61Smpi 
71726d55b61Smpi __dead void
usage(void)71826d55b61Smpi usage(void)
71926d55b61Smpi {
72026d55b61Smpi 	fprintf(stderr, "usage: %s [-dfhlst] file ...\n",
72126d55b61Smpi 	    getprogname());
72226d55b61Smpi 	exit(1);
72326d55b61Smpi }
724