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