|
@@ -17,418 +17,26 @@
|
|
|
#include <stdlib.h>
|
|
#include <stdlib.h>
|
|
|
#include <stdio.h>
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
-#ifdef THREAD_SIMPLE_IMPL
|
|
|
|
|
|
|
+#if defined(THREAD_SIMPLE_IMPL) && !defined(CPPPARSER)
|
|
|
|
|
|
|
|
-#if defined(PHAVE_UCONTEXT_H)
|
|
|
|
|
-
|
|
|
|
|
-#else /* PHAVE_UCONTEXT_H */
|
|
|
|
|
-
|
|
|
|
|
-#endif /* PHAVE_UCONTEXT_H */
|
|
|
|
|
-
|
|
|
|
|
-#if defined(PHAVE_UCONTEXT_H)
|
|
|
|
|
-
|
|
|
|
|
-/* We'd prefer to use getcontext() / setcontext() to portably change
|
|
|
|
|
- execution contexts within C code. That's what these library
|
|
|
|
|
- functions are designed for. */
|
|
|
|
|
-#ifdef __APPLE__
|
|
|
|
|
-#include <sys/ucontext.h>
|
|
|
|
|
-#else
|
|
|
|
|
-#include <ucontext.h>
|
|
|
|
|
|
|
+#ifdef WIN32
|
|
|
|
|
+/* Define this macro to use native Windows threading constructs to
|
|
|
|
|
+ switch contexts. */
|
|
|
|
|
+#define WIN_THREAD_CONTEXT
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
-struct ThreadContext {
|
|
|
|
|
- ucontext_t _ucontext;
|
|
|
|
|
-#ifdef __APPLE__
|
|
|
|
|
- // Due to a bug in OSX 10.5, the system ucontext_t declaration
|
|
|
|
|
- // doesn't reserve enough space, and we need to reserve some
|
|
|
|
|
- // additional space to make room.
|
|
|
|
|
-#define EXTRA_PADDING_SIZE 4096
|
|
|
|
|
- char _extra_padding[EXTRA_PADDING_SIZE];
|
|
|
|
|
-#endif
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-static void
|
|
|
|
|
-begin_context(ContextFunction *thread_func, void *data) {
|
|
|
|
|
- (*thread_func)(data);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-void
|
|
|
|
|
-init_thread_context(struct ThreadContext *context,
|
|
|
|
|
- unsigned char *stack, size_t stack_size,
|
|
|
|
|
- ContextFunction *thread_func, void *data) {
|
|
|
|
|
- if (getcontext(&context->_ucontext) != 0) {
|
|
|
|
|
- fprintf(stderr, "getcontext failed in init_thread_context!\n");
|
|
|
|
|
- // Too bad for you.
|
|
|
|
|
- abort();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- context->_ucontext.uc_stack.ss_sp = stack;
|
|
|
|
|
- context->_ucontext.uc_stack.ss_size = stack_size;
|
|
|
|
|
- context->_ucontext.uc_stack.ss_flags = 0;
|
|
|
|
|
- context->_ucontext.uc_link = NULL;
|
|
|
|
|
-
|
|
|
|
|
- makecontext(&context->_ucontext, (void (*)())&begin_context, 2, thread_func, data);
|
|
|
|
|
-}
|
|
|
|
|
|
|
+#if defined(WIN_THREAD_CONTEXT)
|
|
|
|
|
|
|
|
-void
|
|
|
|
|
-save_thread_context(struct ThreadContext *context,
|
|
|
|
|
- ContextFunction *next_context, void *data) {
|
|
|
|
|
- /* getcontext requires us to use a volatile auto variable to
|
|
|
|
|
- differentiate between pass 1 (immediate return) and pass 2
|
|
|
|
|
- (return from setcontext). */
|
|
|
|
|
- volatile int context_return = 0;
|
|
|
|
|
|
|
+#include "contextSwitch_windows_src.c"
|
|
|
|
|
|
|
|
- if (getcontext(&context->_ucontext) != 0) {
|
|
|
|
|
- fprintf(stderr, "getcontext failed!\n");
|
|
|
|
|
- // Nothing to do here.
|
|
|
|
|
- abort();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+#elif defined(PHAVE_UCONTEXT_H)
|
|
|
|
|
|
|
|
- if (context_return) {
|
|
|
|
|
- /* We have just returned from setcontext. In this case, return
|
|
|
|
|
- from the function. The stack is still good. */
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- context_return = 1;
|
|
|
|
|
-
|
|
|
|
|
- /* We are still in the calling thread. In this case, we cannot
|
|
|
|
|
- return from the function without damaging the stack. Insted,
|
|
|
|
|
- call next_context() and trust the caller to call
|
|
|
|
|
- switch_to_thread_context() in there somewhere. */
|
|
|
|
|
-
|
|
|
|
|
- (*next_context)(data);
|
|
|
|
|
-
|
|
|
|
|
- /* We shouldn't get here. */
|
|
|
|
|
- abort();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-void
|
|
|
|
|
-switch_to_thread_context(struct ThreadContext *context) {
|
|
|
|
|
- setcontext(&context->_ucontext);
|
|
|
|
|
-
|
|
|
|
|
- /* Shouldn't get here. */
|
|
|
|
|
- abort();
|
|
|
|
|
-}
|
|
|
|
|
|
|
+#include "contextSwitch_ucontext_src.c"
|
|
|
|
|
|
|
|
#else
|
|
#else
|
|
|
|
|
|
|
|
-/* Unfortunately, setcontext() is not defined everywhere (even though
|
|
|
|
|
- it claims to be adopted by Posix). So we have to fall back to
|
|
|
|
|
- setjmp() / longjmp() in its absence. This is a hackier solution. */
|
|
|
|
|
-
|
|
|
|
|
-#if defined(_M_IX86) || defined(__i386__)
|
|
|
|
|
-/* Maybe we can implement our own setjmp/longjmp in assembly code.
|
|
|
|
|
- This will be safer than the system version, since who knows what
|
|
|
|
|
- that one's really doing? */
|
|
|
|
|
-
|
|
|
|
|
-typedef int cs_jmp_buf[33];
|
|
|
|
|
-
|
|
|
|
|
-#define CS_JB_SP 4
|
|
|
|
|
-
|
|
|
|
|
-#else
|
|
|
|
|
-
|
|
|
|
|
-/* Fall back to the system implmentation of setjmp/longjmp. */
|
|
|
|
|
-#include <setjmp.h>
|
|
|
|
|
-
|
|
|
|
|
-typedef jmp_buf cs_jmp_buf;
|
|
|
|
|
-#define cs_setjmp setjmp
|
|
|
|
|
-#define cs_longjmp(buf) longjmp(buf, 1)
|
|
|
|
|
-
|
|
|
|
|
-#ifdef JB_SP
|
|
|
|
|
-#define CS_JB_SP JB_SP
|
|
|
|
|
-
|
|
|
|
|
-#elif defined(__ppc__)
|
|
|
|
|
- /* This was determined experimentally through test_setjmp. */
|
|
|
|
|
-#define CS_JB_SP 0
|
|
|
|
|
-
|
|
|
|
|
-#endif
|
|
|
|
|
-
|
|
|
|
|
-#endif /* __i386__ */
|
|
|
|
|
-
|
|
|
|
|
-struct ThreadContext {
|
|
|
|
|
- cs_jmp_buf _jmp_context;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/* The approach is: hack our way onto the new stack pointer right now,
|
|
|
|
|
- then call setjmp() to record that stack pointer in the
|
|
|
|
|
- _jmp_context. Then restore back to the original stack pointer. */
|
|
|
|
|
-
|
|
|
|
|
-#if defined(_M_IX86)
|
|
|
|
|
-/* Here is our own implementation of setjmp and longjmp for I386, via
|
|
|
|
|
- Windows syntax. */
|
|
|
|
|
-
|
|
|
|
|
-/* warning C4731: frame pointer register 'ebp' modified by inline assembly code */
|
|
|
|
|
-#pragma warning(disable:4731)
|
|
|
|
|
-
|
|
|
|
|
-int
|
|
|
|
|
-cs_setjmp(cs_jmp_buf env) {
|
|
|
|
|
- __asm {
|
|
|
|
|
- pop ebp; /* Restore the frame pointer that the compiler pushed */
|
|
|
|
|
-
|
|
|
|
|
- pop edx; /* edx = return address */
|
|
|
|
|
- pop eax; /* eax = &env */
|
|
|
|
|
- push eax; /* keep &env on the stack; the caller will remove it */
|
|
|
|
|
-
|
|
|
|
|
- mov [eax + 0], ebx;
|
|
|
|
|
- mov [eax + 4], edi;
|
|
|
|
|
- mov [eax + 8], esi;
|
|
|
|
|
- mov [eax + 12], ebp;
|
|
|
|
|
- mov [eax + 16], esp;
|
|
|
|
|
- mov [eax + 20], edx;
|
|
|
|
|
-
|
|
|
|
|
- fnsave [eax + 24]; /* save floating-point state */
|
|
|
|
|
-
|
|
|
|
|
- xor eax,eax; /* return 0: pass 1 return */
|
|
|
|
|
- jmp edx; /* this works like ret */
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-void
|
|
|
|
|
-cs_longjmp(cs_jmp_buf env) {
|
|
|
|
|
- _asm {
|
|
|
|
|
- mov eax, env;
|
|
|
|
|
-
|
|
|
|
|
- mov ebx, [eax + 0];
|
|
|
|
|
- mov edi, [eax + 4];
|
|
|
|
|
- mov esi, [eax + 8];
|
|
|
|
|
- mov ebp, [eax + 12];
|
|
|
|
|
- mov esp, [eax + 16];
|
|
|
|
|
- mov edx, [eax + 20];
|
|
|
|
|
-
|
|
|
|
|
- frstor [eax + 24]; /* restore floating-point state */
|
|
|
|
|
-
|
|
|
|
|
- mov eax, 1; /* return 1 from setjmp: pass 2 return */
|
|
|
|
|
- jmp edx; /* return from above setjmp call */
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-#elif defined(__i386__)
|
|
|
|
|
-/* Here is our own implementation of setjmp and longjmp for I386, via
|
|
|
|
|
- GNU syntax. */
|
|
|
|
|
-
|
|
|
|
|
-#if defined(IS_LINUX)
|
|
|
|
|
-/* On Linux, the leading underscores are not implicitly added for C
|
|
|
|
|
- function names. */
|
|
|
|
|
-#define cs_setjmp _cs_setjmp
|
|
|
|
|
-#define cs_longjmp _cs_longjmp
|
|
|
|
|
-#endif
|
|
|
|
|
-
|
|
|
|
|
-int cs_setjmp(cs_jmp_buf env);
|
|
|
|
|
-void cs_longjmp(cs_jmp_buf env);
|
|
|
|
|
-
|
|
|
|
|
-__asm__
|
|
|
|
|
-("_cs_setjmp:\n"
|
|
|
|
|
- "popl %edx\n"
|
|
|
|
|
- "popl %eax\n"
|
|
|
|
|
- "pushl %eax\n"
|
|
|
|
|
-
|
|
|
|
|
- "movl %ebx, 0(%eax)\n"
|
|
|
|
|
- "movl %edi, 4(%eax)\n"
|
|
|
|
|
- "movl %esi, 8(%eax)\n"
|
|
|
|
|
- "movl %ebp, 12(%eax)\n"
|
|
|
|
|
- "movl %esp, 16(%eax)\n"
|
|
|
|
|
- "movl %edx, 20(%eax)\n"
|
|
|
|
|
-
|
|
|
|
|
- "fnsave 24(%eax)\n"
|
|
|
|
|
-
|
|
|
|
|
- "xorl %eax, %eax\n"
|
|
|
|
|
- "jmp *%edx\n");
|
|
|
|
|
-
|
|
|
|
|
-__asm__
|
|
|
|
|
-("_cs_longjmp:\n"
|
|
|
|
|
- "popl %edx\n"
|
|
|
|
|
- "popl %eax\n"
|
|
|
|
|
-
|
|
|
|
|
- "movl 0(%eax), %ebx\n"
|
|
|
|
|
- "movl 4(%eax), %edi\n"
|
|
|
|
|
- "movl 8(%eax), %esi\n"
|
|
|
|
|
- "movl 12(%eax), %ebp\n"
|
|
|
|
|
- "movl 16(%eax), %esp\n"
|
|
|
|
|
- "movl 20(%eax), %edx\n"
|
|
|
|
|
-
|
|
|
|
|
- "frstor 24(%eax)\n"
|
|
|
|
|
-
|
|
|
|
|
- "mov $1,%eax\n"
|
|
|
|
|
- "jmp *%edx\n");
|
|
|
|
|
-
|
|
|
|
|
-#endif /* __i386__ */
|
|
|
|
|
-
|
|
|
|
|
-/* Ideally, including setjmp.h would have defined JB_SP, which will
|
|
|
|
|
- tell us where in the context structure we can muck with the stack
|
|
|
|
|
- pointer. If it didn't define this symbol, we have to guess it. */
|
|
|
|
|
-#ifndef CS_JB_SP
|
|
|
|
|
-
|
|
|
|
|
-#if defined(IS_OSX) && defined(__i386__)
|
|
|
|
|
-/* We have determined this value empirically, via test_setjmp.cxx in
|
|
|
|
|
- this directory. */
|
|
|
|
|
-#define CS_JB_SP 9
|
|
|
|
|
-
|
|
|
|
|
-#endif
|
|
|
|
|
-
|
|
|
|
|
-#endif /* CS_JB_SP */
|
|
|
|
|
-
|
|
|
|
|
-static struct ThreadContext *st_context;
|
|
|
|
|
-static unsigned char *st_stack;
|
|
|
|
|
-static size_t st_stack_size;
|
|
|
|
|
-static ContextFunction *st_thread_func;
|
|
|
|
|
-static void *st_data;
|
|
|
|
|
-
|
|
|
|
|
-static cs_jmp_buf orig_stack;
|
|
|
|
|
-
|
|
|
|
|
-/* We can't declare this function static--gcc might want to inline it
|
|
|
|
|
- in that case, and then the code crashes. I hope this doesn't mean
|
|
|
|
|
- that the stack is still not getting restored correctly in the above
|
|
|
|
|
- assembly code. */
|
|
|
|
|
-void
|
|
|
|
|
-setup_context_2(void) {
|
|
|
|
|
- /* Here we are running on the new stack. Copy the key data onto our
|
|
|
|
|
- new stack. */
|
|
|
|
|
- ContextFunction *volatile thread_func = st_thread_func;
|
|
|
|
|
- void *volatile data = st_data;
|
|
|
|
|
-
|
|
|
|
|
- if (cs_setjmp(st_context->_jmp_context) == 0) {
|
|
|
|
|
- /* The _jmp_context is set up and ready to run. Now restore the
|
|
|
|
|
- original stack and return. We can't simply return from this
|
|
|
|
|
- function, since it might overwrite some of the stack data on
|
|
|
|
|
- the way out. */
|
|
|
|
|
- cs_longjmp(orig_stack);
|
|
|
|
|
-
|
|
|
|
|
- /* Shouldn't get here. */
|
|
|
|
|
- abort();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* We come here the first time the thread starts. */
|
|
|
|
|
- (*thread_func)(data);
|
|
|
|
|
-
|
|
|
|
|
- /* We shouldn't get here, since we don't expect the thread_func to
|
|
|
|
|
- return. */
|
|
|
|
|
- abort();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-static void
|
|
|
|
|
-setup_context_1(void) {
|
|
|
|
|
- /* Save the current stack frame so we can return to it (at the end
|
|
|
|
|
- of setup_context_2()). */
|
|
|
|
|
- if (cs_setjmp(orig_stack) == 0) {
|
|
|
|
|
- /* First, switch to the new stack. Save the current context using
|
|
|
|
|
- setjmp(). This saves out all of the processor register values,
|
|
|
|
|
- though it doesn't muck with the stack. */
|
|
|
|
|
- static cs_jmp_buf temp;
|
|
|
|
|
- if (cs_setjmp(temp) == 0) {
|
|
|
|
|
- /* This is the initial return from setjmp. Still the original
|
|
|
|
|
- stack. */
|
|
|
|
|
-
|
|
|
|
|
- /* Now we overwrite the stack pointer value in the saved
|
|
|
|
|
- register context. This doesn't work with all implementations
|
|
|
|
|
- of setjmp/longjmp. */
|
|
|
|
|
-
|
|
|
|
|
- /* We give ourselves a small buffer of unused space at the top
|
|
|
|
|
- of the stack, to allow for the stack frame and such that this
|
|
|
|
|
- code might be assuming is there. */
|
|
|
|
|
- (*(void **)&temp[CS_JB_SP]) = (st_stack + st_stack_size - 0x100);
|
|
|
|
|
-
|
|
|
|
|
- /* And finally, we place ourselves on the new stack by using
|
|
|
|
|
- longjmp() to reload the modified context. */
|
|
|
|
|
- cs_longjmp(temp);
|
|
|
|
|
-
|
|
|
|
|
- /* Shouldn't get here. */
|
|
|
|
|
- abort();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* This is the second return from setjmp. Now we're on the new
|
|
|
|
|
- stack. */
|
|
|
|
|
- setup_context_2();
|
|
|
|
|
-
|
|
|
|
|
- /* Shouldn't get here. */
|
|
|
|
|
- abort();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* By now we are back to the original stack. */
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-void
|
|
|
|
|
-init_thread_context(struct ThreadContext *context,
|
|
|
|
|
- unsigned char *stack, size_t stack_size,
|
|
|
|
|
- ContextFunction *thread_func, void *data) {
|
|
|
|
|
- /* Copy all of the input parameters to static variables, then begin
|
|
|
|
|
- the stack-switching process. */
|
|
|
|
|
- st_context = context;
|
|
|
|
|
- st_stack = stack;
|
|
|
|
|
- st_stack_size = stack_size;
|
|
|
|
|
- st_thread_func = thread_func;
|
|
|
|
|
- st_data = data;
|
|
|
|
|
-
|
|
|
|
|
- setup_context_1();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-void
|
|
|
|
|
-save_thread_context(struct ThreadContext *context,
|
|
|
|
|
- ContextFunction *next_context, void *data) {
|
|
|
|
|
- if (cs_setjmp(context->_jmp_context) != 0) {
|
|
|
|
|
- /* We have just returned from longjmp. In this case, return from
|
|
|
|
|
- the function. The stack is still good. */
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* We are still in the calling thread. In this case, we cannot
|
|
|
|
|
- return from the function without damaging the stack. Insted,
|
|
|
|
|
- call next_context() and trust the caller to call
|
|
|
|
|
- switch_to_thread_context() in there somewhere. */
|
|
|
|
|
-
|
|
|
|
|
- (*next_context)(data);
|
|
|
|
|
-
|
|
|
|
|
- /* We shouldn't get here. */
|
|
|
|
|
- abort();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-void
|
|
|
|
|
-switch_to_thread_context(struct ThreadContext *context) {
|
|
|
|
|
- cs_longjmp(context->_jmp_context);
|
|
|
|
|
-
|
|
|
|
|
- /* Shouldn't get here. */
|
|
|
|
|
- abort();
|
|
|
|
|
-}
|
|
|
|
|
|
|
+#include "contextSwitch_longjmp_src.c"
|
|
|
|
|
|
|
|
#endif /* PHAVE_UCONTEXT_H */
|
|
#endif /* PHAVE_UCONTEXT_H */
|
|
|
|
|
|
|
|
-struct ThreadContext *
|
|
|
|
|
-alloc_thread_context() {
|
|
|
|
|
- struct ThreadContext *context =
|
|
|
|
|
- (struct ThreadContext *)malloc(sizeof(struct ThreadContext));
|
|
|
|
|
-
|
|
|
|
|
-#if defined(__APPLE__) && defined(_DEBUG)
|
|
|
|
|
- {
|
|
|
|
|
- int p;
|
|
|
|
|
- // Pre-fill the extra_padding with bytes that we can recognize
|
|
|
|
|
- // later.
|
|
|
|
|
- for (p = 0; p < EXTRA_PADDING_SIZE; ++p) {
|
|
|
|
|
- context->_extra_padding[p] = (p & 0xff);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-#endif // __APPLE__
|
|
|
|
|
-
|
|
|
|
|
- return context;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-void
|
|
|
|
|
-free_thread_context(struct ThreadContext *context) {
|
|
|
|
|
-#if defined(__APPLE__) && defined(_DEBUG)
|
|
|
|
|
- {
|
|
|
|
|
- // Because of the OSX 10.5 bug, we anticipate that the extra_padding
|
|
|
|
|
- // may have been filled in with junk. Confirm this.
|
|
|
|
|
- int p = EXTRA_PADDING_SIZE;
|
|
|
|
|
- while (p > 0) {
|
|
|
|
|
- --p;
|
|
|
|
|
- if (context->_extra_padding[p] != (char)(p & 0xff)) {
|
|
|
|
|
- fprintf(stderr, "Context was mangled at byte %d: %d!\n", p, context->_extra_padding[p]);
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-#endif // __APPLE__
|
|
|
|
|
- free(context);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
#endif /* THREAD_SIMPLE_IMPL */
|
|
#endif /* THREAD_SIMPLE_IMPL */
|