Blame libdwfl/linux-core-attach.c

Packit 032894
/* Get Dwarf Frame state for target core file.
Packit 032894
   Copyright (C) 2013, 2014 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 "system.h"
Packit 032894
Packit 032894
#include "../libdw/memory-access.h"
Packit 032894
Packit 032894
struct core_arg
Packit 032894
{
Packit 032894
  Elf *core;
Packit 032894
  Elf_Data *note_data;
Packit 032894
  size_t thread_note_offset;
Packit 032894
  Ebl *ebl;
Packit 032894
};
Packit 032894
Packit 032894
struct thread_arg
Packit 032894
{
Packit 032894
  struct core_arg *core_arg;
Packit 032894
  size_t note_offset;
Packit 032894
};
Packit 032894
Packit 032894
static bool
Packit 032894
core_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
Packit 032894
		  void *dwfl_arg)
Packit 032894
{
Packit 032894
  Dwfl_Process *process = dwfl->process;
Packit 032894
  struct core_arg *core_arg = dwfl_arg;
Packit 032894
  Elf *core = core_arg->core;
Packit 032894
  assert (core != NULL);
Packit 032894
  static size_t phnum;
Packit 032894
  if (elf_getphdrnum (core, &phnum) < 0)
Packit 032894
    {
Packit 032894
      __libdwfl_seterrno (DWFL_E_LIBELF);
Packit 032894
      return false;
Packit 032894
    }
Packit 032894
  for (size_t cnt = 0; cnt < phnum; ++cnt)
Packit 032894
    {
Packit 032894
      GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
Packit 032894
      if (phdr == NULL || phdr->p_type != PT_LOAD)
Packit 032894
	continue;
Packit 032894
      /* Bias is zero here, a core file itself has no bias.  */
Packit 032894
      GElf_Addr start = __libdwfl_segment_start (dwfl, phdr->p_vaddr);
Packit 032894
      GElf_Addr end = __libdwfl_segment_end (dwfl,
Packit 032894
					     phdr->p_vaddr + phdr->p_memsz);
Packit 032894
      unsigned bytes = ebl_get_elfclass (process->ebl) == ELFCLASS64 ? 8 : 4;
Packit 032894
      if (addr < start || addr + bytes > end)
Packit 032894
	continue;
Packit 032894
      Elf_Data *data;
Packit 032894
      data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start,
Packit 032894
				   bytes, ELF_T_ADDR);
Packit 032894
      if (data == NULL)
Packit 032894
	{
Packit 032894
	  __libdwfl_seterrno (DWFL_E_LIBELF);
Packit 032894
	  return false;
Packit 032894
	}
Packit 032894
      assert (data->d_size == bytes);
Packit 032894
      if (bytes == 8)
Packit 032894
	*result = read_8ubyte_unaligned_noncvt (data->d_buf);
Packit 032894
      else
Packit 032894
	*result = read_4ubyte_unaligned_noncvt (data->d_buf);
Packit 032894
      return true;
Packit 032894
    }
Packit 032894
  __libdwfl_seterrno (DWFL_E_ADDR_OUTOFRANGE);
Packit 032894
  return false;
Packit 032894
}
Packit 032894
Packit 032894
static pid_t
Packit 032894
core_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
Packit 032894
		  void **thread_argp)
Packit 032894
{
Packit 032894
  struct core_arg *core_arg = dwfl_arg;
Packit 032894
  Elf *core = core_arg->core;
Packit 032894
  GElf_Nhdr nhdr;
Packit 032894
  size_t name_offset;
Packit 032894
  size_t desc_offset;
Packit 032894
  Elf_Data *note_data = core_arg->note_data;
Packit 032894
  size_t offset;
Packit 032894
Packit 032894
  struct thread_arg *thread_arg;
Packit 032894
  if (*thread_argp == NULL)
Packit 032894
    {
Packit 032894
      core_arg->thread_note_offset = 0;
Packit 032894
      thread_arg = malloc (sizeof (*thread_arg));
Packit 032894
      if (thread_arg == NULL)
Packit 032894
	{
Packit 032894
	  __libdwfl_seterrno (DWFL_E_NOMEM);
Packit 032894
	  return -1;
Packit 032894
	}
Packit 032894
      thread_arg->core_arg = core_arg;
Packit 032894
      *thread_argp = thread_arg;
Packit 032894
    }
Packit 032894
  else
Packit 032894
    thread_arg = (struct thread_arg *) *thread_argp;
Packit 032894
Packit 032894
  while (offset = core_arg->thread_note_offset, offset < note_data->d_size
Packit 032894
	 && (core_arg->thread_note_offset = gelf_getnote (note_data, offset,
Packit 032894
							  &nhdr, &name_offset,
Packit 032894
							  &desc_offset)) > 0)
Packit 032894
    {
Packit 032894
      /* Do not check NAME for now, help broken Linux kernels.  */
Packit 032894
      const char *name = (nhdr.n_namesz == 0
Packit 032894
			  ? "" : note_data->d_buf + name_offset);
Packit 032894
      const char *desc = note_data->d_buf + desc_offset;
Packit 032894
      GElf_Word regs_offset;
Packit 032894
      size_t nregloc;
Packit 032894
      const Ebl_Register_Location *reglocs;
Packit 032894
      size_t nitems;
Packit 032894
      const Ebl_Core_Item *items;
Packit 032894
      if (! ebl_core_note (core_arg->ebl, &nhdr, name, desc,
Packit 032894
			   &regs_offset, &nregloc, &reglocs, &nitems, &items))
Packit 032894
	{
Packit 032894
	  /* This note may be just not recognized, skip it.  */
Packit 032894
	  continue;
Packit 032894
	}
Packit 032894
      if (nhdr.n_type != NT_PRSTATUS)
Packit 032894
	continue;
Packit 032894
      const Ebl_Core_Item *item;
Packit 032894
      for (item = items; item < items + nitems; item++)
Packit 032894
	if (strcmp (item->name, "pid") == 0)
Packit 032894
	  break;
Packit 032894
      if (item == items + nitems)
Packit 032894
	continue;
Packit 032894
      uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
Packit 032894
      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
Packit 032894
		? be32toh (val32) : le32toh (val32));
Packit 032894
      pid_t tid = (int32_t) val32;
Packit 032894
      eu_static_assert (sizeof val32 <= sizeof tid);
Packit 032894
      thread_arg->note_offset = offset;
Packit 032894
      return tid;
Packit 032894
    }
Packit 032894
Packit 032894
  free (thread_arg);
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
static bool
Packit 032894
core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
Packit 032894
{
Packit 032894
  struct thread_arg *thread_arg = thread_arg_voidp;
Packit 032894
  struct core_arg *core_arg = thread_arg->core_arg;
Packit 032894
  Elf *core = core_arg->core;
Packit 032894
  size_t offset = thread_arg->note_offset;
Packit 032894
  GElf_Nhdr nhdr;
Packit 032894
  size_t name_offset;
Packit 032894
  size_t desc_offset;
Packit 032894
  Elf_Data *note_data = core_arg->note_data;
Packit 032894
  size_t nregs = ebl_frame_nregs (core_arg->ebl);
Packit 032894
  assert (nregs > 0);
Packit 032894
  assert (offset < note_data->d_size);
Packit 032894
  size_t getnote_err = gelf_getnote (note_data, offset, &nhdr, &name_offset,
Packit 032894
				     &desc_offset);
Packit 032894
  /* __libdwfl_attach_state_for_core already verified the note is there.  */
Packit 032894
  assert (getnote_err != 0);
Packit 032894
  /* Do not check NAME for now, help broken Linux kernels.  */
Packit 032894
  const char *name = (nhdr.n_namesz == 0
Packit 032894
		      ? "" : note_data->d_buf + name_offset);
Packit 032894
  const char *desc = note_data->d_buf + desc_offset;
Packit 032894
  GElf_Word regs_offset;
Packit 032894
  size_t nregloc;
Packit 032894
  const Ebl_Register_Location *reglocs;
Packit 032894
  size_t nitems;
Packit 032894
  const Ebl_Core_Item *items;
Packit 032894
  int core_note_err = ebl_core_note (core_arg->ebl, &nhdr, name, desc,
Packit 032894
				     &regs_offset, &nregloc, &reglocs,
Packit 032894
				     &nitems, &items);
Packit 032894
  /* __libdwfl_attach_state_for_core already verified the note is there.  */
Packit 032894
  assert (core_note_err != 0);
Packit 032894
  assert (nhdr.n_type == NT_PRSTATUS);
Packit 032894
  const Ebl_Core_Item *item;
Packit 032894
  for (item = items; item < items + nitems; item++)
Packit 032894
    if (strcmp (item->name, "pid") == 0)
Packit 032894
      break;
Packit 032894
  assert (item < items + nitems);
Packit 032894
  pid_t tid;
Packit 032894
  {
Packit 032894
    uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
Packit 032894
    val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
Packit 032894
	     ? be32toh (val32) : le32toh (val32));
Packit 032894
    tid = (int32_t) val32;
Packit 032894
    eu_static_assert (sizeof val32 <= sizeof tid);
Packit 032894
  }
Packit 032894
  /* core_next_thread already found this TID there.  */
Packit 032894
  assert (tid == INTUSE(dwfl_thread_tid) (thread));
Packit 032894
  for (item = items; item < items + nitems; item++)
Packit 032894
    if (item->pc_register)
Packit 032894
      break;
Packit 032894
  if (item < items + nitems)
Packit 032894
    {
Packit 032894
      Dwarf_Word pc;
Packit 032894
      switch (gelf_getclass (core) == ELFCLASS32 ? 32 : 64)
Packit 032894
      {
Packit 032894
	case 32:;
Packit 032894
	  uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
Packit 032894
	  val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
Packit 032894
		   ? be32toh (val32) : le32toh (val32));
Packit 032894
	  /* Do a host width conversion.  */
Packit 032894
	  pc = val32;
Packit 032894
	  break;
Packit 032894
	case 64:;
Packit 032894
	  uint64_t val64 = read_8ubyte_unaligned_noncvt (desc + item->offset);
Packit 032894
	  val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
Packit 032894
		   ? be64toh (val64) : le64toh (val64));
Packit 032894
	  pc = val64;
Packit 032894
	  break;
Packit 032894
	default:
Packit 032894
	  abort ();
Packit 032894
      }
Packit 032894
      INTUSE(dwfl_thread_state_register_pc) (thread, pc);
Packit 032894
    }
Packit 032894
  desc += regs_offset;
Packit 032894
  for (size_t regloci = 0; regloci < nregloc; regloci++)
Packit 032894
    {
Packit 032894
      const Ebl_Register_Location *regloc = reglocs + regloci;
Packit 032894
      // Iterate even regs out of NREGS range so that we can find pc_register.
Packit 032894
      if (regloc->bits != 32 && regloc->bits != 64)
Packit 032894
	continue;
Packit 032894
      const char *reg_desc = desc + regloc->offset;
Packit 032894
      for (unsigned regno = regloc->regno;
Packit 032894
	   regno < regloc->regno + (regloc->count ?: 1U);
Packit 032894
	   regno++)
Packit 032894
	{
Packit 032894
	  /* PPC provides DWARF register 65 irrelevant for
Packit 032894
	     CFI which clashes with register 108 (LR) we need.
Packit 032894
	     LR (108) is provided earlier (in NT_PRSTATUS) than the # 65.
Packit 032894
	     FIXME: It depends now on their order in core notes.
Packit 032894
	     FIXME: It uses private function.  */
Packit 032894
	  if (regno < nregs
Packit 032894
	      && __libdwfl_frame_reg_get (thread->unwound, regno, NULL))
Packit 032894
	    continue;
Packit 032894
	  Dwarf_Word val;
Packit 032894
	  switch (regloc->bits)
Packit 032894
	  {
Packit 032894
	    case 32:;
Packit 032894
	      uint32_t val32 = read_4ubyte_unaligned_noncvt (reg_desc);
Packit 032894
	      reg_desc += sizeof val32;
Packit 032894
	      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
Packit 032894
		       ? be32toh (val32) : le32toh (val32));
Packit 032894
	      /* Do a host width conversion.  */
Packit 032894
	      val = val32;
Packit 032894
	      break;
Packit 032894
	    case 64:;
Packit 032894
	      uint64_t val64 = read_8ubyte_unaligned_noncvt (reg_desc);
Packit 032894
	      reg_desc += sizeof val64;
Packit 032894
	      val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
Packit 032894
		       ? be64toh (val64) : le64toh (val64));
Packit 032894
	      assert (sizeof (*thread->unwound->regs) == sizeof val64);
Packit 032894
	      val = val64;
Packit 032894
	      break;
Packit 032894
	    default:
Packit 032894
	      abort ();
Packit 032894
	  }
Packit 032894
	  /* Registers not valid for CFI are just ignored.  */
Packit 032894
	  if (regno < nregs)
Packit 032894
	    INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val;;
Packit 032894
	  if (regloc->pc_register)
Packit 032894
	    INTUSE(dwfl_thread_state_register_pc) (thread, val);
Packit 032894
	  reg_desc += regloc->pad;
Packit 032894
	}
Packit 032894
    }
Packit 032894
  return true;
Packit 032894
}
Packit 032894
Packit 032894
static void
Packit 032894
core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
Packit 032894
{
Packit 032894
  struct core_arg *core_arg = dwfl_arg;
Packit 032894
  ebl_closebackend (core_arg->ebl);
Packit 032894
  free (core_arg);
Packit 032894
}
Packit 032894
Packit 032894
static const Dwfl_Thread_Callbacks core_thread_callbacks =
Packit 032894
{
Packit 032894
  core_next_thread,
Packit 032894
  NULL, /* get_thread */
Packit 032894
  core_memory_read,
Packit 032894
  core_set_initial_registers,
Packit 032894
  core_detach,
Packit 032894
  NULL, /* core_thread_detach */
Packit 032894
};
Packit 032894
Packit 032894
int
Packit 032894
dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
Packit 032894
{
Packit 032894
  Dwfl_Error err = DWFL_E_NOERROR;
Packit 032894
  Ebl *ebl = ebl_openbackend (core);
Packit 032894
  if (ebl == NULL)
Packit 032894
    {
Packit 032894
      err = DWFL_E_LIBEBL;
Packit 032894
    fail_err:
Packit 032894
      if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
Packit 032894
	dwfl->attacherr = __libdwfl_canon_error (err);
Packit 032894
      __libdwfl_seterrno (err);
Packit 032894
      return -1;
Packit 032894
    }
Packit 032894
  size_t nregs = ebl_frame_nregs (ebl);
Packit 032894
  if (nregs == 0)
Packit 032894
    {
Packit 032894
      err = DWFL_E_NO_UNWIND;
Packit 032894
    fail:
Packit 032894
      ebl_closebackend (ebl);
Packit 032894
      goto fail_err;
Packit 032894
    }
Packit 032894
  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
Packit 032894
  if (ehdr == NULL)
Packit 032894
    {
Packit 032894
      err = DWFL_E_LIBELF;
Packit 032894
      goto fail;
Packit 032894
    }
Packit 032894
  if (ehdr->e_type != ET_CORE)
Packit 032894
    {
Packit 032894
      err = DWFL_E_NO_CORE_FILE;
Packit 032894
      goto fail;
Packit 032894
    }
Packit 032894
  size_t phnum;
Packit 032894
  if (elf_getphdrnum (core, &phnum) < 0)
Packit 032894
    {
Packit 032894
      err = DWFL_E_LIBELF;
Packit 032894
      goto fail;
Packit 032894
    }
Packit 032894
  pid_t pid = -1;
Packit 032894
  Elf_Data *note_data = NULL;
Packit 032894
  for (size_t cnt = 0; cnt < phnum; ++cnt)
Packit 032894
    {
Packit 032894
      GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
Packit 032894
      if (phdr != NULL && phdr->p_type == PT_NOTE)
Packit 032894
	{
Packit 032894
	  note_data = elf_getdata_rawchunk (core, phdr->p_offset,
Packit 032894
					    phdr->p_filesz, (phdr->p_align == 8
Packit 032894
							     ? ELF_T_NHDR8
Packit 032894
							     : ELF_T_NHDR));
Packit 032894
	  break;
Packit 032894
	}
Packit 032894
    }
Packit 032894
  if (note_data == NULL)
Packit 032894
    {
Packit 032894
      err = DWFL_E_LIBELF;
Packit 032894
      goto fail;
Packit 032894
    }
Packit 032894
  size_t offset = 0;
Packit 032894
  GElf_Nhdr nhdr;
Packit 032894
  size_t name_offset;
Packit 032894
  size_t desc_offset;
Packit 032894
  while (offset < note_data->d_size
Packit 032894
	 && (offset = gelf_getnote (note_data, offset,
Packit 032894
				    &nhdr, &name_offset, &desc_offset)) > 0)
Packit 032894
    {
Packit 032894
      /* Do not check NAME for now, help broken Linux kernels.  */
Packit 032894
      const char *name = (nhdr.n_namesz == 0
Packit 032894
			  ? "" : note_data->d_buf + name_offset);
Packit 032894
      const char *desc = note_data->d_buf + desc_offset;
Packit 032894
      GElf_Word regs_offset;
Packit 032894
      size_t nregloc;
Packit 032894
      const Ebl_Register_Location *reglocs;
Packit 032894
      size_t nitems;
Packit 032894
      const Ebl_Core_Item *items;
Packit 032894
      if (! ebl_core_note (ebl, &nhdr, name, desc,
Packit 032894
			   &regs_offset, &nregloc, &reglocs, &nitems, &items))
Packit 032894
	{
Packit 032894
	  /* This note may be just not recognized, skip it.  */
Packit 032894
	  continue;
Packit 032894
	}
Packit 032894
      if (nhdr.n_type != NT_PRPSINFO)
Packit 032894
	continue;
Packit 032894
      const Ebl_Core_Item *item;
Packit 032894
      for (item = items; item < items + nitems; item++)
Packit 032894
	if (strcmp (item->name, "pid") == 0)
Packit 032894
	  break;
Packit 032894
      if (item == items + nitems)
Packit 032894
	continue;
Packit 032894
      uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
Packit 032894
      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
Packit 032894
		? be32toh (val32) : le32toh (val32));
Packit 032894
      pid = (int32_t) val32;
Packit 032894
      eu_static_assert (sizeof val32 <= sizeof pid);
Packit 032894
      break;
Packit 032894
    }
Packit 032894
  if (pid == -1)
Packit 032894
    {
Packit 032894
      /* No valid NT_PRPSINFO recognized in this CORE.  */
Packit 032894
      err = DWFL_E_BADELF;
Packit 032894
      goto fail;
Packit 032894
    }
Packit 032894
  struct core_arg *core_arg = malloc (sizeof *core_arg);
Packit 032894
  if (core_arg == NULL)
Packit 032894
    {
Packit 032894
      err = DWFL_E_NOMEM;
Packit 032894
      goto fail;
Packit 032894
    }
Packit 032894
  core_arg->core = core;
Packit 032894
  core_arg->note_data = note_data;
Packit 032894
  core_arg->thread_note_offset = 0;
Packit 032894
  core_arg->ebl = ebl;
Packit 032894
  if (! INTUSE(dwfl_attach_state) (dwfl, core, pid, &core_thread_callbacks,
Packit 032894
				   core_arg))
Packit 032894
    {
Packit 032894
      free (core_arg);
Packit 032894
      ebl_closebackend (ebl);
Packit 032894
      return -1;
Packit 032894
    }
Packit 032894
  return pid;
Packit 032894
}
Packit 032894
INTDEF (dwfl_core_file_attach)