Branch data Line data Source code
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 : 8474 : compare_secrefs (const void *a, const void *b)
59 : : {
60 : 8474 : struct secref *const *p1 = a;
61 : 8474 : 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 [ + + ]: 8474 : if ((*p1)->start < (*p2)->start)
66 : : return -1;
67 [ + - ]: 54 : if ((*p1)->start > (*p2)->start)
68 : : return 1;
69 : :
70 [ + + ]: 54 : if ((*p1)->end < (*p2)->end)
71 : : return -1;
72 [ + + ]: 28 : if ((*p1)->end > (*p2)->end)
73 : : return 1;
74 : :
75 : : /* Same start/end, then just compare which section came first. */
76 : 16 : return elf_ndxscn ((*p1)->scn) - elf_ndxscn ((*p2)->scn);
77 : : }
78 : :
79 : : static int
80 : 265067 : cache_sections (Dwfl_Module *mod)
81 : : {
82 [ + + ]: 265067 : if (likely (mod->reloc_info != NULL))
83 : 264878 : return mod->reloc_info->count;
84 : :
85 : 189 : struct secref *refs = NULL;
86 : 189 : size_t nrefs = 0;
87 : :
88 : 189 : size_t shstrndx;
89 [ - + ]: 189 : if (unlikely (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0))
90 : : {
91 : 0 : elf_error:
92 : 0 : __libdwfl_seterrno (DWFL_E_LIBELF);
93 : 0 : nrefs = -1;
94 : 0 : goto free_refs;
95 : : }
96 : :
97 : : bool check_reloc_sections = false;
98 : : Elf_Scn *scn = NULL;
99 [ + + ]: 6045 : while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
100 : : {
101 : 5856 : GElf_Shdr shdr_mem;
102 : 5856 : GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
103 [ - + ]: 5856 : if (shdr == NULL)
104 : 0 : goto elf_error;
105 : :
106 [ + + + + ]: 5856 : if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0
107 [ + + ]: 41 : && mod->e_type == ET_REL)
108 : : {
109 : : /* This section might not yet have been looked at. */
110 [ - + ]: 40 : if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx,
111 : 40 : elf_ndxscn (scn),
112 : 40 : &shdr->sh_addr) != DWFL_E_NOERROR)
113 : 0 : continue;
114 : 40 : shdr = gelf_getshdr (scn, &shdr_mem);
115 [ - + ]: 40 : if (unlikely (shdr == NULL))
116 : 0 : goto elf_error;
117 : : }
118 : :
119 [ + + ]: 5856 : if (shdr->sh_flags & SHF_ALLOC)
120 : : {
121 : 8014 : const char *name = elf_strptr (mod->main.elf, shstrndx,
122 : 4007 : shdr->sh_name);
123 [ - + ]: 4007 : if (unlikely (name == NULL))
124 : 0 : goto elf_error;
125 : :
126 : 4007 : struct secref *newref = malloc (sizeof *newref);
127 [ - + ]: 4007 : if (unlikely (newref == NULL))
128 : : {
129 : 0 : nomem:
130 : 0 : __libdwfl_seterrno (DWFL_E_NOMEM);
131 : 0 : nrefs = -1;
132 : 0 : goto free_refs;
133 : : }
134 : :
135 : 4007 : newref->scn = scn;
136 : 4007 : newref->relocs = NULL;
137 : 4007 : newref->name = name;
138 : 4007 : newref->start = dwfl_adjusted_address (mod, shdr->sh_addr);
139 : 4007 : newref->end = newref->start + shdr->sh_size;
140 : 4007 : newref->next = refs;
141 : 4007 : refs = newref;
142 : 4007 : ++nrefs;
143 : : }
144 : :
145 [ + + ]: 5856 : if (mod->e_type == ET_REL
146 [ + + ]: 1166 : && shdr->sh_size != 0
147 [ + + ]: 882 : && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
148 [ - + ]: 34 : && mod->dwfl->callbacks->section_address != NULL)
149 : : {
150 [ + - ]: 34 : if (shdr->sh_info < elf_ndxscn (scn))
151 : : {
152 : : /* We've already looked at the section these relocs apply to. */
153 : 34 : Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
154 [ - + ]: 34 : if (likely (tscn != NULL))
155 [ + + ]: 58 : for (struct secref *sec = refs; sec != NULL; sec = sec->next)
156 [ + + ]: 56 : if (sec->scn == tscn)
157 : : {
158 : 32 : sec->relocs = scn;
159 : 32 : break;
160 : : }
161 : : }
162 : : else
163 : : /* We'll have to do a second pass. */
164 : : check_reloc_sections = true;
165 : : }
166 : : }
167 : :
168 : 189 : mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
169 [ - + ]: 189 : if (unlikely (mod->reloc_info == NULL))
170 : 0 : goto nomem;
171 : :
172 : 189 : struct secref **sortrefs = malloc (nrefs * sizeof sortrefs[0]);
173 [ - + ]: 189 : if (unlikely (sortrefs == NULL))
174 : 0 : goto nomem;
175 : :
176 [ + + ]: 4196 : for (size_t i = nrefs; i-- > 0; refs = refs->next)
177 : 4007 : sortrefs[i] = refs;
178 [ - + ]: 189 : assert (refs == NULL);
179 : :
180 : 189 : qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs);
181 : :
182 : 189 : mod->reloc_info->count = nrefs;
183 [ + + ]: 4196 : for (size_t i = 0; i < nrefs; ++i)
184 : : {
185 : 4007 : mod->reloc_info->refs[i].name = sortrefs[i]->name;
186 : 4007 : mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
187 : 4007 : mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
188 : 4007 : mod->reloc_info->refs[i].start = sortrefs[i]->start;
189 : 4007 : mod->reloc_info->refs[i].end = sortrefs[i]->end;
190 : 4007 : free (sortrefs[i]);
191 : : }
192 : :
193 : 189 : free (sortrefs);
194 : :
195 [ - + ]: 189 : if (unlikely (check_reloc_sections))
196 : : {
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 : : scn = NULL;
202 [ # # ]: 0 : while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
203 : : {
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 : : {
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 : : {
217 : 0 : mod->reloc_info->refs[i].relocs = scn;
218 : 0 : break;
219 : : }
220 : : }
221 : : }
222 : : }
223 : :
224 : 378 : free_refs:
225 [ - + ]: 189 : while (refs != NULL)
226 : : {
227 : 0 : struct secref *ref = refs;
228 : 0 : refs = ref->next;
229 : 0 : free (ref);
230 : : }
231 : :
232 : 189 : return nrefs;
233 : : }
234 : :
235 : :
236 : : int
237 : 1042179 : dwfl_module_relocations (Dwfl_Module *mod)
238 : : {
239 [ + - ]: 1042179 : if (mod == NULL)
240 : : return -1;
241 : :
242 [ + + - + ]: 1042179 : switch (mod->e_type)
243 : : {
244 : 87113 : case ET_REL:
245 : 87113 : return cache_sections (mod);
246 : :
247 : : case ET_DYN:
248 : : return 1;
249 : :
250 : 1417 : case ET_EXEC:
251 [ - + ]: 1417 : assert (mod->main.vaddr == mod->low_addr);
252 : : break;
253 : : }
254 : :
255 : 1417 : return 0;
256 : : }
257 : :
258 : : const char *
259 : 1040771 : dwfl_module_relocation_info (Dwfl_Module *mod, unsigned int idx,
260 : : Elf32_Word *shndxp)
261 : : {
262 [ - + ]: 1040771 : if (mod == NULL)
263 : : return NULL;
264 : :
265 [ - + + ]: 1040771 : switch (mod->e_type)
266 : : {
267 : : case ET_REL:
268 : 87120 : break;
269 : :
270 : 953651 : case ET_DYN:
271 [ - + ]: 953651 : if (idx != 0)
272 : : return NULL;
273 [ - + ]: 953651 : if (shndxp)
274 : 0 : *shndxp = SHN_ABS;
275 : : return "";
276 : :
277 : : default:
278 : : return NULL;
279 : : }
280 : :
281 [ - + ]: 87120 : if (cache_sections (mod) < 0)
282 : : return NULL;
283 : :
284 : 87120 : struct dwfl_relocation *sections = mod->reloc_info;
285 : :
286 [ - + ]: 87120 : if (idx >= sections->count)
287 : : return NULL;
288 : :
289 [ - + ]: 87120 : if (shndxp)
290 : 0 : *shndxp = elf_ndxscn (sections->refs[idx].scn);
291 : :
292 : 87120 : return sections->refs[idx].name;
293 : : }
294 : :
295 : : /* Check that MOD is valid and make sure its relocation has been done. */
296 : : static bool
297 : 1041026 : check_module (Dwfl_Module *mod)
298 : : {
299 [ - + ]: 1041026 : if (mod == NULL)
300 : : return true;
301 : :
302 [ - + ]: 1041026 : if (INTUSE(dwfl_module_getsymtab) (mod) < 0)
303 : : {
304 : 0 : Dwfl_Error error = dwfl_errno ();
305 [ # # ]: 0 : if (error != DWFL_E_NO_SYMTAB)
306 : : {
307 : 0 : __libdwfl_seterrno (error);
308 : 0 : return true;
309 : : }
310 : : }
311 : :
312 [ + + ]: 1041026 : if (mod->dw == NULL)
313 : : {
314 : 167 : Dwarf_Addr bias;
315 [ + + ]: 167 : if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
316 : : {
317 : 156 : Dwfl_Error error = dwfl_errno ();
318 [ - + ]: 156 : if (error != DWFL_E_NO_DWARF)
319 : : {
320 : 0 : __libdwfl_seterrno (error);
321 : 0 : return true;
322 : : }
323 : : }
324 : : }
325 : :
326 : : return false;
327 : : }
328 : :
329 : : /* Find the index in MOD->reloc_info.refs containing *ADDR. */
330 : : static int
331 : 90834 : find_section (Dwfl_Module *mod, Dwarf_Addr *addr)
332 : : {
333 [ - + ]: 90834 : if (cache_sections (mod) < 0)
334 : : return -1;
335 : :
336 : 90834 : struct dwfl_relocation *sections = mod->reloc_info;
337 : :
338 : : /* The sections are sorted by address, so we can use binary search. */
339 : 90834 : size_t l = 0, u = sections->count;
340 [ + + ]: 525569 : while (l < u)
341 : : {
342 : 434728 : size_t idx = (l + u) / 2;
343 [ + + ]: 434728 : if (*addr < sections->refs[idx].start)
344 : : u = idx;
345 [ + + ]: 96712 : else if (*addr > sections->refs[idx].end)
346 : 5885 : l = idx + 1;
347 : : else
348 : : {
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 [ + + ]: 90827 : if (*addr == sections->refs[idx].end
353 [ + + ]: 183 : && idx + 1 < sections->count
354 [ + + ]: 182 : && *addr == sections->refs[idx + 1].start)
355 : 90827 : ++idx;
356 : :
357 : 90827 : *addr -= sections->refs[idx].start;
358 : 90827 : return idx;
359 : : }
360 : : }
361 : :
362 : 7 : __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH));
363 : 7 : return -1;
364 : : }
365 : :
366 : : size_t
367 : : internal_function
368 : 3636 : __libdwfl_find_section_ndx (Dwfl_Module *mod, Dwarf_Addr *addr)
369 : : {
370 : 3636 : int idx = find_section (mod, addr);
371 [ + + ]: 3636 : if (unlikely (idx == -1))
372 : : return SHN_UNDEF;
373 : :
374 : 3629 : return elf_ndxscn (mod->reloc_info->refs[idx].scn);
375 : : }
376 : :
377 : : int
378 : 1040952 : dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr)
379 : : {
380 [ + - ]: 1040952 : if (unlikely (check_module (mod)))
381 : : return -1;
382 : :
383 [ + + + ]: 1040952 : switch (mod->e_type)
384 : : {
385 : 87124 : case ET_REL:
386 : 87124 : return find_section (mod, addr);
387 : :
388 : 953812 : case ET_DYN:
389 : : /* All relative to first and only relocation base: module start. */
390 : 953812 : *addr -= mod->low_addr;
391 : 953812 : break;
392 : :
393 : : 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 : : break;
397 : : }
398 : :
399 : : return 0;
400 : : }
401 : : INTDEF (dwfl_module_relocate_address)
402 : :
403 : : Elf_Scn *
404 : 74 : dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address,
405 : : Dwarf_Addr *bias)
406 : : {
407 [ - + ]: 74 : if (check_module (mod))
408 : : return NULL;
409 : :
410 : 74 : int idx = find_section (mod, address);
411 [ - + ]: 74 : if (idx < 0)
412 : : return NULL;
413 : :
414 [ + + ]: 74 : if (mod->reloc_info->refs[idx].relocs != NULL)
415 : : {
416 [ - + ]: 2 : assert (mod->e_type == ET_REL);
417 : :
418 : 2 : Elf_Scn *tscn = mod->reloc_info->refs[idx].scn;
419 : 2 : Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs;
420 : 2 : Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf,
421 : : relocscn, tscn, true);
422 [ + - ]: 2 : if (likely (result == DWFL_E_NOERROR))
423 : 2 : mod->reloc_info->refs[idx].relocs = NULL;
424 : : else
425 : : {
426 : 0 : __libdwfl_seterrno (result);
427 : 0 : return NULL;
428 : : }
429 : : }
430 : :
431 : 74 : *bias = dwfl_adjusted_address (mod, 0);
432 : 74 : return mod->reloc_info->refs[idx].scn;
433 : : }
434 : : INTDEF (dwfl_module_address_section)
|