[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 #ifdef __cplusplus
16 extern "C" {
17 #endif
18
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
22 #endif
23
24 enum
25 {
26 _SEH3$_NESTED_HANDLER = 0,
27 _SEH3$_CPP_HANDLER = 1,
28 _SEH3$_CLANG_HANDLER = 2,
29 #ifdef __clang__
30 _SEH3$_HANDLER_TYPE = _SEH3$_CLANG_HANDLER,
31 #elif defined(__cplusplus)
32 _SEH3$_HANDLER_TYPE = _SEH3$_CPP_HANDLER,
33 #else
34 _SEH3$_HANDLER_TYPE = _SEH3$_NESTED_HANDLER,
35 #endif
36 };
37
38 typedef struct _SEH3$_SCOPE_TABLE
39 {
40 void *Target;
41 void *Filter;
42 unsigned char TryLevel;
43 unsigned char HandlerType;
44 } SEH3$_SCOPE_TABLE, *PSEH3$_SCOPE_TABLE;
45
46 typedef struct _SEH3$_EXCEPTION_POINTERS
47 {
48 struct _EXCEPTION_RECORD *ExceptionRecord;
49 struct _CONTEXT *ContextRecord;
50 } SEH3$_EXCEPTION_POINTERS, *PSEH3$_EXCEPTION_POINTERS;
51
52 typedef struct _SEH3$_REGISTRATION_FRAME
53 {
54 /* First the Windows base record. Don't move this! */
55 struct _SEH3$_REGISTRATION_FRAME *Next;
56 void *Handler;
57
58 /* Points to the end of the internal registration chain */
59 struct _SEH3$_REGISTRATION_FRAME *EndOfChain;
60
61 /* Pointer to the static scope table */
62 PSEH3$_SCOPE_TABLE ScopeTable;
63
64 /* Except handler stores pointer to exception pointers here */
65 PSEH3$_EXCEPTION_POINTERS volatile ExceptionPointers;
66
67 /* Except handler stores the exception code here */
68 unsigned long ExceptionCode;
69
70 /* Registers that we need to save */
71 unsigned long Esp;
72 unsigned long Ebp;
73
74 char* AllocaFrame;
75 #ifdef _SEH3$_FRAME_ALL_NONVOLATILES
76 unsigned long Ebx;
77 unsigned long Esi;
78 unsigned long Edi;
79 #endif
80 } SEH3$_REGISTRATION_FRAME ,*PSEH3$_REGISTRATION_FRAME;
81
82 /* Prevent gcc from inlining functions that use SEH. */
83 static inline __attribute__((always_inline)) __attribute__((returns_twice)) void _SEH3$_PreventInlining() {}
84
85 /* Unregister the root frame */
86 extern inline __attribute__((always_inline,gnu_inline))
87 void _SEH3$_UnregisterFrame(volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame)
88 {
89 asm volatile ("movl %k[NewHead], %%fs:0"
90 : : [NewHead] "ir" (RegistrationFrame->Next) : "memory");
91 }
92
93 /* Unregister a trylevel frame */
94 extern inline __attribute__((always_inline,gnu_inline))
95 void _SEH3$_UnregisterTryLevel(
96 volatile SEH3$_REGISTRATION_FRAME *TrylevelFrame)
97 {
98 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame;
99 asm volatile ("movl %%fs:0, %k[RegistrationFrame]"
100 : [RegistrationFrame] "=r" (RegistrationFrame) : );
101 RegistrationFrame->EndOfChain = TrylevelFrame->Next;
102 }
103
104 enum
105 {
106 _SEH3$_TryLevel = 0,
107 };
108
109 #ifndef __clang__
110 /* These are global dummy definitions, that get overwritten in the local context of __finally / __except blocks */
111 int __cdecl __attribute__((error ("Can only be used inside a __finally block."))) _abnormal_termination(void);
112 unsigned long __cdecl __attribute__((error("Can only be used inside an exception filter or __except block."))) _exception_code(void);
113 void * __cdecl __attribute__((error("Can only be used inside an exception filter."))) _exception_info(void);
114 #endif
115
116 /* This attribute allows automatic cleanup of the registered frames */
117 #define _SEH3$_AUTO_CLEANUP __attribute__((cleanup(_SEH3$_AutoCleanup)))
118
119 int
120 __attribute__((regparm(3)))
121 __attribute__((returns_twice))
122 _SEH3$_RegisterFrameWithNonVolatiles(
123 volatile SEH3$_REGISTRATION_FRAME* RegistrationFrame,
124 const SEH3$_SCOPE_TABLE* ScopeTable,
125 void* AllocaFrame);
126
127 int
128 __attribute__((regparm(3)))
129 __attribute__((returns_twice))
130 _SEH3$_RegisterTryLevelWithNonVolatiles(
131 volatile SEH3$_REGISTRATION_FRAME* RegistrationFrame,
132 const SEH3$_SCOPE_TABLE* ScopeTable,
133 void* AllocaFrame);
134
135 /* CLANG specific definitions! */
136 #ifdef __clang__
137
138 /* CLANG thinks it is smart and optimizes the alloca away if it is 0 and with it the use of a frame register */
139 #define _SEH3$_EnforceFramePointer() asm volatile ("#\n" : : "m"(*(char*)__builtin_alloca(4)) : "%esp", "memory")
140
141 /* CLANG doesn't have asm goto! */
142 #define _SEH3$_ASM_GOTO(...)
143
144 #define _SEH3$_RegisterFrame_(_TrylevelFrame, _DataTable) \
145 do { \
146 int result = _SEH3$_RegisterFrameWithNonVolatiles(_TrylevelFrame, _DataTable, __builtin_alloca(0)); \
147 if (__builtin_expect(result != 0, 0)) \
148 { \
149 if (result == 1) goto _SEH3$_l_FilterOrFinally; \
150 if (result == 2) goto _SEH3$_l_HandlerTarget; \
151 goto _SEH3$_l_BeforeFilterOrFinally; \
152 } \
153 } while(0)
154
155 #define _SEH3$_RegisterTryLevel_(_TrylevelFrame, _DataTable) \
156 do { \
157 int result = _SEH3$_RegisterTryLevelWithNonVolatiles(_TrylevelFrame, _DataTable, __builtin_alloca(0)); \
158 if (__builtin_expect(result != 0, 0)) \
159 { \
160 if (result == 1) goto _SEH3$_l_FilterOrFinally; \
161 if (result == 2) goto _SEH3$_l_HandlerTarget; \
162 goto _SEH3$_l_BeforeFilterOrFinally; \
163 } \
164 } while(0)
165
166 #define _SEH3$_SCARE_GCC()
167
168 #else /* !__clang__ */
169
170 /* This will make GCC use ebp, even if it was disabled by -fomit-frame-pointer */
171 #define _SEH3$_EnforceFramePointer() asm volatile ("#\n" : : "m"(*(char*)__builtin_alloca(0)) : "%esp", "memory")
172
173 #define _SEH3$_ASM_GOTO(...) asm goto ("#\n" : : : "memory" : __VA_ARGS__)
174
175 #ifdef __cplusplus
176 #define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
177 asm goto ("leal %0, %%eax\n\t" \
178 "leal %1, %%edx\n\t" \
179 "call " #_Function "WithStackLayout" \
180 : \
181 : "m" (*(_TrylevelFrame)), "m" (*(_DataTable)), "c" (__builtin_alloca(0)), "p" (_SEH3$_RegisterFrameWithNonVolatiles) \
182 : "eax", "edx", "memory" \
183 : _SEH3$_l_BeforeTry, _SEH3$_l_HandlerTarget, _SEH3$_l_OnException, _SEH3$_l_BeforeFilterOrFinally, _SEH3$_l_FilterOrFinally)
184
185 #else
186 #define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
187 asm goto ("leal %0, %%eax\n\t" \
188 "leal %1, %%edx\n\t" \
189 "call " #_Function \
190 : \
191 : "m" (*(_TrylevelFrame)), "m" (*(_DataTable)), "p" (_SEH3$_RegisterFrameWithNonVolatiles) \
192 : "eax", "edx", "ecx", "memory" \
193 : _SEH3$_l_BeforeTry, _SEH3$_l_HandlerTarget, _SEH3$_l_OnException, _SEH3$_l_BeforeFilterOrFinally, _SEH3$_l_FilterOrFinally)
194 #endif
195
196 /* This is an asm wrapper around _SEH3$_RegisterFrame */
197 #define _SEH3$_RegisterFrame_(_TrylevelFrame, _DataTable) \
198 _SEH3$_CALL_WRAPPER(__SEH3$_RegisterFrame, _TrylevelFrame, _DataTable)
199
200 /* This is an asm wrapper around _SEH3$_RegisterTryLevel */
201 #define _SEH3$_RegisterTryLevel_(_TrylevelFrame, _DataTable) \
202 _SEH3$_CALL_WRAPPER(__SEH3$_RegisterTryLevel, _TrylevelFrame, _DataTable)
203
204 /* This construct scares GCC so much, that it will stop moving code
205 around into places that are never executed. */
206 #define _SEH3$_SCARE_GCC() \
207 void *plabel; \
208 _SEH3$_ASM_GOTO(_SEH3$_l_BeforeTry, _SEH3$_l_HandlerTarget, _SEH3$_l_OnException, _SEH3$_l_BeforeFilterOrFinally, _SEH3$_l_FilterOrFinally); \
209 asm volatile ("#" : "=a"(plabel) : "p"(&&_SEH3$_l_BeforeTry), "p"(&&_SEH3$_l_HandlerTarget), "p"(&&_SEH3$_l_OnException), "p"(&&_SEH3$_l_FilterOrFinally) \
210 : "ebx", "ecx", "edx", "esi", "edi", "flags", "memory" ); \
211 goto _SEH3$_l_OnException;
212
213 #endif /* __clang__ */
214
215 /* Neither CLANG nor C++ support nested functions */
216 #if defined(__cplusplus) || defined(__clang__)
217
218 /* Use the global unregister function */
219 void
220 __attribute__((regparm(1)))
221 _SEH3$_AutoCleanup(
222 volatile SEH3$_REGISTRATION_FRAME *Frame);
223
224 /* These are only dummies here */
225 #define _SEH3$_DECLARE_CLEANUP_FUNC(_Name)
226 #define _SEH3$_DEFINE_CLEANUP_FUNC(_Name)
227 #define _SEH3$_DECLARE_FILTER_FUNC(_Name)
228 #define _SEH3$_DEFINE_DUMMY_FINALLY(_Name)
229
230 /* On invocation, the AllocaFrame field is loaded with the return esp value */
231 #define _SEH3$_NESTED_FUNC_RETURN(_Result) \
232 /* Restore esp and return to the caller */ \
233 asm volatile ("movl %[FixedEsp], %%esp\n\tret" \
234 : : "a" (_Result), [FixedEsp] "m" (_SEH3$_TrylevelFrame.AllocaFrame) : "ebx", "ecx", "edx", "esi", "edi", "flags", "memory")
235
236 /* The filter "function" */
237 #define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
238 { \
239 /* Evaluate and return the filter expression */ \
240 asm volatile ("#\n" : : : "eax", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"); \
241 _SEH3$_NESTED_FUNC_RETURN((expression)); \
242 }
243
244 #define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
245 { \
246 asm volatile ("#\n" : : : "eax", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"); \
247 /* This construct makes sure that the finally function returns */ \
248 /* a proper value at the end */ \
249 for (; ; (void)({_SEH3$_NESTED_FUNC_RETURN(0); 0;}))
250
251 #define _SEH3$_FILTER(_Filter, _FilterExpression) (&&_SEH3$_l_FilterOrFinally)
252 #define _SEH3$_FINALLY(_Finally) (&&_SEH3$_l_FilterOrFinally)
253
254 #define _SEH3$_DECLARE_EXCEPT_INTRINSICS()
255
256 /* Since we cannot use nested functions, we declare these globally as macros */
257 #define _abnormal_termination() (_SEH3$_TrylevelFrame.ExceptionPointers != 0)
258 #define _exception_code() (_SEH3$_TrylevelFrame.ExceptionCode)
259 #define _exception_info() (_SEH3$_TrylevelFrame.ExceptionPointers)
260
261 #else /* __cplusplus || __clang__ */
262
263 #define _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
264 inline __attribute__((always_inline, gnu_inline)) \
265 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionCode; }
266
267 /* On GCC the filter function is a nested function with __fastcall calling
268 convention. The eax register contains a base address the function uses
269 to address the callers stack frame. __fastcall is chosen, because it gives
270 us an effective was of passing one argument to the function, that we need
271 to tell the function in a first pass to return informtion about the frame
272 base address. Since this is something GCC chooses arbitrarily, we call
273 the function with an arbitrary base address in eax first and then use the
274 result to calculate the correct address for a second call to the function. */
275 #define _SEH3$_DECLARE_FILTER_FUNC(_Name) \
276 auto int __fastcall _Name(int Action)
277
278 #define _SEH3$_NESTED_FUNC_OPEN(_Name) \
279 int __fastcall _Name(int Action) \
280 { \
281 /* This is a fancy way to get information about the frame layout */ \
282 if (Action == 0) return (int)&_SEH3$_TrylevelFrame;
283
284 #define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
285 _SEH3$_NESTED_FUNC_OPEN(_Name) \
286 /* Declare the intrinsics for exception filters */ \
287 _Pragma("GCC diagnostic push") \
288 _Pragma("GCC diagnostic ignored \"-Wshadow\"") \
289 inline __attribute__((always_inline, gnu_inline)) \
290 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionCode; } \
291 inline __attribute__((always_inline, gnu_inline)) \
292 void * _exception_info() { return _SEH3$_TrylevelFrame.ExceptionPointers; } \
293 _Pragma("GCC diagnostic pop") \
294 \
295 /* Now handle the actual filter expression */ \
296 return (expression); \
297 }
298
299 #define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
300 _SEH3$_NESTED_FUNC_OPEN(_Name) \
301 /* Declare the intrinsics for the finally function */ \
302 inline __attribute__((always_inline, gnu_inline)) \
303 int _abnormal_termination() { return (_SEH3$_TrylevelFrame.ExceptionPointers != 0); } \
304 \
305 /* This construct makes sure that the finally function returns */ \
306 /* a proper value at the end */ \
307 for (; ; (void)({return 0; 0;}))
308
309 #define _SEH3$_FILTER(_Filter, _FilterExpression) \
310 (__builtin_constant_p(_FilterExpression) ? (void*)(unsigned long)(unsigned char)(unsigned long)(_FilterExpression) : _Filter)
311
312 #define _SEH3$_FINALLY(_Finally) (_Finally)
313
314 #define _SEH3$_DEFINE_DUMMY_FINALLY(_Name) \
315 auto inline __attribute__((always_inline,gnu_inline)) int _Name(int Action) { (void)Action; return 0; }
316
317 #define _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
318 auto inline __attribute__((always_inline,gnu_inline)) void _Name(volatile SEH3$_REGISTRATION_FRAME *p)
319
320 #define _SEH3$_DEFINE_CLEANUP_FUNC(_Name) \
321 _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
322 { \
323 (void)p; \
324 /* Unregister the frame */ \
325 if (_SEH3$_TryLevel == 1) _SEH3$_UnregisterFrame(&_SEH3$_TrylevelFrame); \
326 else _SEH3$_UnregisterTryLevel(&_SEH3$_TrylevelFrame); \
327 \
328 /* Invoke the finally function (an inline dummy in the __except case) */ \
329 _SEH3$_FinallyFunction(1); \
330 }
331
332 #endif /* __cplusplus || __clang__ */
333
334
335
336 #define _SEH3_TRY \
337 _SEH3$_PreventInlining(); \
338 /* Enter the outer scope */ \
339 do { \
340 /* Declare local labels */ \
341 __label__ _SEH3$_l_BeforeTry; \
342 __label__ _SEH3$_l_DoTry; \
343 __label__ _SEH3$_l_AfterTry; \
344 __label__ _SEH3$_l_EndTry; \
345 __label__ _SEH3$_l_HandlerTarget; \
346 __label__ _SEH3$_l_OnException; \
347 __label__ _SEH3$_l_BeforeFilterOrFinally; \
348 __label__ _SEH3$_l_FilterOrFinally; \
349 (void)&&_SEH3$_l_OnException; \
350 (void)&&_SEH3$_l_BeforeFilterOrFinally; \
351 (void)&&_SEH3$_l_FilterOrFinally; \
352 \
353 _Pragma("GCC diagnostic push") \
354 _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
355 \
356 /* Count the try level. Outside of any __try, _SEH3$_TryLevel is 0 */ \
357 enum { \
358 _SEH3$_PreviousTryLevel = _SEH3$_TryLevel, \
359 _SEH3$_TryLevel = _SEH3$_PreviousTryLevel + 1, \
360 }; \
361 \
362 /* Forward declaration of the auto cleanup function */ \
363 _SEH3$_DECLARE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
364 \
365 /* Allocate a registration frame */ \
366 volatile SEH3$_REGISTRATION_FRAME _SEH3$_AUTO_CLEANUP _SEH3$_TrylevelFrame; \
367 \
368 _Pragma("GCC diagnostic pop") \
369 \
370 goto _SEH3$_l_BeforeTry; \
371 /* Silence warning */ goto _SEH3$_l_AfterTry; \
372 \
373 _SEH3$_l_DoTry: \
374 do
375
376
377 #define _SEH3_EXCEPT(...) \
378 /* End the try block */ \
379 while (0); \
380 _SEH3$_l_AfterTry: (void)0; \
381 goto _SEH3$_l_EndTry; \
382 \
383 _SEH3$_l_BeforeTry: (void)0; \
384 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
385 \
386 _Pragma("GCC diagnostic push") \
387 _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
388 \
389 /* Forward declaration of the filter function */ \
390 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FilterFunction); \
391 \
392 /* Create a static data table that contains the jump target and filter function */ \
393 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { &&_SEH3$_l_HandlerTarget, _SEH3$_FILTER(&_SEH3$_FilterFunction, (__VA_ARGS__)), _SEH3$_TryLevel, _SEH3$_HANDLER_TYPE }; \
394 \
395 /* Register the registration record. */ \
396 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
397 else _SEH3$_RegisterTryLevel_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
398 \
399 /* Define an empty inline finally function */ \
400 _SEH3$_DEFINE_DUMMY_FINALLY(_SEH3$_FinallyFunction) \
401 \
402 /* Allow intrinsics for __except to be used */ \
403 _SEH3$_DECLARE_EXCEPT_INTRINSICS(); \
404 \
405 goto _SEH3$_l_DoTry; \
406 \
407 _SEH3$_l_BeforeFilterOrFinally: (void)0; \
408 /* Make sure the filter function doesn't use esp */ \
409 _SEH3$_EnforceFramePointer(); \
410 \
411 _SEH3$_l_FilterOrFinally: (void)0; \
412 /* Emit the filter function */ \
413 _SEH3$_DEFINE_FILTER_FUNC(_SEH3$_FilterFunction, (__VA_ARGS__)) \
414 \
415 _SEH3$_l_HandlerTarget: (void)0; \
416 _SEH3$_EnforceFramePointer(); \
417 \
418 if (1) \
419 { \
420 /* Prevent this block from being optimized away */ \
421 asm volatile ("#\n"); \
422 do
423
424
425 #define _SEH3_FINALLY \
426 /* End the try block */ \
427 while (0); \
428 _SEH3$_l_AfterTry: (void)0; \
429 /* Set ExceptionPointers to 0, this is used by _abnormal_termination() */ \
430 _SEH3$_TrylevelFrame.ExceptionPointers = 0; \
431 \
432 goto _SEH3$_l_EndTry; \
433 \
434 _SEH3$_l_BeforeTry: (void)0; \
435 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
436 \
437 _Pragma("GCC diagnostic push") \
438 _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
439 \
440 /* Forward declaration of the finally function */ \
441 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FinallyFunction); \
442 \
443 /* Create a static data table that contains the finally function */ \
444 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { 0, _SEH3$_FINALLY(&_SEH3$_FinallyFunction), _SEH3$_TryLevel, _SEH3$_HANDLER_TYPE }; \
445 \
446 /* Register the registration record. */ \
447 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
448 else _SEH3$_RegisterTryLevel_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
449 _SEH3$_TrylevelFrame.ExceptionPointers = (PSEH3$_EXCEPTION_POINTERS)1; \
450 \
451 goto _SEH3$_l_DoTry; \
452 \
453 _SEH3$_l_HandlerTarget: (void)0; \
454 _SEH3$_EnforceFramePointer(); \
455 \
456 _SEH3$_l_BeforeFilterOrFinally: (void)0; \
457 _SEH3$_EnforceFramePointer(); \
458 _SEH3$_l_FilterOrFinally: (void)0; \
459 _SEH3$_FINALLY_FUNC_OPEN(_SEH3$_FinallyFunction)
460
461
462 #define _SEH3_END \
463 while (0); \
464 }; \
465 goto _SEH3$_l_EndTry; \
466 \
467 _SEH3$_l_OnException: (void)0; \
468 /* Force GCC to create proper code pathes */ \
469 _SEH3$_SCARE_GCC(); \
470 \
471 _SEH3$_l_EndTry:(void)0; \
472 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
473 \
474 /* Implementation of the auto cleanup function */ \
475 _SEH3$_DEFINE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
476 \
477 _Pragma("GCC diagnostic pop") \
478 \
479 /* Close the outer scope */ \
480 } while (0);
481
482 #define _SEH3_LEAVE goto _SEH3$_l_AfterTry
483
484 #define _SEH3_VOLATILE volatile
485
486
487 #ifdef __cplusplus
488 }; // extern "C"
489 #endif