- Add AllocaFrame field to the exception registration record. It is required for Clang and C++ handlers.
- Fix the way how "nested functions" are emulated on Clang and C++, respecting the fact that the compiler can and will use a temporary esp-based stack frame below any alloca-allocations for function invocation. This uses the AllocaFrame field to calculate and setup a new temp stack frame for the "nested functions".
- Make use of the HandlerType field in the exception registration record to use different methods for invoking filters / finally functions.
- Write @_SEH3$_CallRtlUnwind@4 in raw asm instead of inline, because Clang cannot deal with stdcall decorations in inline asm (see http://llvm.org/bugs/show_bug.cgi?id=19027)
- Make sure ExceptionPointers are properly initialized in _SEH3$_except_handler

svn path=/trunk/; revision=62466
This commit is contained in:
Timo Kreuzer 2014-03-09 13:55:26 +00:00
parent 1ff93cb52e
commit 7165a7c35b
4 changed files with 255 additions and 74 deletions

View file

@ -67,6 +67,8 @@ typedef struct _SEH3$_REGISTRATION_FRAME
/* Registers that we need to save */
unsigned long Esp;
unsigned long Ebp;
char* AllocaFrame;
#ifdef _SEH3$_FRAME_ALL_NONVOLATILES
unsigned long Ebx;
unsigned long Esi;
@ -101,10 +103,12 @@ enum
_SEH3$_TryLevel = 0,
};
#ifndef __clang__
/* These are global dummy definitions, that get overwritten in the local context of __finally / __except blocks */
int __cdecl __attribute__((error ("Can only be used inside a __finally block."))) _abnormal_termination(void);
unsigned long __cdecl __attribute__((error("Can only be used inside an exception filter or __except block."))) _exception_code(void);
void * __cdecl __attribute__((error("Can only be used inside an exception filter."))) _exception_info(void);
#endif
/* This attribute allows automatic cleanup of the registered frames */
#define _SEH3$_AUTO_CLEANUP __attribute__((cleanup(_SEH3$_Unregister)))
@ -119,15 +123,16 @@ void * __cdecl __attribute__((error("Can only be used inside an exception filter
#define _SEH3$_ASM_GOTO(_Label, ...)
int
__attribute__((regparm(2)))
__attribute__((regparm(3)))
__attribute__((returns_twice))
_SEH3$_RegisterFrameWithNonVolatiles(
volatile SEH3$_REGISTRATION_FRAME* RegistrationFrame,
const SEH3$_SCOPE_TABLE* ScopeTable);
const SEH3$_SCOPE_TABLE* ScopeTable,
void* AllocaFrame);
#define _SEH3$_RegisterFrame_(_TrylevelFrame, _DataTable) \
do { \
int result = _SEH3$_RegisterFrameWithNonVolatiles(_TrylevelFrame, _DataTable); \
int result = _SEH3$_RegisterFrameWithNonVolatiles(_TrylevelFrame, _DataTable, __builtin_alloca(0)); \
if (__builtin_expect(result != 0, 0)) \
{ \
if (result == 1) goto _SEH3$_l_FilterOrFinally; \
@ -137,15 +142,16 @@ _SEH3$_RegisterFrameWithNonVolatiles(
} while(0)
int
__attribute__((regparm(2)))
__attribute__((regparm(3)))
__attribute__((returns_twice))
_SEH3$_RegisterTryLevelWithNonVolatiles(
volatile SEH3$_REGISTRATION_FRAME* RegistrationFrame,
const SEH3$_SCOPE_TABLE* ScopeTable);
const SEH3$_SCOPE_TABLE* ScopeTable,
void* AllocaFrame);
#define _SEH3$_RegisterTryLevel_(_TrylevelFrame, _DataTable) \
do { \
int result = _SEH3$_RegisterTryLevelWithNonVolatiles(_TrylevelFrame, _DataTable); \
int result = _SEH3$_RegisterTryLevelWithNonVolatiles(_TrylevelFrame, _DataTable, __builtin_alloca(0)); \
if (__builtin_expect(result != 0, 0)) \
{ \
if (result == 1) goto _SEH3$_l_FilterOrFinally; \
@ -163,25 +169,34 @@ _SEH3$_RegisterTryLevelWithNonVolatiles(
#define _SEH3$_ASM_GOTO(_Label, ...) asm goto ("#\n" : : : "memory", ## __VA_ARGS__ : _Label)
/* This is an asm wrapper around _SEH3$_RegisterFrame */
#define _SEH3$_RegisterFrame_(_TrylevelFrame, _DataTable) \
#ifdef __cplusplus
#define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
asm goto ("leal %0, %%eax\n" \
"leal %1, %%edx\n" \
"call __SEH3$_RegisterFrame\n" \
"call " #_Function "WithStackLayout\n" \
: \
: "m" (*(_TrylevelFrame)), "m" (*(_DataTable)), "c"(__builtin_alloca(0)) \
: "eax", "edx", "memory" \
: _SEH3$_l_HandlerTarget, _SEH3$_l_FilterOrFinally)
#else
#define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
asm goto ("leal %0, %%eax\n" \
"leal %1, %%edx\n" \
"call " #_Function "\n" \
: \
: "m" (*(_TrylevelFrame)), "m" (*(_DataTable)) \
: "ecx", "edx", "memory" \
: "eax", "edx", "ecx", "memory" \
: _SEH3$_l_HandlerTarget)
#endif
/* This is an asm wrapper around _SEH3$_RegisterFrame */
#define _SEH3$_RegisterFrame_(_TrylevelFrame, _DataTable) \
_SEH3$_CALL_WRAPPER(__SEH3$_RegisterFrame, _TrylevelFrame, _DataTable)
/* This is an asm wrapper around _SEH3$_RegisterTryLevel */
#define _SEH3$_RegisterTryLevel_(_TrylevelFrame, _DataTable) \
asm goto ("leal %0, %%eax\n" \
"leal %1, %%edx\n" \
"call __SEH3$_RegisterTryLevel\n" \
: \
: "m" (*(_TrylevelFrame)), "m" (*(_DataTable)) \
: "ecx", "edx", "memory" \
: _SEH3$_l_HandlerTarget)
_SEH3$_CALL_WRAPPER(__SEH3$_RegisterTryLevel, _TrylevelFrame, _DataTable)
/* This construct scares GCC so much, that it will stop moving code
around into places that are never executed. */
@ -214,15 +229,17 @@ _SEH3$_Unregister(
/* The "nested" functions are a piece of code with a ret instruction at the end */
#define _SEH3$_NESTED_FUNC_OPEN() \
{ \
int SavedEsp, result = 0; \
\
/* Save esp */ \
asm volatile ("movl %%esp, %[SavedEsp]\n" : : [SavedEsp]"m"(SavedEsp));
int _SEH3$_Result = 0; \
/* On invocation, the AllocaFrame field is loaded with the return esp value */
#define _SEH3$_NESTED_FUNC_RETURN() \
/* Restore esp and return to the caller */ \
asm volatile ("movl %[FixedEsp], %%esp\nret\n" \
: : "a"(_SEH3$_Result), [FixedEsp]"m"(_SEH3$_TrylevelFrame.AllocaFrame) : "memory")
#define _SEH3$_NESTED_FUNC_CLOSE() \
/* Restore esp and return to the caller */ \
asm volatile ("movl %[SavedEsp], %%esp\nret\n" \
: : "a"(result), [SavedEsp]"irm"(SavedEsp)); \
/* Return to the caller */ \
_SEH3$_NESTED_FUNC_RETURN(); \
}
/* The filter function */
@ -230,7 +247,7 @@ _SEH3$_Unregister(
_SEH3$_NESTED_FUNC_OPEN() \
{ \
/* Evaluate the filter expression */ \
result = (expression); \
_SEH3$_Result = (expression); \
} \
_SEH3$_NESTED_FUNC_CLOSE()
@ -238,11 +255,10 @@ _SEH3$_Unregister(
_SEH3$_NESTED_FUNC_OPEN() \
/* This construct makes sure that the finally function returns */ \
/* a proper value at the end */ \
for (; ; (void)({asm volatile ("movl %[SavedEsp], %%esp\nret\n" \
: : "a"(result), [SavedEsp]"irm"(SavedEsp)); 0;}))
for (; ; (void)({_SEH3$_NESTED_FUNC_RETURN(); 0;}))
#define _SEH3$_FILTER(_Filter, _FilterExpression) (&&_SEH3$_l_FilterOrFinally)
#define _SEH3$_FINALLY(_Finally) 0
#define _SEH3$_FINALLY(_Finally) (&&_SEH3$_l_FilterOrFinally)
#define _SEH3$_DECLARE_EXCEPT_INTRINSICS()
@ -336,6 +352,7 @@ _SEH3$_Unregister(
__label__ _SEH3$_l_OnException; \
__label__ _SEH3$_l_BeforeFilterOrFinally; \
__label__ _SEH3$_l_FilterOrFinally; \
(void)&&_SEH3$_l_OnException; \
(void)&&_SEH3$_l_BeforeFilterOrFinally; \
(void)&&_SEH3$_l_FilterOrFinally; \
\
@ -419,7 +436,7 @@ _SEH3$_Unregister(
_SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FinallyFunction); \
\
/* Create a static data table that contains the finally function */ \
static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { 0, _SEH3$_FINALLY(&_SEH3$_FinallyFunction) }; \
static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { 0, _SEH3$_FINALLY(&_SEH3$_FinallyFunction), _SEH3$_TryLevel, _SEH3$_HANDLER_TYPE }; \
\
/* Register the registration record. */ \
if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
@ -431,6 +448,7 @@ _SEH3$_Unregister(
_SEH3$_EnforceFramePointer(); \
\
_SEH3$_l_BeforeFilterOrFinally: (void)0; \
_SEH3$_EnforceFramePointer(); \
_SEH3$_l_FilterOrFinally: (void)0; \
_SEH3$_FINALLY_FUNC_OPEN(_SEH3$_FinallyFunction)

View file

@ -63,8 +63,8 @@ _SEH3$_Unregister(
static inline
LONG
_SEH3$_InvokeFilter(
PVOID Record,
_SEH3$_InvokeNestedFunctionFilter(
PSEH3$_REGISTRATION_FRAME RegistrationFrame,
PVOID Filter)
{
LONG FilterResult;
@ -78,18 +78,58 @@ _SEH3$_InvokeFilter(
/* The result is the frame base address that we passed in (0) plus the
offset to the registration record. */
"negl %%eax\n\t"
"addl %[Record], %%eax\n\t"
"addl %[RegistrationFrame], %%eax\n\t"
/* Second call to get the filter result */
"mov $1, %%ecx\n\t"
"call *%[Filter]\n\t"
: "=a"(FilterResult)
: [Record] "m" (Record), [Filter] "m" (Filter)
: [RegistrationFrame] "m" (RegistrationFrame), [Filter] "m" (Filter)
: "ecx", "edx");
return FilterResult;
}
long
__attribute__((regparm(1)))
_SEH3$_InvokeEmbeddedFilter(
PSEH3$_REGISTRATION_FRAME RegistrationFrame);
long
__attribute__((regparm(1)))
_SEH3$_InvokeEmbeddedFilterFromRegistration(
PSEH3$_REGISTRATION_FRAME RegistrationFrame);
static inline
LONG
_SEH3$_InvokeFilter(
PSEH3$_REGISTRATION_FRAME RegistrationFrame,
PVOID Filter)
{
LONG FilterResult;
if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_NESTED_HANDLER)
{
return _SEH3$_InvokeNestedFunctionFilter(RegistrationFrame, Filter);
}
else if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_CPP_HANDLER)
{
/* Call the embedded filter function */
return _SEH3$_InvokeEmbeddedFilter(RegistrationFrame);
}
else if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_CLANG_HANDLER)
{
return _SEH3$_InvokeEmbeddedFilterFromRegistration(RegistrationFrame);
}
else
{
/* Should not happen! Skip this handler */
FilterResult = EXCEPTION_CONTINUE_SEARCH;
}
return FilterResult;
}
static inline
LONG
_SEH3$_GetFilterResult(
@ -136,45 +176,51 @@ void
_SEH3$_JumpToTarget(
PSEH3$_REGISTRATION_FRAME RegistrationFrame)
{
asm volatile (
/* Load the registers */
"movl 20(%%ecx), %%esp\n"
"movl 24(%%ecx), %%ebp\n"
if (RegistrationFrame->ScopeTable->HandlerType == _SEH3$_CLANG_HANDLER)
{
asm volatile (
/* Load the registers */
"movl 20(%%ecx), %%esp\n"
"movl 24(%%ecx), %%ebp\n"
/* Stack pointer is 4 off from the call to __SEH3$_RegisterFrame */
"addl $4, %%esp\n"
/* Stack pointer is 4 off from the call to __SEH3$_RegisterFrame */
"addl $4, %%esp\n"
/* Jump into the exception handler */
"jmp *%[Target]\n"
: :
"c" (RegistrationFrame),
"a" (RegistrationFrame->ScopeTable),
[Target] "m" (RegistrationFrame->ScopeTable->Target)
);
/* Jump into the exception handler */
"jmp *%[Target]\n"
: :
"c" (RegistrationFrame),
"a" (RegistrationFrame->ScopeTable),
[Target] "m" (RegistrationFrame->ScopeTable->Target)
);
}
else
{
asm volatile (
/* Load the registers */
"movl 20(%%ecx), %%esp\n"
"movl 24(%%ecx), %%ebp\n"
/* Stack pointer is 4 off from the call to __SEH3$_RegisterFrame */
"addl $4, %%esp\n"
/* Jump into the exception handler */
"jmp *%[Target]\n"
: :
"c" (RegistrationFrame),
"a" (RegistrationFrame->ScopeTable),
[Target] "m" (RegistrationFrame->ScopeTable->Target)
);
}
__builtin_unreachable();
}
static inline
void
__fastcall
_SEH3$_CallRtlUnwind(
PSEH3$_REGISTRATION_FRAME RegistrationFrame)
{
LONG ClobberedEax;
PSEH3$_REGISTRATION_FRAME RegistrationFrame);
asm volatile(
"push %%ebp\n"
"push $0\n"
"push $0\n"
"push $0\n"
"push %[TargetFrame]\n"
"call _RtlUnwind@16\n"
"pop %%ebp\n"
: "=a" (ClobberedEax)
: [TargetFrame] "a" (RegistrationFrame)
: "ebx", "ecx", "edx", "esi",
"edi", "flags", "memory");
}
EXCEPTION_DISPOSITION
__cdecl
@ -192,6 +238,10 @@ _SEH3$_except_handler(
/* Clear the direction flag. */
asm volatile ("cld\n" : : : "memory");
/* Save the exception pointers on the stack */
ExceptionPointers.ExceptionRecord = ExceptionRecord;
ExceptionPointers.ContextRecord = ContextRecord;
/* Check if this is an unwind */
if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)
{
@ -200,10 +250,6 @@ _SEH3$_except_handler(
}
else
{
/* Save the exception pointers on the stack */
ExceptionPointers.ExceptionRecord = ExceptionRecord;
ExceptionPointers.ContextRecord = ContextRecord;
/* Loop all frames for this registration */
CurrentFrame = EstablisherFrame->EndOfChain;
for (;;)

View file

@ -7,9 +7,10 @@
#define SEH3_REGISTRATION_FRAME_ExceptionPointers 16
#define SEH3_REGISTRATION_FRAME_Esp 20
#define SEH3_REGISTRATION_FRAME_Ebp 24
#define SEH3_REGISTRATION_FRAME_Ebx 28
#define SEH3_REGISTRATION_FRAME_Esi 32
#define SEH3_REGISTRATION_FRAME_Edi 36
#define SEH3_REGISTRATION_FRAME_AllocaFrame 28
#define SEH3_REGISTRATION_FRAME_Ebx 32
#define SEH3_REGISTRATION_FRAME_Esi 36
#define SEH3_REGISTRATION_FRAME_Edi 40
#define SEH3_SCOPE_TABLE_Target 0
#define SEH3_SCOPE_TABLE_Filter 4

View file

@ -15,11 +15,12 @@
/*
* void
* __attribute__((regparm(2)))
* __attribute__((regparm(3)))
* __attribute__((returns_twice))
* _SEH3$_RegisterFrame[WithNonVolatiles](
* PSEH3$_REGISTRATION_FRAME RegistrationFrame<eax>,
* PSEH3$_SCOPE_TABLE ScopeTable<edx>);
* PSEH3$_SCOPE_TABLE ScopeTable<edx>,
* PVOID AllocaFrame);
*/
.global __SEH3$_RegisterFrameWithNonVolatiles
__SEH3$_RegisterFrameWithNonVolatiles:
@ -29,6 +30,12 @@ __SEH3$_RegisterFrameWithNonVolatiles:
mov [eax + SEH3_REGISTRATION_FRAME_Esi], esi
mov [eax + SEH3_REGISTRATION_FRAME_Edi], edi
.global __SEH3$_RegisterFrameWithStackLayout
__SEH3$_RegisterFrameWithStackLayout:
/* Save the pointer to the alloca frame */
mov [eax + SEH3_REGISTRATION_FRAME_AllocaFrame], ecx
.global __SEH3$_RegisterFrame
__SEH3$_RegisterFrame:
@ -57,11 +64,12 @@ __SEH3$_RegisterFrame:
/*
* void
* __attribute__((regparm(2)))
* __attribute__((regparm(3)))
* __attribute__((returns_twice))
* _SEH3$_RegisterTryLevel[WithNonVolatiles](
* PSEH3$_REGISTRATION_FRAME RegistrationFrame<edx>,
* PSEH3$_SCOPE_TABLE ScopeTable<eax>);
* PSEH3$_SCOPE_TABLE ScopeTable<eax>,
* PVOID AllocaFrame);
*/
.global __SEH3$_RegisterTryLevelWithNonVolatiles
__SEH3$_RegisterTryLevelWithNonVolatiles:
@ -71,6 +79,12 @@ __SEH3$_RegisterTryLevelWithNonVolatiles:
mov [eax + SEH3_REGISTRATION_FRAME_Esi], esi
mov [eax + SEH3_REGISTRATION_FRAME_Edi], edi
.global __SEH3$_RegisterTryLevelWithStackLayout
__SEH3$_RegisterTryLevelWithStackLayout:
/* Save the pointer to the alloca frame */
mov [eax + SEH3_REGISTRATION_FRAME_AllocaFrame], ecx
.global __SEH3$_RegisterTryLevel
__SEH3$_RegisterTryLevel:
@ -98,3 +112,105 @@ __SEH3$_RegisterTryLevel:
xor eax, eax
ret
.global __SEH3$_InvokeEmbeddedFilterFromRegistration
__SEH3$_InvokeEmbeddedFilterFromRegistration:
/* Safe the current non-volatiles */
push ebp
push ebx
push esi
push edi
/* Load the non-volatiles from the registration invocation */
mov ebx, [eax + SEH3_REGISTRATION_FRAME_Ebx]
mov esi, [eax + SEH3_REGISTRATION_FRAME_Esi]
mov edi, [eax + SEH3_REGISTRATION_FRAME_Edi]
mov ebp, [eax + SEH3_REGISTRATION_FRAME_Ebp]
/* Get the saved stack pointer */
mov edx, [eax + SEH3_REGISTRATION_FRAME_Esp]
xor eax, eax
inc eax
call [edx]
/* Restore the current non-volatiles */
pop edi
pop esi
pop ebx
pop ebp
ret
.global __SEH3$_InvokeEmbeddedFilter
__SEH3$_InvokeEmbeddedFilter:
/* Safe the current non-volatiles */
push ebp
push ebx
push esi
push edi
/* Load ebp from the registration invocation */
mov ebp, [eax + SEH3_REGISTRATION_FRAME_Ebp]
/* Calculate the size of the temp stack frame region */
mov ecx, [eax + SEH3_REGISTRATION_FRAME_AllocaFrame]
sub ecx, [eax + SEH3_REGISTRATION_FRAME_Esp]
/* Put the return address on the stack */
push offset __SEH3$_InvokeEmbeddedFilterReturn
/* Save the current stack pointer in the AllocaFrame member */
mov [eax + SEH3_REGISTRATION_FRAME_AllocaFrame], esp
/* Allocate enough temp stack space on the stack */
sub esp, ecx
/* Get the scope table */
mov edx, [eax + SEH3_REGISTRATION_FRAME_ScopeTable]
/* Jump into the filter or finally function */
jmp [edx + SEH3_SCOPE_TABLE_Filter]
/* We return to this label with a cleaned up stack */
__SEH3$_InvokeEmbeddedFilterReturn:
/* Restore the current non-volatiles */
pop edi
pop esi
pop ebx
pop ebp
ret
/*
* void
* __fastcall
* _SEH3$_CallRtlUnwind(
* PSEH3$_REGISTRATION_FRAME RegistrationFrame<ecx>)
*/
.global @_SEH3$_CallRtlUnwind@4
@_SEH3$_CallRtlUnwind@4:
push ebp
mov ebp, esp
push edi
push esi
push ebx
push 0 /* ReturnValue */
push 0 /* ExceptionRecord */
push 0 /* TargetIp */
push ecx /* TargetFrame */
call _RtlUnwind@16
pop ebx
pop esi
pop edi
pop ebp
ret