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