[ros-kernel] Macro SEH support, version 2 (Help needed: translating to AT&T assembler syntax)

Skywing skywing at valhallalegends.com
Mon Oct 27 11:16:10 CET 2003


I've done a significant amount of reworking on the macro SEH support that I
introduced on the mailinglist some time ago.  It now passes every test I can
throw at it with flying colors, including interoperating with VC-style SEH.
This includes unwinding and nested exception handlers.  This release fixes a
number of bugs with the original implementation.

Request for help:  I don't know AT&T assembler, so somebody needs to port
this if it's to be used with MinGW (which is the primary purpose for me
writing it).  Unless somebody is willing to lend a hand with GNU-ASM'ing it,
this will probably not benefit the ReactOS project much.

I've tried to code this as defensively as possible, so that any code in the
handler or filter areas will properly run without any special knowledge
about the values of the stack/other registers while in an exception handler.
I'm pretty sure that the macros are virtually bulletproof against the
compiler generating code that doesn't work inside of them, provided you
follow these rules: Enable frame pointer generation.  This is absolutely
crucial to any SEH implementation; without frame pointers, the macros will
fall over and die. Preferably, exit the SEH exception handler with
fallthrough.  If you must exit it otherwise, you can try using the
ExceptCleanup() macro before leaving the handler with a return or similar
statement.  Exiting the SEH handler inside the filter expression is probably
a Bad Idea and may not work, as the "panic stack" will be used through the
remainder of the function.

Additionally, GetExceptionCode() and GetExceptionPointers() should be
available at the proper scopes.  You ought to get an undefined identifier
error if you try to use them elsewhere.

The macros work with "heap-based" SEH in order to work around the Borland
patent.  There are some limitations with using this with regular Microsoft
Windows (see below), but it should be no problem to modify ReactOS to work
with this scheme if necessary.

The semantics for using the macros are as follows:
TRY
{
  try-protected-code;
}
EXCEPT(( filter-expression )) /* Note that double parens are needed */ {
handler-code; } EXCEPT_END();

Issues with "heap-based" SEH:
The default Microsoft RtlUnwind implementation will not call an exception
handler if the exception registration is not within the threads stack
limits.  It would be a good idea to ensure that the ReactOS RtlUnwind
implementation does not have this limitation if we wish to use "heap-based"
SEH and not "stack-based" SEH.

Issues with the macros and MSVC++:
The VC compiler crashes if you use if(0) { code; } or goto label; { code; }
label: instead of __asm jmp label; { code; } label; to prevent the exception
handler from being executed by fallthrough.  I think that this is because
the optimizer decides that the exception handler is unreachable and removes
it, despite an existing reference to code in the handler (the label for the
start of the OS-invoked SEH handler function itself).  Later this causes the
compiler to crash, hence my workaround with __asm jmp EXCEPT_EndOfExcept.

----- Macro definitions follow -----

#define ExceptCleanup() { \
	ExFreePool(EXCEPT_ExceptionPointers); \
	__asm { \
	__asm push		eax \
	__asm mov		eax, dword ptr fs:[0x00000000] \
	__asm mov		eax, dword ptr [eax] \
	__asm mov		dword ptr fs:[0x00000000], eax \
	__asm pop		eax \
	} \
}
#define GetExceptionCode() ((DWORD)(EXCEPT_ExceptionCode))
#define GetExceptionPointers()
((PEXCEPTION_POINTERS)(&EXCEPT_ExceptionPointers))

#define EH_NONCONTINUABLE	0x00000001
#define EH_UNWINDING		0x00000002
#define EH_EXIT_UNWIND		0x00000004
#define EH_STACK_INVALID	0x00000008
#define EH_NESTED_CALL		0x00000010
#define EH_UNWIND_CONTEXT	EH_UNWINDING | EH_EXIT_UNWIND

#define _TRY_SAVED_EBP		0x08
#define _TRY_SAVED_EBX		0x0c
#define _TRY_SAVED_ESI		0x10
#define _TRY_SAVED_EDI		0x14
#define _TRY_SAVED_ESP		0x18

#if 0 // Enable if you don't have these in scope
typedef enum {
	ExceptionContinueExecution,
	ExceptionContinueSearch,
	ExceptionNestedException,
	ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;
#endif

#define TRY { \
	PVOID TRY_ExceptionRegistration = ExAllocatePoolWithTag(PagedPool,
28, ' HES'); \
	volatile DWORD EXCEPT_ExceptionCode; \
	__asm { \
		__asm push		eax \
		__asm push		ecx \
		__asm mov		eax, TRY_ExceptionRegistration \
		__asm mov		ecx, dword ptr fs:[0] \
		__asm mov		dword ptr [eax+0x00], ecx \
		__asm mov		dword ptr fs:[0], eax \
		__asm lea		ecx, EXCEPT_Handler \
		__asm mov		dword ptr [eax+0x04], ecx \
		__asm mov		dword ptr [eax+_TRY_SAVED_EBP], ebp
\
		__asm mov		dword ptr [eax+_TRY_SAVED_EBX], ebx
\
		__asm mov		dword ptr [eax+_TRY_SAVED_ESI], esi
\
		__asm mov		dword ptr [eax+_TRY_SAVED_EDI], edi
\
		__asm mov		dword ptr [eax+_TRY_SAVED_ESP], esp
\
		__asm add		dword ptr [eax+_TRY_SAVED_ESP], 0x08
\
		__asm pop		ecx \
		__asm pop		eax \
	}

#define EXCEPT_LOCALS TYPE CONTEXT + 20
#define EXCEPT_CONTEXT_OFFSET 0
#define EXCEPT_POINTERS_OFFSET TYPE CONTEXT


#ifdef _MSC_VER
#pragma comment(linker, "/INCLUDE:_RtlUnwind at 16")
#endif

#if 0 // Enable if not in scope
// RtlUnwind unwinds procedure call stack frames.
extern "C"
NTSYSAPI
VOID
NTAPI
RtlUnwind(
	IN OUT PVOID TargetFrame OPTIONAL,
	IN PVOID TargetIp OPTIONAL,
	IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
	IN PVOID ReturnValue
	);
#endif

#define EXCEPT(Expression) { __asm { jmp EXCEPT_EndOfExcept} \
	volatile EXCEPTION_POINTERS EXCEPT_ExceptionPointers; \
	EXCEPT_Handler: /* EXCEPTION_DISPOSITION __cdecl
handler(EXCEPTION_RECORD* ExceptionRecord, void* EstablisherFrame, CONTEXT*
ContextRecord, void* DispatcherContext) */ \
	__asm { \
		__asm mov		eax, dword ptr [esp+0x04] \
		__asm test		dword ptr
[eax]EXCEPTION_RECORD.ExceptionFlags, EH_UNWIND_CONTEXT | EH_NESTED_CALL \
		__asm je		EXCEPT_DoHandler \
\
		__asm xor		eax, eax \
		__asm or		eax, ExceptionContinueSearch \
		__asm ret \
	} \
	EXCEPT_DoHandler: /* Can't define this in inline asm macro + __asm{}
block, or compiler complains. */ \
	__asm { \
		__asm sub		esp, EXCEPT_LOCALS \
		__asm mov		[esp]CONTEXT.Ebp, ebp \
		__asm mov		[esp]CONTEXT.Ebx, ebx \
		__asm mov		[esp]CONTEXT.Edi, edi \
		__asm mov		[esp]CONTEXT.Esi, esi \
		__asm pushfd \
		__asm pop		ecx \
		__asm mov		[esp]CONTEXT.EFlags, ecx \
		__asm mov		ebp, dword ptr
[esp+0x08+EXCEPT_LOCALS] \
		__asm mov		ebp, dword ptr [ebp+0x08] \
		__asm lea		edx, EXCEPT_ExceptionPointers \
		__asm mov		ecx, dword ptr
[esp+0x04+EXCEPT_LOCALS] \
		__asm mov
[edx]EXCEPTION_POINTERS.ExceptionRecord, ecx \
		__asm mov		ecx, dword ptr
[esp+0x0c+EXCEPT_LOCALS] \
		__asm mov
[edx]EXCEPTION_POINTERS.ContextRecord, ecx \
		__asm mov		edx,
[edx]EXCEPTION_POINTERS.ExceptionRecord \
		__asm mov		edx,
[edx]EXCEPTION_RECORD.ExceptionCode \
		__asm mov		EXCEPT_ExceptionCode, edx \
		__asm mov		ebp, dword ptr
[esp+0x0c+EXCEPT_LOCALS] \
		__asm mov		eax, [ebp]CONTEXT.EFlags \
		__asm push		eax \
		__asm popfd	\
		__asm mov		ebp, dword ptr
[esp+0x08+EXCEPT_LOCALS] \
		__asm mov		ebx, dword ptr [ebp+_TRY_SAVED_EBX]
\
		__asm mov		esi, dword ptr [ebp+_TRY_SAVED_ESI]
\
		__asm mov		edi, dword ptr [ebp+_TRY_SAVED_EDI]
\
		__asm mov		ebp, dword ptr [ebp+_TRY_SAVED_EBP]
\
		__asm cld \
	} \
\
	switch( (Expression) ) { \
\
	case EXCEPTION_CONTINUE_SEARCH: \
		ExFreePool(TRY_ExceptionRegistration); \
		__asm { \
			__asm mov		ebp, [esp]CONTEXT.Ebp \
			__asm mov		ebx, [esp]CONTEXT.Ebx \
			__asm mov		edi, [esp]CONTEXT.Edi \
			__asm mov		esi, [esp]CONTEXT.Esi \
			__asm add		esp, EXCEPT_LOCALS \
			__asm xor		eax, eax \
			__asm or		eax, ExceptionContinueSearch
\
			__asm ret \
		} \
\
	case EXCEPTION_CONTINUE_EXECUTION: \
		ExFreePool(TRY_ExceptionRegistration); \
		__asm { \
			__asm mov		eax, dword ptr
[esp+0x08+EXCEPT_LOCALS] \
			__asm push		0 \
			__asm push		0 \
			__asm push
__ret_label_CONTINUE_EXECUTION \
			__asm push		eax \
			__asm call		dword ptr [RtlUnwind] \
		} \
		__ret_label_CONTINUE_EXECUTION: \
		__asm { \
			__asm mov		ebp, [esp]CONTEXT.Ebp \
			__asm mov		ebx, [esp]CONTEXT.Ebx \
			__asm mov		edi, [esp]CONTEXT.Edi \
			__asm mov		esi, [esp]CONTEXT.Esi \
			__asm add		esp, EXCEPT_LOCALS \
			__asm xor		eax, eax \
			__asm or		eax,
ExceptionContinueExecution \
			__asm ret \
		} \
\
	case EXCEPTION_EXECUTE_HANDLER: \
		__asm { \
			__asm pushad \
			__asm pushfd \
			__asm mov		eax, dword ptr
[esp+0x08+0x04+0x20+EXCEPT_LOCALS] \
			__asm push		0 \
			__asm push		0 \
			__asm push		__ret_label_EXECUTE_HANDLER
\
			__asm push		eax \
			__asm call		dword ptr [RtlUnwind] \
		} \
		__ret_label_EXECUTE_HANDLER: \
		 __asm { \
			__asm popfd \
			__asm popad \
		} \
		break; \
\
	default: \
		DPRINT("SEH: Invalid disposition returned from filter
expression!\n"); \
		RtlRaiseStatus(STATUS_INVALID_DISPOSITION); \
\
	} \
\
	__asm { \
		__asm mov		ebp, dword ptr
[esp+0x08+EXCEPT_LOCALS] \
		__asm mov		ebx, dword ptr [ebp+_TRY_SAVED_EBX]
\
		__asm mov		esi, dword ptr [ebp+_TRY_SAVED_ESI]
\
		__asm mov		edi, dword ptr [ebp+_TRY_SAVED_EDI]
\
		__asm mov		esp, dword ptr [ebp+_TRY_SAVED_ESP]
\
		__asm mov		ebp, dword ptr [ebp+0x08] \
	} }

-- End --




More information about the Ros-kernel mailing list