xref: /openbsd-src/usr.bin/ctfconv/ctfconv.c (revision c7e8ea31cd41a963f06f0a8ba93948b06aa6b4a4)
1 /*	$OpenBSD: ctfconv.c,v 1.7 2017/08/12 19:00:08 jasper 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/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 {
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 	if (pledge("stdio rpath wpath cpath", NULL) == -1)
96 		err(1, "pledge");
97 
98 	while ((ch = getopt(argc, argv, "dl:o:")) != -1) {
99 		switch (ch) {
100 		case 'd':
101 			dump = 1;	/* ctfdump(1)-like SUNW_ctf sections */
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 
124 	/* Either dump the sections, or write it out. */
125 	if ((dump && (outfile != NULL || label != NULL)) ||
126 	    (!dump && (outfile == NULL || label == NULL)))
127 		usage();
128 
129 	filename = *argv;
130 	error = convert(filename);
131 	if (error != 0)
132 		return error;
133 
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 
143 	if (dump) {
144 		if (pledge("stdio", NULL) == -1)
145 			err(1, "pledge");
146 
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 		}
163 
164 		return 0;
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",
398 			    (im_name(im) == NULL) ? "unknown" : im_name(im),
399 			    im->im_refp ? im->im_refp->it_idx : 0, im->im_off);
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 }
468