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