When backtracing starts in a leaf function, it'll cause the code to go off the rials as the stack frame for leaf functions is incomplete -- it lacks the return address, likely just causing recursive exception handling by trying to follow an invalid pointer chain. Unfortunately, -mno-omit-leaf-frame-pointer isn't supported for the ARM target as an easy fix. Make use of -mapcs-frame instead to force the generation of an APCS stack frame layout[1] that can be traversed reliably. As Clang doesn't support -mapcs-frame, make the stack walking code handle this by using the (old) more compact standard format as a fall-back. Link: https://developer.arm.com/documentation/dui0041/c/ARM-Procedure-Call-Standard/APCS-definition/The-stack-backtrace-data-structure [1] Signed-off-by: Mathias Krause --- I failed to build KUT with Clang for ARM for various reasons, the code is clearly lacking Clang support for ARM, so I doubt this fall-back will be needed / used anytime soon. arm/Makefile.arm | 8 ++++++++ lib/arm/stack.c | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/arm/Makefile.arm b/arm/Makefile.arm index d6250b7fb686..7734e17fe583 100644 --- a/arm/Makefile.arm +++ b/arm/Makefile.arm @@ -39,4 +39,12 @@ tests = include $(SRCDIR)/$(TEST_DIR)/Makefile.common +ifneq ($(KEEP_FRAME_POINTER),) +# Force the generation of an APCS stack frame layout to be able to reliably +# walk the stack. Otherwise the compiler may omit saving LR on the stack for +# leaf functions and, unfortunately, -mno-omit-leaf-frame-pointer isn't +# supported on ARM :( +LATE_CFLAGS += $(call cc-option, -mapcs-frame -DAPCS_FRAMES, "") +endif + arch_clean: arm_clean diff --git a/lib/arm/stack.c b/lib/arm/stack.c index 66d18b47ea53..b2384d8eb4c1 100644 --- a/lib/arm/stack.c +++ b/lib/arm/stack.c @@ -8,6 +8,20 @@ #include #include +/* + * APCS stack frames are generated by code like this: + * | mov ip, sp + * | push {..., fp, ip, lr, pc} + * | sub fp, ip, #4 + */ +#ifdef APCS_FRAMES +# define FP_IDX -3 +# define LR_IDX -1 +#else +# define FP_IDX -1 +# define LR_IDX 0 +#endif + int arch_backtrace_frame(const void *frame, const void **return_addrs, int max_depth, bool current_frame) { @@ -27,10 +41,10 @@ int arch_backtrace_frame(const void *frame, const void **return_addrs, for (depth = 0; depth < max_depth; depth++) { if (!fp) break; - return_addrs[depth] = (void *)fp[0]; + return_addrs[depth] = (void *)fp[LR_IDX]; if (return_addrs[depth] == 0) break; - fp = (unsigned long *)fp[-1]; + fp = (unsigned long *)fp[FP_IDX]; } walking = 0; -- 2.47.3