about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorptitSeb <sebastien.chev@gmail.com>2021-04-11 11:05:36 +0200
committerptitSeb <sebastien.chev@gmail.com>2021-04-11 11:05:36 +0200
commit3b9feeed120af45a2dc346592b328eb0b2e14911 (patch)
tree805803a23c6e2e91fc002b8b8f1b2d9f73a1e298 /src
parent6d3ca2df80b6df2e9aabb73210f9c09fd0df97a1 (diff)
downloadbox64-3b9feeed120af45a2dc346592b328eb0b2e14911.tar.gz
box64-3b9feeed120af45a2dc346592b328eb0b2e14911.zip
Improvement in internal mutex handling on signal, and [DYNAREC] multitasking changes to the JmpTable
Diffstat (limited to 'src')
-rwxr-xr-xsrc/box64context.c84
-rw-r--r--src/custommem.c82
-rwxr-xr-xsrc/include/box64context.h5
-rw-r--r--src/include/custommem.h4
-rwxr-xr-xsrc/include/threads.h3
-rwxr-xr-xsrc/libtools/signals.c26
-rwxr-xr-xsrc/libtools/threads.c29
7 files changed, 184 insertions, 49 deletions
diff --git a/src/box64context.c b/src/box64context.c
index 1fd00750..182a079f 100755
--- a/src/box64context.c
+++ b/src/box64context.c
@@ -68,6 +68,77 @@ void free_tlsdatasize(void* p)
 
 void x64Syscall(x64emu_t *emu);
 
+int unlockMutex()
+{
+    int ret = unlockCustommemMutex();
+    int i;
+    #define GO(A, B)                    \
+        i = checkMutex(&A);             \
+        if(i) {                         \
+            pthread_mutex_unlock(&A);   \
+            ret|=(1<<B);                \
+        }
+
+    GO(my_context->mutex_once, 5)
+    GO(my_context->mutex_once2, 6)
+    GO(my_context->mutex_trace, 7)
+    #ifdef DYNAREC
+    GO(my_context->mutex_dyndump, 8)
+    #else
+    GO(my_context->mutex_lock, 8)
+    #endif
+    GO(my_context->mutex_tls, 9)
+    GO(my_context->mutex_thread, 10)
+    #undef GO
+
+    return ret;
+}
+
+void relockMutex(int locks)
+{
+    relockCustommemMutex(locks);
+    #define GO(A, B)                    \
+        if(locks&(1<<B))                \
+            pthread_mutex_lock(&A);     \
+
+    GO(my_context->mutex_once, 5)
+    GO(my_context->mutex_once2, 6)
+    GO(my_context->mutex_trace, 7)
+    #ifdef DYNAREC
+    GO(my_context->mutex_dyndump, 8)
+    #else
+    GO(my_context->mutex_lock, 8)
+    #endif
+    GO(my_context->mutex_tls, 9)
+    GO(my_context->mutex_thread, 10)
+    #undef GO
+}
+
+static void init_mutexes(box64context_t* context)
+{
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+    pthread_mutex_init(&context->mutex_once, &attr);
+    pthread_mutex_init(&context->mutex_once2, &attr);
+    pthread_mutex_init(&context->mutex_trace, &attr);
+#ifndef DYNAREC
+    pthread_mutex_init(&context->mutex_lock, &attr);
+#else
+    pthread_mutex_init(&context->mutex_dyndump, &attr);
+#endif
+    pthread_mutex_init(&context->mutex_tls, &attr);
+    pthread_mutex_init(&context->mutex_thread, &attr);
+
+    pthread_mutexattr_destroy(&attr);
+}
+
+static void atfork_child_box64context(void)
+{
+    // (re)init mutex if it was lock before the fork
+    init_mutexes(my_context);
+}
+
 EXPORTDYN
 box64context_t *NewBox64Context(int argc)
 {
@@ -96,16 +167,9 @@ box64context_t *NewBox64Context(int argc)
     context->argc = argc;
     context->argv = (char**)calloc(context->argc+1, sizeof(char*));
 
-    pthread_mutex_init(&context->mutex_once, NULL);
-    pthread_mutex_init(&context->mutex_once2, NULL);
-    pthread_mutex_init(&context->mutex_trace, NULL);
-#ifndef DYNAREC
-    pthread_mutex_init(&context->mutex_lock, NULL);
-#else
-    pthread_mutex_init(&context->mutex_dyndump, NULL);
-#endif
-    pthread_mutex_init(&context->mutex_tls, NULL);
-    pthread_mutex_init(&context->mutex_thread, NULL);
+    init_mutexes(context);
+    pthread_atfork(NULL, NULL, atfork_child_box64context);
+
     pthread_key_create(&context->tlskey, free_tlsdatasize);
 
 
diff --git a/src/custommem.c b/src/custommem.c
index 634b849b..f4c0102d 100644
--- a/src/custommem.c
+++ b/src/custommem.c
@@ -21,6 +21,7 @@
 #include <sys/mman.h>
 #include "custommem.h"
 #include "khash.h"
+#include "threads.h"
 #ifdef DYNAREC
 #include "dynablock.h"
 #include "dynarec/arm64_lock.h"
@@ -55,7 +56,7 @@ typedef struct blocklist_s {
 
 #define MMAPSIZE (256*1024)      // allocate 256kb sized blocks
 
-static pthread_mutex_t     mutex_blocks = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t     mutex_blocks;
 static int                 n_blocks = 0;       // number of blocks for custom malloc
 static blocklist_t*        p_blocks = NULL;    // actual blocks for custom malloc
 
@@ -570,23 +571,25 @@ void addJumpTableIfDefault64(void* addr, void* jmp)
         uintptr_t*** tbl = (uintptr_t***)malloc((1<<JMPTABL_SHIFT)*sizeof(uintptr_t**));
         for(int i=0; i<(1<<JMPTABL_SHIFT); ++i)
             tbl[i] = box64_jmptbldefault1;
-        box64_jmptbl3[idx3] = tbl;
+        if(arm64_lock_storeifref(&box64_jmptbl3[idx3], tbl, box64_jmptbldefault2)!=tbl)
+            free(tbl);
     }
     if(box64_jmptbl3[idx3][idx2] == box64_jmptbldefault1) {
         uintptr_t** tbl = (uintptr_t**)malloc((1<<JMPTABL_SHIFT)*sizeof(uintptr_t*));
         for(int i=0; i<(1<<JMPTABL_SHIFT); ++i)
             tbl[i] = box64_jmptbldefault0;
-        box64_jmptbl3[idx3][idx2] = tbl;
+        if(arm64_lock_storeifref(&box64_jmptbl3[idx3][idx2], tbl, box64_jmptbldefault1)!=tbl)
+            free(tbl);
     }
     if(box64_jmptbl3[idx3][idx2][idx1] == box64_jmptbldefault0) {
         uintptr_t* tbl = (uintptr_t*)malloc((1<<JMPTABL_SHIFT)*sizeof(uintptr_t));
         for(int i=0; i<(1<<JMPTABL_SHIFT); ++i)
             tbl[i] = (uintptr_t)arm64_next;
-        box64_jmptbl3[idx3][idx2][idx1] = tbl;
+        if(arm64_lock_storeifref(&box64_jmptbl3[idx3][idx2][idx1], tbl, box64_jmptbldefault0)!=tbl)
+            free(tbl);
     }
 
-    if(box64_jmptbl3[idx3][idx2][idx1][idx0]==(uintptr_t)arm64_next)
-        box64_jmptbl3[idx3][idx2][idx1][idx0] = (uintptr_t)jmp;
+    arm64_lock_storeifref(&box64_jmptbl3[idx3][idx2][idx1][idx0], jmp, arm64_next);
 }
 void setJumpTableDefault64(void* addr)
 {
@@ -638,19 +641,22 @@ uintptr_t getJumpTableAddress64(uintptr_t addr)
         uintptr_t*** tbl = (uintptr_t***)malloc((1<<JMPTABL_SHIFT)*sizeof(uintptr_t**));
         for(int i=0; i<(1<<JMPTABL_SHIFT); ++i)
             tbl[i] = box64_jmptbldefault1;
-        box64_jmptbl3[idx3] = tbl;
+        if(arm64_lock_storeifref(&box64_jmptbl3[idx3], tbl, box64_jmptbldefault2)!=tbl)
+            free(tbl);
     }
     if(box64_jmptbl3[idx3][idx2] == box64_jmptbldefault1) {
         uintptr_t** tbl = (uintptr_t**)malloc((1<<JMPTABL_SHIFT)*sizeof(uintptr_t*));
         for(int i=0; i<(1<<JMPTABL_SHIFT); ++i)
             tbl[i] = box64_jmptbldefault0;
-        box64_jmptbl3[idx3][idx2] = tbl;
+        if(arm64_lock_storeifref(&box64_jmptbl3[idx3][idx2], tbl, box64_jmptbldefault1)!=tbl)
+            free(tbl);
     }
     if(box64_jmptbl3[idx3][idx2][idx1] == box64_jmptbldefault0) {
         uintptr_t* tbl = (uintptr_t*)malloc((1<<JMPTABL_SHIFT)*sizeof(uintptr_t));
         for(int i=0; i<(1<<JMPTABL_SHIFT); ++i)
             tbl[i] = (uintptr_t)arm64_next;
-        box64_jmptbl3[idx3][idx2][idx1] = tbl;
+        if(arm64_lock_storeifref(&box64_jmptbl3[idx3][idx2][idx1], tbl, box64_jmptbldefault0)!=tbl)
+            free(tbl);
     }
 
     return (uintptr_t)&box64_jmptbl3[idx3][idx2][idx1][idx0];
@@ -882,14 +888,57 @@ void* findBlockNearHint(void* hint, size_t size)
 }
 #undef LOWEST
 
-static void atfork_child_custommem(void)
+int unlockCustommemMutex()
 {
-    // unlock mutex if it was lock before the fork
-    pthread_mutex_unlock(&mutex_blocks);
-    pthread_mutex_unlock(&mutex_prot);
+    int ret = 0;
+    int i = 0;
+    #define GO(A, B)                    \
+        i = checkMutex(&A);             \
+        if(i) {                         \
+            pthread_mutex_unlock(&A);   \
+            ret|=(1<<B);                \
+        }
+    GO(mutex_blocks, 0)
+    GO(mutex_prot, 1)
+    #ifdef DYNAREC
+    GO(mutex_mmap, 2)
+    #endif
+    #undef GO
+    return ret;
+}
+
+void relockCustommemMutex(int locks)
+{
+    #define GO(A, B)                    \
+        if(locks&(1<<B))                \
+            pthread_mutex_lock(&A);     \
+
+    GO(mutex_blocks, 0)
+    GO(mutex_prot, 1)
+    #ifdef DYNAREC
+    GO(mutex_mmap, 2)
+    #endif
+    #undef GO
+}
+
+static void init_mutexes(void)
+{
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+    pthread_mutex_init(&mutex_blocks, &attr);
+    pthread_mutex_init(&mutex_prot, &attr);
 #ifdef DYNAREC
-    pthread_mutex_unlock(&mutex_mmap);
+    pthread_mutex_init(&mutex_mmap, &attr);
 #endif
+
+    pthread_mutexattr_destroy(&attr);
+}
+
+static void atfork_child_custommem(void)
+{
+    // (re)init mutex if it was lock before the fork
+    init_mutexes();
 }
 
 void init_custommem_helper(box64context_t* ctx)
@@ -898,9 +947,8 @@ void init_custommem_helper(box64context_t* ctx)
         return;
     inited = 1;
     memprot = kh_init(memprot);
-    pthread_mutex_init(&mutex_prot, NULL);
+    init_mutexes();
 #ifdef DYNAREC
-    pthread_mutex_init(&mutex_mmap, NULL);
 #ifdef ARM64
     if(box64_dynarec)
         for(int i=0; i<(1<<JMPTABL_SHIFT); ++i) {
@@ -988,5 +1036,5 @@ void fini_custommem_helper(box64context_t *ctx)
         #endif
     free(p_blocks);
     pthread_mutex_destroy(&mutex_prot);
-    //pthread_mutex_destroy(&mutex_blocks);
+    pthread_mutex_destroy(&mutex_blocks);
 }
diff --git a/src/include/box64context.h b/src/include/box64context.h
index ffcacc36..4d48c816 100755
--- a/src/include/box64context.h
+++ b/src/include/box64context.h
@@ -200,4 +200,9 @@ int AddTLSPartition(box64context_t* context, int tlssize);
 void thread_set_emu(x64emu_t* emu);
 x64emu_t* thread_get_emu();
 
+// unlock mutex that are locked by current thread (for signal handling). Return a mask of unlock mutex
+int unlockMutex();
+// relock the muxtex that were unlocked
+void relockMutex(int locks);
+
 #endif //__BOX64CONTEXT_H_
diff --git a/src/include/custommem.h b/src/include/custommem.h
index 15f38596..1b86d251 100644
--- a/src/include/custommem.h
+++ b/src/include/custommem.h
@@ -52,6 +52,10 @@ void unlockDB();
 void* find32bitBlock(size_t size);
 void* findBlockNearHint(void* hint, size_t size);
 
+// unlock mutex that are locked by current thread (for signal handling). Return a mask of unlock mutex
+int unlockCustommemMutex();
+// relock the muxtex that were unlocked
+void relockCustommemMutex(int locks);
 
 void init_custommem_helper(box64context_t* ctx);
 void fini_custommem_helper(box64context_t* ctx);
diff --git a/src/include/threads.h b/src/include/threads.h
index fa9d64ab..19d3c9bc 100755
--- a/src/include/threads.h
+++ b/src/include/threads.h
@@ -20,4 +20,7 @@ void fini_pthread_helper(box64context_t* context);
 // prepare an "emuthread structure" in pet and return address of function pointer for a "thread creation routine"
 void* my_prepare_thread(x64emu_t *emu, void* f, void* arg, int ssize, void** pet);
 
+//check if a mutex is locked by current thread (works only for PTHREAD_MUTEX_ERRORCHECK typed mutex)
+int checkMutex(void* m);
+
 #endif //_THREADS_H_
\ No newline at end of file
diff --git a/src/libtools/signals.c b/src/libtools/signals.c
index a0126748..5c66522e 100755
--- a/src/libtools/signals.c
+++ b/src/libtools/signals.c
@@ -434,8 +434,8 @@ uintptr_t getX64Address(dynablock_t* db, uintptr_t arm_addr)
 
 void my_sigactionhandler_oldcode(int32_t sig, siginfo_t* info, void * ucntx, int* old_code, void* cur_db)
 {
-    // need to create some x64_ucontext????
-    pthread_mutex_unlock(&my_context->mutex_trace);   // just in case
+    int Locks = unlockMutex();
+
     printf_log(LOG_DEBUG, "Sigactionhanlder for signal #%d called (jump to %p/%s)\n", sig, (void*)my_context->signals[sig], GetNativeName((void*)my_context->signals[sig]));
 
     uintptr_t restorer = my_context->restorer[sig];
@@ -643,6 +643,7 @@ void my_sigactionhandler_oldcode(int32_t sig, siginfo_t* info, void * ucntx, int
                 *old_code = -1;    // re-init the value to allow another segfault at the same place
             if(used_stack)  // release stack
                 new_ss->ss_flags = 0;
+            relockMutex(Locks);
             longjmp(ejb->jmpbuf, 1);
         }
         printf_log(LOG_INFO, "Warning, context has been changed in Sigactionhanlder%s\n", (sigcontext->uc_mcontext.gregs[X64_RIP]!=sigcontext_copy.uc_mcontext.gregs[X64_RIP])?" (EIP changed)":"");
@@ -681,12 +682,15 @@ void my_sigactionhandler_oldcode(int32_t sig, siginfo_t* info, void * ucntx, int
     #undef GO
 
     printf_log(LOG_DEBUG, "Sigactionhanlder main function returned (exit=%d, restorer=%p)\n", exits, (void*)restorer);
-    if(exits)
+    if(exits) {
+        relockMutex(Locks);
         exit(ret);
+    }
     if(restorer)
         RunFunctionHandler(&exits, 0, restorer, 0);
     if(used_stack)  // release stack
         new_ss->ss_flags = 0;
+    relockMutex(Locks);
 }
 
 void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
@@ -706,6 +710,7 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
     void * pc = NULL;    // unknow arch...
     #warning Unhandled architecture
 #endif
+    int Locks = unlockMutex();
     uint32_t prot = getProtection((uintptr_t)addr);
 #ifdef DYNAREC
     dynablock_t* db = NULL;
@@ -742,12 +747,17 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
                 ejb->emu->ip.q[0] = getX64Address(db, (uintptr_t)pc);
                 ejb->emu->eflags.x64 = p->uc_mcontext.regs[26];
                 dynarec_log(LOG_DEBUG, "Auto-SMC detected, getting out of current Dynablock!\n");
+                relockMutex(Locks);
                 longjmp(ejb->jmpbuf, 2);
             }
             dynarec_log(LOG_INFO, "Warning, Auto-SMC (%p for db %p/%p) detected, but jmpbuffer not ready!\n", (void*)addr, db, (void*)db->x64_addr);
         }
         // done
-        if(prot&PROT_WRITE) return; // if there is no write permission, don't return and continue to program signal handling
+        if(prot&PROT_WRITE) {
+            // if there is no write permission, don't return and continue to program signal handling
+            relockMutex(Locks);
+            return;
+        }
     } else if ((sig==SIGSEGV) && (addr) && (info->si_code == SEGV_ACCERR) && (prot&(PROT_READ|PROT_WRITE))) {
         db = FindDynablockFromNativeAddress(pc);
         db_searched = 1;
@@ -757,6 +767,7 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
         if(addr && pc && db) {
             // probably a glitch due to intensive multitask...
             dynarec_log(/*LOG_DEBUG*/LOG_INFO, "SIGSEGV with Access error on %p for %p , db=%p, retrying\n", pc, addr, db);
+            relockMutex(Locks);
             return; // try again
         }
     }
@@ -771,6 +782,10 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
         printf_log(log_minimum, "%04d|Double %s (code=%d, pc=%p, addr=%p)!\n", GetTID(), signame, old_code, old_pc, old_addr);
 exit(-1);
     } else {
+        if(sig==SIGSEGV && info->si_code==2 && ((prot&~PROT_CUSTOM)==5 || (prot&~PROT_CUSTOM)==7)) {
+            relockMutex(Locks);
+            return; // that's probably just a multi-task glitch, like seen in terraria
+        }
 #ifdef DYNAREC
         if(!db_searched)
             db = FindDynablockFromNativeAddress(pc);
@@ -840,8 +855,7 @@ exit(-1);
         else
             printf_log(log_minimum, "\n");
     }
-    if(sig==SIGSEGV && info->si_code==2 && ((prot&~PROT_CUSTOM)==5 || (prot&~PROT_CUSTOM)==7))
-        return; // that's probably just a multi-task glitch, like seen in terraria
+    relockMutex(Locks);
     if(my_context->signals[sig] && my_context->signals[sig]!=1) {
         if(my_context->is_sigaction[sig])
             my_sigactionhandler_oldcode(sig, info, ucntx, &old_code, db);
diff --git a/src/libtools/threads.c b/src/libtools/threads.c
index 82f558f4..5e969807 100755
--- a/src/libtools/threads.c
+++ b/src/libtools/threads.c
@@ -922,21 +922,6 @@ emu_jmpbuf_t* GetJmpBuf()
 	return ejb;
 }
 
-static void atfork_child(void)
-{
-	//unlock all potential mutex, this is a new fork!
-	pthread_mutex_unlock(&my_context->mutex_once);
-	pthread_mutex_unlock(&my_context->mutex_once2);
-	pthread_mutex_unlock(&my_context->mutex_trace);
-	#ifndef DYNAREC
-	pthread_mutex_unlock(&my_context->mutex_lock);
-	#else
-	pthread_mutex_unlock(&my_context->mutex_dyndump);
-	#endif
-	pthread_mutex_unlock(&my_context->mutex_tls);
-	pthread_mutex_unlock(&my_context->mutex_thread);
-}
-
 void init_pthread_helper()
 {
 	InitCancelThread();
@@ -949,7 +934,6 @@ void init_pthread_helper()
 	unaligned_mutex = kh_init(mutex);
 	#endif
 #endif
-	pthread_atfork(NULL, NULL, atfork_child);
 }
 
 void fini_pthread_helper(box64context_t* context)
@@ -986,3 +970,16 @@ void fini_pthread_helper(box64context_t* context)
 		pthread_setspecific(thread_key, NULL);
 	}
 }
+
+int checkMutex(void* m)
+{
+	pthread_mutex_t* mutex = (pthread_mutex_t*)m;
+	int ret = pthread_mutex_trylock(mutex);
+	if(ret==0) {
+		pthread_mutex_unlock(mutex);
+		return 0;
+	}
+	if(ret==EDEADLK)
+		return 1;
+	return 0;
+}