Merge freeldr from amd64 branch:
[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
196 __SEH2Handle
197 (
198 fulltrylevel->SHT_Common.ST_Body,
199 fulltrylevel->SHT_Esp,
200 fulltrylevel->SHT_Ebp,
201 fulltrylevel->SHT_Ebx,
202 fulltrylevel->SHT_Esi,
203 fulltrylevel->SHT_Edi
204 );
205 }
206
207 extern
208 int __cdecl _SEH2FrameHandler
209 (
210 struct _EXCEPTION_RECORD * ExceptionRecord,
211 void * EstablisherFrame,
212 struct _CONTEXT * ContextRecord,
213 void * DispatcherContext
214 )
215 {
216 _SEH2Frame_t * frame;
217
218 frame = EstablisherFrame;
219
220 /* Unwinding */
221 if(ExceptionRecord->ExceptionFlags & (EXCEPTION_EXIT_UNWIND | EXCEPTION_UNWINDING))
222 {
223 _SEH2LocalUnwind(frame, NULL);
224 }
225 /* Handling */
226 else
227 {
228 int ret = 0;
229 volatile _SEH2TryLevel_t * trylevel;
230 EXCEPTION_POINTERS ep;
231
232 ep.ExceptionRecord = ExceptionRecord;
233 ep.ContextRecord = ContextRecord;
234
235 frame->SF_Code = ExceptionRecord->ExceptionCode;
236
237 for(trylevel = frame->SF_TopTryLevel; trylevel != NULL; trylevel = trylevel->ST_Next)
238 {
239 ret = _SEH2Except(frame, trylevel, &ep);
240
241 if(ret < 0)
242 return ExceptionContinueExecution;
243 else if(ret > 0)
244 _SEH2Handle(frame, trylevel);
245 }
246 }
247
248 return ExceptionContinueSearch;
249 }
250
251 extern
252 void __cdecl _SEH2EnterFrame(_SEH2Frame_t * frame)
253 {
254 frame->SF_Registration.SER_Handler = __SEH2FrameHandler;
255 frame->SF_Code = 0;
256 __SEH2EnterFrame(&frame->SF_Registration);
257 }
258
259 extern
260 int __cdecl _SEH2EnterFrameAndTrylevel(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel)
261 {
262 frame->SF_TopTryLevel = trylevel;
263 _SEH2EnterFrame(frame);
264 return 0;
265 }
266
267 extern
268 void __cdecl _SEH2LeaveFrame(void)
269 {
270 __SEH2LeaveFrame();
271 }
272
273 extern
274 void __cdecl _SEH2Return(void)
275 {
276 _SEH2LocalUnwind(CONTAINING_RECORD(_SEH2CurrentRegistration(), _SEH2Frame_t, SF_Registration), NULL);
277 _SEH2LeaveFrame();
278 }
279
280 /* EOF */