/src/elfutils/libdwfl/segment.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Manage address space lookup table for libdwfl. |
2 | | Copyright (C) 2008, 2009, 2010, 2013, 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 | | |
35 | | GElf_Addr |
36 | | internal_function |
37 | | __libdwfl_segment_start (Dwfl *dwfl, GElf_Addr start) |
38 | 0 | { |
39 | 0 | if (dwfl->segment_align > 1) |
40 | 0 | start &= -dwfl->segment_align; |
41 | 0 | return start; |
42 | 0 | } |
43 | | |
44 | | GElf_Addr |
45 | | internal_function |
46 | | __libdwfl_segment_end (Dwfl *dwfl, GElf_Addr end) |
47 | 0 | { |
48 | 0 | if (dwfl->segment_align > 1) |
49 | 0 | end = (end + dwfl->segment_align - 1) & -dwfl->segment_align; |
50 | 0 | return end; |
51 | 0 | } |
52 | | |
53 | | static bool |
54 | | insert (Dwfl *dwfl, size_t i, GElf_Addr start, GElf_Addr end, int segndx) |
55 | 0 | { |
56 | 0 | bool need_start = (i == 0 || dwfl->lookup_addr[i - 1] != start); |
57 | 0 | bool need_end = (i + 1 >= dwfl->lookup_elts |
58 | 0 | || dwfl->lookup_addr[i + 1] != end); |
59 | 0 | size_t need = need_start + need_end; |
60 | 0 | if (need == 0) |
61 | 0 | return false; |
62 | | |
63 | 0 | if (dwfl->lookup_alloc - dwfl->lookup_elts < need) |
64 | 0 | { |
65 | 0 | size_t n = dwfl->lookup_alloc == 0 ? 16 : dwfl->lookup_alloc * 2; |
66 | 0 | GElf_Addr *naddr = realloc (dwfl->lookup_addr, sizeof naddr[0] * n); |
67 | 0 | if (unlikely (naddr == NULL)) |
68 | 0 | return true; |
69 | 0 | int *nsegndx = realloc (dwfl->lookup_segndx, sizeof nsegndx[0] * n); |
70 | 0 | if (unlikely (nsegndx == NULL)) |
71 | 0 | { |
72 | 0 | if (naddr != dwfl->lookup_addr) |
73 | 0 | free (naddr); |
74 | 0 | return true; |
75 | 0 | } |
76 | 0 | dwfl->lookup_alloc = n; |
77 | 0 | dwfl->lookup_addr = naddr; |
78 | 0 | dwfl->lookup_segndx = nsegndx; |
79 | |
|
80 | 0 | if (dwfl->lookup_module != NULL) |
81 | 0 | { |
82 | | /* Make sure this array is big enough too. */ |
83 | 0 | Dwfl_Module **old = dwfl->lookup_module; |
84 | 0 | dwfl->lookup_module = realloc (dwfl->lookup_module, |
85 | 0 | sizeof dwfl->lookup_module[0] * n); |
86 | 0 | if (unlikely (dwfl->lookup_module == NULL)) |
87 | 0 | { |
88 | 0 | free (old); |
89 | 0 | return true; |
90 | 0 | } |
91 | 0 | } |
92 | 0 | } |
93 | | |
94 | 0 | if (unlikely (i < dwfl->lookup_elts)) |
95 | 0 | { |
96 | 0 | const size_t move = dwfl->lookup_elts - i; |
97 | 0 | memmove (&dwfl->lookup_addr[i + need], &dwfl->lookup_addr[i], |
98 | 0 | move * sizeof dwfl->lookup_addr[0]); |
99 | 0 | memmove (&dwfl->lookup_segndx[i + need], &dwfl->lookup_segndx[i], |
100 | 0 | move * sizeof dwfl->lookup_segndx[0]); |
101 | 0 | if (dwfl->lookup_module != NULL) |
102 | 0 | memmove (&dwfl->lookup_module[i + need], &dwfl->lookup_module[i], |
103 | 0 | move * sizeof dwfl->lookup_module[0]); |
104 | 0 | } |
105 | |
|
106 | 0 | if (need_start) |
107 | 0 | { |
108 | 0 | dwfl->lookup_addr[i] = start; |
109 | 0 | dwfl->lookup_segndx[i] = segndx; |
110 | 0 | if (dwfl->lookup_module != NULL) |
111 | 0 | dwfl->lookup_module[i] = NULL; |
112 | 0 | ++i; |
113 | 0 | } |
114 | 0 | else |
115 | 0 | dwfl->lookup_segndx[i - 1] = segndx; |
116 | |
|
117 | 0 | if (need_end) |
118 | 0 | { |
119 | 0 | dwfl->lookup_addr[i] = end; |
120 | 0 | dwfl->lookup_segndx[i] = -1; |
121 | 0 | if (dwfl->lookup_module != NULL) |
122 | 0 | dwfl->lookup_module[i] = NULL; |
123 | 0 | } |
124 | |
|
125 | 0 | dwfl->lookup_elts += need; |
126 | |
|
127 | 0 | return false; |
128 | 0 | } |
129 | | |
130 | | static int |
131 | | lookup (Dwfl *dwfl, GElf_Addr address, int hint) |
132 | 0 | { |
133 | 0 | if (hint >= 0 |
134 | 0 | && address >= dwfl->lookup_addr[hint] |
135 | 0 | && ((size_t) hint + 1 == dwfl->lookup_elts |
136 | 0 | || address < dwfl->lookup_addr[hint + 1])) |
137 | 0 | return hint; |
138 | | |
139 | | /* Do binary search on the array indexed by module load address. */ |
140 | 0 | size_t l = 0, u = dwfl->lookup_elts; |
141 | 0 | while (l < u) |
142 | 0 | { |
143 | 0 | size_t idx = (l + u) / 2; |
144 | 0 | if (address < dwfl->lookup_addr[idx]) |
145 | 0 | u = idx; |
146 | 0 | else |
147 | 0 | { |
148 | 0 | l = idx + 1; |
149 | 0 | if (l == dwfl->lookup_elts || address < dwfl->lookup_addr[l]) |
150 | 0 | return idx; |
151 | 0 | } |
152 | 0 | } |
153 | | |
154 | 0 | return -1; |
155 | 0 | } |
156 | | |
157 | | static bool |
158 | | reify_segments (Dwfl *dwfl) |
159 | 0 | { |
160 | 0 | int hint = -1; |
161 | 0 | int highest = -1; |
162 | 0 | bool fixup = false; |
163 | 0 | for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next) |
164 | 0 | if (! mod->gc) |
165 | 0 | { |
166 | 0 | const GElf_Addr start = __libdwfl_segment_start (dwfl, mod->low_addr); |
167 | 0 | const GElf_Addr end = __libdwfl_segment_end (dwfl, mod->high_addr); |
168 | 0 | bool resized = false; |
169 | |
|
170 | 0 | int idx = lookup (dwfl, start, hint); |
171 | 0 | if (unlikely (idx < 0)) |
172 | 0 | { |
173 | | /* Module starts below any segment. Insert a low one. */ |
174 | 0 | if (unlikely (insert (dwfl, 0, start, end, -1))) |
175 | 0 | return true; |
176 | 0 | idx = 0; |
177 | 0 | resized = true; |
178 | 0 | } |
179 | 0 | else if (dwfl->lookup_addr[idx] > start) |
180 | 0 | { |
181 | | /* The module starts in the middle of this segment. Split it. */ |
182 | 0 | if (unlikely (insert (dwfl, idx + 1, start, end, |
183 | 0 | dwfl->lookup_segndx[idx]))) |
184 | 0 | return true; |
185 | 0 | ++idx; |
186 | 0 | resized = true; |
187 | 0 | } |
188 | 0 | else if (dwfl->lookup_addr[idx] < start) |
189 | 0 | { |
190 | | /* The module starts past the end of this segment. |
191 | | Add a new one. */ |
192 | 0 | if (unlikely (insert (dwfl, idx + 1, start, end, -1))) |
193 | 0 | return true; |
194 | 0 | ++idx; |
195 | 0 | resized = true; |
196 | 0 | } |
197 | | |
198 | 0 | if ((size_t) idx + 1 < dwfl->lookup_elts |
199 | 0 | && end < dwfl->lookup_addr[idx + 1]) |
200 | 0 | { |
201 | | /* The module ends in the middle of this segment. Split it. */ |
202 | 0 | if (unlikely (insert (dwfl, idx + 1, |
203 | 0 | end, dwfl->lookup_addr[idx + 1], -1))) |
204 | 0 | return true; |
205 | 0 | resized = true; |
206 | 0 | } |
207 | | |
208 | 0 | if (dwfl->lookup_module == NULL) |
209 | 0 | { |
210 | 0 | dwfl->lookup_module = calloc (dwfl->lookup_alloc, |
211 | 0 | sizeof dwfl->lookup_module[0]); |
212 | 0 | if (unlikely (dwfl->lookup_module == NULL)) |
213 | 0 | return true; |
214 | 0 | } |
215 | | |
216 | | /* Cache a backpointer in the module. */ |
217 | 0 | mod->segment = idx; |
218 | | |
219 | | /* Put MOD in the table for each segment that's inside it. */ |
220 | 0 | do |
221 | 0 | dwfl->lookup_module[idx++] = mod; |
222 | 0 | while ((size_t) idx < dwfl->lookup_elts |
223 | 0 | && dwfl->lookup_addr[idx] < end); |
224 | 0 | assert (dwfl->lookup_module[mod->segment] == mod); |
225 | | |
226 | 0 | if (resized && idx - 1 >= highest) |
227 | | /* Expanding the lookup tables invalidated backpointers |
228 | | we've already stored. Reset those ones. */ |
229 | 0 | fixup = true; |
230 | |
|
231 | 0 | highest = idx - 1; |
232 | 0 | hint = (size_t) idx < dwfl->lookup_elts ? idx : -1; |
233 | 0 | } |
234 | | |
235 | 0 | if (fixup) |
236 | | /* Reset backpointer indices invalidated by table insertions. */ |
237 | 0 | for (size_t idx = 0; idx < dwfl->lookup_elts; ++idx) |
238 | 0 | if (dwfl->lookup_module[idx] != NULL) |
239 | 0 | dwfl->lookup_module[idx]->segment = idx; |
240 | |
|
241 | 0 | return false; |
242 | 0 | } |
243 | | |
244 | | int |
245 | | dwfl_addrsegment (Dwfl *dwfl, Dwarf_Addr address, Dwfl_Module **mod) |
246 | 0 | { |
247 | 0 | if (unlikely (dwfl == NULL)) |
248 | 0 | return -1; |
249 | | |
250 | 0 | if (unlikely (dwfl->lookup_module == NULL) |
251 | 0 | && mod != NULL |
252 | 0 | && unlikely (reify_segments (dwfl))) |
253 | 0 | { |
254 | 0 | __libdwfl_seterrno (DWFL_E_NOMEM); |
255 | 0 | return -1; |
256 | 0 | } |
257 | | |
258 | 0 | int idx = lookup (dwfl, address, -1); |
259 | 0 | if (likely (mod != NULL)) |
260 | 0 | { |
261 | 0 | if (unlikely (idx < 0) || unlikely (dwfl->lookup_module == NULL)) |
262 | 0 | *mod = NULL; |
263 | 0 | else |
264 | 0 | { |
265 | 0 | *mod = dwfl->lookup_module[idx]; |
266 | | |
267 | | /* If this segment does not have a module, but the address is |
268 | | the upper boundary of the previous segment's module, use that. */ |
269 | 0 | if (*mod == NULL && idx > 0 && dwfl->lookup_addr[idx] == address) |
270 | 0 | { |
271 | 0 | *mod = dwfl->lookup_module[idx - 1]; |
272 | 0 | if (*mod != NULL && (*mod)->high_addr != address) |
273 | 0 | *mod = NULL; |
274 | 0 | } |
275 | 0 | } |
276 | 0 | } |
277 | |
|
278 | 0 | if (likely (idx >= 0)) |
279 | | /* Translate internal segment table index to user segment index. */ |
280 | 0 | idx = dwfl->lookup_segndx[idx]; |
281 | |
|
282 | 0 | return idx; |
283 | 0 | } |
284 | | INTDEF (dwfl_addrsegment) |
285 | | |
286 | | int |
287 | | dwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias, |
288 | | const void *ident) |
289 | 0 | { |
290 | | /* This was previously used for coalescing segments, but it was buggy since |
291 | | day one. We don't use it anymore. */ |
292 | 0 | (void)ident; |
293 | |
|
294 | 0 | if (dwfl == NULL) |
295 | 0 | return -1; |
296 | | |
297 | 0 | if (ndx < 0) |
298 | 0 | ndx = dwfl->next_segndx; |
299 | |
|
300 | 0 | if (phdr->p_align > 1 && (dwfl->segment_align <= 1 || |
301 | 0 | phdr->p_align < dwfl->segment_align)) |
302 | 0 | dwfl->segment_align = phdr->p_align; |
303 | |
|
304 | 0 | if (unlikely (dwfl->lookup_module != NULL)) |
305 | 0 | { |
306 | 0 | free (dwfl->lookup_module); |
307 | 0 | dwfl->lookup_module = NULL; |
308 | 0 | } |
309 | |
|
310 | 0 | GElf_Addr start = __libdwfl_segment_start (dwfl, bias + phdr->p_vaddr); |
311 | 0 | GElf_Addr end = __libdwfl_segment_end (dwfl, |
312 | 0 | bias + phdr->p_vaddr + phdr->p_memsz); |
313 | | |
314 | | /* Normally just appending keeps us sorted. */ |
315 | |
|
316 | 0 | size_t i = dwfl->lookup_elts; |
317 | 0 | while (i > 0 && unlikely (start < dwfl->lookup_addr[i - 1])) |
318 | 0 | --i; |
319 | |
|
320 | 0 | if (unlikely (insert (dwfl, i, start, end, ndx))) |
321 | 0 | { |
322 | 0 | __libdwfl_seterrno (DWFL_E_NOMEM); |
323 | 0 | return -1; |
324 | 0 | } |
325 | | |
326 | 0 | dwfl->next_segndx = ndx + 1; |
327 | |
|
328 | 0 | return ndx; |
329 | 0 | } |
330 | | INTDEF (dwfl_report_segment) |