[PSEH3]
[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 static inline
54 void _SEH3$_Unregister(
55 volatile SEH3$_REGISTRATION_FRAME *Frame)
56 {
57 if (Frame->Handler)
58 _SEH3$_UnregisterFrame(Frame);
59 else
60 _SEH3$_UnregisterTryLevel(Frame);
61 }
62
63 static inline
64 LONG
65 _SEH3$_InvokeFilter(
66 PVOID Record,
67 PVOID Filter)
68 {
69 LONG FilterResult;
70
71 asm volatile (
72 /* First call with param = 0 to get the frame layout */
73 "xorl %%ecx, %%ecx\n\t"
74 "xorl %%eax, %%eax\n\t"
75 "call *%[Filter]\n\t"
76
77 /* The result is the frame base address that we passed in (0) plus the
78 offset to the registration record. */
79 "negl %%eax\n\t"
80 "addl %[Record], %%eax\n\t"
81
82 /* Second call to get the filter result */
83 "mov $1, %%ecx\n\t"
84 "call *%[Filter]\n\t"
85 : "=a"(FilterResult)
86 : [Record] "m" (Record), [Filter] "m" (Filter)
87 : "ecx", "edx");
88
89 return FilterResult;
90 }
91
92 static inline
93 LONG
94 _SEH3$_GetFilterResult(
95 PSEH3$_REGISTRATION_FRAME Record)
96 {
97 PVOID Filter = Record->ScopeTable->Filter;
98 LONG Result;
99
100 /* Check for __finally frames */
101 if (Record->ScopeTable->Target == NULL)
102 {
103 return EXCEPTION_CONTINUE_SEARCH;
104 }
105
106 /* Check if we have a constant filter */
107 if (((ULONG)Filter & 0xFFFFFF00) == 0)
108 {
109 /* Lowest 8 bit are sign extended to give the result */
110 Result = (LONG)(CHAR)(ULONG)Filter;
111 }
112 else
113 {
114 /* Call the filter function */
115 Result = _SEH3$_InvokeFilter(Record, Filter);
116 }
117
118 /* Normalize the result */
119 if (Result < 0) return EXCEPTION_CONTINUE_EXECUTION;
120 else if (Result > 0) return EXCEPTION_EXECUTE_HANDLER;
121 else return EXCEPTION_CONTINUE_SEARCH;
122 }
123
124 static inline
125 VOID
126 _SEH3$_CallFinally(
127 PSEH3$_REGISTRATION_FRAME Record)
128 {
129 _SEH3$_InvokeFilter(Record, Record->ScopeTable->Filter);
130 }
131
132 __attribute__((noreturn))
133 static inline
134 void
135 _SEH3$_JumpToTarget(
136 PSEH3$_REGISTRATION_FRAME RegistrationFrame)
137 {
138 asm volatile (
139 /* Load the registers */
140 "movl 20(%%ecx), %%esp\n"
141 "movl 24(%%ecx), %%ebp\n"
142
143 /* Stack pointer is 4 off from the call to __SEH3$_RegisterFrame */
144 "addl $4, %%esp\n"
145
146 /* Jump into the exception handler */
147 "jmp *%[Target]\n"
148 : :
149 "c" (RegistrationFrame),
150 "a" (RegistrationFrame->ScopeTable),
151 [Target] "m" (RegistrationFrame->ScopeTable->Target)
152 );
153
154 __builtin_unreachable();
155 }
156
157 static inline
158 void
159 _SEH3$_CallRtlUnwind(
160 PSEH3$_REGISTRATION_FRAME RegistrationFrame)
161 {
162 LONG ClobberedEax;
163
164 asm volatile(
165 "push %%ebp\n"
166 "push $0\n"
167 "push $0\n"
168 "push $0\n"
169 "push %[TargetFrame]\n"
170 "call _RtlUnwind@16\n"
171 "pop %%ebp\n"
172 : "=a" (ClobberedEax)
173 : [TargetFrame] "a" (RegistrationFrame)
174 : "ebx", "ecx", "edx", "esi",
175 "edi", "flags", "memory");
176 }
177
178 EXCEPTION_DISPOSITION
179 __cdecl
180 __attribute__ ((__target__ ("cld")))
181 _SEH3$_except_handler(
182 struct _EXCEPTION_RECORD * ExceptionRecord,
183 PSEH3$_REGISTRATION_FRAME EstablisherFrame,
184 struct _CONTEXT * ContextRecord,
185 void * DispatcherContext)
186 {
187 PSEH3$_REGISTRATION_FRAME CurrentFrame, TargetFrame;
188 SEH3$_EXCEPTION_POINTERS ExceptionPointers;
189 LONG FilterResult;
190
191 /* Clear the direction flag. */
192 asm volatile ("cld\n" : : : "memory");
193
194 /* Check if this is an unwind */
195 if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)
196 {
197 /* Unwind all local frames */
198 TargetFrame = EstablisherFrame->Next;
199 }
200 else
201 {
202 /* Save the exception pointers on the stack */
203 ExceptionPointers.ExceptionRecord = ExceptionRecord;
204 ExceptionPointers.ContextRecord = ContextRecord;
205
206 /* Loop all frames for this registration */
207 CurrentFrame = EstablisherFrame->EndOfChain;
208 for (;;)
209 {
210 /* Check if we have an exception handler */
211 if (CurrentFrame->ScopeTable->Target != NULL)
212 {
213 /* Set exception pointers for this frame */
214 CurrentFrame->ExceptionPointers = &ExceptionPointers;
215
216 /* Get the filter result */
217 FilterResult = _SEH3$_GetFilterResult(CurrentFrame);
218
219 /* Check, if continuuing is requested */
220 if (FilterResult == EXCEPTION_CONTINUE_EXECUTION)
221 {
222 return ExceptionContinueExecution;
223 }
224
225 /* Check if the except handler shall be executed */
226 if (FilterResult == EXCEPTION_EXECUTE_HANDLER) break;
227 }
228
229 /* Bail out if this is the last handler */
230 if (CurrentFrame == EstablisherFrame)
231 return ExceptionContinueSearch;
232
233 /* Go to the next frame */
234 CurrentFrame = CurrentFrame->Next;
235 }
236
237 /* Call RtlUnwind to unwind the frames below this one */
238 _SEH3$_CallRtlUnwind(EstablisherFrame);
239
240 /* Do a local unwind up to this frame */
241 TargetFrame = CurrentFrame;
242 }
243
244 /* Loop frames up to the target frame */
245 for (CurrentFrame = EstablisherFrame->EndOfChain;
246 CurrentFrame != TargetFrame;
247 CurrentFrame = CurrentFrame->Next)
248 {
249 /* Manually unregister the frame */
250 _SEH3$_Unregister(CurrentFrame);
251
252 /* Check if this is an unwind frame */
253 if (CurrentFrame->ScopeTable->Target == NULL)
254 {
255 /* Set exception pointers for this frame */
256 CurrentFrame->ExceptionPointers = &ExceptionPointers;
257
258 /* Call the finally function */
259 _SEH3$_CallFinally(CurrentFrame);
260 }
261 }
262
263 /* Check if this was an unwind */
264 if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)
265 {
266 return ExceptionContinueSearch;
267 }
268
269 /* Unregister the frame. It will be unregistered again at the end of the
270 __except block, due to auto cleanup, but that doesn't hurt.
271 All we do is set either fs:[0] or EstablisherFrame->EndOfChain to
272 CurrentFrame->Next, which will not change it's value. */
273 _SEH3$_Unregister(CurrentFrame);
274
275 /* Jump to the __except block (does not return) */
276 _SEH3$_JumpToTarget(CurrentFrame);
277 }
278