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