Imported Upstream version 0.153
[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 Red Hat elfutils.
4
5    Red Hat elfutils is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by the
7    Free Software Foundation; version 2 of the License.
8
9    Red Hat elfutils is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with Red Hat elfutils; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
17
18    In addition, as a special exception, Red Hat, Inc. gives You the
19    additional right to link the code of Red Hat elfutils with code licensed
20    under any Open Source Initiative certified open source license
21    (http://www.opensource.org/licenses/index.php) which requires the
22    distribution of source code with any binary distribution and to
23    distribute linked combinations of the two.  Non-GPL Code permitted under
24    this exception must only link to the code of Red Hat elfutils through
25    those well defined interfaces identified in the file named EXCEPTION
26    found in the source code files (the "Approved Interfaces").  The files
27    of Non-GPL Code may instantiate templates or use macros or inline
28    functions from the Approved Interfaces without causing the resulting
29    work to be covered by the GNU General Public License.  Only Red Hat,
30    Inc. may make changes or additions to the list of Approved Interfaces.
31    Red Hat's grant of this exception is conditioned upon your not adding
32    any new exceptions.  If you wish to add a new Approved Interface or
33    exception, please contact Red Hat.  You must obey the GNU General Public
34    License in all respects for all of the Red Hat elfutils code and other
35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
36    covered by this exception.  If you modify this file, you may extend this
37    exception to your version of the file, but you are not obligated to do
38    so.  If you do not wish to provide this exception without modification,
39    you must delete this exception statement from your version and license
40    this file solely under the GPL without exception.
41
42    Red Hat elfutils is an included package of the Open Invention Network.
43    An included package of the Open Invention Network is a package for which
44    Open Invention Network licensees cross-license their patents.  No patent
45    license is granted, either expressly or impliedly, by designation as an
46    included package.  Should you wish to participate in the Open Invention
47    Network licensing program, please visit www.openinventionnetwork.com
48    <http://www.openinventionnetwork.com>.  */
49
50 #include "libdwflP.h"
51
52 struct dwfl_relocation
53 {
54   size_t count;
55   struct
56   {
57     Elf_Scn *scn;
58     Elf_Scn *relocs;
59     const char *name;
60     GElf_Addr start, end;
61   } refs[0];
62 };
63
64
65 struct secref
66 {
67   struct secref *next;
68   Elf_Scn *scn;
69   Elf_Scn *relocs;
70   const char *name;
71   GElf_Addr start, end;
72 };
73
74 static int
75 compare_secrefs (const void *a, const void *b)
76 {
77   struct secref *const *p1 = a;
78   struct secref *const *p2 = b;
79
80   /* No signed difference calculation is correct here, since the
81      terms are unsigned and could be more than INT64_MAX apart.  */
82   if ((*p1)->start < (*p2)->start)
83     return -1;
84   if ((*p1)->start > (*p2)->start)
85     return 1;
86
87   return 0;
88 }
89
90 static int
91 cache_sections (Dwfl_Module *mod)
92 {
93   if (likely (mod->reloc_info != NULL))
94     return mod->reloc_info->count;
95
96   struct secref *refs = NULL;
97   size_t nrefs = 0;
98
99   size_t shstrndx;
100   if (unlikely (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0))
101     {
102     elf_error:
103       __libdwfl_seterrno (DWFL_E_LIBELF);
104       return -1;
105     }
106
107   bool check_reloc_sections = false;
108   Elf_Scn *scn = NULL;
109   while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
110     {
111       GElf_Shdr shdr_mem;
112       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
113       if (shdr == NULL)
114         goto elf_error;
115
116       if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0
117           && mod->e_type == ET_REL)
118         {
119           /* This section might not yet have been looked at.  */
120           if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx,
121                                         elf_ndxscn (scn),
122                                         &shdr->sh_addr) != DWFL_E_NOERROR)
123             continue;
124           shdr = gelf_getshdr (scn, &shdr_mem);
125           if (unlikely (shdr == NULL))
126             goto elf_error;
127         }
128
129       if (shdr->sh_flags & SHF_ALLOC)
130         {
131           const char *name = elf_strptr (mod->main.elf, shstrndx,
132                                          shdr->sh_name);
133           if (unlikely (name == NULL))
134             goto elf_error;
135
136           struct secref *newref = alloca (sizeof *newref);
137           newref->scn = scn;
138           newref->relocs = NULL;
139           newref->name = name;
140           newref->start = dwfl_adjusted_address (mod, shdr->sh_addr);
141           newref->end = newref->start + shdr->sh_size;
142           newref->next = refs;
143           refs = newref;
144           ++nrefs;
145         }
146
147       if (mod->e_type == ET_REL
148           && shdr->sh_size != 0
149           && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
150           && mod->dwfl->callbacks->section_address != NULL)
151         {
152           if (shdr->sh_info < elf_ndxscn (scn))
153             {
154               /* We've already looked at the section these relocs apply to.  */
155               Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
156               if (likely (tscn != NULL))
157                 for (struct secref *sec = refs; sec != NULL; sec = sec->next)
158                   if (sec->scn == tscn)
159                     {
160                       sec->relocs = scn;
161                       break;
162                     }
163             }
164           else
165             /* We'll have to do a second pass.  */
166             check_reloc_sections = true;
167         }
168     }
169
170   mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
171   if (mod->reloc_info == NULL)
172     {
173       __libdwfl_seterrno (DWFL_E_NOMEM);
174       return -1;
175     }
176
177   struct secref **sortrefs = alloca (nrefs * sizeof sortrefs[0]);
178   for (size_t i = nrefs; i-- > 0; refs = refs->next)
179     sortrefs[i] = refs;
180   assert (refs == NULL);
181
182   qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs);
183
184   mod->reloc_info->count = nrefs;
185   for (size_t i = 0; i < nrefs; ++i)
186     {
187       mod->reloc_info->refs[i].name = sortrefs[i]->name;
188       mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
189       mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
190       mod->reloc_info->refs[i].start = sortrefs[i]->start;
191       mod->reloc_info->refs[i].end = sortrefs[i]->end;
192     }
193
194   if (unlikely (check_reloc_sections))
195     {
196       /* There was a reloc section that preceded its target section.
197          So we have to scan again now that we have cached all the
198          possible target sections we care about.  */
199
200       scn = NULL;
201       while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
202         {
203           GElf_Shdr shdr_mem;
204           GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
205           if (shdr == NULL)
206             goto elf_error;
207
208           if (shdr->sh_size != 0
209               && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA))
210             {
211               Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
212               if (likely (tscn != NULL))
213                 for (size_t i = 0; i < nrefs; ++i)
214                   if (mod->reloc_info->refs[i].scn == tscn)
215                     {
216                       mod->reloc_info->refs[i].relocs = scn;
217                       break;
218                     }
219             }
220         }
221     }
222
223   return nrefs;
224 }
225
226
227 int
228 dwfl_module_relocations (Dwfl_Module *mod)
229 {
230   if (mod == NULL)
231     return -1;
232
233   switch (mod->e_type)
234     {
235     case ET_REL:
236       return cache_sections (mod);
237
238     case ET_DYN:
239       return 1;
240
241     case ET_EXEC:
242       assert (mod->main.vaddr == mod->low_addr);
243       break;
244     }
245
246   return 0;
247 }
248
249 const char *
250 dwfl_module_relocation_info (Dwfl_Module *mod, unsigned int idx,
251                              Elf32_Word *shndxp)
252 {
253   if (mod == NULL)
254     return NULL;
255
256   switch (mod->e_type)
257     {
258     case ET_REL:
259       break;
260
261     case ET_DYN:
262       if (idx != 0)
263         return NULL;
264       if (shndxp)
265         *shndxp = SHN_ABS;
266       return "";
267
268     default:
269       return NULL;
270     }
271
272   if (cache_sections (mod) < 0)
273     return NULL;
274
275   struct dwfl_relocation *sections = mod->reloc_info;
276
277   if (idx >= sections->count)
278     return NULL;
279
280   if (shndxp)
281     *shndxp = elf_ndxscn (sections->refs[idx].scn);
282
283   return sections->refs[idx].name;
284 }
285
286 /* Check that MOD is valid and make sure its relocation has been done.  */
287 static bool
288 check_module (Dwfl_Module *mod)
289 {
290   if (INTUSE(dwfl_module_getsymtab) (mod) < 0)
291     {
292       Dwfl_Error error = dwfl_errno ();
293       if (error != DWFL_E_NO_SYMTAB)
294         {
295           __libdwfl_seterrno (error);
296           return true;
297         }
298     }
299
300   if (mod->dw == NULL)
301     {
302       Dwarf_Addr bias;
303       if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
304         {
305           Dwfl_Error error = dwfl_errno ();
306           if (error != DWFL_E_NO_DWARF)
307             {
308               __libdwfl_seterrno (error);
309               return true;
310             }
311         }
312     }
313
314   return false;
315 }
316
317 /* Find the index in MOD->reloc_info.refs containing *ADDR.  */
318 static int
319 find_section (Dwfl_Module *mod, Dwarf_Addr *addr)
320 {
321   if (cache_sections (mod) < 0)
322     return -1;
323
324   struct dwfl_relocation *sections = mod->reloc_info;
325
326   /* The sections are sorted by address, so we can use binary search.  */
327   size_t l = 0, u = sections->count;
328   while (l < u)
329     {
330       size_t idx = (l + u) / 2;
331       if (*addr < sections->refs[idx].start)
332         u = idx;
333       else if (*addr > sections->refs[idx].end)
334         l = idx + 1;
335       else
336         {
337           /* Consider the limit of a section to be inside it, unless it's
338              inside the next one.  A section limit address can appear in
339              line records.  */
340           if (*addr == sections->refs[idx].end
341               && idx < sections->count
342               && *addr == sections->refs[idx + 1].start)
343             ++idx;
344
345           *addr -= sections->refs[idx].start;
346           return idx;
347         }
348     }
349
350   __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH));
351   return -1;
352 }
353
354 int
355 dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr)
356 {
357   if (unlikely (check_module (mod)))
358     return -1;
359
360   switch (mod->e_type)
361     {
362     case ET_REL:
363       return find_section (mod, addr);
364
365     case ET_DYN:
366       /* All relative to first and only relocation base: module start.  */
367       *addr -= mod->low_addr;
368       break;
369
370     default:
371       /* Already absolute, dwfl_module_relocations returned zero.  We
372          shouldn't really have been called, but it's a harmless no-op.  */
373       break;
374     }
375
376   return 0;
377 }
378 INTDEF (dwfl_module_relocate_address)
379
380 Elf_Scn *
381 dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address,
382                              Dwarf_Addr *bias)
383 {
384   if (check_module (mod))
385     return NULL;
386
387   int idx = find_section (mod, address);
388   if (idx < 0)
389     return NULL;
390
391   if (mod->reloc_info->refs[idx].relocs != NULL)
392     {
393       assert (mod->e_type == ET_REL);
394
395       Elf_Scn *tscn = mod->reloc_info->refs[idx].scn;
396       Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs;
397       Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf,
398                                                       relocscn, tscn, true);
399       if (likely (result == DWFL_E_NOERROR))
400         mod->reloc_info->refs[idx].relocs = NULL;
401       else
402         {
403           __libdwfl_seterrno (result);
404           return NULL;
405         }
406     }
407
408   *bias = dwfl_adjusted_address (mod, 0);
409   return mod->reloc_info->refs[idx].scn;
410 }
411 INTDEF (dwfl_module_address_section)