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.
5 This file is free software; you can redistribute it and/or modify
6 it under the terms of either
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
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
18 or both in parallel, as here.
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.
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/>. */
33 __libdwfl_segment_start (Dwfl *dwfl, GElf_Addr start)
35 if (dwfl->segment_align > 1)
36 start &= -dwfl->segment_align;
42 __libdwfl_segment_end (Dwfl *dwfl, GElf_Addr end)
44 if (dwfl->segment_align > 1)
45 end = (end + dwfl->segment_align - 1) & -dwfl->segment_align;
50 insert (Dwfl *dwfl, size_t i, GElf_Addr start, GElf_Addr end, int segndx)
52 bool need_start = (i == 0 || dwfl->lookup_addr[i - 1] != start);
53 bool need_end = (i + 1 >= dwfl->lookup_elts
54 || dwfl->lookup_addr[i + 1] != end);
55 size_t need = need_start + need_end;
59 if (dwfl->lookup_alloc - dwfl->lookup_elts < need)
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))
65 int *nsegndx = realloc (dwfl->lookup_segndx, sizeof nsegndx[0] * n);
66 if (unlikely (nsegndx == NULL))
68 if (naddr != dwfl->lookup_addr)
72 dwfl->lookup_alloc = n;
73 dwfl->lookup_addr = naddr;
74 dwfl->lookup_segndx = nsegndx;
76 if (dwfl->lookup_module != NULL)
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))
90 if (unlikely (i < dwfl->lookup_elts))
92 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]);
97 if (dwfl->lookup_module != NULL)
98 memmove (&dwfl->lookup_module[i + need], &dwfl->lookup_module[i],
99 move * sizeof dwfl->lookup_module[0]);
104 dwfl->lookup_addr[i] = start;
105 dwfl->lookup_segndx[i] = segndx;
106 if (dwfl->lookup_module != NULL)
107 dwfl->lookup_module[i] = NULL;
111 dwfl->lookup_segndx[i - 1] = segndx;
115 dwfl->lookup_addr[i] = end;
116 dwfl->lookup_segndx[i] = -1;
117 if (dwfl->lookup_module != NULL)
118 dwfl->lookup_module[i] = NULL;
121 dwfl->lookup_elts += need;
127 lookup (Dwfl *dwfl, GElf_Addr address, int hint)
130 && address >= dwfl->lookup_addr[hint]
131 && ((size_t) hint + 1 == dwfl->lookup_elts
132 || address < dwfl->lookup_addr[hint + 1]))
135 /* Do binary search on the array indexed by module load address. */
136 size_t l = 0, u = dwfl->lookup_elts;
139 size_t idx = (l + u) / 2;
140 if (address < dwfl->lookup_addr[idx])
145 if (l == dwfl->lookup_elts || address < dwfl->lookup_addr[l])
154 reify_segments (Dwfl *dwfl)
159 for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
162 const GElf_Addr start = __libdwfl_segment_start (dwfl, mod->low_addr);
163 const GElf_Addr end = __libdwfl_segment_end (dwfl, mod->high_addr);
164 bool resized = false;
166 int idx = lookup (dwfl, start, hint);
167 if (unlikely (idx < 0))
169 /* Module starts below any segment. Insert a low one. */
170 if (unlikely (insert (dwfl, 0, start, end, -1)))
175 else if (dwfl->lookup_addr[idx] > start)
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])))
184 else if (dwfl->lookup_addr[idx] < start)
186 /* The module starts past the end of this segment.
188 if (unlikely (insert (dwfl, idx + 1, start, end, -1)))
194 if ((size_t) idx + 1 < dwfl->lookup_elts
195 && end < dwfl->lookup_addr[idx + 1])
197 /* The module ends in the middle of this segment. Split it. */
198 if (unlikely (insert (dwfl, idx + 1,
199 end, dwfl->lookup_addr[idx + 1], -1)))
204 if (dwfl->lookup_module == NULL)
206 dwfl->lookup_module = calloc (dwfl->lookup_alloc,
207 sizeof dwfl->lookup_module[0]);
208 if (unlikely (dwfl->lookup_module == NULL))
212 /* Cache a backpointer in the module. */
215 /* Put MOD in the table for each segment that's inside it. */
217 dwfl->lookup_module[idx++] = mod;
218 while ((size_t) idx < dwfl->lookup_elts
219 && dwfl->lookup_addr[idx] < end);
220 assert (dwfl->lookup_module[mod->segment] == mod);
222 if (resized && idx - 1 >= highest)
223 /* Expanding the lookup tables invalidated backpointers
224 we've already stored. Reset those ones. */
228 hint = (size_t) idx < dwfl->lookup_elts ? idx : -1;
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;
241 dwfl_addrsegment (Dwfl *dwfl, Dwarf_Addr address, Dwfl_Module **mod)
243 if (unlikely (dwfl == NULL))
246 if (unlikely (dwfl->lookup_module == NULL)
248 && unlikely (reify_segments (dwfl)))
250 __libdwfl_seterrno (DWFL_E_NOMEM);
254 int idx = lookup (dwfl, address, -1);
255 if (likely (mod != NULL))
257 if (unlikely (idx < 0) || unlikely (dwfl->lookup_module == NULL))
261 *mod = dwfl->lookup_module[idx];
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)
267 *mod = dwfl->lookup_module[idx - 1];
268 if (*mod != NULL && (*mod)->high_addr != address)
274 if (likely (idx >= 0))
275 /* Translate internal segment table index to user segment index. */
276 idx = dwfl->lookup_segndx[idx];
280 INTDEF (dwfl_addrsegment)
283 dwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias,
290 ndx = dwfl->lookup_tail_ndx;
292 if (phdr->p_align > 1 && (dwfl->segment_align <= 1 ||
293 phdr->p_align < dwfl->segment_align))
294 dwfl->segment_align = phdr->p_align;
296 if (unlikely (dwfl->lookup_module != NULL))
298 free (dwfl->lookup_module);
299 dwfl->lookup_module = NULL;
302 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);
306 /* Coalesce into the last one if contiguous and matching. */
307 if (ndx != dwfl->lookup_tail_ndx
309 || ident != dwfl->lookup_tail_ident
310 || start != dwfl->lookup_tail_vaddr
311 || phdr->p_offset != dwfl->lookup_tail_offset)
313 /* Normally just appending keeps us sorted. */
315 size_t i = dwfl->lookup_elts;
316 while (i > 0 && unlikely (start < dwfl->lookup_addr[i - 1]))
319 if (unlikely (insert (dwfl, i, start, end, ndx)))
321 __libdwfl_seterrno (DWFL_E_NOMEM);
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;
333 INTDEF (dwfl_report_segment)