#define _GNU_SOURCE /* See feature_test_macros(7) */ #include #include #include #include #include #include #include #include "box64context.h" #include "debug.h" #include "elfloader.h" #include "custommem.h" #include "threads.h" #include "x64trace.h" #include "bridge.h" #include "librarian.h" #include "library.h" #include "wrapper.h" #include "x64emu.h" #include "signals.h" #include "rcfile.h" #include "gltools.h" #include "rbtree.h" #include "dynarec.h" EXPORTDYN void initAllHelpers(box64context_t* context) { static int inited = 0; if(inited) return; my_context = context; init_pthread_helper(); init_bridge_helper(); init_signal_helper(context); inited = 1; } EXPORTDYN void finiAllHelpers(box64context_t* context) { static int finied = 0; if(finied) return; DeleteParams(); fini_pthread_helper(context); fini_signal_helper(); fini_bridge_helper(); fini_custommem_helper(context); finied = 1; } /// maxval not inclusive int getrand(int maxval) { if(maxval<1024) { return ((random()&0x7fff)*maxval)/0x7fff; } uint64_t r = random(); r = (r*maxval) / RAND_MAX; return r; } void free_tlsdatasize(void* p) { if(!p) return; tlsdatasize_t *data = (tlsdatasize_t*)p; box_free(data->ptr); box_free(p); if(my_context) pthread_setspecific(my_context->tlskey, NULL); } void x64Syscall(x64emu_t *emu); int unlockMutex() { int ret = unlockCustommemMutex(); 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<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; } void relockMutex(int locks) { relockCustommemMutex(locks); #define GO(A, B) \ if(locks&(1<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 } static void init_mutexes(box64context_t* context) { #ifndef DYNAREC pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); pthread_mutex_init(&context->mutex_lock, &attr); pthread_mutex_init(&context->mutex_trace, &attr); pthread_mutex_init(&context->mutex_tls, &attr); pthread_mutex_init(&context->mutex_thread, &attr); pthread_mutex_init(&context->mutex_bridge, &attr); pthread_mutexattr_destroy(&attr); #else #ifdef USE_CUSTOM_MUTEX native_lock_store(&context->mutex_trace, 0); native_lock_store(&context->mutex_tls, 0); native_lock_store(&context->mutex_thread, 0); native_lock_store(&context->mutex_bridge, 0); native_lock_store(&context->mutex_dyndump, 0); #else pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); pthread_mutex_init(&context->mutex_trace, &attr); pthread_mutex_init(&context->mutex_tls, &attr); pthread_mutex_init(&context->mutex_thread, &attr); pthread_mutex_init(&context->mutex_bridge, &attr); pthread_mutex_init(&context->mutex_dyndump, &attr); pthread_mutexattr_destroy(&attr); #endif pthread_mutex_init(&context->mutex_lock, NULL); #endif } static void atfork_child_box64context(void) { // (re)init mutex if it was lock before the fork init_mutexes(my_context); } void freeCycleLog(box64context_t* ctx) { if(cycle_log) { for(int i=0; ilog_call[i]); box_free(ctx->log_ret[i]); } box_free(ctx->log_call); box_free(ctx->log_ret); ctx->log_call = NULL; ctx->log_ret = NULL; } } void initCycleLog(box64context_t* context) { if(cycle_log) { context->log_call = (char**)box_calloc(cycle_log, sizeof(char*)); context->log_ret = (char**)box_calloc(cycle_log, sizeof(char*)); for(int i=0; ilog_call[i] = (char*)box_calloc(256, 1); context->log_ret[i] = (char*)box_calloc(128, 1); } } } EXPORTDYN box64context_t *NewBox64Context(int argc) { #ifdef BUILD_DYNAMIC if(my_context) { ++my_context->count; return my_context; } #endif // init and put default values box64context_t *context = my_context = (box64context_t*)box_calloc(1, sizeof(box64context_t)); initCycleLog(context); context->deferredInit = 1; context->sel_serial = 1; init_custommem_helper(context); #ifdef DYNAREC x64test_init(); #endif context->maplib = NewLibrarian(context); context->local_maplib = NewLibrarian(context); context->versym = NewDictionnary(); context->system = NewBridge(); // Cannot use Bridge name as the map is not initialized yet // create vsyscall context->vsyscall = AddBridge(context->system, vFEv, x64Syscall, 0, NULL); // create the vsyscalls context->vsyscalls[0] = AddVSyscall(context->system, 96); context->vsyscalls[1] = AddVSyscall(context->system, 201); context->vsyscalls[2] = AddVSyscall(context->system, 309); // create the alternate to map at address addAlternate((void*)0xffffffffff600000, (void*)context->vsyscalls[0]); addAlternate((void*)0xffffffffff600400, (void*)context->vsyscalls[1]); addAlternate((void*)0xffffffffff600800, (void*)context->vsyscalls[2]); // create exit bridge context->exit_bridge = AddBridge(context->system, NULL, NULL, 0, NULL); // get handle to box64 itself #ifndef STATICBUILD context->box64lib = dlopen(NULL, RTLD_NOW|RTLD_GLOBAL); #endif context->dlprivate = NewDLPrivate(); context->argc = argc; context->argv = (char**)box_calloc(context->argc+1, sizeof(char*)); init_mutexes(context); pthread_atfork(NULL, NULL, atfork_child_box64context); pthread_key_create(&context->tlskey, free_tlsdatasize); for (int i=0; i<8; ++i) context->canary[i] = 1 + getrand(255); context->canary[getrand(4)] = 0; printf_log(LOG_DEBUG, "Setting up canary (for Stack protector) at FS:0x28, value:%08X\n", *(uint32_t*)context->canary); // init segments for(int i=0; i<16; i++) { context->segtls[i].limit = (uintptr_t)-1LL; } context->segtls[10].key_init = 0; // 0x53 selector context->segtls[10].present = 1; context->segtls[8].key_init = 0; // 0x43 selector context->segtls[8].present = 1; context->segtls[6].key_init = 0; // 0x33 selector context->segtls[6].present = 1; context->segtls[5].key_init = 0; // 0x2b selector context->segtls[5].present = 1; context->segtls[4].key_init = 0; // 0x23 selector context->segtls[4].present = 1; context->segtls[4].is32bits = 1; context->globdata = NewMapSymbols(); context->uniques = NewMapSymbols(); initAllHelpers(context); #ifdef DYNAREC context->db_sizes = init_rbtree(); #endif return context; } void freeALProcWrapper(box64context_t* context); EXPORTDYN void FreeBox64Context(box64context_t** context) { if(!context) return; if(--(*context)->forked >= 0) return; box64context_t* ctx = *context; // local copy to do the cleanning //clean_current_emuthread(); // cleaning main thread seems a bad idea if(ctx->local_maplib) FreeLibrarian(&ctx->local_maplib, NULL); if(ctx->maplib) FreeLibrarian(&ctx->maplib, NULL); FreeDictionnary(&ctx->versym); for(int i=0; ielfsize; ++i) { FreeElfHeader(&ctx->elfs[i]); } box_free(ctx->elfs); FreeCollection(&ctx->box64_path); FreeCollection(&ctx->box64_ld_lib); FreeCollection(&ctx->box64_emulated_libs); // stop trace now if(ctx->dec) DeleteX64TraceDecoder(&ctx->dec); if(ctx->dec32) DeleteX86TraceDecoder(&ctx->dec32); if(ctx->zydis) DeleteX64Trace(ctx); if(ctx->deferredInitList) box_free(ctx->deferredInitList); /*box_free(ctx->argv);*/ /*for (int i=0; ienvc; ++i) box_free(ctx->envv[i]); box_free(ctx->envv);*/ if(ctx->atfork_sz) { box_free(ctx->atforks); ctx->atforks = NULL; ctx->atfork_sz = ctx->atfork_cap = 0; } for(int i=0; isignals[i]!=0 && ctx->signals[i]!=1) { signal(i, SIG_DFL); } *context = NULL; // bye bye my_context CleanStackSize(ctx); FreeDLPrivate(&ctx->dlprivate); box_free(ctx->fullpath); box_free(ctx->box64path); box_free(ctx->bashpath); FreeBridge(&ctx->system); #ifndef STATICBUILD freeGLProcWrapper(ctx); freeALProcWrapper(ctx); #endif if(ctx->stack_clone) box_free(ctx->stack_clone); void* ptr; if ((ptr = pthread_getspecific(ctx->tlskey)) != NULL) { free_tlsdatasize(ptr); } pthread_key_delete(ctx->tlskey); if(ctx->tlsdata) box_free(ctx->tlsdata); free_neededlib(ctx->neededlibs); ctx->neededlibs = NULL; if(ctx->emu_sig) FreeX64Emu(&ctx->emu_sig); FreeMapSymbols(&ctx->globdata); FreeMapSymbols(&ctx->uniques); #ifdef DYNAREC //dynarec_log(LOG_INFO, "BOX64 Dynarec at exit: Max DB=%d, righter=%d\n", ctx->max_db_size, rb_get_righter(ctx->db_sizes)); delete_rbtree(ctx->db_sizes); #endif finiAllHelpers(ctx); #ifdef DYNAREC pthread_mutex_destroy(&ctx->mutex_lock); #else pthread_mutex_destroy(&ctx->mutex_trace); pthread_mutex_destroy(&ctx->mutex_lock); pthread_mutex_destroy(&ctx->mutex_tls); pthread_mutex_destroy(&ctx->mutex_thread); pthread_mutex_destroy(&ctx->mutex_bridge); #endif freeCycleLog(ctx); box_free(ctx); } int AddElfHeader(box64context_t* ctx, elfheader_t* head) { int idx = 0; while(idxelfsize && ctx->elfs[idx]) idx++; if(idx == ctx->elfsize) { if(idx==ctx->elfcap) { // resize... ctx->elfcap += 16; ctx->elfs = (elfheader_t**)box_realloc(ctx->elfs, sizeof(elfheader_t*) * ctx->elfcap); } ctx->elfs[idx] = head; ctx->elfsize++; } else { ctx->elfs[idx] = head; } printf_log(LOG_DEBUG, "Adding \"%s\" as #%d in elf collection\n", ElfName(head), idx); return idx; } void RemoveElfHeader(box64context_t* ctx, elfheader_t* head) { if(GetTLSBase(head)) { // should remove the tls info int tlsbase = GetTLSBase(head); /*if(tlsbase == -ctx->tlssize) { // not really correct, but will do for now ctx->tlssize -= GetTLSSize(head); if(!(++ctx->sel_serial)) ++ctx->sel_serial; }*/ } for(int i=0; ielfsize; ++i) if(ctx->elfs[i] == head) { ctx->elfs[i] = NULL; return; } } int AddTLSPartition(box64context_t* context, int tlssize) { int oldsize = context->tlssize; // should in fact first try to map a hole, but rewinding all elfs and checking filled space, like with the mapmem utilities context->tlssize += tlssize; context->tlsdata = box_realloc(context->tlsdata, context->tlssize); memmove(context->tlsdata+tlssize, context->tlsdata, oldsize); // move to the top, using memmove as regions will probably overlap memset(context->tlsdata, 0, tlssize); // fill new space with 0 (not mandatory) // clean GS segment for current emu if(my_context) { //ResetSegmentsCache(thread_get_emu()); if(!(++context->sel_serial)) ++context->sel_serial; } return -context->tlssize; // negative offset }