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));

Reply via email to