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