• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
compare_secrefs(const void * a,const void * b)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
cache_sections(Dwfl_Module * mod)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
dwfl_module_relocations(Dwfl_Module * mod)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 *
dwfl_module_relocation_info(Dwfl_Module * mod,unsigned int idx,Elf32_Word * shndxp)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
check_module(Dwfl_Module * mod)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
find_section(Dwfl_Module * mod,Dwarf_Addr * addr)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
__libdwfl_find_section_ndx(Dwfl_Module * mod,Dwarf_Addr * addr)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
dwfl_module_relocate_address(Dwfl_Module * mod,Dwarf_Addr * addr)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 }
INTDEF(dwfl_module_relocate_address)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)
435