about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorptitSeb <sebastien.chev@gmail.com>2021-11-18 14:21:16 +0100
committerptitSeb <sebastien.chev@gmail.com>2021-11-18 14:21:16 +0100
commite7290d78d5047662c49a8558d1709ed912346ff7 (patch)
tree10ecdc9b7b9a994087980cf5e65cfefdb48602db /src
parent4f0e98ade5d37b8647cb5d39b6e5a3aded55fa2f (diff)
downloadbox64-e7290d78d5047662c49a8558d1709ed912346ff7.tar.gz
box64-e7290d78d5047662c49a8558d1709ed912346ff7.zip
[DYNAREC] Improved JIT handling, and added a HotPage detection to temporarily disable Dynarec when write occurs on the same page of some Dynablocks (help speedup some C#/Unity3D programs)
Diffstat (limited to 'src')
-rw-r--r--src/custommem.c27
-rwxr-xr-xsrc/dynarec/dynablock.c147
-rwxr-xr-xsrc/dynarec/dynablock_private.h3
-rwxr-xr-xsrc/dynarec/dynarec.c16
-rwxr-xr-xsrc/dynarec/dynarec_arm64.c12
-rwxr-xr-xsrc/dynarec/dynarec_arm64_pass.c3
-rwxr-xr-xsrc/include/dynablock.h2
-rwxr-xr-xsrc/libtools/signals.c42
-rwxr-xr-xsrc/wrapped/wrappedlibdl.c2
9 files changed, 202 insertions, 52 deletions
diff --git a/src/custommem.c b/src/custommem.c
index 3718aebe..ff777bc4 100644
--- a/src/custommem.c
+++ b/src/custommem.c
@@ -769,11 +769,12 @@ void protectDB(uintptr_t addr, uintptr_t size)
         }
     for (uintptr_t i=idx; i<=end; ++i) {
         uint32_t prot = memprot[i>>16][i&0xffff];
-        if(!(prot&PROT_DYNAREC)) {
-            if(!prot)
-                prot = PROT_READ | PROT_WRITE;    // comes from malloc & co, so should not be able to execute
+        if(!prot)
+            prot = PROT_READ | PROT_WRITE | PROT_EXEC;      // comes from malloc & co, so should not be able to execute
+        if((prot&PROT_WRITE)) {
+            prot&=~PROT_WRITE;
+            mprotect((void*)(i<<MEMPROT_SHIFT), 1<<MEMPROT_SHIFT, prot);
             memprot[i>>16][i&0xffff] = prot|PROT_DYNAREC;   // need to use atomic exchange?
-            mprotect((void*)(i<<MEMPROT_SHIFT), 1<<MEMPROT_SHIFT, prot&~PROT_WRITE);
         }
     }
     pthread_mutex_unlock(&mutex_prot);
@@ -802,9 +803,11 @@ void unprotectDB(uintptr_t addr, size_t size)
     for (uintptr_t i=idx; i<=end; ++i) {
         uint32_t prot = memprot[i>>16][i&0xffff];
         if(prot&PROT_DYNAREC) {
-            memprot[i>>16][i&0xffff] = prot&~PROT_DYNAREC;  // need to use atomic exchange?
-            mprotect((void*)(i<<MEMPROT_SHIFT), 1<<MEMPROT_SHIFT, prot&~PROT_DYNAREC);
+            prot&=~PROT_DYNAREC;
+            prot|=PROT_WRITE;
             cleanDBFromAddressRange((i<<MEMPROT_SHIFT), 1<<MEMPROT_SHIFT, 0);
+            mprotect((void*)(i<<MEMPROT_SHIFT), 1<<MEMPROT_SHIFT, prot);
+            memprot[i>>16][i&0xffff] = prot;  // need to use atomic exchange?
         }
     }
     pthread_mutex_unlock(&mutex_prot);
@@ -812,19 +815,23 @@ void unprotectDB(uintptr_t addr, size_t size)
 
 int isprotectedDB(uintptr_t addr, size_t size)
 {
-    dynarec_log(LOG_DEBUG, "isprotectedDB %p -> %p\n", (void*)addr, (void*)(addr+size-1));
+    dynarec_log(LOG_DEBUG, "isprotectedDB %p -> %p => ", (void*)addr, (void*)(addr+size-1));
     uintptr_t idx = (addr>>MEMPROT_SHIFT);
     uintptr_t end = ((addr+size-1LL)>>MEMPROT_SHIFT);
     if(end>=(1LL<<(20+16)))
-        end = (1LL<<(20+16))-1;
-    if(end<idx) // memory addresses higher than 48bits are not tracked
+        end = (1LL<<(20+16))-1LL;
+    if(end<idx) { // memory addresses higher than 48bits are not tracked
+        dynarec_log(LOG_DEBUG, "00\n");
         return 0;
+    }
     for (uintptr_t i=idx; i<=end; ++i) {
         uint32_t prot = memprot[i>>16][i&0xffff];
-        if(!(prot&PROT_DYNAREC)) {
+        if((prot&PROT_WRITE)) {
+            dynarec_log(LOG_DEBUG, "0\n");
             return 0;
         }
     }
+    dynarec_log(LOG_DEBUG, "1\n");
     return 1;
 }
 
diff --git a/src/dynarec/dynablock.c b/src/dynarec/dynablock.c
index 8a1a37e2..abfd688f 100755
--- a/src/dynarec/dynablock.c
+++ b/src/dynarec/dynablock.c
@@ -48,6 +48,8 @@ dynablocklist_t* NewDynablockList(uintptr_t text, int textsz, int direct)
     dynablocklist_t* ret = (dynablocklist_t*)calloc(1, sizeof(dynablocklist_t));
     ret->text = text;
     ret->textsz = textsz;
+    ret->minstart = text;
+    ret->maxend = text+textsz-1;
     if(direct && textsz) {
         ret->direct = (dynablock_t**)calloc(textsz, sizeof(dynablock_t*));
         if(!ret->direct) {printf_log(LOG_NONE, "Warning, fail to create direct block for dynablock @%p\n", (void*)text);}
@@ -61,7 +63,7 @@ void FreeDynablock(dynablock_t* db, int need_lock)
     if(db) {
         if(db->gone)
             return; // already in the process of deletion!
-        dynarec_log(LOG_DEBUG, "FreeDynablock(%p), db->block=%p x64=%p:%p parent=%p, father=%p, with %d son(s) already gone=%d\n", db, db->block, db->x64_addr, db->x64_addr+db->x64_size, db->parent, db->father, db->sons_size, db->gone);
+        dynarec_log(LOG_DEBUG, "FreeDynablock(%p), db->block=%p x64=%p:%p parent=%p, father=%p, with %d son(s) already gone=%d\n", db, db->block, db->x64_addr, db->x64_addr+db->x64_size-1, db->parent, db->father, db->sons_size, db->gone);
         if(need_lock)
             pthread_mutex_lock(&my_context->mutex_dyndump);
         db->done = 0;
@@ -121,6 +123,7 @@ void MarkDynablock(dynablock_t* db)
             db = db->father;    // mark only father
         if(db->need_test)
             return; // already done
+        dynarec_log(LOG_DEBUG, "MarkDynablock %p with %d son(s) %p-%p\n", db, db->sons_size, db->x64_addr, db->x64_addr+db->x64_size-1);
         db->need_test = 1;
         setJumpTableDefault64(db->x64_addr);
         for(int i=0; i<db->sons_size; ++i)
@@ -150,24 +153,19 @@ int IntervalIntersects(uintptr_t start1, uintptr_t end1, uintptr_t start2, uintp
 
 void MarkDirectDynablock(dynablocklist_t* dynablocks, uintptr_t addr, uintptr_t size)
 {
+    // Mark will try to find *any* blocks that intersect the range to mark
     if(!dynablocks)
         return;
     if(!dynablocks->direct)
         return;
     uintptr_t startdb = dynablocks->text;
-    uintptr_t enddb = startdb + dynablocks->textsz -1;
-    uintptr_t start = addr;
-    uintptr_t end = addr+size-1;
-    if(start<startdb)
-        start = startdb;
-    if(end>enddb)
-        end = enddb;
+    uintptr_t sizedb = dynablocks->textsz;
     dynablock_t *db;
-    if(end>startdb && start<enddb)
-        for(uintptr_t i = start; i<end; ++i)
-            if((db=dynablocks->direct[i-startdb]))
-                if(IntervalIntersects((uintptr_t)db->x64_addr, (uintptr_t)db->x64_addr+db->x64_size-1, addr, addr+size+1))
-                    MarkDynablock(db);
+    dynarec_log(LOG_DEBUG, "MarkDirectDynablock %p-%p .. startdb=%p, sizedb=%p\n", (void*)addr, (void*)addr+size-1, (void*)startdb, (void*)sizedb);
+    for(uintptr_t i = 0; i<sizedb; ++i)
+        if((db=dynablocks->direct[i]))
+            if(IntervalIntersects((uintptr_t)db->x64_addr, (uintptr_t)db->x64_addr+db->x64_size-1, addr, addr+size+1))
+                MarkDynablock(db);
 }
 
 int FreeRangeDynablock(dynablocklist_t* dynablocks, uintptr_t addr, uintptr_t size)
@@ -218,13 +216,14 @@ void MarkRangeDynablock(dynablocklist_t* dynablocks, uintptr_t addr, uintptr_t s
 {
     if(!dynablocks)
         return;
+    dynarec_log(LOG_DEBUG, "MarkRangeDynablock %p-%p\n", (void*)addr, (void*)addr+size-1);
     if(dynablocks->direct) {
-        uintptr_t new_addr = addr - dynablocks->maxsz;
-        uintptr_t new_size = size + dynablocks->maxsz;
-        MarkDirectDynablock(dynablocks, new_addr, new_size);
+        uintptr_t new_addr = dynablocks->minstart;
+        uintptr_t new_size = dynablocks->maxend - new_addr + 1;
+        MarkDirectDynablock(dynablocks, addr, size);
         // the blocks check before
         for(unsigned idx=(new_addr)>>DYNAMAP_SHIFT; idx<(addr>>DYNAMAP_SHIFT); ++idx)
-            MarkDirectDynablock(getDB(idx), new_addr, new_size);
+            MarkDirectDynablock(getDB(idx), addr, size);
     }
 }
 
@@ -364,17 +363,18 @@ static dynablock_t* internalDBGetBlock(x64emu_t* emu, uintptr_t addr, uintptr_t
     }
     // check size
     if(block && block->x64_size) {
+        if(dynablocks->minstart>addr)
+            dynablocks->minstart = addr;
         int blocksz = block->x64_size;
-        if(dynablocks->maxsz<blocksz) {
-            dynablocks->maxsz = blocksz;
-            for(unsigned idx=(addr>>DYNAMAP_SHIFT)+1; idx<=((addr+blocksz)>>DYNAMAP_SHIFT); ++idx) {
+        if(dynablocks->maxend<addr+blocksz) {
+            dynablocks->maxend = addr+blocksz;
+            for(unsigned idx=(addr>>DYNAMAP_SHIFT)+1; idx<=((addr+blocksz-1)>>DYNAMAP_SHIFT); ++idx) {
                 dynablocklist_t* dblist;
                 if((dblist = getDB(idx)))
-                    if(dblist->maxsz<blocksz)
-                        dblist->maxsz = blocksz;
+                    if(dblist->minstart>addr)
+                        dblist->minstart = addr;
             }
         }
-        protectDB((uintptr_t)block->x64_addr, block->x64_size);
         // fill-in jumptable
         addJumpTableIfDefault64(block->x64_addr, block->block);
         for(int i=0; i<block->sons_size; ++i) {
@@ -384,24 +384,116 @@ static dynablock_t* internalDBGetBlock(x64emu_t* emu, uintptr_t addr, uintptr_t
         block->done = 1;
     }
 
-    dynarec_log(LOG_DEBUG, " --- DynaRec Block %s @%p:%p (%p, 0x%x bytes, with %d son(s))\n", created?"created":"recycled", (void*)addr, (void*)(addr+((block)?block->x64_size:0)), (block)?block->block:0, (block)?block->size:0, (block)?block->sons_size:0);
+    dynarec_log(LOG_DEBUG, "%04d| --- DynaRec Block %s @%p:%p (%p, 0x%x bytes, with %d son(s))\n", GetTID(), created?"created":"recycled", (void*)addr, (void*)(addr+((block)?block->x64_size:1)-1), (block)?block->block:0, (block)?block->size:0, (block)?block->sons_size:0);
 
     return block;
 }
 
+#define MAX_HOTPAGE 64
+#define HOTPAGE_STEP 64
+static int volatile hotpage_count[MAX_HOTPAGE] = {0};
+static uintptr_t volatile hotpage[MAX_HOTPAGE] = {0};
+static uintptr_t volatile hotpage_size[MAX_HOTPAGE] = {0};
+static volatile int hotpages = 0;
+
+int IsInHotPage(uintptr_t addr) {
+    if(!hotpages)
+        return 0;
+    for(int i=0; i<MAX_HOTPAGE; ++i) {
+        if((hotpage_count[i]>0) && (addr>=hotpage[i]) && (addr<hotpage[i]+0x1000*(hotpage_size[i]+1))) {
+            --hotpage_count[i];
+            if(!hotpage_count[i]) {
+                --hotpages;
+                hotpage_size[i] = 0;
+                dynarec_log(LOG_DEBUG, "End of Hotpage %p\n", (void*)hotpage[i]);
+            }
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int AreaInHotPage(uintptr_t start, uintptr_t end) {
+    if(!hotpages)
+        return 0;
+    for(int i=0; i<MAX_HOTPAGE; ++i) {
+        if(hotpage_count[i]>0)
+            if(IntervalIntersects(start, end, hotpage[i], hotpage[i]+0x1000*(hotpage_size[i]+1)-1)) {
+                --hotpage_count[i];
+                if(!hotpage_count[i]) {
+                    --hotpages;
+                    hotpage_size[i] = 0;
+                    dynarec_log(LOG_DEBUG, "End of Hotpage %p\n", (void*)hotpage[i]);
+                }
+                return 1;
+        }
+    }
+    return 0;
+}
+
+void AddHotPage(uintptr_t addr) {
+    addr&=~0xfff;
+    // look for same address
+    for(int i=0; i<MAX_HOTPAGE; ++i) {
+        if(addr>=hotpage[i] && addr<hotpage[i]+0x1000*(hotpage_size[i]+1)) {
+            if(!hotpage_count[i])
+                ++hotpages;
+            hotpage_count[i] = HOTPAGE_STEP;
+            return;
+        }
+        if(addr==hotpage[i]+0x1000*(hotpage_size[i]+1)) {
+            ++hotpage_size[i];
+            hotpage_count[i] = HOTPAGE_STEP;
+            return;
+        }
+        if(addr+0x1000==hotpage[i]) {
+            ++hotpage_size[i];
+            hotpage[i] = addr;
+            hotpage_count[i] = HOTPAGE_STEP;
+            return;
+        }
+    }
+    // look for empty spot / minium
+    int mincnt = hotpage_count[0];
+    int minidx = 0;
+    for(int i=1; i<MAX_HOTPAGE; ++i)
+        if(hotpage_count[i]<mincnt) {
+            mincnt = hotpage_count[i];
+            minidx = i;
+        }
+    if(hotpage_count[minidx]) {
+        dynarec_log(LOG_NONE, "Warning, not enough Hotpage, replacing %p(%p/%d) with %p\n", (void*)hotpage[minidx], (void*)(0x1000*(hotpage_size[minidx]+1)), hotpage_count[minidx], (void*)addr);
+        hotpage_size[minidx] = 0;
+    } else
+        ++hotpages;
+    hotpage[minidx] = addr;
+    hotpage_count[minidx] = HOTPAGE_STEP;
+}
+
 dynablock_t* DBGetBlock(x64emu_t* emu, uintptr_t addr, int create, dynablock_t** current)
 {
     dynablock_t *db = internalDBGetBlock(emu, addr, addr, create, *current, 1);
     if(db && db->done && db->block && (db->need_test || (db->father && db->father->need_test))) {
-        if(pthread_mutex_trylock(&my_context->mutex_dyndump))
+        if(pthread_mutex_trylock(&my_context->mutex_dyndump)) {
+            dynarec_log(LOG_DEBUG, "mutex_dyndump not available when trying to validate block %p from %p:%p (hash:%X) with %d son(s) for %p\n", db, db->x64_addr, db->x64_addr+db->x64_size-1, db->hash, db->sons_size, (void*)addr);
             return NULL;
+        }
         dynablock_t *father = db->father?db->father:db;
+        if(AreaInHotPage((uintptr_t)father->x64_addr, (uintptr_t)father->x64_addr + father->x64_size - 1)) {
+            dynarec_log(LOG_DEBUG, "Not running block %p from %p:%p with %d son(s) for %p because it's in a hotpage\n", father, father->x64_addr, father->x64_addr+father->x64_size-1, father->sons_size, (void*)addr);
+            pthread_mutex_unlock(&my_context->mutex_dyndump);
+            return NULL;
+        }
         uint32_t hash = X31_hash_code(father->x64_addr, father->x64_size);
         if(hash!=father->hash) {
             father->done = 0;   // invalidating the block
-            dynarec_log(LOG_DEBUG, "Invalidating block %p from %p:%p (hash:%X/%X) with %d son(s) for %p\n", father, father->x64_addr, father->x64_addr+father->x64_size, hash, father->hash, father->sons_size, (void*)addr);
+            dynarec_log(LOG_DEBUG, "Invalidating block %p from %p:%p (hash:%X/%X) with %d son(s) for %p\n", father, father->x64_addr, father->x64_addr+father->x64_size-1, hash, father->hash, father->sons_size, (void*)addr);
             // no more current if it gets invalidated too
-            if(*current && father->x64_addr>=(*current)->x64_addr && (father->x64_addr+father->x64_size)<(*current)->x64_addr)
+            if(*current && IntervalIntersects(
+             (uintptr_t)father->x64_addr, 
+             (uintptr_t)father->x64_addr+father->x64_size-1, 
+             (uintptr_t)(*current)->x64_addr, 
+             (uintptr_t)(*current)->x64_addr+(*current)->x64_size-1))
                 *current = NULL;
             // Free father, it's now invalid!
             FreeDynablock(father, 0);
@@ -409,6 +501,7 @@ dynablock_t* DBGetBlock(x64emu_t* emu, uintptr_t addr, int create, dynablock_t**
             db = internalDBGetBlock(emu, addr, addr, create, *current, 0);
         } else {
             father->need_test = 0;
+            dynarec_log(LOG_DEBUG, "Validating block %p from %p:%p (hash:%X) with %d son(s) for %p\n", father, father->x64_addr, father->x64_addr+father->x64_size-1, father->hash, father->sons_size, (void*)addr);
             protectDB((uintptr_t)father->x64_addr, father->x64_size);
             // fill back jumptable
             addJumpTableIfDefault64(father->x64_addr, father->block);
diff --git a/src/dynarec/dynablock_private.h b/src/dynarec/dynablock_private.h
index dd2ee4c0..e73d35ba 100755
--- a/src/dynarec/dynablock_private.h
+++ b/src/dynarec/dynablock_private.h
@@ -29,7 +29,8 @@ typedef struct dynablock_s {
 typedef struct dynablocklist_s {
     uintptr_t           text;
     int                 textsz;
-    int                 maxsz;     // maxblock size (for this block or previous block)
+    uintptr_t           maxend;    // max address end for anyblock on this blocklist
+    uintptr_t           minstart;  // min start address for block overlapping this blocklist
     dynablock_t**       direct;    // direct mapping (waste of space, so the array is created at first write)
 } dynablocklist_t;
 
diff --git a/src/dynarec/dynarec.c b/src/dynarec/dynarec.c
index 15d9fd13..8edefa58 100755
--- a/src/dynarec/dynarec.c
+++ b/src/dynarec/dynarec.c
@@ -114,16 +114,17 @@ void DynaCall(x64emu_t* emu, uintptr_t addr)
         PushExit(emu);
         R_RIP = addr;
         emu->df = d_none;
-        dynablock_t* block = NULL;
         dynablock_t* current = NULL;
+        dynablock_t* block = DBGetBlock(emu, R_RIP, 1, &current);
+        current = block;
         while(!emu->quit) {
-            block = DBGetBlock(emu, R_RIP, 1, &current);
-            current = block;
             if(!block || !block->block || !block->done) {
                 // no block, of block doesn't have DynaRec content (yet, temp is not null)
                 // Use interpreter (should use single instruction step...)
                 dynarec_log(LOG_DEBUG, "%04d|Calling Interpretor @%p, emu=%p\n", GetTID(), (void*)R_RIP, emu);
                 Run(emu, 1);
+                block = DBGetBlock(emu, R_RIP, 1, &current);
+                current = block;
             } else {
                 dynarec_log(LOG_DEBUG, "%04d|Calling DynaRec Block @%p (%p) of %d x64 instructions (father=%p) emu=%p\n", GetTID(), (void*)R_RIP, block->block, block->isize ,block->father, emu);
                 CHECK_FLAGS(emu);
@@ -131,6 +132,7 @@ void DynaCall(x64emu_t* emu, uintptr_t addr)
                 #ifdef ARM64
                 arm64_prolog(emu, block->block);
                 #endif
+                block = NULL;
             }
             if(emu->fork) {
                 int forktype = emu->fork;
@@ -194,22 +196,24 @@ int DynaRun(x64emu_t* emu)
         return Run(emu, 0);
 #ifdef DYNAREC
     else {
-        dynablock_t* block = NULL;
         dynablock_t* current = NULL;
+        dynablock_t* block = DBGetBlock(emu, R_RIP, 1, &current);;
+        current = block;
         while(!emu->quit) {
-            block = DBGetBlock(emu, R_RIP, 1, &current);
-            current = block;
             if(!block || !block->block || !block->done) {
                 // no block, of block doesn't have DynaRec content (yet, temp is not null)
                 // Use interpreter (should use single instruction step...)
                 dynarec_log(LOG_DEBUG, "%04d|Running Interpretor @%p, emu=%p\n", GetTID(), (void*)R_RIP, emu);
                 Run(emu, 1);
+                block = DBGetBlock(emu, R_RIP, 1, &current);
+                current = block;
             } else {
                 dynarec_log(LOG_DEBUG, "%04d|Running DynaRec Block @%p (%p) of %d x64 insts (father=%p) emu=%p\n", GetTID(), (void*)R_RIP, block->block, block->isize, block->father, emu);
                 // block is here, let's run it!
                 #ifdef ARM64
                 arm64_prolog(emu, block->block);
                 #endif
+                block = NULL;
             }
             if(emu->fork) {
                 int forktype = emu->fork;
diff --git a/src/dynarec/dynarec_arm64.c b/src/dynarec/dynarec_arm64.c
index 995e4633..a24ddce7 100755
--- a/src/dynarec/dynarec_arm64.c
+++ b/src/dynarec/dynarec_arm64.c
@@ -353,6 +353,10 @@ void CancelBlock64()
 }
 
 void* FillBlock64(dynablock_t* block, uintptr_t addr) {
+    if(IsInHotPage(addr)) {
+        dynarec_log(LOG_DEBUG, "Cancelling dynarec FillBlock on hotpage for %p\n", (void*)addr);
+        return NULL;
+    }
     if(addr>=box64_nodynarec_start && addr<box64_nodynarec_end) {
         block->done = 1;
         return (void*)block;
@@ -493,8 +497,12 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr) {
         CancelBlock64();
         return NULL;
     }    // fill sons if any
-    if(!isprotectedDB(addr, end-addr))
-        protectDB(addr, end-addr);
+    if(!isprotectedDB(addr, end-addr)) {
+        dynarec_log(LOG_INFO, "Warning, block unprotected while beeing processed %p:%ld, cancelling\n", block->x64_addr, block->x64_size);
+        CancelBlock64();
+        return NULL;
+        //protectDB(addr, end-addr);
+    }
     dynablock_t** sons = NULL;
     int sons_size = 0;
     if(helper.sons_size) {
diff --git a/src/dynarec/dynarec_arm64_pass.c b/src/dynarec/dynarec_arm64_pass.c
index cd6ba45f..8bc587f7 100755
--- a/src/dynarec/dynarec_arm64_pass.c
+++ b/src/dynarec/dynarec_arm64_pass.c
@@ -15,6 +15,7 @@
 #include "box64stack.h"
 #include "emu/x64run_private.h"
 #include "x64trace.h"
+#include "dynablock.h"
 #include "dynarec_arm64.h"
 #include "dynarec_arm64_private.h"
 #include "arm64_printer.h"
@@ -102,7 +103,7 @@ uintptr_t arm_pass(dynarec_arm_t* dyn, uintptr_t addr)
             ok = 1;
         }
         #if STEP == 0
-        if(!ok && !need_epilog && box64_dynarec_bigblock && getProtection(addr+3)&~PROT_CUSTOM)
+        if(!ok && !need_epilog && box64_dynarec_bigblock && getProtection(addr+3)&~PROT_CUSTOM && !IsInHotPage(addr+3))
             if(*(uint32_t*)addr!=0) {   // check if need to continue (but is next 4 bytes are 0, stop)
                 uintptr_t next = get_closest_next(dyn, addr);
                 if(next && (
diff --git a/src/include/dynablock.h b/src/include/dynablock.h
index b83262cc..a2119643 100755
--- a/src/include/dynablock.h
+++ b/src/include/dynablock.h
@@ -31,5 +31,7 @@ dynablock_t *AddNewDynablock(dynablocklist_t* dynablocks, uintptr_t addr, int* c
 
 // for use in signal handler
 void cancelFillBlock();
+int IsInHotPage(uintptr_t addr);
+void AddHotPage(uintptr_t addr);
 
 #endif //__DYNABLOCK_H_
\ No newline at end of file
diff --git a/src/libtools/signals.c b/src/libtools/signals.c
index 3cb84d7d..5ac76342 100755
--- a/src/libtools/signals.c
+++ b/src/libtools/signals.c
@@ -706,6 +706,9 @@ void my_sigactionhandler_oldcode(int32_t sig, int simple, siginfo_t* info, void
 }
 
 extern __thread void* current_helper;
+#ifdef DYNAREC
+static pthread_mutex_t mutex_dynarec_prot;
+#endif
 
 void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
 {
@@ -745,12 +748,23 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
     dynablock_t* db = NULL;
     int db_searched = 0;
     if ((sig==SIGSEGV) && (addr) && (info->si_code == SEGV_ACCERR) && (prot&PROT_DYNAREC)) {
-        // access error, unprotect the block (and mark them dirty)
-        unprotectDB((uintptr_t)addr, 1);    // unprotect 1 byte... But then, the whole page will be unprotected
+        pthread_mutex_lock(&mutex_dynarec_prot);
         // check if SMC inside block
         db = FindDynablockFromNativeAddress(pc);
         db_searched = 1;
-        dynarec_log(LOG_DEBUG, "SIGSEGV with Access error on %p for %p , db=%p(%p), prot=0x%x\n", pc, addr, db, db?((void*)db->x64_addr):NULL, prot);
+        static uintptr_t repeated_page = 0;
+        dynarec_log(LOG_DEBUG, "SIGSEGV with Access error on %p for %p , db=%p(%p), prot=0x%x (old page=%p)\n", pc, addr, db, db?((void*)db->x64_addr):NULL, prot, (void*)repeated_page);
+        static int repeated_count = 0;
+        if(repeated_page == ((uintptr_t)addr&~0xfff)) {
+            ++repeated_count;   // Access eoor multiple time on same page, disable dynarec on this page a few time...
+            dynarec_log(LOG_DEBUG, "Detecting a Hotpage at %p (%d)\n", (void*)repeated_page, repeated_count);
+            AddHotPage(repeated_page);
+        } else {
+            repeated_page = (uintptr_t)addr&~0xfff;
+            repeated_count = 0;
+        }
+        // access error, unprotect the block (and mark them dirty)
+        unprotectDB((uintptr_t)addr, 1);    // unprotect 1 byte... But then, the whole page will be unprotected
         if(db && ((addr>=db->x64_addr && addr<(db->x64_addr+db->x64_size)) || db->need_test)) {
             // dynablock got auto-dirty! need to get out of it!!!
             emu_jmpbuf_t* ejb = GetJmpBuf();
@@ -785,17 +799,21 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
                     dynarec_log(LOG_INFO, "Dynablock unprotected, getting out!\n");
                 }
                 relockMutex(Locks);
+                pthread_mutex_unlock(&mutex_dynarec_prot);
                 siglongjmp(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) {
+        if((prot&PROT_WRITE) || (prot&PROT_DYNAREC)) {
+            pthread_mutex_unlock(&mutex_dynarec_prot);
             // if there is no write permission, don't return and continue to program signal handling
             relockMutex(Locks);
             return;
         }
+        pthread_mutex_unlock(&mutex_dynarec_prot);
     } else if ((sig==SIGSEGV) && (addr) && (info->si_code == SEGV_ACCERR) && (prot&(PROT_READ|PROT_WRITE))) {
+        pthread_mutex_lock(&mutex_dynarec_prot);
         db = FindDynablockFromNativeAddress(pc);
         db_searched = 1;
         if(db && db->x64_addr>= addr && (db->x64_addr+db->x64_size)<addr) {
@@ -812,6 +830,7 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
                 glitch_addr = addr;
                 glitch_prot = prot;
                 relockMutex(Locks);
+                pthread_mutex_unlock(&mutex_dynarec_prot);
                 return; // try again
             }
 dynarec_log(/*LOG_DEBUG*/LOG_INFO, "Repeated SIGSEGV with Access error on %p for %p, db=%p, prot=0x%x\n", pc, addr, db, prot);
@@ -819,6 +838,7 @@ dynarec_log(/*LOG_DEBUG*/LOG_INFO, "Repeated SIGSEGV with Access error on %p for
             glitch_addr = NULL;
             glitch_prot = 0;
         }
+        pthread_mutex_unlock(&mutex_dynarec_prot);
     }
 #else
     void* db = NULL;
@@ -1281,7 +1301,17 @@ EXPORT int my_swapcontext(x64emu_t* emu, void* ucp1, void* ucp2)
     my_setcontext(emu, ucp2);
     return 0;
 }
+#ifdef DYNAREC
+static void atfork_child_dynarec_prot(void)
+{
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+    pthread_mutex_init(&mutex_dynarec_prot, &attr);
 
+    pthread_mutexattr_destroy(&attr);
+}
+#endif
 void init_signal_helper(box64context_t* context)
 {
     // setup signal handling
@@ -1300,6 +1330,10 @@ void init_signal_helper(box64context_t* context)
     sigaction(SIGILL, &action, NULL);
 
 	pthread_once(&sigstack_key_once, sigstack_key_alloc);
+#ifdef DYNAREC
+    atfork_child_dynarec_prot();
+    pthread_atfork(NULL, NULL, atfork_child_dynarec_prot);
+#endif
 }
 
 void fini_signal_helper()
diff --git a/src/wrapped/wrappedlibdl.c b/src/wrapped/wrappedlibdl.c
index 09336cf9..53566797 100755
--- a/src/wrapped/wrappedlibdl.c
+++ b/src/wrapped/wrappedlibdl.c
@@ -64,7 +64,7 @@ void* my_dlopen(x64emu_t* emu, void *filename, int flag)
     CLEARERR
     if(filename) {
         char* rfilename = (char*)filename;
-        if(box64_zoom && strstr(rfilename, "/libturbojpeg.so")) {
+        if(box64_zoom && rfilename && strstr(rfilename, "/libturbojpeg.so")) {
             void* sys = my_dlopen(emu, "libturbojpeg.so.0", flag);
             if(sys)
                 return sys;