[PSEH]
[reactos.git] / reactos / lib / pseh / i386 / pseh3.c
1 /*
2 * PROJECT: ReactOS system libraries
3 * LICENSE: GNU GPL - See COPYING in the top level directory
4 * PURPOSE: Support library for PSEH3
5 * PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org)
6 */
7
8 /*
9 * - Naming: To avoid naming conflicts, all internal identifiers are prefixed
10 * with _SEH3$_.
11 * - Frame graph: PSEH3 uses the same registration frame for every trylevel.
12 * Only the top trylevel is registered in FS:0, the inner trylevels are linked
13 * to the first trylevel frame. Only the first trylevel frame has the Handler
14 * member set, it's 0 for all others as an identification. The EndOfChain
15 * member of the FS:0 registered frame points to the last internal frame,
16 * which is the frame itself, when only 1 trylevel is present.
17 *
18 * The registration graph looks like this:
19 *
20 * newer handlers
21 * ---------------->
22 *
23 * fs:0 /----------------\
24 * |-----------|<-\ |-----------|<-\ / |----------|<-\ \->|----------|
25 * | <Next> | \-| <Next> | \--/--| <Next> | \---| <Next> |
26 * | <Handler> | | <Handler> | / | <NULL> | | <NULL> |
27 * |-----------| |-----------| / |----------| |----------|
28 * |EndOfChain |---/
29 * | ... |
30 * |-----------|
31 */
32
33 #include <stdarg.h>
34 #include <windef.h>
35 #include <winnt.h>
36
37 /* We need the full structure with all non-volatile */
38 #define _SEH3$_FRAME_ALL_NONVOLATILES 1
39 #include "pseh3.h"
40 #include "pseh3_asmdef.h"
41
42 /* Make sure the asm definitions match the structures */
43 C_ASSERT(SEH3_REGISTRATION_FRAME_Next == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Next));
44 C_ASSERT(SEH3_REGISTRATION_FRAME_Handler == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Handler));
45 C_ASSERT(SEH3_REGISTRATION_FRAME_EndOfChain == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, EndOfChain));
46 C_ASSERT(SEH3_REGISTRATION_FRAME_ScopeTable == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, ScopeTable));
47 C_ASSERT(SEH3_REGISTRATION_FRAME_ExceptionPointers == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, ExceptionPointers));
48 C_ASSERT(SEH3_REGISTRATION_FRAME_Esp == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Esp));
49 C_ASSERT(SEH3_REGISTRATION_FRAME_Ebp == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Ebp));
50 C_ASSERT(SEH3_SCOPE_TABLE_Filter == FIELD_OFFSET(SEH3$_SCOPE_TABLE, Filter));
51 C_ASSERT(SEH3_SCOPE_TABLE_Target == FIELD_OFFSET(SEH3$_SCOPE_TABLE, Target));
52
53 void
54 __attribute__((regparm(1)))
55 _SEH3$_Unregister(
56 volatile SEH3$_REGISTRATION_FRAME *Frame)
57 {
58 if (Frame->Handler)
59 _SEH3$_UnregisterFrame(Frame);
60 else
61 _SEH3$_UnregisterTryLevel(Frame);
62 }
63
64 static inline
65 LONG
66 _SEH3$_InvokeNestedFunctionFilter(
67 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame,
68 PVOID Filter)
69 {
70 LONG FilterResult;
71
72 asm volatile (
73 /* First call with param = 0 to get the frame layout */
74 "xorl %%ecx, %%ecx\n\t"
75 "xorl %%eax, %%eax\n\t"
76 "call *%[Filter]\n\t"
77
78 /* The result is the frame base address that we passed in (0) plus the
79 offset to the registration record. */
80 "negl %%eax\n\t"
81 "addl %[RegistrationFrame], %%eax\n\t"
82
83 /* Second call to get the filter result */
84 "mov $1, %%ecx\n\t"
85 "call *%[Filter]"
86 : "=a" (FilterResult)
87 : [RegistrationFrame] "m" (RegistrationFrame), [Filter] "m" (Filter)
88 : "ecx", "edx");
89
90 return FilterResult;
91 }
92
93 long
94 __attribute__((regparm(1)))
95 _SEH3$_InvokeEmbeddedFilter(
96 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame);
97
98 long
99 __attribute__((regparm(1)))
100 _SEH3$_InvokeEmbeddedFilterFromRegistration(
101 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame);
102
103 static inline
104 LONG
105 _SEH3$_InvokeFilter(
106 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame,
107 PVOID Filter)
108 {
109 LONG FilterResult;
110
111 if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_NESTED_HANDLER)
112 {
113 return _SEH3$_InvokeNestedFunctionFilter(RegistrationFrame, Filter);
114 }
115 else if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_CPP_HANDLER)
116 {
117 /* Call the embedded filter function */
118 return _SEH3$_InvokeEmbeddedFilter(RegistrationFrame);
119 }
120 else if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_CLANG_HANDLER)
121 {
122 return _SEH3$_InvokeEmbeddedFilterFromRegistration(RegistrationFrame);
123 }
124 else
125 {
126 /* Should not happen! Skip this handler */
127 FilterResult = EXCEPTION_CONTINUE_SEARCH;
128 }
129
130 return FilterResult;
131 }
132
133 void
134 __attribute__((regparm(1)))
135 _SEH3$_AutoCleanup(
136 volatile SEH3$_REGISTRATION_FRAME *Frame)
137 {
138 /* Check for __finally frames */
139 if (Frame->ScopeTable->Target == NULL)
140 {
141 _SEH3$_InvokeFilter(Frame, Frame->ScopeTable->Filter);
142 }
143
144 if (Frame->Handler)
145 _SEH3$_UnregisterFrame(Frame);
146 else
147 _SEH3$_UnregisterTryLevel(Frame);
148 }
149
150 static inline
151 LONG
152 _SEH3$_GetFilterResult(
153 PSEH3$_REGISTRATION_FRAME Record)
154 {
155 PVOID Filter = Record->ScopeTable->Filter;
156 LONG Result;
157
158 /* Check for __finally frames */
159 if (Record->ScopeTable->Target == NULL)
160 {
161 return EXCEPTION_CONTINUE_SEARCH;
162 }
163
164 /* Check if we have a constant filter */
165 if (((ULONG)Filter & 0xFFFFFF00) == 0)
166 {
167 /* Lowest 8 bit are sign extended to give the result */
168 Result = (LONG)(CHAR)(ULONG)Filter;
169 }
170 else
171 {
172 /* Call the filter function */
173 Result = _SEH3$_InvokeFilter(Record, Filter);
174 }
175
176 /* Normalize the result */
177 if (Result < 0) return EXCEPTION_CONTINUE_EXECUTION;
178 else if (Result > 0) return EXCEPTION_EXECUTE_HANDLER;
179 else return EXCEPTION_CONTINUE_SEARCH;
180 }
181
182 static inline
183 VOID
184 _SEH3$_CallFinally(
185 PSEH3$_REGISTRATION_FRAME Record)
186 {
187 _SEH3$_InvokeFilter(Record, Record->ScopeTable->Filter);
188 }
189
190 __attribute__((noreturn))
191 static inline
192 void
193 _SEH3$_JumpToTarget(
194 PSEH3$_REGISTRATION_FRAME RegistrationFrame)
195 {
196 if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_CLANG_HANDLER)
197 {
198 asm volatile (
199 /* Load the registers */
200 "movl 24(%%ecx), %%esp\n\t"
201 "movl 28(%%ecx), %%ebp\n\t"
202
203 /* Stack pointer is 4 off from the call to __SEH3$_RegisterFrame */
204 "addl $4, %%esp\n\t"
205
206 /* Jump into the exception handler */
207 "jmp *%[Target]"
208 : :
209 "c" (RegistrationFrame),
210 "a" (RegistrationFrame->ScopeTable),
211 [Target] "m" (RegistrationFrame->ScopeTable->Target)
212 );
213 }
214 else
215 {
216 asm volatile (
217 /* Load the registers */
218 "movl 24(%%ecx), %%esp\n\t"
219 "movl 28(%%ecx), %%ebp\n\t"
220
221 /* Stack pointer is 4 off from the call to __SEH3$_RegisterFrame */
222 "addl $4, %%esp\n\t"
223
224 /* Jump into the exception handler */
225 "jmp *%[Target]"
226 : :
227 "c" (RegistrationFrame),
228 "a" (RegistrationFrame->ScopeTable),
229 [Target] "m" (RegistrationFrame->ScopeTable->Target)
230 );
231 }
232
233 __builtin_unreachable();
234 }
235
236 void
237 __fastcall
238 _SEH3$_CallRtlUnwind(
239 PSEH3$_REGISTRATION_FRAME RegistrationFrame);
240
241
242 EXCEPTION_DISPOSITION
243 __cdecl
244 #ifndef __clang__
245 __attribute__ ((__target__ ("cld")))
246 #endif
247 _SEH3$_except_handler(
248 struct _EXCEPTION_RECORD * ExceptionRecord,
249 PSEH3$_REGISTRATION_FRAME EstablisherFrame,
250 struct _CONTEXT * ContextRecord,
251 void * DispatcherContext)
252 {
253 PSEH3$_REGISTRATION_FRAME CurrentFrame, TargetFrame;
254 SEH3$_EXCEPTION_POINTERS ExceptionPointers;
255 LONG FilterResult;
256
257 /* Clear the direction flag. */
258 asm volatile ("cld" : : : "memory");
259
260 /* Save the exception pointers on the stack */
261 ExceptionPointers.ExceptionRecord = ExceptionRecord;
262 ExceptionPointers.ContextRecord = ContextRecord;
263
264 /* Check if this is an unwind */
265 if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)
266 {
267 /* Unwind all local frames */
268 TargetFrame = EstablisherFrame->Next;
269 }
270 else
271 {
272 /* Loop all frames for this registration */
273 CurrentFrame = EstablisherFrame->EndOfChain;
274 for (;;)
275 {
276 /* Check if we have an exception handler */
277 if (CurrentFrame->ScopeTable->Target != NULL)
278 {
279 /* Set exception pointers and code for this frame */
280 CurrentFrame->ExceptionPointers = &ExceptionPointers;
281 CurrentFrame->ExceptionCode = ExceptionRecord->ExceptionCode;
282
283 /* Get the filter result */
284 FilterResult = _SEH3$_GetFilterResult(CurrentFrame);
285
286 /* Check, if continuuing is requested */
287 if (FilterResult == EXCEPTION_CONTINUE_EXECUTION)
288 {
289 return ExceptionContinueExecution;
290 }
291
292 /* Check if the except handler shall be executed */
293 if (FilterResult == EXCEPTION_EXECUTE_HANDLER) break;
294 }
295
296 /* Bail out if this is the last handler */
297 if (CurrentFrame == EstablisherFrame)
298 return ExceptionContinueSearch;
299
300 /* Go to the next frame */
301 CurrentFrame = CurrentFrame->Next;
302 }
303
304 /* Call RtlUnwind to unwind the frames below this one */
305 _SEH3$_CallRtlUnwind(EstablisherFrame);
306
307 /* Do a local unwind up to this frame */
308 TargetFrame = CurrentFrame;
309 }
310
311 /* Loop frames up to the target frame */
312 for (CurrentFrame = EstablisherFrame->EndOfChain;
313 CurrentFrame != TargetFrame;
314 CurrentFrame = CurrentFrame->Next)
315 {
316 /* Manually unregister the frame */
317 _SEH3$_Unregister(CurrentFrame);
318
319 /* Check if this is an unwind frame */
320 if (CurrentFrame->ScopeTable->Target == NULL)
321 {
322 /* Set exception pointers and code for this frame */
323 CurrentFrame->ExceptionPointers = &ExceptionPointers;
324 CurrentFrame->ExceptionCode = ExceptionRecord->ExceptionCode;
325
326 /* Call the finally function */
327 _SEH3$_CallFinally(CurrentFrame);
328 }
329 }
330
331 /* Check if this was an unwind */
332 if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)
333 {
334 return ExceptionContinueSearch;
335 }
336
337 /* Unregister the frame. It will be unregistered again at the end of the
338 __except block, due to auto cleanup, but that doesn't hurt.
339 All we do is set either fs:[0] or EstablisherFrame->EndOfChain to
340 CurrentFrame->Next, which will not change it's value. */
341 _SEH3$_Unregister(CurrentFrame);
342
343 /* Jump to the __except block (does not return) */
344 _SEH3$_JumpToTarget(CurrentFrame);
345 }
346