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