Blame libdwfl/dwfl_report_elf.c

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