Coverage Report

Created: 2025-07-11 06:46

/src/elfutils/libdw/libdw_findcu.c
Line
Count
Source (jump to first uncovered line)
1
/* Find CU for given offset.
2
   Copyright (C) 2003-2010, 2014, 2016, 2017, 2018 Red Hat, Inc.
3
   This file is part of elfutils.
4
   Written by Ulrich Drepper <drepper@redhat.com>, 2003.
5
6
   This file is free software; you can redistribute it and/or modify
7
   it under the terms of either
8
9
     * the GNU Lesser General Public License as published by the Free
10
       Software Foundation; either version 3 of the License, or (at
11
       your option) any later version
12
13
   or
14
15
     * the GNU General Public License as published by the Free
16
       Software Foundation; either version 2 of the License, or (at
17
       your option) any later version
18
19
   or both in parallel, as here.
20
21
   elfutils is distributed in the hope that it will be useful, but
22
   WITHOUT ANY WARRANTY; without even the implied warranty of
23
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24
   General Public License for more details.
25
26
   You should have received copies of the GNU General Public License and
27
   the GNU Lesser General Public License along with this program.  If
28
   not, see <http://www.gnu.org/licenses/>.  */
29
30
#ifdef HAVE_CONFIG_H
31
# include <config.h>
32
#endif
33
34
#include <assert.h>
35
#include "eu-search.h"
36
#include "libdwP.h"
37
38
static int
39
findcu_cb (const void *arg1, const void *arg2)
40
0
{
41
0
  struct Dwarf_CU *cu1 = (struct Dwarf_CU *) arg1;
42
0
  struct Dwarf_CU *cu2 = (struct Dwarf_CU *) arg2;
43
44
  /* Find out which of the two arguments is the search value.  It has
45
     end offset 0.  */
46
0
  if (cu1->end == 0)
47
0
    {
48
0
      if (cu1->start < cu2->start)
49
0
  return -1;
50
0
      if (cu1->start >= cu2->end)
51
0
  return 1;
52
0
    }
53
0
  else
54
0
    {
55
0
      if (cu2->start < cu1->start)
56
0
  return 1;
57
0
      if (cu2->start >= cu1->end)
58
0
  return -1;
59
0
    }
60
61
0
  return 0;
62
0
}
63
64
int
65
__libdw_finddbg_cb (const void *arg1, const void *arg2)
66
0
{
67
0
  Dwarf *dbg1 = (Dwarf *) arg1;
68
0
  Dwarf *dbg2 = (Dwarf *) arg2;
69
70
0
  Elf_Data *dbg1_data = dbg1->sectiondata[IDX_debug_info];
71
0
  unsigned char *dbg1_start = dbg1_data->d_buf;
72
0
  size_t dbg1_size = dbg1_data->d_size;
73
74
0
  Elf_Data *dbg2_data = dbg2->sectiondata[IDX_debug_info];
75
0
  unsigned char *dbg2_start = dbg2_data->d_buf;
76
0
  size_t dbg2_size = dbg2_data->d_size;
77
78
  /* Find out which of the two arguments is the search value.  It has
79
     a size of 0.  */
80
0
  if (dbg1_size == 0)
81
0
    {
82
0
      if (dbg1_start < dbg2_start)
83
0
  return -1;
84
0
      if (dbg1_start >= dbg2_start + dbg2_size)
85
0
  return 1;
86
0
    }
87
0
  else
88
0
    {
89
0
      if (dbg2_start < dbg1_start)
90
0
  return 1;
91
0
      if (dbg2_start >= dbg1_start + dbg1_size)
92
0
  return -1;
93
0
    }
94
95
0
  return 0;
96
0
}
97
98
struct Dwarf_CU *
99
internal_function
100
__libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
101
0
{
102
0
  Dwarf_Off *const offsetp
103
0
    = debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset;
104
0
  search_tree *tree = debug_types ? &dbg->tu_tree : &dbg->cu_tree;
105
106
0
  Dwarf_Off oldoff = *offsetp;
107
0
  uint16_t version;
108
0
  uint8_t unit_type;
109
0
  uint8_t address_size;
110
0
  uint8_t offset_size;
111
0
  Dwarf_Off abbrev_offset;
112
0
  uint64_t unit_id8;
113
0
  Dwarf_Off subdie_offset;
114
115
0
  if (__libdw_next_unit (dbg, debug_types, oldoff, offsetp, NULL,
116
0
       &version, &unit_type, &abbrev_offset,
117
0
       &address_size, &offset_size,
118
0
       &unit_id8, &subdie_offset) != 0)
119
    /* No more entries.  */
120
0
    return NULL;
121
122
  /* We only know how to handle the DWARF version 2 through 5 formats.
123
     For v4 debug types we only handle version 4.  */
124
0
  if (unlikely (version < 2) || unlikely (version > 5)
125
0
      || (debug_types && unlikely (version != 4)))
126
0
    {
127
0
      __libdw_seterrno (DWARF_E_VERSION);
128
0
      return NULL;
129
0
    }
130
131
  /* We only handle 32 or 64 bit (4 or 8 byte) addresses and offsets.
132
     Just assume we are dealing with 64bit in case the size is "unknown".
133
     Too much code assumes if it isn't 4 then it is 8 (or the other way
134
     around).  */
135
0
  if (unlikely (address_size != 4 && address_size != 8))
136
0
    address_size = 8;
137
0
  if (unlikely (offset_size != 4 && offset_size != 8))
138
0
    offset_size = 8;
139
140
  /* Invalid or truncated debug section data?  */
141
0
  size_t sec_idx = debug_types ? IDX_debug_types : IDX_debug_info;
142
0
  Elf_Data *data = dbg->sectiondata[sec_idx];
143
0
  if (unlikely (*offsetp > data->d_size))
144
0
    *offsetp = data->d_size;
145
146
0
  uint32_t dwp_row;
147
0
  Dwarf_Off dwp_abbrev_offset;
148
0
  if (__libdw_dwp_find_unit (dbg, debug_types, oldoff, version, unit_type,
149
0
           unit_id8, &dwp_row, &dwp_abbrev_offset) != 0)
150
0
    return NULL;
151
0
  abbrev_offset += dwp_abbrev_offset;
152
153
  /* Create an entry for this CU.  */
154
0
  struct Dwarf_CU *newp = libdw_typed_alloc (dbg, struct Dwarf_CU);
155
156
0
  newp->dbg = dbg;
157
0
  newp->sec_idx = sec_idx;
158
0
  newp->start = oldoff;
159
0
  newp->end = *offsetp;
160
0
  newp->dwp_row = dwp_row;
161
0
  newp->address_size = address_size;
162
0
  newp->offset_size = offset_size;
163
0
  newp->version = version;
164
0
  newp->unit_id8 = unit_id8;
165
0
  newp->subdie_offset = subdie_offset;
166
0
  Dwarf_Abbrev_Hash_init (&newp->abbrev_hash, 41);
167
0
  newp->orig_abbrev_offset = newp->last_abbrev_offset = abbrev_offset;
168
0
  newp->files = NULL;
169
0
  newp->lines = NULL;
170
0
  newp->split = (Dwarf_CU *) -1;
171
0
  newp->base_address = (Dwarf_Addr) -1;
172
0
  newp->addr_base = (Dwarf_Off) -1;
173
0
  newp->str_off_base = (Dwarf_Off) -1;
174
0
  newp->ranges_base = (Dwarf_Off) -1;
175
0
  newp->locs_base = (Dwarf_Off) -1;
176
177
0
  newp->startp = data->d_buf + newp->start;
178
0
  newp->endp = data->d_buf + newp->end;
179
0
  eu_search_tree_init (&newp->locs_tree);
180
0
  rwlock_init (newp->abbrev_lock);
181
0
  rwlock_init (newp->split_lock);
182
0
  mutex_init (newp->src_lock);
183
0
  mutex_init (newp->str_off_base_lock);
184
0
  mutex_init (newp->intern_lock);
185
186
  /* v4 debug type units have version == 4 and unit_type == DW_UT_type.  */
187
0
  if (debug_types)
188
0
    newp->unit_type = DW_UT_type;
189
0
  else if (version < 5)
190
0
    {
191
      /* This is a reasonable guess (and needed to get the CUDIE).  */
192
0
      newp->unit_type = DW_UT_compile;
193
194
      /* But set it correctly from the actual CUDIE tag.  */
195
0
      Dwarf_Die cudie = CUDIE (newp);
196
0
      int tag = INTUSE(dwarf_tag) (&cudie);
197
0
      if (tag == DW_TAG_compile_unit)
198
0
  {
199
0
    Dwarf_Attribute dwo_id;
200
0
    if (INTUSE(dwarf_attr) (&cudie, DW_AT_GNU_dwo_id, &dwo_id) != NULL)
201
0
      {
202
0
        Dwarf_Word id8;
203
0
        if (INTUSE(dwarf_formudata) (&dwo_id, &id8) == 0)
204
0
    {
205
0
      if (INTUSE(dwarf_haschildren) (&cudie) == 0
206
0
          && INTUSE(dwarf_hasattr) (&cudie,
207
0
            DW_AT_GNU_dwo_name) == 1)
208
0
        newp->unit_type = DW_UT_skeleton;
209
0
      else
210
0
        newp->unit_type = DW_UT_split_compile;
211
212
0
      newp->unit_id8 = id8;
213
0
    }
214
0
      }
215
0
  }
216
0
      else if (tag == DW_TAG_partial_unit)
217
0
  newp->unit_type = DW_UT_partial;
218
0
      else if (tag == DW_TAG_type_unit)
219
0
  newp->unit_type = DW_UT_type;
220
0
    }
221
0
  else
222
0
    newp->unit_type = unit_type;
223
224
  /* Store a reference to any type unit ids in the hash for quick lookup.  */
225
0
  if (unit_type == DW_UT_type || unit_type == DW_UT_split_type)
226
0
    Dwarf_Sig8_Hash_insert (&dbg->sig8_hash, unit_id8, newp);
227
228
  /* Add the new entry to the search tree.  */
229
0
  if (eu_tsearch (newp, tree, findcu_cb) == NULL)
230
0
    {
231
      /* Something went wrong.  Undo the operation.  */
232
0
      *offsetp = oldoff;
233
0
      __libdw_seterrno (DWARF_E_NOMEM);
234
0
      return NULL;
235
0
    }
236
237
0
  return newp;
238
0
}
239
240
struct Dwarf_CU *
241
internal_function
242
__libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool v4_debug_types)
243
0
{
244
0
  search_tree *tree = v4_debug_types ? &dbg->tu_tree : &dbg->cu_tree;
245
0
  Dwarf_Off *next_offset
246
0
    = v4_debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset;
247
248
  /* Maybe we already know that CU.  */
249
0
  struct Dwarf_CU fake = { .start = start, .end = 0 };
250
0
  struct Dwarf_CU **found = eu_tfind (&fake, tree, findcu_cb);
251
0
  struct Dwarf_CU *result = NULL;
252
0
  if (found != NULL)
253
0
    return *found;
254
255
0
  mutex_lock (dbg->dwarf_lock);
256
257
0
  found = eu_tfind (&fake, tree, findcu_cb);
258
0
  if (found != NULL)
259
0
    {
260
0
      mutex_unlock (dbg->dwarf_lock);
261
0
      return *found;
262
0
    }
263
264
0
  if (start < *next_offset)
265
0
    __libdw_seterrno (DWARF_E_INVALID_DWARF);
266
0
  else
267
0
    {
268
      /* No.  Then read more CUs.  */
269
0
      while (1)
270
0
  {
271
0
    struct Dwarf_CU *newp
272
0
      = __libdw_intern_next_unit (dbg, v4_debug_types);
273
274
0
    if (newp == NULL)
275
0
      {
276
0
        result = NULL;
277
0
        break;
278
0
      }
279
280
    /* Is this the one we are looking for?  */
281
0
    if (start < *next_offset || start == newp->start)
282
0
      {
283
0
        result = newp;
284
0
        break;
285
0
      }
286
0
  }
287
0
    }
288
289
0
  mutex_unlock (dbg->dwarf_lock);
290
0
  return result;
291
0
}
292
293
struct Dwarf_CU *
294
internal_function
295
__libdw_findcu_addr (Dwarf *dbg, void *addr)
296
0
{
297
0
  search_tree *tree;
298
0
  Dwarf_Off start;
299
0
  if (addr >= dbg->sectiondata[IDX_debug_info]->d_buf
300
0
      && addr < (dbg->sectiondata[IDX_debug_info]->d_buf
301
0
     + dbg->sectiondata[IDX_debug_info]->d_size))
302
0
    {
303
0
      tree = &dbg->cu_tree;
304
0
      start = addr - dbg->sectiondata[IDX_debug_info]->d_buf;
305
0
    }
306
0
  else if (dbg->sectiondata[IDX_debug_types] != NULL
307
0
     && addr >= dbg->sectiondata[IDX_debug_types]->d_buf
308
0
     && addr < (dbg->sectiondata[IDX_debug_types]->d_buf
309
0
          + dbg->sectiondata[IDX_debug_types]->d_size))
310
0
    {
311
0
      tree = &dbg->tu_tree;
312
0
      start = addr - dbg->sectiondata[IDX_debug_types]->d_buf;
313
0
    }
314
0
  else
315
0
    return NULL;
316
317
0
  struct Dwarf_CU fake = { .start = start, .end = 0 };
318
0
  struct Dwarf_CU **found = eu_tfind (&fake, tree, findcu_cb);
319
320
0
  if (found != NULL)
321
0
    return *found;
322
323
0
  return NULL;
324
0
}
325
326
Dwarf *
327
internal_function
328
__libdw_find_split_dbg_addr (Dwarf *dbg, void *addr)
329
0
{
330
  /* XXX Assumes split DWARF only has CUs in main IDX_debug_info.  */
331
0
  Elf_Data fake_data = { .d_buf = addr, .d_size = 0 };
332
0
  Dwarf fake = { .sectiondata[IDX_debug_info] = &fake_data };
333
0
  Dwarf **found = eu_tfind (&fake, &dbg->split_tree, __libdw_finddbg_cb);
334
335
0
  if (found != NULL)
336
0
    return *found;
337
338
0
  return NULL;
339
0
}