Blame libdwfl/relocate.c

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