]> sbz's 6dev Repos - ctfdump/.git/blame - ctfdump.c
Almost complete type dump
[ctfdump/.git] / ctfdump.c
CommitLineData
f8621c6a
MP
1/*
2 * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <sys/param.h>
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <sys/exec_elf.h>
21#include <sys/mman.h>
22
23#include <err.h>
24#include <fcntl.h>
25#include <locale.h>
26#include <stdio.h>
27#include <stdint.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31
32#ifdef ZLIB
33#include <zlib.h>
34#endif /* ZLIB */
35
36#include "ctf.h"
37
74b86e22
MP
38#ifndef nitems
39#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
40#endif
41
f8621c6a
MP
42#define SUNW_CTF ".SUNW_ctf"
43
44#define DUMP_OBJECT (1 << 0)
45#define DUMP_FUNCTION (1 << 1)
46#define DUMP_HEADER (1 << 2)
751fc09a 47#define DUMP_LABEL (1 << 3)
80c28199 48#define DUMP_STRTAB (1 << 4)
74b86e22
MP
49#define DUMP_STATISTIC (1 << 5)
50#define DUMP_TYPE (1 << 6)
f8621c6a
MP
51
52int dump(const char *, uint32_t);
53int iself(const char *, size_t);
54int isctf(const char *, size_t);
55__dead void usage(void);
56
57int ctf_dump(const char *, size_t, uint32_t);
74b86e22
MP
58unsigned int ctf_dump_type(struct ctf_header *, const char *, off_t,
59 unsigned int, unsigned int);
60const char *ctf_kind2name(unsigned short);
f8621c6a
MP
61const char *ctf_off2name(struct ctf_header *, const char *, off_t,
62 unsigned int);
63
64int elf_dump(const char *, size_t, uint32_t);
65int elf_getshstrtab(const char *, size_t, const char **, size_t *);
66int elf_getsymtab(const char *, const char *, size_t,
67 const Elf_Sym **, size_t *);
68int elf_getstrtab(const char *, const char *, size_t,
69 const char **, size_t *);
70
71#ifdef ZLIB
72char *decompress(const char *, size_t, off_t);
73#endif /* ZLIB */
74
75int
76main(int argc, char *argv[])
77{
78 const char *filename;
79 uint32_t flags = 0;
80 int ch, error = 0;
81
82 setlocale(LC_ALL, "");
83
84 while ((ch = getopt(argc, argv, "dfhlsSt")) != -1) {
85 switch (ch) {
9fe3b2c8
MP
86 case 'd':
87 flags |= DUMP_OBJECT;
88 break;
acada86d
MP
89 case 'f':
90 flags |= DUMP_FUNCTION;
91 break;
f8621c6a
MP
92 case 'h':
93 flags |= DUMP_HEADER;
94 break;
751fc09a
MP
95 case 'l':
96 flags |= DUMP_LABEL;
97 break;
80c28199
MP
98 case 's':
99 flags |= DUMP_STRTAB;
100 break;
74b86e22
MP
101 case 't':
102 flags |= DUMP_TYPE;
103 break;
f8621c6a
MP
104 default:
105 usage();
106 }
107 }
108
109 argc -= optind;
110 argv += optind;
111
112 while ((filename = *argv++) != NULL)
113 error |= dump(filename, flags);
114
115 return error;
116}
117
118int
119dump(const char *path, uint32_t flags)
120{
121 struct stat st;
122 int fd, error = 1;
123 char *p;
124
125 fd = open(path, O_RDONLY);
126 if (fd == -1) {
127 warn("open");
128 return 1;
129 }
130 if (fstat(fd, &st) == -1) {
131 warn("fstat");
132 return 1;
133 }
134 if (st.st_size < (off_t)sizeof(struct ctf_header)) {
135 warnx("file too small to be CTF");
136 return 1;
137 }
138 if ((uintmax_t)st.st_size > SIZE_MAX) {
139 warnx("file too big to fit memory");
140 return 1;
141 }
142
143 p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
144 if (p == MAP_FAILED)
145 err(1, "mmap");
146
147 if (iself(p, st.st_size)) {
148 error = elf_dump(p, st.st_size, flags);
149 } else if (isctf(p, st.st_size)) {
150 error = ctf_dump(p, st.st_size, flags);
151 }
152
153 munmap(p, st.st_size);
154 close(fd);
155
156 return error;
157}
158
159int
160iself(const char *p, size_t filesize)
161{
162 Elf_Ehdr *eh = (Elf_Ehdr *)p;
163
164 if (eh->e_ehsize < sizeof(Elf_Ehdr) || !IS_ELF(*eh)) {
165 warnx("file is not ELF");
166 return 0;
167 }
168 if (eh->e_ident[EI_CLASS] != ELFCLASS) {
169 warnx("unexpected word size %u", eh->e_ident[EI_CLASS]);
170 return 0;
171 }
172 if (eh->e_ident[EI_VERSION] != ELF_TARG_VER) {
173 warnx("unexpected version %u", eh->e_ident[EI_VERSION]);
174 return 0;
175 }
176 if (eh->e_ident[EI_DATA] >= ELFDATANUM) {
177 warnx("unexpected data format %u", eh->e_ident[EI_DATA]);
178 return 0;
179 }
180 if (eh->e_shoff > filesize) {
181 warnx("bogus section table offset 0x%llx", eh->e_shoff);
182 return 0;
183 }
184 if (eh->e_shentsize < sizeof(Elf_Shdr)) {
185 warnx("bogus section header size %u", eh->e_shentsize);
186 return 0;
187 }
188 if (eh->e_shnum > (filesize - eh->e_shoff) / eh->e_shentsize) {
189 warnx("bogus section header count %u", eh->e_shnum);
190 return 0;
191 }
192 if (eh->e_shstrndx >= eh->e_shnum) {
193 warnx("bogus string table index %u", eh->e_shstrndx);
194 return 0;
195 }
196
197 return 1;
198}
199
200int
201elf_getshstrtab(const char *p, size_t filesize, const char **shstrtab,
202 size_t *shstrtabsize)
203{
204 Elf_Ehdr *eh = (Elf_Ehdr *)p;
205 Elf_Shdr *sh;
206
207 sh = (Elf_Shdr *)(p + eh->e_shoff + eh->e_shstrndx * eh->e_shentsize);
208 if (sh->sh_type != SHT_STRTAB) {
209 warnx("unexpected string table type");
210 return 1;
211 }
212 if (sh->sh_offset > filesize) {
213 warnx("bogus string table offset");
214 return 1;
215 }
216 if (sh->sh_size > filesize - sh->sh_offset) {
217 warnx("bogus string table size");
218 return 1;
219 }
220 if (shstrtab != NULL)
221 *shstrtab = p + sh->sh_offset;
222 if (shstrtabsize != NULL)
223 *shstrtabsize = sh->sh_size;
224
225 return 0;
226}
227
228int
229elf_getsymtab(const char *p, const char *shstrtab, size_t shstrtabsize,
230 const Elf_Sym **symtab, size_t *nsymb)
231{
232 Elf_Ehdr *eh = (Elf_Ehdr *)p;
233 Elf_Shdr *sh;
234 size_t i;
235
236 for (i = 0; i < eh->e_shnum; i++) {
237 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
238
239 if (sh->sh_type != SHT_SYMTAB)
240 continue;
241
242 if ((sh->sh_link >= eh->e_shnum) ||
243 (sh->sh_name >= shstrtabsize))
244 continue;
245
246 if (strncmp(shstrtab + sh->sh_name, ELF_SYMTAB,
247 strlen(ELF_SYMTAB)) == 0) {
248 if (symtab != NULL)
249 *symtab = (Elf_Sym *)(p + sh->sh_offset);
250 if (nsymb != NULL)
251 *nsymb = (sh->sh_size / sh->sh_entsize);
252
253 return 0;
254 }
255 }
256
257 return 1;
258}
259
260int
261elf_getstrtab(const char *p, const char *shstrtab, size_t shstrtabsize,
262 const char **strtab, size_t *strtabsize)
263{
264 Elf_Ehdr *eh = (Elf_Ehdr *)p;
265 Elf_Shdr *sh;
266 size_t i;
267
268 for (i = 0; i < eh->e_shnum; i++) {
269 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
270
271 if (sh->sh_type != SHT_STRTAB)
272 continue;
273
274 if ((sh->sh_link >= eh->e_shnum) ||
275 (sh->sh_name >= shstrtabsize))
276 continue;
277
278 if (strncmp(shstrtab + sh->sh_name, ELF_STRTAB,
279 strlen(ELF_STRTAB)) == 0) {
280 if (strtab != NULL)
281 *strtab = p + sh->sh_offset;
282 if (strtabsize != NULL)
283 *strtabsize = sh->sh_size;
284
285 return 0;
286 }
287 }
288
289 return 1;
290}
291
292const char *strtab;
293const Elf_Sym *symtab;
294size_t strtabsize, nsymb;
295
9fe3b2c8
MP
296const char *
297elf_idx2sym(size_t *idx, unsigned char type)
298{
299 const Elf_Sym *st;
300 size_t i;
301
302 for (i = *idx + 1; i < nsymb; i++) {
303 st = &symtab[i];
304
305 if (ELF_ST_TYPE(st->st_info) != type)
306 continue;
307
308 *idx = i;
309 return strtab + st->st_name;
310 }
311
312 return NULL;
313}
314
f8621c6a
MP
315int
316elf_dump(const char *p, size_t filesize, uint32_t flags)
317{
318 Elf_Ehdr *eh = (Elf_Ehdr *)p;
319 Elf_Shdr *sh;
320 const char *shstrtab;
321 size_t i, shstrtabsize;
322
323 /* Find section header string table location and size. */
324 if (elf_getshstrtab(p, filesize, &shstrtab, &shstrtabsize))
325 return 1;
326
327 /* Find symbol table location and number of symbols. */
328 if (elf_getsymtab(p, shstrtab, shstrtabsize, &symtab, &nsymb))
329 warnx("symbol table not found");
330
331 /* Find string table location and size. */
332 if (elf_getstrtab(p, shstrtab, shstrtabsize, &strtab, &strtabsize))
333 warnx("string table not found");
334
335 /* Find CTF section and dump it. */
336 for (i = 0; i < eh->e_shnum; i++) {
337 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
338
339 if ((sh->sh_link >= eh->e_shnum) ||
340 (sh->sh_name >= shstrtabsize))
341 continue;
342
343 if (strncmp(shstrtab + sh->sh_name, SUNW_CTF, strlen(SUNW_CTF)))
344 continue;
345
346 if (!isctf(p + sh->sh_offset, sh->sh_size))
347 break;
348
349 return ctf_dump(p + sh->sh_offset, sh->sh_size, flags);
350 }
351
352 warnx("%s section not found", SUNW_CTF);
353 return 1;
354}
355
356int
357isctf(const char *p, size_t filesize)
358{
359 struct ctf_header *cth = (struct ctf_header *)p;
360 off_t dlen = cth->cth_stroff + cth->cth_strlen;
361
362 if (cth->cth_magic != CTF_MAGIC || cth->cth_version != CTF_VERSION)
363 return 0;
364
365 if (dlen > filesize && !(cth->cth_flags & CTF_F_COMPRESS)) {
366 warnx("bogus file size");
367 return 0;
368 }
369
370 if ((cth->cth_lbloff & 3) || (cth->cth_objtoff & 1) ||
371 (cth->cth_funcoff & 1) || (cth->cth_typeoff & 3)) {
372 warnx("wrongly aligned offset");
373 return 0;
374 }
375
376 if ((cth->cth_lbloff >= dlen) || (cth->cth_objtoff >= dlen) ||
377 (cth->cth_funcoff >= dlen) || (cth->cth_typeoff >= dlen)) {
378 warnx("truncated file");
379 return 0;
380 }
381
382 if ((cth->cth_lbloff > cth->cth_objtoff) ||
383 (cth->cth_objtoff > cth->cth_funcoff) ||
384 (cth->cth_funcoff > cth->cth_typeoff) ||
385 (cth->cth_typeoff > cth->cth_stroff)) {
386 warnx("corrupted file");
387 return 0;
388 }
389
390 return 1;
391}
392
393int
394ctf_dump(const char *p, size_t size, uint32_t flags)
395{
396 struct ctf_header *cth = (struct ctf_header *)p;
397 char *data = (char *)p;
398 off_t dlen = cth->cth_stroff + cth->cth_strlen;
399
400 if (cth->cth_flags & CTF_F_COMPRESS) {
401 data = decompress(p + sizeof(*cth), size - sizeof(*cth), dlen);
402 if (data == NULL)
403 return 1;
404 }
405
406 if (flags & DUMP_HEADER) {
407 printf("cth_magic = 0x%04x\n", cth->cth_magic);
408 printf("cth_version = %d\n", cth->cth_version);
409 printf("cth_flags = 0x%02x\n", cth->cth_flags);
410 printf("cth_parlabel = %s\n",
411 ctf_off2name(cth, data, dlen, cth->cth_parname));
412 printf("cth_parname = %s\n",
413 ctf_off2name(cth, data, dlen, cth->cth_parname));
414 printf("cth_lbloff = %d\n", cth->cth_lbloff);
415 printf("cth_objtoff = %d\n", cth->cth_objtoff);
416 printf("cth_funcoff = %d\n", cth->cth_funcoff);
417 printf("cth_typeoff = %d\n", cth->cth_typeoff);
418 printf("cth_stroff = %d\n", cth->cth_stroff);
419 printf("cth_strlen = %d\n", cth->cth_strlen);
420 }
421
751fc09a
MP
422 if (flags & DUMP_LABEL) {
423 unsigned int lbloff = cth->cth_lbloff;
424 struct ctf_lblent *ctl;
425
426 while (lbloff < cth->cth_objtoff) {
427 ctl = (struct ctf_lblent *)(data + lbloff);
428
429 printf("%5u %s\n", ctl->ctl_typeidx,
430 ctf_off2name(cth, data, dlen, ctl->ctl_label));
431
432 lbloff += sizeof(*ctl);
433 }
434 }
435
9fe3b2c8
MP
436 if (flags & DUMP_OBJECT) {
437 unsigned int objtoff = cth->cth_objtoff;
438 size_t idx = 0, i = 0;
439 unsigned short *dsp;
440 const char *s;
441 int l;
442
443 while (objtoff < cth->cth_funcoff) {
444 dsp = (unsigned short *)(data + objtoff);
445
446 l = printf("[%zu] %u", i++, *dsp);
447 if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL)
448 printf("%*s %s (%zu)\n", (12 - l), "", s, idx);
449 else
450 printf("\n");
451
452 objtoff += sizeof(*dsp);
453 }
454 }
455
acada86d
MP
456 if (flags & DUMP_FUNCTION) {
457 unsigned short *fsp, kind, vlen;
458 size_t idx = 0, i = 0;
459 const char *s;
460 int l;
461
462 fsp = (unsigned short *)(data + cth->cth_funcoff);
463 while (fsp < (unsigned short *)(data + cth->cth_typeoff)) {
464 kind = CTF_INFO_KIND(*fsp);
465 vlen = CTF_INFO_VLEN(*fsp);
466 fsp++;
467
468 if (kind == CTF_K_UNKNOWN && vlen == 0)
469 continue;
470
471 l = printf("%u [%zu] FUNC", vlen, i++);
472 if ((s = elf_idx2sym(&idx, STT_FUNC)) != NULL)
473 printf(" (%s)", s);
474 printf(" returns: %u args: (", *fsp++);
475 while (vlen-- > 0)
476 printf("%u%s", *fsp++, (vlen > 0) ? ", " : "");
477 printf(")\n");
478 }
479 }
480
80c28199
MP
481 if (flags & DUMP_STRTAB) {
482 unsigned int offset = 0;
483 const char *str;
484
485 while (offset < cth->cth_strlen) {
486 str = data + cth->cth_stroff + offset;
487
488 printf("[%u] ", offset);
489 if (*str != '\0')
490 offset += printf("%s\n", str);
491 else {
492 printf("\\0\n");
493 offset++;
494 }
495 }
496 }
497
74b86e22
MP
498 if (flags & DUMP_TYPE) {
499 unsigned int idx = 1, offset = 0;
500
501 while (offset < cth->cth_stroff)
502 offset += ctf_dump_type(cth, data, dlen, offset, idx++);
503
504 }
505
f8621c6a
MP
506 if (cth->cth_flags & CTF_F_COMPRESS)
507 free(data);
508
509 return 0;
510}
511
74b86e22
MP
512unsigned int
513ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen,
514 unsigned int offset, unsigned int idx)
515{
516 const struct ctf_type *ctt;
517 unsigned short kind, vlen, root;
518 unsigned int toff, tlen = 0;
519 uint64_t size;
520 const char *name, *kname;
521
522 ctt = (struct ctf_type *)(data + cth->cth_typeoff + offset);
523 kind = CTF_INFO_KIND(ctt->ctt_info);
524 vlen = CTF_INFO_VLEN(ctt->ctt_info);
525 root = CTF_INFO_ISROOT(ctt->ctt_info);
526 name = ctf_off2name(cth, data, dlen, ctt->ctt_name);
527
528 if (root)
529 printf("<%u> ", idx);
530 else
531 printf("[%u] ", idx);
532
533 if ((kname = ctf_kind2name(kind)) != NULL)
534 printf("%s %s", kname, name);
535
536 if (ctt->ctt_size <= CTF_MAX_SIZE) {
537 size = ctt->ctt_size;
538 toff = sizeof(struct ctf_stype);
539 } else {
540 size = CTF_TYPE_LSIZE(ctt);
541 toff = sizeof(struct ctf_type);
542 }
543
544 switch (kind) {
545 case CTF_K_UNKNOWN:
546 case CTF_K_FORWARD:
547 break;
548 case CTF_K_INTEGER:
549 tlen = sizeof(unsigned int);
550 break;
551 case CTF_K_FLOAT:
552 break;
553 case CTF_K_ARRAY:
554 tlen = sizeof(struct ctf_array);
555 break;
556 case CTF_K_FUNCTION:
557 tlen = (vlen + (vlen & 1)) * sizeof(unsigned short);
558 break;
559 case CTF_K_STRUCT:
560 case CTF_K_UNION:
561 printf(" (%llu bytes)", size);
562 if (size < CTF_LSTRUCT_THRESH)
563 tlen = vlen * sizeof(struct ctf_member);
564 else
565 tlen = vlen * sizeof(struct ctf_lmember);
566 break;
567 case CTF_K_ENUM:
568 tlen = vlen * sizeof(struct ctf_enum);
569 break;
570 case CTF_K_POINTER:
571 vlen = sizeof(unsigned int);
572 /* FALLTHROUGH */
573 case CTF_K_TYPEDEF:
574 case CTF_K_VOLATILE:
575 case CTF_K_CONST:
576 case CTF_K_RESTRICT:
577 printf(" refers to %u", ctt->ctt_type);
578 break;
579 default:
580 errx(1, "incorrect type %u at offset %u", kind, offset);
581 }
582
583 printf("\n");
584
585 return toff + tlen;
586}
587
588const char *
589ctf_kind2name(unsigned short kind)
590{
591 static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER",
592 "ARRAYS", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD",
593 "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" };
594
595 if (kind >= nitems(kind_name))
596 return NULL;
597
598 return kind_name[kind];
599}
600
f8621c6a
MP
601const char *
602ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen,
603 unsigned int offset)
604{
605 const char *name;
606
607 if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
608 return "external";
609
610 if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
611 return "exceeds strlab";
612
613 if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
614 return "invalid";
615
616 name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
617 if (*name == '\0')
618 return "(anon)";
619
620 return name;
621}
622
623char *
624decompress(const char *buf, size_t size, off_t len)
625{
626#ifdef ZLIB
627 z_stream stream;
628 char *data;
629 int error;
630
631 data = malloc(len);
632 if (data == NULL) {
633 warn(NULL);
634 return NULL;
635 }
636
637 memset(&stream, 0, sizeof(stream));
638 stream.next_in = (void *)buf;
639 stream.avail_in = size;
640 stream.next_out = data;
641 stream.avail_out = len;
642
643 if ((error = inflateInit(&stream)) != Z_OK) {
644 warnx("zlib inflateInit failed: %s", zError(error));
645 goto exit;
646 }
647
648 if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
649 warnx("zlib inflate failed: %s", zError(error));
650 goto exit;
651 }
652
653 if ((error = inflateEnd(&stream)) != Z_OK) {
654 warnx("zlib inflateEnd failed: %s", zError(error));
655 goto exit;
656 }
657
658 if (stream.total_out != len) {
659 warnx("decompression failed: %llu != %llu",
660 stream.total_out, len);
661 goto exit;
662 }
663
664 return data;
665
666exit:
667 free(data);
668#endif /* ZLIB */
669 return NULL;
670}
671
672__dead void
673usage(void)
674{
675 extern char *__progname;
676
677 fprintf(stderr, "usage: %s [-dfhlsSt] [file ...]\n",
678 __progname);
679 exit(1);
680}
681