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 /* Except handler stores the exception code here */
68 unsigned long ExceptionCode
;
70 /* Registers that we need to save */
75 #ifdef _SEH3$_FRAME_ALL_NONVOLATILES
83 } SEH3$_REGISTRATION_FRAME
,*PSEH3$_REGISTRATION_FRAME
;
85 /* Prevent gcc from inlining functions that use SEH. */
86 static inline __attribute__((always_inline
)) __attribute__((returns_twice
)) void _SEH3$
_PreventInlining() {}
88 /* Unregister the root frame */
89 extern inline __attribute__((always_inline
,gnu_inline
))
90 void _SEH3$
_UnregisterFrame(volatile SEH3$_REGISTRATION_FRAME
*RegistrationFrame
)
92 asm volatile ("movl %k[NewHead], %%fs:0"
93 : : [NewHead
] "ir" (RegistrationFrame
->Next
) : "memory");
96 /* Unregister a trylevel frame */
97 extern inline __attribute__((always_inline
,gnu_inline
))
98 void _SEH3$
_UnregisterTryLevel(
99 volatile SEH3$_REGISTRATION_FRAME
*TrylevelFrame
)
101 volatile SEH3$_REGISTRATION_FRAME
*RegistrationFrame
;
102 asm volatile ("movl %%fs:0, %k[RegistrationFrame]"
103 : [RegistrationFrame
] "=r" (RegistrationFrame
) : );
104 RegistrationFrame
->EndOfChain
= TrylevelFrame
->Next
;
113 /* These are global dummy definitions, that get overwritten in the local context of __finally / __except blocks */
114 int __cdecl
__attribute__((error ("Can only be used inside a __finally block."))) _abnormal_termination(void);
115 unsigned long __cdecl
__attribute__((error("Can only be used inside an exception filter or __except block."))) _exception_code(void);
116 void * __cdecl
__attribute__((error("Can only be used inside an exception filter."))) _exception_info(void);
119 /* This attribute allows automatic cleanup of the registered frames */
120 #define _SEH3$_AUTO_CLEANUP __attribute__((cleanup(_SEH3$_AutoCleanup)))
123 __attribute__((regparm(3)))
124 __attribute__((returns_twice
))
125 _SEH3$
_RegisterFrameWithNonVolatiles(
126 volatile SEH3$_REGISTRATION_FRAME
* RegistrationFrame
,
127 const SEH3$_SCOPE_TABLE
* ScopeTable
,
131 __attribute__((regparm(3)))
132 __attribute__((returns_twice
))
133 _SEH3$
_RegisterTryLevelWithNonVolatiles(
134 volatile SEH3$_REGISTRATION_FRAME
* RegistrationFrame
,
135 const SEH3$_SCOPE_TABLE
* ScopeTable
,
138 /* CLANG specific definitions! */
141 /* CLANG thinks it is smart and optimizes the alloca away if it is 0 and with it the use of a frame register */
142 #define _SEH3$_EnforceFramePointer() asm volatile ("#\n" : : "m"(*(char*)__builtin_alloca(4)) : "%esp", "memory")
144 /* CLANG doesn't have asm goto! */
145 #define _SEH3$_ASM_GOTO(...)
147 #define _SEH3$_RegisterFrame_(_TrylevelFrame, _DataTable) \
149 int result = _SEH3$_RegisterFrameWithNonVolatiles(_TrylevelFrame, _DataTable, __builtin_alloca(0)); \
150 if (__builtin_expect(result != 0, 0)) \
152 if (result == 1) goto _SEH3$_l_FilterOrFinally; \
153 if (result == 2) goto _SEH3$_l_HandlerTarget; \
154 goto _SEH3$_l_BeforeFilterOrFinally; \
158 #define _SEH3$_RegisterTryLevel_(_TrylevelFrame, _DataTable) \
160 int result = _SEH3$_RegisterTryLevelWithNonVolatiles(_TrylevelFrame, _DataTable, __builtin_alloca(0)); \
161 if (__builtin_expect(result != 0, 0)) \
163 if (result == 1) goto _SEH3$_l_FilterOrFinally; \
164 if (result == 2) goto _SEH3$_l_HandlerTarget; \
165 goto _SEH3$_l_BeforeFilterOrFinally; \
169 #define _SEH3$_SCARE_GCC()
171 #else /* !__clang__ */
173 /* This will make GCC use ebp, even if it was disabled by -fomit-frame-pointer */
174 #define _SEH3$_EnforceFramePointer() asm volatile ("#\n" : : "m"(*(char*)__builtin_alloca(0)) : "%esp", "memory")
176 #define _SEH3$_ASM_GOTO(...) asm goto ("#\n" : : : "memory" : __VA_ARGS__)
179 #define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
180 asm goto ("leal %0, %%eax\n\t" \
181 "leal %1, %%edx\n\t" \
182 "call " #_Function "WithStackLayout" \
184 : "m" (*(_TrylevelFrame)), "m" (*(_DataTable)), "c" (__builtin_alloca(0)), "p" (_SEH3$_RegisterFrameWithNonVolatiles) \
185 : "eax", "edx", "memory" \
186 : _SEH3$_l_BeforeTry, _SEH3$_l_HandlerTarget, _SEH3$_l_OnException, _SEH3$_l_BeforeFilterOrFinally, _SEH3$_l_FilterOrFinally)
189 #define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
190 asm goto ("leal %0, %%eax\n\t" \
191 "leal %1, %%edx\n\t" \
194 : "m" (*(_TrylevelFrame)), "m" (*(_DataTable)), "p" (_SEH3$_RegisterFrameWithNonVolatiles) \
195 : "eax", "edx", "ecx", "memory" \
196 : _SEH3$_l_BeforeTry, _SEH3$_l_HandlerTarget, _SEH3$_l_OnException, _SEH3$_l_BeforeFilterOrFinally, _SEH3$_l_FilterOrFinally)
199 /* This is an asm wrapper around _SEH3$_RegisterFrame */
200 #define _SEH3$_RegisterFrame_(_TrylevelFrame, _DataTable) \
201 _SEH3$_CALL_WRAPPER(__SEH3$_RegisterFrame, _TrylevelFrame, _DataTable)
203 /* This is an asm wrapper around _SEH3$_RegisterTryLevel */
204 #define _SEH3$_RegisterTryLevel_(_TrylevelFrame, _DataTable) \
205 _SEH3$_CALL_WRAPPER(__SEH3$_RegisterTryLevel, _TrylevelFrame, _DataTable)
207 /* This construct scares GCC so much, that it will stop moving code
208 around into places that are never executed. */
209 #define _SEH3$_SCARE_GCC() \
211 _SEH3$_ASM_GOTO(_SEH3$_l_BeforeTry, _SEH3$_l_HandlerTarget, _SEH3$_l_OnException, _SEH3$_l_BeforeFilterOrFinally, _SEH3$_l_FilterOrFinally); \
212 asm volatile ("#" : "=a"(plabel) : "p"(&&_SEH3$_l_BeforeTry), "p"(&&_SEH3$_l_HandlerTarget), "p"(&&_SEH3$_l_OnException), "p"(&&_SEH3$_l_FilterOrFinally) \
213 : "ebx", "ecx", "edx", "esi", "edi", "flags", "memory" ); \
214 goto _SEH3$_l_OnException;
216 #endif /* __clang__ */
218 /* Neither CLANG nor C++ support nested functions */
219 #if defined(__cplusplus) || defined(__clang__)
221 /* Use the global unregister function */
223 __attribute__((regparm(1)))
225 volatile SEH3$_REGISTRATION_FRAME
*Frame
);
227 /* These are only dummies here */
228 #define _SEH3$_DECLARE_CLEANUP_FUNC(_Name)
229 #define _SEH3$_DEFINE_CLEANUP_FUNC(_Name)
230 #define _SEH3$_DECLARE_FILTER_FUNC(_Name)
231 #define _SEH3$_DEFINE_DUMMY_FINALLY(_Name)
233 /* On invocation, the AllocaFrame field is loaded with the return esp value */
234 #define _SEH3$_NESTED_FUNC_RETURN(_Result) \
235 /* Restore esp and return to the caller */ \
236 asm volatile ("movl %[FixedEsp], %%esp\n\tret" \
237 : : "a" (_Result), [FixedEsp] "m" (_SEH3$_TrylevelFrame.AllocaFrame) : "ebx", "ecx", "edx", "esi", "edi", "flags", "memory")
239 /* The filter "function" */
240 #define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
242 /* Evaluate and return the filter expression */ \
243 asm volatile ("#\n" : : : "eax", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"); \
244 _SEH3$_NESTED_FUNC_RETURN((expression)); \
247 #define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
249 asm volatile ("#\n" : : : "eax", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"); \
250 /* This construct makes sure that the finally function returns */ \
251 /* a proper value at the end */ \
252 for (; ; (void)({_SEH3$_NESTED_FUNC_RETURN(0); 0;}))
254 #define _SEH3$_FILTER(_Filter, _FilterExpression) (&&_SEH3$_l_FilterOrFinally)
255 #define _SEH3$_FINALLY(_Finally) (&&_SEH3$_l_FilterOrFinally)
257 #define _SEH3$_DECLARE_EXCEPT_INTRINSICS()
259 /* Since we cannot use nested functions, we declare these globally as macros */
260 #define _abnormal_termination() (_SEH3$_TrylevelFrame.ExceptionPointers != 0)
261 #define _exception_code() (_SEH3$_TrylevelFrame.ExceptionCode)
262 #define _exception_info() (_SEH3$_TrylevelFrame.ExceptionPointers)
264 #else /* __cplusplus || __clang__ */
266 #define _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
267 inline __attribute__((always_inline, gnu_inline)) \
268 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionCode; }
270 /* On GCC the filter function is a nested function with __fastcall calling
271 convention. The eax register contains a base address the function uses
272 to address the callers stack frame. __fastcall is chosen, because it gives
273 us an effective was of passing one argument to the function, that we need
274 to tell the function in a first pass to return informtion about the frame
275 base address. Since this is something GCC chooses arbitrarily, we call
276 the function with an arbitrary base address in eax first and then use the
277 result to calculate the correct address for a second call to the function. */
278 #define _SEH3$_DECLARE_FILTER_FUNC(_Name) \
279 auto int __fastcall _Name(int Action)
281 #define _SEH3$_NESTED_FUNC_OPEN(_Name) \
282 int __fastcall _Name(int Action) \
284 /* This is a fancy way to get information about the frame layout */ \
285 if (Action == 0) return (int)&_SEH3$_TrylevelFrame;
287 #define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
288 _SEH3$_NESTED_FUNC_OPEN(_Name) \
289 /* Declare the intrinsics for exception filters */ \
290 _Pragma("GCC diagnostic push") \
291 _Pragma("GCC diagnostic ignored \"-Wshadow\"") \
292 inline __attribute__((always_inline, gnu_inline)) \
293 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionCode; } \
294 inline __attribute__((always_inline, gnu_inline)) \
295 void * _exception_info() { return _SEH3$_TrylevelFrame.ExceptionPointers; } \
296 _Pragma("GCC diagnostic pop") \
298 /* Now handle the actual filter expression */ \
299 return (expression); \
302 #define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
303 _SEH3$_NESTED_FUNC_OPEN(_Name) \
304 /* Declare the intrinsics for the finally function */ \
305 inline __attribute__((always_inline, gnu_inline)) \
306 int _abnormal_termination() { return (_SEH3$_TrylevelFrame.ExceptionPointers != 0); } \
308 /* This construct makes sure that the finally function returns */ \
309 /* a proper value at the end */ \
310 for (; ; (void)({return 0; 0;}))
312 #define _SEH3$_FILTER(_Filter, _FilterExpression) \
313 (__builtin_constant_p(_FilterExpression) ? (void*)(unsigned long)(unsigned char)(unsigned long)(_FilterExpression) : _Filter)
315 #define _SEH3$_FINALLY(_Finally) (_Finally)
317 #define _SEH3$_DEFINE_DUMMY_FINALLY(_Name) \
318 auto inline __attribute__((always_inline,gnu_inline)) int _Name(int Action) { (void)Action; return 0; }
320 #define _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
321 auto inline __attribute__((always_inline,gnu_inline)) void _Name(volatile SEH3$_REGISTRATION_FRAME *p)
323 #define _SEH3$_DEFINE_CLEANUP_FUNC(_Name) \
324 _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
327 /* Unregister the frame */ \
328 if (_SEH3$_TryLevel == 1) _SEH3$_UnregisterFrame(&_SEH3$_TrylevelFrame); \
329 else _SEH3$_UnregisterTryLevel(&_SEH3$_TrylevelFrame); \
331 /* Invoke the finally function (an inline dummy in the __except case) */ \
332 _SEH3$_FinallyFunction(1); \
335 #endif /* __cplusplus || __clang__ */
340 _SEH3$_PreventInlining(); \
341 /* Enter the outer scope */ \
343 /* Declare local labels */ \
344 __label__ _SEH3$_l_BeforeTry; \
345 __label__ _SEH3$_l_DoTry; \
346 __label__ _SEH3$_l_AfterTry; \
347 __label__ _SEH3$_l_EndTry; \
348 __label__ _SEH3$_l_HandlerTarget; \
349 __label__ _SEH3$_l_OnException; \
350 __label__ _SEH3$_l_BeforeFilterOrFinally; \
351 __label__ _SEH3$_l_FilterOrFinally; \
352 (void)&&_SEH3$_l_OnException; \
353 (void)&&_SEH3$_l_BeforeFilterOrFinally; \
354 (void)&&_SEH3$_l_FilterOrFinally; \
356 _Pragma("GCC diagnostic push") \
357 _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
359 /* Count the try level. Outside of any __try, _SEH3$_TryLevel is 0 */ \
361 _SEH3$_PreviousTryLevel = _SEH3$_TryLevel, \
362 _SEH3$_TryLevel = _SEH3$_PreviousTryLevel + 1, \
365 /* Forward declaration of the auto cleanup function */ \
366 _SEH3$_DECLARE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
368 /* Allocate a registration frame */ \
369 volatile SEH3$_REGISTRATION_FRAME _SEH3$_AUTO_CLEANUP _SEH3$_TrylevelFrame; \
371 _Pragma("GCC diagnostic pop") \
373 goto _SEH3$_l_BeforeTry; \
374 /* Silence warning */ goto _SEH3$_l_AfterTry; \
380 #define _SEH3_EXCEPT(...) \
381 /* End the try block */ \
383 _SEH3$_l_AfterTry: (void)0; \
384 goto _SEH3$_l_EndTry; \
386 _SEH3$_l_BeforeTry: (void)0; \
387 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
389 _Pragma("GCC diagnostic push") \
390 _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
392 /* Forward declaration of the filter function */ \
393 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FilterFunction); \
395 /* Create a static data table that contains the jump target and filter function */ \
396 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { &&_SEH3$_l_HandlerTarget, _SEH3$_FILTER(&_SEH3$_FilterFunction, (__VA_ARGS__)), _SEH3$_TryLevel, _SEH3$_HANDLER_TYPE }; \
398 /* Register the registration record. */ \
399 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
400 else _SEH3$_RegisterTryLevel_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
402 /* Define an empty inline finally function */ \
403 _SEH3$_DEFINE_DUMMY_FINALLY(_SEH3$_FinallyFunction) \
405 /* Allow intrinsics for __except to be used */ \
406 _SEH3$_DECLARE_EXCEPT_INTRINSICS(); \
408 goto _SEH3$_l_DoTry; \
410 _SEH3$_l_BeforeFilterOrFinally: (void)0; \
411 /* Make sure the filter function doesn't use esp */ \
412 _SEH3$_EnforceFramePointer(); \
414 _SEH3$_l_FilterOrFinally: (void)0; \
415 /* Emit the filter function */ \
416 _SEH3$_DEFINE_FILTER_FUNC(_SEH3$_FilterFunction, (__VA_ARGS__)) \
418 _SEH3$_l_HandlerTarget: (void)0; \
419 _SEH3$_EnforceFramePointer(); \
423 /* Prevent this block from being optimized away */ \
424 asm volatile ("#\n"); \
428 #define _SEH3_FINALLY \
429 /* End the try block */ \
431 _SEH3$_l_AfterTry: (void)0; \
432 /* Set ExceptionPointers to 0, this is used by _abnormal_termination() */ \
433 _SEH3$_TrylevelFrame.ExceptionPointers = 0; \
435 goto _SEH3$_l_EndTry; \
437 _SEH3$_l_BeforeTry: (void)0; \
438 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
440 _Pragma("GCC diagnostic push") \
441 _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
443 /* Forward declaration of the finally function */ \
444 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FinallyFunction); \
446 /* Create a static data table that contains the finally function */ \
447 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { 0, _SEH3$_FINALLY(&_SEH3$_FinallyFunction), _SEH3$_TryLevel, _SEH3$_HANDLER_TYPE }; \
449 /* Register the registration record. */ \
450 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
451 else _SEH3$_RegisterTryLevel_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
452 _SEH3$_TrylevelFrame.ExceptionPointers = (PSEH3$_EXCEPTION_POINTERS)1; \
454 goto _SEH3$_l_DoTry; \
456 _SEH3$_l_HandlerTarget: (void)0; \
457 _SEH3$_EnforceFramePointer(); \
459 _SEH3$_l_BeforeFilterOrFinally: (void)0; \
460 _SEH3$_EnforceFramePointer(); \
461 _SEH3$_l_FilterOrFinally: (void)0; \
462 _SEH3$_FINALLY_FUNC_OPEN(_SEH3$_FinallyFunction)
468 goto _SEH3$_l_EndTry; \
470 _SEH3$_l_OnException: (void)0; \
471 /* Force GCC to create proper code pathes */ \
472 _SEH3$_SCARE_GCC(); \
474 _SEH3$_l_EndTry:(void)0; \
475 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
477 /* Implementation of the auto cleanup function */ \
478 _SEH3$_DEFINE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
480 _Pragma("GCC diagnostic pop") \
482 /* Close the outer scope */ \
485 #define _SEH3_LEAVE goto _SEH3$_l_AfterTry
487 #define _SEH3_VOLATILE volatile