Coverage Report

Created: 2025-07-12 06:44

/src/elfutils/libdw/dwarf_ranges.c
Line
Count
Source (jump to first uncovered line)
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
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
0
{
51
0
  Dwarf *dbg = cu->dbg;
52
0
  if (sec_index == IDX_debug_loc
53
0
      && cu->version < 5
54
0
      && cu->unit_type == DW_UT_split_compile)
55
0
    {
56
      /* GNU DebugFission.  */
57
0
      const unsigned char *addr = *addrp;
58
0
      if (addrend - addr < 1)
59
0
  goto invalid;
60
61
0
      const char code = *addr++;
62
0
      uint64_t begin = 0, end = 0, base = *basep, addr_idx;
63
0
      switch (code)
64
0
  {
65
0
  case DW_LLE_GNU_end_of_list_entry:
66
0
    *addrp = addr;
67
0
    return 2;
68
69
0
  case DW_LLE_GNU_base_address_selection_entry:
70
0
    if (addrend - addr < 1)
71
0
      goto invalid;
72
0
    get_uleb128 (addr_idx, addr, addrend);
73
0
    if (__libdw_addrx (cu, addr_idx, &base) != 0)
74
0
      return -1;
75
0
    *basep = base;
76
0
    *addrp = addr;
77
0
    return 1;
78
79
0
  case DW_LLE_GNU_start_end_entry:
80
0
    if (addrend - addr < 1)
81
0
      goto invalid;
82
0
    get_uleb128 (addr_idx, addr, addrend);
83
0
    if (__libdw_addrx (cu, addr_idx, &begin) != 0)
84
0
      return -1;
85
0
    if (addrend - addr < 1)
86
0
      goto invalid;
87
0
    get_uleb128 (addr_idx, addr, addrend);
88
0
    if (__libdw_addrx (cu, addr_idx, &end) != 0)
89
0
      return -1;
90
91
0
    *beginp = begin;
92
0
    *endp = end;
93
0
    *addrp = addr;
94
0
    return 0;
95
96
0
  case DW_LLE_GNU_start_length_entry:
97
0
    if (addrend - addr < 1)
98
0
      goto invalid;
99
0
    get_uleb128 (addr_idx, addr, addrend);
100
0
    if (__libdw_addrx (cu, addr_idx, &begin) != 0)
101
0
      return -1;
102
0
    if (addrend - addr < 4)
103
0
      goto invalid;
104
0
    end = read_4ubyte_unaligned_inc (dbg, addr);
105
106
0
    *beginp = begin;
107
0
    *endp = begin + end;
108
0
    *addrp = addr;
109
0
    return 0;
110
111
0
  default:
112
0
    goto invalid;
113
0
  }
114
0
    }
115
0
  else if (sec_index == IDX_debug_ranges || sec_index == IDX_debug_loc)
116
0
    {
117
0
      Dwarf_Addr escape = (width == 8 ? (Elf64_Addr) -1
118
0
         : (Elf64_Addr) (Elf32_Addr) -1);
119
0
      Dwarf_Addr begin;
120
0
      Dwarf_Addr end;
121
122
0
      const unsigned char *addr = *addrp;
123
0
      if (addrend - addr < width * 2)
124
0
  {
125
0
  invalid:
126
0
    __libdw_seterrno (DWARF_E_INVALID_DWARF);
127
0
    return -1;
128
0
  }
129
130
0
      bool begin_relocated = READ_AND_RELOCATE (__libdw_relocate_address,
131
0
            begin);
132
0
      bool end_relocated = READ_AND_RELOCATE (__libdw_relocate_address,
133
0
                end);
134
0
      *addrp = addr;
135
136
      /* Unrelocated escape for begin means base address selection.  */
137
0
      if (begin == escape && !begin_relocated)
138
0
  {
139
0
    if (unlikely (end == escape))
140
0
      goto invalid;
141
142
0
    *basep = end;
143
0
    return 1;
144
0
  }
145
146
      /* Unrelocated pair of zeroes means end of range list.  */
147
0
      if (begin == 0 && end == 0 && !begin_relocated && !end_relocated)
148
0
  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
0
      *beginp = begin + *basep;
153
0
      *endp = end + *basep;
154
155
0
      return 0;
156
0
    }
157
0
  else if (sec_index == IDX_debug_rnglists)
158
0
    {
159
0
      const unsigned char *addr = *addrp;
160
0
      if (addrend - addr < 1)
161
0
  goto invalid;
162
163
0
      const char code = *addr++;
164
0
      uint64_t begin = 0, end = 0, base = *basep, addr_idx;
165
0
      switch (code)
166
0
  {
167
0
  case DW_RLE_end_of_list:
168
0
    *addrp = addr;
169
0
    return 2;
170
171
0
  case DW_RLE_base_addressx:
172
0
    if (addrend - addr < 1)
173
0
      goto invalid;
174
0
    get_uleb128 (addr_idx, addr, addrend);
175
0
    if (__libdw_addrx (cu, addr_idx, &base) != 0)
176
0
      return -1;
177
178
0
    *basep = base;
179
0
    *addrp = addr;
180
0
    return 1;
181
182
0
  case DW_RLE_startx_endx:
183
0
    if (addrend - addr < 1)
184
0
      goto invalid;
185
0
    get_uleb128 (addr_idx, addr, addrend);
186
0
    if (__libdw_addrx (cu, addr_idx, &begin) != 0)
187
0
      return -1;
188
0
    if (addrend - addr < 1)
189
0
      goto invalid;
190
0
    get_uleb128 (addr_idx, addr, addrend);
191
0
    if (__libdw_addrx (cu, addr_idx, &end) != 0)
192
0
      return -1;
193
194
0
    *beginp = begin;
195
0
    *endp = end;
196
0
    *addrp = addr;
197
0
    return 0;
198
199
0
  case DW_RLE_startx_length:
200
0
    if (addrend - addr < 1)
201
0
      goto invalid;
202
0
    get_uleb128 (addr_idx, addr, addrend);
203
0
    if (__libdw_addrx (cu, addr_idx, &begin) != 0)
204
0
      return -1;
205
0
    if (addrend - addr < 1)
206
0
      goto invalid;
207
0
    get_uleb128 (end, addr, addrend);
208
209
0
    *beginp = begin;
210
0
    *endp = begin + end;
211
0
    *addrp = addr;
212
0
    return 0;
213
214
0
  case DW_RLE_offset_pair:
215
0
    if (addrend - addr < 1)
216
0
      goto invalid;
217
0
    get_uleb128 (begin, addr, addrend);
218
0
    if (addrend - addr < 1)
219
0
      goto invalid;
220
0
    get_uleb128 (end, addr, addrend);
221
222
0
    *beginp = begin + base;
223
0
    *endp = end + base;
224
0
    *addrp = addr;
225
0
    return 0;
226
227
0
  case DW_RLE_base_address:
228
0
    if (addrend - addr < width)
229
0
      goto invalid;
230
0
    __libdw_read_address_inc (dbg, sec_index, &addr, width, &base);
231
232
0
    *basep = base;
233
0
    *addrp = addr;
234
0
    return 1;
235
236
0
  case DW_RLE_start_end:
237
0
    if (addrend - addr < 2 * width)
238
0
      goto invalid;
239
0
    __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
240
0
    __libdw_read_address_inc (dbg, sec_index, &addr, width, &end);
241
242
0
    *beginp = begin;
243
0
    *endp = end;
244
0
    *addrp = addr;
245
0
    return 0;
246
247
0
  case DW_RLE_start_length:
248
0
    if (addrend - addr < width)
249
0
      goto invalid;
250
0
    __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
251
0
    if (addrend - addr < 1)
252
0
      goto invalid;
253
0
    get_uleb128 (end, addr, addrend);
254
255
0
    *beginp = begin;
256
0
    *endp = begin + end;
257
0
    *addrp = addr;
258
0
    return 0;
259
260
0
  default:
261
0
    goto invalid;
262
0
  }
263
0
    }
264
0
  else if (sec_index == IDX_debug_loclists)
265
0
    {
266
0
      const unsigned char *addr = *addrp;
267
0
      if (addrend - addr < 1)
268
0
  goto invalid;
269
270
0
      const char code = *addr++;
271
0
      uint64_t begin = 0, end = 0, base = *basep, addr_idx;
272
0
      switch (code)
273
0
  {
274
0
  case DW_LLE_end_of_list:
275
0
    *addrp = addr;
276
0
    return 2;
277
278
0
  case DW_LLE_base_addressx:
279
0
    if (addrend - addr < 1)
280
0
      goto invalid;
281
0
    get_uleb128 (addr_idx, addr, addrend);
282
0
    if (__libdw_addrx (cu, addr_idx, &base) != 0)
283
0
      return -1;
284
285
0
    *basep = base;
286
0
    *addrp = addr;
287
0
    return 1;
288
289
0
  case DW_LLE_startx_endx:
290
0
    if (addrend - addr < 1)
291
0
      goto invalid;
292
0
    get_uleb128 (addr_idx, addr, addrend);
293
0
    if (__libdw_addrx (cu, addr_idx, &begin) != 0)
294
0
      return -1;
295
0
    if (addrend - addr < 1)
296
0
      goto invalid;
297
0
    get_uleb128 (addr_idx, addr, addrend);
298
0
    if (__libdw_addrx (cu, addr_idx, &end) != 0)
299
0
      return -1;
300
301
0
    *beginp = begin;
302
0
    *endp = end;
303
0
    *addrp = addr;
304
0
    return 0;
305
306
0
  case DW_LLE_startx_length:
307
0
    if (addrend - addr < 1)
308
0
      goto invalid;
309
0
    get_uleb128 (addr_idx, addr, addrend);
310
0
    if (__libdw_addrx (cu, addr_idx, &begin) != 0)
311
0
      return -1;
312
0
    if (addrend - addr < 1)
313
0
      goto invalid;
314
0
    get_uleb128 (end, addr, addrend);
315
316
0
    *beginp = begin;
317
0
    *endp = begin + end;
318
0
    *addrp = addr;
319
0
    return 0;
320
321
0
  case DW_LLE_offset_pair:
322
0
    if (addrend - addr < 1)
323
0
      goto invalid;
324
0
    get_uleb128 (begin, addr, addrend);
325
0
    if (addrend - addr < 1)
326
0
      goto invalid;
327
0
    get_uleb128 (end, addr, addrend);
328
329
0
    *beginp = begin + base;
330
0
    *endp = end + base;
331
0
    *addrp = addr;
332
0
    return 0;
333
334
0
  case DW_LLE_default_location:
335
0
    *beginp = 0;
336
0
    *endp = (Dwarf_Addr) -1;
337
0
    *addrp = addr;
338
0
    return 0;
339
340
0
  case DW_LLE_base_address:
341
0
    if (addrend - addr < width)
342
0
      goto invalid;
343
0
    __libdw_read_address_inc (dbg, sec_index, &addr, width, &base);
344
345
0
    *basep = base;
346
0
    *addrp = addr;
347
0
    return 1;
348
349
0
  case DW_LLE_start_end:
350
0
    if (addrend - addr < 2 * width)
351
0
      goto invalid;
352
0
    __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
353
0
    __libdw_read_address_inc (dbg, sec_index, &addr, width, &end);
354
355
0
    *beginp = begin;
356
0
    *endp = end;
357
0
    *addrp = addr;
358
0
    return 0;
359
360
0
  case DW_LLE_start_length:
361
0
    if (addrend - addr < width)
362
0
      goto invalid;
363
0
    __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
364
0
    if (addrend - addr < 1)
365
0
      goto invalid;
366
0
    get_uleb128 (end, addr, addrend);
367
368
0
    *beginp = begin;
369
0
    *endp = begin + end;
370
0
    *addrp = addr;
371
0
    return 0;
372
373
0
  default:
374
0
    goto invalid;
375
0
  }
376
0
    }
377
0
  else
378
0
    {
379
0
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
380
0
      return -1;
381
0
    }
382
0
}
383
384
static int
385
initial_offset (Dwarf_Attribute *attr, ptrdiff_t *offset)
386
0
{
387
0
  size_t secidx = (attr->cu->version < 5
388
0
       ? IDX_debug_ranges : IDX_debug_rnglists);
389
390
0
  Dwarf_Word start_offset;
391
0
  if (attr->form == DW_FORM_rnglistx)
392
0
    {
393
0
      Dwarf_Word idx;
394
0
      Dwarf_CU *cu = attr->cu;
395
0
      const unsigned char *datap = attr->valp;
396
0
      const unsigned char *endp = cu->endp;
397
0
      if (datap >= endp)
398
0
  {
399
0
    __libdw_seterrno (DWARF_E_INVALID_DWARF);
400
0
    return -1;
401
0
  }
402
0
      get_uleb128 (idx, datap, endp);
403
404
0
      Elf_Data *data = cu->dbg->sectiondata[secidx];
405
0
      if (data == NULL && cu->unit_type == DW_UT_split_compile)
406
0
  {
407
0
    cu = __libdw_find_split_unit (cu);
408
0
    if (cu != NULL)
409
0
      data = cu->dbg->sectiondata[secidx];
410
0
  }
411
412
0
      if (data == NULL)
413
0
  {
414
0
    __libdw_seterrno (secidx == IDX_debug_ranges
415
0
                            ? DWARF_E_NO_DEBUG_RANGES
416
0
                            : DWARF_E_NO_DEBUG_RNGLISTS);
417
0
    return -1;
418
0
  }
419
420
0
      Dwarf_Off range_base_off = __libdw_cu_ranges_base (cu);
421
422
      /* The section should at least contain room for one offset.  */
423
0
      size_t sec_size = cu->dbg->sectiondata[secidx]->d_size;
424
0
      size_t offset_size = cu->offset_size;
425
0
      if (offset_size > sec_size)
426
0
  {
427
0
  invalid_offset:
428
0
    __libdw_seterrno (DWARF_E_INVALID_OFFSET);
429
0
    return -1;
430
0
  }
431
432
      /* And the base offset should be at least inside the section.  */
433
0
      if (range_base_off > (sec_size - offset_size))
434
0
  goto invalid_offset;
435
436
0
      size_t max_idx = (sec_size - offset_size - range_base_off) / offset_size;
437
0
      if (idx > max_idx)
438
0
  goto invalid_offset;
439
440
0
      datap = (cu->dbg->sectiondata[secidx]->d_buf
441
0
         + range_base_off + (idx * offset_size));
442
0
      if (offset_size == 4)
443
0
  start_offset = read_4ubyte_unaligned (cu->dbg, datap);
444
0
      else
445
0
  start_offset = read_8ubyte_unaligned (cu->dbg, datap);
446
447
0
      start_offset += range_base_off;
448
0
    }
449
0
  else
450
0
    {
451
0
      if (__libdw_formptr (attr, secidx,
452
0
         (secidx == IDX_debug_ranges
453
0
          ? DWARF_E_NO_DEBUG_RANGES
454
0
          : DWARF_E_NO_DEBUG_RNGLISTS),
455
0
         NULL, &start_offset) == NULL)
456
0
  return -1;
457
0
    }
458
459
0
  *offset = start_offset;
460
0
  return 0;
461
0
}
462
463
ptrdiff_t
464
dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep,
465
        Dwarf_Addr *startp, Dwarf_Addr *endp)
466
0
{
467
0
  if (die == NULL)
468
0
    return -1;
469
470
0
  if (offset == 0
471
      /* Usually there is a single contiguous range.  */
472
0
      && INTUSE(dwarf_highpc) (die, endp) == 0
473
0
      && 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
0
    return 1;
478
479
0
  if (offset == 1)
480
0
    return 0;
481
482
  /* We have to look for a noncontiguous range.  */
483
0
  Dwarf_CU *cu = die->cu;
484
0
  if (cu == NULL)
485
0
    {
486
0
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
487
0
      return -1;
488
0
    }
489
490
0
  size_t secidx = (cu->version < 5 ? IDX_debug_ranges : IDX_debug_rnglists);
491
0
  const Elf_Data *d = cu->dbg->sectiondata[secidx];
492
0
  if (cu->unit_type == DW_UT_split_compile && (d == NULL || is_cudie (die)))
493
0
    {
494
0
      Dwarf_CU *skel = __libdw_find_split_unit (cu);
495
0
      if (skel != NULL && skel->dbg->sectiondata[secidx] != NULL)
496
0
  {
497
0
    cu = skel;
498
0
    d = cu->dbg->sectiondata[secidx];
499
0
  }
500
0
    }
501
502
0
  const unsigned char *readp;
503
0
  const unsigned char *readendp;
504
0
  if (offset == 0)
505
0
    {
506
0
      Dwarf_Attribute attr_mem;
507
0
      Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_ranges,
508
0
              &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
0
      if (attr == NULL
515
0
    && is_cudie (die)
516
0
    && die->cu->unit_type == DW_UT_split_compile)
517
0
  attr = INTUSE(dwarf_attr_integrate) (die, DW_AT_ranges, &attr_mem);
518
0
      if (attr == NULL)
519
  /* No PC attributes in this DIE at all, so an empty range list.  */
520
0
  return 0;
521
522
0
      *basep = __libdw_cu_base_address (attr->cu);
523
0
      if (*basep == (Dwarf_Addr) -1)
524
0
  return -1;
525
526
0
      if (initial_offset (attr, &offset) != 0)
527
0
  return -1;
528
0
    }
529
0
  else
530
0
    {
531
0
      if (__libdw_offset_in_section (cu->dbg,
532
0
             secidx, offset, 1))
533
0
  return -1;
534
0
    }
535
536
0
  readp = d->d_buf + offset;
537
0
  readendp = d->d_buf + d->d_size;
538
539
0
  Dwarf_Addr begin;
540
0
  Dwarf_Addr end;
541
542
0
 next:
543
0
  switch (__libdw_read_begin_end_pair_inc (cu, secidx,
544
0
             &readp, readendp,
545
0
             cu->address_size,
546
0
             &begin, &end, basep))
547
0
    {
548
0
    case 0:
549
0
      break;
550
0
    case 1:
551
0
      goto next;
552
0
    case 2:
553
0
      return 0;
554
0
    default:
555
0
      return -1;
556
0
    }
557
558
0
  *startp = begin;
559
0
  *endp = end;
560
0
  return readp - (unsigned char *) d->d_buf;
561
0
}
562
INTDEF (dwarf_ranges)