Dump types before the string table to match ctftools' output.
[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>
37e1dc0c 22#include <sys/ctf.h>
f8621c6a
MP
23
24#include <err.h>
25#include <fcntl.h>
26#include <locale.h>
27#include <stdio.h>
28#include <stdint.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32
33#ifdef ZLIB
34#include <zlib.h>
35#endif /* ZLIB */
36
74b86e22
MP
37#ifndef nitems
38#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
39#endif
40
f8621c6a
MP
41#define DUMP_OBJECT (1 << 0)
42#define DUMP_FUNCTION (1 << 1)
43#define DUMP_HEADER (1 << 2)
751fc09a 44#define DUMP_LABEL (1 << 3)
80c28199 45#define DUMP_STRTAB (1 << 4)
74b86e22
MP
46#define DUMP_STATISTIC (1 << 5)
47#define DUMP_TYPE (1 << 6)
f8621c6a 48
c1bd258a 49int dump(const char *, uint8_t);
f8621c6a
MP
50int isctf(const char *, size_t);
51__dead void usage(void);
52
c1bd258a 53int ctf_dump(const char *, size_t, uint8_t);
74b86e22
MP
54unsigned int ctf_dump_type(struct ctf_header *, const char *, off_t,
55 unsigned int, unsigned int);
56const char *ctf_kind2name(unsigned short);
e7e4276e 57const char *ctf_enc2name(unsigned short);
f8621c6a
MP
58const char *ctf_off2name(struct ctf_header *, const char *, off_t,
59 unsigned int);
60
c1bd258a 61int elf_dump(const char *, size_t, uint8_t);
c36bfc0c
MP
62const char *elf_idx2sym(size_t *, unsigned char);
63
64/* elf.c */
65int iself(const char *, size_t);
f8621c6a
MP
66int elf_getshstrtab(const char *, size_t, const char **, size_t *);
67int elf_getsymtab(const char *, const char *, size_t,
68 const Elf_Sym **, size_t *);
2ef38d60
MP
69int elf_getsection(const char *, const char *, const char *,
70 size_t, const char **, size_t *);
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
f8621c6a
MP
164const char *strtab;
165const Elf_Sym *symtab;
166size_t strtabsize, nsymb;
167
9fe3b2c8
MP
168const char *
169elf_idx2sym(size_t *idx, unsigned char type)
170{
171 const Elf_Sym *st;
172 size_t i;
173
174 for (i = *idx + 1; i < nsymb; i++) {
175 st = &symtab[i];
176
177 if (ELF_ST_TYPE(st->st_info) != type)
178 continue;
179
180 *idx = i;
181 return strtab + st->st_name;
182 }
183
184 return NULL;
185}
186
f8621c6a 187int
c1bd258a 188elf_dump(const char *p, size_t filesize, uint8_t flags)
f8621c6a
MP
189{
190 Elf_Ehdr *eh = (Elf_Ehdr *)p;
191 Elf_Shdr *sh;
192 const char *shstrtab;
193 size_t i, shstrtabsize;
194
195 /* Find section header string table location and size. */
196 if (elf_getshstrtab(p, filesize, &shstrtab, &shstrtabsize))
197 return 1;
198
199 /* Find symbol table location and number of symbols. */
200 if (elf_getsymtab(p, shstrtab, shstrtabsize, &symtab, &nsymb))
201 warnx("symbol table not found");
202
203 /* Find string table location and size. */
2ef38d60 204 if (elf_getsection(p, ELF_STRTAB, shstrtab, shstrtabsize, &strtab, &strtabsize))
f8621c6a
MP
205 warnx("string table not found");
206
207 /* Find CTF section and dump it. */
208 for (i = 0; i < eh->e_shnum; i++) {
209 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
210
211 if ((sh->sh_link >= eh->e_shnum) ||
212 (sh->sh_name >= shstrtabsize))
213 continue;
214
6d70f1d9 215 if (strncmp(shstrtab + sh->sh_name, ELF_CTF, strlen(ELF_CTF)))
f8621c6a
MP
216 continue;
217
218 if (!isctf(p + sh->sh_offset, sh->sh_size))
219 break;
220
221 return ctf_dump(p + sh->sh_offset, sh->sh_size, flags);
222 }
223
6d70f1d9 224 warnx("%s section not found", ELF_CTF);
f8621c6a
MP
225 return 1;
226}
227
228int
229isctf(const char *p, size_t filesize)
230{
231 struct ctf_header *cth = (struct ctf_header *)p;
232 off_t dlen = cth->cth_stroff + cth->cth_strlen;
233
234 if (cth->cth_magic != CTF_MAGIC || cth->cth_version != CTF_VERSION)
235 return 0;
236
237 if (dlen > filesize && !(cth->cth_flags & CTF_F_COMPRESS)) {
238 warnx("bogus file size");
239 return 0;
240 }
241
242 if ((cth->cth_lbloff & 3) || (cth->cth_objtoff & 1) ||
243 (cth->cth_funcoff & 1) || (cth->cth_typeoff & 3)) {
244 warnx("wrongly aligned offset");
245 return 0;
246 }
247
248 if ((cth->cth_lbloff >= dlen) || (cth->cth_objtoff >= dlen) ||
249 (cth->cth_funcoff >= dlen) || (cth->cth_typeoff >= dlen)) {
250 warnx("truncated file");
251 return 0;
252 }
253
254 if ((cth->cth_lbloff > cth->cth_objtoff) ||
255 (cth->cth_objtoff > cth->cth_funcoff) ||
256 (cth->cth_funcoff > cth->cth_typeoff) ||
257 (cth->cth_typeoff > cth->cth_stroff)) {
258 warnx("corrupted file");
259 return 0;
260 }
261
262 return 1;
263}
264
265int
c1bd258a 266ctf_dump(const char *p, size_t size, uint8_t flags)
f8621c6a
MP
267{
268 struct ctf_header *cth = (struct ctf_header *)p;
f8621c6a 269 off_t dlen = cth->cth_stroff + cth->cth_strlen;
b51bd0d8 270 char *data;
f8621c6a
MP
271
272 if (cth->cth_flags & CTF_F_COMPRESS) {
273 data = decompress(p + sizeof(*cth), size - sizeof(*cth), dlen);
274 if (data == NULL)
275 return 1;
b51bd0d8
MP
276 } else {
277 data = (char *)p + sizeof(*cth);
f8621c6a
MP
278 }
279
280 if (flags & DUMP_HEADER) {
6befd992
MP
281 printf(" cth_magic = 0x%04x\n", cth->cth_magic);
282 printf(" cth_version = %d\n", cth->cth_version);
283 printf(" cth_flags = 0x%02x\n", cth->cth_flags);
284 printf(" cth_parlabel = %s\n",
f8621c6a 285 ctf_off2name(cth, data, dlen, cth->cth_parname));
6befd992 286 printf(" cth_parname = %s\n",
f8621c6a 287 ctf_off2name(cth, data, dlen, cth->cth_parname));
6befd992
MP
288 printf(" cth_lbloff = %d\n", cth->cth_lbloff);
289 printf(" cth_objtoff = %d\n", cth->cth_objtoff);
290 printf(" cth_funcoff = %d\n", cth->cth_funcoff);
291 printf(" cth_typeoff = %d\n", cth->cth_typeoff);
292 printf(" cth_stroff = %d\n", cth->cth_stroff);
293 printf(" cth_strlen = %d\n", cth->cth_strlen);
294 printf("\n");
f8621c6a
MP
295 }
296
751fc09a
MP
297 if (flags & DUMP_LABEL) {
298 unsigned int lbloff = cth->cth_lbloff;
299 struct ctf_lblent *ctl;
300
301 while (lbloff < cth->cth_objtoff) {
302 ctl = (struct ctf_lblent *)(data + lbloff);
303
6befd992 304 printf(" %5u %s\n", ctl->ctl_typeidx,
751fc09a
MP
305 ctf_off2name(cth, data, dlen, ctl->ctl_label));
306
307 lbloff += sizeof(*ctl);
308 }
6befd992 309 printf("\n");
751fc09a
MP
310 }
311
9fe3b2c8
MP
312 if (flags & DUMP_OBJECT) {
313 unsigned int objtoff = cth->cth_objtoff;
314 size_t idx = 0, i = 0;
315 unsigned short *dsp;
316 const char *s;
317 int l;
318
319 while (objtoff < cth->cth_funcoff) {
320 dsp = (unsigned short *)(data + objtoff);
321
6befd992 322 l = printf(" [%zu] %u", i++, *dsp);
9fe3b2c8 323 if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL)
86e1759d 324 printf("%*s %s (%zu)\n", (14 - l), "", s, idx);
9fe3b2c8
MP
325 else
326 printf("\n");
327
328 objtoff += sizeof(*dsp);
329 }
ac25938b 330 printf("\n");
9fe3b2c8
MP
331 }
332
acada86d
MP
333 if (flags & DUMP_FUNCTION) {
334 unsigned short *fsp, kind, vlen;
ae3b8a21 335 size_t idx = 0, i = -1;
acada86d
MP
336 const char *s;
337 int l;
338
339 fsp = (unsigned short *)(data + cth->cth_funcoff);
340 while (fsp < (unsigned short *)(data + cth->cth_typeoff)) {
341 kind = CTF_INFO_KIND(*fsp);
342 vlen = CTF_INFO_VLEN(*fsp);
ae3b8a21 343 s = elf_idx2sym(&idx, STT_FUNC);
acada86d 344 fsp++;
ae3b8a21 345 i++;
acada86d
MP
346
347 if (kind == CTF_K_UNKNOWN && vlen == 0)
348 continue;
349
ae3b8a21
MP
350 l = printf(" [%zu] FUNC ", i);
351 if (s != NULL)
86e1759d 352 printf("(%s)", s);
acada86d
MP
353 printf(" returns: %u args: (", *fsp++);
354 while (vlen-- > 0)
355 printf("%u%s", *fsp++, (vlen > 0) ? ", " : "");
356 printf(")\n");
357 }
6befd992 358 printf("\n");
acada86d
MP
359 }
360
11b30cfc
MP
361 if (flags & DUMP_TYPE) {
362 unsigned int idx = 1, offset = cth->cth_typeoff;
363
364 while (offset < cth->cth_stroff) {
365 offset += ctf_dump_type(cth, data, dlen, offset, idx++);
366 }
367 printf("\n");
368 }
369
80c28199
MP
370 if (flags & DUMP_STRTAB) {
371 unsigned int offset = 0;
372 const char *str;
373
374 while (offset < cth->cth_strlen) {
3093320c 375 str = ctf_off2name(cth, data, dlen, offset);
80c28199 376
6befd992 377 printf(" [%u] ", offset);
3093320c 378 if (strcmp(str, "(anon)"))
80c28199
MP
379 offset += printf("%s\n", str);
380 else {
381 printf("\\0\n");
382 offset++;
383 }
384 }
6befd992 385 printf("\n");
80c28199
MP
386 }
387
f8621c6a
MP
388 if (cth->cth_flags & CTF_F_COMPRESS)
389 free(data);
390
391 return 0;
392}
393
74b86e22
MP
394unsigned int
395ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen,
396 unsigned int offset, unsigned int idx)
397{
5cae2a5e 398 const char *p = data + offset;
23543ec6 399 const struct ctf_type *ctt = (struct ctf_type *)p;
e7e4276e
MP
400 const struct ctf_array *cta;
401 unsigned short *argp, i, kind, vlen, root;
23543ec6 402 unsigned int eob, toff;
74b86e22
MP
403 uint64_t size;
404 const char *name, *kname;
405
74b86e22
MP
406 kind = CTF_INFO_KIND(ctt->ctt_info);
407 vlen = CTF_INFO_VLEN(ctt->ctt_info);
408 root = CTF_INFO_ISROOT(ctt->ctt_info);
409 name = ctf_off2name(cth, data, dlen, ctt->ctt_name);
410
411 if (root)
6befd992 412 printf(" <%u> ", idx);
74b86e22 413 else
6befd992 414 printf(" [%u] ", idx);
74b86e22
MP
415
416 if ((kname = ctf_kind2name(kind)) != NULL)
417 printf("%s %s", kname, name);
418
419 if (ctt->ctt_size <= CTF_MAX_SIZE) {
420 size = ctt->ctt_size;
421 toff = sizeof(struct ctf_stype);
422 } else {
423 size = CTF_TYPE_LSIZE(ctt);
424 toff = sizeof(struct ctf_type);
425 }
426
427 switch (kind) {
428 case CTF_K_UNKNOWN:
429 case CTF_K_FORWARD:
430 break;
431 case CTF_K_INTEGER:
e7e4276e 432 eob = *((unsigned int *)(p + toff));
23543ec6 433 toff += sizeof(unsigned int);
e7e4276e
MP
434 printf(" encoding=%s offset=%u bits=%u",
435 ctf_enc2name(CTF_INT_ENCODING(eob)), CTF_INT_OFFSET(eob),
23543ec6 436 CTF_INT_BITS(eob));
74b86e22
MP
437 break;
438 case CTF_K_FLOAT:
e7e4276e 439 eob = *((unsigned int *)(p + toff));
23543ec6
MP
440 toff += sizeof(unsigned int);
441 printf(" encoding=0x%x offset=%u bits=%u",
442 CTF_FP_ENCODING(eob), CTF_FP_OFFSET(eob), CTF_FP_BITS(eob));
74b86e22
MP
443 break;
444 case CTF_K_ARRAY:
e7e4276e
MP
445 cta = (struct ctf_array *)(p + toff);
446 printf(" content: %u index: %u nelems: %u\n", cta->cta_contents,
447 cta->cta_index, cta->cta_nelems);
23543ec6 448 toff += sizeof(struct ctf_array);
74b86e22
MP
449 break;
450 case CTF_K_FUNCTION:
e7e4276e
MP
451 argp = (unsigned short *)(p + toff);
452 printf(" returns: %u args: (%u", ctt->ctt_type, *argp);
453 for (i = 1; i < vlen; i++) {
454 argp++;
455 printf(", %u", *argp);
456 }
457 printf(")");
23543ec6 458 toff += (vlen + (vlen & 1)) * sizeof(unsigned short);
74b86e22
MP
459 break;
460 case CTF_K_STRUCT:
461 case CTF_K_UNION:
23543ec6
MP
462 printf(" (%llu bytes)\n", size);
463
464 if (size < CTF_LSTRUCT_THRESH) {
465 for (i = 0; i < vlen; i++) {
466 struct ctf_member *ctm;
467
468 ctm = (struct ctf_member *)(p + toff);
469 toff += sizeof(struct ctf_member);
470
471 printf("\t%s type=%u off=%u\n",
472 ctf_off2name(cth, data, dlen,
473 ctm->ctm_name),
474 ctm->ctm_type, ctm->ctm_offset);
475 }
476 } else {
477 for (i = 0; i < vlen; i++) {
478 struct ctf_lmember *ctlm;
479
480 ctlm = (struct ctf_lmember *)(p + toff);
481 toff += sizeof(struct ctf_lmember);
482
483 printf("\t%s type=%u off=%llu\n",
484 ctf_off2name(cth, data, dlen,
485 ctlm->ctlm_name),
486 ctlm->ctlm_type, CTF_LMEM_OFFSET(ctlm));
487 }
488 }
74b86e22
MP
489 break;
490 case CTF_K_ENUM:
23543ec6
MP
491 printf("\n");
492 for (i = 0; i < vlen; i++) {
493 struct ctf_enum *cte;
494
495 cte = (struct ctf_enum *)(p + toff);
496 toff += sizeof(struct ctf_enum);
497
498 printf("\t%s = %u\n",
499 ctf_off2name(cth, data, dlen, cte->cte_name),
500 cte->cte_value);
501 }
74b86e22
MP
502 break;
503 case CTF_K_POINTER:
74b86e22
MP
504 case CTF_K_TYPEDEF:
505 case CTF_K_VOLATILE:
506 case CTF_K_CONST:
507 case CTF_K_RESTRICT:
508 printf(" refers to %u", ctt->ctt_type);
509 break;
510 default:
511 errx(1, "incorrect type %u at offset %u", kind, offset);
512 }
513
514 printf("\n");
515
23543ec6 516 return toff;
74b86e22
MP
517}
518
519const char *
520ctf_kind2name(unsigned short kind)
521{
522 static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER",
e7e4276e 523 "ARRAY", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD",
74b86e22
MP
524 "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" };
525
526 if (kind >= nitems(kind_name))
527 return NULL;
528
529 return kind_name[kind];
530}
531
e7e4276e
MP
532const char *
533ctf_enc2name(unsigned short enc)
534{
535 static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR",
536 "BOOL", "SIGNED BOOL" };
537 static char invalid[7];
538
539 if (enc == CTF_INT_VARARGS)
540 return "VARARGS";
541
542 if (enc > 0 && enc < nitems(enc_name))
543 return enc_name[enc - 1];
544
545 snprintf(invalid, sizeof(invalid), "0x%x", enc);
546 return invalid;
547}
548
f8621c6a
MP
549const char *
550ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen,
551 unsigned int offset)
552{
553 const char *name;
554
555 if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
556 return "external";
557
558 if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
559 return "exceeds strlab";
560
561 if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
562 return "invalid";
563
564 name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
565 if (*name == '\0')
566 return "(anon)";
567
568 return name;
569}
570
571char *
572decompress(const char *buf, size_t size, off_t len)
573{
574#ifdef ZLIB
575 z_stream stream;
576 char *data;
577 int error;
578
579 data = malloc(len);
580 if (data == NULL) {
581 warn(NULL);
582 return NULL;
583 }
584
585 memset(&stream, 0, sizeof(stream));
586 stream.next_in = (void *)buf;
587 stream.avail_in = size;
588 stream.next_out = data;
589 stream.avail_out = len;
590
591 if ((error = inflateInit(&stream)) != Z_OK) {
592 warnx("zlib inflateInit failed: %s", zError(error));
593 goto exit;
594 }
595
596 if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
597 warnx("zlib inflate failed: %s", zError(error));
8aa77ab4 598 inflateEnd(&stream);
f8621c6a
MP
599 goto exit;
600 }
601
602 if ((error = inflateEnd(&stream)) != Z_OK) {
603 warnx("zlib inflateEnd failed: %s", zError(error));
604 goto exit;
605 }
606
607 if (stream.total_out != len) {
608 warnx("decompression failed: %llu != %llu",
609 stream.total_out, len);
610 goto exit;
611 }
612
613 return data;
614
615exit:
616 free(data);
617#endif /* ZLIB */
618 return NULL;
619}
620
621__dead void
622usage(void)
623{
624 extern char *__progname;
625
53477564 626 fprintf(stderr, "usage: %s [-dfhlst] [file ...]\n",
f8621c6a
MP
627 __progname);
628 exit(1);
629}
630