mirror of https://github.com/lianthony/NT4.0
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.
473 lines
11 KiB
473 lines
11 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
kdbreak.c
|
|
|
|
Abstract:
|
|
|
|
This module implements machine dependent functions to add and delete
|
|
breakpoints from the kernel debugger breakpoint table.
|
|
|
|
Author:
|
|
|
|
David N. Cutler 2-Aug-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "kdp.h"
|
|
|
|
BOOLEAN BreakpointsSuspended = FALSE;
|
|
|
|
//
|
|
// Define external references.
|
|
//
|
|
|
|
VOID
|
|
KdSetOwedBreakpoints(
|
|
VOID
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGEKD, KdpAddBreakpoint)
|
|
#pragma alloc_text(PAGEKD, KdpDeleteBreakpoint)
|
|
#pragma alloc_text(PAGEKD, KdpDeleteBreakpointRange)
|
|
#pragma alloc_text(PAGEKD, KdpSuspendBreakpoint)
|
|
#pragma alloc_text(PAGEKD, KdpSuspendAllBreakpoints)
|
|
#pragma alloc_text(PAGEKD, KdpRestoreAllBreakpoints)
|
|
#endif
|
|
|
|
|
|
ULONG
|
|
KdpAddBreakpoint (
|
|
IN PVOID Address
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds an entry to the breakpoint table and returns a handle
|
|
to the breakpoint table entry.
|
|
|
|
Arguments:
|
|
|
|
Address - Supplies the address where to set the breakpoint.
|
|
|
|
Return Value:
|
|
|
|
A value of zero is returned if the specified address is already in the
|
|
breakpoint table, there are no free entries in the breakpoint table, the
|
|
specified address is not correctly aligned, or the specified address is
|
|
not valid. Otherwise, the index of the assigned breakpoint table entry
|
|
plus one is returned as the function value.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KDP_BREAKPOINT_TYPE Content;
|
|
ULONG Handle;
|
|
ULONG Index;
|
|
BOOLEAN Accessible;
|
|
|
|
//
|
|
// If the specified address is not properly aligned, then return zero.
|
|
//
|
|
|
|
if (((ULONG)Address & KDP_BREAKPOINT_ALIGN) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the instruction to be replaced. If the instruction cannot be read,
|
|
// then mark breakpoint as not accessible.
|
|
//
|
|
|
|
if (KdpMoveMemory(
|
|
(PCHAR)&Content,
|
|
(PCHAR)Address,
|
|
sizeof(KDP_BREAKPOINT_TYPE) ) != sizeof(KDP_BREAKPOINT_TYPE)) {
|
|
Accessible = FALSE;
|
|
} else {
|
|
Accessible = TRUE;
|
|
}
|
|
|
|
//
|
|
// If the specified address is not write accessible, then return zero.
|
|
//
|
|
|
|
if (Accessible && MmDbgWriteCheck((PVOID)Address) == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Search the breakpoint table for a free entry and check if the specified
|
|
// address is already in the breakpoint table.
|
|
//
|
|
|
|
for (Index = 0; Index < BREAKPOINT_TABLE_SIZE; Index += 1) {
|
|
if (KdpBreakpointTable[Index].Flags == 0) {
|
|
Handle = Index + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a free entry was found, then write breakpoint and return the handle
|
|
// value plus one. Otherwise, return zero.
|
|
//
|
|
|
|
if (Handle != 0) {
|
|
if ( Accessible ) {
|
|
KdpBreakpointTable[Handle - 1].Address = Address;
|
|
KdpBreakpointTable[Handle - 1].Content = Content;
|
|
KdpBreakpointTable[Handle - 1].Flags = KD_BREAKPOINT_IN_USE;
|
|
if (Address < (PVOID)GLOBAL_BREAKPOINT_LIMIT) {
|
|
KdpBreakpointTable[Handle - 1].DirectoryTableBase =
|
|
KeGetCurrentThread()->ApcState.Process->DirectoryTableBase[0];
|
|
}
|
|
KdpMoveMemory(
|
|
(PCHAR)Address,
|
|
(PCHAR)&KdpBreakpointInstruction,
|
|
sizeof(KDP_BREAKPOINT_TYPE)
|
|
);
|
|
} else {
|
|
KdpBreakpointTable[Handle - 1].Address = Address;
|
|
KdpBreakpointTable[Handle - 1].Flags = KD_BREAKPOINT_NEEDS_WRITE;
|
|
KdpOweBreakpoint = TRUE;
|
|
if (Address < (PVOID)GLOBAL_BREAKPOINT_LIMIT) {
|
|
KdpBreakpointTable[Handle - 1].DirectoryTableBase =
|
|
KeGetCurrentThread()->ApcState.Process->DirectoryTableBase[0];
|
|
}
|
|
}
|
|
}
|
|
return Handle;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
KdSetOwedBreakpoints(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called after returning from memory management calls
|
|
that may cause an inpage. Its purpose is to store pending
|
|
breakpoints in pages just made valid.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KDP_BREAKPOINT_TYPE Content;
|
|
BOOLEAN Enable;
|
|
LONG Index;
|
|
|
|
//
|
|
// If we don't owe any breakpoints then return
|
|
//
|
|
|
|
if ( !KdpOweBreakpoint ) {
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Freeze all other processors, disable interrupts, and save debug
|
|
// port state.
|
|
//
|
|
|
|
Enable = KdEnterDebugger(NULL, NULL);
|
|
KdpOweBreakpoint = FALSE;
|
|
|
|
//
|
|
// Search the breakpoint table for breakpoints that need to be
|
|
// written.
|
|
//
|
|
|
|
for (Index = 0; Index < BREAKPOINT_TABLE_SIZE; Index += 1) {
|
|
if (KdpBreakpointTable[Index].Flags & KD_BREAKPOINT_NEEDS_WRITE) {
|
|
|
|
//
|
|
// Breakpoint needs to be written
|
|
//
|
|
|
|
if ((KdpBreakpointTable[Index].Address >= (PVOID)GLOBAL_BREAKPOINT_LIMIT) ||
|
|
(KdpBreakpointTable[Index].DirectoryTableBase ==
|
|
KeGetCurrentThread()->ApcState.Process->DirectoryTableBase[0])) {
|
|
|
|
//
|
|
// Check to see if we have write access to the memory
|
|
//
|
|
if (MmDbgWriteCheck((PVOID)KdpBreakpointTable[Index].Address) == NULL) {
|
|
KdpOweBreakpoint = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Breakpoint is global, or it's directory base matches
|
|
//
|
|
|
|
if (KdpMoveMemory(
|
|
(PCHAR)&Content,
|
|
(PCHAR)KdpBreakpointTable[Index].Address,
|
|
sizeof(KDP_BREAKPOINT_TYPE)
|
|
) != sizeof(KDP_BREAKPOINT_TYPE)) {
|
|
KdpOweBreakpoint = TRUE;
|
|
|
|
} else {
|
|
KdpBreakpointTable[Index].Content = Content;
|
|
KdpBreakpointTable[Index].Flags = KD_BREAKPOINT_IN_USE;
|
|
KdpMoveMemory(
|
|
(PCHAR)KdpBreakpointTable[Index].Address,
|
|
(PCHAR)&KdpBreakpointInstruction,
|
|
sizeof(KDP_BREAKPOINT_TYPE)
|
|
);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Breakpoint is local and it's directory base does not match
|
|
//
|
|
|
|
KdpOweBreakpoint = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
KdExitDebugger(Enable);
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
KdpDeleteBreakpoint (
|
|
IN ULONG Handle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deletes an entry from the breakpoint table.
|
|
|
|
Arguments:
|
|
|
|
Handle - Supplies the index plus one of the breakpoint table entry
|
|
which is to be deleted.
|
|
|
|
Return Value:
|
|
|
|
A value of FALSE is returned if the specified handle is not a valid
|
|
value or the breakpoint cannot be deleted because the old instruction
|
|
cannot be replaced. Otherwise, a value of TRUE is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
|
|
//
|
|
// If the specified handle is not value, then return FALSE.
|
|
//
|
|
|
|
if ((Handle == 0) || (Handle > BREAKPOINT_TABLE_SIZE)) {
|
|
return FALSE;
|
|
}
|
|
Handle -= 1;
|
|
|
|
//
|
|
// If the specified breakpoint table is not valid, the return FALSE.
|
|
//
|
|
|
|
if (KdpBreakpointTable[Handle].Flags == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If the breakpoint is suspended, just delete from the table
|
|
//
|
|
|
|
if (KdpBreakpointTable[Handle].Flags & KD_BREAKPOINT_SUSPENDED) {
|
|
KdpBreakpointTable[Handle].Flags = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Replace the instruction contents.
|
|
//
|
|
|
|
if (KdpBreakpointTable[Handle].Flags & KD_BREAKPOINT_IN_USE &&
|
|
KdpBreakpointTable[Handle].Content != KdpBreakpointInstruction) {
|
|
KdpMoveMemory(
|
|
(PCHAR)KdpBreakpointTable[Handle].Address,
|
|
(PCHAR)&KdpBreakpointTable[Handle].Content,
|
|
sizeof(KDP_BREAKPOINT_TYPE)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Delete breakpoint table entry and return TRUE.
|
|
//
|
|
|
|
KdpBreakpointTable[Handle].Flags = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
KdpDeleteBreakpointRange (
|
|
IN PVOID Lower,
|
|
IN PVOID Upper
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deletes all breakpoints falling in a given range
|
|
from the breakpoint table.
|
|
|
|
Arguments:
|
|
|
|
Lower - inclusive lower address of range from which to remove BPs.
|
|
|
|
Upper - include upper address of range from which to remove BPs.
|
|
|
|
Return Value:
|
|
|
|
TRUE if any breakpoints removed, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Index;
|
|
BOOLEAN ReturnStatus = FALSE;
|
|
|
|
//
|
|
// Examine each entry in the table in turn
|
|
//
|
|
|
|
for (Index = 0; Index < BREAKPOINT_TABLE_SIZE; Index++) {
|
|
|
|
if ( (KdpBreakpointTable[Index].Flags & KD_BREAKPOINT_IN_USE) &&
|
|
((KdpBreakpointTable[Index].Address >= Lower) &&
|
|
(KdpBreakpointTable[Index].Address <= Upper))
|
|
) {
|
|
|
|
//
|
|
// Breakpiont is in use and falls in range, clear it.
|
|
//
|
|
|
|
if (KdpBreakpointTable[Index].Content != KdpBreakpointInstruction) {
|
|
KdpMoveMemory(
|
|
(PCHAR)KdpBreakpointTable[Index].Address,
|
|
(PCHAR)&KdpBreakpointTable[Index].Content,
|
|
sizeof(KDP_BREAKPOINT_TYPE)
|
|
);
|
|
}
|
|
|
|
KdpBreakpointTable[Index].Flags = 0;
|
|
ReturnStatus = TRUE;
|
|
}
|
|
}
|
|
return ReturnStatus;
|
|
}
|
|
|
|
VOID
|
|
KdpSuspendBreakpoint (
|
|
ULONG Handle
|
|
)
|
|
{
|
|
Handle -= 1;
|
|
if ( (KdpBreakpointTable[Handle].Flags != 0) &&
|
|
!(KdpBreakpointTable[Handle].Flags & KD_BREAKPOINT_SUSPENDED) ) {
|
|
KdpMoveMemory(
|
|
(PCHAR)KdpBreakpointTable[Handle].Address,
|
|
(PCHAR)&KdpBreakpointTable[Handle].Content,
|
|
sizeof(KDP_BREAKPOINT_TYPE)
|
|
);
|
|
KdpBreakpointTable[Handle].Flags |= KD_BREAKPOINT_SUSPENDED;
|
|
}
|
|
|
|
return;
|
|
|
|
} // KdpSuspendBreakpoint
|
|
|
|
VOID
|
|
KdpSuspendAllBreakpoints (
|
|
VOID
|
|
)
|
|
{
|
|
ULONG index;
|
|
|
|
BreakpointsSuspended = TRUE;
|
|
|
|
for ( index = 0; index < BREAKPOINT_TABLE_SIZE; index++ ) {
|
|
|
|
if ( (KdpBreakpointTable[index].Flags != 0) &&
|
|
!(KdpBreakpointTable[index].Flags & KD_BREAKPOINT_SUSPENDED) ) {
|
|
|
|
//
|
|
// Replace the instruction contents.
|
|
//
|
|
|
|
if ( KdpBreakpointTable[index].Flags & KD_BREAKPOINT_IN_USE &&
|
|
KdpBreakpointTable[index].Content != KdpBreakpointInstruction ) {
|
|
KdpMoveMemory(
|
|
(PCHAR)KdpBreakpointTable[index].Address,
|
|
(PCHAR)&KdpBreakpointTable[index].Content,
|
|
sizeof(KDP_BREAKPOINT_TYPE)
|
|
);
|
|
KdpBreakpointTable[index].Flags |= KD_BREAKPOINT_SUSPENDED;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
} // KdpSuspendAllBreakpoints
|
|
|
|
VOID
|
|
KdpRestoreAllBreakpoints (
|
|
VOID
|
|
)
|
|
{
|
|
ULONG index;
|
|
|
|
BreakpointsSuspended = FALSE;
|
|
|
|
for ( index = 0; index < BREAKPOINT_TABLE_SIZE; index++ ) {
|
|
|
|
if ( KdpBreakpointTable[index].Flags & KD_BREAKPOINT_SUSPENDED ) {
|
|
KdpMoveMemory(
|
|
(PCHAR)KdpBreakpointTable[index].Address,
|
|
(PCHAR)&KdpBreakpointInstruction,
|
|
sizeof(KDP_BREAKPOINT_TYPE)
|
|
);
|
|
KdpBreakpointTable[index].Flags &= ~KD_BREAKPOINT_SUSPENDED;
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
} // KdpRestoreAllBreakpoints
|