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