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. */
43 internal_function int
__libdw_read_begin_end_pair_inc(Dwarf_CU * cu,int sec_index,const unsigned char ** addrp,const unsigned char * addrend,int width,Dwarf_Addr * beginp,Dwarf_Addr * endp,Dwarf_Addr * basep)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
384 static int
initial_offset(Dwarf_Attribute * attr,ptrdiff_t * offset)385 initial_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
463 ptrdiff_t
dwarf_ranges(Dwarf_Die * die,ptrdiff_t offset,Dwarf_Addr * basep,Dwarf_Addr * startp,Dwarf_Addr * endp)464 dwarf_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 if (attr == NULL
510 && is_cudie (die)
511 && die->cu->unit_type == DW_UT_split_compile)
512 attr = INTUSE(dwarf_attr_integrate) (die, DW_AT_ranges, &attr_mem);
513 if (attr == NULL)
514 /* No PC attributes in this DIE at all, so an empty range list. */
515 return 0;
516
517 *basep = __libdw_cu_base_address (attr->cu);
518 if (*basep == (Dwarf_Addr) -1)
519 return -1;
520
521 if (initial_offset (attr, &offset) != 0)
522 return -1;
523 }
524 else
525 {
526 if (__libdw_offset_in_section (cu->dbg,
527 secidx, offset, 1))
528 return -1;
529 }
530
531 readp = d->d_buf + offset;
532 readendp = d->d_buf + d->d_size;
533
534 Dwarf_Addr begin;
535 Dwarf_Addr end;
536
537 next:
538 switch (__libdw_read_begin_end_pair_inc (cu, secidx,
539 &readp, readendp,
540 cu->address_size,
541 &begin, &end, basep))
542 {
543 case 0:
544 break;
545 case 1:
546 goto next;
547 case 2:
548 return 0;
549 default:
550 return -1;
551 }
552
553 *startp = begin;
554 *endp = end;
555 return readp - (unsigned char *) d->d_buf;
556 }
557 INTDEF (dwarf_ranges)
558