diff options
| author | ptitSeb <sebastien.chev@gmail.com> | 2024-12-31 22:27:55 +0100 |
|---|---|---|
| committer | ptitSeb <sebastien.chev@gmail.com> | 2024-12-31 22:27:55 +0100 |
| commit | 1d20968f822018ccbe1afd7c4d35c4f7b4774341 (patch) | |
| tree | 2c34d0a73b4a3cf9adedebd1962f2e2b1a039188 /src | |
| parent | ca8d569e63dd53183dcb9b1e6446027774083782 (diff) | |
| download | box64-1d20968f822018ccbe1afd7c4d35c4f7b4774341.tar.gz box64-1d20968f822018ccbe1afd7c4d35c4f7b4774341.zip | |
[ARM64_DYNAREC] Improved signal handling and flags handling (tbd on other archs)
Diffstat (limited to 'src')
| -rw-r--r-- | src/dynarec/arm64/dynarec_arm64_arch.c | 81 | ||||
| -rw-r--r-- | src/dynarec/arm64/dynarec_arm64_arch.h | 28 | ||||
| -rw-r--r-- | src/dynarec/dynablock_private.h | 2 | ||||
| -rw-r--r-- | src/dynarec/dynarec_arch.h | 12 | ||||
| -rw-r--r-- | src/dynarec/dynarec_native.c | 15 | ||||
| -rw-r--r-- | src/libtools/signal32.c | 5 | ||||
| -rw-r--r-- | src/libtools/signals.c | 177 |
7 files changed, 270 insertions, 50 deletions
diff --git a/src/dynarec/arm64/dynarec_arm64_arch.c b/src/dynarec/arm64/dynarec_arm64_arch.c new file mode 100644 index 00000000..af84565c --- /dev/null +++ b/src/dynarec/arm64/dynarec_arm64_arch.c @@ -0,0 +1,81 @@ +#include <stddef.h> +#include <stdio.h> +#include <ucontext.h> + +#include "debug.h" +#include "dynablock.h" +#include "x64emu.h" +#include "emu/x64emu_private.h" +#include "x64run.h" +#include "emu/x64run_private.h" +#include "dynarec/dynablock_private.h" +#include "dynarec_arm64_arch.h" + +size_t get_size_arch(dynarec_arm_t* dyn) +{ + if(!box64_dynarec_nativeflags) + return 0; + return dyn->isize*sizeof(arch_flags_t); +} + +void populate_arch(dynarec_arm_t* dyn, void* p) +{ + if(!box64_dynarec_nativeflags) + return; + + arch_flags_t* flags = p; + for(int i=0; i<dyn->size; ++i) { + flags[i].defered = dyn->insts[i].f_entry.dfnone==0; + flags[i].vf = dyn->insts[i].need_nat_flags&NF_VF; + flags[i].nf = dyn->insts[i].need_nat_flags&NF_SF; + flags[i].eq = dyn->insts[i].need_nat_flags&NF_EQ; + flags[i].cf = dyn->insts[i].need_nat_flags&NF_CF; + flags[i].inv_cf = !dyn->insts[i].normal_carry; + } +} + +int getX64AddressInst(dynablock_t* db, uintptr_t native_addr); // define is signal.c + +// NZCV N +#define NZCV_N 31 +// NZCV Z +#define NZCV_Z 30 +// NZCV C +#define NZCV_C 29 +// NZCV V +#define NZCV_V 28 + +void adjust_arch(dynablock_t* db, x64emu_t* emu, ucontext_t* p, uintptr_t x64pc) +{ + if(!db->arch_size || !db->arch) + return; + arch_flags_t* flags = db->arch; + int ninst = getX64AddressInst(db, x64pc); +printf_log(LOG_INFO, "adjust_arch(...), db=%p, x64pc=%p, nints=%d, flags:%s %c%c%c%c%s\n", db, (void*)x64pc, ninst, flags[ninst-1].defered?"defered":"", flags[ninst-1].vf?'V':' ', flags[ninst-1].nf?'S':' ', flags[ninst-1].eq?'Z':' ', flags[ninst-1].cf?'C':' ', (flags[ninst-1].cf && flags[ninst-1].inv_cf)?"inverted":""); + if(ninst<0) + return; + if(ninst==0) { + CHECK_FLAGS(emu); + return; + } + if(flags[ninst-1].defered) { + CHECK_FLAGS(emu); + //return; + } + if(flags[ninst-1].nf) { + CONDITIONAL_SET_FLAG(p->uc_mcontext.pstate&(1<<NZCV_N), F_SF); + } + if(flags[ninst-1].vf) { + CONDITIONAL_SET_FLAG(p->uc_mcontext.pstate&(1<<NZCV_V), F_OF); + } + if(flags[ninst-1].eq) { + CONDITIONAL_SET_FLAG(p->uc_mcontext.pstate&(1<<NZCV_Z), F_ZF); + } + if(flags[ninst-1].cf) { + if(flags[ninst-1].inv_cf) { + CONDITIONAL_SET_FLAG((p->uc_mcontext.pstate&(1<<NZCV_C))==0, F_CF); + } else { + CONDITIONAL_SET_FLAG(p->uc_mcontext.pstate&(1<<NZCV_C), F_CF); + } + } +} \ No newline at end of file diff --git a/src/dynarec/arm64/dynarec_arm64_arch.h b/src/dynarec/arm64/dynarec_arm64_arch.h new file mode 100644 index 00000000..a430cf74 --- /dev/null +++ b/src/dynarec/arm64/dynarec_arm64_arch.h @@ -0,0 +1,28 @@ +#ifndef __DYNAREC_ARM_ARCH_H__ +#define __DYNAREC_ARM_ARCH_H__ + +#include <stddef.h> +#include <ucontext.h> + +#include "x64emu.h" +#include "box64context.h" +#include "dynarec.h" +#include "dynarec_arm64_private.h" + +typedef struct arch_flags_s +{ + uint8_t defered:1; + uint8_t nf:1; + uint8_t eq:1; + uint8_t vf:1; + uint8_t cf:1; + uint8_t inv_cf:1; +} arch_flags_t; + +// get size of arch specific info (can be 0) +size_t get_size_arch(dynarec_arm_t* dyn); +//populate the array +void populate_arch(dynarec_arm_t* dyn, void* p); +//adjust flags and more +void adjust_arch(dynablock_t* db, x64emu_t* emu, ucontext_t* p, uintptr_t native_addr); +#endif // __DYNAREC_ARM_ARCH_H__ diff --git a/src/dynarec/dynablock_private.h b/src/dynarec/dynablock_private.h index a152bd4e..b9e5f55d 100644 --- a/src/dynarec/dynablock_private.h +++ b/src/dynarec/dynablock_private.h @@ -21,6 +21,8 @@ typedef struct dynablock_s { uint8_t is32bits:1; int isize; instsize_t* instsize; + void* arch; // arch dependant per inst info (can be NULL) + size_t arch_size; // size of of arch dependant infos void* jmpnext; // a branch jmpnext code when block is marked } dynablock_t; diff --git a/src/dynarec/dynarec_arch.h b/src/dynarec/dynarec_arch.h index 3dd07d5f..edc09650 100644 --- a/src/dynarec/dynarec_arch.h +++ b/src/dynarec/dynarec_arch.h @@ -17,6 +17,7 @@ #include "arm64/arm64_printer.h" #include "arm64/dynarec_arm64_private.h" #include "arm64/dynarec_arm64_functions.h" +#include "arm64/dynarec_arm64_arch.h" // Limit here is defined by LD litteral, that is 19bits #define MAXBLOCK_SIZE ((1<<19)-200) @@ -24,6 +25,9 @@ #define UPDATE_SPECIFICS(A) updateNativeFlags(A) #define PREUPDATE_SPECIFICS(A) +#define ARCH_SIZE(A) get_size_arch(A) +#define ARCH_FILL(A, B) populate_arch(A, B) +#define ARCH_ADJUST(A, B, C, D) adjust_arch(A, B, C, D) #elif defined(LA64) #define instruction_native_t instruction_la64_t @@ -45,6 +49,10 @@ #define RAZ_SPECIFIC(A, N) #define UPDATE_SPECIFICS(A) #define PREUPDATE_SPECIFICS(A) updateNativeFlags(A) + +#define ARCH_SIZE(A) 0 +#define ARCH_FILL(A, B) {} +#define ARCH_ADJUST(A, B, C, D) {} #elif defined(RV64) #define instruction_native_t instruction_rv64_t @@ -68,6 +76,10 @@ #define RAZ_SPECIFIC(A, N) #define UPDATE_SPECIFICS(A) #define PREUPDATE_SPECIFICS(A) updateNativeFlags(A) + +#define ARCH_SIZE(A) 0 +#define ARCH_FILL(A, B) {} +#define ARCH_ADJUST(A, B, C, D) {} #else #error Unsupported platform #endif diff --git a/src/dynarec/dynarec_native.c b/src/dynarec/dynarec_native.c index 47c1c99e..42b557ed 100644 --- a/src/dynarec/dynarec_native.c +++ b/src/dynarec/dynarec_native.c @@ -562,6 +562,7 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr, int alternate, int is32bit B+16 .. B+23 : jmpnext (or jmp_epilog) address. jumpnext is used when the block needs testing B+24 .. B+31 : empty (in case an architecture needs more than 2 opcodes) B+32 .. B+32+sz : instsize (compressed array with each instruction length on x64 and native side) + C .. C+sz : arch: arch specific info (likes flags info) per inst (can be absent) */ if(addr>=box64_nodynarec_start && addr<box64_nodynarec_end) { @@ -725,14 +726,16 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr, int alternate, int is32bit size_t insts_rsize = (helper.insts_size+2)*sizeof(instsize_t); insts_rsize = (insts_rsize+7)&~7; // round the size... size_t native_size = (helper.native_size+7)&~7; // round the size... + size_t arch_size = ARCH_SIZE(&helper); // ok, now allocate mapped memory, with executable flag on - size_t sz = sizeof(void*) + native_size + helper.table64size*sizeof(uint64_t) + 4*sizeof(void*) + insts_rsize; - // dynablock_t* block (arm insts) table64 jmpnext code instsize + size_t sz = sizeof(void*) + native_size + helper.table64size*sizeof(uint64_t) + 4*sizeof(void*) + insts_rsize + arch_size; + // dynablock_t* block (arm insts) table64 jmpnext code instsize arch void* actual_p = (void*)AllocDynarecMap(sz); void* p = (void*)(((uintptr_t)actual_p) + sizeof(void*)); void* tablestart = p + native_size; void* next = tablestart + helper.table64size*sizeof(uint64_t); void* instsize = next + 4*sizeof(void*); + void* arch = instsize + insts_rsize; if(actual_p==NULL) { dynarec_log(LOG_INFO, "AllocDynarecMap(%p, %zu) failed, canceling block\n", block, sz); CancelBlock64(0); @@ -784,6 +787,14 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr, int alternate, int is32bit block->always_test = helper.always_test; block->dirty = block->always_test; block->is32bits = is32bits; + if(arch_size) { + block->arch = arch; + block->arch_size = arch_size; + ARCH_FILL(&helper, arch); + } else { + block->arch = NULL; + block->arch_size = arch_size; + } *(dynablock_t**)next = block; *(void**)(next+3*sizeof(void*)) = native_next; CreateJmpNext(block->jmpnext, next+3*sizeof(void*)); diff --git a/src/libtools/signal32.c b/src/libtools/signal32.c index 2a7e3a52..2f4a0a16 100644 --- a/src/libtools/signal32.c +++ b/src/libtools/signal32.c @@ -468,7 +468,7 @@ void convert_siginfo_to_32(void* d, void* s, int sig) int write_opcode(uintptr_t rip, uintptr_t native_ip, int is32bits); #define is_memprot_locked (1<<1) #define is_dyndump_locked (1<<8) -void my_sigactionhandler_oldcode_32(int32_t sig, int simple, siginfo_t* info, void * ucntx, int* old_code, void* cur_db) +void my_sigactionhandler_oldcode_32(x64emu_t* emu, int32_t sig, int simple, siginfo_t* info, void * ucntx, int* old_code, void* cur_db) { int Locks = unlockMutex(); int log_minimum = (box64_showsegv)?LOG_NONE:((sig==SIGSEGV && my_context->is_sigaction[sig])?LOG_DEBUG:LOG_INFO); @@ -477,7 +477,8 @@ void my_sigactionhandler_oldcode_32(int32_t sig, int simple, siginfo_t* info, vo uintptr_t restorer = my_context->restorer[sig]; // get that actual ESP first! - x64emu_t *emu = thread_get_emu(); + if(!emu) + emu = thread_get_emu(); uintptr_t frame = R_RSP; #if defined(DYNAREC) #if defined(ARM64) diff --git a/src/libtools/signals.c b/src/libtools/signals.c index 4abf8a4f..7b1bbf30 100644 --- a/src/libtools/signals.c +++ b/src/libtools/signals.c @@ -36,6 +36,7 @@ #include "dynablock.h" #include "../dynarec/dynablock_private.h" #include "dynarec_native.h" +#include "dynarec/dynarec_arch.h" #endif @@ -491,6 +492,31 @@ uintptr_t getX64Address(dynablock_t* db, uintptr_t native_addr) } while(db->instsize[i].x64 || db->instsize[i].nat); return x64addr; } +int getX64AddressInst(dynablock_t* db, uintptr_t x64pc) +{ + uintptr_t x64addr = (uintptr_t)db->x64_addr; + uintptr_t armaddr = (uintptr_t)db->block; + int ret = 0; + if(x64pc<(uintptr_t)db->x64_addr || x64pc>(uintptr_t)db->x64_addr+db->x64_size) + return -1; + int i = 0; + do { + int x64sz = 0; + int armsz = 0; + do { + x64sz+=db->instsize[i].x64; + armsz+=db->instsize[i].nat*4; + ++i; + } while((db->instsize[i-1].x64==15) || (db->instsize[i-1].nat==15)); + // if the opcode is a NOP on ARM side (so armsz==0), it cannot be an address to find + if((x64pc>=x64addr) && (x64pc<(x64addr+x64sz))) + return ret; + armaddr+=armsz; + x64addr+=x64sz; + ret++; + } while(db->instsize[i].x64 || db->instsize[i].nat); + return ret; +} x64emu_t* getEmuSignal(x64emu_t* emu, ucontext_t* p, dynablock_t* db) { #if defined(ARM64) @@ -927,16 +953,10 @@ int sigbus_specialcases(siginfo_t* info, void * ucntx, void* pc, void* _fpsimd) } #ifdef BOX32 -void my_sigactionhandler_oldcode_32(int32_t sig, int simple, siginfo_t* info, void * ucntx, int* old_code, void* cur_db); +void my_sigactionhandler_oldcode_32(ix64emu_t* emu, nt32_t sig, int simple, siginfo_t* info, void * ucntx, int* old_code, void* cur_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) +void my_sigactionhandler_oldcode_64(x64emu_t* emu, int32_t sig, int simple, siginfo_t* info, void * ucntx, int* old_code, void* cur_db) { - #ifdef BOX32 - if(box64_is32bits) { - my_sigactionhandler_oldcode_32(sig, simple, info, ucntx, old_code, cur_db); - return; - } - #endif int Locks = unlockMutex(); int log_minimum = (box64_showsegv)?LOG_NONE:LOG_DEBUG; @@ -1376,6 +1396,63 @@ void my_sigactionhandler_oldcode(x64emu_t* emu, int32_t sig, int simple, siginfo relockMutex(Locks); } +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) +{ + #define GO(A) uintptr_t old_##A = R_##A; + GO(RAX); + GO(RBX); + GO(RCX); + GO(RDX); + GO(RBP); + GO(RSP); + GO(RDI); + GO(RSI); + GO(R8); + GO(R9); + GO(R10); + GO(R11); + GO(R12); + GO(R13); + GO(R14); + GO(R15); + GO(RIP); + #undef GO + #ifdef DYNAREC + dynablock_t* db = cur_db; + if(db) { + copyUCTXreg2Emu(emu, ucntx, x64pc); + adjustregs(emu); + if(db && db->arch_size) + ARCH_ADJUST(db, emu, ucntx, x64pc); + } + #endif + #ifdef BOX32 + if(box64_is32bits) { + my_sigactionhandler_oldcode_32(emu, sig, simple, info, ucntx, old_code, cur_db); + } else + #endif + my_sigactionhandler_oldcode_64(emu, sig, simple, info, ucntx, old_code, cur_db); + #define GO(A) R_##A = old_##A + GO(RAX); + GO(RBX); + GO(RCX); + GO(RDX); + GO(RBP); + GO(RSP); + GO(RDI); + GO(RSI); + GO(R8); + GO(R9); + GO(R10); + GO(R11); + GO(R12); + GO(R13); + GO(R14); + GO(R15); + GO(RIP); + #undef GO +} + extern void* current_helper; #define USE_SIGNAL_MUTEX #ifdef USE_SIGNAL_MUTEX @@ -1486,8 +1563,11 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx) emu = getEmuSignal(emu, p, db); // dynablock got auto-dirty! need to get out of it!!! if(emu->jmpbuf) { - copyUCTXreg2Emu(emu, p, getX64Address(db, (uintptr_t)pc)); + uintptr_t x64pc = getX64Address(db, (uintptr_t)pc); + copyUCTXreg2Emu(emu, p, x64pc); adjustregs(emu); + if(db && db->arch_size) + ARCH_ADJUST(db, emu, p, x64pc); #ifdef ARM64 //TODO: Need proper SIMD/x87 register traking! /*if(fpsimd) { @@ -1611,6 +1691,37 @@ dynarec_log(/*LOG_DEBUG*/LOG_INFO, "Repeated SIGSEGV with Access error on %p for int tid = GetTID(); int mapped = memExist((uintptr_t)addr); const char* signame = (sig==SIGSEGV)?"SIGSEGV":((sig==SIGBUS)?"SIGBUS":((sig==SIGILL)?"SIGILL":"SIGABRT")); + uintptr_t x64pc = (uintptr_t)-1; + x64pc = R_RIP; + rsp = (void*)R_RSP; +#if defined(DYNAREC) +#if defined(ARM64) + if(db) { + x64pc = getX64Address(db, (uintptr_t)pc); + rsp = (void*)p->uc_mcontext.regs[10+_SP]; + } +#elif defined(LA64) + if(db && p->uc_mcontext.__gregs[4]>0x10000) { + emu = (x64emu_t*)p->uc_mcontext.__gregs[4]; + } + if(db) { + x64pc = getX64Address(db, (uintptr_t)pc); + rsp = (void*)p->uc_mcontext.__gregs[12+_SP]; + } +#elif defined(RV64) + if(db && p->uc_mcontext.__gregs[25]>0x10000) { + emu = (x64emu_t*)p->uc_mcontext.__gregs[25]; + } + if(db) { + x64pc = getX64Address(db, (uintptr_t)pc); + rsp = (void*)p->uc_mcontext.__gregs[9]; + } +#else +#error Unsupported Architecture +#endif //arch +#endif //DYNAREC + if(!db && (sig==SIGSEGV) && ((uintptr_t)addr==(x64pc-1))) + x64pc--; if(old_code==info->si_code && old_pc==pc && old_addr==addr && old_tid==tid && old_prot==prot) { printf_log(log_minimum, "%04d|Double %s (code=%d, pc=%p, addr=%p, prot=%02x)!\n", tid, signame, old_code, old_pc, old_addr, prot); exit(-1); @@ -1643,41 +1754,10 @@ dynarec_log(/*LOG_DEBUG*/LOG_INFO, "Repeated SIGSEGV with Access error on %p for old_tid = tid; old_prot = prot; const char* name = (log_minimum<=box64_log)?GetNativeName(pc):NULL; - uintptr_t x64pc = (uintptr_t)-1; const char* x64name = NULL; // Adjust RIP for special case of NULL function run if(sig==SIGSEGV && R_RIP==0x1 && (uintptr_t)info->si_addr==0x0) R_RIP = 0x0; - x64pc = R_RIP; - rsp = (void*)R_RSP; -#if defined(DYNAREC) -#if defined(ARM64) - if(db) { - x64pc = getX64Address(db, (uintptr_t)pc); - rsp = (void*)p->uc_mcontext.regs[10+_SP]; - } -#elif defined(LA64) - if(db && p->uc_mcontext.__gregs[4]>0x10000) { - emu = (x64emu_t*)p->uc_mcontext.__gregs[4]; - } - if(db) { - x64pc = getX64Address(db, (uintptr_t)pc); - rsp = (void*)p->uc_mcontext.__gregs[12+_SP]; - } -#elif defined(RV64) - if(db && p->uc_mcontext.__gregs[25]>0x10000) { - emu = (x64emu_t*)p->uc_mcontext.__gregs[25]; - } - if(db) { - x64pc = getX64Address(db, (uintptr_t)pc); - rsp = (void*)p->uc_mcontext.__gregs[9]; - } -#else -#error Unsupported Architecture -#endif //arch -#endif //DYNAREC - if(!db && (sig==SIGSEGV) && ((uintptr_t)addr==(x64pc-1))) - x64pc--; if(log_minimum<=box64_log) { elfheader_t* elf = FindElfAddress(my_context, x64pc); if(elf) { @@ -1877,7 +1957,7 @@ dynarec_log(/*LOG_DEBUG*/LOG_INFO, "Repeated SIGSEGV with Access error on %p for } relockMutex(Locks); if(my_context->signals[sig] && my_context->signals[sig]!=1) { - my_sigactionhandler_oldcode(emu, sig, my_context->is_sigaction[sig]?0:1, info, ucntx, &old_code, db); + my_sigactionhandler_oldcode(emu, sig, my_context->is_sigaction[sig]?0:1, info, ucntx, &old_code, db, x64pc); return; } // no handler (or double identical segfault) @@ -1904,8 +1984,13 @@ void my_sigactionhandler(int32_t sig, siginfo_t* info, void * ucntx) #else void* db = NULL; #endif - - my_sigactionhandler_oldcode(NULL, sig, 0, info, ucntx, NULL, db); + x64emu_t* emu = thread_get_emu(); + uintptr_t x64pc = R_RIP; + #ifdef DYNAREC + if(db) + x64pc = getX64Address(db, (uintptr_t)pc); + #endif + my_sigactionhandler_oldcode(emu, sig, 0, info, ucntx, NULL, db, x64pc); } #ifndef DYNAREC @@ -1983,7 +2068,7 @@ printf_log(LOG_NONE, "Emu Stack: %p 0x%lx%s\n", emu->init_stack, emu->size_stack printf_log(LOG_NONE, "SIGILL: Opcode at ip is %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n", mem[0], mem[1], mem[2], mem[3], mem[4], mem[5]); } } - my_sigactionhandler_oldcode(emu, sig, 0, &info, NULL, NULL, NULL); + my_sigactionhandler_oldcode(emu, sig, 0, &info, NULL, NULL, NULL, R_RIP); } void check_exec(x64emu_t* emu, uintptr_t addr) @@ -2012,7 +2097,7 @@ void emit_interruption(x64emu_t* emu, int num, void* addr) elfname = ElfName(elf); printf_log(LOG_NONE, "Emit Interruption 0x%x at IP=%p(%s / %s) / addr=%p\n", num, (void*)R_RIP, x64name?x64name:"???", elfname?elfname:"?", addr); } - my_sigactionhandler_oldcode(emu, SIGSEGV, 0, &info, NULL, NULL, NULL); + my_sigactionhandler_oldcode(emu, SIGSEGV, 0, &info, NULL, NULL, NULL, R_RIP); } void emit_div0(x64emu_t* emu, void* addr, int code) @@ -2031,7 +2116,7 @@ void emit_div0(x64emu_t* emu, void* addr, int code) elfname = ElfName(elf); printf_log(LOG_NONE, "Emit Divide by 0 at IP=%p(%s / %s) / addr=%p\n", (void*)R_RIP, x64name?x64name:"???", elfname?elfname:"?", addr); } - my_sigactionhandler_oldcode(emu, SIGSEGV, 0, &info, NULL, NULL, NULL); + my_sigactionhandler_oldcode(emu, SIGSEGV, 0, &info, NULL, NULL, NULL, R_RIP); } EXPORT sighandler_t my_signal(x64emu_t* emu, int signum, sighandler_t handler) |