Browse Source

custom setjmp/longjmp implementation

David Rose 18 years ago
parent
commit
ec03ef8bf4
2 changed files with 82 additions and 20 deletions
  1. 57 18
      panda/src/pipeline/contextSwitch.c
  2. 25 2
      panda/src/pipeline/contextSwitch.h

+ 57 - 18
panda/src/pipeline/contextSwitch.c

@@ -19,11 +19,10 @@
 #include "contextSwitch.h"
 
 #include <stdlib.h>
-#include <stdio.h>
 
 #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();

+ 25 - 2
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 <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
+#endif
+
+#endif  /* __i386__ */
+
 struct ThreadContext {
-  jmp_buf _jmp_context;
+  cs_jmp_buf _jmp_context;
 };
 
 #endif  /* HAVE_UCONTEXT_H */