1 /* Keeping track of DWARF compilation units in libdwfl.
2 Copyright (C) 2005-2010 Red Hat, Inc.
3 This file is part of elfutils.
5 This file is free software; you can redistribute it and/or modify
6 it under the terms of either
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
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
18 or both in parallel, as here.
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.
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/>. */
30 #include "../libdw/libdwP.h"
31 #include "../libdw/memory-access.h"
35 static inline Dwarf_Arange *
36 dwar (Dwfl_Module *mod, unsigned int idx)
38 return &mod->dw->aranges->info[mod->aranges[idx].arange];
43 addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange)
45 if (mod->aranges == NULL)
47 struct dwfl_arange *aranges = NULL;
48 Dwarf_Aranges *dwaranges = NULL;
50 if (INTUSE(dwarf_getaranges) (mod->dw, &dwaranges, &naranges) != 0)
53 /* If the module has no aranges (when no code is included) we
57 aranges = malloc (naranges * sizeof *aranges);
58 if (unlikely (aranges == NULL))
61 /* libdw has sorted its list by address, which is how we want it.
62 But the sorted list is full of not-quite-contiguous runs pointing
63 to the same CU. We don't care about the little gaps inside the
64 module, we'll consider them part of the surrounding CU anyway.
65 Collect our own array with just one record for each run of ranges
66 pointing to one CU. */
70 for (size_t i = 0; i < dwaranges->naranges; ++i)
71 if (i == 0 || dwaranges->info[i].offset != lastcu)
73 aranges[naranges].arange = i;
74 aranges[naranges].cu = NULL;
76 lastcu = dwaranges->info[i].offset;
80 /* Store the final array, which is probably much smaller than before. */
81 mod->naranges = naranges;
82 mod->aranges = (realloc (aranges, naranges * sizeof aranges[0])
84 mod->lazycu += naranges;
87 /* The address must be inside the module to begin with. */
88 addr = dwfl_deadjust_dwarf_addr (mod, addr);
90 /* The ranges are sorted by address, so we can use binary search. */
91 size_t l = 0, u = mod->naranges;
94 size_t idx = (l + u) / 2;
95 Dwarf_Addr start = dwar (mod, idx)->addr;
101 else if (addr > start)
103 if (idx + 1 < mod->naranges)
105 if (addr >= dwar (mod, idx + 1)->addr)
113 /* It might be in the last range. */
114 const Dwarf_Arange *last
115 = &mod->dw->aranges->info[mod->dw->aranges->naranges - 1];
116 if (addr > last->addr + last->length)
121 *arange = &mod->aranges[idx];
122 return DWFL_E_NOERROR;
125 return DWFL_E_ADDR_OUTOFRANGE;
132 struct dwfl_cu *cu = arg;
133 if (cu == (void *) -1l)
136 assert (cu->mod->lazycu == 0);
139 /* One reason fewer to keep the lazy lookup table for CUs. */
141 less_lazy (Dwfl_Module *mod)
143 if (--mod->lazycu > 0)
146 /* We know about all the CUs now, we don't need this table. */
147 tdestroy (mod->lazy_cu_root, nofree);
148 mod->lazy_cu_root = NULL;
151 static inline Dwarf_Off
152 cudie_offset (const struct dwfl_cu *cu)
154 return DIE_OFFSET_FROM_CU_OFFSET (cu->die.cu->start, cu->die.cu->offset_size,
155 cu->die.cu->type_sig8 != 0);
159 compare_cukey (const void *a, const void *b)
161 return cudie_offset (a) - cudie_offset (b);
164 /* Intern the CU if necessary. */
166 intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result)
168 struct Dwarf_CU dwkey;
171 dwkey.offset_size = 0;
172 dwkey.start = cuoff - (3 * 0 - 4 + 3);
173 struct dwfl_cu **found = tsearch (&key, &mod->lazy_cu_root, &compare_cukey);
174 if (unlikely (found == NULL))
177 if (*found == &key || *found == NULL)
179 if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size))
181 /* This is the EOF marker. Now we have interned all the CUs.
182 One increment in MOD->lazycu counts not having hit EOF yet. */
183 *found = (void *) -1l;
188 /* This is a new entry, meaning we haven't looked at this CU. */
192 struct dwfl_cu *cu = malloc (sizeof *cu);
193 if (unlikely (cu == NULL))
200 /* XXX use non-searching lookup */
201 Dwarf_Die *die = INTUSE(dwarf_offdie) (mod->dw, cuoff, &cu->die);
204 assert (die == &cu->die);
206 struct dwfl_cu **newvec = realloc (mod->cu, ((mod->ncu + 1)
207 * sizeof (mod->cu[0])));
215 mod->cu[mod->ncu++] = cu;
216 if (cu->die.cu->start == 0)
224 return DWFL_E_NOERROR;
228 /* Traverse all the CUs in the module. */
232 __libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu,
236 struct dwfl_cu **nextp;
240 /* Start the traversal. */
242 nextp = &mod->first_cu;
246 /* Continue following LASTCU. */
247 cuoff = lastcu->die.cu->end;
248 nextp = &lastcu->next;
255 int end = INTUSE(dwarf_nextcu) (mod->dw, cuoff, &nextoff, &cuhdrsz,
262 return DWFL_E_NOERROR;
265 Dwfl_Error result = intern_cu (mod, cuoff + cuhdrsz, nextp);
266 if (result != DWFL_E_NOERROR)
269 if ((*nextp)->next == NULL && nextoff == (Dwarf_Off) -1l)
270 (*nextp)->next = (void *) -1l;
273 *cu = *nextp == (void *) -1l ? NULL : *nextp;
274 return DWFL_E_NOERROR;
278 /* Intern the CU arange points to, if necessary. */
281 arangecu (Dwfl_Module *mod, struct dwfl_arange *arange, struct dwfl_cu **cu)
283 if (arange->cu == NULL)
285 const Dwarf_Arange *dwarange = &mod->dw->aranges->info[arange->arange];
286 Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu);
287 if (result != DWFL_E_NOERROR)
289 assert (arange->cu != NULL && arange->cu != (void *) -1l);
290 less_lazy (mod); /* Each arange with null ->cu counts once. */
294 return DWFL_E_NOERROR;
299 __libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_cu **cu)
301 struct dwfl_arange *arange;
302 return addrarange (mod, addr, &arange) ?: arangecu (mod, arange, cu);