/* | |
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 */ |