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