blob: 85f592395dfc7c65a83e587ba1d354dc753655a3 [file] [log] [blame]
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00001/* Keeping track of DWARF compilation units in libdwfl.
2 Copyright (C) 2005 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,
16 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17
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 {
68 Dwarf_Aranges *dwaranges;
Roland McGrath4959bf82005-08-09 10:31:08 +000069 if (INTUSE(dwarf_getaranges) (mod->dw, &dwaranges, NULL) != 0)
Ulrich Drepperb08d5a82005-07-26 05:00:05 +000070 return DWFL_E_LIBDW;
71
72 struct dwfl_arange *aranges = malloc (dwaranges->naranges
73 * sizeof *aranges);
74 if (unlikely (aranges == NULL))
75 return DWFL_E_NOMEM;
76
77 /* libdw has sorted its list by address, which is how we want it.
78 But the sorted list is full of not-quite-contiguous runs pointing
79 to the same CU. We don't care about the little gaps inside the
80 module, we'll consider them part of the surrounding CU anyway.
81 Collect our own array with just one record for each run of ranges
82 pointing to one CU. */
83
84 size_t naranges = 0;
85 Dwarf_Off lastcu = 0;
86 for (size_t i = 0; i < dwaranges->naranges; ++i)
87 if (i == 0 || dwaranges->info[i].offset != lastcu)
88 {
89 aranges[naranges].arange = i;
90 aranges[naranges].cu = NULL;
91 ++naranges;
92 lastcu = dwaranges->info[i].offset;
93 }
94
95 /* Store the final array, which is probably much smaller than before. */
96 mod->naranges = naranges;
97 mod->aranges = (realloc (aranges, naranges * sizeof aranges[0])
98 ?: aranges);
99 mod->lazycu += naranges;
100 }
101
102 /* The address must be inside the module to begin with. */
103 addr -= mod->debug.bias;
104
105 /* The ranges are sorted by address, so we can use binary search. */
106 size_t l = 0, u = mod->naranges;
107 while (l < u)
108 {
109 size_t idx = (l + u) / 2;
110 Dwarf_Addr start = dwar (mod, idx)->addr;
111 if (addr < start)
112 {
113 u = idx;
114 continue;
115 }
116 else if (addr > start)
117 {
118 if (idx + 1 < mod->naranges)
119 {
120 if (addr >= dwar (mod, idx + 1)->addr)
121 {
122 l = idx + 1;
123 continue;
124 }
125 }
126 else
127 {
128 /* It might be in the last range. */
129 const Dwarf_Arange *last
130 = &mod->dw->aranges->info[mod->dw->aranges->naranges - 1];
131 if (addr > last->addr + last->length)
132 break;
133 }
134 }
135
136 *arange = &mod->aranges[idx];
137 return DWFL_E_NOERROR;
138 }
139
140 return DWFL_E_ADDR_OUTOFRANGE;
141}
142
143
144static void
145nofree (void *arg)
146{
147 struct dwfl_cu *cu = arg;
148 if (cu == (void *) -1l)
149 return;
150
151 assert (cu->mod->lazycu == 0);
152}
153
154/* One reason fewer to keep the lazy lookup table for CUs. */
155static inline void
156less_lazy (Dwfl_Module *mod)
157{
158 if (--mod->lazycu > 0)
159 return;
160
161 /* We know about all the CUs now, we don't need this table. */
162 tdestroy (mod->lazy_cu_root, nofree);
163 mod->lazy_cu_root = NULL;
164}
165
166static inline Dwarf_Off
167cudie_offset (const struct dwfl_cu *cu)
168{
169 return cu->die.cu->start + 3 * cu->die.cu->offset_size - 4 + 3;
170}
171
172static int
173compare_cukey (const void *a, const void *b)
174{
175 return cudie_offset (a) - cudie_offset (b);
176}
177
178/* Intern the CU if necessary. */
179static Dwfl_Error
180intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result)
181{
182 struct Dwarf_CU dwkey;
183 struct dwfl_cu key;
184 key.die.cu = &dwkey;
185 dwkey.offset_size = 0;
186 dwkey.start = cuoff - (3 * 0 - 4 + 3);
187 struct dwfl_cu **found = tsearch (&key, &mod->lazy_cu_root, &compare_cukey);
188 if (unlikely (found == NULL))
189 return DWFL_E_NOMEM;
190
191 if (*found == &key || *found == NULL)
192 {
193 if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size))
194 {
195 /* This is the EOF marker. Now we have interned all the CUs.
196 One increment in MOD->lazycu counts not having hit EOF yet. */
197 *found = (void *) -1l;
198 less_lazy (mod);
199 }
200 else
201 {
202 /* This is a new entry, meaning we haven't looked at this CU. */
203
204 *found = NULL;
205
206 struct dwfl_cu *cu = malloc (sizeof *cu);
207 if (unlikely (cu == NULL))
208 return DWFL_E_NOMEM;
209
210 cu->mod = mod;
211 cu->next = NULL;
212 cu->lines = NULL;
213
214 /* XXX use non-searching lookup */
Roland McGrath4959bf82005-08-09 10:31:08 +0000215 Dwarf_Die *die = INTUSE(dwarf_offdie) (mod->dw, cuoff, &cu->die);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000216 if (die == NULL)
217 return DWFL_E_LIBDW;
218 assert (die == &cu->die);
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
237 *result = *found;
238 return DWFL_E_NOERROR;
239}
240
241
242/* Traverse all the CUs in the module. */
243
244Dwfl_Error
245internal_function_def
246__libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu,
247 struct dwfl_cu **cu)
248{
249 Dwarf_Off cuoff;
250 struct dwfl_cu **nextp;
251
252 if (lastcu == NULL)
253 {
254 /* Start the traversal. */
255 cuoff = 0;
256 nextp = &mod->first_cu;
257 }
258 else
259 {
260 /* Continue following LASTCU. */
261 cuoff = lastcu->die.cu->end;
262 nextp = &lastcu->next;
263 }
264
265 if (*nextp == NULL)
266 {
267 size_t cuhdrsz;
268 Dwarf_Off nextoff;
Roland McGrath995f92d2005-08-26 05:55:04 +0000269 int end = INTUSE(dwarf_nextcu) (mod->dw, cuoff, &nextoff, &cuhdrsz,
270 NULL, NULL, NULL);
271 if (end < 0)
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000272 return DWFL_E_LIBDW;
Roland McGrath995f92d2005-08-26 05:55:04 +0000273 if (end > 0)
274 {
275 *cu = NULL;
276 return DWFL_E_NOERROR;
277 }
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000278
279 Dwfl_Error result = intern_cu (mod, cuoff + cuhdrsz, nextp);
280 if (result != DWFL_E_NOERROR)
281 return result;
282
283 if ((*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
294static Dwfl_Error
295arangecu (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
311Dwfl_Error
312internal_function_def
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}