575 lines
15 KiB
C
575 lines
15 KiB
C
/*++
|
||
|
||
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:
|
||
|
||
RtlpWalkFrameChain64
|
||
|
||
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;
|
||
|
||
//
|
||
// In kernel mode avoid running at irql levels where we cannot
|
||
// take page faults. The walking code will touch various sections
|
||
// from driver images and this will cause page faults.
|
||
//
|
||
|
||
#ifdef NTOS_KERNEL_RUNTIME
|
||
|
||
if (KeGetCurrentIrql() > PASSIVE_LEVEL) {
|
||
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;
|
||
}
|
||
|