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