[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 /* CLANG must safe non-volatiles, because it uses a return-twice algorithm */
16 #if defined(__clang__) && !defined(_SEH3$_FRAME_ALL_NONVOLATILES)
17 #define _SEH3$_FRAME_ALL_NONVOLATILES 1
18 #endif
19
20 typedef struct _SEH3$_SCOPE_TABLE
21 {
22 void *Target;
23 void *Filter;
24 } SEH3$_SCOPE_TABLE, *PSEH3$_SCOPE_TABLE;
25
26 typedef struct _SEH3$_EXCEPTION_POINTERS
27 {
28 struct _EXCEPTION_RECORD *ExceptionRecord;
29 struct _CONTEXT *ContextRecord;
30 } SEH3$_EXCEPTION_POINTERS, *PSEH3$_EXCEPTION_POINTERS;
31
32 typedef struct _SEH3$_REGISTRATION_FRAME
33 {
34 /* First the Windows base record. Don't move this! */
35 struct _SEH3$_REGISTRATION_FRAME *Next;
36 void *Handler;
37
38 /* Points to the end of the internal registration chain */
39 struct _SEH3$_REGISTRATION_FRAME *EndOfChain;
40
41 /* Pointer to the static scope table */
42 PSEH3$_SCOPE_TABLE ScopeTable;
43
44 /* Except handler stores pointer to exception pointers here */
45 PSEH3$_EXCEPTION_POINTERS volatile ExceptionPointers;
46
47 /* Registers that we need to save */
48 unsigned long Esp;
49 unsigned long Ebp;
50 #ifdef _SEH3$_FRAME_ALL_NONVOLATILES
51 unsigned long Ebx;
52 unsigned long Esi;
53 unsigned long Edi;
54 #endif
55 } SEH3$_REGISTRATION_FRAME ,*PSEH3$_REGISTRATION_FRAME;
56
57 /* Prevent gcc from inlining functions that use SEH. */
58 static inline __attribute__((always_inline)) __attribute__((returns_twice)) void _SEH3$_PreventInlining() {}
59
60 extern inline __attribute__((always_inline,gnu_inline))
61 void _SEH3$_UnregisterFrame(volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame)
62 {
63 asm volatile ("movl %k[NewHead], %%fs:0"
64 : : [NewHead] "ir" (RegistrationFrame->Next) : "memory");
65 }
66
67 extern inline __attribute__((always_inline,gnu_inline))
68 void _SEH3$_UnregisterTryLevel(
69 volatile SEH3$_REGISTRATION_FRAME *TrylevelFrame)
70 {
71 volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame;
72 asm volatile ("movl %%fs:0, %k[RegistrationFrame]"
73 : [RegistrationFrame] "=r" (RegistrationFrame) : );
74 RegistrationFrame->EndOfChain = TrylevelFrame->Next;
75 }
76
77 enum
78 {
79 _SEH3$_TryLevel = 0,
80 };
81
82 /* These are global dummy definitions, that get overwritten in the local context of __finally / __except blocks */
83 int __cdecl __attribute__((error ("Can only be used inside a __finally block."))) _abnormal_termination(void);
84 unsigned long __cdecl __attribute__((error("Can only be used inside an exception filter or __except block."))) _exception_code(void);
85 void * __cdecl __attribute__((error("Can only be used inside an exception filter."))) _exception_info(void);
86
87 /* Define the registers that get clobbered, when reaching the __except block.
88 We specify ebp on optimized builds without frame pointer, since it will be
89 used by GCC as a general purpose register then. */
90 #if defined(__OPTIMIZE__) && defined(_ALLOW_OMIT_FRAME_POINTER)
91 #define _SEH3$_CLOBBER_ON_EXCEPTION "ebp", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"
92 #else
93 #define _SEH3$_CLOBBER_ON_EXCEPTION "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"
94 #endif
95
96 /* This attribute allows automatic cleanup of the registered frames */
97 #define _SEH3$_AUTO_CLEANUP __attribute__((cleanup(_SEH3$_AutoCleanup)))
98
99 #define _SEH3$_ASM_GOTO(_Asm, _Label, ...) asm goto (_Asm : : : "memory", ## __VA_ARGS__ : _Label)
100
101 #define _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
102 inline __attribute__((always_inline, gnu_inline)) \
103 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; }
104
105 /* This is an asm wrapper around _SEH3$_RegisterFrame */
106 #define _SEH3$_RegisterFrame(_TrylevelFrame, _DataTable, _Target) \
107 asm goto ("leal %0, %%edx\n" \
108 "call __SEH3$_RegisterFrame\n" \
109 : \
110 : "m" (*(_TrylevelFrame)), "a" (_DataTable) \
111 : "ecx", "edx", "memory" \
112 : _Target)
113
114 /* This is an asm wrapper around _SEH3$_EnterTryLevel */
115 #define _SEH3$_RegisterTryLevel(_TrylevelFrame, _DataTable, _Target) \
116 asm goto ("leal %0, %%edx\n" \
117 "call __SEH3$_RegisterTryLevel\n" \
118 : \
119 : "m" (*(_TrylevelFrame)), "a" (_DataTable) \
120 : "ecx", "edx", "memory" \
121 : _Target)
122
123 /* On GCC the filter function is a nested function with __fastcall calling
124 convention. The eax register contains a base address the function uses
125 to address the callers stack frame. __fastcall is chosen, because it gives
126 us an effective was of passing one argument to the function, that we need
127 to tell the function in a first pass to return informtion about the frame
128 base address. Since this is something GCC chooses arbitrarily, we call
129 the function with an arbitrary base address in eax first and then use the
130 result to calculate the correct address for a second call to the function. */
131 #define _SEH3$_DECLARE_FILTER_FUNC(_Name) \
132 auto int __fastcall _Name(int Action)
133
134 #define _SEH3$_NESTED_FUNC_OPEN(_Name) \
135 int __fastcall _Name(int Action) \
136 { \
137 /* This is a fancy way to get information about the frame layout */ \
138 if (Action == 0) return (int)&_SEH3$_TrylevelFrame;
139
140 #define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
141 _SEH3$_NESTED_FUNC_OPEN(_Name) \
142 /* Declare the intrinsics for exception filters */ \
143 inline __attribute__((always_inline, gnu_inline)) \
144 unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; } \
145 inline __attribute__((always_inline, gnu_inline)) \
146 void * _exception_info() { return _SEH3$_TrylevelFrame.ExceptionPointers; } \
147 \
148 /* Now handle the actual filter expression */ \
149 return (expression); \
150 }
151
152 #define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
153 _SEH3$_NESTED_FUNC_OPEN(_Name) \
154 /* Declare the intrinsics for the finally function */ \
155 inline __attribute__((always_inline, gnu_inline)) \
156 int _abnormal_termination() { return (_SEH3$_TrylevelFrame.ScopeTable != 0); }
157
158 #define _SEH3$_FILTER(_Filter, _FilterExpression) \
159 (__builtin_constant_p(_FilterExpression) ? (void*)(unsigned long)(unsigned char)(unsigned long)(_FilterExpression) : _Filter)
160
161 #define _SEH3$_DEFINE_DUMMY_FINALLY(_Name) \
162 auto inline __attribute__((always_inline,gnu_inline)) int _Name(int Action) { (void)Action; return 0; }
163
164 #define _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
165 auto inline __attribute__((always_inline,gnu_inline)) void _Name(volatile SEH3$_REGISTRATION_FRAME *p)
166
167 #define _SEH3$_DEFINE_CLEANUP_FUNC(_Name) \
168 _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
169 { \
170 (void)p; \
171 /* Unregister the frame */ \
172 if (_SEH3$_TryLevel == 1) _SEH3$_UnregisterFrame(&_SEH3$_TrylevelFrame); \
173 else _SEH3$_UnregisterTryLevel(&_SEH3$_TrylevelFrame); \
174 \
175 /* Invoke the finally function (an inline dummy in the __except case) */ \
176 _SEH3$_FinallyFunction(1); \
177 }
178
179 /* This construct scares GCC so much, that it will stop moving code
180 around into places that are never executed. */
181 #define _SEH3$_SCARE_GCC() \
182 void *plabel; \
183 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_BeforeTry); \
184 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_HandlerTarget); \
185 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
186 asm volatile ("#" : "=a"(plabel) : "p"(&&_SEH3$_l_BeforeTry), "p"(&&_SEH3$_l_HandlerTarget), "p"(&&_SEH3$_l_OnException) \
187 : _SEH3$_CLOBBER_ON_EXCEPTION ); \
188 goto _SEH3$_l_OnException;
189
190
191 #define _SEH3_TRY \
192 _SEH3$_PreventInlining(); \
193 /* Enter the outer scope */ \
194 do { \
195 /* Declare local labels */ \
196 __label__ _SEH3$_l_BeforeTry; \
197 __label__ _SEH3$_l_DoTry; \
198 __label__ _SEH3$_l_AfterTry; \
199 __label__ _SEH3$_l_EndTry; \
200 __label__ _SEH3$_l_HandlerTarget; \
201 __label__ _SEH3$_l_OnException; \
202 \
203 /* Count the try level. Outside of any __try, _SEH3$_TryLevel is 0 */ \
204 enum { \
205 _SEH3$_PreviousTryLevel = _SEH3$_TryLevel, \
206 _SEH3$_TryLevel = _SEH3$_PreviousTryLevel + 1, \
207 }; \
208 \
209 /* Forward declaration of the auto cleanup function */ \
210 _SEH3$_DECLARE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
211 \
212 /* Allocate a registration frame */ \
213 volatile SEH3$_REGISTRATION_FRAME _SEH3$_AUTO_CLEANUP _SEH3$_TrylevelFrame; \
214 \
215 goto _SEH3$_l_BeforeTry; \
216 /* Silence warning */ goto _SEH3$_l_AfterTry; \
217 \
218 _SEH3$_l_DoTry: \
219 do
220
221
222 #define _SEH3_EXCEPT(...) \
223 /* End the try block */ \
224 while (0); \
225 _SEH3$_l_AfterTry: (void)0; \
226 goto _SEH3$_l_EndTry; \
227 \
228 _SEH3$_l_BeforeTry: (void)0; \
229 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
230 \
231 /* Forward declaration of the filter function */ \
232 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FilterFunction); \
233 \
234 /* Create a static data table that contains the jump target and filter function */ \
235 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { &&_SEH3$_l_HandlerTarget, _SEH3$_FILTER(&_SEH3$_FilterFunction, (__VA_ARGS__)) }; \
236 \
237 /* Register the registration record. */ \
238 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
239 else _SEH3$_RegisterTryLevel(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
240 \
241 /* Emit the filter function */ \
242 _SEH3$_DEFINE_FILTER_FUNC(_SEH3$_FilterFunction, (__VA_ARGS__)) \
243 \
244 /* Define an empty inline finally function */ \
245 _SEH3$_DEFINE_DUMMY_FINALLY(_SEH3$_FinallyFunction) \
246 \
247 /* Allow intrinsics for __except to be used */ \
248 _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
249 \
250 goto _SEH3$_l_DoTry; \
251 \
252 _SEH3$_l_HandlerTarget: (void)0; \
253 \
254 if (1) \
255 { \
256 /* Prevent this block from being optimized away */ \
257 asm volatile ("#\n"); \
258 do
259
260
261 #define _SEH3_FINALLY \
262 /* End the try block */ \
263 while (0); \
264 _SEH3$_l_AfterTry: (void)0; \
265 /* Set ScopeTable to 0, this is used by _abnormal_termination() */ \
266 _SEH3$_TrylevelFrame.ScopeTable = 0; \
267 \
268 goto _SEH3$_l_EndTry; \
269 \
270 _SEH3$_l_BeforeTry: (void)0; \
271 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
272 \
273 /* Forward declaration of the finally function */ \
274 _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FinallyFunction); \
275 \
276 /* Create a static data table that contains the finally function */ \
277 static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { 0, &_SEH3$_FinallyFunction }; \
278 \
279 /* Register the registration record. */ \
280 if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
281 else _SEH3$_RegisterTryLevel(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
282 \
283 goto _SEH3$_l_DoTry; \
284 \
285 _SEH3$_l_HandlerTarget: (void)0; \
286 \
287 _SEH3$_FINALLY_FUNC_OPEN(_SEH3$_FinallyFunction) \
288 /* This construct makes sure that the finally function returns */ \
289 /* a proper value at the end */ \
290 for (; ; (void)({return 0; 0;}))
291
292
293 #define _SEH3_END \
294 while (0); \
295 }; \
296 goto _SEH3$_l_EndTry; \
297 \
298 _SEH3$_l_OnException: (void)0; \
299 /* Force GCC to create proper code pathes */ \
300 _SEH3$_SCARE_GCC() \
301 \
302 _SEH3$_l_EndTry:(void)0; \
303 _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
304 \
305 /* Implementation of the auto cleanup function */ \
306 _SEH3$_DEFINE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
307 \
308 /* Close the outer scope */ \
309 } while (0);
310
311 #define _SEH3_LEAVE goto _SEH3$_l_AfterTry
312
313 #define _SEH3_VOLATILE volatile