From 45cb5eb1410df3285077e723d54bb4db8e8424f1 Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Tue, 25 Jan 2011 16:26:54 +0000 Subject: [PATCH] [CMAKE] On MSVC builds, compile native SEH support into PSEH library. This should be part of the crt, but our crt is too disorganized to handle that. The empty C file is for cmake to get the library right. svn path=/branches/cmake-bringup/; revision=50486 --- lib/pseh/CMakeLists.txt | 10 + lib/pseh/dummy.c | 2 + lib/pseh/i386/seh.s | 441 +++++++++++++++++++++++++++++++++++++ lib/pseh/i386/seh_prolog.s | 152 +++++++++++++ 4 files changed, 605 insertions(+) create mode 100644 lib/pseh/dummy.c create mode 100644 lib/pseh/i386/seh.s create mode 100644 lib/pseh/i386/seh_prolog.s diff --git a/lib/pseh/CMakeLists.txt b/lib/pseh/CMakeLists.txt index 3ca12e79b5e..23207e13a8a 100644 --- a/lib/pseh/CMakeLists.txt +++ b/lib/pseh/CMakeLists.txt @@ -16,4 +16,14 @@ if(NOT MSVC) add_library(pseh ${SOURCE}) add_dependencies(pseh psdk) +else() + + list(APPEND SOURCE + dummy.c + i386/seh.s + i386/seh_prolog.s) + + add_library(pseh ${SOURCE}) + add_dependencies(pseh asm) + endif() diff --git a/lib/pseh/dummy.c b/lib/pseh/dummy.c new file mode 100644 index 00000000000..c21b66d7297 --- /dev/null +++ b/lib/pseh/dummy.c @@ -0,0 +1,2 @@ + /* intentionally empty file */ + diff --git a/lib/pseh/i386/seh.s b/lib/pseh/i386/seh.s new file mode 100644 index 00000000000..6fde1e181f7 --- /dev/null +++ b/lib/pseh/i386/seh.s @@ -0,0 +1,441 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CRT + * FILE: lib/crt/misc/i386/seh.S + * PURPOSE: SEH Support for the CRT + * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) + */ + +/* INCLUDES ******************************************************************/ + +#include + +#define DISPOSITION_DISMISS 0 +#define DISPOSITION_CONTINUE_SEARCH 1 +#define DISPOSITION_COLLIDED_UNWIND 3 + +#define EXCEPTION_EXIT_UNWIND 4 +#define EXCEPTION_UNWINDING 2 + + +EXTERN _RtlUnwind@16:PROC + +/* GLOBALS *******************************************************************/ + +PUBLIC __global_unwind2 +PUBLIC __local_unwind2 +PUBLIC __abnormal_termination +PUBLIC __except_handler2 +PUBLIC __except_handler3 + +/* FUNCTIONS *****************************************************************/ + +.code +_unwind_handler: + + /* Check if we were unwinding and continue search if not */ + mov ecx, [esp+4] + test dword ptr [ecx+4], EXCEPTION_EXIT_UNWIND + EXCEPTION_UNWINDING + mov eax, DISPOSITION_CONTINUE_SEARCH + jz unwind_handler_return + + /* We have a collision, do a local unwind */ + mov eax, [esp+20] + push ebp + mov ebp, [eax+16] + mov edx, [eax+40] + push edx + mov edx, [eax+36] + push edx + call __local_unwind2 + add esp, 8 + pop ebp + + /* Set new try level */ + mov eax, [esp+8] + mov edx, [esp+16] + mov [edx], eax + + /* Return collided unwind */ + mov eax, DISPOSITION_COLLIDED_UNWIND + +unwind_handler_return: + ret + + +__global_unwind2: + + /* Create stack and save all registers */ + push ebp + mov ebp, esp + push ebx + push esi + push edi + push ebp + + /* Call unwind */ + push 0 + push 0 + push glu_return + push [ebp+8] + call _RtlUnwind@16 + +glu_return: + /* Restore registers and return */ + pop ebp + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + ret + + +__abnormal_termination: + + /* Assume false */ + xor eax, eax + + /* Check if the handler is the unwind handler */ + mov ecx, fs:0 + cmp dword ptr [ecx+4], offset _unwind_handler + jne short ab_return + + /* Get the try level */ + mov edx, [ecx+12] + mov edx, [edx+12] + + /* Compare it */ + cmp [ecx+8], edx + jne ab_return + + /* Return true */ + mov eax, 1 + + /* Return */ +ab_return: + ret + + +__local_unwind2: + + /* Save volatiles */ + push ebx + push esi + push edi + + /* Get the exception registration */ + mov eax, [esp+16] + + /* Setup SEH to protect the unwind */ + push ebp + push eax + push -2 + push offset _unwind_handler + push fs:0 + mov fs:0, esp + +unwind_loop: + /* Get the exception registration and try level */ + mov eax, [esp+36] + mov ebx, [eax+8] + mov esi, [eax+12] + + /* Validate the unwind */ + cmp esi, -1 + je unwind_return + cmp dword ptr [esp+40], -1 + je unwind_ok + cmp esi, [esp+40] + jbe unwind_return + +unwind_ok: + /* Get the new enclosing level and save it */ + lea esi, [esi+esi*2] + mov ecx, [ebx+esi*4] + mov [esp+8], ecx + mov [eax+12], ecx + + /* Check the filter type */ + cmp dword ptr [ebx+esi*4+4], 0 + jnz __NLG_Return2 + + /* FIXME: NLG Notification */ + + /* Call the handler */ + call dword ptr [ebx+esi*4+8] + +__NLG_Return2: + /* Unwind again */ + jmp unwind_loop + +unwind_return: + /* Cleanup SEH */ + pop fs:0 + add esp, 16 + pop edi + pop esi + pop ebx + ret + + +__except_handler2: + + /* Setup stack and save volatiles */ + push ebp + mov ebp, esp + sub esp, 8 + push ebx + push esi + push edi + push ebp + + /* Clear direction flag */ + cld + + /* Get exception registration and record */ + mov ebx, [ebp+12] + mov eax, [ebp+8] + + /* Check if this is an unwind */ + test dword ptr [eax+4], EXCEPTION_EXIT_UNWIND + EXCEPTION_UNWINDING + jnz except_unwind2 + + /* Save exception pointers structure */ + mov [ebp-8], eax + mov eax, [ebp+16] + mov [ebp-4], eax + lea eax, [ebp-8] + mov [ebx+20], eax + + /* Get the try level and scope table */ + mov esi, [ebx+12] + mov edi, [ebx+8] + +except_loop2: + /* Validate try level */ + cmp esi, -1 + je except_search2 + + /* Check if this is the termination handler */ + lea ecx, [esi+esi*2] + cmp dword ptr [edi+ecx*4+4], 0 + jz except_continue2 + + /* Save registers and call filter, then restore them */ + push esi + push ebp + mov ebp, [ebx+16] + call dword ptr [edi+ecx*4+4] + pop ebp + pop esi + + /* Restore ebx and check the result */ + mov ebx, [ebp+12] + or eax, eax + jz except_continue2 + js except_dismiss2 + + /* So this is an accept, call the termination handlers */ + mov edi, [ebx+8] + push ebx + call __global_unwind2 + add esp, 4 + + /* Restore ebp */ + mov ebp, [ebx+16] + + /* Do local unwind */ + push esi + push ebx + call __local_unwind2 + add esp, 8 + + /* Set new try level */ + lea ecx, [esi+esi*2] + mov eax, [edi+ecx*4] + mov [ebx+12], eax + + /* Call except handler */ + call dword ptr [edi+ecx*4+8] + +except_continue2: + /* Reload try level and except again */ + mov edi, [ebx+8] + lea ecx, [esi+esi*2] + mov esi, [edi+ecx*4] + jmp except_loop2 + +except_dismiss2: + /* Dismiss it */ + mov eax, DISPOSITION_DISMISS + jmp except_return2 + +except_search2: + /* Continue searching */ + mov eax, DISPOSITION_CONTINUE_SEARCH + jmp except_return2 + + /* Do local unwind */ +except_unwind2: + push ebp + mov ebp, [ebx+16] + push -1 + push ebx + call __local_unwind2 + add esp, 8 + + /* Retore EBP and set return disposition */ + pop ebp + mov eax, DISPOSITION_CONTINUE_SEARCH + +except_return2: + /* Restore registers and stack */ + pop ebp + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + ret + + +__except_handler3: + + /* Setup stack and save volatiles */ + push ebp + mov ebp, esp + sub esp, 8 + push ebx + push esi + push edi + push ebp + + /* Clear direction flag */ + cld + + /* Get exception registration and record */ + mov ebx, [ebp+12] + mov eax, [ebp+8] + + /* Check if this is an unwind */ + test dword ptr [eax+4], EXCEPTION_EXIT_UNWIND + EXCEPTION_UNWINDING + jnz except_unwind3 + + /* Save exception pointers structure */ + mov [ebp-8], eax + mov eax, [ebp+16] + mov [ebp-4], eax + lea eax, [ebp-8] + mov [ebx-4], eax + + /* Get the try level and scope table */ + mov esi, [ebx+12] + mov edi, [ebx+8] + + /* FIXME: Validate the SEH exception */ + +except_loop3: + /* Validate try level */ + cmp esi, -1 + je except_search3 + + /* Check if this is the termination handler */ + lea ecx, [esi+esi*2] + mov eax, [edi+ecx*4+4] + or eax, eax + jz except_continue3 + + /* Save registers clear them all */ + push esi + push ebp + lea ebp, [ebx+16] + xor ebx, ebx + xor ecx, ecx + xor edx, edx + xor esi, esi + xor edi, edi + + /* Call the filter and restore our registers */ + call eax + pop ebp + pop esi + + /* Restore ebx and check the result */ + mov ebx, [ebp+12] + or eax, eax + jz except_continue3 + js except_dismiss3 + + /* So this is an accept, call the termination handlers */ + mov edi, [ebx+8] + push ebx + call __global_unwind2 + add esp, 4 + + /* Restore ebp */ + lea ebp, [ebx+16] + + /* Do local unwind */ + push esi + push ebx + call __local_unwind2 + add esp, 8 + + /* FIXME: Do NLG Notification */ + + /* Set new try level */ + lea ecx, [esi+esi*2] + mov eax, [edi+ecx*4] + mov [ebx+12], eax + + /* Clear registers and call except handler */ + mov eax, [edi+ecx*4+8] + xor ebx, ebx + xor ecx, ecx + xor edx, edx + xor esi, esi + xor edi, edi + call eax + +except_continue3: + /* Reload try level and except again */ + mov edi, [ebx+8] + lea ecx, [esi+esi*2] + mov esi, [edi+ecx*4] + jmp except_loop3 + +except_dismiss3: + /* Dismiss it */ + mov eax, DISPOSITION_DISMISS + jmp except_return3 + +except_search3: + /* Continue searching */ + mov eax, DISPOSITION_CONTINUE_SEARCH + jmp except_return3 + + /* Do local unwind */ +except_unwind3: + push ebp + mov ebp, [ebx+16] + push -1 + push ebx + call __local_unwind2 + add esp, 8 + + /* Retore EBP and set return disposition */ + pop ebp + mov eax, DISPOSITION_CONTINUE_SEARCH + +except_return3: + /* Restore registers and stack */ + pop ebp + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + ret + +END diff --git a/lib/pseh/i386/seh_prolog.s b/lib/pseh/i386/seh_prolog.s new file mode 100644 index 00000000000..98b45baf97b --- /dev/null +++ b/lib/pseh/i386/seh_prolog.s @@ -0,0 +1,152 @@ +/* + * COPYRIGHT: GNU GPL, see COPYING in the top level directory + * PROJECT: ReactOS CRT + * FILE: lib/crt/misc/i386/seh_prolog.S + * PURPOSE: SEH Support for MSVC + * PROGRAMMERS: Timo Kreuzer + */ + +/* INCLUDES ******************************************************************/ + +#include + +EXTERN __except_handler3:PROC + +/* The very first thing a function compiled with MSVC containing SEH + * will do is call __SEH_prolog like this: + * + * push + * push
+ * call __SEH_prolog + * + * When entering the function the stack layout is like this: + * + * esp + 08: OLDFRAME.StackBytes + * esp + 04: OLDFRAME.SEHTable + * esp + 00: OLDFRAME.ReturnAddress + * + * __SEH_prolog will now setup the stack to the following layout: + * + * esp + N + 24: SEH_FRAME.OriginalEbp OLDFRAME.StackBytes + * esp + N + 20: SEH_FRAME.Disable OLDFRAME.SEHTable + * esp + N + 1C: SEH_FRAME.SEHTable OLDFRAME.ReturnAddress + * esp + N + 18: SEH_FRAME.Handler + * esp + N + 14: SEH_FRAME.PreviousRecord + * esp + N + 10: SEH_FRAME.unused + * esp + N + 0c: SEH_FRAME.NewEsp + * + * N bytes local variables + * ... + * esp + 08: SAFE_AREA.Ebx + * esp + 04: SAFE_AREA.Esi + * esp + 00: SAFE_AREA.Edi + * + * all this is documented here (with some minor errors): + * http://reactos-blog.blogspot.com/2009/08/inside-mind-of-reactos-developer.html + */ + +OLDFRAME_ReturnAddress = 0 /* 0x00 */ +OLDFRAME_SEHTable = 4 /* 0x04 */ +OLDFRAME_StackBytes = 8 /* 0x08 */ +OLDFRAME_Size = 12 /* 0x0c */ + +SEH_FRAME_NewEsp = 0 /* 0x00 */ +SEH_FRAME_unused = 4 /* 0x04 */ +SEH_FRAME_PreviousRecord = 8 /* 0x08 */ +SEH_FRAME_Handler = 12 /* 0x0c */ +SEH_FRAME_SEHTable = 16 /* 0x10 */ +SEH_FRAME_Disable = 20 /* 0x14 */ +SEH_FRAME_OriginalEbp = 24 /* 0x18 */ +SEH_FRAME_Size = 28 /* 0x1c */ + +SAFE_AREA_Edi = 0 /* 0x00 */ +SAFE_AREA_Esi = 4 /* 0x04 */ +SAFE_AREA_Ebx = 8 /* 0x08 */ +SAFE_AREA_Size = 12 /* 0x0c */ + + +.code + +PUBLIC __SEH_prolog +__SEH_prolog: + + /* Get the number of stack bytes to reserve */ + mov eax, [esp + OLDFRAME_StackBytes] + + /* Push address of __except_handler3 on the stack */ + push offset __except_handler3 + + /* Push the old exception record on the stack */ + push dword ptr fs:0 + + /* Adjust stack allocation, add size of the stack frame minus 2 pushes */ + add eax, SEH_FRAME_Size + SAFE_AREA_Size - OLDFRAME_Size - 8 + + /* Save old ebp, overwriting OLDFRAME.StackBytes */ + mov [esp + 8 + OLDFRAME_StackBytes], ebp + + /* Load new ebp, pointing to OLDFRAME.StackBytes */ + lea ebp, [esp + 8 + OLDFRAME_StackBytes] + + /* Allocate stack space */ + sub esp, eax + + /* Push the return address on the stack */ + push dword ptr [ebp - OLDFRAME_StackBytes + OLDFRAME_ReturnAddress] + + /* Get address of the SEH table */ + mov eax, [ebp + OLDFRAME_SEHTable] + + /* Save new esp */ + mov [ebp - SEH_FRAME_OriginalEbp + SEH_FRAME_NewEsp], esp + + /* Safe SEH table, overwriting OLDFRAME.ReturnAddress */ + mov [ebp - SEH_FRAME_OriginalEbp + SEH_FRAME_SEHTable], eax + + /* Safe the disable value, overwriting OLDFRAME.SEHTable */ + mov dword ptr [ebp - SEH_FRAME_OriginalEbp + SEH_FRAME_Disable], -1 + + /* Load the address of the new registration record */ + lea eax, [ebp - SEH_FRAME_OriginalEbp + SEH_FRAME_PreviousRecord] + + /* Save registers */ + mov [esp + SAFE_AREA_Edi], edi + mov [esp + SAFE_AREA_Esi], esi + mov [esp + SAFE_AREA_Ebx], ebx + + /* Enqueue the new record */ + mov fs:[0], eax + + /* Return to the caller */ + ret + + +PUBLIC __SEH_epilog +__SEH_epilog: + + /* Restore the previous exception registration record */ + mov ecx, [ebp - SEH_FRAME_OriginalEbp + SEH_FRAME_PreviousRecord] + mov fs:[0], ecx + + /* Restore saved registers */ + mov edi, [esp + 4 + SAFE_AREA_Edi] + mov esi, [esp + 4 + SAFE_AREA_Esi] + mov ebx, [esp + 4 + SAFE_AREA_Ebx] + + /* Get the return address */ + mov ecx, [esp] + + /* Clean up stack */ + mov esp, ebp + + /* Get previous ebp */ + mov ebp, [esp] + + /* Save return address */ + mov [esp], ecx + + /* Return to the caller */ + ret + + +END