Blame libdwfl/core-file.c

Packit Service 97d2fb
/* Core file handling.
Packit Service 97d2fb
   Copyright (C) 2008-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
#include <config.h>
Packit Service 97d2fb
#include "../libelf/libelfP.h"	/* For NOTE_ALIGN.  */
Packit Service 97d2fb
#undef	_
Packit Service 97d2fb
#include "libdwflP.h"
Packit Service 97d2fb
#include <gelf.h>
Packit Service 97d2fb
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
#include <endian.h>
Packit Service 97d2fb
#include <byteswap.h>
Packit Service 97d2fb
#include "system.h"
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* On failure return, we update *NEXT to point back at OFFSET.  */
Packit Service 97d2fb
static inline Elf *
Packit Service 97d2fb
do_fail (int error, off_t *next, off_t offset)
Packit Service 97d2fb
{
Packit Service 97d2fb
    if (next != NULL)
Packit Service 97d2fb
      *next = offset;
Packit Service 97d2fb
    //__libelf_seterrno (error);
Packit Service 97d2fb
    __libdwfl_seterrno (DWFL_E (LIBELF, error));
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
#define fail(error) do_fail (error, next, offset)
Packit Service 97d2fb
Packit Service 97d2fb
/* This is a prototype of what a new libelf interface might be.
Packit Service 97d2fb
   This implementation is pessimal for non-mmap cases and should
Packit Service 97d2fb
   be replaced by more diddling inside libelf internals.  */
Packit Service 97d2fb
static Elf *
Packit Service 97d2fb
elf_begin_rand (Elf *parent, off_t offset, off_t size, off_t *next)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (parent == NULL)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  off_t min = (parent->kind == ELF_K_ELF ?
Packit Service 97d2fb
		(parent->class == ELFCLASS32
Packit Service 97d2fb
		 ? sizeof (Elf32_Ehdr) : sizeof (Elf64_Ehdr))
Packit Service 97d2fb
		: parent->kind == ELF_K_AR ? SARMAG
Packit Service 97d2fb
		: 0);
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (offset < min)
Packit Service 97d2fb
      || unlikely (offset >= (off_t) parent->maximum_size))
Packit Service 97d2fb
    return fail (ELF_E_RANGE);
Packit Service 97d2fb
Packit Service 97d2fb
  /* For an archive, fetch just the size field
Packit Service 97d2fb
     from the archive header to override SIZE.  */
Packit Service 97d2fb
  if (parent->kind == ELF_K_AR)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      struct ar_hdr h = { .ar_size = "" };
Packit Service 97d2fb
Packit Service 97d2fb
      if (unlikely (parent->maximum_size - offset < sizeof h))
Packit Service 97d2fb
	return fail (ELF_E_RANGE);
Packit Service 97d2fb
Packit Service 97d2fb
      if (parent->map_address != NULL)
Packit Service 97d2fb
	memcpy (h.ar_size, parent->map_address + parent->start_offset + offset,
Packit Service 97d2fb
		sizeof h.ar_size);
Packit Service 97d2fb
      else if (unlikely (pread_retry (parent->fildes,
Packit Service 97d2fb
				      h.ar_size, sizeof (h.ar_size),
Packit Service 97d2fb
				      parent->start_offset + offset
Packit Service 97d2fb
				      + offsetof (struct ar_hdr, ar_size))
Packit Service 97d2fb
			 != sizeof (h.ar_size)))
Packit Service 97d2fb
	return fail (ELF_E_READ_ERROR);
Packit Service 97d2fb
Packit Service 97d2fb
      offset += sizeof h;
Packit Service 97d2fb
Packit Service 97d2fb
      char *endp;
Packit Service 97d2fb
      size = strtoll (h.ar_size, &endp, 10);
Packit Service 97d2fb
      if (unlikely (endp == h.ar_size)
Packit Service 97d2fb
	  || unlikely ((off_t) parent->maximum_size - offset < size))
Packit Service 97d2fb
	return fail (ELF_E_INVALID_ARCHIVE);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely ((off_t) parent->maximum_size - offset < size))
Packit Service 97d2fb
    return fail (ELF_E_RANGE);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Even if we fail at this point, update *NEXT to point past the file.  */
Packit Service 97d2fb
  if (next != NULL)
Packit Service 97d2fb
    *next = offset + size;
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (offset == 0)
Packit Service 97d2fb
      && unlikely (size == (off_t) parent->maximum_size))
Packit Service 97d2fb
    return elf_clone (parent, parent->cmd);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Note the image is guaranteed live only as long as PARENT
Packit Service 97d2fb
     lives.  Using elf_memory is quite suboptimal if the whole
Packit Service 97d2fb
     file is not mmap'd.  We really should have something like
Packit Service 97d2fb
     a generalization of the archive support.  */
Packit Service 97d2fb
  Elf_Data *data = elf_getdata_rawchunk (parent, offset, size, ELF_T_BYTE);
Packit Service 97d2fb
  if (data == NULL)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
  assert ((off_t) data->d_size == size);
Packit Service 97d2fb
  return elf_memory (data->d_buf, size);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_report_core_segments (Dwfl *dwfl, Elf *elf, size_t phnum, GElf_Phdr *notes)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (unlikely (dwfl == NULL))
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  int result = 0;
Packit Service 97d2fb
Packit Service 97d2fb
  if (notes != NULL)
Packit Service 97d2fb
    notes->p_type = PT_NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  for (size_t ndx = 0; result >= 0 && ndx < phnum; ++ndx)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      GElf_Phdr phdr_mem;
Packit Service 97d2fb
      GElf_Phdr *phdr = gelf_getphdr (elf, ndx, &phdr_mem);
Packit Service 97d2fb
      if (unlikely (phdr == NULL))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_LIBELF);
Packit Service 97d2fb
	  return -1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      switch (phdr->p_type)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	case PT_LOAD:
Packit Service 97d2fb
	  result = dwfl_report_segment (dwfl, ndx, phdr, 0, NULL);
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
Packit Service 97d2fb
	case PT_NOTE:
Packit Service 97d2fb
	  if (notes != NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      *notes = *phdr;
Packit Service 97d2fb
	      notes = NULL;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return result;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Never read more than this much without mmap.  */
Packit Service 97d2fb
#define MAX_EAGER_COST	8192
Packit Service 97d2fb
Packit Service 97d2fb
/* Dwfl_Module_Callback passed to and called by dwfl_segment_report_module
Packit Service 97d2fb
   to read in a segment as ELF image directly if possible or indicate an
Packit Service 97d2fb
   attempt must be made to read in the while segment right now.  */
Packit Service 97d2fb
static bool
Packit Service 97d2fb
core_file_read_eagerly (Dwfl_Module *mod,
Packit Service 97d2fb
			void **userdata __attribute__ ((unused)),
Packit Service 97d2fb
			const char *name __attribute__ ((unused)),
Packit Service 97d2fb
			Dwarf_Addr start __attribute__ ((unused)),
Packit Service 97d2fb
			void **buffer, size_t *buffer_available,
Packit Service 97d2fb
			GElf_Off cost, GElf_Off worthwhile,
Packit Service 97d2fb
			GElf_Off whole,
Packit Service 97d2fb
			GElf_Off contiguous __attribute__ ((unused)),
Packit Service 97d2fb
			void *arg, Elf **elfp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Elf *core = arg;
Packit Service 97d2fb
Packit Service 97d2fb
  /* The available buffer is often the whole segment when the core file
Packit Service 97d2fb
     was mmap'd if used together with the dwfl_elf_phdr_memory_callback.
Packit Service 97d2fb
     Which means that if it is complete we can just construct the whole
Packit Service 97d2fb
     ELF image right now without having to read in anything more.  */
Packit Service 97d2fb
  if (whole <= *buffer_available)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* All there ever was, we already have on hand.  */
Packit Service 97d2fb
Packit Service 97d2fb
      if (core->map_address == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* We already malloc'd the buffer.  */
Packit Service 97d2fb
	  *elfp = elf_memory (*buffer, whole);
Packit Service 97d2fb
	  if (unlikely (*elfp == NULL))
Packit Service 97d2fb
	    return false;
Packit Service 97d2fb
Packit Service 97d2fb
	  (*elfp)->flags |= ELF_F_MALLOCED;
Packit Service 97d2fb
	  *buffer = NULL;
Packit Service 97d2fb
	  *buffer_available = 0;
Packit Service 97d2fb
	  return true;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* We can use the image inside the core file directly.  */
Packit Service 97d2fb
      *elfp = elf_begin_rand (core, *buffer - core->map_address, whole, NULL);
Packit Service 97d2fb
      *buffer = NULL;
Packit Service 97d2fb
      *buffer_available = 0;
Packit Service 97d2fb
      return *elfp != NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* We don't have the whole file.  Which either means the core file
Packit Service 97d2fb
     wasn't mmap'd, but needs to still be read in, or that the segment
Packit Service 97d2fb
     is truncated.  Figure out if this is better than nothing.  */
Packit Service 97d2fb
Packit Service 97d2fb
  if (worthwhile == 0)
Packit Service 97d2fb
    /* Caller doesn't think so.  */
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
Packit Service 97d2fb
  /*
Packit Service 97d2fb
    XXX would like to fall back to partial file via memory
Packit Service 97d2fb
    when build id find_elf fails
Packit Service 97d2fb
    also, link_map name may give file name from disk better than partial here
Packit Service 97d2fb
    requires find_elf hook re-doing the magic to fall back if no file found
Packit Service 97d2fb
  */
Packit Service 97d2fb
Packit Service 97d2fb
  if (whole > MAX_EAGER_COST && mod->build_id_len > 0)
Packit Service 97d2fb
    /* We can't cheaply read the whole file here, so we'd
Packit Service 97d2fb
       be using a partial file.  But there is a build ID that could
Packit Service 97d2fb
       help us find the whole file, which might be more useful than
Packit Service 97d2fb
       what we have.  We'll just rely on that.  */
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
Packit Service 97d2fb
  /* The file is either small (most likely the vdso) or big and incomplete,
Packit Service 97d2fb
     but we don't have a build-id.  */
Packit Service 97d2fb
Packit Service 97d2fb
  if (core->map_address != NULL)
Packit Service 97d2fb
    /* It's cheap to get, so get it.  */
Packit Service 97d2fb
    return true;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Only use it if there isn't too much to be read.  */
Packit Service 97d2fb
  return cost <= MAX_EAGER_COST;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static inline void
Packit Service 97d2fb
update_end (GElf_Phdr *pphdr, const GElf_Off align,
Packit Service 97d2fb
            GElf_Off *pend, GElf_Addr *pend_vaddr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  *pend = (pphdr->p_offset + pphdr->p_filesz + align - 1) & -align;
Packit Service 97d2fb
  *pend_vaddr = (pphdr->p_vaddr + pphdr->p_memsz + align - 1) & -align;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Use following contiguous segments to get towards SIZE.  */
Packit Service 97d2fb
static inline bool
Packit Service 97d2fb
do_more (size_t size, GElf_Phdr *pphdr, const GElf_Off align,
Packit Service 97d2fb
         Elf *elf, GElf_Off start, int *pndx,
Packit Service 97d2fb
         GElf_Off *pend, GElf_Addr *pend_vaddr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  while (*pend <= start || *pend - start < size)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (pphdr->p_filesz < pphdr->p_memsz)
Packit Service 97d2fb
	/* This segment is truncated, so no following one helps us.  */
Packit Service 97d2fb
	return false;
Packit Service 97d2fb
Packit Service 97d2fb
      if (unlikely (gelf_getphdr (elf, (*pndx)++, pphdr) == NULL))
Packit Service 97d2fb
	return false;
Packit Service 97d2fb
Packit Service 97d2fb
      if (pphdr->p_type == PT_LOAD)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (pphdr->p_offset > *pend
Packit Service 97d2fb
	      || pphdr->p_vaddr > *pend_vaddr)
Packit Service 97d2fb
	    /* It's discontiguous!  */
Packit Service 97d2fb
	    return false;
Packit Service 97d2fb
Packit Service 97d2fb
	  update_end (pphdr, align, pend, pend_vaddr);
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
#define more(size) do_more (size, &phdr, align, elf, start, &ndx, &end, &end_vaddr)
Packit Service 97d2fb
Packit Service 97d2fb
bool
Packit Service 97d2fb
dwfl_elf_phdr_memory_callback (Dwfl *dwfl, int ndx,
Packit Service 97d2fb
			       void **buffer, size_t *buffer_available,
Packit Service 97d2fb
			       GElf_Addr vaddr,
Packit Service 97d2fb
			       size_t minread,
Packit Service 97d2fb
			       void *arg)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Elf *elf = arg;
Packit Service 97d2fb
Packit Service 97d2fb
  if (ndx == -1)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Called for cleanup.  */
Packit Service 97d2fb
      if (elf->map_address == NULL)
Packit Service 97d2fb
	free (*buffer);
Packit Service 97d2fb
      *buffer = NULL;
Packit Service 97d2fb
      *buffer_available = 0;
Packit Service 97d2fb
      return false;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  const GElf_Off align = dwfl->segment_align ?: 1;
Packit Service 97d2fb
  GElf_Phdr phdr;
Packit Service 97d2fb
Packit Service 97d2fb
  do
Packit Service 97d2fb
    if (unlikely (gelf_getphdr (elf, ndx++, &phdr) == NULL))
Packit Service 97d2fb
      return false;
Packit Service 97d2fb
  while (phdr.p_type != PT_LOAD
Packit Service 97d2fb
	 || ((phdr.p_vaddr + phdr.p_memsz + align - 1) & -align) <= vaddr);
Packit Service 97d2fb
Packit Service 97d2fb
  GElf_Off start = vaddr - phdr.p_vaddr + phdr.p_offset;
Packit Service 97d2fb
  GElf_Off end;
Packit Service 97d2fb
  GElf_Addr end_vaddr;
Packit Service 97d2fb
Packit Service 97d2fb
  update_end (&phdr, align, &end, &end_vaddr);
Packit Service 97d2fb
Packit Service 97d2fb
  /* We need at least this much.  */
Packit Service 97d2fb
  if (! more (minread))
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
Packit Service 97d2fb
  /* See how much more we can get of what the caller wants.  */
Packit Service 97d2fb
  (void) more (*buffer_available);
Packit Service 97d2fb
Packit Service 97d2fb
  /* If it's already on hand anyway, use as much as there is.  */
Packit Service 97d2fb
  if (elf->map_address != NULL)
Packit Service 97d2fb
    (void) more (elf->maximum_size - start);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Make sure we don't look past the end of the actual file,
Packit Service 97d2fb
     even if the headers tell us to.  */
Packit Service 97d2fb
  if (unlikely (end > elf->maximum_size))
Packit Service 97d2fb
    end = elf->maximum_size;
Packit Service 97d2fb
Packit Service 97d2fb
  /* If the file is too small, there is nothing at all to get.  */
Packit Service 97d2fb
  if (unlikely (start >= end))
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
Packit Service 97d2fb
  if (elf->map_address != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      void *contents = elf->map_address + elf->start_offset + start;
Packit Service 97d2fb
      size_t size = end - start;
Packit Service 97d2fb
Packit Service 97d2fb
      if (minread == 0)		/* String mode.  */
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  const void *eos = memchr (contents, '\0', size);
Packit Service 97d2fb
	  if (unlikely (eos == NULL) || unlikely (eos == contents))
Packit Service 97d2fb
	    return false;
Packit Service 97d2fb
	  size = eos + 1 - contents;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (*buffer == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  *buffer = contents;
Packit Service 97d2fb
	  *buffer_available = size;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  *buffer_available = MIN (size, *buffer_available);
Packit Service 97d2fb
	  memcpy (*buffer, contents, *buffer_available);
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      void *into = *buffer;
Packit Service 97d2fb
      if (*buffer == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  *buffer_available = MIN (minread ?: 512,
Packit Service 97d2fb
				   MAX (4096, MIN (end - start,
Packit Service 97d2fb
						   *buffer_available)));
Packit Service 97d2fb
	  into = malloc (*buffer_available);
Packit Service 97d2fb
	  if (unlikely (into == NULL))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdwfl_seterrno (DWFL_E_NOMEM);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      ssize_t nread = pread_retry (elf->fildes, into, *buffer_available, start);
Packit Service 97d2fb
      if (nread < (ssize_t) minread)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (into != *buffer)
Packit Service 97d2fb
	    free (into);
Packit Service 97d2fb
	  if (nread < 0)
Packit Service 97d2fb
	    __libdwfl_seterrno (DWFL_E_ERRNO);
Packit Service 97d2fb
	  return false;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (minread == 0)		/* String mode.  */
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  const void *eos = memchr (into, '\0', nread);
Packit Service 97d2fb
	  if (unlikely (eos == NULL) || unlikely (eos == into))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      if (*buffer == NULL)
Packit Service 97d2fb
		free (into);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  nread = eos + 1 - into;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (*buffer == NULL)
Packit Service 97d2fb
	*buffer = into;
Packit Service 97d2fb
      *buffer_available = nread;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Free the contents of R_DEBUG_INFO without the R_DEBUG_INFO memory itself.  */
Packit Service 97d2fb
Packit Service 97d2fb
static void
Packit Service 97d2fb
clear_r_debug_info (struct r_debug_info *r_debug_info)
Packit Service 97d2fb
{
Packit Service 97d2fb
  while (r_debug_info->module != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      struct r_debug_info_module *module = r_debug_info->module;
Packit Service 97d2fb
      r_debug_info->module = module->next;
Packit Service 97d2fb
      elf_end (module->elf);
Packit Service 97d2fb
      if (module->fd != -1)
Packit Service 97d2fb
	close (module->fd);
Packit Service 97d2fb
      free (module);
Packit Service 97d2fb
    }
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
bool
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_dynamic_vaddr_get (Elf *elf, GElf_Addr *vaddrp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  size_t phnum;
Packit Service 97d2fb
  if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
  for (size_t i = 0; i < phnum; ++i)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      GElf_Phdr phdr_mem;
Packit Service 97d2fb
      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
Packit Service 97d2fb
      if (unlikely (phdr == NULL))
Packit Service 97d2fb
	return false;
Packit Service 97d2fb
      if (phdr->p_type == PT_DYNAMIC)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  *vaddrp = phdr->p_vaddr;
Packit Service 97d2fb
	  return true;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
  return false;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
Packit Service 97d2fb
{
Packit Service 97d2fb
  size_t phnum;
Packit Service 97d2fb
  if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_LIBELF);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  bool cleanup_user_core = false;
Packit Service 97d2fb
  if (dwfl->user_core != NULL)
Packit Service 97d2fb
    free (dwfl->user_core->executable_for_core);
Packit Service 97d2fb
  if (executable == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (dwfl->user_core != NULL)
Packit Service 97d2fb
	dwfl->user_core->executable_for_core = NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (dwfl->user_core == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  cleanup_user_core = true;
Packit Service 97d2fb
	  dwfl->user_core = calloc (1, sizeof (struct Dwfl_User_Core));
Packit Service 97d2fb
	  if (dwfl->user_core == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdwfl_seterrno (DWFL_E_NOMEM);
Packit Service 97d2fb
	      return -1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  dwfl->user_core->fd = -1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      dwfl->user_core->executable_for_core = strdup (executable);
Packit Service 97d2fb
      if (dwfl->user_core->executable_for_core == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (cleanup_user_core)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (dwfl->user_core);
Packit Service 97d2fb
	      dwfl->user_core = NULL;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_NOMEM);
Packit Service 97d2fb
	  return -1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* First report each PT_LOAD segment.  */
Packit Service 97d2fb
  GElf_Phdr notes_phdr;
Packit Service 97d2fb
  int ndx = dwfl_report_core_segments (dwfl, elf, phnum, &notes_phdr);
Packit Service 97d2fb
  if (unlikely (ndx <= 0))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (cleanup_user_core)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  free (dwfl->user_core->executable_for_core);
Packit Service 97d2fb
	  free (dwfl->user_core);
Packit Service 97d2fb
	  dwfl->user_core = NULL;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      return ndx;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Next, we should follow the chain from DT_DEBUG.  */
Packit Service 97d2fb
Packit Service 97d2fb
  const void *auxv = NULL;
Packit Service 97d2fb
  const void *note_file = NULL;
Packit Service 97d2fb
  size_t auxv_size = 0;
Packit Service 97d2fb
  size_t note_file_size = 0;
Packit Service 97d2fb
  if (likely (notes_phdr.p_type == PT_NOTE))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* PT_NOTE -> NT_AUXV -> AT_PHDR -> PT_DYNAMIC -> DT_DEBUG */
Packit Service 97d2fb
Packit Service 97d2fb
      Elf_Data *notes = elf_getdata_rawchunk (elf,
Packit Service 97d2fb
					      notes_phdr.p_offset,
Packit Service 97d2fb
					      notes_phdr.p_filesz,
Packit Service 97d2fb
					      (notes_phdr.p_align == 8
Packit Service 97d2fb
					       ? ELF_T_NHDR8
Packit Service 97d2fb
					       : ELF_T_NHDR));
Packit Service 97d2fb
      if (likely (notes != NULL))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  size_t pos = 0;
Packit Service 97d2fb
	  GElf_Nhdr nhdr;
Packit Service 97d2fb
	  size_t name_pos;
Packit Service 97d2fb
	  size_t desc_pos;
Packit Service 97d2fb
	  while ((pos = gelf_getnote (notes, pos, &nhdr,
Packit Service 97d2fb
				      &name_pos, &desc_pos)) > 0)
Packit Service 97d2fb
	    if (nhdr.n_namesz == sizeof "CORE"
Packit Service 97d2fb
		&& !memcmp (notes->d_buf + name_pos, "CORE", sizeof "CORE"))
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		if (nhdr.n_type == NT_AUXV)
Packit Service 97d2fb
		  {
Packit Service 97d2fb
		    auxv = notes->d_buf + desc_pos;
Packit Service 97d2fb
		    auxv_size = nhdr.n_descsz;
Packit Service 97d2fb
		  }
Packit Service 97d2fb
		if (nhdr.n_type == NT_FILE)
Packit Service 97d2fb
		  {
Packit Service 97d2fb
		    note_file = notes->d_buf + desc_pos;
Packit Service 97d2fb
		    note_file_size = nhdr.n_descsz;
Packit Service 97d2fb
		  }
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Now we have NT_AUXV contents.  From here on this processing could be
Packit Service 97d2fb
     used for a live process with auxv read from /proc.  */
Packit Service 97d2fb
Packit Service 97d2fb
  struct r_debug_info r_debug_info;
Packit Service 97d2fb
  memset (&r_debug_info, 0, sizeof r_debug_info);
Packit Service 97d2fb
  int retval = dwfl_link_map_report (dwfl, auxv, auxv_size,
Packit Service 97d2fb
				     dwfl_elf_phdr_memory_callback, elf,
Packit Service 97d2fb
				     &r_debug_info);
Packit Service 97d2fb
  int listed = retval > 0 ? retval : 0;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Now sniff segment contents for modules hinted by information gathered
Packit Service 97d2fb
     from DT_DEBUG.  */
Packit Service 97d2fb
Packit Service 97d2fb
  ndx = 0;
Packit Service 97d2fb
  do
Packit Service 97d2fb
    {
Packit Service 97d2fb
      int seg = dwfl_segment_report_module (dwfl, ndx, NULL,
Packit Service 97d2fb
					    &dwfl_elf_phdr_memory_callback, elf,
Packit Service 97d2fb
					    core_file_read_eagerly, elf,
Packit Service 97d2fb
					    note_file, note_file_size,
Packit Service 97d2fb
					    &r_debug_info);
Packit Service 97d2fb
      if (unlikely (seg < 0))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  clear_r_debug_info (&r_debug_info);
Packit Service 97d2fb
	  return seg;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      if (seg > ndx)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  ndx = seg;
Packit Service 97d2fb
	  ++listed;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	++ndx;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  while (ndx < (int) phnum);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Now report the modules from dwfl_link_map_report which were not filtered
Packit Service 97d2fb
     out by dwfl_segment_report_module.  */
Packit Service 97d2fb
Packit Service 97d2fb
  Dwfl_Module **lastmodp = &dwfl->modulelist;
Packit Service 97d2fb
  while (*lastmodp != NULL)
Packit Service 97d2fb
    lastmodp = &(*lastmodp)->next;
Packit Service 97d2fb
  for (struct r_debug_info_module *module = r_debug_info.module;
Packit Service 97d2fb
       module != NULL; module = module->next)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (module->elf == NULL)
Packit Service 97d2fb
	continue;
Packit Service 97d2fb
      GElf_Addr file_dynamic_vaddr;
Packit Service 97d2fb
      if (! __libdwfl_dynamic_vaddr_get (module->elf, &file_dynamic_vaddr))
Packit Service 97d2fb
	continue;
Packit Service 97d2fb
      Dwfl_Module *mod;
Packit Service 97d2fb
      mod = __libdwfl_report_elf (dwfl, basename (module->name), module->name,
Packit Service 97d2fb
				  module->fd, module->elf,
Packit Service 97d2fb
				  module->l_ld - file_dynamic_vaddr,
Packit Service 97d2fb
				  true, true);
Packit Service 97d2fb
      if (mod == NULL)
Packit Service 97d2fb
	continue;
Packit Service 97d2fb
      ++listed;
Packit Service 97d2fb
      module->elf = NULL;
Packit Service 97d2fb
      module->fd = -1;
Packit Service 97d2fb
      /* Move this module to the end of the list, so that we end
Packit Service 97d2fb
	 up with a list in the same order as the link_map chain.  */
Packit Service 97d2fb
      if (mod->next != NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (*lastmodp != mod)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      lastmodp = &dwfl->modulelist;
Packit Service 97d2fb
	      while (*lastmodp != mod)
Packit Service 97d2fb
		lastmodp = &(*lastmodp)->next;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  *lastmodp = mod->next;
Packit Service 97d2fb
	  mod->next = NULL;
Packit Service 97d2fb
	  while (*lastmodp != NULL)
Packit Service 97d2fb
	    lastmodp = &(*lastmodp)->next;
Packit Service 97d2fb
	  *lastmodp = mod;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      lastmodp = &mod->next;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  clear_r_debug_info (&r_debug_info);
Packit Service 97d2fb
Packit Service 97d2fb
  /* We return the number of modules we found if we found any.
Packit Service 97d2fb
     If we found none, we return -1 instead of 0 if there was an
Packit Service 97d2fb
     error rather than just nothing found.  */
Packit Service 97d2fb
  return listed > 0 ? listed : retval;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_core_file_report)
Packit Service 97d2fb
NEW_VERSION (dwfl_core_file_report, ELFUTILS_0.158)
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef SYMBOL_VERSIONING
Packit Service 97d2fb
int _compat_without_executable_dwfl_core_file_report (Dwfl *dwfl, Elf *elf);
Packit Service 97d2fb
COMPAT_VERSION_NEWPROTO (dwfl_core_file_report, ELFUTILS_0.146,
Packit Service 97d2fb
			 without_executable)
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
_compat_without_executable_dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
Packit Service 97d2fb
{
Packit Service 97d2fb
  return dwfl_core_file_report (dwfl, elf, NULL);
Packit Service 97d2fb
}
Packit Service 97d2fb
#endif