Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 01 May 2013 14:17:32 -0600
From:      Ian Lepore <ian@FreeBSD.org>
To:        freebsd-arm <freebsd-arm@FreeBSD.org>
Subject:   A fix for the clang + eabi + kdb backtrace endless loop
Message-ID:  <1367439452.1180.92.camel@revolution.hippie.lan>

next in thread | raw e-mail | index | archive | help

--=-R74sECJmrSUq+3Vwtpin
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit

The attached patch fixes the problem where a kdb backtrace loops
endlessly on an eabi kernel.  

I'm no expert on this stuff, so while this fixes the problem for me, I'm
not sure it's right, especially the STOP_UNWINDING in exception_exit
(which handles a test case of breaking into the debugger with the
keyboard and typing 'bt').  I'm not going to commit this until it's been
reviewed by someone who actually knows what they're doing. :)

-- Ian


--=-R74sECJmrSUq+3Vwtpin
Content-Disposition: inline; filename="eabi_unwind_fixes.diff"
Content-Type: text/x-patch; name="eabi_unwind_fixes.diff"; charset="us-ascii"
Content-Transfer-Encoding: 7bit

Index: sys/arm/arm/db_trace.c
===================================================================
--- sys/arm/arm/db_trace.c	(revision 248960)
+++ sys/arm/arm/db_trace.c	(working copy)
@@ -354,7 +354,7 @@
 		index = db_find_index(state->start_pc);
 
 		if (index->insn == EXIDX_CANTUNWIND) {
-			db_printf("Unable to unwind\n");
+			db_printf("Unable to unwind further\n");
 			break;
 		} else if (index->insn & (1 << 31)) {
 			/* The data is within the instruction */
@@ -412,6 +412,23 @@
 			}
 		}
 		db_printf("\n");
+
+		/* Stop if we've unwound back to the thread entry point. */
+		if (state->registers[FP] == 0) {
+			break;
+		}
+
+		/*
+		 * Stop if the unwind function didn't change the PC, to avoid
+		 * getting stuck in this loop forever.  If this happens, it's an
+		 * indication that the unwind information is incorrect somehow
+		 * for the function named in the last frame printed before you
+		 * see this message.
+		 */
+		if (state->start_pc == state->registers[PC]) {
+			db_printf("Unwind failure (PC didn't change)\n");
+			break;
+		}
 	}
 }
 #endif
Index: sys/arm/arm/exception.S
===================================================================
--- sys/arm/arm/exception.S	(revision 248960)
+++ sys/arm/arm/exception.S	(working copy)
@@ -196,15 +196,19 @@
  * Interrupts are disabled at suitable points to avoid ASTs
  * being posted between testing and exit to user mode.
  *
- * This function uses PULLFRAMEFROMSVCANDEXIT and
- * DO_AST
- * only be called if the exception handler used PUSHFRAMEINSVC
+ * This function uses PULLFRAMEFROMSVCANDEXIT and DO_AST and can
+ * only be called if the exception handler used PUSHFRAMEINSVC.
  *
+ * For EABI, don't try to unwind any further than this, because the unwind
+ * info generated here is a single INSN_FINISH instruction.  Nothing gets 
+ * unwound (no registers change) so the unwind would loop here forever.
  */
 
-exception_exit:
+ASENTRY_NP(exception_exit)
+	STOP_UNWINDING
 	DO_AST
 	PULLFRAMEFROMSVCANDEXIT
+END(exception_exit)
 
 /*
  * undefined_entry:

--=-R74sECJmrSUq+3Vwtpin--




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?1367439452.1180.92.camel>