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