reactos/sdk/lib/pseh/i386/framebased-gcchack.c

311 lines
7.6 KiB
C
Raw Normal View History

/*
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)
{
Supersedes r38084. Take three modified include/reactos/libs/pseh/pseh2.h modified lib/pseh/framebased-gcchack.c modified lib/pseh/i386/framebased-gcchack.S Disassemble trampolines in the library, instead of the macros. Results in better, smaller code. As a side effect, PSEH no longer requires a trampoline for nested functions - which results in even better, even smaller code in many common cases where the nested functions don't use any variables from the containing function Simulate a no-op setjmp so that GCC correctly handles variables in registers, instead of surprise-corrupting random variables in random conditions Save EBP and ESP every time a _SEH2_TRY/_SEH2_EXCEPT is entered, instead of only the first time. Probably not entirely correct yet Don't generate a nested function for a _SEH2_EXCEPT() filter expression if the value is a compile-time constant: convert the value to (void *)0, (void *)1 or (void *)-1, and set that as the filter, instead (like Visual C++ does, incidentally) If a _SEH2_EXCEPT() filter expression is a compile-time constant evaluating to EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH, allow GCC to optimize out the body of the _SEH2_EXCEPT (because it'd be unreachable). This should really result in a compile-time warning, but #pragma message is unsupported in GCC 4.1.3 Let _SEH2_EXCEPT() accept a comma expression as filter expression (e.g. _SEH2_EXCEPT(MessageBox(...), EXCEPTION_EXECUTE_HANDLER) instead of _SEH2_EXCEPT((MessageBox(...), EXCEPTION_EXECUTE_HANDLER))) Small optimizations in PSEH library Clean up GCC hacks Remove currently unused PSEH 3 hacks svn path=/trunk/; revision=38197
2008-12-20 13:05:57 +00:00
void * filter = trylevel->ST_Filter;
void * context = NULL;
__SEH_EXCEPT_RET ret;
Supersedes r38084. Take three modified include/reactos/libs/pseh/pseh2.h modified lib/pseh/framebased-gcchack.c modified lib/pseh/i386/framebased-gcchack.S Disassemble trampolines in the library, instead of the macros. Results in better, smaller code. As a side effect, PSEH no longer requires a trampoline for nested functions - which results in even better, even smaller code in many common cases where the nested functions don't use any variables from the containing function Simulate a no-op setjmp so that GCC correctly handles variables in registers, instead of surprise-corrupting random variables in random conditions Save EBP and ESP every time a _SEH2_TRY/_SEH2_EXCEPT is entered, instead of only the first time. Probably not entirely correct yet Don't generate a nested function for a _SEH2_EXCEPT() filter expression if the value is a compile-time constant: convert the value to (void *)0, (void *)1 or (void *)-1, and set that as the filter, instead (like Visual C++ does, incidentally) If a _SEH2_EXCEPT() filter expression is a compile-time constant evaluating to EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH, allow GCC to optimize out the body of the _SEH2_EXCEPT (because it'd be unreachable). This should really result in a compile-time warning, but #pragma message is unsupported in GCC 4.1.3 Let _SEH2_EXCEPT() accept a comma expression as filter expression (e.g. _SEH2_EXCEPT(MessageBox(...), EXCEPTION_EXECUTE_HANDLER) instead of _SEH2_EXCEPT((MessageBox(...), EXCEPTION_EXECUTE_HANDLER))) Small optimizations in PSEH library Clean up GCC hacks Remove currently unused PSEH 3 hacks svn path=/trunk/; revision=38197
2008-12-20 13:05:57 +00:00
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)
{
Supersedes r38084. Take three modified include/reactos/libs/pseh/pseh2.h modified lib/pseh/framebased-gcchack.c modified lib/pseh/i386/framebased-gcchack.S Disassemble trampolines in the library, instead of the macros. Results in better, smaller code. As a side effect, PSEH no longer requires a trampoline for nested functions - which results in even better, even smaller code in many common cases where the nested functions don't use any variables from the containing function Simulate a no-op setjmp so that GCC correctly handles variables in registers, instead of surprise-corrupting random variables in random conditions Save EBP and ESP every time a _SEH2_TRY/_SEH2_EXCEPT is entered, instead of only the first time. Probably not entirely correct yet Don't generate a nested function for a _SEH2_EXCEPT() filter expression if the value is a compile-time constant: convert the value to (void *)0, (void *)1 or (void *)-1, and set that as the filter, instead (like Visual C++ does, incidentally) If a _SEH2_EXCEPT() filter expression is a compile-time constant evaluating to EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH, allow GCC to optimize out the body of the _SEH2_EXCEPT (because it'd be unreachable). This should really result in a compile-time warning, but #pragma message is unsupported in GCC 4.1.3 Let _SEH2_EXCEPT() accept a comma expression as filter expression (e.g. _SEH2_EXCEPT(MessageBox(...), EXCEPTION_EXECUTE_HANDLER) instead of _SEH2_EXCEPT((MessageBox(...), EXCEPTION_EXECUTE_HANDLER))) Small optimizations in PSEH library Clean up GCC hacks Remove currently unused PSEH 3 hacks svn path=/trunk/; revision=38197
2008-12-20 13:05:57 +00:00
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");
Supersedes r38084. Take three modified include/reactos/libs/pseh/pseh2.h modified lib/pseh/framebased-gcchack.c modified lib/pseh/i386/framebased-gcchack.S Disassemble trampolines in the library, instead of the macros. Results in better, smaller code. As a side effect, PSEH no longer requires a trampoline for nested functions - which results in even better, even smaller code in many common cases where the nested functions don't use any variables from the containing function Simulate a no-op setjmp so that GCC correctly handles variables in registers, instead of surprise-corrupting random variables in random conditions Save EBP and ESP every time a _SEH2_TRY/_SEH2_EXCEPT is entered, instead of only the first time. Probably not entirely correct yet Don't generate a nested function for a _SEH2_EXCEPT() filter expression if the value is a compile-time constant: convert the value to (void *)0, (void *)1 or (void *)-1, and set that as the filter, instead (like Visual C++ does, incidentally) If a _SEH2_EXCEPT() filter expression is a compile-time constant evaluating to EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH, allow GCC to optimize out the body of the _SEH2_EXCEPT (because it'd be unreachable). This should really result in a compile-time warning, but #pragma message is unsupported in GCC 4.1.3 Let _SEH2_EXCEPT() accept a comma expression as filter expression (e.g. _SEH2_EXCEPT(MessageBox(...), EXCEPTION_EXECUTE_HANDLER) instead of _SEH2_EXCEPT((MessageBox(...), EXCEPTION_EXECUTE_HANDLER))) Small optimizations in PSEH library Clean up GCC hacks Remove currently unused PSEH 3 hacks svn path=/trunk/; revision=38197
2008-12-20 13:05:57 +00:00
}
}
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;
Supersedes r38084. Take three modified include/reactos/libs/pseh/pseh2.h modified lib/pseh/framebased-gcchack.c modified lib/pseh/i386/framebased-gcchack.S Disassemble trampolines in the library, instead of the macros. Results in better, smaller code. As a side effect, PSEH no longer requires a trampoline for nested functions - which results in even better, even smaller code in many common cases where the nested functions don't use any variables from the containing function Simulate a no-op setjmp so that GCC correctly handles variables in registers, instead of surprise-corrupting random variables in random conditions Save EBP and ESP every time a _SEH2_TRY/_SEH2_EXCEPT is entered, instead of only the first time. Probably not entirely correct yet Don't generate a nested function for a _SEH2_EXCEPT() filter expression if the value is a compile-time constant: convert the value to (void *)0, (void *)1 or (void *)-1, and set that as the filter, instead (like Visual C++ does, incidentally) If a _SEH2_EXCEPT() filter expression is a compile-time constant evaluating to EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH, allow GCC to optimize out the body of the _SEH2_EXCEPT (because it'd be unreachable). This should really result in a compile-time warning, but #pragma message is unsupported in GCC 4.1.3 Let _SEH2_EXCEPT() accept a comma expression as filter expression (e.g. _SEH2_EXCEPT(MessageBox(...), EXCEPTION_EXECUTE_HANDLER) instead of _SEH2_EXCEPT((MessageBox(...), EXCEPTION_EXECUTE_HANDLER))) Small optimizations in PSEH library Clean up GCC hacks Remove currently unused PSEH 3 hacks svn path=/trunk/; revision=38197
2008-12-20 13:05:57 +00:00
_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;
Supersedes r38084. Take three modified include/reactos/libs/pseh/pseh2.h modified lib/pseh/framebased-gcchack.c modified lib/pseh/i386/framebased-gcchack.S Disassemble trampolines in the library, instead of the macros. Results in better, smaller code. As a side effect, PSEH no longer requires a trampoline for nested functions - which results in even better, even smaller code in many common cases where the nested functions don't use any variables from the containing function Simulate a no-op setjmp so that GCC correctly handles variables in registers, instead of surprise-corrupting random variables in random conditions Save EBP and ESP every time a _SEH2_TRY/_SEH2_EXCEPT is entered, instead of only the first time. Probably not entirely correct yet Don't generate a nested function for a _SEH2_EXCEPT() filter expression if the value is a compile-time constant: convert the value to (void *)0, (void *)1 or (void *)-1, and set that as the filter, instead (like Visual C++ does, incidentally) If a _SEH2_EXCEPT() filter expression is a compile-time constant evaluating to EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH, allow GCC to optimize out the body of the _SEH2_EXCEPT (because it'd be unreachable). This should really result in a compile-time warning, but #pragma message is unsupported in GCC 4.1.3 Let _SEH2_EXCEPT() accept a comma expression as filter expression (e.g. _SEH2_EXCEPT(MessageBox(...), EXCEPTION_EXECUTE_HANDLER) instead of _SEH2_EXCEPT((MessageBox(...), EXCEPTION_EXECUTE_HANDLER))) Small optimizations in PSEH library Clean up GCC hacks Remove currently unused PSEH 3 hacks svn path=/trunk/; revision=38197
2008-12-20 13:05:57 +00:00
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);
Supersedes r38084. Take three modified include/reactos/libs/pseh/pseh2.h modified lib/pseh/framebased-gcchack.c modified lib/pseh/i386/framebased-gcchack.S Disassemble trampolines in the library, instead of the macros. Results in better, smaller code. As a side effect, PSEH no longer requires a trampoline for nested functions - which results in even better, even smaller code in many common cases where the nested functions don't use any variables from the containing function Simulate a no-op setjmp so that GCC correctly handles variables in registers, instead of surprise-corrupting random variables in random conditions Save EBP and ESP every time a _SEH2_TRY/_SEH2_EXCEPT is entered, instead of only the first time. Probably not entirely correct yet Don't generate a nested function for a _SEH2_EXCEPT() filter expression if the value is a compile-time constant: convert the value to (void *)0, (void *)1 or (void *)-1, and set that as the filter, instead (like Visual C++ does, incidentally) If a _SEH2_EXCEPT() filter expression is a compile-time constant evaluating to EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH, allow GCC to optimize out the body of the _SEH2_EXCEPT (because it'd be unreachable). This should really result in a compile-time warning, but #pragma message is unsupported in GCC 4.1.3 Let _SEH2_EXCEPT() accept a comma expression as filter expression (e.g. _SEH2_EXCEPT(MessageBox(...), EXCEPTION_EXECUTE_HANDLER) instead of _SEH2_EXCEPT((MessageBox(...), EXCEPTION_EXECUTE_HANDLER))) Small optimizations in PSEH library Clean up GCC hacks Remove currently unused PSEH 3 hacks svn path=/trunk/; revision=38197
2008-12-20 13:05:57 +00:00
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 */