diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/rtl/i386/exdsptch.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to '')
-rw-r--r-- | private/ntos/rtl/i386/exdsptch.c | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/private/ntos/rtl/i386/exdsptch.c b/private/ntos/rtl/i386/exdsptch.c new file mode 100644 index 000000000..2efec6aca --- /dev/null +++ b/private/ntos/rtl/i386/exdsptch.c @@ -0,0 +1,535 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + exdsptch.c + +Abstract: + + This module implements the dispatching of exception and the unwinding of + procedure call frames. + +Author: + + David N. Cutler (davec) 13-Aug-1989 + +Environment: + + Any mode. + +Revision History: + + 10 april 90 bryanwi + + Port to the 386. + +--*/ + +#include "ntrtlp.h" + + +// +// Dispatcher context structure definition. +// + +typedef struct _DISPATCHER_CONTEXT { + PEXCEPTION_REGISTRATION_RECORD RegistrationPointer; + } DISPATCHER_CONTEXT; + +// +// Execute handler for exception function prototype. +// + +EXCEPTION_DISPOSITION +RtlpExecuteHandlerForException ( + IN PEXCEPTION_RECORD ExceptionRecord, + IN PVOID EstablisherFrame, + IN OUT PCONTEXT ContextRecord, + IN OUT PVOID DispatcherContext, + IN PEXCEPTION_ROUTINE ExceptionRoutine + ); + +// +// Execute handler for unwind function prototype. +// + +EXCEPTION_DISPOSITION +RtlpExecuteHandlerForUnwind ( + IN PEXCEPTION_RECORD ExceptionRecord, + IN PVOID EstablisherFrame, + IN OUT PCONTEXT ContextRecord, + IN OUT PVOID DispatcherContext, + IN PEXCEPTION_ROUTINE ExceptionRoutine + ); + + + +BOOLEAN +RtlDispatchException ( + IN PEXCEPTION_RECORD ExceptionRecord, + IN PCONTEXT ContextRecord + ) + +/*++ + +Routine Description: + + This function attempts to dispatch an exception to a call frame based + handler by searching backwards through the stack based call frames. The + search begins with the frame specified in the context record and continues + backward until either a handler is found that handles the exception, the + stack is found to be invalid (i.e., out of limits or unaligned), or the end + of the call hierarchy is reached. + +Arguments: + + ExceptionRecord - Supplies a pointer to an exception record. + + ContextRecord - Supplies a pointer to a context record. + +Return Value: + + If the exception is handled by one of the frame based handlers, then + a value of TRUE is returned. Otherwise a value of FALSE is returned. + +--*/ + +{ + + DISPATCHER_CONTEXT DispatcherContext; + EXCEPTION_DISPOSITION Disposition; + PEXCEPTION_REGISTRATION_RECORD RegistrationPointer; + PEXCEPTION_REGISTRATION_RECORD NestedRegistration; + ULONG HighAddress; + ULONG HighLimit; + ULONG LowLimit; + EXCEPTION_RECORD ExceptionRecord1; + + // + // Get current stack limits. + // + + RtlpGetStackLimits(&LowLimit, &HighLimit); + + // + // Start with the frame specified by the context record and search + // backwards through the call frame hierarchy attempting to find an + // exception handler that will handler the exception. + // + + RegistrationPointer = RtlpGetRegistrationHead(); + NestedRegistration = 0; + while (RegistrationPointer != EXCEPTION_CHAIN_END) { + + // + // If the call frame is not within the specified stack limits or the + // call frame is unaligned, then set the stack invalid flag in the + // exception record and return FALSE. Else check to determine if the + // frame has an exception handler. + // + + HighAddress = (ULONG)RegistrationPointer + + sizeof(EXCEPTION_REGISTRATION_RECORD); + + if ( ((ULONG)RegistrationPointer < LowLimit) || + (HighAddress > HighLimit) || + (((ULONG)RegistrationPointer & 0x3) != 0) ) { + ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID; + return FALSE; + } else { + +#if !defined(WX86_i386) + + ULONG Index; + // + // The handler must be executed by calling another routine + // that is written in assembler. This is required because + // up level addressing of the handler information is required + // when a nested exception is encountered. + // + + if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { + Index = RtlpLogExceptionHandler( + ExceptionRecord, + ContextRecord, + 0, + (PULONG)RegistrationPointer, + 4 * sizeof(ULONG)); + // can't use sizeof(EXCEPTION_REGISTRATION_RECORD + // because we need the 2 dwords above it. + } +#endif + + Disposition = RtlpExecuteHandlerForException( + ExceptionRecord, + (PVOID)RegistrationPointer, + ContextRecord, + (PVOID)&DispatcherContext, + (PEXCEPTION_ROUTINE)RegistrationPointer->Handler); + +#if !defined(WX86_i386) + if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { + RtlpLogLastExceptionDisposition(Index, Disposition); + } +#endif + + + // + // If the current scan is within a nested context and the frame + // just examined is the end of the context region, then clear + // the nested context frame and the nested exception in the + // exception flags. + // + + if (NestedRegistration == RegistrationPointer) { + ExceptionRecord->ExceptionFlags &= (~EXCEPTION_NESTED_CALL); + NestedRegistration = 0; + } + + // + // Case on the handler disposition. + // + + switch (Disposition) { + + // + // The disposition is to continue execution. If the + // exception is not continuable, then raise the exception + // STATUS_NONCONTINUABLE_EXCEPTION. Otherwise return + // TRUE. + // + + case ExceptionContinueExecution : + if ((ExceptionRecord->ExceptionFlags & + EXCEPTION_NONCONTINUABLE) != 0) { + ExceptionRecord1.ExceptionCode = + STATUS_NONCONTINUABLE_EXCEPTION; + ExceptionRecord1.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + ExceptionRecord1.ExceptionRecord = ExceptionRecord; + ExceptionRecord1.NumberParameters = 0; + RtlRaiseException(&ExceptionRecord1); + } else { + return TRUE; + } + + // + // The disposition is to continue the search. Get next + // frame address and continue the search. + // + + case ExceptionContinueSearch : + break; + + // + // The disposition is nested exception. Set the nested + // context frame to the establisher frame address and set + // nested exception in the exception flags. + // + + case ExceptionNestedException : + ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL; + if (DispatcherContext.RegistrationPointer > NestedRegistration) { + NestedRegistration = DispatcherContext.RegistrationPointer; + } + break; + + // + // All other disposition values are invalid. Raise + // invalid disposition exception. + // + + default : + ExceptionRecord1.ExceptionCode = STATUS_INVALID_DISPOSITION; + ExceptionRecord1.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + ExceptionRecord1.ExceptionRecord = ExceptionRecord; + ExceptionRecord1.NumberParameters = 0; + RtlRaiseException(&ExceptionRecord1); + break; + } + + // + // If chain goes in wrong direction or loops, report an + // invalid exception stack, otherwise go on to the next one. + // + + RegistrationPointer = RegistrationPointer->Next; + } + } + return FALSE; +} + +VOID +RtlUnwind ( + IN PVOID TargetFrame OPTIONAL, + IN PVOID TargetIp OPTIONAL, + IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, + IN PVOID ReturnValue + ) + +/*++ + +Routine Description: + + This function initiates an unwind of procedure call frames. The machine + state at the time of the call to unwind is captured in a context record + and the unwinding flag is set in the exception flags of the exception + record. If the TargetFrame parameter is not specified, then the exit unwind + flag is also set in the exception flags of the exception record. A backward + walk through the procedure call frames is then performed to find the target + of the unwind operation. + + N.B. The captured context passed to unwinding handlers will not be + a completely accurate context set for the 386. This is because + there isn't a standard stack frame in which registers are stored. + + Only the integer registers are affected. The segement and + control registers (ebp, esp) will have correct values for + the flat 32 bit environment. + + N.B. If you change the number of arguments, make sure you change the + adjustment of ESP after the call to RtlpCaptureContext (for + STDCALL calling convention) + +Arguments: + + TargetFrame - Supplies an optional pointer to the call frame that is the + target of the unwind. If this parameter is not specified, then an exit + unwind is performed. + + TargetIp - Supplies an optional instruction address that specifies the + continuation address of the unwind. This address is ignored if the + target frame parameter is not specified. + + ExceptionRecord - Supplies an optional pointer to an exception record. + + ReturnValue - Supplies a value that is to be placed in the integer + function return register just before continuing execution. + +Return Value: + + None. + +--*/ + +{ + PCONTEXT ContextRecord; + CONTEXT ContextRecord1; + DISPATCHER_CONTEXT DispatcherContext; + EXCEPTION_DISPOSITION Disposition; + PEXCEPTION_REGISTRATION_RECORD RegistrationPointer; + PEXCEPTION_REGISTRATION_RECORD PriorPointer; + ULONG HighAddress; + ULONG HighLimit; + ULONG LowLimit; + EXCEPTION_RECORD ExceptionRecord1; + EXCEPTION_RECORD ExceptionRecord2; + + // + // Get current stack limits. + // + + RtlpGetStackLimits(&LowLimit, &HighLimit); + + // + // If an exception record is not specified, then build a local exception + // record for use in calling exception handlers during the unwind operation. + // + + if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) { + ExceptionRecord = &ExceptionRecord1; + ExceptionRecord1.ExceptionCode = STATUS_UNWIND; + ExceptionRecord1.ExceptionFlags = 0; + ExceptionRecord1.ExceptionRecord = NULL; + ExceptionRecord1.ExceptionAddress = RtlpGetReturnAddress(); + ExceptionRecord1.NumberParameters = 0; + } + + // + // If the target frame of the unwind is specified, then set EXCEPTION_UNWINDING + // flag in the exception flags. Otherwise set both EXCEPTION_EXIT_UNWIND and + // EXCEPTION_UNWINDING flags in the exception flags. + // + + if (ARGUMENT_PRESENT(TargetFrame) == TRUE) { + ExceptionRecord->ExceptionFlags |= EXCEPTION_UNWINDING; + } else { + ExceptionRecord->ExceptionFlags |= (EXCEPTION_UNWINDING | + EXCEPTION_EXIT_UNWIND); + } + + // + // Capture the context. + // + + ContextRecord = &ContextRecord1; + ContextRecord1.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS; + RtlpCaptureContext(ContextRecord); + +#ifdef STD_CALL + // + // Adjust captured context to pop our arguments off the stack + // + ContextRecord->Esp += sizeof(TargetFrame) + + sizeof(TargetIp) + + sizeof(ExceptionRecord) + + sizeof(ReturnValue); +#endif + ContextRecord->Eax = (ULONG)ReturnValue; + + // + // Scan backward through the call frame hierarchy, calling exception + // handlers as they are encountered, until the target frame of the unwind + // is reached. + // + + RegistrationPointer = RtlpGetRegistrationHead(); + while (RegistrationPointer != EXCEPTION_CHAIN_END) { + + // + // If this is the target of the unwind, then continue execution + // by calling the continue system service. + // + + if ((ULONG)RegistrationPointer == (ULONG)TargetFrame) { + ZwContinue(ContextRecord, FALSE); + + // + // If the target frame is lower in the stack than the current frame, + // then raise STATUS_INVALID_UNWIND exception. + // + + } else if ( (ARGUMENT_PRESENT(TargetFrame) == TRUE) && + ((ULONG)TargetFrame < (ULONG)RegistrationPointer) ) { + ExceptionRecord2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET; + ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + ExceptionRecord2.ExceptionRecord = ExceptionRecord; + ExceptionRecord2.NumberParameters = 0; + RtlRaiseException(&ExceptionRecord2); + } + + // + // If the call frame is not within the specified stack limits or the + // call frame is unaligned, then raise the exception STATUS_BAD_STACK. + // Else restore the state from the specified frame to the context + // record. + // + + HighAddress = (ULONG)RegistrationPointer + + sizeof(EXCEPTION_REGISTRATION_RECORD); + + if ( ((ULONG)RegistrationPointer < LowLimit) || + (HighAddress > HighLimit) || + (((ULONG)RegistrationPointer & 0x3) != 0) ) { + ExceptionRecord2.ExceptionCode = STATUS_BAD_STACK; + ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + ExceptionRecord2.ExceptionRecord = ExceptionRecord; + ExceptionRecord2.NumberParameters = 0; + RtlRaiseException(&ExceptionRecord2); + } else { + + // + // The handler must be executed by calling another routine + // that is written in assembler. This is required because + // up level addressing of the handler information is required + // when a collided unwind is encountered. + // + + Disposition = RtlpExecuteHandlerForUnwind( + ExceptionRecord, + (PVOID)RegistrationPointer, + ContextRecord, + (PVOID)&DispatcherContext, + RegistrationPointer->Handler); + + // + // Case on the handler disposition. + // + + switch (Disposition) { + + // + // The disposition is to continue the search. Get next + // frame address and continue the search. + // + + case ExceptionContinueSearch : + break; + + // + // The disposition is colided unwind. Maximize the target + // of the unwind and change the context record pointer. + // + + case ExceptionCollidedUnwind : + + // + // Pick up the registration pointer that was active at + // the time of the unwind, and simply continue. + // + + RegistrationPointer = DispatcherContext.RegistrationPointer; + break; + + + // + // All other disposition values are invalid. Raise + // invalid disposition exception. + // + + default : + ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION; + ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + ExceptionRecord2.ExceptionRecord = ExceptionRecord; + ExceptionRecord2.NumberParameters = 0; + RtlRaiseException(&ExceptionRecord2); + break; + } + + // + // Step to next registration record + // + + PriorPointer = RegistrationPointer; + RegistrationPointer = RegistrationPointer->Next; + + // + // Unlink the unwind handler, since it's been called. + // + + RtlpUnlinkHandler(PriorPointer); + + // + // If chain goes in wrong direction or loops, raise an + // exception. + // + + } + } + + if (TargetFrame == EXCEPTION_CHAIN_END) { + + // + // Caller simply wants to unwind all exception records. + // This differs from an exit_unwind in that no "exit" is desired. + // Do a normal continue, since we've effectively found the + // "target" the caller wanted. + // + + ZwContinue(ContextRecord, FALSE); + + } else { + + // + // Either (1) a real exit unwind was performed, or (2) the + // specified TargetFrame is not present in the exception handler + // list. In either case, give debugger and subsystem a chance + // to see the unwind. + // + + ZwRaiseException(ExceptionRecord, ContextRecord, FALSE); + + } + return; +} |