Coverage Report

Created: 2026-04-09 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libdwarf/src/lib/libdwarf/dwarf_ranges.c
Line
Count
Source
1
/*
2
  Copyright (C) 2008-2020 David Anderson. All Rights Reserved.
3
  Portions Copyright 2012 SN Systems Ltd. All rights reserved.
4
5
  This program is free software; you can redistribute it
6
  and/or modify it under the terms of version 2.1 of the
7
  GNU Lesser General Public License as published by the Free
8
  Software Foundation.
9
10
  This program is distributed in the hope that it would be
11
  useful, but WITHOUT ANY WARRANTY; without even the implied
12
  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13
  PURPOSE.
14
15
  Further, this software is distributed without any warranty
16
  that it is free of the rightful claim of any third person
17
  regarding infringement or the like.  Any license provided
18
  herein, whether implied or otherwise, applies only to this
19
  software file.  Patent licenses, if any, provided herein
20
  do not apply to combinations of this program with other
21
  software, or any other product whatsoever.
22
23
  You should have received a copy of the GNU Lesser General
24
  Public License along with this program; if not, write the
25
  Free Software Foundation, Inc., 51 Franklin Street - Fifth
26
  Floor, Boston MA 02110-1301, USA.
27
28
*/
29
30
#include <config.h>
31
32
#include <stdlib.h> /* calloc() free() */
33
#include <stdio.h> /* printf debugging */
34
35
#if defined(_WIN32) && defined(HAVE_STDAFX_H)
36
#include "stdafx.h"
37
#endif /* HAVE_STDAFX_H */
38
39
#include "dwarf.h"
40
#include "libdwarf.h"
41
#include "dwarf_local_malloc.h"
42
#include "libdwarf_private.h"
43
#include "dwarf_base_types.h"
44
#include "dwarf_opaque.h"
45
#include "dwarf_alloc.h"
46
#include "dwarf_error.h"
47
#include "dwarf_util.h"
48
#include "dwarf_string.h"
49
50
#define DEBUG_RANGES 1
51
#undef DEBUG_RANGES
52
53
struct ranges_entry {
54
    struct ranges_entry *next;
55
    Dwarf_Ranges cur;
56
};
57
58
static void
59
free_allocated_ranges( struct ranges_entry *base)
60
1.85k
{
61
1.85k
    struct ranges_entry *cur = 0;
62
1.85k
    struct ranges_entry *next = 0;
63
44.9k
    for ( cur = base ; cur ; cur = next ) {
64
43.1k
        next = cur->next;
65
43.1k
        free(cur);
66
43.1k
    }
67
1.85k
}
68
69
/*  We encapsulate the macro use so we can
70
    free local malloc resources that would otherwise
71
    leak. See the call points below. */
72
static int
73
read_unaligned_addr_check(Dwarf_Debug dbg,
74
    Dwarf_Addr *addr_out,
75
    Dwarf_Small *rangeptr,
76
    unsigned address_size,
77
    Dwarf_Error *error,
78
    Dwarf_Small *section_end)
79
86.2k
{
80
86.2k
    Dwarf_Addr a = 0;
81
82
86.2k
    READ_UNALIGNED_CK(dbg,a,
83
86.2k
        Dwarf_Addr, rangeptr,
84
86.2k
        address_size,
85
86.2k
        error,section_end);
86
86.2k
    *addr_out = a;
87
86.2k
    return DW_DLV_OK;
88
86.2k
}
89
/*  As of DWARF5 the ranges section each range list set has
90
    a range-list-table header. See "7.28 Range List Table"
91
    in the DWARF5 standard.
92
    For DWARF5 the offset should be the offset of
93
    the range-list-table-header for that range list.
94
    For DWARF3 and DWARF4 the offset has to be that
95
    of a range list.
96
*/
97
/*  Ranges and pc values can be in a split dwarf object.
98
    In that case the appropriate values need to be
99
    incremented by data from the executable in
100
    the compilation unit with the same dwo_id.
101
102
    We return an error which is on the incoming dbg, not
103
    the possibly-tied-dbg localdbg.
104
    If incoming die is NULL there is no context, so do not look
105
    for a tied file, and address_size is the size
106
    of the overall object, not the address_size of the context. */
107
42.0k
#define MAX_ADDR ((address_size == 8)? \
108
42.0k
    0xffffffffffffffffULL:0xffffffff)
109
/*  New 10 September 2020 to accommodate the
110
    GNU extension of DWARF4 split-dwarf.
111
    The actual_offset field is set by the function
112
    to the actual final offset of the ranges
113
    in the separate tied (a.out) file. */
114
int dwarf_get_ranges_b(Dwarf_Debug dbg,
115
    Dwarf_Off rangesoffset,
116
    Dwarf_Die die,
117
    Dwarf_Off *actual_offset /* in .debug_ranges */,
118
    Dwarf_Ranges ** rangesbuf,
119
    Dwarf_Signed * listlen,
120
    Dwarf_Unsigned * bytecount,
121
    Dwarf_Error * error)
122
8.04k
{
123
8.04k
    Dwarf_Small *rangeptr = 0;
124
8.04k
    Dwarf_Small *beginrangeptr = 0;
125
8.04k
    Dwarf_Small *section_end = 0;
126
8.04k
    unsigned entry_count = 0;
127
8.04k
    struct ranges_entry *base = 0;
128
8.04k
    struct ranges_entry *last = 0;
129
8.04k
    struct ranges_entry *curre = 0;
130
8.04k
    Dwarf_Ranges * ranges_data_out = 0;
131
8.04k
    unsigned copyindex = 0;
132
8.04k
    Dwarf_Half address_size = 0;
133
8.04k
    int res = DW_DLV_ERROR;
134
8.04k
    Dwarf_Unsigned ranges_base = 0;
135
8.04k
    Dwarf_Debug    localdbg = dbg;
136
137
    /* default for dwarf_get_ranges() */
138
8.04k
    Dwarf_Half     die_version = 3;
139
140
8.04k
    Dwarf_Half offset_size= 4;
141
8.04k
    Dwarf_CU_Context cucontext = 0;
142
143
8.04k
    CHECK_DBG(dbg,error,"dwarf_get_ranges_b()");
144
8.04k
    address_size = localdbg->de_pointer_size; /* default  */
145
8.04k
    if (die) {
146
        /*  printing DW_AT_ranges attribute. and the local DIE
147
            it belongs to.
148
            If we wind up using the tied file the die_version
149
            had better match! It cannot be other than a match.
150
            Can return DW_DLV_ERROR, not DW_DLV_NO_ENTRY.
151
            Add err code if error.  Version comes from the
152
            cu context, not the DIE itself. */
153
8.04k
        res = dwarf_get_version_of_die(die,&die_version,
154
8.04k
            &offset_size);
155
8.04k
        if (res == DW_DLV_ERROR) {
156
0
            _dwarf_error(dbg, error, DW_DLE_DIE_NO_CU_CONTEXT);
157
0
            return DW_DLV_ERROR;
158
0
        }
159
8.04k
        if (!die->di_cu_context) {
160
0
            _dwarf_error(dbg, error, DW_DLE_DIE_NO_CU_CONTEXT);
161
0
            return DW_DLV_ERROR;
162
0
        }
163
8.04k
        cucontext = die->di_cu_context;
164
        /*  The DW4 ranges base was never used in GNU
165
            but did get emitted, the note says, but
166
            the note is probably obsolete (so, now wrong).
167
            http://llvm.1065342.n5.nabble.com/DebugInfo\
168
            -DW-AT-GNU-ranges-base-in-non-fission-\
169
            td64194.html
170
            HOWEVER: in dw4 GNU fission extension
171
            it is used and matters.
172
            */
173
        /*  ranges_base was merged from tied context.
174
            Otherwise it is zero. But not if
175
            the current die is the skeleton */
176
8.04k
        if (cucontext->cc_unit_type != DW_UT_skeleton) {
177
7.71k
            ranges_base = cucontext->cc_ranges_base;
178
7.71k
        }
179
8.04k
        rangesoffset += ranges_base;
180
8.04k
        address_size = cucontext->cc_address_size;
181
8.04k
    } else {
182
        /*  Printing by raw offset
183
            The caller will use the bytecount to
184
            increment to the next part of .debug_ranges
185
            and will call again with the next offset */
186
0
    }
187
188
8.04k
    localdbg = dbg;
189
8.04k
    res = _dwarf_load_section(localdbg, &localdbg->de_debug_ranges,
190
8.04k
        error);
191
8.04k
    if (res == DW_DLV_ERROR) {
192
165
        return res;
193
165
    }
194
    /*  FIX. HAS_TIED or ? */
195
7.87k
    if (res == DW_DLV_NO_ENTRY) {
196
        /* data is in a.out, not dwp */
197
5.34k
        if (!DBG_HAS_SECONDARY(dbg)) {
198
5.34k
            return DW_DLV_NO_ENTRY;
199
5.34k
        }
200
0
        localdbg = dbg->de_secondary_dbg;
201
0
        res = _dwarf_load_section(localdbg,
202
0
            &localdbg->de_debug_ranges, error);
203
0
        if (res == DW_DLV_ERROR) {
204
            /*  Error will automatically be put on dbg (main
205
                dbg), not localdbg (tieddbg) as of late 2024. */
206
0
            return res;
207
0
        }
208
0
        if (res == DW_DLV_NO_ENTRY) {
209
0
            return res;
210
0
        }
211
0
    }
212
213
    /*  Be safe in case adding rangesoffset and rangebase
214
        overflows. */
215
2.53k
    if (rangesoffset  >= localdbg->de_debug_ranges.dss_size) {
216
        /* Documented behavior in libdwarf2.1.mm */
217
675
        return DW_DLV_NO_ENTRY;
218
675
    }
219
1.85k
    if (ranges_base >= localdbg->de_debug_ranges.dss_size) {
220
0
        dwarfstring m;
221
222
0
        dwarfstring_constructor(&m);
223
0
        dwarfstring_append_printf_u(&m,
224
0
            "DW_DLE_DEBUG_RANGES_OFFSET_BAD: "
225
0
            " ranges base is 0x%lx ",ranges_base);
226
0
        dwarfstring_append_printf_u(&m,
227
0
            " and section size is 0x%lx.",
228
0
            localdbg->de_debug_ranges.dss_size);
229
0
        _dwarf_error_string(dbg, error,
230
0
            DW_DLE_DEBUG_RANGES_OFFSET_BAD,
231
0
            dwarfstring_string(&m));
232
0
        dwarfstring_destructor(&m);
233
0
        return DW_DLV_ERROR;
234
0
    }
235
1.85k
    if (rangesoffset >= localdbg->de_debug_ranges.dss_size) {
236
0
        dwarfstring m;
237
238
0
        dwarfstring_constructor(&m);
239
0
        dwarfstring_append_printf_u(&m,
240
0
            "DW_DLE_DEBUG_RANGES_OFFSET_BAD: "
241
0
            " ranges base+offset  is 0x%lx ",
242
0
            ranges_base+rangesoffset);
243
0
        dwarfstring_append_printf_u(&m,
244
0
            " and section size is 0x%lx.",
245
0
            localdbg->de_debug_ranges.dss_size);
246
0
        _dwarf_error_string(dbg, error,
247
0
            DW_DLE_DEBUG_RANGES_OFFSET_BAD,
248
0
            dwarfstring_string(&m));
249
0
        dwarfstring_destructor(&m);
250
0
        return DW_DLV_ERROR;
251
0
    }
252
253
    /*  tied address_size must match the dwo address_size */
254
1.85k
    section_end = localdbg->de_debug_ranges.dss_data +
255
1.85k
        localdbg->de_debug_ranges.dss_size;
256
1.85k
    rangeptr = localdbg->de_debug_ranges.dss_data;
257
1.85k
    rangeptr += rangesoffset;
258
1.85k
    beginrangeptr = rangeptr;
259
260
43.8k
    for (;;) {
261
43.8k
        struct ranges_entry * re = 0;
262
263
43.8k
        if (rangeptr == section_end) {
264
406
            break;
265
406
        }
266
43.4k
        if (rangeptr  > section_end) {
267
0
            dwarfstring m;
268
269
0
            free_allocated_ranges(base);
270
0
            dwarfstring_constructor(&m);
271
0
            dwarfstring_append(&m,
272
0
                "DW_DLE_DEBUG_RANGES_OFFSET_BAD: "
273
0
                " ranges pointer ran off the end "
274
0
                "of the  section");
275
0
            _dwarf_error_string(dbg, error,
276
0
                DW_DLE_DEBUG_RANGES_OFFSET_BAD,
277
0
                dwarfstring_string(&m));
278
0
            dwarfstring_destructor(&m);
279
0
            return DW_DLV_ERROR;
280
0
        }
281
43.4k
        re = calloc(1, sizeof(struct ranges_entry));
282
43.4k
        if (!re) {
283
0
            free_allocated_ranges(base);
284
0
            _dwarf_error(dbg, error, DW_DLE_DEBUG_RANGES_OUT_OF_MEM);
285
0
            return DW_DLV_ERROR;
286
0
        }
287
43.4k
        if ((rangeptr + (2*address_size)) > section_end) {
288
373
            free(re);
289
373
            free_allocated_ranges(base);
290
373
            _dwarf_error_string(dbg, error,
291
373
                DW_DLE_DEBUG_RANGES_OFFSET_BAD,
292
373
                "DW_DLE_DEBUG_RANGES_OFFSET_BAD: "
293
373
                " Not at the end of the ranges section "
294
373
                " but there is not enough room in the section "
295
373
                " for the next ranges entry");
296
373
            return  DW_DLV_ERROR;
297
373
        }
298
43.1k
        entry_count++;
299
43.1k
        res = read_unaligned_addr_check(localdbg,&re->cur.dwr_addr1,
300
43.1k
            rangeptr, address_size,error,section_end);
301
43.1k
        if (res != DW_DLV_OK) {
302
0
            free(re);
303
0
            free_allocated_ranges(base);
304
0
            return res;
305
0
        }
306
43.1k
        rangeptr +=  address_size;
307
43.1k
        res = read_unaligned_addr_check(localdbg,&re->cur.dwr_addr2,
308
43.1k
            rangeptr, address_size,error,section_end);
309
43.1k
        if (res != DW_DLV_OK) {
310
0
            free(re);
311
0
            free_allocated_ranges(base);
312
0
            return res;
313
0
        }
314
43.1k
        rangeptr +=  address_size;
315
43.1k
        if (!base) {
316
1.76k
            base = re;
317
1.76k
            last = re;
318
41.3k
        } else {
319
41.3k
            last->next = re;
320
41.3k
            last = re;
321
41.3k
        }
322
43.1k
        if (re->cur.dwr_addr1 == 0 && re->cur.dwr_addr2 == 0) {
323
1.08k
            re->cur.dwr_type =  DW_RANGES_END;
324
1.08k
            break;
325
42.0k
        } else if (re->cur.dwr_addr1 == MAX_ADDR) {
326
1.38k
            re->cur.dwr_type =  DW_RANGES_ADDRESS_SELECTION;
327
40.6k
        } else {
328
40.6k
            re->cur.dwr_type =  DW_RANGES_ENTRY;
329
40.6k
        }
330
43.1k
    }
331
332
    /* We return ranges on dbg, so use that to allocate. */
333
1.48k
    ranges_data_out =   (Dwarf_Ranges *)
334
1.48k
        _dwarf_get_alloc(dbg,DW_DLA_RANGES,entry_count);
335
1.48k
    if (!ranges_data_out) {
336
        /* Error, apply to original, not local dbg. */
337
0
        free_allocated_ranges(base);
338
0
        _dwarf_error(dbg, error, DW_DLE_DEBUG_RANGES_OUT_OF_MEM);
339
0
        return DW_DLV_ERROR;
340
0
    }
341
1.48k
    curre = base;
342
1.48k
    *rangesbuf = ranges_data_out;
343
1.48k
    *listlen = entry_count;
344
31.9k
    for (copyindex = 0; curre && (copyindex < entry_count);
345
30.5k
        ++copyindex,++ranges_data_out,curre=curre->next) {
346
30.5k
        *ranges_data_out = curre->cur;
347
30.5k
    }
348
    /* ASSERT: curre == NULL */
349
1.48k
    free_allocated_ranges(base);
350
1.48k
    base = 0;
351
    /* Callers will often not care about the bytes used. */
352
1.48k
    if (actual_offset) {
353
1.48k
        *actual_offset = rangesoffset;
354
1.48k
    }
355
1.48k
    if (bytecount) {
356
1.48k
        *bytecount = rangeptr - beginrangeptr;
357
1.48k
    }
358
1.48k
    return DW_DLV_OK;
359
1.48k
}
360
361
void
362
dwarf_dealloc_ranges(Dwarf_Debug dbg, Dwarf_Ranges * rangesbuf,
363
    Dwarf_Signed rangecount)
364
1.48k
{
365
1.48k
    (void)rangecount;
366
1.48k
    dwarf_dealloc(dbg,rangesbuf, DW_DLA_RANGES);
367
1.48k
}
368
369
/*  In the past, determined DIE base_address,
370
    but that was wrong.
371
    Only a CU_die DW_AT_low_pc can provide
372
    a CU-wide base address and that is done when a CU is first
373
    read, and available as cucontext->cc_base_address
374
    and cc_base_address_present.  */
375
static int
376
_dwarf_determine_die_range_offset(Dwarf_Debug dw_dbg,
377
    Dwarf_Die       dw_die,
378
    Dwarf_Bool     *have_die_ranges_offset,
379
    Dwarf_Unsigned *die_ranges_offset,
380
    Dwarf_Bool     *have_die_base_addr,
381
    Dwarf_Unsigned *die_base_addr,
382
    Dwarf_Error    *dw_error)
383
0
{
384
0
    Dwarf_Bool       hasatranges = FALSE;
385
0
    Dwarf_Attribute  attr = 0;
386
0
    Dwarf_Unsigned   rangeoffset_local = 0;
387
0
    int              res = 0;
388
0
    Dwarf_CU_Context cucon = 0;
389
390
0
    res = dwarf_hasattr(dw_die,DW_AT_ranges, &hasatranges,dw_error);
391
0
    if (res != DW_DLV_OK) {
392
0
        return res;
393
0
    }
394
0
    if (!hasatranges) {
395
        /* Give up, this die not directly relevant. */
396
0
        return res;
397
0
    }
398
0
    res = dwarf_attr(dw_die,DW_AT_ranges,&attr,dw_error);
399
0
    if (res != DW_DLV_OK) {
400
0
        if (res == DW_DLV_ERROR) {
401
0
            dwarf_dealloc_error(dw_dbg,*dw_error);
402
0
            *dw_error = 0;
403
0
        }
404
0
        return res;
405
0
    }
406
0
    res = dwarf_global_formref(attr,
407
0
        &rangeoffset_local, dw_error);
408
0
    if (res != DW_DLV_OK) {
409
0
        if (res == DW_DLV_ERROR) {
410
0
            dwarf_dealloc_attribute(attr);
411
0
            dwarf_dealloc_error(dw_dbg,*dw_error);
412
0
            *dw_error = 0;
413
0
            return res;
414
0
        }
415
0
    }
416
0
    cucon = dw_die->di_cu_context;
417
0
    if (cucon->cc_base_address_present) {
418
0
        *die_base_addr = cucon->cc_base_address;
419
0
        *have_die_base_addr = TRUE;
420
0
    }
421
    /*  rangeoffset_local was set . */
422
0
    dwarf_dealloc_attribute(attr);
423
0
    attr = 0;
424
0
    *have_die_ranges_offset = TRUE;
425
0
    *die_ranges_offset = rangeoffset_local;
426
0
    return DW_DLV_OK;
427
0
}
428
429
/*  Must not ever return DW_DLV_NO_ENTRY.
430
    Uses cu_context sometimes, so the base address
431
    is from the CU_DIE of the CU that
432
    dw_die is a child of.
433
    We attempt to cover what compilers actually do
434
    in the GNU dwarf4 extensions, but rules are not
435
    fully documented and it is difficult to be
436
    sure what is fully correct. */
437
int
438
dwarf_get_ranges_baseaddress(Dwarf_Debug dw_dbg,
439
    Dwarf_Die       dw_die,
440
    Dwarf_Bool     *dw_known_base,
441
    Dwarf_Unsigned *dw_baseaddress,
442
    Dwarf_Bool     *dw_at_ranges_offset_present,
443
    Dwarf_Unsigned *dw_at_ranges_offset,
444
    Dwarf_Error    *dw_error)
445
0
{
446
0
    Dwarf_CU_Context context = 0;
447
0
    Dwarf_Bool     have_die_ranges_offset = FALSE;
448
0
    Dwarf_Unsigned die_ranges_offset = 0;
449
0
    Dwarf_Bool     have_die_base_addr = FALSE;
450
0
    Dwarf_Unsigned die_base_addr = 0;
451
0
    int            res = 0;
452
453
0
    CHECK_DBG(dw_dbg,dw_error,"dwarf_get_ranges_baseaddress()");
454
0
    if (!dw_die) {
455
0
        if (dw_known_base) {
456
0
            *dw_known_base = FALSE;
457
0
            *dw_at_ranges_offset_present = FALSE;
458
0
        }
459
0
        if (dw_baseaddress) {
460
0
            *dw_baseaddress = 0;
461
0
            *dw_at_ranges_offset = 0;
462
0
        }
463
0
        return DW_DLV_OK;
464
0
    }
465
    /*  If the DIE passed in has a DW_AT_ranges attribute
466
        we use that DIE ranges offset.  */
467
0
    res = _dwarf_determine_die_range_offset(dw_dbg,
468
0
        dw_die,&have_die_ranges_offset,&die_ranges_offset,
469
0
        &have_die_base_addr,&die_base_addr,dw_error);
470
0
    if (res != DW_DLV_OK ) {
471
0
        if (res == DW_DLV_ERROR) {
472
            /*  Suppressing knowledge of any error */
473
0
            dwarf_dealloc_error(dw_dbg,*dw_error);
474
0
            *dw_error = 0;
475
0
        }
476
0
    }
477
0
    context = dw_die->di_cu_context;
478
0
    if (!context) {
479
0
        _dwarf_error_string(dw_dbg, dw_error,
480
0
            DW_DLE_DIE_NO_CU_CONTEXT,
481
0
            "DW_DLE_DIE_NO_CU_CONTEXT: in a call to "
482
0
            "dwarf_get_ranges_baseaddress");
483
0
        return DW_DLV_ERROR;
484
0
    }
485
0
    if (dw_at_ranges_offset) {
486
0
        *dw_at_ranges_offset = die_ranges_offset;
487
0
    }
488
0
    if (dw_at_ranges_offset_present) {
489
0
        *dw_at_ranges_offset_present = have_die_ranges_offset;
490
0
    }
491
0
    if (context->cc_base_address_present) {
492
0
        *dw_baseaddress = context->cc_base_address;
493
0
        *dw_known_base = context->cc_base_address_present;
494
0
    }
495
0
    return DW_DLV_OK;
496
0
}