custom setjmp/longjmp implementation

This commit is contained in:
David Rose 2007-06-22 14:29:50 +00:00
parent 666f897031
commit ec03ef8bf4
2 changed files with 82 additions and 20 deletions

View File

@ -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();

View File

@ -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 */