Blame libdwfl/derelocate.c

Packit Service 97d2fb
/* Recover relocatibility for addresses computed from debug information.
Packit Service 97d2fb
   Copyright (C) 2005-2010, 2013, 2015 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 "libdwflP.h"
Packit Service 97d2fb
Packit Service 97d2fb
struct dwfl_relocation
Packit Service 97d2fb
{
Packit Service 97d2fb
  size_t count;
Packit Service 97d2fb
  struct
Packit Service 97d2fb
  {
Packit Service 97d2fb
    Elf_Scn *scn;
Packit Service 97d2fb
    Elf_Scn *relocs;
Packit Service 97d2fb
    const char *name;
Packit Service 97d2fb
    GElf_Addr start, end;
Packit Service 97d2fb
  } refs[0];
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
struct secref
Packit Service 97d2fb
{
Packit Service 97d2fb
  struct secref *next;
Packit Service 97d2fb
  Elf_Scn *scn;
Packit Service 97d2fb
  Elf_Scn *relocs;
Packit Service 97d2fb
  const char *name;
Packit Service 97d2fb
  GElf_Addr start, end;
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
compare_secrefs (const void *a, const void *b)
Packit Service 97d2fb
{
Packit Service 97d2fb
  struct secref *const *p1 = a;
Packit Service 97d2fb
  struct secref *const *p2 = b;
Packit Service 97d2fb
Packit Service 97d2fb
  /* No signed difference calculation is correct here, since the
Packit Service 97d2fb
     terms are unsigned and could be more than INT64_MAX apart.  */
Packit Service 97d2fb
  if ((*p1)->start < (*p2)->start)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
  if ((*p1)->start > (*p2)->start)
Packit Service 97d2fb
    return 1;
Packit Service 97d2fb
Packit Service 97d2fb
  if ((*p1)->end < (*p2)->end)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
  if ((*p1)->end > (*p2)->end)
Packit Service 97d2fb
    return 1;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Same start/end, then just compare which section came first.  */
Packit Service 97d2fb
  return elf_ndxscn ((*p1)->scn) - elf_ndxscn ((*p2)->scn);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
cache_sections (Dwfl_Module *mod)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (likely (mod->reloc_info != NULL))
Packit Service 97d2fb
    return mod->reloc_info->count;
Packit Service 97d2fb
Packit Service 97d2fb
  struct secref *refs = NULL;
Packit Service 97d2fb
  size_t nrefs = 0;
Packit Service 97d2fb
Packit Service 97d2fb
  size_t shstrndx;
Packit Service 97d2fb
  if (unlikely (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0))
Packit Service 97d2fb
    {
Packit Service 97d2fb
    elf_error:
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_LIBELF);
Packit Service 97d2fb
      nrefs = -1;
Packit Service 97d2fb
      goto free_refs;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  bool check_reloc_sections = false;
Packit Service 97d2fb
  Elf_Scn *scn = NULL;
Packit Service 97d2fb
  while ((scn = elf_nextscn (mod->main.elf, 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 (shdr == NULL)
Packit Service 97d2fb
	goto elf_error;
Packit Service 97d2fb
Packit Service 97d2fb
      if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0
Packit Service 97d2fb
	  && mod->e_type == ET_REL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* This section might not yet have been looked at.  */
Packit Service 97d2fb
	  if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx,
Packit Service 97d2fb
					elf_ndxscn (scn),
Packit Service 97d2fb
					&shdr->sh_addr) != DWFL_E_NOERROR)
Packit Service 97d2fb
	    continue;
Packit Service 97d2fb
	  shdr = gelf_getshdr (scn, &shdr_mem);
Packit Service 97d2fb
	  if (unlikely (shdr == NULL))
Packit Service 97d2fb
	    goto elf_error;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (shdr->sh_flags & SHF_ALLOC)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  const char *name = elf_strptr (mod->main.elf, shstrndx,
Packit Service 97d2fb
					 shdr->sh_name);
Packit Service 97d2fb
	  if (unlikely (name == NULL))
Packit Service 97d2fb
	    goto elf_error;
Packit Service 97d2fb
Packit Service 97d2fb
	  struct secref *newref = malloc (sizeof *newref);
Packit Service 97d2fb
	  if (unlikely (newref == NULL))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	    nomem:
Packit Service 97d2fb
	      __libdwfl_seterrno (DWFL_E_NOMEM);
Packit Service 97d2fb
	      nrefs = -1;
Packit Service 97d2fb
	      goto free_refs;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  newref->scn = scn;
Packit Service 97d2fb
	  newref->relocs = NULL;
Packit Service 97d2fb
	  newref->name = name;
Packit Service 97d2fb
	  newref->start = dwfl_adjusted_address (mod, shdr->sh_addr);
Packit Service 97d2fb
	  newref->end = newref->start + shdr->sh_size;
Packit Service 97d2fb
	  newref->next = refs;
Packit Service 97d2fb
	  refs = newref;
Packit Service 97d2fb
	  ++nrefs;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (mod->e_type == ET_REL
Packit Service 97d2fb
	  && shdr->sh_size != 0
Packit Service 97d2fb
	  && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
Packit Service 97d2fb
	  && mod->dwfl->callbacks->section_address != NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (shdr->sh_info < elf_ndxscn (scn))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      /* We've already looked at the section these relocs apply to.  */
Packit Service 97d2fb
	      Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
Packit Service 97d2fb
	      if (likely (tscn != NULL))
Packit Service 97d2fb
		for (struct secref *sec = refs; sec != NULL; sec = sec->next)
Packit Service 97d2fb
		  if (sec->scn == tscn)
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      sec->relocs = scn;
Packit Service 97d2fb
		      break;
Packit Service 97d2fb
		    }
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    /* We'll have to do a second pass.  */
Packit Service 97d2fb
	    check_reloc_sections = true;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
Packit Service 97d2fb
  if (unlikely (mod->reloc_info == NULL))
Packit Service 97d2fb
    goto nomem;
Packit Service 97d2fb
Packit Service 97d2fb
  struct secref **sortrefs = malloc (nrefs * sizeof sortrefs[0]);
Packit Service 97d2fb
  if (unlikely (sortrefs == NULL))
Packit Service 97d2fb
    goto nomem;
Packit Service 97d2fb
Packit Service 97d2fb
  for (size_t i = nrefs; i-- > 0; refs = refs->next)
Packit Service 97d2fb
    sortrefs[i] = refs;
Packit Service 97d2fb
  assert (refs == NULL);
Packit Service 97d2fb
Packit Service 97d2fb
  qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs);
Packit Service 97d2fb
Packit Service 97d2fb
  mod->reloc_info->count = nrefs;
Packit Service 97d2fb
  for (size_t i = 0; i < nrefs; ++i)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      mod->reloc_info->refs[i].name = sortrefs[i]->name;
Packit Service 97d2fb
      mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
Packit Service 97d2fb
      mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
Packit Service 97d2fb
      mod->reloc_info->refs[i].start = sortrefs[i]->start;
Packit Service 97d2fb
      mod->reloc_info->refs[i].end = sortrefs[i]->end;
Packit Service 97d2fb
      free (sortrefs[i]);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  free (sortrefs);
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (check_reloc_sections))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* There was a reloc section that preceded its target section.
Packit Service 97d2fb
	 So we have to scan again now that we have cached all the
Packit Service 97d2fb
	 possible target sections we care about.  */
Packit Service 97d2fb
Packit Service 97d2fb
      scn = NULL;
Packit Service 97d2fb
      while ((scn = elf_nextscn (mod->main.elf, 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 (shdr == NULL)
Packit Service 97d2fb
	    goto elf_error;
Packit Service 97d2fb
Packit Service 97d2fb
      	  if (shdr->sh_size != 0
Packit Service 97d2fb
	      && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
Packit Service 97d2fb
	      if (likely (tscn != NULL))
Packit Service 97d2fb
		for (size_t i = 0; i < nrefs; ++i)
Packit Service 97d2fb
		  if (mod->reloc_info->refs[i].scn == tscn)
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      mod->reloc_info->refs[i].relocs = scn;
Packit Service 97d2fb
		      break;
Packit Service 97d2fb
		    }
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
free_refs:
Packit Service 97d2fb
  while (refs != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      struct secref *ref = refs;
Packit Service 97d2fb
      refs = ref->next;
Packit Service 97d2fb
      free (ref);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return nrefs;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_module_relocations (Dwfl_Module *mod)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (mod == NULL)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  switch (mod->e_type)
Packit Service 97d2fb
    {
Packit Service 97d2fb
    case ET_REL:
Packit Service 97d2fb
      return cache_sections (mod);
Packit Service 97d2fb
Packit Service 97d2fb
    case ET_DYN:
Packit Service 97d2fb
      return 1;
Packit Service 97d2fb
Packit Service 97d2fb
    case ET_EXEC:
Packit Service 97d2fb
      assert (mod->main.vaddr == mod->low_addr);
Packit Service 97d2fb
      break;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
const char *
Packit Service 97d2fb
dwfl_module_relocation_info (Dwfl_Module *mod, unsigned int idx,
Packit Service 97d2fb
			     Elf32_Word *shndxp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (mod == NULL)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  switch (mod->e_type)
Packit Service 97d2fb
    {
Packit Service 97d2fb
    case ET_REL:
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case ET_DYN:
Packit Service 97d2fb
      if (idx != 0)
Packit Service 97d2fb
	return NULL;
Packit Service 97d2fb
      if (shndxp)
Packit Service 97d2fb
	*shndxp = SHN_ABS;
Packit Service 97d2fb
      return "";
Packit Service 97d2fb
Packit Service 97d2fb
    default:
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (cache_sections (mod) < 0)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  struct dwfl_relocation *sections = mod->reloc_info;
Packit Service 97d2fb
Packit Service 97d2fb
  if (idx >= sections->count)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  if (shndxp)
Packit Service 97d2fb
    *shndxp = elf_ndxscn (sections->refs[idx].scn);
Packit Service 97d2fb
Packit Service 97d2fb
  return sections->refs[idx].name;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Check that MOD is valid and make sure its relocation has been done.  */
Packit Service 97d2fb
static bool
Packit Service 97d2fb
check_module (Dwfl_Module *mod)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (mod == NULL)
Packit Service 97d2fb
    return true;
Packit Service 97d2fb
Packit Service 97d2fb
  if (INTUSE(dwfl_module_getsymtab) (mod) < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwfl_Error error = dwfl_errno ();
Packit Service 97d2fb
      if (error != DWFL_E_NO_SYMTAB)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdwfl_seterrno (error);
Packit Service 97d2fb
	  return true;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (mod->dw == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwarf_Addr bias;
Packit Service 97d2fb
      if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  Dwfl_Error error = dwfl_errno ();
Packit Service 97d2fb
	  if (error != DWFL_E_NO_DWARF)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdwfl_seterrno (error);
Packit Service 97d2fb
	      return true;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return false;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Find the index in MOD->reloc_info.refs containing *ADDR.  */
Packit Service 97d2fb
static int
Packit Service 97d2fb
find_section (Dwfl_Module *mod, Dwarf_Addr *addr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (cache_sections (mod) < 0)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  struct dwfl_relocation *sections = mod->reloc_info;
Packit Service 97d2fb
Packit Service 97d2fb
  /* The sections are sorted by address, so we can use binary search.  */
Packit Service 97d2fb
  size_t l = 0, u = sections->count;
Packit Service 97d2fb
  while (l < u)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      size_t idx = (l + u) / 2;
Packit Service 97d2fb
      if (*addr < sections->refs[idx].start)
Packit Service 97d2fb
	u = idx;
Packit Service 97d2fb
      else if (*addr > sections->refs[idx].end)
Packit Service 97d2fb
	l = idx + 1;
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Consider the limit of a section to be inside it, unless it's
Packit Service 97d2fb
	     inside the next one.  A section limit address can appear in
Packit Service 97d2fb
	     line records.  */
Packit Service 97d2fb
	  if (*addr == sections->refs[idx].end
Packit Service 97d2fb
	      && idx + 1 < sections->count
Packit Service 97d2fb
	      && *addr == sections->refs[idx + 1].start)
Packit Service 97d2fb
	    ++idx;
Packit Service 97d2fb
Packit Service 97d2fb
	  *addr -= sections->refs[idx].start;
Packit Service 97d2fb
	  return idx;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH));
Packit Service 97d2fb
  return -1;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
size_t
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_find_section_ndx (Dwfl_Module *mod, Dwarf_Addr *addr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  int idx = find_section (mod, addr);
Packit Service 97d2fb
  if (unlikely (idx == -1))
Packit Service 97d2fb
    return SHN_UNDEF;
Packit Service 97d2fb
Packit Service 97d2fb
  return elf_ndxscn (mod->reloc_info->refs[idx].scn);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (unlikely (check_module (mod)))
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  switch (mod->e_type)
Packit Service 97d2fb
    {
Packit Service 97d2fb
    case ET_REL:
Packit Service 97d2fb
      return find_section (mod, addr);
Packit Service 97d2fb
Packit Service 97d2fb
    case ET_DYN:
Packit Service 97d2fb
      /* All relative to first and only relocation base: module start.  */
Packit Service 97d2fb
      *addr -= mod->low_addr;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    default:
Packit Service 97d2fb
      /* Already absolute, dwfl_module_relocations returned zero.  We
Packit Service 97d2fb
	 shouldn't really have been called, but it's a harmless no-op.  */
Packit Service 97d2fb
      break;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_module_relocate_address)
Packit Service 97d2fb
Packit Service 97d2fb
Elf_Scn *
Packit Service 97d2fb
dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address,
Packit Service 97d2fb
			     Dwarf_Addr *bias)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (check_module (mod))
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  int idx = find_section (mod, address);
Packit Service 97d2fb
  if (idx < 0)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  if (mod->reloc_info->refs[idx].relocs != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      assert (mod->e_type == ET_REL);
Packit Service 97d2fb
Packit Service 97d2fb
      Elf_Scn *tscn = mod->reloc_info->refs[idx].scn;
Packit Service 97d2fb
      Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs;
Packit Service 97d2fb
      Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf,
Packit Service 97d2fb
						      relocscn, tscn, true);
Packit Service 97d2fb
      if (likely (result == DWFL_E_NOERROR))
Packit Service 97d2fb
	mod->reloc_info->refs[idx].relocs = NULL;
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdwfl_seterrno (result);
Packit Service 97d2fb
	  return NULL;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  *bias = dwfl_adjusted_address (mod, 0);
Packit Service 97d2fb
  return mod->reloc_info->refs[idx].scn;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_module_address_section)