d1168893894bb4b2181f9483201922d7826a4766
[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 /* Prevent gcc from inlining functions that use SEH. */
49 static inline __attribute__((always_inline)) __attribute__((returns_twice)) void _SEH3$_PreventInlining() {}
50
51 extern inline __attribute__((always_inline,gnu_inline))
52 void _SEH3$_UnregisterFrame(volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame)
53 {
54 asm volatile ("movl %k[NewHead], %%fs:0"
55 : : [NewHead] "ir" (RegistrationFrame->Next) : "memory");
56 }
57
58 extern inline __attribute__((always_inline,gnu_inline))
59 void _SEH3$_UnregisterTryLevel(
60 volatile SEH3$_REGISTRATION_FRAME *TrylevelFrame)
61 {
62 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame;
63 asm volatile ("movl %%fs:0, %k[RegistrationFrame]"
64 : [RegistrationFrame] "=r" (RegistrationFrame) : );
65 RegistrationFrame->EndOfChain = TrylevelFrame->Next;
66 }
67
68 enum
69 {
70 _SEH3$_TryLevel = 0,
71 };
72
73 /* These are global dummy definitions, that get overwritten in the local context of __finally / __except blocks */
74 int __cdecl __attribute__((error ("Can only be used inside a __finally block."))) _abnormal_termination(void);
75 unsigned long __cdecl __attribute__((error("Can only be used inside an exception filter or __except block."))) _exception_code(void);
76 void * __cdecl __attribute__((error("Can only be used inside an exception filter."))) _exception_info(void);
77
78 /* Define the registers that get clobbered, when reaching the __except block.
79 We specify ebp on optimized builds without frame pointer, since it will be
80 used by GCC as a general purpose register then. */
81 #if defined(__OPTIMIZE__) && defined(_ALLOW_OMIT_FRAME_POINTER)
82 #define _SEH3$_CLOBBER_ON_EXCEPTION "ebp", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"
83 #else
84 #define _SEH3$_CLOBBER_ON_EXCEPTION "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"
85 #endif
86
87 /* This attribute allows automatic cleanup of the registered frames */
88 #define _SEH3$_AUTO_CLEANUP __attribute__((cleanup(_SEH3$_AutoCleanup)))
89
90 #define _SEH3$_ASM_GOTO(_Asm, _Label, ...) asm goto (_Asm : : : "memory", ## __VA_ARGS__ : _Label)
91
92 #define _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
93 inline __attribute__((always_inline, gnu_inline)) \
94 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; }
95
96 /* This is an asm wrapper around _SEH3$_RegisterFrame */
97 #define _SEH3$_RegisterFrame(_TrylevelFrame, _DataTable, _Target) \
98 asm goto ("leal %0, %%ecx\n" \
99 "call __SEH3$_RegisterFrame\n" \
100 : \
101 : "m" (*(_TrylevelFrame)), "a" (_DataTable) \
102 : "ecx", "edx", "memory" \
103 : _Target)
104
105 /* This is an asm wrapper around _SEH3$_EnterTryLevel */
106 #define _SEH3$_RegisterTryLevel(_TrylevelFrame, _DataTable, _Target) \
107 asm goto ("leal %0, %%ecx\n" \
108 "call __SEH3$_RegisterTryLevel\n" \
109 : \
110 : "m" (*(_TrylevelFrame)), "a" (_DataTable) \
111 : "ecx", "edx", "memory" \
112 : _Target)
113
114 /* On GCC the filter function is a nested function with __fastcall calling
115 convention. The eax register contains a base address the function uses
116 to address the callers stack frame. __fastcall is chosen, because it gives
117 us an effective was of passing one argument to the function, that we need
118 to tell the function in a first pass to return informtion about the frame
119 base address. Since this is something GCC chooses arbitrarily, we call
120 the function with an arbitrary base address in eax first and then use the
121 result to calculate the correct address for a second call to the function. */
122 #define _SEH3$_DECLARE_FILTER_FUNC(_Name) \
123 auto int __fastcall _Name(int Action)
124
125 #define _SEH3$_NESTED_FUNC_OPEN(_Name) \
126 int __fastcall _Name(int Action) \
127 { \
128 /* This is a fancy way to get information about the frame layout */ \
129 if (Action == 0) return (int)&_SEH3$_TrylevelFrame;
130
131 #define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
132 _SEH3$_NESTED_FUNC_OPEN(_Name) \
133 /* Declare the intrinsics for exception filters */ \
134 inline __attribute__((always_inline, gnu_inline)) \
135 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; } \
136 inline __attribute__((always_inline, gnu_inline)) \
137 void * _exception_info() { return _SEH3$_TrylevelFrame.ExceptionPointers; } \
138 \
139 /* Now handle the actual filter expression */ \
140 return (expression); \
141 }
142
143 #define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
144 _SEH3$_NESTED_FUNC_OPEN(_Name) \
145 /* Declare the intrinsics for the finally function */ \
146 inline __attribute__((always_inline, gnu_inline)) \
147 int _abnormal_termination() { return (_SEH3$_TrylevelFrame.ScopeTable != 0); }
148
149 #define _SEH3$_FILTER(_Filter, _FilterExpression) \
150 (__builtin_constant_p(_FilterExpression) ? (void*)(unsigned long)(unsigned char)(unsigned long)(_FilterExpression) : _Filter)
151
152 #define _SEH3$_DEFINE_DUMMY_FINALLY(_Name) \
153 auto inline __attribute__((always_inline,gnu_inline)) int _Name(int Action) { (void)Action; return 0; }
154
155 #define _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
156 auto inline __attribute__((always_inline,gnu_inline)) void _Name(volatile SEH3$_REGISTRATION_FRAME *p)
157
158 #define _SEH3$_DEFINE_CLEANUP_FUNC(_Name) \
159 _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
160 { \
161 (void)p; \
162 /* Unregister the frame */ \
163 if (_SEH3$_TryLevel == 1) _SEH3$_UnregisterFrame(&_SEH3$_TrylevelFrame); \
164 else _SEH3$_UnregisterTryLevel(&_SEH3$_TrylevelFrame); \
165 \
166 /* Invoke the finally function (an inline dummy in the __except case) */ \
167 _SEH3$_FinallyFunction(1); \
168 }
169
170 /* This construct scares GCC so much, that it will stop moving code
171 around into places that are never executed. */
172 #define _SEH3$_SCARE_GCC() \
173 void *plabel; \
174 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_BeforeTry); \
175 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_HandlerTarget); \
176 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
177 asm volatile ("#" : "=a"(plabel) : "p"(&&_SEH3$_l_BeforeTry), "p"(&&_SEH3$_l_HandlerTarget), "p"(&&_SEH3$_l_OnException) \
178 : _SEH3$_CLOBBER_ON_EXCEPTION ); \
179 goto _SEH3$_l_OnException;
180
181
182 #define _SEH3_TRY \
183 _SEH3$_PreventInlining(); \
184 /* Enter the outer scope */ \
185 do { \
186 /* Declare local labels */ \
187 __label__ _SEH3$_l_BeforeTry; \
188 __label__ _SEH3$_l_DoTry; \
189 __label__ _SEH3$_l_AfterTry; \
190 __label__ _SEH3$_l_EndTry; \
191 __label__ _SEH3$_l_HandlerTarget; \
192 __label__ _SEH3$_l_OnException; \
193 \
194 /* Count the try level. Outside of any __try, _SEH3$_TryLevel is 0 */ \
195 enum { \
196 _SEH3$_PreviousTryLevel = _SEH3$_TryLevel, \
197 _SEH3$_TryLevel = _SEH3$_PreviousTryLevel + 1, \
198 }; \
199 \
200 /* Forward declaration of the auto cleanup function */ \
201 _SEH3$_DECLARE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
202 \
203 /* Allocate a registration frame */ \
204 volatile SEH3$_REGISTRATION_FRAME _SEH3$_AUTO_CLEANUP _SEH3$_TrylevelFrame; \
205 \
206 goto _SEH3$_l_BeforeTry; \
207 /* Silence warning */ goto _SEH3$_l_AfterTry; \
208 \
209 _SEH3$_l_DoTry: \
210 do
211
212
213 #define _SEH3_EXCEPT(...) \
214 /* End the try block */ \
215 while (0); \
216 _SEH3$_l_AfterTry: (void)0; \
217 goto _SEH3$_l_EndTry; \
218 \
219 _SEH3$_l_BeforeTry: (void)0; \
220 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
221 \
222 /* Forward declaration of the filter function */ \
223 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FilterFunction); \
224 \
225 /* Create a static data table that contains the jump target and filter function */ \
226 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { &&_SEH3$_l_HandlerTarget, _SEH3$_FILTER(&_SEH3$_FilterFunction, (__VA_ARGS__)) }; \
227 \
228 /* Register the registration record. */ \
229 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
230 else _SEH3$_RegisterTryLevel(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
231 \
232 /* Emit the filter function */ \
233 _SEH3$_DEFINE_FILTER_FUNC(_SEH3$_FilterFunction, (__VA_ARGS__)) \
234 \
235 /* Define an empty inline finally function */ \
236 _SEH3$_DEFINE_DUMMY_FINALLY(_SEH3$_FinallyFunction) \
237 \
238 /* Allow intrinsics for __except to be used */ \
239 _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
240 \
241 goto _SEH3$_l_DoTry; \
242 \
243 _SEH3$_l_HandlerTarget: (void)0; \
244 \
245 if (1) \
246 { \
247 /* Prevent this block from being optimized away */ \
248 asm volatile ("#\n"); \
249 do
250
251
252 #define _SEH3_FINALLY \
253 /* End the try block */ \
254 while (0); \
255 _SEH3$_l_AfterTry: (void)0; \
256 /* Set ScopeTable to 0, this is used by _abnormal_termination() */ \
257 _SEH3$_TrylevelFrame.ScopeTable = 0; \
258 \
259 goto _SEH3$_l_EndTry; \
260 \
261 _SEH3$_l_BeforeTry: (void)0; \
262 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
263 \
264 /* Forward declaration of the finally function */ \
265 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FinallyFunction); \
266 \
267 /* Create a static data table that contains the finally function */ \
268 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { 0, &_SEH3$_FinallyFunction }; \
269 \
270 /* Register the registration record. */ \
271 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
272 else _SEH3$_RegisterTryLevel(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
273 \
274 goto _SEH3$_l_DoTry; \
275 \
276 _SEH3$_l_HandlerTarget: (void)0; \
277 \
278 _SEH3$_FINALLY_FUNC_OPEN(_SEH3$_FinallyFunction) \
279 /* This construct makes sure that the finally function returns */ \
280 /* a proper value at the end */ \
281 for (; ; (void)({return 0; 0;}))
282
283
284 #define _SEH3_END \
285 while (0); \
286 }; \
287 goto _SEH3$_l_EndTry; \
288 \
289 _SEH3$_l_OnException: (void)0; \
290 /* Force GCC to create proper code pathes */ \
291 _SEH3$_SCARE_GCC() \
292 \
293 _SEH3$_l_EndTry:(void)0; \
294 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
295 \
296 /* Implementation of the auto cleanup function */ \
297 _SEH3$_DEFINE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
298 \
299 /* Close the outer scope */ \
300 } while (0);
301
302 #define _SEH3_LEAVE goto _SEH3$_l_AfterTry
303
304 #define _SEH3_VOLATILE volatile