1/* Enumerate the PC ranges covered by a DIE. 2 Copyright (C) 2005, 2007, 2009, 2018 Red Hat, Inc. 3 This file is part of elfutils. 4 5 This file is free software; you can redistribute it and/or modify 6 it under the terms of either 7 8 * the GNU Lesser General Public License as published by the Free 9 Software Foundation; either version 3 of the License, or (at 10 your option) any later version 11 12 or 13 14 * the GNU General Public License as published by the Free 15 Software Foundation; either version 2 of the License, or (at 16 your option) any later version 17 18 or both in parallel, as here. 19 20 elfutils is distributed in the hope that it will be useful, but 21 WITHOUT ANY WARRANTY; without even the implied warranty of 22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 General Public License for more details. 24 25 You should have received copies of the GNU General Public License and 26 the GNU Lesser General Public License along with this program. If 27 not, see <http://www.gnu.org/licenses/>. */ 28 29#ifdef HAVE_CONFIG_H 30# include <config.h> 31#endif 32 33#include "libdwP.h" 34#include <dwarf.h> 35#include <assert.h> 36 37/* Read up begin/end pair and increment read pointer. 38 - If it's normal range record, set up `*beginp' and `*endp' and return 0. 39 - If it's a default location, set `*beginp' (0), `*endp' (-1) and return 0. 40 - If it's base address selection record, set up `*basep' and return 1. 41 - If it's end of rangelist, don't set anything and return 2 42 - If an error occurs, don't set anything and return -1. */ 43internal_function int 44__libdw_read_begin_end_pair_inc (Dwarf_CU *cu, int sec_index, 45 const unsigned char **addrp, 46 const unsigned char *addrend, 47 int width, 48 Dwarf_Addr *beginp, Dwarf_Addr *endp, 49 Dwarf_Addr *basep) 50{ 51 Dwarf *dbg = cu->dbg; 52 if (sec_index == IDX_debug_loc 53 && cu->version < 5 54 && cu->unit_type == DW_UT_split_compile) 55 { 56 /* GNU DebugFission. */ 57 const unsigned char *addr = *addrp; 58 if (addrend - addr < 1) 59 goto invalid; 60 61 const char code = *addr++; 62 uint64_t begin = 0, end = 0, base = *basep, addr_idx; 63 switch (code) 64 { 65 case DW_LLE_GNU_end_of_list_entry: 66 *addrp = addr; 67 return 2; 68 69 case DW_LLE_GNU_base_address_selection_entry: 70 if (addrend - addr < 1) 71 goto invalid; 72 get_uleb128 (addr_idx, addr, addrend); 73 if (__libdw_addrx (cu, addr_idx, &base) != 0) 74 return -1; 75 *basep = base; 76 *addrp = addr; 77 return 1; 78 79 case DW_LLE_GNU_start_end_entry: 80 if (addrend - addr < 1) 81 goto invalid; 82 get_uleb128 (addr_idx, addr, addrend); 83 if (__libdw_addrx (cu, addr_idx, &begin) != 0) 84 return -1; 85 if (addrend - addr < 1) 86 goto invalid; 87 get_uleb128 (addr_idx, addr, addrend); 88 if (__libdw_addrx (cu, addr_idx, &end) != 0) 89 return -1; 90 91 *beginp = begin; 92 *endp = end; 93 *addrp = addr; 94 return 0; 95 96 case DW_LLE_GNU_start_length_entry: 97 if (addrend - addr < 1) 98 goto invalid; 99 get_uleb128 (addr_idx, addr, addrend); 100 if (__libdw_addrx (cu, addr_idx, &begin) != 0) 101 return -1; 102 if (addrend - addr < 4) 103 goto invalid; 104 end = read_4ubyte_unaligned_inc (dbg, addr); 105 106 *beginp = begin; 107 *endp = begin + end; 108 *addrp = addr; 109 return 0; 110 111 default: 112 goto invalid; 113 } 114 } 115 else if (sec_index == IDX_debug_ranges || sec_index == IDX_debug_loc) 116 { 117 Dwarf_Addr escape = (width == 8 ? (Elf64_Addr) -1 118 : (Elf64_Addr) (Elf32_Addr) -1); 119 Dwarf_Addr begin; 120 Dwarf_Addr end; 121 122 const unsigned char *addr = *addrp; 123 if (addrend - addr < width * 2) 124 { 125 invalid: 126 __libdw_seterrno (DWARF_E_INVALID_DWARF); 127 return -1; 128 } 129 130 bool begin_relocated = READ_AND_RELOCATE (__libdw_relocate_address, 131 begin); 132 bool end_relocated = READ_AND_RELOCATE (__libdw_relocate_address, 133 end); 134 *addrp = addr; 135 136 /* Unrelocated escape for begin means base address selection. */ 137 if (begin == escape && !begin_relocated) 138 { 139 if (unlikely (end == escape)) 140 goto invalid; 141 142 *basep = end; 143 return 1; 144 } 145 146 /* Unrelocated pair of zeroes means end of range list. */ 147 if (begin == 0 && end == 0 && !begin_relocated && !end_relocated) 148 return 2; 149 150 /* Don't check for begin_relocated == end_relocated. Serve the data 151 to the client even though it may be buggy. */ 152 *beginp = begin + *basep; 153 *endp = end + *basep; 154 155 return 0; 156 } 157 else if (sec_index == IDX_debug_rnglists) 158 { 159 const unsigned char *addr = *addrp; 160 if (addrend - addr < 1) 161 goto invalid; 162 163 const char code = *addr++; 164 uint64_t begin = 0, end = 0, base = *basep, addr_idx; 165 switch (code) 166 { 167 case DW_RLE_end_of_list: 168 *addrp = addr; 169 return 2; 170 171 case DW_RLE_base_addressx: 172 if (addrend - addr < 1) 173 goto invalid; 174 get_uleb128 (addr_idx, addr, addrend); 175 if (__libdw_addrx (cu, addr_idx, &base) != 0) 176 return -1; 177 178 *basep = base; 179 *addrp = addr; 180 return 1; 181 182 case DW_RLE_startx_endx: 183 if (addrend - addr < 1) 184 goto invalid; 185 get_uleb128 (addr_idx, addr, addrend); 186 if (__libdw_addrx (cu, addr_idx, &begin) != 0) 187 return -1; 188 if (addrend - addr < 1) 189 goto invalid; 190 get_uleb128 (addr_idx, addr, addrend); 191 if (__libdw_addrx (cu, addr_idx, &end) != 0) 192 return -1; 193 194 *beginp = begin; 195 *endp = end; 196 *addrp = addr; 197 return 0; 198 199 case DW_RLE_startx_length: 200 if (addrend - addr < 1) 201 goto invalid; 202 get_uleb128 (addr_idx, addr, addrend); 203 if (__libdw_addrx (cu, addr_idx, &begin) != 0) 204 return -1; 205 if (addrend - addr < 1) 206 goto invalid; 207 get_uleb128 (end, addr, addrend); 208 209 *beginp = begin; 210 *endp = begin + end; 211 *addrp = addr; 212 return 0; 213 214 case DW_RLE_offset_pair: 215 if (addrend - addr < 1) 216 goto invalid; 217 get_uleb128 (begin, addr, addrend); 218 if (addrend - addr < 1) 219 goto invalid; 220 get_uleb128 (end, addr, addrend); 221 222 *beginp = begin + base; 223 *endp = end + base; 224 *addrp = addr; 225 return 0; 226 227 case DW_RLE_base_address: 228 if (addrend - addr < width) 229 goto invalid; 230 __libdw_read_address_inc (dbg, sec_index, &addr, width, &base); 231 232 *basep = base; 233 *addrp = addr; 234 return 1; 235 236 case DW_RLE_start_end: 237 if (addrend - addr < 2 * width) 238 goto invalid; 239 __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin); 240 __libdw_read_address_inc (dbg, sec_index, &addr, width, &end); 241 242 *beginp = begin; 243 *endp = end; 244 *addrp = addr; 245 return 0; 246 247 case DW_RLE_start_length: 248 if (addrend - addr < width) 249 goto invalid; 250 __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin); 251 if (addrend - addr < 1) 252 goto invalid; 253 get_uleb128 (end, addr, addrend); 254 255 *beginp = begin; 256 *endp = begin + end; 257 *addrp = addr; 258 return 0; 259 260 default: 261 goto invalid; 262 } 263 } 264 else if (sec_index == IDX_debug_loclists) 265 { 266 const unsigned char *addr = *addrp; 267 if (addrend - addr < 1) 268 goto invalid; 269 270 const char code = *addr++; 271 uint64_t begin = 0, end = 0, base = *basep, addr_idx; 272 switch (code) 273 { 274 case DW_LLE_end_of_list: 275 *addrp = addr; 276 return 2; 277 278 case DW_LLE_base_addressx: 279 if (addrend - addr < 1) 280 goto invalid; 281 get_uleb128 (addr_idx, addr, addrend); 282 if (__libdw_addrx (cu, addr_idx, &base) != 0) 283 return -1; 284 285 *basep = base; 286 *addrp = addr; 287 return 1; 288 289 case DW_LLE_startx_endx: 290 if (addrend - addr < 1) 291 goto invalid; 292 get_uleb128 (addr_idx, addr, addrend); 293 if (__libdw_addrx (cu, addr_idx, &begin) != 0) 294 return -1; 295 if (addrend - addr < 1) 296 goto invalid; 297 get_uleb128 (addr_idx, addr, addrend); 298 if (__libdw_addrx (cu, addr_idx, &end) != 0) 299 return -1; 300 301 *beginp = begin; 302 *endp = end; 303 *addrp = addr; 304 return 0; 305 306 case DW_LLE_startx_length: 307 if (addrend - addr < 1) 308 goto invalid; 309 get_uleb128 (addr_idx, addr, addrend); 310 if (__libdw_addrx (cu, addr_idx, &begin) != 0) 311 return -1; 312 if (addrend - addr < 1) 313 goto invalid; 314 get_uleb128 (end, addr, addrend); 315 316 *beginp = begin; 317 *endp = begin + end; 318 *addrp = addr; 319 return 0; 320 321 case DW_LLE_offset_pair: 322 if (addrend - addr < 1) 323 goto invalid; 324 get_uleb128 (begin, addr, addrend); 325 if (addrend - addr < 1) 326 goto invalid; 327 get_uleb128 (end, addr, addrend); 328 329 *beginp = begin + base; 330 *endp = end + base; 331 *addrp = addr; 332 return 0; 333 334 case DW_LLE_default_location: 335 *beginp = 0; 336 *endp = (Dwarf_Addr) -1; 337 *addrp = addr; 338 return 0; 339 340 case DW_LLE_base_address: 341 if (addrend - addr < width) 342 goto invalid; 343 __libdw_read_address_inc (dbg, sec_index, &addr, width, &base); 344 345 *basep = base; 346 *addrp = addr; 347 return 1; 348 349 case DW_LLE_start_end: 350 if (addrend - addr < 2 * width) 351 goto invalid; 352 __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin); 353 __libdw_read_address_inc (dbg, sec_index, &addr, width, &end); 354 355 *beginp = begin; 356 *endp = end; 357 *addrp = addr; 358 return 0; 359 360 case DW_LLE_start_length: 361 if (addrend - addr < width) 362 goto invalid; 363 __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin); 364 if (addrend - addr < 1) 365 goto invalid; 366 get_uleb128 (end, addr, addrend); 367 368 *beginp = begin; 369 *endp = begin + end; 370 *addrp = addr; 371 return 0; 372 373 default: 374 goto invalid; 375 } 376 } 377 else 378 { 379 __libdw_seterrno (DWARF_E_INVALID_DWARF); 380 return -1; 381 } 382} 383 384static int 385initial_offset (Dwarf_Attribute *attr, ptrdiff_t *offset) 386{ 387 size_t secidx = (attr->cu->version < 5 388 ? IDX_debug_ranges : IDX_debug_rnglists); 389 390 Dwarf_Word start_offset; 391 if (attr->form == DW_FORM_rnglistx) 392 { 393 Dwarf_Word idx; 394 Dwarf_CU *cu = attr->cu; 395 const unsigned char *datap = attr->valp; 396 const unsigned char *endp = cu->endp; 397 if (datap >= endp) 398 { 399 __libdw_seterrno (DWARF_E_INVALID_DWARF); 400 return -1; 401 } 402 get_uleb128 (idx, datap, endp); 403 404 Elf_Data *data = cu->dbg->sectiondata[secidx]; 405 if (data == NULL && cu->unit_type == DW_UT_split_compile) 406 { 407 cu = __libdw_find_split_unit (cu); 408 if (cu != NULL) 409 data = cu->dbg->sectiondata[secidx]; 410 } 411 412 if (data == NULL) 413 { 414 __libdw_seterrno (secidx == IDX_debug_ranges 415 ? DWARF_E_NO_DEBUG_RANGES 416 : DWARF_E_NO_DEBUG_RNGLISTS); 417 return -1; 418 } 419 420 Dwarf_Off range_base_off = __libdw_cu_ranges_base (cu); 421 422 /* The section should at least contain room for one offset. */ 423 size_t sec_size = cu->dbg->sectiondata[secidx]->d_size; 424 size_t offset_size = cu->offset_size; 425 if (offset_size > sec_size) 426 { 427 invalid_offset: 428 __libdw_seterrno (DWARF_E_INVALID_OFFSET); 429 return -1; 430 } 431 432 /* And the base offset should be at least inside the section. */ 433 if (range_base_off > (sec_size - offset_size)) 434 goto invalid_offset; 435 436 size_t max_idx = (sec_size - offset_size - range_base_off) / offset_size; 437 if (idx > max_idx) 438 goto invalid_offset; 439 440 datap = (cu->dbg->sectiondata[secidx]->d_buf 441 + range_base_off + (idx * offset_size)); 442 if (offset_size == 4) 443 start_offset = read_4ubyte_unaligned (cu->dbg, datap); 444 else 445 start_offset = read_8ubyte_unaligned (cu->dbg, datap); 446 447 start_offset += range_base_off; 448 } 449 else 450 { 451 if (__libdw_formptr (attr, secidx, 452 (secidx == IDX_debug_ranges 453 ? DWARF_E_NO_DEBUG_RANGES 454 : DWARF_E_NO_DEBUG_RNGLISTS), 455 NULL, &start_offset) == NULL) 456 return -1; 457 } 458 459 *offset = start_offset; 460 return 0; 461} 462 463ptrdiff_t 464dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep, 465 Dwarf_Addr *startp, Dwarf_Addr *endp) 466{ 467 if (die == NULL) 468 return -1; 469 470 if (offset == 0 471 /* Usually there is a single contiguous range. */ 472 && INTUSE(dwarf_highpc) (die, endp) == 0 473 && INTUSE(dwarf_lowpc) (die, startp) == 0) 474 /* A offset into .debug_ranges will never be 1, it must be at least a 475 multiple of 4. So we can return 1 as a special case value to mark 476 there are no ranges to look for on the next call. */ 477 return 1; 478 479 if (offset == 1) 480 return 0; 481 482 /* We have to look for a noncontiguous range. */ 483 Dwarf_CU *cu = die->cu; 484 if (cu == NULL) 485 { 486 __libdw_seterrno (DWARF_E_INVALID_DWARF); 487 return -1; 488 } 489 490 size_t secidx = (cu->version < 5 ? IDX_debug_ranges : IDX_debug_rnglists); 491 const Elf_Data *d = cu->dbg->sectiondata[secidx]; 492 if (d == NULL && cu->unit_type == DW_UT_split_compile) 493 { 494 Dwarf_CU *skel = __libdw_find_split_unit (cu); 495 if (skel != NULL) 496 { 497 cu = skel; 498 d = cu->dbg->sectiondata[secidx]; 499 } 500 } 501 502 const unsigned char *readp; 503 const unsigned char *readendp; 504 if (offset == 0) 505 { 506 Dwarf_Attribute attr_mem; 507 Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_ranges, 508 &attr_mem); 509 /* Note that above we use dwarf_attr, not dwarf_attr_integrate. 510 The only case where the ranges can come from another DIE 511 attribute are the split CU case. In that case we also have a 512 different CU to check against. But that is already set up 513 above using __libdw_find_split_unit. */ 514 if (attr == NULL 515 && is_cudie (die) 516 && die->cu->unit_type == DW_UT_split_compile) 517 attr = INTUSE(dwarf_attr_integrate) (die, DW_AT_ranges, &attr_mem); 518 if (attr == NULL) 519 /* No PC attributes in this DIE at all, so an empty range list. */ 520 return 0; 521 522 *basep = __libdw_cu_base_address (attr->cu); 523 if (*basep == (Dwarf_Addr) -1) 524 return -1; 525 526 if (initial_offset (attr, &offset) != 0) 527 return -1; 528 } 529 else 530 { 531 if (__libdw_offset_in_section (cu->dbg, 532 secidx, offset, 1)) 533 return -1; 534 } 535 536 readp = d->d_buf + offset; 537 readendp = d->d_buf + d->d_size; 538 539 Dwarf_Addr begin; 540 Dwarf_Addr end; 541 542 next: 543 switch (__libdw_read_begin_end_pair_inc (cu, secidx, 544 &readp, readendp, 545 cu->address_size, 546 &begin, &end, basep)) 547 { 548 case 0: 549 break; 550 case 1: 551 goto next; 552 case 2: 553 return 0; 554 default: 555 return -1; 556 } 557 558 *startp = begin; 559 *endp = end; 560 return readp - (unsigned char *) d->d_buf; 561} 562INTDEF (dwarf_ranges) 563