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