blob: 7aa23b5010df098971dedbfddabc6fea1075aa4f [file] [log] [blame]
Brian Silverman86497922018-02-10 19:28:39 -05001/* 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
39static inline Dwarf_Arange *
40dwar (Dwfl_Module *mod, unsigned int idx)
41{
42 return &mod->dw->aranges->info[mod->aranges[idx].arange];
43}
44
45
46static Dwfl_Error
47addrarange (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 mod->aranges = (realloc (aranges, naranges * sizeof aranges[0])
87 ?: aranges);
88 mod->lazycu += naranges;
89 }
90
91 /* The address must be inside the module to begin with. */
92 addr = dwfl_deadjust_dwarf_addr (mod, addr);
93
94 /* The ranges are sorted by address, so we can use binary search. */
95 size_t l = 0, u = mod->naranges;
96 while (l < u)
97 {
98 size_t idx = (l + u) / 2;
99 Dwarf_Addr start = dwar (mod, idx)->addr;
100 if (addr < start)
101 {
102 u = idx;
103 continue;
104 }
105 else if (addr > start)
106 {
107 if (idx + 1 < mod->naranges)
108 {
109 if (addr >= dwar (mod, idx + 1)->addr)
110 {
111 l = idx + 1;
112 continue;
113 }
114 }
115 else
116 {
117 /* It might be in the last range. */
118 const Dwarf_Arange *last
119 = &mod->dw->aranges->info[mod->dw->aranges->naranges - 1];
120 if (addr > last->addr + last->length)
121 break;
122 }
123 }
124
125 *arange = &mod->aranges[idx];
126 return DWFL_E_NOERROR;
127 }
128
129 return DWFL_E_ADDR_OUTOFRANGE;
130}
131
132
133static void
134nofree (void *arg)
135{
136 struct dwfl_cu *cu = arg;
137 if (cu == (void *) -1l)
138 return;
139
140 assert (cu->mod->lazycu == 0);
141}
142
143/* One reason fewer to keep the lazy lookup table for CUs. */
144static inline void
145less_lazy (Dwfl_Module *mod)
146{
147 if (--mod->lazycu > 0)
148 return;
149
150 /* We know about all the CUs now, we don't need this table. */
151 tdestroy (mod->lazy_cu_root, nofree);
152 mod->lazy_cu_root = NULL;
153}
154
155static inline Dwarf_Off
156cudie_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 return DIE_OFFSET_FROM_CU_OFFSET (cu->die.cu->start, cu->die.cu->offset_size,
163 0);
164}
165
166static int
167compare_cukey (const void *a, const void *b)
168{
169 Dwarf_Off a_off = cudie_offset (a);
170 Dwarf_Off b_off = cudie_offset (b);
171 return (a_off < b_off) ? -1 : ((a_off > b_off) ? 1 : 0);
172}
173
174/* Intern the CU if necessary. */
175static Dwfl_Error
176intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result)
177{
178 if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size))
179 {
180 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 *result = (void *) -1;
185 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 Dwarf_Die *die = INTUSE(dwarf_offdie) (mod->dw, cuoff, &cudie);
198 if (die == NULL)
199 return DWFL_E_LIBDW;
200
201 struct Dwarf_CU dwkey;
202 struct dwfl_cu key;
203 key.die.cu = &dwkey;
204 dwkey.offset_size = 0;
205 dwkey.start = cuoff - (3 * 0 - 4 + 3);
206 struct dwfl_cu **found = tsearch (&key, &mod->lazy_cu_root, &compare_cukey);
207 if (unlikely (found == NULL))
208 return DWFL_E_NOMEM;
209
210 if (*found == &key || *found == NULL)
211 {
212 /* This is a new entry, meaning we haven't looked at this CU. */
213
214 *found = NULL;
215
216 struct dwfl_cu *cu = malloc (sizeof *cu);
217 if (unlikely (cu == NULL))
218 return DWFL_E_NOMEM;
219
220 cu->mod = mod;
221 cu->next = NULL;
222 cu->lines = NULL;
223 cu->die = cudie;
224
225 struct dwfl_cu **newvec = realloc (mod->cu, ((mod->ncu + 1)
226 * sizeof (mod->cu[0])));
227 if (newvec == NULL)
228 {
229 free (cu);
230 return DWFL_E_NOMEM;
231 }
232 mod->cu = newvec;
233
234 mod->cu[mod->ncu++] = cu;
235 if (cu->die.cu->start == 0)
236 mod->first_cu = cu;
237
238 *found = cu;
239 }
240
241 *result = *found;
242 return DWFL_E_NOERROR;
243}
244
245
246/* Traverse all the CUs in the module. */
247
248Dwfl_Error
249internal_function
250__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 if (lastcu == NULL)
257 {
258 /* Start the traversal. */
259 cuoff = 0;
260 nextp = &mod->first_cu;
261 }
262 else
263 {
264 /* Continue following LASTCU. */
265 cuoff = lastcu->die.cu->end;
266 nextp = &lastcu->next;
267 }
268
269 if (*nextp == NULL)
270 {
271 size_t cuhdrsz;
272 Dwarf_Off nextoff;
273 int end = INTUSE(dwarf_nextcu) (mod->dw, cuoff, &nextoff, &cuhdrsz,
274 NULL, NULL, NULL);
275 if (end < 0)
276 return DWFL_E_LIBDW;
277 if (end > 0)
278 {
279 *cu = NULL;
280 return DWFL_E_NOERROR;
281 }
282
283 Dwfl_Error result = intern_cu (mod, cuoff + cuhdrsz, nextp);
284 if (result != DWFL_E_NOERROR)
285 return result;
286
287 if (*nextp != (void *) -1
288 && (*nextp)->next == NULL && nextoff == (Dwarf_Off) -1l)
289 (*nextp)->next = (void *) -1l;
290 }
291
292 *cu = *nextp == (void *) -1l ? NULL : *nextp;
293 return DWFL_E_NOERROR;
294}
295
296
297/* Intern the CU arange points to, if necessary. */
298
299static Dwfl_Error
300arangecu (Dwfl_Module *mod, struct dwfl_arange *arange, struct dwfl_cu **cu)
301{
302 if (arange->cu == NULL)
303 {
304 const Dwarf_Arange *dwarange = &mod->dw->aranges->info[arange->arange];
305 Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu);
306 if (result != DWFL_E_NOERROR)
307 return result;
308 assert (arange->cu != NULL && arange->cu != (void *) -1l);
309 less_lazy (mod); /* Each arange with null ->cu counts once. */
310 }
311
312 *cu = arange->cu;
313 return DWFL_E_NOERROR;
314}
315
316Dwfl_Error
317internal_function
318__libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_cu **cu)
319{
320 struct dwfl_arange *arange;
321 return addrarange (mod, addr, &arange) ?: arangecu (mod, arange, cu);
322}