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.
582 lines
16 KiB
582 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
getcalr.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the routine RtlGetCallerAddress. It will
|
|
return the address of the caller, and the callers caller to the
|
|
specified procedure.
|
|
|
|
Author:
|
|
|
|
William K. Cheung (wcheung) 17-Jan-1996
|
|
|
|
based on version by Larry Osterman (larryo) 18-Mar-1991
|
|
|
|
Revision History:
|
|
|
|
18-Feb-2001 (silviuc) : added RtlCaptureStackBackTrace.
|
|
|
|
--*/
|
|
#include "ntrtlp.h"
|
|
|
|
//
|
|
// Undefine get callers address since it is defined as a macro.
|
|
//
|
|
|
|
#undef RtlGetCallersAddress
|
|
|
|
ULONG
|
|
RtlpWalkFrameChainExceptionFilter (
|
|
ULONG ExceptionCode,
|
|
PVOID ExceptionRecord
|
|
);
|
|
|
|
|
|
PRUNTIME_FUNCTION
|
|
RtlpLookupFunctionEntryForStackWalks (
|
|
IN ULONGLONG ControlPc,
|
|
OUT PULONGLONG ImageBase,
|
|
OUT PULONGLONG TargetGp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function searches the currently active function tables for an
|
|
entry that corresponds to the specified PC value.
|
|
|
|
This code is identical to RtlLookupFunctionEntry, except that it does not
|
|
check the dynamic function table list.
|
|
|
|
Arguments:
|
|
|
|
ControlPc - Supplies the virtual address of an instruction bundle
|
|
within the specified function.
|
|
|
|
ImageBase - Returns the base address of the module to which the
|
|
function belongs.
|
|
|
|
TargetGp - Returns the global pointer value of the module.
|
|
|
|
Return Value:
|
|
|
|
If there is no entry in the function table for the specified PC, then
|
|
NULL is returned. Otherwise, the address of the function table entry
|
|
that corresponds to the specified PC is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRUNTIME_FUNCTION FunctionEntry;
|
|
PRUNTIME_FUNCTION FunctionTable;
|
|
ULONG SizeOfExceptionTable;
|
|
ULONG Size;
|
|
LONG High;
|
|
LONG Middle;
|
|
LONG Low;
|
|
USHORT i;
|
|
|
|
//
|
|
// Search for the image that includes the specified swizzled PC value.
|
|
//
|
|
|
|
*ImageBase = (ULONG_PTR)RtlPcToFileHeader((PVOID)ControlPc,
|
|
(PVOID *)ImageBase);
|
|
|
|
|
|
//
|
|
// If an image is found that includes the specified PC, then locate the
|
|
// function table for the image.
|
|
//
|
|
|
|
if ((PVOID)*ImageBase != NULL) {
|
|
|
|
*TargetGp = (ULONG_PTR)(RtlImageDirectoryEntryToData(
|
|
(PVOID)*ImageBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
|
|
&Size
|
|
));
|
|
|
|
FunctionTable = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData(
|
|
(PVOID)*ImageBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_EXCEPTION,
|
|
&SizeOfExceptionTable);
|
|
|
|
//
|
|
// If a function table is located, then search the table for a
|
|
// function table entry for the specified PC.
|
|
//
|
|
|
|
if (FunctionTable != NULL) {
|
|
|
|
//
|
|
// Initialize search indices.
|
|
//
|
|
|
|
Low = 0;
|
|
High = (SizeOfExceptionTable / sizeof(RUNTIME_FUNCTION)) - 1;
|
|
ControlPc = ControlPc - *ImageBase;
|
|
|
|
//
|
|
// Perform binary search on the function table for a function table
|
|
// entry that subsumes the specified PC.
|
|
//
|
|
|
|
while (High >= 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 + High) >> 1;
|
|
FunctionEntry = &FunctionTable[Middle];
|
|
|
|
if (ControlPc < FunctionEntry->BeginAddress) {
|
|
High = Middle - 1;
|
|
|
|
} else if (ControlPc >= FunctionEntry->EndAddress) {
|
|
Low = Middle + 1;
|
|
|
|
} else {
|
|
return FunctionEntry;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
RtlGetCallersAddress (
|
|
OUT PVOID *CallersPc,
|
|
OUT PVOID *CallersCallersPc
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the address of the routine that called the routine
|
|
that called this routine, and the routine that called the routine that
|
|
called this routine. For example, if A called B called C which called
|
|
this routine, the return addresses in A and B would be returned.
|
|
|
|
Arguments:
|
|
|
|
CallersPc - Supplies a pointer to a variable that receives the address
|
|
of the caller of the caller of this routine (B).
|
|
|
|
CallersCallersPc - Supplies a pointer to a variable that receives the
|
|
address of the caller of the caller of the caller of this routine
|
|
(A).
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Note:
|
|
|
|
If either of the calling stack frames exceeds the limits of the stack,
|
|
they are set to NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifdef REALLY_GET_CALLERS_CALLER
|
|
CONTEXT ContextRecord;
|
|
FRAME_POINTERS EstablisherFrame;
|
|
PRUNTIME_FUNCTION FunctionEntry;
|
|
BOOLEAN InFunction;
|
|
ULONG_PTR NextPc;
|
|
ULONGLONG HighStackLimit, LowStackLimit;
|
|
ULONGLONG HighBStoreLimit, LowBStoreLimit;
|
|
ULONGLONG ImageBase;
|
|
ULONGLONG TargetGp;
|
|
|
|
//
|
|
// Assume the function table entries for the various routines cannot be
|
|
// found or there are not four procedure activation records on the stack.
|
|
//
|
|
|
|
*CallersPc = NULL;
|
|
*CallersCallersPc = NULL;
|
|
|
|
//
|
|
// Capture the current context.
|
|
//
|
|
|
|
RtlCaptureContext(&ContextRecord);
|
|
NextPc = (ULONG_PTR)ContextRecord.BrRp;
|
|
|
|
//
|
|
// Get the high and low limits of the current thread's stack.
|
|
//
|
|
|
|
Rtlp64GetStackLimits(&LowStackLimit, &HighStackLimit);
|
|
Rtlp64GetBStoreLimits(&LowBStoreLimit, &HighBStoreLimit);
|
|
|
|
//
|
|
// Attempt to unwind to the caller of this routine (C).
|
|
//
|
|
|
|
FunctionEntry = RtlpLookupFunctionEntryForStackWalks(NextPc, &ImageBase, &TargetGp);
|
|
if (FunctionEntry != NULL) {
|
|
|
|
//
|
|
// A function entry was found for this routine. Virtually unwind
|
|
// to the caller of this routine (C).
|
|
//
|
|
|
|
NextPc = RtlVirtualUnwind(NextPc,
|
|
FunctionEntry,
|
|
&ContextRecord,
|
|
&InFunction,
|
|
&EstablisherFrame,
|
|
NULL);
|
|
|
|
//
|
|
// Attempt to unwind to the caller of the caller of this routine (B).
|
|
//
|
|
|
|
FunctionEntry = RtlpLookupFunctionEntryForStackWalks(NextPc);
|
|
if ((FunctionEntry != NULL) && (((ULONG_PTR)ContextRecord.IntSp < HighStackLimit) && ((ULONG_PTR)ContextRecord.RsBSP > LowBStoreLimit))) {
|
|
|
|
//
|
|
// A function table entry was found for the caller of the caller
|
|
// of this routine (B). Virtually unwind to the caller of the
|
|
// caller of this routine (B).
|
|
//
|
|
|
|
NextPc = RtlVirtualUnwind(NextPc,
|
|
FunctionEntry,
|
|
&ContextRecord,
|
|
&InFunction,
|
|
&EstablisherFrame,
|
|
NULL);
|
|
|
|
*CallersPc = (PVOID)NextPc;
|
|
|
|
//
|
|
// Attempt to unwind to the caller of the caller of the caller
|
|
// of the caller of this routine (A).
|
|
//
|
|
|
|
FunctionEntry = RtlpLookupFunctionEntryForStackWalks(NextPc);
|
|
if ((FunctionEntry != NULL) && (((ULONG_PTR)ContextRecord.IntSp < HighStackLimit) && ((ULONG_PTR)ContextRecord.RsBSP > LowBStoreLimit))) {
|
|
|
|
//
|
|
// A function table entry was found for the caller of the
|
|
// caller of the caller of this routine (A). Virtually unwind
|
|
// to the caller of the caller of the caller of this routine
|
|
// (A).
|
|
//
|
|
|
|
NextPc = RtlVirtualUnwind(NextPc,
|
|
FunctionEntry,
|
|
&ContextRecord,
|
|
&InFunction,
|
|
&EstablisherFrame,
|
|
NULL);
|
|
|
|
*CallersCallersPc = (PVOID)NextPc;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
*CallersPc = NULL;
|
|
*CallersCallersPc = NULL;
|
|
#endif // REALLY_GET_CALLERS_CALLER
|
|
return;
|
|
}
|
|
|
|
ULONG
|
|
RtlWalkFrameChain (
|
|
OUT PVOID *Callers,
|
|
IN ULONG Count,
|
|
IN ULONG Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
RtlWalkFrameChain
|
|
|
|
Description:
|
|
|
|
This function tries to walk the call chain and fill out a vector of
|
|
return addresses. The function works only on IA64. It is possible that
|
|
the function cannot fill the requested number of callers.
|
|
In this case the function will just return with
|
|
a less then requested count.
|
|
|
|
In kernel mode the function should not take
|
|
any exceptions (page faults) because it can be called at all sorts of
|
|
irql levels. It needs to be tested if this is the case.
|
|
|
|
Return value:
|
|
|
|
The number of identified return addresses on the stack. This can be less
|
|
then the Count requested if the stack ends or we encounter an error while
|
|
virtually unwinding the stack.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONTEXT ContextRecord;
|
|
FRAME_POINTERS EstablisherFrame;
|
|
PRUNTIME_FUNCTION FunctionEntry;
|
|
BOOLEAN InFunction;
|
|
ULONG_PTR NextPc, ControlPc;
|
|
ULONGLONG HighStackLimit, LowStackLimit;
|
|
ULONGLONG HighBStoreLimit, LowBStoreLimit;
|
|
ULONGLONG ImageBase;
|
|
ULONGLONG TargetGp;
|
|
ULONG CallersFound;
|
|
|
|
//
|
|
// IA64 code does not support any flags.
|
|
//
|
|
|
|
if (Flags != 0) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// In kernel mode avoid running at irql levels where we cannot
|
|
// take page faults. The walking code will access various sections
|
|
// from driver and system images and this will cause page faults.
|
|
//
|
|
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
|
|
if (KeAreAllApcsDisabled () == TRUE) {
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Assume the function table entries for the various routines cannot be
|
|
// found or there are not enough procedure activation records on the stack.
|
|
//
|
|
|
|
CallersFound = 0;
|
|
RtlZeroMemory (Callers, Count * sizeof(PVOID));
|
|
|
|
//
|
|
// Capture the current context.
|
|
//
|
|
|
|
RtlCaptureContext (&ContextRecord);
|
|
NextPc = (ULONG_PTR)ContextRecord.BrRp;
|
|
|
|
//
|
|
// Get the high and low limits of the current thread's stack.
|
|
//
|
|
|
|
Rtlp64GetStackLimits (&LowStackLimit, &HighStackLimit);
|
|
Rtlp64GetBStoreLimits (&LowBStoreLimit, &HighBStoreLimit);
|
|
|
|
//
|
|
// Loop to get requested number of callers.
|
|
//
|
|
|
|
try {
|
|
while (CallersFound < Count) {
|
|
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
|
|
//
|
|
// We need to check the NextPc value that we have got from
|
|
// CaptureContext() or VirtualUnwind(). It can happen that
|
|
// we pick up a bogus value from a session driver but in the
|
|
// current process no session space is mapped.
|
|
//
|
|
|
|
if ((MmIsSessionAddress ((PVOID)NextPc) == TRUE) &&
|
|
(MmGetSessionId (PsGetCurrentProcess()) == 0)) {
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
FunctionEntry = RtlpLookupFunctionEntryForStackWalks (NextPc,
|
|
&ImageBase,
|
|
&TargetGp);
|
|
|
|
//
|
|
// If we cannot find a function table entry or we are not
|
|
// within stack limits or backing store limits anymore
|
|
// we are done.
|
|
//
|
|
|
|
if (FunctionEntry == NULL) {
|
|
break;
|
|
}
|
|
|
|
if ((ULONG_PTR)(ContextRecord.IntSp) >= HighStackLimit ||
|
|
(ULONG_PTR)(ContextRecord.IntSp) <= LowStackLimit) {
|
|
|
|
break;
|
|
}
|
|
|
|
if ((ULONG_PTR)(ContextRecord.RsBSP) <= LowBStoreLimit ||
|
|
(ULONG_PTR)(ContextRecord.RsBSP) >= HighBStoreLimit) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// A function table entry was found.
|
|
// Virtually unwind to the caller of this routine.
|
|
//
|
|
|
|
NextPc = RtlVirtualUnwind (ImageBase,
|
|
NextPc,
|
|
FunctionEntry,
|
|
&ContextRecord,
|
|
&InFunction,
|
|
&EstablisherFrame,
|
|
NULL);
|
|
|
|
Callers[CallersFound] = (PVOID)NextPc;
|
|
CallersFound += 1;
|
|
}
|
|
|
|
} except (RtlpWalkFrameChainExceptionFilter (_exception_code(), _exception_info())) {
|
|
|
|
CallersFound = 0;
|
|
}
|
|
|
|
return CallersFound;
|
|
}
|
|
|
|
|
|
USHORT
|
|
RtlCaptureStackBackTrace(
|
|
IN ULONG FramesToSkip,
|
|
IN ULONG FramesToCapture,
|
|
OUT PVOID *BackTrace,
|
|
OUT PULONG BackTraceHash
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine walks up the stack frames, capturing the return address from
|
|
each frame requested.
|
|
|
|
Arguments:
|
|
|
|
FramesToSkip - frames detected but not included in the stack trace
|
|
|
|
FramesToCapture - frames to be captured in the stack trace buffer.
|
|
One of the frames will be for RtlCaptureStackBackTrace.
|
|
|
|
BackTrace - stack trace buffer
|
|
|
|
BackTraceHash - very simple hash value that can be used to organize
|
|
hash tables. It is just an arithmetic sum of the pointers in the
|
|
stack trace buffer. If NULL then no hash value is computed.
|
|
|
|
Return Value:
|
|
|
|
Number of return addresses returned in the stack trace buffer.
|
|
|
|
--*/
|
|
{
|
|
PVOID Trace [2 * MAX_STACK_DEPTH];
|
|
ULONG FramesFound;
|
|
ULONG HashValue;
|
|
ULONG Index;
|
|
|
|
//
|
|
// One more frame to skip for the "capture" function (RtlWalkFrameChain).
|
|
//
|
|
|
|
FramesToSkip += 1;
|
|
|
|
//
|
|
// Sanity checks.
|
|
//
|
|
|
|
if (FramesToCapture + FramesToSkip >= 2 * MAX_STACK_DEPTH) {
|
|
return 0;
|
|
}
|
|
|
|
FramesFound = RtlWalkFrameChain (Trace,
|
|
FramesToCapture + FramesToSkip,
|
|
0);
|
|
|
|
if (FramesFound <= FramesToSkip) {
|
|
return 0;
|
|
}
|
|
|
|
for (Index = 0, HashValue = 0; Index < FramesToCapture; Index += 1) {
|
|
|
|
if (FramesToSkip + Index >= FramesFound) {
|
|
break;
|
|
}
|
|
|
|
BackTrace[Index] = Trace[FramesToSkip + Index];
|
|
HashValue += PtrToUlong(BackTrace[Index]);
|
|
}
|
|
|
|
if (BackTraceHash != NULL) {
|
|
|
|
*BackTraceHash = HashValue;
|
|
}
|
|
|
|
return (USHORT)Index;
|
|
}
|
|
|
|
|
|
ULONG
|
|
RtlpWalkFrameChainExceptionFilter (
|
|
ULONG ExceptionCode,
|
|
PVOID ExceptionRecord
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the exception filter used by RtlWalkFramechain function.
|
|
|
|
Arguments:
|
|
|
|
ExceptionCode - exception code
|
|
ExceptionRecord - structure with pointers to .exr and .cxr
|
|
|
|
Return Value:
|
|
|
|
Always EXCEPTION_EXECUTE_HANDLER.
|
|
|
|
--*/
|
|
{
|
|
|
|
#if DBG
|
|
DbgPrint ("Unexpected exception (info %p) in RtlWalkFrameChain ...\n",
|
|
ExceptionRecord);
|
|
|
|
DbgBreakPoint ();
|
|
#endif
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|