blob: 3c1f8bc6da96c7c91b7bb3adc9b3fcce8e344ef7 [file] [log] [blame]
Roland McGrathb4d6f0f2008-08-25 22:55:17 +00001/* Manage address space lookup table for libdwfl.
2 Copyright (C) 2008 Red Hat, Inc.
3 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 {
86 free (naddr);
87 return true;
88 }
89 dwfl->lookup_alloc = n;
90 dwfl->lookup_addr = naddr;
91 dwfl->lookup_segndx = nsegndx;
92 }
93
94 if (unlikely (i < dwfl->lookup_elts))
95 {
96 memcpy (&dwfl->lookup_addr[i + need], &dwfl->lookup_addr[i],
97 need * sizeof dwfl->lookup_addr[0]);
98 memcpy (&dwfl->lookup_segndx[i + need], &dwfl->lookup_segndx[i],
99 need * sizeof dwfl->lookup_segndx[0]);
100 if (dwfl->lookup_module != NULL)
101 memcpy (&dwfl->lookup_module[i + need], &dwfl->lookup_module[i],
102 need * sizeof dwfl->lookup_module[0]);
103 }
104
105 if (need_start)
106 {
107 dwfl->lookup_addr[i] = start;
108 dwfl->lookup_segndx[i] = segndx;
109 ++i;
110 }
111 else
112 dwfl->lookup_segndx[i - 1] = segndx;
113
114 if (need_end)
115 {
116 dwfl->lookup_addr[i] = end;
117 dwfl->lookup_segndx[i] = -1;
118 }
119
120 dwfl->lookup_elts += need;
121
122 return false;
123}
124
125static int
126lookup (Dwfl *dwfl, GElf_Addr address, int hint)
127{
128 if (hint >= 0
129 && address >= dwfl->lookup_addr[hint]
130 && ((size_t) hint + 1 == dwfl->lookup_elts
131 || address <= dwfl->lookup_addr[hint + 1]))
132 return hint;
133
134 /* Do binary search on the array indexed by module load address. */
135 size_t l = 0, u = dwfl->lookup_elts;
136 while (l < u)
137 {
138 size_t idx = (l + u) / 2;
139 if (address < dwfl->lookup_addr[idx])
140 u = idx;
141 else
142 {
143 l = idx + 1;
144 if (l == dwfl->lookup_elts || address < dwfl->lookup_addr[l])
145 return idx;
146 }
147 }
148
149 return -1;
150}
151
152static bool
153reify_segments (Dwfl *dwfl)
154{
155 int hint = -1;
156 for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
157 if (! mod->gc)
158 {
159 const GElf_Addr start = segment_start (dwfl, mod->low_addr);
160 const GElf_Addr end = segment_end (dwfl, mod->high_addr);
161
162 int idx = lookup (dwfl, start, hint);
163 if (unlikely (idx < 0))
164 {
165 /* Module starts below any segment. Insert a low one. */
166 if (unlikely (insert (dwfl, 0, start, end, -1)))
167 return true;
168 idx = 0;
169 }
170 else if (dwfl->lookup_addr[idx] > start)
171 {
172 /* The module starts in the middle of this segment. Split it. */
173 if (unlikely (insert (dwfl, idx + 1, start, end,
174 dwfl->lookup_segndx[idx])))
175 return true;
176 ++idx;
177 }
178
179 if ((size_t) idx + 1 < dwfl->lookup_elts
180 && end < dwfl->lookup_addr[idx + 1]
181 /* The module ends in the middle of this segment. Split it. */
182 && unlikely (insert (dwfl, idx + 1,
183 end, dwfl->lookup_addr[idx + 1], -1)))
184 return true;
185
186 if (dwfl->lookup_module == NULL)
187 {
188 dwfl->lookup_module = calloc (dwfl->lookup_alloc,
189 sizeof dwfl->lookup_module[0]);
190 if (unlikely (dwfl->lookup_module == NULL))
191 return true;
192 }
193
194 /* Cache a backpointer in the module. */
195 mod->segment = idx;
196
197 /* Put MOD in the table for each segment that's inside it. */
198 do
199 dwfl->lookup_module[idx++] = mod;
200 while ((size_t) idx < dwfl->lookup_elts
201 && dwfl->lookup_addr[idx] < end);
202 hint = (size_t) idx < dwfl->lookup_elts ? idx : -1;
203 }
204
205 return false;
206}
207
208int
209dwfl_addrsegment (Dwfl *dwfl, Dwarf_Addr address, Dwfl_Module **mod)
210{
211 if (unlikely (dwfl == NULL))
212 return -1;
213
214 if (unlikely (dwfl->lookup_module == NULL)
215 && mod != NULL
216 && unlikely (reify_segments (dwfl)))
217 {
218 __libdwfl_seterrno (DWFL_E_NOMEM);
219 return -1;
220 }
221
222 int idx = lookup (dwfl, address, -1);
223 if (likely (mod != NULL))
224 {
225 if (unlikely (idx < 0) || unlikely (dwfl->lookup_module == NULL))
226 *mod = NULL;
227 else
228 {
229 *mod = dwfl->lookup_module[idx];
230
231 /* If this segment does not have a module, but the address is
232 the upper boundary of the previous segment's module, use that. */
233 if (*mod == NULL && idx > 0 && dwfl->lookup_addr[idx] == address)
234 {
235 *mod = dwfl->lookup_module[idx - 1];
236 if (*mod != NULL && (*mod)->high_addr != address)
237 *mod = NULL;
238 }
239 }
240 }
241
242 if (likely (idx >= 0))
243 /* Translate internal segment table index to user segment index. */
244 idx = dwfl->lookup_segndx[idx];
245
246 return idx;
247}
248INTDEF (dwfl_addrsegment)
249
250int
251dwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias,
252 const void *ident)
253{
254 if (dwfl == NULL)
255 return -1;
256
257 if (ndx < 0)
258 ndx = dwfl->lookup_tail_ndx;
259
260 if (phdr->p_align > 1 && (dwfl->segment_align <= 1 ||
261 phdr->p_align < dwfl->segment_align))
262 dwfl->segment_align = phdr->p_align;
263
264 GElf_Addr start = segment_start (dwfl, bias + phdr->p_vaddr);
265 GElf_Addr end = segment_end (dwfl, bias + phdr->p_vaddr + phdr->p_memsz);
266
267 /* Coalesce into the last one if contiguous and matching. */
268 if (ndx != dwfl->lookup_tail_ndx
269 || ident == NULL
270 || ident != dwfl->lookup_tail_ident
271 || start != dwfl->lookup_tail_vaddr
272 || phdr->p_offset != dwfl->lookup_tail_offset)
273 {
274 /* Normally just appending keeps us sorted. */
275
276 size_t i = dwfl->lookup_elts;
277 while (i > 0 && unlikely (start < dwfl->lookup_addr[i - 1]))
278 --i;
279
280 if (unlikely (insert (dwfl, i, start, end, ndx)))
281 {
282 __libdwfl_seterrno (DWFL_E_NOMEM);
283 return -1;
284 }
285 }
286
287 dwfl->lookup_tail_ident = ident;
288 dwfl->lookup_tail_vaddr = end;
289 dwfl->lookup_tail_offset = end - bias - phdr->p_vaddr + phdr->p_offset;
290 dwfl->lookup_tail_ndx = ndx + 1;
291
292 if (unlikely (dwfl->lookup_module != NULL))
293 {
294 free (dwfl->lookup_module);
295 dwfl->lookup_module = NULL;
296 }
297
298 return ndx;
299}
300INTDEF (dwfl_report_segment)