576 lines
16 KiB
C
576 lines
16 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
kdtrap.c
|
||
|
||
Abstract:
|
||
|
||
This module contains code to implement the target side of the portable
|
||
kernel debugger.
|
||
|
||
Author:
|
||
|
||
Bryan M. Willman (bryanwi) 25-Sep-90
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "kdp.h"
|
||
|
||
//
|
||
// globals
|
||
//
|
||
ULONG KdpPageInAddress;
|
||
WORK_QUEUE_ITEM KdpPageInWorkItem;
|
||
|
||
//
|
||
// externs
|
||
//
|
||
extern PUCHAR KdpCopyDataToStack(PUCHAR, ULONG);
|
||
extern BOOLEAN KdpControlCPressed;
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGEKD, KdpTrap)
|
||
#pragma alloc_text(PAGEKD, KdIsThisAKdTrap)
|
||
#endif
|
||
|
||
|
||
|
||
|
||
#pragma optimize( "", off )
|
||
VOID
|
||
KdpPageInData (
|
||
IN PUCHAR volatile DataAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to page in data at the supplied address.
|
||
It is called either directly from KdpTrap() or from a worker
|
||
thread that is queued by KdpTrap().
|
||
|
||
Arguments:
|
||
|
||
DataAddress - Supplies a pointer to the data to be paged in.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
if (MmIsSystemAddressAccessable(DataAddress)) {
|
||
UCHAR c = *DataAddress;
|
||
DataAddress = &c;
|
||
}
|
||
KdpControlCPending = TRUE;
|
||
}
|
||
#pragma optimize( "", on )
|
||
|
||
|
||
|
||
BOOLEAN
|
||
KdpTrap (
|
||
IN PKTRAP_FRAME TrapFrame,
|
||
IN PKEXCEPTION_FRAME ExceptionFrame,
|
||
IN PEXCEPTION_RECORD ExceptionRecord,
|
||
IN PCONTEXT ContextRecord,
|
||
IN KPROCESSOR_MODE PreviousMode,
|
||
IN BOOLEAN SecondChance
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called whenever a exception is dispatched and the kernel
|
||
debugger is active.
|
||
|
||
Arguments:
|
||
|
||
TrapFrame - Supplies a pointer to a trap frame that describes the
|
||
trap.
|
||
|
||
ExceptionFrame - Supplies a pointer to a exception frame that describes
|
||
the trap.
|
||
|
||
ExceptionRecord - Supplies a pointer to an exception record that
|
||
describes the exception.
|
||
|
||
ContextRecord - Supplies the context at the time of the exception.
|
||
|
||
PreviousMode - Supplies the previous processor mode.
|
||
|
||
SecondChance - Supplies a boolean value that determines whether this is
|
||
the second chance (TRUE) that the exception has been raised.
|
||
|
||
Return Value:
|
||
|
||
A value of TRUE is returned if the exception is handled. Otherwise a
|
||
value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
BOOLEAN Completion = FALSE;
|
||
BOOLEAN Enable;
|
||
BOOLEAN UnloadSymbols = FALSE;
|
||
ULONG RetValue;
|
||
STRING String, ReplyString;
|
||
PUCHAR Buffer;
|
||
PKD_SYMBOLS_INFO SymbolInfo;
|
||
PVOID SavedEsp;
|
||
PKPRCB Prcb;
|
||
|
||
_asm {
|
||
//
|
||
// Save esp on ebp frame so c-runtime registers are restored correctly
|
||
//
|
||
|
||
mov SavedEsp, esp
|
||
}
|
||
|
||
//
|
||
// Print, Prompt, Load symbols, Unload symbols, are all special
|
||
// cases of STATUS_BREAKPOINT
|
||
//
|
||
|
||
re_enter_debugger:
|
||
if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
|
||
(ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK)) {
|
||
|
||
//
|
||
// We have one of the support functions.
|
||
//
|
||
|
||
if (KdDebuggerNotPresent &&
|
||
ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_PROMPT) {
|
||
ContextRecord->Eip++;
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
//
|
||
// Since some of these functions can be entered from user mode,
|
||
// we hold off entering the debugger until the user mode buffers
|
||
// are copied. (Because they may not be present in memory, and
|
||
// they must be paged in before we raise Irql to the
|
||
// Highest level.)
|
||
//
|
||
//
|
||
|
||
switch (ExceptionRecord->ExceptionInformation[0]) {
|
||
|
||
//
|
||
// ExceptionInformation[1] is PSTRING to print
|
||
//
|
||
|
||
case BREAKPOINT_PRINT:
|
||
|
||
if (PreviousMode == UserMode) {
|
||
|
||
//
|
||
// Move user mode parameters to kernel stack
|
||
//
|
||
|
||
try {
|
||
String = *((PSTRING)ExceptionRecord->ExceptionInformation[1]);
|
||
if (String.Length > 512) {
|
||
break;
|
||
}
|
||
ProbeForRead(String.Buffer, String.Length, sizeof(UCHAR));
|
||
String.Buffer =
|
||
KdpCopyDataToStack(String.Buffer, String.Length);
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// If an exception occurs then don't handle
|
||
// this DebugService request.
|
||
//
|
||
|
||
break;
|
||
}
|
||
|
||
} else {
|
||
String = *((PSTRING)ExceptionRecord->ExceptionInformation[1]);
|
||
}
|
||
Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
|
||
if (KdpPrintString(&String)) {
|
||
ContextRecord->Eax = (ULONG)(STATUS_BREAKPOINT);
|
||
} else {
|
||
ContextRecord->Eax = STATUS_SUCCESS;
|
||
}
|
||
ContextRecord->Eip++;
|
||
KdExitDebugger(Enable);
|
||
Completion = TRUE;
|
||
break;
|
||
|
||
//
|
||
// ExceptionInformation[1] is prompt string,
|
||
// ExceptionInformation[2] is return string
|
||
//
|
||
|
||
case BREAKPOINT_PROMPT:
|
||
if (PreviousMode == UserMode) {
|
||
|
||
//
|
||
// Move user mode parameters to kernel stack
|
||
//
|
||
|
||
|
||
try {
|
||
String = *((PSTRING)ExceptionRecord->ExceptionInformation[1]);
|
||
if (String.Length > 512) {
|
||
break;
|
||
}
|
||
ProbeForRead(String.Buffer, String.Length, sizeof(CHAR));
|
||
String.Buffer =
|
||
KdpCopyDataToStack(String.Buffer, String.Length);
|
||
|
||
ReplyString = *((PSTRING)ExceptionRecord->ExceptionInformation[2]);
|
||
if (ReplyString.MaximumLength > 512) {
|
||
break;
|
||
}
|
||
ProbeForWrite(ReplyString.Buffer,
|
||
ReplyString.MaximumLength,
|
||
sizeof(CHAR));
|
||
Buffer = ReplyString.Buffer;
|
||
ReplyString.Buffer =
|
||
KdpCopyDataToStack(
|
||
ReplyString.Buffer,
|
||
ReplyString.MaximumLength
|
||
);
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// If an exception occurs then don't handle
|
||
// this DebugService request.
|
||
//
|
||
|
||
break;
|
||
}
|
||
} else {
|
||
String = *((PSTRING)ExceptionRecord->ExceptionInformation[1]);
|
||
ReplyString = *((PSTRING)ExceptionRecord->ExceptionInformation[2]);
|
||
}
|
||
|
||
//
|
||
// Prompt, keep prompting until no breakin seen.
|
||
//
|
||
|
||
Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
|
||
do {
|
||
RetValue = KdpPromptString(&String, &ReplyString);
|
||
} while (RetValue == TRUE);
|
||
|
||
ContextRecord->Eax = ReplyString.Length;
|
||
ContextRecord->Eip++;
|
||
KdExitDebugger(Enable);
|
||
|
||
if (PreviousMode == UserMode) {
|
||
|
||
//
|
||
// Restore user mode return parameters
|
||
//
|
||
|
||
try {
|
||
KdpQuickMoveMemory(
|
||
Buffer,
|
||
ReplyString.Buffer,
|
||
ReplyString.Length
|
||
);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// If an exception occurs then don't handle
|
||
// this DebugService request.
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
Completion = TRUE;
|
||
break;
|
||
|
||
//
|
||
// ExceptionInformation[1] is file name of new module
|
||
// ExceptionInformaiton[2] is the base of the dll
|
||
//
|
||
|
||
case BREAKPOINT_UNLOAD_SYMBOLS:
|
||
UnloadSymbols = TRUE;
|
||
|
||
//
|
||
// Fall through
|
||
//
|
||
|
||
case BREAKPOINT_LOAD_SYMBOLS:
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
break;
|
||
}
|
||
|
||
Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
|
||
SymbolInfo = (PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2];
|
||
|
||
Completion = KdpReportLoadSymbolsStateChange(
|
||
(PSTRING)ExceptionRecord->ExceptionInformation[1],
|
||
SymbolInfo,
|
||
UnloadSymbols,
|
||
ContextRecord
|
||
);
|
||
ContextRecord->Eip++;
|
||
KdExitDebugger(Enable);
|
||
break;
|
||
|
||
//
|
||
// Unknown command
|
||
//
|
||
|
||
default:
|
||
// return FALSE
|
||
break;
|
||
}
|
||
|
||
} else {
|
||
|
||
if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) ||
|
||
(ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) ||
|
||
(NtGlobalFlag & FLG_STOP_ON_EXCEPTION) ||
|
||
SecondChance) {
|
||
|
||
if (!SecondChance &&
|
||
(ExceptionRecord->ExceptionCode == STATUS_PORT_DISCONNECTED ||
|
||
NT_SUCCESS( ExceptionRecord->ExceptionCode )
|
||
)
|
||
) {
|
||
//
|
||
// User does not really want to see these either.
|
||
// so do NOT report it to debugger.
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Report state change to kernel debugger on host
|
||
//
|
||
|
||
|
||
Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
|
||
Prcb = KeGetCurrentPrcb();
|
||
|
||
KiSaveProcessorControlState(&Prcb->ProcessorState);
|
||
RtlCopyMemory(&Prcb->ProcessorState.ContextFrame,
|
||
ContextRecord,
|
||
sizeof (CONTEXT));
|
||
|
||
Completion = KdpReportExceptionStateChange(
|
||
ExceptionRecord,
|
||
&Prcb->ProcessorState.ContextFrame,
|
||
SecondChance
|
||
);
|
||
|
||
RtlCopyMemory(ContextRecord,
|
||
&Prcb->ProcessorState.ContextFrame,
|
||
sizeof (CONTEXT) );
|
||
|
||
KiRestoreProcessorControlState(&KeGetCurrentPrcb()->ProcessorState);
|
||
|
||
KdExitDebugger(Enable);
|
||
|
||
//
|
||
// check to see if the user of the remote debugger
|
||
// requested memory to be paged in
|
||
//
|
||
if (KdpPageInAddress) {
|
||
|
||
if (KeGetCurrentIrql() <= APC_LEVEL) {
|
||
|
||
//
|
||
// if the IQRL is below DPC level then cause
|
||
// the page fault to occur and then re-enter
|
||
// the debugger. this whole process is transparent
|
||
// to the user.
|
||
//
|
||
KdpPageInData( (PUCHAR)KdpPageInAddress );
|
||
KdpPageInAddress = 0;
|
||
KdpControlCPending = FALSE;
|
||
goto re_enter_debugger;
|
||
|
||
} else {
|
||
|
||
//
|
||
// we cannot take a page fault
|
||
// here so a worker item is queued to take the
|
||
// page fault. after the worker item takes the
|
||
// page fault it sets the contol-c flag so that
|
||
// the user re-enters the debugger just as if
|
||
// control-c was pressed.
|
||
//
|
||
if (KdpControlCPressed) {
|
||
ExInitializeWorkItem(
|
||
&KdpPageInWorkItem,
|
||
(PWORKER_THREAD_ROUTINE) KdpPageInData,
|
||
(PVOID) KdpPageInAddress
|
||
);
|
||
ExQueueWorkItem( &KdpPageInWorkItem, DelayedWorkQueue );
|
||
KdpPageInAddress = 0;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
KdpControlCPressed = FALSE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is real exception that user doesn't want to see,
|
||
// so do NOT report it to debugger.
|
||
//
|
||
|
||
// return FALSE;
|
||
}
|
||
}
|
||
|
||
_asm {
|
||
mov esp, SavedEsp
|
||
}
|
||
return Completion;
|
||
|
||
UNREFERENCED_PARAMETER(PreviousMode);
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
KdIsThisAKdTrap (
|
||
IN PEXCEPTION_RECORD ExceptionRecord,
|
||
IN PCONTEXT ContextRecord,
|
||
IN KPROCESSOR_MODE PreviousMode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called whenever a user-mode exception occurs and
|
||
it might be a kernel debugger exception (Like DbgPrint/DbgPrompt ).
|
||
|
||
Arguments:
|
||
|
||
ExceptionRecord - Supplies a pointer to an exception record that
|
||
describes the exception.
|
||
|
||
ContextRecord - Supplies the context at the time of the exception.
|
||
|
||
PreviousMode - Supplies the previous processor mode.
|
||
|
||
Return Value:
|
||
|
||
A value of TRUE is returned if this is for the kernel debugger.
|
||
Otherwise, a value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
|
||
(ExceptionRecord->NumberParameters > 0) &&
|
||
(ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK)) {
|
||
|
||
return TRUE;
|
||
} else {
|
||
return FALSE;
|
||
}
|
||
UNREFERENCED_PARAMETER(ContextRecord);
|
||
}
|
||
|
||
BOOLEAN
|
||
KdpCheckTracePoint(
|
||
IN PEXCEPTION_RECORD ExceptionRecord,
|
||
IN OUT PCONTEXT ContextRecord
|
||
);
|
||
|
||
VOID
|
||
SaveSymLoad(
|
||
IN PSTRING PathName,
|
||
IN PVOID BaseOfDll,
|
||
IN LONG ProcessId,
|
||
IN BOOLEAN UnloadSymbols
|
||
);
|
||
|
||
BOOLEAN
|
||
KdpStub (
|
||
IN PKTRAP_FRAME TrapFrame,
|
||
IN PKEXCEPTION_FRAME ExceptionFrame,
|
||
IN PEXCEPTION_RECORD ExceptionRecord,
|
||
IN PCONTEXT ContextRecord,
|
||
IN KPROCESSOR_MODE PreviousMode,
|
||
IN BOOLEAN SecondChance
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine provides a kernel debugger stub routine to catch debug
|
||
prints in a checked system when the kernel debugger is not active.
|
||
|
||
Arguments:
|
||
|
||
TrapFrame - Supplies a pointer to a trap frame that describes the
|
||
trap.
|
||
|
||
ExceptionFrame - Supplies a pointer to a exception frame that describes
|
||
the trap.
|
||
|
||
ExceptionRecord - Supplies a pointer to an exception record that
|
||
describes the exception.
|
||
|
||
ContextRecord - Supplies the context at the time of the exception.
|
||
|
||
PreviousMode - Supplies the previous processor mode.
|
||
|
||
SecondChance - Supplies a boolean value that determines whether this is
|
||
the second chance (TRUE) that the exception has been raised.
|
||
|
||
Return Value:
|
||
|
||
A value of TRUE is returned if the exception is handled. Otherwise a
|
||
value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
PULONG SymbolArgs;
|
||
//
|
||
// If the breakpoint is a debug print, then return TRUE. Otherwise,
|
||
// return FALSE.
|
||
//
|
||
|
||
if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
|
||
(ExceptionRecord->NumberParameters > 0) &&
|
||
((ExceptionRecord->ExceptionInformation[0] == BREAKPOINT_LOAD_SYMBOLS)||
|
||
(ExceptionRecord->ExceptionInformation[0] == BREAKPOINT_UNLOAD_SYMBOLS)||
|
||
(ExceptionRecord->ExceptionInformation[0] == BREAKPOINT_PRINT))) {
|
||
|
||
ContextRecord->Eip++;
|
||
return(TRUE);
|
||
} else if (KdPitchDebugger == TRUE) {
|
||
return(FALSE);
|
||
} else {
|
||
return(KdpCheckTracePoint(ExceptionRecord,ContextRecord));
|
||
}
|
||
}
|