[PSEH]
[reactos.git] / reactos / include / reactos / libs / pseh / pseh3.h
1 /*
2 * PROJECT: ReactOS system libraries
3 * LICENSE: GNU GPL - See COPYING in the top level directory
4 * PURPOSE: Header for PSEH3
5 * PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org)
6 */
7
8 /* For additional information see pseh3.c in the related library. */
9
10 #pragma once
11 #define _PSEH3_H_
12
13 #include "excpt.h"
14
15 typedef struct _SEH3$_SCOPE_TABLE
16 {
17 void *Target;
18 void *Filter;
19 } SEH3$_SCOPE_TABLE, *PSEH3$_SCOPE_TABLE;
20
21 typedef struct _SEH3$_EXCEPTION_POINTERS
22 {
23 struct _EXCEPTION_RECORD *ExceptionRecord;
24 struct _CONTEXT *ContextRecord;
25 } SEH3$_EXCEPTION_POINTERS, *PSEH3$_EXCEPTION_POINTERS;
26
27 typedef struct _SEH3$_REGISTRATION_FRAME
28 {
29 /* First the Windows base record. Don't move this! */
30 struct _SEH3$_REGISTRATION_FRAME *Next;
31 void *Handler;
32
33 /* Points to the end of the internal registration chain */
34 struct _SEH3$_REGISTRATION_FRAME *EndOfChain;
35
36 /* Pointer to the static scope table */
37 PSEH3$_SCOPE_TABLE ScopeTable;
38
39 /* Except handler stores pointer to exception pointers here */
40 PSEH3$_EXCEPTION_POINTERS volatile ExceptionPointers;
41
42 /* Registers that we need to save */
43 unsigned long Esp;
44 unsigned long Ebp;
45
46 } SEH3$_REGISTRATION_FRAME ,*PSEH3$_REGISTRATION_FRAME;
47
48 extern inline __attribute__((always_inline,gnu_inline))
49 void _SEH3$_UnregisterFrame(volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame)
50 {
51 asm volatile ("movl %k[NewHead], %%fs:0"
52 : : [NewHead] "ir" (RegistrationFrame->Next) : "memory");
53 }
54
55 extern inline __attribute__((always_inline,gnu_inline))
56 void _SEH3$_UnregisterTryLevel(
57 volatile SEH3$_REGISTRATION_FRAME *TrylevelFrame)
58 {
59 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame;
60 asm volatile ("movl %%fs:0, %k[RegistrationFrame]"
61 : [RegistrationFrame] "=r" (RegistrationFrame) : );
62 RegistrationFrame->EndOfChain = TrylevelFrame->Next;
63 }
64
65 enum
66 {
67 _SEH3$_TryLevel = 0,
68 };
69
70 /* These are global dummy definitions, that get overwritten in the local context of __finally / __except blocks */
71 int __cdecl __attribute__((error ("Can only be used inside a __finally block."))) _abnormal_termination(void);
72 unsigned long __cdecl __attribute__((error("Can only be used inside an exception filter or __except block."))) _exception_code(void);
73 void * __cdecl __attribute__((error("Can only be used inside an exception filter."))) _exception_info(void);
74
75 /* Define the registers that get clobbered, when reaching the __except block.
76 We specify ebp on optimized builds without frame pointer, since it will be
77 used by GCC as a general purpose register then. */
78 #if defined(__OPTIMIZE__) && defined(_ALLOW_OMIT_FRAME_POINTER)
79 #define _SEH3$_CLOBBER_ON_EXCEPTION "ebp", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"
80 #else
81 #define _SEH3$_CLOBBER_ON_EXCEPTION "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"
82 #endif
83
84 /* This attribute allows automatic cleanup of the registered frames */
85 #define _SEH3$_AUTO_CLEANUP __attribute__((cleanup(_SEH3$_AutoCleanup)))
86
87 #define _SEH3$_ASM_GOTO(_Asm, _Label, ...) asm goto (_Asm : : : "memory", ## __VA_ARGS__ : _Label)
88
89 #define _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
90 inline __attribute__((always_inline, gnu_inline)) \
91 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; }
92
93 /* This is an asm wrapper around _SEH3$_RegisterFrame */
94 #define _SEH3$_RegisterFrame(_TrylevelFrame, _DataTable, _Target) \
95 asm goto ("call __SEH3$_RegisterFrame\n" \
96 : \
97 : "c" (_TrylevelFrame), "a" (_DataTable) \
98 : "edx", "memory" \
99 : _Target)
100
101 /* This is an asm wrapper around _SEH3$_EnterTryLevel */
102 #define _SEH3$_RegisterTryLevel(_TrylevelFrame, _DataTable, _Target) \
103 asm goto ("call __SEH3$_RegisterTryLevel\n" \
104 : \
105 : "c" (_TrylevelFrame), "a" (_DataTable) \
106 : "edx", "memory" \
107 : _Target)
108
109 /* On GCC the filter function is a nested function with __fastcall calling
110 convention. The eax register contains a base address the function uses
111 to address the callers stack frame. __fastcall is chosen, because it gives
112 us an effective was of passing one argument to the function, that we need
113 to tell the function in a first pass to return informtion about the frame
114 base address. Since this is something GCC chooses arbitrarily, we call
115 the function with an arbitrary base address in eax first and then use the
116 result to calculate the correct address for a second call to the function. */
117 #define _SEH3$_DECLARE_FILTER_FUNC(_Name) \
118 auto int __fastcall _Name(int Action)
119
120 #define _SEH3$_NESTED_FUNC_OPEN(_Name) \
121 int __fastcall _Name(int Action) \
122 { \
123 /* This is a fancy way to get information about the frame layout */ \
124 if (Action == 0) return (int)&_SEH3$_TrylevelFrame;
125
126 #define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
127 _SEH3$_NESTED_FUNC_OPEN(_Name) \
128 /* Declare the intrinsics for exception filters */ \
129 inline __attribute__((always_inline, gnu_inline)) \
130 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; } \
131 inline __attribute__((always_inline, gnu_inline)) \
132 void * _exception_info() { return _SEH3$_TrylevelFrame.ExceptionPointers; } \
133 \
134 /* Now handle the actual filter expression */ \
135 return (expression); \
136 }
137
138 #define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
139 _SEH3$_NESTED_FUNC_OPEN(_Name) \
140 /* Declare the intrinsics for the finally function */ \
141 inline __attribute__((always_inline, gnu_inline)) \
142 int _abnormal_termination() { return (_SEH3$_TrylevelFrame.ScopeTable != 0); }
143
144 #define _SEH3$_FILTER(_Filter, _FilterExpression) \
145 (__builtin_constant_p(_FilterExpression) ? (void*)(unsigned long)(unsigned char)(unsigned long)(_FilterExpression) : _Filter)
146
147 #define _SEH3$_DEFINE_DUMMY_FINALLY(_Name) \
148 auto inline __attribute__((always_inline,gnu_inline)) int _Name(int Action) { return 0; }
149
150 #define _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
151 auto inline __attribute__((always_inline,gnu_inline)) void _Name(volatile SEH3$_REGISTRATION_FRAME *p)
152
153 #define _SEH3$_DEFINE_CLEANUP_FUNC(_Name) \
154 _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
155 { \
156 /* Unregister the frame */ \
157 if (_SEH3$_TryLevel == 1) _SEH3$_UnregisterFrame(&_SEH3$_TrylevelFrame); \
158 else _SEH3$_UnregisterTryLevel(&_SEH3$_TrylevelFrame); \
159 \
160 /* Invoke the finally function (an inline dummy in the __except case) */ \
161 _SEH3$_FinallyFunction(1); \
162 }
163
164 /* This construct scares GCC so much, that it will stop moving code
165 around into places that are never executed. */
166 #define _SEH3$_SCARE_GCC() \
167 void *plabel; \
168 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_HandlerTarget); \
169 asm volatile ("#" : "=a"(plabel) : "p"(&&_SEH3$_l_BeforeTry), "p"(&&_SEH3$_l_HandlerTarget), "p"(&&_SEH3$_l_OnException) \
170 : _SEH3$_CLOBBER_ON_EXCEPTION ); \
171 goto *plabel;
172
173
174 #define _SEH3_TRY \
175 /* Enter the outer scope */ \
176 do { \
177 /* Declare local labels */ \
178 __label__ _SEH3$_l_BeforeTry; \
179 __label__ _SEH3$_l_DoTry; \
180 __label__ _SEH3$_l_AfterTry; \
181 __label__ _SEH3$_l_EndTry; \
182 __label__ _SEH3$_l_HandlerTarget; \
183 __label__ _SEH3$_l_OnException; \
184 \
185 /* Count the try level. Outside of any __try, _SEH3$_TryLevel is 0 */ \
186 enum { \
187 _SEH3$_PreviousTryLevel = _SEH3$_TryLevel, \
188 _SEH3$_TryLevel = _SEH3$_PreviousTryLevel + 1, \
189 }; \
190 \
191 /* Forward declaration of the auto cleanup function */ \
192 _SEH3$_DECLARE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
193 \
194 /* Allocate a registration frame */ \
195 volatile SEH3$_REGISTRATION_FRAME _SEH3$_AUTO_CLEANUP _SEH3$_TrylevelFrame; \
196 \
197 goto _SEH3$_l_BeforeTry; \
198 /* Silence warning */ goto _SEH3$_l_AfterTry; \
199 \
200 _SEH3$_l_DoTry: \
201 do
202
203
204 #define _SEH3_EXCEPT(...) \
205 /* End the try block */ \
206 while (0); \
207 _SEH3$_l_AfterTry: (void)0; \
208 goto _SEH3$_l_EndTry; \
209 \
210 _SEH3$_l_BeforeTry: (void)0; \
211 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
212 \
213 /* Forward declaration of the filter function */ \
214 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FilterFunction); \
215 \
216 /* Create a static data table that contains the jump target and filter function */ \
217 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { &&_SEH3$_l_HandlerTarget, _SEH3$_FILTER(&_SEH3$_FilterFunction, (__VA_ARGS__)) }; \
218 \
219 /* Register the registration record. */ \
220 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
221 else _SEH3$_RegisterTryLevel(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
222 \
223 /* Emit the filter function */ \
224 _SEH3$_DEFINE_FILTER_FUNC(_SEH3$_FilterFunction, (__VA_ARGS__)) \
225 \
226 /* Define an empty inline finally function */ \
227 _SEH3$_DEFINE_DUMMY_FINALLY(_SEH3$_FinallyFunction) \
228 \
229 /* Allow intrinsics for __except to be used */ \
230 _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
231 \
232 goto _SEH3$_l_DoTry; \
233 \
234 _SEH3$_l_HandlerTarget: (void)0; \
235 \
236 if (1) \
237 { \
238 do
239
240
241 #define _SEH3_FINALLY \
242 /* End the try block */ \
243 while (0); \
244 _SEH3$_l_AfterTry: (void)0; \
245 /* Set ScopeTable to 0, this is used by _abnormal_termination() */ \
246 _SEH3$_TrylevelFrame.ScopeTable = 0; \
247 \
248 goto _SEH3$_l_EndTry; \
249 \
250 _SEH3$_l_BeforeTry: (void)0; \
251 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
252 \
253 /* Forward declaration of the finally function */ \
254 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FinallyFunction); \
255 \
256 /* Create a static data table that contains the finally function */ \
257 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { 0, &_SEH3$_FinallyFunction }; \
258 \
259 /* Register the registration record. */ \
260 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
261 else _SEH3$_RegisterTryLevel(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
262 \
263 goto _SEH3$_l_DoTry; \
264 \
265 _SEH3$_l_HandlerTarget: (void)0; \
266 \
267 _SEH3$_FINALLY_FUNC_OPEN(_SEH3$_FinallyFunction) \
268 /* This construct makes sure that the finally function returns */ \
269 /* a proper value at the end */ \
270 for (; ; (void)({return 0; 0;}))
271
272
273 #define _SEH3_END \
274 while (0); \
275 }; \
276 goto _SEH3$_l_EndTry; \
277 \
278 _SEH3$_l_OnException: (void)0; \
279 /* Force GCC to create proper code pathes */ \
280 _SEH3$_SCARE_GCC() \
281 \
282 _SEH3$_l_EndTry:(void)0; \
283 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
284 \
285 /* Implementation of the auto cleanup function */ \
286 _SEH3$_DEFINE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
287 \
288 /* Close the outer scope */ \
289 } while (0);
290
291 #define _SEH3_LEAVE goto _SEH3$_l_AfterTry
292
293 #define _SEH3_VOLATILE volatile