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