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