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