Squashed 'third_party/elfutils/' content from commit 555e15e

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