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