diff --git a/src/dynarec/dynarec_arch.h b/src/dynarec/dynarec_arch.h index 1c2f15e91a..a92229d774 100644 --- a/src/dynarec/dynarec_arch.h +++ b/src/dynarec/dynarec_arch.h @@ -33,6 +33,7 @@ #define ARCH_ADJUST(A, B, C, D) adjust_arch(A, B, C, D) #define STOP_NATIVE_FLAGS(A, B) A->insts[B].nat_flags_op = NAT_FLAG_OP_UNUSABLE #define ARCH_UNALIGNED(A, B) arch_unaligned(A, B) +#define ARCH_HOST_CALL(A, B, C) 0 extern uint32_t arm64_crc(void* p, uint32_t len); extern uint32_t arm64_x31_hash(void* p, uint32_t len); #define ARCH_CRC(A, B) if(cpuext.crc32) return arm64_crc(A, B); else return arm64_x31_hash(A, B) @@ -74,6 +75,7 @@ extern void arm64_next_invalid(); #define ARCH_ADJUST(A, B, C, D) adjust_arch(A, B, C, D) #define STOP_NATIVE_FLAGS(A, B) {} #define ARCH_UNALIGNED(A, B) arch_unaligned(A, B) +#define ARCH_HOST_CALL(A, B, C) arch_host_call(A, B, C) extern uint32_t la64_crc(void* p, uint32_t len); extern void la64_crc_autocrc(); // same as la64_crc, but not using regular ABI #define ARCH_CRC(A, B) return la64_crc(A, B) @@ -117,6 +119,7 @@ extern void la64_next_invalid(); #define ARCH_ADJUST(A, B, C, D) {} #define STOP_NATIVE_FLAGS(A, B) {} #define ARCH_UNALIGNED(A, B) arch_unaligned(A, B) +#define ARCH_HOST_CALL(A, B, C) 0 #define ARCH_NOP 0b0010011 #define ARCH_UDF 0xc0001073 @@ -153,6 +156,7 @@ extern void la64_next_invalid(); #define ARCH_ADJUST(A, B, C, D) adjust_arch(A, B, C, D) #define STOP_NATIVE_FLAGS(A, B) {} #define ARCH_UNALIGNED(A, B) arch_unaligned(A, B) +#define ARCH_HOST_CALL(A, B, C) 0 extern uint32_t ppc64le_fast_hash(void* p, uint32_t len); #define ARCH_CRC(A, B) return ppc64le_fast_hash(A, B) diff --git a/src/dynarec/la64/dynarec_la64_arch.c b/src/dynarec/la64/dynarec_la64_arch.c index c0dc26609c..29cb72c9a4 100644 --- a/src/dynarec/la64/dynarec_la64_arch.c +++ b/src/dynarec/la64/dynarec_la64_arch.c @@ -17,19 +17,23 @@ typedef struct arch_arch_s { - uint16_t unaligned:1; - uint16_t seq:10; // how many instruction on the same values - uint16_t up32; // GPRs with pending 32-bit zero-up at this instruction - uint16_t ymm_zero; // YMM upper halves with a deferred architectural zero - int16_t rsp; // pending rsp offset at this instruction + uint16_t unaligned : 1; + uint16_t host_call : 1; // instruction performs a native call + uint16_t seq : 10; // how many instruction on the same values + uint16_t up32; // GPRs with pending 32-bit zero-up at this instruction + uint16_t ymm_zero; // YMM upper halves with a deferred architectural zero + int16_t rsp; // pending rsp offset at this instruction + uint32_t call_window; // spill offset within the instruction } arch_arch_t; typedef struct arch_build_s { uint8_t unaligned; + uint8_t host_call; uint16_t up32; uint16_t ymm_zero; int16_t rsp; + uint32_t call_window; } arch_build_t; static int arch_build(dynarec_la64_t* dyn, int ninst, arch_build_t* arch) @@ -37,11 +41,13 @@ static int arch_build(dynarec_la64_t* dyn, int ninst, arch_build_t* arch) memset(arch, 0, sizeof(arch_build_t)); // opcode can handle unaligned arch->unaligned = dyn->insts[ninst].unaligned; + arch->host_call = dyn->insts[ninst].host_call; + arch->call_window = dyn->insts[ninst].call_window; // pending 32-bit zero-ups at this instruction arch->up32 = dyn->insts[ninst].up32_pending; arch->ymm_zero = dyn->insts[ninst].vector_liveness.ymm_pending; arch->rsp = dyn->insts[ninst].rsp_entry; - return arch->unaligned || arch->up32 || arch->ymm_zero || arch->rsp; + return arch->unaligned || arch->host_call || arch->up32 || arch->ymm_zero || arch->rsp; } size_t get_size_arch(dynarec_la64_t* dyn) @@ -73,6 +79,8 @@ size_t get_size_arch(dynarec_la64_t* dyn) static void build_next(arch_arch_t* arch, arch_build_t* build) { arch->unaligned = build->unaligned; + arch->host_call = build->host_call; + arch->call_window = build->call_window; arch->seq = 0; arch->up32 = build->up32; arch->ymm_zero = build->ymm_zero; @@ -134,6 +142,26 @@ int arch_unaligned(dynablock_t* db, uintptr_t x64pc) return arch->unaligned; } +int arch_host_call(dynablock_t* db, void* native_pc, uintptr_t x64pc) +{ + if (!db) return 0; + if (!db->arch_size || !db->arch) return 0; + int ninst = getX64AddressInst(db, x64pc); + if (ninst < 0 || ninst >= db->isize) return 0; + arch_arch_t* arch = arch_for_ninst(db, ninst); + if (!arch->host_call || !arch->call_window) return 0; + // getX64Address + uintptr_t addr = (uintptr_t)db->block + db->prefixsize; + int i = 0; + for (int inst = 0; inst < ninst; ++inst) { + do { + addr += db->instsize[i].nat * 4; + ++i; + } while ((db->instsize[i - 1].x64 == 15) || (db->instsize[i - 1].nat == 15)); + } + return ((uintptr_t)native_pc - addr) >= arch->call_window; +} + void adjust_arch(dynablock_t* db, x64emu_t* emu, ucontext_t* p, uintptr_t x64pc) { (void)p; @@ -154,5 +182,5 @@ void adjust_arch(dynablock_t* db, x64emu_t* emu, ucontext_t* p, uintptr_t x64pc) ymm_zero &= ymm_zero - 1; emu->ymm[r].u128 = 0; } - if (arch->rsp) emu->regs[_SP].q[0] += arch->rsp; + if (arch->rsp && !arch->host_call) emu->regs[_SP].q[0] += arch->rsp; } diff --git a/src/dynarec/la64/dynarec_la64_arch.h b/src/dynarec/la64/dynarec_la64_arch.h index 23c41a7ead..cd5762249d 100644 --- a/src/dynarec/la64/dynarec_la64_arch.h +++ b/src/dynarec/la64/dynarec_la64_arch.h @@ -17,4 +17,6 @@ void* populate_arch(dynarec_la64_t* dyn, void* p, size_t sz); void adjust_arch(dynablock_t* db, x64emu_t* emu, ucontext_t* p, uintptr_t x64pc); // get if instruction can be regenerated for unaligned access int arch_unaligned(dynablock_t* db, uintptr_t x64pc); +// get if the native pc is in the native call window of the instruction +int arch_host_call(dynablock_t* db, void* native_pc, uintptr_t x64pc); #endif // __DYNAREC_LA_ARCH_H__ diff --git a/src/dynarec/la64/dynarec_la64_helper.c b/src/dynarec/la64/dynarec_la64_helper.c index c5130beec0..bba03218da 100644 --- a/src/dynarec/la64/dynarec_la64_helper.c +++ b/src/dynarec/la64/dynarec_la64_helper.c @@ -584,6 +584,7 @@ void call_c(dynarec_la64_t* dyn, int ninst, la64_consts_t fnc, int reg, int ret, STORE_REG(RSP); STORE_REG(RBP); ST_D(xRIP, xEmu, offsetof(x64emu_t, ip)); + dyn->insts[ninst].call_window = dyn->native_size - dyn->insts[ninst].address; } TABLE64C(reg, fnc); if (arg1) MV(A1, arg1); @@ -641,6 +642,7 @@ void call_n(dynarec_la64_t* dyn, int ninst, void* fnc, int w) ST_D(xRSP, xEmu, offsetof(x64emu_t, regs[_SP])); ST_D(xRBP, xEmu, offsetof(x64emu_t, regs[_BP])); ST_D(xRBX, xEmu, offsetof(x64emu_t, regs[_BX])); + dyn->insts[ninst].call_window = dyn->native_size - dyn->insts[ninst].address; int nfp = (abs(w) & 15) - 1; if (nfp > 0) for (int i = 0; i < nfp; ++i) diff --git a/src/dynarec/la64/dynarec_la64_pass0.h b/src/dynarec/la64/dynarec_la64_pass0.h index 35c4bc41d1..2f035bdf66 100644 --- a/src/dynarec/la64/dynarec_la64_pass0.h +++ b/src/dynarec/la64/dynarec_la64_pass0.h @@ -86,6 +86,7 @@ dyn->insts[ninst].comis_fusion = -1; \ dyn->insts[ninst].comis_mark = 0; \ dyn->insts[ninst].host_call = 0; \ + dyn->insts[ninst].call_window = 0; \ dyn->insts[ninst].f_entry = dyn->f; \ if (ninst) { dyn->insts[ninst - 1].x64.size = dyn->insts[ninst].x64.addr - dyn->insts[ninst - 1].x64.addr; } \ AREFLAGSNEEDED() diff --git a/src/dynarec/la64/dynarec_la64_private.h b/src/dynarec/la64/dynarec_la64_private.h index ba8645b5db..7f7699d5ce 100644 --- a/src/dynarec/la64/dynarec_la64_private.h +++ b/src/dynarec/la64/dynarec_la64_private.h @@ -174,6 +174,7 @@ typedef struct instruction_la64_s { int8_t comis_fusion; uint8_t comis_mark:1; uint8_t host_call:1; + int call_window; // native byte offset where the regs have been spilled to emu before a native call int16_t rsp_entry; // pending rsp offset at entry int16_t rsp_flush; // rsp offset to emit right after this push/pop uint8_t rsp_merge : 1; // this push/pop is emitted with merged rsp offset diff --git a/src/include/sigtools.h b/src/include/sigtools.h index 089174b16b..fc607ff932 100644 --- a/src/include/sigtools.h +++ b/src/include/sigtools.h @@ -54,7 +54,7 @@ int checkMutex(uint32_t mask); int write_opcode(uintptr_t rip, uintptr_t native_ip, int is32bits); void adjustregs(x64emu_t* emu, void* pc); -void copyUCTXreg2Emu(x64emu_t* emu, ucontext_t* p, uintptr_t ip); +void copyUCTXreg2Emu(x64emu_t* emu, ucontext_t* p, dynablock_t* db, uintptr_t ip); #ifdef DYNAREC void copyEmu2USignalCTXreg(ucontext_t* p, x64emu_t* emu, void* new_pc); #endif diff --git a/src/libtools/signals.c b/src/libtools/signals.c index 3750538409..2930796371 100644 --- a/src/libtools/signals.c +++ b/src/libtools/signals.c @@ -346,7 +346,7 @@ void leave_critical_section() emu->deferred_signal_processing = 0; } -int my_sigactionhandler_oldcode_64(x64emu_t* emu, int32_t sig, int simple, siginfo_t* info, void * ucntx, int* old_code, void* cur_db) +int my_sigactionhandler_oldcode_64(x64emu_t* emu, int32_t sig, int simple, siginfo_t* info, void * ucntx, int* old_code, void* cur_db, uintptr_t x64pc) { int Locks = unlockMutex(); int log_minimum = (BOX64ENV(showsegv))?LOG_NONE:LOG_DEBUG; @@ -388,8 +388,12 @@ int my_sigactionhandler_oldcode_64(x64emu_t* emu, int32_t sig, int simple, sigin void* pc = NULL; if(p) { pc = (void*)CONTEXT_PC(p); - if(db) - frame = (uintptr_t)CONTEXT_REG(p, xRSP); //this should not be needed, as emu has been "adjusted" to dynablock value already in the caller + if(db) { + if(ARCH_HOST_CALL(db, pc, x64pc)) + frame = R_RSP; + else + frame = (uintptr_t)CONTEXT_REG(p, xRSP); //this should not be needed, as emu has been "adjusted" to dynablock value already in the caller + } } #else (void)ucntx; (void)cur_db; @@ -757,7 +761,7 @@ void my_sigactionhandler_oldcode(x64emu_t* emu, int32_t sig, int simple, siginfo dynablock_t* db = cur_db; if(db && ucntx) { void * pc =(void*)CONTEXT_PC((ucontext_t*)ucntx); - copyUCTXreg2Emu(emu, ucntx, x64pc); + copyUCTXreg2Emu(emu, ucntx, db, x64pc); adjustregs(emu, pc); if(db && db->arch_size) ARCH_ADJUST(db, emu, ucntx, x64pc); @@ -769,7 +773,7 @@ void my_sigactionhandler_oldcode(x64emu_t* emu, int32_t sig, int simple, siginfo direct_ret = my_sigactionhandler_oldcode_32(emu, sig, simple, info, ucntx, old_code, cur_db); } else #endif - direct_ret = my_sigactionhandler_oldcode_64(emu, sig, simple, info, ucntx, old_code, cur_db); + direct_ret = my_sigactionhandler_oldcode_64(emu, sig, simple, info, ucntx, old_code, cur_db, x64pc); if(direct_ret) return; #define GO(A) R_##A = old_##A @@ -957,7 +961,7 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx) return; } else { // dynablock got dirty! need to get out of it!!! - copyUCTXreg2Emu(emu, p, x64pc); + copyUCTXreg2Emu(emu, p, db, x64pc); // only copy as it's a return address, so there is just the "epilog" to mimic here on "ret" type. "loop" type need everything if(type_callret) { adjustregs(emu, pc); @@ -1010,7 +1014,7 @@ else dynarec_log(LOG_INFO, "SIGILL at %p/%p for Dynablock (%p, x64addr=%p) with emu = getEmuSignal(emu, p, db); // dynablock got auto-dirty! need to get out of it!!! uintptr_t x64pc = getX64Address(db, (uintptr_t)pc); - copyUCTXreg2Emu(emu, p, x64pc); + copyUCTXreg2Emu(emu, p, db, x64pc); adjustregs(emu, pc); if(db && db->arch_size) ARCH_ADJUST(db, emu, p, x64pc); @@ -1258,7 +1262,7 @@ dynarec_log(/*LOG_DEBUG*/LOG_INFO, "%04d|Repeated SIGSEGV with Access error on % #undef GO #ifdef DYNAREC if(db) - copyUCTXreg2Emu(emu, p, x64pc); + copyUCTXreg2Emu(emu, p, db, x64pc); #endif nptrs = my_backtrace_ip(emu, buffer, BT_BUF_SIZE); strings = my_backtrace_symbols(emu, (uintptr_t*)buffer, nptrs); diff --git a/src/libtools/sigtools.c b/src/libtools/sigtools.c index 513a953906..7dc6534705 100644 --- a/src/libtools/sigtools.c +++ b/src/libtools/sigtools.c @@ -720,10 +720,12 @@ void adjustregs(x64emu_t* emu, void* pc) #endif } -void copyUCTXreg2Emu(x64emu_t* emu, ucontext_t* p, uintptr_t ip) +void copyUCTXreg2Emu(x64emu_t* emu, ucontext_t* p, dynablock_t* db, uintptr_t ip) { #ifdef DYNAREC - #define GO(R) emu->regs[_##R].q[0] = CONTEXT_REG(p, x##R) + int host_call = p ? ARCH_HOST_CALL(db, (void*)CONTEXT_PC(p), ip) : 0; +#define GO(R) \ + if (!host_call) emu->regs[_##R].q[0] = CONTEXT_REG(p, x##R) GO(RAX); GO(RCX); GO(RDX); @@ -734,6 +736,8 @@ void copyUCTXreg2Emu(x64emu_t* emu, ucontext_t* p, uintptr_t ip) GO(RDI); GO(R8); GO(R9); +#undef GO +#define GO(R) emu->regs[_##R].q[0] = CONTEXT_REG(p, x##R) GO(R10); GO(R11); GO(R12); diff --git a/src/libtools/syscall_user_dispatch.c b/src/libtools/syscall_user_dispatch.c index 180846be75..68c6efc414 100644 --- a/src/libtools/syscall_user_dispatch.c +++ b/src/libtools/syscall_user_dispatch.c @@ -39,7 +39,7 @@ #define AUDIT_ARCH_I386 (EM_386 | __AUDIT_ARCH_LE) #endif -int my_sigactionhandler_oldcode_64(x64emu_t* emu, int32_t sig, int simple, siginfo_t* info, void* ucntx, int* old_code, void* cur_db); +int my_sigactionhandler_oldcode_64(x64emu_t* emu, int32_t sig, int simple, siginfo_t* info, void* ucntx, int* old_code, void* cur_db, uintptr_t x64pc); #ifdef BOX32 int my_sigactionhandler_oldcode_32(x64emu_t* emu, int32_t sig, int simple, siginfo_t* info, void* ucntx, int* old_code, void* cur_db); #endif @@ -63,7 +63,7 @@ static void sud_emit_sigsys(x64emu_t* emu, uintptr_t call_addr, long syscall_nr, #else (void)is32bits; #endif - my_sigactionhandler_oldcode_64(emu, X64_SIGSYS, 0, &info, NULL, NULL, NULL); + my_sigactionhandler_oldcode_64(emu, X64_SIGSYS, 0, &info, NULL, NULL, NULL, call_addr); } long my_syscall_user_dispatch_prctl(x64emu_t* emu, unsigned long op, unsigned long offset, unsigned long len, void* selector)