GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ctfdump/ctfdump.c Lines: 0 306 0.0 %
Date: 2017-11-13 Branches: 0 204 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: ctfdump.c,v 1.19 2017/11/06 14:59:27 mpi 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 flock cpath wpath", 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
			printf(", %u", *argp);
480
		}
481
		printf(")");
482
		toff += (vlen + (vlen & 1)) * sizeof(uint16_t);
483
		break;
484
	case CTF_K_STRUCT:
485
	case CTF_K_UNION:
486
		printf(" (%llu bytes)\n", size);
487
488
		if (size < CTF_LSTRUCT_THRESH) {
489
			for (i = 0; i < vlen; i++) {
490
				struct ctf_member	*ctm;
491
492
				if (toff > (stroff - sizeof(*ctm)))
493
					break;
494
495
				ctm = (struct ctf_member *)(p + toff);
496
				toff += sizeof(struct ctf_member);
497
498
				printf("\t%s type=%u off=%u\n",
499
				    ctf_off2name(cth, data, dlen,
500
					ctm->ctm_name),
501
				    ctm->ctm_type, ctm->ctm_offset);
502
			}
503
		} else {
504
			for (i = 0; i < vlen; i++) {
505
				struct ctf_lmember	*ctlm;
506
507
				if (toff > (stroff - sizeof(*ctlm)))
508
					break;
509
510
				ctlm = (struct ctf_lmember *)(p + toff);
511
				toff += sizeof(struct ctf_lmember);
512
513
				printf("\t%s type=%u off=%llu\n",
514
				    ctf_off2name(cth, data, dlen,
515
					ctlm->ctlm_name),
516
				    ctlm->ctlm_type, CTF_LMEM_OFFSET(ctlm));
517
			}
518
		}
519
		break;
520
	case CTF_K_ENUM:
521
		printf("\n");
522
		for (i = 0; i < vlen; i++) {
523
			struct ctf_enum	*cte;
524
525
			if (toff > (stroff - sizeof(*cte)))
526
				break;
527
528
			cte = (struct ctf_enum *)(p + toff);
529
			toff += sizeof(struct ctf_enum);
530
531
			printf("\t%s = %d\n",
532
			    ctf_off2name(cth, data, dlen, cte->cte_name),
533
			    cte->cte_value);
534
		}
535
		break;
536
	case CTF_K_POINTER:
537
	case CTF_K_TYPEDEF:
538
	case CTF_K_VOLATILE:
539
	case CTF_K_CONST:
540
	case CTF_K_RESTRICT:
541
		printf(" refers to %u", ctt->ctt_type);
542
		break;
543
	default:
544
		errx(1, "incorrect type %u at offset %u", kind, *offset);
545
	}
546
547
	printf("\n");
548
549
	*offset += toff;
550
}
551
552
const char *
553
ctf_kind2name(uint16_t kind)
554
{
555
	static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER",
556
	   "ARRAY", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD",
557
	   "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" };
558
559
	if (kind >= nitems(kind_name))
560
		return NULL;
561
562
	return kind_name[kind];
563
}
564
565
const char *
566
ctf_enc2name(uint16_t enc)
567
{
568
	static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR",
569
	    "BOOL", "SIGNED BOOL" };
570
	static char invalid[7];
571
572
	if (enc == CTF_INT_VARARGS)
573
		return "VARARGS";
574
575
	if (enc > 0 && enc <= nitems(enc_name))
576
		return enc_name[enc - 1];
577
578
	snprintf(invalid, sizeof(invalid), "0x%x", enc);
579
	return invalid;
580
}
581
582
const char *
583
ctf_fpenc2name(uint16_t enc)
584
{
585
	static const char *enc_name[] = { "SINGLE", "DOUBLE", NULL, NULL,
586
	    NULL, "LDOUBLE" };
587
	static char invalid[7];
588
589
	if (enc > 0 && enc <= nitems(enc_name) && enc_name[enc - 1] != NULL)
590
		return enc_name[enc - 1];
591
592
	snprintf(invalid, sizeof(invalid), "0x%x", enc);
593
	return invalid;
594
}
595
596
const char *
597
ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen,
598
    uint32_t offset)
599
{
600
	const char		*name;
601
602
	if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
603
		return "external";
604
605
	if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
606
		return "exceeds strlab";
607
608
	if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
609
		return "invalid";
610
611
	name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
612
	if (*name == '\0')
613
		return "(anon)";
614
615
	return name;
616
}
617
618
char *
619
decompress(const char *buf, size_t size, off_t len)
620
{
621
#ifdef ZLIB
622
	z_stream		 stream;
623
	char			*data;
624
	int			 error;
625
626
	data = malloc(len);
627
	if (data == NULL) {
628
		warn(NULL);
629
		return NULL;
630
	}
631
632
	memset(&stream, 0, sizeof(stream));
633
	stream.next_in = (void *)buf;
634
	stream.avail_in = size;
635
	stream.next_out = (uint8_t *)data;
636
	stream.avail_out = len;
637
638
	if ((error = inflateInit(&stream)) != Z_OK) {
639
		warnx("zlib inflateInit failed: %s", zError(error));
640
		goto exit;
641
	}
642
643
	if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
644
		warnx("zlib inflate failed: %s", zError(error));
645
		inflateEnd(&stream);
646
		goto exit;
647
	}
648
649
	if ((error = inflateEnd(&stream)) != Z_OK) {
650
		warnx("zlib inflateEnd failed: %s", zError(error));
651
		goto exit;
652
	}
653
654
	if (stream.total_out != len) {
655
		warnx("decompression failed: %llu != %llu",
656
		    stream.total_out, len);
657
		goto exit;
658
	}
659
660
	return data;
661
662
exit:
663
	free(data);
664
#endif /* ZLIB */
665
	return NULL;
666
}
667
668
__dead void
669
usage(void)
670
{
671
	fprintf(stderr, "usage: %s [-dfhlst] file ...\n",
672
	    getprogname());
673
	exit(1);
674
}