blob: 2983cf232cf0a53868fb9e181ba0f176d77c859f [file] [log] [blame]
Roland McGrathb4d6f0f2008-08-25 22:55:17 +00001/* Manage address space lookup table for libdwfl.
Mark Wielaard7d3879e2015-04-02 13:39:03 +02002 Copyright (C) 2008, 2009, 2010, 2013, 2015 Red Hat, Inc.
Mark Wielaardde2ed972012-06-05 17:15:16 +02003 This file is part of elfutils.
Roland McGrathb4d6f0f2008-08-25 22:55:17 +00004
Mark Wielaardde2ed972012-06-05 17:15:16 +02005 This file is free software; you can redistribute it and/or modify
6 it under the terms of either
Roland McGrathb4d6f0f2008-08-25 22:55:17 +00007
Mark Wielaardde2ed972012-06-05 17:15:16 +02008 * 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
Roland McGrathb4d6f0f2008-08-25 22:55:17 +000021 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
Mark Wielaardde2ed972012-06-05 17:15:16 +020025 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/>. */
Roland McGrathb4d6f0f2008-08-25 22:55:17 +000028
29#include "libdwflP.h"
30
Jan Kratochvil0b867462013-05-30 14:37:38 +020031GElf_Addr
32internal_function
33__libdwfl_segment_start (Dwfl *dwfl, GElf_Addr start)
Roland McGrathb4d6f0f2008-08-25 22:55:17 +000034{
35 if (dwfl->segment_align > 1)
36 start &= -dwfl->segment_align;
37 return start;
38}
39
Jan Kratochvil0b867462013-05-30 14:37:38 +020040GElf_Addr
41internal_function
42__libdwfl_segment_end (Dwfl *dwfl, GElf_Addr end)
Roland McGrathb4d6f0f2008-08-25 22:55:17 +000043{
44 if (dwfl->segment_align > 1)
45 end = (end + dwfl->segment_align - 1) & -dwfl->segment_align;
46 return end;
47}
48
49static bool
50insert (Dwfl *dwfl, size_t i, GElf_Addr start, GElf_Addr end, int segndx)
51{
52 bool need_start = (i == 0 || dwfl->lookup_addr[i - 1] != start);
Mark Wielaard7d3879e2015-04-02 13:39:03 +020053 bool need_end = (i + 1 >= dwfl->lookup_elts
54 || dwfl->lookup_addr[i + 1] != end);
Roland McGrathb4d6f0f2008-08-25 22:55:17 +000055 size_t need = need_start + need_end;
56 if (need == 0)
57 return false;
58
59 if (dwfl->lookup_alloc - dwfl->lookup_elts < need)
60 {
61 size_t n = dwfl->lookup_alloc == 0 ? 16 : dwfl->lookup_alloc * 2;
62 GElf_Addr *naddr = realloc (dwfl->lookup_addr, sizeof naddr[0] * n);
63 if (unlikely (naddr == NULL))
64 return true;
65 int *nsegndx = realloc (dwfl->lookup_segndx, sizeof nsegndx[0] * n);
66 if (unlikely (nsegndx == NULL))
67 {
Roland McGrath9cf28e42008-09-30 06:35:35 +000068 if (naddr != dwfl->lookup_addr)
69 free (naddr);
Roland McGrathb4d6f0f2008-08-25 22:55:17 +000070 return true;
71 }
72 dwfl->lookup_alloc = n;
73 dwfl->lookup_addr = naddr;
74 dwfl->lookup_segndx = nsegndx;
Roland McGrath9cf28e42008-09-30 06:35:35 +000075
76 if (dwfl->lookup_module != NULL)
77 {
78 /* Make sure this array is big enough too. */
79 Dwfl_Module **old = dwfl->lookup_module;
80 dwfl->lookup_module = realloc (dwfl->lookup_module,
81 sizeof dwfl->lookup_module[0] * n);
82 if (unlikely (dwfl->lookup_module == NULL))
83 {
84 free (old);
85 return true;
86 }
87 }
Roland McGrathb4d6f0f2008-08-25 22:55:17 +000088 }
89
90 if (unlikely (i < dwfl->lookup_elts))
91 {
Roland McGrath4820a052010-05-04 19:46:56 -070092 const size_t move = dwfl->lookup_elts - i;
93 memmove (&dwfl->lookup_addr[i + need], &dwfl->lookup_addr[i],
94 move * sizeof dwfl->lookup_addr[0]);
95 memmove (&dwfl->lookup_segndx[i + need], &dwfl->lookup_segndx[i],
96 move * sizeof dwfl->lookup_segndx[0]);
Roland McGrathb4d6f0f2008-08-25 22:55:17 +000097 if (dwfl->lookup_module != NULL)
Roland McGrath4820a052010-05-04 19:46:56 -070098 memmove (&dwfl->lookup_module[i + need], &dwfl->lookup_module[i],
99 move * sizeof dwfl->lookup_module[0]);
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000100 }
101
102 if (need_start)
103 {
104 dwfl->lookup_addr[i] = start;
105 dwfl->lookup_segndx[i] = segndx;
Roland McGrathaba26e02010-05-06 01:12:15 -0700106 if (dwfl->lookup_module != NULL)
107 dwfl->lookup_module[i] = NULL;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000108 ++i;
109 }
110 else
111 dwfl->lookup_segndx[i - 1] = segndx;
112
113 if (need_end)
114 {
115 dwfl->lookup_addr[i] = end;
116 dwfl->lookup_segndx[i] = -1;
Roland McGrathaba26e02010-05-06 01:12:15 -0700117 if (dwfl->lookup_module != NULL)
118 dwfl->lookup_module[i] = NULL;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000119 }
120
121 dwfl->lookup_elts += need;
122
123 return false;
124}
125
126static int
127lookup (Dwfl *dwfl, GElf_Addr address, int hint)
128{
129 if (hint >= 0
130 && address >= dwfl->lookup_addr[hint]
131 && ((size_t) hint + 1 == dwfl->lookup_elts
Roland McGrath74afbee2009-01-22 04:52:56 -0800132 || address < dwfl->lookup_addr[hint + 1]))
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000133 return hint;
134
135 /* Do binary search on the array indexed by module load address. */
136 size_t l = 0, u = dwfl->lookup_elts;
137 while (l < u)
138 {
139 size_t idx = (l + u) / 2;
140 if (address < dwfl->lookup_addr[idx])
141 u = idx;
142 else
143 {
144 l = idx + 1;
145 if (l == dwfl->lookup_elts || address < dwfl->lookup_addr[l])
146 return idx;
147 }
148 }
149
150 return -1;
151}
152
153static bool
154reify_segments (Dwfl *dwfl)
155{
156 int hint = -1;
Roland McGrath4820a052010-05-04 19:46:56 -0700157 int highest = -1;
158 bool fixup = false;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000159 for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
160 if (! mod->gc)
161 {
Jan Kratochvil0b867462013-05-30 14:37:38 +0200162 const GElf_Addr start = __libdwfl_segment_start (dwfl, mod->low_addr);
163 const GElf_Addr end = __libdwfl_segment_end (dwfl, mod->high_addr);
Roland McGrath4820a052010-05-04 19:46:56 -0700164 bool resized = false;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000165
166 int idx = lookup (dwfl, start, hint);
167 if (unlikely (idx < 0))
168 {
169 /* Module starts below any segment. Insert a low one. */
170 if (unlikely (insert (dwfl, 0, start, end, -1)))
171 return true;
172 idx = 0;
Roland McGrath4820a052010-05-04 19:46:56 -0700173 resized = true;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000174 }
175 else if (dwfl->lookup_addr[idx] > start)
176 {
177 /* The module starts in the middle of this segment. Split it. */
178 if (unlikely (insert (dwfl, idx + 1, start, end,
179 dwfl->lookup_segndx[idx])))
180 return true;
181 ++idx;
Roland McGrath4820a052010-05-04 19:46:56 -0700182 resized = true;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000183 }
Roland McGrath9cf28e42008-09-30 06:35:35 +0000184 else if (dwfl->lookup_addr[idx] < start)
185 {
186 /* The module starts past the end of this segment.
187 Add a new one. */
188 if (unlikely (insert (dwfl, idx + 1, start, end, -1)))
189 return true;
190 ++idx;
Roland McGrath4820a052010-05-04 19:46:56 -0700191 resized = true;
Roland McGrath9cf28e42008-09-30 06:35:35 +0000192 }
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000193
Roland McGrath9cf28e42008-09-30 06:35:35 +0000194 if ((size_t) idx + 1 < dwfl->lookup_elts
Roland McGrath4820a052010-05-04 19:46:56 -0700195 && end < dwfl->lookup_addr[idx + 1])
196 {
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000197 /* The module ends in the middle of this segment. Split it. */
Roland McGrath4820a052010-05-04 19:46:56 -0700198 if (unlikely (insert (dwfl, idx + 1,
199 end, dwfl->lookup_addr[idx + 1], -1)))
200 return true;
201 resized = true;
202 }
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000203
204 if (dwfl->lookup_module == NULL)
205 {
206 dwfl->lookup_module = calloc (dwfl->lookup_alloc,
207 sizeof dwfl->lookup_module[0]);
208 if (unlikely (dwfl->lookup_module == NULL))
209 return true;
210 }
211
212 /* Cache a backpointer in the module. */
213 mod->segment = idx;
214
215 /* Put MOD in the table for each segment that's inside it. */
216 do
217 dwfl->lookup_module[idx++] = mod;
218 while ((size_t) idx < dwfl->lookup_elts
219 && dwfl->lookup_addr[idx] < end);
Roland McGrath4820a052010-05-04 19:46:56 -0700220 assert (dwfl->lookup_module[mod->segment] == mod);
221
222 if (resized && idx - 1 >= highest)
223 /* Expanding the lookup tables invalidated backpointers
224 we've already stored. Reset those ones. */
225 fixup = true;
226
227 highest = idx - 1;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000228 hint = (size_t) idx < dwfl->lookup_elts ? idx : -1;
229 }
230
Roland McGrath4820a052010-05-04 19:46:56 -0700231 if (fixup)
232 /* Reset backpointer indices invalidated by table insertions. */
233 for (size_t idx = 0; idx < dwfl->lookup_elts; ++idx)
234 if (dwfl->lookup_module[idx] != NULL)
235 dwfl->lookup_module[idx]->segment = idx;
236
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000237 return false;
238}
239
240int
241dwfl_addrsegment (Dwfl *dwfl, Dwarf_Addr address, Dwfl_Module **mod)
242{
243 if (unlikely (dwfl == NULL))
244 return -1;
245
246 if (unlikely (dwfl->lookup_module == NULL)
247 && mod != NULL
248 && unlikely (reify_segments (dwfl)))
249 {
250 __libdwfl_seterrno (DWFL_E_NOMEM);
251 return -1;
252 }
253
254 int idx = lookup (dwfl, address, -1);
255 if (likely (mod != NULL))
256 {
257 if (unlikely (idx < 0) || unlikely (dwfl->lookup_module == NULL))
258 *mod = NULL;
259 else
260 {
261 *mod = dwfl->lookup_module[idx];
262
263 /* If this segment does not have a module, but the address is
264 the upper boundary of the previous segment's module, use that. */
265 if (*mod == NULL && idx > 0 && dwfl->lookup_addr[idx] == address)
266 {
267 *mod = dwfl->lookup_module[idx - 1];
268 if (*mod != NULL && (*mod)->high_addr != address)
269 *mod = NULL;
270 }
271 }
272 }
273
274 if (likely (idx >= 0))
275 /* Translate internal segment table index to user segment index. */
276 idx = dwfl->lookup_segndx[idx];
277
278 return idx;
279}
280INTDEF (dwfl_addrsegment)
281
282int
283dwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias,
284 const void *ident)
285{
286 if (dwfl == NULL)
287 return -1;
288
289 if (ndx < 0)
290 ndx = dwfl->lookup_tail_ndx;
291
292 if (phdr->p_align > 1 && (dwfl->segment_align <= 1 ||
293 phdr->p_align < dwfl->segment_align))
294 dwfl->segment_align = phdr->p_align;
295
Roland McGrath9cf28e42008-09-30 06:35:35 +0000296 if (unlikely (dwfl->lookup_module != NULL))
297 {
298 free (dwfl->lookup_module);
299 dwfl->lookup_module = NULL;
300 }
301
Jan Kratochvil0b867462013-05-30 14:37:38 +0200302 GElf_Addr start = __libdwfl_segment_start (dwfl, bias + phdr->p_vaddr);
303 GElf_Addr end = __libdwfl_segment_end (dwfl,
304 bias + phdr->p_vaddr + phdr->p_memsz);
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000305
306 /* Coalesce into the last one if contiguous and matching. */
307 if (ndx != dwfl->lookup_tail_ndx
308 || ident == NULL
309 || ident != dwfl->lookup_tail_ident
310 || start != dwfl->lookup_tail_vaddr
311 || phdr->p_offset != dwfl->lookup_tail_offset)
312 {
313 /* Normally just appending keeps us sorted. */
314
315 size_t i = dwfl->lookup_elts;
316 while (i > 0 && unlikely (start < dwfl->lookup_addr[i - 1]))
317 --i;
318
319 if (unlikely (insert (dwfl, i, start, end, ndx)))
320 {
321 __libdwfl_seterrno (DWFL_E_NOMEM);
322 return -1;
323 }
324 }
325
326 dwfl->lookup_tail_ident = ident;
327 dwfl->lookup_tail_vaddr = end;
328 dwfl->lookup_tail_offset = end - bias - phdr->p_vaddr + phdr->p_offset;
329 dwfl->lookup_tail_ndx = ndx + 1;
330
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000331 return ndx;
332}
333INTDEF (dwfl_report_segment)