windows-nt/Source/XPSP1/NT/base/ntos/ke/aligntrk.c
2020-09-26 16:20:57 +08:00

1316 lines
33 KiB
C

/*++
Copyright (c) 1990 Microsoft Corporation
Copyright (c) 1993, 1994 Digital Equipment Corporation
Module Name:
aligntrk.c
Abstract:
This module implements the code necessary to dispatch exceptions to the
proper mode and invoke the exception dispatcher.
Author:
David N. Cutler (davec) 3-Apr-1990
Environment:
Kernel mode only.
Revision History:
Thomas Van Baak (tvb) 12-May-1992
Adapted for Alpha AXP.
Forrest Foltz (forrestf) 30-Dec-1999
Broke out increasingly complex and common alignment fault handling into
this file.
--*/
#include "ki.h"
//
// EXINFO_EFFECTIVE_ADDRESS: slot number [0...4] for faulting address.
//
#if defined(_IA64_)
#define EXINFO_EFFECTIVE_ADDRESS 1
#else // !_IA64_
#define EXINFO_EFFECTIVE_ADDRESS 2
#endif // !_IA64_
//
// Data misalignment exception (auto alignment fixup) control.
//
// If KiEnableAlignmentFaultExceptions is 0, then no alignment
// exceptions are raised and all misaligned user and kernel mode data
// references are emulated. This is consistent with NT/Alpha version
// 3.1 behavior.
//
// If KiEnableAlignmentFaultExceptions is 1, then the
// current thread automatic alignment fixup enable determines whether
// emulation is attempted in user mode. This is consistent with NT/Mips
// behavior.
//
// If KiEnableAlignmentFaultExceptions is 2, then the behavior depends
// on the execution mode at the time of the fault. Kernel-mode code gets
// type 1 behaivor above (no fixup), user-mode code gets type 0 above
// (fixup).
//
// This last mode is temporary until we flush out the remaining user-mode
// alignment faults, at which point the option will be removed and the
// default value will be set to 1.
//
// N.B. This default value may be reset from the Registry during init.
//
ULONG KiEnableAlignmentFaultExceptions = 1;
#define IsWow64Process() (PsGetCurrentProcess()->Wow64Process != NULL)
#if DBG
//
// Globals to track the number of alignment exception fixups in both user and
// kernel.
//
ULONG KiKernelFixupCount = 0;
ULONG KiUserFixupCount = 0;
//
// Set KiBreakOnAlignmentFault to the desired combination of
// the following flags.
//
#define KE_ALIGNMENT_BREAK_USER 0x01
#define KE_ALIGNMENT_BREAK_KERNEL 0x02
ULONG KiBreakOnAlignmentFault = KE_ALIGNMENT_BREAK_USER;
__inline
BOOLEAN
KI_BREAK_ON_ALIGNMENT_FAULT(
IN KPROCESSOR_MODE PreviousMode
)
/*++
Routine description:
Given that an alignment fault has been encountered, determines whether
a debug break should occur based on the execution mode of the fault and
flags in KiBreakOnAlignmentFault.
Arguments:
PreviousMode - The execution mode at the time of the fault.
Return Value:
TRUE if a debug break should occur, FALSE otherwise.
--*/
{
if ((KiBreakOnAlignmentFault & KE_ALIGNMENT_BREAK_USER) != 0 &&
PreviousMode == UserMode) {
return TRUE;
}
if ((KiBreakOnAlignmentFault & KE_ALIGNMENT_BREAK_KERNEL) != 0 &&
PreviousMode == KernelMode) {
return TRUE;
}
return FALSE;
}
//
// Structures to track alignment fault locations on a global basis. These
// are used in the checked kernel only, as an aid in finding and fixing
// alignment faults in the system.
//
#define MAX_IMAGE_NAME_CHARS 15
typedef struct _ALIGNMENT_FAULT_IMAGE *PALIGNMENT_FAULT_IMAGE;
typedef struct _ALIGNMENT_FAULT_LOCATION *PALIGNMENT_FAULT_LOCATION;
typedef struct _ALIGNMENT_FAULT_IMAGE {
//
// Head of singly-linked list of fault locations associated with this image
//
PALIGNMENT_FAULT_LOCATION LocationHead;
//
// Total number of alignment faults associated with this image.
//
ULONG Count;
//
// Number of unique alignment fault locations found in this image
//
ULONG Instances;
//
// Name of the image
//
CHAR Name[ MAX_IMAGE_NAME_CHARS + 1 ];
} ALIGNMENT_FAULT_IMAGE;
BOOLEAN
KiNewGlobalAlignmentFault(
IN PVOID ProgramCounter,
IN KPROCESSOR_MODE PreviousMode,
OUT PALIGNMENT_FAULT_IMAGE *AlignmentFaultImage
);
#endif
NTSTATUS
KipRecordAlignmentException(
IN PVOID ProgramCounter,
OUT PALIGNMENT_EXCEPTION_RECORD *ExceptionRecord
);
PALIGNMENT_EXCEPTION_RECORD
KipFindAlignmentException(
IN PVOID ProgramCounter
);
PALIGNMENT_EXCEPTION_RECORD
KipAllocateAlignmentExceptionRecord( VOID );
BOOLEAN
KiHandleAlignmentFault(
IN PEXCEPTION_RECORD ExceptionRecord,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame,
IN KPROCESSOR_MODE PreviousMode,
IN BOOLEAN FirstChance,
OUT BOOLEAN *ExceptionForwarded
)
/*++
Routine description:
This routine deals with alignment exceptions as appropriate. See comments
at the beginning of this module.
Arguments:
ExceptionRecord - Supplies a pointer to an exception record.
ExceptionFrame - Supplies a pointer to an exception frame.
TrapFrame - Supplies a pointer to a trap frame.
PreviousMode - Supplies the previous processor mode.
FirstChance - Supplies a boolean variable that specifies whether this
is the first (TRUE) or second (FALSE) time that this exception has
been processed.
ExceptionForwarded - On return, indicates whether the exception had
already been forwarded to a user-mode debugger.
Return Value:
TRUE if the alignment exception was handled, FALSE otherwise.
--*/
{
BOOLEAN AlignmentFaultHandled;
BOOLEAN NewAlignmentFault;
BOOLEAN EmulateAlignmentFault;
BOOLEAN ExceptionWasForwarded;
BOOLEAN AutoAlignment;
NTSTATUS Status;
PVOID ProgramCounter;
PVOID EffectiveAddress;
#if DBG
PALIGNMENT_FAULT_IMAGE FaultImage;
#endif
//
// Assume the fault was not handled and that the exception had not
// been forwarded to a user-mode debugger.
//
AlignmentFaultHandled = FALSE;
ExceptionWasForwarded = FALSE;
if (FirstChance != FALSE) {
//
// This is the first chance for handling an exception... we haven't yet
// searched for an exception handler.
//
EmulateAlignmentFault = FALSE;
AutoAlignment = FALSE;
ProgramCounter = (PVOID)ExceptionRecord->ExceptionAddress;
//
// Determine whether autoalignment is enabled for thread. If a DPC or
// an interrupt is being executed, then we are in an arbitrary thread
// context. Per-process and per-thread settings are ignored in this
// case.
//
if (IsWow64Process() != FALSE) {
//
// For now, autoalignment is on (both user and kernel) for Wow64
// processes.
//
AutoAlignment = TRUE;
}
if (PreviousMode == UserMode &&
(KeGetCurrentThread()->AutoAlignment != FALSE ||
KeGetCurrentThread()->ApcState.Process->AutoAlignment != FALSE)) {
//
// The fault occured in user mode, and the thread and/or process
// has autoalignment turned on.
//
#if defined(_IA64_)
//
// On IA64 platform, reset psr.ac bit to disable alignment check
//
TrapFrame->StIPSR &= ~(ULONGLONG)(1ULL << PSR_AC);
#endif // defined(_IA64_)
AutoAlignment = TRUE;
}
if (PreviousMode == UserMode &&
PsGetCurrentProcess()->DebugPort != NULL &&
AutoAlignment == FALSE) {
BOOLEAN DebuggerHandledException;
PALIGNMENT_EXCEPTION_RECORD AlignmentExceptionRecord;
//
// The alignment exception is in user mode, there is a debugger
// attached, and autoalignment is not enabled for this thread.
//
// Determine whether this exception has already been observed
// and, if so, whether we should break into the debugger.
//
Status = KipRecordAlignmentException( ProgramCounter,
&AlignmentExceptionRecord );
if (!NT_SUCCESS(Status)) {
AlignmentExceptionRecord = NULL;
}
if (AlignmentExceptionRecord != NULL &&
AlignmentExceptionRecord->AutoFixup != FALSE) {
//
// The alignment exception record for this location
// indicates that an automatic fixup should be applied
// without notifying the debugger. This is because
// the user entered 'gh' at the debug prompt the last
// time we reported this fault.
//
EmulateAlignmentFault = TRUE;
} else {
//
// Forward the exception to the debugger.
//
ExceptionWasForwarded = TRUE;
DebuggerHandledException =
DbgkForwardException( ExceptionRecord, TRUE, FALSE );
if (DebuggerHandledException != FALSE) {
//
// The user continued with "gh", so fix up this and all
// subsequent alignment exceptions at this address.
//
EmulateAlignmentFault = TRUE;
if (AlignmentExceptionRecord != NULL) {
AlignmentExceptionRecord->AutoFixup = TRUE;
}
}
}
} else if ((KiEnableAlignmentFaultExceptions == 0) ||
(AutoAlignment != FALSE) ||
(PreviousMode == UserMode &&
KiEnableAlignmentFaultExceptions == 2)) {
//
// Emulate the alignment if:
//
// KiEnableAlignmentFaultExceptions is 0, OR
// this thread has enabled alignment fixups, OR
// the current process is a WOW64 process, OR
// KiEnableAlignmentFaultExceptions is 2 and the fault occured
// in usermode
//
EmulateAlignmentFault = TRUE;
} else {
//
// We are not fixing up the alignment fault.
//
#if defined(_IA64_)
//
// On IA64 platform, set psr.ac bit to enable h/w alignment check
//
TrapFrame->StIPSR |= (1ULL << PSR_AC);
#endif // defined(_IA64_)
}
#if DBG
//
// Count alignment faults by mode.
//
if (PreviousMode == KernelMode) {
KiKernelFixupCount += 1;
} else {
KiUserFixupCount += 1;
}
EffectiveAddress =
(PVOID)ExceptionRecord->ExceptionInformation[EXINFO_EFFECTIVE_ADDRESS];
NewAlignmentFault = KiNewGlobalAlignmentFault( ProgramCounter,
PreviousMode,
&FaultImage );
if (NewAlignmentFault != FALSE) {
//
// Attempt to determine and display the name of the offending
// image.
//
DbgPrint("KE: %s Fixup: %.16s [%.16s], Pc=%.16p, Addr=%.16p ... Total=%ld %s\n",
(PreviousMode == KernelMode) ? "Kernel" : "User",
&PsGetCurrentProcess()->ImageFileName[0],
FaultImage->Name,
ProgramCounter,
EffectiveAddress,
(PreviousMode == KernelMode) ? KiKernelFixupCount : KiUserFixupCount,
IsWow64Process() ? "(Wow64)" : "");
if (AutoAlignment == FALSE &&
KI_BREAK_ON_ALIGNMENT_FAULT( PreviousMode ) != FALSE &&
ExceptionWasForwarded == FALSE) {
if (EmulateAlignmentFault == FALSE) {
DbgPrint("KE: Misaligned access WILL NOT be emulated\n");
}
//
// This alignment fault would not normally have been fixed up,
// and KiBreakOnAlignmentFault flags indicate that we should
// break into the kernel debugger.
//
// Also, we know that we have not broken into a user-mode
// debugger as a result of this fault.
//
if (PreviousMode != KernelMode) {
RtlMakeStackTraceDataPresent();
}
DbgBreakPoint();
}
}
#endif
//
// Emulate the reference according to the decisions made above.
//
if (EmulateAlignmentFault != FALSE) {
if (KiEmulateReference(ExceptionRecord,
ExceptionFrame,
TrapFrame) != FALSE) {
KeGetCurrentPrcb()->KeAlignmentFixupCount += 1;
AlignmentFaultHandled = TRUE;
}
}
}
*ExceptionForwarded = ExceptionWasForwarded;
return AlignmentFaultHandled;
}
NTSTATUS
KipRecordAlignmentException(
IN PVOID ProgramCounter,
OUT PALIGNMENT_EXCEPTION_RECORD *ExceptionRecord
)
/*++
Routine Description:
This routine searches for an existing ALIGNMENT_EXCEPTION_RECORD on the
per-process list of alignment exceptions. If a match is not found, then
a new record is created.
Arguments:
ProgramCounter - Supplies the address of the faulting instruction.
ExceptionRecord - Supplies a pointer into which is placed the address
of the matching alignment exception record.
Return Value:
STATUS_SUCCESS if the operation was successful, or an appropriate error
code otherwise.
--*/
{
PALIGNMENT_EXCEPTION_RECORD exceptionRecord;
NTSTATUS status;
//
// Lock the alignment exception database
//
KeEnterCriticalRegion();
ExAcquireResourceExclusive( &PsLoadedModuleResource, TRUE );
exceptionRecord = KipFindAlignmentException( ProgramCounter );
if (exceptionRecord == NULL) {
//
// New exception. Allocate a new record.
//
exceptionRecord = KipAllocateAlignmentExceptionRecord();
if (exceptionRecord == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto exitUnlock;
}
exceptionRecord->ProgramCounter = ProgramCounter;
}
exceptionRecord->Count += 1;
*ExceptionRecord = exceptionRecord;
status = STATUS_SUCCESS;
exitUnlock:
ExReleaseResourceLite( &PsLoadedModuleResource );
KeLeaveCriticalRegion();
return status;
}
PALIGNMENT_EXCEPTION_RECORD
KipAllocateAlignmentExceptionRecord(
VOID
)
/*++
Routine Description:
This is a support routine for KipRecordAlignmentException(). Its purpose
is to locate an available alignment exception record in the per-process
alignment exception list. If none is found, a new alignment exception
table will be allocated and linked into the per-process list.
Arguments:
None.
Return Value:
A pointer to the new alignment exception record if successful, or NULL
otherwise.
--*/
{
PKTHREAD thread;
PKPROCESS process;
PALIGNMENT_EXCEPTION_RECORD exceptionRecord;
PALIGNMENT_EXCEPTION_TABLE exceptionTable;
ULONG exceptionTableCount;
//
// Free exception records have a NULL program counter.
//
exceptionRecord = KipFindAlignmentException( NULL );
if (exceptionRecord == NULL) {
thread = KeGetCurrentThread();
process = thread->ApcState.Process;
//
// Ensure that we haven't exceeded the maximum number of alignment
// exception tables for this process. We could keep a count but we
// do not care about performance here... this code only executes when
// the process is running under a debugger and we're likely about
// to break in.
//
exceptionTableCount = 0;
exceptionTable = process->AlignmentExceptionTable;
while (exceptionTable != NULL) {
exceptionTableCount += 1;
exceptionTable = exceptionTable->Next;
}
if (exceptionTableCount == MAXIMUM_ALIGNMENT_TABLES) {
return NULL;
}
//
// Allocate a new exception table and insert it at the
// head of the per-process list.
//
exceptionTable = ExAllocatePoolWithTag( PagedPool,
sizeof(ALIGNMENT_EXCEPTION_TABLE),
'tpcX' );
if (exceptionTable == NULL) {
return NULL;
}
RtlZeroMemory( exceptionTable, sizeof(ALIGNMENT_EXCEPTION_TABLE) );
exceptionTable->Next = process->AlignmentExceptionTable;
process->AlignmentExceptionTable = exceptionTable;
//
// Allocate the first record in the array
//
exceptionRecord = &exceptionTable->RecordArray[0];
}
return exceptionRecord;
}
PALIGNMENT_EXCEPTION_RECORD
KipFindAlignmentException(
IN PVOID ProgramCounter
)
/*++
Routine Description:
This routine searches the alignment exception tables associated with
the current process for an alignment exception record that matches
the supplied program counter.
Arguments:
ProgramCounter - Supplies the address of the faulting instruction.
Return Value:
A pointer to the matching alignment exception record, or NULL if none
was found.
--*/
{
PKTHREAD thread;
PKPROCESS process;
PALIGNMENT_EXCEPTION_RECORD exceptionRecord;
PALIGNMENT_EXCEPTION_RECORD lastExceptionRecord;
PALIGNMENT_EXCEPTION_TABLE exceptionTable;
thread = KeGetCurrentThread();
process = thread->ApcState.Process;
//
// Walk the singly-linked list of exception tables dangling
// off of the process.
//
exceptionTable = process->AlignmentExceptionTable;
while (exceptionTable != NULL) {
//
// Scan this table looking for a match.
//
exceptionRecord = exceptionTable->RecordArray;
lastExceptionRecord =
&exceptionTable->RecordArray[ ALIGNMENT_RECORDS_PER_TABLE ];
while (exceptionRecord < lastExceptionRecord) {
if (exceptionRecord->ProgramCounter == ProgramCounter) {
//
// Found it.
//
return exceptionRecord;
}
exceptionRecord++;
}
if (ProgramCounter == NULL) {
//
// Caller was looking for a free exception record. If one exists
// it will be in the first table, which was just examined.
//
break;
}
//
// Go look in the next exception table.
//
exceptionTable = exceptionTable->Next;
}
return NULL;
}
#if DBG
//
// The following routines are used to maintain a global database of alignment
// faults that were found in the system. Alignment faults are stored according
// to the name of the image and the offset within that image. In this way an
// existing alignment fault record will be found if it occurs in the same image
// loaded at a different base address in a new process.
//
typedef struct _ALIGNMENT_FAULT_LOCATION {
//
// Pointer to fault image associated with this location
//
PALIGNMENT_FAULT_IMAGE Image;
//
// Linkage for singly-linked list of fault locations associated with the
// same image.
//
PALIGNMENT_FAULT_LOCATION Next;
//
// Offset of the PC address within the image.
//
ULONG_PTR OffsetFromBase;
//
// Number of alignment faults taken at this location.
//
ULONG Count;
} ALIGNMENT_FAULT_LOCATION;
//
// The maximum number of individual alignment fault locations that will be
// tracked.
//
#define MAX_FAULT_LOCATIONS 2048
#define MAX_FAULT_IMAGES 128
ALIGNMENT_FAULT_LOCATION KiAlignmentFaultLocations[ MAX_FAULT_LOCATIONS ];
ULONG KiAlignmentFaultLocationCount = 0;
ALIGNMENT_FAULT_IMAGE KiAlignmentFaultImages[ MAX_FAULT_IMAGES ];
ULONG KiAlignmentFaultImageCount = 0;
KSPIN_LOCK KipGlobalAlignmentDatabaseLock;
VOID
KiCopyLastPathElement(
IN PUNICODE_STRING Source,
IN OUT PULONG StringBufferLen,
OUT PCHAR StringBuffer
);
PALIGNMENT_FAULT_IMAGE
KiFindAlignmentFaultImage(
IN PUCHAR ImageName
);
PLDR_DATA_TABLE_ENTRY
KiFindLoaderDataTableEntry(
IN PLIST_ENTRY ListHead,
IN PVOID ProgramCounter,
IN KPROCESSOR_MODE PreviousMode
);
BOOLEAN
KiIncrementLocationAlignmentFault(
IN PALIGNMENT_FAULT_IMAGE FaultImage,
IN ULONG_PTR OffsetFromBase
);
BOOLEAN
KiGetLdrDataTableInformation(
IN PVOID ProgramCounter,
IN KPROCESSOR_MODE PreviousMode,
IN OUT PULONG ImageNameBufferLength,
OUT PCHAR ImageNameBuffer,
OUT PVOID *ImageBase
)
/*++
Routine Description:
This routine returns the name of the image that contains the supplied
address.
Arguments:
ProgramCounter - Supplies the address for which we would like the
name of the containing image.
PreviousMode - Indicates whether the module is a user or kernel image.
ImageNameBufferLength - Supplies a pointer to a buffer length value. On
entry, this value represents the maximum length of StringBuffer. On
exit, the value is set to the actual number of characters stored.
ImageNameBuffer - Supplies a pointer to the output ANSI string into which
the module name will be placed. This string will not be null
terminated.
ImageBase - Supplies a pointer to a location into which the base address
of the located image is placed.
Return Value:
Returns TRUE if a module was located and its name copied to ImageNameBuffer,
or FALSE otherwise.
--*/
{
PLIST_ENTRY head;
PPEB peb;
PLDR_DATA_TABLE_ENTRY tableEntry;
BOOLEAN status;
//
// Since we may be poking around in user space, be sure to recover
// gracefully from any exceptions thrown.
//
try {
//
// Choose the appropriate module list based on whether the fault
// occured in user- or kernel-space.
//
if (PreviousMode == KernelMode) {
head = &PsLoadedModuleList;
} else {
peb = PsGetCurrentProcess()->Peb;
head = &peb->Ldr->InLoadOrderModuleList;
}
tableEntry = KiFindLoaderDataTableEntry( head,
ProgramCounter,
PreviousMode );
if (tableEntry != NULL) {
//
// The module of interest was located. Copy its name and
// base address to the output paramters.
//
KiCopyLastPathElement( &tableEntry->BaseDllName,
ImageNameBufferLength,
ImageNameBuffer );
*ImageBase = tableEntry->DllBase;
status = TRUE;
} else {
//
// A module containing the supplied program counter could not be
// found.
//
status = FALSE;
}
} except(ExSystemExceptionFilter()) {
status = FALSE;
}
return status;
}
PLDR_DATA_TABLE_ENTRY
KiFindLoaderDataTableEntry(
IN PLIST_ENTRY ListHead,
IN PVOID ProgramCounter,
IN KPROCESSOR_MODE PreviousMode
)
/*++
Routine Description:
This is a support routine for KiGetLdrDataTableInformation. Its purpose is
to search a LDR_DATA_TABLE_ENTRY list, looking for a module that contains
the supplied program counter.
Arguments:
ListHead - Supplies a pointer to the LIST_ENTRY that represents the head of
the LDR_DATA_TABLE_ENTRY list to search.
ProgramCounter - Supplies the code location of the faulting instruction.
Return Value:
Returns a pointer to the matching LDR_DATA_TABLE_ENTRY structure, or NULL
if no match is found.
--*/
{
ULONG nodeNumber;
PLIST_ENTRY next;
PLDR_DATA_TABLE_ENTRY ldrDataTableEntry;
ULONG_PTR imageStart;
ULONG_PTR imageEnd;
//
// Walk the user- or kernel-mode module list. It is up to the caller
// to capture any exceptions as a result of the lists being corrupt.
//
nodeNumber = 0;
next = ListHead;
if (PreviousMode != KernelMode) {
ProbeForReadSmallStructure( next,
sizeof(LIST_ENTRY),
PROBE_ALIGNMENT(LIST_ENTRY) );
}
while (TRUE) {
nodeNumber += 1;
next = next->Flink;
if (next == ListHead || nodeNumber > 10000) {
//
// The end of the module list has been reached, or the
// list has been corrupted with a cycle. Indicate that
// no matching module could be located.
//
ldrDataTableEntry = NULL;
break;
}
ldrDataTableEntry = CONTAINING_RECORD( next,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks );
if (PreviousMode != KernelMode) {
ProbeForReadSmallStructure( ldrDataTableEntry,
sizeof(LDR_DATA_TABLE_ENTRY),
PROBE_ALIGNMENT(LDR_DATA_TABLE_ENTRY) );
}
imageStart = (ULONG_PTR)ldrDataTableEntry->DllBase;
if (imageStart > (ULONG_PTR)ProgramCounter) {
//
// The start of this module is past the program counter,
// keep looking.
//
continue;
}
imageEnd = imageStart + ldrDataTableEntry->SizeOfImage;
if (imageEnd > (ULONG_PTR)ProgramCounter) {
//
// Found a match.
//
break;
}
}
return ldrDataTableEntry;
}
VOID
KiCopyLastPathElement(
IN PUNICODE_STRING Source,
IN OUT PULONG StringBufferLen,
OUT PCHAR StringBuffer
)
/*++
Routine Description:
This routine locates the last path element of the path name represented by
Source and copies it to StringBuffer.
Arguments:
Source - Supplies a pointer to the source UNICODE_STRING path.
StringBufferLen - Supplies a pointer to a buffer length value. On entry,
this value represents the maximum length of StringBuffer. On exit, the
value is set to the actual number of characters stored.
StringBuffer - Supplies a pointer to the output string buffer that is to
contain the last path element. This string is not null terminated.
Return Value:
None.
--*/
{
PWCHAR src;
PCHAR dst;
USHORT charCount;
//
// The name of the module containing the specified address is at
// ldrDataTableEntry->BaseDllName. It might contain just the name,
// or it might contain the whole path.
//
// Start at the end of the module path and work back until one
// of the following is encountered:
//
// - ModuleName->MaximumLength characters
// - the beginning of the module path string
// - a path seperator
//
charCount = Source->Length / sizeof(WCHAR);
src = &Source->Buffer[ charCount ];
charCount = 0;
while (TRUE) {
if (charCount >= *StringBufferLen) {
break;
}
if (src == Source->Buffer) {
break;
}
if (*(src-1) == L'\\') {
break;
}
src--;
charCount++;
}
//
// Now copy the characters into the output string. We do our own
// ansi-to-unicode conversion because the NLS routines cannot be
// called at raised IRQL.
//
dst = StringBuffer;
*StringBufferLen = charCount;
while (charCount > 0) {
*dst++ = (CHAR)(*src++);
charCount--;
}
}
BOOLEAN
KiNewGlobalAlignmentFault(
IN PVOID ProgramCounter,
IN KPROCESSOR_MODE PreviousMode,
OUT PALIGNMENT_FAULT_IMAGE *AlignmentFaultImage
)
/*++
Routine Description:
This routine looks for an existing alignment fault in the global
fault database. A new record is created if a match could not be
found. The count is incremented, and a pointer to the associated
image record is returned.
Arguments:
ProgramCounter - Supplies the code location of the faulting instruction.
PreviousMode - Supplies the execution mode at the time of the fault.
AlignmentFaultImage - Supplies a location into which the pointer to the
associated ALIGNMENT_FAULT_IMAGE structure is placed.
Return Value:
TRUE if an existing alignment fault match was not found, FALSE otherwise.
--*/
{
ULONG_PTR imageOffset;
CHAR imageNameBuffer[ MAX_IMAGE_NAME_CHARS + 1 ];
ULONG imageNameBufferLength;
PCHAR imageName;
PALIGNMENT_FAULT_IMAGE alignmentFaultImage;
BOOLEAN newFault;
BOOLEAN foundLdrDataInfo;
PVOID imageBase;
KIRQL oldIrql;
imageNameBufferLength = MAX_IMAGE_NAME_CHARS;
foundLdrDataInfo = KiGetLdrDataTableInformation( ProgramCounter,
PreviousMode,
&imageNameBufferLength,
imageNameBuffer,
&imageBase );
if (foundLdrDataInfo == FALSE) {
//
// Couldn't find an image for this program counter.
//
imageBase = NULL;
imageName = "Unavailable";
} else {
imageNameBuffer[ imageNameBufferLength ] = '\0';
imageName = imageNameBuffer;
}
//
// Acquire the spinlock at synch level so that we can handle exceptions
// from ISRs
//
imageOffset = (ULONG_PTR)ProgramCounter - (ULONG_PTR)imageBase;
oldIrql = KeAcquireSpinLockRaiseToSynch( &KipGlobalAlignmentDatabaseLock );
alignmentFaultImage = KiFindAlignmentFaultImage( imageName );
if (alignmentFaultImage == NULL) {
//
// Image table must be full
//
newFault = FALSE;
} else {
newFault = KiIncrementLocationAlignmentFault( alignmentFaultImage,
imageOffset );
}
KeReleaseSpinLock( &KipGlobalAlignmentDatabaseLock, oldIrql );
*AlignmentFaultImage = alignmentFaultImage;
return newFault;
}
BOOLEAN
KiIncrementLocationAlignmentFault(
IN PALIGNMENT_FAULT_IMAGE FaultImage,
IN ULONG_PTR OffsetFromBase
)
/*++
Routine Description:
This is a support routine for KiNewGlobalAligmentFault. Its purpose is to
find or create an alignment fault record once the appropriate alignment
fault image has been found or created.
Arguments:
FaultImage - Supplies a pointer to the ALIGNMENT_FAULT_IMAGE associated
with this alignment fault.
OffsetFromBase - Supplies the image offset within the image of the faulting
instruction.
Return Value:
TRUE if an existing alignment fault match was not found, FALSE otherwise.
--*/
{
PALIGNMENT_FAULT_LOCATION faultLocation;
//
// Walk the location table, looking for a match.
//
faultLocation = FaultImage->LocationHead;
while (faultLocation != NULL) {
if (faultLocation->OffsetFromBase == OffsetFromBase) {
faultLocation->Count++;
return FALSE;
}
faultLocation = faultLocation->Next;
}
//
// Could not find a match. Build a new alignment fault record.
//
if (KiAlignmentFaultLocationCount >= MAX_FAULT_LOCATIONS) {
//
// Table is full. Indicate that this is not a new alignment fault.
//
return FALSE;
}
faultLocation = &KiAlignmentFaultLocations[ KiAlignmentFaultLocationCount ];
faultLocation->Image = FaultImage;
faultLocation->Next = FaultImage->LocationHead;
faultLocation->OffsetFromBase = OffsetFromBase;
faultLocation->Count = 1;
FaultImage->LocationHead = faultLocation;
FaultImage->Instances += 1;
KiAlignmentFaultLocationCount++;
return TRUE;
}
PALIGNMENT_FAULT_IMAGE
KiFindAlignmentFaultImage(
IN PUCHAR ImageName
)
/*++
Routine Description:
This is a support routine for KiNewGlobalAlignmentFault. Its purpose is to
walk the global ALIGNMENT_FAULT_IMAGE list looking for an image name that
matches ImageName. If none is found, a new image record is created and
inserted into the list.
Arguments:
ImageName - Supplies a pointer to the ANSI image name.
Return Value:
Returns a pointer to the matching ALIGNMENT_FAULT_IMAGE structure.
--*/
{
PALIGNMENT_FAULT_IMAGE faultImage;
PALIGNMENT_FAULT_IMAGE lastImage;
if (ImageName == NULL || *ImageName == '\0') {
//
// No image name was supplied.
//
return NULL;
}
//
// Walk the image table, looking for a match.
//
faultImage = &KiAlignmentFaultImages[ 0 ];
lastImage = &KiAlignmentFaultImages[ KiAlignmentFaultImageCount ];
while (faultImage < lastImage) {
if (strcmp(ImageName, faultImage->Name) == 0) {
//
// Found it.
//
faultImage->Count += 1;
return faultImage;
}
faultImage += 1;
}
//
// Create a new fault image if there's room
//
if (KiAlignmentFaultImageCount >= MAX_FAULT_IMAGES) {
//
// Table is full up.
//
return NULL;
}
KiAlignmentFaultImageCount += 1;
//
// Zero the image record. The records start out zero-initialized, this
// is in case KiAlignmentFaultImageCount was manually reset to zero via
// the debugger.
//
RtlZeroMemory( faultImage, sizeof(ALIGNMENT_FAULT_IMAGE) );
faultImage->Count = 1;
strcpy( faultImage->Name, ImageName );
return faultImage;
}
#endif // DBG