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