Add a hack (disabled by default) that attempts to unwind the stack on
ARM by simply scanning up and looking for words that look like they
might be return addresses. Last-ditch hack for when the CFI trail
goes cold.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12641 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_stacktrace.c b/coregrind/m_stacktrace.c
index 19b347b..6731278 100644
--- a/coregrind/m_stacktrace.c
+++ b/coregrind/m_stacktrace.c
@@ -575,6 +575,87 @@
#if defined(VGP_arm_linux)
+static Bool in_same_fn ( Addr a1, Addr a2 )
+{
+# define M_VG_ERRTXT 500
+ UChar buf_a1[M_VG_ERRTXT], buf_a2[M_VG_ERRTXT];
+ /* The following conditional looks grossly inefficient and
+ surely could be majorly improved, with not much effort. */
+ if (VG_(get_fnname_raw) (a1, buf_a1, M_VG_ERRTXT))
+ if (VG_(get_fnname_raw) (a2, buf_a2, M_VG_ERRTXT))
+ if (VG_(strncmp)(buf_a1, buf_a2, M_VG_ERRTXT))
+ return True;
+# undef M_VG_ERRTXT
+ return False;
+}
+
+static Bool in_same_page ( Addr a1, Addr a2 ) {
+ return (a1 & ~0xFFF) == (a2 & ~0xFFF);
+}
+
+static Addr abs_diff ( Addr a1, Addr a2 ) {
+ return (Addr)(a1 > a2 ? a1 - a2 : a2 - a1);
+}
+
+static Bool has_XT_perms ( Addr a )
+{
+ NSegment const* seg = VG_(am_find_nsegment)(a);
+ return seg && seg->hasX && seg->hasT;
+}
+
+static Bool looks_like_Thumb_call32 ( UShort w0, UShort w1 )
+{
+ if (0)
+ VG_(printf)("isT32call %04x %04x\n", (UInt)w0, (UInt)w1);
+ // BL simm26
+ if ((w0 & 0xF800) == 0xF000 && (w1 & 0xC000) == 0xC000) return True;
+ // BLX simm26
+ if ((w0 & 0xF800) == 0xF000 && (w1 & 0xC000) == 0xC000) return True;
+ return False;
+}
+
+static Bool looks_like_Thumb_call16 ( UShort w0 )
+{
+ return False;
+}
+
+static Bool looks_like_ARM_call ( UInt a0 )
+{
+ if (0)
+ VG_(printf)("isA32call %08x\n", a0);
+ // Leading E forces unconditional only -- fix
+ if ((a0 & 0xFF000000) == 0xEB000000) return True;
+ return False;
+}
+
+static Bool looks_like_RA ( Addr ra )
+{
+ /* 'ra' is a plausible return address if it points to
+ an instruction after a call insn. */
+ Bool isT = (ra & 1);
+ if (isT) {
+ // returning to Thumb code
+ ra &= ~1;
+ ra -= 4;
+ if (has_XT_perms(ra)) {
+ UShort w0 = *(UShort*)ra;
+ UShort w1 = in_same_page(ra, ra+2) ? *(UShort*)(ra+2) : 0;
+ if (looks_like_Thumb_call16(w1) || looks_like_Thumb_call32(w0,w1))
+ return True;
+ }
+ } else {
+ // ARM
+ ra &= ~3;
+ ra -= 4;
+ if (has_XT_perms(ra)) {
+ UInt a0 = *(UInt*)ra;
+ if (looks_like_ARM_call(a0))
+ return True;
+ }
+ }
+ return False;
+}
+
UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
/*OUT*/Addr* ips, UInt max_n_ips,
/*OUT*/Addr* sps, /*OUT*/Addr* fps,
@@ -637,6 +718,7 @@
i = 1;
/* Loop unwinding the stack. */
+ Bool do_stack_scan = False;
while (True) {
if (debug) {
@@ -658,9 +740,48 @@
continue;
}
/* No luck. We have to give up. */
+ do_stack_scan = True;
break;
}
+ if (0/*DISABLED BY DEFAULT*/ && do_stack_scan && i < max_n_ips && i <= 2) {
+ Int nByStackScan = 0;
+ Addr lr = uregs.r14;
+ Addr sp = uregs.r13 & ~3;
+ Addr pc = uregs.r15;
+ // First see if LR contains
+ // something that could be a valid return address.
+ if (!in_same_fn(lr, pc) && looks_like_RA(lr)) {
+ // take it only if 'cand' isn't obviously a duplicate
+ // of the last found IP value
+ Addr cand = (lr & 0xFFFFFFFE) - 1;
+ if (abs_diff(cand, ips[i-1]) > 1) {
+ if (sps) sps[i] = 0;
+ if (fps) fps[i] = 0;
+ ips[i++] = cand;
+ nByStackScan++;
+ }
+ }
+ while (in_same_page(sp, uregs.r13)) {
+ if (i >= max_n_ips)
+ break;
+ // we're in the same page; fairly safe to keep going
+ UWord w = *(UWord*)(sp & ~0x3);
+ if (looks_like_RA(w)) {
+ Addr cand = (w & 0xFFFFFFFE) - 1;
+ // take it only if 'cand' isn't obviously a duplicate
+ // of the last found IP value
+ if (abs_diff(cand, ips[i-1]) > 1) {
+ if (sps) sps[i] = 0;
+ if (fps) fps[i] = 0;
+ ips[i++] = cand;
+ if (++nByStackScan >= 5) break;
+ }
+ }
+ sp += 4;
+ }
+ }
+
n_found = i;
return n_found;
}