[PATCH RFC 3/3] arm64: unwind stack on exception

Caleb Connolly caleb.connolly at linaro.org
Wed Jul 10 18:26:20 CEST 2024


We already build arm64 images with frame pointers. Let's finally make
use of them in tandem with the new symbol lookup support by unwinding
the stack when an exception occurs, producing a backtrace similar to
those emitted by Linux.

In addition, introduce a dedicated unwind_stack() function which can be
called from anywhere to print a backtrace.

Signed-off-by: Caleb Connolly <caleb.connolly at linaro.org>
---
 arch/arm/include/asm/ptrace.h |  4 +++
 arch/arm/lib/interrupts_64.c  | 76 +++++++++++++++++++++++++++++++++++++++++++
 lib/Kconfig                   |  6 ++--
 3 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
index a836f6cc60db..f42cfb33e918 100644
--- a/arch/arm/include/asm/ptrace.h
+++ b/arch/arm/include/asm/ptrace.h
@@ -22,8 +22,12 @@
 
 #ifdef __KERNEL__
 extern void show_regs(struct pt_regs *);
 
+#if CONFIG_IS_ENABLED(SYMBOL_LOOKUP)
+void unwind_stack(void);
+#endif
+
 #define predicate(x)	(x & 0xf0000000)
 #define PREDICATE_ALWAYS	0xe0000000
 
 #endif
diff --git a/arch/arm/lib/interrupts_64.c b/arch/arm/lib/interrupts_64.c
index b3024ba514ec..24edeb0de51e 100644
--- a/arch/arm/lib/interrupts_64.c
+++ b/arch/arm/lib/interrupts_64.c
@@ -3,15 +3,17 @@
  * (C) Copyright 2013
  * David Feng <fenghua at phytium.com.cn>
  */
 
+#include <asm-generic/sections.h>
 #include <asm/esr.h>
 #include <asm/global_data.h>
 #include <asm/ptrace.h>
 #include <irq_func.h>
 #include <linux/compiler.h>
 #include <efi_loader.h>
 #include <semihosting.h>
+#include <symbols.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
 int interrupt_init(void)
@@ -80,8 +82,77 @@ static void dump_instr(struct pt_regs *regs)
 		printf(i == 0 ? "(%08x) " : "%08x ", addr[i]);
 	printf("\n");
 }
 
+#if IS_ENABLED(CONFIG_SYMBOL_LOOKUP)
+
+static void print_sym(unsigned long symaddr, const char *symname, unsigned long offset)
+{
+	if (symaddr > (unsigned long)__bss_end)
+		printf("\t<%#016lx> ???\n", symaddr);
+	else
+		printf("\t<%#016lx> %s+%#lx\n", symaddr, symname, offset);
+}
+
+/* Stack frames automatically generated by compiler emitted code */
+struct stack_frame {
+	struct stack_frame *prev_ptr; /* Alays a bigger address on ARM64 */
+	unsigned long lr;
+	char data[];
+};
+
+static void __unwind_stack(unsigned long lr, unsigned long x29)
+{
+	char symname[KSYM_NAME_LEN+1] = { 0 };
+	unsigned long addr, offset;
+	struct stack_frame *fp, *last_fp;
+
+	/*
+	 * Either the place where unwind_stack() was called, or the
+	 * instruction that caused an exception
+	 */
+	symbols_lookup(lr, &addr, &offset, symname);
+	print_sym(addr, symname, offset);
+
+	fp = (struct stack_frame *)x29;
+	while (fp && fp->prev_ptr > fp) {
+		symbols_lookup(fp->lr, &addr, &offset, symname);
+		/*
+		 * _start is used over gd->relocaddr because it will be correct
+		 * if U-Boot was built as relocatable and loaded at an arbitrary
+		 * address (both pre and post-relocation).
+		 */
+		print_sym(addr + (unsigned long)_start, symname, offset);
+		last_fp = fp;
+		fp = (struct stack_frame *)(u64)fp->prev_ptr;
+	}
+}
+
+/**
+ * unwind_stack() - Unwind the callstack and print a backtrace.
+ *
+ * This function can be called for debugging purposes to walk backwards through
+ * stack frames, printing the function name and offset of the branch instruction.
+ *
+ * It reads out the link register (x30) which contains the return address of the
+ * caller, and x29 which contains the frame pointer of the caller.
+ */
+void unwind_stack(void)
+{
+	unsigned long x29, x30;
+
+	asm("mov %0, x29" : "=r" (x29));
+	asm("mov %0, x30" : "=r" (x30));
+
+	__unwind_stack(x30, x29);
+}
+
+#else
+
+#define __unwind_stack(lr, x29) do {} while (0)
+
+#endif
+
 void show_regs(struct pt_regs *regs)
 {
 	int i;
 
@@ -95,8 +166,13 @@ void show_regs(struct pt_regs *regs)
 		printf("x%-2d: %016lx x%-2d: %016lx\n",
 		       i, regs->regs[i], i+1, regs->regs[i+1]);
 	printf("\n");
 	dump_instr(regs);
+
+	if (IS_ENABLED(CONFIG_SYMBOL_LOOKUP)) {
+		printf("\nBacktrace:\n");
+		__unwind_stack(regs->elr, regs->regs[29]);
+	}
 }
 
 /*
  * Try to "emulate" a semihosting call in the event that we don't have a
diff --git a/lib/Kconfig b/lib/Kconfig
index 06a78f83b7d6..092b780aeeb8 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -979,11 +979,13 @@ config VPL_OF_LIBFDT_ASSUME_MASK
 	  unsafe execution. See FDT_ASSUME_PERFECT, etc. in libfdt_internal.h
 
 config SYMBOL_LOOKUP
 	bool "Enable symbol lookup"
+	default n
 	help
-	  This enables support for looking up symbol names from addresses. The
-	  primary usecase for this is improved debugging support.
+	  This enables support for looking up symbol names from addresses. When
+	  enabled U-Boot will print stack traces with function names when an
+	  exception occurs.
 
 menu "System tables"
 	depends on (!EFI && !SYS_COREBOOT) || (ARM && EFI_LOADER)
 

-- 
2.45.2



More information about the U-Boot mailing list