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)
8 /* For additional information see pseh3.c in the related library. */
19 /* CLANG must safe non-volatiles, because it uses a return-twice algorithm */
20 #if defined(__clang__) && !defined(_SEH3$_FRAME_ALL_NONVOLATILES)
21 #define _SEH3$_FRAME_ALL_NONVOLATILES 1
26 _SEH3$_NESTED_HANDLER
= 0,
27 _SEH3$_CPP_HANDLER
= 1,
28 _SEH3$_CLANG_HANDLER
= 2,
30 _SEH3$_HANDLER_TYPE
= _SEH3$_CLANG_HANDLER
,
31 #elif defined(__cplusplus)
32 _SEH3$_HANDLER_TYPE
= _SEH3$_CPP_HANDLER
,
34 _SEH3$_HANDLER_TYPE
= _SEH3$_NESTED_HANDLER
,
38 typedef struct _SEH3$_SCOPE_TABLE
42 unsigned char TryLevel
;
43 unsigned char HandlerType
;
44 } SEH3$_SCOPE_TABLE
, *PSEH3$_SCOPE_TABLE
;
46 typedef struct _SEH3$_EXCEPTION_POINTERS
48 struct _EXCEPTION_RECORD
*ExceptionRecord
;
49 struct _CONTEXT
*ContextRecord
;
50 } SEH3$_EXCEPTION_POINTERS
, *PSEH3$_EXCEPTION_POINTERS
;
52 typedef struct _SEH3$_REGISTRATION_FRAME
54 /* First the Windows base record. Don't move this! */
55 struct _SEH3$_REGISTRATION_FRAME
*Next
;
58 /* Points to the end of the internal registration chain */
59 struct _SEH3$_REGISTRATION_FRAME
*EndOfChain
;
61 /* Pointer to the static scope table */
62 PSEH3$_SCOPE_TABLE ScopeTable
;
64 /* Except handler stores pointer to exception pointers here */
65 PSEH3$_EXCEPTION_POINTERS
volatile ExceptionPointers
;
67 /* Registers that we need to save */
72 #ifdef _SEH3$_FRAME_ALL_NONVOLATILES
77 } SEH3$_REGISTRATION_FRAME
,*PSEH3$_REGISTRATION_FRAME
;
79 /* Prevent gcc from inlining functions that use SEH. */
80 static inline __attribute__((always_inline
)) __attribute__((returns_twice
)) void _SEH3$
_PreventInlining() {}
82 /* Unregister the root frame */
83 extern inline __attribute__((always_inline
,gnu_inline
))
84 void _SEH3$
_UnregisterFrame(volatile SEH3$_REGISTRATION_FRAME
*RegistrationFrame
)
86 asm volatile ("movl %k[NewHead], %%fs:0"
87 : : [NewHead
] "ir" (RegistrationFrame
->Next
) : "memory");
90 /* Unregister a trylevel frame */
91 extern inline __attribute__((always_inline
,gnu_inline
))
92 void _SEH3$
_UnregisterTryLevel(
93 volatile SEH3$_REGISTRATION_FRAME
*TrylevelFrame
)
95 volatile SEH3$_REGISTRATION_FRAME
*RegistrationFrame
;
96 asm volatile ("movl %%fs:0, %k[RegistrationFrame]"
97 : [RegistrationFrame
] "=r" (RegistrationFrame
) : );
98 RegistrationFrame
->EndOfChain
= TrylevelFrame
->Next
;
107 /* These are global dummy definitions, that get overwritten in the local context of __finally / __except blocks */
108 int __cdecl
__attribute__((error ("Can only be used inside a __finally block."))) _abnormal_termination(void);
109 unsigned long __cdecl
__attribute__((error("Can only be used inside an exception filter or __except block."))) _exception_code(void);
110 void * __cdecl
__attribute__((error("Can only be used inside an exception filter."))) _exception_info(void);
113 /* This attribute allows automatic cleanup of the registered frames */
114 #define _SEH3$_AUTO_CLEANUP __attribute__((cleanup(_SEH3$_AutoCleanup)))
116 /* CLANG specific definitions! */
119 /* CLANG thinks it is smart and optimizes the alloca away if it is 0 and with it the use of a frame register */
120 #define _SEH3$_EnforceFramePointer() asm volatile ("#\n" : : "m"(*(char*)__builtin_alloca(4)) : "%esp", "memory")
122 /* CLANG doesn't have asm goto! */
123 #define _SEH3$_ASM_GOTO(_Label, ...)
126 __attribute__((regparm(3)))
127 __attribute__((returns_twice
))
128 _SEH3$
_RegisterFrameWithNonVolatiles(
129 volatile SEH3$_REGISTRATION_FRAME
* RegistrationFrame
,
130 const SEH3$_SCOPE_TABLE
* ScopeTable
,
133 #define _SEH3$_RegisterFrame_(_TrylevelFrame, _DataTable) \
135 int result = _SEH3$_RegisterFrameWithNonVolatiles(_TrylevelFrame, _DataTable, __builtin_alloca(0)); \
136 if (__builtin_expect(result != 0, 0)) \
138 if (result == 1) goto _SEH3$_l_FilterOrFinally; \
139 if (result == 2) goto _SEH3$_l_HandlerTarget; \
140 goto _SEH3$_l_BeforeFilterOrFinally; \
145 __attribute__((regparm(3)))
146 __attribute__((returns_twice
))
147 _SEH3$
_RegisterTryLevelWithNonVolatiles(
148 volatile SEH3$_REGISTRATION_FRAME
* RegistrationFrame
,
149 const SEH3$_SCOPE_TABLE
* ScopeTable
,
152 #define _SEH3$_RegisterTryLevel_(_TrylevelFrame, _DataTable) \
154 int result = _SEH3$_RegisterTryLevelWithNonVolatiles(_TrylevelFrame, _DataTable, __builtin_alloca(0)); \
155 if (__builtin_expect(result != 0, 0)) \
157 if (result == 1) goto _SEH3$_l_FilterOrFinally; \
158 if (result == 2) goto _SEH3$_l_HandlerTarget; \
159 goto _SEH3$_l_BeforeFilterOrFinally; \
163 #define _SEH3$_SCARE_GCC()
165 #else /* !__clang__ */
167 /* This will make GCC use ebp, even if it was disabled by -fomit-frame-pointer */
168 #define _SEH3$_EnforceFramePointer() asm volatile ("#\n" : : "m"(*(char*)__builtin_alloca(0)) : "%esp", "memory")
170 #define _SEH3$_ASM_GOTO(_Label, ...) asm goto ("#\n" : : : "memory", ## __VA_ARGS__ : _Label)
173 #define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
174 asm goto ("leal %0, %%eax\n" \
176 "call " #_Function "WithStackLayout\n" \
178 : "m" (*(_TrylevelFrame)), "m" (*(_DataTable)), "c"(__builtin_alloca(0)) \
179 : "eax", "edx", "memory" \
180 : _SEH3$_l_HandlerTarget, _SEH3$_l_FilterOrFinally)
183 #define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
184 asm goto ("leal %0, %%eax\n" \
186 "call " #_Function "\n" \
188 : "m" (*(_TrylevelFrame)), "m" (*(_DataTable)) \
189 : "eax", "edx", "ecx", "memory" \
190 : _SEH3$_l_HandlerTarget)
193 /* This is an asm wrapper around _SEH3$_RegisterFrame */
194 #define _SEH3$_RegisterFrame_(_TrylevelFrame, _DataTable) \
195 _SEH3$_CALL_WRAPPER(__SEH3$_RegisterFrame, _TrylevelFrame, _DataTable)
197 /* This is an asm wrapper around _SEH3$_RegisterTryLevel */
198 #define _SEH3$_RegisterTryLevel_(_TrylevelFrame, _DataTable) \
199 _SEH3$_CALL_WRAPPER(__SEH3$_RegisterTryLevel, _TrylevelFrame, _DataTable)
201 /* This construct scares GCC so much, that it will stop moving code
202 around into places that are never executed. */
203 #define _SEH3$_SCARE_GCC() \
205 _SEH3$_ASM_GOTO(_SEH3$_l_BeforeTry); \
206 _SEH3$_ASM_GOTO(_SEH3$_l_HandlerTarget); \
207 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
208 asm volatile ("#" : "=a"(plabel) : "p"(&&_SEH3$_l_BeforeTry), "p"(&&_SEH3$_l_HandlerTarget), "p"(&&_SEH3$_l_OnException) \
209 : "ebx", "ecx", "edx", "esi", "edi", "flags", "memory" ); \
210 goto _SEH3$_l_OnException;
212 #endif /* __clang__ */
214 /* Neither CLANG nor C++ support nested functions */
215 #if defined(__cplusplus) || defined(__clang__)
217 /* Use the global unregister function */
219 __attribute__((regparm(1)))
221 volatile SEH3$_REGISTRATION_FRAME
*Frame
);
223 /* These are only dummies here */
224 #define _SEH3$_DECLARE_CLEANUP_FUNC(_Name)
225 #define _SEH3$_DEFINE_CLEANUP_FUNC(_Name)
226 #define _SEH3$_DECLARE_FILTER_FUNC(_Name)
227 #define _SEH3$_DEFINE_DUMMY_FINALLY(_Name)
229 /* On invocation, the AllocaFrame field is loaded with the return esp value */
230 #define _SEH3$_NESTED_FUNC_RETURN(_Result) \
231 /* Restore esp and return to the caller */ \
232 asm volatile ("movl %[FixedEsp], %%esp\nret\n" \
233 : : "a"(_Result), [FixedEsp]"m"(_SEH3$_TrylevelFrame.AllocaFrame) : "memory")
235 /* The filter "function" */
236 #define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
238 /* Evaluate and return the filter expression */ \
239 _SEH3$_NESTED_FUNC_RETURN((expression)); \
242 #define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
244 /* This construct makes sure that the finally function returns */ \
245 /* a proper value at the end */ \
246 for (; ; (void)({_SEH3$_NESTED_FUNC_RETURN(0); 0;}))
248 #define _SEH3$_FILTER(_Filter, _FilterExpression) (&&_SEH3$_l_FilterOrFinally)
249 #define _SEH3$_FINALLY(_Finally) (&&_SEH3$_l_FilterOrFinally)
251 #define _SEH3$_DECLARE_EXCEPT_INTRINSICS()
253 /* Since we cannot use nested functions, we declare these globally as macros */
254 #define _abnormal_termination() (_SEH3$_TrylevelFrame.ExceptionPointers != 0)
255 #define _exception_code() (_SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode)
256 #define _exception_info() (_SEH3$_TrylevelFrame.ExceptionPointers)
258 #else /* __cplusplus || __clang__ */
260 #define _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
261 inline __attribute__((always_inline, gnu_inline)) \
262 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; }
264 /* On GCC the filter function is a nested function with __fastcall calling
265 convention. The eax register contains a base address the function uses
266 to address the callers stack frame. __fastcall is chosen, because it gives
267 us an effective was of passing one argument to the function, that we need
268 to tell the function in a first pass to return informtion about the frame
269 base address. Since this is something GCC chooses arbitrarily, we call
270 the function with an arbitrary base address in eax first and then use the
271 result to calculate the correct address for a second call to the function. */
272 #define _SEH3$_DECLARE_FILTER_FUNC(_Name) \
273 auto int __fastcall _Name(int Action)
275 #define _SEH3$_NESTED_FUNC_OPEN(_Name) \
276 int __fastcall _Name(int Action) \
278 /* This is a fancy way to get information about the frame layout */ \
279 if (Action == 0) return (int)&_SEH3$_TrylevelFrame;
281 #define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
282 _SEH3$_NESTED_FUNC_OPEN(_Name) \
283 /* Declare the intrinsics for exception filters */ \
284 inline __attribute__((always_inline, gnu_inline)) \
285 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; } \
286 inline __attribute__((always_inline, gnu_inline)) \
287 void * _exception_info() { return _SEH3$_TrylevelFrame.ExceptionPointers; } \
289 /* Now handle the actual filter expression */ \
290 return (expression); \
293 #define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
294 _SEH3$_NESTED_FUNC_OPEN(_Name) \
295 /* Declare the intrinsics for the finally function */ \
296 inline __attribute__((always_inline, gnu_inline)) \
297 int _abnormal_termination() { return (_SEH3$_TrylevelFrame.ExceptionPointers != 0); } \
299 /* This construct makes sure that the finally function returns */ \
300 /* a proper value at the end */ \
301 for (; ; (void)({return 0; 0;}))
303 #define _SEH3$_FILTER(_Filter, _FilterExpression) \
304 (__builtin_constant_p(_FilterExpression) ? (void*)(unsigned long)(unsigned char)(unsigned long)(_FilterExpression) : _Filter)
306 #define _SEH3$_FINALLY(_Finally) (_Finally)
308 #define _SEH3$_DEFINE_DUMMY_FINALLY(_Name) \
309 auto inline __attribute__((always_inline,gnu_inline)) int _Name(int Action) { (void)Action; return 0; }
311 #define _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
312 auto inline __attribute__((always_inline,gnu_inline)) void _Name(volatile SEH3$_REGISTRATION_FRAME *p)
314 #define _SEH3$_DEFINE_CLEANUP_FUNC(_Name) \
315 _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
318 /* Unregister the frame */ \
319 if (_SEH3$_TryLevel == 1) _SEH3$_UnregisterFrame(&_SEH3$_TrylevelFrame); \
320 else _SEH3$_UnregisterTryLevel(&_SEH3$_TrylevelFrame); \
322 /* Invoke the finally function (an inline dummy in the __except case) */ \
323 _SEH3$_FinallyFunction(1); \
326 #endif /* __cplusplus || __clang__ */
331 _SEH3$_PreventInlining(); \
332 /* Enter the outer scope */ \
334 /* Declare local labels */ \
335 __label__ _SEH3$_l_BeforeTry; \
336 __label__ _SEH3$_l_DoTry; \
337 __label__ _SEH3$_l_AfterTry; \
338 __label__ _SEH3$_l_EndTry; \
339 __label__ _SEH3$_l_HandlerTarget; \
340 __label__ _SEH3$_l_OnException; \
341 __label__ _SEH3$_l_BeforeFilterOrFinally; \
342 __label__ _SEH3$_l_FilterOrFinally; \
343 (void)&&_SEH3$_l_OnException; \
344 (void)&&_SEH3$_l_BeforeFilterOrFinally; \
345 (void)&&_SEH3$_l_FilterOrFinally; \
347 /* Count the try level. Outside of any __try, _SEH3$_TryLevel is 0 */ \
349 _SEH3$_PreviousTryLevel = _SEH3$_TryLevel, \
350 _SEH3$_TryLevel = _SEH3$_PreviousTryLevel + 1, \
353 /* Forward declaration of the auto cleanup function */ \
354 _SEH3$_DECLARE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
356 /* Allocate a registration frame */ \
357 volatile SEH3$_REGISTRATION_FRAME _SEH3$_AUTO_CLEANUP _SEH3$_TrylevelFrame; \
359 goto _SEH3$_l_BeforeTry; \
360 /* Silence warning */ goto _SEH3$_l_AfterTry; \
366 #define _SEH3_EXCEPT(...) \
367 /* End the try block */ \
369 _SEH3$_l_AfterTry: (void)0; \
370 goto _SEH3$_l_EndTry; \
372 _SEH3$_l_BeforeTry: (void)0; \
373 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
375 /* Forward declaration of the filter function */ \
376 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FilterFunction); \
378 /* Create a static data table that contains the jump target and filter function */ \
379 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { &&_SEH3$_l_HandlerTarget, _SEH3$_FILTER(&_SEH3$_FilterFunction, (__VA_ARGS__)), _SEH3$_TryLevel, _SEH3$_HANDLER_TYPE }; \
381 /* Register the registration record. */ \
382 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
383 else _SEH3$_RegisterTryLevel_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
385 /* Define an empty inline finally function */ \
386 _SEH3$_DEFINE_DUMMY_FINALLY(_SEH3$_FinallyFunction) \
388 /* Allow intrinsics for __except to be used */ \
389 _SEH3$_DECLARE_EXCEPT_INTRINSICS(); \
391 goto _SEH3$_l_DoTry; \
393 _SEH3$_l_BeforeFilterOrFinally: (void)0; \
394 /* Make sure the filter function doesn't use esp */ \
395 _SEH3$_EnforceFramePointer(); \
397 _SEH3$_l_FilterOrFinally: (void)0; \
398 /* Emit the filter function */ \
399 _SEH3$_DEFINE_FILTER_FUNC(_SEH3$_FilterFunction, (__VA_ARGS__)) \
401 _SEH3$_l_HandlerTarget: (void)0; \
402 _SEH3$_EnforceFramePointer(); \
406 /* Prevent this block from being optimized away */ \
407 asm volatile ("#\n"); \
411 #define _SEH3_FINALLY \
412 /* End the try block */ \
414 _SEH3$_l_AfterTry: (void)0; \
415 /* Set ExceptionPointers to 0, this is used by _abnormal_termination() */ \
416 _SEH3$_TrylevelFrame.ExceptionPointers = 0; \
418 goto _SEH3$_l_EndTry; \
420 _SEH3$_l_BeforeTry: (void)0; \
421 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
423 /* Forward declaration of the finally function */ \
424 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FinallyFunction); \
426 /* Create a static data table that contains the finally function */ \
427 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { 0, _SEH3$_FINALLY(&_SEH3$_FinallyFunction), _SEH3$_TryLevel, _SEH3$_HANDLER_TYPE }; \
429 /* Register the registration record. */ \
430 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
431 else _SEH3$_RegisterTryLevel_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
433 goto _SEH3$_l_DoTry; \
435 _SEH3$_l_HandlerTarget: (void)0; \
436 _SEH3$_EnforceFramePointer(); \
438 _SEH3$_l_BeforeFilterOrFinally: (void)0; \
439 _SEH3$_EnforceFramePointer(); \
440 _SEH3$_l_FilterOrFinally: (void)0; \
441 _SEH3$_FINALLY_FUNC_OPEN(_SEH3$_FinallyFunction)
447 goto _SEH3$_l_EndTry; \
449 _SEH3$_l_OnException: (void)0; \
450 /* Force GCC to create proper code pathes */ \
451 _SEH3$_SCARE_GCC(); \
453 _SEH3$_l_EndTry:(void)0; \
454 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
456 /* Implementation of the auto cleanup function */ \
457 _SEH3$_DEFINE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
459 /* Close the outer scope */ \
462 #define _SEH3_LEAVE goto _SEH3$_l_AfterTry
464 #define _SEH3_VOLATILE volatile