From 8015d273e7d6a81ab8f66c45f6844f7d6eec5eb9 Mon Sep 17 00:00:00 2001 From: Mark Zhuang Date: Wed, 4 Mar 2026 00:45:10 +0800 Subject: [PATCH] [RV64_DYNAREC] Add BOX64_DYNAREC_CALLN_RESTORE option for call_n a0/a1 preservation On RV64, xRDI is mapped to a0 and xRSI to a1 (RISC-V return value registers). After call_n's JALR, a0/a1 contain the native function's return value, silently overwriting xRDI/xRSI. Per x86-64 ABI this is correct (RDI/RSI are caller-saved), but some programs (e.g. WeMeet's coroutine library) violate the ABI by depending on caller-saved registers surviving function calls. On native x86-64, RAX and RDI are separate physical registers, so a function returning 0 in RAX doesn't affect RDI. On RV64, the aliasing (xRDI=a0=return value) causes these ABI-violating programs to crash. BOX64_DYNAREC_CALLN_RESTORE=1 saves/restores xRDI(a0) and xRSI(a1) around native calls in call_n. Off by default. Enable per-app via box64rc. --- src/dynarec/rv64/dynarec_rv64_helper.c | 13 ++++++++++++- src/include/env.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/dynarec/rv64/dynarec_rv64_helper.c b/src/dynarec/rv64/dynarec_rv64_helper.c index 80e80f6d04..4cd4524ea9 100644 --- a/src/dynarec/rv64/dynarec_rv64_helper.c +++ b/src/dynarec/rv64/dynarec_rv64_helper.c @@ -657,10 +657,17 @@ void call_c(dynarec_rv64_t* dyn, int ninst, rv64_consts_t fnc, int reg, int ret, void call_n(dynarec_rv64_t* dyn, int ninst, void* fnc, int w) { MAYUSE(fnc); + int calln_restore = BOX64DRENV(dynarec_calln_restore); fpu_pushcache(dyn, ninst, x3, 1); // save RSP in case there are x86 callbacks... SD(xRSP, xEmu, offsetof(x64emu_t, regs[_SP])); SD(xRBP, xEmu, offsetof(x64emu_t, regs[_BP])); + if (calln_restore) { + // Save caller-saved x86 regs that alias RISC-V return value regs (a0/a1). + // Needed for ABI-violating code that depends on caller-saved regs surviving calls. + STORE_REG(RDI); // xRDI = a0 = RISC-V return value register + STORE_REG(RSI); // xRSI = a1 = RISC-V second return value register + } // check if additional sextw needed int sextw_mask = ((w > 0 ? w : -w) >> 4) & 0b111111; for (int i = 0; i < 6; i++) { @@ -682,7 +689,11 @@ void call_n(dynarec_rv64_t* dyn, int ninst, void* fnc, int w) MV(xRAX, A0); MV(xRDX, A1); } - // all done, restore all regs + if (calln_restore) { + // Restore xRDI/xRSI from emu state, since native call clobbered a0/a1 + LOAD_REG(RDI); + LOAD_REG(RSI); + } // reinitialize sew if (dyn->vector_sew != VECTOR_SEWNA) diff --git a/src/include/env.h b/src/include/env.h index b8f7a403af..c44f90b587 100644 --- a/src/include/env.h +++ b/src/include/env.h @@ -52,6 +52,7 @@ extern char* ftrace_name; INTEGER(BOX64_DYNAREC_BIGBLOCK, dynarec_bigblock, 2, 0, 3, 1) \ BOOLEAN(BOX64_DYNAREC_BLEEDING_EDGE, dynarec_bleeding_edge, 1, 0) \ INTEGER(BOX64_DYNAREC_CALLRET, dynarec_callret, 0, 0, 2, 1) \ + BOOLEAN(BOX64_DYNAREC_CALLN_RESTORE, dynarec_calln_restore, 0, 1) \ BOOLEAN(BOX64_DYNAREC_DF, dynarec_df, 1, 1) \ INTEGER(BOX64_DYNAREC_DIRTY, dynarec_dirty, 0, 0, 2, 0) \ BOOLEAN(BOX64_DYNAREC_HOTPAGE_ALT, dynarec_hotpage_alt, 1, 0) \