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