[PSEH] Add implementation for GCC amd64

Also, put include directory next to the library and use
target_include_directories(.. INTERFACE ..) to get this right.
This is because :
 - Having includes & implementation in two different places buggers me
 - This makes sure that there is no "if it compiles everything is fine" behaviour from anyone
   because now even static libraries need it for GCC amd64 build
Also add __USE_PSEH2__ define for the non SEH-aware compilers out there and use it in a few headers
where we define macros involving __try
This commit is contained in:
Jérôme Gardou 2021-04-22 11:11:34 +02:00 committed by Jérôme Gardou
parent d31856cda1
commit ba74a05a17
14 changed files with 230 additions and 50 deletions

View file

@ -12,13 +12,15 @@ elseif(ARCH STREQUAL "arm")
arm/seh_prolog.s)
endif()
if(MSVC OR ARCH STREQUAL "amd64")
if(MSVC OR ((CMAKE_C_COMPILER_ID STREQUAL "Clang") AND (ARCH STREQUAL "amd64")))
list(APPEND SOURCE dummy.c)
add_asm_files(pseh_asm ${ASM_SOURCE})
add_library(pseh ${SOURCE} ${pseh_asm})
add_dependencies(pseh asm)
elseif((CMAKE_C_COMPILER_ID STREQUAL "GNU") AND (ARCH STREQUAL "amd64"))
# for GCC amd64 this is just an interface library, with our home-made plugin
add_library(pseh INTERFACE)
target_compile_options(pseh INTERFACE -fplugin=$<TARGET_FILE:native-gcc_plugin_seh>)
else()
if(USE_PSEH3)
@ -43,4 +45,13 @@ else()
target_link_libraries(pseh chkstk)
add_dependencies(pseh psdk)
target_include_directories(pseh PRIVATE include/pseh)
endif()
target_include_directories(pseh INTERFACE include)
# Make it clear that we are using PSEH2
if ((CMAKE_C_COMPILER_ID STREQUAL "GNU") OR
((CMAKE_C_COMPILER_ID STREQUAL "Clang") AND (NOT (ARCH STREQUAL "amd64"))))
target_compile_definitions(pseh INTERFACE __USE_PSEH2__)
endif()

View file

@ -0,0 +1,32 @@
/*
Copyright (c) 2004/2005 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.
*/
#ifndef KJK_PSEH_EXCPT_H_
#define KJK_PSEH_EXCPT_H_
#define _SEH_CONTINUE_EXECUTION (-1)
#define _SEH_CONTINUE_SEARCH (0)
#define _SEH_EXECUTE_HANDLER (1)
#endif
/* EOF */

View file

@ -0,0 +1,377 @@
/*
Copyright (c) 2004/2005 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.
*/
#ifndef KJK_PSEH_FRAMEBASED_H_
#define KJK_PSEH_FRAMEBASED_H_
#if ((__GNUC__ > 4) && (__GNUC_MINOR__ > 1))
/* warning: this will hide uninitialized variable warnings in the following code */
# pragma GCC diagnostic ignored "-Wuninitialized"
#endif
#include <pseh/framebased/internal.h>
#include <pseh/excpt.h>
#ifndef offsetof
# include <stddef.h>
#endif
#if defined(_SEH_NO_NATIVE_NLG)
# error PSEH setjmp/longjmp fallback is no longer supported
#endif
#if defined(__GNUC__)
# define _SEHLongJmp __builtin_longjmp
# define _SEHSetJmp __builtin_setjmp
typedef void * _SEHJmpBuf_t[5];
#else
# include <setjmp.h>
# define _SEHLongJmp longjmp
# define _SEHSetJmp setjmp
# define _SEHJmpBuf_t jmp_buf
#endif
#ifdef __cplusplus
# define _SEH_INIT_CONST static const
#else
# define _SEH_INIT_CONST register const
#endif
typedef struct __SEHFrame
{
_SEHPortableFrame_t SEH_Header;
void * volatile SEH_Locals;
}
_SEHFrame_t;
typedef struct __SEHTryLevel
{
_SEHPortableTryLevel_t ST_Header;
_SEHJmpBuf_t ST_JmpBuf;
}
_SEHTryLevel_t;
static __declspec(noreturn) __inline void __stdcall _SEHCompilerSpecificHandler
(
_SEHPortableTryLevel_t * trylevel
)
{
_SEHTryLevel_t * mytrylevel;
mytrylevel = _SEH_CONTAINING_RECORD(trylevel, _SEHTryLevel_t, ST_Header);
_SEHLongJmp(mytrylevel->ST_JmpBuf, 1);
}
static const int _SEHScopeKind = 1;
static _SEHPortableFrame_t * const _SEHPortableFrame = 0;
static _SEHPortableTryLevel_t * const _SEHPortableTryLevel = 0;
/* SHARED LOCALS */
/* Access the locals for the current frame */
#define _SEH_ACCESS_LOCALS(LOCALS_) \
_SEH_LOCALS_TYPENAME(LOCALS_) * _SEHPLocals; \
_SEHPLocals = \
_SEH_PVOID_CAST \
( \
_SEH_LOCALS_TYPENAME(LOCALS_) *, \
_SEH_CONTAINING_RECORD(_SEHPortableFrame, _SEHFrame_t, SEH_Header) \
->SEH_Locals \
);
/* Access local variable VAR_ */
#define _SEH_VAR(VAR_) _SEHPLocals->VAR_
/* FILTER FUNCTIONS */
/* Declares a filter function's prototype */
#define _SEH_FILTER(NAME_) \
long __stdcall NAME_ \
( \
struct _EXCEPTION_POINTERS * _SEHExceptionPointers, \
struct __SEHPortableFrame * _SEHPortableFrame \
)
/* Declares a static filter */
#define _SEH_STATIC_FILTER(ACTION_) ((_SEHFilter_t)((ACTION_) + 2))
/* Declares a PSEH filter wrapping a regular filter function */
#define _SEH_WRAP_FILTER(WRAPPER_, NAME_) \
static __inline _SEH_FILTER(WRAPPER_) \
{ \
return (NAME_)(_SEHExceptionPointers); \
}
/* FINALLY FUNCTIONS */
/* Declares a finally function's prototype */
#define _SEH_FINALLYFUNC(NAME_) \
void __stdcall NAME_ \
( \
struct __SEHPortableFrame * _SEHPortableFrame \
)
/* Declares a PSEH finally function wrapping a regular function */
#define _SEH_WRAP_FINALLY(WRAPPER_, NAME_) \
_SEH_WRAP_FINALLY_ARGS(WRAPPER_, NAME_, ())
#define _SEH_WRAP_FINALLY_ARGS(WRAPPER_, NAME_, ARGS_) \
static __inline _SEH_FINALLYFUNC(WRAPPER_) \
{ \
NAME_ ARGS_; \
}
#define _SEH_WRAP_FINALLY_LOCALS_ARGS(WRAPPER_, LOCALS_, NAME_, ARGS_) \
static __inline _SEH_FINALLYFUNC(WRAPPER_) \
{ \
_SEH_ACCESS_LOCALS(LOCALS_); \
NAME_ ARGS_; \
}
/* SAFE BLOCKS */
#ifdef __cplusplus
# define _SEH_DECLARE_HANDLERS(FILTER_, FINALLY_) \
static const _SEHHandlers_t _SEHHandlers = { (FILTER_), (FINALLY_) };
#else
# define _SEH_DECLARE_HANDLERS(FILTER_, FINALLY_) \
_SEHHandlers_t _SEHHandlers = { (0), (0) }; \
_SEHHandlers.SH_Filter = (FILTER_); \
_SEHHandlers.SH_Finally = (FINALLY_);
#endif
#define _SEH_SetExceptionCode(CODE_) (_SEHPortableFrame->SPF_Code = (CODE_))
#define _SEH_GetExceptionCode() (unsigned long)(_SEHPortableFrame->SPF_Code)
#define _SEH_GetExceptionPointers() \
((struct _EXCEPTION_POINTERS *)_SEHExceptionPointers)
#define _SEH_AbnormalTermination() (_SEHPortableFrame->SPF_Code != 0)
#define _SEH_LEAVE break
#define _SEH_YIELD(STMT_) \
for(;;) \
{ \
if(!_SEHScopeKind) \
_SEHReturn(); \
\
STMT_; \
}
#ifdef _ARM_
#define _SEH_TRY \
for(;;) \
{ \
\
{ \
\
for(;;) \
{ \
if(1) \
{ \
for(;;) \
{ \
{
#define _SEH_EXCEPT(FILTER_) \
} \
\
break; \
} \
\
break; \
} \
else \
{ \
{ \
break; \
} \
} \
\
break; \
} \
\
\
if(0) \
{
#define _SEH_FINALLY(FINALLY_) \
} \
\
break; \
} \
\
break; \
} \
else \
{ \
} \
\
break; \
} \
\
(FINALLY_)(&_SEHFrame.SEH_Header); \
\
if(0) \
{
#define _SEH_END \
} \
} \
\
\
break; \
}
#else
#define _SEH_TRY \
for(;;) \
{ \
_SEH_INIT_CONST int _SEHTopTryLevel = (_SEHScopeKind != 0); \
_SEHPortableFrame_t * const _SEHCurPortableFrame = _SEHPortableFrame; \
_SEHPortableTryLevel_t * const _SEHPrevPortableTryLevel = _SEHPortableTryLevel; \
\
{ \
_SEH_INIT_CONST int _SEHScopeKind = 0; \
register int _SEHState = 0; \
register int _SEHHandle = 0; \
_SEHFrame_t _SEHFrame; \
_SEHTryLevel_t _SEHTryLevel; \
_SEHPortableFrame_t * const _SEHPortableFrame = \
_SEHTopTryLevel ? &_SEHFrame.SEH_Header : _SEHCurPortableFrame; \
_SEHPortableTryLevel_t * const _SEHPortableTryLevel = &_SEHTryLevel.ST_Header; \
\
(void)_SEHScopeKind; \
(void)_SEHPortableFrame; \
(void)_SEHPortableTryLevel; \
(void)_SEHHandle; \
\
for(;;) \
{ \
if(_SEHState) \
{ \
for(;;) \
{ \
{
#define _SEH_EXCEPT(FILTER_) \
} \
\
break; \
} \
\
break; \
} \
else \
{ \
if((_SEHHandle = _SEHSetJmp(_SEHTryLevel.ST_JmpBuf)) == 0) \
{ \
_SEHTryLevel.ST_Header.SPT_Handlers.SH_Filter = (FILTER_); \
_SEHTryLevel.ST_Header.SPT_Handlers.SH_Finally = 0; \
\
_SEHTryLevel.ST_Header.SPT_Next = _SEHPrevPortableTryLevel; \
_SEHFrame.SEH_Header.SPF_TopTryLevel = &_SEHTryLevel.ST_Header; \
\
if(_SEHTopTryLevel) \
{ \
if(&_SEHLocals != _SEHDummyLocals) \
_SEHFrame.SEH_Locals = &_SEHLocals; \
\
_SEH_EnableTracing(_SEH_DO_DEFAULT_TRACING); \
_SEHFrame.SEH_Header.SPF_Handler = _SEHCompilerSpecificHandler; \
_SEHEnterFrame(&_SEHFrame.SEH_Header); \
} \
\
++ _SEHState; \
continue; \
} \
else \
{ \
break; \
} \
} \
\
break; \
} \
\
_SEHPortableFrame->SPF_TopTryLevel = _SEHPrevPortableTryLevel; \
\
if(_SEHHandle) \
{
#define _SEH_FINALLY(FINALLY_) \
} \
\
break; \
} \
\
_SEHPortableFrame->SPF_TopTryLevel = _SEHPrevPortableTryLevel; \
break; \
} \
else \
{ \
_SEHTryLevel.ST_Header.SPT_Handlers.SH_Filter = 0; \
_SEHTryLevel.ST_Header.SPT_Handlers.SH_Finally = (FINALLY_); \
\
_SEHTryLevel.ST_Header.SPT_Next = _SEHPrevPortableTryLevel; \
_SEHFrame.SEH_Header.SPF_TopTryLevel = &_SEHTryLevel.ST_Header; \
\
if(_SEHTopTryLevel) \
{ \
if(&_SEHLocals != _SEHDummyLocals) \
_SEHFrame.SEH_Locals = &_SEHLocals; \
\
_SEH_EnableTracing(_SEH_DO_DEFAULT_TRACING); \
_SEHFrame.SEH_Header.SPF_Handler = _SEHCompilerSpecificHandler; \
_SEHEnterFrame(&_SEHFrame.SEH_Header); \
} \
\
++ _SEHState; \
continue; \
} \
\
break; \
} \
\
(FINALLY_)(&_SEHFrame.SEH_Header); \
\
if(0) \
{
#define _SEH_END \
} \
} \
\
if(_SEHTopTryLevel) \
_SEHLeaveFrame(); \
\
break; \
}
#endif
#define _SEH_HANDLE _SEH_EXCEPT(_SEH_STATIC_FILTER(_SEH_EXECUTE_HANDLER))
#define _SEH_EnableTracing(LEVEL_) ((void)(_SEHPortableFrame->SPF_Tracing = (LEVEL_)))
#define _SEH_DisableTracing() ((void)(_SEHPortableFrame->SPF_Tracing = _SEH_DO_TRACE_NONE))
#endif
/* EOF */

View file

@ -0,0 +1,140 @@
/*
Copyright (c) 2004/2005 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.
*/
#ifndef KJK_PSEH_FRAMEBASED_INTERNAL_H_
#define KJK_PSEH_FRAMEBASED_INTERNAL_H_
#define _SEH_DO_TRACE_ENTER_LEAVE (1 << 0)
#define _SEH_DO_TRACE_EXCEPTION_RECORD (1 << 1)
#define _SEH_DO_TRACE_CONTEXT (1 << 2)
#define _SEH_DO_TRACE_UNWIND (1 << 3)
#define _SEH_DO_TRACE_TRYLEVEL (1 << 4)
#define _SEH_DO_TRACE_CALL_FILTER (1 << 5)
#define _SEH_DO_TRACE_FILTER (1 << 6)
#define _SEH_DO_TRACE_CALL_HANDLER (1 << 7)
#define _SEH_DO_TRACE_CALL_FINALLY (1 << 8)
#define _SEH_DO_TRACE_NONE (0)
#define _SEH_DO_TRACE_ALL (-1)
#ifndef _SEH_DO_DEFAULT_TRACING
#define _SEH_DO_DEFAULT_TRACING _SEH_DO_TRACE_NONE
#endif
struct _EXCEPTION_RECORD;
struct _EXCEPTION_POINTERS;
struct _CONTEXT;
typedef int (__cdecl * _SEHFrameHandler_t)
(
struct _EXCEPTION_RECORD *,
void *,
struct _CONTEXT *,
void *
);
typedef struct __SEHRegistration
{
struct __SEHRegistration * SER_Prev;
_SEHFrameHandler_t SER_Handler;
}
_SEHRegistration_t;
struct __SEHPortableFrame;
struct __SEHPortableTryLevel;
typedef long (__stdcall * _SEHFilter_t)
(
struct _EXCEPTION_POINTERS *,
struct __SEHPortableFrame *
);
typedef void (__stdcall * _SEHHandler_t)
(
struct __SEHPortableTryLevel *
);
typedef void (__stdcall * _SEHFinally_t)
(
struct __SEHPortableFrame *
);
typedef struct __SEHHandlers
{
_SEHFilter_t SH_Filter;
_SEHFinally_t SH_Finally;
}
_SEHHandlers_t;
typedef struct __SEHPortableTryLevel
{
struct __SEHPortableTryLevel * volatile SPT_Next;
volatile _SEHHandlers_t SPT_Handlers;
}
_SEHPortableTryLevel_t;
typedef struct __SEHPortableFrame
{
_SEHRegistration_t SPF_Registration;
unsigned long SPF_Code;
volatile _SEHHandler_t SPF_Handler;
_SEHPortableTryLevel_t * volatile SPF_TopTryLevel;
volatile int SPF_Tracing;
}
_SEHPortableFrame_t;
#ifdef __cplusplus
extern "C"
{
#endif
extern void __stdcall _SEHEnterFrame_s(_SEHPortableFrame_t *);
extern void __stdcall _SEHLeaveFrame_s(void);
extern void __stdcall _SEHReturn_s(void);
#if !defined(_SEH_NO_FASTCALL)
# ifdef _M_IX86
# define _SEH_FASTCALL __fastcall
# else
# define _SEH_FASTCALL __stdcall
# endif
extern void _SEH_FASTCALL _SEHEnterFrame_f(_SEHPortableFrame_t *);
extern void _SEH_FASTCALL _SEHLeaveFrame_f(void);
extern void _SEH_FASTCALL _SEHReturn_f(void);
# define _SEHEnterFrame _SEHEnterFrame_f
# define _SEHLeaveFrame _SEHLeaveFrame_f
# define _SEHReturn _SEHReturn_f
#else
# define _SEHEnterFrame _SEHEnterFrame_s
# define _SEHLeaveFrame _SEHLeaveFrame_s
# define _SEHReturn _SEHReturn_s
#endif
#ifdef __cplusplus
}
#endif
#endif
/* EOF */

View file

@ -0,0 +1,84 @@
/*
Copyright (c) 2004/2005 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.
*/
#ifndef KJK_PSEH_H_
#define KJK_PSEH_H_
/* Some useful macros */
#if defined(__cplusplus)
# define _SEH_PVOID_CAST(TYPE_, P_) ((TYPE_)(P_))
#else
# define _SEH_PVOID_CAST(TYPE_, P_) (P_)
#endif
#if defined(FIELD_OFFSET)
# define _SEH_FIELD_OFFSET FIELD_OFFSET
#else
# include <stddef.h>
# define _SEH_FIELD_OFFSET offsetof
#endif
#if defined(CONTAINING_RECORD)
# define _SEH_CONTAINING_RECORD CONTAINING_RECORD
#else
# define _SEH_CONTAINING_RECORD(ADDR_, TYPE_, FIELD_) \
((TYPE_ *)(((char *)(ADDR_)) - _SEH_FIELD_OFFSET(TYPE_, FIELD_)))
#endif
#if defined(__CONCAT)
# define _SEH_CONCAT __CONCAT
#else
# define _SEH_CONCAT1(X_, Y_) X_ ## Y_
# define _SEH_CONCAT(X_, Y_) _SEH_CONCAT1(X_, Y_)
#endif
/*
Note: just define __inline to an empty symbol if your C compiler doesn't
support it
*/
#ifdef __cplusplus
# ifndef __inline
# define __inline inline
# endif
#endif
/* Locals sharing support */
#define _SEH_LOCALS_TYPENAME(BASENAME_) \
struct _SEH_CONCAT(_SEHLocalsTag, BASENAME_)
#define _SEH_DEFINE_LOCALS(BASENAME_) \
_SEH_LOCALS_TYPENAME(BASENAME_)
#define _SEH_DECLARE_LOCALS(BASENAME_) \
_SEH_LOCALS_TYPENAME(BASENAME_) _SEHLocals; \
_SEH_LOCALS_TYPENAME(BASENAME_) * _SEHPLocals; \
_SEHPLocals = &_SEHLocals;
/* Dummy locals */
static int _SEHLocals;
static void * const _SEHDummyLocals = &_SEHLocals;
#include <pseh/framebased.h>
#endif
/* EOF */

View file

@ -0,0 +1,452 @@
/*
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.
*/
#ifndef KJK_PSEH2_H_
#define KJK_PSEH2_H_
#if defined(_USE_NATIVE_SEH) || defined(_MSC_VER)
#include <excpt.h>
#define _SEH2_TRY __try
#define _SEH2_FINALLY __finally
#define _SEH2_EXCEPT(...) __except(__VA_ARGS__)
#define _SEH2_END
#define _SEH2_GetExceptionInformation() (GetExceptionInformation())
#define _SEH2_GetExceptionCode() (GetExceptionCode())
#define _SEH2_AbnormalTermination() (AbnormalTermination())
#define _SEH2_YIELD(STMT_) STMT_
#define _SEH2_LEAVE __leave
#define _SEH2_VOLATILE
#elif defined(__GNUC__) && !defined(__clang__) && defined(_M_AMD64)
#include "pseh2_64.h"
#elif defined(_USE_DUMMY_PSEH) || defined (__arm__) || defined(_M_AMD64)
extern int _SEH2_Volatile0;
extern int _SEH2_VolatileExceptionCode;
#define _SEH2_TRY \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wunused-label\"")\
{ \
__label__ __seh2_scope_end__;
#define _SEH2_FINALLY \
__seh2_scope_end__:; \
} \
if (1) \
{ \
__label__ __seh2_scope_end__;
#define _SEH2_EXCEPT(...) \
__seh2_scope_end__:; \
} \
if (_SEH2_Volatile0 || (0 && (__VA_ARGS__))) \
{ \
__label__ __seh2_scope_end__;
#define _SEH2_END \
__seh2_scope_end__:; \
} \
_Pragma("GCC diagnostic pop")
#define _SEH2_GetExceptionInformation() ((struct _EXCEPTION_POINTERS*)0)
#define _SEH2_GetExceptionCode() _SEH2_VolatileExceptionCode
#define _SEH2_AbnormalTermination() (0)
#define _SEH2_YIELD(STMT_) STMT_
#define _SEH2_LEAVE goto __seh2_scope_end__;
#define _SEH2_VOLATILE volatile
#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
#define _SEH2_VOLATILE volatile
#elif defined(__GNUC__)
struct _EXCEPTION_RECORD;
struct _EXCEPTION_POINTERS;
struct _CONTEXT;
typedef int (__cdecl * _SEH2FrameHandler_t)
(
struct _EXCEPTION_RECORD *,
void *,
struct _CONTEXT *,
void *
);
typedef struct __SEH2Registration
{
struct __SEH2Registration * SER_Prev;
_SEH2FrameHandler_t SER_Handler;
}
_SEH2Registration_t;
typedef struct __SEH2Frame
{
_SEH2Registration_t SF_Registration;
volatile struct __SEH2TryLevel * volatile SF_TopTryLevel;
volatile unsigned long SF_Code;
}
_SEH2Frame_t;
typedef struct __SEH2TryLevel
{
volatile struct __SEH2TryLevel * ST_Next;
void * ST_Filter;
void * ST_Body;
}
_SEH2TryLevel_t;
typedef struct __SEH2HandleTryLevel
{
_SEH2TryLevel_t SHT_Common;
void * volatile SHT_Esp;
void * volatile SHT_Ebp;
void * volatile SHT_Ebx;
void * volatile SHT_Esi;
void * volatile SHT_Edi;
}
_SEH2HandleTryLevel_t;
#ifdef __cplusplus
extern "C" {
#endif
extern int __cdecl _SEH2EnterFrameAndTrylevel(_SEH2Frame_t *, volatile _SEH2TryLevel_t *);
extern __attribute__((returns_twice)) int __cdecl _SEH2EnterFrameAndHandleTrylevel(_SEH2Frame_t *, volatile _SEH2HandleTryLevel_t *, void *);
extern __attribute__((returns_twice)) int __cdecl _SEH2EnterHandleTrylevel(_SEH2Frame_t *, volatile _SEH2HandleTryLevel_t *, void *);
extern void __cdecl _SEH2LeaveFrame(void);
extern void __cdecl _SEH2Return(void);
#ifdef __cplusplus
}
#endif
/* Prevent gcc from inlining functions that use SEH. */
#if ((__GNUC__ >= 4) && (__GNUC_MINOR__ >= 7))
static inline __attribute__((always_inline)) __attribute__((returns_twice)) void _SEH_DontInline() {}
#define __PREVENT_GCC_FROM_INLINING_SEH_FUNCTIONS() _SEH_DontInline();
#else
#define __PREVENT_GCC_FROM_INLINING_SEH_FUNCTIONS()
#endif
/* A no-op side effect that scares GCC */
#define __SEH_SIDE_EFFECT __asm__ __volatile__("#")
/* A no-op without any real side effects, but silences warnings */
#define __SEH_PRETEND_SIDE_EFFECT (void)0
/* Forces GCC to consider the specified label reachable */
#define __SEH_USE_LABEL(L_) if(__SEH_VOLATILE_FALSE) goto L_;
/* Makes GCC pretend the specified label is reachable, to silence warnings */
#define __SEH_PRETEND_USE_LABEL(L_) (void)(&&L_)
/* Soft memory barrier */
#define __SEH_BARRIER __asm__ __volatile__("#":::"memory")
/* GCC doesn't know that this equals zero */
#define __SEH_VOLATILE_ZERO ({ int zero = 0; __asm__ __volatile__("#" : "+g" (zero)); zero; })
#define __SEH_VOLATILE_FALSE __builtin_expect(__SEH_VOLATILE_ZERO, 0)
#define __SEH_VOLATILE_TRUE __builtin_expect(!__SEH_VOLATILE_ZERO, 1)
#define ___SEH_STRINGIFY(X_) # X_
#define __SEH_STRINGIFY(X_) ___SEH_STRINGIFY(X_)
#define __SEH_EXCEPT_RET long
#define __SEH_EXCEPT_ARGS __attribute__((unused)) _SEH2Frame_t * _SEH2FrameP, __attribute__((unused)) struct _EXCEPTION_POINTERS * _SEHExceptionInformation
#define __SEH_EXCEPT_ARGS_ , __SEH_EXCEPT_ARGS
#define __SEH_EXCEPT_PFN __SEH_DECLARE_EXCEPT_PFN
#define __SEH_DECLARE_EXCEPT_PFN(NAME_) __SEH_EXCEPT_RET (__cdecl * NAME_)(__SEH_EXCEPT_ARGS)
#define __SEH_DECLARE_EXCEPT(NAME_) __SEH_EXCEPT_RET __cdecl NAME_(__SEH_EXCEPT_ARGS)
#define __SEH_DEFINE_EXCEPT(NAME_) __SEH_EXCEPT_RET __cdecl NAME_(__SEH_EXCEPT_ARGS)
#define __SEH_FINALLY_RET void
#define __SEH_FINALLY_ARGS void
#define __SEH_FINALLY_ARGS_
#define __SEH_FINALLY_PFN __SEH_DECLARE_FINALLY_PFN
#define __SEH_DECLARE_FINALLY_PFN(NAME_) __SEH_FINALLY_RET (__cdecl * NAME_)(__SEH_FINALLY_ARGS)
#define __SEH_DECLARE_FINALLY(NAME_) __SEH_FINALLY_RET __cdecl NAME_(__SEH_FINALLY_ARGS)
#define __SEH_DEFINE_FINALLY(NAME_) __SEH_FINALLY_RET __cdecl NAME_(__SEH_FINALLY_ARGS)
#define __SEH_RETURN_EXCEPT(R_) return (long)(R_)
#define __SEH_RETURN_FINALLY() return
#define __SEH_BEGIN_TRY \
{ \
__label__ _SEHEndTry; \
\
__SEH_PRETEND_USE_LABEL(_SEHEndTry); \
\
{ \
__SEH_BARRIER;
#define __SEH_END_TRY \
__SEH_BARRIER; \
} \
_SEHEndTry:; \
}
#define __SEH_SET_TRYLEVEL(TRYLEVEL_) \
{ \
__SEH_BARRIER; _SEH2FrameP->SF_TopTryLevel = (TRYLEVEL_); __SEH_BARRIER; \
}
#define __SEH_ENTER_FRAME_AND_TRYLEVEL(TRYLEVEL_) (_SEH2EnterFrameAndTrylevel(_SEH2FrameP, (TRYLEVEL_)))
#define __SEH_ENTER_TRYLEVEL(TRYLEVEL_) ((__SEH_SET_TRYLEVEL((TRYLEVEL_))), 0)
#define __SEH_ENTER_FRAME_AND_HANDLE_TRYLEVEL(TRYLEVEL_, HANDLE_) _SEH2EnterFrameAndHandleTrylevel(_SEH2FrameP, (TRYLEVEL_), (HANDLE_))
#define __SEH_ENTER_HANDLE_TRYLEVEL(TRYLEVEL_, HANDLE_) _SEH2EnterHandleTrylevel(_SEH2FrameP, (TRYLEVEL_), (HANDLE_))
#define __SEH_ENTER_SCOPE(TRYLEVEL_) (_SEHTopTryLevel ? __SEH_ENTER_FRAME_AND_TRYLEVEL(TRYLEVEL_) : __SEH_ENTER_TRYLEVEL(TRYLEVEL_))
#define __SEH_ENTER_HANDLE_SCOPE(TRYLEVEL_, HANDLE_) (({ __SEH_BARRIER; __asm__ __volatile__("mov %%esp, %0" : "=m" ((TRYLEVEL_)->SHT_Esp)); __SEH_BARRIER; }), (_SEHTopTryLevel ? __SEH_ENTER_FRAME_AND_HANDLE_TRYLEVEL((TRYLEVEL_), (HANDLE_)) : __SEH_ENTER_HANDLE_TRYLEVEL((TRYLEVEL_), (HANDLE_))))
#define __SEH_LEAVE_TRYLEVEL() \
if(!_SEHTopTryLevel) \
{ \
__SEH_SET_TRYLEVEL(_SEHPrevTryLevelP); \
} \
#define __SEH_LEAVE_FRAME() \
if(_SEHTopTryLevel) \
{ \
_SEH2LeaveFrame(); \
}
#define __SEH_END_SCOPE_CHAIN \
static __attribute__((unused)) const int _SEH2ScopeKind = 1; \
static __attribute__((unused)) _SEH2Frame_t * const _SEH2FrameP = 0; \
static __attribute__((unused)) _SEH2TryLevel_t * const _SEH2TryLevelP = 0;
#define __SEH_BEGIN_SCOPE \
for(;;) \
{ \
const int _SEHTopTryLevel = (_SEH2ScopeKind != 0); \
_SEH2Frame_t * const _SEHCurFrameP = _SEH2FrameP; \
volatile _SEH2TryLevel_t * const _SEHPrevTryLevelP = _SEH2TryLevelP; \
__attribute__((unused)) int _SEHAbnormalTermination; \
\
(void)_SEHTopTryLevel; \
(void)_SEHCurFrameP; \
(void)_SEHPrevTryLevelP; \
\
{ \
__label__ _SEHBeforeTry; \
__label__ _SEHDoTry; \
__label__ _SEHAfterTry; \
static const int _SEH2ScopeKind = 0; \
volatile _SEH2TryLevel_t _SEHTryLevel; \
volatile _SEH2HandleTryLevel_t _SEHHandleTryLevel; \
_SEH2Frame_t _SEH2Frame[_SEHTopTryLevel ? 1 : 0]; \
volatile _SEH2TryLevel_t * _SEH2TryLevelP; \
_SEH2Frame_t * const _SEH2FrameP = _SEHTopTryLevel ? \
_SEH2Frame : _SEHCurFrameP; \
\
(void)_SEH2ScopeKind; \
(void)_SEHTryLevel; \
(void)_SEHHandleTryLevel; \
(void)_SEH2FrameP; \
(void)_SEH2TryLevelP; \
\
goto _SEHBeforeTry; \
\
_SEHDoTry:;
#define __SEH_END_SCOPE \
} \
\
break; \
}
#define __SEH_SCOPE_LOCALS \
__label__ _SEHBeginExcept; \
__label__ _SEHEndExcept; \
\
auto __SEH_DECLARE_FINALLY(_SEHFinally);
#define _SEH2_TRY \
__PREVENT_GCC_FROM_INLINING_SEH_FUNCTIONS() \
__SEH_BEGIN_SCOPE \
{ \
__SEH_SCOPE_LOCALS; \
\
__SEH_BEGIN_TRY \
{
#define _SEH2_FINALLY \
} \
__SEH_END_TRY; \
\
goto _SEHAfterTry; \
_SEHBeforeTry:; \
\
__SEH_PRETEND_USE_LABEL(_SEHBeginExcept); \
__SEH_PRETEND_USE_LABEL(_SEHEndExcept); \
\
_SEHTryLevel.ST_Filter = 0; \
_SEHTryLevel.ST_Body = &_SEHFinally; \
_SEHTryLevel.ST_Next = _SEHPrevTryLevelP; \
__SEH_ENTER_SCOPE(&_SEHTryLevel); \
_SEH2TryLevelP = &_SEHTryLevel; \
\
_SEHAbnormalTermination = 1; \
\
goto _SEHDoTry; \
_SEHAfterTry:; \
\
_SEHAbnormalTermination = 0; \
\
__SEH_LEAVE_TRYLEVEL(); \
\
_SEHFinally(); \
goto _SEHEndExcept; \
\
_SEHBeginExcept:; \
\
__attribute__((noinline)) __SEH_DEFINE_FINALLY(_SEHFinally) \
{ \
__SEH_END_SCOPE_CHAIN; \
\
(void)_SEH2ScopeKind; \
(void)_SEH2FrameP; \
(void)_SEH2TryLevelP; \
\
for(;; ({ __SEH_RETURN_FINALLY(); })) \
{
#define _SEH2_EXCEPT(...) \
} \
__SEH_END_TRY; \
\
goto _SEHAfterTry; \
\
_SEHBeforeTry:; \
\
{ \
__attribute__((unused)) struct _EXCEPTION_POINTERS * volatile _SEHExceptionInformation; \
\
if(__builtin_constant_p((__VA_ARGS__)) && (__VA_ARGS__) <= 0) \
{ \
if((__VA_ARGS__) < 0) \
{ \
_SEHTryLevel.ST_Filter = (void *)-1; \
_SEHTryLevel.ST_Body = 0; \
} \
else \
{ \
_SEHTryLevel.ST_Filter = (void *)0; \
_SEHTryLevel.ST_Body = 0; \
} \
\
_SEHTryLevel.ST_Next = _SEHPrevTryLevelP; \
__SEH_ENTER_SCOPE(&_SEHTryLevel); \
_SEH2TryLevelP = &_SEHTryLevel; \
} \
else \
{ \
if(__builtin_constant_p((__VA_ARGS__)) && (__VA_ARGS__) > 0) \
_SEHHandleTryLevel.SHT_Common.ST_Filter = (void *)1; \
else \
{ \
__SEH_DEFINE_EXCEPT(_SEHExcept) \
{ \
__SEH_RETURN_EXCEPT((__VA_ARGS__)); \
} \
\
_SEHHandleTryLevel.SHT_Common.ST_Filter = &_SEHExcept; \
} \
\
_SEHHandleTryLevel.SHT_Common.ST_Next = _SEHPrevTryLevelP; \
_SEH2TryLevelP = &_SEHHandleTryLevel.SHT_Common; \
\
if(__builtin_expect(__SEH_ENTER_HANDLE_SCOPE(&_SEHHandleTryLevel, &&_SEHBeginExcept), 0)) \
goto _SEHBeginExcept; \
} \
} \
\
goto _SEHDoTry; \
\
__attribute__((unused)) __SEH_DEFINE_FINALLY(_SEHFinally) { __SEH_RETURN_FINALLY(); } \
\
_SEHAfterTry:; \
__SEH_LEAVE_TRYLEVEL(); \
\
goto _SEHEndExcept; \
\
_SEHBeginExcept:; \
{ \
{ \
__SEH_BARRIER;
#define _SEH2_END \
__SEH_BARRIER; \
} \
} \
\
_SEHEndExcept:; \
\
__SEH_LEAVE_FRAME(); \
} \
__SEH_END_SCOPE;
#define _SEH2_GetExceptionInformation() (_SEHExceptionInformation)
#define _SEH2_GetExceptionCode() ((_SEH2FrameP)->SF_Code)
#define _SEH2_AbnormalTermination() (_SEHAbnormalTermination)
#define _SEH2_YIELD(STMT_) \
for(;;) \
{ \
if(!_SEH2ScopeKind) \
_SEH2Return(); \
\
STMT_; \
}
#define _SEH2_LEAVE goto _SEHEndTry
__SEH_END_SCOPE_CHAIN;
#define _SEH2_VOLATILE volatile
#else
#error no PSEH support
#endif
#endif /* !KJK_PSEH2_H_ */
/* EOF */

View file

@ -0,0 +1,169 @@
#pragma once
/* Declare our global trampoline function for filter and unwinder */
__asm__(
".p2align 4, 0x90\n"
".seh_proc __seh2_global_filter_func\n"
"__seh2_global_filter_func:\n"
"\tpush %rbp\n"
"\t.seh_pushreg %rbp\n"
"\tsub $32, %rsp\n"
"\t.seh_stackalloc 32\n"
"\t.seh_endprologue\n"
/* Restore frame pointer. */
"\tmov %rdx, %rbp\n"
/* Actually execute the filter funclet */
"\tjmp *%rax\n"
"__seh2_global_filter_func_exit:\n"
"\t.p2align 4\n"
"\tadd $32, %rsp\n"
"\tpop %rbp\n"
"\tret\n"
"\t.seh_endproc");
#define _SEH2_TRY \
{ \
__label__ __seh2$$begin_try__; \
__label__ __seh2$$end_try__; \
/* \
* We close the current SEH block for this function and install our own. \
* At this point GCC emitted its prologue, and if it saves more \
* registers, the relevant instruction will be valid for our scope as well. \
* We also count the number of try blocks at assembly level \
* to properly set the handler data when we're done. \
*/ \
__seh2$$begin_try__: \
{ \
__label__ __seh2$$leave_scope__;
#define _SEH2_EXCEPT(...) \
__seh2$$leave_scope__: __MINGW_ATTRIB_UNUSED; \
} \
__seh2$$end_try__: \
/* Call our home-made pragma */ \
_Pragma("REACTOS seh(except)") \
if (0) \
{ \
__label__ __seh2$$leave_scope__; \
__label__ __seh2$$filter__; \
__label__ __seh2$$begin_except__; \
LONG __MINGW_ATTRIB_UNUSED __seh2$$exception_code__ = 0; \
/* Add our handlers to the list */ \
__asm__ __volatile__ goto ("\n" \
"\t.seh_handlerdata\n" \
"\t.rva %l0\n" /* Begin of tried code */ \
"\t.rva %l1 + 1\n" /* End of tried code */ \
"\t.rva %l2\n" /* Filter function */ \
"\t.rva %l3\n" /* Called on except */ \
"\t.seh_code\n" \
: /* No output */ \
: /* No input */ \
: /* No clobber */ \
: __seh2$$begin_try__, \
__seh2$$end_try__, \
__seh2$$filter__, \
__seh2$$begin_except__); \
if (0) \
{ \
/* Jump to the global filter. Tell it where the filter funclet lies */ \
__label__ __seh2$$filter_funclet__; \
__seh2$$filter__: \
__asm__ __volatile__ goto( \
"\tleaq %l0(%%rip), %%rax\n" \
"\tjmp __seh2_global_filter_func\n" \
: /* No output */ \
: /* No input */ \
: "%rax" \
: __seh2$$filter_funclet__); \
/* Actually declare our filter funclet */ \
struct _EXCEPTION_POINTERS* __seh2$$exception_ptr__; \
__seh2$$filter_funclet__: \
/* At this point, the compiler can't count on any register being valid */ \
__asm__ __volatile__("" \
: "=c"(__seh2$$exception_ptr__) /* First argument of the filter function */ \
: /* No input */ \
: /* Everything */ \
"%rax", "%rbx","%rdx", "%rdi", "%rsi", \
"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"); \
/* Save exception code */ \
__seh2$$exception_code__ = __seh2$$exception_ptr__->ExceptionRecord->ExceptionCode; \
/* Actually evaluate our filter */ \
register long __MINGW_ATTRIB_UNUSED __seh2$$filter_funclet_ret __asm__("eax") = \
((__VA_ARGS__)); \
/* Go back to the global filter function */ \
__asm__("jmp __seh2_global_filter_func_exit"); \
} \
/* Protect us from emitting instructions to jump back to the filter function */ \
enum \
{ \
__seh2$$abnormal_termination__ = 0 \
}; \
__seh2$$begin_except__:
#define _SEH2_FINALLY \
__seh2$$leave_scope__: __MINGW_ATTRIB_UNUSED; \
} \
__seh2$$end_try__: \
/* Call our home-made pragma */ \
_Pragma("REACTOS seh(finally)") \
if (1) \
{ \
__label__ __seh2$$finally__; \
__label__ __seh2$$begin_finally__; \
__label__ __seh2$$leave_scope__; \
int __seh2$$abnormal_termination__; \
/* Add our handlers to the list */ \
__asm__ __volatile__ goto ("\n" \
"\t.seh_handlerdata\n" \
"\t.rva %l0\n" /* Begin of tried code */ \
"\t.rva %l1 + 1\n" /* End of tried code */ \
"\t.rva %l2\n" /* Filter function */ \
"\t.long 0\n" /* Nothing for unwind code */ \
"\t.seh_code\n" \
: /* No output */ \
: /* No input */ \
: /* No clobber */ \
: __seh2$$begin_try__, \
__seh2$$end_try__, \
__seh2$$finally__); \
if (0) \
{ \
/* Jump to the global trampoline. Tell it where the unwind code really lies */ \
__seh2$$finally__: \
__asm__ __volatile__ goto( \
"\tleaq %l0(%%rip), %%rax\n" \
"\tjmp __seh2_global_filter_func\n" \
: /* No output */ \
: /* No input */ \
: /* No clobber */ \
: __seh2$$begin_finally__); \
} \
\
/* Zero-out rcx to indicate normal termination */ \
__asm__ __volatile__("xor %%rcx, %%rcx" \
: /* No output */ \
: /* No input */ \
: /* Everything - We might come from __C_specific_handler here */ \
"%rax", "%rbx", "%rcx", "%rdx", "%rdi", "%rsi", \
"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"); \
/* Actually declare our finally funclet */ \
__seh2$$begin_finally__: \
__asm__ __volatile__("" \
: "=c" (__seh2$$abnormal_termination__));
#define _SEH2_END \
__seh2$$leave_scope__: __MINGW_ATTRIB_UNUSED; \
if (__seh2$$abnormal_termination__) \
{ \
__asm__("jmp __seh2_global_filter_func_exit"); \
} \
} \
}
#define _SEH2_GetExceptionInformation() __seh2$$exception_ptr__
#define _SEH2_GetExceptionCode() __seh2$$exception_code__
#define _SEH2_AbnormalTermination() __seh2$$abnormal_termination__
#define _SEH2_LEAVE goto __seh2$$leave_scope__
#define _SEH2_YIELD(__stmt) __stmt
#define _SEH2_VOLATILE volatile

View file

@ -0,0 +1,476 @@
/*
* 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>
#ifdef __cplusplus
extern "C" {
#endif
/* CLANG must safe non-volatiles, because it uses a return-twice algorithm */
#if defined(__clang__) && !defined(_SEH3$_FRAME_ALL_NONVOLATILES)
#define _SEH3$_FRAME_ALL_NONVOLATILES 1
#endif
enum
{
_SEH3$_NESTED_HANDLER = 0,
_SEH3$_CPP_HANDLER = 1,
_SEH3$_CLANG_HANDLER = 2,
#ifdef __clang__
_SEH3$_HANDLER_TYPE = _SEH3$_CLANG_HANDLER,
#elif defined(__cplusplus)
_SEH3$_HANDLER_TYPE = _SEH3$_CPP_HANDLER,
#else
_SEH3$_HANDLER_TYPE = _SEH3$_NESTED_HANDLER,
#endif
};
typedef struct _SEH3$_SCOPE_TABLE
{
void *Target;
void *Filter;
unsigned char TryLevel;
unsigned char HandlerType;
} 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;
/* Except handler stores the exception code here */
unsigned long ExceptionCode;
/* 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;
unsigned long Edi;
#endif
#ifdef __clang__
void *ReturnAddress;
#endif
} SEH3$_REGISTRATION_FRAME ,*PSEH3$_REGISTRATION_FRAME;
/* Prevent gcc from inlining functions that use SEH. */
static inline __attribute__((always_inline)) __attribute__((returns_twice)) void _SEH3$_PreventInlining() {}
/* Unregister the root 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");
}
/* Unregister a trylevel frame */
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,
};
#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$_AutoCleanup)))
int
__attribute__((regparm(3)))
__attribute__((returns_twice))
_SEH3$_RegisterFrameWithNonVolatiles(
volatile SEH3$_REGISTRATION_FRAME* RegistrationFrame,
const SEH3$_SCOPE_TABLE* ScopeTable,
void* AllocaFrame);
int
__attribute__((regparm(3)))
__attribute__((returns_twice))
_SEH3$_RegisterTryLevelWithNonVolatiles(
volatile SEH3$_REGISTRATION_FRAME* RegistrationFrame,
const SEH3$_SCOPE_TABLE* ScopeTable,
void* AllocaFrame);
/* CLANG specific definitions! */
#ifdef __clang__
/* CLANG thinks it is smart and optimizes the alloca away if it is 0 and with it the use of a frame register */
#define _SEH3$_EnforceFramePointer() asm volatile ("#\n" : : "m"(*(char*)__builtin_alloca(4)) : "%esp", "memory")
/* CLANG doesn't have asm goto! */
#define _SEH3$_ASM_GOTO(...)
#define _SEH3$_RegisterFrame_(_TrylevelFrame, _DataTable) \
do { \
int result = _SEH3$_RegisterFrameWithNonVolatiles(_TrylevelFrame, _DataTable, __builtin_alloca(0)); \
if (__builtin_expect(result != 0, 0)) \
{ \
if (result == 1) goto _SEH3$_l_FilterOrFinally; \
if (result == 2) goto _SEH3$_l_HandlerTarget; \
goto _SEH3$_l_BeforeFilterOrFinally; \
} \
} while(0)
#define _SEH3$_RegisterTryLevel_(_TrylevelFrame, _DataTable) \
do { \
int result = _SEH3$_RegisterTryLevelWithNonVolatiles(_TrylevelFrame, _DataTable, __builtin_alloca(0)); \
if (__builtin_expect(result != 0, 0)) \
{ \
if (result == 1) goto _SEH3$_l_FilterOrFinally; \
if (result == 2) goto _SEH3$_l_HandlerTarget; \
goto _SEH3$_l_BeforeFilterOrFinally; \
} \
} while(0)
#define _SEH3$_SCARE_GCC()
#else /* !__clang__ */
/* This will make GCC use ebp, even if it was disabled by -fomit-frame-pointer */
#define _SEH3$_EnforceFramePointer() asm volatile ("#\n" : : "m"(*(char*)__builtin_alloca(0)) : "%esp", "memory")
#define _SEH3$_ASM_GOTO(...) asm goto ("#\n" : : : "memory" : __VA_ARGS__)
#ifdef __cplusplus
#define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
asm goto ("leal %0, %%eax\n\t" \
"leal %1, %%edx\n\t" \
"call " #_Function "WithStackLayout" \
: \
: "m" (*(_TrylevelFrame)), "m" (*(_DataTable)), "c" (__builtin_alloca(0)), "p" (_SEH3$_RegisterFrameWithNonVolatiles) \
: "eax", "edx", "memory" \
: _SEH3$_l_BeforeTry, _SEH3$_l_HandlerTarget, _SEH3$_l_OnException, _SEH3$_l_BeforeFilterOrFinally, _SEH3$_l_FilterOrFinally)
#else
#define _SEH3$_CALL_WRAPPER(_Function, _TrylevelFrame, _DataTable) \
asm goto ("leal %0, %%eax\n\t" \
"leal %1, %%edx\n\t" \
"call " #_Function \
: \
: "m" (*(_TrylevelFrame)), "m" (*(_DataTable)), "p" (_SEH3$_RegisterFrameWithNonVolatiles) \
: "eax", "edx", "ecx", "memory" \
: _SEH3$_l_BeforeTry, _SEH3$_l_HandlerTarget, _SEH3$_l_OnException, _SEH3$_l_BeforeFilterOrFinally, _SEH3$_l_FilterOrFinally)
#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. */
#define _SEH3$_SCARE_GCC() \
void *plabel; \
_SEH3$_ASM_GOTO(_SEH3$_l_BeforeTry, _SEH3$_l_HandlerTarget, _SEH3$_l_OnException, _SEH3$_l_BeforeFilterOrFinally, _SEH3$_l_FilterOrFinally); \
asm volatile ("#" : "=a"(plabel) : "p"(&&_SEH3$_l_BeforeTry), "p"(&&_SEH3$_l_HandlerTarget), "p"(&&_SEH3$_l_OnException), "p"(&&_SEH3$_l_FilterOrFinally) \
: "ebx", "ecx", "edx", "esi", "edi", "flags", "memory" ); \
goto _SEH3$_l_OnException;
#endif /* __clang__ */
/* Neither CLANG nor C++ support nested functions */
#if defined(__cplusplus) || defined(__clang__)
/* Use the global unregister function */
void
__attribute__((regparm(1)))
_SEH3$_AutoCleanup(
volatile SEH3$_REGISTRATION_FRAME *Frame);
/* These are only dummies here */
#define _SEH3$_DECLARE_CLEANUP_FUNC(_Name)
#define _SEH3$_DEFINE_CLEANUP_FUNC(_Name)
#define _SEH3$_DECLARE_FILTER_FUNC(_Name)
#define _SEH3$_DEFINE_DUMMY_FINALLY(_Name)
/* On invocation, the AllocaFrame field is loaded with the return esp value */
#define _SEH3$_NESTED_FUNC_RETURN(_Result) \
/* Restore esp and return to the caller */ \
asm volatile ("movl %[FixedEsp], %%esp\n\tret" \
: : "a" (_Result), [FixedEsp] "m" (_SEH3$_TrylevelFrame.AllocaFrame) : "ebx", "ecx", "edx", "esi", "edi", "flags", "memory")
/* The filter "function" */
#define _SEH3$_DEFINE_FILTER_FUNC(_Name, expression) \
{ \
/* Evaluate and return the filter expression */ \
asm volatile ("#\n" : : : "eax", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"); \
_SEH3$_NESTED_FUNC_RETURN((expression)); \
}
#define _SEH3$_FINALLY_FUNC_OPEN(_Name) \
{ \
asm volatile ("#\n" : : : "eax", "ebx", "ecx", "edx", "esi", "edi", "flags", "memory"); \
/* This construct makes sure that the finally function returns */ \
/* a proper value at the end */ \
for (; ; (void)({_SEH3$_NESTED_FUNC_RETURN(0); 0;}))
#define _SEH3$_FILTER(_Filter, _FilterExpression) (&&_SEH3$_l_FilterOrFinally)
#define _SEH3$_FINALLY(_Finally) (&&_SEH3$_l_FilterOrFinally)
#define _SEH3$_DECLARE_EXCEPT_INTRINSICS()
/* Since we cannot use nested functions, we declare these globally as macros */
#define _abnormal_termination() (_SEH3$_TrylevelFrame.ExceptionPointers != 0)
#define _exception_code() (_SEH3$_TrylevelFrame.ExceptionCode)
#define _exception_info() (_SEH3$_TrylevelFrame.ExceptionPointers)
#else /* __cplusplus || __clang__ */
#define _SEH3$_DECLARE_EXCEPT_INTRINSICS() \
inline __attribute__((always_inline, gnu_inline)) \
unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionCode; }
/* 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 */ \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wshadow\"") \
inline __attribute__((always_inline, gnu_inline)) \
unsigned long _exception_code() { return _SEH3$_TrylevelFrame.ExceptionCode; } \
inline __attribute__((always_inline, gnu_inline)) \
void * _exception_info() { return _SEH3$_TrylevelFrame.ExceptionPointers; } \
_Pragma("GCC diagnostic pop") \
\
/* 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.ExceptionPointers != 0); } \
\
/* This construct makes sure that the finally function returns */ \
/* a proper value at the end */ \
for (; ; (void)({return 0; 0;}))
#define _SEH3$_FILTER(_Filter, _FilterExpression) \
(__builtin_constant_p(_FilterExpression) ? (void*)(unsigned long)(unsigned char)(unsigned long)(_FilterExpression) : _Filter)
#define _SEH3$_FINALLY(_Finally) (_Finally)
#define _SEH3$_DEFINE_DUMMY_FINALLY(_Name) \
auto inline __attribute__((always_inline,gnu_inline)) int _Name(int Action) { (void)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) \
{ \
(void)p; \
/* 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); \
}
#endif /* __cplusplus || __clang__ */
#define _SEH3_TRY \
_SEH3$_PreventInlining(); \
/* Enter the outer scope */ \
if (1) { \
/* 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; \
__label__ _SEH3$_l_BeforeFilterOrFinally; \
__label__ _SEH3$_l_FilterOrFinally; \
(void)&&_SEH3$_l_OnException; \
(void)&&_SEH3$_l_BeforeFilterOrFinally; \
(void)&&_SEH3$_l_FilterOrFinally; \
\
/* 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: \
if (1)
#define _SEH3_EXCEPT(...) \
/* End of the try block */ \
_SEH3$_l_AfterTry: (void)0; \
goto _SEH3$_l_EndTry; \
\
_SEH3$_l_BeforeTry: (void)0; \
_SEH3$_ASM_GOTO(_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__)), _SEH3$_TryLevel, _SEH3$_HANDLER_TYPE }; \
\
/* Register the registration record. */ \
if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
else _SEH3$_RegisterTryLevel_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
\
/* 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_BeforeFilterOrFinally: (void)0; \
/* Make sure the filter function doesn't use esp */ \
_SEH3$_EnforceFramePointer(); \
\
_SEH3$_l_FilterOrFinally: (void)0; \
/* Emit the filter function */ \
_SEH3$_DEFINE_FILTER_FUNC(_SEH3$_FilterFunction, (__VA_ARGS__)) \
\
_SEH3$_l_HandlerTarget: (void)0; \
_SEH3$_EnforceFramePointer(); \
\
if (1) \
{ \
/* Prevent this block from being optimized away */ \
asm volatile ("#\n"); \
if (1)
#define _SEH3_FINALLY \
/* End of the try block */ \
_SEH3$_l_AfterTry: (void)0; \
/* Set ExceptionPointers to 0, this is used by _abnormal_termination() */ \
_SEH3$_TrylevelFrame.ExceptionPointers = 0; \
\
goto _SEH3$_l_EndTry; \
\
_SEH3$_l_BeforeTry: (void)0; \
_SEH3$_ASM_GOTO(_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$_FINALLY(&_SEH3$_FinallyFunction), _SEH3$_TryLevel, _SEH3$_HANDLER_TYPE }; \
\
/* Register the registration record. */ \
if (_SEH3$_TryLevel == 1) _SEH3$_RegisterFrame_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
else _SEH3$_RegisterTryLevel_(&_SEH3$_TrylevelFrame, &_SEH3$_ScopeTable); \
_SEH3$_TrylevelFrame.ExceptionPointers = (PSEH3$_EXCEPTION_POINTERS)1; \
\
goto _SEH3$_l_DoTry; \
\
_SEH3$_l_HandlerTarget: (void)0; \
_SEH3$_EnforceFramePointer(); \
\
_SEH3$_l_BeforeFilterOrFinally: (void)0; \
_SEH3$_EnforceFramePointer(); \
_SEH3$_l_FilterOrFinally: (void)0; \
_SEH3$_FINALLY_FUNC_OPEN(_SEH3$_FinallyFunction)
#define _SEH3_END \
}; \
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(_SEH3$_l_OnException); \
\
/* Implementation of the auto cleanup function */ \
_SEH3$_DEFINE_CLEANUP_FUNC(_SEH3$_AutoCleanup); \
\
/* Close the outer scope */ \
}
#define _SEH3_LEAVE goto _SEH3$_l_AfterTry
#define _SEH3_VOLATILE volatile
#ifdef __cplusplus
}; // extern "C"
#endif