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