diff options
| author | ptitSeb <sebastien.chev@gmail.com> | 2021-03-02 15:18:23 +0100 |
|---|---|---|
| committer | ptitSeb <sebastien.chev@gmail.com> | 2021-03-02 15:18:23 +0100 |
| commit | eb6c4594e1831daeddc469ed69c4dee917e627c1 (patch) | |
| tree | ff8e5ec2506fde9ff69ca0850e84667669c8f471 /src/libtools/threads.c | |
| parent | 1b9d1dc6093028ceb0f2eeeccf9cfbc5610fcf30 (diff) | |
| download | box64-eb6c4594e1831daeddc469ed69c4dee917e627c1.tar.gz box64-eb6c4594e1831daeddc469ed69c4dee917e627c1.zip | |
Added basic thread infrastructure
Diffstat (limited to 'src/libtools/threads.c')
| -rwxr-xr-x | src/libtools/threads.c | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/src/libtools/threads.c b/src/libtools/threads.c new file mode 100755 index 00000000..a245d6a9 --- /dev/null +++ b/src/libtools/threads.c @@ -0,0 +1,808 @@ +// __USE_UNIX98 is needed for sttype / gettype definition +#define __USE_UNIX98 +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <signal.h> +#include <errno.h> +#include <setjmp.h> + +#include "debug.h" +#include "box64context.h" +#include "threads.h" +#include "emu/x64emu_private.h" +//#include "tools/bridge_private.h" +#include "x64run.h" +#include "x64emu.h" +#include "box64stack.h" +//#include "callback.h" +#include "custommem.h" +#include "khash.h" +#include "emu/x64run_private.h" +#include "x64trace.h" +//#include "dynarec.h" +//#include "bridge.h" +#ifdef DYNAREC +#include "dynablock.h" +#endif + +//void _pthread_cleanup_push_defer(void* buffer, void* routine, void* arg); // declare hidden functions +//void _pthread_cleanup_pop_restore(void* buffer, int exec); +//void _pthread_cleanup_push(void* buffer, void* routine, void* arg); // declare hidden functions +//void _pthread_cleanup_pop(void* buffer, int exec); + +typedef struct threadstack_s { + void* stack; + size_t stacksize; +} threadstack_t; + +// longjmp / setjmp +//typedef struct jump_buff_i386_s { +// uint32_t save_ebx; +// uint32_t save_esi; +// uint32_t save_edi; +// uint32_t save_ebp; +// uint32_t save_esp; +// uint32_t save_eip; +//} jump_buff_i386_t; + +//typedef struct __jmp_buf_tag_s { +// jump_buff_i386_t __jmpbuf; +// int __mask_was_saved; +// __sigset_t __saved_mask; +//} __jmp_buf_tag_t; + +//typedef struct x86_unwind_buff_s { +// struct { +// jump_buff_i386_t __cancel_jmp_buf; +// int __mask_was_saved; +// } __cancel_jmp_buf[1]; +// void *__pad[4]; +//} x86_unwind_buff_t __attribute__((__aligned__)); + +KHASH_MAP_INIT_INT(threadstack, threadstack_t*) +//KHASH_MAP_INIT_INT(cancelthread, __pthread_unwind_buf_t*) + +void CleanStackSize(box64context_t* context) +{ + threadstack_t *ts; + if(!context || !context->stacksizes) + return; + pthread_mutex_lock(&context->mutex_thread); + kh_foreach_value(context->stacksizes, ts, free(ts)); + kh_destroy(threadstack, context->stacksizes); + context->stacksizes = NULL; + pthread_mutex_unlock(&context->mutex_thread); +} + +void FreeStackSize(kh_threadstack_t* map, uintptr_t attr) +{ + pthread_mutex_lock(&my_context->mutex_thread); + khint_t k = kh_get(threadstack, map, attr); + if(k!=kh_end(map)) { + free(kh_value(map, k)); + kh_del(threadstack, map, k); + } + pthread_mutex_unlock(&my_context->mutex_thread); +} + +void AddStackSize(kh_threadstack_t* map, uintptr_t attr, void* stack, size_t stacksize) +{ + khint_t k; + int ret; + pthread_mutex_lock(&my_context->mutex_thread); + k = kh_put(threadstack, map, attr, &ret); + threadstack_t* ts = kh_value(map, k) = (threadstack_t*)calloc(1, sizeof(threadstack_t)); + ts->stack = stack; + ts->stacksize = stacksize; + pthread_mutex_unlock(&my_context->mutex_thread); +} + +// return stack from attr (or from current emu if attr is not found..., wich is wrong but approximate enough?) +int GetStackSize(x64emu_t* emu, uintptr_t attr, void** stack, size_t* stacksize) +{ + if(emu->context->stacksizes && attr) { + pthread_mutex_lock(&my_context->mutex_thread); + khint_t k = kh_get(threadstack, emu->context->stacksizes, attr); + if(k!=kh_end(emu->context->stacksizes)) { + threadstack_t* ts = kh_value(emu->context->stacksizes, k); + *stack = ts->stack; + *stacksize = ts->stacksize; + pthread_mutex_unlock(&my_context->mutex_thread); + return 1; + } + pthread_mutex_unlock(&my_context->mutex_thread); + } + // should a Warning be emited? + *stack = emu->init_stack; + *stacksize = emu->size_stack; + return 0; +} + +//static void InitCancelThread() +//{ +//} + +//static void FreeCancelThread(box64context_t* context) +//{ +// if(!context) +// return; +//} +//static __pthread_unwind_buf_t* AddCancelThread(x86_unwind_buff_t* buff) +//{ +// __pthread_unwind_buf_t* r = (__pthread_unwind_buf_t*)calloc(1, sizeof(__pthread_unwind_buf_t)); +// buff->__pad[1] = r; +// return r; +//} + +//static __pthread_unwind_buf_t* GetCancelThread(x86_unwind_buff_t* buff) +//{ +// return (__pthread_unwind_buf_t*)buff->__pad[1]; +//} + +//static void DelCancelThread(x86_unwind_buff_t* buff) +//{ +// __pthread_unwind_buf_t* r = (__pthread_unwind_buf_t*)buff->__pad[1]; +// free(r); +// buff->__pad[1] = NULL; +//} + +typedef struct emuthread_s { + uintptr_t fnc; + void* arg; + x64emu_t* emu; +} emuthread_t; + +static void emuthread_destroy(void* p) +{ + emuthread_t *et = (emuthread_t*)p; + FreeX64Emu(&et->emu); + free(et); +} + +static pthread_key_t thread_key; +static pthread_once_t thread_key_once = PTHREAD_ONCE_INIT; + +static void thread_key_alloc() { + pthread_key_create(&thread_key, emuthread_destroy); +} + +void thread_set_emu(x64emu_t* emu) +{ + // create the key + pthread_once(&thread_key_once, thread_key_alloc); + emuthread_t *et = (emuthread_t*)pthread_getspecific(thread_key); + if(!et) { + et = (emuthread_t*)calloc(1, sizeof(emuthread_t)); + } else { + if(et->emu != emu) + FreeX64Emu(&et->emu); + } + et->emu = emu; + et->emu->type = EMUTYPE_MAIN; + pthread_setspecific(thread_key, et); +} + +x64emu_t* thread_get_emu() +{ + // create the key + pthread_once(&thread_key_once, thread_key_alloc); + emuthread_t *et = (emuthread_t*)pthread_getspecific(thread_key); + if(!et) { + int stacksize = 2*1024*1024; + // try to get stack size of the thread + pthread_attr_t attr; + if(!pthread_getattr_np(pthread_self(), &attr)) { + size_t stack_size; + void *stack_addr; + if(!pthread_attr_getstack(&attr, &stack_addr, &stack_size)) + stacksize = stack_size; + pthread_attr_destroy(&attr); + } + void* stack = calloc(1, stacksize); + x64emu_t *emu = NewX64Emu(my_context, 0, (uintptr_t)stack, stacksize, 1); + SetupX64Emu(emu); + thread_set_emu(emu); + return emu; + } + return et->emu; +} + +static void* pthread_routine(void* p) +{ + // create the key + pthread_once(&thread_key_once, thread_key_alloc); + // free current emuthread if it exist + { + void* t = pthread_getspecific(thread_key); + if(t) { + // not sure how this could happens + printf_log(LOG_INFO, "Clean of an existing ET for Thread %04d\n", GetTID()); + emuthread_destroy(t); + } + } + pthread_setspecific(thread_key, p); + // call the function + emuthread_t *et = (emuthread_t*)p; + et->emu->type = EMUTYPE_MAIN; + // setup callstack and run... + x64emu_t* emu = et->emu; + R_RSP -= 4; + uint64_t *sp = (uint64_t*)R_RSP; + *sp = (uintptr_t)et->arg; +// PushExit(emu); + R_RIP = et->fnc; +// DynaRun(et->emu); + void* ret = (void*)R_RAX; + //void* ret = (void*)RunFunctionWithEmu(et->emu, 0, et->fnc, 1, et->arg); + return ret; +} + +EXPORT int my_pthread_attr_destroy(x64emu_t* emu, void* attr) +{ + if(emu->context->stacksizes) + FreeStackSize(emu->context->stacksizes, (uintptr_t)attr); + return pthread_attr_destroy(attr); +} + +EXPORT int my_pthread_attr_getstack(x64emu_t* emu, void* attr, void** stackaddr, size_t* stacksize) +{ + int ret = pthread_attr_getstack(attr, stackaddr, stacksize); + if (ret==0) + GetStackSize(emu, (uintptr_t)attr, stackaddr, stacksize); + return ret; +} + +EXPORT int my_pthread_attr_setstack(x64emu_t* emu, void* attr, void* stackaddr, size_t stacksize) +{ + if(!emu->context->stacksizes) { + emu->context->stacksizes = kh_init(threadstack); + } + AddStackSize(emu->context->stacksizes, (uintptr_t)attr, stackaddr, stacksize); + //Don't call actual setstack... + //return pthread_attr_setstack(attr, stackaddr, stacksize); + return pthread_attr_setstacksize(attr, stacksize); +} + +//EXPORT int my_pthread_create(x64emu_t *emu, void* t, void* attr, void* start_routine, void* arg) +//{ +// int stacksize = 2*1024*1024; //default stack size is 2Mo +// void* attr_stack; +// size_t attr_stacksize; +// int own; +// void* stack; +// +// if(attr) { +// size_t stsize; +// if(pthread_attr_getstacksize(attr, &stsize)==0) +// stacksize = stsize; +// } +// if(GetStackSize(emu, (uintptr_t)attr, &attr_stack, &attr_stacksize)) +// { +// stack = attr_stack; +// stacksize = attr_stacksize; +// own = 0; +// } else { +// stack = malloc(stacksize); +// own = 1; +// } +// +// emuthread_t *et = (emuthread_t*)calloc(1, sizeof(emuthread_t)); +// x64emu_t *emuthread = NewX86Emu(my_context, (uintptr_t)start_routine, (uintptr_t)stack, stacksize, own); +// SetupX86Emu(emuthread); +// SetFS(emuthread, GetFS(emu)); +// et->emu = emuthread; +// et->fnc = (uintptr_t)start_routine; +// et->arg = arg; +// #ifdef DYNAREC +// if(box86_dynarec) { +// // pre-creation of the JIT code for the entry point of the thread +// dynablock_t *current = NULL; +// DBGetBlock(emu, (uintptr_t)start_routine, 1, ¤t); +// } +// #endif +// // create thread +// return pthread_create((pthread_t*)t, (const pthread_attr_t *)attr, +// pthread_routine, et); +//} + +void* my_prepare_thread(x64emu_t *emu, void* f, void* arg, int ssize, void** pet) +{ + int stacksize = (ssize)?ssize:(2*1024*1024); //default stack size is 2Mo + void* stack = malloc(stacksize); + emuthread_t *et = (emuthread_t*)calloc(1, sizeof(emuthread_t)); + x64emu_t *emuthread = NewX64Emu(emu->context, (uintptr_t)f, (uintptr_t)stack, stacksize, 1); + SetupX64Emu(emuthread); + SetFS(emuthread, GetFS(emu)); + et->emu = emuthread; + et->fnc = (uintptr_t)f; + et->arg = arg; + #ifdef DYNAREC + // pre-creation of the JIT code for the entry point of the thread + dynablock_t *current = NULL; + DBGetBlock(emu, (uintptr_t)f, 1, ¤t); + #endif + *pet = et; + return pthread_routine; +} + +//void my_longjmp(x64emu_t* emu, /*struct __jmp_buf_tag __env[1]*/void *p, int32_t __val); + +//#define CANCEL_MAX 8 +//static __thread x64emu_t* cancel_emu[CANCEL_MAX] = {0}; +//static __thread x86_unwind_buff_t* cancel_buff[CANCEL_MAX] = {0}; +//static __thread int cancel_deep = 0; +//EXPORT void my___pthread_register_cancel(void* E, void* B) +//{ +// // get a stack local copy of the args, as may be live in some register depending the architecture (like ARM) +// if(cancel_deep<0) { +// printf_log(LOG_NONE/*LOG_INFO*/, "BOX86: Warning, inconsistant value in __pthread_register_cancel (%d)\n", cancel_deep); +// cancel_deep = 0; +// } +// if(cancel_deep!=CANCEL_MAX-1) +// ++cancel_deep; +// else +// {printf_log(LOG_NONE/*LOG_INFO*/, "BOX86: Warning, calling __pthread_register_cancel(...) too many time\n");} +// +// cancel_emu[cancel_deep] = (x64emu_t*)E; +// // on i386, the function as __cleanup_fct_attribute attribute: so 1st parameter is in register +// x86_unwind_buff_t* buff = cancel_buff[cancel_deep] = (x86_unwind_buff_t*)((x64emu_t*)E)->regs[_AX].dword[0]; +// __pthread_unwind_buf_t * pbuff = AddCancelThread(buff); +// if(__sigsetjmp((struct __jmp_buf_tag*)(void*)pbuff->__cancel_jmp_buf, 0)) { +// //DelCancelThread(cancel_buff); // no del here, it will be delete by unwind_next... +// int i = cancel_deep--; +// x64emu_t* emu = cancel_emu[i]; +// my_longjmp(emu, cancel_buff[i]->__cancel_jmp_buf, 1); +// DynaRun(emu); // resume execution +// return; +// } +// +// __pthread_register_cancel(pbuff); +//} + +//EXPORT void my___pthread_unregister_cancel(x64emu_t* emu, x86_unwind_buff_t* buff) +//{ +// // on i386, the function as __cleanup_fct_attribute attribute: so 1st parameter is in register +// buff = (x86_unwind_buff_t*)R_EAX; +// __pthread_unwind_buf_t * pbuff = GetCancelThread(buff); +// __pthread_unregister_cancel(pbuff); +// +// --cancel_deep; +// DelCancelThread(buff); +//} + +//EXPORT void my___pthread_unwind_next(x64emu_t* emu, void* p) +//{ +// // on i386, the function as __cleanup_fct_attribute attribute: so 1st parameter is in register +// x86_unwind_buff_t* buff = (x86_unwind_buff_t*)R_EAX; +// __pthread_unwind_buf_t pbuff = *GetCancelThread(buff); +// DelCancelThread(buff); +// // function is noreturn, putting stuff on the stack to have it auto-free (is that correct?) +// __pthread_unwind_next(&pbuff); +// // just in case it does return +// emu->quit = 1; +//} +#if 0 +KHASH_MAP_INIT_INT(once, int) + +#define SUPER() \ +GO(0) \ +GO(1) \ +GO(2) \ +GO(3) \ +GO(4) \ +GO(5) \ +GO(6) \ +GO(7) \ +GO(8) \ +GO(9) \ +GO(10) \ +GO(11) \ +GO(12) \ +GO(13) \ +GO(14) \ +GO(15) \ +GO(16) \ +GO(17) \ +GO(18) \ +GO(19) \ +GO(20) \ +GO(21) \ +GO(22) \ +GO(23) \ +GO(24) \ +GO(25) \ +GO(26) \ +GO(27) \ +GO(28) \ +GO(29) + +// once_callback +#define GO(A) \ +static uintptr_t my_once_callback_fct_##A = 0; \ +static void my_once_callback_##A() \ +{ \ + RunFunction(my_context, my_once_callback_fct_##A, 0, 0);\ +} +SUPER() +#undef GO +static void* findonce_callbackFct(void* fct) +{ + if(!fct) return fct; + if(GetNativeFnc((uintptr_t)fct)) return GetNativeFnc((uintptr_t)fct); + #define GO(A) if(my_once_callback_fct_##A == (uintptr_t)fct) return my_once_callback_##A; + SUPER() + #undef GO + #define GO(A) if(my_once_callback_fct_##A == 0) {my_once_callback_fct_##A = (uintptr_t)fct; return my_once_callback_##A; } + SUPER() + #undef GO + printf_log(LOG_NONE, "Warning, no more slot for pthread once_callback callback\n"); + return NULL; +} +// key_destructor +#define GO(A) \ +static uintptr_t my_key_destructor_fct_##A = 0; \ +static void my_key_destructor_##A(void* a) \ +{ \ + RunFunction(my_context, my_key_destructor_fct_##A, 1, a);\ +} +SUPER() +#undef GO +static void* findkey_destructorFct(void* fct) +{ + if(!fct) return fct; + if(GetNativeFnc((uintptr_t)fct)) return GetNativeFnc((uintptr_t)fct); + #define GO(A) if(my_key_destructor_fct_##A == (uintptr_t)fct) return my_key_destructor_##A; + SUPER() + #undef GO + #define GO(A) if(my_key_destructor_fct_##A == 0) {my_key_destructor_fct_##A = (uintptr_t)fct; return my_key_destructor_##A; } + SUPER() + #undef GO + printf_log(LOG_NONE, "Warning, no more slot for pthread key_destructor callback\n"); + return NULL; +} +// cleanup_routine +#define GO(A) \ +static uintptr_t my_cleanup_routine_fct_##A = 0; \ +static void my_cleanup_routine_##A(void* a) \ +{ \ + RunFunction(my_context, my_cleanup_routine_fct_##A, 1, a);\ +} +SUPER() +#undef GO +static void* findcleanup_routineFct(void* fct) +{ + if(!fct) return fct; + if(GetNativeFnc((uintptr_t)fct)) return GetNativeFnc((uintptr_t)fct); + #define GO(A) if(my_cleanup_routine_fct_##A == (uintptr_t)fct) return my_cleanup_routine_##A; + SUPER() + #undef GO + #define GO(A) if(my_cleanup_routine_fct_##A == 0) {my_cleanup_routine_fct_##A = (uintptr_t)fct; return my_cleanup_routine_##A; } + SUPER() + #undef GO + printf_log(LOG_NONE, "Warning, no more slot for pthread cleanup_routine callback\n"); + return NULL; +} + +#undef SUPER + +int EXPORT my_pthread_once(x64emu_t* emu, void* once, void* cb) +{ + return pthread_once(once, findonce_callbackFct(cb)); +} +EXPORT int my___pthread_once(x64emu_t* emu, void* once, void* cb) __attribute__((alias("my_pthread_once"))); + +EXPORT int my_pthread_key_create(x64emu_t* emu, void* key, void* dtor) +{ + return pthread_key_create(key, findkey_destructorFct(dtor)); +} +EXPORT int my___pthread_key_create(x64emu_t* emu, void* key, void* dtor) __attribute__((alias("my_pthread_key_create"))); + +// phtread_cond_init with null attr seems to only write 1 (NULL) dword on x86, while it's 48 bytes on ARM. +// Not sure why as sizeof(pthread_cond_init) is 48 on both platform... But Neverwinter Night init seems to rely on that +// What about cond that are statically initialized? +// Note, this is is a versionned function (the pthread_cond_*), and this seems to correspond to an old behaviour + +KHASH_MAP_INIT_INT(mapcond, pthread_cond_t*); + +// should all access to that map be behind a mutex? +kh_mapcond_t *mapcond = NULL; + +static pthread_cond_t* add_cond(void* cond) +{ + pthread_mutex_lock(&my_context->mutex_thread); + khint_t k; + int ret; + pthread_cond_t *c; + k = kh_put(mapcond, mapcond, (uintptr_t)cond, &ret); + if(!ret) + c = kh_value(mapcond, k); // already there... reinit an existing one? + else + c = kh_value(mapcond, k) = (pthread_cond_t*)calloc(1, sizeof(pthread_cond_t)); + *(void**)cond = cond; + pthread_mutex_unlock(&my_context->mutex_thread); + return c; +} +static pthread_cond_t* get_cond(void* cond) +{ + pthread_cond_t* ret; + int r; + pthread_mutex_lock(&my_context->mutex_thread); + khint_t k = kh_get(mapcond, mapcond, *(uintptr_t*)cond); + if(k==kh_end(mapcond)) { + khint_t k = kh_get(mapcond, mapcond, (uintptr_t)cond); + if(k==kh_end(mapcond)) { + printf_log(LOG_DEBUG, "BOX86: Note: phtread_cond not found, create a new empty one\n"); + ret = (pthread_cond_t*)calloc(1, sizeof(pthread_cond_t)); + k = kh_put(mapcond, mapcond, (uintptr_t)cond, &r); + kh_value(mapcond, k) = ret; + *(void**)cond = cond; + pthread_cond_init(ret, NULL); + } else + ret = kh_value(mapcond, k); + } else + ret = kh_value(mapcond, k); + pthread_mutex_unlock(&my_context->mutex_thread); + return ret; +} +static void del_cond(void* cond) +{ + if(!mapcond) + return; + pthread_mutex_lock(&my_context->mutex_thread); + khint_t k = kh_get(mapcond, mapcond, *(uintptr_t*)cond); + if(k!=kh_end(mapcond)) { + free(kh_value(mapcond, k)); + kh_del(mapcond, mapcond, k); + } + pthread_mutex_unlock(&my_context->mutex_thread); +} +pthread_mutex_t* getAlignedMutex(pthread_mutex_t* m); + +EXPORT int my_pthread_cond_broadcast(x64emu_t* emu, void* cond) +{ + pthread_cond_t * c = get_cond(cond); + return pthread_cond_broadcast(c); +} +EXPORT int my_pthread_cond_destroy(x64emu_t* emu, void* cond) +{ + pthread_cond_t * c = get_cond(cond); + int ret = pthread_cond_destroy(c); + if(c!=cond) del_cond(cond); + return ret; +} +EXPORT int my_pthread_cond_init(x64emu_t* emu, void* cond, void* attr) +{ + pthread_cond_t *c = add_cond(cond); + return pthread_cond_init(c, (const pthread_condattr_t*)attr); +} +EXPORT int my_pthread_cond_signal(x64emu_t* emu, void* cond) +{ + pthread_cond_t * c = get_cond(cond); + return pthread_cond_signal(c); +} +EXPORT int my_pthread_cond_timedwait(x64emu_t* emu, void* cond, void* mutex, void* abstime) +{ + pthread_cond_t * c = get_cond(cond); + return pthread_cond_timedwait(c, getAlignedMutex((pthread_mutex_t*)mutex), (const struct timespec*)abstime); +} +EXPORT int my_pthread_cond_wait(x64emu_t* emu, void* cond, void* mutex) +{ + pthread_cond_t * c = get_cond(cond); + return pthread_cond_wait(c, getAlignedMutex((pthread_mutex_t*)mutex)); +} + +EXPORT int my_pthread_mutexattr_setkind_np(x64emu_t* emu, void* t, int kind) +{ + // does "kind" needs some type of translation? + return pthread_mutexattr_settype(t, kind); +} + +EXPORT int my_pthread_attr_setscope(x64emu_t* emu, void* attr, int scope) +{ + if(scope!=PTHREAD_SCOPE_SYSTEM) printf_log(LOG_INFO, "Warning, scope of call to pthread_attr_setscope(...) changed from %d to PTHREAD_SCOPE_SYSTEM\n", scope); + return pthread_attr_setscope(attr, PTHREAD_SCOPE_SYSTEM); + //The scope is either PTHREAD_SCOPE_SYSTEM or PTHREAD_SCOPE_PROCESS + // but PTHREAD_SCOPE_PROCESS doesn't seem supported on ARM linux, and PTHREAD_SCOPE_SYSTEM is default +} + +EXPORT void my__pthread_cleanup_push_defer(x64emu_t* emu, void* buffer, void* routine, void* arg) +{ + _pthread_cleanup_push_defer(buffer, findcleanup_routineFct(routine), arg); +} + +EXPORT void my__pthread_cleanup_push(x64emu_t* emu, void* buffer, void* routine, void* arg) +{ + _pthread_cleanup_push(buffer, findcleanup_routineFct(routine), arg); +} + +EXPORT void my__pthread_cleanup_pop_restore(x64emu_t* emu, void* buffer, int exec) +{ + _pthread_cleanup_pop_restore(buffer, exec); +} + +EXPORT void my__pthread_cleanup_pop(x64emu_t* emu, void* buffer, int exec) +{ + _pthread_cleanup_pop(buffer, exec); +} + +// getaffinity_np (pthread or attr) hav an "old" version (glibc-2.3.3) that only have 2 args, cpusetsize is omited +EXPORT int my_pthread_getaffinity_np(x64emu_t* emu, pthread_t thread, int cpusetsize, void* cpuset) +{ + if(cpusetsize>0x1000) { + // probably old version of the function, that didn't have cpusetsize.... + cpuset = (void*)cpusetsize; + cpusetsize = sizeof(cpu_set_t); + } + + int ret = pthread_getaffinity_np(thread, cpusetsize, cpuset); + if(ret<0) { + printf_log(LOG_INFO, "Warning, pthread_getaffinity_np(%p, %d, %p) errored, with errno=%d\n", (void*)thread, cpusetsize, cpuset, errno); + } + + return ret; +} + +EXPORT int my_pthread_setaffinity_np(x64emu_t* emu, pthread_t thread, int cpusetsize, void* cpuset) +{ + if(cpusetsize>0x1000) { + // probably old version of the function, that didn't have cpusetsize.... + cpuset = (void*)cpusetsize; + cpusetsize = sizeof(cpu_set_t); + } + + int ret = pthread_setaffinity_np(thread, cpusetsize, cpuset); + if(ret<0) { + printf_log(LOG_INFO, "Warning, pthread_setaffinity_np(%p, %d, %p) errored, with errno=%d\n", (void*)thread, cpusetsize, cpuset, errno); + } + + return ret; +} + +EXPORT int my_pthread_attr_setaffinity_np(x64emu_t* emu, void* attr, uint32_t cpusetsize, void* cpuset) +{ + if(cpusetsize>0x1000) { + // probably old version of the function, that didn't have cpusetsize.... + cpuset = (void*)cpusetsize; + cpusetsize = sizeof(cpu_set_t); + } + + int ret = pthread_attr_setaffinity_np(attr, cpusetsize, cpuset); + if(ret<0) { + printf_log(LOG_INFO, "Warning, pthread_attr_setaffinity_np(%p, %d, %p) errored, with errno=%d\n", attr, cpusetsize, cpuset, errno); + } + + return ret; +} + +EXPORT int my_pthread_kill(x64emu_t* emu, void* thread, int sig) +{ + // check for old "is everything ok?" + if(thread==NULL && sig==0) + return pthread_kill(pthread_self(), 0); + return pthread_kill((pthread_t)thread, sig); +} + +EXPORT void my_pthread_exit(x64emu_t* emu, void* retval) +{ + emu->quit = 1; // to be safe + pthread_exit(retval); +} + +#ifndef NOALIGN +// mutex alignment +KHASH_MAP_INIT_INT(mutex, pthread_mutex_t*) + +static kh_mutex_t* unaligned_mutex = NULL; + +pthread_mutex_t* getAlignedMutex(pthread_mutex_t* m) +{ + if(!(((uintptr_t)m)&3)) + return m; + khint_t k = kh_get(mutex, unaligned_mutex, (uintptr_t)m); + if(k!=kh_end(unaligned_mutex)) + return kh_value(unaligned_mutex, k); + int r; + k = kh_put(mutex, unaligned_mutex, (uintptr_t)m, &r); + pthread_mutex_t* ret = kh_value(unaligned_mutex, k) = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); + memcpy(ret, m, sizeof(pthread_mutex_t)); + return ret; +} +EXPORT int my_pthread_mutex_destroy(pthread_mutex_t *m) +{ + if(!(((uintptr_t)m)&3)) + return pthread_mutex_destroy(m); + khint_t k = kh_get(mutex, unaligned_mutex, (uintptr_t)m); + if(k!=kh_end(unaligned_mutex)) { + pthread_mutex_t *n = kh_value(unaligned_mutex, k); + kh_del(mutex, unaligned_mutex, k); + int ret = pthread_mutex_destroy(n); + free(n); + return ret; + } + return pthread_mutex_destroy(m); +} +int my___pthread_mutex_destroy(pthread_mutex_t *m) __attribute__((alias("my_pthread_mutex_destroy"))); + +EXPORT int my_pthread_mutex_init(pthread_mutex_t *m, pthread_mutexattr_t *att) +{ + return pthread_mutex_init(getAlignedMutex(m), att); +} +EXPORT int my___pthread_mutex_init(pthread_mutex_t *m, pthread_mutexattr_t *att) __attribute__((alias("my_pthread_mutex_init"))); + +EXPORT int my_pthread_mutex_lock(pthread_mutex_t *m) +{ + return pthread_mutex_lock(getAlignedMutex(m)); +} +EXPORT int my___pthread_mutex_lock(pthread_mutex_t *m) __attribute__((alias("my_pthread_mutex_lock"))); + +EXPORT int my_pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec * t) +{ + return pthread_mutex_timedlock(getAlignedMutex(m), t); +} +EXPORT int my___pthread_mutex_trylock(pthread_mutex_t *m, const struct timespec * t) __attribute__((alias("my_pthread_mutex_timedlock"))); + +EXPORT int my_pthread_mutex_trylock(pthread_mutex_t *m) +{ + return pthread_mutex_trylock(getAlignedMutex(m)); +} +EXPORT int my___pthread_mutex_unlock(pthread_mutex_t *m) __attribute__((alias("my_pthread_mutex_trylock"))); + +EXPORT int my_pthread_mutex_unlock(pthread_mutex_t *m) +{ + return pthread_mutex_unlock(getAlignedMutex(m)); +} + +#endif +#endif +//static void emujmpbuf_destroy(void* p) +//{ +// emu_jmpbuf_t *ej = (emu_jmpbuf_t*)p; +// free(ej->jmpbuf); +// free(ej); +//} + +//static pthread_key_t jmpbuf_key; + +//emu_jmpbuf_t* GetJmpBuf() +//{ +// emu_jmpbuf_t *ejb = (emu_jmpbuf_t*)pthread_getspecific(jmpbuf_key); +// if(!ejb) { +// ejb = (emu_jmpbuf_t*)calloc(1, sizeof(emu_jmpbuf_t)); +// ejb->jmpbuf = calloc(1, sizeof(struct __jmp_buf_tag)); +// pthread_setspecific(jmpbuf_key, ejb); +// } +// return ejb; +//} + +void init_pthread_helper() +{ +// InitCancelThread(); +// mapcond = kh_init(mapcond); +// pthread_key_create(&jmpbuf_key, emujmpbuf_destroy); +#ifndef NOALIGN +// unaligned_mutex = kh_init(mutex); +#endif +} + +void fini_pthread_helper(box64context_t* context) +{ +// FreeCancelThread(context); + CleanStackSize(context); +// pthread_cond_t *cond; +// kh_foreach_value(mapcond, cond, +// pthread_cond_destroy(cond); +// free(cond); +// ); +// kh_destroy(mapcond, mapcond); +// mapcond = NULL; +#ifndef NOALIGN +// pthread_mutex_t *m; +// kh_foreach_value(unaligned_mutex, m, +// pthread_mutex_destroy(m); +// free(m); +// ); +// kh_destroy(mutex, unaligned_mutex); +#endif +} |