xref: /reactos/sdk/lib/pseh/i386/pseh3.c (revision 53f498c9)
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