mirror of https://github.com/ptitSeb/box64.git
2633 lines
97 KiB
C
2633 lines
97 KiB
C
#define _GNU_SOURCE
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <syscall.h>
|
|
#include <stddef.h>
|
|
#include <stdarg.h>
|
|
#include <ucontext.h>
|
|
#include <setjmp.h>
|
|
#include <sys/mman.h>
|
|
#include <pthread.h>
|
|
#ifndef ANDROID
|
|
#include <execinfo.h>
|
|
#endif
|
|
|
|
#include "os.h"
|
|
#include "backtrace.h"
|
|
#include "box64context.h"
|
|
#include "debug.h"
|
|
#include "x64emu.h"
|
|
#include "emu/x64emu_private.h"
|
|
#include "emu/x64run_private.h"
|
|
#include "signals.h"
|
|
#include "box64stack.h"
|
|
#include "box64cpu.h"
|
|
#include "callback.h"
|
|
#include "elfloader.h"
|
|
#include "threads.h"
|
|
#include "emu/x87emu_private.h"
|
|
#include "custommem.h"
|
|
#include "bridge.h"
|
|
#include "khash.h"
|
|
#ifdef DYNAREC
|
|
#include "dynablock.h"
|
|
#include "../dynarec/dynablock_private.h"
|
|
#include "dynarec_native.h"
|
|
#include "dynarec/dynarec_arch.h"
|
|
#endif
|
|
|
|
|
|
typedef uint64_t x64_gregset_t[23];
|
|
enum
|
|
{
|
|
X64_R8 = 0,
|
|
# define X64_R8 X64_R8
|
|
X64_R9,
|
|
# define X64_R9 X64_R9
|
|
X64_R10,
|
|
# define X64_R10 X64_R10
|
|
X64_R11,
|
|
# define X64_R11 X64_R11
|
|
X64_R12,
|
|
# define X64_R12 X64_R12
|
|
X64_R13,
|
|
# define X64_R13 X64_R13
|
|
X64_R14,
|
|
# define X64_R14 X64_R14
|
|
X64_R15,
|
|
# define X64_R15 X64_R15
|
|
X64_RDI,
|
|
# define X64_RDI X64_RDI
|
|
X64_RSI,
|
|
# define X64_RSI X64_RSI
|
|
X64_RBP,
|
|
# define X64_RBP X64_RBP
|
|
X64_RBX,
|
|
# define X64_RBX X64_RBX
|
|
X64_RDX,
|
|
# define X64_RDX X64_RDX
|
|
X64_RAX,
|
|
# define X64_RAX X64_RAX
|
|
X64_RCX,
|
|
# define X64_RCX X64_RCX
|
|
X64_RSP,
|
|
# define X64_RSP X64_RSP
|
|
X64_RIP,
|
|
# define X64_RIP X64_RIP
|
|
X64_EFL,
|
|
# define X64_EFL X64_EFL
|
|
X64_CSGSFS, /* Actually short cs, gs, fs, __pad0. */
|
|
# define X64_CSGSFS X64_CSGSFS
|
|
X64_ERR,
|
|
# define X64_ERR X64_ERR
|
|
X64_TRAPNO,
|
|
# define X64_TRAPNO X64_TRAPNO
|
|
X64_OLDMASK,
|
|
# define X64_OLDMASK X64_OLDMASK
|
|
X64_CR2
|
|
# define X64_CR2 X64_CR2
|
|
};
|
|
|
|
struct x64_fpreg
|
|
{
|
|
uint64_t value;
|
|
}__attribute__((packed));
|
|
|
|
struct x64_fpxreg
|
|
{
|
|
unsigned short significand[4];
|
|
unsigned short exponent;
|
|
unsigned short padding[3];
|
|
}__attribute__((packed));
|
|
|
|
struct x64_xmmreg
|
|
{
|
|
uint32_t element[4];
|
|
}__attribute__((packed));
|
|
|
|
struct x64_fpstate
|
|
{
|
|
/* Regular FPU environment. */
|
|
uint16_t cw;
|
|
uint16_t sw;
|
|
uint16_t tw;
|
|
uint16_t fop;
|
|
uint64_t rip;
|
|
uint64_t rdp;
|
|
uint32_t mxcsr;
|
|
uint32_t mxcsr_mask;
|
|
struct x64_fpreg _st[8];
|
|
struct x64_xmmreg _xmm[16];
|
|
uint32_t res[24];
|
|
}__attribute__((packed));
|
|
|
|
typedef struct x64_fpstate *x64_fpregset_t;
|
|
|
|
typedef struct x64_stack_s
|
|
{
|
|
void *ss_sp;
|
|
int ss_flags;
|
|
size_t ss_size;
|
|
} x64_stack_t;
|
|
|
|
struct sigcontext_x64
|
|
{
|
|
uint64_t r8;
|
|
uint64_t r9;
|
|
uint64_t r10;
|
|
uint64_t r11;
|
|
uint64_t r12;
|
|
uint64_t r13;
|
|
uint64_t r14;
|
|
uint64_t r15;
|
|
uint64_t di;
|
|
uint64_t si;
|
|
uint64_t bp;
|
|
uint64_t bx;
|
|
uint64_t dx;
|
|
uint64_t ax;
|
|
uint64_t cx;
|
|
uint64_t sp;
|
|
uint64_t ip;
|
|
uint64_t flags;
|
|
uint64_t cs;
|
|
uint64_t gs;
|
|
uint64_t fs;
|
|
uint64_t ss;
|
|
uint64_t err;
|
|
uint64_t trapno;
|
|
uint64_t oldmask;
|
|
uint64_t cr2;
|
|
uint64_t fpstate; /* Zero when no FPU/extended context */
|
|
uint64_t reserved1[8];
|
|
};
|
|
|
|
struct x64_sigcontext
|
|
{
|
|
uint64_t r8;
|
|
uint64_t r9;
|
|
uint64_t r10;
|
|
uint64_t r11;
|
|
uint64_t r12;
|
|
uint64_t r13;
|
|
uint64_t r14;
|
|
uint64_t r15;
|
|
uint64_t rdi;
|
|
uint64_t rsi;
|
|
uint64_t rbp;
|
|
uint64_t rbx;
|
|
uint64_t rdx;
|
|
uint64_t rax;
|
|
uint64_t rcx;
|
|
uint64_t rsp;
|
|
uint64_t rip;
|
|
uint64_t eflags; /* RFLAGS */
|
|
uint16_t cs;
|
|
uint16_t gs;
|
|
uint16_t fs;
|
|
union {
|
|
uint16_t ss; /* If UC_SIGCONTEXT_SS */
|
|
uint16_t __pad0; /* Alias name for old (!UC_SIGCONTEXT_SS) user-space */
|
|
};
|
|
uint64_t err;
|
|
uint64_t trapno;
|
|
uint64_t oldmask;
|
|
uint64_t cr2;
|
|
struct x64_fpstate *fpstate; /* Zero when no FPU context */
|
|
uint64_t reserved1[8];
|
|
};
|
|
|
|
struct x64_libc_fpstate
|
|
{
|
|
/* 64-bit FXSAVE format. */
|
|
uint16_t cwd;
|
|
uint16_t swd;
|
|
uint16_t ftw;
|
|
uint16_t fop;
|
|
uint64_t rip;
|
|
uint64_t rdp;
|
|
uint32_t mxcsr;
|
|
uint32_t mxcr_mask;
|
|
struct x64_fpxreg st[8];
|
|
struct x64_xmmreg xmm[16];
|
|
uint32_t res1[24];
|
|
};
|
|
|
|
typedef struct x64_mcontext_s
|
|
{
|
|
x64_gregset_t gregs;
|
|
struct x64_libc_fpstate *fpregs;
|
|
uint64_t res[8];
|
|
} x64_mcontext_t;
|
|
|
|
// /!\ signal sig_set is different than glibc __sig_set
|
|
#define _NSIG_WORDS (128 / sizeof(unsigned long int))
|
|
|
|
typedef struct {
|
|
unsigned long int sig[_NSIG_WORDS];
|
|
} x64_sigset_t;
|
|
|
|
typedef struct x64_ucontext_s
|
|
{
|
|
uint64_t uc_flags;
|
|
struct x64_ucontext_s* uc_link;
|
|
x64_stack_t uc_stack;
|
|
x64_mcontext_t uc_mcontext;
|
|
x64_sigset_t uc_sigmask;
|
|
struct x64_libc_fpstate xstate;
|
|
uint64_t ssp[4];
|
|
} x64_ucontext_t;
|
|
|
|
typedef struct x64_sigframe_s {
|
|
uintptr_t pretcode; // pointer to retcode
|
|
int sig;
|
|
x64_mcontext_t cpustate;
|
|
struct x64_libc_fpstate xstate;
|
|
uintptr_t extramask[64-1];
|
|
char retcode[8];
|
|
} x64_sigframe_t;
|
|
|
|
struct kernel_sigaction {
|
|
void (*k_sa_handler) (int);
|
|
unsigned long sa_flags;
|
|
void (*sa_restorer) (void);
|
|
unsigned long sa_mask;
|
|
unsigned long sa_mask2;
|
|
};
|
|
|
|
static void sigstack_destroy(void* p)
|
|
{
|
|
x64_stack_t *ss = (x64_stack_t*)p;
|
|
box_free(ss);
|
|
}
|
|
|
|
static pthread_key_t sigstack_key;
|
|
static pthread_once_t sigstack_key_once = PTHREAD_ONCE_INIT;
|
|
|
|
static void sigstack_key_alloc() {
|
|
pthread_key_create(&sigstack_key, sigstack_destroy);
|
|
}
|
|
|
|
x64_stack_t* sigstack_getstack() {
|
|
return (x64_stack_t*)pthread_getspecific(sigstack_key);
|
|
}
|
|
|
|
#ifndef DYNAREC
|
|
typedef void dynablock_t;
|
|
dynablock_t* FindDynablockFromNativeAddress(void* addr) {return NULL;}
|
|
uintptr_t getX64Address(dynablock_t* db, uintptr_t pc) {return 0;}
|
|
#endif
|
|
|
|
// this allow handling "safe" function that just abort if accessing a bad address
|
|
static __thread JUMPBUFF signal_jmpbuf;
|
|
#ifdef ANDROID
|
|
#define SIG_JMPBUF signal_jmpbuf
|
|
#else
|
|
#define SIG_JMPBUF &signal_jmpbuf
|
|
#endif
|
|
static __thread int signal_jmpbuf_active = 0;
|
|
|
|
|
|
//1<<1 is mutex_prot, 1<<8 is mutex_dyndump
|
|
#define is_memprot_locked (1<<1)
|
|
#define is_dyndump_locked (1<<8)
|
|
uint64_t RunFunctionHandler(x64emu_t* emu, int* exit, int dynarec, x64_ucontext_t* sigcontext, uintptr_t fnc, int nargs, ...)
|
|
{
|
|
if(fnc==0 || fnc==1) {
|
|
va_list va;
|
|
va_start (va, nargs);
|
|
int sig = va_arg(va, int);
|
|
va_end (va);
|
|
printf_log(LOG_NONE, "%04d|Warning, calling Signal %d function handler %s\n", GetTID(), sig, fnc?"SIG_IGN":"SIG_DFL");
|
|
if(fnc==0) {
|
|
printf_log(LOG_NONE, "Unhandled signal caught, aborting\n");
|
|
abort();
|
|
}
|
|
return 0;
|
|
}
|
|
#ifdef HAVE_TRACE
|
|
uintptr_t old_start = trace_start, old_end = trace_end;
|
|
#if 0
|
|
trace_start = 0; trace_end = 1; // disabling trace, globably for now...
|
|
#endif
|
|
#endif
|
|
#ifndef USE_CUSTOM_MEM
|
|
// because a signal can interupt a malloc-like function
|
|
// Dynarec cannot be used in signal handling unless custom malloc is used
|
|
dynarec = 0;
|
|
#endif
|
|
if(!emu)
|
|
emu = thread_get_emu();
|
|
#ifdef DYNAREC
|
|
if (BOX64ENV(dynarec_test))
|
|
emu->test.test = 0;
|
|
#endif
|
|
|
|
/*SetFS(emu, default_fs);*/
|
|
for (int i=0; i<6; ++i)
|
|
emu->segs_serial[i] = 0;
|
|
|
|
int align = nargs&1;
|
|
|
|
if(nargs>6)
|
|
R_RSP -= (nargs-6+align)*sizeof(void*); // need to push in reverse order
|
|
|
|
uint64_t *p = (uint64_t*)R_RSP;
|
|
|
|
va_list va;
|
|
va_start (va, nargs);
|
|
for (int i=0; i<nargs; ++i) {
|
|
if(i<6) {
|
|
int nn[] = {_DI, _SI, _DX, _CX, _R8, _R9};
|
|
emu->regs[nn[i]].q[0] = va_arg(va, uint64_t);
|
|
} else {
|
|
*p = va_arg(va, uint64_t);
|
|
p++;
|
|
}
|
|
}
|
|
va_end (va);
|
|
|
|
printf_log(LOG_DEBUG, "%04d|signal #%d function handler %p called, RSP=%p\n", GetTID(), R_EDI, (void*)fnc, (void*)R_RSP);
|
|
|
|
int oldquitonlongjmp = emu->flags.quitonlongjmp;
|
|
emu->flags.quitonlongjmp = 2;
|
|
int old_cs = R_CS;
|
|
R_CS = 0x33;
|
|
|
|
emu->eflags.x64 &= ~(1<<F_TF); // this one needs to cleared
|
|
|
|
if(dynarec)
|
|
DynaCall(emu, fnc);
|
|
else
|
|
EmuCall(emu, fnc);
|
|
|
|
if(nargs>6 && !emu->flags.longjmp)
|
|
R_RSP+=((nargs-6+align)*sizeof(void*));
|
|
|
|
if(!emu->flags.longjmp && R_CS==0x33)
|
|
R_CS = old_cs;
|
|
|
|
emu->flags.quitonlongjmp = oldquitonlongjmp;
|
|
|
|
#ifdef DYNAREC
|
|
if (BOX64ENV(dynarec_test)) {
|
|
emu->test.test = 0;
|
|
emu->test.clean = 0;
|
|
}
|
|
#endif
|
|
|
|
if(emu->flags.longjmp) {
|
|
// longjmp inside signal handler, lets grab all relevent value and do the actual longjmp in the signal handler
|
|
emu->flags.longjmp = 0;
|
|
if(sigcontext) {
|
|
sigcontext->uc_mcontext.gregs[X64_R8] = R_R8;
|
|
sigcontext->uc_mcontext.gregs[X64_R9] = R_R9;
|
|
sigcontext->uc_mcontext.gregs[X64_R10] = R_R10;
|
|
sigcontext->uc_mcontext.gregs[X64_R11] = R_R11;
|
|
sigcontext->uc_mcontext.gregs[X64_R12] = R_R12;
|
|
sigcontext->uc_mcontext.gregs[X64_R13] = R_R13;
|
|
sigcontext->uc_mcontext.gregs[X64_R14] = R_R14;
|
|
sigcontext->uc_mcontext.gregs[X64_R15] = R_R15;
|
|
sigcontext->uc_mcontext.gregs[X64_RAX] = R_RAX;
|
|
sigcontext->uc_mcontext.gregs[X64_RCX] = R_RCX;
|
|
sigcontext->uc_mcontext.gregs[X64_RDX] = R_RDX;
|
|
sigcontext->uc_mcontext.gregs[X64_RDI] = R_RDI;
|
|
sigcontext->uc_mcontext.gregs[X64_RSI] = R_RSI;
|
|
sigcontext->uc_mcontext.gregs[X64_RBP] = R_RBP;
|
|
sigcontext->uc_mcontext.gregs[X64_RSP] = R_RSP;
|
|
sigcontext->uc_mcontext.gregs[X64_RBX] = R_RBX;
|
|
sigcontext->uc_mcontext.gregs[X64_RIP] = R_RIP;
|
|
// flags
|
|
sigcontext->uc_mcontext.gregs[X64_EFL] = emu->eflags.x64;
|
|
// get segments
|
|
sigcontext->uc_mcontext.gregs[X64_CSGSFS] = ((uint64_t)(R_CS)) | (((uint64_t)(R_GS))<<16) | (((uint64_t)(R_FS))<<32);
|
|
} else {
|
|
printf_log(LOG_NONE, "Warning, longjmp in signal but no sigcontext to change\n");
|
|
}
|
|
}
|
|
if(exit)
|
|
*exit = emu->exit;
|
|
|
|
uint64_t ret = R_RAX;
|
|
|
|
#ifdef HAVE_TRACE
|
|
trace_start = old_start; trace_end = old_end;
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
EXPORT int my_sigaltstack(x64emu_t* emu, const x64_stack_t* ss, x64_stack_t* oss)
|
|
{
|
|
if(!ss && !oss) { // this is not true, ss can be NULL to retreive oss info only
|
|
errno = EFAULT;
|
|
return -1;
|
|
}
|
|
signal_jmpbuf_active = 1;
|
|
if(sigsetjmp(SIG_JMPBUF, 1)) {
|
|
// segfault while gathering function name...
|
|
errno = EFAULT;
|
|
return -1;
|
|
}
|
|
|
|
x64_stack_t *new_ss = (x64_stack_t*)pthread_getspecific(sigstack_key);
|
|
if(oss) {
|
|
if(!new_ss) {
|
|
oss->ss_flags = SS_DISABLE;
|
|
oss->ss_sp = emu->init_stack;
|
|
oss->ss_size = emu->size_stack;
|
|
} else {
|
|
oss->ss_flags = new_ss->ss_flags;
|
|
oss->ss_sp = new_ss->ss_sp;
|
|
oss->ss_size = new_ss->ss_size;
|
|
}
|
|
}
|
|
if(!ss) {
|
|
signal_jmpbuf_active = 0;
|
|
return 0;
|
|
}
|
|
printf_log(LOG_DEBUG, "%04d|sigaltstack called ss=%p[flags=0x%x, sp=%p, ss=0x%lx], oss=%p\n", GetTID(), ss, ss->ss_flags, ss->ss_sp, ss->ss_size, oss);
|
|
if(ss->ss_flags && ss->ss_flags!=SS_DISABLE && ss->ss_flags!=SS_ONSTACK) {
|
|
errno = EINVAL;
|
|
signal_jmpbuf_active = 0;
|
|
return -1;
|
|
}
|
|
|
|
if(ss->ss_flags==SS_DISABLE) {
|
|
if(new_ss)
|
|
box_free(new_ss);
|
|
pthread_setspecific(sigstack_key, NULL);
|
|
signal_jmpbuf_active = 0;
|
|
return 0;
|
|
}
|
|
|
|
if(!new_ss)
|
|
new_ss = (x64_stack_t*)box_calloc(1, sizeof(x64_stack_t));
|
|
new_ss->ss_flags = 0;
|
|
new_ss->ss_sp = ss->ss_sp;
|
|
new_ss->ss_size = ss->ss_size;
|
|
|
|
pthread_setspecific(sigstack_key, new_ss);
|
|
signal_jmpbuf_active = 0;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DYNAREC
|
|
uintptr_t getX64Address(dynablock_t* db, uintptr_t native_addr)
|
|
{
|
|
uintptr_t x64addr = (uintptr_t)db->x64_addr;
|
|
uintptr_t armaddr = (uintptr_t)db->block;
|
|
if(native_addr<(uintptr_t)db->block || native_addr>(uintptr_t)db->block+db->size)
|
|
return 0;
|
|
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((native_addr>=armaddr) && (native_addr<(armaddr+armsz)))
|
|
return x64addr;
|
|
armaddr+=armsz;
|
|
x64addr+=x64sz;
|
|
} 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)
|
|
if(db && p->uc_mcontext.regs[0]>0x10000) {
|
|
emu = (x64emu_t*)p->uc_mcontext.regs[0];
|
|
}
|
|
#elif defined(LA64)
|
|
if(db && p->uc_mcontext.__gregs[4]>0x10000) {
|
|
emu = (x64emu_t*)p->uc_mcontext.__gregs[4];
|
|
}
|
|
#elif defined(RV64)
|
|
if(db && p->uc_mcontext.__gregs[25]>0x10000) {
|
|
emu = (x64emu_t*)p->uc_mcontext.__gregs[25];
|
|
}
|
|
#else
|
|
#error Unsupported Architecture
|
|
#endif //arch
|
|
return emu;
|
|
}
|
|
#endif
|
|
int write_opcode(uintptr_t rip, uintptr_t native_ip, int is32bits);
|
|
void adjustregs(x64emu_t* emu) {
|
|
// tests some special cases
|
|
uint8_t* mem = (uint8_t*)R_RIP;
|
|
rex_t rex = {0};
|
|
int rep = 0;
|
|
int is66 = 0;
|
|
int idx = 0;
|
|
rex.is32bits = (R_CS==0x0023);
|
|
while ((mem[idx]>=0x40 && mem[idx]<=0x4f && !rex.is32bits) || mem[idx]==0xF2 || mem[idx]==0xF3 || mem[idx]==0x66) {
|
|
switch(mem[idx]) {
|
|
case 0x40 ... 0x4f:
|
|
rex.rex = mem[idx];
|
|
break;
|
|
case 0xF2:
|
|
case 0xF3:
|
|
rep = mem[idx]-0xF1;
|
|
break;
|
|
case 0x66:
|
|
is66 = 1;
|
|
break;
|
|
}
|
|
++idx;
|
|
}
|
|
dynarec_log(LOG_INFO, "Checking opcode: rex=%02hhx is32bits=%d, rep=%d is66=%d %02hhX %02hhX %02hhX %02hhX\n", rex.rex, rex.is32bits, rep, is66, mem[idx+0], mem[idx+1], mem[idx+2], mem[idx+3]);
|
|
#ifdef DYNAREC
|
|
#ifdef ARM64
|
|
if(mem[idx+0]==0xA4) {
|
|
// (rep) movsb, read done, write not...
|
|
if(emu->eflags.f._F_DF)
|
|
R_RSI++;
|
|
else
|
|
R_RSI--;
|
|
return;
|
|
}
|
|
if(mem[idx+0]==0xA5) {
|
|
// (rep) movsd, read done, write not...
|
|
int step = (emu->eflags.f._F_DF)?-1:+1;
|
|
if(rex.w) step*=8;
|
|
else if(is66) step *=2;
|
|
else step*=4;
|
|
R_RSI-=step;
|
|
return;
|
|
}
|
|
if(mem[idx+0]==0x8F && (mem[idx+1]&0xc0)!=0xc0) {
|
|
// POP Ed, issue on write address, restore RSP as in before the pop
|
|
R_RSP -= is66?2:(rex.is32bits?4:8);
|
|
}
|
|
#elif defined(LA64)
|
|
#elif defined(RV64)
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void copyUCTXreg2Emu(x64emu_t* emu, ucontext_t* p, uintptr_t ip) {
|
|
#ifdef DYNAREC
|
|
#ifdef ARM64
|
|
emu->regs[_AX].q[0] = p->uc_mcontext.regs[10];
|
|
emu->regs[_CX].q[0] = p->uc_mcontext.regs[11];
|
|
emu->regs[_DX].q[0] = p->uc_mcontext.regs[12];
|
|
emu->regs[_BX].q[0] = p->uc_mcontext.regs[13];
|
|
emu->regs[_SP].q[0] = p->uc_mcontext.regs[14];
|
|
emu->regs[_BP].q[0] = p->uc_mcontext.regs[15];
|
|
emu->regs[_SI].q[0] = p->uc_mcontext.regs[16];
|
|
emu->regs[_DI].q[0] = p->uc_mcontext.regs[17];
|
|
emu->regs[_R8].q[0] = p->uc_mcontext.regs[18];
|
|
emu->regs[_R9].q[0] = p->uc_mcontext.regs[19];
|
|
emu->regs[_R10].q[0] = p->uc_mcontext.regs[20];
|
|
emu->regs[_R11].q[0] = p->uc_mcontext.regs[21];
|
|
emu->regs[_R12].q[0] = p->uc_mcontext.regs[22];
|
|
emu->regs[_R13].q[0] = p->uc_mcontext.regs[23];
|
|
emu->regs[_R14].q[0] = p->uc_mcontext.regs[24];
|
|
emu->regs[_R15].q[0] = p->uc_mcontext.regs[25];
|
|
emu->ip.q[0] = ip;
|
|
emu->eflags.x64 = p->uc_mcontext.regs[26];
|
|
#elif defined(LA64)
|
|
emu->regs[_AX].q[0] = p->uc_mcontext.__gregs[12];
|
|
emu->regs[_CX].q[0] = p->uc_mcontext.__gregs[13];
|
|
emu->regs[_DX].q[0] = p->uc_mcontext.__gregs[14];
|
|
emu->regs[_BX].q[0] = p->uc_mcontext.__gregs[15];
|
|
emu->regs[_SP].q[0] = p->uc_mcontext.__gregs[16];
|
|
emu->regs[_BP].q[0] = p->uc_mcontext.__gregs[17];
|
|
emu->regs[_SI].q[0] = p->uc_mcontext.__gregs[18];
|
|
emu->regs[_DI].q[0] = p->uc_mcontext.__gregs[19];
|
|
emu->regs[_R8].q[0] = p->uc_mcontext.__gregs[23];
|
|
emu->regs[_R9].q[0] = p->uc_mcontext.__gregs[24];
|
|
emu->regs[_R10].q[0] = p->uc_mcontext.__gregs[25];
|
|
emu->regs[_R11].q[0] = p->uc_mcontext.__gregs[26];
|
|
emu->regs[_R12].q[0] = p->uc_mcontext.__gregs[27];
|
|
emu->regs[_R13].q[0] = p->uc_mcontext.__gregs[28];
|
|
emu->regs[_R14].q[0] = p->uc_mcontext.__gregs[29];
|
|
emu->regs[_R15].q[0] = p->uc_mcontext.__gregs[30];
|
|
emu->ip.q[0] = ip;
|
|
emu->eflags.x64 = p->uc_mcontext.__gregs[31];
|
|
#elif defined(RV64)
|
|
emu->regs[_AX].q[0] = p->uc_mcontext.__gregs[16];
|
|
emu->regs[_CX].q[0] = p->uc_mcontext.__gregs[13];
|
|
emu->regs[_DX].q[0] = p->uc_mcontext.__gregs[12];
|
|
emu->regs[_BX].q[0] = p->uc_mcontext.__gregs[24];
|
|
emu->regs[_SP].q[0] = p->uc_mcontext.__gregs[9];
|
|
emu->regs[_BP].q[0] = p->uc_mcontext.__gregs[8];
|
|
emu->regs[_SI].q[0] = p->uc_mcontext.__gregs[11];
|
|
emu->regs[_DI].q[0] = p->uc_mcontext.__gregs[10];
|
|
emu->regs[_R8].q[0] = p->uc_mcontext.__gregs[14];
|
|
emu->regs[_R9].q[0] = p->uc_mcontext.__gregs[15];
|
|
emu->regs[_R10].q[0] = p->uc_mcontext.__gregs[26];
|
|
emu->regs[_R11].q[0] = p->uc_mcontext.__gregs[27];
|
|
emu->regs[_R12].q[0] = p->uc_mcontext.__gregs[18];
|
|
emu->regs[_R13].q[0] = p->uc_mcontext.__gregs[19];
|
|
emu->regs[_R14].q[0] = p->uc_mcontext.__gregs[20];
|
|
emu->regs[_R15].q[0] = p->uc_mcontext.__gregs[21];
|
|
emu->ip.q[0] = ip;
|
|
emu->eflags.x64 = p->uc_mcontext.__gregs[23];
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
KHASH_SET_INIT_INT64(unaligned)
|
|
static kh_unaligned_t *unaligned = NULL;
|
|
|
|
void add_unaligned_address(uintptr_t addr)
|
|
{
|
|
if(!unaligned)
|
|
unaligned = kh_init(unaligned);
|
|
khint_t k;
|
|
int ret;
|
|
k = kh_put(unaligned, unaligned, addr, &ret); // just add
|
|
}
|
|
|
|
int is_addr_unaligned(uintptr_t addr)
|
|
{
|
|
if(!unaligned)
|
|
return 0;
|
|
khint_t k = kh_get(unaligned, unaligned, addr);
|
|
return (k==kh_end(unaligned))?0:1;
|
|
}
|
|
|
|
#ifdef DYNAREC
|
|
int mark_db_unaligned(dynablock_t* db, uintptr_t x64pc)
|
|
{
|
|
add_unaligned_address(x64pc);
|
|
db->hash++; // dirty the block
|
|
MarkDynablock(db); // and mark it
|
|
if(BOX64ENV(showsegv)) printf_log(LOG_INFO, "Marked db %p as dirty, and address %p as needing unaligned handling\n", db, (void*)x64pc);
|
|
return 2; // marked, exit handling...
|
|
}
|
|
#endif
|
|
|
|
int sigbus_specialcases(siginfo_t* info, void * ucntx, void* pc, void* _fpsimd, dynablock_t* db, uintptr_t x64pc)
|
|
{
|
|
if((uintptr_t)pc<0x10000)
|
|
return 0;
|
|
#ifdef DYNAREC
|
|
if(ARCH_UNALIGNED(db, x64pc))
|
|
/*return*/ mark_db_unaligned(db, x64pc); // don't force an exit for now
|
|
#endif
|
|
#ifdef ARM64
|
|
ucontext_t *p = (ucontext_t *)ucntx;
|
|
uint32_t opcode = *(uint32_t*)pc;
|
|
struct fpsimd_context *fpsimd = (struct fpsimd_context *)_fpsimd;
|
|
//printf_log(LOG_INFO, "Checking SIGBUS special cases with pc=%p, opcode=%x, fpsimd=%p\n", pc, opcode, fpsimd);
|
|
if((opcode&0b10111111110000000000000000000000)==0b10111001000000000000000000000000) {
|
|
// this is STR
|
|
int scale = (opcode>>30)&3;
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
uint64_t offset = (opcode>>10)&0b111111111111;
|
|
offset<<=scale;
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest] + offset);
|
|
uint64_t value = p->uc_mcontext.regs[val];
|
|
if(scale==3 && (((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<2; ++i)
|
|
((volatile uint32_t*)addr)[i] = (value>>(i*32))&0xffffffff;
|
|
} else
|
|
for(int i=0; i<(1<<scale); ++i)
|
|
addr[i] = (value>>(i*8))&0xff;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b10111111111000000000110000000000) == 0b10111000000000000000000000000000) {
|
|
// this is a STUR that SIGBUS if accessing unaligned device memory
|
|
int size = 1<<((opcode>>30)&3);
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
int64_t offset = (opcode>>12)&0b111111111;
|
|
if((offset>>(9-1))&1)
|
|
offset |= (0xffffffffffffffffll<<9);
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest] + offset);
|
|
uint64_t value = p->uc_mcontext.regs[val];
|
|
if(size==8 && (((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<2; ++i)
|
|
((volatile uint32_t*)addr)[i] = (value>>(i*32))&0xffffffff;
|
|
} else
|
|
for(int i=0; i<size; ++i)
|
|
addr[i] = (value>>(i*8))&0xff;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b00111111010000000000000000000000)==0b00111101000000000000000000000000) {
|
|
// this is VSTR
|
|
int scale = (opcode>>30)&3;
|
|
if((opcode>>23)&1)
|
|
scale+=4;
|
|
if(scale>4)
|
|
return 0;
|
|
if(!fpsimd)
|
|
return 0;
|
|
uint64_t offset = (opcode>>10)&0b111111111111;
|
|
offset<<=scale;
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest] + offset);
|
|
__uint128_t value = fpsimd->vregs[val];
|
|
if(scale>2 && (((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<(1<<(scale-2)); ++i)
|
|
((volatile uint32_t*)addr)[i] = (value>>(i*32))&0xffffffff;
|
|
} else
|
|
for(int i=0; i<(1<<scale); ++i)
|
|
addr[i] = (value>>(i*8))&0xff;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b00111111011000000000110000000000)==0b00111100000000000000000000000000) {
|
|
// this is VSTRU
|
|
int scale = (opcode>>30)&3;
|
|
if((opcode>>23)&1)
|
|
scale+=4;
|
|
if(scale>4)
|
|
return 0;
|
|
if(!fpsimd)
|
|
return 0;
|
|
int64_t offset = (opcode>>12)&0b111111111;
|
|
if((offset>>(9-1))&1)
|
|
offset |= (0xffffffffffffffffll<<9);
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest] + offset);
|
|
__uint128_t value = fpsimd->vregs[val];
|
|
if(scale>2 && (((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<(1<<(scale-2)); ++i)
|
|
((volatile uint32_t*)addr)[i] = (value>>(i*32))&0xffffffff;
|
|
} else
|
|
for(int i=0; i<(1<<scale); ++i)
|
|
addr[i] = (value>>(i*8))&0xff;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b00111111010000000000000000000000)==0b00111101010000000000000000000000) {
|
|
// this is VLDR
|
|
int scale = (opcode>>30)&3;
|
|
if((opcode>>23)&1)
|
|
scale+=4;
|
|
if(scale>4)
|
|
return 0;
|
|
if(!fpsimd)
|
|
return 0;
|
|
uint64_t offset = (opcode>>10)&0b111111111111;
|
|
offset<<=scale;
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest] + offset);
|
|
__uint128_t value = 0;
|
|
if(scale>2 && (((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<(1<<(scale-2)); ++i)
|
|
value |= ((__uint128_t)(((volatile uint32_t*)addr)[i]))<<(i*32);
|
|
} else
|
|
for(int i=0; i<(1<<scale); ++i)
|
|
value |= ((__uint128_t)addr[i])<<(i*8);
|
|
fpsimd->vregs[val] = value;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b00111111011000000000110000000000)==0b00111100010000000000000000000000) {
|
|
// this is VLDRU
|
|
int scale = (opcode>>30)&3;
|
|
if((opcode>>23)&1)
|
|
scale+=4;
|
|
if(scale>4)
|
|
return 0;
|
|
if(!fpsimd)
|
|
return 0;
|
|
int64_t offset = (opcode>>12)&0b111111111;
|
|
if((offset>>(9-1))&1)
|
|
offset |= (0xffffffffffffffffll<<9);
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest] + offset);
|
|
__uint128_t value = 0;
|
|
if(scale>2 && (((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<(1<<(scale-2)); ++i)
|
|
value |= ((__uint128_t)(((volatile uint32_t*)addr)[i]))<<(i*32);
|
|
} else
|
|
for(int i=0; i<(1<<scale); ++i)
|
|
value |= ((__uint128_t)addr[i])<<(i*8);
|
|
fpsimd->vregs[val] = value;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b10111111110000000000000000000000)==0b10111001010000000000000000000000) {
|
|
// this is LDR
|
|
int scale = (opcode>>30)&3;
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
uint64_t offset = (opcode>>10)&0b111111111111;
|
|
offset<<=scale;
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest] + offset);
|
|
uint64_t value = 0;
|
|
if(scale==3 && (((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<2; ++i)
|
|
value |= ((uint64_t)((volatile uint32_t*)addr)[i]) << (i*32);
|
|
} else
|
|
for(int i=0; i<(1<<scale); ++i)
|
|
value |= ((uint64_t)addr[i]) << (i*8);
|
|
p->uc_mcontext.regs[val] = value;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b10111111111000000000110000000000) == 0b10111000010000000000000000000000) {
|
|
// this is a LDUR
|
|
int size = 1<<((opcode>>30)&3);
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
int64_t offset = (opcode>>12)&0b111111111;
|
|
if((offset>>(9-1))&1)
|
|
offset |= (0xffffffffffffffffll<<9);
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest] + offset);
|
|
uint64_t value = 0;
|
|
if(size==8 && (((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<2; ++i)
|
|
value |= ((uint64_t)((volatile uint32_t*)addr)[i]) << (i*32);
|
|
} else
|
|
for(int i=0; i<size; ++i)
|
|
value |= ((uint64_t)addr[i]) << (i*8);
|
|
p->uc_mcontext.regs[val] = value;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b11111111110000000000000000000000)==0b01111001000000000000000000000000) {
|
|
// this is STRH
|
|
int scale = (opcode>>30)&3;
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
uint64_t offset = (opcode>>10)&0b111111111111;
|
|
offset<<=scale;
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest] + offset);
|
|
uint64_t value = p->uc_mcontext.regs[val];
|
|
for(int i=0; i<(1<<scale); ++i)
|
|
addr[i] = (value>>(i*8))&0xff;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b11111111111000000000110000000000)==0b01111000000000000000000000000000) {
|
|
// this is STURH
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
int64_t offset = (opcode>>12)&0b111111111;
|
|
if((offset>>(9-1))&1)
|
|
offset |= (0xffffffffffffffffll<<9);
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest] + offset);
|
|
uint64_t value = p->uc_mcontext.regs[val];
|
|
for(int i=0; i<2; ++i)
|
|
addr[i] = (value>>(i*8))&0xff;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b11111111111000000000110000000000)==0b01111000001000000000100000000000) {
|
|
// this is STRH reg, reg
|
|
int scale = (opcode>>30)&3;
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
int dest2 = (opcode>>16)&31;
|
|
int option = (opcode>>13)&0b111;
|
|
int S = (opcode>>12)&1;
|
|
if(option!=0b011)
|
|
return 0; // only LSL is supported
|
|
uint64_t offset = p->uc_mcontext.regs[dest2]<<S;
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest] + offset);
|
|
uint64_t value = p->uc_mcontext.regs[val];
|
|
for(int i=0; i<(1<<scale); ++i)
|
|
addr[i] = (value>>(i*8))&0xff;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b11111111110000000000000000000000)==0b10101001000000000000000000000000) {
|
|
// This is STP reg1, reg2, [reg3 + off]
|
|
int scale = 2+((opcode>>31)&1);
|
|
int val1 = opcode&31;
|
|
int val2 = (opcode>>10)&31;
|
|
int dest = (opcode>>5)&31;
|
|
int64_t offset = (opcode>>15)&0b1111111;
|
|
if((offset>>(7-1))&1)
|
|
offset |= (0xffffffffffffffffll<<7);
|
|
offset <<= scale;
|
|
uintptr_t addr= p->uc_mcontext.regs[dest] + offset;
|
|
if((((uintptr_t)addr)&3)==0) {
|
|
((volatile uint32_t*)addr)[0] = p->uc_mcontext.regs[val1];
|
|
((volatile uint32_t*)addr)[1] = p->uc_mcontext.regs[val2];
|
|
} else {
|
|
__uint128_t value = ((__uint128_t)p->uc_mcontext.regs[val2])<<64 | p->uc_mcontext.regs[val1];
|
|
for(int i=0; i<(1<<scale); ++i)
|
|
((volatile uint8_t*)addr)[i] = (value>>(i*8))&0xff;
|
|
}
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b11111111110000000000000000000000)==0b10101101000000000000000000000000) {
|
|
// This is (V)STP qreg1, qreg2, [reg3 + off]
|
|
int scale = 2+((opcode>>30)&3);
|
|
int val1 = opcode&31;
|
|
int val2 = (opcode>>10)&31;
|
|
int dest = (opcode>>5)&31;
|
|
int64_t offset = (opcode>>15)&0b1111111;
|
|
if((offset>>(7-1))&1)
|
|
offset |= (0xffffffffffffffffll<<7);
|
|
offset <<= scale;
|
|
uintptr_t addr= p->uc_mcontext.regs[dest] + offset;
|
|
if((((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<4; ++i)
|
|
((volatile uint32_t*)addr)[0+i] = (fpsimd->vregs[val1]>>(i*32))&0xffffffff;
|
|
for(int i=0; i<4; ++i)
|
|
((volatile uint32_t*)addr)[4+i] = (fpsimd->vregs[val2]>>(i*32))&0xffffffff;
|
|
} else {
|
|
for(int i=0; i<16; ++i)
|
|
((volatile uint8_t*)addr)[i] = (fpsimd->vregs[val1]>>(i*8))&0xff;
|
|
for(int i=0; i<16; ++i)
|
|
((volatile uint8_t*)addr)[16+i] = (fpsimd->vregs[val2]>>(i*8))&0xff;
|
|
}
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b10111111111111111111110000000000)==0b00001101000000001000010000000000) {
|
|
// this is ST1.D
|
|
int idx = (opcode>>30)&1;
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest]);
|
|
uint64_t value = fpsimd->vregs[val]>>(idx*64);
|
|
if((((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<2; ++i)
|
|
((volatile uint32_t*)addr)[i] = (value>>(i*32))&0xffffffff;
|
|
} else
|
|
for(int i=0; i<8; ++i)
|
|
addr[i] = (value>>(i*8))&0xff;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b10111111111000000000110000000000) == 0b10111000010000000000010000000000) {
|
|
// this is a LDR postoffset
|
|
int size = 1<<((opcode>>30)&3);
|
|
int val = opcode&31;
|
|
int dest = (opcode>>5)&31;
|
|
int64_t offset = (opcode>>12)&0b111111111;
|
|
if((offset>>(9-1))&1)
|
|
offset |= (0xffffffffffffffffll<<9);
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[dest]);
|
|
uint64_t value = 0;
|
|
if(size==8 && (((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<2; ++i)
|
|
value |= ((uint64_t)((volatile uint32_t*)addr)[i]) << (i*32);
|
|
} else
|
|
for(int i=0; i<size; ++i)
|
|
value |= ((uint64_t)addr[i]) << (i*8);
|
|
p->uc_mcontext.regs[val] = value;
|
|
p->uc_mcontext.regs[dest] += offset;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
if((opcode&0b10111111111000000000110000000000) == 0b10111000000000000000010000000000) {
|
|
// this is a STR postoffset
|
|
int size = 1<<((opcode>>30)&3);
|
|
int val = opcode&31;
|
|
int src = (opcode>>5)&31;
|
|
int64_t offset = (opcode>>12)&0b111111111;
|
|
if((offset>>(9-1))&1)
|
|
offset |= (0xffffffffffffffffll<<9);
|
|
volatile uint8_t* addr = (void*)(p->uc_mcontext.regs[src]);
|
|
uint64_t value = p->uc_mcontext.regs[val];
|
|
if(size==8 && (((uintptr_t)addr)&3)==0) {
|
|
for(int i=0; i<2; ++i)
|
|
((volatile uint32_t*)addr)[i] = (value>>(i*32))&0xffffffff;
|
|
} else
|
|
for(int i=0; i<size; ++i)
|
|
addr[i] = (value>>(i*8))&0xff;
|
|
p->uc_mcontext.regs[src] += offset;
|
|
p->uc_mcontext.pc+=4; // go to next opcode
|
|
return 1;
|
|
}
|
|
#elif RV64
|
|
#define GET_FIELD(v, high, low) (((v) >> low) & ((1ULL << (high - low + 1)) - 1))
|
|
#define SIGN_EXT(val, val_sz) (((int32_t)(val) << (32 - (val_sz))) >> (32 - (val_sz)))
|
|
|
|
ucontext_t *p = (ucontext_t *)ucntx;
|
|
uint32_t inst = *(uint32_t*)pc;
|
|
|
|
uint32_t funct3 = GET_FIELD(inst, 14, 12);
|
|
uint32_t opcode = GET_FIELD(inst, 6, 0);
|
|
if ((opcode == 0b0100011 || opcode == 0b0100111 /* F */) && (funct3 == 0b010 /* (F)SW */ || funct3 == 0b011 /* (F)SD */ || funct3 == 0b001 /* SH */)) {
|
|
int val = (inst >> 20) & 0x1f;
|
|
int dest = (inst >> 15) & 0x1f;
|
|
int64_t imm = (GET_FIELD(inst, 31, 25) << 5) | (GET_FIELD(inst, 11, 7));
|
|
imm = SIGN_EXT(imm, 12);
|
|
volatile uint8_t *addr = (void *)(p->uc_mcontext.__gregs[dest] + imm);
|
|
uint64_t value = opcode == 0b0100011 ? p->uc_mcontext.__gregs[val] : p->uc_mcontext.__fpregs.__d.__f[val<<1];
|
|
for(int i = 0; i < (funct3 == 0b010 ? 4 : funct3 == 0b011 ? 8 : 2); ++i) {
|
|
addr[i] = (value >> (i * 8)) & 0xff;
|
|
}
|
|
p->uc_mcontext.__gregs[0] += 4; // pc += 4
|
|
return 1;
|
|
} else {
|
|
printf_log(LOG_NONE, "Unsupported SIGBUS special cases with pc=%p, opcode=%x\n", pc, inst);
|
|
}
|
|
|
|
#undef GET_FIELD
|
|
#undef SIGN_EXT
|
|
#endif
|
|
return 0;
|
|
#undef CHECK
|
|
}
|
|
|
|
#ifdef USE_CUSTOM_MUTEX
|
|
extern uint32_t mutex_prot;
|
|
extern uint32_t mutex_blocks;
|
|
#else
|
|
extern pthread_mutex_t mutex_prot;
|
|
extern pthread_mutex_t mutex_blocks;
|
|
#endif
|
|
|
|
// unlock mutex that are locked by current thread (for signal handling). Return a mask of unlock mutex
|
|
int unlockMutex()
|
|
{
|
|
int ret = 0;
|
|
int i;
|
|
#ifdef USE_CUSTOM_MUTEX
|
|
uint32_t tid = (uint32_t)GetTID();
|
|
#define GO(A, B) \
|
|
i = (native_lock_storeifref2_d(&A, 0, tid) == tid); \
|
|
if (i) { \
|
|
ret |= (1 << B); \
|
|
}
|
|
#else
|
|
#define GO(A, B) \
|
|
i = checkUnlockMutex(&A); \
|
|
if (i) { \
|
|
ret |= (1 << B); \
|
|
}
|
|
#endif
|
|
|
|
GO(mutex_blocks, 0)
|
|
GO(mutex_prot, 1)
|
|
|
|
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)
|
|
GO(my_context->mutex_bridge, 11)
|
|
#undef GO
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef BOX32
|
|
void my_sigactionhandler_oldcode_32(x64emu_t* emu, int32_t sig, int simple, siginfo_t* info, void * ucntx, int* old_code, void* cur_db);
|
|
#endif
|
|
void 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 log_minimum = (BOX64ENV(showsegv))?LOG_NONE:LOG_DEBUG;
|
|
|
|
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];
|
|
// get that actual ESP first!
|
|
if(!emu)
|
|
emu = thread_get_emu();
|
|
uintptr_t frame = R_RSP;
|
|
#if defined(DYNAREC)
|
|
#if defined(ARM64)
|
|
dynablock_t* db = (dynablock_t*)cur_db;//FindDynablockFromNativeAddress(pc);
|
|
ucontext_t *p = (ucontext_t *)ucntx;
|
|
void* pc = NULL;
|
|
if(p) {
|
|
pc = (void*)p->uc_mcontext.pc;
|
|
if(db)
|
|
frame = (uintptr_t)p->uc_mcontext.regs[10+_SP];
|
|
}
|
|
#elif defined(LA64)
|
|
dynablock_t* db = (dynablock_t*)cur_db;//FindDynablockFromNativeAddress(pc);
|
|
ucontext_t *p = (ucontext_t *)ucntx;
|
|
void* pc = NULL;
|
|
if(p) {
|
|
pc = (void*)p->uc_mcontext.__pc;
|
|
if(db)
|
|
frame = (uintptr_t)p->uc_mcontext.__gregs[12+_SP];
|
|
}
|
|
#elif defined(RV64)
|
|
dynablock_t* db = (dynablock_t*)cur_db;//FindDynablockFromNativeAddress(pc);
|
|
ucontext_t *p = (ucontext_t *)ucntx;
|
|
void* pc = NULL;
|
|
if(p) {
|
|
pc = (void*)p->uc_mcontext.__gregs[0];
|
|
if(db)
|
|
frame = (uintptr_t)p->uc_mcontext.__gregs[9];
|
|
}
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
#else
|
|
(void)ucntx; (void)cur_db;
|
|
void* pc = NULL;
|
|
#endif
|
|
// setup libc context stack frame, on caller stack
|
|
frame = frame&~15;
|
|
|
|
// stack tracking
|
|
x64_stack_t *new_ss = my_context->onstack[sig]?(x64_stack_t*)pthread_getspecific(sigstack_key):NULL;
|
|
int used_stack = 0;
|
|
if(new_ss) {
|
|
if(new_ss->ss_flags == SS_ONSTACK) { // already using it!
|
|
frame = ((uintptr_t)emu->regs[_SP].q[0] - 128) & ~0x0f;
|
|
} else {
|
|
frame = (uintptr_t)(((uintptr_t)new_ss->ss_sp + new_ss->ss_size - 16) & ~0x0f);
|
|
used_stack = 1;
|
|
new_ss->ss_flags = SS_ONSTACK;
|
|
}
|
|
} else {
|
|
frame -= 0x200; // redzone
|
|
}
|
|
|
|
// TODO: do I need to really setup 2 stack frame? That doesn't seems right!
|
|
// setup stack frame
|
|
frame -= 512+64+16*16;
|
|
void* xstate = (void*)frame;
|
|
frame -= sizeof(siginfo_t);
|
|
siginfo_t* info2 = (siginfo_t*)frame;
|
|
memcpy(info2, info, sizeof(siginfo_t));
|
|
// try to fill some sigcontext....
|
|
frame -= sizeof(x64_ucontext_t);
|
|
x64_ucontext_t *sigcontext = (x64_ucontext_t*)frame;
|
|
// get general register
|
|
sigcontext->uc_mcontext.gregs[X64_R8] = R_R8;
|
|
sigcontext->uc_mcontext.gregs[X64_R9] = R_R9;
|
|
sigcontext->uc_mcontext.gregs[X64_R10] = R_R10;
|
|
sigcontext->uc_mcontext.gregs[X64_R11] = R_R11;
|
|
sigcontext->uc_mcontext.gregs[X64_R12] = R_R12;
|
|
sigcontext->uc_mcontext.gregs[X64_R13] = R_R13;
|
|
sigcontext->uc_mcontext.gregs[X64_R14] = R_R14;
|
|
sigcontext->uc_mcontext.gregs[X64_R15] = R_R15;
|
|
sigcontext->uc_mcontext.gregs[X64_RAX] = R_RAX;
|
|
sigcontext->uc_mcontext.gregs[X64_RCX] = R_RCX;
|
|
sigcontext->uc_mcontext.gregs[X64_RDX] = R_RDX;
|
|
sigcontext->uc_mcontext.gregs[X64_RDI] = R_RDI;
|
|
sigcontext->uc_mcontext.gregs[X64_RSI] = R_RSI;
|
|
sigcontext->uc_mcontext.gregs[X64_RBP] = R_RBP;
|
|
sigcontext->uc_mcontext.gregs[X64_RSP] = R_RSP;
|
|
sigcontext->uc_mcontext.gregs[X64_RBX] = R_RBX;
|
|
sigcontext->uc_mcontext.gregs[X64_RIP] = R_RIP;
|
|
// flags
|
|
sigcontext->uc_mcontext.gregs[X64_EFL] = emu->eflags.x64;
|
|
// get segments
|
|
sigcontext->uc_mcontext.gregs[X64_CSGSFS] = ((uint64_t)(R_CS)) | (((uint64_t)(R_GS))<<16) | (((uint64_t)(R_FS))<<32);
|
|
if(R_CS==0x23) {
|
|
// trucate regs to 32bits, just in case
|
|
#define GO(R) sigcontext->uc_mcontext.gregs[X64_R##R]&=0xFFFFFFFF
|
|
GO(AX);
|
|
GO(CX);
|
|
GO(DX);
|
|
GO(DI);
|
|
GO(SI);
|
|
GO(BP);
|
|
GO(SP);
|
|
GO(BX);
|
|
GO(IP);
|
|
#undef GO
|
|
}
|
|
// get FloatPoint status
|
|
sigcontext->uc_mcontext.fpregs = xstate;//(struct x64_libc_fpstate*)&sigcontext->xstate;
|
|
fpu_xsave_mask(emu, xstate, 0, 0b111);
|
|
memcpy(&sigcontext->xstate, xstate, sizeof(sigcontext->xstate));
|
|
((struct x64_fpstate*)xstate)->res[12] = 0x46505853; // magic number to signal an XSTATE type of fpregs
|
|
((struct x64_fpstate*)xstate)->res[13] = 0; // offset to xstate after this?
|
|
// get signal mask
|
|
|
|
if(new_ss) {
|
|
sigcontext->uc_stack.ss_sp = new_ss->ss_sp;
|
|
sigcontext->uc_stack.ss_size = new_ss->ss_size;
|
|
sigcontext->uc_stack.ss_flags = new_ss->ss_flags;
|
|
} else
|
|
sigcontext->uc_stack.ss_flags = SS_DISABLE;
|
|
// Try to guess some X64_TRAPNO
|
|
/*
|
|
TRAP_x86_DIVIDE = 0, // Division by zero exception
|
|
TRAP_x86_TRCTRAP = 1, // Single-step exception
|
|
TRAP_x86_NMI = 2, // NMI interrupt
|
|
TRAP_x86_BPTFLT = 3, // Breakpoint exception
|
|
TRAP_x86_OFLOW = 4, // Overflow exception
|
|
TRAP_x86_BOUND = 5, // Bound range exception
|
|
TRAP_x86_PRIVINFLT = 6, // Invalid opcode exception
|
|
TRAP_x86_DNA = 7, // Device not available exception
|
|
TRAP_x86_DOUBLEFLT = 8, // Double fault exception
|
|
TRAP_x86_FPOPFLT = 9, // Coprocessor segment overrun
|
|
TRAP_x86_TSSFLT = 10, // Invalid TSS exception
|
|
TRAP_x86_SEGNPFLT = 11, // Segment not present exception
|
|
TRAP_x86_STKFLT = 12, // Stack fault
|
|
TRAP_x86_PROTFLT = 13, // General protection fault
|
|
TRAP_x86_PAGEFLT = 14, // Page fault
|
|
TRAP_x86_ARITHTRAP = 16, // Floating point exception
|
|
TRAP_x86_ALIGNFLT = 17, // Alignment check exception
|
|
TRAP_x86_MCHK = 18, // Machine check exception
|
|
TRAP_x86_CACHEFLT = 19 // SIMD exception (via SIGFPE) if CPU is SSE capable otherwise Cache flush exception (via SIGSEV)
|
|
*/
|
|
uint32_t prot = getProtection((uintptr_t)info->si_addr);
|
|
uint32_t mmapped = memExist((uintptr_t)info->si_addr);
|
|
uint32_t sysmapped = (info->si_addr<(void*)box64_pagesize)?1:mmapped;
|
|
uint32_t real_prot = 0;
|
|
if(prot&PROT_READ) real_prot|=PROT_READ;
|
|
if(prot&PROT_WRITE) real_prot|=PROT_WRITE;
|
|
if(prot&PROT_EXEC) real_prot|=PROT_WRITE;
|
|
if(prot&PROT_DYNAREC) real_prot|=PROT_WRITE;
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 0;
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 0;
|
|
if(sig==SIGBUS)
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 17;
|
|
else if(sig==SIGSEGV) {
|
|
if((uintptr_t)info->si_addr == sigcontext->uc_mcontext.gregs[X64_RIP]) {
|
|
if(info->si_errno==0xbad0) {
|
|
//bad opcode
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 0;
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 13;
|
|
info2->si_code = 128;
|
|
info2->si_errno = 0;
|
|
info2->si_addr = NULL;
|
|
} else if (info->si_errno==0xecec) {
|
|
// no excute bit on segment
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 0x14|((sysmapped && !(real_prot&PROT_READ))?0:1);
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 14;
|
|
if(!mmapped) info2->si_code = 1;
|
|
info2->si_errno = 0;
|
|
} else if (info->si_errno==0xb09d) {
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 0;
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 5;
|
|
info2->si_errno = 0;
|
|
}else {
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 0x14|((sysmapped && !(real_prot&PROT_READ))?0:1);
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 14;
|
|
}
|
|
} else {
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 14;
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 4|((sysmapped && !(real_prot&PROT_READ))?0:1);
|
|
if(write_opcode(sigcontext->uc_mcontext.gregs[X64_RIP], (uintptr_t)pc, (R_CS==0x23)))
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] |= 2;
|
|
}
|
|
if(info->si_code == SEGV_ACCERR && old_code)
|
|
*old_code = -1;
|
|
if(info->si_errno==0x1234) {
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 13;
|
|
info2->si_errno = 0;
|
|
} else if(info->si_errno==0xdead) {
|
|
// INT x
|
|
uint8_t int_n = info->si_code;
|
|
info2->si_errno = 0;
|
|
info2->si_code = 128;
|
|
info2->si_addr = NULL;
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 13;
|
|
// some special cases...
|
|
if(int_n==3) {
|
|
info2->si_signo = SIGTRAP;
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 3;
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 0;
|
|
} else if(int_n==0x04) {
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 4;
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 0;
|
|
} else if (int_n==0x29 || int_n==0x2c || int_n==0x2d) {
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 0x02|(int_n<<3);
|
|
} else {
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 0x0a|(int_n<<3);
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 13;
|
|
}
|
|
} else if(info->si_errno==0xcafe) { // divide by 0
|
|
info2->si_errno = 0;
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 0;
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 0;
|
|
info2->si_signo = SIGFPE;
|
|
}
|
|
} else if(sig==SIGFPE) {
|
|
if (info->si_code == FPE_INTOVF)
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 4;
|
|
else
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 19;
|
|
} else if(sig==SIGILL) {
|
|
info2->si_code = 2;
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = 6;
|
|
} else if(sig==SIGTRAP) {
|
|
if(info->si_code==1) { //single step
|
|
info2->si_code = 2;
|
|
info2->si_addr = (void*)sigcontext->uc_mcontext.gregs[X64_RIP];
|
|
} else
|
|
info2->si_code = 128;
|
|
sigcontext->uc_mcontext.gregs[X64_TRAPNO] = info->si_code;
|
|
sigcontext->uc_mcontext.gregs[X64_ERR] = 0;
|
|
}
|
|
//TODO: SIGABRT generate what?
|
|
printf_log((sig==10)?LOG_DEBUG:log_minimum, "Signal %d: si_addr=%p, TRAPNO=%d, ERR=%d, RIP=%p, prot=%x, mmapped:%d\n", sig, (void*)info2->si_addr, sigcontext->uc_mcontext.gregs[X64_TRAPNO], sigcontext->uc_mcontext.gregs[X64_ERR],sigcontext->uc_mcontext.gregs[X64_RIP], prot, mmapped);
|
|
// call the signal handler
|
|
x64_ucontext_t sigcontext_copy = *sigcontext;
|
|
// save old value from emu
|
|
#define GO(A) uint64_t old_##A = R_##A
|
|
GO(RAX);
|
|
GO(RDI);
|
|
GO(RSI);
|
|
GO(RDX);
|
|
GO(RCX);
|
|
GO(R8);
|
|
GO(R9);
|
|
GO(RBP);
|
|
#undef GO
|
|
// set stack pointer
|
|
R_RSP = frame;
|
|
// set frame pointer
|
|
R_RBP = sigcontext->uc_mcontext.gregs[X64_RBP];
|
|
|
|
int exits = 0;
|
|
int ret;
|
|
int dynarec = 0;
|
|
#ifdef DYNAREC
|
|
if(sig!=SIGSEGV && !(Locks&is_dyndump_locked) && !(Locks&is_memprot_locked))
|
|
dynarec = 1;
|
|
#endif
|
|
ret = RunFunctionHandler(emu, &exits, dynarec, sigcontext, my_context->signals[info2->si_signo], 3, info2->si_signo, info2, sigcontext);
|
|
// restore old value from emu
|
|
if(used_stack) // release stack
|
|
new_ss->ss_flags = 0;
|
|
#define GO(A) R_##A = old_##A
|
|
GO(RAX);
|
|
GO(RDI);
|
|
GO(RSI);
|
|
GO(RDX);
|
|
GO(RCX);
|
|
GO(R8);
|
|
GO(R9);
|
|
GO(RBP);
|
|
#undef GO
|
|
|
|
if(memcmp(sigcontext, &sigcontext_copy, sizeof(x64_ucontext_t))) {
|
|
if(emu->jmpbuf) {
|
|
#define GO(R) emu->regs[_##R].q[0]=sigcontext->uc_mcontext.gregs[X64_R##R]
|
|
GO(AX);
|
|
GO(CX);
|
|
GO(DX);
|
|
GO(DI);
|
|
GO(SI);
|
|
GO(BP);
|
|
GO(SP);
|
|
GO(BX);
|
|
#undef GO
|
|
#define GO(R) emu->regs[_##R].q[0]=sigcontext->uc_mcontext.gregs[X64_##R]
|
|
GO(R8);
|
|
GO(R9);
|
|
GO(R10);
|
|
GO(R11);
|
|
GO(R12);
|
|
GO(R13);
|
|
GO(R14);
|
|
GO(R15);
|
|
#undef GO
|
|
emu->ip.q[0]=sigcontext->uc_mcontext.gregs[X64_RIP];
|
|
// flags
|
|
emu->eflags.x64=sigcontext->uc_mcontext.gregs[X64_EFL];
|
|
// get segments
|
|
uint16_t seg;
|
|
seg = (sigcontext->uc_mcontext.gregs[X64_CSGSFS] >> 0)&0xffff;
|
|
#define GO(S) if(emu->segs[_##S]!=seg) emu->segs[_##S]=seg
|
|
GO(CS);
|
|
seg = (sigcontext->uc_mcontext.gregs[X64_CSGSFS] >> 16)&0xffff;
|
|
GO(GS);
|
|
seg = (sigcontext->uc_mcontext.gregs[X64_CSGSFS] >> 32)&0xffff;
|
|
GO(FS);
|
|
#undef GO
|
|
for(int i=0; i<6; ++i)
|
|
emu->segs_serial[i] = 0;
|
|
printf_log((sig==10)?LOG_DEBUG:log_minimum, "Context has been changed in Sigactionhanlder, doing siglongjmp to resume emu at %p, RSP=%p\n", (void*)R_RIP, (void*)R_RSP);
|
|
if(old_code)
|
|
*old_code = -1; // re-init the value to allow another segfault at the same place
|
|
//relockMutex(Locks); // do not relock mutex, because of the siglongjmp, whatever was running is canceled
|
|
#ifdef DYNAREC
|
|
if(Locks & is_dyndump_locked)
|
|
CancelBlock64(1);
|
|
#endif
|
|
#ifdef RV64
|
|
emu->xSPSave = emu->old_savedsp;
|
|
#endif
|
|
#ifdef ANDROID
|
|
siglongjmp(*emu->jmpbuf, 1);
|
|
#else
|
|
siglongjmp(emu->jmpbuf, 1);
|
|
#endif
|
|
}
|
|
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)":"");
|
|
}
|
|
// restore regs...
|
|
#define GO(R) R_##R=sigcontext->uc_mcontext.gregs[X64_##R]
|
|
GO(RAX);
|
|
GO(RCX);
|
|
GO(RDX);
|
|
GO(RDI);
|
|
GO(RSI);
|
|
GO(RBP);
|
|
GO(RSP);
|
|
GO(RBX);
|
|
GO(R8);
|
|
GO(R9);
|
|
GO(R10);
|
|
GO(R11);
|
|
GO(R12);
|
|
GO(R13);
|
|
GO(R14);
|
|
GO(R15);
|
|
GO(RIP);
|
|
#undef GO
|
|
emu->eflags.x64=sigcontext->uc_mcontext.gregs[X64_EFL];
|
|
uint16_t seg;
|
|
seg = (sigcontext->uc_mcontext.gregs[X64_CSGSFS] >> 0)&0xffff;
|
|
#define GO(S) emu->segs[_##S]=seg; emu->segs_serial[_##S] = 0;
|
|
GO(CS);
|
|
seg = (sigcontext->uc_mcontext.gregs[X64_CSGSFS] >> 16)&0xffff;
|
|
GO(GS);
|
|
seg = (sigcontext->uc_mcontext.gregs[X64_CSGSFS] >> 32)&0xffff;
|
|
GO(FS);
|
|
#undef GO
|
|
|
|
printf_log(LOG_DEBUG, "Sigactionhanlder main function returned (exit=%d, restorer=%p)\n", exits, (void*)restorer);
|
|
if(exits) {
|
|
//relockMutex(Locks); // the thread will exit, so no relock there
|
|
#ifdef DYNAREC
|
|
if(Locks & is_dyndump_locked)
|
|
CancelBlock64(1);
|
|
#endif
|
|
exit(ret);
|
|
}
|
|
if(restorer)
|
|
RunFunctionHandler(emu, &exits, 0, NULL, restorer, 0);
|
|
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
|
|
sse_regs_t old_xmm[16];
|
|
sse_regs_t old_ymm[16];
|
|
mmx87_regs_t old_mmx[8];
|
|
mmx87_regs_t old_x87[8];
|
|
uint32_t old_top = emu->top;
|
|
memcpy(old_xmm, emu->xmm, sizeof(old_xmm));
|
|
memcpy(old_ymm, emu->ymm, sizeof(old_ymm));
|
|
memcpy(old_mmx, emu->mmx, sizeof(old_mmx));
|
|
memcpy(old_x87, emu->x87, sizeof(old_x87));
|
|
#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
|
|
memcpy(emu->xmm, old_xmm, sizeof(old_xmm));
|
|
memcpy(emu->ymm, old_ymm, sizeof(old_ymm));
|
|
memcpy(emu->mmx, old_mmx, sizeof(old_mmx));
|
|
memcpy(emu->x87, old_x87, sizeof(old_x87));
|
|
emu->top = old_top;
|
|
}
|
|
|
|
extern void* current_helper;
|
|
#define USE_SIGNAL_MUTEX
|
|
#ifdef USE_SIGNAL_MUTEX
|
|
#ifdef USE_CUSTOM_MUTEX
|
|
static uint32_t mutex_dynarec_prot = 0;
|
|
#else
|
|
static pthread_mutex_t mutex_dynarec_prot = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
|
|
#endif
|
|
#define lock_signal() mutex_lock(&mutex_dynarec_prot)
|
|
#define unlock_signal() mutex_unlock(&mutex_dynarec_prot)
|
|
#else // USE_SIGNAL_MUTEX
|
|
#define lock_signal()
|
|
#define unlock_signal()
|
|
#endif
|
|
|
|
extern int box64_quit;
|
|
extern int box64_exit_code;
|
|
|
|
void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx)
|
|
{
|
|
// sig==SIGSEGV || sig==SIGBUS || sig==SIGILL || sig==SIGABRT here!
|
|
int log_minimum = (BOX64ENV(showsegv))?LOG_NONE:((sig==SIGSEGV && my_context->is_sigaction[sig])?LOG_DEBUG:LOG_INFO);
|
|
if(signal_jmpbuf_active) {
|
|
signal_jmpbuf_active = 0;
|
|
longjmp(SIG_JMPBUF, 1);
|
|
}
|
|
ucontext_t *p = (ucontext_t *)ucntx;
|
|
void* addr = (void*)info->si_addr; // address that triggered the issue
|
|
void* rsp = NULL;
|
|
x64emu_t* emu = thread_get_emu();
|
|
int tid = GetTID();
|
|
#ifdef __aarch64__
|
|
void * pc = (void*)p->uc_mcontext.pc;
|
|
struct fpsimd_context *fpsimd = NULL;
|
|
// find fpsimd struct
|
|
{
|
|
struct _aarch64_ctx * ff = (struct _aarch64_ctx*)p->uc_mcontext.__reserved;
|
|
while (ff->magic && !fpsimd) {
|
|
if(ff->magic==FPSIMD_MAGIC)
|
|
fpsimd = (struct fpsimd_context*)ff;
|
|
else
|
|
ff = (struct _aarch64_ctx*)((uintptr_t)ff + ff->size);
|
|
}
|
|
}
|
|
#elif defined __x86_64__
|
|
void * pc = (void*)p->uc_mcontext.gregs[X64_RIP];
|
|
void* fpsimd = NULL;
|
|
#elif defined __powerpc64__
|
|
void * pc = (void*)p->uc_mcontext.gp_regs[PT_NIP];
|
|
void* fpsimd = NULL;
|
|
#elif defined(LA64)
|
|
void * pc = (void*)p->uc_mcontext.__pc;
|
|
void* fpsimd = NULL;
|
|
#elif defined(SW64)
|
|
void * pc = (void*)p->uc_mcontext.sc_pc;
|
|
void* fpsimd = NULL;
|
|
#elif defined(RV64)
|
|
void * pc = (void*)p->uc_mcontext.__gregs[REG_PC];
|
|
void* fpsimd = NULL;
|
|
#else
|
|
void * pc = NULL; // unknow arch...
|
|
void* fpsimd = NULL;
|
|
#warning Unhandled architecture
|
|
#endif
|
|
dynablock_t* db = NULL;
|
|
int db_searched = 0;
|
|
uintptr_t x64pc = (uintptr_t)-1;
|
|
x64pc = R_RIP;
|
|
if((sig==SIGBUS) && (addr!=pc)) {
|
|
db = FindDynablockFromNativeAddress(pc);
|
|
if(db)
|
|
x64pc = getX64Address(db, (uintptr_t)pc);
|
|
db_searched = 1;
|
|
int fixed = 0;
|
|
if((fixed=sigbus_specialcases(info, ucntx, pc, fpsimd, db, x64pc))) {
|
|
// special case fixed, restore everything and just continues
|
|
if (BOX64ENV(log)>=LOG_DEBUG || BOX64ENV(showsegv)) {
|
|
static void* old_pc[2] = {0};
|
|
static int old_pc_i = 0;
|
|
if(old_pc[0]!=pc && old_pc[1]!=pc) {
|
|
old_pc[old_pc_i++] = pc;
|
|
if(old_pc_i==2)
|
|
old_pc_i = 0;
|
|
uint8_t* x64 = (uint8_t*)x64pc;
|
|
if(db)
|
|
printf_log(LOG_INFO, "Special unaligned case fixed @%p, opcode=%08x (addr=%p, db=%p, x64pc=%p[%02hhX %02hhX %02hhX %02hhX %02hhX])\n", pc, *(uint32_t*)pc, addr, db, x64pc, x64[0], x64[1], x64[2], x64[3], x64[4], x64[5]);
|
|
else
|
|
printf_log(LOG_INFO, "Special unaligned case fixed @%p, opcode=%08x (addr=%p)\n", pc, *(uint32_t*)pc, addr);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
#ifdef ARCH_NOP
|
|
if(sig==SIGILL) {
|
|
db = FindDynablockFromNativeAddress(pc);
|
|
if(db)
|
|
x64pc = getX64Address(db, (uintptr_t)pc); // this will be incorect in the case of the callret!
|
|
db_searched = 1;
|
|
if(db && db->callret_size) {
|
|
int is_callrets = 0;
|
|
int type_callret = 0;
|
|
for(int i=0; i<db->callret_size && !is_callrets; ++i)
|
|
if(pc==(db->block+db->callrets[i].offs)) {
|
|
is_callrets = 1;
|
|
type_callret = db->callrets[i].type;
|
|
}
|
|
if(is_callrets) {
|
|
if(!type_callret) {
|
|
// adjust x64pc for "ret" type
|
|
#ifdef __aarch64__
|
|
x64pc = p->uc_mcontext.regs[27];
|
|
#elif defined(LA64)
|
|
x64pc = p->uc_mcontext.__gregs[20];
|
|
#elif defined(RV64)
|
|
x64pc = p->uc_mcontext.__gregs[22];
|
|
#endif
|
|
}
|
|
// check if block is still valid
|
|
int is_hotpage = checkInHotPage(x64pc);
|
|
uint32_t hash = (db->gone || is_hotpage)?0:X31_hash_code(db->x64_addr, db->x64_size);
|
|
if(!db->gone && !is_hotpage && hash==db->hash) {
|
|
dynarec_log(LOG_INFO, "Dynablock (%p, x64addr=%p, always_test=%d) is clean, %s continuing at %p (%p)!\n", db, db->x64_addr, db->always_test, type_callret?"self-loop":"ret from callret", (void*)x64pc, (void*)addr);
|
|
// it's good! go next opcode
|
|
#ifdef __aarch64__
|
|
p->uc_mcontext.pc+=4;
|
|
#elif defined(LA64)
|
|
p->uc_mcontext.__pc+=4;
|
|
#elif defined(RV64)
|
|
p->uc_mcontext.__gregs[REG_PC]+=4;
|
|
#endif
|
|
if(db->always_test)
|
|
protectDB((uintptr_t)db->x64_addr, 1);
|
|
else {
|
|
if(db->callret_size) {
|
|
// mark all callrets to NOP
|
|
for(int i=0; i<db->callret_size; ++i)
|
|
*(uint32_t*)(db->block+db->callrets[i].offs) = ARCH_NOP;
|
|
ClearCache(db->block, db->size);
|
|
}
|
|
protectDBJumpTable((uintptr_t)db->x64_addr, db->x64_size, db->block, db->jmpnext);
|
|
}
|
|
return;
|
|
} else {
|
|
// dynablock got dirty! need to get out of it!!!
|
|
if(emu->jmpbuf) {
|
|
copyUCTXreg2Emu(emu, p, x64pc);
|
|
// only copy as it's a return address, so there is just the "epilog" to mimic here on "ret" type. "loop" type need everything
|
|
if(type_callret) {
|
|
adjustregs(emu);
|
|
if(db && db->arch_size)
|
|
ARCH_ADJUST(db, emu, p, x64pc);
|
|
}
|
|
dynarec_log(LOG_INFO, "Dynablock (%p, x64addr=%p) %s, getting out at %s %p (%p)!\n", db, db->x64_addr, is_hotpage?"in HotPage":"dirty",(void*)R_RIP, type_callret?"self-loop":"ret from callret", (void*)addr);
|
|
emu->test.clean = 0;
|
|
#ifdef ANDROID
|
|
siglongjmp(*(JUMPBUFF*)emu->jmpbuf, 2);
|
|
#else
|
|
siglongjmp(emu->jmpbuf, 2);
|
|
#endif
|
|
}
|
|
dynarec_log(LOG_INFO, "Warning, Dirty %s (%p for db %p/%p) detected, but jmpbuffer not ready!\n", type_callret?"self-loop":"ret from callret", (void*)addr, db, (void*)db->x64_addr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
int Locks = unlockMutex();
|
|
uint32_t prot = getProtection((uintptr_t)addr);
|
|
#ifdef BAD_SIGNAL
|
|
// try to see if the si_code makes sense
|
|
// the RK3588 tend to need a special Kernel that seems to have a weird behaviour sometimes
|
|
if((sig==SIGSEGV) && (addr) && (info->si_code == 1) && getMmapped((uintptr_t)addr)) {
|
|
printf_log(LOG_DEBUG, "Workaround for suspicious si_code for %p / prot=0x%hhx\n", addr, prot);
|
|
info->si_code = 2;
|
|
}
|
|
#endif
|
|
#ifdef RV64
|
|
if((sig==SIGSEGV) && (addr==pc) && (info->si_code==2) && (prot==(PROT_READ|PROT_WRITE|PROT_EXEC))) {
|
|
if(!db_searched) {
|
|
db = FindDynablockFromNativeAddress(pc);
|
|
if(db)
|
|
x64pc = getX64Address(db, (uintptr_t)pc);
|
|
db_searched = 1;
|
|
}
|
|
int fixed = 0;
|
|
if((fixed = sigbus_specialcases(info, ucntx, pc, fpsimd, db, x64pc))) {
|
|
// special case fixed, restore everything and just continues
|
|
if (BOX64ENV(log) >= LOG_DEBUG || BOX64ENV(showsegv)) {
|
|
static void* old_pc[2] = {0};
|
|
static int old_pc_i = 0;
|
|
if(old_pc[0]!=pc && old_pc[1]!=pc) {
|
|
old_pc[old_pc_i++] = pc;
|
|
if(old_pc_i==2)
|
|
old_pc_i = 0;
|
|
printf_log(LOG_NONE, "Special unalinged cased fixed @%p, opcode=%08x (addr=%p)\n", pc, *(uint32_t *)pc, addr);
|
|
}
|
|
}
|
|
relockMutex(Locks);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef DYNAREC
|
|
if((Locks & is_dyndump_locked) && ((sig==SIGSEGV) || (sig==SIGBUS)) && current_helper) {
|
|
printf_log(LOG_INFO, "FillBlock triggered a %s at %p from %p\n", (sig==SIGSEGV)?"segfault":"bus error", addr, pc);
|
|
CancelBlock64(0);
|
|
relockMutex(Locks);
|
|
cancelFillBlock(); // Segfault inside a Fillblock, cancel it's creation...
|
|
// cancelFillBlock does not return
|
|
}
|
|
if ((sig==SIGSEGV) && (addr) && (info->si_code == SEGV_ACCERR) && (prot&PROT_DYNAREC)) {
|
|
lock_signal();
|
|
// check if SMC inside block
|
|
if(!db_searched) {
|
|
db = FindDynablockFromNativeAddress(pc);
|
|
if(db)
|
|
x64pc = getX64Address(db, (uintptr_t)pc);
|
|
db_searched = 1;
|
|
}
|
|
// access error, unprotect the block (and mark them dirty)
|
|
unprotectDB((uintptr_t)addr, 1, 1); // unprotect 1 byte... But then, the whole page will be unprotected
|
|
CheckHotPage((uintptr_t)addr);
|
|
int db_need_test = (db && !BOX64ENV(dynarec_dirty))?getNeedTest((uintptr_t)db->x64_addr):0;
|
|
if(db && ((addr>=db->x64_addr && addr<(db->x64_addr+db->x64_size)) || db_need_test)) {
|
|
emu = getEmuSignal(emu, p, db);
|
|
// dynablock got auto-dirty! need to get out of it!!!
|
|
if(emu->jmpbuf) {
|
|
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);
|
|
dynarec_log(LOG_INFO, "Dynablock (%p, x64addr=%p, need_test=%d/%d/%d) %s, getting out at %p (%p)!\n", db, db->x64_addr, db_need_test, db->dirty, db->always_test, (addr>=db->x64_addr && addr<(db->x64_addr+db->x64_size))?"Auto-SMC":"unprotected", (void*)R_RIP, (void*)addr);
|
|
//relockMutex(Locks);
|
|
unlock_signal();
|
|
if(Locks & is_dyndump_locked)
|
|
CancelBlock64(1);
|
|
emu->test.clean = 0;
|
|
#ifdef ANDROID
|
|
siglongjmp(*(JUMPBUFF*)emu->jmpbuf, 2);
|
|
#else
|
|
siglongjmp(emu->jmpbuf, 2);
|
|
#endif
|
|
}
|
|
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)/*|| (prot&PROT_DYNAREC)*/) {
|
|
unlock_signal();
|
|
dynarec_log(LOG_INFO, "Writting from %p(%s) to %p!\n", (void*)R_RIP, getAddrFunctionName(R_RIP), (void*)addr);
|
|
// if there is no write permission, don't return and continue to program signal handling
|
|
relockMutex(Locks);
|
|
return;
|
|
}
|
|
unlock_signal();
|
|
} else if ((sig==SIGSEGV) && (addr) && (info->si_code == SEGV_ACCERR) && ((prot&(PROT_READ|PROT_WRITE))==(PROT_READ|PROT_WRITE))) {
|
|
lock_signal();
|
|
if(!db_searched) {
|
|
db = FindDynablockFromNativeAddress(pc);
|
|
if(db)
|
|
x64pc = getX64Address(db, (uintptr_t)pc);
|
|
db_searched = 1;
|
|
}
|
|
if(db && db->x64_addr>= addr && (db->x64_addr+db->x64_size)<addr) {
|
|
dynarec_log(LOG_INFO, "Warning, addr inside current dynablock!\n");
|
|
}
|
|
// mark stuff as unclean
|
|
if(BOX64ENV(dynarec))
|
|
cleanDBFromAddressRange(((uintptr_t)addr)&~(box64_pagesize-1), box64_pagesize, 0);
|
|
static void* glitch_pc = NULL;
|
|
static void* glitch_addr = NULL;
|
|
static uint32_t glitch_prot = 0;
|
|
if(addr && pc /*&& db*/) {
|
|
if((glitch_pc!=pc || glitch_addr!=addr || glitch_prot!=prot)) {
|
|
// probably a glitch due to intensive multitask...
|
|
dynarec_log(/*LOG_DEBUG*/LOG_INFO, "%04d|SIGSEGV with Access error on %p for %p, db=%p, prot=0x%x, retrying\n", tid, pc, addr, db, prot);
|
|
glitch_pc = pc;
|
|
glitch_addr = addr;
|
|
glitch_prot = prot;
|
|
relockMutex(Locks);
|
|
unlock_signal();
|
|
return; // try again
|
|
}
|
|
dynarec_log(/*LOG_DEBUG*/LOG_INFO, "%04d|Repeated SIGSEGV with Access error on %p for %p, db=%p, prot=0x%x\n", tid, pc, addr, db, prot);
|
|
glitch_pc = NULL;
|
|
glitch_addr = NULL;
|
|
glitch_prot = 0;
|
|
relockMutex(Locks);
|
|
unlock_signal();
|
|
return; // try again
|
|
}
|
|
if(addr && pc && ((prot&(PROT_READ|PROT_WRITE))==(PROT_READ|PROT_WRITE))) {
|
|
static void* glitch2_pc = NULL;
|
|
static void* glitch2_addr = NULL;
|
|
static int glitch2_prot = 0;
|
|
if((glitch2_pc!=pc || glitch2_addr!=addr || glitch2_prot!=prot)) {
|
|
dynarec_log(LOG_INFO, "Is that a multi process glitch too?\n");
|
|
//printf_log(LOG_INFO, "Is that a multi process glitch too?\n");
|
|
glitch2_pc = pc;
|
|
glitch2_addr = addr;
|
|
glitch2_prot = prot;
|
|
sched_yield(); // give time to the other process
|
|
refreshProtection((uintptr_t)addr);
|
|
relockMutex(Locks);
|
|
sched_yield(); // give time to the other process
|
|
unlock_signal();
|
|
return; // try again
|
|
}
|
|
glitch2_pc = NULL;
|
|
glitch2_addr = NULL;
|
|
glitch2_prot = 0;
|
|
}
|
|
unlock_signal();
|
|
} else if ((sig==SIGSEGV) && (addr) && (info->si_code == SEGV_ACCERR) && (prot&PROT_DYNAREC_R)) {
|
|
// unprotect and continue to signal handler, because Write is not there on purpose
|
|
unprotectDB((uintptr_t)addr, 1, 1); // unprotect 1 byte... But then, the whole page will be unprotected
|
|
}
|
|
if(!db_searched) {
|
|
db = FindDynablockFromNativeAddress(pc);
|
|
if(db)
|
|
x64pc = getX64Address(db, (uintptr_t)pc);
|
|
db_searched = 1;
|
|
}
|
|
#endif
|
|
if((sig==SIGSEGV || sig==SIGBUS) && box64_quit) {
|
|
printf_log(LOG_INFO, "Sigfault/Segbus while quitting, exiting silently\n");
|
|
_exit(box64_exit_code); // Hack, segfault while quiting, exit silently
|
|
}
|
|
static int old_code = -1;
|
|
static void* old_pc = 0;
|
|
static void* old_addr = 0;
|
|
static int old_tid = 0;
|
|
static uint32_t old_prot = 0;
|
|
int mapped = memExist((uintptr_t)addr);
|
|
const char* signame = (sig==SIGSEGV)?"SIGSEGV":((sig==SIGBUS)?"SIGBUS":((sig==SIGILL)?"SIGILL":"SIGABRT"));
|
|
rsp = (void*)R_RSP;
|
|
#if defined(DYNAREC)
|
|
#if defined(ARM64)
|
|
if(db) {
|
|
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) {
|
|
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) {
|
|
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, x64pc=%p, addr=%p, prot=%02x)!\n", tid, signame, old_code, old_pc, x64pc, old_addr, prot);
|
|
exit(-1);
|
|
} else {
|
|
if((sig==SIGSEGV) && (info->si_code == SEGV_ACCERR) && ((prot&~PROT_CUSTOM)==(PROT_READ|PROT_WRITE) || (prot&~PROT_CUSTOM)==(PROT_READ|PROT_WRITE|PROT_EXEC))) {
|
|
static uintptr_t old_addr = 0;
|
|
#ifdef DYNAREC
|
|
if(prot==(PROT_READ|PROT_WRITE|PROT_EXEC))
|
|
if(cleanDBFromAddressRange(((uintptr_t)addr)&~(box64_pagesize-1), box64_pagesize, 0)) {
|
|
printf_log(/*LOG_DEBUG*/LOG_INFO, "%04d| Strange SIGSEGV with Access error on %p for %p with DynaBlock(s) in range, db=%p, Lock=0x%x)\n", tid, pc, addr, db, Locks);
|
|
refreshProtection((uintptr_t)addr);
|
|
relockMutex(Locks);
|
|
return;
|
|
}
|
|
#endif
|
|
printf_log(/*LOG_DEBUG*/LOG_INFO, "%04d| Strange SIGSEGV with Access error on %p for %p%s, db=%p, prot=0x%x (old_addr=%p, Lock=0x%x)\n", tid, pc, addr, mapped?" mapped":"", db, prot, (void*)old_addr, Locks);
|
|
if(!(old_addr==(uintptr_t)addr && old_prot==prot) || mapped) {
|
|
old_addr = (uintptr_t)addr;
|
|
old_prot = prot;
|
|
refreshProtection(old_addr);
|
|
relockMutex(Locks);
|
|
sched_yield(); // give time to the other process
|
|
return; // that's probably just a multi-task glitch, like seen in terraria
|
|
}
|
|
old_addr = 0;
|
|
}
|
|
old_code = info->si_code;
|
|
old_pc = pc;
|
|
old_addr = addr;
|
|
old_tid = tid;
|
|
old_prot = prot;
|
|
const char* name = NULL;
|
|
const char* x64name = NULL;
|
|
if (log_minimum<=BOX64ENV(log)) {
|
|
signal_jmpbuf_active = 1;
|
|
if(sigsetjmp(SIG_JMPBUF, 1)) {
|
|
// segfault while gathering function name...
|
|
name = "???";
|
|
} else
|
|
name = GetNativeName(pc);
|
|
signal_jmpbuf_active = 0;
|
|
}
|
|
// Adjust RIP for special case of NULL function run
|
|
if(sig==SIGSEGV && R_RIP==0x1 && (uintptr_t)info->si_addr==0x0)
|
|
R_RIP = 0x0;
|
|
if(log_minimum<=BOX64ENV(log)) {
|
|
elfheader_t* elf = FindElfAddress(my_context, x64pc);
|
|
if(elf) {
|
|
signal_jmpbuf_active = 1;
|
|
if(sigsetjmp(SIG_JMPBUF, 1)) {
|
|
// segfault while gathering function name...
|
|
x64name = "?";
|
|
} else
|
|
x64name = getAddrFunctionName(x64pc);
|
|
signal_jmpbuf_active = 0;
|
|
}
|
|
}
|
|
if(BOX64ENV(jitgdb)) {
|
|
pid_t pid = getpid();
|
|
int v = vfork(); // is this ok in a signal handler???
|
|
if(v<0) {
|
|
printf("Error while forking, cannot launch gdb (errp%d/%s)\n", errno, strerror(errno));
|
|
} else if(v) {
|
|
// parent process, the one that have the segfault
|
|
volatile int waiting = 1;
|
|
printf("Waiting for %s (pid %d)...\n", (BOX64ENV(jitgdb)==2)?"gdbserver":"gdb", pid);
|
|
while(waiting) {
|
|
// using gdb, use "set waiting=0" to stop waiting...
|
|
usleep(1000);
|
|
}
|
|
} else {
|
|
char myarg[50] = {0};
|
|
sprintf(myarg, "%d", pid);
|
|
if(BOX64ENV(jitgdb)==2)
|
|
execlp("gdbserver", "gdbserver", "127.0.0.1:1234", "--attach", myarg, (char*)NULL);
|
|
else if(BOX64ENV(jitgdb)==3)
|
|
execlp("lldb", "lldb", "-p", myarg, (char*)NULL);
|
|
else
|
|
execlp("gdb", "gdb", "-pid", myarg, (char*)NULL);
|
|
exit(-1);
|
|
}
|
|
}
|
|
print_rolling_log(log_minimum);
|
|
|
|
if((BOX64ENV(showbt) || sig==SIGABRT) && log_minimum<=BOX64ENV(log)) {
|
|
// show native bt
|
|
ShowNativeBT(log_minimum);
|
|
|
|
#define BT_BUF_SIZE 100
|
|
int nptrs;
|
|
void *buffer[BT_BUF_SIZE];
|
|
char **strings;
|
|
|
|
extern int my_backtrace_ip(x64emu_t* emu, void** buffer, int size); // in wrappedlibc
|
|
extern char** my_backtrace_symbols(x64emu_t* emu, uintptr_t* buffer, int size);
|
|
// save and set real RIP/RSP
|
|
#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
|
|
if(db)
|
|
copyUCTXreg2Emu(emu, p, x64pc);
|
|
#endif
|
|
nptrs = my_backtrace_ip(emu, buffer, BT_BUF_SIZE);
|
|
strings = my_backtrace_symbols(emu, (uintptr_t*)buffer, nptrs);
|
|
if(strings) {
|
|
for (int j = 0; j < nptrs; j++)
|
|
printf_log(log_minimum, "EmulatedBT: %s\n", strings[j]);
|
|
free(strings);
|
|
} else
|
|
printf_log(log_minimum, "EmulatedBT: none\n");
|
|
#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
|
|
}
|
|
|
|
if(log_minimum<=BOX64ENV(log)) {
|
|
static const char* reg_name[] = {"RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI", " R8", " R9","R10","R11", "R12","R13","R14","R15"};
|
|
static const char* seg_name[] = {"ES", "CS", "SS", "DS", "FS", "GS"};
|
|
int shown_regs = 0;
|
|
#ifdef DYNAREC
|
|
uint32_t hash = 0;
|
|
if(db)
|
|
hash = X31_hash_code(db->x64_addr, db->x64_size);
|
|
printf_log(log_minimum, "%04d|%s @%p (%s) (x64pc=%p/\"%s\", rsp=%p, stack=%p:%p own=%p fp=%p), for accessing %p (code=%d/prot=%x), db=%p(%p:%p/%p:%p/%s:%s, hash:%x/%x) handler=%p",
|
|
GetTID(), signame, pc, name, (void*)x64pc, x64name?:"???", rsp,
|
|
emu->init_stack, emu->init_stack+emu->size_stack, emu->stack2free, (void*)R_RBP,
|
|
addr, info->si_code,
|
|
prot, db, db?db->block:0, db?(db->block+db->size):0,
|
|
db?db->x64_addr:0, db?(db->x64_addr+db->x64_size):0,
|
|
getAddrFunctionName((uintptr_t)(db?db->x64_addr:0)),
|
|
(db?getNeedTest((uintptr_t)db->x64_addr):0)?"needs_test":"clean", db?db->hash:0, hash,
|
|
(void*)my_context->signals[sig]);
|
|
#if defined(ARM64)
|
|
if(db) {
|
|
shown_regs = 1;
|
|
for (int i=0; i<16; ++i) {
|
|
if(!(i%4)) printf_log_prefix(0, log_minimum, "\n");
|
|
printf_log_prefix(0, log_minimum, "%s:0x%016llx ", reg_name[i], p->uc_mcontext.regs[10+i]);
|
|
}
|
|
printf_log_prefix(0, log_minimum, "\n");
|
|
for (int i=0; i<6; ++i)
|
|
printf_log_prefix(0, log_minimum, "%s:0x%04x ", seg_name[i], emu->segs[i]);
|
|
}
|
|
if(rsp!=addr && getProtection((uintptr_t)rsp-4*8) && getProtection((uintptr_t)rsp+4*8))
|
|
for (int i=-4; i<4; ++i) {
|
|
printf_log_prefix(0, log_minimum, "%sRSP%c0x%02x:0x%016lx", (i%4)?" ":"\n", i<0?'-':'+', abs(i)*8, *(uintptr_t*)(rsp+i*8));
|
|
}
|
|
#elif defined(RV64)
|
|
if(db) {
|
|
shown_regs = 1;
|
|
for (int i=0; i<16; ++i) {
|
|
if(!(i%4)) printf_log_prefix(0, log_minimum, "\n");
|
|
printf_log_prefix(0, log_minimum, "%s:0x%016llx ", reg_name[i], p->uc_mcontext.__gregs[(((uint8_t[]) { 16, 13, 12, 24, 9, 8, 11, 10, 14, 15, 26, 27, 18, 19, 20, 21 })[i])]);
|
|
}
|
|
printf_log_prefix(0, log_minimum, "\n");
|
|
for (int i=0; i<6; ++i)
|
|
printf_log_prefix(0, log_minimum, "%s:0x%04x ", seg_name[i], emu->segs[i]);
|
|
}
|
|
if(rsp!=addr && getProtection((uintptr_t)rsp-4*8) && getProtection((uintptr_t)rsp+4*8))
|
|
for (int i=-4; i<4; ++i) {
|
|
printf_log_prefix(0, log_minimum, "%sRSP%c0x%02x:0x%016lx", (i%4)?" ":"\n", i<0?'-':'+', abs(i)*8, *(uintptr_t*)(rsp+i*8));
|
|
}
|
|
#elif defined(LA64)
|
|
if(db) {
|
|
shown_regs = 1;
|
|
for (int i=0; i<16; ++i) {
|
|
if(!(i%4)) printf_log_prefix(0, log_minimum, "\n");
|
|
printf_log_prefix(0, log_minimum, "%s:0x%016llx ", reg_name[i], p->uc_mcontext.__gregs[12+i]);
|
|
}
|
|
printf_log_prefix(0, log_minimum, "\n");
|
|
for (int i=0; i<6; ++i)
|
|
printf_log_prefix(0, log_minimum, "%s:0x%04x ", seg_name[i], emu->segs[i]);
|
|
}
|
|
if(rsp!=addr && getProtection((uintptr_t)rsp-4*8) && getProtection((uintptr_t)rsp+4*8))
|
|
for (int i=-4; i<4; ++i) {
|
|
printf_log_prefix(0, log_minimum, "%sRSP%c0x%02x:0x%016lx", (i%4)?" ":"\n", i<0?'-':'+', abs(i)*8, *(uintptr_t*)(rsp+i*8));
|
|
}
|
|
#else
|
|
#warning TODO
|
|
#endif
|
|
#else
|
|
printf_log(log_minimum, "%04d|%s @%p (%s) (x64pc=%p/\"%s\", rsp=%p), for accessing %p (code=%d)", GetTID(), signame, pc, name, (void*)x64pc, x64name?:"???", rsp, addr, info->si_code);
|
|
#endif
|
|
if(!shown_regs) {
|
|
for (int i=0; i<16; ++i) {
|
|
if(!(i%4)) printf_log_prefix(0, log_minimum, "\n");
|
|
printf_log_prefix(0, log_minimum, "%s:0x%016llx ", reg_name[i], emu->regs[i].q[0]);
|
|
}
|
|
printf_log_prefix(0, log_minimum, "\n");
|
|
for (int i=0; i<6; ++i)
|
|
printf_log_prefix(0, log_minimum, "%s:0x%04x ", seg_name[i], emu->segs[i]);
|
|
}
|
|
if(sig==SIGILL) {
|
|
printf_log_prefix(0, log_minimum, " opcode=%02X %02X %02X %02X %02X %02X %02X %02X (%02X %02X %02X %02X %02X)\n", ((uint8_t*)pc)[0], ((uint8_t*)pc)[1], ((uint8_t*)pc)[2], ((uint8_t*)pc)[3], ((uint8_t*)pc)[4], ((uint8_t*)pc)[5], ((uint8_t*)pc)[6], ((uint8_t*)pc)[7], ((uint8_t*)x64pc)[0], ((uint8_t*)x64pc)[1], ((uint8_t*)x64pc)[2], ((uint8_t*)x64pc)[3], ((uint8_t*)x64pc)[4]);
|
|
} else if(sig==SIGBUS || (sig==SIGSEGV && (x64pc!=(uintptr_t)addr) && (pc!=addr)) && (getProtection_fast(x64pc)&PROT_READ) && (getProtection_fast((uintptr_t)pc)&PROT_READ)) {
|
|
printf_log_prefix(0, log_minimum, " %sopcode=%02X %02X %02X %02X %02X %02X %02X %02X (opcode=%08x)\n", (emu->segs[_CS]==0x23)?"x86":"x64", ((uint8_t*)x64pc)[0], ((uint8_t*)x64pc)[1], ((uint8_t*)x64pc)[2], ((uint8_t*)x64pc)[3], ((uint8_t*)x64pc)[4], ((uint8_t*)x64pc)[5], ((uint8_t*)x64pc)[6], ((uint8_t*)x64pc)[7], *(uint32_t*)pc);
|
|
} else {
|
|
printf_log_prefix(0, log_minimum, "\n");
|
|
}
|
|
}
|
|
}
|
|
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, x64pc);
|
|
return;
|
|
}
|
|
// no handler (or double identical segfault)
|
|
// set default and that's it, instruction will restart and default segfault handler will be called...
|
|
if(my_context->signals[sig]!=1 || sig==SIGSEGV || sig==SIGILL || sig==SIGFPE || sig==SIGABRT) {
|
|
signal(sig, (void*)my_context->signals[sig]);
|
|
}
|
|
}
|
|
|
|
void my_sigactionhandler(int32_t sig, siginfo_t* info, void * ucntx)
|
|
{
|
|
void* pc = NULL;
|
|
#ifdef DYNAREC
|
|
ucontext_t *p = (ucontext_t *)ucntx;
|
|
#ifdef ARM64
|
|
pc = (void*)p->uc_mcontext.pc;
|
|
#elif defined(LA64)
|
|
pc = (void*)p->uc_mcontext.__pc;
|
|
#elif defined(RV64)
|
|
pc = (void*)p->uc_mcontext.__gregs[0];
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
#endif
|
|
dynablock_t* db = FindDynablockFromNativeAddress(pc);
|
|
x64emu_t* emu = thread_get_emu();
|
|
uintptr_t x64pc = R_RIP;
|
|
if(db)
|
|
x64pc = getX64Address(db, (uintptr_t)pc);
|
|
if(BOX64ENV(showsegv)) printf_log(LOG_INFO, "sigaction handler for sig %d, pc=%p, x64pc=%p, db=%p\n", sig, pc, x64pc, db);
|
|
my_sigactionhandler_oldcode(emu, sig, 0, info, ucntx, NULL, db, x64pc);
|
|
}
|
|
|
|
EXPORT sighandler_t my_signal(x64emu_t* emu, int signum, sighandler_t handler)
|
|
{
|
|
if(signum<0 || signum>MAX_SIGNAL)
|
|
return SIG_ERR;
|
|
|
|
if(signum==SIGSEGV && emu->context->no_sigsegv)
|
|
return 0;
|
|
|
|
// create a new handler
|
|
my_context->signals[signum] = (uintptr_t)handler;
|
|
my_context->is_sigaction[signum] = 0;
|
|
my_context->restorer[signum] = 0;
|
|
my_context->onstack[signum] = 0;
|
|
|
|
if(signum==SIGSEGV || signum==SIGBUS || signum==SIGILL || signum==SIGABRT)
|
|
return 0;
|
|
|
|
if(handler!=NULL && handler!=(sighandler_t)1) {
|
|
struct sigaction newact = {0};
|
|
struct sigaction oldact = {0};
|
|
newact.sa_flags = 0x04;
|
|
newact.sa_sigaction = my_sigactionhandler;
|
|
sigaction(signum, &newact, &oldact);
|
|
return oldact.sa_handler;
|
|
} else
|
|
return signal(signum, handler);
|
|
}
|
|
EXPORT sighandler_t my___sysv_signal(x64emu_t* emu, int signum, sighandler_t handler) __attribute__((alias("my_signal")));
|
|
EXPORT sighandler_t my_sysv_signal(x64emu_t* emu, int signum, sighandler_t handler) __attribute__((alias("my_signal"))); // not completely exact
|
|
|
|
int EXPORT my_sigaction(x64emu_t* emu, int signum, const x64_sigaction_t *act, x64_sigaction_t *oldact)
|
|
{
|
|
printf_log(LOG_DEBUG, "Sigaction(signum=%d, act=%p(f=%p, flags=0x%x), old=%p)\n", signum, act, act?act->_u._sa_handler:NULL, act?act->sa_flags:0, oldact);
|
|
if(signum<0 || signum>MAX_SIGNAL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if(signum==SIGSEGV && emu->context->no_sigsegv)
|
|
return 0;
|
|
|
|
if(signum==SIGILL && emu->context->no_sigill)
|
|
return 0;
|
|
struct sigaction newact = {0};
|
|
struct sigaction old = {0};
|
|
uintptr_t old_handler = my_context->signals[signum];
|
|
if(act) {
|
|
newact.sa_mask = act->sa_mask;
|
|
newact.sa_flags = act->sa_flags&~0x04000000; // No sa_restorer...
|
|
if(act->sa_flags&0x04) {
|
|
my_context->signals[signum] = (uintptr_t)act->_u._sa_sigaction;
|
|
my_context->is_sigaction[signum] = 1;
|
|
if(act->_u._sa_handler!=NULL && act->_u._sa_handler!=(sighandler_t)1) {
|
|
newact.sa_sigaction = my_sigactionhandler;
|
|
} else
|
|
newact.sa_sigaction = act->_u._sa_sigaction;
|
|
} else {
|
|
my_context->signals[signum] = (uintptr_t)act->_u._sa_handler;
|
|
my_context->is_sigaction[signum] = 0;
|
|
if(act->_u._sa_handler!=NULL && act->_u._sa_handler!=(sighandler_t)1) {
|
|
newact.sa_flags|=0x04;
|
|
newact.sa_sigaction = my_sigactionhandler;
|
|
} else
|
|
newact.sa_handler = act->_u._sa_handler;
|
|
}
|
|
my_context->restorer[signum] = (act->sa_flags&0x04000000)?(uintptr_t)act->sa_restorer:0;
|
|
my_context->onstack[signum] = (act->sa_flags&SA_ONSTACK)?1:0;
|
|
}
|
|
int ret = 0;
|
|
if(signum!=SIGSEGV && signum!=SIGBUS && signum!=SIGILL && signum!=SIGABRT)
|
|
ret = sigaction(signum, act?&newact:NULL, oldact?&old:NULL);
|
|
if(oldact) {
|
|
oldact->sa_flags = old.sa_flags;
|
|
oldact->sa_mask = old.sa_mask;
|
|
if(old.sa_flags & 0x04)
|
|
oldact->_u._sa_sigaction = old.sa_sigaction; //TODO should wrap...
|
|
else
|
|
oldact->_u._sa_handler = old.sa_handler; //TODO should wrap...
|
|
if((uintptr_t)oldact->_u._sa_sigaction == (uintptr_t)my_sigactionhandler && old_handler)
|
|
oldact->_u._sa_sigaction = (void*)old_handler;
|
|
oldact->sa_restorer = NULL; // no handling for now...
|
|
}
|
|
return ret;
|
|
}
|
|
int EXPORT my___sigaction(x64emu_t* emu, int signum, const x64_sigaction_t *act, x64_sigaction_t *oldact)
|
|
__attribute__((alias("my_sigaction")));
|
|
|
|
int EXPORT my_syscall_rt_sigaction(x64emu_t* emu, int signum, const x64_sigaction_restorer_t *act, x64_sigaction_restorer_t *oldact, int sigsetsize)
|
|
{
|
|
printf_log(LOG_DEBUG, "Syscall/Sigaction(signum=%d, act=%p, old=%p, size=%d)\n", signum, act, oldact, sigsetsize);
|
|
if(signum<0 || signum>MAX_SIGNAL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if(signum==SIGSEGV && emu->context->no_sigsegv)
|
|
return 0;
|
|
// TODO, how to handle sigsetsize>4?!
|
|
if(signum==32 || signum==33) {
|
|
// cannot use libc sigaction, need to use syscall!
|
|
struct kernel_sigaction newact = {0};
|
|
struct kernel_sigaction old = {0};
|
|
if(act) {
|
|
printf_log(LOG_DEBUG, " New (kernel) action flags=0x%x mask=0x%lx\n", act->sa_flags, *(uint64_t*)&act->sa_mask);
|
|
memcpy(&newact.sa_mask, &act->sa_mask, (sigsetsize>16)?16:sigsetsize);
|
|
newact.sa_flags = act->sa_flags&~0x04000000; // No sa_restorer...
|
|
if(act->sa_flags&0x04) {
|
|
my_context->signals[signum] = (uintptr_t)act->_u._sa_sigaction;
|
|
my_context->is_sigaction[signum] = 1;
|
|
if(act->_u._sa_handler!=NULL && act->_u._sa_handler!=(sighandler_t)1) {
|
|
newact.k_sa_handler = (void*)my_sigactionhandler;
|
|
} else {
|
|
newact.k_sa_handler = (void*)act->_u._sa_sigaction;
|
|
}
|
|
} else {
|
|
my_context->signals[signum] = (uintptr_t)act->_u._sa_handler;
|
|
my_context->is_sigaction[signum] = 0;
|
|
if(act->_u._sa_handler!=NULL && act->_u._sa_handler!=(sighandler_t)1) {
|
|
newact.sa_flags|=0x4;
|
|
newact.k_sa_handler = (void*)my_sigactionhandler;
|
|
} else {
|
|
newact.k_sa_handler = act->_u._sa_handler;
|
|
}
|
|
}
|
|
my_context->restorer[signum] = (act->sa_flags&0x04000000)?(uintptr_t)act->sa_restorer:0;
|
|
}
|
|
|
|
if(oldact) {
|
|
old.sa_flags = oldact->sa_flags;
|
|
memcpy(&old.sa_mask, &oldact->sa_mask, (sigsetsize>16)?16:sigsetsize);
|
|
}
|
|
|
|
int ret = syscall(__NR_rt_sigaction, signum, act?&newact:NULL, oldact?&old:NULL, (sigsetsize>16)?16:sigsetsize);
|
|
if(oldact && ret==0) {
|
|
oldact->sa_flags = old.sa_flags;
|
|
memcpy(&oldact->sa_mask, &old.sa_mask, (sigsetsize>16)?16:sigsetsize);
|
|
if(old.sa_flags & 0x04)
|
|
oldact->_u._sa_sigaction = (void*)old.k_sa_handler; //TODO should wrap...
|
|
else
|
|
oldact->_u._sa_handler = old.k_sa_handler; //TODO should wrap...
|
|
}
|
|
return ret;
|
|
} else {
|
|
// using libc sigaction
|
|
struct sigaction newact = {0};
|
|
struct sigaction old = {0};
|
|
if(act) {
|
|
printf_log(LOG_DEBUG, " New action for signal #%d flags=0x%x mask=0x%lx\n", signum, act->sa_flags, *(uint64_t*)&act->sa_mask);
|
|
newact.sa_mask = act->sa_mask;
|
|
newact.sa_flags = act->sa_flags&~0x04000000; // No sa_restorer...
|
|
if(act->sa_flags&0x04) {
|
|
if(act->_u._sa_handler!=NULL && act->_u._sa_handler!=(sighandler_t)1) {
|
|
my_context->signals[signum] = (uintptr_t)act->_u._sa_sigaction;
|
|
newact.sa_sigaction = my_sigactionhandler;
|
|
} else {
|
|
newact.sa_sigaction = act->_u._sa_sigaction;
|
|
}
|
|
} else {
|
|
if(act->_u._sa_handler!=NULL && act->_u._sa_handler!=(sighandler_t)1) {
|
|
my_context->signals[signum] = (uintptr_t)act->_u._sa_handler;
|
|
my_context->is_sigaction[signum] = 0;
|
|
newact.sa_sigaction = my_sigactionhandler;
|
|
newact.sa_flags|=0x4;
|
|
} else {
|
|
newact.sa_handler = act->_u._sa_handler;
|
|
}
|
|
}
|
|
my_context->restorer[signum] = (act->sa_flags&0x04000000)?(uintptr_t)act->sa_restorer:0;
|
|
}
|
|
|
|
if(oldact) {
|
|
old.sa_flags = oldact->sa_flags;
|
|
old.sa_mask = oldact->sa_mask;
|
|
}
|
|
int ret = 0;
|
|
|
|
if(signum!=SIGSEGV && signum!=SIGBUS && signum!=SIGILL && signum!=SIGABRT)
|
|
ret = sigaction(signum, act?&newact:NULL, oldact?&old:NULL);
|
|
if(oldact && ret==0) {
|
|
oldact->sa_flags = old.sa_flags;
|
|
memcpy(&oldact->sa_mask, &old.sa_mask, (sigsetsize>8)?8:sigsetsize);
|
|
if(old.sa_flags & 0x04)
|
|
oldact->_u._sa_sigaction = old.sa_sigaction; //TODO should wrap...
|
|
else
|
|
oldact->_u._sa_handler = old.sa_handler; //TODO should wrap...
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
EXPORT sighandler_t my_sigset(x64emu_t* emu, int signum, sighandler_t handler)
|
|
{
|
|
// emulated SIG_HOLD
|
|
if(handler == (sighandler_t)2) {
|
|
x64_sigaction_t oact;
|
|
sigset_t nset;
|
|
sigset_t oset;
|
|
if (sigemptyset (&nset) < 0)
|
|
return (sighandler_t)-1;
|
|
if (sigaddset (&nset, signum) < 0)
|
|
return (sighandler_t)-1;
|
|
if (sigprocmask (SIG_BLOCK, &nset, &oset) < 0)
|
|
return (sighandler_t)-1;
|
|
if (sigismember (&oset, signum))
|
|
return (sighandler_t)2;
|
|
if (my_sigaction (emu, signum, NULL, &oact) < 0)
|
|
return (sighandler_t)-1;
|
|
return oact._u._sa_handler;
|
|
}
|
|
return my_signal(emu, signum, handler);
|
|
}
|
|
|
|
EXPORT int my_getcontext(x64emu_t* emu, void* ucp)
|
|
{
|
|
// printf_log(LOG_NONE, "Warning: call to partially implemented getcontext\n");
|
|
x64_ucontext_t *u = (x64_ucontext_t*)ucp;
|
|
// stack traking
|
|
u->uc_stack.ss_sp = NULL;
|
|
u->uc_stack.ss_size = 0; // this need to filled
|
|
// get general register
|
|
u->uc_mcontext.gregs[X64_RAX] = R_RAX;
|
|
u->uc_mcontext.gregs[X64_RCX] = R_RCX;
|
|
u->uc_mcontext.gregs[X64_RDX] = R_RDX;
|
|
u->uc_mcontext.gregs[X64_RDI] = R_RDI;
|
|
u->uc_mcontext.gregs[X64_RSI] = R_RSI;
|
|
u->uc_mcontext.gregs[X64_RBP] = R_RBP;
|
|
u->uc_mcontext.gregs[X64_RIP] = *(uint64_t*)R_RSP;
|
|
u->uc_mcontext.gregs[X64_RSP] = R_RSP+sizeof(uintptr_t);
|
|
u->uc_mcontext.gregs[X64_RBX] = R_RBX;
|
|
u->uc_mcontext.gregs[X64_R8] = R_R8;
|
|
u->uc_mcontext.gregs[X64_R9] = R_R9;
|
|
u->uc_mcontext.gregs[X64_R10] = R_R10;
|
|
u->uc_mcontext.gregs[X64_R11] = R_R11;
|
|
u->uc_mcontext.gregs[X64_R12] = R_R12;
|
|
u->uc_mcontext.gregs[X64_R13] = R_R13;
|
|
u->uc_mcontext.gregs[X64_R14] = R_R14;
|
|
u->uc_mcontext.gregs[X64_R15] = R_R15;
|
|
// get segments
|
|
u->uc_mcontext.gregs[X64_CSGSFS] = ((uint64_t)(R_CS)) | (((uint64_t)(R_GS))<<16) | (((uint64_t)(R_FS))<<32);
|
|
// get FloatPoint status
|
|
u->uc_mcontext.fpregs = ucp + 408;
|
|
fpu_savenv(emu, (void*)u->uc_mcontext.fpregs, 1);
|
|
*(uint32_t*)(ucp + 432) = emu->mxcsr.x32;
|
|
|
|
// get signal mask
|
|
sigprocmask(SIG_SETMASK, NULL, (sigset_t*)&u->uc_sigmask);
|
|
|
|
return 0;
|
|
}
|
|
|
|
EXPORT int my_setcontext(x64emu_t* emu, void* ucp)
|
|
{
|
|
// printf_log(LOG_NONE, "Warning: call to partially implemented setcontext\n");
|
|
x64_ucontext_t *u = (x64_ucontext_t*)ucp;
|
|
// stack tracking
|
|
emu->init_stack = u->uc_stack.ss_sp;
|
|
emu->size_stack = u->uc_stack.ss_size;
|
|
// set general register
|
|
R_RAX = u->uc_mcontext.gregs[X64_RAX];
|
|
R_RCX = u->uc_mcontext.gregs[X64_RCX];
|
|
R_RDX = u->uc_mcontext.gregs[X64_RDX];
|
|
R_RDI = u->uc_mcontext.gregs[X64_RDI];
|
|
R_RSI = u->uc_mcontext.gregs[X64_RSI];
|
|
R_RBP = u->uc_mcontext.gregs[X64_RBP];
|
|
R_RIP = u->uc_mcontext.gregs[X64_RIP];
|
|
R_RSP = u->uc_mcontext.gregs[X64_RSP];
|
|
R_RBX = u->uc_mcontext.gregs[X64_RBX];
|
|
R_R8 = u->uc_mcontext.gregs[X64_R8];
|
|
R_R9 = u->uc_mcontext.gregs[X64_R9];
|
|
R_R10 = u->uc_mcontext.gregs[X64_R10];
|
|
R_R11 = u->uc_mcontext.gregs[X64_R11];
|
|
R_R12 = u->uc_mcontext.gregs[X64_R12];
|
|
R_R13 = u->uc_mcontext.gregs[X64_R13];
|
|
R_R14 = u->uc_mcontext.gregs[X64_R14];
|
|
R_R15 = u->uc_mcontext.gregs[X64_R15];
|
|
// get segments
|
|
R_CS = (u->uc_mcontext.gregs[X64_CSGSFS]>> 0)&0xffff;
|
|
R_GS = (u->uc_mcontext.gregs[X64_CSGSFS]>>16)&0xffff;
|
|
R_FS = (u->uc_mcontext.gregs[X64_CSGSFS]>>32)&0xffff;
|
|
// set FloatPoint status
|
|
fpu_loadenv(emu, (void*)u->uc_mcontext.fpregs, 1);
|
|
emu->mxcsr.x32 = *(uint32_t*)(ucp + 432);
|
|
// set signal mask
|
|
sigprocmask(SIG_SETMASK, (sigset_t*)&u->uc_sigmask, NULL);
|
|
errno = 0;
|
|
|
|
return R_EAX;
|
|
}
|
|
void vFEv(x64emu_t *emu, uintptr_t fnc);
|
|
EXPORT void my_start_context(x64emu_t* emu)
|
|
{
|
|
// this is call indirectly by swapcontext from a makecontext, and will link context or just exit
|
|
x64_ucontext_t *u = *(x64_ucontext_t**)R_RBX;
|
|
if(u)
|
|
my_setcontext(emu, u);
|
|
else
|
|
emu->quit = 1;
|
|
}
|
|
|
|
EXPORT void my_makecontext(x64emu_t* emu, void* ucp, void* fnc, int32_t argc, int64_t* argv)
|
|
{
|
|
// printf_log(LOG_NONE, "Warning: call to unimplemented makecontext\n");
|
|
x64_ucontext_t *u = (x64_ucontext_t*)ucp;
|
|
// setup stack
|
|
uintptr_t* rsp = (uintptr_t*)(u->uc_stack.ss_sp + u->uc_stack.ss_size - sizeof(uintptr_t));
|
|
// setup the function
|
|
u->uc_mcontext.gregs[X64_RIP] = (intptr_t)fnc;
|
|
// setup return to private start_context uc_link
|
|
*rsp = (uintptr_t)u->uc_link;
|
|
u->uc_mcontext.gregs[X64_RBX] = (uintptr_t)rsp;
|
|
--rsp;
|
|
// setup args
|
|
int n = 3;
|
|
int j = 0;
|
|
int regs_abi[] = {_DI, _SI, _DX, _CX, _R8, _R9};
|
|
for (int i=0; i<argc; ++i) {
|
|
// get value first
|
|
uint32_t v;
|
|
if(n<6)
|
|
v = emu->regs[regs_abi[n++]].dword[0];
|
|
else
|
|
v = argv[j++];
|
|
// push value
|
|
switch(i) {
|
|
case 0: u->uc_mcontext.gregs[X64_RDI] = v; break;
|
|
case 1: u->uc_mcontext.gregs[X64_RSI] = v; break;
|
|
case 2: u->uc_mcontext.gregs[X64_RDX] = v; break;
|
|
case 3: u->uc_mcontext.gregs[X64_RCX] = v; break;
|
|
case 4: u->uc_mcontext.gregs[X64_R8] = v; break;
|
|
case 5: u->uc_mcontext.gregs[X64_R9] = v; break;
|
|
default:
|
|
--rsp;
|
|
*rsp = argv[(argc-1)-i];
|
|
}
|
|
}
|
|
// push the return value
|
|
--rsp;
|
|
*rsp = AddCheckBridge(my_context->system, vFEv, my_start_context, 0, "my_start_context");//my_context->exit_bridge;
|
|
u->uc_mcontext.gregs[X64_RSP] = (uintptr_t)rsp;
|
|
}
|
|
|
|
void box64_abort() {
|
|
if(BOX64ENV(showbt) && LOG_INFO<=BOX64ENV(log)) {
|
|
// show native bt
|
|
#define BT_BUF_SIZE 100
|
|
int nptrs;
|
|
void *buffer[BT_BUF_SIZE];
|
|
char **strings;
|
|
x64emu_t* emu = thread_get_emu();
|
|
|
|
#ifndef ANDROID
|
|
nptrs = backtrace(buffer, BT_BUF_SIZE);
|
|
strings = backtrace_symbols(buffer, nptrs);
|
|
if(strings) {
|
|
for (int j = 0; j < nptrs; j++)
|
|
printf_log(LOG_INFO, "NativeBT: %s\n", strings[j]);
|
|
free(strings);
|
|
} else
|
|
printf_log(LOG_INFO, "NativeBT: none (%d/%s)\n", errno, strerror(errno));
|
|
#endif
|
|
extern int my_backtrace_ip(x64emu_t* emu, void** buffer, int size); // in wrappedlibc
|
|
extern char** my_backtrace_symbols(x64emu_t* emu, uintptr_t* buffer, int size);
|
|
nptrs = my_backtrace_ip(emu, buffer, BT_BUF_SIZE);
|
|
strings = my_backtrace_symbols(emu, (uintptr_t*)buffer, nptrs);
|
|
if(strings) {
|
|
for (int j = 0; j < nptrs; j++)
|
|
printf_log(LOG_INFO, "EmulatedBT: %s\n", strings[j]);
|
|
free(strings);
|
|
} else
|
|
printf_log(LOG_INFO, "EmulatedBT: none\n");
|
|
}
|
|
abort();
|
|
}
|
|
|
|
|
|
EXPORT int my_swapcontext(x64emu_t* emu, void* ucp1, void* ucp2)
|
|
{
|
|
// printf_log(LOG_NONE, "Warning: call to unimplemented swapcontext\n");
|
|
// grab current context in ucp1
|
|
my_getcontext(emu, ucp1);
|
|
// activate ucp2
|
|
my_setcontext(emu, ucp2);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef USE_SIGNAL_MUTEX
|
|
static void atfork_child_dynarec_prot(void)
|
|
{
|
|
#ifdef USE_CUSTOM_MUTEX
|
|
native_lock_store(&mutex_dynarec_prot, 0);
|
|
#else
|
|
pthread_mutex_t tmp = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
|
|
memcpy(&mutex_dynarec_prot, &tmp, sizeof(mutex_dynarec_prot));
|
|
#endif
|
|
}
|
|
#endif
|
|
void init_signal_helper(box64context_t* context)
|
|
{
|
|
// setup signal handling
|
|
for(int i=0; i<=MAX_SIGNAL; ++i) {
|
|
context->signals[i] = 0; // SIG_DFL
|
|
}
|
|
struct sigaction action = {0};
|
|
action.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER;
|
|
action.sa_sigaction = my_box64signalhandler;
|
|
sigaction(SIGSEGV, &action, NULL);
|
|
action.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER;
|
|
action.sa_sigaction = my_box64signalhandler;
|
|
sigaction(SIGBUS, &action, NULL);
|
|
action.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER;
|
|
action.sa_sigaction = my_box64signalhandler;
|
|
sigaction(SIGILL, &action, NULL);
|
|
action.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER;
|
|
action.sa_sigaction = my_box64signalhandler;
|
|
sigaction(SIGABRT, &action, NULL);
|
|
|
|
pthread_once(&sigstack_key_once, sigstack_key_alloc);
|
|
#ifdef USE_SIGNAL_MUTEX
|
|
atfork_child_dynarec_prot();
|
|
pthread_atfork(NULL, NULL, atfork_child_dynarec_prot);
|
|
#endif
|
|
}
|
|
|
|
void fini_signal_helper()
|
|
{
|
|
signal(SIGSEGV, SIG_DFL);
|
|
signal(SIGBUS, SIG_DFL);
|
|
signal(SIGILL, SIG_DFL);
|
|
signal(SIGABRT, SIG_DFL);
|
|
}
|