Sync to trunk head (r42241)
[reactos.git] / reactos / lib / pseh / i386 / framebased-gcchack.c
1 /*
2 Copyright (c) 2008 KJK::Hyperion
3
4 Permission is hereby granted, free of charge, to any person obtaining a
5 copy of this software and associated documentation files (the "Software"),
6 to deal in the Software without restriction, including without limitation
7 the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 and/or sell copies of the Software, and to permit persons to whom the
9 Software is furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 DEALINGS IN THE SOFTWARE.
21 */
22
23 #define _NTSYSTEM_ /* removes dllimport attribute from RtlUnwind */
24
25 #define STRICT
26 #define WIN32_LEAN_AND_MEAN
27 #include <windows.h>
28
29 #include <pseh/pseh2.h>
30 #include <excpt.h>
31 #include <intrin.h>
32
33 #ifndef EXCEPTION_EXIT_UNWIND
34 #define EXCEPTION_EXIT_UNWIND 4
35 #endif
36
37 #ifndef EXCEPTION_UNWINDING
38 #define EXCEPTION_UNWINDING 2
39 #endif
40
41 extern DECLSPEC_NORETURN int __SEH2Handle(void *, void *, void *, void *, void *, void *);
42 extern int __cdecl __SEH2FrameHandler(struct _EXCEPTION_RECORD *, void *, struct _CONTEXT *, void *);
43 extern int __cdecl __SEH2UnwindHandler(struct _EXCEPTION_RECORD *, void *, struct _CONTEXT *, void *);
44
45 FORCEINLINE
46 _SEH2Registration_t * __cdecl _SEH2CurrentRegistration(void)
47 {
48 return (_SEH2Registration_t *)__readfsdword(0);
49 }
50
51 FORCEINLINE
52 void __cdecl __SEH2EnterFrame(_SEH2Registration_t * frame)
53 {
54 frame->SER_Prev = _SEH2CurrentRegistration();
55 __writefsdword(0, (unsigned long)frame);
56 }
57
58 FORCEINLINE
59 void __cdecl __SEH2LeaveFrame(void)
60 {
61 __writefsdword(0, (unsigned long)_SEH2CurrentRegistration()->SER_Prev);
62 }
63
64 FORCEINLINE
65 void _SEH2GlobalUnwind(void * target)
66 {
67 __asm__ __volatile__
68 (
69 "push %%ebp\n"
70 "push $0\n"
71 "push $0\n"
72 "push $Return%=\n"
73 "push %[target]\n"
74 "call %c[RtlUnwind]\n"
75 "Return%=: pop %%ebp\n" :
76 :
77 [target] "g" (target), [RtlUnwind] "g" (&RtlUnwind) :
78 "eax", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"
79 );
80 }
81
82 static
83 __SEH_EXCEPT_RET _SEH2Except(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel, struct _EXCEPTION_POINTERS * ep)
84 {
85 void * filter = trylevel->ST_Filter;
86 void * context = NULL;
87 __SEH_EXCEPT_RET ret;
88
89 if(filter == (void *)0)
90 return 0;
91
92 if(filter == (void *)1)
93 return 1;
94
95 if(filter == (void *)-1)
96 return -1;
97
98 if(_SEHIsTrampoline((_SEHTrampoline_t *)filter))
99 {
100 context = _SEHClosureFromTrampoline((_SEHTrampoline_t *)filter);
101 filter = _SEHFunctionFromTrampoline((_SEHTrampoline_t *)filter);
102 }
103
104 __asm__ __volatile__
105 (
106 "push %[ep]\n"
107 "push %[frame]\n"
108 "call *%[filter]\n"
109 "pop %%edx\n"
110 "pop %%edx\n" :
111 [ret] "=a" (ret) :
112 "c" (context), [filter] "r" (filter), [frame] "g" (frame), [ep] "g" (ep) :
113 "edx", "flags", "memory"
114 );
115
116 return ret;
117 }
118
119 static
120 void _SEH2Finally(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel)
121 {
122 if(trylevel->ST_Filter == NULL && trylevel->ST_Body != NULL)
123 {
124 void * body = trylevel->ST_Body;
125 void * context = NULL;
126
127 if(_SEHIsTrampoline((_SEHTrampoline_t *)body))
128 {
129 context = _SEHClosureFromTrampoline((_SEHTrampoline_t *)body);
130 body = _SEHFunctionFromTrampoline((_SEHTrampoline_t *)body);
131 }
132
133 __asm__ __volatile__("call *%1\n" : : "c" (context), "r" (body) : "eax", "edx", "flags", "memory");
134 }
135 }
136
137 typedef struct __SEH2UnwindFrame
138 {
139 _SEH2Registration_t SUF_Registration;
140 _SEH2Frame_t * SUF_Frame;
141 volatile _SEH2TryLevel_t * SUF_TargetTryLevel;
142 }
143 _SEH2UnwindFrame_t;
144
145 static void _SEH2LocalUnwind(_SEH2Frame_t *, volatile _SEH2TryLevel_t *);
146
147 extern
148 int __cdecl _SEH2UnwindHandler
149 (
150 struct _EXCEPTION_RECORD * ExceptionRecord,
151 void * EstablisherFrame,
152 struct _CONTEXT * ContextRecord,
153 void * DispatcherContext
154 )
155 {
156 if(ExceptionRecord->ExceptionFlags & (EXCEPTION_EXIT_UNWIND | EXCEPTION_UNWINDING))
157 {
158 _SEH2UnwindFrame_t * unwindframe = CONTAINING_RECORD(EstablisherFrame, _SEH2UnwindFrame_t, SUF_Registration);
159 _SEH2LocalUnwind(unwindframe->SUF_Frame, unwindframe->SUF_TargetTryLevel);
160 *((void **)DispatcherContext) = EstablisherFrame;
161 return ExceptionCollidedUnwind;
162 }
163
164 return ExceptionContinueSearch;
165 }
166
167 static
168 void _SEH2LocalUnwind(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * dsttrylevel)
169 {
170 volatile _SEH2TryLevel_t * trylevel;
171 _SEH2UnwindFrame_t unwindframe;
172
173 unwindframe.SUF_Frame = frame;
174 unwindframe.SUF_TargetTryLevel = dsttrylevel;
175
176 unwindframe.SUF_Registration.SER_Handler = &__SEH2UnwindHandler;
177 __SEH2EnterFrame(&unwindframe.SUF_Registration);
178
179 for(trylevel = frame->SF_TopTryLevel; trylevel && trylevel != dsttrylevel; trylevel = trylevel->ST_Next)
180 {
181 frame->SF_TopTryLevel = trylevel->ST_Next;
182 _SEH2Finally(frame, trylevel);
183 }
184
185 __SEH2LeaveFrame();
186 }
187
188 static DECLSPEC_NORETURN
189 void _SEH2Handle(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel)
190 {
191 volatile _SEH2HandleTryLevel_t * fulltrylevel = CONTAINING_RECORD(trylevel, _SEH2HandleTryLevel_t, SHT_Common);
192
193 _SEH2GlobalUnwind(frame);
194 _SEH2LocalUnwind(frame, &fulltrylevel->SHT_Common);
195 frame->SF_TopTryLevel = fulltrylevel->SHT_Common.ST_Next;
196
197 __SEH2Handle
198 (
199 fulltrylevel->SHT_Common.ST_Body,
200 fulltrylevel->SHT_Esp,
201 fulltrylevel->SHT_Ebp,
202 fulltrylevel->SHT_Ebx,
203 fulltrylevel->SHT_Esi,
204 fulltrylevel->SHT_Edi
205 );
206 }
207
208 extern
209 int __cdecl _SEH2FrameHandler
210 (
211 struct _EXCEPTION_RECORD * ExceptionRecord,
212 void * EstablisherFrame,
213 struct _CONTEXT * ContextRecord,
214 void * DispatcherContext
215 )
216 {
217 _SEH2Frame_t * frame;
218
219 frame = EstablisherFrame;
220
221 /* Unwinding */
222 if(ExceptionRecord->ExceptionFlags & (EXCEPTION_EXIT_UNWIND | EXCEPTION_UNWINDING))
223 {
224 _SEH2LocalUnwind(frame, NULL);
225 }
226 /* Handling */
227 else
228 {
229 int ret = 0;
230 volatile _SEH2TryLevel_t * trylevel;
231 EXCEPTION_POINTERS ep;
232
233 ep.ExceptionRecord = ExceptionRecord;
234 ep.ContextRecord = ContextRecord;
235
236 frame->SF_Code = ExceptionRecord->ExceptionCode;
237
238 for(trylevel = frame->SF_TopTryLevel; trylevel != NULL; trylevel = trylevel->ST_Next)
239 {
240 ret = _SEH2Except(frame, trylevel, &ep);
241
242 if(ret < 0)
243 return ExceptionContinueExecution;
244 else if(ret > 0)
245 _SEH2Handle(frame, trylevel);
246 }
247 }
248
249 return ExceptionContinueSearch;
250 }
251
252 extern
253 void __cdecl _SEH2EnterFrame(_SEH2Frame_t * frame)
254 {
255 frame->SF_Registration.SER_Handler = __SEH2FrameHandler;
256 frame->SF_Code = 0;
257 __SEH2EnterFrame(&frame->SF_Registration);
258 }
259
260 extern
261 int __cdecl _SEH2EnterFrameAndTrylevel(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel)
262 {
263 frame->SF_TopTryLevel = trylevel;
264 _SEH2EnterFrame(frame);
265 return 0;
266 }
267
268 extern
269 void __cdecl _SEH2LeaveFrame(void)
270 {
271 __SEH2LeaveFrame();
272 }
273
274 extern
275 void __cdecl _SEH2Return(void)
276 {
277 _SEH2LocalUnwind(CONTAINING_RECORD(_SEH2CurrentRegistration(), _SEH2Frame_t, SF_Registration), NULL);
278 _SEH2LeaveFrame();
279 }
280
281 /* EOF */