mirror of
https://github.com/reactos/reactos.git
synced 2025-04-19 12:08:55 +00:00

- Fix/add prototypes for RtlCaptureContext, RtlDispatchException and RtlUnwind - Fix EXCEPTION_REGISTRATION_RECORD structure and PEXCEPTION_ROUTINE - Add w32api excpt.h (based on mingw) with PSDK compatibility fixes - Fix seriously broken User-Mode Ldr thunk and APC Callback prototypes - Fix KiUserExceptionDispatcher - Remove useless NTDLL entrypoint - Implement NTDLL Ki* callbacks in ASM - Implement RtlCaptureContext - Fix RtlRaiseException to handle cases when a user-mode debugger is present - Fix RtlRaiseStatus as above, plus set the exception address and capture context - Little cleanup of RTL headers - Implement RtlpGetStackLimits, RtlpGetExceptionList, RtlpSetExceptionList, RtlpGetExceptionAddress in ASM - Fix RtlDispatchException, add cases for exceptions in the DPC stack and validate the validity of the exception frames. Add support for exception logging by the global flag. Use TRAP_FRAME/EXCPETION_FRAME instead of Context. - Fix RtlUnwind logic, support cases where it's called with custom arguments instead of NULL. - Reimplement RtlpCaptureContext to work properly, convert exception handler calling functions to INTEL syntax and fix some bugs (like checking for the right unwind flag, clearing volatile register values, etc. Also use some optimizations to increase speed. - Modify some kernel functions (like KeContextToTrapFrame, KiDispatchException, KiInitializeUserApc, etc.) to support a PKEXCEPTION_FRAME for future PPC compatibility. - Reimplement RtlCaptureUnicodeString/FreeUnicodeString as inlined probe macros and optimize them. - Export ExRaiseStatus/Exception as Rtl* - Reimplement NtContinue to have more platform-independent code, and to protect and validate user-mode context and parameters with SEH. - Implement KiRaiseException, add SEH to all user-mode parameters and when copying data to the user-mode stack. - Fix KiInitializeUserApc to use KeTrapFrameToContext, to save the debug registers, not to deliver APCs during v86 mode, and to protect user-mode stack operations in SEH and probing. Also make it generate the proper stack for the user-mode callback. - Implement KiUnexpectedInterrupt and KiCoprocessorError - Reimplement NtRaiseException in ASM to take advantage of optimizations due to the trap frame being in the stack when called through System call interface. - Fix Ntcontinue to respect AlertThread paramter - Fix some functiosn to return with KiServiceExit2 instead of KiServiceExit when required/needed - Fix KiDispatchException's logic, fix hacks when calling KeUserExceptionDispatcher, use correct context flags,... - Make NTDLL Ki* callbacks have SEH to protect them and return to kernel-mode with notification of any exceptions (the kernel-mode code to handle this isn't written yet though) svn path=/trunk/; revision=17811
353 lines
12 KiB
C
353 lines
12 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Run-Time Library
|
|
* PURPOSE: User-mode exception support for IA-32
|
|
* FILE: lib/rtl/i386/exception.c
|
|
* PROGRAMERS: Alex Ionescu (alex@relsoft.net)
|
|
* Casper S. Hornstrup (chorns@users.sourceforge.net)
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <rtl.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* PRIVATE FUNCTIONS *********************************************************/
|
|
|
|
VOID
|
|
STDCALL
|
|
RtlpGetStackLimits(PULONG_PTR StackBase,
|
|
PULONG_PTR StackLimit);
|
|
|
|
PEXCEPTION_REGISTRATION_RECORD
|
|
STDCALL
|
|
RtlpGetExceptionList(VOID);
|
|
|
|
VOID
|
|
STDCALL
|
|
RtlpSetExceptionList(PEXCEPTION_REGISTRATION_RECORD NewExceptionList);
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
VOID
|
|
STDCALL
|
|
RtlGetCallersAddress(OUT PVOID *CallersAddress,
|
|
OUT PVOID *CallersCaller)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
STDCALL
|
|
RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
|
|
IN PCONTEXT Context)
|
|
{
|
|
PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, NestedFrame = NULL;
|
|
PEXCEPTION_REGISTRATION_RECORD DispatcherContext;
|
|
EXCEPTION_RECORD ExceptionRecord2;
|
|
EXCEPTION_DISPOSITION ReturnValue;
|
|
ULONG_PTR StackLow, StackHigh;
|
|
ULONG_PTR RegistrationFrameEnd;
|
|
DPRINT1("RtlDispatchException(): %p, %p \n", ExceptionRecord, Context);
|
|
|
|
/* Get the current stack limits and registration frame */
|
|
RtlpGetStackLimits(&StackLow, &StackHigh);
|
|
RegistrationFrame = RtlpGetExceptionList();
|
|
DPRINT1("RegistrationFrame is 0x%X\n", RegistrationFrame);
|
|
|
|
/* Now loop every frame */
|
|
while (RegistrationFrame != EXCEPTION_CHAIN_END)
|
|
{
|
|
/* Find out where it ends */
|
|
RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
|
|
sizeof(*RegistrationFrame);
|
|
|
|
/* Make sure the registration frame is located within the stack */
|
|
if ((RegistrationFrameEnd > StackHigh) ||
|
|
((ULONG_PTR)RegistrationFrame < StackLow) ||
|
|
((ULONG_PTR)RegistrationFrame & 0x3))
|
|
{
|
|
/* Check if this happened in the DPC Stack */
|
|
if (RtlpHandleDpcStackException(RegistrationFrame,
|
|
RegistrationFrameEnd,
|
|
&StackLow,
|
|
&StackHigh))
|
|
{
|
|
/* Use DPC Stack Limits and restart */
|
|
continue;
|
|
}
|
|
|
|
/* Set invalid stack and return false */
|
|
ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
|
|
DPRINT1("Invalid exception frame\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check if logging is enabled */
|
|
DPRINT1("Checking for logging\n");
|
|
RtlpCheckLogException(ExceptionRecord,
|
|
Context,
|
|
RegistrationFrame,
|
|
sizeof(*RegistrationFrame));
|
|
|
|
/* Call the handler */
|
|
DPRINT1("Executing handler: %p\n", RegistrationFrame->Handler);
|
|
ReturnValue = RtlpExecuteHandlerForException(ExceptionRecord,
|
|
RegistrationFrame,
|
|
Context,
|
|
&DispatcherContext,
|
|
RegistrationFrame->Handler);
|
|
DPRINT1("Handler returned: %lx\n", ReturnValue);
|
|
|
|
/* Check if this is a nested frame */
|
|
if (RegistrationFrame == NestedFrame)
|
|
{
|
|
/* Mask out the flag and the nested frame */
|
|
ExceptionRecord->ExceptionFlags &= ~EXCEPTION_NESTED_CALL;
|
|
NestedFrame = NULL;
|
|
}
|
|
|
|
/* Handle the dispositions */
|
|
if (ReturnValue == ExceptionContinueExecution)
|
|
{
|
|
/* Check if it was non-continuable */
|
|
if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
|
|
{
|
|
/* Set up the exception record */
|
|
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
|
|
ExceptionRecord2.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
|
|
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
|
ExceptionRecord2.NumberParameters = 0;
|
|
|
|
/* Raise the exception */
|
|
DPRINT1("Non-continuable\n");
|
|
RtlRaiseException(&ExceptionRecord2);
|
|
}
|
|
else
|
|
{
|
|
/* Return to caller */
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (ReturnValue == ExceptionNestedException)
|
|
{
|
|
/* Turn the nested flag on */
|
|
ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
|
|
|
|
/* Update the current nested frame */
|
|
if (NestedFrame < DispatcherContext) NestedFrame = DispatcherContext;
|
|
}
|
|
else if (ReturnValue == ExceptionContinueSearch)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
/* Set up the exception record */
|
|
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
|
|
ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
|
|
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
|
ExceptionRecord2.NumberParameters = 0;
|
|
|
|
/* Raise the exception */
|
|
RtlRaiseException(&ExceptionRecord2);
|
|
}
|
|
|
|
/* Go to the next frame */
|
|
RegistrationFrame = RegistrationFrame->Next;
|
|
}
|
|
|
|
/* Unhandled, return false */
|
|
DPRINT1("FALSE:(\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
STDCALL
|
|
RtlUnwind(PVOID RegistrationFrame OPTIONAL,
|
|
PVOID ReturnAddress OPTIONAL,
|
|
PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
|
|
PVOID EaxValue)
|
|
{
|
|
PEXCEPTION_REGISTRATION_RECORD RegistrationFrame2, OldFrame;
|
|
PEXCEPTION_REGISTRATION_RECORD DispatcherContext;
|
|
EXCEPTION_RECORD ExceptionRecord2, ExceptionRecord3;
|
|
EXCEPTION_DISPOSITION ReturnValue;
|
|
ULONG_PTR StackLow, StackHigh;
|
|
ULONG_PTR RegistrationFrameEnd;
|
|
CONTEXT LocalContext;
|
|
PCONTEXT Context;
|
|
DPRINT1("RtlUnwind(). RegistrationFrame 0x%X\n", RegistrationFrame);
|
|
|
|
/* Get the current stack limits */
|
|
RtlpGetStackLimits(&StackLow, &StackHigh);
|
|
|
|
/* Check if we don't have an exception record */
|
|
if (!ExceptionRecord)
|
|
{
|
|
/* Overwrite the argument */
|
|
ExceptionRecord = &ExceptionRecord3;
|
|
|
|
/* Setup a local one */
|
|
ExceptionRecord3.ExceptionFlags = 0;
|
|
ExceptionRecord3.ExceptionCode = STATUS_UNWIND;
|
|
ExceptionRecord3.ExceptionRecord = NULL;
|
|
ExceptionRecord3.ExceptionAddress = RtlpGetExceptionAddress();
|
|
ExceptionRecord3.NumberParameters = 0;
|
|
}
|
|
|
|
/* Check if we have a frame */
|
|
if (RegistrationFrame)
|
|
{
|
|
/* Set it as unwinding */
|
|
ExceptionRecord->ExceptionFlags |= EXCEPTION_UNWINDING;
|
|
}
|
|
else
|
|
{
|
|
/* Set the Exit Unwind flag as well */
|
|
ExceptionRecord->ExceptionFlags |= (EXCEPTION_UNWINDING |
|
|
EXCEPTION_EXIT_UNWIND);
|
|
}
|
|
|
|
/* Now capture the context */
|
|
Context = &LocalContext;
|
|
LocalContext.ContextFlags = CONTEXT_INTEGER |
|
|
CONTEXT_CONTROL |
|
|
CONTEXT_SEGMENTS;
|
|
RtlpCaptureContext(Context);
|
|
|
|
/* Pop the current arguments off */
|
|
LocalContext.Esp += sizeof(RegistrationFrame) +
|
|
sizeof(ReturnAddress) +
|
|
sizeof(ExceptionRecord) +
|
|
sizeof(ReturnValue);
|
|
|
|
/* Set the new value for EAX */
|
|
LocalContext.Eax = (ULONG)EaxValue;
|
|
|
|
/* Get the current frame */
|
|
RegistrationFrame2 = RtlpGetExceptionList();
|
|
|
|
/* Now loop every frame */
|
|
while (RegistrationFrame2 != EXCEPTION_CHAIN_END)
|
|
{
|
|
DPRINT1("RegistrationFrame is 0x%X\n", RegistrationFrame2);
|
|
|
|
/* If this is the target */
|
|
if (RegistrationFrame2 == RegistrationFrame)
|
|
{
|
|
/* Continue execution */
|
|
ZwContinue(Context, FALSE);
|
|
}
|
|
|
|
/* Check if the frame is too low */
|
|
if ((RegistrationFrame) && ((ULONG_PTR)RegistrationFrame <
|
|
(ULONG_PTR)RegistrationFrame2))
|
|
{
|
|
/* Create an invalid unwind exception */
|
|
ExceptionRecord2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
|
|
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
|
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
|
|
ExceptionRecord2.NumberParameters = 0;
|
|
|
|
/* Raise the exception */
|
|
DPRINT1("Frame is invalid\n");
|
|
RtlRaiseException(&ExceptionRecord2);
|
|
}
|
|
|
|
/* Find out where it ends */
|
|
RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame2 +
|
|
sizeof(*RegistrationFrame2);
|
|
|
|
/* Make sure the registration frame is located within the stack */
|
|
if ((RegistrationFrameEnd > StackHigh) ||
|
|
((ULONG_PTR)RegistrationFrame < StackLow) ||
|
|
((ULONG_PTR)RegistrationFrame & 0x3))
|
|
{
|
|
/* Check if this happened in the DPC Stack */
|
|
if (RtlpHandleDpcStackException(RegistrationFrame,
|
|
RegistrationFrameEnd,
|
|
&StackLow,
|
|
&StackHigh))
|
|
{
|
|
/* Use DPC Stack Limits and restart */
|
|
continue;
|
|
}
|
|
|
|
/* Create an invalid stack exception */
|
|
ExceptionRecord2.ExceptionCode = STATUS_BAD_STACK;
|
|
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
|
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
|
|
ExceptionRecord2.NumberParameters = 0;
|
|
|
|
/* Raise the exception */
|
|
DPRINT1("Frame has bad stack\n");
|
|
RtlRaiseException(&ExceptionRecord2);
|
|
}
|
|
else
|
|
{
|
|
/* Call the handler */
|
|
DPRINT1("Executing unwind handler: %p\n", RegistrationFrame2->Handler);
|
|
ReturnValue = RtlpExecuteHandlerForUnwind(ExceptionRecord,
|
|
RegistrationFrame2,
|
|
Context,
|
|
&DispatcherContext,
|
|
RegistrationFrame2->Handler);
|
|
DPRINT1("Handler returned: %lx\n", ReturnValue);
|
|
|
|
/* Handle the dispositions */
|
|
if (ReturnValue == ExceptionContinueSearch)
|
|
{
|
|
/* Get out of here */
|
|
break;
|
|
}
|
|
else if (ReturnValue == ExceptionCollidedUnwind)
|
|
{
|
|
/* Get the previous frame */
|
|
RegistrationFrame2 = DispatcherContext;
|
|
}
|
|
else
|
|
{
|
|
/* Set up the exception record */
|
|
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
|
|
ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
|
|
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
|
ExceptionRecord2.NumberParameters = 0;
|
|
|
|
/* Raise the exception */
|
|
RtlRaiseException(&ExceptionRecord2);
|
|
}
|
|
|
|
/* Go to the next frame */
|
|
OldFrame = RegistrationFrame2;
|
|
RegistrationFrame2 = RegistrationFrame2->Next;
|
|
|
|
/* Remove this handler */
|
|
RtlpSetExceptionList(OldFrame);
|
|
}
|
|
}
|
|
|
|
/* Check if we reached the end */
|
|
if (RegistrationFrame == EXCEPTION_CHAIN_END)
|
|
{
|
|
/* Unwind completed, so we don't exit */
|
|
ZwContinue(Context, FALSE);
|
|
}
|
|
else
|
|
{
|
|
/* This is an exit_unwind or the frame wasn't present in the list */
|
|
ZwRaiseException(ExceptionRecord, Context, FALSE);
|
|
}
|
|
}
|
|
|
|
/* EOF */
|