Blame libdwfl/frame_unwind.c

Packit Service 97d2fb
/* Get previous frame state for an existing frame state.
Packit Service 97d2fb
   Copyright (C) 2013, 2014, 2016 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 "cfi.h"
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
#include "libdwflP.h"
Packit Service 97d2fb
#include "../libdw/dwarf.h"
Packit Service 97d2fb
#include <system.h>
Packit Service 97d2fb
Packit Service 97d2fb
/* Maximum number of DWARF expression stack slots before returning an error.  */
Packit Service 97d2fb
#define DWARF_EXPR_STACK_MAX 0x100
Packit Service 97d2fb
Packit Service 97d2fb
/* Maximum number of DWARF expression executed operations before returning an
Packit Service 97d2fb
   error.  */
Packit Service 97d2fb
#define DWARF_EXPR_STEPS_MAX 0x1000
Packit Service 97d2fb
Packit Service 97d2fb
bool
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_frame_reg_get (Dwfl_Frame *state, unsigned regno, Dwarf_Addr *val)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Ebl *ebl = state->thread->process->ebl;
Packit Service 97d2fb
  if (! ebl_dwarf_to_regno (ebl, &regno))
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
  if (regno >= ebl_frame_nregs (ebl))
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
  if ((state->regs_set[regno / sizeof (*state->regs_set) / 8]
Packit Service 97d2fb
       & ((uint64_t) 1U << (regno % (sizeof (*state->regs_set) * 8)))) == 0)
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
  if (val)
Packit Service 97d2fb
    *val = state->regs[regno];
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
bool
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_frame_reg_set (Dwfl_Frame *state, unsigned regno, Dwarf_Addr val)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Ebl *ebl = state->thread->process->ebl;
Packit Service 97d2fb
  if (! ebl_dwarf_to_regno (ebl, &regno))
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
  if (regno >= ebl_frame_nregs (ebl))
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
  /* For example i386 user_regs_struct has signed fields.  */
Packit Service 97d2fb
  if (ebl_get_elfclass (ebl) == ELFCLASS32)
Packit Service 97d2fb
    val &= 0xffffffff;
Packit Service 97d2fb
  state->regs_set[regno / sizeof (*state->regs_set) / 8] |=
Packit Service 97d2fb
		((uint64_t) 1U << (regno % (sizeof (*state->regs_set) * 8)));
Packit Service 97d2fb
  state->regs[regno] = val;
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static bool
Packit Service 97d2fb
state_get_reg (Dwfl_Frame *state, unsigned regno, Dwarf_Addr *val)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (! __libdwfl_frame_reg_get (state, regno, val))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
Packit Service 97d2fb
      return false;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
bra_compar (const void *key_voidp, const void *elem_voidp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwarf_Word offset = (uintptr_t) key_voidp;
Packit Service 97d2fb
  const Dwarf_Op *op = elem_voidp;
Packit Service 97d2fb
  return (offset > op->offset) - (offset < op->offset);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
struct eval_stack {
Packit Service 97d2fb
  Dwarf_Addr *addrs;
Packit Service 97d2fb
  size_t used;
Packit Service 97d2fb
  size_t allocated;
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
static bool
Packit Service 97d2fb
do_push (struct eval_stack *stack, Dwarf_Addr val)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (stack->used >= DWARF_EXPR_STACK_MAX)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
Packit Service 97d2fb
      return false;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (stack->used == stack->allocated)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      stack->allocated = MAX (stack->allocated * 2, 32);
Packit Service 97d2fb
      Dwarf_Addr *new_addrs;
Packit Service 97d2fb
      new_addrs = realloc (stack->addrs,
Packit Service 97d2fb
			   stack->allocated * sizeof (*stack->addrs));
Packit Service 97d2fb
      if (new_addrs == NULL)
Packit Service 97d2fb
        {
Packit Service 97d2fb
          __libdwfl_seterrno (DWFL_E_NOMEM);
Packit Service 97d2fb
          return false;
Packit Service 97d2fb
        }
Packit Service 97d2fb
      stack->addrs = new_addrs;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  stack->addrs[stack->used++] = val;
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static bool
Packit Service 97d2fb
do_pop (struct eval_stack *stack, Dwarf_Addr *val)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (stack->used == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
Packit Service 97d2fb
      return false;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  *val = stack->addrs[--stack->used];
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* If FRAME is NULL is are computing CFI frame base.  In such case another
Packit Service 97d2fb
   DW_OP_call_frame_cfa is no longer permitted.  */
Packit Service 97d2fb
Packit Service 97d2fb
static bool
Packit Service 97d2fb
expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
Packit Service 97d2fb
	   size_t nops, Dwarf_Addr *result, Dwarf_Addr bias)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwfl_Process *process = state->thread->process;
Packit Service 97d2fb
  if (nops == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
Packit Service 97d2fb
      return false;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  struct eval_stack stack =
Packit Service 97d2fb
    {
Packit Service 97d2fb
      .addrs = NULL,
Packit Service 97d2fb
      .used = 0,
Packit Service 97d2fb
      .allocated = 0
Packit Service 97d2fb
    };
Packit Service 97d2fb
Packit Service 97d2fb
#define pop(x) do_pop(&stack, x)
Packit Service 97d2fb
#define push(x) do_push(&stack, x)
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_Addr val1, val2;
Packit Service 97d2fb
  bool is_location = false;
Packit Service 97d2fb
  size_t steps_count = 0;
Packit Service 97d2fb
  for (const Dwarf_Op *op = ops; op < ops + nops; op++)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (++steps_count > DWARF_EXPR_STEPS_MAX)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
Packit Service 97d2fb
	  return false;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      switch (op->atom)
Packit Service 97d2fb
      {
Packit Service 97d2fb
	/* DW_OP_* order matches libgcc/unwind-dw2.c execute_stack_op:  */
Packit Service 97d2fb
	case DW_OP_lit0 ... DW_OP_lit31:
Packit Service 97d2fb
	  if (! push (op->atom - DW_OP_lit0))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_addr:
Packit Service 97d2fb
	  if (! push (op->number + bias))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_GNU_encoded_addr:
Packit Service 97d2fb
	  /* Missing support in the rest of elfutils.  */
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_UNSUPPORTED_DWARF);
Packit Service 97d2fb
	  return false;
Packit Service 97d2fb
	case DW_OP_const1u:
Packit Service 97d2fb
	case DW_OP_const1s:
Packit Service 97d2fb
	case DW_OP_const2u:
Packit Service 97d2fb
	case DW_OP_const2s:
Packit Service 97d2fb
	case DW_OP_const4u:
Packit Service 97d2fb
	case DW_OP_const4s:
Packit Service 97d2fb
	case DW_OP_const8u:
Packit Service 97d2fb
	case DW_OP_const8s:
Packit Service 97d2fb
	case DW_OP_constu:
Packit Service 97d2fb
	case DW_OP_consts:
Packit Service 97d2fb
	  if (! push (op->number))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_reg0 ... DW_OP_reg31:
Packit Service 97d2fb
	  if (! state_get_reg (state, op->atom - DW_OP_reg0, &val1)
Packit Service 97d2fb
	      || ! push (val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_regx:
Packit Service 97d2fb
	  if (! state_get_reg (state, op->number, &val1) || ! push (val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_breg0 ... DW_OP_breg31:
Packit Service 97d2fb
	  if (! state_get_reg (state, op->atom - DW_OP_breg0, &val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  val1 += op->number;
Packit Service 97d2fb
	  if (! push (val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_bregx:
Packit Service 97d2fb
	  if (! state_get_reg (state, op->number, &val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  val1 += op->number2;
Packit Service 97d2fb
	  if (! push (val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_dup:
Packit Service 97d2fb
	  if (! pop (&val1) || ! push (val1) || ! push (val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_drop:
Packit Service 97d2fb
	  if (! pop (&val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_pick:
Packit Service 97d2fb
	  if (stack.used <= op->number)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  if (! push (stack.addrs[stack.used - 1 - op->number]))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_over:
Packit Service 97d2fb
	  if (! pop (&val1) || ! pop (&val2)
Packit Service 97d2fb
	      || ! push (val2) || ! push (val1) || ! push (val2))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_swap:
Packit Service 97d2fb
	  if (! pop (&val1) || ! pop (&val2) || ! push (val1) || ! push (val2))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_rot:
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    Dwarf_Addr val3;
Packit Service 97d2fb
	    if (! pop (&val1) || ! pop (&val2) || ! pop (&val3)
Packit Service 97d2fb
		|| ! push (val1) || ! push (val3) || ! push (val2))
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		free (stack.addrs);
Packit Service 97d2fb
		return false;
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_deref:
Packit Service 97d2fb
	case DW_OP_deref_size:
Packit Service 97d2fb
	  if (process->callbacks->memory_read == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      __libdwfl_seterrno (DWFL_E_INVALID_ARGUMENT);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  if (! pop (&val1)
Packit Service 97d2fb
	      || ! process->callbacks->memory_read (process->dwfl, val1, &val1,
Packit Service 97d2fb
						    process->callbacks_arg))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  if (op->atom == DW_OP_deref_size)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      const int elfclass = frame->cache->e_ident[EI_CLASS];
Packit Service 97d2fb
	      const unsigned addr_bytes = elfclass == ELFCLASS32 ? 4 : 8;
Packit Service 97d2fb
	      if (op->number > addr_bytes)
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  free (stack.addrs);
Packit Service 97d2fb
		  __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
Packit Service 97d2fb
		  return false;
Packit Service 97d2fb
		}
Packit Service 97d2fb
#if BYTE_ORDER == BIG_ENDIAN
Packit Service 97d2fb
	      if (op->number == 0)
Packit Service 97d2fb
		val1 = 0;
Packit Service 97d2fb
	      else
Packit Service 97d2fb
		val1 >>= (addr_bytes - op->number) * 8;
Packit Service 97d2fb
#else
Packit Service 97d2fb
	      if (op->number < 8)
Packit Service 97d2fb
		val1 &= (1ULL << (op->number * 8)) - 1;
Packit Service 97d2fb
#endif
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  if (! push (val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
#define UNOP(atom, expr)						\
Packit Service 97d2fb
	case atom:							\
Packit Service 97d2fb
	  if (! pop (&val1) || ! push (expr))				\
Packit Service 97d2fb
	    {								\
Packit Service 97d2fb
	      free (stack.addrs);					\
Packit Service 97d2fb
	      return false;						\
Packit Service 97d2fb
	    }								\
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	UNOP (DW_OP_abs, llabs ((int64_t) val1))
Packit Service 97d2fb
	UNOP (DW_OP_neg, -(int64_t) val1)
Packit Service 97d2fb
	UNOP (DW_OP_not, ~val1)
Packit Service 97d2fb
#undef UNOP
Packit Service 97d2fb
	case DW_OP_plus_uconst:
Packit Service 97d2fb
	  if (! pop (&val1) || ! push (val1 + op->number))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
#define BINOP(atom, op)							\
Packit Service 97d2fb
	case atom:							\
Packit Service 97d2fb
	  if (! pop (&val2) || ! pop (&val1) || ! push (val1 op val2))	\
Packit Service 97d2fb
	    {								\
Packit Service 97d2fb
	      free (stack.addrs);					\
Packit Service 97d2fb
	      return false;						\
Packit Service 97d2fb
	    }								\
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
#define BINOP_SIGNED(atom, op)						\
Packit Service 97d2fb
	case atom:							\
Packit Service 97d2fb
	  if (! pop (&val2) || ! pop (&val1)				\
Packit Service 97d2fb
	      || ! push ((int64_t) val1 op (int64_t) val2))		\
Packit Service 97d2fb
	    {								\
Packit Service 97d2fb
	      free (stack.addrs);					\
Packit Service 97d2fb
	      return false;						\
Packit Service 97d2fb
	    }								\
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	BINOP (DW_OP_and, &)
Packit Service 97d2fb
	case DW_OP_div:
Packit Service 97d2fb
	  if (! pop (&val2) || ! pop (&val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  if (val2 == 0)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  if (! push ((int64_t) val1 / (int64_t) val2))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	BINOP (DW_OP_minus, -)
Packit Service 97d2fb
	case DW_OP_mod:
Packit Service 97d2fb
	  if (! pop (&val2) || ! pop (&val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  if (val2 == 0)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  if (! push (val1 % val2))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	BINOP (DW_OP_mul, *)
Packit Service 97d2fb
	BINOP (DW_OP_or, |)
Packit Service 97d2fb
	BINOP (DW_OP_plus, +)
Packit Service 97d2fb
	BINOP (DW_OP_shl, <<)
Packit Service 97d2fb
	BINOP (DW_OP_shr, >>)
Packit Service 97d2fb
	BINOP_SIGNED (DW_OP_shra, >>)
Packit Service 97d2fb
	BINOP (DW_OP_xor, ^)
Packit Service 97d2fb
	BINOP_SIGNED (DW_OP_le, <=)
Packit Service 97d2fb
	BINOP_SIGNED (DW_OP_ge, >=)
Packit Service 97d2fb
	BINOP_SIGNED (DW_OP_eq, ==)
Packit Service 97d2fb
	BINOP_SIGNED (DW_OP_lt, <)
Packit Service 97d2fb
	BINOP_SIGNED (DW_OP_gt, >)
Packit Service 97d2fb
	BINOP_SIGNED (DW_OP_ne, !=)
Packit Service 97d2fb
#undef BINOP
Packit Service 97d2fb
#undef BINOP_SIGNED
Packit Service 97d2fb
	case DW_OP_bra:
Packit Service 97d2fb
	  if (! pop (&val1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  if (val1 == 0)
Packit Service 97d2fb
	    break;
Packit Service 97d2fb
	  FALLTHROUGH;
Packit Service 97d2fb
	case DW_OP_skip:;
Packit Service 97d2fb
	  Dwarf_Word offset = op->offset + 1 + 2 + (int16_t) op->number;
Packit Service 97d2fb
	  const Dwarf_Op *found = bsearch ((void *) (uintptr_t) offset, ops, nops,
Packit Service 97d2fb
					   sizeof (*ops), bra_compar);
Packit Service 97d2fb
	  if (found == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      /* PPC32 vDSO has such invalid operations.  */
Packit Service 97d2fb
	      __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  /* Undo the 'for' statement increment.  */
Packit Service 97d2fb
	  op = found - 1;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_nop:
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	/* DW_OP_* not listed in libgcc/unwind-dw2.c execute_stack_op:  */
Packit Service 97d2fb
	case DW_OP_call_frame_cfa:;
Packit Service 97d2fb
	  // Not used by CFI itself but it is synthetized by elfutils internation.
Packit Service 97d2fb
	  Dwarf_Op *cfa_ops;
Packit Service 97d2fb
	  size_t cfa_nops;
Packit Service 97d2fb
	  Dwarf_Addr cfa;
Packit Service 97d2fb
	  if (frame == NULL
Packit Service 97d2fb
	      || dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops) != 0
Packit Service 97d2fb
	      || ! expr_eval (state, NULL, cfa_ops, cfa_nops, &cfa, bias)
Packit Service 97d2fb
	      || ! push (cfa))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdwfl_seterrno (DWFL_E_LIBDW);
Packit Service 97d2fb
	      free (stack.addrs);
Packit Service 97d2fb
	      return false;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  is_location = true;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	case DW_OP_stack_value:
Packit Service 97d2fb
	  // Not used by CFI itself but it is synthetized by elfutils internation.
Packit Service 97d2fb
	  is_location = false;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	default:
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
Packit Service 97d2fb
	  return false;
Packit Service 97d2fb
      }
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (! pop (result))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (stack.addrs);
Packit Service 97d2fb
      return false;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  free (stack.addrs);
Packit Service 97d2fb
  if (is_location)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (process->callbacks->memory_read == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_INVALID_ARGUMENT);
Packit Service 97d2fb
	  return false;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      if (! process->callbacks->memory_read (process->dwfl, *result, result,
Packit Service 97d2fb
					     process->callbacks_arg))
Packit Service 97d2fb
	return false;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
#undef push
Packit Service 97d2fb
#undef pop
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static Dwfl_Frame *
Packit Service 97d2fb
new_unwound (Dwfl_Frame *state)
Packit Service 97d2fb
{
Packit Service 97d2fb
  assert (state->unwound == NULL);
Packit Service 97d2fb
  Dwfl_Thread *thread = state->thread;
Packit Service 97d2fb
  Dwfl_Process *process = thread->process;
Packit Service 97d2fb
  Ebl *ebl = process->ebl;
Packit Service 97d2fb
  size_t nregs = ebl_frame_nregs (ebl);
Packit Service 97d2fb
  assert (nregs > 0);
Packit Service 97d2fb
  Dwfl_Frame *unwound;
Packit Service 97d2fb
  unwound = malloc (sizeof (*unwound) + sizeof (*unwound->regs) * nregs);
Packit Service 97d2fb
  if (unlikely (unwound == NULL))
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
  state->unwound = unwound;
Packit Service 97d2fb
  unwound->thread = thread;
Packit Service 97d2fb
  unwound->unwound = NULL;
Packit Service 97d2fb
  unwound->signal_frame = false;
Packit Service 97d2fb
  unwound->initial_frame = false;
Packit Service 97d2fb
  unwound->pc_state = DWFL_FRAME_STATE_ERROR;
Packit Service 97d2fb
  memset (unwound->regs_set, 0, sizeof (unwound->regs_set));
Packit Service 97d2fb
  return unwound;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* The logic is to call __libdwfl_seterrno for any CFI bytecode interpretation
Packit Service 97d2fb
   error so one can easily catch the problem with a debugger.  Still there are
Packit Service 97d2fb
   archs with invalid CFI for some registers where the registers are never used
Packit Service 97d2fb
   later.  Therefore we continue unwinding leaving the registers undefined.  */
Packit Service 97d2fb
Packit Service 97d2fb
static void
Packit Service 97d2fb
handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwarf_Frame *frame;
Packit Service 97d2fb
  if (INTUSE(dwarf_cfi_addrframe) (cfi, pc, &frame) != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_LIBDW);
Packit Service 97d2fb
      return;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  Dwfl_Frame *unwound = new_unwound (state);
Packit Service 97d2fb
  if (unwound == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_NOMEM);
Packit Service 97d2fb
      return;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  unwound->signal_frame = frame->fde->cie->signal_frame;
Packit Service 97d2fb
  Dwfl_Thread *thread = state->thread;
Packit Service 97d2fb
  Dwfl_Process *process = thread->process;
Packit Service 97d2fb
  Ebl *ebl = process->ebl;
Packit Service 97d2fb
  size_t nregs = ebl_frame_nregs (ebl);
Packit Service 97d2fb
  assert (nregs > 0);
Packit Service 97d2fb
Packit Service 97d2fb
  /* The return register is special for setting the unwound->pc_state.  */
Packit Service 97d2fb
  unsigned ra = frame->fde->cie->return_address_register;
Packit Service 97d2fb
  bool ra_set = false;
Packit Service 97d2fb
  if (! ebl_dwarf_to_regno (ebl, &ra))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
Packit Service 97d2fb
      return;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  for (unsigned regno = 0; regno < nregs; regno++)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwarf_Op reg_ops_mem[3], *reg_ops;
Packit Service 97d2fb
      size_t reg_nops;
Packit Service 97d2fb
      if (dwarf_frame_register (frame, regno, reg_ops_mem, &reg_ops,
Packit Service 97d2fb
				&reg_nops) != 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_LIBDW);
Packit Service 97d2fb
	  continue;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      Dwarf_Addr regval;
Packit Service 97d2fb
      if (reg_nops == 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (reg_ops == reg_ops_mem)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      /* REGNO is undefined.  */
Packit Service 97d2fb
	      if (regno == ra)
Packit Service 97d2fb
		unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
Packit Service 97d2fb
	      continue;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  else if (reg_ops == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      /* REGNO is same-value.  */
Packit Service 97d2fb
	      if (! state_get_reg (state, regno, &regval))
Packit Service 97d2fb
		continue;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
Packit Service 97d2fb
	      continue;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else if (! expr_eval (state, frame, reg_ops, reg_nops, &regval, bias))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* PPC32 vDSO has various invalid operations, ignore them.  The
Packit Service 97d2fb
	     register will look as unset causing an error later, if used.
Packit Service 97d2fb
	     But PPC32 does not use such registers.  */
Packit Service 97d2fb
	  continue;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* Some architectures encode some extra info in the return address.  */
Packit Service 97d2fb
      if (regno == frame->fde->cie->return_address_register)
Packit Service 97d2fb
	regval &= ebl_func_addr_mask (ebl);
Packit Service 97d2fb
Packit Service 97d2fb
      /* This is another strange PPC[64] case.  There are two
Packit Service 97d2fb
	 registers numbers that can represent the same DWARF return
Packit Service 97d2fb
	 register number.  We only want one to actually set the return
Packit Service 97d2fb
	 register value.  But we always want to override the value if
Packit Service 97d2fb
	 the register is the actual CIE return address register.  */
Packit Service 97d2fb
      if (ra_set && regno != frame->fde->cie->return_address_register)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  unsigned r = regno;
Packit Service 97d2fb
	  if (ebl_dwarf_to_regno (ebl, &r) && r == ra)
Packit Service 97d2fb
	    continue;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (! __libdwfl_frame_reg_set (unwound, regno, regval))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
Packit Service 97d2fb
	  continue;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else if (! ra_set)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  unsigned r = regno;
Packit Service 97d2fb
          if (ebl_dwarf_to_regno (ebl, &r) && r == ra)
Packit Service 97d2fb
	    ra_set = true;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (unwound->pc_state == DWFL_FRAME_STATE_ERROR)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (__libdwfl_frame_reg_get (unwound,
Packit Service 97d2fb
				   frame->fde->cie->return_address_register,
Packit Service 97d2fb
				   &unwound->pc))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* PPC32 __libc_start_main properly CFI-unwinds PC as zero.
Packit Service 97d2fb
	     Currently none of the archs supported for unwinding have
Packit Service 97d2fb
	     zero as a valid PC.  */
Packit Service 97d2fb
	  if (unwound->pc == 0)
Packit Service 97d2fb
	    unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
Packit Service 97d2fb
	      /* In SPARC the return address register actually contains
Packit Service 97d2fb
		 the address of the call instruction instead of the return
Packit Service 97d2fb
		 address.  Therefore we add here an offset defined by the
Packit Service 97d2fb
		 backend.  Most likely 0.  */
Packit Service 97d2fb
	      unwound->pc += ebl_ra_offset (ebl);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* We couldn't set the return register, either it was bogus,
Packit Service 97d2fb
	     or the return pc is undefined, maybe end of call stack.  */
Packit Service 97d2fb
	  unsigned pcreg = frame->fde->cie->return_address_register;
Packit Service 97d2fb
	  if (! ebl_dwarf_to_regno (ebl, &pcreg)
Packit Service 97d2fb
	      || pcreg >= ebl_frame_nregs (ebl))
Packit Service 97d2fb
	    __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
  free (frame);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static bool
Packit Service 97d2fb
setfunc (int firstreg, unsigned nregs, const Dwarf_Word *regs, void *arg)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwfl_Frame *state = arg;
Packit Service 97d2fb
  Dwfl_Frame *unwound = state->unwound;
Packit Service 97d2fb
  if (firstreg < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      assert (firstreg == -1);
Packit Service 97d2fb
      assert (nregs == 1);
Packit Service 97d2fb
      assert (unwound->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED);
Packit Service 97d2fb
      unwound->pc = *regs;
Packit Service 97d2fb
      unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
Packit Service 97d2fb
      return true;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  while (nregs--)
Packit Service 97d2fb
    if (! __libdwfl_frame_reg_set (unwound, firstreg++, *regs++))
Packit Service 97d2fb
      return false;
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static bool
Packit Service 97d2fb
getfunc (int firstreg, unsigned nregs, Dwarf_Word *regs, void *arg)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwfl_Frame *state = arg;
Packit Service 97d2fb
  assert (firstreg >= 0);
Packit Service 97d2fb
  while (nregs--)
Packit Service 97d2fb
    if (! __libdwfl_frame_reg_get (state, firstreg++, regs++))
Packit Service 97d2fb
      return false;
Packit Service 97d2fb
  return true;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static bool
Packit Service 97d2fb
readfunc (Dwarf_Addr addr, Dwarf_Word *datap, void *arg)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwfl_Frame *state = arg;
Packit Service 97d2fb
  Dwfl_Thread *thread = state->thread;
Packit Service 97d2fb
  Dwfl_Process *process = thread->process;
Packit Service 97d2fb
  return process->callbacks->memory_read (process->dwfl, addr, datap,
Packit Service 97d2fb
					  process->callbacks_arg);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
void
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_frame_unwind (Dwfl_Frame *state)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (state->unwound)
Packit Service 97d2fb
    return;
Packit Service 97d2fb
  /* Do not ask dwfl_frame_pc for ISACTIVATION, it would try to unwind STATE
Packit Service 97d2fb
     which would deadlock us.  */
Packit Service 97d2fb
  Dwarf_Addr pc;
Packit Service 97d2fb
  bool ok = INTUSE(dwfl_frame_pc) (state, &pc, NULL);
Packit Service 97d2fb
  if (!ok)
Packit Service 97d2fb
    return;
Packit Service 97d2fb
  /* Check whether this is the initial frame or a signal frame.
Packit Service 97d2fb
     Then we need to unwind from the original, unadjusted PC.  */
Packit Service 97d2fb
  if (! state->initial_frame && ! state->signal_frame)
Packit Service 97d2fb
    pc--;
Packit Service 97d2fb
  Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (state->thread->process->dwfl, pc);
Packit Service 97d2fb
  if (mod == NULL)
Packit Service 97d2fb
    __libdwfl_seterrno (DWFL_E_NO_DWARF);
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwarf_Addr bias;
Packit Service 97d2fb
      Dwarf_CFI *cfi_eh = INTUSE(dwfl_module_eh_cfi) (mod, &bias);
Packit Service 97d2fb
      if (cfi_eh)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  handle_cfi (state, pc - bias, cfi_eh, bias);
Packit Service 97d2fb
	  if (state->unwound)
Packit Service 97d2fb
	    return;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      Dwarf_CFI *cfi_dwarf = INTUSE(dwfl_module_dwarf_cfi) (mod, &bias);
Packit Service 97d2fb
      if (cfi_dwarf)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  handle_cfi (state, pc - bias, cfi_dwarf, bias);
Packit Service 97d2fb
	  if (state->unwound)
Packit Service 97d2fb
	    return;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
  assert (state->unwound == NULL);
Packit Service 97d2fb
  Dwfl_Thread *thread = state->thread;
Packit Service 97d2fb
  Dwfl_Process *process = thread->process;
Packit Service 97d2fb
  Ebl *ebl = process->ebl;
Packit Service 97d2fb
  if (new_unwound (state) == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_NOMEM);
Packit Service 97d2fb
      return;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  state->unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
Packit Service 97d2fb
  // &Dwfl_Frame.signal_frame cannot be passed as it is a bitfield.
Packit Service 97d2fb
  bool signal_frame = false;
Packit Service 97d2fb
  if (! ebl_unwind (ebl, pc, setfunc, getfunc, readfunc, state, &signal_frame))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      // Discard the unwind attempt.  During next __libdwfl_frame_unwind call
Packit Service 97d2fb
      // we may have for example the appropriate Dwfl_Module already mapped.
Packit Service 97d2fb
      assert (state->unwound->unwound == NULL);
Packit Service 97d2fb
      free (state->unwound);
Packit Service 97d2fb
      state->unwound = NULL;
Packit Service 97d2fb
      // __libdwfl_seterrno has been called above.
Packit Service 97d2fb
      return;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  assert (state->unwound->pc_state == DWFL_FRAME_STATE_PC_SET);
Packit Service 97d2fb
  state->unwound->signal_frame = signal_frame;
Packit Service 97d2fb
}