From 5873363e4cbcc17f5dad84ad2e56f08c4801b172 Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Mon, 25 May 2026 10:09:21 +0000 Subject: [PATCH] [SIGNAL] Added partial deferred signal support (#3892) * [SIGNAL] Added partial deferred signal support The cold startup time of `wine explorer` on my 3B6000 dropped from 21s to 13s. * Try to fix wow64 build --- src/custommem.c | 71 +++++++++++++++++++++++++--------- src/emu/x64emu.c | 14 +++++++ src/emu/x64emu_private.h | 8 ++++ src/include/box64context.h | 1 + src/include/signals.h | 5 +++ src/libtools/signal32.c | 7 +++- src/libtools/signals.c | 79 +++++++++++++++++++++++++++++++++++++- src/libtools/threads.c | 8 +++- 8 files changed, 172 insertions(+), 21 deletions(-) diff --git a/src/custommem.c b/src/custommem.c index c59c609eb..209319d4b 100644 --- a/src/custommem.c +++ b/src/custommem.c @@ -16,6 +16,9 @@ #include "callback.h" #include "x64trace.h" #include "custommem.h" +#ifndef _WIN32 +#include "signals.h" +#endif #include "khash.h" #include "threads.h" #include "rbtree.h" @@ -562,24 +565,57 @@ static uintptr_t defered_prot_p = 0; static size_t defered_prot_sz = 0; static uint32_t defered_prot_prot = 0; static mem_flag_t defered_prot_flags = MEM_ALLOCATED; -static sigset_t critical_prot = {0}; -static void setProtection_generic(uintptr_t addr, size_t sz, uint32_t prot, mem_flag_t flags); -#define LOCK_PROT() sigset_t old_sig = {0}; pthread_sigmask(SIG_BLOCK, &critical_prot, &old_sig); mutex_lock(&mutex_prot) -#define LOCK_PROT_READ() sigset_t old_sig = {0}; pthread_sigmask(SIG_BLOCK, &critical_prot, &old_sig); mutex_lock(&mutex_prot) -#define LOCK_PROT_FAST() mutex_lock(&mutex_prot) -#define UNLOCK_PROT() if(defered_prot_p) { \ - uintptr_t p = defered_prot_p; size_t sz = defered_prot_sz; uint32_t prot = defered_prot_prot; mem_flag_t f = defered_prot_flags;\ - defered_prot_p = 0; \ - pthread_sigmask(SIG_SETMASK, &old_sig, NULL); \ - mutex_unlock(&mutex_prot); \ - setProtection_generic(p, sz, prot, f); \ - } else { \ - pthread_sigmask(SIG_SETMASK, &old_sig, NULL); \ - mutex_unlock(&mutex_prot); \ - } -#define UNLOCK_PROT_READ() mutex_unlock(&mutex_prot); pthread_sigmask(SIG_SETMASK, &old_sig, NULL) -#define UNLOCK_PROT_FAST() mutex_unlock(&mutex_prot) +#ifdef _WIN32 +void enter_critical_section(void) {} +void leave_critical_section(void) {} +#endif + +static void setProtection_generic(uintptr_t addr, size_t sz, uint32_t prot, mem_flag_t flags); +#define LOCK_PROT() \ + do { \ + enter_critical_section(); \ + mutex_lock(&mutex_prot); \ + } while (0) + +#define LOCK_PROT_READ() \ + do { \ + enter_critical_section(); \ + mutex_lock(&mutex_prot); \ + } while (0) + +#define LOCK_PROT_FAST() \ + do { \ + mutex_lock(&mutex_prot); \ + } while (0) + +#define UNLOCK_PROT() \ + do { \ + if (defered_prot_p) { \ + uintptr_t p = defered_prot_p; \ + size_t sz = defered_prot_sz; \ + uint32_t prot = defered_prot_prot; \ + mem_flag_t f = defered_prot_flags; \ + defered_prot_p = 0; \ + mutex_unlock(&mutex_prot); \ + leave_critical_section(); \ + setProtection_generic(p, sz, prot, f); \ + } else { \ + mutex_unlock(&mutex_prot); \ + leave_critical_section(); \ + } \ + } while (0) + +#define UNLOCK_PROT_READ() \ + do { \ + mutex_unlock(&mutex_prot); \ + leave_critical_section(); \ + } while (0) + +#define UNLOCK_PROT_FAST() \ + do { \ + mutex_unlock(&mutex_prot); \ + } while (0) #ifdef TRACE_MEMSTAT static uint64_t customMalloc_allocated = 0; @@ -3095,7 +3131,6 @@ void init_custommem_helper(box64context_t* ctx) for(int i=0; icritical_section = 0; + emu->deferred_signal_processing = 0; + emu->deferred_signal_count = 0; + memset((void*)emu->deferred_signal_pending, 0, sizeof(emu->deferred_signal_pending)); + memset(emu->deferred_siginfo, 0, sizeof(emu->deferred_siginfo)); + #endif +} + static void internalX64Setup(x64emu_t* emu, box64context_t *context, uintptr_t start, uintptr_t stack, int stacksize, int ownstack) { emu->context = context; @@ -90,6 +101,7 @@ static void internalX64Setup(x64emu_t* emu, box64context_t *context, uintptr_t s emu->mxcsr.x32 = 0x1f80; // want some new jmpbuf for error recovery emu->flags.need_jmpbuf = 1; + reset_deferred_signal(emu); } EXPORTDYN @@ -219,6 +231,7 @@ void CloneEmu(x64emu_t *newemu, const x64emu_t* emu) newemu->quit = emu->quit; newemu->error = emu->error; newemu->x64emu_parity_tab = emu->x64emu_parity_tab; + reset_deferred_signal(newemu); } void CopyEmu(x64emu_t *newemu, const x64emu_t* emu) @@ -247,6 +260,7 @@ void CopyEmu(x64emu_t *newemu, const x64emu_t* emu) newemu->mxcsr = emu->mxcsr; newemu->quit = emu->quit; newemu->error = emu->error; + reset_deferred_signal(newemu); } box64context_t* GetEmuContext(x64emu_t* emu) diff --git a/src/emu/x64emu_private.h b/src/emu/x64emu_private.h index 58ff56ad4..34437851d 100644 --- a/src/emu/x64emu_private.h +++ b/src/emu/x64emu_private.h @@ -4,6 +4,7 @@ #include "regs.h" #include "os.h" #include "box64context.h" +#include typedef struct x64_ucontext_s x64_ucontext_t; #ifdef BOX32 @@ -135,6 +136,13 @@ typedef struct x64emu_s { tlsdatasize_t *tlsdata; // other informations int type; // EMUTYPE_xxx define + #ifndef _WIN32 + volatile sig_atomic_t critical_section; + volatile sig_atomic_t deferred_signal_processing; + volatile sig_atomic_t deferred_signal_count; + volatile sig_atomic_t deferred_signal_pending[MAX_SIGNAL+1]; + siginfo_t deferred_siginfo[MAX_SIGNAL+1]; + #endif #ifdef BOX32 int libc_err; // copy of errno from libc int libc_herr; // copy of h_errno from libc diff --git a/src/include/box64context.h b/src/include/box64context.h index 11a53afc5..a81436139 100644 --- a/src/include/box64context.h +++ b/src/include/box64context.h @@ -287,6 +287,7 @@ int AddTLSPartition(box64context_t* context, int tlssize); // defined in fact in threads.c void thread_set_emu(x64emu_t* emu); void thread_forget_emu(); +x64emu_t* thread_get_emu_no_create(void); x64emu_t* thread_get_emu(void); // relock the muxtex that were unlocked diff --git a/src/include/signals.h b/src/include/signals.h index 9c8bba19d..930cc35e9 100644 --- a/src/include/signals.h +++ b/src/include/signals.h @@ -71,6 +71,11 @@ int my___sigaction(x64emu_t* emu, int signum, const x64_sigaction_t *act, x64_si int my_syscall_rt_sigaction(x64emu_t* emu, int signum, const x64_sigaction_restorer_t *act, x64_sigaction_restorer_t *oldact, int sigsetsize); +void enter_critical_section(); +void leave_critical_section(); +int defer_signal(x64emu_t* emu, int signum, siginfo_t* info); +void cancel_deferred_signal_processing(x64emu_t* emu); + void init_signal_helper(box64context_t* context); void fini_signal_helper(void); diff --git a/src/libtools/signal32.c b/src/libtools/signal32.c index 0728ada20..9cc934bc8 100644 --- a/src/libtools/signal32.c +++ b/src/libtools/signal32.c @@ -771,6 +771,7 @@ int my_sigactionhandler_oldcode_32(x64emu_t* emu, int32_t sig, int simple, sigin #ifdef DYNAREC dynablock_leave_runtime((dynablock_t*)cur_db); #endif + cancel_deferred_signal_processing(emu); #ifdef ANDROID siglongjmp(*emu->jmpbuf, skip); #else @@ -817,6 +818,10 @@ int my_sigactionhandler_oldcode_32(x64emu_t* emu, int32_t sig, int simple, sigin void my32_sigactionhandler(int32_t sig, siginfo_t* info, void * ucntx) { + sig = signal_to_x64(sig); + x64emu_t* emu = thread_get_emu_no_create(); + if (defer_signal(emu, sig, info)) + return; #ifdef DYNAREC ucontext_t *p = (ucontext_t *)ucntx; #ifdef ARM64 @@ -835,7 +840,7 @@ void my32_sigactionhandler(int32_t sig, siginfo_t* info, void * ucntx) void* db = NULL; #endif - my_sigactionhandler_oldcode_32(NULL, sig, 0, info, ucntx, NULL, db); + my_sigactionhandler_oldcode_32(emu, sig, 0, info, ucntx, NULL, db); } diff --git a/src/libtools/signals.c b/src/libtools/signals.c index 08bc543f7..ecb074e98 100644 --- a/src/libtools/signals.c +++ b/src/libtools/signals.c @@ -258,9 +258,80 @@ x64emu_t* getEmuSignal(x64emu_t* emu, ucontext_t* p, dynablock_t* db) } #endif +void my_sigactionhandler_oldcode(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 + +static int is_signal_deferrable(int sig) +{ + switch (sig) { + case X64_SIGSEGV: + case X64_SIGBUS: + case X64_SIGILL: + case X64_SIGABRT: + return 0; + } + return 1; +} + +int defer_signal(x64emu_t* emu, int signum, siginfo_t* info) +{ + if (!emu || signum < 0 || signum > MAX_SIGNAL || !is_signal_deferrable(signum) || emu->critical_section <= 0) + return 0; + + if (info) + emu->deferred_siginfo[signum] = *info; + else + memset(&emu->deferred_siginfo[signum], 0, sizeof(emu->deferred_siginfo[signum])); + emu->deferred_siginfo[signum].si_signo = signum; + if (!emu->deferred_signal_pending[signum]) + ++emu->deferred_signal_count; + emu->deferred_signal_pending[signum] = 1; + return 1; +} + +void cancel_deferred_signal_processing(x64emu_t* emu) +{ + if (!emu) return; + emu->critical_section = 0; + emu->deferred_signal_processing = 0; +} + +void enter_critical_section() +{ + x64emu_t* emu = thread_get_emu_no_create(); + if (emu) ++emu->critical_section; +} + +void leave_critical_section() +{ + x64emu_t* emu = thread_get_emu_no_create(); + if (!emu || emu->critical_section <= 0) + return; + if (--emu->critical_section || !emu->deferred_signal_count || emu->deferred_signal_processing) + return; + + emu->deferred_signal_processing = 1; + while (emu->deferred_signal_count) { + int handled = 0; + for (int sig = 1; sig <= MAX_SIGNAL; ++sig) { + if (!emu->deferred_signal_pending[sig]) + continue; + siginfo_t info = emu->deferred_siginfo[sig]; + emu->deferred_signal_pending[sig] = 0; + --emu->deferred_signal_count; + handled = 1; + my_sigactionhandler_oldcode(emu, sig, 0, &info, NULL, NULL, NULL, R_RIP); + } + if (!handled) { + emu->deferred_signal_count = 0; + break; + } + } + 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 Locks = unlockMutex(); @@ -520,6 +591,7 @@ int my_sigactionhandler_oldcode_64(x64emu_t* emu, int32_t sig, int simple, sigin #ifdef DYNAREC dynablock_leave_runtime((dynablock_t*)cur_db); #endif + cancel_deferred_signal_processing(emu); #ifdef ANDROID siglongjmp(*emu->jmpbuf, skip); #else @@ -841,6 +913,7 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx) #if defined(RV64) || defined(PPC64LE) emu->xSPSave = emu->old_savedsp; #endif + cancel_deferred_signal_processing(emu); #ifdef ANDROID siglongjmp(*(JUMPBUFF*)emu->jmpbuf, 3); #else @@ -915,6 +988,7 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx) #if defined(RV64) || defined(PPC64LE) emu->xSPSave = emu->old_savedsp; #endif + cancel_deferred_signal_processing(emu); #ifdef ANDROID siglongjmp(*(JUMPBUFF*)emu->jmpbuf, 2); #else @@ -1259,13 +1333,16 @@ dynarec_log(/*LOG_DEBUG*/LOG_INFO, "%04d|Repeated SIGSEGV with Access error on % void my_sigactionhandler(int32_t sig, siginfo_t* info, void * ucntx) { sig = signal_to_x64(sig); + x64emu_t* emu = thread_get_emu_no_create(); + if (defer_signal(emu, sig, info)) + return; void* pc = NULL; #ifdef DYNAREC ucontext_t *p = (ucontext_t *)ucntx; pc = (void*)CONTEXT_PC(p); #endif dynablock_t* db = FindDynablockFromNativeAddress(pc); - x64emu_t* emu = thread_get_emu(); + if (!emu) emu = thread_get_emu(); uintptr_t x64pc = R_RIP; if(db) x64pc = getX64Address(db, (uintptr_t)pc); diff --git a/src/libtools/threads.c b/src/libtools/threads.c index 31724b685..b1c4f6387 100644 --- a/src/libtools/threads.c +++ b/src/libtools/threads.c @@ -256,7 +256,13 @@ void thread_set_emu(x64emu_t* emu) pthread_setspecific(thread_key, et); } -x64emu_t* thread_get_emu() +x64emu_t* thread_get_emu_no_create(void) +{ + emuthread_t* et = (emuthread_t*)pthread_getspecific(thread_key); + return et ? et->emu : NULL; +} + +x64emu_t* thread_get_emu(void) { emuthread_t *et = (emuthread_t*)pthread_getspecific(thread_key); if(!et) {