xref: /openbsd/usr.bin/ctfconv/dw.c (revision f6aab3d8)
1 /*	$OpenBSD: dw.c,v 1.5 2021/10/25 19:54:29 kn Exp $ */
2 
3 /*
4  * Copyright (c) 2016 Martin Pieuchot
5  * Copyright (c) 2014 Matthew Dempsky <matthew@dempsky.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/queue.h>
21 
22 #include <errno.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "dw.h"
28 #include "dwarf.h"
29 #include "pool.h"
30 
31 #ifndef NOPOOL
32 struct pool dcu_pool, die_pool, dav_pool, dab_pool, dat_pool;
33 #endif /* NOPOOL */
34 
35 #ifndef nitems
36 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
37 #endif
38 
39 static int	 dw_read_u8(struct dwbuf *, uint8_t *);
40 static int	 dw_read_u16(struct dwbuf *, uint16_t *);
41 static int	 dw_read_u32(struct dwbuf *, uint32_t *);
42 static int	 dw_read_u64(struct dwbuf *, uint64_t *);
43 
44 static int	 dw_read_sleb128(struct dwbuf *, int64_t *);
45 static int	 dw_read_uleb128(struct dwbuf *, uint64_t *);
46 
47 static int	 dw_read_bytes(struct dwbuf *, void *, size_t);
48 static int	 dw_read_string(struct dwbuf *, const char **);
49 static int	 dw_read_buf(struct dwbuf *, struct dwbuf *, size_t);
50 
51 static int	 dw_skip_bytes(struct dwbuf *, size_t);
52 
53 static int	 dw_attr_parse(struct dwbuf *, struct dwattr *, uint8_t,
54 		     struct dwaval_queue *);
55 static void	 dw_attr_purge(struct dwaval_queue *);
56 static int	 dw_die_parse(struct dwbuf *, size_t, uint8_t,
57 		     struct dwabbrev_queue *, struct dwdie_queue *);
58 static void	 dw_die_purge(struct dwdie_queue *);
59 
60 static int
61 dw_read_bytes(struct dwbuf *d, void *v, size_t n)
62 {
63 	if (d->len < n)
64 		return -1;
65 	memcpy(v, d->buf, n);
66 	d->buf += n;
67 	d->len -= n;
68 	return 0;
69 }
70 
71 static int
72 dw_read_u8(struct dwbuf *d, uint8_t *v)
73 {
74 	return dw_read_bytes(d, v, sizeof(*v));
75 }
76 
77 static int
78 dw_read_u16(struct dwbuf *d, uint16_t *v)
79 {
80 	return dw_read_bytes(d, v, sizeof(*v));
81 }
82 
83 static int
84 dw_read_u32(struct dwbuf *d, uint32_t *v)
85 {
86 	return dw_read_bytes(d, v, sizeof(*v));
87 }
88 
89 static int
90 dw_read_u64(struct dwbuf *d, uint64_t *v)
91 {
92 	return dw_read_bytes(d, v, sizeof(*v));
93 }
94 
95 /* Read a DWARF LEB128 (little-endian base-128) value. */
96 static inline int
97 dw_read_leb128(struct dwbuf *d, uint64_t *v, int signextend)
98 {
99 	unsigned int shift = 0;
100 	uint64_t res = 0;
101 	uint8_t x;
102 
103 	while (shift < 64 && !dw_read_u8(d, &x)) {
104 		res |= (uint64_t)(x & 0x7f) << shift;
105 		shift += 7;
106 		if ((x & 0x80) == 0) {
107 			if (signextend && shift < 64 && (x & 0x40) != 0)
108 				res |= ~(uint64_t)0 << shift;
109 			*v = res;
110 			return 0;
111 		}
112 	}
113 	return -1;
114 }
115 
116 static int
117 dw_read_sleb128(struct dwbuf *d, int64_t *v)
118 {
119 	return dw_read_leb128(d, (uint64_t *)v, 1);
120 }
121 
122 static int
123 dw_read_uleb128(struct dwbuf *d, uint64_t *v)
124 {
125 	return dw_read_leb128(d, v, 0);
126 }
127 
128 /* Read a NUL terminated string. */
129 static int
130 dw_read_string(struct dwbuf *d, const char **s)
131 {
132 	const char *end = memchr(d->buf, '\0', d->len);
133 	size_t n;
134 
135 	if (end == NULL)
136 		return -1;
137 
138 	n = end - d->buf + 1;
139 	*s = d->buf;
140 	d->buf += n;
141 	d->len -= n;
142 	return 0;
143 }
144 
145 static int
146 dw_read_buf(struct dwbuf *d, struct dwbuf *v, size_t n)
147 {
148 	if (d->len < n)
149 		return -1;
150 	v->buf = d->buf;
151 	v->len = n;
152 	d->buf += n;
153 	d->len -= n;
154 	return 0;
155 }
156 
157 static int
158 dw_skip_bytes(struct dwbuf *d, size_t n)
159 {
160 	if (d->len < n)
161 		return -1;
162 	d->buf += n;
163 	d->len -= n;
164 	return 0;
165 }
166 
167 const char *
168 dw_tag2name(uint64_t tag)
169 {
170 	static const char *dw_tags[] = { DW_TAG_NAMES };
171 
172 	if (tag <= nitems(dw_tags))
173 		return dw_tags[tag - 1];
174 
175 	if (tag == DW_TAG_lo_user)
176 		return "DW_TAG_lo_user";
177 	if (tag == DW_TAG_hi_user)
178 		return "DW_TAG_hi_user";
179 
180 	return NULL;
181 }
182 
183 const char *
184 dw_at2name(uint64_t at)
185 {
186 	static const char *dw_attrs[] = { DW_AT_NAMES };
187 
188 	if (at <= nitems(dw_attrs))
189 		return dw_attrs[at - 1];
190 
191 	if (at == DW_AT_lo_user)
192 		return "DW_AT_lo_user";
193 	if (at == DW_AT_hi_user)
194 		return "DW_AT_hi_user";
195 
196 	return NULL;
197 }
198 
199 const char *
200 dw_form2name(uint64_t form)
201 {
202 	static const char *dw_forms[] = { DW_FORM_NAMES };
203 
204 	if (form <= nitems(dw_forms))
205 		return dw_forms[form - 1];
206 
207 	if (form == DW_FORM_GNU_ref_alt)
208 		return "DW_FORM_GNU_ref_alt";
209 	if (form == DW_FORM_GNU_strp_alt)
210 		return "DW_FORM_GNU_strp_alt";
211 
212 	return NULL;
213 }
214 
215 const char *
216 dw_op2name(uint8_t op)
217 {
218 	static const char *dw_ops[] = { DW_OP_NAMES };
219 
220 	if (op <= nitems(dw_ops))
221 		return dw_ops[op - 1];
222 
223 	if (op == DW_OP_lo_user)
224 		return "DW_OP_lo_user";
225 	if (op == DW_OP_hi_user)
226 		return "DW_OP_hi_user";
227 
228 	return NULL;
229 }
230 
231 static int
232 dw_attr_parse(struct dwbuf *dwbuf, struct dwattr *dat, uint8_t psz,
233     struct dwaval_queue *davq)
234 {
235 	struct dwaval	*dav;
236 	uint64_t	 form = dat->dat_form;
237 	int		 error = 0, i = 0;
238 
239 	while (form == DW_FORM_indirect) {
240 		/* XXX loop prevention not strict enough? */
241 		if (dw_read_uleb128(dwbuf, &form) || (++i > 3))
242 			return ELOOP;
243 	}
244 
245 	dav = pzalloc(&dav_pool, sizeof(*dav));
246 	if (dav == NULL)
247 		return ENOMEM;
248 
249 	dav->dav_dat = dat;
250 
251 	switch (form) {
252 	case DW_FORM_addr:
253 	case DW_FORM_ref_addr:
254 		if (psz == sizeof(uint32_t))
255 			error = dw_read_u32(dwbuf, &dav->dav_u32);
256 		else
257 			error = dw_read_u64(dwbuf, &dav->dav_u64);
258 		break;
259 	case DW_FORM_block1:
260 		error = dw_read_u8(dwbuf, &dav->dav_u8);
261 		if (error == 0)
262 			error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u8);
263 		break;
264 	case DW_FORM_block2:
265 		error = dw_read_u16(dwbuf, &dav->dav_u16);
266 		if (error == 0)
267 			error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u16);
268 		break;
269 	case DW_FORM_block4:
270 		error = dw_read_u32(dwbuf, &dav->dav_u32);
271 		if (error == 0)
272 			error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u32);
273 		break;
274 	case DW_FORM_block:
275 		error = dw_read_uleb128(dwbuf, &dav->dav_u64);
276 		if (error == 0)
277 			error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u64);
278 		break;
279 	case DW_FORM_data1:
280 	case DW_FORM_flag:
281 	case DW_FORM_ref1:
282 		error = dw_read_u8(dwbuf, &dav->dav_u8);
283 		break;
284 	case DW_FORM_data2:
285 	case DW_FORM_ref2:
286 		error = dw_read_u16(dwbuf, &dav->dav_u16);
287 		break;
288 	case DW_FORM_data4:
289 	case DW_FORM_ref4:
290 		error = dw_read_u32(dwbuf, &dav->dav_u32);
291 		break;
292 	case DW_FORM_data8:
293 	case DW_FORM_ref8:
294 		error = dw_read_u64(dwbuf, &dav->dav_u64);
295 		break;
296 	case DW_FORM_ref_udata:
297 	case DW_FORM_udata:
298 		error = dw_read_uleb128(dwbuf, &dav->dav_u64);
299 		break;
300 	case DW_FORM_sdata:
301 		error = dw_read_sleb128(dwbuf, &dav->dav_s64);
302 		break;
303 	case DW_FORM_string:
304 		error = dw_read_string(dwbuf, &dav->dav_str);
305 		break;
306 	case DW_FORM_strp:
307 		error = dw_read_u32(dwbuf, &dav->dav_u32);
308 		break;
309 	case DW_FORM_flag_present:
310 		dav->dav_u8 = 1;
311 		break;
312 	default:
313 		error = ENOENT;
314 		break;
315 	}
316 
317 	if (error) {
318 		pfree(&dav_pool, dav);
319 		return error;
320 	}
321 
322 	SIMPLEQ_INSERT_TAIL(davq, dav, dav_next);
323 	return 0;
324 }
325 
326 static void
327 dw_attr_purge(struct dwaval_queue *davq)
328 {
329 	struct dwaval	*dav;
330 
331 	while ((dav = SIMPLEQ_FIRST(davq)) != NULL) {
332 		SIMPLEQ_REMOVE_HEAD(davq, dav_next);
333 		pfree(&dav_pool, dav);
334 	}
335 
336 	SIMPLEQ_INIT(davq);
337 }
338 
339 static int
340 dw_die_parse(struct dwbuf *dwbuf, size_t nextoff, uint8_t psz,
341     struct dwabbrev_queue *dabq, struct dwdie_queue *dieq)
342 {
343 	struct dwdie	*die;
344 	struct dwabbrev	*dab;
345 	struct dwattr	*dat;
346 	uint64_t	 code;
347 	size_t		 doff;
348 	uint8_t		 lvl = 0;
349 	int		 error;
350 
351 
352 	while (dwbuf->len > 0) {
353 		doff = nextoff - dwbuf->len;
354 		if (dw_read_uleb128(dwbuf, &code))
355 			return -1;
356 
357 		if (code == 0) {
358 			lvl--;
359 			continue;
360 		}
361 
362 		SIMPLEQ_FOREACH(dab, dabq, dab_next) {
363 			if (dab->dab_code == code)
364 				break;
365 		}
366 		if (dab == NULL)
367 			return ESRCH;
368 
369 		die = pmalloc(&die_pool, sizeof(*die));
370 		if (die == NULL)
371 			return ENOMEM;
372 
373 		die->die_lvl = lvl;
374 		die->die_dab = dab;
375 		die->die_offset = doff;
376 		SIMPLEQ_INIT(&die->die_avals);
377 
378 		SIMPLEQ_FOREACH(dat, &dab->dab_attrs, dat_next) {
379 			error = dw_attr_parse(dwbuf, dat, psz, &die->die_avals);
380 			if (error != 0) {
381 				dw_attr_purge(&die->die_avals);
382 				return error;
383 			}
384 		}
385 
386 		if (dab->dab_children == DW_CHILDREN_yes)
387 			lvl++;
388 
389 		SIMPLEQ_INSERT_TAIL(dieq, die, die_next);
390 	}
391 
392 	return 0;
393 }
394 
395 static void
396 dw_die_purge(struct dwdie_queue *dieq)
397 {
398 	struct dwdie	*die;
399 
400 	while ((die = SIMPLEQ_FIRST(dieq)) != NULL) {
401 		SIMPLEQ_REMOVE_HEAD(dieq, die_next);
402 		dw_attr_purge(&die->die_avals);
403 		pfree(&die_pool, die);
404 	}
405 
406 	SIMPLEQ_INIT(dieq);
407 }
408 
409 int
410 dw_ab_parse(struct dwbuf *abseg, struct dwabbrev_queue *dabq)
411 {
412 	struct dwabbrev	*dab;
413 	uint64_t	 code, tag;
414 	uint8_t		 children;
415 
416 	if (abseg->len == 0)
417 		return EINVAL;
418 
419 	for (;;) {
420 		if (dw_read_uleb128(abseg, &code) || (code == 0))
421 			break;
422 
423 		if (dw_read_uleb128(abseg, &tag) ||
424 		    dw_read_u8(abseg, &children))
425 			return -1;
426 
427 		dab = pmalloc(&dab_pool, sizeof(*dab));
428 		if (dab == NULL)
429 			return ENOMEM;
430 
431 		dab->dab_code = code;
432 		dab->dab_tag = tag;
433 		dab->dab_children = children;
434 		SIMPLEQ_INIT(&dab->dab_attrs);
435 
436 		SIMPLEQ_INSERT_TAIL(dabq, dab, dab_next);
437 
438 		for (;;) {
439 			struct dwattr *dat;
440 			uint64_t attr = 0, form = 0;
441 
442 			if (dw_read_uleb128(abseg, &attr) ||
443 			    dw_read_uleb128(abseg, &form))
444 				return -1;
445 
446 			if ((attr == 0) && (form == 0))
447 				break;
448 
449 			dat = pmalloc(&dat_pool, sizeof(*dat));
450 			if (dat == NULL)
451 				return ENOMEM;
452 
453 			dat->dat_attr = attr;
454 			dat->dat_form = form;
455 
456 			SIMPLEQ_INSERT_TAIL(&dab->dab_attrs, dat, dat_next);
457 		}
458 	}
459 
460 	return 0;
461 }
462 
463 void
464 dw_dabq_purge(struct dwabbrev_queue *dabq)
465 {
466 	struct dwabbrev	*dab;
467 
468 	while ((dab = SIMPLEQ_FIRST(dabq)) != NULL) {
469 		struct dwattr *dat;
470 
471 		SIMPLEQ_REMOVE_HEAD(dabq, dab_next);
472 		while ((dat = SIMPLEQ_FIRST(&dab->dab_attrs)) != NULL) {
473 			SIMPLEQ_REMOVE_HEAD(&dab->dab_attrs, dat_next);
474 			pfree(&dat_pool, dat);
475 		}
476 
477 		pfree(&dab_pool, dab);
478 	}
479 
480 	SIMPLEQ_INIT(dabq);
481 }
482 
483 int
484 dw_cu_parse(struct dwbuf *info, struct dwbuf *abbrev, size_t seglen,
485     struct dwcu **dcup)
486 {
487 	struct dwbuf	 abseg = *abbrev;
488 	struct dwbuf	 dwbuf;
489 	size_t		 segoff, nextoff, addrsize;
490 	struct dwcu	*dcu = NULL;
491 	uint32_t	 length = 0, abbroff = 0;
492 	uint16_t	 version;
493 	uint8_t		 psz;
494 	int		 error;
495 #ifndef NOPOOL
496 	static int 	 dw_pool_inited = 0;
497 
498 	if (!dw_pool_inited) {
499 		pool_init(&dcu_pool, "dcu", 1, sizeof(struct dwcu));
500 		pool_init(&dab_pool, "dab", 32, sizeof(struct dwabbrev));
501 		pool_init(&dat_pool, "dat", 32, sizeof(struct dwattr));
502 		pool_init(&die_pool, "die", 512, sizeof(struct dwdie));
503 		pool_init(&dav_pool, "dav", 1024, sizeof(struct dwaval));
504 		dw_pool_inited = 1;
505 	}
506 #endif /* NOPOOL */
507 
508 	if (info->len == 0 || abbrev->len == 0)
509 		return EINVAL;
510 
511 	/* Offset in the segment of the current Compile Unit. */
512 	segoff = seglen - info->len;
513 
514 	if (dw_read_u32(info, &length))
515 		return -1;
516 
517 	if (length >= 0xfffffff0 || length > info->len)
518 		return EOVERFLOW;
519 
520 	/* Offset of the next Compile Unit. */
521 	nextoff = segoff + length + sizeof(uint32_t);
522 
523 	if (dw_read_buf(info, &dwbuf, length))
524 		return -1;
525 
526 	addrsize = 4; /* XXX */
527 
528 	if (dw_read_u16(&dwbuf, &version) ||
529 	    dw_read_bytes(&dwbuf, &abbroff, addrsize) ||
530 	    dw_read_u8(&dwbuf, &psz))
531 		return -1;
532 
533 	if (dw_skip_bytes(&abseg, abbroff))
534 		return -1;
535 
536 	/* Only DWARF2 until extended. */
537 	if (version != 2)
538 		return ENOTSUP;
539 
540 	dcu = pmalloc(&dcu_pool, sizeof(*dcu));
541 	if (dcu == NULL)
542 		return ENOMEM;
543 
544 	dcu->dcu_offset = segoff;
545 	dcu->dcu_length = length;
546 	dcu->dcu_version = version;
547 	dcu->dcu_abbroff = abbroff;
548 	dcu->dcu_psize = psz;
549 	SIMPLEQ_INIT(&dcu->dcu_abbrevs);
550 	SIMPLEQ_INIT(&dcu->dcu_dies);
551 
552 	error = dw_ab_parse(&abseg, &dcu->dcu_abbrevs);
553 	if (error != 0) {
554 		dw_dcu_free(dcu);
555 		return error;
556 	}
557 
558 	error = dw_die_parse(&dwbuf, nextoff, psz, &dcu->dcu_abbrevs,
559 	    &dcu->dcu_dies);
560 	if (error != 0) {
561 		dw_dcu_free(dcu);
562 		return error;
563 	}
564 
565 	if (dcup != NULL)
566 		*dcup = dcu;
567 	else
568 		dw_dcu_free(dcu);
569 
570 	return 0;
571 }
572 
573 void
574 dw_dcu_free(struct dwcu *dcu)
575 {
576 	if (dcu == NULL)
577 		return;
578 
579 	dw_die_purge(&dcu->dcu_dies);
580 	dw_dabq_purge(&dcu->dcu_abbrevs);
581 	pfree(&dcu_pool, dcu);
582 }
583 
584 int
585 dw_loc_parse(struct dwbuf *dwbuf, uint8_t *pop, uint64_t *poper1,
586     uint64_t *poper2)
587 {
588 	uint64_t oper1 = 0, oper2 = 0;
589 	uint8_t op;
590 
591 	if (dw_read_u8(dwbuf, &op))
592 		return -1;
593 
594 	if (pop != NULL)
595 		*pop = op;
596 
597 	switch (op) {
598 	case DW_OP_constu:
599 	case DW_OP_plus_uconst:
600 	case DW_OP_regx:
601 	case DW_OP_piece:
602 		dw_read_uleb128(dwbuf, &oper1);
603 		break;
604 
605 	case DW_OP_consts:
606 	case DW_OP_breg0 ... DW_OP_breg31:
607 	case DW_OP_fbreg:
608 		dw_read_sleb128(dwbuf, &oper1);
609 		break;
610 	default:
611 		return ENOTSUP;
612 	}
613 
614 	if (poper1 != NULL)
615 		*poper1 = oper1;
616 	if (poper2 != NULL)
617 		*poper2 = oper2;
618 
619 	return 0;
620 }
621