714 lines
19 KiB
C
714 lines
19 KiB
C
/*++
|
||
|
||
Copyright (c) 1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
dmpstate.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the architecture specific routine that dumps
|
||
the machine state when a bug check occurs and no debugger is hooked
|
||
to the system. It is assumed that it is called from bug check.
|
||
|
||
Author:
|
||
|
||
David N. Cutler (davec) 17-Jan-1992
|
||
|
||
Environment:
|
||
|
||
Kernel mode.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "ki.h"
|
||
|
||
//
|
||
// Define forward referenced prototypes.
|
||
//
|
||
|
||
VOID
|
||
KiDisplayString (
|
||
IN ULONG Column,
|
||
IN ULONG Row,
|
||
IN PCHAR Buffer
|
||
);
|
||
|
||
PRUNTIME_FUNCTION
|
||
KiLookupFunctionEntry (
|
||
IN ULONG ControlPc
|
||
);
|
||
|
||
PVOID
|
||
KiPcToFileHeader(
|
||
IN PVOID PcValue,
|
||
OUT PVOID *BaseOfImage,
|
||
OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry
|
||
);
|
||
|
||
//
|
||
// Define external data.
|
||
//
|
||
|
||
extern LIST_ENTRY PsLoadedModuleList;
|
||
|
||
VOID
|
||
KeDumpMachineState (
|
||
IN PKPROCESSOR_STATE ProcessorState,
|
||
IN PCHAR Buffer,
|
||
IN PULONG BugCheckParameters,
|
||
IN ULONG NumberOfParameters,
|
||
IN PKE_BUGCHECK_UNICODE_TO_ANSI UnicodeToAnsiRoutine
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function formats and displays the machine state at the time of the
|
||
to bug check.
|
||
|
||
Arguments:
|
||
|
||
ProcessorState - Supplies a pointer to a processor state record.
|
||
|
||
Buffer - Supplies a pointer to a buffer to be used to output machine
|
||
state information.
|
||
|
||
BugCheckParameters - Supplies a pointer to an array of additional
|
||
bug check information.
|
||
|
||
NumberOfParameters - Suppiles the size of the bug check parameters
|
||
array.
|
||
|
||
UnicodeToAnsiRoutine - Supplies a pointer to a routine to convert Unicode strings
|
||
to Ansi strings without touching paged translation tables.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PCONTEXT ContextRecord;
|
||
ULONG ControlPc;
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
ULONG DisplayColumn;
|
||
ULONG DisplayHeight;
|
||
ULONG DisplayRow;
|
||
ULONG DisplayWidth;
|
||
UNICODE_STRING DllName;
|
||
ULONG EstablisherFrame;
|
||
PRUNTIME_FUNCTION FunctionEntry;
|
||
PVOID ImageBase;
|
||
ULONG Index;
|
||
BOOLEAN InFunction;
|
||
ULONG LastStack;
|
||
PLIST_ENTRY ModuleListHead;
|
||
PLIST_ENTRY NextEntry;
|
||
ULONG NextPc;
|
||
ULONG StackLimit;
|
||
UCHAR AnsiBuffer[ 32 ];
|
||
ULONG DateStamp;
|
||
|
||
//
|
||
// Query display parameters.
|
||
//
|
||
|
||
HalQueryDisplayParameters(&DisplayWidth,
|
||
&DisplayHeight,
|
||
&DisplayColumn,
|
||
&DisplayRow);
|
||
|
||
//
|
||
// Display any addresses that fall within the range of any module in
|
||
// the loaded module list.
|
||
//
|
||
|
||
for (Index = 0; Index < NumberOfParameters; Index += 1) {
|
||
ImageBase = KiPcToFileHeader((PVOID)*BugCheckParameters,
|
||
&ImageBase,
|
||
&DataTableEntry);
|
||
|
||
if (ImageBase != NULL) {
|
||
sprintf(Buffer,
|
||
"*** %08lX has base at %08lX - %s\n",
|
||
*BugCheckParameters,
|
||
ImageBase,
|
||
(*UnicodeToAnsiRoutine)( &DataTableEntry->BaseDllName, AnsiBuffer, sizeof( AnsiBuffer )));
|
||
|
||
HalDisplayString(Buffer);
|
||
}
|
||
|
||
BugCheckParameters += 1;
|
||
}
|
||
|
||
//
|
||
// Virtually unwind to the caller of bug check.
|
||
//
|
||
|
||
ContextRecord = &ProcessorState->ContextFrame;
|
||
LastStack = (ULONG)ContextRecord->XIntSp;
|
||
ControlPc = (ULONG)(ContextRecord->XIntRa - 4);
|
||
NextPc = ControlPc;
|
||
FunctionEntry = KiLookupFunctionEntry(ControlPc);
|
||
if (FunctionEntry != NULL) {
|
||
NextPc = RtlVirtualUnwind(ControlPc | 1,
|
||
FunctionEntry,
|
||
ContextRecord,
|
||
&InFunction,
|
||
&EstablisherFrame,
|
||
NULL);
|
||
}
|
||
|
||
//
|
||
// At this point the context record contains the machine state at the
|
||
// call to bug check.
|
||
//
|
||
// Put out the machine state at the time of the bugcheck.
|
||
//
|
||
|
||
sprintf(Buffer,
|
||
"\nMachine State at Call to Bug Check PC : %08lX PSR : %08lX\n\n",
|
||
(ULONG)ContextRecord->XIntRa,
|
||
ContextRecord->Psr);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
//
|
||
// Format and output the integer registers.
|
||
//
|
||
|
||
sprintf(Buffer,
|
||
"AT :%8lX V0 :%8lX V1 :%8lX A0 :%8lX\n",
|
||
(ULONG)ContextRecord->XIntAt,
|
||
(ULONG)ContextRecord->XIntV0,
|
||
(ULONG)ContextRecord->XIntV1,
|
||
(ULONG)ContextRecord->XIntA0);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"A1 :%8lX A2 :%8lX A3 :%8lX T0 :%8lX\n",
|
||
(ULONG)ContextRecord->XIntA1,
|
||
(ULONG)ContextRecord->XIntA2,
|
||
(ULONG)ContextRecord->XIntA3,
|
||
(ULONG)ContextRecord->XIntT0);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"T1 :%8lX T2 :%8lX T3 :%8lX T4 :%8lX\n",
|
||
(ULONG)ContextRecord->XIntT1,
|
||
(ULONG)ContextRecord->XIntT2,
|
||
(ULONG)ContextRecord->XIntT3,
|
||
(ULONG)ContextRecord->XIntT4);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"T5 :%8lX T6 :%8lX T7 :%8lX T8 :%8lX\n",
|
||
(ULONG)ContextRecord->XIntT5,
|
||
(ULONG)ContextRecord->XIntT6,
|
||
(ULONG)ContextRecord->XIntT7,
|
||
(ULONG)ContextRecord->XIntT8);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"T9 :%8lX S0 :%8lX S1 :%8lX S2 :%8lX\n",
|
||
(ULONG)ContextRecord->XIntT9,
|
||
(ULONG)ContextRecord->XIntS0,
|
||
(ULONG)ContextRecord->XIntS1,
|
||
(ULONG)ContextRecord->XIntS2);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"S3 :%8lX S4 :%8lX S5 :%8lX S6 :%8lX\n",
|
||
(ULONG)ContextRecord->XIntS3,
|
||
(ULONG)ContextRecord->XIntS4,
|
||
(ULONG)ContextRecord->XIntS5,
|
||
(ULONG)ContextRecord->XIntS6);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"S7 :%8lX S8 :%8lX GP :%8lX SP :%8lX\n",
|
||
(ULONG)ContextRecord->XIntS7,
|
||
(ULONG)ContextRecord->XIntS8,
|
||
(ULONG)ContextRecord->XIntGp,
|
||
(ULONG)ContextRecord->XIntSp);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"RA :%8lX LO :%8lX HI :%8lX FSR:%8lX\n",
|
||
(ULONG)ContextRecord->XIntRa,
|
||
(ULONG)ContextRecord->XIntLo,
|
||
(ULONG)ContextRecord->XIntHi,
|
||
(ULONG)ContextRecord->Fsr);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
//
|
||
// Format and output the firswt four floating registers.
|
||
//
|
||
|
||
sprintf(Buffer,
|
||
"F0 :%8lX F1 :%8lX F2 :%8lX F3 :%8lX\n",
|
||
ContextRecord->FltF0,
|
||
ContextRecord->FltF1,
|
||
ContextRecord->FltF2,
|
||
ContextRecord->FltF3);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F4 :%8lX F5 :%8lX F6 :%8lX F7 :%8lX\n",
|
||
ContextRecord->FltF4,
|
||
ContextRecord->FltF5,
|
||
ContextRecord->FltF6,
|
||
ContextRecord->FltF7);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F8 :%8lX F9 :%8lX F10:%8lX F11:%8lX\n",
|
||
ContextRecord->FltF8,
|
||
ContextRecord->FltF9,
|
||
ContextRecord->FltF10,
|
||
ContextRecord->FltF11);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F12:%8lX F13:%8lX F14:%8lX F15:%8lX\n",
|
||
ContextRecord->FltF12,
|
||
ContextRecord->FltF13,
|
||
ContextRecord->FltF14,
|
||
ContextRecord->FltF15);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F16:%8lX F17:%8lX F18:%8lX F19:%8lX\n",
|
||
ContextRecord->FltF16,
|
||
ContextRecord->FltF17,
|
||
ContextRecord->FltF18,
|
||
ContextRecord->FltF19);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F20:%8lX F21:%8lX F22:%8lX F23:%8lX\n",
|
||
ContextRecord->FltF20,
|
||
ContextRecord->FltF21,
|
||
ContextRecord->FltF22,
|
||
ContextRecord->FltF23);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F24:%8lX F25:%8lX F26:%8lX F27:%8lX\n",
|
||
ContextRecord->FltF24,
|
||
ContextRecord->FltF25,
|
||
ContextRecord->FltF26,
|
||
ContextRecord->FltF27);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F28:%8lX F29:%8lX F30:%8lX F31:%8lX\n\n",
|
||
ContextRecord->FltF28,
|
||
ContextRecord->FltF29,
|
||
ContextRecord->FltF30,
|
||
ContextRecord->FltF31);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
//
|
||
// Output short stack back trace with base address.
|
||
//
|
||
|
||
DllName.Length = 0;
|
||
DllName.Buffer = L"";
|
||
if (FunctionEntry != NULL) {
|
||
StackLimit = (ULONG)KeGetCurrentThread()->KernelStack;
|
||
HalDisplayString("Callee-Sp Return-Ra Dll Base - Name\n");
|
||
for (Index = 0; Index < 8; Index += 1) {
|
||
ImageBase = KiPcToFileHeader((PVOID)ControlPc,
|
||
&ImageBase,
|
||
&DataTableEntry);
|
||
|
||
sprintf(Buffer,
|
||
" %08lX %08lX : %08lX - %s\n",
|
||
(ULONG)ContextRecord->XIntSp,
|
||
NextPc + 4,
|
||
ImageBase,
|
||
(*UnicodeToAnsiRoutine)( (ImageBase != NULL) ? &DataTableEntry->BaseDllName : &DllName,
|
||
AnsiBuffer, sizeof( AnsiBuffer )));
|
||
|
||
HalDisplayString(Buffer);
|
||
if ((NextPc != ControlPc) || ((ULONG)ContextRecord->XIntSp != LastStack)) {
|
||
ControlPc = NextPc;
|
||
LastStack = (ULONG)ContextRecord->XIntSp;
|
||
FunctionEntry = KiLookupFunctionEntry(ControlPc);
|
||
if ((FunctionEntry != NULL) && (LastStack < StackLimit)) {
|
||
NextPc = RtlVirtualUnwind(ControlPc | 1,
|
||
FunctionEntry,
|
||
ContextRecord,
|
||
&InFunction,
|
||
&EstablisherFrame,
|
||
NULL);
|
||
} else {
|
||
NextPc = (ULONG)ContextRecord->XIntRa;
|
||
}
|
||
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Output the build number and other useful information.
|
||
//
|
||
|
||
sprintf(Buffer,
|
||
"\nIRQL : %d, DPC Active : %s, SYSVER 0x%08x\n",
|
||
KeGetCurrentIrql(),
|
||
KeIsExecutingDpc() ? "TRUE" : "FALSE",
|
||
NtBuildNumber);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
//
|
||
// Output the processor id and the primary cache sizes.
|
||
//
|
||
|
||
sprintf(Buffer,
|
||
"Processor Id %d.%d, Icache : %d, Dcache : %d\n",
|
||
(PCR->ProcessorId >> 8) & 0xff,
|
||
PCR->ProcessorId & 0xff,
|
||
PCR->FirstLevelIcacheSize,
|
||
PCR->FirstLevelDcacheSize);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
//
|
||
// If the display width is greater than 80 + 24 (the size of a DLL
|
||
// name and base address), then display all the modules loaded in
|
||
// the system.
|
||
//
|
||
|
||
HalQueryDisplayParameters(&DisplayWidth,
|
||
&DisplayHeight,
|
||
&DisplayColumn,
|
||
&DisplayRow);
|
||
|
||
if (DisplayWidth > (80 + 24)) {
|
||
if (KeLoaderBlock != NULL) {
|
||
ModuleListHead = &KeLoaderBlock->LoadOrderListHead;
|
||
|
||
} else {
|
||
ModuleListHead = &PsLoadedModuleList;
|
||
}
|
||
|
||
//
|
||
// Output display headers.
|
||
//
|
||
|
||
Index = 1;
|
||
KiDisplayString(80, Index, "Dll Base DateStmp - Name");
|
||
NextEntry = ModuleListHead->Flink;
|
||
if (NextEntry != NULL) {
|
||
|
||
//
|
||
// Scan the list of loaded modules and display their base
|
||
// address and name.
|
||
//
|
||
|
||
while (NextEntry != ModuleListHead) {
|
||
Index += 1;
|
||
DataTableEntry = CONTAINING_RECORD(NextEntry,
|
||
LDR_DATA_TABLE_ENTRY,
|
||
InLoadOrderLinks);
|
||
|
||
if (MmDbgReadCheck(DataTableEntry->DllBase) != NULL) {
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
|
||
NtHeaders = RtlImageNtHeader(DataTableEntry->DllBase);
|
||
DateStamp = NtHeaders->FileHeader.TimeDateStamp;
|
||
|
||
} else {
|
||
DateStamp = 0;
|
||
}
|
||
sprintf(Buffer,
|
||
"%08lX %08lx - %s",
|
||
DataTableEntry->DllBase,
|
||
DateStamp,
|
||
(*UnicodeToAnsiRoutine)( &DataTableEntry->BaseDllName, AnsiBuffer, sizeof( AnsiBuffer )));
|
||
|
||
KiDisplayString(80, Index, Buffer);
|
||
NextEntry = NextEntry->Flink;
|
||
if (Index > DisplayHeight) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Reset the current display position.
|
||
//
|
||
|
||
HalSetDisplayParameters(DisplayColumn, DisplayRow);
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
KiDisplayString (
|
||
IN ULONG Column,
|
||
IN ULONG Row,
|
||
IN PCHAR Buffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function display a string starting at the specified column and row
|
||
position on the screen.
|
||
|
||
Arguments:
|
||
|
||
Column - Supplies the starting column of where the string is displayed.
|
||
|
||
Row - Supplies the starting row of where the string is displayed.
|
||
|
||
Bufer - Supplies a pointer to the string that is displayed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Position the cursor and display the string.
|
||
//
|
||
|
||
HalSetDisplayParameters(Column, Row);
|
||
HalDisplayString(Buffer);
|
||
return;
|
||
}
|
||
|
||
PRUNTIME_FUNCTION
|
||
KiLookupFunctionEntry (
|
||
IN ULONG ControlPc
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function searches the currently active function tables for an entry
|
||
that corresponds to the specified PC value.
|
||
|
||
Arguments:
|
||
|
||
ControlPc - Supplies the address of an instruction within the specified
|
||
function.
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
PRUNTIME_FUNCTION FunctionEntry;
|
||
PRUNTIME_FUNCTION FunctionTable;
|
||
ULONG SizeOfExceptionTable;
|
||
LONG High;
|
||
PVOID ImageBase;
|
||
LONG Low;
|
||
LONG Middle;
|
||
|
||
//
|
||
// Search for the image that includes the specified PC value.
|
||
//
|
||
|
||
ImageBase = KiPcToFileHeader((PVOID)ControlPc,
|
||
&ImageBase,
|
||
&DataTableEntry);
|
||
|
||
//
|
||
// If an image is found that includes the specified PC, then locate the
|
||
// function table for the image.
|
||
//
|
||
|
||
if (ImageBase != NULL) {
|
||
FunctionTable = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData(
|
||
ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION,
|
||
&SizeOfExceptionTable);
|
||
|
||
//
|
||
// If a function table is located, then search the function table
|
||
// for a function table entry for the specified PC.
|
||
//
|
||
|
||
if (FunctionTable != NULL) {
|
||
|
||
//
|
||
// Initialize search indicies.
|
||
//
|
||
|
||
Low = 0;
|
||
High = (SizeOfExceptionTable / sizeof(RUNTIME_FUNCTION)) - 1;
|
||
|
||
//
|
||
// 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 {
|
||
|
||
//
|
||
// The capability exists for more than one function entry
|
||
// to map to the same function. This permits a function to
|
||
// have discontiguous code segments described by separate
|
||
// function table entries. If the ending prologue address
|
||
// is not within the limits of the begining and ending
|
||
// address of the function able entry, then the prologue
|
||
// ending address is the address of a function table entry
|
||
// that accurately describes the ending prologue address.
|
||
//
|
||
|
||
if ((FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) ||
|
||
(FunctionEntry->PrologEndAddress >= FunctionEntry->EndAddress)) {
|
||
FunctionEntry = (PRUNTIME_FUNCTION)FunctionEntry->PrologEndAddress;
|
||
}
|
||
|
||
return FunctionEntry;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// A function table entry for the specified PC was not found.
|
||
//
|
||
|
||
return NULL;
|
||
}
|
||
|
||
PVOID
|
||
KiPcToFileHeader(
|
||
IN PVOID PcValue,
|
||
OUT PVOID *BaseOfImage,
|
||
OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the base of an image that contains the
|
||
specified PcValue. An image contains the PcValue if the PcValue
|
||
is within the ImageBase, and the ImageBase plus the size of the
|
||
virtual image.
|
||
|
||
Arguments:
|
||
|
||
PcValue - Supplies a PcValue.
|
||
|
||
BaseOfImage - Returns the base address for the image containing the
|
||
PcValue. This value must be added to any relative addresses in
|
||
the headers to locate portions of the image.
|
||
|
||
DataTableEntry - Suppies a pointer to a variable that receives the
|
||
address of the data table entry that describes the image.
|
||
|
||
Return Value:
|
||
|
||
NULL - No image was found that contains the PcValue.
|
||
|
||
NON-NULL - Returns the base address of the image that contain the
|
||
PcValue.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PLIST_ENTRY ModuleListHead;
|
||
PLDR_DATA_TABLE_ENTRY Entry;
|
||
PLIST_ENTRY Next;
|
||
ULONG Bounds;
|
||
PVOID ReturnBase, Base;
|
||
|
||
//
|
||
// If the module list has been initialized, then scan the list to
|
||
// locate the appropriate entry.
|
||
//
|
||
|
||
if (KeLoaderBlock != NULL) {
|
||
ModuleListHead = &KeLoaderBlock->LoadOrderListHead;
|
||
|
||
} else {
|
||
ModuleListHead = &PsLoadedModuleList;
|
||
}
|
||
|
||
ReturnBase = NULL;
|
||
Next = ModuleListHead->Flink;
|
||
if (Next != NULL) {
|
||
while (Next != ModuleListHead) {
|
||
Entry = CONTAINING_RECORD(Next,
|
||
LDR_DATA_TABLE_ENTRY,
|
||
InLoadOrderLinks);
|
||
|
||
Next = Next->Flink;
|
||
Base = Entry->DllBase;
|
||
Bounds = (ULONG)Base + Entry->SizeOfImage;
|
||
if ((ULONG)PcValue >= (ULONG)Base && (ULONG)PcValue < Bounds) {
|
||
*DataTableEntry = Entry;
|
||
ReturnBase = Base;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
*BaseOfImage = ReturnBase;
|
||
return ReturnBase;
|
||
}
|