📄 exdsptch.c
字号:
(OldEntry->PrologEndAddress >= OldEntry->EndAddress)) {
OldEntry = (PRUNTIME_FUNCTION)OldEntry->PrologEndAddress;
}
return OldEntry;
}
}
// If a new function table is located, then search the function table
// for a function table entry for the specified PC.
if (NewTable != NULL) {
// Initialize search indicies.
Low = 0;
// Perform binary search on the function table for a function table
// entry that subsumes the specified PC.
while (NewHigh >= Low) {
// Compute next probe index and test entry. If the specified PC
// is greater than of equal to the beginning address and less
// than the ending address of the function table entry, then
// return the address of the function table entry. Otherwise,
// continue the search.
Middle = (Low + NewHigh) >> 1;
NewEntry = &NewTable[Middle];
InstrShift = 1 + NewEntry->ThirtyTwoBits;
EndAddress = NewEntry->pFuncStart + (NewEntry->FuncLen << InstrShift);
if (ControlPc < NewEntry->pFuncStart) {
NewHigh = Middle - 1;
} else if (ControlPc >= EndAddress) {
Low = Middle + 1;
} else {
prf->BeginAddress = NewEntry->pFuncStart;
prf->EndAddress = EndAddress;
prf->PrologEndAddress = NewEntry->pFuncStart + (NewEntry->PrologLen << InstrShift);
if (NewEntry->ExceptionFlag) {
NewEH = (PPDATA_EH)NewEntry->pFuncStart - 1;
prf->ExceptionHandler = NewEH->pHandler;
prf->HandlerData = NewEH->pHandlerData;
} else {
prf->ExceptionHandler = 0;
prf->HandlerData = 0;
}
return prf;
}
}
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
}
// A function table entry for the specified PC was not found.
return NULL;
}
#endif // COMBINED_PDATA
BOOLEAN
NKUnwind(
IN PTHREAD pth,
IN PCALLSTACK TargetCStk OPTIONAL,
IN ULONG TargetFrame OPTIONAL,
IN ULONG TargetIp OPTIONAL,
IN PEXCEPTION_RECORD ExceptionRecord,
IN ULONG ReturnValue,
IN PCONTEXT ContextRecord)
/*++
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
scan through the procedure call frames is then performed to find the target
of the unwind operation.
As each frame is encounter, 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.
Arguments:
pth - Supplies a pointer to thread structure
TargetCStk - Supplies an optional pointer to a PSL callstack to unwind to.
This parameter is used instead of TargetFrame.
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.
ContextRecord - Supplies a pointer to a context record that can be used
to store context druing the unwind operation.
Return Value:
None.
--*/
{
ULONG ControlPc;
DISPATCHER_CONTEXT DispatcherContext;
PCONTEXT OriginalContext;
EXCEPTION_DISPOSITION Disposition;
ULONG EstablisherFrame;
ULONG ExceptionFlags;
PRUNTIME_FUNCTION FunctionEntry;
BOOLEAN InFunction;
ULONG HighLimit;
ULONG LowLimit;
ULONG NextPc;
PCALLSTACK pcstk;
RUNTIME_FUNCTION TempFunctionEntry;
// 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.
NKGetStackLimits(pth, &LowLimit, &HighLimit);
pcstk = pth->pcstkTop;
ControlPc = CONTEXT_TO_PROGRAM_COUNTER(ContextRecord);
CONTEXT_TO_PROGRAM_COUNTER(ContextRecord) = (ULONG)TargetIp;
EstablisherFrame = ContextRecord->IntSp;
OriginalContext = ContextRecord;
// 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 (!TargetFrame && !TargetCStk)
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 {
DEBUGMSG(ZONE_SEH, (TEXT("NKUnwind(%08X): Proc=%8.8lx ControlPc=%8.8lx SP=%8.8lx\r\n"),
ContextRecord,
pCurProc, ControlPc, ContextRecord->IntSp));
if (ControlPc == SYSCALL_RETURN || ControlPc+INST_SIZE == SYSCALL_RETURN
|| ControlPc == DIRECT_RETURN || ControlPc+INST_SIZE == DIRECT_RETURN) {
// Server IPC return. If doing a PSL unwind, and this is the
// target process, then terminate to unwind loop.
PPROCESS pProc = pcstk->pprcLast;
if (pcstk == TargetCStk) {
DEBUGMSG(ZONE_SEH, (TEXT("NKUnwind: TargetCStk reached.\r\n")));
break;
}
// Update the return address and process context
// information from the thread's call stack list.
ContextRecord->IntRa = (ULONG)pcstk->retAddr;
RESTORE_EXTRA_INFO(ContextRecord, pcstk->extra);
if ((ulong)pProc < 0x10000) {
/* This call is returning from kernel mode server. Restore
* the thread's previous operating mode and return.
*/
SetContextMode(ContextRecord, (ULONG)pProc);
} else {
/* Returning from a user mode server, restore the previous
* address space information and return.
*/
UpdateASID(pth, pProc, pcstk->akyLast ? pcstk->akyLast : pth->aky);
}
ControlPc = ContextRecord->IntRa - INST_SIZE;
// Unlink the most recent CALLSTACK object
pth->pcstkTop = pcstk->pcstkNext;
if (IsValidKPtr(pcstk))
FreeMem(pcstk,HEAP_CALLSTACK);
pcstk = pth->pcstkTop;
DEBUGMSG(ZONE_SEH, (TEXT("NKUnwind: PSL ret=%8.8lx\r\n"),
ContextRecord->IntRa));
}
// Lookup the function table entry using the point at which control
// left the procedure.
ControlPc = ZeroPtr(ControlPc);
FunctionEntry = NKLookupFunctionEntry(pCurProc, ControlPc, &TempFunctionEntry);
// If there is a function table entry for the routine, then virtually
// unwind to the caller of the routine to obtain the virtual frame
// pointer of the establisher, but don't update the context record.
if (FunctionEntry != NULL) {
NextPc = RtlpVirtualUnwind(ControlPc,
FunctionEntry,
ContextRecord,
&InFunction,
&EstablisherFrame);
// 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 < LowLimit) || (EstablisherFrame > HighLimit) ||
(TargetFrame && (TargetFrame < EstablisherFrame)) ||
((EstablisherFrame & STK_ALIGN) != 0)) {
RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord);
} else if ((FunctionEntry->ExceptionHandler != NULL) && InFunction) {
// 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;
DispatcherContext.ContextRecord = ContextRecord;
// 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)
ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
ExceptionRecord->ExceptionFlags = ExceptionFlags;
// Set the specified return value in case the exception
// handler directly continues execution.
ContextRecord->RetValue = (ULONG)ReturnValue;
DEBUGMSG(ZONE_SEH, (TEXT("Calling unwind handler @%8.8lx Frame=%8.8lx\r\n"),
FunctionEntry->ExceptionHandler, EstablisherFrame));
Disposition = RtlpExecuteHandlerForUnwind(ExceptionRecord,
EstablisherFrame,
ContextRecord,
&DispatcherContext,
FunctionEntry->ExceptionHandler);
DEBUGMSG(1, (TEXT(" disposition = %d\r\n"), Disposition));
// 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.
//
// If the target frame has not been reached, then
// virtually unwind to the caller of the current
// routine, update the context record, and continue
// the search for a handler.
case ExceptionContinueSearch :
if (EstablisherFrame != (ULONG)TargetFrame) {
NextPc = RtlVirtualUnwind(ControlPc,
FunctionEntry,
ContextRecord,
&InFunction,
&EstablisherFrame);
}
break;
// The disposition is collided unwind.
//
// Set the target of the current unwind to the context
// record of the previous unwind, 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;
ContextRecord = DispatcherContext.ContextRecord;
CONTEXT_TO_PROGRAM_COUNTER(ContextRecord) = (ULONG)TargetIp;
ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
EstablisherFrame = DispatcherContext.EstablisherFrame;
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 {
// If the target frame has not been reached, then virtually unwind to the
// caller of the current routine and update the context record.
if (EstablisherFrame != (ULONG)TargetFrame) {
NextPc = RtlVirtualUnwind(ControlPc,
FunctionEntry,
ContextRecord,
&InFunction,
&EstablisherFrame);
}
}
} else {
// Set point at which control left the previous routine.
NextPc = ContextRecord->IntRa - INST_SIZE;
// If the next control PC is the same as the old control PC, then
// the function table is not correctly formed.
if (NextPc == ControlPc)
break; ///RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -