Blame libdwfl/elf-from-memory.c

Packit 032894
/* Reconstruct an ELF file by reading the segments out of remote memory.
Packit 032894
   Copyright (C) 2005-2011, 2014, 2015 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
#include <config.h>
Packit 032894
#include "../libelf/libelfP.h"
Packit 032894
#undef _
Packit 032894
Packit 032894
#include "libdwflP.h"
Packit 032894
Packit 032894
#include <gelf.h>
Packit 032894
#include <sys/types.h>
Packit 032894
#include <stdbool.h>
Packit 032894
#include <stdlib.h>
Packit 032894
#include <string.h>
Packit 032894
Packit 032894
/* Reconstruct an ELF file by reading the segments out of remote memory
Packit 032894
   based on the ELF file header at EHDR_VMA and the ELF program headers it
Packit 032894
   points to.  If not null, *LOADBASEP is filled in with the difference
Packit 032894
   between the addresses from which the segments were read, and the
Packit 032894
   addresses the file headers put them at.
Packit 032894
Packit 032894
   The function READ_MEMORY is called to copy at least MINREAD and at most
Packit 032894
   MAXREAD bytes from the remote memory at target address ADDRESS into the
Packit 032894
   local buffer at DATA; it should return -1 for errors (with code in
Packit 032894
   `errno'), 0 if it failed to read at least MINREAD bytes due to EOF, or
Packit 032894
   the number of bytes read if >= MINREAD.  ARG is passed through.
Packit 032894
Packit 032894
   PAGESIZE is the minimum page size and alignment used for the PT_LOAD
Packit 032894
   segments.  */
Packit 032894
Packit 032894
Elf *
Packit 032894
elf_from_remote_memory (GElf_Addr ehdr_vma,
Packit 032894
			GElf_Xword pagesize,
Packit 032894
			GElf_Addr *loadbasep,
Packit 032894
			ssize_t (*read_memory) (void *arg, void *data,
Packit 032894
						GElf_Addr address,
Packit 032894
						size_t minread,
Packit 032894
						size_t maxread),
Packit 032894
			void *arg)
Packit 032894
{
Packit 032894
  /* We might have to reserve some memory for the phdrs.  Set to NULL
Packit 032894
     here so we can always safely free it.  */
Packit 032894
  void *phdrsp = NULL;
Packit 032894
Packit 032894
  /* First read in the file header and check its sanity.  */
Packit 032894
Packit 032894
  const size_t initial_bufsize = 256;
Packit 032894
  unsigned char *buffer = malloc (initial_bufsize);
Packit 032894
  if (unlikely (buffer == NULL))
Packit 032894
    {
Packit 032894
    no_memory:
Packit 032894
      __libdwfl_seterrno (DWFL_E_NOMEM);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  ssize_t nread = (*read_memory) (arg, buffer, ehdr_vma,
Packit 032894
				  sizeof (Elf32_Ehdr), initial_bufsize);
Packit 032894
  if (nread <= 0)
Packit 032894
    {
Packit 032894
    read_error:
Packit 032894
      free (buffer);
Packit 032894
      free (phdrsp);
Packit 032894
      __libdwfl_seterrno (nread < 0 ? DWFL_E_ERRNO : DWFL_E_TRUNCATED);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  if (memcmp (buffer, ELFMAG, SELFMAG) != 0)
Packit 032894
    {
Packit 032894
    bad_elf:
Packit 032894
      free (buffer);
Packit 032894
      free (phdrsp);
Packit 032894
      __libdwfl_seterrno (DWFL_E_BADELF);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Extract the information we need from the file header.  */
Packit 032894
Packit 032894
  union
Packit 032894
  {
Packit 032894
    Elf32_Ehdr e32;
Packit 032894
    Elf64_Ehdr e64;
Packit 032894
  } ehdr;
Packit 032894
  Elf_Data xlatefrom =
Packit 032894
    {
Packit 032894
      .d_type = ELF_T_EHDR,
Packit 032894
      .d_buf = buffer,
Packit 032894
      .d_version = EV_CURRENT,
Packit 032894
    };
Packit 032894
  Elf_Data xlateto =
Packit 032894
    {
Packit 032894
      .d_type = ELF_T_EHDR,
Packit 032894
      .d_buf = &ehdr,
Packit 032894
      .d_size = sizeof ehdr,
Packit 032894
      .d_version = EV_CURRENT,
Packit 032894
    };
Packit 032894
Packit 032894
  GElf_Off phoff;
Packit 032894
  uint_fast16_t phnum;
Packit 032894
  uint_fast16_t phentsize;
Packit 032894
  GElf_Off shdrs_end;
Packit 032894
Packit 032894
  switch (buffer[EI_CLASS])
Packit 032894
    {
Packit 032894
    case ELFCLASS32:
Packit 032894
      xlatefrom.d_size = sizeof (Elf32_Ehdr);
Packit 032894
      if (elf32_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
Packit 032894
	{
Packit 032894
	libelf_error:
Packit 032894
	  __libdwfl_seterrno (DWFL_E_LIBELF);
Packit 032894
	  return NULL;
Packit 032894
	}
Packit 032894
      phoff = ehdr.e32.e_phoff;
Packit 032894
      phnum = ehdr.e32.e_phnum;
Packit 032894
      phentsize = ehdr.e32.e_phentsize;
Packit 032894
      if (phentsize != sizeof (Elf32_Phdr) || phnum == 0)
Packit 032894
	goto bad_elf;
Packit 032894
      /* NOTE if the number of sections is > 0xff00 then e_shnum
Packit 032894
	 is zero and the actual number would come from the section
Packit 032894
	 zero sh_size field. We ignore this here because getting shdrs
Packit 032894
	 is just a nice bonus (see below where we trim the last phdrs
Packit 032894
	 PT_LOAD segment).  */
Packit 032894
      shdrs_end = ehdr.e32.e_shoff + ehdr.e32.e_shnum * ehdr.e32.e_shentsize;
Packit 032894
      break;
Packit 032894
Packit 032894
    case ELFCLASS64:
Packit 032894
      xlatefrom.d_size = sizeof (Elf64_Ehdr);
Packit 032894
      if (elf64_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
Packit 032894
	goto libelf_error;
Packit 032894
      phoff = ehdr.e64.e_phoff;
Packit 032894
      phnum = ehdr.e64.e_phnum;
Packit 032894
      phentsize = ehdr.e64.e_phentsize;
Packit 032894
      if (phentsize != sizeof (Elf64_Phdr) || phnum == 0)
Packit 032894
	goto bad_elf;
Packit 032894
      /* See the NOTE above for shdrs_end and ehdr.e32.e_shnum.  */
Packit 032894
      shdrs_end = ehdr.e64.e_shoff + ehdr.e64.e_shnum * ehdr.e64.e_shentsize;
Packit 032894
      break;
Packit 032894
Packit 032894
    default:
Packit 032894
      goto bad_elf;
Packit 032894
    }
Packit 032894
Packit 032894
Packit 032894
  /* The file header tells where to find the program headers.
Packit 032894
     These are what we use to actually choose what to read.  */
Packit 032894
Packit 032894
  xlatefrom.d_type = xlateto.d_type = ELF_T_PHDR;
Packit 032894
  xlatefrom.d_size = phnum * phentsize;
Packit 032894
Packit 032894
  if ((size_t) nread >= phoff + phnum * phentsize)
Packit 032894
    /* We already have all the phdrs from the initial read.  */
Packit 032894
    xlatefrom.d_buf = buffer + phoff;
Packit 032894
  else
Packit 032894
    {
Packit 032894
      /* Read in the program headers.  */
Packit 032894
Packit 032894
      if (initial_bufsize < (size_t)phnum * phentsize)
Packit 032894
	{
Packit 032894
	  unsigned char *newbuf = realloc (buffer, phnum * phentsize);
Packit 032894
	  if (newbuf == NULL)
Packit 032894
	    {
Packit 032894
	      free (buffer);
Packit 032894
	      free (phdrsp);
Packit 032894
	      goto no_memory;
Packit 032894
	    }
Packit 032894
	  buffer = newbuf;
Packit 032894
	}
Packit 032894
      nread = (*read_memory) (arg, buffer, ehdr_vma + phoff,
Packit 032894
			      phnum * phentsize, phnum * phentsize);
Packit 032894
      if (nread <= 0)
Packit 032894
	goto read_error;
Packit 032894
Packit 032894
      xlatefrom.d_buf = buffer;
Packit 032894
    }
Packit 032894
Packit 032894
  bool class32 = ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32;
Packit 032894
  size_t phdr_size = class32 ? sizeof (Elf32_Phdr) : sizeof (Elf64_Phdr);
Packit 032894
  if (unlikely (phnum > SIZE_MAX / phdr_size))
Packit 032894
    {
Packit 032894
      free (buffer);
Packit 032894
      goto no_memory;
Packit 032894
    }
Packit 032894
  const size_t phdrsp_bytes = phnum * phdr_size;
Packit 032894
  phdrsp = malloc (phdrsp_bytes);
Packit 032894
  if (unlikely (phdrsp == NULL))
Packit 032894
    {
Packit 032894
      free (buffer);
Packit 032894
      goto no_memory;
Packit 032894
    }
Packit 032894
Packit 032894
  xlateto.d_buf = phdrsp;
Packit 032894
  xlateto.d_size = phdrsp_bytes;
Packit 032894
Packit 032894
  /* Scan for PT_LOAD segments to find the total size of the file image.  */
Packit 032894
  size_t contents_size = 0;
Packit 032894
  GElf_Off segments_end = 0;
Packit 032894
  GElf_Off segments_end_mem = 0;
Packit 032894
  GElf_Addr loadbase = ehdr_vma;
Packit 032894
  bool found_base = false;
Packit 032894
  Elf32_Phdr (*p32)[phnum] = phdrsp;
Packit 032894
  Elf64_Phdr (*p64)[phnum] = phdrsp;
Packit 032894
  switch (ehdr.e32.e_ident[EI_CLASS])
Packit 032894
    {
Packit 032894
      /* Sanity checks segments and calculates segment_end,
Packit 032894
	 segments_end, segments_end_mem and loadbase (if not
Packit 032894
	 found_base yet).  Returns true if sanity checking failed,
Packit 032894
	 false otherwise.  */
Packit 032894
      inline bool handle_segment (GElf_Addr vaddr, GElf_Off offset,
Packit 032894
				  GElf_Xword filesz, GElf_Xword memsz)
Packit 032894
	{
Packit 032894
	  /* Sanity check the segment load aligns with the pagesize.  */
Packit 032894
	  if (((vaddr - offset) & (pagesize - 1)) != 0)
Packit 032894
	    return true;
Packit 032894
Packit 032894
	  GElf_Off segment_end = ((offset + filesz + pagesize - 1)
Packit 032894
				  & -pagesize);
Packit 032894
Packit 032894
	  if (segment_end > (GElf_Off) contents_size)
Packit 032894
	    contents_size = segment_end;
Packit 032894
Packit 032894
	  if (!found_base && (offset & -pagesize) == 0)
Packit 032894
	    {
Packit 032894
	      loadbase = ehdr_vma - (vaddr & -pagesize);
Packit 032894
	      found_base = true;
Packit 032894
	    }
Packit 032894
Packit 032894
	  segments_end = offset + filesz;
Packit 032894
	  segments_end_mem = offset + memsz;
Packit 032894
	  return false;
Packit 032894
	}
Packit 032894
Packit 032894
    case ELFCLASS32:
Packit 032894
      if (elf32_xlatetom (&xlateto, &xlatefrom,
Packit 032894
			  ehdr.e32.e_ident[EI_DATA]) == NULL)
Packit 032894
	goto libelf_error;
Packit 032894
      for (uint_fast16_t i = 0; i < phnum; ++i)
Packit 032894
	if ((*p32)[i].p_type == PT_LOAD)
Packit 032894
	  if (handle_segment ((*p32)[i].p_vaddr, (*p32)[i].p_offset,
Packit 032894
			      (*p32)[i].p_filesz, (*p32)[i].p_memsz))
Packit 032894
	    goto bad_elf;
Packit 032894
      break;
Packit 032894
Packit 032894
    case ELFCLASS64:
Packit 032894
      if (elf64_xlatetom (&xlateto, &xlatefrom,
Packit 032894
			  ehdr.e64.e_ident[EI_DATA]) == NULL)
Packit 032894
	goto libelf_error;
Packit 032894
      for (uint_fast16_t i = 0; i < phnum; ++i)
Packit 032894
	if ((*p64)[i].p_type == PT_LOAD)
Packit 032894
	  if (handle_segment ((*p64)[i].p_vaddr, (*p64)[i].p_offset,
Packit 032894
			      (*p64)[i].p_filesz, (*p64)[i].p_memsz))
Packit 032894
	    goto bad_elf;
Packit 032894
      break;
Packit 032894
Packit 032894
    default:
Packit 032894
      abort ();
Packit 032894
      break;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Trim the last segment so we don't bother with zeros in the last page
Packit 032894
     that are off the end of the file.  However, if the extra bit in that
Packit 032894
     page includes the section headers and the memory isn't extended (which
Packit 032894
     might indicate it will have been reused otherwise), keep them.  */
Packit 032894
  if ((GElf_Off) contents_size > segments_end
Packit 032894
      && (GElf_Off) contents_size >= shdrs_end
Packit 032894
      && segments_end == segments_end_mem)
Packit 032894
    {
Packit 032894
      contents_size = segments_end;
Packit 032894
      if ((GElf_Off) contents_size < shdrs_end)
Packit 032894
	contents_size = shdrs_end;
Packit 032894
    }
Packit 032894
  else
Packit 032894
    contents_size = segments_end;
Packit 032894
Packit 032894
  free (buffer);
Packit 032894
Packit 032894
  /* Now we know the size of the whole image we want read in.  */
Packit 032894
  buffer = calloc (1, contents_size);
Packit 032894
  if (buffer == NULL)
Packit 032894
    {
Packit 032894
      free (phdrsp);
Packit 032894
      goto no_memory;
Packit 032894
    }
Packit 032894
Packit 032894
  switch (ehdr.e32.e_ident[EI_CLASS])
Packit 032894
    {
Packit 032894
      /* Reads the given segment.  Returns true if reading fails,
Packit 032894
	 false otherwise.  */
Packit 032894
      inline bool handle_segment (GElf_Addr vaddr, GElf_Off offset,
Packit 032894
				  GElf_Xword filesz)
Packit 032894
	{
Packit 032894
	  GElf_Off start = offset & -pagesize;
Packit 032894
	  GElf_Off end = (offset + filesz + pagesize - 1) & -pagesize;
Packit 032894
	  if (end > (GElf_Off) contents_size)
Packit 032894
	    end = contents_size;
Packit 032894
	  nread = (*read_memory) (arg, buffer + start,
Packit 032894
				  (loadbase + vaddr) & -pagesize,
Packit 032894
				  end - start, end - start);
Packit 032894
	  return nread <= 0;
Packit 032894
	}
Packit 032894
Packit 032894
    case ELFCLASS32:
Packit 032894
      for (uint_fast16_t i = 0; i < phnum; ++i)
Packit 032894
	if ((*p32)[i].p_type == PT_LOAD)
Packit 032894
	  if (handle_segment ((*p32)[i].p_vaddr, (*p32)[i].p_offset,
Packit 032894
			      (*p32)[i].p_filesz))
Packit 032894
	    goto read_error;
Packit 032894
Packit 032894
      /* If the segments visible in memory didn't include the section
Packit 032894
	 headers, then clear them from the file header.  */
Packit 032894
      if (contents_size < shdrs_end)
Packit 032894
	{
Packit 032894
	  ehdr.e32.e_shoff = 0;
Packit 032894
	  ehdr.e32.e_shnum = 0;
Packit 032894
	  ehdr.e32.e_shstrndx = 0;
Packit 032894
	}
Packit 032894
Packit 032894
      /* This will normally have been in the first PT_LOAD segment.  But it
Packit 032894
	 conceivably could be missing, and we might have just changed it.  */
Packit 032894
      xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR;
Packit 032894
      xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e32;
Packit 032894
      xlatefrom.d_buf = &ehdr.e32;
Packit 032894
      xlateto.d_buf = buffer;
Packit 032894
      if (elf32_xlatetof (&xlateto, &xlatefrom,
Packit 032894
			  ehdr.e32.e_ident[EI_DATA]) == NULL)
Packit 032894
	goto libelf_error;
Packit 032894
      break;
Packit 032894
Packit 032894
    case ELFCLASS64:
Packit 032894
      for (uint_fast16_t i = 0; i < phnum; ++i)
Packit 032894
	if ((*p64)[i].p_type == PT_LOAD)
Packit 032894
	  if (handle_segment ((*p64)[i].p_vaddr, (*p64)[i].p_offset,
Packit 032894
			      (*p64)[i].p_filesz))
Packit 032894
	    goto read_error;
Packit 032894
Packit 032894
      /* If the segments visible in memory didn't include the section
Packit 032894
	 headers, then clear them from the file header.  */
Packit 032894
      if (contents_size < shdrs_end)
Packit 032894
	{
Packit 032894
	  ehdr.e64.e_shoff = 0;
Packit 032894
	  ehdr.e64.e_shnum = 0;
Packit 032894
	  ehdr.e64.e_shstrndx = 0;
Packit 032894
	}
Packit 032894
Packit 032894
      /* This will normally have been in the first PT_LOAD segment.  But it
Packit 032894
	 conceivably could be missing, and we might have just changed it.  */
Packit 032894
      xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR;
Packit 032894
      xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e64;
Packit 032894
      xlatefrom.d_buf = &ehdr.e64;
Packit 032894
      xlateto.d_buf = buffer;
Packit 032894
      if (elf64_xlatetof (&xlateto, &xlatefrom,
Packit 032894
			  ehdr.e64.e_ident[EI_DATA]) == NULL)
Packit 032894
	goto libelf_error;
Packit 032894
      break;
Packit 032894
Packit 032894
    default:
Packit 032894
      abort ();
Packit 032894
      break;
Packit 032894
    }
Packit 032894
Packit 032894
  free (phdrsp);
Packit 032894
  phdrsp = NULL;
Packit 032894
Packit 032894
  /* Now we have the image.  Open libelf on it.  */
Packit 032894
Packit 032894
  Elf *elf = elf_memory ((char *) buffer, contents_size);
Packit 032894
  if (elf == NULL)
Packit 032894
    {
Packit 032894
      free (buffer);
Packit 032894
      goto libelf_error;
Packit 032894
    }
Packit 032894
Packit 032894
  elf->flags |= ELF_F_MALLOCED;
Packit 032894
  if (loadbasep != NULL)
Packit 032894
    *loadbasep = loadbase;
Packit 032894
  return elf;
Packit 032894
}