summaryrefslogtreecommitdiffstats
path: root/libdwfl
diff options
context:
space:
mode:
Diffstat (limited to 'libdwfl')
-rw-r--r--libdwfl/dwfl_frame.c3
-rw-r--r--libdwfl/frame_unwind.c46
-rw-r--r--libdwfl/libdwflP.h7
3 files changed, 38 insertions, 18 deletions
diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c
index 28008e90..89fd9205 100644
--- a/libdwfl/dwfl_frame.c
+++ b/libdwfl/dwfl_frame.c
@@ -1,5 +1,5 @@
/* Get Dwarf Frame state for target PID or core file.
- Copyright (C) 2013 Red Hat, Inc.
+ Copyright (C) 2013-2014 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -98,6 +98,7 @@ state_alloc (Dwfl_Thread *thread)
state->thread = thread;
state->signal_frame = false;
state->initial_frame = true;
+ state->cfa = 0;
state->pc_state = DWFL_FRAME_STATE_ERROR;
memset (state->regs_set, 0, sizeof (state->regs_set));
thread->unwound = state;
diff --git a/libdwfl/frame_unwind.c b/libdwfl/frame_unwind.c
index 3ce45479..d434c468 100644
--- a/libdwfl/frame_unwind.c
+++ b/libdwfl/frame_unwind.c
@@ -1,5 +1,5 @@
/* Get previous frame state for an existing frame state.
- Copyright (C) 2013 Red Hat, Inc.
+ Copyright (C) 2013-2014 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -105,8 +105,8 @@ bra_compar (const void *key_voidp, const void *elem_voidp)
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)
+expr_eval (Dwfl_Frame *state, const Dwarf_Op *ops, size_t nops,
+ Dwarf_Addr *result, Dwarf_Addr bias, const int elfclass)
{
Dwfl_Process *process = state->thread->process;
if (nops == 0)
@@ -310,7 +310,6 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
}
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)
{
@@ -450,16 +449,15 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
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);
+ if (state->unwound->cfa == 0)
+ {
+ /* DW_OP_call_frame_cfa is needed to compute CFA itself. */
+ free (stack);
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ if (! push (state->unwound->cfa))
+ {
free (stack);
return false;
}
@@ -510,6 +508,7 @@ new_unwound (Dwfl_Frame *state)
unwound->unwound = NULL;
unwound->signal_frame = false;
unwound->initial_frame = false;
+ unwound->cfa = 0;
unwound->pc_state = DWFL_FRAME_STATE_ERROR;
memset (unwound->regs_set, 0, sizeof (unwound->regs_set));
}
@@ -536,12 +535,29 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
Ebl *ebl = process->ebl;
size_t nregs = ebl_frame_nregs (ebl);
assert (nregs > 0);
+ const int elfclass = frame->cache->e_ident[EI_CLASS];
/* 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);
+ // Set unwound->CFA.
+ Dwarf_Op *cfa_ops;
+ size_t cfa_nops;
+ if (dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops) != 0
+ || ! expr_eval (state, cfa_ops, cfa_nops, &unwound->cfa, bias, elfclass))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ return;
+ }
+ if (unwound->cfa == 0
+ || (! state->initial_frame && unwound->cfa <= state->cfa))
+ {
+ __libdwfl_seterrno (DWFL_E_UNWIND_BAD_CFA);
+ return;
+ }
+
for (unsigned regno = 0; regno < nregs; regno++)
{
Dwarf_Op reg_ops_mem[3], *reg_ops;
@@ -574,7 +590,7 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
continue;
}
}
- else if (! expr_eval (state, frame, reg_ops, reg_nops, &regval, bias))
+ else if (! expr_eval (state, reg_ops, reg_nops, &regval, bias, elfclass))
{
/* PPC32 vDSO has various invalid operations, ignore them. The
register will look as unset causing an error later, if used.
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 63615a8a..5f6bbcdd 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -1,5 +1,5 @@
/* Internal definitions for libdwfl.
- Copyright (C) 2005-2013 Red Hat, Inc.
+ Copyright (C) 2005-2014 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -89,7 +89,8 @@ typedef struct Dwfl_Process Dwfl_Process;
DWFL_ERROR (ATTACH_STATE_CONFLICT, N_("Dwfl already has attached state")) \
DWFL_ERROR (NO_ATTACH_STATE, N_("Dwfl has no attached state")) \
DWFL_ERROR (NO_UNWIND, N_("Unwinding not supported for this architecture")) \
- DWFL_ERROR (INVALID_ARGUMENT, N_("Invalid argument"))
+ DWFL_ERROR (INVALID_ARGUMENT, N_("Invalid argument")) \
+ DWFL_ERROR (UNWIND_BAD_CFA, N_("Unwind not monotonous (corrupt stack?)"))
#define DWFL_ERROR(name, text) DWFL_E_##name,
typedef enum { DWFL_ERRORS DWFL_E_NUM } Dwfl_Error;
@@ -241,6 +242,8 @@ struct Dwfl_Frame
Dwfl_Frame *unwound;
bool signal_frame : 1;
bool initial_frame : 1;
+ /* Used to catch infinite unwinding. */
+ Dwarf_Addr cfa;
enum
{
/* This structure is still being initialized or there was an error