mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
436 lines
14 KiB
436 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1993 Digital Equipment Corporation
|
|
|
|
Module Name:
|
|
|
|
ghandler.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the C specific exception handler that provides
|
|
structured exception handling for code generated by the GEM compiler.
|
|
|
|
Author:
|
|
|
|
John Parks (parks) 12-Jan-1993
|
|
Thomas Van Baak (tvb) 28-Jan-1993
|
|
|
|
Environment:
|
|
|
|
Any mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "nt.h"
|
|
|
|
//
|
|
// Define procedure prototypes for exception filter and termination handler
|
|
// execution routines defined in jmpunwnd.s.
|
|
//
|
|
|
|
LONG
|
|
__C_ExecuteExceptionFilter (
|
|
PEXCEPTION_POINTERS ExceptionPointers,
|
|
EXCEPTION_FILTER ExceptionFilter,
|
|
ULONG EstablisherFrame
|
|
);
|
|
|
|
ULONG
|
|
__C_ExecuteTerminationHandler (
|
|
BOOLEAN AbnormalTermination,
|
|
TERMINATION_HANDLER TerminationHandler,
|
|
ULONG EstablisherFrame
|
|
);
|
|
|
|
//
|
|
// Define local procedure prototypes.
|
|
//
|
|
|
|
EXCEPTION_DISPOSITION
|
|
_OtsCSpecificHandler (
|
|
IN PEXCEPTION_RECORD ExceptionRecord,
|
|
IN PVOID EstablisherFrame,
|
|
IN OUT PCONTEXT ContextRecord,
|
|
IN OUT PDISPATCHER_CONTEXT DispatcherContext
|
|
);
|
|
|
|
ULONG
|
|
_OtsLocalFinallyUnwind (
|
|
IN PSEH_CONTEXT SehContext,
|
|
IN PSEH_BLOCK TargetSeb,
|
|
IN PVOID RealFramePointer
|
|
);
|
|
|
|
//
|
|
// Define local macros.
|
|
//
|
|
|
|
#define IS_EXCEPT(Seb) ((Seb)->JumpTarget != 0)
|
|
#define IS_FINALLY(Seb) ((Seb)->JumpTarget == 0)
|
|
|
|
//
|
|
// Initialize an exception record for the unwind with the SEB of the target
|
|
// included as one information parameter. This is done so that the target
|
|
// frame of the unwind may execute all the finally handlers necessary given
|
|
// the SEB pointer at the unwind target.
|
|
//
|
|
|
|
#define MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord, Seb) { \
|
|
ExceptionRecord->ExceptionCode = STATUS_UNWIND; \
|
|
ExceptionRecord->ExceptionFlags = EXCEPTION_UNWINDING; \
|
|
ExceptionRecord->ExceptionRecord = NULL; \
|
|
ExceptionRecord->ExceptionAddress = 0; \
|
|
ExceptionRecord->NumberParameters = 1; \
|
|
ExceptionRecord->ExceptionInformation[0] = (ULONG)(Seb); \
|
|
}
|
|
|
|
EXCEPTION_DISPOSITION
|
|
_OtsCSpecificHandler (
|
|
IN PEXCEPTION_RECORD ExceptionRecord,
|
|
IN PVOID EstablisherFrame,
|
|
IN OUT PCONTEXT ContextRecord,
|
|
IN OUT PDISPATCHER_CONTEXT DispatcherContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function walks up the list of SEB's associated with the specified
|
|
procedure and calls except filters and finally handlers as necessary.
|
|
|
|
It is called in two different contexts:
|
|
(i) by the exception dispatcher after an exception is raised
|
|
(ii) by the unwinder during an unwind operation
|
|
|
|
In the first case, is searches the SEB list for except filters to evaluate.
|
|
In the second case, it searches for finally handlers to execute.
|
|
|
|
Arguments:
|
|
|
|
ExceptionRecord - Supplies a pointer to an exception record.
|
|
|
|
EstablisherFrame - Supplies a (virtual frame) pointer to the frame of the
|
|
establisher function.
|
|
|
|
ContextRecord - Supplies a pointer to a context record.
|
|
|
|
DispatcherContext - Supplies a pointer to the exception dispatcher or
|
|
unwind dispatcher context.
|
|
|
|
Return Value:
|
|
|
|
If the exception is handled by one of the exception filter routines, then
|
|
there is no return from this routine and RtlUnwind is called. Otherwise,
|
|
an exception disposition value of continue execution or continue search is
|
|
returned.
|
|
|
|
Notes:
|
|
In context (i) there are 3 possibilities:
|
|
|
|
(a) If an exception filter returns a value greater that 0 (meaning
|
|
that the associated handler should be invoked) there is no
|
|
return from this function. RtlUnwind is called to unwind the
|
|
stack to the exception handler corresponding to that filter.
|
|
|
|
(b) If an exception filter returns a value less than 0 (meaning
|
|
that the exception should be dismissed), this routine returns
|
|
value ExceptionContinueExecution.
|
|
|
|
(c) If every filter returns value 0 (meaning that the search for a
|
|
handler should continue elsewhere), this function returns
|
|
ExceptionContinueSearch.
|
|
|
|
In context (ii) there are 2 possibilities:
|
|
|
|
(d) If no branches are detected out of finally handlers, this
|
|
function returns ExceptionContinueSearch.
|
|
|
|
(e) If a branch is detected out of a finally handler, there is no
|
|
return from this routine. RtlUnwind is called to unwind to the
|
|
branch target (and cancel the current unwind).
|
|
|
|
There may be long jumps out of both except filters and finally handlers
|
|
in which case this routine will be peeled off the stack without returning.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG ContinuationAddress;
|
|
EXCEPTION_FILTER ExceptionFilter;
|
|
PVOID ExceptionHandler;
|
|
EXCEPTION_POINTERS ExceptionPointers;
|
|
LONG FilterValue;
|
|
ULONG RealFramePointer;
|
|
PSEH_BLOCK Seb;
|
|
PSEH_CONTEXT SehContext;
|
|
PSEH_BLOCK TargetSeb;
|
|
TERMINATION_HANDLER TerminationHandler;
|
|
|
|
//
|
|
// Get the address of the SEH context which is at some negative offset
|
|
// from the virtual frame pointer. For GEM, the handler data field of
|
|
// the function entry contains that offset. The current SEB pointer and
|
|
// the RFP (static link) are obtained from the SEH context.
|
|
//
|
|
|
|
SehContext = (PSEH_CONTEXT)((ULONG)EstablisherFrame +
|
|
(LONG)DispatcherContext->FunctionEntry->HandlerData);
|
|
RealFramePointer = SehContext->RealFramePointer;
|
|
|
|
//
|
|
// If this is a dispatching context, walk up the list of SEBs evaluating
|
|
// except filters.
|
|
//
|
|
|
|
if (IS_DISPATCHING(ExceptionRecord->ExceptionFlags)) {
|
|
|
|
//
|
|
// Set up the ExceptionPointers structure that is used by except
|
|
// filters to obtain data for the GetExceptionInformation intrinsic
|
|
// function. Copy the current SEB pointer into a local variable
|
|
// because the real SEB pointer is only modified in unwind contexts.
|
|
//
|
|
|
|
ExceptionPointers.ExceptionRecord = ExceptionRecord;
|
|
ExceptionPointers.ContextRecord = ContextRecord;
|
|
|
|
for (Seb = SehContext->CurrentSeb; Seb != NULL; Seb = Seb->ParentSeb) {
|
|
if (IS_EXCEPT(Seb)) {
|
|
|
|
//
|
|
// This is an except filter. Get the addresses of the filter
|
|
// and exception handler from the SEB, then call the except
|
|
// filter.
|
|
//
|
|
|
|
ExceptionFilter = (EXCEPTION_FILTER)Seb->HandlerAddress;
|
|
ExceptionHandler = (PVOID)Seb->JumpTarget;
|
|
|
|
FilterValue = __C_ExecuteExceptionFilter(&ExceptionPointers,
|
|
ExceptionFilter,
|
|
RealFramePointer);
|
|
|
|
//
|
|
// If the except filter < 0, dismiss the exception. If > 0,
|
|
// store the exception code on the stack for the except
|
|
// handler, modify the given ExceptionRecord so that finally
|
|
// handlers will be called properly during the unwind, then
|
|
// unwind down to the except handler. If = 0, resume the
|
|
// search for except filters.
|
|
//
|
|
|
|
if (FilterValue < 0) {
|
|
return ExceptionContinueExecution;
|
|
|
|
} else if (FilterValue > 0) {
|
|
SehContext->ExceptionCode = ExceptionRecord->ExceptionCode;
|
|
MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord,
|
|
Seb->ParentSeb);
|
|
RtlUnwind2(EstablisherFrame,
|
|
ExceptionHandler,
|
|
ExceptionRecord,
|
|
0,
|
|
ContextRecord);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (!IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags)) {
|
|
|
|
//
|
|
// This is an unwind but is not the target frame. Since the function
|
|
// is being terminated, finally handlers for all try bodies that are
|
|
// presently in scope must be executed. Walk up the SEB list all the
|
|
// way to the top executing finally handlers. This corresponds to
|
|
// exiting all try bodies that are presently in scope.
|
|
//
|
|
|
|
while (SehContext->CurrentSeb != NULL) {
|
|
|
|
//
|
|
// Get the address of the SEB and then update the SEH context.
|
|
//
|
|
|
|
Seb = SehContext->CurrentSeb;
|
|
SehContext->CurrentSeb = Seb->ParentSeb;
|
|
|
|
if (IS_FINALLY(Seb)) {
|
|
|
|
//
|
|
// This is a finally handler. Get the address of the handler
|
|
// from the SEB and call the finally handler.
|
|
//
|
|
|
|
TerminationHandler = (TERMINATION_HANDLER)Seb->HandlerAddress;
|
|
ContinuationAddress =
|
|
__C_ExecuteTerminationHandler(TRUE,
|
|
TerminationHandler,
|
|
RealFramePointer);
|
|
|
|
//
|
|
// If the finally handler returns a non-zero result, there
|
|
// was a branch out of the handler (to that address) and this
|
|
// routine should unwind to that target.
|
|
//
|
|
|
|
if (ContinuationAddress != 0) {
|
|
MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord,
|
|
SehContext->CurrentSeb);
|
|
RtlUnwind(EstablisherFrame,
|
|
(PVOID)ContinuationAddress,
|
|
ExceptionRecord,
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is the target frame of an unwind. Since the target may be
|
|
// in a different try scope than the one defined by the current SEB
|
|
// pointer, finally handlers between the two scopes must execute.
|
|
// Walk up the SEB list from the current SEB to the target SEB and
|
|
// execute all finally handlers encountered.
|
|
//
|
|
|
|
TargetSeb = (PSEH_BLOCK)ExceptionRecord->ExceptionInformation[0];
|
|
ContinuationAddress = _OtsLocalFinallyUnwind(SehContext,
|
|
TargetSeb,
|
|
(PVOID)RealFramePointer);
|
|
if (ContinuationAddress != 0) {
|
|
|
|
//
|
|
// A non-zero result indicates there was a branch out of a
|
|
// finally handler that was being executed during the unwind.
|
|
// This routine should unwind to that address.
|
|
//
|
|
|
|
MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord,
|
|
SehContext->CurrentSeb);
|
|
RtlUnwind(EstablisherFrame,
|
|
(PVOID)ContinuationAddress,
|
|
ExceptionRecord,
|
|
0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Continue search for exception or termination handlers.
|
|
//
|
|
|
|
return ExceptionContinueSearch;
|
|
}
|
|
|
|
ULONG
|
|
_OtsLocalFinallyUnwind (
|
|
IN PSEH_CONTEXT SehContext,
|
|
IN PSEH_BLOCK TargetSeb,
|
|
IN PVOID RealFramePointer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function walks up the SEB tree of the current procedure from the
|
|
current SEB to the target SEB and executes all the finally handlers it
|
|
encounters.
|
|
|
|
Calls to this function are inserted into user code by the compiler when
|
|
there are branches out of guarded regions that may require finally
|
|
handlers to execute.
|
|
|
|
This function is also called from _OtsCSpecificHandler when the target
|
|
frame is reached during an unwind operation. There may be finally handlers
|
|
that should execute before resuming execution at the unwind target.
|
|
|
|
Arguments:
|
|
|
|
SehContext - Supplies the address of the SEH context structure which is
|
|
located in the stack frame.
|
|
|
|
TargetSeb - Supplies the address of the SEB corresponding to the branch
|
|
target address.
|
|
|
|
RealFramePointer - Supplies the (real frame) pointer of the establisher
|
|
frame, which is the current stack frame. This is used to set up the
|
|
static link if a finally handler is invoked.
|
|
|
|
Return Value:
|
|
|
|
If a branch out of a finally handler is detected, the function value is
|
|
the address of the branch target. Otherwise, the function value is zero.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG ContinuationAddress;
|
|
BOOLEAN Nested;
|
|
PSEH_BLOCK Seb;
|
|
TERMINATION_HANDLER TerminationHandler;
|
|
|
|
//
|
|
// If the SEB pointers are the same, no finally handlers need to execute.
|
|
// The branch is to a target location in the same guarded scope.
|
|
//
|
|
|
|
if (SehContext->CurrentSeb == TargetSeb) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// If the current SEB scope is not nested within the target SEB scope, no
|
|
// finally handlers need to execute. Reset the current SEB pointer to the
|
|
// target SEB pointer and return.
|
|
//
|
|
|
|
Nested = FALSE;
|
|
Seb = SehContext->CurrentSeb;
|
|
|
|
while (Seb != NULL) {
|
|
Seb = Seb->ParentSeb;
|
|
if (Seb == TargetSeb) {
|
|
Nested = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (Nested == FALSE) {
|
|
SehContext->CurrentSeb = TargetSeb;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Walk up the list of SEB blocks executing finally handlers. If a branch
|
|
// out of a finally is encountered along the way, return the target
|
|
// address, otherwise return 0.
|
|
//
|
|
|
|
while (SehContext->CurrentSeb != TargetSeb) {
|
|
|
|
//
|
|
// Get the address of the SEB and then update the SEH context.
|
|
//
|
|
|
|
Seb = SehContext->CurrentSeb;
|
|
SehContext->CurrentSeb = Seb->ParentSeb;
|
|
|
|
if (IS_FINALLY(Seb)) {
|
|
TerminationHandler = (TERMINATION_HANDLER)Seb->HandlerAddress;
|
|
ContinuationAddress =
|
|
__C_ExecuteTerminationHandler(TRUE,
|
|
TerminationHandler,
|
|
(ULONG)RealFramePointer);
|
|
if (ContinuationAddress != 0) {
|
|
return ContinuationAddress;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|