[PSEH3]
authorTimo Kreuzer <timo.kreuzer@reactos.org>
Sun, 9 Sep 2012 21:12:00 +0000 (21:12 +0000)
committerTimo Kreuzer <timo.kreuzer@reactos.org>
Sun, 9 Sep 2012 21:12:00 +0000 (21:12 +0000)
Implement PSEH 3.0. Currently disabled by default. Dedicated to Amine Khaldi.

svn path=/trunk/; revision=57259

reactos/CMakeLists.txt
reactos/cmake/config.cmake
reactos/include/reactos/libs/pseh/pseh2.h
reactos/include/reactos/libs/pseh/pseh3.h [new file with mode: 0644]
reactos/lib/pseh/CMakeLists.txt
reactos/lib/pseh/i386/pseh3.c [new file with mode: 0644]
reactos/lib/pseh/i386/pseh3_asmdef.h [new file with mode: 0644]
reactos/lib/pseh/i386/pseh3_i386.S [new file with mode: 0644]

index 71fb4da..2393f5a 100644 (file)
@@ -122,6 +122,10 @@ else()
         add_definitions(-D_WINKD_=1)
     endif()
 
         add_definitions(-D_WINKD_=1)
     endif()
 
+    if(USE_PSEH3)
+        add_definitions(-D_USE_PSEH3=1)
+    endif()
+
     # Version Options
     add_definitions(-DWINVER=0x502
                     -D_WIN32_IE=0x600
     # Version Options
     add_definitions(-DWINVER=0x502
                     -D_WIN32_IE=0x600
index 59ce32a..3c28265 100644 (file)
@@ -44,7 +44,7 @@ if(MSVC)
     else()
         set(_WINKD_ TRUE CACHE BOOL "Whether to compile with the KD protocol.")
     endif()
     else()
         set(_WINKD_ TRUE CACHE BOOL "Whether to compile with the KD protocol.")
     endif()
-    
+
 else()
     set(KDBG TRUE CACHE BOOL
 "Whether to compile in the integrated kernel debugger.")
 else()
     set(KDBG TRUE CACHE BOOL
 "Whether to compile in the integrated kernel debugger.")
@@ -75,4 +75,9 @@ set(_PREFAST_ FALSE CACHE BOOL
 set(_VS_ANALYZE_ FALSE CACHE BOOL
 "Whether to enable static analysis while compiling.")
 
 set(_VS_ANALYZE_ FALSE CACHE BOOL
 "Whether to enable static analysis while compiling.")
 
+else()
+
+set(USE_PSEH3 FALSE CACHE BOOL
+"Whether to use the new PSEH3 library (requires GCC 4.5 and newer).")
+
 endif()
 endif()
index 2bf817f..f74805d 100644 (file)
@@ -23,7 +23,7 @@
 #ifndef KJK_PSEH2_H_
 #define KJK_PSEH2_H_
 
 #ifndef KJK_PSEH2_H_
 #define KJK_PSEH2_H_
 
-#if defined(USE_NATIVE_SEH) || defined(_MSC_VER)
+#if defined(_USE_NATIVE_SEH) || defined(_MSC_VER)
 
 #include <excpt.h>
 #define _SEH2_TRY __try
 
 #include <excpt.h>
 #define _SEH2_TRY __try
@@ -36,7 +36,7 @@
 #define _SEH2_YIELD(STMT_) STMT_
 #define _SEH2_LEAVE __leave
 
 #define _SEH2_YIELD(STMT_) STMT_
 #define _SEH2_LEAVE __leave
 
-#elif defined(USE_DUMMY_PSEH) || defined (__arm__) || defined(__clang__) || defined(_M_AMD64)
+#elif defined(_USE_DUMMY_PSEH) || defined (__arm__) || defined(__clang__) || defined(_M_AMD64)
 
 #define _SEH2_TRY  {
 #define _SEH2_FINALLY }  {
 
 #define _SEH2_TRY  {
 #define _SEH2_FINALLY }  {
 #define _SEH2_YIELD(STMT_) STMT_
 #define _SEH2_LEAVE
 
 #define _SEH2_YIELD(STMT_) STMT_
 #define _SEH2_LEAVE
 
+#elif defined(_USE_PSEH3)
+
+#include "pseh3.h"
+
+/* Compatibility macros */
+#define _SEH2_TRY _SEH3_TRY
+#define _SEH2_EXCEPT _SEH3_EXCEPT
+#define _SEH2_FINALLY _SEH3_FINALLY
+#define _SEH2_END _SEH3_END
+#define _SEH2_GetExceptionInformation() ((struct _EXCEPTION_POINTERS*)_exception_info())
+#define _SEH2_GetExceptionCode _exception_code
+#define _SEH2_AbnormalTermination _abnormal_termination
+#define _SEH2_LEAVE _SEH3_LEAVE
+#define _SEH2_YIELD(x) x
+
 #elif defined(__GNUC__)
 
 struct _EXCEPTION_RECORD;
 #elif defined(__GNUC__)
 
 struct _EXCEPTION_RECORD;
diff --git a/reactos/include/reactos/libs/pseh/pseh3.h b/reactos/include/reactos/libs/pseh/pseh3.h
new file mode 100644 (file)
index 0000000..8d94e9c
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * PROJECT:         ReactOS system libraries
+ * LICENSE:         GNU GPL - See COPYING in the top level directory
+ * PURPOSE:         Header for PSEH3
+ * PROGRAMMER:      Timo Kreuzer (timo.kreuzer@reactos.org)
+ */
+
+/* For additional information see pseh3.c in the related library. */
+
+#pragma once
+#define _PSEH3_H_
+
+#include "excpt.h"
+
+typedef struct _SEH3$_SCOPE_TABLE
+{
+    void *Target;
+    void *Filter;
+} SEH3$_SCOPE_TABLE, *PSEH3$_SCOPE_TABLE;
+
+typedef struct _SEH3$_EXCEPTION_POINTERS
+{
+    struct _EXCEPTION_RECORD *ExceptionRecord;
+    struct _CONTEXT *ContextRecord;
+} SEH3$_EXCEPTION_POINTERS, *PSEH3$_EXCEPTION_POINTERS;
+
+typedef struct _SEH3$_REGISTRATION_FRAME
+{
+    /* First the Windows base record. Don't move this! */
+    struct _SEH3$_REGISTRATION_FRAME *Next;
+    void *Handler;
+
+    /* Points to the end of the internal registration chain */
+    struct _SEH3$_REGISTRATION_FRAME *EndOfChain;
+
+    /* Pointer to the static scope table */
+    PSEH3$_SCOPE_TABLE ScopeTable;
+
+    /* Except handler stores pointer to exception pointers here */
+    PSEH3$_EXCEPTION_POINTERS volatile ExceptionPointers;
+
+    /* Registers that we need to save */
+    unsigned long Esp;
+    unsigned long Ebp;
+
+} SEH3$_REGISTRATION_FRAME ,*PSEH3$_REGISTRATION_FRAME;
+
+extern inline __attribute__((always_inline,gnu_inline))
+void _SEH3$_UnregisterFrame(volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame)
+{
+    asm volatile ("movl %k[NewHead], %%fs:0"
+                  : : [NewHead] "ir" (RegistrationFrame->Next) : "memory");
+}
+
+extern inline __attribute__((always_inline,gnu_inline))
+void _SEH3$_UnregisterTryLevel(
+    volatile SEH3$_REGISTRATION_FRAME *TrylevelFrame)
+{
+    volatile SEH3$_REGISTRATION_FRAME *RegistrationFrame;
+    asm volatile ("movl %%fs:0, %k[RegistrationFrame]"
+                  : [RegistrationFrame] "=r" (RegistrationFrame) : );
+    RegistrationFrame->EndOfChain = TrylevelFrame->Next;
+}
+
+enum
+{
+    _SEH3$_TryLevel = 0,
+};
+
+/* 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);
+
+/* Define the registers that get clobbered, when reaching the __except block.
+   We specify ebp on optimized builds without frame pointer, since it will be
+   used by GCC as a general purpose register then. */
+#if defined(__OPTIMIZE__) && defined(_ALLOW_OMIT_FRAME_POINTER)
+#define _SEH3$_CLOBBER_ON_EXCEPTION "ebp", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"
+#else
+#define _SEH3$_CLOBBER_ON_EXCEPTION "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"
+#endif
+
+/* This attribute allows automatic cleanup of the registered frames */
+#define _SEH3$_AUTO_CLEANUP __attribute__((cleanup(_SEH3$_AutoCleanup)))
+
+#define _SEH3$_ASM_GOTO(_Asm, _Label, ...) asm goto (_Asm : : : "memory", ## __VA_ARGS__ : _Label)
+
+#define _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
+    inline __attribute__((always_inline, gnu_inline)) \
+    unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; } \
+
+/* This is an asm wrapper around _SEH3$_RegisterFrame */
+#define _SEH3$_RegisterFrame(_TrylevelFrame, _DataTable, _Target) \
+    asm goto ("call __SEH3$_RegisterFrame\n" \
+              : \
+              : "c" (_TrylevelFrame), "a" (_DataTable) \
+              : "edx", "memory" \
+              : _Target)
+
+/* This is an asm wrapper around _SEH3$_EnterTryLevel */
+#define _SEH3$_RegisterTryLevel(_TrylevelFrame, _DataTable, _Target) \
+    asm goto ("call __SEH3$_RegisterTryLevel\n" \
+              : \
+              : "c" (_TrylevelFrame), "a" (_DataTable) \
+              : "edx", "memory" \
+              : _Target)
+
+/* On GCC the filter function is a nested function with __fastcall calling
+   convention. The eax register contains a base address the function uses
+   to address the callers stack frame. __fastcall is chosen, because it gives
+   us an effective was of passing one argument to the function, that we need
+   to tell the function in a first pass to return informtion about the frame
+   base address. Since this is something GCC chooses arbitrarily, we call
+   the function with an arbitrary base address in eax first and then use the
+   result to calculate the correct address for a second call to the function. */
+#define _SEH3$_DECLARE_FILTER_FUNC(_Name) \
+    auto int __fastcall _Name(int Action)
+
+#define _SEH3$_NESTED_FUNC_OPEN(_Name) \
+    int __fastcall _Name(int Action) \
+    { \
+        /* This is a fancy way to get information about the frame layout */ \
+        if (Action == 0) return (int)&_SEH3$_TrylevelFrame;
+
+#define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
+    _SEH3$_NESTED_FUNC_OPEN(_Name) \
+        /* Declare the intrinsics for exception filters */ \
+        inline __attribute__((always_inline, gnu_inline)) \
+        unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionPointers->ExceptionRecord->ExceptionCode; } \
+        inline __attribute__((always_inline, gnu_inline)) \
+        void * _exception_info() { return _SEH3$_TrylevelFrame.ExceptionPointers; } \
+\
+        /* Now handle the actual filter expression */ \
+        return (expression); \
+    }
+
+#define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
+    _SEH3$_NESTED_FUNC_OPEN(_Name) \
+        /* Declare the intrinsics for the finally function */ \
+        inline __attribute__((always_inline, gnu_inline)) \
+        int _abnormal_termination() { return (_SEH3$_TrylevelFrame.ScopeTable != 0); } \
+
+#define _SEH3$_FILTER(_Filter, _FilterExpression) \
+    (__builtin_constant_p(_FilterExpression) ? (void*)(unsigned long)(unsigned char)(unsigned long)(_FilterExpression) : _Filter)
+
+#define _SEH3$_DEFINE_DUMMY_FINALLY(_Name) \
+    auto inline __attribute__((always_inline,gnu_inline)) int _Name(int Action) { return 0; }
+
+#define _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
+    auto inline __attribute__((always_inline,gnu_inline)) void _Name(volatile SEH3$_REGISTRATION_FRAME *p)
+
+#define _SEH3$_DEFINE_CLEANUP_FUNC(_Name) \
+    _SEH3$_DECLARE_CLEANUP_FUNC(_Name) \
+    { \
+        /* Unregister the frame */ \
+        if (_SEH3$_TryLevel == 1) _SEH3$_UnregisterFrame(&_SEH3$_TrylevelFrame); \
+        else _SEH3$_UnregisterTryLevel(&_SEH3$_TrylevelFrame); \
+\
+        /* Invoke the finally function (an inline dummy in the __except case) */ \
+        _SEH3$_FinallyFunction(1); \
+    }
+
+/* This construct scares GCC so much, that it will stop moving code
+   around into places that are never executed. */
+#define _SEH3$_SCARE_GCC() \
+        void *plabel; \
+        _SEH3$_ASM_GOTO("#\n", _SEH3$_l_HandlerTarget); \
+        asm volatile ("#" : "=a"(plabel) : "p"(&&_SEH3$_l_BeforeTry), "p"(&&_SEH3$_l_HandlerTarget), "p"(&&_SEH3$_l_OnException) \
+                      : _SEH3$_CLOBBER_ON_EXCEPTION ); \
+        goto *plabel;
+
+
+#define _SEH3_TRY \
+    /* Enter the outer scope */ \
+    do { \
+        /* Declare local labels */ \
+        __label__ _SEH3$_l_BeforeTry; \
+        __label__ _SEH3$_l_DoTry; \
+        __label__ _SEH3$_l_AfterTry; \
+        __label__ _SEH3$_l_EndTry; \
+        __label__ _SEH3$_l_HandlerTarget; \
+        __label__ _SEH3$_l_OnException; \
+\
+        /* Count the try level. Outside of any __try, _SEH3$_TryLevel is 0 */ \
+        enum { \
+            _SEH3$_PreviousTryLevel = _SEH3$_TryLevel, \
+            _SEH3$_TryLevel = _SEH3$_PreviousTryLevel + 1, \
+        }; \
+\
+        /* Forward declaration of the auto cleanup function */ \
+        _SEH3$_DECLARE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
+\
+        /* Allocate a registration frame */ \
+        volatile SEH3$_REGISTRATION_FRAME _SEH3$_AUTO_CLEANUP _SEH3$_TrylevelFrame; \
+\
+        goto _SEH3$_l_BeforeTry; \
+        /* Silence warning */ goto _SEH3$_l_AfterTry; \
+\
+    _SEH3$_l_DoTry: \
+        do
+
+
+#define _SEH3_EXCEPT(...) \
+        /* End the try block */ \
+        while (0); \
+    _SEH3$_l_AfterTry: (void)0; \
+        goto _SEH3$_l_EndTry; \
+\
+    _SEH3$_l_BeforeTry: (void)0; \
+        _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
+\
+        /* Forward declaration of the filter function */ \
+        _SEH3$_DECLARE_FILTER_FUNC(_SEH3$_FilterFunction); \
+\
+        /* Create a static data table that contains the jump target and filter function */ \
+        static const SEH3$_SCOPE_TABLE _SEH3$_ScopeTable = { &&_SEH3$_l_HandlerTarget, _SEH3$_FILTER(&_SEH3$_FilterFunction, (__VA_ARGS__)) }; \
+\
+        /* Register the registration record. */ \
+        if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
+        else _SEH3$_RegisterTryLevel(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
+\
+        /* Emit the filter function */ \
+        _SEH3$_DEFINE_FILTER_FUNC(_SEH3$_FilterFunction, (__VA_ARGS__)) \
+\
+        /* Define an empty inline finally function */ \
+        _SEH3$_DEFINE_DUMMY_FINALLY(_SEH3$_FinallyFunction) \
+\
+        /* Allow intrinsics for __except to be used */ \
+        _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
+\
+        goto _SEH3$_l_DoTry; \
+\
+    _SEH3$_l_HandlerTarget: (void)0; \
+\
+        if (1) \
+        { \
+            do
+
+
+#define _SEH3_FINALLY \
+        /* End the try block */ \
+        while (0); \
+    _SEH3$_l_AfterTry: (void)0; \
+        /* Set ScopeTable to 0, this is used by _abnormal_termination() */ \
+         _SEH3$_TrylevelFrame.ScopeTable = 0; \
+\
+        goto _SEH3$_l_EndTry; \
+\
+    _SEH3$_l_BeforeTry: (void)0; \
+        _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
+\
+        /* Forward declaration of the finally function */ \
+        _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$_FinallyFunction }; \
+\
+        /* Register the registration record. */ \
+        if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
+        else _SEH3$_RegisterTryLevel(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable, _SEH3$_l_HandlerTarget); \
+\
+        goto _SEH3$_l_DoTry; \
+\
+    _SEH3$_l_HandlerTarget: (void)0; \
+\
+        _SEH3$_FINALLY_FUNC_OPEN(_SEH3$_FinallyFunction) \
+            /* This construct makes sure that the finally function returns */ \
+            /* a proper value at the end */ \
+            for (; ; (void)({return 0; 0;}))
+
+
+#define _SEH3_END \
+            while (0); \
+        }; \
+        goto _SEH3$_l_EndTry; \
+\
+    _SEH3$_l_OnException: (void)0; \
+        /* Force GCC to create proper code pathes */ \
+        _SEH3$_SCARE_GCC() \
+\
+    _SEH3$_l_EndTry:(void)0; \
+        _SEH3$_ASM_GOTO("#\n", _SEH3$_l_OnException); \
+\
+        /* Implementation of the auto cleanup function */ \
+        _SEH3$_DEFINE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
+\
+    /* Close the outer scope */ \
+    } while (0);
+
+#define _SEH3_LEAVE goto _SEH3$_l_AfterTry
+
index 7f3bc53..1e957bf 100644 (file)
@@ -1,8 +1,12 @@
 
 if(NOT MSVC)
 
 
 if(NOT MSVC)
 
-    list(APPEND SOURCE framebased.c)
-    if(ARCH STREQUAL "i386")
+    if (USE_PSEH3)
+        include_directories(${REACTOS_SOURCE_DIR}/include/reactos/libs/pseh)
+        list(APPEND SOURCE
+            i386/pseh3.c
+            i386/pseh3_i386.S)
+    elseif(ARCH STREQUAL "i386")
         list(APPEND SOURCE
             i386/framebased.S
             i386/framebased-gcchack.c
         list(APPEND SOURCE
             i386/framebased.S
             i386/framebased-gcchack.c
diff --git a/reactos/lib/pseh/i386/pseh3.c b/reactos/lib/pseh/i386/pseh3.c
new file mode 100644 (file)
index 0000000..d748594
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * PROJECT:         ReactOS system libraries
+ * LICENSE:         GNU GPL - See COPYING in the top level directory
+ * PURPOSE:         Support library for PSEH3
+ * PROGRAMMER:      Timo Kreuzer (timo.kreuzer@reactos.org)
+ */
+
+/*
+ * - Naming: To avoid naming conflicts, all internal identifiers are prefixed
+ *   with _SEH3$_.
+ * - Frame graph: PSEH3 uses the same registration frame for every trylevel.
+ *   Only the top trylevel is registered in FS:0, the inner trylevels are linked
+ *   to the first trylevel frame. Only the first trylevel frame has the Handler
+ *   member set, it's 0 for all others as an identification. The EndOfChain
+ *   member of the FS:0 registered frame points to the last internal frame,
+ *   which is the frame itself, when only 1 trylevel is present.
+ *
+ * The registration graph looks like this:
+ *
+ *              newer handlers
+ *             ---------------->
+ *
+ *                       fs:0             /----------------\
+ * |-----------|<-\  |-----------|<-\    / |----------|<-\ \->|----------|
+ * |  <Next>   |   \-|  <Next>   |   \--/--|  <Next>  |   \---|  <Next>  |
+ * | <Handler> |     | <Handler> |     /   |  <NULL>  |       |  <NULL>  |
+ * |-----------|     |-----------|    /    |----------|       |----------|
+ *                   |EndOfChain |---/
+ *                   |   ...     |
+ *                   |-----------|
+ */
+
+#include <stdarg.h>
+#include <windef.h>
+#include <winnt.h>
+
+#include "pseh3.h"
+#include "pseh3_asmdef.h"
+
+/* Make sure the asm definitions match the structures */
+C_ASSERT(SEH3_REGISTRATION_FRAME_Next == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Next));
+C_ASSERT(SEH3_REGISTRATION_FRAME_Handler == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Handler));
+C_ASSERT(SEH3_REGISTRATION_FRAME_EndOfChain == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, EndOfChain));
+C_ASSERT(SEH3_REGISTRATION_FRAME_ScopeTable == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, ScopeTable));
+C_ASSERT(SEH3_REGISTRATION_FRAME_ExceptionPointers == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, ExceptionPointers));
+C_ASSERT(SEH3_REGISTRATION_FRAME_Esp == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Esp));
+C_ASSERT(SEH3_REGISTRATION_FRAME_Ebp == FIELD_OFFSET(SEH3$_REGISTRATION_FRAME, Ebp));
+C_ASSERT(SEH3_SCOPE_TABLE_Filter == FIELD_OFFSET(SEH3$_SCOPE_TABLE, Filter));
+C_ASSERT(SEH3_SCOPE_TABLE_Target == FIELD_OFFSET(SEH3$_SCOPE_TABLE, Target));
+
+static inline
+void _SEH3$_Unregister(
+    volatile SEH3$_REGISTRATION_FRAME *Frame)
+{
+    if (Frame->Handler)
+        _SEH3$_UnregisterFrame(Frame);
+    else
+        _SEH3$_UnregisterTryLevel(Frame);
+}
+
+static inline
+LONG
+_SEH3$_InvokeFilter(
+    PVOID Record,
+    PVOID Filter)
+{
+    LONG FilterResult;
+
+    asm volatile (
+        /* First call with param = 0 to get the frame layout */
+        "xorl %%ecx, %%ecx\n\t"
+        "xorl %%eax, %%eax\n\t"
+        "call *%[Filter]\n\t"
+
+        /* 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"
+
+        /* Second call to get the filter result */
+        "mov $1, %%ecx\n\t"
+        "call *%[Filter]\n\t"
+        : "=a"(FilterResult)
+        : [Record] "m" (Record), [Filter] "m" (Filter)
+        : "ecx", "edx");
+
+    return FilterResult;
+}
+
+static inline
+LONG
+_SEH3$_GetFilterResult(
+    PSEH3$_REGISTRATION_FRAME Record)
+{
+    PVOID Filter = Record->ScopeTable->Filter;
+    LONG Result;
+
+    if (Record->ScopeTable->Target == NULL)
+    {
+        return EXCEPTION_CONTINUE_SEARCH;
+    }
+
+    /* Check if we have a constant filter */
+    if (((ULONG)Filter & 0xFFFFFF00) == 0)
+    {
+        /* Lowest 8 bit are sign extended to give the result */
+        Result = (LONG)(CHAR)(ULONG)Filter;
+    }
+    else
+    {
+        /* Call the filter function */
+        Result = _SEH3$_InvokeFilter(Record, Filter);
+    }
+
+    /* Normalize the result */
+    if (Result < 0) return EXCEPTION_CONTINUE_EXECUTION;
+    else if (Result > 0) return EXCEPTION_EXECUTE_HANDLER;
+    else return EXCEPTION_CONTINUE_SEARCH;
+}
+
+static inline
+VOID
+_SEH3$_CallFinally(
+    PSEH3$_REGISTRATION_FRAME Record)
+{
+    _SEH3$_InvokeFilter(Record, Record->ScopeTable->Filter);
+}
+
+__attribute__((noreturn))
+static inline
+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)
+    );
+
+    __builtin_unreachable();
+}
+
+static inline
+void
+_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");
+}
+
+EXCEPTION_DISPOSITION
+__cdecl
+__attribute__ ((__target__ ("cld")))
+_SEH3$_except_handler(
+    struct _EXCEPTION_RECORD * ExceptionRecord,
+    PSEH3$_REGISTRATION_FRAME EstablisherFrame,
+    struct _CONTEXT * ContextRecord,
+    void * DispatcherContext)
+{
+    PSEH3$_REGISTRATION_FRAME CurrentFrame, TargetFrame;
+    SEH3$_EXCEPTION_POINTERS ExceptionPointers;
+    LONG FilterResult;
+
+    /* Clear the direction flag. */
+    asm volatile ("cld\n" : : : "memory");
+
+    /* Check if this is an unwind */
+    if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWIND)
+    {
+        /* Unwind all local frames */
+        TargetFrame = EstablisherFrame->Next;
+    }
+    else
+    {
+        /* Save the exception pointers on the stack */
+        ExceptionPointers.ExceptionRecord = ExceptionRecord;
+        ExceptionPointers.ContextRecord = ContextRecord;
+
+        /* Loop all frames for this registration */
+        CurrentFrame = EstablisherFrame->EndOfChain;
+        for (;;)
+        {
+            /* Check if we have an exception handler */
+            if (CurrentFrame->ScopeTable->Target != NULL)
+            {
+                /* Set exception pointers for this frame */
+                CurrentFrame->ExceptionPointers = &ExceptionPointers;
+
+                /* Get the filter result */
+                FilterResult = _SEH3$_GetFilterResult(CurrentFrame);
+
+                /* Check, if continuuing is requested */
+                if (FilterResult == EXCEPTION_CONTINUE_EXECUTION)
+                {
+                    return ExceptionContinueExecution;
+                }
+
+                /* Check if the except handler shall be executed */
+                if (FilterResult == EXCEPTION_EXECUTE_HANDLER) break;
+            }
+
+            /* Bail out if this is the last handler */
+            if (CurrentFrame == EstablisherFrame)
+                return ExceptionContinueSearch;
+
+            /* Go to the next frame */
+            CurrentFrame = CurrentFrame->Next;
+        }
+
+        /* Call RtlUnwind to unwind the frames below this one */
+        _SEH3$_CallRtlUnwind(EstablisherFrame);
+
+        /* Do a local unwind up to this frame */
+        TargetFrame = CurrentFrame;
+    }
+
+    /* Loop frames up to the target frame */
+    for (CurrentFrame = EstablisherFrame->EndOfChain;
+         CurrentFrame != TargetFrame;
+         CurrentFrame = CurrentFrame->Next)
+    {
+        /* Manually unregister the frame */
+        _SEH3$_Unregister(CurrentFrame);
+
+        /* Check if this is an unwind frame */
+        if (CurrentFrame->ScopeTable->Target == NULL)
+        {
+            /* Set exception pointers for this frame */
+            CurrentFrame->ExceptionPointers = &ExceptionPointers;
+
+            /* Call the finally function */
+            _SEH3$_CallFinally(CurrentFrame);
+        }
+    }
+
+    /* Check if this was an unwind */
+    if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)
+    {
+        return ExceptionContinueSearch;
+    }
+
+    /* Unregister the frame. It will be unregistered again at the end of the
+       __except block, due to auto cleanup, but that doesn't hurt.
+       All we do is set either fs:[0] or EstablisherFrame->EndOfChain to
+       CurrentFrame->Next, which will not change it's value. */
+    _SEH3$_Unregister(CurrentFrame);
+
+    /* Jump to the __except block (does not return) */
+    _SEH3$_JumpToTarget(CurrentFrame);
+}
+
diff --git a/reactos/lib/pseh/i386/pseh3_asmdef.h b/reactos/lib/pseh/i386/pseh3_asmdef.h
new file mode 100644 (file)
index 0000000..6fd821c
--- /dev/null
@@ -0,0 +1,12 @@
+
+
+#define SEH3_REGISTRATION_FRAME_Next 0
+#define SEH3_REGISTRATION_FRAME_Handler 4
+#define SEH3_REGISTRATION_FRAME_EndOfChain 8
+#define SEH3_REGISTRATION_FRAME_ScopeTable 12
+#define SEH3_REGISTRATION_FRAME_ExceptionPointers 16
+#define SEH3_REGISTRATION_FRAME_Esp 20
+#define SEH3_REGISTRATION_FRAME_Ebp 24
+
+#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
new file mode 100644 (file)
index 0000000..eca7182
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * PROJECT:         ReactOS system libraries
+ * LICENSE:         GNU GPL - See COPYING in the top level directory
+ * PURPOSE:         Support library for PSEH3
+ * PROGRAMMER:      Timo Kreuzer (timo.kreuzer@reactos.org)
+ */
+
+#include "pseh3_asmdef.h"
+
+.intel_syntax noprefix
+
+.text
+
+
+.extern __SEH3$_except_handler
+
+/*
+ *  void
+ *  _SEH3$_RegisterFrame(
+ *       PSEH_REGISTRATION_FRAME RegistrationRecord<ecx>,
+ *       PSEH_DATA_TABLE DataTable<eax>);
+ */
+.global __SEH3$_RegisterFrame
+__SEH3$_RegisterFrame:
+
+    /* Save the address of the static data table */
+    mov [ecx + SEH3_REGISTRATION_FRAME_ScopeTable], eax
+
+    /* Set the handler address */
+    mov dword ptr [ecx + SEH3_REGISTRATION_FRAME_Handler], offset __SEH3$_except_handler
+
+    /* Set this as the end of the internal chain */
+    mov dword ptr [ecx + SEH3_REGISTRATION_FRAME_EndOfChain], ecx
+
+    /* Register the frame in the TEB */
+    mov eax, dword ptr fs:[0]
+    mov [ecx + SEH3_REGISTRATION_FRAME_Next], eax
+    mov dword ptr fs:[0], ecx
+
+    /* Save the registers */
+    mov [ecx + SEH3_REGISTRATION_FRAME_Esp], esp
+    mov [ecx + SEH3_REGISTRATION_FRAME_Ebp], ebp
+
+    ret
+
+
+.global __SEH3$_RegisterTryLevel
+__SEH3$_RegisterTryLevel:
+
+    /* Save the address of the static data table */
+    mov [ecx + SEH3_REGISTRATION_FRAME_ScopeTable], eax
+
+    /* Set the handler address to NULL as identification */
+    and dword ptr [ecx + SEH3_REGISTRATION_FRAME_Handler], 0
+
+    /* Get the current registered frame */
+    mov eax, dword ptr fs:[0]
+
+    /* Get the current end of the chain and set this as Next */
+    mov edx, [eax + SEH3_REGISTRATION_FRAME_EndOfChain]
+    mov [ecx + SEH3_REGISTRATION_FRAME_Next], edx
+
+    /* Set this as the end of the internal chain */
+    mov dword ptr [eax + SEH3_REGISTRATION_FRAME_EndOfChain], ecx
+
+    /* Save the registers */
+    mov [ecx + SEH3_REGISTRATION_FRAME_Esp], esp
+    mov [ecx + SEH3_REGISTRATION_FRAME_Ebp], ebp
+
+    ret
+
+