Clean up listify-rest-args VOP on x86-64.
[sbcl.git] / tests / win32-stack-unwind.c
1 /* Compiled and loaded by win32-foreign-stack-unwind.impure.lisp
2  *
3  * establish_return_frame(callback_ptr) establishes an SEH frame
4  * that will cause an unwind to itself followed by a return on
5  * any exception, and then calls the callback_ptr.
6  *
7  * perform_test_unwind() does an unwind to the SEH frame
8  * established by establish_return_frame().
9  *
10  * The name of the game for the tests is to establish a callback
11  * that establishes something with a dynamic contour and
12  * possibly a control transfer semantic (such as a binding or an
13  * unwind-protect) and then call perform_test_unwind() or cause
14  * an exception that should be handled by SBCL and see what
15  * breaks.
16  */
17
18 /* This software is part of the SBCL system. See the README file for
19  * more information.
20  *
21  * While most of SBCL is derived from the CMU CL system, the test
22  * files (like this one) were written from scratch after the fork
23  * from CMU CL.
24  *
25  * This software is in the public domain and is provided with
26  * absolutely no warranty. See the COPYING and CREDITS files for
27  * more information.
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 #define WIN32_LEAN_AND_MEAN
34 #include <windows.h>
35 #include <excpt.h>
36
37 #include <setjmp.h>
38
39 /* The "public" API */
40
41 typedef void (*callback_ptr)(void);
42
43 void establish_return_frame(callback_ptr callback);
44 void perform_test_unwind(void);
45
46
47 /* The implementation */
48
49 static
50 void **saved_exception_frame;
51
52 static
53 DWORD saved_ebp;
54
55 static void *get_seh_frame(void)
56 {
57     void* retval;
58     asm volatile ("movl %%fs:0,%0": "=r" (retval));
59     return retval;
60 }
61
62 static void set_seh_frame(void *frame)
63 {
64     asm volatile ("movl %0,%%fs:0": : "r" (frame));
65 }
66
67
68 static
69 EXCEPTION_DISPOSITION handle_exception(EXCEPTION_RECORD *exception_record,
70                                        void **exception_frame,
71                                        CONTEXT *context,
72                                        void *dc)
73 {
74     /* If an exception occurs and is passed to us to handle, just
75      * unwind.  One or more test cases check for SBCL handling
76      * breakpoint exceptions properly.  This makes sure that it
77      * doesn't unless a new exception frame is estabished when a
78      * callback occurs. */
79     if (!(exception_record->ExceptionFlags
80           & (EH_UNWINDING | EH_EXIT_UNWIND))) {
81         perform_test_unwind();
82     }
83
84     return ExceptionContinueSearch;
85 }
86
87 static void
88   __attribute__((returns_twice))
89   invoke_callback(callback_ptr callback, DWORD *unwind_token);
90
91 asm("_invoke_callback:"
92     "pushl %ebp; movl %esp, %ebp;"
93     "movl 12(%ebp), %eax;"
94     "movl %ebp, (%eax);"
95     "call *8(%ebp);"
96     "movl %ebp, %esp; popl %ebp; ret");
97
98 static void do_unwind(void *target_frame, DWORD unwind_token);
99 asm("_do_unwind:"
100     "pushl $target; pushl %ebp; movl %esp, %ebp;"
101     "pushl $0xcafe; pushl $0; pushl $-1; pushl 12(%ebp); call _RtlUnwind@16;"
102     "target:"
103     "movl 16(%ebp), %esp; popl %ebp; ret");
104
105
106 void establish_return_frame(callback_ptr callback)
107 {
108     void *exception_frame[2];
109
110     saved_exception_frame = exception_frame;
111     exception_frame[0] = get_seh_frame();
112     exception_frame[1] = handle_exception;
113     set_seh_frame(exception_frame);
114
115     /* Do a setjmp just to explicitly spill callee-saved registers, i.e.
116      * the portable equivalent of:
117      *   asm("" : : : "%esi", "%edi", "%ebx");
118      * which is needed because otherwise those registers would be trashed
119      * following the stack unwind, and leave us with a likely crash upon
120      * return to call_into_c.
121      *
122      * The returns_twice attribute on invoke_callback should take care
123      * of this already, but does not seem to take effect, at least with
124      * the version of gcc I am using.
125      *
126      * Note: __builtin_setjmp, not setjmp, because only the former has
127      * the desired effect on the immediate call site. */
128     jmp_buf env;
129     __builtin_setjmp(env);
130
131     invoke_callback(callback, &saved_ebp);
132
133     if (exception_frame != get_seh_frame()) {
134         /* It is never good for this to happen. */
135         printf("exception frame mismatch on callback return.\n");
136     }
137
138     set_seh_frame(exception_frame[0]);
139 }
140
141 void perform_test_unwind(void)
142 {
143     do_unwind(saved_exception_frame, saved_ebp);
144 }
145
146 /* EOF */