xref: /reactos/sdk/lib/pseh/i386/framebased-gcchack.c (revision c2c66aff)
1 /*
2 	Copyright (c) 2008 KJK::Hyperion
3 
4 	Permission is hereby granted, free of charge, to any person obtaining a
5 	copy of this software and associated documentation files (the "Software"),
6 	to deal in the Software without restriction, including without limitation
7 	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 	and/or sell copies of the Software, and to permit persons to whom the
9 	Software is furnished to do so, subject to the following conditions:
10 
11 	The above copyright notice and this permission notice shall be included in
12 	all copies or substantial portions of the Software.
13 
14 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 	DEALINGS IN THE SOFTWARE.
21 */
22 
23 #define _NTSYSTEM_ /* removes dllimport attribute from RtlUnwind */
24 
25 #define STRICT
26 #include <windef.h>
27 #include <stdarg.h>
28 
29 #include <pseh/pseh2.h>
30 #include <excpt.h>
31 #include <intrin.h>
32 
33 #ifndef EXCEPTION_EXIT_UNWIND
34 #define EXCEPTION_EXIT_UNWIND 4
35 #endif
36 
37 #ifndef EXCEPTION_UNWINDING
38 #define EXCEPTION_UNWINDING 2
39 #endif
40 
41 extern DECLSPEC_NORETURN int __SEH2Handle(void *, void *, void *, void *, void *, void *);
42 extern int __cdecl __SEH2FrameHandler(struct _EXCEPTION_RECORD *, void *, struct _CONTEXT *, void *);
43 extern int __cdecl __SEH2UnwindHandler(struct _EXCEPTION_RECORD *, void *, struct _CONTEXT *, void *);
44 
45 typedef struct __SEHTrampoline
46 {
47 	unsigned char STR_MovEcx;
48 	unsigned char * STR_Closure;
49 	unsigned char STR_Jmp;
50 	unsigned char * STR_Function;
51 }
52 __attribute__((packed))
53 _SEHTrampoline_t;
54 
55 FORCEINLINE
_SEHIsTrampoline(_SEHTrampoline_t * trampoline_)56 int _SEHIsTrampoline(_SEHTrampoline_t * trampoline_)
57 {
58 	return trampoline_->STR_MovEcx == 0xb9 && trampoline_->STR_Jmp == 0xe9;
59 }
60 
61 FORCEINLINE
_SEHFunctionFromTrampoline(_SEHTrampoline_t * trampoline_)62 void * _SEHFunctionFromTrampoline(_SEHTrampoline_t * trampoline_)
63 {
64 	return (int)(trampoline_ + 1) + trampoline_->STR_Function;
65 }
66 
67 FORCEINLINE
_SEHClosureFromTrampoline(_SEHTrampoline_t * trampoline_)68 void * _SEHClosureFromTrampoline(_SEHTrampoline_t * trampoline_)
69 {
70 	return trampoline_->STR_Closure;
71 }
72 
73 FORCEINLINE
_SEH2CurrentRegistration(void)74 _SEH2Registration_t * __cdecl _SEH2CurrentRegistration(void)
75 {
76 	return (_SEH2Registration_t *)__readfsdword(0);
77 }
78 
79 FORCEINLINE
__SEH2EnterFrame(_SEH2Registration_t * frame)80 void __cdecl __SEH2EnterFrame(_SEH2Registration_t * frame)
81 {
82 	frame->SER_Prev = _SEH2CurrentRegistration();
83 	__writefsdword(0, (unsigned long)frame);
84 }
85 
86 FORCEINLINE
__SEH2LeaveFrame(void)87 void __cdecl __SEH2LeaveFrame(void)
88 {
89 	__writefsdword(0, (unsigned long)_SEH2CurrentRegistration()->SER_Prev);
90 }
91 
92 FORCEINLINE
_SEH2GlobalUnwind(void * target)93 void _SEH2GlobalUnwind(void * target)
94 {
95 	__asm__ __volatile__
96 	(
97 		"push %%ebp\n\t"
98 		"push $0\n\t"
99 		"push $0\n\t"
100 		"push $Return%=\n\t"
101 		"push %[target]\n\t"
102 		"call %c[RtlUnwind]\n"
103 		"Return%=:\n\t"
104 		"pop %%ebp" :
105 		:
106 		[target] "g" (target), [RtlUnwind] "g" (&RtlUnwind) :
107 		"eax", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"
108 	);
109 }
110 
111 static
_SEH2Except(_SEH2Frame_t * frame,volatile _SEH2TryLevel_t * trylevel,struct _EXCEPTION_POINTERS * ep)112 __SEH_EXCEPT_RET _SEH2Except(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel, struct _EXCEPTION_POINTERS * ep)
113 {
114 	void * filter = trylevel->ST_Filter;
115 	void * context = NULL;
116 	__SEH_EXCEPT_RET ret;
117 
118 	if(filter == (void *)0)
119 		return 0;
120 
121 	if(filter == (void *)1)
122 		return 1;
123 
124 	if(filter == (void *)-1)
125 		return -1;
126 
127 	if(_SEHIsTrampoline((_SEHTrampoline_t *)filter))
128 	{
129 		context = _SEHClosureFromTrampoline((_SEHTrampoline_t *)filter);
130 		filter = _SEHFunctionFromTrampoline((_SEHTrampoline_t *)filter);
131 	}
132 
133 	__asm__ __volatile__
134 	(
135 		"push %[ep]\n\t"
136 		"push %[frame]\n\t"
137 		"call *%[filter]\n\t"
138 		"pop %%edx\n\t"
139 		"pop %%edx" :
140 		[ret] "=a" (ret) :
141 		"c" (context), [filter] "r" (filter), [frame] "r" (frame), [ep] "r" (ep) :
142 		"edx", "flags", "memory"
143 	);
144 
145 	return ret;
146 }
147 
148 static
_SEH2Finally(_SEH2Frame_t * frame,volatile _SEH2TryLevel_t * trylevel)149 void _SEH2Finally(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel)
150 {
151 	if(trylevel->ST_Filter == NULL && trylevel->ST_Body != NULL)
152 	{
153 		void * body = trylevel->ST_Body;
154 		void * context = NULL;
155 
156 		if(_SEHIsTrampoline((_SEHTrampoline_t *)body))
157 		{
158 			context = _SEHClosureFromTrampoline((_SEHTrampoline_t *)body);
159 			body = _SEHFunctionFromTrampoline((_SEHTrampoline_t *)body);
160 		}
161 
162 		__asm__ __volatile__("call *%1" : : "c" (context), "r" (body) : "eax", "edx", "flags", "memory");
163 	}
164 }
165 
166 typedef struct __SEH2UnwindFrame
167 {
168 	_SEH2Registration_t SUF_Registration;
169 	_SEH2Frame_t * SUF_Frame;
170 	volatile _SEH2TryLevel_t * SUF_TargetTryLevel;
171 }
172 _SEH2UnwindFrame_t;
173 
174 static void _SEH2LocalUnwind(_SEH2Frame_t *, volatile _SEH2TryLevel_t *);
175 
176 extern
_SEH2UnwindHandler(struct _EXCEPTION_RECORD * ExceptionRecord,void * EstablisherFrame,struct _CONTEXT * ContextRecord,void * DispatcherContext)177 int __cdecl _SEH2UnwindHandler
178 (
179 	struct _EXCEPTION_RECORD * ExceptionRecord,
180 	void * EstablisherFrame,
181 	struct _CONTEXT * ContextRecord,
182 	void * DispatcherContext
183 )
184 {
185 	if(ExceptionRecord->ExceptionFlags & (EXCEPTION_EXIT_UNWIND | EXCEPTION_UNWINDING))
186 	{
187 		_SEH2UnwindFrame_t * unwindframe = CONTAINING_RECORD(EstablisherFrame, _SEH2UnwindFrame_t, SUF_Registration);
188 		_SEH2LocalUnwind(unwindframe->SUF_Frame, unwindframe->SUF_TargetTryLevel);
189 		*((void **)DispatcherContext) = EstablisherFrame;
190 		return ExceptionCollidedUnwind;
191 	}
192 
193 	return ExceptionContinueSearch;
194 }
195 
196 static
_SEH2LocalUnwind(_SEH2Frame_t * frame,volatile _SEH2TryLevel_t * dsttrylevel)197 void _SEH2LocalUnwind(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * dsttrylevel)
198 {
199 	volatile _SEH2TryLevel_t * trylevel;
200 	_SEH2UnwindFrame_t unwindframe;
201 
202 	unwindframe.SUF_Frame = frame;
203 	unwindframe.SUF_TargetTryLevel = dsttrylevel;
204 
205 	unwindframe.SUF_Registration.SER_Handler = &__SEH2UnwindHandler;
206 	__SEH2EnterFrame(&unwindframe.SUF_Registration);
207 
208 	for(trylevel = frame->SF_TopTryLevel; trylevel && trylevel != dsttrylevel; trylevel = trylevel->ST_Next)
209 	{
210 		frame->SF_TopTryLevel = trylevel->ST_Next;
211 		_SEH2Finally(frame, trylevel);
212 	}
213 
214 	__SEH2LeaveFrame();
215 }
216 
217 static DECLSPEC_NORETURN
_SEH2Handle(_SEH2Frame_t * frame,volatile _SEH2TryLevel_t * trylevel)218 void _SEH2Handle(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel)
219 {
220 	volatile _SEH2HandleTryLevel_t * fulltrylevel = CONTAINING_RECORD(trylevel, _SEH2HandleTryLevel_t, SHT_Common);
221 
222 	_SEH2GlobalUnwind(frame);
223 	_SEH2LocalUnwind(frame, &fulltrylevel->SHT_Common);
224 	frame->SF_TopTryLevel = fulltrylevel->SHT_Common.ST_Next;
225 
226 	__SEH2Handle
227 	(
228 		fulltrylevel->SHT_Common.ST_Body,
229 		fulltrylevel->SHT_Esp,
230 		fulltrylevel->SHT_Ebp,
231 		fulltrylevel->SHT_Ebx,
232 		fulltrylevel->SHT_Esi,
233 		fulltrylevel->SHT_Edi
234 	);
235 }
236 
237 extern
_SEH2FrameHandler(struct _EXCEPTION_RECORD * ExceptionRecord,void * EstablisherFrame,struct _CONTEXT * ContextRecord,void * DispatcherContext)238 int __cdecl _SEH2FrameHandler
239 (
240 	struct _EXCEPTION_RECORD * ExceptionRecord,
241 	void * EstablisherFrame,
242 	struct _CONTEXT * ContextRecord,
243 	void * DispatcherContext
244 )
245 {
246 	_SEH2Frame_t * frame;
247 
248 	frame = EstablisherFrame;
249 
250 	/* Unwinding */
251 	if(ExceptionRecord->ExceptionFlags & (EXCEPTION_EXIT_UNWIND | EXCEPTION_UNWINDING))
252 	{
253 		_SEH2LocalUnwind(frame, NULL);
254 	}
255 	/* Handling */
256 	else
257 	{
258 		int ret = 0;
259 		volatile _SEH2TryLevel_t * trylevel;
260 		EXCEPTION_POINTERS ep;
261 
262 		ep.ExceptionRecord = ExceptionRecord;
263 		ep.ContextRecord = ContextRecord;
264 
265 		frame->SF_Code = ExceptionRecord->ExceptionCode;
266 
267 		for(trylevel = frame->SF_TopTryLevel; trylevel != NULL; trylevel = trylevel->ST_Next)
268 		{
269 			ret = _SEH2Except(frame, trylevel, &ep);
270 
271 			if(ret < 0)
272 				return ExceptionContinueExecution;
273 			else if(ret > 0)
274 				_SEH2Handle(frame, trylevel);
275 		}
276 	}
277 
278 	return ExceptionContinueSearch;
279 }
280 
281 extern
_SEH2EnterFrame(_SEH2Frame_t * frame)282 void __cdecl _SEH2EnterFrame(_SEH2Frame_t * frame)
283 {
284 	frame->SF_Registration.SER_Handler = __SEH2FrameHandler;
285 	frame->SF_Code = 0;
286 	__SEH2EnterFrame(&frame->SF_Registration);
287 }
288 
289 extern
_SEH2EnterFrameAndTrylevel(_SEH2Frame_t * frame,volatile _SEH2TryLevel_t * trylevel)290 int __cdecl _SEH2EnterFrameAndTrylevel(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel)
291 {
292 	frame->SF_TopTryLevel = trylevel;
293 	_SEH2EnterFrame(frame);
294 	return 0;
295 }
296 
297 extern
_SEH2LeaveFrame(void)298 void __cdecl _SEH2LeaveFrame(void)
299 {
300 	__SEH2LeaveFrame();
301 }
302 
303 extern
_SEH2Return(void)304 void __cdecl _SEH2Return(void)
305 {
306 	_SEH2LocalUnwind(CONTAINING_RECORD(_SEH2CurrentRegistration(), _SEH2Frame_t, SF_Registration), NULL);
307 	_SEH2LeaveFrame();
308 }
309 
310 /* EOF */
311