diff options
| author | ptitSeb <sebastien.chev@gmail.com> | 2021-11-18 14:21:16 +0100 |
|---|---|---|
| committer | ptitSeb <sebastien.chev@gmail.com> | 2021-11-18 14:21:16 +0100 |
| commit | e7290d78d5047662c49a8558d1709ed912346ff7 (patch) | |
| tree | 10ecdc9b7b9a994087980cf5e65cfefdb48602db /src | |
| parent | 4f0e98ade5d37b8647cb5d39b6e5a3aded55fa2f (diff) | |
| download | box64-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.c | 27 | ||||
| -rwxr-xr-x | src/dynarec/dynablock.c | 147 | ||||
| -rwxr-xr-x | src/dynarec/dynablock_private.h | 3 | ||||
| -rwxr-xr-x | src/dynarec/dynarec.c | 16 | ||||
| -rwxr-xr-x | src/dynarec/dynarec_arm64.c | 12 | ||||
| -rwxr-xr-x | src/dynarec/dynarec_arm64_pass.c | 3 | ||||
| -rwxr-xr-x | src/include/dynablock.h | 2 | ||||
| -rwxr-xr-x | src/libtools/signals.c | 42 | ||||
| -rwxr-xr-x | src/wrapped/wrappedlibdl.c | 2 |
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, ¤t); + current = block; while(!emu->quit) { - block = DBGetBlock(emu, R_RIP, 1, ¤t); - 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, ¤t); + 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, ¤t);; + current = block; while(!emu->quit) { - block = DBGetBlock(emu, R_RIP, 1, ¤t); - 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, ¤t); + 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; |