Do not trash registers in tests/win32-stack-unwind.c
authorDavid Lichteblau <david@lichteblau.com>
Fri, 5 Oct 2012 17:56:45 +0000 (19:56 +0200)
committerDavid Lichteblau <david@lichteblau.com>
Fri, 5 Oct 2012 17:58:44 +0000 (19:58 +0200)
tests/win32-stack-unwind.c

index 4ed4aa8..fd93aa2 100755 (executable)
@@ -34,6 +34,7 @@
 #include <windows.h>
 #include <excpt.h>
 
+#include <setjmp.h>
 
 /* The "public" API */
 
@@ -83,7 +84,9 @@ EXCEPTION_DISPOSITION handle_exception(EXCEPTION_RECORD *exception_record,
     return ExceptionContinueSearch;
 }
 
-static void invoke_callback(callback_ptr callback, DWORD *unwind_token);
+static void
+  __attribute__((returns_twice))
+  invoke_callback(callback_ptr callback, DWORD *unwind_token);
 
 asm("_invoke_callback:"
     "pushl %ebp; movl %esp, %ebp;"
@@ -109,6 +112,22 @@ void establish_return_frame(callback_ptr callback)
     exception_frame[1] = handle_exception;
     set_seh_frame(exception_frame);
 
+    /* Do a setjmp just to explicitly spill callee-saved registers, i.e.
+     * the portable equivalent of:
+     *   asm("" : : : "%esi", "%edi", "%ebx");
+     * which is needed because otherwise those registers would be trashed
+     * following the stack unwind, and leave us with a likely crash upon
+     * return to call_into_c.
+     *
+     * The returns_twice attribute on invoke_callback should take care
+     * of this already, but does not seem to take effect, at least with
+     * the version of gcc I am using.
+     *
+     * Note: __builtin_setjmp, not setjmp, because only the former has
+     * the desired effect on the immediate call site. */
+    jmp_buf env;
+    __builtin_setjmp(env);
+
     invoke_callback(callback, &saved_ebp);
 
     if (exception_frame != get_seh_frame()) {