872 lines
32 KiB
C
872 lines
32 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
Copyright (c) 1993 Digital Equipment Corporation
|
||
|
||
Module Name:
|
||
|
||
unwindr.c
|
||
|
||
Abstract:
|
||
|
||
This module implements two alternate versions of the unwind function
|
||
required by Alpha AXP during the GEM compiler transition period. The
|
||
code is adapted from RtlUnwind (exdsptch.c). These functions can be
|
||
deleted if GEM uses scope table based structured exception handling
|
||
and can materialize virtual frame pointers.
|
||
|
||
Author:
|
||
|
||
Thomas Van Baak (tvb) 18-Nov-1992
|
||
|
||
Environment:
|
||
|
||
Any mode.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "ntrtlp.h"
|
||
|
||
//
|
||
// Define local macros.
|
||
//
|
||
// Raise noncontinuable exception with associated exception record.
|
||
//
|
||
|
||
#define RAISE_EXCEPTION(Status, ExceptionRecordt) { \
|
||
EXCEPTION_RECORD ExceptionRecordn; \
|
||
\
|
||
ExceptionRecordn.ExceptionCode = Status; \
|
||
ExceptionRecordn.ExceptionFlags = EXCEPTION_NONCONTINUABLE; \
|
||
ExceptionRecordn.ExceptionRecord = ExceptionRecordt; \
|
||
ExceptionRecordn.NumberParameters = 0; \
|
||
RtlRaiseException(&ExceptionRecordn); \
|
||
}
|
||
|
||
//
|
||
// The low 2 bits of ExceptionHandler are flags bits and not part of the
|
||
// exception handler address.
|
||
//
|
||
|
||
#define IS_HANDLER_DEFINED(FunctionEntry) \
|
||
(((ULONG)FunctionEntry->ExceptionHandler & ~0x3) != 0)
|
||
|
||
#if DBG
|
||
|
||
//
|
||
// Maintain a short history of PC's for malformed function table errors.
|
||
//
|
||
|
||
#define PC_HISTORY_DEPTH 4
|
||
|
||
//
|
||
// Definition of global flag to debug/validate exception handling.
|
||
// See ntrtlalp.h for the bit definitions in this flag word.
|
||
//
|
||
|
||
ULONG RtlDebugFlags;
|
||
|
||
#endif
|
||
|
||
#define Virtual VirtualFramePointer
|
||
#define Real RealFramePointer
|
||
|
||
VOID
|
||
RtlUnwindRfp (
|
||
IN PVOID TargetRealFrame 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 TargetRealFrame parameter is not specified, then the exit
|
||
unwind flag is also set in the exception flags of the exception record.
|
||
A backward scan through the procedure call frames is then performed to
|
||
find the target of the unwind operation.
|
||
|
||
As each frame is encountered, the PC where control left the corresponding
|
||
function is determined and used to lookup exception handler information
|
||
in the runtime function table built by the linker. If the respective
|
||
routine has an exception handler, then the handler is called.
|
||
|
||
This function is identical to RtlUnwind except that the TargetRealFrame
|
||
parameter is the real frame pointer instead of the virtual frame pointer.
|
||
|
||
Arguments:
|
||
|
||
TargetRealFrame - 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.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
CONTEXT ContextRecord1;
|
||
CONTEXT ContextRecord2;
|
||
ULONG ControlPc;
|
||
#if DBG
|
||
ULONG ControlPcHistory[PC_HISTORY_DEPTH];
|
||
ULONG ControlPcHistoryIndex = 0;
|
||
#endif
|
||
DISPATCHER_CONTEXT DispatcherContext;
|
||
EXCEPTION_DISPOSITION Disposition;
|
||
FRAME_POINTERS EstablisherFrame;
|
||
ULONG ExceptionFlags;
|
||
EXCEPTION_RECORD ExceptionRecord1;
|
||
#if DBG
|
||
LONG FrameDepth = 0;
|
||
#endif
|
||
PRUNTIME_FUNCTION FunctionEntry;
|
||
ULONG HighLimit;
|
||
BOOLEAN InFunction;
|
||
ULONG LastPc;
|
||
ULONG LowLimit;
|
||
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND) {
|
||
DbgPrint("\nRtlUnwindRfp(TargetRealFrame = %lx, TargetIp = %lx,, ReturnValue = %lx)\n",
|
||
TargetRealFrame, TargetIp, ReturnValue);
|
||
}
|
||
#endif
|
||
//
|
||
// Get current stack limits, capture the current context, virtually
|
||
// unwind to the caller of this routine, get the initial PC value, and
|
||
// set the unwind target address.
|
||
//
|
||
|
||
RtlpGetStackLimits(&LowLimit, &HighLimit);
|
||
RtlCaptureContext(&ContextRecord1);
|
||
ControlPc = (ULONG)ContextRecord1.IntRa;
|
||
FunctionEntry = RtlLookupFunctionEntry(ControlPc);
|
||
LastPc = RtlVirtualUnwind(ControlPc,
|
||
FunctionEntry,
|
||
&ContextRecord1,
|
||
&InFunction,
|
||
&EstablisherFrame,
|
||
NULL);
|
||
|
||
ControlPc = LastPc;
|
||
ContextRecord1.Fir = (ULONGLONG)(LONG)TargetIp;
|
||
|
||
//
|
||
// 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.ExceptionRecord = NULL;
|
||
ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc;
|
||
ExceptionRecord1.NumberParameters = 0;
|
||
}
|
||
|
||
//
|
||
// If the target frame of the unwind is specified, then a normal unwind
|
||
// is being performed. Otherwise, an exit unwind is being performed.
|
||
//
|
||
|
||
ExceptionFlags = EXCEPTION_UNWINDING;
|
||
if (ARGUMENT_PRESENT(TargetRealFrame) == FALSE) {
|
||
ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND;
|
||
}
|
||
|
||
//
|
||
// Scan backward through the call frame hierarchy and call exception
|
||
// handlers until the target frame of the unwind is reached.
|
||
//
|
||
|
||
do {
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
|
||
DbgPrint("RtlUnwindRfp: Loop: FrameDepth = %d, Rfp = %lx, sp = %lx, ControlPc = %lx\n",
|
||
FrameDepth, EstablisherFrame.Real, ContextRecord1.IntSp, ControlPc);
|
||
FrameDepth -= 1;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Lookup the function table entry using the point at which control
|
||
// left the procedure.
|
||
//
|
||
|
||
FunctionEntry = RtlLookupFunctionEntry(ControlPc);
|
||
|
||
//
|
||
// If there is a function table entry for the routine, then copy the
|
||
// context record, virtually unwind to the caller of the current
|
||
// routine to obtain the real frame pointer of the establisher and
|
||
// check if there is an exception handler for the frame.
|
||
//
|
||
|
||
if (FunctionEntry != NULL) {
|
||
RtlMoveMemory(&ContextRecord2, &ContextRecord1, sizeof(CONTEXT));
|
||
LastPc = RtlVirtualUnwind(ControlPc,
|
||
FunctionEntry,
|
||
&ContextRecord1,
|
||
&InFunction,
|
||
&EstablisherFrame,
|
||
NULL);
|
||
|
||
//
|
||
// If the real and virtual frame pointers are not within the
|
||
// specified stack limits, the frame pointers are unaligned, or
|
||
// the target frame is below the real frame and an exit unwind is
|
||
// not being performed, then raise the exception STATUS_BAD_STACK.
|
||
// Otherwise, check to determine if the current routine has an
|
||
// exception handler.
|
||
//
|
||
|
||
if ((EstablisherFrame.Real < LowLimit) ||
|
||
(EstablisherFrame.Virtual > HighLimit) ||
|
||
(EstablisherFrame.Real > EstablisherFrame.Virtual) ||
|
||
((ARGUMENT_PRESENT(TargetRealFrame) != FALSE) &&
|
||
((ULONG)TargetRealFrame < EstablisherFrame.Real)) ||
|
||
((EstablisherFrame.Virtual & 0xF) != 0) ||
|
||
((EstablisherFrame.Real & 0xF) != 0)) {
|
||
#if DBG
|
||
DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n");
|
||
DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n",
|
||
EstablisherFrame.Virtual, EstablisherFrame.Real);
|
||
DbgPrint(" TargetRealFrame = %08lx\n", TargetRealFrame);
|
||
if ((ARGUMENT_PRESENT(TargetRealFrame) != FALSE) &&
|
||
((ULONG)TargetRealFrame < EstablisherFrame.Real)) {
|
||
DbgPrint(" TargetRealFrame is below EstablisherFrame.Real!\n");
|
||
}
|
||
DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n",
|
||
(ULONG)ContextRecord2.IntSp);
|
||
DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
|
||
LowLimit, HighLimit);
|
||
DbgPrint(" LastPc = %08lx, ControlPc = %08lx\n",
|
||
LastPc, ControlPc);
|
||
DbgPrint(" Now raising STATUS_BAD_STACK exception.\n");
|
||
#endif
|
||
RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord);
|
||
|
||
} else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
|
||
DbgPrint("RtlUnwindRfp: ExceptionHandler = %lx, HandlerData = %lx\n",
|
||
FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// The frame has an exception handler.
|
||
//
|
||
// The control PC, establisher frame pointer, the address
|
||
// of the function table entry, and the address of the
|
||
// context record are all stored in the dispatcher context.
|
||
// This information is used by the unwind linkage routine
|
||
// and can be used by the exception handler itself.
|
||
//
|
||
// A linkage routine written in assembler is used to actually
|
||
// call the actual exception handler. This is required by the
|
||
// exception handler that is associated with the linkage
|
||
// routine so it can have access to two sets of dispatcher
|
||
// context when it is called.
|
||
//
|
||
|
||
DispatcherContext.ControlPc = ControlPc;
|
||
DispatcherContext.FunctionEntry = FunctionEntry;
|
||
DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
|
||
DispatcherContext.ContextRecord = &ContextRecord2;
|
||
|
||
//
|
||
// Call the exception handler.
|
||
//
|
||
|
||
do {
|
||
|
||
//
|
||
// If the establisher frame is the target of the unwind
|
||
// operation, then set the target unwind flag.
|
||
//
|
||
|
||
if ((ULONG)TargetRealFrame == EstablisherFrame.Real) {
|
||
ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
|
||
}
|
||
|
||
ExceptionRecord->ExceptionFlags = ExceptionFlags;
|
||
|
||
//
|
||
// Set the specified return value in case the exception
|
||
// handler directly continues execution.
|
||
//
|
||
|
||
ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue;
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
|
||
DbgPrint("RtlUnwindRfp: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc);
|
||
}
|
||
#endif
|
||
Disposition =
|
||
RtlpExecuteHandlerForUnwind(ExceptionRecord,
|
||
EstablisherFrame.Virtual,
|
||
&ContextRecord2,
|
||
&DispatcherContext,
|
||
FunctionEntry->ExceptionHandler);
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
|
||
DbgPrint("RtlUnwindRfp: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition);
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Clear target unwind and collided unwind flags.
|
||
//
|
||
|
||
ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND |
|
||
EXCEPTION_TARGET_UNWIND);
|
||
|
||
//
|
||
// Case on the handler disposition.
|
||
//
|
||
|
||
switch (Disposition) {
|
||
|
||
//
|
||
// The disposition is to continue the search.
|
||
//
|
||
// Continue the search for a handler or continue
|
||
// execution.
|
||
//
|
||
|
||
case ExceptionContinueSearch :
|
||
break;
|
||
|
||
//
|
||
// The disposition is collided unwind.
|
||
//
|
||
// Set the target of the current unwind to the context
|
||
// record of the previous unwind, virtually unwind to
|
||
// the caller of the old routine, and reexecute the
|
||
// exception handler from the collided frame with the
|
||
// collided unwind flag set in the exception record.
|
||
//
|
||
|
||
case ExceptionCollidedUnwind :
|
||
ControlPc = DispatcherContext.ControlPc;
|
||
FunctionEntry = DispatcherContext.FunctionEntry;
|
||
RtlMoveMemory(&ContextRecord1,
|
||
DispatcherContext.ContextRecord,
|
||
sizeof(CONTEXT));
|
||
|
||
ContextRecord1.Fir = (ULONGLONG)(LONG)TargetIp;
|
||
RtlMoveMemory(&ContextRecord2,
|
||
&ContextRecord1,
|
||
sizeof(CONTEXT));
|
||
|
||
ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
|
||
LastPc = RtlVirtualUnwind(ControlPc,
|
||
FunctionEntry,
|
||
&ContextRecord1,
|
||
&InFunction,
|
||
&EstablisherFrame,
|
||
NULL);
|
||
break;
|
||
|
||
//
|
||
// All other disposition values are invalid.
|
||
//
|
||
// Raise invalid disposition exception.
|
||
//
|
||
|
||
default :
|
||
RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
|
||
}
|
||
|
||
} while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Set point at which control left the previous routine.
|
||
//
|
||
|
||
LastPc = (ULONG)ContextRecord1.IntRa - 4;
|
||
|
||
//
|
||
// If the next control PC is the same as the old control PC, then
|
||
// the function table is not correctly formed.
|
||
//
|
||
|
||
if (LastPc == ControlPc) {
|
||
#if DBG
|
||
ULONG Count;
|
||
DbgPrint("\n****** Warning - malformed function table (unwind).\n");
|
||
DbgPrint("ControlPc = %08lx, %08lx", LastPc, ControlPc);
|
||
for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
|
||
if (ControlPcHistoryIndex > 0) {
|
||
ControlPcHistoryIndex -= 1;
|
||
ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
|
||
DbgPrint(", %08lx", ControlPc);
|
||
}
|
||
}
|
||
DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
|
||
DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n");
|
||
#endif
|
||
RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set point at which control left the previous routine.
|
||
//
|
||
|
||
#if DBG
|
||
ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
|
||
ControlPcHistoryIndex += 1;
|
||
#endif
|
||
ControlPc = LastPc;
|
||
|
||
} while ((EstablisherFrame.Real < HighLimit) &&
|
||
(EstablisherFrame.Real != (ULONG)TargetRealFrame));
|
||
|
||
//
|
||
// If the establisher stack pointer is equal to the target frame
|
||
// pointer, then continue execution. Otherwise, an exit unwind was
|
||
// performed or the target of the unwind did not exist and the
|
||
// debugger and subsystem are given a second chance to handle the
|
||
// unwind.
|
||
//
|
||
|
||
if (EstablisherFrame.Real == (ULONG)TargetRealFrame) {
|
||
ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue;
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND) {
|
||
DbgPrint("RtlUnwindRfp: finished unwinding, and calling RtlpRestoreContext\n");
|
||
}
|
||
#endif
|
||
RtlpRestoreContext(&ContextRecord2);
|
||
|
||
} else {
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND) {
|
||
DbgPrint("RtlUnwindRfp: finished unwinding, but calling ZwRaiseException\n");
|
||
}
|
||
#endif
|
||
ZwRaiseException(ExceptionRecord, &ContextRecord1, FALSE);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
RtlUnwindReturn (
|
||
IN PVOID TargetFrame,
|
||
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. A backward scan through the procedure call frames is then
|
||
performed to find the target of the unwind operation. When the target
|
||
frame is reached, a return is made to the caller of the target frame
|
||
with the return value specified by the return value parameter.
|
||
|
||
As each frame is encountered, the PC where control left the corresponding
|
||
function is determined and used to lookup exception handler information
|
||
in the runtime function table built by the linker. If the respective
|
||
routine has an exception handler, then the handler is called.
|
||
|
||
This function is identical to RtlUnwind except control resumes in the
|
||
caller of the target frame, not in the target frame itself.
|
||
|
||
Arguments:
|
||
|
||
TargetFrame - Supplies an optional pointer to the call frame that is the
|
||
target of the unwind.
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
CONTEXT ContextRecord1;
|
||
CONTEXT ContextRecord2;
|
||
ULONG ControlPc;
|
||
#if DBG
|
||
ULONG ControlPcHistory[PC_HISTORY_DEPTH];
|
||
ULONG ControlPcHistoryIndex = 0;
|
||
#endif
|
||
DISPATCHER_CONTEXT DispatcherContext;
|
||
EXCEPTION_DISPOSITION Disposition;
|
||
FRAME_POINTERS EstablisherFrame;
|
||
ULONG ExceptionFlags;
|
||
EXCEPTION_RECORD ExceptionRecord1;
|
||
#if DBG
|
||
LONG FrameDepth = 0;
|
||
#endif
|
||
PRUNTIME_FUNCTION FunctionEntry;
|
||
ULONG HighLimit;
|
||
BOOLEAN InFunction;
|
||
ULONG LastPc;
|
||
ULONG LowLimit;
|
||
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND) {
|
||
DbgPrint("\nRtlUnwindReturn(TargetFrame = %lx,, ReturnValue = %lx)\n",
|
||
TargetFrame, ReturnValue);
|
||
}
|
||
#endif
|
||
//
|
||
// Get current stack limits, capture the current context, virtually
|
||
// unwind to the caller of this routine, get the initial PC value, and
|
||
// set the unwind target address.
|
||
//
|
||
|
||
RtlpGetStackLimits(&LowLimit, &HighLimit);
|
||
RtlCaptureContext(&ContextRecord1);
|
||
ControlPc = (ULONG)ContextRecord1.IntRa;
|
||
FunctionEntry = RtlLookupFunctionEntry(ControlPc);
|
||
LastPc = RtlVirtualUnwind(ControlPc,
|
||
FunctionEntry,
|
||
&ContextRecord1,
|
||
&InFunction,
|
||
&EstablisherFrame,
|
||
NULL);
|
||
|
||
ControlPc = LastPc;
|
||
ContextRecord1.Fir = 0;
|
||
|
||
//
|
||
// 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.ExceptionRecord = NULL;
|
||
ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc;
|
||
ExceptionRecord1.NumberParameters = 0;
|
||
}
|
||
|
||
//
|
||
// A target frame of the unwind is specified so a normal unwind is
|
||
// being performed.
|
||
//
|
||
|
||
ExceptionFlags = EXCEPTION_UNWINDING;
|
||
|
||
//
|
||
// Scan backward through the call frame hierarchy and call exception
|
||
// handlers until the target frame of the unwind is reached.
|
||
//
|
||
|
||
do {
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
|
||
DbgPrint("RtlUnwindReturn: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n",
|
||
FrameDepth, ContextRecord1.IntSp, ControlPc);
|
||
FrameDepth -= 1;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Lookup the function table entry using the point at which control
|
||
// left the procedure.
|
||
//
|
||
|
||
FunctionEntry = RtlLookupFunctionEntry(ControlPc);
|
||
|
||
//
|
||
// If there is a function table entry for the routine, then copy the
|
||
// context record, virtually unwind to the caller of the current
|
||
// routine to obtain the virtual frame pointer of the establisher and
|
||
// check if there is an exception handler for the frame.
|
||
//
|
||
|
||
if (FunctionEntry != NULL) {
|
||
RtlMoveMemory(&ContextRecord2, &ContextRecord1, sizeof(CONTEXT));
|
||
LastPc = RtlVirtualUnwind(ControlPc,
|
||
FunctionEntry,
|
||
&ContextRecord1,
|
||
&InFunction,
|
||
&EstablisherFrame,
|
||
NULL);
|
||
|
||
//
|
||
// If the virtual frame pointer is not within the specified stack
|
||
// limits, the virtual frame pointer is unaligned, or the target
|
||
// frame is below the virtual frame and an exit unwind is not being
|
||
// performed, then raise the exception STATUS_BAD_STACK. Otherwise,
|
||
// check to determine if the current routine has an exception
|
||
// handler.
|
||
//
|
||
|
||
if ((EstablisherFrame.Virtual < LowLimit) ||
|
||
(EstablisherFrame.Virtual > HighLimit) ||
|
||
((ULONG)TargetFrame < EstablisherFrame.Virtual) ||
|
||
((EstablisherFrame.Virtual & 0xF) != 0)) {
|
||
#if DBG
|
||
DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n");
|
||
DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n",
|
||
EstablisherFrame.Virtual, EstablisherFrame.Real);
|
||
DbgPrint(" TargetFrame = %08lx\n", TargetFrame);
|
||
if ((ARGUMENT_PRESENT(TargetFrame) != FALSE) &&
|
||
((ULONG)TargetFrame < EstablisherFrame.Virtual)) {
|
||
DbgPrint(" TargetFrame is below EstablisherFrame!\n");
|
||
}
|
||
DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n",
|
||
(ULONG)ContextRecord2.IntSp);
|
||
DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
|
||
LowLimit, HighLimit);
|
||
DbgPrint(" LastPc = %08lx, ControlPc = %08lx\n",
|
||
LastPc, ControlPc);
|
||
DbgPrint(" Now raising STATUS_BAD_STACK exception.\n");
|
||
#endif
|
||
RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord);
|
||
|
||
} else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
|
||
DbgPrint("RtlUnwindReturn: ExceptionHandler = %lx, HandlerData = %lx\n",
|
||
FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// The frame has an exception handler.
|
||
//
|
||
// The control PC, establisher frame pointer, the address
|
||
// of the function table entry, and the address of the
|
||
// context record are all stored in the dispatcher context.
|
||
// This information is used by the unwind linkage routine
|
||
// and can be used by the exception handler itself.
|
||
//
|
||
// A linkage routine written in assembler is used to actually
|
||
// call the actual exception handler. This is required by the
|
||
// exception handler that is associated with the linkage
|
||
// routine so it can have access to two sets of dispatcher
|
||
// context when it is called.
|
||
//
|
||
|
||
DispatcherContext.ControlPc = ControlPc;
|
||
DispatcherContext.FunctionEntry = FunctionEntry;
|
||
DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
|
||
DispatcherContext.ContextRecord = &ContextRecord2;
|
||
|
||
//
|
||
// Call the exception handler.
|
||
//
|
||
|
||
do {
|
||
|
||
//
|
||
// If the establisher frame is the target of the unwind
|
||
// operation, then set the target unwind flag.
|
||
//
|
||
|
||
if ((ULONG)TargetFrame == EstablisherFrame.Virtual) {
|
||
ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
|
||
}
|
||
|
||
ExceptionRecord->ExceptionFlags = ExceptionFlags;
|
||
|
||
//
|
||
// Set the specified return value in case the exception
|
||
// handler directly continues execution.
|
||
//
|
||
|
||
ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue;
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
|
||
DbgPrint("RtlUnwindReturn: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc);
|
||
}
|
||
#endif
|
||
Disposition =
|
||
RtlpExecuteHandlerForUnwind(ExceptionRecord,
|
||
EstablisherFrame.Virtual,
|
||
&ContextRecord2,
|
||
&DispatcherContext,
|
||
FunctionEntry->ExceptionHandler);
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
|
||
DbgPrint("RtlUnwindReturn: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition);
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Clear target unwind and collided unwind flags.
|
||
//
|
||
|
||
ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND |
|
||
EXCEPTION_TARGET_UNWIND);
|
||
|
||
//
|
||
// Case on the handler disposition.
|
||
//
|
||
|
||
switch (Disposition) {
|
||
|
||
//
|
||
// The disposition is to continue the search.
|
||
//
|
||
// Continue the search for a handler or continue
|
||
// execution.
|
||
//
|
||
|
||
case ExceptionContinueSearch :
|
||
break;
|
||
|
||
//
|
||
// The disposition is collided unwind.
|
||
//
|
||
// Set the target of the current unwind to the context
|
||
// record of the previous unwind, virtually unwind to
|
||
// the caller of the old routine, and reexecute the
|
||
// exception handler from the collided frame with the
|
||
// collided unwind flag set in the exception record.
|
||
//
|
||
|
||
case ExceptionCollidedUnwind :
|
||
ControlPc = DispatcherContext.ControlPc;
|
||
FunctionEntry = DispatcherContext.FunctionEntry;
|
||
RtlMoveMemory(&ContextRecord1,
|
||
DispatcherContext.ContextRecord,
|
||
sizeof(CONTEXT));
|
||
|
||
ContextRecord1.Fir = 0;
|
||
RtlMoveMemory(&ContextRecord2,
|
||
&ContextRecord1,
|
||
sizeof(CONTEXT));
|
||
|
||
ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
|
||
LastPc = RtlVirtualUnwind(ControlPc,
|
||
FunctionEntry,
|
||
&ContextRecord1,
|
||
&InFunction,
|
||
&EstablisherFrame,
|
||
NULL);
|
||
break;
|
||
|
||
//
|
||
// All other disposition values are invalid.
|
||
//
|
||
// Raise invalid disposition exception.
|
||
//
|
||
|
||
default :
|
||
RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
|
||
}
|
||
|
||
} while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Set point at which control left the previous routine.
|
||
//
|
||
|
||
LastPc = (ULONG)ContextRecord1.IntRa - 4;
|
||
|
||
//
|
||
// If the next control PC is the same as the old control PC, then
|
||
// the function table is not correctly formed.
|
||
//
|
||
|
||
if (LastPc == ControlPc) {
|
||
#if DBG
|
||
ULONG Count;
|
||
DbgPrint("\n****** Warning - malformed function table (unwind).\n");
|
||
DbgPrint("ControlPc = %08lx, %08lx", LastPc, ControlPc);
|
||
for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
|
||
if (ControlPcHistoryIndex > 0) {
|
||
ControlPcHistoryIndex -= 1;
|
||
ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
|
||
DbgPrint(", %08lx", ControlPc);
|
||
}
|
||
}
|
||
DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
|
||
DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n");
|
||
#endif
|
||
RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set point at which control left the previous routine.
|
||
//
|
||
|
||
#if DBG
|
||
ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
|
||
ControlPcHistoryIndex += 1;
|
||
#endif
|
||
ControlPc = LastPc;
|
||
|
||
} while ((EstablisherFrame.Virtual < HighLimit) &&
|
||
(EstablisherFrame.Virtual != (ULONG)TargetFrame));
|
||
|
||
//
|
||
// If the establisher stack pointer is equal to the target frame pointer,
|
||
// then continue execution at the point where the call to the target frame
|
||
// was made. Otherwise the target of the unwind did not exist and the
|
||
// debugger and subsystem are given a second chance to handle the unwind.
|
||
//
|
||
|
||
if ((ULONG)ContextRecord1.IntSp == (ULONG)TargetFrame) {
|
||
ContextRecord1.IntV0 = (ULONGLONG)(LONG)ReturnValue;
|
||
|
||
//
|
||
// Set the continuation address to the address after the point where
|
||
// control left the previous frame and entered the target frame.
|
||
//
|
||
|
||
ContextRecord1.Fir = (ULONGLONG)(LONG)LastPc + 4;
|
||
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND) {
|
||
DbgPrint("RtlUnwindReturn: finished unwinding, and calling RtlpRestoreContext\n");
|
||
}
|
||
#endif
|
||
RtlpRestoreContext(&ContextRecord1);
|
||
|
||
} else {
|
||
#if DBG
|
||
if (RtlDebugFlags & RTL_DBG_UNWIND) {
|
||
DbgPrint("RtlUnwindReturn: finished unwinding, but calling ZwRaiseException\n");
|
||
}
|
||
#endif
|
||
ZwRaiseException(ExceptionRecord, &ContextRecord1, FALSE);
|
||
}
|
||
}
|