From ec03ef8bf4f27a6c3ab9d5f6b074d72c2019c730 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 22 Jun 2007 14:29:50 +0000 Subject: [PATCH] custom setjmp/longjmp implementation --- panda/src/pipeline/contextSwitch.c | 75 +++++++++++++++++++++++------- panda/src/pipeline/contextSwitch.h | 27 ++++++++++- 2 files changed, 82 insertions(+), 20 deletions(-) diff --git a/panda/src/pipeline/contextSwitch.c b/panda/src/pipeline/contextSwitch.c index de6ab117f4..e30b7ab325 100644 --- a/panda/src/pipeline/contextSwitch.c +++ b/panda/src/pipeline/contextSwitch.c @@ -19,11 +19,10 @@ #include "contextSwitch.h" #include -#include #ifdef THREAD_SIMPLE_IMPL -#ifdef HAVE_UCONTEXT_H +#if defined(HAVE_UCONTEXT_H) /* The getcontext() / setcontext() implementation. Easy-peasy. */ @@ -81,7 +80,6 @@ switch_to_thread_context(struct ThreadContext *context) { abort(); } - #else /* The setjmp() / longjmp() implementation. A bit hackier. */ @@ -90,25 +88,62 @@ switch_to_thread_context(struct ThreadContext *context) { then call setjmp() to record that stack pointer in the _jmp_context. Then restore back to the original stack pointer. */ +#if defined(__i386__) +/* Here is own own implementation of setjmp and longjmp for I386, via + GNU syntax. */ + +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 JB_SP +#ifndef CS_JB_SP #if defined(IS_OSX) && defined(__i386__) /* We have determined this value empirically, via test_setjmp.cxx in this directory. */ -#define JB_SP 9 +#define CS_JB_SP 9 #elif defined(WIN32) /* We have determined this value empirically, via test_setjmp.cxx in this directory. */ -#define JB_SP 4 +#define CS_JB_SP 4 #endif -#endif /* JB_SP */ +#endif /* CS_JB_SP */ static struct ThreadContext *st_context; static unsigned char *st_stack; @@ -116,21 +151,25 @@ static size_t st_stack_size; static ContextFunction *st_thread_func; static void *st_data; -static jmp_buf orig_stack; +static cs_jmp_buf orig_stack; -static void +/* 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 (setjmp(st_context->_jmp_context) == 0) { + 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. */ - longjmp(orig_stack, 1); + cs_longjmp(orig_stack); /* Shouldn't get here. */ abort(); @@ -148,23 +187,23 @@ 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 (setjmp(orig_stack) == 0) { + 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 jmp_buf temp; - if (setjmp(temp) == 0) { + 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. */ - (*(void **)&temp[JB_SP]) = (st_stack + st_stack_size); + (*(void **)&temp[CS_JB_SP]) = (st_stack + st_stack_size); /* And finally, we place ourselves on the new stack by using longjmp() to reload the modified context. */ - longjmp(temp, 1); + cs_longjmp(temp); /* Shouldn't get here. */ abort(); @@ -198,7 +237,7 @@ init_thread_context(struct ThreadContext *context, void save_thread_context(struct ThreadContext *context, ContextFunction *next_context, void *data) { - if (setjmp(context->_jmp_context) != 0) { + 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; @@ -217,7 +256,7 @@ void save_thread_context(struct ThreadContext *context, void switch_to_thread_context(struct ThreadContext *context) { - longjmp(context->_jmp_context, 1); + cs_longjmp(context->_jmp_context); /* Shouldn't get here. */ abort(); diff --git a/panda/src/pipeline/contextSwitch.h b/panda/src/pipeline/contextSwitch.h index d0e503e1d4..01d15f2fde 100644 --- a/panda/src/pipeline/contextSwitch.h +++ b/panda/src/pipeline/contextSwitch.h @@ -34,7 +34,7 @@ #ifdef THREAD_SIMPLE_IMPL -#ifdef HAVE_UCONTEXT_H +#if defined(HAVE_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. */ @@ -48,10 +48,33 @@ struct ThreadContext { /* 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(__i386__) +/* Maybe we can implement our own setjmp/longjmp in assembly code. + This will be safe 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 +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 +#endif + +#endif /* __i386__ */ + struct ThreadContext { - jmp_buf _jmp_context; + cs_jmp_buf _jmp_context; }; #endif /* HAVE_UCONTEXT_H */