1 /* Relocate debug information.
2 Copyright (C) 2005-2009 Red Hat, Inc.
3 This file is part of Red Hat elfutils.
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.
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.
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.
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.
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>. */
52 typedef uint8_t GElf_Byte;
54 /* Adjust *VALUE to add the load address of the SHNDX section.
55 We update the section header in place to cache the result. */
59 __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx,
60 Elf32_Word shndx, GElf_Addr *value)
62 assert (mod->e_type == ET_REL);
64 Elf_Scn *refscn = elf_getscn (elf, shndx);
65 GElf_Shdr refshdr_mem, *refshdr = gelf_getshdr (refscn, &refshdr_mem);
69 if (refshdr->sh_addr == 0 && (refshdr->sh_flags & SHF_ALLOC))
71 /* This is a loaded section. Find its actual
72 address and update the section header. */
74 if (*shstrndx == SHN_UNDEF
75 && unlikely (elf_getshdrstrndx (elf, shstrndx) < 0))
78 const char *name = elf_strptr (elf, *shstrndx, refshdr->sh_name);
79 if (unlikely (name == NULL))
82 if ((*mod->dwfl->callbacks->section_address) (MODCB_ARGS (mod),
87 if (refshdr->sh_addr == (Dwarf_Addr) -1l)
88 /* The callback indicated this section wasn't really loaded but we
90 refshdr->sh_addr = 0; /* Make no adjustment below. */
92 /* Update the in-core file's section header to show the final
93 load address (or unloadedness). This serves as a cache,
94 so we won't get here again for the same section. */
95 if (likely (refshdr->sh_addr != 0)
96 && unlikely (! gelf_update_shdr (refscn, refshdr)))
100 if (refshdr->sh_flags & SHF_ALLOC)
101 /* Apply the adjustment. */
102 *value += refshdr->sh_addr + mod->main.bias;
104 return DWFL_E_NOERROR;
108 /* Cache used by relocate_getsym. */
109 struct reloc_symtab_cache
113 Elf_Data *symxndxdata;
114 Elf_Data *symstrdata;
118 #define RELOC_SYMTAB_CACHE(cache) \
119 struct reloc_symtab_cache cache = \
120 { NULL, NULL, NULL, NULL, SHN_UNDEF, SHN_UNDEF }
122 /* This is just doing dwfl_module_getsym, except that we must always use
123 the symbol table in RELOCATED itself when it has one, not MOD->symfile. */
125 relocate_getsym (Dwfl_Module *mod,
126 Elf *relocated, struct reloc_symtab_cache *cache,
127 int symndx, GElf_Sym *sym, GElf_Word *shndx)
129 if (cache->symdata == NULL)
131 if (mod->symfile == NULL || mod->symfile->elf != relocated)
133 /* We have to look up the symbol table in the file we are
134 relocating, if it has its own. These reloc sections refer to
135 the symbol table in this file, and a symbol table in the main
136 file might not match. However, some tools did produce ET_REL
137 .debug files with relocs but no symtab of their own. */
139 while ((scn = elf_nextscn (relocated, scn)) != NULL)
141 GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
143 switch (shdr->sh_type)
148 cache->symelf = relocated;
149 cache->symdata = elf_getdata (scn, NULL);
150 cache->strtabndx = shdr->sh_link;
151 if (unlikely (cache->symdata == NULL))
152 return DWFL_E_LIBELF;
154 case SHT_SYMTAB_SHNDX:
155 cache->symxndxdata = elf_getdata (scn, NULL);
156 if (unlikely (cache->symxndxdata == NULL))
157 return DWFL_E_LIBELF;
160 if (cache->symdata != NULL && cache->symxndxdata != NULL)
164 if (cache->symdata == NULL)
166 /* We might not have looked for a symbol table file yet,
167 when coming from __libdwfl_relocate_section. */
168 if (unlikely (mod->symfile == NULL)
169 && unlikely (INTUSE(dwfl_module_getsymtab) (mod) < 0))
170 return dwfl_errno ();
172 /* The symbol table we have already cached is the one from
173 the file being relocated, so it's what we need. Or else
174 this is an ET_REL .debug file with no .symtab of its own;
175 the symbols refer to the section indices in the main file. */
176 cache->symelf = mod->symfile->elf;
177 cache->symdata = mod->symdata;
178 cache->symxndxdata = mod->symxndxdata;
179 cache->symstrdata = mod->symstrdata;
183 if (unlikely (gelf_getsymshndx (cache->symdata, cache->symxndxdata,
184 symndx, sym, shndx) == NULL))
185 return DWFL_E_LIBELF;
187 if (sym->st_shndx != SHN_XINDEX)
188 *shndx = sym->st_shndx;
190 switch (sym->st_shndx)
195 return DWFL_E_NOERROR;
198 return __libdwfl_relocate_value (mod, cache->symelf, &cache->symshstrndx,
199 *shndx, &sym->st_value);
202 /* Handle an undefined symbol. We really only support ET_REL for Linux
203 kernel modules, and offline archives. The behavior of the Linux module
204 loader is very simple and easy to mimic. It only matches magically
205 exported symbols, and we match any defined symbols. But we get the same
206 answer except when the module's symbols are undefined and would prevent
207 it from being loaded. */
209 resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
210 GElf_Sym *sym, GElf_Word shndx)
212 /* First we need its name. */
213 if (sym->st_name != 0)
215 if (symtab->symstrdata == NULL)
217 /* Cache the strtab for this symtab. */
218 assert (referer->symfile == NULL
219 || referer->symfile->elf != symtab->symelf);
220 symtab->symstrdata = elf_getdata (elf_getscn (symtab->symelf,
223 if (unlikely (symtab->symstrdata == NULL))
224 return DWFL_E_LIBELF;
226 if (unlikely (sym->st_name >= symtab->symstrdata->d_size))
227 return DWFL_E_BADSTROFF;
229 const char *name = symtab->symstrdata->d_buf;
230 name += sym->st_name;
232 for (Dwfl_Module *m = referer->dwfl->modulelist; m != NULL; m = m->next)
235 /* Get this module's symtab.
236 If we got a fresh error reading the table, report it.
237 If we just have no symbols in this module, no harm done. */
238 if (m->symdata == NULL
239 && m->symerr == DWFL_E_NOERROR
240 && INTUSE(dwfl_module_getsymtab) (m) < 0
241 && m->symerr != DWFL_E_NO_SYMTAB)
244 for (size_t ndx = 1; ndx < m->syments; ++ndx)
246 sym = gelf_getsymshndx (m->symdata, m->symxndxdata,
248 if (unlikely (sym == NULL))
249 return DWFL_E_LIBELF;
250 if (sym->st_shndx != SHN_XINDEX)
251 shndx = sym->st_shndx;
253 /* We are looking for a defined global symbol with a name. */
254 if (shndx == SHN_UNDEF || shndx == SHN_COMMON
255 || GELF_ST_BIND (sym->st_info) == STB_LOCAL
256 || sym->st_name == 0)
259 /* Get this candidate symbol's name. */
260 if (unlikely (sym->st_name >= m->symstrdata->d_size))
261 return DWFL_E_BADSTROFF;
262 const char *n = m->symstrdata->d_buf;
265 /* Does the name match? */
266 if (strcmp (name, n))
270 if (shndx == SHN_ABS) /* XXX maybe should apply bias? */
271 return DWFL_E_NOERROR;
273 if (m->e_type != ET_REL)
275 sym->st_value += m->symfile->bias;
276 return DWFL_E_NOERROR;
279 /* In an ET_REL file, the symbol table values are relative
280 to the section, not to the module's load base. */
281 size_t symshstrndx = SHN_UNDEF;
282 return __libdwfl_relocate_value (m, m->symfile->elf,
284 shndx, &sym->st_value);
289 return DWFL_E_RELUNDEF;
293 relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
294 size_t shstrndx, struct reloc_symtab_cache *reloc_symtab,
295 Elf_Scn *scn, GElf_Shdr *shdr,
296 Elf_Scn *tscn, bool debugscn, bool partial)
298 /* First, fetch the name of the section these relocations apply to. */
300 GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
301 const char *tname = elf_strptr (relocated, shstrndx, tshdr->sh_name);
303 return DWFL_E_LIBELF;
305 if (debugscn && ! ebl_debugscn_p (mod->ebl, tname))
306 /* This relocation section is not for a debugging section.
307 Nothing to do here. */
308 return DWFL_E_NOERROR;
310 /* Fetch the section data that needs the relocations applied. */
311 Elf_Data *tdata = elf_rawdata (tscn, NULL);
313 return DWFL_E_LIBELF;
315 /* Apply one relocation. Returns true for any invalid data. */
316 Dwfl_Error relocate (GElf_Addr offset, const GElf_Sxword *addend,
317 int rtype, int symndx)
319 /* First see if this is a reloc we can handle.
320 If we are skipping it, don't bother resolving the symbol. */
321 Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype);
322 if (unlikely (type == ELF_T_NUM))
323 return DWFL_E_BADRELTYPE;
325 /* First, resolve the symbol to an absolute value. */
328 if (symndx == STN_UNDEF)
329 /* When strip removes a section symbol referring to a
330 section moved into the debuginfo file, it replaces
331 that symbol index in relocs with STN_UNDEF. We
332 don't actually need the symbol, because those relocs
333 are always references relative to the nonallocated
334 debugging sections, which start at zero. */
340 Dwfl_Error error = relocate_getsym (mod, relocated, reloc_symtab,
341 symndx, &sym, &shndx);
342 if (unlikely (error != DWFL_E_NOERROR))
345 if (shndx == SHN_UNDEF || shndx == SHN_COMMON)
347 /* Maybe we can figure it out anyway. */
348 error = resolve_symbol (mod, reloc_symtab, &sym, shndx);
349 if (error != DWFL_E_NOERROR)
353 value = sym.st_value;
356 /* These are the types we can relocate. */
357 #define TYPES DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half); \
358 DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword); \
359 DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword)
363 #define DO_TYPE(NAME, Name) \
365 size = sizeof (GElf_##Name); \
370 return DWFL_E_BADRELTYPE;
373 if (offset + size > tdata->d_size)
374 return DWFL_E_BADRELOFF;
376 #define DO_TYPE(NAME, Name) GElf_##Name Name;
377 union { TYPES; } tmpbuf;
384 .d_version = EV_CURRENT,
389 .d_buf = tdata->d_buf + offset,
391 .d_version = EV_CURRENT,
394 /* XXX check for overflow? */
397 /* For the addend form, we have the value already. */
401 #define DO_TYPE(NAME, Name) \
403 tmpbuf.Name = value; \
413 /* Extract the original value and apply the reloc. */
414 Elf_Data *d = gelf_xlatetom (relocated, &tmpdata, &rdata,
415 ehdr->e_ident[EI_DATA]);
417 return DWFL_E_LIBELF;
418 assert (d == &tmpdata);
421 #define DO_TYPE(NAME, Name) \
423 tmpbuf.Name += (GElf_##Name) value; \
432 /* Now convert the relocated datum back to the target
433 format. This will write into rdata.d_buf, which
434 points into the raw section data being relocated. */
435 Elf_Data *s = gelf_xlatetof (relocated, &rdata, &tmpdata,
436 ehdr->e_ident[EI_DATA]);
438 return DWFL_E_LIBELF;
439 assert (s == &rdata);
441 /* We have applied this relocation! */
442 return DWFL_E_NOERROR;
445 /* Fetch the relocation section and apply each reloc in it. */
446 Elf_Data *reldata = elf_getdata (scn, NULL);
448 return DWFL_E_LIBELF;
450 Dwfl_Error result = DWFL_E_NOERROR;
451 bool first_badreltype = true;
452 inline void check_badreltype (void)
454 if (first_badreltype)
456 first_badreltype = false;
457 if (ebl_get_elfmachine (mod->ebl) == EM_NONE)
458 /* This might be because ebl_openbackend failed to find
459 any libebl_CPU.so library. Diagnose that clearly. */
460 result = DWFL_E_UNKNOWN_MACHINE;
464 size_t nrels = shdr->sh_size / shdr->sh_entsize;
466 if (shdr->sh_type == SHT_REL)
467 for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
469 GElf_Rel rel_mem, *r = gelf_getrel (reldata, relidx, &rel_mem);
471 return DWFL_E_LIBELF;
472 result = relocate (r->r_offset, NULL,
473 GELF_R_TYPE (r->r_info),
474 GELF_R_SYM (r->r_info));
480 /* We applied the relocation. Elide it. */
481 memset (&rel_mem, 0, sizeof rel_mem);
482 gelf_update_rel (reldata, relidx, &rel_mem);
485 case DWFL_E_BADRELTYPE:
486 case DWFL_E_RELUNDEF:
487 /* We couldn't handle this relocation. Skip it. */
488 result = DWFL_E_NOERROR;
495 for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
497 GElf_Rela rela_mem, *r = gelf_getrela (reldata, relidx,
500 return DWFL_E_LIBELF;
501 result = relocate (r->r_offset, &r->r_addend,
502 GELF_R_TYPE (r->r_info),
503 GELF_R_SYM (r->r_info));
509 /* We applied the relocation. Elide it. */
510 memset (&rela_mem, 0, sizeof rela_mem);
511 gelf_update_rela (reldata, relidx, &rela_mem);
514 case DWFL_E_BADRELTYPE:
515 case DWFL_E_RELUNDEF:
516 /* We couldn't handle this relocation. Skip it. */
517 result = DWFL_E_NOERROR;
524 if (likely (result == DWFL_E_NOERROR))
526 if (!partial || complete == nrels)
527 /* Mark this relocation section as being empty now that we have
528 done its work. This affects unstrip -R, so e.g. it emits an
529 empty .rela.debug_info along with a .debug_info that has
530 already been fully relocated. */
532 else if (complete != 0)
534 /* We handled some of the relocations but not all.
535 We've zeroed out the ones we processed.
536 Now remove them from the section. */
539 if (shdr->sh_type == SHT_REL)
540 for (size_t relidx = 0; relidx < nrels; ++relidx)
543 GElf_Rel *r = gelf_getrel (reldata, relidx, &rel_mem);
544 if (r->r_info != 0 || r->r_offset != 0)
547 gelf_update_rel (reldata, next, r);
552 for (size_t relidx = 0; relidx < nrels; ++relidx)
555 GElf_Rela *r = gelf_getrela (reldata, relidx, &rela_mem);
556 if (r->r_info != 0 || r->r_offset != 0 || r->r_addend != 0)
559 gelf_update_rela (reldata, next, r);
566 shdr->sh_size = reldata->d_size = nrels * shdr->sh_entsize;
567 gelf_update_shdr (scn, shdr);
575 __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug)
577 assert (mod->e_type == ET_REL);
580 const GElf_Ehdr *ehdr = gelf_getehdr (debugfile, &ehdr_mem);
582 return DWFL_E_LIBELF;
585 if (elf_getshdrstrndx (debugfile, &d_shstrndx) < 0)
586 return DWFL_E_LIBELF;
588 RELOC_SYMTAB_CACHE (reloc_symtab);
590 /* Look at each section in the debuginfo file, and process the
591 relocation sections for debugging sections. */
592 Dwfl_Error result = DWFL_E_NOERROR;
594 while (result == DWFL_E_NOERROR
595 && (scn = elf_nextscn (debugfile, scn)) != NULL)
598 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
600 if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
601 && shdr->sh_size != 0)
603 /* It's a relocation section. */
605 Elf_Scn *tscn = elf_getscn (debugfile, shdr->sh_info);
606 if (unlikely (tscn == NULL))
607 result = DWFL_E_LIBELF;
609 result = relocate_section (mod, debugfile, ehdr, d_shstrndx,
610 &reloc_symtab, scn, shdr, tscn,
620 __libdwfl_relocate_section (Dwfl_Module *mod, Elf *relocated,
621 Elf_Scn *relocscn, Elf_Scn *tscn, bool partial)
626 RELOC_SYMTAB_CACHE (reloc_symtab);
629 if (elf_getshdrstrndx (relocated, &shstrndx) < 0)
630 return DWFL_E_LIBELF;
632 return (__libdwfl_module_getebl (mod)
633 ?: relocate_section (mod, relocated,
634 gelf_getehdr (relocated, &ehdr_mem), shstrndx,
636 relocscn, gelf_getshdr (relocscn, &shdr_mem),
637 tscn, false, partial));