1 /* Keeping track of DWARF compilation units in libdwfl.
2 Copyright (C) 2005 Red Hat, Inc.
4 This program is Open Source software; you can redistribute it and/or
5 modify it under the terms of the Open Software License version 1.0 as
6 published by the Open Source Initiative.
8 You should have received a copy of the Open Software License along
9 with this program; if not, you may obtain a copy of the Open Software
10 License version 1.0 from http://www.opensource.org/licenses/osl.php or
11 by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
12 3001 King Ranch Road, Ukiah, CA 95482. */
15 #include "../libdw/libdwP.h"
16 #include "../libdw/memory-access.h"
20 static inline Dwarf_Arange *
21 dwar (Dwfl_Module *mod, unsigned int idx)
23 return &mod->dw->aranges->info[mod->aranges[idx].arange];
28 addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange)
30 if (mod->aranges == NULL)
32 Dwarf_Aranges *dwaranges;
33 if (INTUSE(dwarf_getaranges) (mod->dw, &dwaranges, NULL) != 0)
36 struct dwfl_arange *aranges = malloc (dwaranges->naranges
38 if (unlikely (aranges == NULL))
41 /* libdw has sorted its list by address, which is how we want it.
42 But the sorted list is full of not-quite-contiguous runs pointing
43 to the same CU. We don't care about the little gaps inside the
44 module, we'll consider them part of the surrounding CU anyway.
45 Collect our own array with just one record for each run of ranges
46 pointing to one CU. */
50 for (size_t i = 0; i < dwaranges->naranges; ++i)
51 if (i == 0 || dwaranges->info[i].offset != lastcu)
53 aranges[naranges].arange = i;
54 aranges[naranges].cu = NULL;
56 lastcu = dwaranges->info[i].offset;
59 /* Store the final array, which is probably much smaller than before. */
60 mod->naranges = naranges;
61 mod->aranges = (realloc (aranges, naranges * sizeof aranges[0])
63 mod->lazycu += naranges;
66 /* The address must be inside the module to begin with. */
67 addr -= mod->debug.bias;
69 /* The ranges are sorted by address, so we can use binary search. */
70 size_t l = 0, u = mod->naranges;
73 size_t idx = (l + u) / 2;
74 Dwarf_Addr start = dwar (mod, idx)->addr;
80 else if (addr > start)
82 if (idx + 1 < mod->naranges)
84 if (addr >= dwar (mod, idx + 1)->addr)
92 /* It might be in the last range. */
93 const Dwarf_Arange *last
94 = &mod->dw->aranges->info[mod->dw->aranges->naranges - 1];
95 if (addr > last->addr + last->length)
100 *arange = &mod->aranges[idx];
101 return DWFL_E_NOERROR;
104 return DWFL_E_ADDR_OUTOFRANGE;
111 struct dwfl_cu *cu = arg;
112 if (cu == (void *) -1l)
115 assert (cu->mod->lazycu == 0);
118 /* One reason fewer to keep the lazy lookup table for CUs. */
120 less_lazy (Dwfl_Module *mod)
122 if (--mod->lazycu > 0)
125 /* We know about all the CUs now, we don't need this table. */
126 tdestroy (mod->lazy_cu_root, nofree);
127 mod->lazy_cu_root = NULL;
130 static inline Dwarf_Off
131 cudie_offset (const struct dwfl_cu *cu)
133 return cu->die.cu->start + 3 * cu->die.cu->offset_size - 4 + 3;
137 compare_cukey (const void *a, const void *b)
139 return cudie_offset (a) - cudie_offset (b);
142 /* Intern the CU if necessary. */
144 intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result)
146 struct Dwarf_CU dwkey;
149 dwkey.offset_size = 0;
150 dwkey.start = cuoff - (3 * 0 - 4 + 3);
151 struct dwfl_cu **found = tsearch (&key, &mod->lazy_cu_root, &compare_cukey);
152 if (unlikely (found == NULL))
155 if (*found == &key || *found == NULL)
157 if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size))
159 /* This is the EOF marker. Now we have interned all the CUs.
160 One increment in MOD->lazycu counts not having hit EOF yet. */
161 *found = (void *) -1l;
166 /* This is a new entry, meaning we haven't looked at this CU. */
170 struct dwfl_cu *cu = malloc (sizeof *cu);
171 if (unlikely (cu == NULL))
178 /* XXX use non-searching lookup */
179 Dwarf_Die *die = INTUSE(dwarf_offdie) (mod->dw, cuoff, &cu->die);
182 assert (die == &cu->die);
184 struct dwfl_cu **newvec = realloc (mod->cu, ((mod->ncu + 1)
185 * sizeof (mod->cu[0])));
193 mod->cu[mod->ncu++] = cu;
194 if (cu->die.cu->start == 0)
202 return DWFL_E_NOERROR;
206 /* Traverse all the CUs in the module. */
209 internal_function_def
210 __libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu,
214 struct dwfl_cu **nextp;
218 /* Start the traversal. */
220 nextp = &mod->first_cu;
224 /* Continue following LASTCU. */
225 cuoff = lastcu->die.cu->end;
226 nextp = &lastcu->next;
233 int end = INTUSE(dwarf_nextcu) (mod->dw, cuoff, &nextoff, &cuhdrsz,
240 return DWFL_E_NOERROR;
243 Dwfl_Error result = intern_cu (mod, cuoff + cuhdrsz, nextp);
244 if (result != DWFL_E_NOERROR)
247 if ((*nextp)->next == NULL && nextoff == (Dwarf_Off) -1l)
248 (*nextp)->next = (void *) -1l;
251 *cu = *nextp == (void *) -1l ? NULL : *nextp;
252 return DWFL_E_NOERROR;
256 /* Intern the CU arange points to, if necessary. */
259 arangecu (Dwfl_Module *mod, struct dwfl_arange *arange, struct dwfl_cu **cu)
261 if (arange->cu == NULL)
263 const Dwarf_Arange *dwarange = &mod->dw->aranges->info[arange->arange];
264 Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu);
265 if (result != DWFL_E_NOERROR)
267 assert (arange->cu != NULL && arange->cu != (void *) -1l);
268 less_lazy (mod); /* Each arange with null ->cu counts once. */
272 return DWFL_E_NOERROR;
276 internal_function_def
277 __libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_cu **cu)
279 struct dwfl_arange *arange;
280 return addrarange (mod, addr, &arange) ?: arangecu (mod, arange, cu);