/*++ 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); } }