From 7165a7c35b83411cffbaf9d57d5f22a86e333e5f Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Sun, 9 Mar 2014 13:55:26 +0000 Subject: [PATCH] [PSEH3] - 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 --- reactos/include/reactos/libs/pseh/pseh3.h | 76 ++++++++----- reactos/lib/pseh/i386/pseh3.c | 122 ++++++++++++++------- reactos/lib/pseh/i386/pseh3_asmdef.h | 7 +- reactos/lib/pseh/i386/pseh3_i386.S | 124 +++++++++++++++++++++- 4 files changed, 255 insertions(+), 74 deletions(-) diff --git a/reactos/include/reactos/libs/pseh/pseh3.h b/reactos/include/reactos/libs/pseh/pseh3.h index cc82a0d5b04..66d8e9bd998 100644 --- a/reactos/include/reactos/libs/pseh/pseh3.h +++ b/reactos/include/reactos/libs/pseh/pseh3.h @@ -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) diff --git a/reactos/lib/pseh/i386/pseh3.c b/reactos/lib/pseh/i386/pseh3.c index 47d742d3f98..7e7d438ea1f 100644 --- a/reactos/lib/pseh/i386/pseh3.c +++ b/reactos/lib/pseh/i386/pseh3.c @@ -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 (;;) diff --git a/reactos/lib/pseh/i386/pseh3_asmdef.h b/reactos/lib/pseh/i386/pseh3_asmdef.h index f8d847eb323..87d126fa8ca 100644 --- a/reactos/lib/pseh/i386/pseh3_asmdef.h +++ b/reactos/lib/pseh/i386/pseh3_asmdef.h @@ -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 diff --git a/reactos/lib/pseh/i386/pseh3_i386.S b/reactos/lib/pseh/i386/pseh3_i386.S index bd37a74642f..8e409fa2406 100644 --- a/reactos/lib/pseh/i386/pseh3_i386.S +++ b/reactos/lib/pseh/i386/pseh3_i386.S @@ -15,11 +15,12 @@ /* * void - * __attribute__((regparm(2))) + * __attribute__((regparm(3))) * __attribute__((returns_twice)) * _SEH3$_RegisterFrame[WithNonVolatiles]( * PSEH3$_REGISTRATION_FRAME RegistrationFrame, - * PSEH3$_SCOPE_TABLE ScopeTable); + * PSEH3$_SCOPE_TABLE ScopeTable, + * 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, - * PSEH3$_SCOPE_TABLE ScopeTable); + * PSEH3$_SCOPE_TABLE ScopeTable, + * 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) + */ +.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 +