mirror of
https://github.com/reactos/reactos.git
synced 2024-08-30 23:28:58 +00:00
311 lines
7.6 KiB
C
311 lines
7.6 KiB
C
/*
|
|
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
|
|
#include <windef.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <pseh/pseh2.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\t"
|
|
"push $0\n\t"
|
|
"push $0\n\t"
|
|
"push $Return%=\n\t"
|
|
"push %[target]\n\t"
|
|
"call %c[RtlUnwind]\n"
|
|
"Return%=:\n\t"
|
|
"pop %%ebp" :
|
|
:
|
|
[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\t"
|
|
"push %[frame]\n\t"
|
|
"call *%[filter]\n\t"
|
|
"pop %%edx\n\t"
|
|
"pop %%edx" :
|
|
[ret] "=a" (ret) :
|
|
"c" (context), [filter] "r" (filter), [frame] "r" (frame), [ep] "r" (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" : : "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 */
|