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