mirror of
https://github.com/ptitSeb/box64.git
synced 2026-11-07 09:26:11 -10:00
[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
This commit is contained in:
1 parent
ef7852773b
commit
5873363e4c
8 files changed
+172
-21
No files matched your search
+53
-18
@@ -16,6 +16,9 @@
|
|||||||
#include "callback.h"
|
#include "callback.h"
|
||||||
#include "x64trace.h"
|
#include "x64trace.h"
|
||||||
#include "custommem.h"
|
#include "custommem.h"
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include "signals.h"
|
||||||
|
#endif
|
||||||
#include "khash.h"
|
#include "khash.h"
|
||||||
#include "threads.h"
|
#include "threads.h"
|
||||||
#include "rbtree.h"
|
#include "rbtree.h"
|
||||||
@@ -562,24 +565,57 @@ static uintptr_t defered_prot_p = 0;
|
|||||||
static size_t defered_prot_sz = 0;
|
static size_t defered_prot_sz = 0;
|
||||||
static uint32_t defered_prot_prot = 0;
|
static uint32_t defered_prot_prot = 0;
|
||||||
static mem_flag_t defered_prot_flags = MEM_ALLOCATED;
|
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
|
#ifdef TRACE_MEMSTAT
|
||||||
static uint64_t customMalloc_allocated = 0;
|
static uint64_t customMalloc_allocated = 0;
|
||||||
@@ -3095,7 +3131,6 @@ void init_custommem_helper(box64context_t* ctx)
|
|||||||
for(int i=0; i<n_blocks; ++i)
|
for(int i=0; i<n_blocks; ++i)
|
||||||
rb_set(blockstree, (uintptr_t)p_blocks[i].block, (uintptr_t)p_blocks[i].block+p_blocks[i].size, i);
|
rb_set(blockstree, (uintptr_t)p_blocks[i].block, (uintptr_t)p_blocks[i].block+p_blocks[i].size, i);
|
||||||
memprot = rbtree_init("memprot");
|
memprot = rbtree_init("memprot");
|
||||||
sigfillset(&critical_prot);
|
|
||||||
#ifdef DYNAREC
|
#ifdef DYNAREC
|
||||||
if(BOX64ENV(dynarec)) {
|
if(BOX64ENV(dynarec)) {
|
||||||
#ifdef JMPTABL_SHIFT4
|
#ifdef JMPTABL_SHIFT4
|
||||||
|
|||||||
@@ -41,6 +41,17 @@ static uint32_t x86emu_parity_tab[8] =
|
|||||||
0x69969669,
|
0x69969669,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void reset_deferred_signal(x64emu_t* emu)
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
emu->critical_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)
|
static void internalX64Setup(x64emu_t* emu, box64context_t *context, uintptr_t start, uintptr_t stack, int stacksize, int ownstack)
|
||||||
{
|
{
|
||||||
emu->context = context;
|
emu->context = context;
|
||||||
@@ -90,6 +101,7 @@ static void internalX64Setup(x64emu_t* emu, box64context_t *context, uintptr_t s
|
|||||||
emu->mxcsr.x32 = 0x1f80;
|
emu->mxcsr.x32 = 0x1f80;
|
||||||
// want some new jmpbuf for error recovery
|
// want some new jmpbuf for error recovery
|
||||||
emu->flags.need_jmpbuf = 1;
|
emu->flags.need_jmpbuf = 1;
|
||||||
|
reset_deferred_signal(emu);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORTDYN
|
EXPORTDYN
|
||||||
@@ -219,6 +231,7 @@ void CloneEmu(x64emu_t *newemu, const x64emu_t* emu)
|
|||||||
newemu->quit = emu->quit;
|
newemu->quit = emu->quit;
|
||||||
newemu->error = emu->error;
|
newemu->error = emu->error;
|
||||||
newemu->x64emu_parity_tab = emu->x64emu_parity_tab;
|
newemu->x64emu_parity_tab = emu->x64emu_parity_tab;
|
||||||
|
reset_deferred_signal(newemu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CopyEmu(x64emu_t *newemu, const x64emu_t* emu)
|
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->mxcsr = emu->mxcsr;
|
||||||
newemu->quit = emu->quit;
|
newemu->quit = emu->quit;
|
||||||
newemu->error = emu->error;
|
newemu->error = emu->error;
|
||||||
|
reset_deferred_signal(newemu);
|
||||||
}
|
}
|
||||||
|
|
||||||
box64context_t* GetEmuContext(x64emu_t* emu)
|
box64context_t* GetEmuContext(x64emu_t* emu)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "regs.h"
|
#include "regs.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "box64context.h"
|
#include "box64context.h"
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
typedef struct x64_ucontext_s x64_ucontext_t;
|
typedef struct x64_ucontext_s x64_ucontext_t;
|
||||||
#ifdef BOX32
|
#ifdef BOX32
|
||||||
@@ -135,6 +136,13 @@ typedef struct x64emu_s {
|
|||||||
tlsdatasize_t *tlsdata;
|
tlsdatasize_t *tlsdata;
|
||||||
// other informations
|
// other informations
|
||||||
int type; // EMUTYPE_xxx define
|
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
|
#ifdef BOX32
|
||||||
int libc_err; // copy of errno from libc
|
int libc_err; // copy of errno from libc
|
||||||
int libc_herr; // copy of h_errno from libc
|
int libc_herr; // copy of h_errno from libc
|
||||||
|
|||||||
@@ -287,6 +287,7 @@ int AddTLSPartition(box64context_t* context, int tlssize);
|
|||||||
// defined in fact in threads.c
|
// defined in fact in threads.c
|
||||||
void thread_set_emu(x64emu_t* emu);
|
void thread_set_emu(x64emu_t* emu);
|
||||||
void thread_forget_emu();
|
void thread_forget_emu();
|
||||||
|
x64emu_t* thread_get_emu_no_create(void);
|
||||||
x64emu_t* thread_get_emu(void);
|
x64emu_t* thread_get_emu(void);
|
||||||
|
|
||||||
// relock the muxtex that were unlocked
|
// relock the muxtex that were unlocked
|
||||||
|
|||||||
@@ -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);
|
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 init_signal_helper(box64context_t* context);
|
||||||
void fini_signal_helper(void);
|
void fini_signal_helper(void);
|
||||||
|
|
||||||
|
|||||||
@@ -771,6 +771,7 @@ int my_sigactionhandler_oldcode_32(x64emu_t* emu, int32_t sig, int simple, sigin
|
|||||||
#ifdef DYNAREC
|
#ifdef DYNAREC
|
||||||
dynablock_leave_runtime((dynablock_t*)cur_db);
|
dynablock_leave_runtime((dynablock_t*)cur_db);
|
||||||
#endif
|
#endif
|
||||||
|
cancel_deferred_signal_processing(emu);
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
siglongjmp(*emu->jmpbuf, skip);
|
siglongjmp(*emu->jmpbuf, skip);
|
||||||
#else
|
#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)
|
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
|
#ifdef DYNAREC
|
||||||
ucontext_t *p = (ucontext_t *)ucntx;
|
ucontext_t *p = (ucontext_t *)ucntx;
|
||||||
#ifdef ARM64
|
#ifdef ARM64
|
||||||
@@ -835,7 +840,7 @@ void my32_sigactionhandler(int32_t sig, siginfo_t* info, void * ucntx)
|
|||||||
void* db = NULL;
|
void* db = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
my_sigactionhandler_oldcode_32(NULL, sig, 0, info, ucntx, NULL, db);
|
my_sigactionhandler_oldcode_32(emu, sig, 0, info, ucntx, NULL, db);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+78
-1
@@ -258,9 +258,80 @@ x64emu_t* getEmuSignal(x64emu_t* emu, ucontext_t* p, dynablock_t* db)
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
#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);
|
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
|
#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 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();
|
int Locks = unlockMutex();
|
||||||
@@ -520,6 +591,7 @@ int my_sigactionhandler_oldcode_64(x64emu_t* emu, int32_t sig, int simple, sigin
|
|||||||
#ifdef DYNAREC
|
#ifdef DYNAREC
|
||||||
dynablock_leave_runtime((dynablock_t*)cur_db);
|
dynablock_leave_runtime((dynablock_t*)cur_db);
|
||||||
#endif
|
#endif
|
||||||
|
cancel_deferred_signal_processing(emu);
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
siglongjmp(*emu->jmpbuf, skip);
|
siglongjmp(*emu->jmpbuf, skip);
|
||||||
#else
|
#else
|
||||||
@@ -841,6 +913,7 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
|
|||||||
#if defined(RV64) || defined(PPC64LE)
|
#if defined(RV64) || defined(PPC64LE)
|
||||||
emu->xSPSave = emu->old_savedsp;
|
emu->xSPSave = emu->old_savedsp;
|
||||||
#endif
|
#endif
|
||||||
|
cancel_deferred_signal_processing(emu);
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
siglongjmp(*(JUMPBUFF*)emu->jmpbuf, 3);
|
siglongjmp(*(JUMPBUFF*)emu->jmpbuf, 3);
|
||||||
#else
|
#else
|
||||||
@@ -915,6 +988,7 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
|
|||||||
#if defined(RV64) || defined(PPC64LE)
|
#if defined(RV64) || defined(PPC64LE)
|
||||||
emu->xSPSave = emu->old_savedsp;
|
emu->xSPSave = emu->old_savedsp;
|
||||||
#endif
|
#endif
|
||||||
|
cancel_deferred_signal_processing(emu);
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
siglongjmp(*(JUMPBUFF*)emu->jmpbuf, 2);
|
siglongjmp(*(JUMPBUFF*)emu->jmpbuf, 2);
|
||||||
#else
|
#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)
|
void my_sigactionhandler(int32_t sig, siginfo_t* info, void * ucntx)
|
||||||
{
|
{
|
||||||
sig = signal_to_x64(sig);
|
sig = signal_to_x64(sig);
|
||||||
|
x64emu_t* emu = thread_get_emu_no_create();
|
||||||
|
if (defer_signal(emu, sig, info))
|
||||||
|
return;
|
||||||
void* pc = NULL;
|
void* pc = NULL;
|
||||||
#ifdef DYNAREC
|
#ifdef DYNAREC
|
||||||
ucontext_t *p = (ucontext_t *)ucntx;
|
ucontext_t *p = (ucontext_t *)ucntx;
|
||||||
pc = (void*)CONTEXT_PC(p);
|
pc = (void*)CONTEXT_PC(p);
|
||||||
#endif
|
#endif
|
||||||
dynablock_t* db = FindDynablockFromNativeAddress(pc);
|
dynablock_t* db = FindDynablockFromNativeAddress(pc);
|
||||||
x64emu_t* emu = thread_get_emu();
|
if (!emu) emu = thread_get_emu();
|
||||||
uintptr_t x64pc = R_RIP;
|
uintptr_t x64pc = R_RIP;
|
||||||
if(db)
|
if(db)
|
||||||
x64pc = getX64Address(db, (uintptr_t)pc);
|
x64pc = getX64Address(db, (uintptr_t)pc);
|
||||||
|
|||||||
@@ -256,7 +256,13 @@ void thread_set_emu(x64emu_t* emu)
|
|||||||
pthread_setspecific(thread_key, et);
|
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);
|
emuthread_t *et = (emuthread_t*)pthread_getspecific(thread_key);
|
||||||
if(!et) {
|
if(!et) {
|
||||||
|
|||||||
Reference in new issue
Block a user