@@ -260,6 +260,7 @@ extern int tdep_access_fpreg (struct cursor *c, unw_regnum_t reg,
#define UNW_ARM_METHOD_ALL 0xFF
#define UNW_ARM_METHOD_DWARF 0x01
#define UNW_ARM_METHOD_FRAME 0x02
+#define UNW_ARM_METHOD_EXIDX 0x04
#define unwi_unwind_method UNW_OBJ(unwind_method)
extern int unwi_unwind_method;
@@ -47,6 +47,10 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
c->dwarf.as = unw_local_addr_space;
c->dwarf.as_arg = uc;
+
+ if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
+ arm_exidx_init_local ();
+
return common_init (c, 1);
}
@@ -1,5 +1,6 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2008 CodeSourcery
+ Copyright 2011 Linaro Limited
This file is part of libunwind.
@@ -24,6 +25,48 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#include "unwind_i.h"
#include "offsets.h"
+#include "ex_tables.h"
+
+static inline int
+arm_exidx_step (struct cursor *c)
+{
+ 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;
+
+ entry = arm_exidx_table_lookup (table, (void *)c->dwarf.ip);
+ if (NULL == entry)
+ return -UNW_ENOINFO;
+
+ ret = arm_exidx_extract (entry, buf);
+ if (ret < 0)
+ return ret;
+
+ ret = arm_exidx_decode (buf, ret, &c->dwarf);
+ if (ret < 0)
+ return ret;
+
+ 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
unw_step (unw_cursor_t *cursor)
@@ -33,8 +76,7 @@ unw_step (unw_cursor_t *cursor)
Debug (1, "(cursor=%p)\n", c);
- /* Try DWARF-based unwinding... this is the only method likely to work for
- ARM. */
+ /* First, try DWARF-based unwinding. */
if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
{
ret = dwarf_step (&c->dwarf);
@@ -50,6 +92,18 @@ unw_step (unw_cursor_t *cursor)
}
}
+ /* Next, try extbl-based unwinding. */
+ if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
+ {
+ ret = arm_exidx_step (c);
+ if (ret >= 0)
+ return 1;
+ if (ret == -UNW_ESTOPUNWIND)
+ return 0;
+ }
+
+ /* Fall back on APCS frame parsing.
+ Note: This won't work in case the ARM EABI is used. */
if (unlikely (ret < 0))
{
if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME))