From 2d6a50435bb743be1e4d88eee002372344348349 Mon Sep 17 00:00:00 2001
From: Yichao Yu <yyc1992@gmail.com>
Date: Sun, 29 Aug 2021 13:43:01 -0700
Subject: [PATCH] Prefer EXTBL unwinding on ARM
It is part of the C++ ABI so a EXTBL unwind info that's not `CANT_UNWIND`
should always be reliable/correct.
Ignore `ESTOPUNWIND` so that a `CANT_UNWIND` info can fallback to unwinding
using the debug info instead.
---
include/tdep-arm/libunwind_i.h | 4 +++
src/arm/Gex_tables.c | 18 ++++++++---
src/arm/Gstep.c | 55 ++++++++++++++++++++--------------
3 files changed, 51 insertions(+), 26 deletions(-)
diff --git a/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h
index 88ebfb069..5bd28c953 100644
--- a/include/tdep-arm/libunwind_i.h
+++ b/include/tdep-arm/libunwind_i.h
@@ -256,6 +256,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
#define tdep_init_done UNW_OBJ(init_done)
#define tdep_init UNW_OBJ(init)
#define arm_find_proc_info UNW_OBJ(find_proc_info)
+#define arm_find_proc_info2 UNW_OBJ(find_proc_info2)
#define arm_put_unwind_info UNW_OBJ(put_unwind_info)
/* Platforms that support UNW_INFO_FORMAT_TABLE need to define
tdep_search_unwind_table. */
@@ -297,6 +298,9 @@ extern void tdep_init (void);
extern int arm_find_proc_info (unw_addr_space_t as, unw_word_t ip,
unw_proc_info_t *pi, int need_unwind_info,
void *arg);
+extern int arm_find_proc_info2 (unw_addr_space_t as, unw_word_t ip,
+ unw_proc_info_t *pi, int need_unwind_info,
+ void *arg, int methods);
extern void arm_put_unwind_info (unw_addr_space_t as,
unw_proc_info_t *pi, void *arg);
extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
diff --git a/src/arm/Gex_tables.c b/src/arm/Gex_tables.c
index efdcf2978..083d2b2f7 100644
--- a/src/arm/Gex_tables.c
+++ b/src/arm/Gex_tables.c
@@ -506,18 +506,20 @@ arm_phdr_cb (struct dl_phdr_info *info, size_t size, void *data)
}
HIDDEN int
-arm_find_proc_info (unw_addr_space_t as, unw_word_t ip,
- unw_proc_info_t *pi, int need_unwind_info, void *arg)
+arm_find_proc_info2 (unw_addr_space_t as, unw_word_t ip,
+ unw_proc_info_t *pi, int need_unwind_info, void *arg,
+ int methods)
{
int ret = -1;
intrmask_t saved_mask;
Debug (14, "looking for IP=0x%lx\n", (long) ip);
- if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
+ if (UNW_TRY_METHOD (UNW_ARM_METHOD_DWARF) && (methods & UNW_ARM_METHOD_DWARF))
ret = dwarf_find_proc_info (as, ip, pi, need_unwind_info, arg);
- if (ret < 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
+ if (ret < 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX) &&
+ (methods & UNW_ARM_METHOD_EXIDX))
{
struct arm_cb_data cb_data;
@@ -540,6 +542,14 @@ arm_find_proc_info (unw_addr_space_t as, unw_word_t ip,
return ret;
}
+HIDDEN int
+arm_find_proc_info (unw_addr_space_t as, unw_word_t ip,
+ unw_proc_info_t *pi, int need_unwind_info, void *arg)
+{
+ return arm_find_proc_info2 (as, ip, pi, need_unwind_info, arg,
+ UNW_ARM_METHOD_ALL);
+}
+
HIDDEN void
arm_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
{
diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c
index 895e8a892..e4ada651b 100644
--- a/src/arm/Gstep.c
+++ b/src/arm/Gstep.c
@@ -54,17 +54,22 @@ arm_exidx_step (struct cursor *c)
c->dwarf.as_arg);
if (ret == -UNW_ENOINFO)
{
+#ifdef UNW_LOCAL_ONLY
+ if ((ret = arm_find_proc_info2 (c->dwarf.as, ip, &c->dwarf.pi,
+ 1, c->dwarf.as_arg,
+ UNW_ARM_METHOD_EXIDX)) < 0)
+ return ret;
+#else
if ((ret = tdep_find_proc_info (&c->dwarf, ip, 1)) < 0)
return ret;
+#endif
}
if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX)
return -UNW_ENOINFO;
ret = arm_exidx_extract (&c->dwarf, buf);
- if (ret == -UNW_ESTOPUNWIND)
- return 0;
- else if (ret < 0)
+ if (ret < 0)
return ret;
ret = arm_exidx_decode (buf, ret, &c->dwarf);
@@ -88,6 +93,7 @@ unw_step (unw_cursor_t *cursor)
{
struct cursor *c = (struct cursor *) cursor;
int ret = -UNW_EUNSPEC;
+ int has_stopunwind = 0;
Debug (1, "(cursor=%p)\n", c);
@@ -95,17 +101,31 @@ unw_step (unw_cursor_t *cursor)
if (unw_is_signal_frame (cursor) > 0)
return arm_handle_signal_frame (cursor);
+ /* First, try extbl-based unwinding. */
+ if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
+ {
+ ret = arm_exidx_step (c);
+ Debug(1, "arm_exidx_step()=%d\n", ret);
+ if (ret > 0)
+ return 1;
+ if (ret == 0)
+ return ret;
+ if (ret == -UNW_ESTOPUNWIND)
+ has_stopunwind = 1;
+ }
+
#ifdef CONFIG_DEBUG_FRAME
- /* First, try DWARF-based unwinding. */
+ /* Second, try DWARF-based unwinding. */
if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
{
+ Debug (13, "%s(ret=%d), trying extbl\n",
+ UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) ? "arm_exidx_step() failed " : "",
+ ret);
ret = dwarf_step (&c->dwarf);
Debug(1, "dwarf_step()=%d\n", ret);
if (likely (ret > 0))
return 1;
- else if (unlikely (ret == -UNW_ESTOPUNWIND))
- return ret;
if (ret < 0 && ret != -UNW_ENOINFO)
{
@@ -115,18 +135,9 @@ unw_step (unw_cursor_t *cursor)
}
#endif /* CONFIG_DEBUG_FRAME */
- /* Next, try extbl-based unwinding. */
- if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
- {
- Debug (13, "%s(ret=%d), trying extbl\n",
- UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() failed " : "",
- ret);
- ret = arm_exidx_step (c);
- if (ret > 0)
- return 1;
- if (ret == -UNW_ESTOPUNWIND || ret == 0)
- return ret;
- }
+ // Before trying the fallback, if any unwind info tell us to stop, do that.
+ if (has_stopunwind)
+ return -UNW_ESTOPUNWIND;
/* Fall back on APCS frame parsing.
Note: This won't work in case the ARM EABI is used. */
@@ -139,13 +150,13 @@ unw_step (unw_cursor_t *cursor)
if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME))
{
Debug (13, "%s%s%s%s(ret=%d), trying frame-chain\n",
- UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() " : "",
- (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) && UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "and " : "",
UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) ? "arm_exidx_step() " : "",
- (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) || UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "failed " : "",
+ (UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) && UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) ? "and " : "",
+ UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() " : "",
+ (UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) || UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) ? "failed " : "",
ret);
ret = UNW_ESUCCESS;
- /* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */
+ /* EXIDX and/or DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */
unw_word_t instr, i;
dwarf_loc_t ip_loc, fp_loc;
unw_word_t frame;