custom setjmp/longjmp implementation
This commit is contained in:
parent
666f897031
commit
ec03ef8bf4
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue