libdw: Record ill-specified CFA rule and diagnose in dwarf_frame_cfa, not immediately at decode time.
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 50fba72..a6e8457 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,5 +1,10 @@
2010-04-26 Roland McGrath <roland@redhat.com>
+ * cfi.h (struct Dwarf_Frame_s): Add cfa_invalid alternative in cfa_rule.
+ * cfi.c (execute_cfi): Set that instead of doing cfi_assert for
+ DW_CFA_def_cfa_{offset*,register} when a non-offset rule is in force.
+ * dwarf_frame_cfa.c (dwarf_frame_cfa): Handle cfa_invalid.
+
* dwarf_getlocation.c (__libdw_intern_expression): Take new arg CFAP.
Prepend DW_OP_call_frame_cfa if true.
(getlocation): Update caller.
diff --git a/libdw/cfi.c b/libdw/cfi.c
index 5936659..3cb378b 100644
--- a/libdw/cfi.c
+++ b/libdw/cfi.c
@@ -117,6 +117,12 @@
return true;
}
+ inline void require_cfa_offset (void)
+ {
+ if (unlikely (fs->cfa_rule != cfa_offset))
+ fs->cfa_rule = cfa_invalid;
+ }
+
#define register_rule(regno, r_rule, r_value) do { \
if (unlikely (! enough_registers (regno))) \
goto out; \
@@ -177,7 +183,7 @@
case DW_CFA_def_cfa_register:
get_uleb128 (regno, program);
- cfi_assert (fs->cfa_rule == cfa_offset);
+ require_cfa_offset ();
fs->cfa_val_reg = regno;
continue;
@@ -190,7 +196,7 @@
case DW_CFA_def_cfa_offset:
get_uleb128 (offset, program);
def_cfa_offset:
- cfi_assert (fs->cfa_rule == cfa_offset);
+ require_cfa_offset ();
fs->cfa_val_offset = offset;
continue;
diff --git a/libdw/cfi.h b/libdw/cfi.h
index 64f3f15..e04e76d 100644
--- a/libdw/cfi.h
+++ b/libdw/cfi.h
@@ -1,5 +1,5 @@
/* Internal definitions for libdw CFI interpreter.
- Copyright (C) 2009 Red Hat, Inc.
+ Copyright (C) 2009-2010 Red Hat, Inc.
This file is part of Red Hat elfutils.
Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -191,8 +191,11 @@
which has the return_address_register and signal_frame flag. */
struct dwarf_fde *fde;
- /* The CFA is unknown, is R+N, or is computed by a DWARF expression. */
- enum { cfa_undefined, cfa_offset, cfa_expr } cfa_rule;
+ /* The CFA is unknown, is R+N, or is computed by a DWARF expression.
+ A bogon in the CFI can indicate an invalid/incalculable rule.
+ We store that as cfa_invalid rather than barfing when processing it,
+ so callers can ignore the bogon unless they really need that CFA. */
+ enum { cfa_undefined, cfa_offset, cfa_expr, cfa_invalid } cfa_rule;
union
{
Dwarf_Op offset;
diff --git a/libdw/dwarf_frame_cfa.c b/libdw/dwarf_frame_cfa.c
index 03c5fbd..0ba26b2 100644
--- a/libdw/dwarf_frame_cfa.c
+++ b/libdw/dwarf_frame_cfa.c
@@ -88,6 +88,11 @@
ops, nops, IDX_debug_frame);
break;
+ case cfa_invalid:
+ __libdw_seterrno (DWARF_E_INVALID_CFI);
+ result = -1;
+ break;
+
default:
abort ();
}