Annotation of src/usr.bin/ctfconv/ctfconv.c, Revision 1.18
1.18 ! mpi 1: /* $OpenBSD: ctfconv.c,v 1.17 2018/08/08 20:15:17 mestre Exp $ */
1.2 jasper 2:
1.1 mpi 3: /*
4: * Copyright (c) 2016-2017 Martin Pieuchot
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/queue.h>
23: #include <sys/tree.h>
24: #include <sys/ctf.h>
25:
26: #include <assert.h>
1.13 mpi 27: #include <elf.h>
1.1 mpi 28: #include <err.h>
29: #include <fcntl.h>
1.18 ! mpi 30: #include <limits.h>
1.1 mpi 31: #include <locale.h>
32: #include <stdio.h>
33: #include <stdint.h>
34: #include <stdlib.h>
35: #include <string.h>
36: #include <unistd.h>
37:
38: #include "itype.h"
39: #include "xmalloc.h"
40:
41: #ifndef nitems
42: #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
43: #endif
44:
45: #define DEBUG_ABBREV ".debug_abbrev"
46: #define DEBUG_INFO ".debug_info"
47: #define DEBUG_LINE ".debug_line"
48: #define DEBUG_STR ".debug_str"
49:
50: __dead void usage(void);
51: int convert(const char *);
52: int generate(const char *, const char *, int);
53: int elf_convert(char *, size_t);
54: void elf_sort(void);
1.18 ! mpi 55: char *guess_static_local_name(char *);
1.11 mpi 56: struct itype *find_symb(struct itype *, size_t);
1.1 mpi 57: void dump_type(struct itype *);
58: void dump_func(struct itype *, int *);
59: void dump_obj(struct itype *, int *);
60:
61: /* elf.c */
62: int iself(const char *, size_t);
63: int elf_getshstab(const char *, size_t, const char **, size_t *);
1.12 jsg 64: ssize_t elf_getsymtab(const char *, size_t, const char *, size_t,
1.16 mpi 65: const Elf_Sym **, size_t *, const char **, size_t *);
1.10 jsg 66: ssize_t elf_getsection(char *, size_t, const char *, const char *,
1.1 mpi 67: size_t, const char **, size_t *);
68:
69: /* parse.c */
70: void dwarf_parse(const char *, size_t, const char *, size_t);
71:
72: const char *ctf_enc2name(unsigned short);
73:
74: /* lists of parsed types and functions */
75: struct itype_queue itypeq = TAILQ_HEAD_INITIALIZER(itypeq);
76: struct itype_queue ifuncq = TAILQ_HEAD_INITIALIZER(ifuncq);
77: struct itype_queue iobjq = TAILQ_HEAD_INITIALIZER(iobjq);
78:
79: __dead void
80: usage(void)
81: {
1.7 jasper 82: fprintf(stderr, "usage: %s [-d] -l label -o outfile file\n",
1.1 mpi 83: getprogname());
84: exit(1);
85: }
86:
87: int
88: main(int argc, char *argv[])
89: {
90: const char *filename, *label = NULL, *outfile = NULL;
91: int dump = 0;
92: int ch, error = 0;
93: struct itype *it;
94:
95: setlocale(LC_ALL, "");
96:
97: while ((ch = getopt(argc, argv, "dl:o:")) != -1) {
98: switch (ch) {
99: case 'd':
1.5 jasper 100: dump = 1; /* ctfdump(1)-like SUNW_ctf sections */
1.1 mpi 101: break;
102: case 'l':
103: if (label != NULL)
104: usage();
105: label = optarg;
106: break;
107: case 'o':
108: if (outfile != NULL)
109: usage();
110: outfile = optarg;
111: break;
112: default:
113: usage();
114: }
115: }
116:
117: argc -= optind;
118: argv += optind;
119:
120: if (argc != 1)
121: usage();
122:
1.5 jasper 123: /* Either dump the sections, or write it out. */
124: if ((dump && (outfile != NULL || label != NULL)) ||
125: (!dump && (outfile == NULL || label == NULL)))
1.1 mpi 126: usage();
127:
128: filename = *argv;
1.17 mestre 129:
130: if (unveil(filename, "r") == -1)
131: err(1, "unveil");
132:
133: if (outfile != NULL) {
134: if (unveil(outfile, "wc") == -1)
135: err(1, "unveil");
136: }
137:
138: if (pledge("stdio rpath wpath cpath", NULL) == -1)
139: err(1, "pledge");
140:
1.1 mpi 141: error = convert(filename);
142: if (error != 0)
143: return error;
144:
1.6 jasper 145: if (outfile != NULL) {
146: if (pledge("stdio wpath cpath", NULL) == -1)
147: err(1, "pledge");
148:
149: error = generate(outfile, label, 1);
150: if (error != 0)
151: return error;
152: }
153:
1.1 mpi 154: if (dump) {
1.6 jasper 155: if (pledge("stdio", NULL) == -1)
156: err(1, "pledge");
157:
1.1 mpi 158: int fidx = -1, oidx = -1;
159:
160: TAILQ_FOREACH(it, &iobjq, it_symb)
161: dump_obj(it, &oidx);
162: printf("\n");
163:
164: TAILQ_FOREACH(it, &ifuncq, it_symb)
165: dump_func(it, &fidx);
166: printf("\n");
167:
168: TAILQ_FOREACH(it, &itypeq, it_next) {
169: if (it->it_flags & (ITF_FUNC|ITF_OBJ))
170: continue;
171:
172: dump_type(it);
173: }
1.5 jasper 174:
175: return 0;
1.1 mpi 176: }
177:
178: return 0;
179: }
180:
181: int
182: convert(const char *path)
183: {
184: struct stat st;
185: int fd, error = 1;
186: char *p;
187:
188: fd = open(path, O_RDONLY);
189: if (fd == -1) {
190: warn("open %s", path);
191: return 1;
192: }
193: if (fstat(fd, &st) == -1) {
194: warn("fstat %s", path);
1.9 jsg 195: close(fd);
1.1 mpi 196: return 1;
197: }
198: if ((uintmax_t)st.st_size > SIZE_MAX) {
199: warnx("file too big to fit memory");
1.9 jsg 200: close(fd);
1.1 mpi 201: return 1;
202: }
203:
204: p = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
205: if (p == MAP_FAILED)
206: err(1, "mmap");
207:
208: if (iself(p, st.st_size))
209: error = elf_convert(p, st.st_size);
210:
211: munmap(p, st.st_size);
212: close(fd);
213:
214: return error;
215: }
216:
217: const char *dstrbuf;
218: size_t dstrlen;
219: const char *strtab;
220: const Elf_Sym *symtab;
221: size_t strtabsz, nsymb;
222:
223: int
224: elf_convert(char *p, size_t filesize)
225: {
226: const char *shstab;
227: const char *infobuf, *abbuf;
228: size_t infolen, ablen;
229: size_t shstabsz;
230:
231: /* Find section header string table location and size. */
232: if (elf_getshstab(p, filesize, &shstab, &shstabsz))
233: return 1;
234:
1.16 mpi 235: /* Find symbol table and associated string table. */
236: if (elf_getsymtab(p, filesize, shstab, shstabsz, &symtab, &nsymb,
237: &strtab, &strtabsz) == -1)
1.1 mpi 238: warnx("symbol table not found");
239:
240: /* Find abbreviation location and size. */
1.10 jsg 241: if (elf_getsection(p, filesize, DEBUG_ABBREV, shstab, shstabsz, &abbuf,
1.1 mpi 242: &ablen) == -1) {
243: warnx("%s section not found", DEBUG_ABBREV);
244: return 1;
245: }
246:
1.10 jsg 247: if (elf_getsection(p, filesize, DEBUG_INFO, shstab, shstabsz, &infobuf,
1.1 mpi 248: &infolen) == -1) {
249: warnx("%s section not found", DEBUG_INFO);
250: return 1;
251: }
252:
253: /* Find string table location and size. */
1.10 jsg 254: if (elf_getsection(p, filesize, DEBUG_STR, shstab, shstabsz, &dstrbuf,
1.1 mpi 255: &dstrlen) == -1)
256: warnx("%s section not found", DEBUG_STR);
257:
258: dwarf_parse(infobuf, infolen, abbuf, ablen);
259:
260: /* Sort functions */
261: elf_sort();
262:
263: return 0;
264: }
265:
1.18 ! mpi 266: /*
! 267: * Guess which part of a local symbol name correspond to the variable
! 268: * name.
! 269: *
! 270: * gcc 4.2.1 emits:
! 271: *
! 272: * varname.id
! 273: *
! 274: * clang 8 emits:
! 275: *
! 276: * funcname.varname
! 277: *
! 278: */
! 279: char *
! 280: guess_static_local_name(char *sname)
! 281: {
! 282: const char *errstr;
! 283: char *first, *second;
! 284:
! 285: first = strtok(sname, ".");
! 286: if (first == NULL)
! 287: return NULL;
! 288:
! 289: /* Skip meta symbols - gcc style. */
! 290: if (strncmp(first, "__func__", sizeof("__func__") - 1) == 0 ||
! 291: strncmp(first, "__FUNCTION__", sizeof("__FUNCTION__") - 1) == 0 ||
! 292: strncmp(first, "__warned", sizeof("__warned") - 1) == 0)
! 293: return NULL;
! 294:
! 295: second = strtok(NULL, "\0");
! 296: if (second == NULL)
! 297: return first;
! 298:
! 299: /* Skip meta symbols - clang style. */
! 300: if (strncmp(second, "__warned", sizeof("__warned") - 1) == 0)
! 301: return NULL;
! 302:
! 303: /* If `second' isn't a number, assume clang-style name. */
! 304: if (strtonum(second, 1, INT_MAX, &errstr) == 0)
! 305: return second;
! 306:
! 307: return first;
! 308: }
! 309:
1.11 mpi 310: struct itype *
311: find_symb(struct itype *tmp, size_t stroff)
312: {
313: struct itype *it;
314: char *sname, *p;
315:
316: if (strtab == NULL || stroff >= strtabsz)
317: return NULL;
318:
319: sname = xstrdup(strtab + stroff);
1.18 ! mpi 320: if ((p = guess_static_local_name(sname)) == NULL) {
1.11 mpi 321: free(sname);
322: return NULL;
323: }
324:
325: strlcpy(tmp->it_name, p, ITNAME_MAX);
326: free(sname);
327: it = RB_FIND(isymb_tree, &isymbt, tmp);
328:
329: /* Restore original name */
330: if (it == NULL)
331: strlcpy(tmp->it_name, (strtab + stroff), ITNAME_MAX);
332:
333: return it;
334: }
335:
1.1 mpi 336: void
337: elf_sort(void)
338: {
339: struct itype *it, tmp;
340: size_t i;
341:
342: memset(&tmp, 0, sizeof(tmp));
343: for (i = 0; i < nsymb; i++) {
344: const Elf_Sym *st = &symtab[i];
345:
346: if (st->st_shndx == SHN_UNDEF || st->st_shndx == SHN_COMMON)
347: continue;
348:
349: switch (ELF_ST_TYPE(st->st_info)) {
350: case STT_FUNC:
351: tmp.it_flags = ITF_FUNC;
352: break;
353: case STT_OBJECT:
354: tmp.it_flags = ITF_OBJ;
355: break;
356: default:
357: continue;
358: }
359:
1.11 mpi 360: it = find_symb(&tmp, st->st_name);
1.1 mpi 361: if (it == NULL) {
362: /* Insert 'unknown' entry to match symbol order. */
363: it = it_dup(&tmp);
364: it->it_refp = it;
365: #ifdef DEBUG
366: warnx("symbol not found: %s", it_name(it));
367: #endif
368: }
369:
370: if (it->it_flags & ITF_INSERTED) {
371: #ifdef DEBUG
372: warnx("%s: already inserted", it_name(it));
373: #endif
374: it = it_dup(it);
375: }
376:
377: /* Save symbol index for dump. */
378: it->it_ref = i;
379:
380: it->it_flags |= ITF_INSERTED;
381: if (it->it_flags & ITF_FUNC)
382: TAILQ_INSERT_TAIL(&ifuncq, it, it_symb);
383: else
384: TAILQ_INSERT_TAIL(&iobjq, it, it_symb);
385: }
386: }
387:
388: const char *
389: type_name(struct itype *it)
390: {
391: const char *name;
392:
393: name = it_name(it);
394: if (name == NULL)
395: return "(anon)";
396:
397: return name;
398: }
399:
400: /* Display parsed types a la ctfdump(1) */
401: void
402: dump_type(struct itype *it)
403: {
404: struct imember *im;
405:
406: #ifdef DEBUG
407: switch (it->it_type) {
408: case CTF_K_POINTER:
409: case CTF_K_TYPEDEF:
410: case CTF_K_VOLATILE:
411: case CTF_K_CONST:
412: case CTF_K_RESTRICT:
413: case CTF_K_ARRAY:
414: case CTF_K_FUNCTION:
415: if (it->it_refp == NULL) {
416: printf("unresolved: %s type=%d\n", it_name(it),
417: it->it_type);
418: return;
419: }
420: default:
421: break;
422: }
423: #endif
424:
425: switch (it->it_type) {
426: case CTF_K_FLOAT:
427: case CTF_K_INTEGER:
428: printf(" [%u] %s %s encoding=%s offset=0 bits=%u\n",
429: it->it_idx,
430: (it->it_type == CTF_K_INTEGER) ? "INTEGER" : "FLOAT",
431: it_name(it), ctf_enc2name(it->it_enc), it->it_size);
432: break;
433: case CTF_K_POINTER:
434: printf(" <%u> POINTER %s refers to %u\n", it->it_idx,
435: type_name(it), it->it_refp->it_idx);
436: break;
437: case CTF_K_TYPEDEF:
438: printf(" <%u> TYPEDEF %s refers to %u\n",
439: it->it_idx, it_name(it), it->it_refp->it_idx);
440: break;
441: case CTF_K_VOLATILE:
442: printf(" <%u> VOLATILE %s refers to %u\n", it->it_idx,
443: type_name(it), it->it_refp->it_idx);
444: break;
445: case CTF_K_CONST:
446: printf(" <%u> CONST %s refers to %u\n", it->it_idx,
447: type_name(it), it->it_refp->it_idx);
448: break;
449: case CTF_K_RESTRICT:
450: printf(" <%u> RESTRICT %s refers to %u\n", it->it_idx,
451: it_name(it), it->it_refp->it_idx);
452: break;
453: case CTF_K_ARRAY:
454: printf(" [%u] ARRAY %s content: %u index: %u nelems: %u\n",
455: it->it_idx, type_name(it), it->it_refp->it_idx, long_tidx,
456: it->it_nelems);
457: printf("\n");
458: break;
459: case CTF_K_STRUCT:
460: case CTF_K_UNION:
461: printf(" [%u] %s %s (%u bytes)\n", it->it_idx,
462: (it->it_type == CTF_K_STRUCT) ? "STRUCT" : "UNION",
463: type_name(it), it->it_size);
464: TAILQ_FOREACH(im, &it->it_members, im_next) {
1.14 mpi 465: printf("\t%s type=%u off=%zu\n",
1.3 mpi 466: (im_name(im) == NULL) ? "unknown" : im_name(im),
1.4 mpi 467: im->im_refp ? im->im_refp->it_idx : 0, im->im_off);
1.1 mpi 468: }
469: printf("\n");
470: break;
471: case CTF_K_ENUM:
1.15 mpi 472: printf(" [%u] ENUM %s\n", it->it_idx, type_name(it));
473: TAILQ_FOREACH(im, &it->it_members, im_next) {
474: printf("\t%s = %zu\n", im_name(im), im->im_ref);
475: }
476: printf("\n");
1.1 mpi 477: break;
478: case CTF_K_FUNCTION:
479: printf(" [%u] FUNCTION (%s) returns: %u args: (",
480: it->it_idx, (it_name(it) != NULL) ? it_name(it) : "anon",
481: it->it_refp->it_idx);
482: TAILQ_FOREACH(im, &it->it_members, im_next) {
483: printf("%u%s", im->im_refp->it_idx,
484: TAILQ_NEXT(im, im_next) ? ", " : "");
485: }
486: printf(")\n");
487: break;
488: default:
489: assert(0 == 1);
490: }
491: }
492:
493: void
494: dump_func(struct itype *it, int *idx)
495: {
496: struct imember *im;
497:
498: (*idx)++;
499:
500: if (it->it_type == CTF_K_UNKNOWN && it->it_nelems == 0)
501: return;
502:
503: printf(" [%u] FUNC (%s) returns: %u args: (", (*idx),
504: (it_name(it) != NULL) ? it_name(it) : "unknown",
505: it->it_refp->it_idx);
506: TAILQ_FOREACH(im, &it->it_members, im_next) {
507: printf("%u%s", im->im_refp->it_idx,
508: TAILQ_NEXT(im, im_next) ? ", " : "");
509: }
510: printf(")\n");
511: }
512:
513: void
514: dump_obj(struct itype *it, int *idx)
515: {
516: int l;
517:
518: (*idx)++;
519:
520: l = printf(" [%u] %u", (*idx), it->it_refp->it_idx);
521: printf("%*s %s (%llu)\n", 14 - l, "", it_name(it), it->it_ref);
522: }
523:
524: const char *
525: ctf_enc2name(unsigned short enc)
526: {
527: static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR",
528: "BOOL", "SIGNED BOOL" };
529: static char invalid[7];
530:
531: if (enc == CTF_INT_VARARGS)
532: return "VARARGS";
533:
534: if (enc > 0 && enc < nitems(enc_name))
535: return enc_name[enc - 1];
536:
537: snprintf(invalid, sizeof(invalid), "0x%x", enc);
538: return invalid;
539: }