Blame libdwfl/dwfl_report_elf.c

Packit Service 97d2fb
/* Report a module to libdwfl based on ELF program headers.
Packit Service 97d2fb
   Copyright (C) 2005-2010 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
#include <fcntl.h>
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* We start every ET_REL module at a moderately aligned boundary.
Packit Service 97d2fb
   This keeps the low addresses easy to read compared to a layout
Packit Service 97d2fb
   starting at 0 (as when using -e).  It also makes it unlikely
Packit Service 97d2fb
   that a middle section will have a larger alignment and require
Packit Service 97d2fb
   rejiggering (see below).  */
Packit Service 97d2fb
#define REL_MIN_ALIGN	((GElf_Xword) 0x100)
Packit Service 97d2fb
Packit Service 97d2fb
bool
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_elf_address_range (Elf *elf, GElf_Addr base, bool add_p_vaddr,
Packit Service 97d2fb
			     bool sanity, GElf_Addr *vaddrp,
Packit Service 97d2fb
			     GElf_Addr *address_syncp, GElf_Addr *startp,
Packit Service 97d2fb
			     GElf_Addr *endp, GElf_Addr *biasp,
Packit Service 97d2fb
			     GElf_Half *e_typep)
Packit Service 97d2fb
{
Packit Service 97d2fb
  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
Packit Service 97d2fb
  if (ehdr == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
    elf_error:
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_LIBELF);
Packit Service 97d2fb
      return false;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  GElf_Addr vaddr = 0;
Packit Service 97d2fb
  GElf_Addr address_sync = 0;
Packit Service 97d2fb
  GElf_Addr start = 0, end = 0, bias = 0;
Packit Service 97d2fb
  switch (ehdr->e_type)
Packit Service 97d2fb
    {
Packit Service 97d2fb
    case ET_REL:
Packit Service 97d2fb
      /* For a relocatable object, we do an arbitrary section layout.
Packit Service 97d2fb
	 By updating the section header in place, we leave the layout
Packit Service 97d2fb
	 information to be found by relocation.  */
Packit Service 97d2fb
Packit Service 97d2fb
      start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
Packit Service 97d2fb
Packit Service 97d2fb
      bool first = true;
Packit Service 97d2fb
      Elf_Scn *scn = NULL;
Packit Service 97d2fb
      while ((scn = elf_nextscn (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 (unlikely (shdr == NULL))
Packit Service 97d2fb
	    goto elf_error;
Packit Service 97d2fb
Packit Service 97d2fb
	  if (shdr->sh_flags & SHF_ALLOC)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      const GElf_Xword align = shdr->sh_addralign ?: 1;
Packit Service 97d2fb
	      const GElf_Addr next = (end + align - 1) & -align;
Packit Service 97d2fb
	      if (shdr->sh_addr == 0
Packit Service 97d2fb
		  /* Once we've started doing layout we have to do it all,
Packit Service 97d2fb
		     unless we just layed out the first section at 0 when
Packit Service 97d2fb
		     it already was at 0.  */
Packit Service 97d2fb
		  || (bias == 0 && end > start && end != next))
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  shdr->sh_addr = next;
Packit Service 97d2fb
		  if (end == base)
Packit Service 97d2fb
		    /* This is the first section assigned a location.
Packit Service 97d2fb
		       Use its aligned address as the module's base.  */
Packit Service 97d2fb
		    start = base = shdr->sh_addr;
Packit Service 97d2fb
		  else if (unlikely (base & (align - 1)))
Packit Service 97d2fb
		    {
Packit Service 97d2fb
		      /* If BASE has less than the maximum alignment of
Packit Service 97d2fb
			 any section, we eat more than the optimal amount
Packit Service 97d2fb
			 of padding and so make the module's apparent
Packit Service 97d2fb
			 size come out larger than it would when placed
Packit Service 97d2fb
			 at zero.  So reset the layout with a better base.  */
Packit Service 97d2fb
Packit Service 97d2fb
		      start = end = base = (base + align - 1) & -align;
Packit Service 97d2fb
		      Elf_Scn *prev_scn = NULL;
Packit Service 97d2fb
		      do
Packit Service 97d2fb
			{
Packit Service 97d2fb
			  prev_scn = elf_nextscn (elf, prev_scn);
Packit Service 97d2fb
			  GElf_Shdr prev_shdr_mem;
Packit Service 97d2fb
			  GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
Packit Service 97d2fb
							       &prev_shdr_mem);
Packit Service 97d2fb
			  if (unlikely (prev_shdr == NULL))
Packit Service 97d2fb
			    goto elf_error;
Packit Service 97d2fb
			  if (prev_shdr->sh_flags & SHF_ALLOC)
Packit Service 97d2fb
			    {
Packit Service 97d2fb
			      const GElf_Xword prev_align
Packit Service 97d2fb
				= prev_shdr->sh_addralign ?: 1;
Packit Service 97d2fb
Packit Service 97d2fb
			      prev_shdr->sh_addr
Packit Service 97d2fb
				= (end + prev_align - 1) & -prev_align;
Packit Service 97d2fb
			      end = prev_shdr->sh_addr + prev_shdr->sh_size;
Packit Service 97d2fb
Packit Service 97d2fb
			      if (unlikely (! gelf_update_shdr (prev_scn,
Packit Service 97d2fb
								prev_shdr)))
Packit Service 97d2fb
				goto elf_error;
Packit Service 97d2fb
			    }
Packit Service 97d2fb
			}
Packit Service 97d2fb
		      while (prev_scn != scn);
Packit Service 97d2fb
		      continue;
Packit Service 97d2fb
		    }
Packit Service 97d2fb
Packit Service 97d2fb
		  end = shdr->sh_addr + shdr->sh_size;
Packit Service 97d2fb
		  if (likely (shdr->sh_addr != 0)
Packit Service 97d2fb
		      && unlikely (! gelf_update_shdr (scn, shdr)))
Packit Service 97d2fb
		    goto elf_error;
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      else
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  /* The address is already assigned.  Just track it.  */
Packit Service 97d2fb
		  if (first || end < shdr->sh_addr + shdr->sh_size)
Packit Service 97d2fb
		    end = shdr->sh_addr + shdr->sh_size;
Packit Service 97d2fb
		  if (first || bias > shdr->sh_addr)
Packit Service 97d2fb
		    /* This is the lowest address in the module.  */
Packit Service 97d2fb
		    bias = shdr->sh_addr;
Packit Service 97d2fb
Packit Service 97d2fb
		  if ((shdr->sh_addr - bias + base) & (align - 1))
Packit Service 97d2fb
		    /* This section winds up misaligned using BASE.
Packit Service 97d2fb
		       Adjust BASE upwards to make it congruent to
Packit Service 97d2fb
		       the lowest section address in the file modulo ALIGN.  */
Packit Service 97d2fb
		    base = (((base + align - 1) & -align)
Packit Service 97d2fb
			    + (bias & (align - 1)));
Packit Service 97d2fb
		}
Packit Service 97d2fb
Packit Service 97d2fb
	      first = false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (bias != 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* The section headers had nonzero sh_addr values.  The layout
Packit Service 97d2fb
	     was already done.  We've just collected the total span.
Packit Service 97d2fb
	     Now just compute the bias from the requested base.  */
Packit Service 97d2fb
	  start = base;
Packit Service 97d2fb
	  end = end - bias + start;
Packit Service 97d2fb
	  bias = start - bias;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
      /* Everything else has to have program headers.  */
Packit Service 97d2fb
Packit Service 97d2fb
    case ET_EXEC:
Packit Service 97d2fb
    case ET_CORE:
Packit Service 97d2fb
      /* An assigned base address is meaningless for these.  */
Packit Service 97d2fb
      base = 0;
Packit Service 97d2fb
      add_p_vaddr = true;
Packit Service 97d2fb
      FALLTHROUGH;
Packit Service 97d2fb
    case ET_DYN:
Packit Service 97d2fb
    default:;
Packit Service 97d2fb
      size_t phnum;
Packit Service 97d2fb
      if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
Packit Service 97d2fb
	goto elf_error;
Packit Service 97d2fb
      for (size_t i = 0; i < phnum; ++i)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
Packit Service 97d2fb
	  if (unlikely (ph == NULL))
Packit Service 97d2fb
	    goto elf_error;
Packit Service 97d2fb
	  if (ph->p_type == PT_LOAD)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      vaddr = ph->p_vaddr & -ph->p_align;
Packit Service 97d2fb
	      address_sync = ph->p_vaddr + ph->p_memsz;
Packit Service 97d2fb
	      break;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
      if (add_p_vaddr)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  start = base + vaddr;
Packit Service 97d2fb
	  bias = base;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  start = base;
Packit Service 97d2fb
	  bias = base - vaddr;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      for (size_t i = phnum; i-- > 0;)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
Packit Service 97d2fb
	  if (unlikely (ph == NULL))
Packit Service 97d2fb
	    goto elf_error;
Packit Service 97d2fb
	  if (ph->p_type == PT_LOAD
Packit Service 97d2fb
	      && ph->p_vaddr + ph->p_memsz > 0)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      end = bias + (ph->p_vaddr + ph->p_memsz);
Packit Service 97d2fb
	      break;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (end == 0 && sanity)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_NO_PHDR);
Packit Service 97d2fb
	  return false;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      break;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (vaddrp)
Packit Service 97d2fb
    *vaddrp = vaddr;
Packit Service 97d2fb
  if (address_syncp)
Packit Service 97d2fb
    *address_syncp = address_sync;
Packit Service 97d2fb
  if (startp)
Packit Service 97d2fb
    *startp = start;
Packit Service 97d2fb
  if (endp)
Packit Service 97d2fb
    *endp = end;
Packit Service 97d2fb
  if (biasp)
Packit Service 97d2fb
    *biasp = bias;
Packit Service 97d2fb
  if (e_typep)
Packit Service 97d2fb
    *e_typep = ehdr->e_type;
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Dwfl_Module *
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
Packit Service 97d2fb
		      int fd, Elf *elf, GElf_Addr base, bool add_p_vaddr,
Packit Service 97d2fb
		      bool sanity)
Packit Service 97d2fb
{
Packit Service 97d2fb
  GElf_Addr vaddr, address_sync, start, end, bias;
Packit Service 97d2fb
  GElf_Half e_type;
Packit Service 97d2fb
  if (! __libdwfl_elf_address_range (elf, base, add_p_vaddr, sanity, &vaddr,
Packit Service 97d2fb
				     &address_sync, &start, &end, &bias,
Packit Service 97d2fb
				     &e_type))
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
  Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
Packit Service 97d2fb
  if (m != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (m->main.name == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  m->main.name = strdup (file_name);
Packit Service 97d2fb
	  m->main.fd = fd;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else if ((fd >= 0 && m->main.fd != fd)
Packit Service 97d2fb
	       || strcmp (m->main.name, file_name))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	overlap:
Packit Service 97d2fb
	  m->gc = true;
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_OVERLAP);
Packit Service 97d2fb
	  return NULL;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* Preinstall the open ELF handle for the module.  */
Packit Service 97d2fb
      if (m->main.elf == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  m->main.elf = elf;
Packit Service 97d2fb
	  m->main.vaddr = vaddr;
Packit Service 97d2fb
	  m->main.address_sync = address_sync;
Packit Service 97d2fb
	  m->main_bias = bias;
Packit Service 97d2fb
	  m->e_type = e_type;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  elf_end (elf);
Packit Service 97d2fb
	  if (m->main_bias != bias
Packit Service 97d2fb
	      || m->main.vaddr != vaddr || m->main.address_sync != address_sync)
Packit Service 97d2fb
	    goto overlap;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
  return m;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Dwfl_Module *
Packit Service 97d2fb
dwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
Packit Service 97d2fb
		 GElf_Addr base, bool add_p_vaddr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  bool closefd = false;
Packit Service 97d2fb
  if (fd < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      closefd = true;
Packit Service 97d2fb
      fd = open (file_name, O_RDONLY);
Packit Service 97d2fb
      if (fd < 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_ERRNO);
Packit Service 97d2fb
	  return NULL;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  Elf *elf;
Packit Service 97d2fb
  Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false);
Packit Service 97d2fb
  if (error != DWFL_E_NOERROR)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (error);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
Packit Service 97d2fb
					   fd, elf, base, add_p_vaddr, true);
Packit Service 97d2fb
  if (mod == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      elf_end (elf);
Packit Service 97d2fb
      if (closefd)
Packit Service 97d2fb
	close (fd);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return mod;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_report_elf)
Packit Service 97d2fb
NEW_VERSION (dwfl_report_elf, ELFUTILS_0.156)
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef SYMBOL_VERSIONING
Packit Service 97d2fb
Dwfl_Module *
Packit Service 97d2fb
  _compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name,
Packit Service 97d2fb
					       const char *file_name, int fd,
Packit Service 97d2fb
					       GElf_Addr base);
Packit Service 97d2fb
COMPAT_VERSION_NEWPROTO (dwfl_report_elf, ELFUTILS_0.122, without_add_p_vaddr)
Packit Service 97d2fb
Packit Service 97d2fb
Dwfl_Module *
Packit Service 97d2fb
_compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name,
Packit Service 97d2fb
					     const char *file_name, int fd,
Packit Service 97d2fb
					     GElf_Addr base)
Packit Service 97d2fb
{
Packit Service 97d2fb
  return dwfl_report_elf (dwfl, name, file_name, fd, base, true);
Packit Service 97d2fb
}
Packit Service 97d2fb
#endif