Imported Upstream version 0.155
[platform/upstream/elfutils.git] / libdwfl / derelocate.c
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 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
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
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 *
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
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
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 < 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 int
334 dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr)
335 {
336   if (unlikely (check_module (mod)))
337     return -1;
338
339   switch (mod->e_type)
340     {
341     case ET_REL:
342       return find_section (mod, addr);
343
344     case ET_DYN:
345       /* All relative to first and only relocation base: module start.  */
346       *addr -= mod->low_addr;
347       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 dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address,
361                              Dwarf_Addr *bias)
362 {
363   if (check_module (mod))
364     return NULL;
365
366   int idx = find_section (mod, address);
367   if (idx < 0)
368     return NULL;
369
370   if (mod->reloc_info->refs[idx].relocs != NULL)
371     {
372       assert (mod->e_type == ET_REL);
373
374       Elf_Scn *tscn = mod->reloc_info->refs[idx].scn;
375       Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs;
376       Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf,
377                                                       relocscn, tscn, true);
378       if (likely (result == DWFL_E_NOERROR))
379         mod->reloc_info->refs[idx].relocs = NULL;
380       else
381         {
382           __libdwfl_seterrno (result);
383           return NULL;
384         }
385     }
386
387   *bias = dwfl_adjusted_address (mod, 0);
388   return mod->reloc_info->refs[idx].scn;
389 }
390 INTDEF (dwfl_module_address_section)