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