• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #define WIN32_LEAN_AND_MEAN
27 #include <windows.h>
28 
29 #include <pseh/pseh2-common.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"
98 		"push $0\n"
99 		"push $0\n"
100 		"push $Return%=\n"
101 		"push %[target]\n"
102 		"call %c[RtlUnwind]\n"
103 		"Return%=: pop %%ebp\n" :
104 		:
105 		[target] "g" (target), [RtlUnwind] "g" (&RtlUnwind) :
106 		"eax", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"
107 	);
108 }
109 
110 static
_SEH2Except(_SEH2Frame_t * frame,volatile _SEH2TryLevel_t * trylevel,struct _EXCEPTION_POINTERS * ep)111 __SEH_EXCEPT_RET _SEH2Except(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel, struct _EXCEPTION_POINTERS * ep)
112 {
113 	void * filter = trylevel->ST_Filter;
114 	void * context = NULL;
115 	__SEH_EXCEPT_RET ret;
116 
117 	if(filter == (void *)0)
118 		return 0;
119 
120 	if(filter == (void *)1)
121 		return 1;
122 
123 	if(filter == (void *)-1)
124 		return -1;
125 
126 	if(_SEHIsTrampoline((_SEHTrampoline_t *)filter))
127 	{
128 		context = _SEHClosureFromTrampoline((_SEHTrampoline_t *)filter);
129 		filter = _SEHFunctionFromTrampoline((_SEHTrampoline_t *)filter);
130 	}
131 
132 	__asm__ __volatile__
133 	(
134 		"push %[ep]\n"
135 		"push %[frame]\n"
136 		"call *%[filter]\n"
137 		"pop %%edx\n"
138 		"pop %%edx\n" :
139 		[ret] "=a" (ret) :
140 		"c" (context), [filter] "r" (filter), [frame] "g" (frame), [ep] "g" (ep) :
141 		"edx", "flags", "memory"
142 	);
143 
144 	return ret;
145 }
146 
147 static
_SEH2Finally(_SEH2Frame_t * frame,volatile _SEH2TryLevel_t * trylevel)148 void _SEH2Finally(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel)
149 {
150 	if(trylevel->ST_Filter == NULL && trylevel->ST_Body != NULL)
151 	{
152 		void * body = trylevel->ST_Body;
153 		void * context = NULL;
154 
155 		if(_SEHIsTrampoline((_SEHTrampoline_t *)body))
156 		{
157 			context = _SEHClosureFromTrampoline((_SEHTrampoline_t *)body);
158 			body = _SEHFunctionFromTrampoline((_SEHTrampoline_t *)body);
159 		}
160 
161 		__asm__ __volatile__("call *%1\n" : : "c" (context), "r" (body) : "eax", "edx", "flags", "memory");
162 	}
163 }
164 
165 typedef struct __SEH2UnwindFrame
166 {
167 	_SEH2Registration_t SUF_Registration;
168 	_SEH2Frame_t * SUF_Frame;
169 	volatile _SEH2TryLevel_t * SUF_TargetTryLevel;
170 }
171 _SEH2UnwindFrame_t;
172 
173 static void _SEH2LocalUnwind(_SEH2Frame_t *, volatile _SEH2TryLevel_t *);
174 
175 extern
_SEH2UnwindHandler(struct _EXCEPTION_RECORD * ExceptionRecord,void * EstablisherFrame,struct _CONTEXT * ContextRecord,void * DispatcherContext)176 int __cdecl _SEH2UnwindHandler
177 (
178 	struct _EXCEPTION_RECORD * ExceptionRecord,
179 	void * EstablisherFrame,
180 	struct _CONTEXT * ContextRecord,
181 	void * DispatcherContext
182 )
183 {
184 	if(ExceptionRecord->ExceptionFlags & (EXCEPTION_EXIT_UNWIND | EXCEPTION_UNWINDING))
185 	{
186 		_SEH2UnwindFrame_t * unwindframe = CONTAINING_RECORD(EstablisherFrame, _SEH2UnwindFrame_t, SUF_Registration);
187 		_SEH2LocalUnwind(unwindframe->SUF_Frame, unwindframe->SUF_TargetTryLevel);
188 		*((void **)DispatcherContext) = EstablisherFrame;
189 		return ExceptionCollidedUnwind;
190 	}
191 
192 	return ExceptionContinueSearch;
193 }
194 
195 static
_SEH2LocalUnwind(_SEH2Frame_t * frame,volatile _SEH2TryLevel_t * dsttrylevel)196 void _SEH2LocalUnwind(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * dsttrylevel)
197 {
198 	volatile _SEH2TryLevel_t * trylevel;
199 	_SEH2UnwindFrame_t unwindframe;
200 
201 	unwindframe.SUF_Frame = frame;
202 	unwindframe.SUF_TargetTryLevel = dsttrylevel;
203 
204 	unwindframe.SUF_Registration.SER_Handler = &__SEH2UnwindHandler;
205 	__SEH2EnterFrame(&unwindframe.SUF_Registration);
206 
207 	for(trylevel = frame->SF_TopTryLevel; trylevel && trylevel != dsttrylevel; trylevel = trylevel->ST_Next)
208 	{
209 		frame->SF_TopTryLevel = trylevel->ST_Next;
210 		_SEH2Finally(frame, trylevel);
211 	}
212 
213 	__SEH2LeaveFrame();
214 }
215 
216 static DECLSPEC_NORETURN
_SEH2Handle(_SEH2Frame_t * frame,volatile _SEH2TryLevel_t * trylevel)217 void _SEH2Handle(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel)
218 {
219 	volatile _SEH2HandleTryLevel_t * fulltrylevel = CONTAINING_RECORD(trylevel, _SEH2HandleTryLevel_t, SHT_Common);
220 
221 	_SEH2GlobalUnwind(frame);
222 	_SEH2LocalUnwind(frame, &fulltrylevel->SHT_Common);
223 	frame->SF_TopTryLevel = fulltrylevel->SHT_Common.ST_Next;
224 
225 	__SEH2Handle
226 	(
227 		fulltrylevel->SHT_Common.ST_Body,
228 		fulltrylevel->SHT_Esp,
229 		fulltrylevel->SHT_Ebp,
230 		fulltrylevel->SHT_Ebx,
231 		fulltrylevel->SHT_Esi,
232 		fulltrylevel->SHT_Edi
233 	);
234 }
235 
236 extern
_SEH2FrameHandler(struct _EXCEPTION_RECORD * ExceptionRecord,void * EstablisherFrame,struct _CONTEXT * ContextRecord,void * DispatcherContext)237 int __cdecl _SEH2FrameHandler
238 (
239 	struct _EXCEPTION_RECORD * ExceptionRecord,
240 	void * EstablisherFrame,
241 	struct _CONTEXT * ContextRecord,
242 	void * DispatcherContext
243 )
244 {
245 	_SEH2Frame_t * frame;
246 
247 	frame = EstablisherFrame;
248 
249 	/* Unwinding */
250 	if(ExceptionRecord->ExceptionFlags & (EXCEPTION_EXIT_UNWIND | EXCEPTION_UNWINDING))
251 	{
252 		_SEH2LocalUnwind(frame, NULL);
253 	}
254 	/* Handling */
255 	else
256 	{
257 		int ret = 0;
258 		volatile _SEH2TryLevel_t * trylevel;
259 		EXCEPTION_POINTERS ep;
260 
261 		ep.ExceptionRecord = ExceptionRecord;
262 		ep.ContextRecord = ContextRecord;
263 
264 		frame->SF_Code = ExceptionRecord->ExceptionCode;
265 
266 		for(trylevel = frame->SF_TopTryLevel; trylevel != NULL; trylevel = trylevel->ST_Next)
267 		{
268 			ret = _SEH2Except(frame, trylevel, &ep);
269 
270 			if(ret < 0)
271 				return ExceptionContinueExecution;
272 			else if(ret > 0)
273 				_SEH2Handle(frame, trylevel);
274 		}
275 	}
276 
277 	return ExceptionContinueSearch;
278 }
279 
280 extern
_SEH2EnterFrame(_SEH2Frame_t * frame)281 void __cdecl _SEH2EnterFrame(_SEH2Frame_t * frame)
282 {
283 	frame->SF_Registration.SER_Handler = __SEH2FrameHandler;
284 	frame->SF_Code = 0;
285 	__SEH2EnterFrame(&frame->SF_Registration);
286 }
287 
288 extern
_SEH2EnterFrameAndTrylevel(_SEH2Frame_t * frame,volatile _SEH2TryLevel_t * trylevel)289 int __cdecl _SEH2EnterFrameAndTrylevel(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel)
290 {
291 	frame->SF_TopTryLevel = trylevel;
292 	_SEH2EnterFrame(frame);
293 	return 0;
294 }
295 
296 extern
_SEH2LeaveFrame(void)297 void __cdecl _SEH2LeaveFrame(void)
298 {
299 	__SEH2LeaveFrame();
300 }
301 
302 extern
_SEH2Return(void)303 void __cdecl _SEH2Return(void)
304 {
305 	_SEH2LocalUnwind(CONTAINING_RECORD(_SEH2CurrentRegistration(), _SEH2Frame_t, SF_Registration), NULL);
306 	_SEH2LeaveFrame();
307 }
308 
309 /* EOF */
310