xref: /openbsd-src/usr.bin/ctfdump/ctfdump.c (revision 4b70baf6e17fc8b27fc1f7fa7929335753fa94c3)
1 /*	$OpenBSD: ctfdump.c,v 1.22 2019/03/16 16:35:03 sunil Exp $ */
2 
3 /*
4  * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
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/ctf.h>
23 
24 #include <elf.h>
25 #include <err.h>
26 #include <fcntl.h>
27 #include <locale.h>
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #ifdef ZLIB
35 #include <zlib.h>
36 #endif /* ZLIB */
37 
38 #ifndef nitems
39 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
40 #endif
41 
42 #define DUMP_OBJECT	(1 << 0)
43 #define DUMP_FUNCTION	(1 << 1)
44 #define DUMP_HEADER	(1 << 2)
45 #define DUMP_LABEL	(1 << 3)
46 #define DUMP_STRTAB	(1 << 4)
47 #define DUMP_STATISTIC	(1 << 5)
48 #define DUMP_TYPE	(1 << 6)
49 
50 int		 dump(const char *, uint8_t);
51 int		 isctf(const char *, size_t);
52 __dead void	 usage(void);
53 
54 int		 ctf_dump(const char *, size_t, uint8_t);
55 void		 ctf_dump_type(struct ctf_header *, const char *, off_t,
56 		     uint32_t, uint32_t *, uint32_t);
57 const char	*ctf_kind2name(uint16_t);
58 const char	*ctf_enc2name(uint16_t);
59 const char	*ctf_fpenc2name(uint16_t);
60 const char	*ctf_off2name(struct ctf_header *, const char *, off_t,
61 		     uint32_t);
62 
63 int		 elf_dump(char *, size_t, uint8_t);
64 const char	*elf_idx2sym(size_t *, uint8_t);
65 
66 /* elf.c */
67 int		 iself(const char *, size_t);
68 int		 elf_getshstab(const char *, size_t, const char **, size_t *);
69 ssize_t		 elf_getsymtab(const char *, size_t filesize, const char *,
70 		     size_t, const Elf_Sym **, size_t *, const char **,
71 		     size_t *);
72 ssize_t		 elf_getsection(char *, size_t, const char *, const char *,
73 		     size_t, const char **, size_t *);
74 
75 char		*decompress(const char *, size_t, off_t);
76 
77 int
78 main(int argc, char *argv[])
79 {
80 	const char *filename;
81 	uint8_t flags = 0;
82 	int ch, error = 0;
83 
84 	setlocale(LC_ALL, "");
85 
86 	if (pledge("stdio rpath", NULL) == -1)
87 		err(1, "pledge");
88 
89 	while ((ch = getopt(argc, argv, "dfhlst")) != -1) {
90 		switch (ch) {
91 		case 'd':
92 			flags |= DUMP_OBJECT;
93 			break;
94 		case 'f':
95 			flags |= DUMP_FUNCTION;
96 			break;
97 		case 'h':
98 			flags |= DUMP_HEADER;
99 			break;
100 		case 'l':
101 			flags |= DUMP_LABEL;
102 			break;
103 		case 's':
104 			flags |= DUMP_STRTAB;
105 			break;
106 		case 't':
107 			flags |= DUMP_TYPE;
108 			break;
109 		default:
110 			usage();
111 		}
112 	}
113 
114 	argc -= optind;
115 	argv += optind;
116 
117 	if (argc <= 0)
118 		usage();
119 
120 	/* Dump everything by default */
121 	if (flags == 0)
122 		flags = 0xff;
123 
124 	while ((filename = *argv++) != NULL)
125 		error |= dump(filename, flags);
126 
127 	return error;
128 }
129 
130 int
131 dump(const char *path, uint8_t flags)
132 {
133 	struct stat		 st;
134 	int			 fd, error = 1;
135 	char			*p;
136 
137 	fd = open(path, O_RDONLY);
138 	if (fd == -1) {
139 		warn("open");
140 		return 1;
141 	}
142 	if (fstat(fd, &st) == -1) {
143 		warn("fstat");
144 		close(fd);
145 		return 1;
146 	}
147 	if ((uintmax_t)st.st_size > SIZE_MAX) {
148 		warnx("file too big to fit memory");
149 		close(fd);
150 		return 1;
151 	}
152 
153 	p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
154 	if (p == MAP_FAILED)
155 		err(1, "mmap");
156 
157 	if (iself(p, st.st_size)) {
158 		error = elf_dump(p, st.st_size, flags);
159 	} else if (isctf(p, st.st_size)) {
160 		error = ctf_dump(p, st.st_size, flags);
161 	}
162 
163 	munmap(p, st.st_size);
164 	close(fd);
165 
166 	return error;
167 }
168 
169 const char		*strtab;
170 const Elf_Sym		*symtab;
171 size_t			 strtabsz, nsymb;
172 
173 const char *
174 elf_idx2sym(size_t *idx, uint8_t type)
175 {
176 	const Elf_Sym	*st;
177 	size_t		 i;
178 
179 	if (strtab == NULL)
180 		return NULL;
181 
182 	for (i = *idx + 1; i < nsymb; i++) {
183 		st = &symtab[i];
184 
185 		if (ELF_ST_TYPE(st->st_info) != type)
186 			continue;
187 
188 		if (st->st_name >= strtabsz)
189 			break;
190 
191 		*idx = i;
192 		return strtab + st->st_name;
193 	}
194 
195 	return NULL;
196 }
197 
198 int
199 elf_dump(char *p, size_t filesize, uint8_t flags)
200 {
201 	Elf_Ehdr		*eh = (Elf_Ehdr *)p;
202 	Elf_Shdr		*sh;
203 	const char		*shstab;
204 	size_t			 i, shstabsz;
205 
206 	/* Find section header string table location and size. */
207 	if (elf_getshstab(p, filesize, &shstab, &shstabsz))
208 		return 1;
209 
210 	/* Find symbol table and associated string table. */
211 	if (elf_getsymtab(p, filesize, shstab, shstabsz, &symtab, &nsymb,
212 	    &strtab, &strtabsz) == -1)
213 		warnx("symbol table not found");
214 
215 	/* Find CTF section and dump it. */
216 	for (i = 0; i < eh->e_shnum; i++) {
217 		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
218 
219 		if ((sh->sh_link >= eh->e_shnum) ||
220 		    (sh->sh_name >= shstabsz))
221 			continue;
222 
223 		if (strncmp(shstab + sh->sh_name, ELF_CTF, strlen(ELF_CTF)))
224 			continue;
225 
226 		if ((sh->sh_offset + sh->sh_size) > filesize)
227 			continue;
228 
229 		if (!isctf(p + sh->sh_offset, sh->sh_size))
230 			break;
231 
232 		return ctf_dump(p + sh->sh_offset, sh->sh_size, flags);
233 	}
234 
235 	warnx("%s section not found", ELF_CTF);
236 	return 1;
237 }
238 
239 int
240 isctf(const char *p, size_t filesize)
241 {
242 	struct ctf_header	*cth = (struct ctf_header *)p;
243 	off_t 			 dlen;
244 
245 	if (filesize < sizeof(struct ctf_header)) {
246 		warnx("file too small to be CTF");
247 		return 0;
248 	}
249 
250 	if (cth->cth_magic != CTF_MAGIC || cth->cth_version != CTF_VERSION)
251 		return 0;
252 
253 	dlen = (off_t)cth->cth_stroff + cth->cth_strlen;
254 	if (dlen > (off_t)filesize && !(cth->cth_flags & CTF_F_COMPRESS)) {
255 		warnx("bogus file size");
256 		return 0;
257 	}
258 
259 	if ((cth->cth_lbloff & 3) || (cth->cth_objtoff & 1) ||
260 	    (cth->cth_funcoff & 1) || (cth->cth_typeoff & 3)) {
261 		warnx("wrongly aligned offset");
262 		return 0;
263 	}
264 
265 	if ((cth->cth_lbloff >= dlen) || (cth->cth_objtoff >= dlen) ||
266 	    (cth->cth_funcoff >= dlen) || (cth->cth_typeoff >= dlen)) {
267 		warnx("truncated file");
268 		return 0;
269 	}
270 
271 	if ((cth->cth_lbloff > cth->cth_objtoff) ||
272 	    (cth->cth_objtoff > cth->cth_funcoff) ||
273 	    (cth->cth_funcoff > cth->cth_typeoff) ||
274 	    (cth->cth_typeoff > cth->cth_stroff)) {
275 		warnx("corrupted file");
276 		return 0;
277 	}
278 
279 	return 1;
280 }
281 
282 int
283 ctf_dump(const char *p, size_t size, uint8_t flags)
284 {
285 	struct ctf_header	*cth = (struct ctf_header *)p;
286 	off_t 			 dlen;
287 	char			*data;
288 
289 	dlen = (off_t)cth->cth_stroff + cth->cth_strlen;
290 	if (cth->cth_flags & CTF_F_COMPRESS) {
291 		data = decompress(p + sizeof(*cth), size - sizeof(*cth), dlen);
292 		if (data == NULL)
293 			return 1;
294 	} else {
295 		data = (char *)p + sizeof(*cth);
296 	}
297 
298 	if (flags & DUMP_HEADER) {
299 		printf("  cth_magic    = 0x%04x\n", cth->cth_magic);
300 		printf("  cth_version  = %u\n", cth->cth_version);
301 		printf("  cth_flags    = 0x%02x\n", cth->cth_flags);
302 		printf("  cth_parlabel = %s\n",
303 		    ctf_off2name(cth, data, dlen, cth->cth_parlabel));
304 		printf("  cth_parname  = %s\n",
305 		    ctf_off2name(cth, data, dlen, cth->cth_parname));
306 		printf("  cth_lbloff   = %u\n", cth->cth_lbloff);
307 		printf("  cth_objtoff  = %u\n", cth->cth_objtoff);
308 		printf("  cth_funcoff  = %u\n", cth->cth_funcoff);
309 		printf("  cth_typeoff  = %u\n", cth->cth_typeoff);
310 		printf("  cth_stroff   = %u\n", cth->cth_stroff);
311 		printf("  cth_strlen   = %u\n", cth->cth_strlen);
312 		printf("\n");
313 	}
314 
315 	if (flags & DUMP_LABEL) {
316 		uint32_t		 lbloff = cth->cth_lbloff;
317 		struct ctf_lblent	*ctl;
318 
319 		while (lbloff < cth->cth_objtoff) {
320 			ctl = (struct ctf_lblent *)(data + lbloff);
321 
322 			printf("  %5u %s\n", ctl->ctl_typeidx,
323 			    ctf_off2name(cth, data, dlen, ctl->ctl_label));
324 
325 			lbloff += sizeof(*ctl);
326 		}
327 		printf("\n");
328 	}
329 
330 	if (flags & DUMP_OBJECT) {
331 		uint32_t		 objtoff = cth->cth_objtoff;
332 		size_t			 idx = 0, i = 0;
333 		uint16_t		*dsp;
334 		const char		*s;
335 		int			 l;
336 
337 		while (objtoff < cth->cth_funcoff) {
338 			dsp = (uint16_t *)(data + objtoff);
339 
340 			l = printf("  [%zu] %u", i++, *dsp);
341 			if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL)
342 				printf("%*s %s (%zu)\n", (14 - l), "", s, idx);
343 			else
344 				printf("\n");
345 
346 			objtoff += sizeof(*dsp);
347 		}
348 		printf("\n");
349 	}
350 
351 	if (flags & DUMP_FUNCTION) {
352 		uint16_t		*fsp, kind, vlen;
353 		uint16_t		*fstart, *fend;
354 		size_t			 idx = 0, i = -1;
355 		const char		*s;
356 		int			 l;
357 
358 		fstart = (uint16_t *)(data + cth->cth_funcoff);
359 		fend = (uint16_t *)(data + cth->cth_typeoff);
360 
361 		fsp = fstart;
362 		while (fsp < fend) {
363 			kind = CTF_INFO_KIND(*fsp);
364 			vlen = CTF_INFO_VLEN(*fsp);
365 			s = elf_idx2sym(&idx, STT_FUNC);
366 			fsp++;
367 			i++;
368 
369 			if (kind == CTF_K_UNKNOWN && vlen == 0)
370 				continue;
371 
372 			l = printf("  [%zu] FUNC ", i);
373 			if (s != NULL)
374 				printf("(%s) ", s);
375 			printf("returns: %u args: (", *fsp++);
376 			while (vlen-- > 0 && fsp < fend)
377 				printf("%u%s", *fsp++, (vlen > 0) ? ", " : "");
378 			printf(")\n");
379 		}
380 		printf("\n");
381 	}
382 
383 	if (flags & DUMP_TYPE) {
384 		uint32_t		 idx = 1, offset = cth->cth_typeoff;
385 		uint32_t		 stroff = cth->cth_stroff;
386 
387 		while (offset < stroff) {
388 			ctf_dump_type(cth, data, dlen, stroff, &offset, idx++);
389 		}
390 		printf("\n");
391 	}
392 
393 	if (flags & DUMP_STRTAB) {
394 		uint32_t		 offset = 0;
395 		const char		*str;
396 
397 		while (offset < cth->cth_strlen) {
398 			str = ctf_off2name(cth, data, dlen, offset);
399 
400 			printf("  [%u] ", offset);
401 			if (strcmp(str, "(anon)"))
402 				offset += printf("%s\n", str);
403 			else {
404 				printf("\\0\n");
405 				offset++;
406 			}
407 		}
408 		printf("\n");
409 	}
410 
411 	if (cth->cth_flags & CTF_F_COMPRESS)
412 		free(data);
413 
414 	return 0;
415 }
416 
417 void
418 ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen,
419     uint32_t stroff, uint32_t *offset, uint32_t idx)
420 {
421 	const char		*p = data + *offset;
422 	const struct ctf_type	*ctt = (struct ctf_type *)p;
423 	const struct ctf_array	*cta;
424 	uint16_t		*argp, i, kind, vlen, root;
425 	uint32_t		 eob, toff;
426 	uint64_t		 size;
427 	const char		*name, *kname;
428 
429 	kind = CTF_INFO_KIND(ctt->ctt_info);
430 	vlen = CTF_INFO_VLEN(ctt->ctt_info);
431 	root = CTF_INFO_ISROOT(ctt->ctt_info);
432 	name = ctf_off2name(cth, data, dlen, ctt->ctt_name);
433 
434 	if (root)
435 		printf("  <%u> ", idx);
436 	else
437 		printf("  [%u] ", idx);
438 
439 	if ((kname = ctf_kind2name(kind)) != NULL)
440 		printf("%s %s", kname, name);
441 
442 	if (ctt->ctt_size <= CTF_MAX_SIZE) {
443 		size = ctt->ctt_size;
444 		toff = sizeof(struct ctf_stype);
445 	} else {
446 		size = CTF_TYPE_LSIZE(ctt);
447 		toff = sizeof(struct ctf_type);
448 	}
449 
450 	switch (kind) {
451 	case CTF_K_UNKNOWN:
452 	case CTF_K_FORWARD:
453 		break;
454 	case CTF_K_INTEGER:
455 		eob = *((uint32_t *)(p + toff));
456 		toff += sizeof(uint32_t);
457 		printf(" encoding=%s offset=%u bits=%u",
458 		    ctf_enc2name(CTF_INT_ENCODING(eob)), CTF_INT_OFFSET(eob),
459 		    CTF_INT_BITS(eob));
460 		break;
461 	case CTF_K_FLOAT:
462 		eob = *((uint32_t *)(p + toff));
463 		toff += sizeof(uint32_t);
464 		printf(" encoding=%s offset=%u bits=%u",
465 		    ctf_fpenc2name(CTF_FP_ENCODING(eob)), CTF_FP_OFFSET(eob),
466 		    CTF_FP_BITS(eob));
467 		break;
468 	case CTF_K_ARRAY:
469 		cta = (struct ctf_array *)(p + toff);
470 		printf(" content: %u index: %u nelems: %u\n", cta->cta_contents,
471 		    cta->cta_index, cta->cta_nelems);
472 		toff += sizeof(struct ctf_array);
473 		break;
474 	case CTF_K_FUNCTION:
475 		argp = (uint16_t *)(p + toff);
476 		printf(" returns: %u args: (%u", ctt->ctt_type, *argp);
477 		for (i = 1; i < vlen; i++) {
478 			argp++;
479 			if ((const char *)argp > data + dlen)
480 				errx(1, "offset exceeds CTF section");
481 
482 			printf(", %u", *argp);
483 		}
484 		printf(")");
485 		toff += (vlen + (vlen & 1)) * sizeof(uint16_t);
486 		break;
487 	case CTF_K_STRUCT:
488 	case CTF_K_UNION:
489 		printf(" (%llu bytes)\n", size);
490 
491 		if (size < CTF_LSTRUCT_THRESH) {
492 			for (i = 0; i < vlen; i++) {
493 				struct ctf_member	*ctm;
494 
495 				if (p + toff > data + dlen)
496 					errx(1, "offset exceeds CTF section");
497 
498 				if (toff > (stroff - sizeof(*ctm)))
499 					break;
500 
501 				ctm = (struct ctf_member *)(p + toff);
502 				toff += sizeof(struct ctf_member);
503 
504 				printf("\t%s type=%u off=%u\n",
505 				    ctf_off2name(cth, data, dlen,
506 					ctm->ctm_name),
507 				    ctm->ctm_type, ctm->ctm_offset);
508 			}
509 		} else {
510 			for (i = 0; i < vlen; i++) {
511 				struct ctf_lmember	*ctlm;
512 
513 				if (p + toff > data + dlen)
514 					errx(1, "offset exceeds CTF section");
515 
516 				if (toff > (stroff - sizeof(*ctlm)))
517 					break;
518 
519 				ctlm = (struct ctf_lmember *)(p + toff);
520 				toff += sizeof(struct ctf_lmember);
521 
522 				printf("\t%s type=%u off=%llu\n",
523 				    ctf_off2name(cth, data, dlen,
524 					ctlm->ctlm_name),
525 				    ctlm->ctlm_type, CTF_LMEM_OFFSET(ctlm));
526 			}
527 		}
528 		break;
529 	case CTF_K_ENUM:
530 		printf("\n");
531 		for (i = 0; i < vlen; i++) {
532 			struct ctf_enum	*cte;
533 
534 			if (p + toff > data + dlen)
535 				errx(1, "offset exceeds CTF section");
536 
537 			if (toff > (stroff - sizeof(*cte)))
538 				break;
539 
540 			cte = (struct ctf_enum *)(p + toff);
541 			toff += sizeof(struct ctf_enum);
542 
543 			printf("\t%s = %d\n",
544 			    ctf_off2name(cth, data, dlen, cte->cte_name),
545 			    cte->cte_value);
546 		}
547 		break;
548 	case CTF_K_POINTER:
549 	case CTF_K_TYPEDEF:
550 	case CTF_K_VOLATILE:
551 	case CTF_K_CONST:
552 	case CTF_K_RESTRICT:
553 		printf(" refers to %u", ctt->ctt_type);
554 		break;
555 	default:
556 		errx(1, "incorrect type %u at offset %u", kind, *offset);
557 	}
558 
559 	printf("\n");
560 
561 	*offset += toff;
562 }
563 
564 const char *
565 ctf_kind2name(uint16_t kind)
566 {
567 	static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER",
568 	   "ARRAY", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD",
569 	   "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" };
570 
571 	if (kind >= nitems(kind_name))
572 		return NULL;
573 
574 	return kind_name[kind];
575 }
576 
577 const char *
578 ctf_enc2name(uint16_t enc)
579 {
580 	static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR",
581 	    "BOOL", "SIGNED BOOL" };
582 	static char invalid[7];
583 
584 	if (enc == CTF_INT_VARARGS)
585 		return "VARARGS";
586 
587 	if (enc > 0 && enc <= nitems(enc_name))
588 		return enc_name[enc - 1];
589 
590 	snprintf(invalid, sizeof(invalid), "0x%x", enc);
591 	return invalid;
592 }
593 
594 const char *
595 ctf_fpenc2name(uint16_t enc)
596 {
597 	static const char *enc_name[] = { "SINGLE", "DOUBLE", NULL, NULL,
598 	    NULL, "LDOUBLE" };
599 	static char invalid[7];
600 
601 	if (enc > 0 && enc <= nitems(enc_name) && enc_name[enc - 1] != NULL)
602 		return enc_name[enc - 1];
603 
604 	snprintf(invalid, sizeof(invalid), "0x%x", enc);
605 	return invalid;
606 }
607 
608 const char *
609 ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen,
610     uint32_t offset)
611 {
612 	const char		*name;
613 
614 	if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
615 		return "external";
616 
617 	if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
618 		return "exceeds strlab";
619 
620 	if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
621 		return "invalid";
622 
623 	name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
624 	if (*name == '\0')
625 		return "(anon)";
626 
627 	return name;
628 }
629 
630 char *
631 decompress(const char *buf, size_t size, off_t len)
632 {
633 #ifdef ZLIB
634 	z_stream		 stream;
635 	char			*data;
636 	int			 error;
637 
638 	data = malloc(len);
639 	if (data == NULL) {
640 		warn(NULL);
641 		return NULL;
642 	}
643 
644 	memset(&stream, 0, sizeof(stream));
645 	stream.next_in = (void *)buf;
646 	stream.avail_in = size;
647 	stream.next_out = (uint8_t *)data;
648 	stream.avail_out = len;
649 
650 	if ((error = inflateInit(&stream)) != Z_OK) {
651 		warnx("zlib inflateInit failed: %s", zError(error));
652 		goto exit;
653 	}
654 
655 	if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
656 		warnx("zlib inflate failed: %s", zError(error));
657 		inflateEnd(&stream);
658 		goto exit;
659 	}
660 
661 	if ((error = inflateEnd(&stream)) != Z_OK) {
662 		warnx("zlib inflateEnd failed: %s", zError(error));
663 		goto exit;
664 	}
665 
666 	if (stream.total_out != len) {
667 		warnx("decompression failed: %llu != %llu",
668 		    stream.total_out, len);
669 		goto exit;
670 	}
671 
672 	return data;
673 
674 exit:
675 	free(data);
676 #endif /* ZLIB */
677 	return NULL;
678 }
679 
680 __dead void
681 usage(void)
682 {
683 	fprintf(stderr, "usage: %s [-dfhlst] file ...\n",
684 	    getprogname());
685 	exit(1);
686 }
687