1 /* Recover relocatibility for addresses computed from debug information.
2 Copyright (C) 2005, 2006, 2007, 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
52 struct dwfl_relocation
53 {
54 size_t count;
55 struct
56 {
57 Elf_Scn *scn;
58 Elf_Scn *relocs;
59 const char *name;
60 GElf_Addr start, end;
61 } refs[0];
62 };
63
64
65 struct secref
66 {
67 struct secref *next;
68 Elf_Scn *scn;
69 Elf_Scn *relocs;
70 const char *name;
71 GElf_Addr start, end;
72 };
73
74 static int
compare_secrefs(const void * a,const void * b)75 compare_secrefs (const void *a, const void *b)
76 {
77 struct secref *const *p1 = a;
78 struct secref *const *p2 = b;
79
80 /* No signed difference calculation is correct here, since the
81 terms are unsigned and could be more than INT64_MAX apart. */
82 if ((*p1)->start < (*p2)->start)
83 return -1;
84 if ((*p1)->start > (*p2)->start)
85 return 1;
86
87 return 0;
88 }
89
90 static int
cache_sections(Dwfl_Module * mod)91 cache_sections (Dwfl_Module *mod)
92 {
93 struct secref *refs = NULL;
94 size_t nrefs = 0;
95
96 size_t shstrndx;
97 if (unlikely (elf_getshstrndx (mod->main.elf, &shstrndx) < 0))
98 {
99 elf_error:
100 __libdwfl_seterrno (DWFL_E_LIBELF);
101 return -1;
102 }
103
104 bool check_reloc_sections = false;
105 Elf_Scn *scn = NULL;
106 while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
107 {
108 GElf_Shdr shdr_mem;
109 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
110 if (shdr == NULL)
111 goto elf_error;
112
113 if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0)
114 {
115 /* This section might not yet have been looked at. */
116 if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx,
117 elf_ndxscn (scn),
118 &shdr->sh_addr) != DWFL_E_NOERROR)
119 continue;
120 shdr = gelf_getshdr (scn, &shdr_mem);
121 if (unlikely (shdr == NULL))
122 goto elf_error;
123 }
124
125 if (shdr->sh_flags & SHF_ALLOC)
126 {
127 const char *name = elf_strptr (mod->main.elf, shstrndx,
128 shdr->sh_name);
129 if (unlikely (name == NULL))
130 goto elf_error;
131
132 struct secref *newref = alloca (sizeof *newref);
133 newref->scn = scn;
134 newref->relocs = NULL;
135 newref->name = name;
136 newref->start = shdr->sh_addr + mod->main.bias;
137 newref->end = newref->start + shdr->sh_size;
138 newref->next = refs;
139 refs = newref;
140 ++nrefs;
141 }
142
143 if (mod->e_type == ET_REL
144 && shdr->sh_size != 0
145 && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
146 && mod->dwfl->callbacks->section_address != NULL)
147 {
148 if (shdr->sh_info < elf_ndxscn (scn))
149 {
150 /* We've already looked at the section these relocs apply to. */
151 Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
152 if (likely (tscn != NULL))
153 for (struct secref *sec = refs; sec != NULL; sec = sec->next)
154 if (sec->scn == tscn)
155 {
156 sec->relocs = scn;
157 break;
158 }
159 }
160 else
161 /* We'll have to do a second pass. */
162 check_reloc_sections = true;
163 }
164 }
165
166 mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
167 if (mod->reloc_info == NULL)
168 {
169 __libdwfl_seterrno (DWFL_E_NOMEM);
170 return -1;
171 }
172
173 struct secref **sortrefs = alloca (nrefs * sizeof sortrefs[0]);
174 for (size_t i = nrefs; i-- > 0; refs = refs->next)
175 sortrefs[i] = refs;
176 assert (refs == NULL);
177
178 qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs);
179
180 mod->reloc_info->count = nrefs;
181 for (size_t i = 0; i < nrefs; ++i)
182 {
183 mod->reloc_info->refs[i].name = sortrefs[i]->name;
184 mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
185 mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
186 mod->reloc_info->refs[i].start = sortrefs[i]->start;
187 mod->reloc_info->refs[i].end = sortrefs[i]->end;
188 }
189
190 if (unlikely (check_reloc_sections))
191 {
192 /* There was a reloc section that preceded its target section.
193 So we have to scan again now that we have cached all the
194 possible target sections we care about. */
195
196 scn = NULL;
197 while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
198 {
199 GElf_Shdr shdr_mem;
200 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
201 if (shdr == NULL)
202 goto elf_error;
203
204 if (shdr->sh_size != 0
205 && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA))
206 {
207 Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
208 if (likely (tscn != NULL))
209 for (size_t i = 0; i < nrefs; ++i)
210 if (mod->reloc_info->refs[i].scn == tscn)
211 {
212 mod->reloc_info->refs[i].relocs = scn;
213 break;
214 }
215 }
216 }
217 }
218
219 return nrefs;
220 }
221
222
223 int
dwfl_module_relocations(Dwfl_Module * mod)224 dwfl_module_relocations (Dwfl_Module *mod)
225 {
226 if (mod == NULL)
227 return -1;
228
229 if (mod->reloc_info != NULL)
230 return mod->reloc_info->count;
231
232 switch (mod->e_type)
233 {
234 case ET_REL:
235 return cache_sections (mod);
236
237 case ET_DYN:
238 return 1;
239
240 case ET_EXEC:
241 assert (mod->main.bias == 0);
242 assert (mod->debug.bias == 0);
243 break;
244 }
245
246 return 0;
247 }
248
249 const char *
dwfl_module_relocation_info(Dwfl_Module * mod,unsigned int idx,Elf32_Word * shndxp)250 dwfl_module_relocation_info (Dwfl_Module *mod, unsigned int idx,
251 Elf32_Word *shndxp)
252 {
253 if (mod == NULL)
254 return NULL;
255
256 switch (mod->e_type)
257 {
258 case ET_REL:
259 break;
260
261 case ET_DYN:
262 if (idx != 0)
263 return NULL;
264 if (shndxp)
265 *shndxp = SHN_ABS;
266 return "";
267
268 default:
269 return NULL;
270 }
271
272 if (unlikely (mod->reloc_info == NULL) && cache_sections (mod) < 0)
273 return NULL;
274
275 struct dwfl_relocation *sections = mod->reloc_info;
276
277 if (idx >= sections->count)
278 return NULL;
279
280 if (shndxp)
281 *shndxp = elf_ndxscn (sections->refs[idx].scn);
282
283 return sections->refs[idx].name;
284 }
285
286 /* Check that MOD is valid and make sure its relocation has been done. */
287 static bool
check_module(Dwfl_Module * mod)288 check_module (Dwfl_Module *mod)
289 {
290 if (INTUSE(dwfl_module_getsymtab) (mod) < 0)
291 {
292 Dwfl_Error error = dwfl_errno ();
293 if (error != DWFL_E_NO_SYMTAB)
294 {
295 __libdwfl_seterrno (error);
296 return true;
297 }
298 }
299
300 if (mod->dw == NULL)
301 {
302 Dwarf_Addr bias;
303 if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
304 {
305 Dwfl_Error error = dwfl_errno ();
306 if (error != DWFL_E_NO_DWARF)
307 {
308 __libdwfl_seterrno (error);
309 return true;
310 }
311 }
312 }
313
314 return false;
315 }
316
317 /* Find the index in MOD->reloc_info.refs containing *ADDR. */
318 static int
find_section(Dwfl_Module * mod,Dwarf_Addr * addr)319 find_section (Dwfl_Module *mod, Dwarf_Addr *addr)
320 {
321 if (unlikely (mod->reloc_info == NULL) && cache_sections (mod) < 0)
322 return -1;
323
324 struct dwfl_relocation *sections = mod->reloc_info;
325
326 /* The sections are sorted by address, so we can use binary search. */
327 size_t l = 0, u = sections->count;
328 while (l < u)
329 {
330 size_t idx = (l + u) / 2;
331 if (*addr < sections->refs[idx].start)
332 u = idx;
333 else if (*addr > sections->refs[idx].end)
334 l = idx + 1;
335 else
336 {
337 /* Consider the limit of a section to be inside it, unless it's
338 inside the next one. A section limit address can appear in
339 line records. */
340 if (*addr == sections->refs[idx].end
341 && idx < sections->count
342 && *addr == sections->refs[idx + 1].start)
343 ++idx;
344
345 *addr -= sections->refs[idx].start;
346 return idx;
347 }
348 }
349
350 __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH));
351 return -1;
352 }
353
354 int
dwfl_module_relocate_address(Dwfl_Module * mod,Dwarf_Addr * addr)355 dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr)
356 {
357 if (unlikely (check_module (mod)))
358 return -1;
359
360 switch (mod->e_type)
361 {
362 case ET_REL:
363 return find_section (mod, addr);
364
365 case ET_DYN:
366 /* All relative to first and only relocation base: module start. */
367 *addr -= mod->low_addr;
368 break;
369
370 default:
371 /* Already absolute, dwfl_module_relocations returned zero. We
372 shouldn't really have been called, but it's a harmless no-op. */
373 break;
374 }
375
376 return 0;
377 }
INTDEF(dwfl_module_relocate_address)378 INTDEF (dwfl_module_relocate_address)
379
380 Elf_Scn *
381 dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address,
382 Dwarf_Addr *bias)
383 {
384 if (check_module (mod))
385 return NULL;
386
387 int idx = find_section (mod, address);
388 if (idx < 0)
389 return NULL;
390
391 if (mod->reloc_info->refs[idx].relocs != NULL)
392 {
393 assert (mod->e_type == ET_REL);
394
395 Elf_Scn *tscn = mod->reloc_info->refs[idx].scn;
396 Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs;
397 Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf,
398 relocscn, tscn, true);
399 if (likely (result == DWFL_E_NOERROR))
400 mod->reloc_info->refs[idx].relocs = NULL;
401 else
402 {
403 __libdwfl_seterrno (result);
404 return NULL;
405 }
406 }
407
408 *bias = mod->main.bias;
409 return mod->reloc_info->refs[idx].scn;
410 }
411 INTDEF (dwfl_module_address_section)
412