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