contextSwitch_longjmp_src.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /* Filename: contextSwitch_longjmp_src.c
  2. * Created by: drose (15Apr10)
  3. *
  4. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  5. *
  6. * PANDA 3D SOFTWARE
  7. * Copyright (c) Carnegie Mellon University. All rights reserved.
  8. *
  9. * All use of this software is subject to the terms of the revised BSD
  10. * license. You should have received a copy of this license along
  11. * with this source code in a file named "LICENSE."
  12. *
  13. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  14. /* This is the implementation of user-space context switching using
  15. setmp() / longjmp(). This is the hackier implementation,
  16. which is necessary if setcontext() is not available. */
  17. const int needs_stack_prealloc = 1;
  18. const int is_os_threads = 0;
  19. #if defined(_M_IX86) || defined(__i386__)
  20. /* Maybe we can implement our own setjmp/longjmp in assembly code.
  21. This will be safer than the system version, since who knows what
  22. that one's really doing? */
  23. typedef int cs_jmp_buf[33];
  24. #define CS_JB_SP 4
  25. #else
  26. /* Fall back to the system implementation of setjmp/longjmp. */
  27. #include <setjmp.h>
  28. typedef jmp_buf cs_jmp_buf;
  29. #define cs_setjmp setjmp
  30. #define cs_longjmp(buf) longjmp(buf, 1)
  31. #ifdef JB_SP
  32. #define CS_JB_SP JB_SP
  33. #elif defined(__ppc__)
  34. /* This was determined experimentally through test_setjmp. */
  35. #define CS_JB_SP 0
  36. #endif
  37. #endif /* __i386__ */
  38. struct ThreadContext {
  39. cs_jmp_buf _jmp_context;
  40. };
  41. /* The approach is: hack our way onto the new stack pointer right now,
  42. then call setjmp() to record that stack pointer in the
  43. _jmp_context. Then restore back to the original stack pointer. */
  44. #if defined(_M_IX86)
  45. /* Here is our own implementation of setjmp and longjmp for I386, via
  46. Windows syntax. */
  47. /* warning C4731: frame pointer register 'ebp' modified by inline assembly code */
  48. #pragma warning(disable:4731)
  49. int
  50. cs_setjmp(cs_jmp_buf env) {
  51. __asm {
  52. pop ebp; /* Restore the frame pointer that the compiler pushed */
  53. pop edx; /* edx = return address */
  54. pop eax; /* eax = &env */
  55. push eax; /* keep &env on the stack; the caller will remove it */
  56. mov [eax + 0], ebx;
  57. mov [eax + 4], edi;
  58. mov [eax + 8], esi;
  59. mov [eax + 12], ebp;
  60. mov [eax + 16], esp;
  61. mov [eax + 20], edx;
  62. fnsave [eax + 24]; /* save floating-point state */
  63. xor eax,eax; /* return 0: pass 1 return */
  64. jmp edx; /* this works like ret */
  65. }
  66. }
  67. void
  68. cs_longjmp(cs_jmp_buf env) {
  69. _asm {
  70. mov eax, env;
  71. mov ebx, [eax + 0];
  72. mov edi, [eax + 4];
  73. mov esi, [eax + 8];
  74. mov ebp, [eax + 12];
  75. mov esp, [eax + 16];
  76. mov edx, [eax + 20];
  77. frstor [eax + 24]; /* restore floating-point state */
  78. mov eax, 1; /* return 1 from setjmp: pass 2 return */
  79. jmp edx; /* return from above setjmp call */
  80. }
  81. }
  82. #elif defined(__i386__)
  83. /* Here is our own implementation of setjmp and longjmp for I386, via
  84. GNU syntax. */
  85. #if defined(IS_LINUX)
  86. /* On Linux, the leading underscores are not implicitly added for C
  87. function names. */
  88. #define cs_setjmp _cs_setjmp
  89. #define cs_longjmp _cs_longjmp
  90. #endif
  91. int cs_setjmp(cs_jmp_buf env);
  92. void cs_longjmp(cs_jmp_buf env);
  93. __asm__
  94. ("_cs_setjmp:\n"
  95. "popl %edx\n"
  96. "popl %eax\n"
  97. "pushl %eax\n"
  98. "movl %ebx, 0(%eax)\n"
  99. "movl %edi, 4(%eax)\n"
  100. "movl %esi, 8(%eax)\n"
  101. "movl %ebp, 12(%eax)\n"
  102. "movl %esp, 16(%eax)\n"
  103. "movl %edx, 20(%eax)\n"
  104. "fnsave 24(%eax)\n"
  105. "xorl %eax, %eax\n"
  106. "jmp *%edx\n");
  107. __asm__
  108. ("_cs_longjmp:\n"
  109. "popl %edx\n"
  110. "popl %eax\n"
  111. "movl 0(%eax), %ebx\n"
  112. "movl 4(%eax), %edi\n"
  113. "movl 8(%eax), %esi\n"
  114. "movl 12(%eax), %ebp\n"
  115. "movl 16(%eax), %esp\n"
  116. "movl 20(%eax), %edx\n"
  117. "frstor 24(%eax)\n"
  118. "mov $1,%eax\n"
  119. "jmp *%edx\n");
  120. #endif /* __i386__ */
  121. /* Ideally, including setjmp.h would have defined JB_SP, which will
  122. tell us where in the context structure we can muck with the stack
  123. pointer. If it didn't define this symbol, we have to guess it. */
  124. #ifndef CS_JB_SP
  125. #if defined(IS_OSX) && defined(__i386__)
  126. /* We have determined this value empirically, via test_setjmp.cxx in
  127. this directory. */
  128. #define CS_JB_SP 9
  129. #endif
  130. #endif /* CS_JB_SP */
  131. static struct ThreadContext *st_context;
  132. static unsigned char *st_stack;
  133. static size_t st_stack_size;
  134. static ThreadFunction *st_thread_func;
  135. static void *st_data;
  136. static cs_jmp_buf orig_stack;
  137. /* We can't declare this function static--gcc might want to inline it
  138. in that case, and then the code crashes. I hope this doesn't mean
  139. that the stack is still not getting restored correctly in the above
  140. assembly code. */
  141. void
  142. setup_context_2(void) {
  143. /* Here we are running on the new stack. Copy the key data onto our
  144. new stack. */
  145. ThreadFunction *volatile thread_func = st_thread_func;
  146. void *volatile data = st_data;
  147. if (cs_setjmp(st_context->_jmp_context) == 0) {
  148. /* The _jmp_context is set up and ready to run. Now restore the
  149. original stack and return. We can't simply return from this
  150. function, since it might overwrite some of the stack data on
  151. the way out. */
  152. cs_longjmp(orig_stack);
  153. /* Shouldn't get here. */
  154. abort();
  155. }
  156. /* We come here the first time the thread starts. */
  157. (*thread_func)(data);
  158. /* We shouldn't get here, since we don't expect the thread_func to
  159. return. */
  160. abort();
  161. }
  162. static void
  163. setup_context_1(void) {
  164. /* Save the current stack frame so we can return to it (at the end
  165. of setup_context_2()). */
  166. if (cs_setjmp(orig_stack) == 0) {
  167. /* First, switch to the new stack. Save the current context using
  168. setjmp(). This saves out all of the processor register values,
  169. though it doesn't muck with the stack. */
  170. static cs_jmp_buf temp;
  171. if (cs_setjmp(temp) == 0) {
  172. /* This is the initial return from setjmp. Still the original
  173. stack. */
  174. /* Now we overwrite the stack pointer value in the saved
  175. register context. This doesn't work with all implementations
  176. of setjmp/longjmp. */
  177. /* We give ourselves a small buffer of unused space at the top
  178. of the stack, to allow for the stack frame and such that this
  179. code might be assuming is there. */
  180. (*(void **)&temp[CS_JB_SP]) = (st_stack + st_stack_size - 0x100);
  181. /* And finally, we place ourselves on the new stack by using
  182. longjmp() to reload the modified context. */
  183. cs_longjmp(temp);
  184. /* Shouldn't get here. */
  185. abort();
  186. }
  187. /* This is the second return from setjmp. Now we're on the new
  188. stack. */
  189. setup_context_2();
  190. /* Shouldn't get here. */
  191. abort();
  192. }
  193. /* By now we are back to the original stack. */
  194. }
  195. void
  196. init_thread_context(struct ThreadContext *context,
  197. unsigned char *stack, size_t stack_size,
  198. ThreadFunction *thread_func, void *data) {
  199. /* Copy all of the input parameters to static variables, then begin
  200. the stack-switching process. */
  201. st_context = context;
  202. st_stack = stack;
  203. st_stack_size = stack_size;
  204. st_thread_func = thread_func;
  205. st_data = data;
  206. setup_context_1();
  207. }
  208. void
  209. save_thread_context(struct ThreadContext *context,
  210. ContextFunction *next_context, void *data) {
  211. if (cs_setjmp(context->_jmp_context) != 0) {
  212. /* We have just returned from longjmp. In this case, return from
  213. the function. The stack is still good. */
  214. return;
  215. }
  216. /* We are still in the calling thread. In this case, we cannot
  217. return from the function without damaging the stack. Insted,
  218. call next_context() and trust the caller to call
  219. switch_to_thread_context() in there somewhere. */
  220. (*next_context)(context, data);
  221. /* We shouldn't get here. */
  222. abort();
  223. }
  224. void
  225. switch_to_thread_context(struct ThreadContext *from_context,
  226. struct ThreadContext *to_context) {
  227. cs_longjmp(to_context->_jmp_context);
  228. /* Shouldn't get here. */
  229. abort();
  230. }
  231. struct ThreadContext *
  232. alloc_thread_context() {
  233. struct ThreadContext *context =
  234. (struct ThreadContext *)malloc(sizeof(struct ThreadContext));
  235. memset(context, 0, sizeof(struct ThreadContext));
  236. return context;
  237. }
  238. void
  239. free_thread_context(struct ThreadContext *context) {
  240. free(context);
  241. }