From b824c3ef3905edacd32d67998763152956f5f198 Mon Sep 17 00:00:00 2001 From: Casper Hornstrup Date: Sat, 26 Oct 2002 00:38:01 +0000 Subject: [PATCH] *** empty log message *** svn path=/trunk/; revision=3658 --- reactos/ntoskrnl/rtl/i386/except.s | 290 ++++++++++++++++ reactos/ntoskrnl/rtl/i386/exception.c | 475 ++++++++++++++++++++++++++ reactos/ntoskrnl/rtl/i386/seh.s | 366 ++++++++++++++++++++ 3 files changed, 1131 insertions(+) create mode 100755 reactos/ntoskrnl/rtl/i386/except.s create mode 100755 reactos/ntoskrnl/rtl/i386/exception.c create mode 100755 reactos/ntoskrnl/rtl/i386/seh.s diff --git a/reactos/ntoskrnl/rtl/i386/except.s b/reactos/ntoskrnl/rtl/i386/except.s new file mode 100755 index 00000000000..750c253274e --- /dev/null +++ b/reactos/ntoskrnl/rtl/i386/except.s @@ -0,0 +1,290 @@ +/* $Id: except.s,v 1.1 2002/10/26 00:36:54 chorns Exp $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * PURPOSE: Kernel-mode exception support for IA-32 + * FILE: ntoskrnl/rtl/i386/except.s + * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net) + * NOTES: This file is shared with lib/ntdll/rtl/i386/except.s. + * Please keep them in sync. + */ + +#define EXCEPTION_UNWINDING 0x02 + +#define EREC_FLAGS 0x04 + +#define ExceptionContinueExecution 0 +#define ExceptionContinueSearch 1 +#define ExceptionNestedException 2 +#define ExceptionCollidedUnwind 3 + +.globl _RtlpExecuteHandlerForException +.globl _RtlpExecuteHandlerForUnwind + +#define CONTEXT_FLAGS 0x00 +#define CONTEXT_SEGGS 0x8C +#define CONTEXT_SEGFS 0x90 +#define CONTEXT_SEGES 0x94 +#define CONTEXT_SEGDS 0x98 +#define CONTEXT_EDI 0x9C +#define CONTEXT_ESI 0xA0 +#define CONTEXT_EBX 0xA4 +#define CONTEXT_EDX 0xA8 +#define CONTEXT_ECX 0xAC +#define CONTEXT_EAX 0xB0 +#define CONTEXT_EBP 0xB4 +#define CONTEXT_EIP 0xB8 +#define CONTEXT_SEGCS 0xBC +#define CONTEXT_EFLAGS 0xC0 +#define CONTEXT_ESP 0xC4 +#define CONTEXT_SEGSS 0xC8 + + +#define RCC_CONTEXT 0x08 + +// EAX = value to print +_do_debug: + pushal + pushl %eax + call _AsmDebug@4 + popal + ret + +#ifndef __NTOSKRNL__ + +// +// VOID +// RtlpCaptureContext(PCONTEXT pContext); +// +// Parameters: +// [ESP+08h] - PCONTEXT_X86 pContext +// Registers: +// None +// Returns: +// Nothing +// Notes: +// Grabs the current CPU context. +.globl _RtlpCaptureContext +_RtlpCaptureContext: + pushl %ebp + movl %esp, %ebp + movl RCC_CONTEXT(%ebp), %edx // EDX = Address of context structure + + cld + pushf + pop %eax + movl %eax, CONTEXT_EFLAGS(%edx) + xorl %eax, %eax + movl %eax, CONTEXT_EAX(%edx) + movl %eax, CONTEXT_EBX(%edx) + movl %eax, CONTEXT_ECX(%edx) + movl %eax, CONTEXT_EDX(%edx) + movl %eax, CONTEXT_ESI(%edx) + movl %eax, CONTEXT_EDI(%edx) + movl %cs, %eax + movl %eax, CONTEXT_SEGCS(%edx) + movl %ds, %eax + movl %eax, CONTEXT_SEGDS(%edx) + movl %es, %eax + movl %eax, CONTEXT_SEGES(%edx) + movl %fs, %eax + movl %eax, CONTEXT_SEGFS(%edx) + movl %gs, %eax + movl %eax, CONTEXT_SEGGS(%edx) + movl %ss, %eax + movl %eax, CONTEXT_SEGSS(%edx) + + // + // STACK LAYOUT: - (ESP to put in context structure) + // - RETURN ADDRESS OF CALLER OF CALLER + // - EBP OF CALLER OF CALLER + // ... + // - RETURN ADDRESS OF CALLER + // - EBP OF CALLER + // ... + // + + // Get return address of the caller of the caller of this function + movl %ebp, %ebx + //movl 4(%ebx), %eax // EAX = return address of caller + movl (%ebx), %ebx // EBX = EBP of caller + + movl 4(%ebx), %eax // EAX = return address of caller of caller + movl (%ebx), %ebx // EBX = EBP of caller of caller + + movl %eax, CONTEXT_EIP(%edx) // EIP = return address of caller of caller + movl %ebx, CONTEXT_EBP(%edx) // EBP = EBP of caller of caller + addl $8, %ebx + movl %ebx, CONTEXT_ESP(%edx) // ESP = EBP of caller of caller + 8 + + movl %ebp, %esp + popl %ebp + ret + +#endif /* !__NTOSKRNL__ */ + +#define REH_ERECORD 0x08 +#define REH_RFRAME 0x0C +#define REH_CONTEXT 0x10 +#define REH_DCONTEXT 0x14 +#define REH_EROUTINE 0x18 + +// Parameters: +// None +// Registers: +// [EBP+08h] - PEXCEPTION_RECORD ExceptionRecord +// [EBP+0Ch] - PEXCEPTION_REGISTRATION RegistrationFrame +// [EBP+10h] - PVOID Context +// [EBP+14h] - PVOID DispatcherContext +// [EBP+18h] - PEXCEPTION_HANDLER ExceptionRoutine +// EDX - Address of protecting exception handler +// Returns: +// EXCEPTION_DISPOSITION +// Notes: +// Setup the protecting exception handler and call the exception +// handler in the right context. +_RtlpExecuteHandler: + pushl %ebp + movl %esp, %ebp + pushl REH_RFRAME(%ebp) + + pushl %edx + pushl %fs:0x0 + movl %esp, %fs:0x0 + + // Prepare to call the exception handler + pushl REH_DCONTEXT(%ebp) + pushl REH_CONTEXT(%ebp) + pushl REH_RFRAME(%ebp) + pushl REH_ERECORD(%ebp) + + // Now call the exception handler + movl REH_EROUTINE(%ebp), %eax + call *%eax + + cmpl $-1, %fs:0x0 + jne .reh_stack_looks_ok + + // This should not happen + pushl 0 + pushl 0 + pushl 0 + pushl 0 + call _RtlAssert@16 + +.reh_loop: + jmp .reh_loop + +.reh_stack_looks_ok: + movl %fs:0x0, %esp + + // Return to the 'front-end' for this function + popl %fs:0x0 + movl %ebp, %esp + popl %ebp + ret + + +#define REP_ERECORD 0x04 +#define REP_RFRAME 0x08 +#define REP_CONTEXT 0x0C +#define REP_DCONTEXT 0x10 + +// Parameters: +// [ESP+04h] - PEXCEPTION_RECORD ExceptionRecord +// [ESP+08h] - PEXCEPTION_REGISTRATION RegistrationFrame +// [ESP+0Ch] - PCONTEXT Context +// [ESP+10h] - PVOID DispatcherContext +// Registers: +// None +// Returns: +// EXCEPTION_DISPOSITION +// Notes: +// This exception handler protects the exception handling +// mechanism by detecting nested exceptions. +_RtlpExceptionProtector: + movl $ExceptionContinueSearch, %eax + movl REP_ERECORD(%esp), %ecx + testl $EXCEPTION_UNWINDING, EREC_FLAGS(%ecx) + jnz .rep_end + + // Unwinding is not taking place, so return ExceptionNestedException + + // Set DispatcherContext field to the exception registration for the + // exception handler that executed when a nested exception occurred + movl REP_DCONTEXT(%esp), %ecx movl REP_RFRAME(%esp), %eax + movl %eax, (%ecx) + movl $ExceptionNestedException, %eax + +.rep_end: + ret + + +// Parameters: +// [ESP+04h] - PEXCEPTION_RECORD ExceptionRecord +// [ESP+08h] - PEXCEPTION_REGISTRATION RegistrationFrame +// [ESP+0Ch] - PCONTEXT Context +// [ESP+10h] - PVOID DispatcherContext +// [ESP+14h] - PEXCEPTION_HANDLER ExceptionHandler +// Registers: +// None +// Returns: +// EXCEPTION_DISPOSITION +// Notes: +// Front-end +_RtlpExecuteHandlerForException: + movl $_RtlpExceptionProtector, %edx + jmp _RtlpExecuteHandler + + +#define RUP_ERECORD 0x04 +#define RUP_RFRAME 0x08 +#define RUP_CONTEXT 0x0C +#define RUP_DCONTEXT 0x10 + +// Parameters: +// [ESP+04h] - PEXCEPTION_RECORD ExceptionRecord +// [ESP+08h] - PEXCEPTION_REGISTRATION RegistrationFrame +// [ESP+0Ch] - PCONTEXT Context +// [ESP+10h] - PVOID DispatcherContext +// Registers: +// None +// Returns: +// EXCEPTION_DISPOSITION +// Notes: +// This exception handler protects the exception handling +// mechanism by detecting collided unwinds. +_RtlpUnwindProtector: + movl $ExceptionContinueSearch, %eax + movl %ecx, RUP_ERECORD(%esp) + testl $EXCEPTION_UNWINDING, EREC_FLAGS(%ecx) + jz .rup_end + + // Unwinding is taking place, so return ExceptionCollidedUnwind + + movl RUP_RFRAME(%esp), %ecx + movl RUP_DCONTEXT(%esp), %edx + + // Set DispatcherContext field to the exception registration for the + // exception handler that executed when a collision occurred + movl RUP_RFRAME(%ecx), %eax + movl %eax, (%edx) + movl $ExceptionCollidedUnwind, %eax + +.rup_end: + ret + + +// Parameters: +// [ESP+04h] - PEXCEPTION_RECORD ExceptionRecord +// [ESP+08h] - PEXCEPTION_REGISTRATION RegistrationFrame +// [ESP+0Ch] - PCONTEXT Context +// [ESP+10h] - PVOID DispatcherContext +// [ESP+14h] - PEXCEPTION_HANDLER ExceptionHandler +// Registers: +// None +// Returns: +// EXCEPTION_DISPOSITION +_RtlpExecuteHandlerForUnwind: + movl $_RtlpUnwindProtector, %edx + jmp _RtlpExecuteHandler diff --git a/reactos/ntoskrnl/rtl/i386/exception.c b/reactos/ntoskrnl/rtl/i386/exception.c new file mode 100755 index 00000000000..3983ae4a52a --- /dev/null +++ b/reactos/ntoskrnl/rtl/i386/exception.c @@ -0,0 +1,475 @@ +/* $Id: exception.c,v 1.1 2002/10/26 00:36:54 chorns Exp $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * PURPOSE: Kernel-mode exception support for IA-32 + * FILE: ntoskrnl/rtl/i386/exception.c + * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net) + */ + +/* INCLUDES *****************************************************************/ + +#include +#include +#include + +#define NDEBUG +#include + +/* FUNCTIONS ***************************************************************/ + +#if 1 +VOID STDCALL +MsvcrtDebug(ULONG Value) +{ + DbgPrint("KernelDebug 0x%.08x\n", Value); +} +#endif + +int +_abnormal_termination(void) +{ + DbgPrint("Abnormal Termination\n"); + return 0; +} + +struct _CONTEXT; + +EXCEPTION_DISPOSITION +_except_handler2( + struct _EXCEPTION_RECORD *ExceptionRecord, + void *RegistrationFrame, + struct _CONTEXT *ContextRecord, + void *DispatcherContext) +{ + DbgPrint("_except_handler2()\n"); + return (EXCEPTION_DISPOSITION)0; +} + +void __cdecl +_global_unwind2(PEXCEPTION_REGISTRATION RegistrationFrame) +{ + RtlUnwind(RegistrationFrame, &&__ret_label, NULL, 0); +__ret_label: + // return is important + return; +} + + +/* Implemented in except.s */ + +VOID +RtlpCaptureContext(PCONTEXT pContext); + +/* Macros that will help streamline the SEH implementations for + kernel mode and user mode */ + +#define SehpGetStackLimits(StackBase, StackLimit) \ +{ \ + (*(StackBase)) = KeGetCurrentKPCR()->StackBase; \ + (*(StackLimit)) = KeGetCurrentKPCR()->StackLimit; \ +} + +#define SehpGetExceptionList() \ + (PEXCEPTION_REGISTRATION)(KeGetCurrentThread()->TrapFrame ->ExceptionList) + +#define SehpSetExceptionList(NewExceptionList) \ + KeGetCurrentThread()->TrapFrame->ExceptionList = (PVOID)(NewExceptionList) + +#define SehpCaptureContext(Context) \ +{ \ + KeTrapFrameToContext(KeGetCurrentThread()->TrapFrame, (Context)); \ +} + +/*** Code below this line is shared with lib/ntdll/arch/ia32/exception.c - please keep in sync ***/ + +VOID STDCALL +AsmDebug(ULONG Value) +{ + DbgPrint("Value 0x%.08x\n", Value); +} + + +/* Declare a few prototypes for the functions in except.s */ + +EXCEPTION_DISPOSITION +RtlpExecuteHandlerForException( + PEXCEPTION_RECORD ExceptionRecord, + PEXCEPTION_REGISTRATION RegistrationFrame, + PCONTEXT Context, + PVOID DispatcherContext, + PEXCEPTION_HANDLER ExceptionHandler); + +EXCEPTION_DISPOSITION +RtlpExecuteHandlerForUnwind( + PEXCEPTION_RECORD ExceptionRecord, + PEXCEPTION_REGISTRATION RegistrationFrame, + PCONTEXT Context, + PVOID DispatcherContext, + PEXCEPTION_HANDLER ExceptionHandler); + + +#ifndef NDEBUG + +VOID RtlpDumpExceptionRegistrations(VOID) +{ + PEXCEPTION_REGISTRATION Current; + + DbgPrint("Dumping exception registrations:\n"); + + Current = SehpGetExceptionList(); + + if ((ULONG_PTR)Current != -1) + { + while ((ULONG_PTR)Current != -1) + { + DbgPrint(" (0x%08X) HANDLER (0x%08X)\n", Current, Current->handler); + Current = Current->prev; + } + DbgPrint(" End-Of-List\n"); + } else { + DbgPrint(" No exception registrations exists.\n"); + } +} + +#endif /* NDEBUG */ + +ULONG +RtlpDispatchException(IN PEXCEPTION_RECORD ExceptionRecord, + IN PCONTEXT Context) +{ + PEXCEPTION_REGISTRATION RegistrationFrame; + DWORD DispatcherContext; + DWORD ReturnValue; + + DPRINT("RtlpDispatchException()\n"); + +#ifndef NDEBUG + RtlpDumpExceptionRegistrations(); +#endif /* NDEBUG */ + + RegistrationFrame = SehpGetExceptionList(); + + DPRINT("RegistrationFrame is 0x%X\n", RegistrationFrame); + + while ((ULONG_PTR)RegistrationFrame != -1) + { + EXCEPTION_RECORD ExceptionRecord2; + DWORD Temp = 0; + //PVOID RegistrationFrameEnd = (PVOID)RegistrationFrame + 8; + + // Make sure the registration frame is located within the stack + + DPRINT("Error checking\n"); +#if 0 + if (Teb->Tib.StackBase > RegistrationFrameEnd) + { + DPRINT("Teb->Tib.StackBase (0x%.08x) > RegistrationFrameEnd (0x%.08x)\n", + Teb->Tib.StackBase, RegistrationFrameEnd); + ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID; + return ExceptionContinueExecution; + } + // FIXME: Stack top, correct? + if (Teb->Tib.StackLimit < RegistrationFrameEnd) + { + DPRINT("Teb->Tib.StackLimit (0x%.08x) > RegistrationFrameEnd (0x%.08x)\n", + Teb->Tib.StackLimit, RegistrationFrameEnd); + ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID; + return ExceptionContinueExecution; + } + + // Make sure stack is DWORD aligned + if ((ULONG_PTR)RegistrationFrame & 3) + { + DPRINT("RegistrationFrameEnd (0x%.08x) is not DWORD aligned.\n", + RegistrationFrameEnd); + ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID; + return ExceptionContinueExecution; + } +#endif + +#if 0 + /* FIXME: */ + if (someFlag) + RtlpLogLastExceptionDisposition( hLog, retValue ); +#endif + + DPRINT("Calling handler at 0x%X\n", RegistrationFrame->handler); + DPRINT("ExceptionRecord 0x%X\n", ExceptionRecord); + DPRINT("RegistrationFrame 0x%X\n", RegistrationFrame); + DPRINT("Context 0x%X\n", Context); + DPRINT("&DispatcherContext 0x%X\n", &DispatcherContext); + + ReturnValue = RtlpExecuteHandlerForException( + ExceptionRecord, + RegistrationFrame, + Context, + &DispatcherContext, + RegistrationFrame->handler); + +#ifndef NDEBUG + + DPRINT("Exception handler said 0x%X\n", ReturnValue); + DPRINT("RegistrationFrame == 0x%.08x\n", RegistrationFrame); + { + PULONG sp = (PULONG)((PVOID)RegistrationFrame - 0x08); + DPRINT("StandardESP == 0x%.08x\n", sp[0]); + DPRINT("Exception Pointers == 0x%.08x\n", sp[1]); + DPRINT("PrevFrame == 0x%.08x\n", sp[2]); + DPRINT("Handler == 0x%.08x\n", sp[3]); + DPRINT("ScopeTable == 0x%.08x\n", sp[4]); + DPRINT("TryLevel == 0x%.08x\n", sp[5]); + DPRINT("EBP == 0x%.08x\n", sp[6]); + } + +#endif + + if (RegistrationFrame == NULL) + { + ExceptionRecord->ExceptionFlags &= ~EXCEPTION_NESTED_CALL; // Turn off flag + } + + if (ReturnValue == ExceptionContinueExecution) + { + DPRINT("ReturnValue == ExceptionContinueExecution\n"); + if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) + { + DPRINT("(ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) == TRUE\n"); + + ExceptionRecord2.ExceptionRecord = ExceptionRecord; + ExceptionRecord2.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; + ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + ExceptionRecord2.NumberParameters = 0; + RtlRaiseException(&ExceptionRecord2); + } + else + { + /* Copy the (possibly changed) context back to the trap frame and return */ + NtContinue(Context, FALSE); + return ExceptionContinueExecution; + } + } + else if (ReturnValue == ExceptionContinueSearch) + { + DPRINT("ReturnValue == ExceptionContinueSearch\n"); + + /* Nothing to do here */ + } + else if (ReturnValue == ExceptionNestedException) + { + DPRINT("ReturnValue == ExceptionNestedException\n"); + + ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND; + if (DispatcherContext > Temp) + { + Temp = DispatcherContext; + } + } + else /* if (ReturnValue == ExceptionCollidedUnwind) */ + { + DPRINT("ReturnValue == ExceptionCollidedUnwind or unknown\n"); + + ExceptionRecord2.ExceptionRecord = ExceptionRecord; + ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION; + ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + ExceptionRecord2.NumberParameters = 0; + RtlRaiseException(&ExceptionRecord2); + } + + RegistrationFrame = RegistrationFrame->prev; // Go to previous frame + } + + /* No exception handler will handle this exception */ + + DPRINT("RtlpDispatchException(): Return ExceptionContinueExecution\n"); + + return ExceptionContinueExecution; +} + +VOID STDCALL +RtlRaiseStatus(NTSTATUS Status) +{ + EXCEPTION_RECORD ExceptionRecord; + + DPRINT("RtlRaiseStatus(Status 0x%.08x)\n", Status); + + ExceptionRecord.ExceptionCode = Status; + ExceptionRecord.ExceptionRecord = NULL; + ExceptionRecord.NumberParameters = 0; + ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + RtlRaiseException (& ExceptionRecord); +} + +VOID STDCALL +RtlUnwind(PEXCEPTION_REGISTRATION RegistrationFrame, + PVOID ReturnAddress, + PEXCEPTION_RECORD ExceptionRecord, + DWORD EaxValue) +{ + PEXCEPTION_REGISTRATION ERHead; + PEXCEPTION_RECORD pExceptRec; + EXCEPTION_RECORD TempER; + CONTEXT Context; + + DPRINT("RtlUnwind(). RegistrationFrame 0x%X\n", RegistrationFrame); + +#ifndef NDEBUG + RtlpDumpExceptionRegistrations(); +#endif /* NDEBUG */ + + ERHead = SehpGetExceptionList(); + + DPRINT("ERHead is 0x%X\n", ERHead); + + if (ExceptionRecord == NULL) // The normal case + { + DPRINT("ExceptionRecord == NULL (normal)\n"); + + pExceptRec = &TempER; + pExceptRec->ExceptionFlags = 0; + pExceptRec->ExceptionCode = STATUS_UNWIND; + pExceptRec->ExceptionRecord = NULL; + pExceptRec->ExceptionAddress = ReturnAddress; + pExceptRec->ExceptionInformation[0] = 0; + } + + if (RegistrationFrame) + pExceptRec->ExceptionFlags |= EXCEPTION_UNWINDING; + else + pExceptRec->ExceptionFlags |= (EXCEPTION_UNWINDING|EXCEPTION_EXIT_UNWIND); + +#ifndef NDEBUG + DPRINT("ExceptionFlags == 0x%x:\n", pExceptRec->ExceptionFlags); + if (pExceptRec->ExceptionFlags & EXCEPTION_UNWINDING) + { + DPRINT(" * EXCEPTION_UNWINDING (0x%x)\n", EXCEPTION_UNWINDING); + } + if (pExceptRec->ExceptionFlags & EXCEPTION_EXIT_UNWIND) + { + DPRINT(" * EXCEPTION_EXIT_UNWIND (0x%x)\n", EXCEPTION_EXIT_UNWIND); + } +#endif /* NDEBUG */ + + Context.ContextFlags = + (CONTEXT_i386 | CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS); + + SehpCaptureContext(&Context); + + DPRINT("Context.Eip = 0x%.08x\n", Context.Eip); + DPRINT("Context.Ebp = 0x%.08x\n", Context.Ebp); + DPRINT("Context.Esp = 0x%.08x\n", Context.Esp); + + Context.Esp += 0x10; + Context.Eax = EaxValue; + + // Begin traversing the list of EXCEPTION_REGISTRATION + while ((ULONG_PTR)ERHead != -1) + { + EXCEPTION_RECORD er2; + + DPRINT("ERHead 0x%X\n", ERHead); + + if (ERHead == RegistrationFrame) + { + DPRINT("Continueing execution\n"); + NtContinue(&Context, FALSE); + return; + } + else + { + // If there's an exception frame, but it's lower on the stack + // than the head of the exception list, something's wrong! + if (RegistrationFrame && (RegistrationFrame <= ERHead)) + { + DPRINT("The exception frame is bad\n"); + + // Generate an exception to bail out + er2.ExceptionRecord = pExceptRec; + er2.NumberParameters = 0; + er2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET; + er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + + RtlRaiseException(&er2); + } + } + +#if 0 + Stack = ERHead + sizeof(EXCEPTION_REGISTRATION); + if ( (Teb->Tib.StackBase <= (PVOID)ERHead ) // Make sure that ERHead + && (Teb->Tib.->StackLimit >= (PVOID)Stack ) // is in range, and a multiple + && (0 == ((ULONG_PTR)ERHead & 3)) ) // of 4 (i.e., sane) + { +#else + if (1) { +#endif + PEXCEPTION_REGISTRATION NewERHead; + PEXCEPTION_REGISTRATION pCurrExceptReg; + EXCEPTION_DISPOSITION ReturnValue; + + DPRINT("Executing handler at 0x%X for unwind\n", ERHead->handler); + + ReturnValue = RtlpExecuteHandlerForUnwind( + pExceptRec, + ERHead, + &Context, + &NewERHead, + ERHead->handler); + + DPRINT("Handler at 0x%X returned 0x%X\n", ERHead->handler, ReturnValue); + + if (ReturnValue != ExceptionContinueSearch) + { + if (ReturnValue != ExceptionCollidedUnwind) + { + DPRINT("Bad return value\n"); + + er2.ExceptionRecord = pExceptRec; + er2.NumberParameters = 0; + er2.ExceptionCode = STATUS_INVALID_DISPOSITION; + er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + + RtlRaiseException(&er2); + } + else + { + ERHead = NewERHead; + } + } + + pCurrExceptReg = ERHead; + ERHead = ERHead->prev; + + DPRINT("New ERHead is 0x%X\n", ERHead); + + DPRINT("Setting exception registration at 0x%X as current\n", + RegistrationFrame->prev); + + // Unlink the exception handler + SehpSetExceptionList(RegistrationFrame->prev); + } + else // The stack looks goofy! Raise an exception to bail out + { + DPRINT("Bad stack\n"); + + er2.ExceptionRecord = pExceptRec; + er2.NumberParameters = 0; + er2.ExceptionCode = STATUS_BAD_STACK; + er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + + RtlRaiseException(&er2); + } + } + + // If we get here, we reached the end of the EXCEPTION_REGISTRATION list. + // This shouldn't happen normally. + + DPRINT("Ran out of exception registrations. RegistrationFrame is (0x%X)\n", + RegistrationFrame); + + if ((ULONG_PTR)RegistrationFrame == -1) + NtContinue(&Context, FALSE); + else + NtRaiseException(pExceptRec, &Context, 0); +} + +/* EOF */ diff --git a/reactos/ntoskrnl/rtl/i386/seh.s b/reactos/ntoskrnl/rtl/i386/seh.s new file mode 100755 index 00000000000..411f308e41d --- /dev/null +++ b/reactos/ntoskrnl/rtl/i386/seh.s @@ -0,0 +1,366 @@ +/* $Id: seh.s,v 1.1 2002/10/26 00:38:01 chorns Exp $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * PURPOSE: Runtime library exception support for IA-32 + * FILE: ntoskrnl/rtl/i386/seh.s + * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net) + * NOTES: This file is shared with lib/msvcrt/except/seh.s. + * Please keep them in sync. + */ + +#define ExceptionContinueExecution 0 +#define ExceptionContinueSearch 1 +#define ExceptionNestedException 2 +#define ExceptionCollidedUnwind 3 + +#define EXCEPTION_NONCONTINUABLE 0x01 +#define EXCEPTION_UNWINDING 0x02 +#define EXCEPTION_EXIT_UNWIND 0x04 +#define EXCEPTION_STACK_INVALID 0x08 +#define EXCEPTION_NESTED_CALL 0x10 +#define EXCEPTION_TARGET_UNWIND 0x20 +#define EXCEPTION_COLLIDED_UNWIND 0x40 + +#define EXCEPTION_UNWIND_MODE \ +( EXCEPTION_UNWINDING \ + | EXCEPTION_EXIT_UNWIND \ + | EXCEPTION_TARGET_UNWIND \ + | EXCEPTION_COLLIDED_UNWIND) + +#define EREC_CODE 0x00 +#define EREC_FLAGS 0x04 +#define EREC_RECORD 0x08 +#define EREC_ADDRESS 0x0C +#define EREC_NUMPARAMS 0x10 +#define EREC_INFO 0x14 + +#define TRYLEVEL_NONE -1 +#define TRYLEVEL_INVALID -2 + +#define ER_STANDARDESP -0x08 +#define ER_EPOINTERS -0x04 +#define ER_PREVFRAME 0x00 +#define ER_HANDLER 0x04 +#define ER_SCOPETABLE 0x08 +#define ER_TRYLEVEL 0x0C +#define ER_EBP 0x10 + +#define ST_TRYLEVEL 0x00 +#define ST_FILTER 0x04 +#define ST_HANDLER 0x08 + +#define CONTEXT_EDI 0x9C +#define CONTEXT_EBX 0xA4 +#define CONTEXT_EIP 0xB8 + +.globl __local_unwind2 +.globl __except_handler3 + +// EAX = value to print +_do_debug: + pushal + pushl %eax + call _MsvcrtDebug@4 + popal + ret + +#define LU2_TRYLEVEL 0x08 +#define LU2_REGFRAME 0x04 + +// +// void +// _local_unwind2(PEXCEPTION_REGISTRATION RegistrationFrame, +// LONG TryLevel) +// +// Parameters: +// [EDX+08h] - PEXCEPTION_REGISTRATION RegistrationFrame +// [EDX+04h] - LONG TryLevel +// Registers: +// EBP - EBP of call frame we are unwinding +// Returns: +// Nothing +// Notes: +// Run all termination handlers for a call frame from the current +// try-level up to (but not including) the given stop try-level. +__local_unwind2: + // Setup our call frame so we can access parameters using EDX + //pushl %ebp + movl %esp, %edx + + // FIXME: Setup an EXCEPTION_REGISTRATION entry to protect the + // unwinding in case something goes wrong + +.lu2_next_scope: + + // Keep a pointer to the exception registration in EBX + movl LU2_REGFRAME(%edx), %ebx + + // If we have reached the end of the chain or we're asked to stop here + // by the caller then exit + movl ER_TRYLEVEL(%ebx), %eax + + cmpl $-1, %eax + je .lu2_done + + cmpl LU2_TRYLEVEL(%edx), %eax + je .lu2_done + + // Keep a pointer to the scopetable in ESI + movl ER_SCOPETABLE(%ebx), %esi + + // Compute the offset of the entry in the scopetable that describes + // the scope that is to be unwound. Put the offset in EDI. + movl ST_TRYLEVEL(%esi), %edi + lea (%edi, %edi, 2), %edi + shll $2, %edi + addl %esi, %edi + + // If this is not a termination handler then skip it + cmpl $0, ST_FILTER(%edi) + jne .lu2_next_scope + + // Save the previous try-level in the exception registration structure + movl ST_TRYLEVEL(%edi), %eax + movl %eax, ER_TRYLEVEL(%ebx) + + // Fetch the address of the termination handler + movl ST_HANDLER(%edi), %eax + + // Termination handlers may trash all registers so save the + // important ones and then call the handler + pushl %edx + call *%eax + + // Get our base pointer back + popl %edx + + jmp .lu2_next_scope + +.lu2_done: + + // FIXME: Tear down the EXCEPTION_REGISTRATION entry setup to protect + // the unwinding + + //movl %esi, %esp + //popl %ebp + ret + +#define EH3_DISPCONTEXT 0x14 +#define EH3_CONTEXT 0x10 +#define EH3_REGFRAME 0x0C +#define EH3_ERECORD 0x08 + +// Parameters: +// [ESP+14h] - PVOID DispatcherContext +// [ESP+10h] - PCONTEXT Context +// [ESP+0Ch] - PEXCEPTION_REGISTRATION RegistrationFrame +// [ESP+08h] - PEXCEPTION_RECORD ExceptionRecord +// Registers: +// Unknown +// Returns: +// EXCEPTION_DISPOSITION - How this handler handled the exception +// Notes: +// Try to find an exception handler that will handle the exception. +// Traverse the entries in the scopetable that is associated with the +// exception registration passed as a parameter to this function. +// If an exception handler that will handle the exception is found, it +// is called and this function never returns +__except_handler3: + // Setup our call frame so we can access parameters using EBP + pushl %ebp // Standard ESP in frame (considered part of EXCEPTION_REGISTRATION) + movl %esp, %ebp + + // Don't trust the direction flag to be cleared + cld + + // Either we're called to handle an exception or we're called to unwind + movl EH3_ERECORD(%ebp), %eax + testl $EXCEPTION_UNWIND_MODE, EREC_FLAGS(%eax) + jnz .eh3_unwind + + // Keep a pointer to the exception registration in EBX + movl EH3_REGFRAME(%ebp), %ebx + + // Build an EXCEPTION_POINTERS structure on the stack and store it's + // address in the EXCEPTION_REGISTRATION structure + movl EH3_CONTEXT(%esp), %eax + pushl %ebx // Registration frame + pushl %eax // Context + movl %esp, ER_EPOINTERS(%ebx) // Pointer to EXCEPTION_REGISTRATION on the stack + + // Keep current try-level in EDI + movl ER_TRYLEVEL(%ebx), %edi + + // Keep a pointer to the scopetable in ESI + movl ER_SCOPETABLE(%ebx), %esi + +.eh3_next_scope: + + // If we have reached the end of the chain then exit + cmpl $-1, %edi + je .eh3_search + + // Compute the offset of the entry in the scopetable and store + // the absolute address in EAX + lea (%edi, %edi, 2), %eax + shll $2, %eax + addl %esi, %eax + + // Fetch the address of the filter routine + movl ST_FILTER(%eax), %eax + + // If this is a termination handler then skip it + cmpl $0, %eax + je .eh3_continue + + // Filter routines may trash all registers so save the important + // ones before restoring the call frame ebp and calling the handler + pushl %ebp + pushl %edi // Stop try-level + lea ER_EBP(%ebx), %ebp + call *%eax + popl %edi // Stop try-level + popl %ebp + + // Reload EBX with registration frame address + movl EH3_REGFRAME(%ebp), %ebx + + // Be more flexible here by checking if the return value is less than + // zero, equal to zero, or larger than zero instead of the defined + // values: + // -1 (EXCEPTION_CONTINUE_EXECUTION) + // 0 (EXCEPTION_CONTINUE_SEARCH) + // +1 (EXCEPTION_EXECUTE_HANDLER) + orl %eax, %eax + jz .eh3_continue + js .eh3_dismiss + + // Filter returned: EXCEPTION_EXECUTE_HANDLER + + // Ask the OS to perform global unwinding. + pushl %edi // Save stop try-level + pushl %ebx // Save registration frame address + pushl %ebx // Registration frame address + call __global_unwind2 + popl %eax // Remove parameter to __global_unwind2 + popl %ebx // Restore registration frame address + popl %edi // Restore stop try-level + + // Change the context structure so _except_finish is called in the + // correct context since we return ExceptionContinueExecution. + movl EH3_CONTEXT(%ebp), %eax + + movl %edi, CONTEXT_EDI(%eax) // Stop try-level + movl %ebx, CONTEXT_EBX(%eax) // Registration frame address + movl $_except_finish, CONTEXT_EIP(%eax) + + movl $ExceptionContinueExecution, %eax + jmp .eh3_return + + // Filter returned: EXCEPTION_CONTINUE_SEARCH +.eh3_continue: + + // Reload ESI because the filter routine may have trashed it + movl ER_SCOPETABLE(%ebx), %esi + + // Go one try-level closer to the top + lea (%edi, %edi, 2), %edi + shll $2, %edi + addl %esi, %edi + movl ST_TRYLEVEL(%edi), %edi + + jmp .eh3_next_scope + + // Filter returned: EXCEPTION_CONTINUE_EXECUTION + // Continue execution like nothing happened +.eh3_dismiss: + movl $ExceptionContinueExecution, %eax + jmp .eh3_return + + // Tell the OS to search for another handler that will handle the exception +.eh3_search: + + movl $ExceptionContinueSearch, %eax + jmp .eh3_return + + // Perform local unwinding +.eh3_unwind: + + movl $ExceptionContinueSearch, %eax + testl $EXCEPTION_TARGET_UNWIND, EREC_FLAGS(%eax) + jnz .eh3_return + + // Save some important registers + pushl %ebp + + lea ER_EBP(%ebx), %ebp + pushl $-1 + pushl %ebx + call __local_unwind2 + addl $8, %esp + + // Restore some important registers + popl %ebp + + movl $ExceptionContinueSearch, %eax + + // Get me out of here +.eh3_return: + + movl %ebp, %esp + popl %ebp + ret + +// Parameters: +// None +// Registers: +// EBX - Pointer to exception registration structure +// EDI - Stop try-level +// Returns: +// - +// Notes: +// - +_except_finish: + + // Setup EBP for the exception handler. By doing this the exception + // handler can access local variables as normal + lea ER_EBP(%ebx), %ebp + + // Save some important registers + pushl %ebp + pushl %ebx + pushl %edi + + // Stop try-level + pushl %edi + + // Pointer to exception registration structure + pushl %ebx + call __local_unwind2 + addl $8, %esp + + // Restore some important registers + popl %edi + popl %ebx + popl %ebp + + // Keep a pointer to the scopetable in ESI + movl ER_SCOPETABLE(%ebx), %esi + + // Compute the offset of the entry in the scopetable and store + // the absolute address in EDI + lea (%edi, %edi, 2), %edi + shll $2, %edi + addl %esi, %edi + + // Set the current try-level to the previous try-level and call + // the exception handler + movl ST_TRYLEVEL(%edi), %eax + movl %eax, ER_TRYLEVEL(%ebx) + movl ST_HANDLER(%edi), %eax + + call *%eax + + // We should never get here + ret