Line data Source code
1 : /* Keeping track of DWARF compilation units in libdwfl.
2 : Copyright (C) 2005-2010, 2015 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 : #include "libdwflP.h"
30 : #include "../libdw/libdwP.h"
31 : #include "../libdw/memory-access.h"
32 : #include <search.h>
33 :
34 :
35 : static inline Dwarf_Arange *
36 : dwar (Dwfl_Module *mod, unsigned int idx)
37 : {
38 414 : return &mod->dw->aranges->info[mod->aranges[idx].arange];
39 : }
40 :
41 :
42 : static Dwfl_Error
43 345 : addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange)
44 : {
45 345 : if (mod->aranges == NULL)
46 : {
47 58 : struct dwfl_arange *aranges = NULL;
48 58 : Dwarf_Aranges *dwaranges = NULL;
49 : size_t naranges;
50 58 : if (INTUSE(dwarf_getaranges) (mod->dw, &dwaranges, &naranges) != 0)
51 0 : return DWFL_E_LIBDW;
52 :
53 : /* If the module has no aranges (when no code is included) we
54 : allocate nothing. */
55 58 : if (naranges != 0)
56 : {
57 54 : aranges = malloc (naranges * sizeof *aranges);
58 54 : if (unlikely (aranges == NULL))
59 : return DWFL_E_NOMEM;
60 :
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. */
67 :
68 54 : naranges = 0;
69 54 : Dwarf_Off lastcu = 0;
70 135 : for (size_t i = 0; i < dwaranges->naranges; ++i)
71 81 : if (i == 0 || dwaranges->info[i].offset != lastcu)
72 : {
73 73 : aranges[naranges].arange = i;
74 73 : aranges[naranges].cu = NULL;
75 73 : ++naranges;
76 73 : lastcu = dwaranges->info[i].offset;
77 : }
78 : }
79 :
80 : /* Store the final array, which is probably much smaller than before. */
81 58 : mod->naranges = naranges;
82 116 : mod->aranges = (realloc (aranges, naranges * sizeof aranges[0])
83 58 : ?: aranges);
84 58 : mod->lazycu += naranges;
85 : }
86 :
87 : /* The address must be inside the module to begin with. */
88 345 : addr = dwfl_deadjust_dwarf_addr (mod, addr);
89 :
90 : /* The ranges are sorted by address, so we can use binary search. */
91 345 : size_t l = 0, u = mod->naranges;
92 728 : while (l < u)
93 : {
94 375 : size_t idx = (l + u) / 2;
95 750 : Dwarf_Addr start = dwar (mod, idx)->addr;
96 375 : if (addr < start)
97 : {
98 0 : u = idx;
99 0 : continue;
100 : }
101 375 : else if (addr > start)
102 : {
103 268 : if (idx + 1 < mod->naranges)
104 : {
105 78 : if (addr >= dwar (mod, idx + 1)->addr)
106 : {
107 38 : l = idx + 1;
108 38 : continue;
109 : }
110 : }
111 : else
112 : {
113 : /* It might be in the last range. */
114 229 : const Dwarf_Arange *last
115 229 : = &mod->dw->aranges->info[mod->dw->aranges->naranges - 1];
116 229 : if (addr > last->addr + last->length)
117 : break;
118 : }
119 : }
120 :
121 337 : *arange = &mod->aranges[idx];
122 337 : return DWFL_E_NOERROR;
123 : }
124 :
125 : return DWFL_E_ADDR_OUTOFRANGE;
126 : }
127 :
128 :
129 : static void
130 0 : nofree (void *arg)
131 : {
132 0 : struct dwfl_cu *cu = arg;
133 0 : if (cu == (void *) -1l)
134 : return;
135 :
136 0 : assert (cu->mod->lazycu == 0);
137 : }
138 :
139 : /* One reason fewer to keep the lazy lookup table for CUs. */
140 : static inline void
141 : less_lazy (Dwfl_Module *mod)
142 : {
143 59 : if (--mod->lazycu > 0)
144 : return;
145 :
146 : /* We know about all the CUs now, we don't need this table. */
147 0 : tdestroy (mod->lazy_cu_root, nofree);
148 0 : mod->lazy_cu_root = NULL;
149 : }
150 :
151 : static inline Dwarf_Off
152 : cudie_offset (const struct dwfl_cu *cu)
153 : {
154 : /* These are real CUs, so there never is a type_sig8. Note
155 : initialization of dwkey.start and offset_size in intern_cu ()
156 : to see why this calculates the same value for both key and
157 : die.cu search items. */
158 128824 : return DIE_OFFSET_FROM_CU_OFFSET (cu->die.cu->start, cu->die.cu->offset_size,
159 : 0);
160 : }
161 :
162 : static int
163 64412 : compare_cukey (const void *a, const void *b)
164 : {
165 128824 : Dwarf_Off a_off = cudie_offset (a);
166 128824 : Dwarf_Off b_off = cudie_offset (b);
167 64412 : return (a_off < b_off) ? -1 : ((a_off > b_off) ? 1 : 0);
168 : }
169 :
170 : /* Intern the CU if necessary. */
171 : static Dwfl_Error
172 5625 : intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result)
173 : {
174 5625 : if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size))
175 : {
176 0 : if (likely (mod->lazycu == 1))
177 : {
178 : /* This is the EOF marker. Now we have interned all the CUs.
179 : One increment in MOD->lazycu counts not having hit EOF yet. */
180 0 : *result = (void *) -1;
181 0 : less_lazy (mod);
182 : return DWFL_E_NOERROR;
183 : }
184 : else
185 : {
186 : /* Unexpected EOF, most likely a bogus aranges. */
187 : return (DWFL_E (LIBDW, DWARF_E_INVALID_DWARF));
188 : }
189 : }
190 :
191 : /* Make sure the cuoff points to a real DIE. */
192 : Dwarf_Die cudie;
193 5625 : Dwarf_Die *die = INTUSE(dwarf_offdie) (mod->dw, cuoff, &cudie);
194 5625 : if (die == NULL)
195 : return DWFL_E_LIBDW;
196 :
197 : struct Dwarf_CU dwkey;
198 : struct dwfl_cu key;
199 5625 : key.die.cu = &dwkey;
200 5625 : dwkey.offset_size = 0;
201 5625 : dwkey.start = cuoff - (3 * 0 - 4 + 3);
202 5625 : struct dwfl_cu **found = tsearch (&key, &mod->lazy_cu_root, &compare_cukey);
203 5625 : if (unlikely (found == NULL))
204 : return DWFL_E_NOMEM;
205 :
206 5625 : if (*found == &key || *found == NULL)
207 : {
208 : /* This is a new entry, meaning we haven't looked at this CU. */
209 :
210 5624 : *found = NULL;
211 :
212 5624 : struct dwfl_cu *cu = malloc (sizeof *cu);
213 5624 : if (unlikely (cu == NULL))
214 : return DWFL_E_NOMEM;
215 :
216 5624 : cu->mod = mod;
217 5624 : cu->next = NULL;
218 5624 : cu->lines = NULL;
219 5624 : cu->die = cudie;
220 :
221 5624 : struct dwfl_cu **newvec = realloc (mod->cu, ((mod->ncu + 1)
222 : * sizeof (mod->cu[0])));
223 5624 : if (newvec == NULL)
224 : {
225 0 : free (cu);
226 0 : return DWFL_E_NOMEM;
227 : }
228 5624 : mod->cu = newvec;
229 :
230 5624 : mod->cu[mod->ncu++] = cu;
231 5624 : if (cu->die.cu->start == 0)
232 97 : mod->first_cu = cu;
233 :
234 5624 : *found = cu;
235 : }
236 :
237 5625 : *result = *found;
238 5625 : return DWFL_E_NOERROR;
239 : }
240 :
241 :
242 : /* Traverse all the CUs in the module. */
243 :
244 : Dwfl_Error
245 : internal_function
246 5634 : __libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu,
247 : struct dwfl_cu **cu)
248 : {
249 : Dwarf_Off cuoff;
250 : struct dwfl_cu **nextp;
251 :
252 5634 : if (lastcu == NULL)
253 : {
254 : /* Start the traversal. */
255 57 : cuoff = 0;
256 57 : nextp = &mod->first_cu;
257 : }
258 : else
259 : {
260 : /* Continue following LASTCU. */
261 5577 : cuoff = lastcu->die.cu->end;
262 5577 : nextp = &lastcu->next;
263 : }
264 :
265 5634 : if (*nextp == NULL)
266 : {
267 : size_t cuhdrsz;
268 : Dwarf_Off nextoff;
269 5623 : int end = INTUSE(dwarf_nextcu) (mod->dw, cuoff, &nextoff, &cuhdrsz,
270 : NULL, NULL, NULL);
271 5623 : if (end < 0)
272 57 : return DWFL_E_LIBDW;
273 5623 : if (end > 0)
274 : {
275 57 : *cu = NULL;
276 57 : return DWFL_E_NOERROR;
277 : }
278 :
279 5566 : Dwfl_Error result = intern_cu (mod, cuoff + cuhdrsz, nextp);
280 5566 : if (result != DWFL_E_NOERROR)
281 : return result;
282 :
283 5566 : if (*nextp != (void *) -1
284 5566 : && (*nextp)->next == NULL && nextoff == (Dwarf_Off) -1l)
285 0 : (*nextp)->next = (void *) -1l;
286 : }
287 :
288 5577 : *cu = *nextp == (void *) -1l ? NULL : *nextp;
289 5577 : return DWFL_E_NOERROR;
290 : }
291 :
292 :
293 : /* Intern the CU arange points to, if necessary. */
294 :
295 : static Dwfl_Error
296 337 : arangecu (Dwfl_Module *mod, struct dwfl_arange *arange, struct dwfl_cu **cu)
297 : {
298 337 : if (arange->cu == NULL)
299 : {
300 59 : const Dwarf_Arange *dwarange = &mod->dw->aranges->info[arange->arange];
301 59 : Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu);
302 59 : if (result != DWFL_E_NOERROR)
303 : return result;
304 59 : assert (arange->cu != NULL && arange->cu != (void *) -1l);
305 59 : less_lazy (mod); /* Each arange with null ->cu counts once. */
306 : }
307 :
308 337 : *cu = arange->cu;
309 337 : return DWFL_E_NOERROR;
310 : }
311 :
312 : Dwfl_Error
313 : internal_function
314 345 : __libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_cu **cu)
315 : {
316 : struct dwfl_arange *arange;
317 345 : return addrarange (mod, addr, &arange) ?: arangecu (mod, arange, cu);
318 : }
|