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