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