[PSEH3]
authorTimo Kreuzer <timo.kreuzer@reactos.org>
Sun, 9 Mar 2014 13:55:26 +0000 (13:55 +0000)
committerTimo Kreuzer <timo.kreuzer@reactos.org>
Sun, 9 Mar 2014 13:55:26 +0000 (13:55 +0000)
- 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
reactos/lib/pseh/i386/pseh3.c
reactos/lib/pseh/i386/pseh3_asmdef.h
reactos/lib/pseh/i386/pseh3_i386.S

index cc82a0d..66d8e9b 100644 (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)) \
-              : "ecx", "edx", "memory" \
-              : _SEH3$_l_HandlerTarget)
+              : "m" (*(_TrylevelFrame)), "m" (*(_DataTable)), "c"(__builtin_alloca(0)) \
+              : "eax", "edx", "memory" \
+              : _SEH3$_l_HandlerTarget, _SEH3$_l_FilterOrFinally)
 
-/* This is an asm wrapper around _SEH3$_RegisterTryLevel */
-#define _SEH3$_RegisterTryLevel_(_TrylevelFrame, _DataTable) \
+#else
+#define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
     asm goto ("leal %0, %%eax\n" \
               "leal %1, %%edx\n" \
-              "call __SEH3$_RegisterTryLevel\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) \
+     _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; \
 
-#define _SEH3$_NESTED_FUNC_CLOSE() \
+/* 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 %[SavedEsp], %%esp\nret\n" \
-            : : "a"(result), [SavedEsp]"irm"(SavedEsp)); \
+        asm volatile ("movl %[FixedEsp], %%esp\nret\n" \
+            : : "a"(_SEH3$_Result), [FixedEsp]"m"(_SEH3$_TrylevelFrame.AllocaFrame) : "memory")
+
+#define _SEH3$_NESTED_FUNC_CLOSE() \
+        /* 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)
 
index 47d742d..7e7d438 100644 (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"
-
-        /* 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)
-    );
+    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"
+
+            /* 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;
-
-    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");
-}
+    PSEH3$_REGISTRATION_FRAME RegistrationFrame);
+
 
 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 (;;)
index f8d847e..87d126f 100644 (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
index bd37a74..8e409fa 100644 (file)
 
 /*
  *  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
+