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