diff --git a/include/tdep-arm/ex_tables.h b/include/tdep-arm/ex_tables.h
index 3805c60..7369b35 100644
--- a/include/tdep-arm/ex_tables.h
+++ b/include/tdep-arm/ex_tables.h
@@ -64,16 +64,12 @@ enum arm_exbuf_cmd_flags {
 #define ARM_EXBUF_COUNT(x)	((x) & 0x0f)
 #define ARM_EXBUF_END(x)	(ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
 
-struct arm_exbuf_callback_data {
-  uint8_t ops[11];
-  uint8_t n_ops;
+struct arm_exbuf_data
+{
   arm_exbuf_cmd_t cmd;
   uint32_t data;
-  void *cb_data;
 };
 
-typedef int (*arm_exbuf_callback_t)(struct arm_exbuf_callback_data *aecb);
-
 static inline void *
 prel31_to_addr (void *addr)
 {
@@ -99,30 +95,7 @@ int arm_exidx_entry_extract (struct elf_image *ei,
 int arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf);
 
 int arm_exidx_decode (const uint8_t *buf, uint8_t len,
-		arm_exbuf_callback_t cb, void *cb_data);
-
-struct arm_stackframe {
-  void *fp;
-  void *sp;
-  void *lr;
-  void *pc;
-};
-
-struct arm_exidx_vrs {
-  uint32_t vrs[16];
-};
-
-enum arm_exidx_vrs_regs {
-  FP_thumb = 7,
-  FP_arm = 11,
-  SP = 13,
-  LR = 14,
-  PC = 15,
-};
-
-void arm_exidx_frame_to_vrs(struct arm_stackframe *f, struct arm_exidx_vrs *s);
-int arm_exidx_vrs_to_frame(struct arm_exidx_vrs *s, struct arm_stackframe *f);
-
-int arm_exidx_vrs_callback (struct arm_exbuf_callback_data *aecd);
+		      struct dwarf_cursor *c);
+int arm_exidx_apply_cmd (struct arm_exbuf_data *edata, struct dwarf_cursor *c);
 
 #endif // ARM_EX_TABLES_H
diff --git a/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h
index 839415c..acaf6d7 100644
--- a/include/tdep-arm/libunwind_i.h
+++ b/include/tdep-arm/libunwind_i.h
@@ -55,7 +55,6 @@ struct unw_addr_space
 struct cursor
   {
     struct dwarf_cursor dwarf;		/* must be first */
-    struct arm_stackframe frame;
     unw_word_t sigcontext_addr;
   };
 
diff --git a/src/arm/Ginit_local.c b/src/arm/Ginit_local.c
index 2a0d73a..cdf05d2 100644
--- a/src/arm/Ginit_local.c
+++ b/src/arm/Ginit_local.c
@@ -39,7 +39,6 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
 PROTECTED int
 unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
 {
-  register void *current_sp asm ("sp");
   struct cursor *c = (struct cursor *) cursor;
 
   if (tdep_needs_initialization)
@@ -51,18 +50,7 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
   c->dwarf.as_arg = uc;
 
   if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
-    {
-      int arm_exidx_init_done = 0;
-      if (!arm_exidx_init_done)
-	{
-	  arm_exidx_init_done = 1;
-	  arm_exidx_init_local ("libunwind");
-	}
-      c->frame.fp = __builtin_frame_address (0);
-      c->frame.sp = current_sp;
-      c->frame.lr = __builtin_return_address (0);
-      c->frame.pc = &unw_init_local;
-    }
+    arm_exidx_init_local ("libunwind");
 
   return common_init (c, 1);
 }
diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c
index ab556d7..1b4b135 100644
--- a/src/arm/Gstep.c
+++ b/src/arm/Gstep.c
@@ -30,27 +30,42 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 static inline int
 arm_exidx_step (struct cursor *c)
 {
-  struct arm_exidx_table *table = arm_exidx_table_find (c->frame.pc);
+  unw_word_t old_ip, old_cfa;
+  struct arm_exidx_table *table;
+  struct arm_exidx_entry *entry;
+  uint8_t buf[32];
+  int ret;
+
+  old_ip = c->dwarf.ip;
+  old_cfa = c->dwarf.cfa;
+
+  /* mark PC unsaved */
+  c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC;
+
+  table = arm_exidx_table_find ((void *)c->dwarf.ip);
   if (NULL == table)
     return -UNW_ENOINFO;
 
-  struct arm_exidx_entry *entry = arm_exidx_table_lookup (table, c->frame.pc);
+  entry = arm_exidx_table_lookup (table, (void *)c->dwarf.ip);
   if (NULL == entry)
     return -UNW_ENOINFO;
 
-  struct arm_exidx_vrs s;
-  arm_exidx_frame_to_vrs (&c->frame, &s);
-
-  uint8_t buf[32];
-  int ret = arm_exidx_extract (entry, buf);
+  ret = arm_exidx_extract (entry, buf);
   if (ret < 0)
     return ret;
 
-  ret = arm_exidx_decode (buf, ret, &arm_exidx_vrs_callback, &s);
+  ret = arm_exidx_decode (buf, ret, &c->dwarf);
   if (ret < 0)
-    return -ret;
+    return ret;
 
-  return arm_exidx_vrs_to_frame (&s, &c->frame);
+  if (c->dwarf.ip == old_ip && c->dwarf.cfa == old_cfa)
+    {
+      Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
+	       __FUNCTION__, (long) c->dwarf.ip);
+      return -UNW_EBADFRAME;
+    }
+
+  return (c->dwarf.ip == 0) ? 0 : 1;
 }
 
 PROTECTED int
@@ -65,9 +80,9 @@ unw_step (unw_cursor_t *cursor)
     {
       ret = arm_exidx_step (c);
       if (ret >= 0)
-        return ret;
-      if (ret < 0 && ret != -UNW_ENOINFO)
-	return ret;
+	return 1;
+      if (ret == -UNW_ESTOPUNWIND)
+	return 0;
     }
 
   /* Next, try DWARF-based unwinding. */
diff --git a/src/arm/ex_tables.c b/src/arm/ex_tables.c
index 3b5e8ef..6fe2947 100644
--- a/src/arm/ex_tables.c
+++ b/src/arm/ex_tables.c
@@ -120,164 +120,146 @@ arm_exidx_table_lookup (struct arm_exidx_table *table, void *pc)
   return first;
 }
 
-static inline int
-arm_exidx_frame_reg(void *pc)
-{
-  return ((unsigned)pc & 1) ? FP_thumb : FP_arm;
-}
-
-HIDDEN void
-arm_exidx_frame_to_vrs(struct arm_stackframe *f, struct arm_exidx_vrs *s)
-{
-  int fp_reg = arm_exidx_frame_reg(f->pc);
-  s->vrs[fp_reg] = (uint32_t)f->fp;
-  s->vrs[SP] = (uint32_t)f->sp;
-  s->vrs[LR] = (uint32_t)f->lr;
-  s->vrs[PC] = 0;
-}
-
+/**
+ * Applies the given command onto the new state to the given dwarf_cursor.
+ */
 HIDDEN int
-arm_exidx_vrs_to_frame(struct arm_exidx_vrs *s, struct arm_stackframe *f)
+arm_exidx_apply_cmd (struct arm_exbuf_data *edata, struct dwarf_cursor *c)
 {
-  if (s->vrs[PC] == 0)
-    s->vrs[PC] = s->vrs[LR];
-
-  if (f->pc == (void *)s->vrs[PC])
-    return -1;
-
-  int fp_reg = arm_exidx_frame_reg(f->pc);
-  f->fp = (void *)s->vrs[fp_reg];
-  f->sp = (void *)s->vrs[SP];
-  f->lr = (void *)s->vrs[LR];
-  f->pc = (void *)s->vrs[PC];
-
-  return 0;
-}
+  int ret = 0;
+  unsigned i;
 
-HIDDEN int
-arm_exidx_vrs_callback (struct arm_exbuf_callback_data *aecd)
-{
-  struct arm_exidx_vrs *s = aecd->cb_data;
-  int ret = 0, i;
-  switch (aecd->cmd)
+  switch (edata->cmd)
     {
-      case ARM_EXIDX_CMD_FINISH:
-	break;
-      case ARM_EXIDX_CMD_DATA_PUSH:
-	Debug (2, "vsp = vsp - %d\n", aecd->data);
-	s->vrs[SP] -= aecd->data;
-	break;
-      case ARM_EXIDX_CMD_DATA_POP:
-	Debug (2, "vsp = vsp + %d\n", aecd->data);
-	s->vrs[SP] += aecd->data;
-	break;
-      case ARM_EXIDX_CMD_REG_POP:
-	for (i = 0; i < 16; i++)
-	  if (aecd->data & (1 << i))
-	    {
-	      s->vrs[i] = *(uint32_t*)s->vrs[SP];
-	      s->vrs[SP] += 4;
-	      Debug (2, "pop {r%d}\n", i);
-	    }
-	break;
-      case ARM_EXIDX_CMD_REG_TO_SP:
-	assert (aecd->data < 16);
-	Debug (2, "vsp = r%d\n", aecd->data);
-	s->vrs[SP] = s->vrs[aecd->data];
-	break;
-      case ARM_EXIDX_CMD_VFP_POP:
-	/* Skip VFP registers, but be sure to adjust stack */
-	for (i = ARM_EXBUF_START (aecd->data); i < ARM_EXBUF_END (aecd->data); i++)
-	  s->vrs[SP] += 8;
-	if (!(aecd->data & ARM_EXIDX_VFP_DOUBLE))
-	  s->vrs[SP] += 4;
-	break;
-      case ARM_EXIDX_CMD_WREG_POP:
-	for (i = ARM_EXBUF_START (aecd->data); i < ARM_EXBUF_END (aecd->data); i++)
-	  s->vrs[SP] += 8;
-	break;
-      case ARM_EXIDX_CMD_WCGR_POP:
-	for (i = 0; i < 4; i++)
-	  if (aecd->data & (1 << i))
-	    s->vrs[SP] += 4;
-	break;
-      case ARM_EXIDX_CMD_REFUSED:
-      case ARM_EXIDX_CMD_RESERVED:
-	ret = -1;
-	break;
+    case ARM_EXIDX_CMD_FINISH:
+      /* Set LR to PC if not set already.  */
+      if (DWARF_IS_NULL_LOC (c->loc[UNW_ARM_R15]))
+	c->loc[UNW_ARM_R15] = c->loc[UNW_ARM_R14];
+      /* Set IP.  */
+      dwarf_get (c, c->loc[UNW_ARM_R15], &c->ip);
+      break;
+    case ARM_EXIDX_CMD_DATA_PUSH:
+      Debug (2, "vsp = vsp - %d\n", edata->data);
+      c->cfa -= edata->data;
+      break;
+    case ARM_EXIDX_CMD_DATA_POP:
+      Debug (2, "vsp = vsp + %d\n", edata->data);
+      c->cfa += edata->data;
+      break;
+    case ARM_EXIDX_CMD_REG_POP:
+      for (i = 0; i < 16; i++)
+	if (edata->data & (1 << i))
+	  {
+	    Debug (2, "pop {r%d}\n", i);
+	    c->loc[UNW_ARM_R0 + i] = DWARF_LOC (c->cfa, 0);
+	    c->cfa += 4;
+	  }
+      /* Set cfa in case the SP got popped. */
+      if (edata->data & (1 << 13))
+	dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa);
+      break;
+    case ARM_EXIDX_CMD_REG_TO_SP:
+      assert (edata->data < 16);
+      Debug (2, "vsp = r%d\n", edata->data);
+      c->loc[UNW_ARM_R13] = c->loc[UNW_ARM_R0 + edata->data];
+      dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa);
+      break;
+    case ARM_EXIDX_CMD_VFP_POP:
+      /* Skip VFP registers, but be sure to adjust stack */
+      for (i = ARM_EXBUF_START (edata->data); i < ARM_EXBUF_END (edata->data);
+	   i++)
+	c->cfa += 8;
+      if (!(edata->data & ARM_EXIDX_VFP_DOUBLE))
+	c->cfa += 4;
+      break;
+    case ARM_EXIDX_CMD_WREG_POP:
+      for (i = ARM_EXBUF_START (edata->data); i < ARM_EXBUF_END (edata->data);
+	   i++)
+	c->cfa += 8;
+      break;
+    case ARM_EXIDX_CMD_WCGR_POP:
+      for (i = 0; i < 4; i++)
+	if (edata->data & (1 << i))
+	  c->cfa += 4;
+      break;
+    case ARM_EXIDX_CMD_REFUSED:
+    case ARM_EXIDX_CMD_RESERVED:
+      ret = -1;
+      break;
     }
   return ret;
 }
 
+
 HIDDEN int
-arm_exidx_decode (const uint8_t *buf, uint8_t len,
-		arm_exbuf_callback_t cb, void *cb_data)
+arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c)
 {
-#define READ_OP() aecb.ops[aecb.n_ops++] = *buf++
+#define READ_OP() *buf++
   const uint8_t *end = buf + len;
   int ret;
+  struct arm_exbuf_data edata;
 
   assert(buf != NULL);
-  assert(cb != NULL);
+  assert(len > 0);
+
   while (buf < end)
     {
-      struct arm_exbuf_callback_data aecb = { .cb_data = cb_data };
       uint8_t op = READ_OP ();
       if ((op & 0xc0) == 0x00)
 	{
-	  aecb.cmd = ARM_EXIDX_CMD_DATA_POP;
-	  aecb.data = (((int)op & 0x3f) << 2) + 4;
+	  edata.cmd = ARM_EXIDX_CMD_DATA_POP;
+	  edata.data = (((int)op & 0x3f) << 2) + 4;
 	}
       else if ((op & 0xc0) == 0x40)
 	{
-	  aecb.cmd = ARM_EXIDX_CMD_DATA_PUSH;
-	  aecb.data = (((int)op & 0x3f) << 2) + 4;
+	  edata.cmd = ARM_EXIDX_CMD_DATA_PUSH;
+	  edata.data = (((int)op & 0x3f) << 2) + 4;
 	}
       else if ((op & 0xf0) == 0x80)
 	{
 	  uint8_t op2 = READ_OP ();
 	  if (op == 0x80 && op2 == 0x00)
-	    aecb.cmd = ARM_EXIDX_CMD_REFUSED;
+	    edata.cmd = ARM_EXIDX_CMD_REFUSED;
 	  else
 	    {
-	      aecb.cmd = ARM_EXIDX_CMD_REG_POP;
-	      aecb.data = ((op & 0xf) << 8) | op2;
-	      aecb.data = aecb.data << 4;
+	      edata.cmd = ARM_EXIDX_CMD_REG_POP;
+	      edata.data = ((op & 0xf) << 8) | op2;
+	      edata.data = edata.data << 4;
 	    }
 	}
       else if ((op & 0xf0) == 0x90)
 	{
 	  if (op == 0x9d || op == 0x9f)
-	    aecb.cmd = ARM_EXIDX_CMD_RESERVED;
+	    edata.cmd = ARM_EXIDX_CMD_RESERVED;
 	  else
 	    {
-	      aecb.cmd = ARM_EXIDX_CMD_REG_TO_SP;
-	      aecb.data = op & 0x0f;
+	      edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
+	      edata.data = op & 0x0f;
 	    }
 	}
       else if ((op & 0xf0) == 0xa0)
 	{
 	  unsigned end = (op & 0x07);
-	  aecb.data = (1 << (end + 1)) - 1;
-	  aecb.data = aecb.data << 4;
+	  edata.data = (1 << (end + 1)) - 1;
+	  edata.data = edata.data << 4;
 	  if (op & 0x08)
-	    aecb.data |= 1 << 14;
-	  aecb.cmd = ARM_EXIDX_CMD_REG_POP;
+	    edata.data |= 1 << 14;
+	  edata.cmd = ARM_EXIDX_CMD_REG_POP;
 	}
       else if (op == ARM_EXTBL_OP_FINISH)
 	{
-	  aecb.cmd = ARM_EXIDX_CMD_FINISH;
+	  edata.cmd = ARM_EXIDX_CMD_FINISH;
 	  buf = end;
 	}
       else if (op == 0xb1)
 	{
 	  uint8_t op2 = READ_OP ();
 	  if (op2 == 0 || (op2 & 0xf0))
-	    aecb.cmd = ARM_EXIDX_CMD_RESERVED;
+	    edata.cmd = ARM_EXIDX_CMD_RESERVED;
 	  else
 	    {
-	      aecb.cmd = ARM_EXIDX_CMD_REG_POP;
-	      aecb.data = op2 & 0x0f;
+	      edata.cmd = ARM_EXIDX_CMD_REG_POP;
+	      edata.data = op2 & 0x0f;
 	    }
 	}
       else if (op == 0xb2)
@@ -291,50 +273,50 @@ arm_exidx_decode (const uint8_t *buf, uint8_t len,
 	      shift += 7;
 	    }
 	  while (byte & 0x80);
-	  aecb.data = offset * 4 + 0x204;
-	  aecb.cmd = ARM_EXIDX_CMD_DATA_POP;
+	  edata.data = offset * 4 + 0x204;
+	  edata.cmd = ARM_EXIDX_CMD_DATA_POP;
 	}
       else if (op == 0xb3 || op == 0xc8 || op == 0xc9)
 	{
-	  aecb.cmd = ARM_EXIDX_CMD_VFP_POP;
-	  aecb.data = READ_OP ();
+	  edata.cmd = ARM_EXIDX_CMD_VFP_POP;
+	  edata.data = READ_OP ();
 	  if (op == 0xc8)
-	    aecb.data |= ARM_EXIDX_VFP_SHIFT_16;
+	    edata.data |= ARM_EXIDX_VFP_SHIFT_16;
 	  if (op != 0xb3)
-	    aecb.data |= ARM_EXIDX_VFP_DOUBLE;
+	    edata.data |= ARM_EXIDX_VFP_DOUBLE;
 	}
       else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0)
 	{
-	  aecb.cmd = ARM_EXIDX_CMD_VFP_POP;
-	  aecb.data = 0x80 | (op & 0x07);
+	  edata.cmd = ARM_EXIDX_CMD_VFP_POP;
+	  edata.data = 0x80 | (op & 0x07);
 	  if ((op & 0xf8) == 0xd0)
-	    aecb.data |= ARM_EXIDX_VFP_DOUBLE;
+	    edata.data |= ARM_EXIDX_VFP_DOUBLE;
 	}
       else if (op >= 0xc0 && op <= 0xc5)
 	{
-	  aecb.cmd = ARM_EXIDX_CMD_WREG_POP;
-	  aecb.data = 0xa0 | (op & 0x07);
+	  edata.cmd = ARM_EXIDX_CMD_WREG_POP;
+	  edata.data = 0xa0 | (op & 0x07);
 	}
       else if (op == 0xc6)
 	{
-	  aecb.cmd = ARM_EXIDX_CMD_WREG_POP;
-	  aecb.data = READ_OP ();
+	  edata.cmd = ARM_EXIDX_CMD_WREG_POP;
+	  edata.data = READ_OP ();
 	}
       else if (op == 0xc7)
 	{
 	  uint8_t op2 = READ_OP ();
 	  if (op2 == 0 || (op2 & 0xf0))
-	    aecb.cmd = ARM_EXIDX_CMD_RESERVED;
+	    edata.cmd = ARM_EXIDX_CMD_RESERVED;
 	  else
 	    {
-	      aecb.cmd = ARM_EXIDX_CMD_WCGR_POP;
-	      aecb.data = op2 & 0x0f;
+	      edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
+	      edata.data = op2 & 0x0f;
 	    }
 	}
       else
-	aecb.cmd = ARM_EXIDX_CMD_RESERVED;
+	edata.cmd = ARM_EXIDX_CMD_RESERVED;
 
-      ret = (*cb) (&aecb);
+      ret = arm_exidx_apply_cmd (&edata, c);
       if (ret < 0)
 	return ret;
     }
