GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ctfconv/ctfconv.c Lines: 88 203 43.3 %
Date: 2017-11-07 Branches: 45 126 35.7 %

Line Branch Exec Source
1
/*	$OpenBSD: ctfconv.c,v 1.12 2017/09/29 16:05:53 jsg 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/exec_elf.h>
22
#include <sys/mman.h>
23
#include <sys/queue.h>
24
#include <sys/tree.h>
25
#include <sys/ctf.h>
26
27
#include <assert.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 *);
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
4
	setlocale(LC_ALL, "");
94
95
2
	if (pledge("stdio rpath wpath cpath flock", NULL) == -1)
96
		err(1, "pledge");
97
98
6
	while ((ch = getopt(argc, argv, "dl:o:")) != -1) {
99

4
		switch (ch) {
100
		case 'd':
101
			dump = 1;	/* ctfdump(1)-like SUNW_ctf sections */
102
			break;
103
		case 'l':
104
2
			if (label != NULL)
105
				usage();
106
2
			label = optarg;
107
2
			break;
108
		case 'o':
109
2
			if (outfile != NULL)
110
				usage();
111
2
			outfile = optarg;
112
2
			break;
113
		default:
114
			usage();
115
		}
116
	}
117
118
2
	argc -= optind;
119
2
	argv += optind;
120
121
2
	if (argc != 1)
122
		usage();
123
124
	/* Either dump the sections, or write it out. */
125

2
	if ((dump && (outfile != NULL || label != NULL)) ||
126

4
	    (!dump && (outfile == NULL || label == NULL)))
127
		usage();
128
129
2
	filename = *argv;
130
2
	error = convert(filename);
131
2
	if (error != 0)
132
		return error;
133
134
2
	if (outfile != NULL) {
135
2
		if (pledge("stdio wpath cpath flock rpath", NULL) == -1)
136
			err(1, "pledge");
137
138
2
		error = generate(outfile, label, 1);
139
2
		if (error != 0)
140
			return error;
141
	}
142
143
2
	if (dump) {
144
		if (pledge("stdio flock rpath cpath wpath", 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
2
	return 0;
168
2
}
169
170
int
171
convert(const char *path)
172
{
173
4
	struct stat		 st;
174
	int			 fd, error = 1;
175
	char			*p;
176
177
2
	fd = open(path, O_RDONLY);
178
2
	if (fd == -1) {
179
		warn("open %s", path);
180
		return 1;
181
	}
182
2
	if (fstat(fd, &st) == -1) {
183
		warn("fstat %s", path);
184
		close(fd);
185
		return 1;
186
	}
187
2
	if ((uintmax_t)st.st_size > SIZE_MAX) {
188
		warnx("file too big to fit memory");
189
		close(fd);
190
		return 1;
191
	}
192
193
2
	p = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
194
2
	if (p == MAP_FAILED)
195
		err(1, "mmap");
196
197
2
	if (iself(p, st.st_size))
198
2
		error = elf_convert(p, st.st_size);
199
200
2
	munmap(p, st.st_size);
201
2
	close(fd);
202
203
2
	return error;
204
2
}
205
206
const char		*dstrbuf;
207
size_t			 dstrlen;
208
const char		*strtab;
209
const Elf_Sym		*symtab;
210
size_t			 strtabsz, nsymb;
211
212
int
213
elf_convert(char *p, size_t filesize)
214
{
215
4
	const char		*shstab;
216
2
	const char		*infobuf, *abbuf;
217
2
	size_t			 infolen, ablen;
218
2
	size_t			 shstabsz;
219
220
	/* Find section header string table location and size. */
221
2
	if (elf_getshstab(p, filesize, &shstab, &shstabsz))
222
		return 1;
223
224
	/* Find symbol table location and number of symbols. */
225
2
	if (elf_getsymtab(p, filesize, shstab, shstabsz, &symtab, &nsymb) == -1)
226
		warnx("symbol table not found");
227
228
	/* Find string table location and size. */
229
4
	if (elf_getsection(p, filesize, ELF_STRTAB, shstab, shstabsz, &strtab,
230
2
	    &strtabsz) == -1)
231
		warnx("string table not found");
232
233
	/* Find abbreviation location and size. */
234
4
	if (elf_getsection(p, filesize, DEBUG_ABBREV, shstab, shstabsz, &abbuf,
235
2
	    &ablen) == -1) {
236
		warnx("%s section not found", DEBUG_ABBREV);
237
		return 1;
238
	}
239
240
4
	if (elf_getsection(p, filesize, DEBUG_INFO, shstab, shstabsz, &infobuf,
241
2
	    &infolen) == -1) {
242
		warnx("%s section not found", DEBUG_INFO);
243
		return 1;
244
	}
245
246
	/* Find string table location and size. */
247
4
	if (elf_getsection(p, filesize, DEBUG_STR, shstab, shstabsz, &dstrbuf,
248
2
	    &dstrlen) == -1)
249
		warnx("%s section not found", DEBUG_STR);
250
251
2
	dwarf_parse(infobuf, infolen, abbuf, ablen);
252
253
	/* Sort functions */
254
2
	elf_sort();
255
256
2
	return 0;
257
2
}
258
259
struct itype *
260
find_symb(struct itype *tmp, size_t stroff)
261
{
262
	struct itype		*it;
263
	char 			*sname, *p;
264
265

84
	if (strtab == NULL || stroff >= strtabsz)
266
		return NULL;
267
268
	/*
269
	 * Skip local suffix
270
	 *
271
	 * FIXME: only skip local copies.
272
	 */
273
28
	sname = xstrdup(strtab + stroff);
274
28
	if ((p = strtok(sname, ".")) == NULL) {
275
		free(sname);
276
		return NULL;
277
	}
278
279
28
	strlcpy(tmp->it_name, p, ITNAME_MAX);
280
28
	free(sname);
281
28
	it = RB_FIND(isymb_tree, &isymbt, tmp);
282
283
	/* Restore original name */
284
28
	if (it == NULL)
285
14
		strlcpy(tmp->it_name, (strtab + stroff), ITNAME_MAX);
286
287
28
	return it;
288
28
}
289
290
void
291
elf_sort(void)
292
{
293
4
	struct itype		*it, tmp;
294
	size_t			 i;
295
296
2
	memset(&tmp, 0, sizeof(tmp));
297
232
	for (i = 0; i < nsymb; i++) {
298
114
		const Elf_Sym	*st = &symtab[i];
299
300

218
		if (st->st_shndx == SHN_UNDEF || st->st_shndx == SHN_COMMON)
301
10
			continue;
302
303
112
		switch (ELF_ST_TYPE(st->st_info)) {
304
		case STT_FUNC:
305
			tmp.it_flags = ITF_FUNC;
306
8
			break;
307
		case STT_OBJECT:
308
			tmp.it_flags = ITF_OBJ;
309
			break;
310
		default:
311
76
			continue;
312
		}
313
314
28
		it = find_symb(&tmp, st->st_name);
315
28
		if (it == NULL) {
316
			/* Insert 'unknown' entry to match symbol order. */
317
14
			it = it_dup(&tmp);
318
14
			it->it_refp = it;
319
#ifdef DEBUG
320
			warnx("symbol not found: %s", it_name(it));
321
#endif
322
14
		}
323
324
28
		if (it->it_flags & ITF_INSERTED) {
325
#ifdef DEBUG
326
			warnx("%s: already inserted", it_name(it));
327
#endif
328
			it = it_dup(it);
329
		}
330
331
		/* Save symbol index for dump. */
332
28
		it->it_ref = i;
333
334
28
		it->it_flags |= ITF_INSERTED;
335
28
		if (it->it_flags & ITF_FUNC)
336
8
			TAILQ_INSERT_TAIL(&ifuncq, it, it_symb);
337
		else
338
20
			TAILQ_INSERT_TAIL(&iobjq, it, it_symb);
339
28
	}
340
2
}
341
342
const char *
343
type_name(struct itype *it)
344
{
345
	const char *name;
346
347
	name = it_name(it);
348
	if (name == NULL)
349
		return "(anon)";
350
351
	return name;
352
}
353
354
/* Display parsed types a la ctfdump(1) */
355
void
356
dump_type(struct itype *it)
357
{
358
	struct imember *im;
359
360
#ifdef DEBUG
361
	switch (it->it_type) {
362
	case CTF_K_POINTER:
363
	case CTF_K_TYPEDEF:
364
	case CTF_K_VOLATILE:
365
	case CTF_K_CONST:
366
	case CTF_K_RESTRICT:
367
	case CTF_K_ARRAY:
368
	case CTF_K_FUNCTION:
369
		if (it->it_refp == NULL) {
370
			printf("unresolved: %s type=%d\n", it_name(it),
371
			    it->it_type);
372
			return;
373
		}
374
	default:
375
		break;
376
	}
377
#endif
378
379
	switch (it->it_type) {
380
	case CTF_K_FLOAT:
381
	case CTF_K_INTEGER:
382
		printf("  [%u] %s %s encoding=%s offset=0 bits=%u\n",
383
		    it->it_idx,
384
		    (it->it_type == CTF_K_INTEGER) ? "INTEGER" : "FLOAT",
385
		    it_name(it), ctf_enc2name(it->it_enc), it->it_size);
386
		break;
387
	case CTF_K_POINTER:
388
		printf("  <%u> POINTER %s refers to %u\n", it->it_idx,
389
		    type_name(it), it->it_refp->it_idx);
390
		break;
391
	case CTF_K_TYPEDEF:
392
		printf("  <%u> TYPEDEF %s refers to %u\n",
393
		    it->it_idx, it_name(it), it->it_refp->it_idx);
394
		break;
395
	case CTF_K_VOLATILE:
396
		printf("  <%u> VOLATILE %s refers to %u\n", it->it_idx,
397
		    type_name(it), it->it_refp->it_idx);
398
		break;
399
	case CTF_K_CONST:
400
		printf("  <%u> CONST %s refers to %u\n", it->it_idx,
401
		    type_name(it), it->it_refp->it_idx);
402
		break;
403
	case CTF_K_RESTRICT:
404
		printf("  <%u> RESTRICT %s refers to %u\n", it->it_idx,
405
		    it_name(it), it->it_refp->it_idx);
406
		break;
407
	case CTF_K_ARRAY:
408
		printf("  [%u] ARRAY %s content: %u index: %u nelems: %u\n",
409
		    it->it_idx, type_name(it), it->it_refp->it_idx, long_tidx,
410
		    it->it_nelems);
411
		printf("\n");
412
		break;
413
	case CTF_K_STRUCT:
414
	case CTF_K_UNION:
415
		printf("  [%u] %s %s (%u bytes)\n", it->it_idx,
416
		    (it->it_type == CTF_K_STRUCT) ? "STRUCT" : "UNION",
417
		    type_name(it), it->it_size);
418
		TAILQ_FOREACH(im, &it->it_members, im_next) {
419
			printf("\t%s type=%u off=%zd\n",
420
			    (im_name(im) == NULL) ? "unknown" : im_name(im),
421
			    im->im_refp ? im->im_refp->it_idx : 0, im->im_off);
422
		}
423
		printf("\n");
424
		break;
425
	case CTF_K_ENUM:
426
		printf("  [%u] ENUM %s\n\n", it->it_idx, type_name(it));
427
		break;
428
	case CTF_K_FUNCTION:
429
		printf("  [%u] FUNCTION (%s) returns: %u args: (",
430
		    it->it_idx, (it_name(it) != NULL) ? it_name(it) : "anon",
431
		    it->it_refp->it_idx);
432
		TAILQ_FOREACH(im, &it->it_members, im_next) {
433
			printf("%u%s", im->im_refp->it_idx,
434
			    TAILQ_NEXT(im, im_next) ? ", " : "");
435
		}
436
		printf(")\n");
437
		break;
438
	default:
439
		assert(0 == 1);
440
	}
441
}
442
443
void
444
dump_func(struct itype *it, int *idx)
445
{
446
	struct imember *im;
447
448
	(*idx)++;
449
450
	if (it->it_type == CTF_K_UNKNOWN && it->it_nelems == 0)
451
		return;
452
453
	printf("  [%u] FUNC (%s) returns: %u args: (", (*idx),
454
	    (it_name(it) != NULL) ? it_name(it) : "unknown",
455
	    it->it_refp->it_idx);
456
	TAILQ_FOREACH(im, &it->it_members, im_next) {
457
		printf("%u%s", im->im_refp->it_idx,
458
		    TAILQ_NEXT(im, im_next) ? ", " : "");
459
	}
460
	printf(")\n");
461
}
462
463
void
464
dump_obj(struct itype *it, int *idx)
465
{
466
	int l;
467
468
	(*idx)++;
469
470
	l = printf("  [%u] %u", (*idx), it->it_refp->it_idx);
471
	printf("%*s %s (%llu)\n", 14 - l, "", it_name(it), it->it_ref);
472
}
473
474
const char *
475
ctf_enc2name(unsigned short enc)
476
{
477
	static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR",
478
	    "BOOL", "SIGNED BOOL" };
479
	static char invalid[7];
480
481
	if (enc == CTF_INT_VARARGS)
482
		return "VARARGS";
483
484
	if (enc > 0 && enc < nitems(enc_name))
485
		return enc_name[enc - 1];
486
487
	snprintf(invalid, sizeof(invalid), "0x%x", enc);
488
	return invalid;
489
}