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, ®no))
+ 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, ®no))
+ 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, ®_ops,
+ ®_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, ®val))
+ continue;
+ }
+ else
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ continue;
+ }
+ }
+ else if (! expr_eval (state, frame, reg_ops, reg_nops, ®val, 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;
+}