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 ();
     }