| /* |
| Copyright (c) 2008 KJK::Hyperion |
| |
| Permission is hereby granted, free of charge, to any person obtaining a |
| copy of this software and associated documentation files (the "Software"), |
| to deal in the Software without restriction, including without limitation |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| and/or sell copies of the Software, and to permit persons to whom the |
| Software is furnished to do so, subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included in |
| all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #define _NTSYSTEM_ /* removes dllimport attribute from RtlUnwind */ |
| |
| #define STRICT |
| #define WIN32_LEAN_AND_MEAN |
| #include <windows.h> |
| |
| #include <pseh/pseh2-common.h> |
| #include <excpt.h> |
| #include <intrin.h> |
| |
| #ifndef EXCEPTION_EXIT_UNWIND |
| #define EXCEPTION_EXIT_UNWIND 4 |
| #endif |
| |
| #ifndef EXCEPTION_UNWINDING |
| #define EXCEPTION_UNWINDING 2 |
| #endif |
| |
| extern DECLSPEC_NORETURN int __SEH2Handle(void *, void *, void *, void *, void *, void *); |
| extern int __cdecl __SEH2FrameHandler(struct _EXCEPTION_RECORD *, void *, struct _CONTEXT *, void *); |
| extern int __cdecl __SEH2UnwindHandler(struct _EXCEPTION_RECORD *, void *, struct _CONTEXT *, void *); |
| |
| typedef struct __SEHTrampoline |
| { |
| unsigned char STR_MovEcx; |
| unsigned char * STR_Closure; |
| unsigned char STR_Jmp; |
| unsigned char * STR_Function; |
| } |
| __attribute__((packed)) |
| _SEHTrampoline_t; |
| |
| FORCEINLINE |
| int _SEHIsTrampoline(_SEHTrampoline_t * trampoline_) |
| { |
| return trampoline_->STR_MovEcx == 0xb9 && trampoline_->STR_Jmp == 0xe9; |
| } |
| |
| FORCEINLINE |
| void * _SEHFunctionFromTrampoline(_SEHTrampoline_t * trampoline_) |
| { |
| return (int)(trampoline_ + 1) + trampoline_->STR_Function; |
| } |
| |
| FORCEINLINE |
| void * _SEHClosureFromTrampoline(_SEHTrampoline_t * trampoline_) |
| { |
| return trampoline_->STR_Closure; |
| } |
| |
| FORCEINLINE |
| _SEH2Registration_t * __cdecl _SEH2CurrentRegistration(void) |
| { |
| return (_SEH2Registration_t *)__readfsdword(0); |
| } |
| |
| FORCEINLINE |
| void __cdecl __SEH2EnterFrame(_SEH2Registration_t * frame) |
| { |
| frame->SER_Prev = _SEH2CurrentRegistration(); |
| __writefsdword(0, (unsigned long)frame); |
| } |
| |
| FORCEINLINE |
| void __cdecl __SEH2LeaveFrame(void) |
| { |
| __writefsdword(0, (unsigned long)_SEH2CurrentRegistration()->SER_Prev); |
| } |
| |
| FORCEINLINE |
| void _SEH2GlobalUnwind(void * target) |
| { |
| __asm__ __volatile__ |
| ( |
| "push %%ebp\n" |
| "push $0\n" |
| "push $0\n" |
| "push $Return%=\n" |
| "push %[target]\n" |
| "call %c[RtlUnwind]\n" |
| "Return%=: pop %%ebp\n" : |
| : |
| [target] "g" (target), [RtlUnwind] "g" (&RtlUnwind) : |
| "eax", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory" |
| ); |
| } |
| |
| static |
| __SEH_EXCEPT_RET _SEH2Except(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel, struct _EXCEPTION_POINTERS * ep) |
| { |
| void * filter = trylevel->ST_Filter; |
| void * context = NULL; |
| __SEH_EXCEPT_RET ret; |
| |
| if(filter == (void *)0) |
| return 0; |
| |
| if(filter == (void *)1) |
| return 1; |
| |
| if(filter == (void *)-1) |
| return -1; |
| |
| if(_SEHIsTrampoline((_SEHTrampoline_t *)filter)) |
| { |
| context = _SEHClosureFromTrampoline((_SEHTrampoline_t *)filter); |
| filter = _SEHFunctionFromTrampoline((_SEHTrampoline_t *)filter); |
| } |
| |
| __asm__ __volatile__ |
| ( |
| "push %[ep]\n" |
| "push %[frame]\n" |
| "call *%[filter]\n" |
| "pop %%edx\n" |
| "pop %%edx\n" : |
| [ret] "=a" (ret) : |
| "c" (context), [filter] "r" (filter), [frame] "g" (frame), [ep] "g" (ep) : |
| "edx", "flags", "memory" |
| ); |
| |
| return ret; |
| } |
| |
| static |
| void _SEH2Finally(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel) |
| { |
| if(trylevel->ST_Filter == NULL && trylevel->ST_Body != NULL) |
| { |
| void * body = trylevel->ST_Body; |
| void * context = NULL; |
| |
| if(_SEHIsTrampoline((_SEHTrampoline_t *)body)) |
| { |
| context = _SEHClosureFromTrampoline((_SEHTrampoline_t *)body); |
| body = _SEHFunctionFromTrampoline((_SEHTrampoline_t *)body); |
| } |
| |
| __asm__ __volatile__("call *%1\n" : : "c" (context), "r" (body) : "eax", "edx", "flags", "memory"); |
| } |
| } |
| |
| typedef struct __SEH2UnwindFrame |
| { |
| _SEH2Registration_t SUF_Registration; |
| _SEH2Frame_t * SUF_Frame; |
| volatile _SEH2TryLevel_t * SUF_TargetTryLevel; |
| } |
| _SEH2UnwindFrame_t; |
| |
| static void _SEH2LocalUnwind(_SEH2Frame_t *, volatile _SEH2TryLevel_t *); |
| |
| extern |
| int __cdecl _SEH2UnwindHandler |
| ( |
| struct _EXCEPTION_RECORD * ExceptionRecord, |
| void * EstablisherFrame, |
| struct _CONTEXT * ContextRecord, |
| void * DispatcherContext |
| ) |
| { |
| if(ExceptionRecord->ExceptionFlags & (EXCEPTION_EXIT_UNWIND | EXCEPTION_UNWINDING)) |
| { |
| _SEH2UnwindFrame_t * unwindframe = CONTAINING_RECORD(EstablisherFrame, _SEH2UnwindFrame_t, SUF_Registration); |
| _SEH2LocalUnwind(unwindframe->SUF_Frame, unwindframe->SUF_TargetTryLevel); |
| *((void **)DispatcherContext) = EstablisherFrame; |
| return ExceptionCollidedUnwind; |
| } |
| |
| return ExceptionContinueSearch; |
| } |
| |
| static |
| void _SEH2LocalUnwind(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * dsttrylevel) |
| { |
| volatile _SEH2TryLevel_t * trylevel; |
| _SEH2UnwindFrame_t unwindframe; |
| |
| unwindframe.SUF_Frame = frame; |
| unwindframe.SUF_TargetTryLevel = dsttrylevel; |
| |
| unwindframe.SUF_Registration.SER_Handler = &__SEH2UnwindHandler; |
| __SEH2EnterFrame(&unwindframe.SUF_Registration); |
| |
| for(trylevel = frame->SF_TopTryLevel; trylevel && trylevel != dsttrylevel; trylevel = trylevel->ST_Next) |
| { |
| frame->SF_TopTryLevel = trylevel->ST_Next; |
| _SEH2Finally(frame, trylevel); |
| } |
| |
| __SEH2LeaveFrame(); |
| } |
| |
| static DECLSPEC_NORETURN |
| void _SEH2Handle(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel) |
| { |
| volatile _SEH2HandleTryLevel_t * fulltrylevel = CONTAINING_RECORD(trylevel, _SEH2HandleTryLevel_t, SHT_Common); |
| |
| _SEH2GlobalUnwind(frame); |
| _SEH2LocalUnwind(frame, &fulltrylevel->SHT_Common); |
| frame->SF_TopTryLevel = fulltrylevel->SHT_Common.ST_Next; |
| |
| __SEH2Handle |
| ( |
| fulltrylevel->SHT_Common.ST_Body, |
| fulltrylevel->SHT_Esp, |
| fulltrylevel->SHT_Ebp, |
| fulltrylevel->SHT_Ebx, |
| fulltrylevel->SHT_Esi, |
| fulltrylevel->SHT_Edi |
| ); |
| } |
| |
| extern |
| int __cdecl _SEH2FrameHandler |
| ( |
| struct _EXCEPTION_RECORD * ExceptionRecord, |
| void * EstablisherFrame, |
| struct _CONTEXT * ContextRecord, |
| void * DispatcherContext |
| ) |
| { |
| _SEH2Frame_t * frame; |
| |
| frame = EstablisherFrame; |
| |
| /* Unwinding */ |
| if(ExceptionRecord->ExceptionFlags & (EXCEPTION_EXIT_UNWIND | EXCEPTION_UNWINDING)) |
| { |
| _SEH2LocalUnwind(frame, NULL); |
| } |
| /* Handling */ |
| else |
| { |
| int ret = 0; |
| volatile _SEH2TryLevel_t * trylevel; |
| EXCEPTION_POINTERS ep; |
| |
| ep.ExceptionRecord = ExceptionRecord; |
| ep.ContextRecord = ContextRecord; |
| |
| frame->SF_Code = ExceptionRecord->ExceptionCode; |
| |
| for(trylevel = frame->SF_TopTryLevel; trylevel != NULL; trylevel = trylevel->ST_Next) |
| { |
| ret = _SEH2Except(frame, trylevel, &ep); |
| |
| if(ret < 0) |
| return ExceptionContinueExecution; |
| else if(ret > 0) |
| _SEH2Handle(frame, trylevel); |
| } |
| } |
| |
| return ExceptionContinueSearch; |
| } |
| |
| extern |
| void __cdecl _SEH2EnterFrame(_SEH2Frame_t * frame) |
| { |
| frame->SF_Registration.SER_Handler = __SEH2FrameHandler; |
| frame->SF_Code = 0; |
| __SEH2EnterFrame(&frame->SF_Registration); |
| } |
| |
| extern |
| int __cdecl _SEH2EnterFrameAndTrylevel(_SEH2Frame_t * frame, volatile _SEH2TryLevel_t * trylevel) |
| { |
| frame->SF_TopTryLevel = trylevel; |
| _SEH2EnterFrame(frame); |
| return 0; |
| } |
| |
| extern |
| void __cdecl _SEH2LeaveFrame(void) |
| { |
| __SEH2LeaveFrame(); |
| } |
| |
| extern |
| void __cdecl _SEH2Return(void) |
| { |
| _SEH2LocalUnwind(CONTAINING_RECORD(_SEH2CurrentRegistration(), _SEH2Frame_t, SF_Registration), NULL); |
| _SEH2LeaveFrame(); |
| } |
| |
| /* EOF */ |