blob: 8f01ea6bd2d0cb086d999aaac0e2c6be23d50573 [file] [log] [blame]
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00001/* Keeping track of DWARF compilation units in libdwfl.
Ulrich Dreppere7a73172006-05-22 18:16:45 +00002 Copyright (C) 2005, 2006 Red Hat, Inc.
Ulrich Drepper361df7d2006-04-04 21:38:57 +00003 This file is part of Red Hat elfutils.
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00004
Ulrich Drepper361df7d2006-04-04 21:38:57 +00005 Red Hat elfutils is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by the
7 Free Software Foundation; version 2 of the License.
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00008
Ulrich Drepper361df7d2006-04-04 21:38:57 +00009 Red Hat elfutils is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with Red Hat elfutils; if not, write to the Free Software Foundation,
Ulrich Drepper1e9ef502006-04-04 22:29:06 +000016 Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
Ulrich Drepper361df7d2006-04-04 21:38:57 +000017
18 In addition, as a special exception, Red Hat, Inc. gives You the
19 additional right to link the code of Red Hat elfutils with code licensed
20 under any Open Source Initiative certified open source license
21 (http://www.opensource.org/licenses/index.php) which requires the
22 distribution of source code with any binary distribution and to
23 distribute linked combinations of the two. Non-GPL Code permitted under
24 this exception must only link to the code of Red Hat elfutils through
25 those well defined interfaces identified in the file named EXCEPTION
26 found in the source code files (the "Approved Interfaces"). The files
27 of Non-GPL Code may instantiate templates or use macros or inline
28 functions from the Approved Interfaces without causing the resulting
29 work to be covered by the GNU General Public License. Only Red Hat,
30 Inc. may make changes or additions to the list of Approved Interfaces.
31 Red Hat's grant of this exception is conditioned upon your not adding
32 any new exceptions. If you wish to add a new Approved Interface or
33 exception, please contact Red Hat. You must obey the GNU General Public
34 License in all respects for all of the Red Hat elfutils code and other
35 code used in conjunction with Red Hat elfutils except the Non-GPL Code
36 covered by this exception. If you modify this file, you may extend this
37 exception to your version of the file, but you are not obligated to do
38 so. If you do not wish to provide this exception without modification,
39 you must delete this exception statement from your version and license
40 this file solely under the GPL without exception.
41
42 Red Hat elfutils is an included package of the Open Invention Network.
43 An included package of the Open Invention Network is a package for which
44 Open Invention Network licensees cross-license their patents. No patent
45 license is granted, either expressly or impliedly, by designation as an
46 included package. Should you wish to participate in the Open Invention
47 Network licensing program, please visit www.openinventionnetwork.com
48 <http://www.openinventionnetwork.com>. */
Ulrich Drepperb08d5a82005-07-26 05:00:05 +000049
50#include "libdwflP.h"
51#include "../libdw/libdwP.h"
52#include "../libdw/memory-access.h"
53#include <search.h>
54
55
56static inline Dwarf_Arange *
57dwar (Dwfl_Module *mod, unsigned int idx)
58{
59 return &mod->dw->aranges->info[mod->aranges[idx].arange];
60}
61
62
63static Dwfl_Error
64addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange)
65{
66 if (mod->aranges == NULL)
67 {
Ulrich Dreppere7a73172006-05-22 18:16:45 +000068 struct dwfl_arange *aranges = NULL;
69 Dwarf_Aranges *dwaranges = NULL;
70 size_t naranges;
71 if (INTUSE(dwarf_getaranges) (mod->dw, &dwaranges, &naranges) != 0)
Ulrich Drepperb08d5a82005-07-26 05:00:05 +000072 return DWFL_E_LIBDW;
73
Ulrich Dreppere7a73172006-05-22 18:16:45 +000074 /* If the module has no aranges (when no code is included) we
75 allocate nothing. */
76 if (naranges != 0)
77 {
78 aranges = malloc (naranges * sizeof *aranges);
79 if (unlikely (aranges == NULL))
80 return DWFL_E_NOMEM;
Ulrich Drepperb08d5a82005-07-26 05:00:05 +000081
Ulrich Dreppere7a73172006-05-22 18:16:45 +000082 /* libdw has sorted its list by address, which is how we want it.
83 But the sorted list is full of not-quite-contiguous runs pointing
84 to the same CU. We don't care about the little gaps inside the
85 module, we'll consider them part of the surrounding CU anyway.
86 Collect our own array with just one record for each run of ranges
87 pointing to one CU. */
Ulrich Drepperb08d5a82005-07-26 05:00:05 +000088
Ulrich Dreppere7a73172006-05-22 18:16:45 +000089 naranges = 0;
90 Dwarf_Off lastcu = 0;
91 for (size_t i = 0; i < dwaranges->naranges; ++i)
92 if (i == 0 || dwaranges->info[i].offset != lastcu)
93 {
94 aranges[naranges].arange = i;
95 aranges[naranges].cu = NULL;
96 ++naranges;
97 lastcu = dwaranges->info[i].offset;
98 }
99 }
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000100
101 /* Store the final array, which is probably much smaller than before. */
102 mod->naranges = naranges;
103 mod->aranges = (realloc (aranges, naranges * sizeof aranges[0])
104 ?: aranges);
105 mod->lazycu += naranges;
106 }
107
108 /* The address must be inside the module to begin with. */
109 addr -= mod->debug.bias;
110
111 /* The ranges are sorted by address, so we can use binary search. */
112 size_t l = 0, u = mod->naranges;
113 while (l < u)
114 {
115 size_t idx = (l + u) / 2;
116 Dwarf_Addr start = dwar (mod, idx)->addr;
117 if (addr < start)
118 {
119 u = idx;
120 continue;
121 }
122 else if (addr > start)
123 {
124 if (idx + 1 < mod->naranges)
125 {
126 if (addr >= dwar (mod, idx + 1)->addr)
127 {
128 l = idx + 1;
129 continue;
130 }
131 }
132 else
133 {
134 /* It might be in the last range. */
135 const Dwarf_Arange *last
136 = &mod->dw->aranges->info[mod->dw->aranges->naranges - 1];
137 if (addr > last->addr + last->length)
138 break;
139 }
140 }
141
142 *arange = &mod->aranges[idx];
143 return DWFL_E_NOERROR;
144 }
145
146 return DWFL_E_ADDR_OUTOFRANGE;
147}
148
149
150static void
151nofree (void *arg)
152{
153 struct dwfl_cu *cu = arg;
154 if (cu == (void *) -1l)
155 return;
156
157 assert (cu->mod->lazycu == 0);
158}
159
160/* One reason fewer to keep the lazy lookup table for CUs. */
161static inline void
162less_lazy (Dwfl_Module *mod)
163{
164 if (--mod->lazycu > 0)
165 return;
166
167 /* We know about all the CUs now, we don't need this table. */
168 tdestroy (mod->lazy_cu_root, nofree);
169 mod->lazy_cu_root = NULL;
170}
171
172static inline Dwarf_Off
173cudie_offset (const struct dwfl_cu *cu)
174{
175 return cu->die.cu->start + 3 * cu->die.cu->offset_size - 4 + 3;
176}
177
178static int
179compare_cukey (const void *a, const void *b)
180{
181 return cudie_offset (a) - cudie_offset (b);
182}
183
184/* Intern the CU if necessary. */
185static Dwfl_Error
186intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result)
187{
188 struct Dwarf_CU dwkey;
189 struct dwfl_cu key;
190 key.die.cu = &dwkey;
191 dwkey.offset_size = 0;
192 dwkey.start = cuoff - (3 * 0 - 4 + 3);
193 struct dwfl_cu **found = tsearch (&key, &mod->lazy_cu_root, &compare_cukey);
194 if (unlikely (found == NULL))
195 return DWFL_E_NOMEM;
196
197 if (*found == &key || *found == NULL)
198 {
199 if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size))
200 {
201 /* This is the EOF marker. Now we have interned all the CUs.
202 One increment in MOD->lazycu counts not having hit EOF yet. */
203 *found = (void *) -1l;
204 less_lazy (mod);
205 }
206 else
207 {
208 /* This is a new entry, meaning we haven't looked at this CU. */
209
210 *found = NULL;
211
212 struct dwfl_cu *cu = malloc (sizeof *cu);
213 if (unlikely (cu == NULL))
214 return DWFL_E_NOMEM;
215
216 cu->mod = mod;
217 cu->next = NULL;
218 cu->lines = NULL;
219
220 /* XXX use non-searching lookup */
Roland McGrath4959bf82005-08-09 10:31:08 +0000221 Dwarf_Die *die = INTUSE(dwarf_offdie) (mod->dw, cuoff, &cu->die);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000222 if (die == NULL)
223 return DWFL_E_LIBDW;
224 assert (die == &cu->die);
225
226 struct dwfl_cu **newvec = realloc (mod->cu, ((mod->ncu + 1)
227 * sizeof (mod->cu[0])));
228 if (newvec == NULL)
229 {
230 free (cu);
231 return DWFL_E_NOMEM;
232 }
233 mod->cu = newvec;
234
235 mod->cu[mod->ncu++] = cu;
236 if (cu->die.cu->start == 0)
237 mod->first_cu = cu;
238
239 *found = cu;
240 }
241 }
242
243 *result = *found;
244 return DWFL_E_NOERROR;
245}
246
247
248/* Traverse all the CUs in the module. */
249
250Dwfl_Error
Ulrich Drepper077c65f2006-07-12 19:54:51 +0000251internal_function
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000252__libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu,
253 struct dwfl_cu **cu)
254{
255 Dwarf_Off cuoff;
256 struct dwfl_cu **nextp;
257
258 if (lastcu == NULL)
259 {
260 /* Start the traversal. */
261 cuoff = 0;
262 nextp = &mod->first_cu;
263 }
264 else
265 {
266 /* Continue following LASTCU. */
267 cuoff = lastcu->die.cu->end;
268 nextp = &lastcu->next;
269 }
270
271 if (*nextp == NULL)
272 {
273 size_t cuhdrsz;
274 Dwarf_Off nextoff;
Roland McGrath995f92d2005-08-26 05:55:04 +0000275 int end = INTUSE(dwarf_nextcu) (mod->dw, cuoff, &nextoff, &cuhdrsz,
276 NULL, NULL, NULL);
277 if (end < 0)
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000278 return DWFL_E_LIBDW;
Roland McGrath995f92d2005-08-26 05:55:04 +0000279 if (end > 0)
280 {
281 *cu = NULL;
282 return DWFL_E_NOERROR;
283 }
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000284
285 Dwfl_Error result = intern_cu (mod, cuoff + cuhdrsz, nextp);
286 if (result != DWFL_E_NOERROR)
287 return result;
288
289 if ((*nextp)->next == NULL && nextoff == (Dwarf_Off) -1l)
290 (*nextp)->next = (void *) -1l;
291 }
292
293 *cu = *nextp == (void *) -1l ? NULL : *nextp;
294 return DWFL_E_NOERROR;
295}
296
297
298/* Intern the CU arange points to, if necessary. */
299
300static Dwfl_Error
301arangecu (Dwfl_Module *mod, struct dwfl_arange *arange, struct dwfl_cu **cu)
302{
303 if (arange->cu == NULL)
304 {
305 const Dwarf_Arange *dwarange = &mod->dw->aranges->info[arange->arange];
306 Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu);
307 if (result != DWFL_E_NOERROR)
308 return result;
309 assert (arange->cu != NULL && arange->cu != (void *) -1l);
310 less_lazy (mod); /* Each arange with null ->cu counts once. */
311 }
312
313 *cu = arange->cu;
314 return DWFL_E_NOERROR;
315}
316
317Dwfl_Error
Ulrich Drepper077c65f2006-07-12 19:54:51 +0000318internal_function
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000319__libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_cu **cu)
320{
321 struct dwfl_arange *arange;
322 return addrarange (mod, addr, &arange) ?: arangecu (mod, arange, cu);
323}