GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ctfdump/ctfdump.c Lines: 203 312 65.1 %
Date: 2017-11-07 Branches: 92 186 49.5 %

Line Branch Exec Source
1
/*	$OpenBSD: ctfdump.c,v 1.8 2017/09/23 12:25:39 uwe 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/exec_elf.h>
22
#include <sys/mman.h>
23
#include <sys/ctf.h>
24
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
uint32_t	 ctf_dump_type(struct ctf_header *, const char *, off_t,
56
		     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 *, const char *, size_t,
70
		     const Elf_Sym **, size_t *);
71
ssize_t		 elf_getsection(char *, const char *, const char *,
72
		     size_t, const char **, size_t *);
73
74
char		*decompress(const char *, size_t, off_t);
75
76
int
77
main(int argc, char *argv[])
78
{
79
	const char *filename;
80
	uint8_t flags = 0;
81
	int ch, error = 0;
82
83
4
	setlocale(LC_ALL, "");
84
85
2
	if (pledge("stdio rpath flock cpath wpath", NULL) == -1)
86
		err(1, "pledge");
87
88
2
	while ((ch = getopt(argc, argv, "dfhlst")) != -1) {
89
		switch (ch) {
90
		case 'd':
91
			flags |= DUMP_OBJECT;
92
			break;
93
		case 'f':
94
			flags |= DUMP_FUNCTION;
95
			break;
96
		case 'h':
97
			flags |= DUMP_HEADER;
98
			break;
99
		case 'l':
100
			flags |= DUMP_LABEL;
101
			break;
102
		case 's':
103
			flags |= DUMP_STRTAB;
104
			break;
105
		case 't':
106
			flags |= DUMP_TYPE;
107
			break;
108
		default:
109
			usage();
110
		}
111
	}
112
113
2
	argc -= optind;
114
2
	argv += optind;
115
116
2
	if (argc <= 0)
117
		usage();
118
119
	/* Dump everything by default */
120
2
	if (flags == 0)
121
2
		flags = 0xff;
122
123
6
	while ((filename = *argv++) != NULL)
124
2
		error |= dump(filename, flags);
125
126
	return error;
127
2
}
128
129
int
130
dump(const char *path, uint8_t flags)
131
{
132
4
	struct stat		 st;
133
	int			 fd, error = 1;
134
	char			*p;
135
136
2
	fd = open(path, O_RDONLY);
137
2
	if (fd == -1) {
138
		warn("open");
139
		return 1;
140
	}
141
2
	if (fstat(fd, &st) == -1) {
142
		warn("fstat");
143
		close(fd);
144
		return 1;
145
	}
146
2
	if ((uintmax_t)st.st_size > SIZE_MAX) {
147
		warnx("file too big to fit memory");
148
		close(fd);
149
		return 1;
150
	}
151
152
2
	p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
153
2
	if (p == MAP_FAILED)
154
		err(1, "mmap");
155
156
2
	if (iself(p, st.st_size)) {
157
2
		error = elf_dump(p, st.st_size, flags);
158
2
	} else if (isctf(p, st.st_size)) {
159
		error = ctf_dump(p, st.st_size, flags);
160
	}
161
162
2
	munmap(p, st.st_size);
163
2
	close(fd);
164
165
2
	return error;
166
2
}
167
168
const char		*strtab;
169
const Elf_Sym		*symtab;
170
size_t			 strtabsz, nsymb;
171
172
const char *
173
elf_idx2sym(size_t *idx, uint8_t type)
174
{
175
	const Elf_Sym	*st;
176
	size_t		 i;
177
178
426
	for (i = *idx + 1; i < nsymb; i++) {
179
198
		st = &symtab[i];
180
181
198
		if (ELF_ST_TYPE(st->st_info) != type)
182
			continue;
183
184
30
		*idx = i;
185
30
		return strtab + st->st_name;
186
	}
187
188
	return NULL;
189
30
}
190
191
int
192
elf_dump(char *p, size_t filesize, uint8_t flags)
193
{
194
4
	Elf_Ehdr		*eh = (Elf_Ehdr *)p;
195
	Elf_Shdr		*sh;
196
2
	const char		*shstab;
197
2
	size_t			 i, shstabsz;
198
199
	/* Find section header string table location and size. */
200
2
	if (elf_getshstab(p, filesize, &shstab, &shstabsz))
201
		return 1;
202
203
	/* Find symbol table location and number of symbols. */
204
2
	if (elf_getsymtab(p, shstab, shstabsz, &symtab, &nsymb) == -1)
205
		warnx("symbol table not found");
206
207
	/* Find string table location and size. */
208
4
	if (elf_getsection(p, ELF_STRTAB, shstab, shstabsz, &strtab,
209
2
	    &strtabsz) == -1)
210
		warnx("string table not found");
211
212
	/* Find CTF section and dump it. */
213
120
	for (i = 0; i < eh->e_shnum; i++) {
214
60
		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
215
216

120
		if ((sh->sh_link >= eh->e_shnum) ||
217
60
		    (sh->sh_name >= shstabsz))
218
			continue;
219
220
60
		if (strncmp(shstab + sh->sh_name, ELF_CTF, strlen(ELF_CTF)))
221
			continue;
222
223
2
		if (!isctf(p + sh->sh_offset, sh->sh_size))
224
			break;
225
226
2
		return ctf_dump(p + sh->sh_offset, sh->sh_size, flags);
227
	}
228
229
	warnx("%s section not found", ELF_CTF);
230
	return 1;
231
2
}
232
233
int
234
isctf(const char *p, size_t filesize)
235
{
236
4
	struct ctf_header	*cth = (struct ctf_header *)p;
237
	off_t 			 dlen;
238
239
2
	if (filesize < sizeof(struct ctf_header)) {
240
		warnx("file too small to be CTF");
241
		return 0;
242
	}
243
244

4
	if (cth->cth_magic != CTF_MAGIC || cth->cth_version != CTF_VERSION)
245
		return 0;
246
247
2
	dlen = cth->cth_stroff + cth->cth_strlen;
248

4
	if (dlen > (off_t)filesize && !(cth->cth_flags & CTF_F_COMPRESS)) {
249
		warnx("bogus file size");
250
		return 0;
251
	}
252
253

6
	if ((cth->cth_lbloff & 3) || (cth->cth_objtoff & 1) ||
254
4
	    (cth->cth_funcoff & 1) || (cth->cth_typeoff & 3)) {
255
		warnx("wrongly aligned offset");
256
		return 0;
257
	}
258
259

6
	if ((cth->cth_lbloff >= dlen) || (cth->cth_objtoff >= dlen) ||
260
4
	    (cth->cth_funcoff >= dlen) || (cth->cth_typeoff >= dlen)) {
261
		warnx("truncated file");
262
		return 0;
263
	}
264
265

4
	if ((cth->cth_lbloff > cth->cth_objtoff) ||
266
2
	    (cth->cth_objtoff > cth->cth_funcoff) ||
267
2
	    (cth->cth_funcoff > cth->cth_typeoff) ||
268
2
	    (cth->cth_typeoff > cth->cth_stroff)) {
269
		warnx("corrupted file");
270
		return 0;
271
	}
272
273
2
	return 1;
274
2
}
275
276
int
277
ctf_dump(const char *p, size_t size, uint8_t flags)
278
{
279
4
	struct ctf_header	*cth = (struct ctf_header *)p;
280
2
	off_t 			 dlen = cth->cth_stroff + cth->cth_strlen;
281
	char			*data;
282
283
2
	if (cth->cth_flags & CTF_F_COMPRESS) {
284
2
		data = decompress(p + sizeof(*cth), size - sizeof(*cth), dlen);
285
2
		if (data == NULL)
286
			return 1;
287
	} else {
288
		data = (char *)p + sizeof(*cth);
289
	}
290
291
2
	if (flags & DUMP_HEADER) {
292
2
		printf("  cth_magic    = 0x%04x\n", cth->cth_magic);
293
2
		printf("  cth_version  = %d\n", cth->cth_version);
294
2
		printf("  cth_flags    = 0x%02x\n", cth->cth_flags);
295
2
		printf("  cth_parlabel = %s\n",
296
2
		    ctf_off2name(cth, data, dlen, cth->cth_parname));
297
2
		printf("  cth_parname  = %s\n",
298
2
		    ctf_off2name(cth, data, dlen, cth->cth_parname));
299
2
		printf("  cth_lbloff   = %d\n", cth->cth_lbloff);
300
2
		printf("  cth_objtoff  = %d\n", cth->cth_objtoff);
301
2
		printf("  cth_funcoff  = %d\n", cth->cth_funcoff);
302
2
		printf("  cth_typeoff  = %d\n", cth->cth_typeoff);
303
2
		printf("  cth_stroff   = %d\n", cth->cth_stroff);
304
2
		printf("  cth_strlen   = %d\n", cth->cth_strlen);
305
2
		printf("\n");
306
2
	}
307
308
2
	if (flags & DUMP_LABEL) {
309
2
		uint32_t		 lbloff = cth->cth_lbloff;
310
		struct ctf_lblent	*ctl;
311
312
8
		while (lbloff < cth->cth_objtoff) {
313
2
			ctl = (struct ctf_lblent *)(data + lbloff);
314
315
4
			printf("  %5u %s\n", ctl->ctl_typeidx,
316
2
			    ctf_off2name(cth, data, dlen, ctl->ctl_label));
317
318
2
			lbloff += sizeof(*ctl);
319
		}
320
2
		printf("\n");
321
2
	}
322
323
2
	if (flags & DUMP_OBJECT) {
324
2
		uint32_t		 objtoff = cth->cth_objtoff;
325
2
		size_t			 idx = 0, i = 0;
326
		uint16_t		*dsp;
327
		const char		*s;
328
		int			 l;
329
330
44
		while (objtoff < cth->cth_funcoff) {
331
20
			dsp = (uint16_t *)(data + objtoff);
332
333
20
			l = printf("  [%zu] %u", i++, *dsp);
334
20
			if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL)
335
20
				printf("%*s %s (%zu)\n", (14 - l), "", s, idx);
336
			else
337
				printf("\n");
338
339
20
			objtoff += sizeof(*dsp);
340
		}
341
2
		printf("\n");
342
2
	}
343
344
2
	if (flags & DUMP_FUNCTION) {
345
		uint16_t		*fsp, kind, vlen;
346
2
		size_t			 idx = 0, i = -1;
347
		const char		*s;
348
		int			 l;
349
350
2
		fsp = (uint16_t *)(data + cth->cth_funcoff);
351
16
		while (fsp < (uint16_t *)(data + cth->cth_typeoff)) {
352
10
			kind = CTF_INFO_KIND(*fsp);
353
10
			vlen = CTF_INFO_VLEN(*fsp);
354
10
			s = elf_idx2sym(&idx, STT_FUNC);
355
10
			fsp++;
356
10
			i++;
357
358

18
			if (kind == CTF_K_UNKNOWN && vlen == 0)
359
				continue;
360
361
2
			l = printf("  [%zu] FUNC ", i);
362
2
			if (s != NULL)
363
2
				printf("(%s)", s);
364
2
			printf(" returns: %u args: (", *fsp++);
365
12
			while (vlen-- > 0)
366
4
				printf("%u%s", *fsp++, (vlen > 0) ? ", " : "");
367
2
			printf(")\n");
368
		}
369
2
		printf("\n");
370
2
	}
371
372
2
	if (flags & DUMP_TYPE) {
373
2
		uint32_t		 idx = 1, offset = cth->cth_typeoff;
374
375
40
		while (offset < cth->cth_stroff) {
376
18
			offset += ctf_dump_type(cth, data, dlen, offset, idx++);
377
		}
378
2
		printf("\n");
379
2
	}
380
381
2
	if (flags & DUMP_STRTAB) {
382
		uint32_t		 offset = 0;
383
		const char		*str;
384
385
16
		while (offset < cth->cth_strlen) {
386
12
			str = ctf_off2name(cth, data, dlen, offset);
387
388
12
			printf("  [%u] ", offset);
389
12
			if (strcmp(str, "(anon)"))
390
10
				offset += printf("%s\n", str);
391
			else {
392
2
				printf("\\0\n");
393
2
				offset++;
394
			}
395
		}
396
2
		printf("\n");
397
2
	}
398
399
2
	if (cth->cth_flags & CTF_F_COMPRESS)
400
2
		free(data);
401
402
2
	return 0;
403
2
}
404
405
uint32_t
406
ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen,
407
    uint32_t offset, uint32_t idx)
408
{
409
36
	const char		*p = data + offset;
410
18
	const struct ctf_type	*ctt = (struct ctf_type *)p;
411
	const struct ctf_array	*cta;
412
	uint16_t		*argp, i, kind, vlen, root;
413
	uint32_t		 eob, toff;
414
	uint64_t		 size;
415
	const char		*name, *kname;
416
417
18
	kind = CTF_INFO_KIND(ctt->ctt_info);
418
18
	vlen = CTF_INFO_VLEN(ctt->ctt_info);
419
18
	root = CTF_INFO_ISROOT(ctt->ctt_info);
420
18
	name = ctf_off2name(cth, data, dlen, ctt->ctt_name);
421
422
18
	if (root)
423
		printf("  <%u> ", idx);
424
	else
425
18
		printf("  [%u] ", idx);
426
427
18
	if ((kname = ctf_kind2name(kind)) != NULL)
428
18
		printf("%s %s", kname, name);
429
430
18
	if (ctt->ctt_size <= CTF_MAX_SIZE) {
431
18
		size = ctt->ctt_size;
432
		toff = sizeof(struct ctf_stype);
433
18
	} else {
434
		size = CTF_TYPE_LSIZE(ctt);
435
		toff = sizeof(struct ctf_type);
436
	}
437
438



36
	switch (kind) {
439
	case CTF_K_UNKNOWN:
440
	case CTF_K_FORWARD:
441
		break;
442
	case CTF_K_INTEGER:
443
8
		eob = *((uint32_t *)(p + toff));
444
8
		toff += sizeof(uint32_t);
445
8
		printf(" encoding=%s offset=%u bits=%u",
446
8
		    ctf_enc2name(CTF_INT_ENCODING(eob)), CTF_INT_OFFSET(eob),
447
8
		    CTF_INT_BITS(eob));
448
8
		break;
449
	case CTF_K_FLOAT:
450
6
		eob = *((uint32_t *)(p + toff));
451
6
		toff += sizeof(uint32_t);
452
6
		printf(" encoding=%s offset=%u bits=%u",
453
6
		    ctf_fpenc2name(CTF_FP_ENCODING(eob)), CTF_FP_OFFSET(eob),
454
6
		    CTF_FP_BITS(eob));
455
6
		break;
456
	case CTF_K_ARRAY:
457
		cta = (struct ctf_array *)(p + toff);
458
		printf(" content: %u index: %u nelems: %u\n", cta->cta_contents,
459
		    cta->cta_index, cta->cta_nelems);
460
		toff += sizeof(struct ctf_array);
461
		break;
462
	case CTF_K_FUNCTION:
463
		argp = (uint16_t *)(p + toff);
464
		printf(" returns: %u args: (%u", ctt->ctt_type, *argp);
465
		for (i = 1; i < vlen; i++) {
466
			argp++;
467
			printf(", %u", *argp);
468
		}
469
		printf(")");
470
		toff += (vlen + (vlen & 1)) * sizeof(uint16_t);
471
		break;
472
	case CTF_K_STRUCT:
473
	case CTF_K_UNION:
474
		printf(" (%llu bytes)\n", size);
475
476
		if (size < CTF_LSTRUCT_THRESH) {
477
			for (i = 0; i < vlen; i++) {
478
				struct ctf_member	*ctm;
479
480
				ctm = (struct ctf_member *)(p + toff);
481
				toff += sizeof(struct ctf_member);
482
483
				printf("\t%s type=%u off=%u\n",
484
				    ctf_off2name(cth, data, dlen,
485
					ctm->ctm_name),
486
				    ctm->ctm_type, ctm->ctm_offset);
487
			}
488
		} else {
489
			for (i = 0; i < vlen; i++) {
490
				struct ctf_lmember	*ctlm;
491
492
				ctlm = (struct ctf_lmember *)(p + toff);
493
				toff += sizeof(struct ctf_lmember);
494
495
				printf("\t%s type=%u off=%llu\n",
496
				    ctf_off2name(cth, data, dlen,
497
					ctlm->ctlm_name),
498
				    ctlm->ctlm_type, CTF_LMEM_OFFSET(ctlm));
499
			}
500
		}
501
		break;
502
	case CTF_K_ENUM:
503
		printf("\n");
504
		for (i = 0; i < vlen; i++) {
505
			struct ctf_enum	*cte;
506
507
			cte = (struct ctf_enum *)(p + toff);
508
			toff += sizeof(struct ctf_enum);
509
510
			printf("\t%s = %d\n",
511
			    ctf_off2name(cth, data, dlen, cte->cte_name),
512
			    cte->cte_value);
513
		}
514
		break;
515
	case CTF_K_POINTER:
516
	case CTF_K_TYPEDEF:
517
	case CTF_K_VOLATILE:
518
	case CTF_K_CONST:
519
	case CTF_K_RESTRICT:
520
4
		printf(" refers to %u", ctt->ctt_type);
521
4
		break;
522
	default:
523
		errx(1, "incorrect type %u at offset %u", kind, offset);
524
	}
525
526
18
	printf("\n");
527
528
18
	return toff;
529
}
530
531
const char *
532
ctf_kind2name(uint16_t kind)
533
{
534
	static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER",
535
	   "ARRAY", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD",
536
	   "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" };
537
538
36
	if (kind >= nitems(kind_name))
539
		return NULL;
540
541
18
	return kind_name[kind];
542
18
}
543
544
const char *
545
ctf_enc2name(uint16_t enc)
546
{
547
	static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR",
548
	    "BOOL", "SIGNED BOOL" };
549
	static char invalid[7];
550
551
16
	if (enc == CTF_INT_VARARGS)
552
		return "VARARGS";
553
554

16
	if (enc > 0 && enc <= nitems(enc_name))
555
8
		return enc_name[enc - 1];
556
557
	snprintf(invalid, sizeof(invalid), "0x%x", enc);
558
	return invalid;
559
8
}
560
561
const char *
562
ctf_fpenc2name(uint16_t enc)
563
{
564
	static const char *enc_name[] = { "SINGLE", "DOUBLE", NULL, NULL,
565
	    NULL, "LDOUBLE" };
566
	static char invalid[7];
567
568

24
	if (enc > 0 && enc <= nitems(enc_name) && enc_name[enc - 1] != NULL)
569
6
		return enc_name[enc - 1];
570
571
	snprintf(invalid, sizeof(invalid), "0x%x", enc);
572
	return invalid;
573
6
}
574
575
const char *
576
ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen,
577
    uint32_t offset)
578
{
579
	const char		*name;
580
581
72
	if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
582
		return "external";
583
584
36
	if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
585
		return "exceeds strlab";
586
587
36
	if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
588
		return "invalid";
589
590
36
	name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
591
36
	if (*name == '\0')
592
10
		return "(anon)";
593
594
26
	return name;
595
36
}
596
597
char *
598
decompress(const char *buf, size_t size, off_t len)
599
{
600
#ifdef ZLIB
601
4
	z_stream		 stream;
602
	char			*data;
603
	int			 error;
604
605
2
	data = malloc(len);
606
2
	if (data == NULL) {
607
		warn(NULL);
608
		return NULL;
609
	}
610
611
2
	memset(&stream, 0, sizeof(stream));
612
2
	stream.next_in = (void *)buf;
613
2
	stream.avail_in = size;
614
2
	stream.next_out = (uint8_t *)data;
615
2
	stream.avail_out = len;
616
617
2
	if ((error = inflateInit(&stream)) != Z_OK) {
618
		warnx("zlib inflateInit failed: %s", zError(error));
619
		goto exit;
620
	}
621
622
2
	if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
623
		warnx("zlib inflate failed: %s", zError(error));
624
		inflateEnd(&stream);
625
		goto exit;
626
	}
627
628
2
	if ((error = inflateEnd(&stream)) != Z_OK) {
629
		warnx("zlib inflateEnd failed: %s", zError(error));
630
		goto exit;
631
	}
632
633
2
	if (stream.total_out != len) {
634
		warnx("decompression failed: %llu != %llu",
635
		    stream.total_out, len);
636
		goto exit;
637
	}
638
639
2
	return data;
640
641
exit:
642
	free(data);
643
#endif /* ZLIB */
644
	return NULL;
645
2
}
646
647
__dead void
648
usage(void)
649
{
650
	fprintf(stderr, "usage: %s [-dfhlst] file ...\n",
651
	    getprogname());
652
	exit(1);
653
}