|
Packit Service |
97d2fb |
/* Relocate debug information.
|
|
Packit Service |
97d2fb |
Copyright (C) 2005-2011, 2014, 2016, 2018 Red Hat, Inc.
|
|
Packit Service |
97d2fb |
This file is part of elfutils.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
This file is free software; you can redistribute it and/or modify
|
|
Packit Service |
97d2fb |
it under the terms of either
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
* the GNU Lesser General Public License as published by the Free
|
|
Packit Service |
97d2fb |
Software Foundation; either version 3 of the License, or (at
|
|
Packit Service |
97d2fb |
your option) any later version
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
or
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
* the GNU General Public License as published by the Free
|
|
Packit Service |
97d2fb |
Software Foundation; either version 2 of the License, or (at
|
|
Packit Service |
97d2fb |
your option) any later version
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
or both in parallel, as here.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
elfutils is distributed in the hope that it will be useful, but
|
|
Packit Service |
97d2fb |
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
97d2fb |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit Service |
97d2fb |
General Public License for more details.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
You should have received copies of the GNU General Public License and
|
|
Packit Service |
97d2fb |
the GNU Lesser General Public License along with this program. If
|
|
Packit Service |
97d2fb |
not, see <http://www.gnu.org/licenses/>. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#ifdef HAVE_CONFIG_H
|
|
Packit Service |
97d2fb |
# include <config.h>
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#include "libelfP.h"
|
|
Packit Service |
97d2fb |
#include "libdwflP.h"
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
typedef uint8_t GElf_Byte;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Adjust *VALUE to add the load address of the SHNDX section.
|
|
Packit Service |
97d2fb |
We update the section header in place to cache the result. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Dwfl_Error
|
|
Packit Service |
97d2fb |
internal_function
|
|
Packit Service |
97d2fb |
__libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx,
|
|
Packit Service |
97d2fb |
Elf32_Word shndx, GElf_Addr *value)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* No adjustment needed for section zero, it is never loaded.
|
|
Packit Service |
97d2fb |
Handle it first, just in case the ELF file has strange section
|
|
Packit Service |
97d2fb |
zero flags set. */
|
|
Packit Service |
97d2fb |
if (shndx == 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Elf_Scn *refscn = elf_getscn (elf, shndx);
|
|
Packit Service |
97d2fb |
GElf_Shdr refshdr_mem, *refshdr = gelf_getshdr (refscn, &refshdr_mem);
|
|
Packit Service |
97d2fb |
if (refshdr == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (refshdr->sh_addr == 0 && (refshdr->sh_flags & SHF_ALLOC))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* This is a loaded section. Find its actual
|
|
Packit Service |
97d2fb |
address and update the section header. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (*shstrndx == SHN_UNDEF
|
|
Packit Service |
97d2fb |
&& unlikely (elf_getshdrstrndx (elf, shstrndx) < 0))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
const char *name = elf_strptr (elf, *shstrndx, refshdr->sh_name);
|
|
Packit Service |
97d2fb |
if (unlikely (name == NULL))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if ((*mod->dwfl->callbacks->section_address) (MODCB_ARGS (mod),
|
|
Packit Service |
97d2fb |
name, shndx, refshdr,
|
|
Packit Service |
97d2fb |
&refshdr->sh_addr))
|
|
Packit Service |
97d2fb |
return CBFAIL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (refshdr->sh_addr == (Dwarf_Addr) -1l)
|
|
Packit Service |
97d2fb |
/* The callback indicated this section wasn't really loaded but we
|
|
Packit Service |
97d2fb |
don't really care. */
|
|
Packit Service |
97d2fb |
refshdr->sh_addr = 0; /* Make no adjustment below. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Update the in-core file's section header to show the final
|
|
Packit Service |
97d2fb |
load address (or unloadedness). This serves as a cache,
|
|
Packit Service |
97d2fb |
so we won't get here again for the same section. */
|
|
Packit Service |
97d2fb |
if (likely (refshdr->sh_addr != 0)
|
|
Packit Service |
97d2fb |
&& unlikely (! gelf_update_shdr (refscn, refshdr)))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (refshdr->sh_flags & SHF_ALLOC)
|
|
Packit Service |
97d2fb |
/* Apply the adjustment. */
|
|
Packit Service |
97d2fb |
*value += dwfl_adjusted_address (mod, refshdr->sh_addr);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Cache used by relocate_getsym. */
|
|
Packit Service |
97d2fb |
struct reloc_symtab_cache
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Elf *symelf;
|
|
Packit Service |
97d2fb |
Elf_Data *symdata;
|
|
Packit Service |
97d2fb |
Elf_Data *symxndxdata;
|
|
Packit Service |
97d2fb |
Elf_Data *symstrdata;
|
|
Packit Service |
97d2fb |
size_t symshstrndx;
|
|
Packit Service |
97d2fb |
size_t strtabndx;
|
|
Packit Service |
97d2fb |
};
|
|
Packit Service |
97d2fb |
#define RELOC_SYMTAB_CACHE(cache) \
|
|
Packit Service |
97d2fb |
struct reloc_symtab_cache cache = \
|
|
Packit Service |
97d2fb |
{ NULL, NULL, NULL, NULL, SHN_UNDEF, SHN_UNDEF }
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* This is just doing dwfl_module_getsym, except that we must always use
|
|
Packit Service |
97d2fb |
the symbol table in RELOCATED itself when it has one, not MOD->symfile. */
|
|
Packit Service |
97d2fb |
static Dwfl_Error
|
|
Packit Service |
97d2fb |
relocate_getsym (Dwfl_Module *mod,
|
|
Packit Service |
97d2fb |
Elf *relocated, struct reloc_symtab_cache *cache,
|
|
Packit Service |
97d2fb |
int symndx, GElf_Sym *sym, GElf_Word *shndx)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (cache->symdata == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (mod->symfile == NULL || mod->symfile->elf != relocated)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* We have to look up the symbol table in the file we are
|
|
Packit Service |
97d2fb |
relocating, if it has its own. These reloc sections refer to
|
|
Packit Service |
97d2fb |
the symbol table in this file, and a symbol table in the main
|
|
Packit Service |
97d2fb |
file might not match. However, some tools did produce ET_REL
|
|
Packit Service |
97d2fb |
.debug files with relocs but no symtab of their own. */
|
|
Packit Service |
97d2fb |
Elf_Scn *scn = NULL;
|
|
Packit Service |
97d2fb |
while ((scn = elf_nextscn (relocated, scn)) != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
|
|
Packit Service |
97d2fb |
if (shdr != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* We need uncompressed data. */
|
|
Packit Service |
97d2fb |
if ((shdr->sh_type == SHT_SYMTAB
|
|
Packit Service |
97d2fb |
|| shdr->sh_type == SHT_SYMTAB_SHNDX)
|
|
Packit Service |
97d2fb |
&& (shdr->sh_flags & SHF_COMPRESSED) != 0)
|
|
Packit Service |
97d2fb |
if (elf_compress (scn, 0, 0) < 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
switch (shdr->sh_type)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
default:
|
|
Packit Service |
97d2fb |
continue;
|
|
Packit Service |
97d2fb |
case SHT_SYMTAB:
|
|
Packit Service |
97d2fb |
cache->symelf = relocated;
|
|
Packit Service |
97d2fb |
cache->symdata = elf_getdata (scn, NULL);
|
|
Packit Service |
97d2fb |
cache->strtabndx = shdr->sh_link;
|
|
Packit Service |
97d2fb |
if (unlikely (cache->symdata == NULL))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
case SHT_SYMTAB_SHNDX:
|
|
Packit Service |
97d2fb |
cache->symxndxdata = elf_getdata (scn, NULL);
|
|
Packit Service |
97d2fb |
if (unlikely (cache->symxndxdata == NULL))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
if (cache->symdata != NULL && cache->symxndxdata != NULL)
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
if (cache->symdata == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* We might not have looked for a symbol table file yet,
|
|
Packit Service |
97d2fb |
when coming from __libdwfl_relocate_section. */
|
|
Packit Service |
97d2fb |
if (unlikely (mod->symfile == NULL)
|
|
Packit Service |
97d2fb |
&& unlikely (INTUSE(dwfl_module_getsymtab) (mod) < 0))
|
|
Packit Service |
97d2fb |
return dwfl_errno ();
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* The symbol table we have already cached is the one from
|
|
Packit Service |
97d2fb |
the file being relocated, so it's what we need. Or else
|
|
Packit Service |
97d2fb |
this is an ET_REL .debug file with no .symtab of its own;
|
|
Packit Service |
97d2fb |
the symbols refer to the section indices in the main file. */
|
|
Packit Service |
97d2fb |
cache->symelf = mod->symfile->elf;
|
|
Packit Service |
97d2fb |
cache->symdata = mod->symdata;
|
|
Packit Service |
97d2fb |
cache->symxndxdata = mod->symxndxdata;
|
|
Packit Service |
97d2fb |
cache->symstrdata = mod->symstrdata;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (unlikely (gelf_getsymshndx (cache->symdata, cache->symxndxdata,
|
|
Packit Service |
97d2fb |
symndx, sym, shndx) == NULL))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (sym->st_shndx != SHN_XINDEX)
|
|
Packit Service |
97d2fb |
*shndx = sym->st_shndx;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
switch (sym->st_shndx)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
case SHN_ABS:
|
|
Packit Service |
97d2fb |
case SHN_UNDEF:
|
|
Packit Service |
97d2fb |
return DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
case SHN_COMMON:
|
|
Packit Service |
97d2fb |
sym->st_value = 0; /* Value is size, not helpful. */
|
|
Packit Service |
97d2fb |
return DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return __libdwfl_relocate_value (mod, cache->symelf, &cache->symshstrndx,
|
|
Packit Service |
97d2fb |
*shndx, &sym->st_value);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Handle an undefined symbol. We really only support ET_REL for Linux
|
|
Packit Service |
97d2fb |
kernel modules, and offline archives. The behavior of the Linux module
|
|
Packit Service |
97d2fb |
loader is very simple and easy to mimic. It only matches magically
|
|
Packit Service |
97d2fb |
exported symbols, and we match any defined symbols. But we get the same
|
|
Packit Service |
97d2fb |
answer except when the module's symbols are undefined and would prevent
|
|
Packit Service |
97d2fb |
it from being loaded. */
|
|
Packit Service |
97d2fb |
static Dwfl_Error
|
|
Packit Service |
97d2fb |
resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
|
|
Packit Service |
97d2fb |
GElf_Sym *sym, GElf_Word shndx)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* First we need its name. */
|
|
Packit Service |
97d2fb |
if (sym->st_name != 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (symtab->symstrdata == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Cache the strtab for this symtab. */
|
|
Packit Service |
97d2fb |
assert (referer->symfile == NULL
|
|
Packit Service |
97d2fb |
|| referer->symfile->elf != symtab->symelf);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Elf_Scn *scn = elf_getscn (symtab->symelf, symtab->strtabndx);
|
|
Packit Service |
97d2fb |
if (scn == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
GElf_Shdr shdr_mem;
|
|
Packit Service |
97d2fb |
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
|
|
Packit Service |
97d2fb |
if (shdr == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (symtab->symshstrndx == SHN_UNDEF
|
|
Packit Service |
97d2fb |
&& elf_getshdrstrndx (symtab->symelf, &symtab->symshstrndx) < 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
const char *sname = elf_strptr (symtab->symelf, symtab->symshstrndx,
|
|
Packit Service |
97d2fb |
shdr->sh_name);
|
|
Packit Service |
97d2fb |
if (sname == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* If the section is already decompressed, that isn't an error. */
|
|
Packit Service |
97d2fb |
if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
|
|
Packit Service |
97d2fb |
elf_compress_gnu (scn, 0, 0);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
|
|
Packit Service |
97d2fb |
if (elf_compress (scn, 0, 0) < 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
symtab->symstrdata = elf_getdata (scn, NULL);
|
|
Packit Service |
97d2fb |
if (unlikely (symtab->symstrdata == NULL
|
|
Packit Service |
97d2fb |
|| symtab->symstrdata->d_buf == NULL))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
if (unlikely (sym->st_name >= symtab->symstrdata->d_size))
|
|
Packit Service |
97d2fb |
return DWFL_E_BADSTROFF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
const char *name = symtab->symstrdata->d_buf;
|
|
Packit Service |
97d2fb |
name += sym->st_name;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
for (Dwfl_Module *m = referer->dwfl->modulelist; m != NULL; m = m->next)
|
|
Packit Service |
97d2fb |
if (m != referer)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Get this module's symtab.
|
|
Packit Service |
97d2fb |
If we got a fresh error reading the table, report it.
|
|
Packit Service |
97d2fb |
If we just have no symbols in this module, no harm done. */
|
|
Packit Service |
97d2fb |
if (m->symdata == NULL
|
|
Packit Service |
97d2fb |
&& m->symerr == DWFL_E_NOERROR
|
|
Packit Service |
97d2fb |
&& INTUSE(dwfl_module_getsymtab) (m) < 0
|
|
Packit Service |
97d2fb |
&& m->symerr != DWFL_E_NO_SYMTAB)
|
|
Packit Service |
97d2fb |
return m->symerr;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
for (size_t ndx = 1; ndx < m->syments; ++ndx)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
sym = gelf_getsymshndx (m->symdata, m->symxndxdata,
|
|
Packit Service |
97d2fb |
ndx, sym, &shndx);
|
|
Packit Service |
97d2fb |
if (unlikely (sym == NULL))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
if (sym->st_shndx != SHN_XINDEX)
|
|
Packit Service |
97d2fb |
shndx = sym->st_shndx;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* We are looking for a defined global symbol with a name. */
|
|
Packit Service |
97d2fb |
if (shndx == SHN_UNDEF || shndx == SHN_COMMON
|
|
Packit Service |
97d2fb |
|| GELF_ST_BIND (sym->st_info) == STB_LOCAL
|
|
Packit Service |
97d2fb |
|| sym->st_name == 0)
|
|
Packit Service |
97d2fb |
continue;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Get this candidate symbol's name. */
|
|
Packit Service |
97d2fb |
if (unlikely (sym->st_name >= m->symstrdata->d_size))
|
|
Packit Service |
97d2fb |
return DWFL_E_BADSTROFF;
|
|
Packit Service |
97d2fb |
const char *n = m->symstrdata->d_buf;
|
|
Packit Service |
97d2fb |
n += sym->st_name;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Does the name match? */
|
|
Packit Service |
97d2fb |
if (strcmp (name, n))
|
|
Packit Service |
97d2fb |
continue;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* We found it! */
|
|
Packit Service |
97d2fb |
if (shndx == SHN_ABS) /* XXX maybe should apply bias? */
|
|
Packit Service |
97d2fb |
return DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (m->e_type != ET_REL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
sym->st_value = dwfl_adjusted_st_value (m, m->symfile->elf,
|
|
Packit Service |
97d2fb |
sym->st_value);
|
|
Packit Service |
97d2fb |
return DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* In an ET_REL file, the symbol table values are relative
|
|
Packit Service |
97d2fb |
to the section, not to the module's load base. */
|
|
Packit Service |
97d2fb |
size_t symshstrndx = SHN_UNDEF;
|
|
Packit Service |
97d2fb |
return __libdwfl_relocate_value (m, m->symfile->elf,
|
|
Packit Service |
97d2fb |
&symshstrndx,
|
|
Packit Service |
97d2fb |
shndx, &sym->st_value);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return DWFL_E_RELUNDEF;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Apply one relocation. Returns true for any invalid data. */
|
|
Packit Service |
97d2fb |
static Dwfl_Error
|
|
Packit Service |
97d2fb |
relocate (Dwfl_Module * const mod,
|
|
Packit Service |
97d2fb |
Elf * const relocated,
|
|
Packit Service |
97d2fb |
struct reloc_symtab_cache * const reloc_symtab,
|
|
Packit Service |
97d2fb |
Elf_Data * const tdata,
|
|
Packit Service |
97d2fb |
const GElf_Ehdr * const ehdr,
|
|
Packit Service |
97d2fb |
GElf_Addr offset,
|
|
Packit Service |
97d2fb |
const GElf_Sxword *addend,
|
|
Packit Service |
97d2fb |
int rtype,
|
|
Packit Service |
97d2fb |
int symndx)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* First see if this is a reloc we can handle.
|
|
Packit Service |
97d2fb |
If we are skipping it, don't bother resolving the symbol. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (unlikely (rtype == 0))
|
|
Packit Service |
97d2fb |
/* In some odd situations, the linker can leave R_*_NONE relocs
|
|
Packit Service |
97d2fb |
behind. This is probably bogus ld -r behavior, but the only
|
|
Packit Service |
97d2fb |
cases it's known to appear in are harmless: DWARF data
|
|
Packit Service |
97d2fb |
referring to addresses in a section that has been discarded.
|
|
Packit Service |
97d2fb |
So we just pretend it's OK without further relocation. */
|
|
Packit Service |
97d2fb |
return DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
int addsub = 0;
|
|
Packit Service |
97d2fb |
Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype, &addsub);
|
|
Packit Service |
97d2fb |
if (unlikely (type == ELF_T_NUM))
|
|
Packit Service |
97d2fb |
return DWFL_E_BADRELTYPE;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* First, resolve the symbol to an absolute value. */
|
|
Packit Service |
97d2fb |
GElf_Addr value;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (symndx == STN_UNDEF)
|
|
Packit Service |
97d2fb |
/* When strip removes a section symbol referring to a
|
|
Packit Service |
97d2fb |
section moved into the debuginfo file, it replaces
|
|
Packit Service |
97d2fb |
that symbol index in relocs with STN_UNDEF. We
|
|
Packit Service |
97d2fb |
don't actually need the symbol, because those relocs
|
|
Packit Service |
97d2fb |
are always references relative to the nonallocated
|
|
Packit Service |
97d2fb |
debugging sections, which start at zero. */
|
|
Packit Service |
97d2fb |
value = 0;
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
GElf_Sym sym;
|
|
Packit Service |
97d2fb |
GElf_Word shndx;
|
|
Packit Service |
97d2fb |
Dwfl_Error error = relocate_getsym (mod, relocated, reloc_symtab,
|
|
Packit Service |
97d2fb |
symndx, &sym, &shndx);
|
|
Packit Service |
97d2fb |
if (unlikely (error != DWFL_E_NOERROR))
|
|
Packit Service |
97d2fb |
return error;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (shndx == SHN_UNDEF || shndx == SHN_COMMON)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Maybe we can figure it out anyway. */
|
|
Packit Service |
97d2fb |
error = resolve_symbol (mod, reloc_symtab, &sym, shndx);
|
|
Packit Service |
97d2fb |
if (error != DWFL_E_NOERROR
|
|
Packit Service |
97d2fb |
&& !(error == DWFL_E_RELUNDEF && shndx == SHN_COMMON))
|
|
Packit Service |
97d2fb |
return error;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
value = sym.st_value;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* These are the types we can relocate. */
|
|
Packit Service |
97d2fb |
#define TYPES DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half); \
|
|
Packit Service |
97d2fb |
DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword); \
|
|
Packit Service |
97d2fb |
DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword)
|
|
Packit Service |
97d2fb |
size_t size;
|
|
Packit Service |
97d2fb |
switch (type)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
#define DO_TYPE(NAME, Name) \
|
|
Packit Service |
97d2fb |
case ELF_T_##NAME: \
|
|
Packit Service |
97d2fb |
if (addsub != 0 && addend == NULL) \
|
|
Packit Service |
97d2fb |
/* These do not make sense with SHT_REL. */ \
|
|
Packit Service |
97d2fb |
return DWFL_E_BADRELTYPE; \
|
|
Packit Service |
97d2fb |
size = sizeof (GElf_##Name); \
|
|
Packit Service |
97d2fb |
break
|
|
Packit Service |
97d2fb |
TYPES;
|
|
Packit Service |
97d2fb |
#undef DO_TYPE
|
|
Packit Service |
97d2fb |
default:
|
|
Packit Service |
97d2fb |
return DWFL_E_BADRELTYPE;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (offset > tdata->d_size || tdata->d_size - offset < size)
|
|
Packit Service |
97d2fb |
return DWFL_E_BADRELOFF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#define DO_TYPE(NAME, Name) GElf_##Name Name;
|
|
Packit Service |
97d2fb |
union { TYPES; } tmpbuf;
|
|
Packit Service |
97d2fb |
#undef DO_TYPE
|
|
Packit Service |
97d2fb |
Elf_Data tmpdata =
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
.d_type = type,
|
|
Packit Service |
97d2fb |
.d_buf = &tmpbuf,
|
|
Packit Service |
97d2fb |
.d_size = size,
|
|
Packit Service |
97d2fb |
.d_version = EV_CURRENT,
|
|
Packit Service |
97d2fb |
};
|
|
Packit Service |
97d2fb |
Elf_Data rdata =
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
.d_type = type,
|
|
Packit Service |
97d2fb |
.d_buf = tdata->d_buf + offset,
|
|
Packit Service |
97d2fb |
.d_size = size,
|
|
Packit Service |
97d2fb |
.d_version = EV_CURRENT,
|
|
Packit Service |
97d2fb |
};
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* XXX check for overflow? */
|
|
Packit Service |
97d2fb |
if (addend)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* For the addend form, we have the value already. */
|
|
Packit Service |
97d2fb |
value += *addend;
|
|
Packit Service |
97d2fb |
/* For ADD/SUB relocations we need to fetch the section
|
|
Packit Service |
97d2fb |
contents. */
|
|
Packit Service |
97d2fb |
if (addsub != 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Elf_Data *d = gelf_xlatetom (relocated, &tmpdata, &rdata,
|
|
Packit Service |
97d2fb |
ehdr->e_ident[EI_DATA]);
|
|
Packit Service |
97d2fb |
if (d == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
assert (d == &tmpdata);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
switch (type)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
#define DO_TYPE(NAME, Name) \
|
|
Packit Service |
97d2fb |
case ELF_T_##NAME: \
|
|
Packit Service |
97d2fb |
if (addsub != 0) \
|
|
Packit Service |
97d2fb |
tmpbuf.Name += value * addsub; \
|
|
Packit Service |
97d2fb |
else \
|
|
Packit Service |
97d2fb |
tmpbuf.Name = value; \
|
|
Packit Service |
97d2fb |
break
|
|
Packit Service |
97d2fb |
TYPES;
|
|
Packit Service |
97d2fb |
#undef DO_TYPE
|
|
Packit Service |
97d2fb |
default:
|
|
Packit Service |
97d2fb |
abort ();
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Extract the original value and apply the reloc. */
|
|
Packit Service |
97d2fb |
Elf_Data *d = gelf_xlatetom (relocated, &tmpdata, &rdata,
|
|
Packit Service |
97d2fb |
ehdr->e_ident[EI_DATA]);
|
|
Packit Service |
97d2fb |
if (d == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
assert (d == &tmpdata);
|
|
Packit Service |
97d2fb |
switch (type)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
#define DO_TYPE(NAME, Name) \
|
|
Packit Service |
97d2fb |
case ELF_T_##NAME: \
|
|
Packit Service |
97d2fb |
tmpbuf.Name += (GElf_##Name) value; \
|
|
Packit Service |
97d2fb |
break
|
|
Packit Service |
97d2fb |
TYPES;
|
|
Packit Service |
97d2fb |
#undef DO_TYPE
|
|
Packit Service |
97d2fb |
default:
|
|
Packit Service |
97d2fb |
abort ();
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Now convert the relocated datum back to the target
|
|
Packit Service |
97d2fb |
format. This will write into rdata.d_buf, which
|
|
Packit Service |
97d2fb |
points into the raw section data being relocated. */
|
|
Packit Service |
97d2fb |
Elf_Data *s = gelf_xlatetof (relocated, &rdata, &tmpdata,
|
|
Packit Service |
97d2fb |
ehdr->e_ident[EI_DATA]);
|
|
Packit Service |
97d2fb |
if (s == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
assert (s == &rdata);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* We have applied this relocation! */
|
|
Packit Service |
97d2fb |
return DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
static inline void
|
|
Packit Service |
97d2fb |
check_badreltype (bool *first_badreltype,
|
|
Packit Service |
97d2fb |
Dwfl_Module *mod,
|
|
Packit Service |
97d2fb |
Dwfl_Error *result)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (*first_badreltype)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
*first_badreltype = false;
|
|
Packit Service |
97d2fb |
if (ebl_get_elfmachine (mod->ebl) == EM_NONE)
|
|
Packit Service |
97d2fb |
/* This might be because ebl_openbackend failed to find
|
|
Packit Service |
97d2fb |
any libebl_CPU.so library. Diagnose that clearly. */
|
|
Packit Service |
97d2fb |
*result = DWFL_E_UNKNOWN_MACHINE;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
static Dwfl_Error
|
|
Packit Service |
97d2fb |
relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
|
|
Packit Service |
97d2fb |
size_t shstrndx, struct reloc_symtab_cache *reloc_symtab,
|
|
Packit Service |
97d2fb |
Elf_Scn *scn, GElf_Shdr *shdr,
|
|
Packit Service |
97d2fb |
Elf_Scn *tscn, bool debugscn, bool partial)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* First, fetch the name of the section these relocations apply to.
|
|
Packit Service |
97d2fb |
Then try to decompress both relocation and target section. */
|
|
Packit Service |
97d2fb |
GElf_Shdr tshdr_mem;
|
|
Packit Service |
97d2fb |
GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
|
|
Packit Service |
97d2fb |
if (tshdr == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
const char *tname = elf_strptr (relocated, shstrndx, tshdr->sh_name);
|
|
Packit Service |
97d2fb |
if (tname == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (debugscn && ! ebl_debugscn_p (mod->ebl, tname))
|
|
Packit Service |
97d2fb |
/* This relocation section is not for a debugging section.
|
|
Packit Service |
97d2fb |
Nothing to do here. */
|
|
Packit Service |
97d2fb |
return DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (strncmp (tname, ".zdebug", strlen ("zdebug")) == 0)
|
|
Packit Service |
97d2fb |
elf_compress_gnu (tscn, 0, 0);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if ((tshdr->sh_flags & SHF_COMPRESSED) != 0)
|
|
Packit Service |
97d2fb |
if (elf_compress (tscn, 0, 0) < 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Reload Shdr in case section was just decompressed. */
|
|
Packit Service |
97d2fb |
tshdr = gelf_getshdr (tscn, &tshdr_mem);
|
|
Packit Service |
97d2fb |
if (tshdr == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (unlikely (tshdr->sh_type == SHT_NOBITS)
|
|
Packit Service |
97d2fb |
|| unlikely (tshdr->sh_size == 0))
|
|
Packit Service |
97d2fb |
/* No contents to relocate. */
|
|
Packit Service |
97d2fb |
return DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
const char *sname = elf_strptr (relocated, shstrndx, shdr->sh_name);
|
|
Packit Service |
97d2fb |
if (sname == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (strncmp (sname, ".zdebug", strlen ("zdebug")) == 0)
|
|
Packit Service |
97d2fb |
elf_compress_gnu (scn, 0, 0);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
|
|
Packit Service |
97d2fb |
if (elf_compress (scn, 0, 0) < 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Reload Shdr in case section was just decompressed. */
|
|
Packit Service |
97d2fb |
GElf_Shdr shdr_mem;
|
|
Packit Service |
97d2fb |
shdr = gelf_getshdr (scn, &shdr_mem);
|
|
Packit Service |
97d2fb |
if (shdr == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Fetch the section data that needs the relocations applied. */
|
|
Packit Service |
97d2fb |
Elf_Data *tdata = elf_rawdata (tscn, NULL);
|
|
Packit Service |
97d2fb |
if (tdata == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* If either the section that needs the relocation applied, or the
|
|
Packit Service |
97d2fb |
section that the relocations come from overlap one of the ehdrs,
|
|
Packit Service |
97d2fb |
shdrs or phdrs data then we refuse to do the relocations. It
|
|
Packit Service |
97d2fb |
isn't illegal for ELF section data to overlap the header data,
|
|
Packit Service |
97d2fb |
but updating the (relocation) data might corrupt the in-memory
|
|
Packit Service |
97d2fb |
libelf headers causing strange corruptions or errors.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
This is only an issue if the ELF is mmapped and the section data
|
|
Packit Service |
97d2fb |
comes from the mmapped region (is not malloced or decompressed).
|
|
Packit Service |
97d2fb |
*/
|
|
Packit Service |
97d2fb |
if (relocated->map_address != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
size_t ehsize = gelf_fsize (relocated, ELF_T_EHDR, 1, EV_CURRENT);
|
|
Packit Service |
97d2fb |
if (unlikely (shdr->sh_offset < ehsize
|
|
Packit Service |
97d2fb |
|| tshdr->sh_offset < ehsize))
|
|
Packit Service |
97d2fb |
return DWFL_E_BADELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
GElf_Off shdrs_start = ehdr->e_shoff;
|
|
Packit Service |
97d2fb |
size_t shnums;
|
|
Packit Service |
97d2fb |
if (elf_getshdrnum (relocated, &shnums) < 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
/* Overflows will have been checked by elf_getshdrnum/get|rawdata. */
|
|
Packit Service |
97d2fb |
size_t shentsize = gelf_fsize (relocated, ELF_T_SHDR, 1, EV_CURRENT);
|
|
Packit Service |
97d2fb |
GElf_Off shdrs_end = shdrs_start + shnums * shentsize;
|
|
Packit Service |
97d2fb |
if (unlikely (shdrs_start < shdr->sh_offset + shdr->sh_size
|
|
Packit Service |
97d2fb |
&& shdr->sh_offset < shdrs_end))
|
|
Packit Service |
97d2fb |
if ((scn->flags & ELF_F_MALLOCED) == 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_BADELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (unlikely (shdrs_start < tshdr->sh_offset + tshdr->sh_size
|
|
Packit Service |
97d2fb |
&& tshdr->sh_offset < shdrs_end))
|
|
Packit Service |
97d2fb |
if ((tscn->flags & ELF_F_MALLOCED) == 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_BADELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
GElf_Off phdrs_start = ehdr->e_phoff;
|
|
Packit Service |
97d2fb |
size_t phnums;
|
|
Packit Service |
97d2fb |
if (elf_getphdrnum (relocated, &phnums) < 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
if (phdrs_start != 0 && phnums != 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Overflows will have been checked by elf_getphdrnum/get|rawdata. */
|
|
Packit Service |
97d2fb |
size_t phentsize = gelf_fsize (relocated, ELF_T_PHDR, 1, EV_CURRENT);
|
|
Packit Service |
97d2fb |
GElf_Off phdrs_end = phdrs_start + phnums * phentsize;
|
|
Packit Service |
97d2fb |
if (unlikely (phdrs_start < shdr->sh_offset + shdr->sh_size
|
|
Packit Service |
97d2fb |
&& shdr->sh_offset < phdrs_end))
|
|
Packit Service |
97d2fb |
if ((scn->flags & ELF_F_MALLOCED) == 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_BADELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (unlikely (phdrs_start < tshdr->sh_offset + tshdr->sh_size
|
|
Packit Service |
97d2fb |
&& tshdr->sh_offset < phdrs_end))
|
|
Packit Service |
97d2fb |
if ((tscn->flags & ELF_F_MALLOCED) == 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_BADELF;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Fetch the relocation section and apply each reloc in it. */
|
|
Packit Service |
97d2fb |
Elf_Data *reldata = elf_getdata (scn, NULL);
|
|
Packit Service |
97d2fb |
if (reldata == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Dwfl_Error result = DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
bool first_badreltype = true;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
size_t sh_entsize
|
|
Packit Service |
97d2fb |
= gelf_fsize (relocated, shdr->sh_type == SHT_REL ? ELF_T_REL : ELF_T_RELA,
|
|
Packit Service |
97d2fb |
1, EV_CURRENT);
|
|
Packit Service |
97d2fb |
size_t nrels = shdr->sh_size / sh_entsize;
|
|
Packit Service |
97d2fb |
size_t complete = 0;
|
|
Packit Service |
97d2fb |
if (shdr->sh_type == SHT_REL)
|
|
Packit Service |
97d2fb |
for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
GElf_Rel rel_mem, *r = gelf_getrel (reldata, relidx, &rel_mem);
|
|
Packit Service |
97d2fb |
if (r == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
result = relocate (mod, relocated, reloc_symtab, tdata, ehdr,
|
|
Packit Service |
97d2fb |
r->r_offset, NULL,
|
|
Packit Service |
97d2fb |
GELF_R_TYPE (r->r_info),
|
|
Packit Service |
97d2fb |
GELF_R_SYM (r->r_info));
|
|
Packit Service |
97d2fb |
check_badreltype (&first_badreltype, mod, &result);
|
|
Packit Service |
97d2fb |
if (partial)
|
|
Packit Service |
97d2fb |
switch (result)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
case DWFL_E_NOERROR:
|
|
Packit Service |
97d2fb |
/* We applied the relocation. Elide it. */
|
|
Packit Service |
97d2fb |
memset (&rel_mem, 0, sizeof rel_mem);
|
|
Packit Service |
97d2fb |
if (unlikely (gelf_update_rel (reldata, relidx, &rel_mem) == 0))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
++complete;
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
case DWFL_E_BADRELTYPE:
|
|
Packit Service |
97d2fb |
case DWFL_E_RELUNDEF:
|
|
Packit Service |
97d2fb |
/* We couldn't handle this relocation. Skip it. */
|
|
Packit Service |
97d2fb |
result = DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
default:
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
GElf_Rela rela_mem, *r = gelf_getrela (reldata, relidx,
|
|
Packit Service |
97d2fb |
&rela_mem);
|
|
Packit Service |
97d2fb |
if (r == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
result = relocate (mod, relocated, reloc_symtab, tdata, ehdr,
|
|
Packit Service |
97d2fb |
r->r_offset, &r->r_addend,
|
|
Packit Service |
97d2fb |
GELF_R_TYPE (r->r_info),
|
|
Packit Service |
97d2fb |
GELF_R_SYM (r->r_info));
|
|
Packit Service |
97d2fb |
check_badreltype (&first_badreltype, mod, &result);
|
|
Packit Service |
97d2fb |
if (partial)
|
|
Packit Service |
97d2fb |
switch (result)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
case DWFL_E_NOERROR:
|
|
Packit Service |
97d2fb |
/* We applied the relocation. Elide it. */
|
|
Packit Service |
97d2fb |
memset (&rela_mem, 0, sizeof rela_mem);
|
|
Packit Service |
97d2fb |
if (unlikely (gelf_update_rela (reldata, relidx,
|
|
Packit Service |
97d2fb |
&rela_mem) == 0))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
++complete;
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
case DWFL_E_BADRELTYPE:
|
|
Packit Service |
97d2fb |
case DWFL_E_RELUNDEF:
|
|
Packit Service |
97d2fb |
/* We couldn't handle this relocation. Skip it. */
|
|
Packit Service |
97d2fb |
result = DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
default:
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (likely (result == DWFL_E_NOERROR))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (!partial || complete == nrels)
|
|
Packit Service |
97d2fb |
/* Mark this relocation section as being empty now that we have
|
|
Packit Service |
97d2fb |
done its work. This affects unstrip -R, so e.g. it emits an
|
|
Packit Service |
97d2fb |
empty .rela.debug_info along with a .debug_info that has
|
|
Packit Service |
97d2fb |
already been fully relocated. */
|
|
Packit Service |
97d2fb |
nrels = 0;
|
|
Packit Service |
97d2fb |
else if (complete != 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* We handled some of the relocations but not all.
|
|
Packit Service |
97d2fb |
We've zeroed out the ones we processed.
|
|
Packit Service |
97d2fb |
Now remove them from the section. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
size_t next = 0;
|
|
Packit Service |
97d2fb |
if (shdr->sh_type == SHT_REL)
|
|
Packit Service |
97d2fb |
for (size_t relidx = 0; relidx < nrels; ++relidx)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
GElf_Rel rel_mem;
|
|
Packit Service |
97d2fb |
GElf_Rel *r = gelf_getrel (reldata, relidx, &rel_mem);
|
|
Packit Service |
97d2fb |
if (unlikely (r == NULL))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
if (r->r_info != 0 || r->r_offset != 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (next != relidx)
|
|
Packit Service |
97d2fb |
if (unlikely (gelf_update_rel (reldata, next, r) == 0))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
++next;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
for (size_t relidx = 0; relidx < nrels; ++relidx)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
GElf_Rela rela_mem;
|
|
Packit Service |
97d2fb |
GElf_Rela *r = gelf_getrela (reldata, relidx, &rela_mem);
|
|
Packit Service |
97d2fb |
if (unlikely (r == NULL))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
if (r->r_info != 0 || r->r_offset != 0 || r->r_addend != 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (next != relidx)
|
|
Packit Service |
97d2fb |
if (unlikely (gelf_update_rela (reldata, next, r) == 0))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
++next;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
nrels = next;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
shdr->sh_size = reldata->d_size = nrels * sh_entsize;
|
|
Packit Service |
97d2fb |
if (unlikely (gelf_update_shdr (scn, shdr) == 0))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return result;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Dwfl_Error
|
|
Packit Service |
97d2fb |
internal_function
|
|
Packit Service |
97d2fb |
__libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
assert (mod->e_type == ET_REL);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
GElf_Ehdr ehdr_mem;
|
|
Packit Service |
97d2fb |
const GElf_Ehdr *ehdr = gelf_getehdr (debugfile, &ehdr_mem);
|
|
Packit Service |
97d2fb |
if (ehdr == NULL)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
size_t d_shstrndx;
|
|
Packit Service |
97d2fb |
if (elf_getshdrstrndx (debugfile, &d_shstrndx) < 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
RELOC_SYMTAB_CACHE (reloc_symtab);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Look at each section in the debuginfo file, and process the
|
|
Packit Service |
97d2fb |
relocation sections for debugging sections. */
|
|
Packit Service |
97d2fb |
Dwfl_Error result = DWFL_E_NOERROR;
|
|
Packit Service |
97d2fb |
Elf_Scn *scn = NULL;
|
|
Packit Service |
97d2fb |
while (result == DWFL_E_NOERROR
|
|
Packit Service |
97d2fb |
&& (scn = elf_nextscn (debugfile, scn)) != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
GElf_Shdr shdr_mem;
|
|
Packit Service |
97d2fb |
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
|
|
Packit Service |
97d2fb |
if (unlikely (shdr == NULL))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
|
|
Packit Service |
97d2fb |
&& shdr->sh_size != 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* It's a relocation section. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Elf_Scn *tscn = elf_getscn (debugfile, shdr->sh_info);
|
|
Packit Service |
97d2fb |
if (unlikely (tscn == NULL))
|
|
Packit Service |
97d2fb |
result = DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
result = relocate_section (mod, debugfile, ehdr, d_shstrndx,
|
|
Packit Service |
97d2fb |
&reloc_symtab, scn, shdr, tscn,
|
|
Packit Service |
97d2fb |
debug, true /* partial always OK. */);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return result;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Dwfl_Error
|
|
Packit Service |
97d2fb |
internal_function
|
|
Packit Service |
97d2fb |
__libdwfl_relocate_section (Dwfl_Module *mod, Elf *relocated,
|
|
Packit Service |
97d2fb |
Elf_Scn *relocscn, Elf_Scn *tscn, bool partial)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
GElf_Ehdr ehdr_mem;
|
|
Packit Service |
97d2fb |
GElf_Shdr shdr_mem;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
RELOC_SYMTAB_CACHE (reloc_symtab);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
size_t shstrndx;
|
|
Packit Service |
97d2fb |
if (elf_getshdrstrndx (relocated, &shstrndx) < 0)
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Dwfl_Error result = __libdwfl_module_getebl (mod);
|
|
Packit Service |
97d2fb |
if (unlikely (result != DWFL_E_NOERROR))
|
|
Packit Service |
97d2fb |
return result;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
GElf_Ehdr *ehdr = gelf_getehdr (relocated, &ehdr_mem);
|
|
Packit Service |
97d2fb |
if (unlikely (ehdr == NULL))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
GElf_Shdr *shdr = gelf_getshdr (relocscn, &shdr_mem);
|
|
Packit Service |
97d2fb |
if (unlikely (shdr == NULL))
|
|
Packit Service |
97d2fb |
return DWFL_E_LIBELF;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return relocate_section (mod, relocated, ehdr, shstrndx, &reloc_symtab,
|
|
Packit Service |
97d2fb |
relocscn, shdr, tscn, false, partial);
|
|
Packit Service |
97d2fb |
}
|