1 /* Manage address space lookup table for libdwfl.
2 Copyright (C) 2008, 2009, 2010 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/>. */
32 segment_start (Dwfl *dwfl, GElf_Addr start)
34 if (dwfl->segment_align > 1)
35 start &= -dwfl->segment_align;
40 segment_end (Dwfl *dwfl, GElf_Addr end)
42 if (dwfl->segment_align > 1)
43 end = (end + dwfl->segment_align - 1) & -dwfl->segment_align;
48 insert (Dwfl *dwfl, size_t i, GElf_Addr start, GElf_Addr end, int segndx)
50 bool need_start = (i == 0 || dwfl->lookup_addr[i - 1] != start);
51 bool need_end = (i >= dwfl->lookup_elts || dwfl->lookup_addr[i + 1] != end);
52 size_t need = need_start + need_end;
56 if (dwfl->lookup_alloc - dwfl->lookup_elts < need)
58 size_t n = dwfl->lookup_alloc == 0 ? 16 : dwfl->lookup_alloc * 2;
59 GElf_Addr *naddr = realloc (dwfl->lookup_addr, sizeof naddr[0] * n);
60 if (unlikely (naddr == NULL))
62 int *nsegndx = realloc (dwfl->lookup_segndx, sizeof nsegndx[0] * n);
63 if (unlikely (nsegndx == NULL))
65 if (naddr != dwfl->lookup_addr)
69 dwfl->lookup_alloc = n;
70 dwfl->lookup_addr = naddr;
71 dwfl->lookup_segndx = nsegndx;
73 if (dwfl->lookup_module != NULL)
75 /* Make sure this array is big enough too. */
76 Dwfl_Module **old = dwfl->lookup_module;
77 dwfl->lookup_module = realloc (dwfl->lookup_module,
78 sizeof dwfl->lookup_module[0] * n);
79 if (unlikely (dwfl->lookup_module == NULL))
87 if (unlikely (i < dwfl->lookup_elts))
89 const size_t move = dwfl->lookup_elts - i;
90 memmove (&dwfl->lookup_addr[i + need], &dwfl->lookup_addr[i],
91 move * sizeof dwfl->lookup_addr[0]);
92 memmove (&dwfl->lookup_segndx[i + need], &dwfl->lookup_segndx[i],
93 move * sizeof dwfl->lookup_segndx[0]);
94 if (dwfl->lookup_module != NULL)
95 memmove (&dwfl->lookup_module[i + need], &dwfl->lookup_module[i],
96 move * sizeof dwfl->lookup_module[0]);
101 dwfl->lookup_addr[i] = start;
102 dwfl->lookup_segndx[i] = segndx;
103 if (dwfl->lookup_module != NULL)
104 dwfl->lookup_module[i] = NULL;
108 dwfl->lookup_segndx[i - 1] = segndx;
112 dwfl->lookup_addr[i] = end;
113 dwfl->lookup_segndx[i] = -1;
114 if (dwfl->lookup_module != NULL)
115 dwfl->lookup_module[i] = NULL;
118 dwfl->lookup_elts += need;
124 lookup (Dwfl *dwfl, GElf_Addr address, int hint)
127 && address >= dwfl->lookup_addr[hint]
128 && ((size_t) hint + 1 == dwfl->lookup_elts
129 || address < dwfl->lookup_addr[hint + 1]))
132 /* Do binary search on the array indexed by module load address. */
133 size_t l = 0, u = dwfl->lookup_elts;
136 size_t idx = (l + u) / 2;
137 if (address < dwfl->lookup_addr[idx])
142 if (l == dwfl->lookup_elts || address < dwfl->lookup_addr[l])
151 reify_segments (Dwfl *dwfl)
156 for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
159 const GElf_Addr start = segment_start (dwfl, mod->low_addr);
160 const GElf_Addr end = segment_end (dwfl, mod->high_addr);
161 bool resized = false;
163 int idx = lookup (dwfl, start, hint);
164 if (unlikely (idx < 0))
166 /* Module starts below any segment. Insert a low one. */
167 if (unlikely (insert (dwfl, 0, start, end, -1)))
172 else if (dwfl->lookup_addr[idx] > start)
174 /* The module starts in the middle of this segment. Split it. */
175 if (unlikely (insert (dwfl, idx + 1, start, end,
176 dwfl->lookup_segndx[idx])))
181 else if (dwfl->lookup_addr[idx] < start)
183 /* The module starts past the end of this segment.
185 if (unlikely (insert (dwfl, idx + 1, start, end, -1)))
191 if ((size_t) idx + 1 < dwfl->lookup_elts
192 && end < dwfl->lookup_addr[idx + 1])
194 /* The module ends in the middle of this segment. Split it. */
195 if (unlikely (insert (dwfl, idx + 1,
196 end, dwfl->lookup_addr[idx + 1], -1)))
201 if (dwfl->lookup_module == NULL)
203 dwfl->lookup_module = calloc (dwfl->lookup_alloc,
204 sizeof dwfl->lookup_module[0]);
205 if (unlikely (dwfl->lookup_module == NULL))
209 /* Cache a backpointer in the module. */
212 /* Put MOD in the table for each segment that's inside it. */
214 dwfl->lookup_module[idx++] = mod;
215 while ((size_t) idx < dwfl->lookup_elts
216 && dwfl->lookup_addr[idx] < end);
217 assert (dwfl->lookup_module[mod->segment] == mod);
219 if (resized && idx - 1 >= highest)
220 /* Expanding the lookup tables invalidated backpointers
221 we've already stored. Reset those ones. */
225 hint = (size_t) idx < dwfl->lookup_elts ? idx : -1;
229 /* Reset backpointer indices invalidated by table insertions. */
230 for (size_t idx = 0; idx < dwfl->lookup_elts; ++idx)
231 if (dwfl->lookup_module[idx] != NULL)
232 dwfl->lookup_module[idx]->segment = idx;
238 dwfl_addrsegment (Dwfl *dwfl, Dwarf_Addr address, Dwfl_Module **mod)
240 if (unlikely (dwfl == NULL))
243 if (unlikely (dwfl->lookup_module == NULL)
245 && unlikely (reify_segments (dwfl)))
247 __libdwfl_seterrno (DWFL_E_NOMEM);
251 int idx = lookup (dwfl, address, -1);
252 if (likely (mod != NULL))
254 if (unlikely (idx < 0) || unlikely (dwfl->lookup_module == NULL))
258 *mod = dwfl->lookup_module[idx];
260 /* If this segment does not have a module, but the address is
261 the upper boundary of the previous segment's module, use that. */
262 if (*mod == NULL && idx > 0 && dwfl->lookup_addr[idx] == address)
264 *mod = dwfl->lookup_module[idx - 1];
265 if (*mod != NULL && (*mod)->high_addr != address)
271 if (likely (idx >= 0))
272 /* Translate internal segment table index to user segment index. */
273 idx = dwfl->lookup_segndx[idx];
277 INTDEF (dwfl_addrsegment)
280 dwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias,
287 ndx = dwfl->lookup_tail_ndx;
289 if (phdr->p_align > 1 && (dwfl->segment_align <= 1 ||
290 phdr->p_align < dwfl->segment_align))
291 dwfl->segment_align = phdr->p_align;
293 if (unlikely (dwfl->lookup_module != NULL))
295 free (dwfl->lookup_module);
296 dwfl->lookup_module = NULL;
299 GElf_Addr start = segment_start (dwfl, bias + phdr->p_vaddr);
300 GElf_Addr end = segment_end (dwfl, bias + phdr->p_vaddr + phdr->p_memsz);
302 /* Coalesce into the last one if contiguous and matching. */
303 if (ndx != dwfl->lookup_tail_ndx
305 || ident != dwfl->lookup_tail_ident
306 || start != dwfl->lookup_tail_vaddr
307 || phdr->p_offset != dwfl->lookup_tail_offset)
309 /* Normally just appending keeps us sorted. */
311 size_t i = dwfl->lookup_elts;
312 while (i > 0 && unlikely (start < dwfl->lookup_addr[i - 1]))
315 if (unlikely (insert (dwfl, i, start, end, ndx)))
317 __libdwfl_seterrno (DWFL_E_NOMEM);
322 dwfl->lookup_tail_ident = ident;
323 dwfl->lookup_tail_vaddr = end;
324 dwfl->lookup_tail_offset = end - bias - phdr->p_vaddr + phdr->p_offset;
325 dwfl->lookup_tail_ndx = ndx + 1;
329 INTDEF (dwfl_report_segment)