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 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 * 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 * 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 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 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 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 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 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 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 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 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 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