[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$_Unregister)))
115
116 /* CLANG specific definitions! */
117 #ifdef __clang__
118
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")
121
122 /* CLANG doesn't have asm goto! */
123 #define _SEH3$_ASM_GOTO(_Label, ...)
124
125 int
126 __attribute__((regparm(3)))
127 __attribute__((returns_twice))
128 _SEH3$_RegisterFrameWithNonVolatiles(
129 volatile SEH3$_REGISTRATION_FRAME* RegistrationFrame,
130 const SEH3$_SCOPE_TABLE* ScopeTable,
131 void* AllocaFrame);
132
133 #define _SEH3$_RegisterFrame_(_TrylevelFrame, _DataTable) \
134 do { \
135 int result = _SEH3$_RegisterFrameWithNonVolatiles(_TrylevelFrame, _DataTable, __builtin_alloca(0)); \
136 if (__builtin_expect(result != 0, 0)) \
137 { \
138 if (result == 1) goto _SEH3$_l_FilterOrFinally; \
139 if (result == 2) goto _SEH3$_l_HandlerTarget; \
140 goto _SEH3$_l_BeforeFilterOrFinally; \
141 } \
142 } while(0)
143
144 int
145 __attribute__((regparm(3)))
146 __attribute__((returns_twice))
147 _SEH3$_RegisterTryLevelWithNonVolatiles(
148 volatile SEH3$_REGISTRATION_FRAME* RegistrationFrame,
149 const SEH3$_SCOPE_TABLE* ScopeTable,
150 void* AllocaFrame);
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(_Label, ...) asm goto ("#\n" : : : "memory", ## __VA_ARGS__ : _Label)
171
172 #ifdef __cplusplus
173 #define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
174 asm goto ("leal %0, %%eax\n" \
175 "leal %1, %%edx\n" \
176 "call " #_Function "WithStackLayout\n" \
177 : \
178 : "m" (*(_TrylevelFrame)), "m" (*(_DataTable)), "c"(__builtin_alloca(0)) \
179 : "eax", "edx", "memory" \
180 : _SEH3$_l_HandlerTarget, _SEH3$_l_FilterOrFinally)
181
182 #else
183 #define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
184 asm goto ("leal %0, %%eax\n" \
185 "leal %1, %%edx\n" \
186 "call " #_Function "\n" \
187 : \
188 : "m" (*(_TrylevelFrame)), "m" (*(_DataTable)) \
189 : "eax", "edx", "ecx", "memory" \
190 : _SEH3$_l_HandlerTarget)
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); \
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;
211
212 #endif /* __clang__ */
213
214 /* Neither CLANG nor C++ support nested functions */
215 #if defined(__cplusplus) || defined(__clang__)
216
217 /* Use the global unregister function */
218 void
219 __attribute__((regparm(1)))
220 _SEH3$_Unregister(
221 volatile SEH3$_REGISTRATION_FRAME *Frame);
222
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)
228
229 /* The "nested" functions are a piece of code with a ret instruction at the end */
230 #define _SEH3$_NESTED_FUNC_OPEN() \
231 { \
232 int _SEH3$_Result = 0; \
233
234 /* On invocation, the AllocaFrame field is loaded with the return esp value */
235 #define _SEH3$_NESTED_FUNC_RETURN() \
236 /* Restore esp and return to the caller */ \
237 asm volatile ("movl %[FixedEsp], %%esp\nret\n" \
238 : : "a"(_SEH3$_Result), [FixedEsp]"m"(_SEH3$_TrylevelFrame.AllocaFrame) : "memory")
239
240 #define _SEH3$_NESTED_FUNC_CLOSE() \
241 /* Return to the caller */ \
242 _SEH3$_NESTED_FUNC_RETURN(); \
243 }
244
245 /* The filter function */
246 #define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
247 _SEH3$_NESTED_FUNC_OPEN() \
248 { \
249 /* Evaluate the filter expression */ \
250 _SEH3$_Result = (expression); \
251 } \
252 _SEH3$_NESTED_FUNC_CLOSE()
253
254 #define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
255 _SEH3$_NESTED_FUNC_OPEN() \
256 /* This construct makes sure that the finally function returns */ \
257 /* a proper value at the end */ \
258 for (; ; (void)({_SEH3$_NESTED_FUNC_RETURN(); 0;}))
259
260 #define _SEH3$_FILTER(_Filter, _FilterExpression) (&&_SEH3$_l_FilterOrFinally)
261 #define _SEH3$_FINALLY(_Finally) (&&_SEH3$_l_FilterOrFinally)
262
263 #define _SEH3$_DECLARE_EXCEPT_INTRINSICS()
264
265 /* Since we cannot use nested functions, we declare these globally as macros */
266 #define _abnormal_termination() (_SEH3$_TrylevelFrame.ScopeTable != 0)
267 #define _exception_code() (_SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode)
268 #define _exception_info() (_SEH3$_TrylevelFrame.ExceptionPointers)
269
270 #else /* __cplusplus || __clang__ */
271
272 #define _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
273 inline __attribute__((always_inline, gnu_inline)) \
274 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; }
275
276 /* On GCC the filter function is a nested function with __fastcall calling
277 convention. The eax register contains a base address the function uses
278 to address the callers stack frame. __fastcall is chosen, because it gives
279 us an effective was of passing one argument to the function, that we need
280 to tell the function in a first pass to return informtion about the frame
281 base address. Since this is something GCC chooses arbitrarily, we call
282 the function with an arbitrary base address in eax first and then use the
283 result to calculate the correct address for a second call to the function. */
284 #define _SEH3$_DECLARE_FILTER_FUNC(_Name) \
285 auto int __fastcall _Name(int Action)
286
287 #define _SEH3$_NESTED_FUNC_OPEN(_Name) \
288 int __fastcall _Name(int Action) \
289 { \
290 /* This is a fancy way to get information about the frame layout */ \
291 if (Action == 0) return (int)&_SEH3$_TrylevelFrame;
292
293 #define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
294 _SEH3$_NESTED_FUNC_OPEN(_Name) \
295 /* Declare the intrinsics for exception filters */ \
296 inline __attribute__((always_inline, gnu_inline)) \
297 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; } \
298 inline __attribute__((always_inline, gnu_inline)) \
299 void * _exception_info() { return _SEH3$_TrylevelFrame.ExceptionPointers; } \
300 \
301 /* Now handle the actual filter expression */ \
302 return (expression); \
303 }
304
305 #define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
306 _SEH3$_NESTED_FUNC_OPEN(_Name) \
307 /* Declare the intrinsics for the finally function */ \
308 inline __attribute__((always_inline, gnu_inline)) \
309 int _abnormal_termination() { return (_SEH3$_TrylevelFrame.ScopeTable != 0); } \
310 \
311 /* This construct makes sure that the finally function returns */ \
312 /* a proper value at the end */ \
313 for (; ; (void)({return 0; 0;}))
314
315 #define _SEH3$_FILTER(_Filter, _FilterExpression) \
316 (__builtin_constant_p(_FilterExpression) ? (void*)(unsigned long)(unsigned char)(unsigned long)(_FilterExpression) : _Filter)
317
318 #define _SEH3$_FINALLY(_Finally) (_Finally)
319
320 #define _SEH3$_DEFINE_DUMMY_FINALLY(_Name) \
321 auto inline __attribute__((always_inline,gnu_inline)) int _Name(int Action) { (void)Action; return 0; }
322
323 #define _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
324 auto inline __attribute__((always_inline,gnu_inline)) void _Name(volatile SEH3$_REGISTRATION_FRAME *p)
325
326 #define _SEH3$_DEFINE_CLEANUP_FUNC(_Name) \
327 _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
328 { \
329 (void)p; \
330 /* Unregister the frame */ \
331 if (_SEH3$_TryLevel == 1) _SEH3$_UnregisterFrame(&_SEH3$_TrylevelFrame); \
332 else _SEH3$_UnregisterTryLevel(&_SEH3$_TrylevelFrame); \
333 \
334 /* Invoke the finally function (an inline dummy in the __except case) */ \
335 _SEH3$_FinallyFunction(1); \
336 }
337
338 #endif /* __cplusplus || __clang__ */
339
340
341
342 #define _SEH3_TRY \
343 _SEH3$_PreventInlining(); \
344 /* Enter the outer scope */ \
345 do { \
346 /* Declare local labels */ \
347 __label__ _SEH3$_l_BeforeTry; \
348 __label__ _SEH3$_l_DoTry; \
349 __label__ _SEH3$_l_AfterTry; \
350 __label__ _SEH3$_l_EndTry; \
351 __label__ _SEH3$_l_HandlerTarget; \
352 __label__ _SEH3$_l_OnException; \
353 __label__ _SEH3$_l_BeforeFilterOrFinally; \
354 __label__ _SEH3$_l_FilterOrFinally; \
355 (void)&&_SEH3$_l_OnException; \
356 (void)&&_SEH3$_l_BeforeFilterOrFinally; \
357 (void)&&_SEH3$_l_FilterOrFinally; \
358 \
359 /* Count the try level. Outside of any __try, _SEH3$_TryLevel is 0 */ \
360 enum { \
361 _SEH3$_PreviousTryLevel = _SEH3$_TryLevel, \
362 _SEH3$_TryLevel = _SEH3$_PreviousTryLevel + 1, \
363 }; \
364 \
365 /* Forward declaration of the auto cleanup function */ \
366 _SEH3$_DECLARE_CLEANUP_FUNC(_SEH3$_Unregister); \
367 \
368 /* Allocate a registration frame */ \
369 volatile SEH3$_REGISTRATION_FRAME _SEH3$_AUTO_CLEANUP _SEH3$_TrylevelFrame; \
370 \
371 goto _SEH3$_l_BeforeTry; \
372 /* Silence warning */ goto _SEH3$_l_AfterTry; \
373 \
374 _SEH3$_l_DoTry: \
375 do
376
377
378 #define _SEH3_EXCEPT(...) \
379 /* End the try block */ \
380 while (0); \
381 _SEH3$_l_AfterTry: (void)0; \
382 goto _SEH3$_l_EndTry; \
383 \
384 _SEH3$_l_BeforeTry: (void)0; \
385 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
386 \
387 /* Forward declaration of the filter function */ \
388 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FilterFunction); \
389 \
390 /* Create a static data table that contains the jump target and filter function */ \
391 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { &&_SEH3$_l_HandlerTarget, _SEH3$_FILTER(&_SEH3$_FilterFunction, (__VA_ARGS__)), _SEH3$_TryLevel, _SEH3$_HANDLER_TYPE }; \
392 \
393 /* Register the registration record. */ \
394 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
395 else _SEH3$_RegisterTryLevel_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
396 \
397 /* Define an empty inline finally function */ \
398 _SEH3$_DEFINE_DUMMY_FINALLY(_SEH3$_FinallyFunction) \
399 \
400 /* Allow intrinsics for __except to be used */ \
401 _SEH3$_DECLARE_EXCEPT_INTRINSICS(); \
402 \
403 goto _SEH3$_l_DoTry; \
404 \
405 _SEH3$_l_BeforeFilterOrFinally: (void)0; \
406 /* Make sure the filter function doesn't use esp */ \
407 _SEH3$_EnforceFramePointer(); \
408 \
409 _SEH3$_l_FilterOrFinally: (void)0; \
410 /* Emit the filter function */ \
411 _SEH3$_DEFINE_FILTER_FUNC(_SEH3$_FilterFunction, (__VA_ARGS__)) \
412 \
413 _SEH3$_l_HandlerTarget: (void)0; \
414 _SEH3$_EnforceFramePointer(); \
415 \
416 if (1) \
417 { \
418 /* Prevent this block from being optimized away */ \
419 asm volatile ("#\n"); \
420 do
421
422
423 #define _SEH3_FINALLY \
424 /* End the try block */ \
425 while (0); \
426 _SEH3$_l_AfterTry: (void)0; \
427 /* Set ScopeTable to 0, this is used by _abnormal_termination() */ \
428 _SEH3$_TrylevelFrame.ScopeTable = 0; \
429 \
430 goto _SEH3$_l_EndTry; \
431 \
432 _SEH3$_l_BeforeTry: (void)0; \
433 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
434 \
435 /* Forward declaration of the finally function */ \
436 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FinallyFunction); \
437 \
438 /* Create a static data table that contains the finally function */ \
439 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { 0, _SEH3$_FINALLY(&_SEH3$_FinallyFunction), _SEH3$_TryLevel, _SEH3$_HANDLER_TYPE }; \
440 \
441 /* Register the registration record. */ \
442 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
443 else _SEH3$_RegisterTryLevel_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
444 \
445 goto _SEH3$_l_DoTry; \
446 \
447 _SEH3$_l_HandlerTarget: (void)0; \
448 _SEH3$_EnforceFramePointer(); \
449 \
450 _SEH3$_l_BeforeFilterOrFinally: (void)0; \
451 _SEH3$_EnforceFramePointer(); \
452 _SEH3$_l_FilterOrFinally: (void)0; \
453 _SEH3$_FINALLY_FUNC_OPEN(_SEH3$_FinallyFunction)
454
455
456 #define _SEH3_END \
457 while (0); \
458 }; \
459 goto _SEH3$_l_EndTry; \
460 \
461 _SEH3$_l_OnException: (void)0; \
462 /* Force GCC to create proper code pathes */ \
463 _SEH3$_SCARE_GCC(); \
464 \
465 _SEH3$_l_EndTry:(void)0; \
466 _SEH3$_ASM_GOTO(_SEH3$_l_OnException); \
467 \
468 /* Implementation of the auto cleanup function */ \
469 _SEH3$_DEFINE_CLEANUP_FUNC(_SEH3$_Unregister); \
470 \
471 /* Close the outer scope */ \
472 } while (0);
473
474 #define _SEH3_LEAVE goto _SEH3$_l_AfterTry
475
476 #define _SEH3_VOLATILE volatile
477
478
479 #ifdef __cplusplus
480 }; // extern "C"
481 #endif