This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit cbe4ceb15895db2e5ab983e358cd203f6139a5c7 Author: liang.huang <[email protected]> AuthorDate: Wed Jul 15 12:13:31 2026 +0800 arch/risc-v: preserve ra across ecall for fp-chain backtrace sys_callN() wraps a bare ecall and calls no other function, so the compiler treats it as a leaf function: with frame pointers enabled, it only needs to spill the caller's s0, which it places in what up_backtrace()'s fp-chain walk assumes is ra's stack slot, while the real ra slot is never written. sched_backtrace() then misreads that slot as the return address for this frame, either resolving to a bogus symbol or, if the adjacent garbage happens to look out-of-range, terminating the backtrace early. Add "ra" to the ecall clobber list so the compiler spills/reloads ra around the ecall like a normal call site, keeping ra and the saved s0 in their expected slots. Gate this on CONFIG_FRAME_POINTER && CONFIG_SCHED_BACKTRACE, the only combination where up_backtrace()'s fp-chain walk is both valid (FRAME_POINTER) and actually exercised (SCHED_BACKTRACE); other configurations keep the original "memory"-only clobber and pay no extra cost. This only fixes the syscall boundary. Leaf functions that do not cross a syscall (e.g. up_idle()) can still lose their ra slot the same way and are not addressed here. Signed-off-by: liang.huang <[email protected]> --- arch/risc-v/include/syscall.h | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/arch/risc-v/include/syscall.h b/arch/risc-v/include/syscall.h index 185557ed74a..d81f6684d05 100644 --- a/arch/risc-v/include/syscall.h +++ b/arch/risc-v/include/syscall.h @@ -139,6 +139,23 @@ uintptr_t sys_call6(unsigned int nbr, uintptr_t parm1, uintptr_t parm2, uintptr_t parm3, uintptr_t parm4, uintptr_t parm5, uintptr_t parm6); #else + +/* ecall is a leaf "call" as far as the compiler can tell: sys_callN() + * itself never calls another function, so with frame pointers enabled + * the compiler stores the caller's saved fp (s0) at what up_backtrace() + * assumes is ra's slot, leaving ra's own slot never written. Clobbering + * "ra" forces it to be spilled/reloaded around the ecall so the fp-chain + * backtrace sched_backtrace() relies on can find it. Only worth the + * extra spill when both frame pointers and backtrace support are built + * in; otherwise there is no fp-chain consumer to fix up for. + */ + +#if defined(CONFIG_FRAME_POINTER) && defined(CONFIG_SCHED_BACKTRACE) +# define RISCV_ECALL_CLOBBERS "memory", "ra" +#else +# define RISCV_ECALL_CLOBBERS "memory" +#endif + /**************************************************************************** * Name: sys_call0 * @@ -155,7 +172,7 @@ static inline uintptr_t sys_call0(unsigned int nbr) ( "ecall" :: "r"(r0) - : "memory" + : RISCV_ECALL_CLOBBERS ); asm volatile("nop" : "=r"(r0)); @@ -180,7 +197,7 @@ static inline uintptr_t sys_call1(unsigned int nbr, uintptr_t parm1) ( "ecall" :: "r"(r0), "r"(r1) - : "memory" + : RISCV_ECALL_CLOBBERS ); asm volatile("nop" : "=r"(r0)); @@ -207,7 +224,7 @@ static inline uintptr_t sys_call2(unsigned int nbr, uintptr_t parm1, ( "ecall" :: "r"(r0), "r"(r1), "r"(r2) - : "memory" + : RISCV_ECALL_CLOBBERS ); asm volatile("nop" : "=r"(r0)); @@ -235,7 +252,7 @@ static inline uintptr_t sys_call3(unsigned int nbr, uintptr_t parm1, ( "ecall" :: "r"(r0), "r"(r1), "r"(r2), "r"(r3) - : "memory" + : RISCV_ECALL_CLOBBERS ); asm volatile("nop" : "=r"(r0)); @@ -265,7 +282,7 @@ static inline uintptr_t sys_call4(unsigned int nbr, uintptr_t parm1, ( "ecall" :: "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4) - : "memory" + : RISCV_ECALL_CLOBBERS ); asm volatile("nop" : "=r"(r0)); @@ -296,7 +313,7 @@ static inline uintptr_t sys_call5(unsigned int nbr, uintptr_t parm1, ( "ecall" :: "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5) - : "memory" + : RISCV_ECALL_CLOBBERS ); asm volatile("nop" : "=r"(r0)); @@ -329,7 +346,7 @@ static inline uintptr_t sys_call6(unsigned int nbr, uintptr_t parm1, ( "ecall" :: "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5), "r"(r6) - : "memory" + : RISCV_ECALL_CLOBBERS ); asm volatile("nop" : "=r"(r0));
