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 56 int _SEHIsTrampoline(_SEHTrampoline_t * trampoline_) 57 { 58 return trampoline_->STR_MovEcx == 0xb9 && trampoline_->STR_Jmp == 0xe9; 59 } 60 61 FORCEINLINE 62 void * _SEHFunctionFromTrampoline(_SEHTrampoline_t * trampoline_) 63 { 64 return (int)(trampoline_ + 1) + trampoline_->STR_Function; 65 } 66 67 FORCEINLINE 68 void * _SEHClosureFromTrampoline(_SEHTrampoline_t * trampoline_) 69 { 70 return trampoline_->STR_Closure; 71 } 72 73 FORCEINLINE 74 _SEH2Registration_t * __cdecl _SEH2CurrentRegistration(void) 75 { 76 return (_SEH2Registration_t *)__readfsdword(0); 77 } 78 79 FORCEINLINE 80 void __cdecl __SEH2EnterFrame(_SEH2Registration_t * frame) 81 { 82 frame->SER_Prev = _SEH2CurrentRegistration(); 83 __writefsdword(0, (unsigned long)frame); 84 } 85 86 FORCEINLINE 87 void __cdecl __SEH2LeaveFrame(void) 88 { 89 __writefsdword(0, (unsigned long)_SEH2CurrentRegistration()->SER_Prev); 90 } 91 92 FORCEINLINE 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 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 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 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 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 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 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 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 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 298 void __cdecl _SEH2LeaveFrame(void) 299 { 300 __SEH2LeaveFrame(); 301 } 302 303 extern 304 void __cdecl _SEH2Return(void) 305 { 306 _SEH2LocalUnwind(CONTAINING_RECORD(_SEH2CurrentRegistration(), _SEH2Frame_t, SF_Registration), NULL); 307 _SEH2LeaveFrame(); 308 } 309 310 /* EOF */ 311