Hello Skywing, I have forwarded your request to wine-devel also in the event your SEH implementation might help them.
Thanks Steven
--- Skywing skywing@valhallalegends.com wrote:
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.
__________________________________ Do you Yahoo!? Exclusive Video Premiere - Britney Spears http://launch.yahoo.com/promos/britneyspears/
#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@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] \ } }