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