blob: d39fdab9121f59a11e582fdcdbc0922b74e11e7a [file] [log] [blame]
Roland McGrathb4d6f0f2008-08-25 22:55:17 +00001/* Manage address space lookup table for libdwfl.
Roland McGrath4820a052010-05-04 19:46:56 -07002 Copyright (C) 2008, 2009, 2010 Red Hat, Inc.
Roland McGrathb4d6f0f2008-08-25 22:55:17 +00003 This file is part of Red Hat elfutils.
4
5 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.
8
9 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 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>. */
49
50#include "libdwflP.h"
51
52static GElf_Addr
53segment_start (Dwfl *dwfl, GElf_Addr start)
54{
55 if (dwfl->segment_align > 1)
56 start &= -dwfl->segment_align;
57 return start;
58}
59
60static GElf_Addr
61segment_end (Dwfl *dwfl, GElf_Addr end)
62{
63 if (dwfl->segment_align > 1)
64 end = (end + dwfl->segment_align - 1) & -dwfl->segment_align;
65 return end;
66}
67
68static bool
69insert (Dwfl *dwfl, size_t i, GElf_Addr start, GElf_Addr end, int segndx)
70{
71 bool need_start = (i == 0 || dwfl->lookup_addr[i - 1] != start);
72 bool need_end = (i >= dwfl->lookup_elts || dwfl->lookup_addr[i + 1] != end);
73 size_t need = need_start + need_end;
74 if (need == 0)
75 return false;
76
77 if (dwfl->lookup_alloc - dwfl->lookup_elts < need)
78 {
79 size_t n = dwfl->lookup_alloc == 0 ? 16 : dwfl->lookup_alloc * 2;
80 GElf_Addr *naddr = realloc (dwfl->lookup_addr, sizeof naddr[0] * n);
81 if (unlikely (naddr == NULL))
82 return true;
83 int *nsegndx = realloc (dwfl->lookup_segndx, sizeof nsegndx[0] * n);
84 if (unlikely (nsegndx == NULL))
85 {
Roland McGrath9cf28e42008-09-30 06:35:35 +000086 if (naddr != dwfl->lookup_addr)
87 free (naddr);
Roland McGrathb4d6f0f2008-08-25 22:55:17 +000088 return true;
89 }
90 dwfl->lookup_alloc = n;
91 dwfl->lookup_addr = naddr;
92 dwfl->lookup_segndx = nsegndx;
Roland McGrath9cf28e42008-09-30 06:35:35 +000093
94 if (dwfl->lookup_module != NULL)
95 {
96 /* Make sure this array is big enough too. */
97 Dwfl_Module **old = dwfl->lookup_module;
98 dwfl->lookup_module = realloc (dwfl->lookup_module,
99 sizeof dwfl->lookup_module[0] * n);
100 if (unlikely (dwfl->lookup_module == NULL))
101 {
102 free (old);
103 return true;
104 }
105 }
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000106 }
107
108 if (unlikely (i < dwfl->lookup_elts))
109 {
Roland McGrath4820a052010-05-04 19:46:56 -0700110 const size_t move = dwfl->lookup_elts - i;
111 memmove (&dwfl->lookup_addr[i + need], &dwfl->lookup_addr[i],
112 move * sizeof dwfl->lookup_addr[0]);
113 memmove (&dwfl->lookup_segndx[i + need], &dwfl->lookup_segndx[i],
114 move * sizeof dwfl->lookup_segndx[0]);
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000115 if (dwfl->lookup_module != NULL)
Roland McGrath4820a052010-05-04 19:46:56 -0700116 memmove (&dwfl->lookup_module[i + need], &dwfl->lookup_module[i],
117 move * sizeof dwfl->lookup_module[0]);
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000118 }
119
120 if (need_start)
121 {
122 dwfl->lookup_addr[i] = start;
123 dwfl->lookup_segndx[i] = segndx;
124 ++i;
125 }
126 else
127 dwfl->lookup_segndx[i - 1] = segndx;
128
129 if (need_end)
130 {
131 dwfl->lookup_addr[i] = end;
132 dwfl->lookup_segndx[i] = -1;
133 }
134
135 dwfl->lookup_elts += need;
136
137 return false;
138}
139
140static int
141lookup (Dwfl *dwfl, GElf_Addr address, int hint)
142{
143 if (hint >= 0
144 && address >= dwfl->lookup_addr[hint]
145 && ((size_t) hint + 1 == dwfl->lookup_elts
Roland McGrath74afbee2009-01-22 04:52:56 -0800146 || address < dwfl->lookup_addr[hint + 1]))
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000147 return hint;
148
149 /* Do binary search on the array indexed by module load address. */
150 size_t l = 0, u = dwfl->lookup_elts;
151 while (l < u)
152 {
153 size_t idx = (l + u) / 2;
154 if (address < dwfl->lookup_addr[idx])
155 u = idx;
156 else
157 {
158 l = idx + 1;
159 if (l == dwfl->lookup_elts || address < dwfl->lookup_addr[l])
160 return idx;
161 }
162 }
163
164 return -1;
165}
166
167static bool
168reify_segments (Dwfl *dwfl)
169{
170 int hint = -1;
Roland McGrath4820a052010-05-04 19:46:56 -0700171 int highest = -1;
172 bool fixup = false;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000173 for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
174 if (! mod->gc)
175 {
176 const GElf_Addr start = segment_start (dwfl, mod->low_addr);
177 const GElf_Addr end = segment_end (dwfl, mod->high_addr);
Roland McGrath4820a052010-05-04 19:46:56 -0700178 bool resized = false;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000179
180 int idx = lookup (dwfl, start, hint);
181 if (unlikely (idx < 0))
182 {
183 /* Module starts below any segment. Insert a low one. */
184 if (unlikely (insert (dwfl, 0, start, end, -1)))
185 return true;
186 idx = 0;
Roland McGrath4820a052010-05-04 19:46:56 -0700187 resized = true;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000188 }
189 else if (dwfl->lookup_addr[idx] > start)
190 {
191 /* The module starts in the middle of this segment. Split it. */
192 if (unlikely (insert (dwfl, idx + 1, start, end,
193 dwfl->lookup_segndx[idx])))
194 return true;
195 ++idx;
Roland McGrath4820a052010-05-04 19:46:56 -0700196 resized = true;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000197 }
Roland McGrath9cf28e42008-09-30 06:35:35 +0000198 else if (dwfl->lookup_addr[idx] < start)
199 {
200 /* The module starts past the end of this segment.
201 Add a new one. */
202 if (unlikely (insert (dwfl, idx + 1, start, end, -1)))
203 return true;
204 ++idx;
Roland McGrath4820a052010-05-04 19:46:56 -0700205 resized = true;
Roland McGrath9cf28e42008-09-30 06:35:35 +0000206 }
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000207
Roland McGrath9cf28e42008-09-30 06:35:35 +0000208 if ((size_t) idx + 1 < dwfl->lookup_elts
Roland McGrath4820a052010-05-04 19:46:56 -0700209 && end < dwfl->lookup_addr[idx + 1])
210 {
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000211 /* The module ends in the middle of this segment. Split it. */
Roland McGrath4820a052010-05-04 19:46:56 -0700212 if (unlikely (insert (dwfl, idx + 1,
213 end, dwfl->lookup_addr[idx + 1], -1)))
214 return true;
215 resized = true;
216 }
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000217
218 if (dwfl->lookup_module == NULL)
219 {
220 dwfl->lookup_module = calloc (dwfl->lookup_alloc,
221 sizeof dwfl->lookup_module[0]);
222 if (unlikely (dwfl->lookup_module == NULL))
223 return true;
224 }
225
226 /* Cache a backpointer in the module. */
227 mod->segment = idx;
228
229 /* Put MOD in the table for each segment that's inside it. */
230 do
231 dwfl->lookup_module[idx++] = mod;
232 while ((size_t) idx < dwfl->lookup_elts
233 && dwfl->lookup_addr[idx] < end);
Roland McGrath4820a052010-05-04 19:46:56 -0700234 assert (dwfl->lookup_module[mod->segment] == mod);
235
236 if (resized && idx - 1 >= highest)
237 /* Expanding the lookup tables invalidated backpointers
238 we've already stored. Reset those ones. */
239 fixup = true;
240
241 highest = idx - 1;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000242 hint = (size_t) idx < dwfl->lookup_elts ? idx : -1;
243 }
244
Roland McGrath4820a052010-05-04 19:46:56 -0700245 if (fixup)
246 /* Reset backpointer indices invalidated by table insertions. */
247 for (size_t idx = 0; idx < dwfl->lookup_elts; ++idx)
248 if (dwfl->lookup_module[idx] != NULL)
249 dwfl->lookup_module[idx]->segment = idx;
250
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000251 return false;
252}
253
254int
255dwfl_addrsegment (Dwfl *dwfl, Dwarf_Addr address, Dwfl_Module **mod)
256{
257 if (unlikely (dwfl == NULL))
258 return -1;
259
260 if (unlikely (dwfl->lookup_module == NULL)
261 && mod != NULL
262 && unlikely (reify_segments (dwfl)))
263 {
264 __libdwfl_seterrno (DWFL_E_NOMEM);
265 return -1;
266 }
267
268 int idx = lookup (dwfl, address, -1);
269 if (likely (mod != NULL))
270 {
271 if (unlikely (idx < 0) || unlikely (dwfl->lookup_module == NULL))
272 *mod = NULL;
273 else
274 {
275 *mod = dwfl->lookup_module[idx];
276
277 /* If this segment does not have a module, but the address is
278 the upper boundary of the previous segment's module, use that. */
279 if (*mod == NULL && idx > 0 && dwfl->lookup_addr[idx] == address)
280 {
281 *mod = dwfl->lookup_module[idx - 1];
282 if (*mod != NULL && (*mod)->high_addr != address)
283 *mod = NULL;
284 }
285 }
286 }
287
288 if (likely (idx >= 0))
289 /* Translate internal segment table index to user segment index. */
290 idx = dwfl->lookup_segndx[idx];
291
292 return idx;
293}
294INTDEF (dwfl_addrsegment)
295
296int
297dwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias,
298 const void *ident)
299{
300 if (dwfl == NULL)
301 return -1;
302
303 if (ndx < 0)
304 ndx = dwfl->lookup_tail_ndx;
305
306 if (phdr->p_align > 1 && (dwfl->segment_align <= 1 ||
307 phdr->p_align < dwfl->segment_align))
308 dwfl->segment_align = phdr->p_align;
309
Roland McGrath9cf28e42008-09-30 06:35:35 +0000310 if (unlikely (dwfl->lookup_module != NULL))
311 {
312 free (dwfl->lookup_module);
313 dwfl->lookup_module = NULL;
314 }
315
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000316 GElf_Addr start = segment_start (dwfl, bias + phdr->p_vaddr);
317 GElf_Addr end = segment_end (dwfl, bias + phdr->p_vaddr + phdr->p_memsz);
318
319 /* Coalesce into the last one if contiguous and matching. */
320 if (ndx != dwfl->lookup_tail_ndx
321 || ident == NULL
322 || ident != dwfl->lookup_tail_ident
323 || start != dwfl->lookup_tail_vaddr
324 || phdr->p_offset != dwfl->lookup_tail_offset)
325 {
326 /* Normally just appending keeps us sorted. */
327
328 size_t i = dwfl->lookup_elts;
329 while (i > 0 && unlikely (start < dwfl->lookup_addr[i - 1]))
330 --i;
331
332 if (unlikely (insert (dwfl, i, start, end, ndx)))
333 {
334 __libdwfl_seterrno (DWFL_E_NOMEM);
335 return -1;
336 }
337 }
338
339 dwfl->lookup_tail_ident = ident;
340 dwfl->lookup_tail_vaddr = end;
341 dwfl->lookup_tail_offset = end - bias - phdr->p_vaddr + phdr->p_offset;
342 dwfl->lookup_tail_ndx = ndx + 1;
343
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000344 return ndx;
345}
346INTDEF (dwfl_report_segment)