mirror of https://github.com/lianthony/NT4.0
7919 lines
226 KiB
7919 lines
226 KiB
/*** ntcmd.c - command processor for NT debugger
|
|
*
|
|
* Copyright <C> 1990, Microsoft Corporation
|
|
*
|
|
* Purpose:
|
|
* To determine if the command processor should be invoked, and
|
|
* if so, to parse and execute those commands entered.
|
|
*
|
|
* Revision History:
|
|
*
|
|
* [-] 20-Mar-1990 Richk Created.
|
|
*
|
|
*************************************************************************/
|
|
|
|
#include <ctype.h>
|
|
#include <xxsetjmp.h>
|
|
#include <string.h>
|
|
#include <crt\io.h>
|
|
#include <fcntl.h>
|
|
#include <sys\types.h>
|
|
#include <sys\stat.h>
|
|
#include <time.h>
|
|
|
|
#ifndef KERNEL
|
|
#include <profile.h>
|
|
#endif
|
|
|
|
// Do the two step to get the "right" definition of va_args...
|
|
|
|
#if defined(I386_HOST) && defined(MIPS)
|
|
#undef MIPS
|
|
#define OLDMIPS
|
|
#define i386
|
|
#endif
|
|
#include <stdarg.h>
|
|
#ifdef OLDMIPS
|
|
#undef i386
|
|
#define MIPS
|
|
#endif
|
|
|
|
#include "ntsdp.h"
|
|
|
|
#include <common.ver>
|
|
|
|
PUCHAR Version_String =
|
|
"\n"
|
|
#ifdef KERNEL
|
|
"Microsoft(R) Windows NT Kernel Debugger\n"
|
|
#else
|
|
#ifdef CHICAGO
|
|
"Microsoft(R) Windows 95/Windows NT User-Mode Debugger, "
|
|
#else
|
|
"Microsoft(R) Windows NT Debugger\n"
|
|
#endif
|
|
#endif
|
|
"Version 4.00\n"
|
|
VER_LEGALCOPYRIGHT_STR
|
|
"\n"
|
|
"\n";
|
|
|
|
#ifdef KERNEL
|
|
extern PSTR CrashFileName;
|
|
ULONG X86BiosBaseAddress;
|
|
#endif
|
|
extern ulong EXPRLastExpression; // from module ntexpr.c
|
|
|
|
#define MAXPCOFFSET 10
|
|
|
|
#ifdef KERNEL
|
|
|
|
#define CRASH_BUGCHECK_CODE 0xDEADDEAD
|
|
|
|
ULONG InitialSP = 0;
|
|
|
|
#endif
|
|
|
|
ULONG EXPRLastDump = 0L;
|
|
jmp_buf cmd_return;
|
|
|
|
CONTEXT RegisterContext;
|
|
VDMCONTEXT VDMRegisterContext;
|
|
|
|
void InitNtCmd(void);
|
|
extern BOOLEAN KdVerbose; // from ntsym.c
|
|
|
|
#ifndef KERNEL
|
|
extern DWORD dwPidToDebug;
|
|
extern PUCHAR DefaultExtDllName;
|
|
NTSD_EXTENSION_APIS NtsdExtensions;
|
|
HANDLE hNtsdDefaultLibrary;
|
|
HANDLE hNtsdUserDefaultLibrary;
|
|
void ProcessStateChange(BOOLEAN, BOOLEAN);
|
|
void ProcessWatchTraceEvent( ADDR );
|
|
#else
|
|
#define BOOL BOOLEAN
|
|
void DelImages(void);
|
|
void ProcessStateChange(ULONG, PDBGKD_CONTROL_REPORT, PCHAR);
|
|
extern ULONG NumberProcessors;
|
|
void ProcessWatchTraceEvent( PDBGKD_TRACE_DATA, ADDR );
|
|
void ClearTraceDataSyms ( void );
|
|
#endif
|
|
|
|
extern PUCHAR LogFileName;
|
|
extern BOOLEAN fLogAppend;
|
|
|
|
extern BOOLEAN UserRegTest(ULONG);
|
|
extern BOOLEAN ReadVirtualMemory(PUCHAR, PUCHAR, ULONG, PULONG);
|
|
extern LocateTextInSource(void *, void*);
|
|
extern BOOLEAN MYOB;
|
|
extern BOOLEAN NotStupid;
|
|
ULONG GetExpressionRoutine(char *CommandString);
|
|
|
|
void error(ULONG);
|
|
void RemoveDelChar(PUCHAR);
|
|
|
|
#ifdef i386
|
|
#include "86reg.h"
|
|
#endif
|
|
// MBH -usoft bug; they had lower case mips which isn't defined
|
|
#ifdef MIPS
|
|
#include "ntreg.h"
|
|
#endif
|
|
#ifdef ALPHA
|
|
#include "ntreg.h"
|
|
#include "alphaops.h"
|
|
#endif
|
|
#ifdef _PPC_
|
|
#include "ntreg.h"
|
|
#endif
|
|
|
|
#if defined(KERNEL)
|
|
extern void SetWaitCtrlHandler(void);
|
|
extern void SetCmdCtrlHandler(void);
|
|
#endif
|
|
|
|
#if defined(i386) && defined(KERNEL)
|
|
extern int G_mode_32;
|
|
#endif
|
|
extern char *InitialCommand;
|
|
BOOLEAN InitialCommandRead;
|
|
|
|
//ULONG HexValue(ULONG);
|
|
ULONGLONG HexValueL(ULONG);
|
|
#define HexValue(a) ((ULONG) HexValueL(a))
|
|
|
|
void HexList(PUCHAR, ULONG *, ULONG);
|
|
void AsciiList(PUCHAR, ULONG *);
|
|
ULONG GetIdList(void);
|
|
void GetRange(PADDR, PULONG, PBOOLEAN, ULONG
|
|
#ifdef i386
|
|
, ULONG
|
|
#endif
|
|
);
|
|
void ProcessCommands(void);
|
|
void OutDisCurrent(BOOLEAN, BOOLEAN);
|
|
|
|
|
|
API_VERSION ApiVersion = { BUILD_MAJOR_VERSION, BUILD_MINOR_VERSION, API_VERSION_NUMBER, 0 };
|
|
|
|
API_VERSION ImagehlpAV;
|
|
|
|
void PrintVersionInformation(void);
|
|
void VerifyVersionInformation(void);
|
|
PUCHAR SetDefaultExtDllName(PUCHAR);
|
|
|
|
void RestoreBrkpts(void);
|
|
BOOLEAN SetBrkpts(void);
|
|
#ifndef KERNEL
|
|
void RemoveProcessBps(PPROCESS_INFO);
|
|
void RemoveThreadBps(PTHREAD_INFO);
|
|
|
|
void SuspendAllThreads(void);
|
|
void ResumeAllThreads(void);
|
|
|
|
void ChangeRegContext(PTHREAD_INFO);
|
|
BOOLEAN FreezeThreads(void);
|
|
void UnfreezeThreads(void);
|
|
|
|
#define MAXNTCALLS 150
|
|
|
|
struct _ntcalls {
|
|
char Name[100];
|
|
ULONG Count;
|
|
} NtCallTable[MAXNTCALLS];
|
|
|
|
ULONG NtCalls;
|
|
BOOLEAN fDeferredDecrement;
|
|
#else
|
|
void ChangeKdRegContext(ULONG, PVOID);
|
|
void InitFirCache(ULONG, PUCHAR);
|
|
//void UpdateFirCache(ULONG);
|
|
#endif
|
|
|
|
#ifndef KERNEL
|
|
BOOLEAN Timing;
|
|
ULONG SystemReportedTime;
|
|
void fnOutputProcessInfo(PPROCESS_INFO);
|
|
void fnOutputThreadInfo(PTHREAD_INFO);
|
|
ULONG fnSetBp(ULONG, UCHAR, UCHAR, PADDR, ULONG, PTHREAD_INFO, PUCHAR);
|
|
void fnGoExecution(PADDR, ULONG, PTHREAD_INFO, BOOLEAN, PADDR);
|
|
void fnStepTrace(PADDR, ULONG, PTHREAD_INFO, BOOLEAN, UCHAR);
|
|
BOOLEAN SetSpecificBrkpt(ULONG);
|
|
#else
|
|
ULONG fnSetBp(ULONG, UCHAR, UCHAR, PADDR, ULONG, BOOLEAN, PUCHAR);
|
|
void fnGoExecution(PADDR, ULONG, PADDR);
|
|
void fnStepTrace(PADDR, ULONG, UCHAR);
|
|
#endif
|
|
|
|
BOOLEAN WatchTrace;
|
|
#ifdef KERNEL
|
|
BOOLEAN WatchWhole;
|
|
BOOLEAN BrkpointsSuspended;
|
|
#endif
|
|
LIST_ENTRY WatchList;
|
|
LONG WatchLevel;
|
|
LONG WatchTRCalls;
|
|
LONG WatchSumIt;
|
|
LONG WatchThreadMismatch;
|
|
typedef struct _WATCH_SYM {
|
|
LIST_ENTRY Links;
|
|
ULONG InstrCount;
|
|
ULONG SubordinateInstrCount;
|
|
LONG Level;
|
|
BOOLEAN fQueued;
|
|
ADDR PCPointer;
|
|
CHAR Symbol[SYMBOLSIZE];
|
|
} WATCH_SYM, *PWATCH_SYM;
|
|
|
|
#define MAXDEPTH 500
|
|
WATCH_SYM WatchSymbols[MAXDEPTH];
|
|
|
|
PWATCH_SYM CurrentWatchSym;
|
|
ADDR WatchTarget;
|
|
ULONG WatchCount;
|
|
#ifndef KERNEL
|
|
ULONG KernelCalls;
|
|
#endif
|
|
BOOLEAN Watching;
|
|
|
|
#ifdef KERNEL
|
|
ULONG BeginCurFunc; // Beginning of current func for Watch
|
|
ULONG EndCurFunc; // End of current func for Watch
|
|
extern ULONG LookupSymbolInDll(PCHAR, PCHAR);
|
|
extern NTSTATUS DbgKdSetSpecialCalls(ULONG, PULONG);
|
|
NTSTATUS DbgKdCrash(DWORD BugCheckCode);
|
|
extern DBGKD_GET_VERSION vs;
|
|
extern BOOLEAN NotStupid;
|
|
#endif
|
|
|
|
|
|
#if defined(ALPHA)
|
|
|
|
#define INCREMENT_LEVEL(buff) (strstr(buffer," jsr"))
|
|
#define DECREMENT_LEVEL(buff) (strstr(buffer," ret ") && strstr(buffer, " ra"))
|
|
#define SYSTEM_CALL(buff) (strstr(buffer," CallSys"))
|
|
#define ADDR_REG A0_REG
|
|
|
|
#elif defined(MIPS)
|
|
|
|
#define INCREMENT_LEVEL(buff) (strstr(buffer," jal"))
|
|
#define DECREMENT_LEVEL(buff) (strstr(buffer," jr ") && strstr(buffer, " ra"))
|
|
#define SYSTEM_CALL(buff) (strstr(buffer," syscall"))
|
|
#define ADDR_REG REGA0
|
|
|
|
#elif defined(_PPC_)
|
|
|
|
#define INCREMENT_LEVEL(buff) (strstr(buffer," bl") || strstr(buffer," blrl"))
|
|
#define DECREMENT_LEVEL(buff) (strstr(buffer," blr"))
|
|
#define SYSTEM_CALL(buff) (strstr(buffer," sc"))
|
|
#define ADDR_REG GPR3
|
|
BOOLEAN ppcPrefix = TRUE;
|
|
|
|
#elif defined(i386)
|
|
|
|
#define INCREMENT_LEVEL(buff) (strstr(buffer," call"))
|
|
#define SYSTEM_CALL(buff) (strstr(buffer," int ") && strstr(buffer, " 2e"))
|
|
#ifdef KERNEL
|
|
#define DECREMENT_LEVEL(buff) (strstr(buffer," ret")||strstr(buffer," iretd"))
|
|
#else
|
|
#define DECREMENT_LEVEL(buff) (strstr(buffer," ret"))
|
|
#endif
|
|
#define ADDR_REG REGEAX
|
|
|
|
#else
|
|
|
|
#error "unknown CPU"
|
|
|
|
#endif
|
|
|
|
void fnBangCmd(PUCHAR, PUCHAR*);
|
|
void fnInteractiveEnterMemory(PADDR, ULONG);
|
|
void fnDotCommand(void);
|
|
void fnEvaluateExp(void);
|
|
void fnAssemble(PADDR);
|
|
void fnUnassemble(PADDR, ULONG, BOOLEAN, BOOLEAN);
|
|
void fnEnterMemory(PADDR, PUCHAR, ULONG);
|
|
void fnChangeBpState(ULONG, UCHAR);
|
|
void fnListBpState(void);
|
|
ULONG fnDumpAsciiMemory(PADDR, ULONG);
|
|
ULONG fnDumpUnicodeMemory (PADDR startaddr, ULONG count);
|
|
void fnDumpByteMemory(PADDR, ULONG);
|
|
void fnDumpWordMemory(PADDR, ULONG);
|
|
void fnDumpDwordMemory(PADDR, ULONG);
|
|
void fnDumpDwordAndCharMemory(PADDR, ULONG);
|
|
void fnDumpListMemory(PADDR, ULONG, ULONG);
|
|
|
|
#ifdef KERNEL
|
|
void fnInputIo(ULONG, UCHAR);
|
|
void fnOutputIo (ULONG, ULONG, UCHAR);
|
|
void fnCache ();
|
|
#endif
|
|
void fnCompareMemory(PADDR, ULONG, PADDR);
|
|
void fnMoveMemory(PADDR, ULONG, PADDR);
|
|
void fnFillMemory(PADDR, ULONG, PUCHAR, ULONG);
|
|
void fnSearchMemory(PADDR, ULONG, PUCHAR, ULONG);
|
|
|
|
void parseScriptCmd(void);
|
|
#ifndef KERNEL
|
|
void parseThreadCmds(void);
|
|
void parseProcessCmds(void);
|
|
ULONG parseBpCmd(BOOLEAN, PTHREAD_INFO);
|
|
void parseGoCmd(PTHREAD_INFO, BOOLEAN);
|
|
void parseStepTrace(PTHREAD_INFO, BOOLEAN, UCHAR);
|
|
#else
|
|
ULONG parseBpCmd(BOOLEAN, BOOLEAN, char);
|
|
void parseGoCmd(void);
|
|
void parseStepTrace(UCHAR);
|
|
#endif
|
|
void parseRegCmd(void);
|
|
|
|
VOID parseStackTrace(PULONG, PADDR*, PULONG, PULONG, PULONG);
|
|
|
|
#if defined(i386) && !defined(KERNEL)
|
|
BOOLEAN fOutputRegs = TRUE; // set if output regs on breakpoint
|
|
#else
|
|
BOOLEAN fOutputRegs; // set if output regs on breakpoint
|
|
#endif
|
|
|
|
void fnSetSuffix(void);
|
|
|
|
BOOLEAN GetMemByte(PADDR, PUCHAR);
|
|
BOOLEAN GetMemWord(PADDR, PUSHORT);
|
|
BOOLEAN GetMemDword(PADDR, PULONG);
|
|
ULONG GetMemString(PADDR, PUCHAR, ULONG);
|
|
BOOLEAN SetMemByte(PADDR, UCHAR);
|
|
BOOLEAN SetMemWord(PADDR, USHORT);
|
|
BOOLEAN SetMemDword(PADDR, ULONG);
|
|
ULONG SetMemString(PADDR, PUCHAR, ULONG);
|
|
void OutputSymAddr(ULONG, BOOLEAN, BOOLEAN);
|
|
static void ExpandUserRegs(PUCHAR);
|
|
#if defined(KERNEL) && defined(i386)
|
|
static DESCRIPTOR_TABLE_ENTRY csDesc;
|
|
#endif
|
|
|
|
NTSTATUS WriteBreakPoint( ADDR, PULONG );
|
|
NTSTATUS RestoreBreakPoint( ULONG );
|
|
|
|
#ifndef KERNEL
|
|
void GetSymbolRoutine(LPVOID, PUCHAR, PULONG);
|
|
DWORD disasmExportRoutine(LPDWORD, LPSTR, BOOL);
|
|
NTSTATUS GetClientId(void);
|
|
#endif
|
|
ULONG disasmRoutine(PULONG, PUCHAR, BOOLEAN);
|
|
|
|
|
|
#if defined(KERNEL)
|
|
extern jmp_buf reboot;
|
|
|
|
#if defined(i386)
|
|
static USHORT lastSel = 0xFFFF;
|
|
static ULONG lastOffset;
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(i386)
|
|
BOOLEAN STtrace;
|
|
ULONG STeip, STesp, STebp;
|
|
extern ULONG GetDregValue(ULONG);
|
|
extern void SetDregValue(ULONG, ULONG);
|
|
#endif
|
|
|
|
void fnLogOpen(BOOLEAN);
|
|
void fnLogClose(void);
|
|
void lprintf(char *);
|
|
|
|
#ifndef KERNEL
|
|
extern PPROCESS_INFO pProcessFromIndex(UCHAR);
|
|
extern PTHREAD_INFO pThreadFromIndex(UCHAR);
|
|
#endif
|
|
|
|
#if defined(KERNEL)
|
|
extern BOOLEAN SymbolOnlyExpr(void);
|
|
extern BOOLEAN DbgKdpBreakIn; // TEMP TEMP TEMP
|
|
#endif
|
|
|
|
int loghandle = -1;
|
|
|
|
#ifdef KERNEL
|
|
BOOLEAN fSwitched;
|
|
extern USHORT NtsdCurrentProcessor;
|
|
extern USHORT SwitchProcessor;
|
|
extern USHORT DefaultProcessor;
|
|
extern void SaveProcessorState(void);
|
|
extern void RestoreProcessorState(void);
|
|
#ifdef i386
|
|
BOOLEAN fSetGlobalDataBrkpts;
|
|
BOOLEAN fDataBrkptsChanged;
|
|
#endif
|
|
#endif
|
|
|
|
UCHAR chCommand[_MAX_PATH]; // top-level command buffer
|
|
|
|
UCHAR chLastCommand[_MAX_PATH]; // last command executed
|
|
|
|
// state variables for top-level command processing
|
|
|
|
PUCHAR pchStart = chCommand; // start of command buffer
|
|
PUCHAR pchCommand = chCommand; // current pointer in command buffer
|
|
ULONG cbPrompt = 8; // size of prompt string
|
|
//jmp_buf *pjbufReturn = &cmd_return; // pointer to error return jmp_buf
|
|
BOOLEAN fJmpBuf; // TEMP TEMP - workaround
|
|
BOOLEAN fDisableErrorPrint;
|
|
BOOLEAN fPhysicalAddress;
|
|
jmp_buf asm_return; // TEMP TEMP
|
|
|
|
UCHAR dumptype = 'b'; // 'a' - ascii; 'b' - byte...
|
|
UCHAR entertype = 'b'; // ...'w' - word; 'd' - dword
|
|
ADDR dumpDefault; // default dump address
|
|
ADDR unasmDefault; // default unassembly address
|
|
ADDR assemDefault; // default assembly address
|
|
ULONG baseDefault = 16; // default input base
|
|
|
|
ULONG EAsize; // 0 if no EA, else size of value
|
|
ULONG EAvalue; // EA value if EAsize is nonzero
|
|
|
|
struct Brkpt {
|
|
char status;
|
|
UCHAR option; // nz for data bp - 0=exec 1=write 3=r/w
|
|
UCHAR size; // nz for data bp - 0=byte 1=word 3=dword
|
|
UCHAR dregindx; // set for enabled data bp - 0 to 3
|
|
BOOLEAN fBpSet;
|
|
#ifdef KERNEL
|
|
BOOLEAN bpInternal; // this is an internal kernel bp
|
|
#endif // KERNEL
|
|
ADDR addr;
|
|
ULONG handle;
|
|
ULONG passcnt;
|
|
ULONG setpasscnt;
|
|
char szcommand[SYMBOLSIZE];
|
|
#ifndef KERNEL
|
|
PPROCESS_INFO pProcess;
|
|
PTHREAD_INFO pThread;
|
|
#endif
|
|
} brkptlist[MAX_NUMBER_OF_BREAKPOINTS];
|
|
|
|
#ifdef BP_CORRUPTION
|
|
IsAddrInBrkptList(
|
|
DWORD Address
|
|
)
|
|
{
|
|
return ((DWORD)brkptlist <= Address) &&
|
|
(Address < (DWORD)(brkptlist + MAX_NUMBER_OF_BREAKPOINTS));
|
|
}
|
|
|
|
//
|
|
// This does not lock the first or last page of the breakpoint list if
|
|
// they share a page with anything else.
|
|
//
|
|
// the point of this is not to perfectly protect the table, but to
|
|
// see if we can catch whoever is writing on it.
|
|
//
|
|
int BpLock;
|
|
DWORD BpOldProtect;
|
|
PVOID BpLockBase;
|
|
DWORD BpLockSize;
|
|
|
|
VOID
|
|
LockBreakpointList(
|
|
VOID
|
|
)
|
|
{
|
|
if (BpLock++ == 0) {
|
|
VirtualProtect(BpLockBase, BpLockSize, PAGE_READONLY, &BpOldProtect);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UnlockBreakpointList(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD xxxProtect;
|
|
if (--BpLock == 0) {
|
|
VirtualProtect(BpLockBase, BpLockSize, BpOldProtect, &xxxProtect);
|
|
} else if (BpLock < 0) {
|
|
dprintf("%s: Unmatched UnlockBreakpointList!!\n", DebuggerName);
|
|
BpLock = 0;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
ValidAddr(
|
|
PADDR paddr
|
|
)
|
|
{
|
|
return
|
|
(((paddr->type & ~(ADDR_32 | FLAT_COMPUTED)) == 0) &&
|
|
(paddr->off == paddr->flat) &&
|
|
#ifdef KERNEL
|
|
((paddr->off & 0x80000000) != 0)
|
|
#else
|
|
((paddr->off & 0x80000000) == 0)
|
|
#endif
|
|
) ||
|
|
( ((paddr->type & ~(ADDR_V86 | ADDR_16 | FLAT_COMPUTED)) == 0) &&
|
|
(paddr->seg != 0) &&
|
|
((paddr->off & 0xffff0000) == 0)
|
|
) ||
|
|
( ((paddr->type & ~(ADDR_1632 | FLAT_COMPUTED)) == 0) &&
|
|
(paddr->seg != 0)
|
|
)
|
|
;
|
|
}
|
|
|
|
VOID
|
|
ValidateBreakpointTable(
|
|
PCHAR file,
|
|
int line
|
|
)
|
|
{
|
|
int i;
|
|
BOOL hit = FALSE;
|
|
struct Brkpt *b = brkptlist;
|
|
|
|
for (i = 0; i < MAX_NUMBER_OF_BREAKPOINTS; i++, b++) {
|
|
BOOL valid = (
|
|
|
|
(b->fBpSet == 0) || (
|
|
|
|
(b->fBpSet == 1) &&
|
|
(b->status == 0 || b->status == 'e' || b->status == 'd') &&
|
|
( (b->option == 255) ||
|
|
((b->option == 0 || b->option == 1 || b->option == 3) &&
|
|
(b->size == 0 || b->size == 1 || b->size == 3))
|
|
) &&
|
|
(b->dregindx == 0 || b->dregindx == 1 || b->dregindx == 2 || b->dregindx == 3) &&
|
|
#ifdef KERNEL
|
|
(b->bpInternal == 0 || b->bpInternal == 1) &&
|
|
#endif // KERNEL
|
|
(ValidAddr(&b->addr)) &&
|
|
/*
|
|
handle &&
|
|
passcnt &&
|
|
setpasscnt &&
|
|
szcommand[60] &&
|
|
#ifndef KERNEL
|
|
pProcess &&
|
|
pThread &&
|
|
#endif
|
|
*/
|
|
1));
|
|
|
|
if (!valid) {
|
|
if (!hit) {
|
|
hit = TRUE;
|
|
dprintf("%s: %d\n", file, line);
|
|
}
|
|
dprintf("**** bad brkptlist --> %d\n", i);
|
|
dprintf("Addr: %04x:%08x %08x type: %08x\n",
|
|
b->addr.seg, b->addr.off, b->addr.flat, b->addr.type);
|
|
dprintf("Status: %c Option: %d Size: %d Reg: %d\n",
|
|
b->status,
|
|
b->option,
|
|
b->size,
|
|
b->dregindx
|
|
);
|
|
dprintf("Set: %d\n", b->fBpSet);
|
|
#ifdef KERNEL
|
|
dprintf("Internal: %d\n", b->bpInternal);
|
|
#endif // KERNEL
|
|
dprintf("Handle: %08x\n", b->handle);
|
|
dprintf("Passcnt: %08x\n", b->passcnt);
|
|
dprintf("SetPasscnt: %08x\n", b->setpasscnt);
|
|
}
|
|
}
|
|
}
|
|
#endif // BP_CORRUPTION
|
|
|
|
extern int fControlC;
|
|
extern int fFlushInput;
|
|
ULONG pageSize;
|
|
|
|
extern void fnSetException(void);
|
|
|
|
UCHAR chSymbolSuffix = 'a'; // suffix to add to symbol if search
|
|
// failure - 'n'-none 'a'-'w'-append
|
|
|
|
UCHAR cmdState = 'i'; // state of present command processing
|
|
// 'i'-init; 'g'-go; 't'-trace
|
|
// 'p'-step; 'c'-cmd
|
|
|
|
#ifndef KERNEL
|
|
extern UCHAR oldcmdState;
|
|
PTHREAD_INFO pThreadCmd; // pointer to thread to qualify any
|
|
// temporary breakpoint using 'g', 't', 'p'
|
|
BOOLEAN fFreeze; // TRUE if suspending all threads except
|
|
// that pointed by pThreadCmd
|
|
#endif
|
|
#if defined(i386)
|
|
#define CURRENT_STACK GetRegValue(REGESP)
|
|
#endif
|
|
#if defined(MIPS)
|
|
#define CURRENT_STACK GetRegValue(REGSP)
|
|
#endif
|
|
#if defined(ALPHA)
|
|
#define CURRENT_STACK GetRegValue(SP_REG)
|
|
#endif
|
|
#if defined(_PPC_)
|
|
#define CURRENT_STACK GetRegValue(GPR1)
|
|
#endif
|
|
|
|
#if defined(ALPHA) || defined(PPC) || defined(_MIPS_)
|
|
extern opTableInit();
|
|
extern BOOLEAN NeedUpper(ULONG, PCONTEXT);
|
|
extern void printQuadReg();
|
|
extern void printFloatReg();
|
|
#endif
|
|
|
|
ADDR steptraceaddr; // defined if cmdState is 't' or 'p'
|
|
ULONG steptracehandle; // handle of trace breakpoint
|
|
ULONG steptracepasscnt; // passcount of trace breakpoint
|
|
BOOLEAN fStepTraceBpSet; // TRUE if trace breakpoint set
|
|
#ifndef KERNEL
|
|
PPROCESS_INFO pProcessStepBrkpt; // process of trace breakpoint
|
|
#endif
|
|
ULONG steptracelow; // low offset for range step/trace
|
|
ULONG steptracehigh; // high offset for range step/trace
|
|
|
|
#ifndef i386
|
|
ADDR deferaddr; // address of deferred breakpoint
|
|
#endif
|
|
ULONG deferhandle; // handle of deferred breakpoint
|
|
BOOLEAN fDeferBpSet; // TRUE if trace breakpoint set
|
|
BOOLEAN fDeferDefined; // TRUE if deferred breakpoint is used
|
|
#ifndef KERNEL
|
|
PPROCESS_INFO pProcessDeferBrkpt; // process of deferred breakpoint
|
|
#endif
|
|
UCHAR chExceptionHandle; // defined only when cmdState == 'g'
|
|
// values are: 'n' - not handled
|
|
// 'h' - handled
|
|
|
|
// defined if cmdState is 'g'
|
|
ULONG gocnt; // number of "go" breakpoints active
|
|
struct GoBrkpt {
|
|
ADDR addr; // address of breakpoint
|
|
ULONG handle; // handle of breakpoint
|
|
BOOLEAN fBpSet; // TRUE if breakpoint is set
|
|
} golist[10];
|
|
#ifndef KERNEL
|
|
PPROCESS_INFO pProcessGoBrkpt; // process of "go" breakpoints
|
|
#endif
|
|
|
|
static SHORT lastSelector = -1;
|
|
static ULONG lastBaseOffset;
|
|
|
|
extern PUCHAR pszScriptFile;
|
|
static FILE *streamCmd;
|
|
static void igrep(void);
|
|
BOOLEAN fPointerExpression;
|
|
|
|
#ifdef KERNEL
|
|
VOID
|
|
HandleBPWithStatus(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG Status = (ULONG)GetRegValue(ADDR_REG);
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
|
|
switch(Status) {
|
|
|
|
case DBG_STATUS_CONTROL_C:
|
|
case DBG_STATUS_SYSRQ:
|
|
if (MYOB || NotStupid) {
|
|
return;
|
|
}
|
|
|
|
GetConsoleScreenBufferInfo(ConsoleOutputHandle,&csbi);
|
|
SetConsoleTextAttribute(ConsoleOutputHandle, FOREGROUND_RED | FOREGROUND_INTENSITY);
|
|
|
|
dprintf("*******************************************************************************\n");
|
|
dprintf("* *\n");
|
|
if (Status == DBG_STATUS_SYSRQ) {
|
|
dprintf("* You are seeing this message because you pressed the SysRq/PrintScreen *\n");
|
|
dprintf("* key on your test machine's keyboard. *\n");
|
|
}
|
|
dprintf("* *\n");
|
|
if (Status == DBG_STATUS_CONTROL_C) {
|
|
dprintf("* You are seeing this message because you pressed CTRL+C on your debugger *\n");
|
|
dprintf("* machine's keyboard. *\n");
|
|
}
|
|
dprintf("* *\n");
|
|
dprintf("* THIS IS NOT A BUG OR A SYSTEM CRASH *\n");
|
|
dprintf("* *\n");
|
|
dprintf("* If you did not intend to break into the debugger, press the \"g\" key, then *\n");
|
|
dprintf("* press the \"Enter\" key now. This message might immediately reappear. If it *\n");
|
|
dprintf("* does, press \"g\" and \"Enter\" again. *\n");
|
|
dprintf("* *\n");
|
|
dprintf("*******************************************************************************\n");
|
|
|
|
SetConsoleTextAttribute(ConsoleOutputHandle, csbi.wAttributes);
|
|
|
|
return;
|
|
|
|
case DBG_STATUS_BUGCHECK_FIRST:
|
|
case DBG_STATUS_BUGCHECK_SECOND:
|
|
case DBG_STATUS_FATAL:
|
|
return;
|
|
}
|
|
}
|
|
#endif // KERNEL
|
|
|
|
VOID
|
|
FreeWatchTrace()
|
|
{
|
|
#if KERNEL // !!! Why does BJB have 0?
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
PWATCH_SYM pSym;
|
|
|
|
ListHead = &WatchList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
pSym = CONTAINING_RECORD( ListNext, WATCH_SYM, Links );
|
|
ListNext = pSym->Links.Flink;
|
|
free(pSym);
|
|
}
|
|
#endif // KERNEL
|
|
}
|
|
|
|
VOID
|
|
PrintWatchSym(
|
|
IN PWATCH_SYM Sym
|
|
)
|
|
{
|
|
LONG i;
|
|
|
|
#ifdef KERNEL
|
|
//
|
|
// The kernel/non-kernel distinction is because the user level
|
|
// ntsd code doesn't handle the watch symbol chain correctly, so
|
|
// it doesn't keep track of "SubordinateInstrCount." If someone
|
|
// fixes it, then this can be put in in user mode, too.
|
|
//
|
|
|
|
dprintf("%4ld %4ld",Sym->InstrCount,Sym->SubordinateInstrCount);
|
|
#define MAX_INDENT_LEVEL 11
|
|
|
|
#else
|
|
|
|
Profile(&ps_ProfileTrace, Sym->Symbol, Sym->InstrCount, Sym->Level);
|
|
|
|
WatchSumIt += Sym->InstrCount;
|
|
dprintf("%4ld [%3ld] ",Sym->InstrCount, Sym->Level);
|
|
#define MAX_INDENT_LEVEL 12
|
|
|
|
#endif
|
|
|
|
if (Sym->Level < MAX_INDENT_LEVEL) {
|
|
for (i=0; i<Sym->Level; i++) {
|
|
dprintf(" ");
|
|
}
|
|
} else {
|
|
for (i=0; i<MAX_INDENT_LEVEL + 1; i++) {
|
|
dprintf(" ");
|
|
}
|
|
}
|
|
dprintf(" %s\n",Sym->Symbol);
|
|
}
|
|
|
|
/*** InitNtCmd - one-time debugger initialization
|
|
*
|
|
* Purpose:
|
|
* To perform the one-time initialization of global
|
|
* variables used in the debugger.
|
|
*
|
|
* Input:
|
|
* None.
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void InitNtCmd (void)
|
|
{
|
|
ULONG count;
|
|
|
|
VerifyVersionInformation();
|
|
|
|
for (count = 0; count < MAX_NUMBER_OF_BREAKPOINTS; count++) {
|
|
brkptlist[count].status = '\0';
|
|
brkptlist[count].fBpSet = FALSE;
|
|
}
|
|
|
|
#ifdef BP_CORRUPTION
|
|
BpLockBase = (PVOID) ((((DWORD)brkptlist) + pageSize - 1) & ~(pageSize - 1));
|
|
BpLockSize = ((DWORD)(brkptlist + MAX_NUMBER_OF_BREAKPOINTS) & ~(pageSize - 1)) - (DWORD)BpLockBase;
|
|
|
|
LockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
|
|
for (count = 0; count < 10; count++) {
|
|
golist[count].fBpSet = FALSE;
|
|
}
|
|
|
|
#ifdef ALPHA
|
|
opTableInit();
|
|
#endif
|
|
|
|
#ifndef KERNEL
|
|
BrkptInit();
|
|
#endif
|
|
fStepTraceBpSet = FALSE;
|
|
fDeferBpSet = FALSE;
|
|
steptracelow = (ULONG)-1;
|
|
chCommand[0] = '\0';
|
|
#if defined(KERNEL)
|
|
if (InitialCommand != NULL) {
|
|
strcpy(chCommand, InitialCommand);
|
|
InitialCommandRead = FALSE;
|
|
}
|
|
#endif
|
|
|
|
if (!pszScriptFile) {
|
|
pszScriptFile = "ntsd.ini";
|
|
}
|
|
if (!(streamCmd = fopen(pszScriptFile, "r"))) {
|
|
#ifdef KERNEL
|
|
if (CrashFileName) {
|
|
streamCmd = stdin;
|
|
} else {
|
|
streamCmd = NULL;
|
|
}
|
|
#else
|
|
streamCmd = stdin;
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
#define BRKPTHIT 1
|
|
#define GOLISTHIT 2
|
|
#define STEPTRACEHIT 4
|
|
|
|
/*** ProcessStateChange - process each debugger system event
|
|
*
|
|
* Purpose:
|
|
* This routine is called for each NT debugger event, such
|
|
* as process creation or termination, thread creation or
|
|
* termination, or breakpoints. The program context for both
|
|
* registers and memory is first made valid and accessible.
|
|
* The command processor state in CmdState, the current
|
|
* program counter in pcaddr, and the values of passcounts
|
|
* are used to determine if one or more breakpoint conditions
|
|
* have been met. If so, the command processor routine
|
|
* ProcessCommands is called. Once ProcessCommands is finished
|
|
* through a go, step, or trace command, the program context
|
|
* is restored and the routine returns.
|
|
*
|
|
* Input:
|
|
* None.
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void ProcessStateChange (
|
|
#ifndef KERNEL
|
|
BOOLEAN fBrkpt,
|
|
BOOLEAN fBrkptContinue
|
|
#else
|
|
ULONG pcEntryAddr,
|
|
PDBGKD_CONTROL_REPORT pCtlReport,
|
|
PCHAR traceData
|
|
#endif
|
|
)
|
|
{
|
|
ULONG count;
|
|
BOOLEAN fBrkptHit = 0;
|
|
BOOLEAN fPassThrough = FALSE;
|
|
BOOLEAN fHardBrkpt = FALSE;
|
|
BOOLEAN fLoopError = FALSE;
|
|
ADDR pcaddr;
|
|
UCHAR bBrkptInstr[4];
|
|
BOOLEAN fSymbol;
|
|
BOOLEAN fOutputDisCurrent = TRUE;
|
|
BOOLEAN WatchStepOver = FALSE;
|
|
|
|
#ifndef KERNEL
|
|
UCHAR Symbol[MAX_SYMBOL_LEN];
|
|
UCHAR prevCmdState;
|
|
ULONG displacement;
|
|
LONG cBrkptType;
|
|
PPROCESS_INFO pProcessOrig;
|
|
static BOOLEAN fProfilingBrkptFound = FALSE;
|
|
static ULONG ProfilingBrkptIndex;
|
|
#endif
|
|
|
|
|
|
#ifdef BP_CORRUPTION
|
|
ValidateBreakpointTable(__FILE__, __LINE__);
|
|
#endif BP_CORRUPTION
|
|
|
|
lastSelector = -1; // Prevent stale selector values
|
|
|
|
if ( Flat(steptraceaddr) != -1 ) {
|
|
NotFlat(steptraceaddr);
|
|
ComputeFlatAddress(&steptraceaddr,NULL);
|
|
}
|
|
|
|
#ifdef KERNEL
|
|
ChangeKdRegContext(pcEntryAddr, pCtlReport);
|
|
ClearTraceFlag();
|
|
#ifdef i386
|
|
fDataBrkptsChanged = FALSE;
|
|
fVm86 = VM86(GetRegValue(REGEFL));
|
|
if (!fVm86) {
|
|
csDesc.Selector = (ULONG)GetRegValue(REGCS);
|
|
DbgKdLookupSelector(NtsdCurrentProcessor, &csDesc);
|
|
f16pm = !csDesc.Descriptor.HighWord.Bits.Default_Big;
|
|
}
|
|
#endif
|
|
if (!CrashFileName) {
|
|
InitFirCache(pCtlReport->InstructionCount, pCtlReport->InstructionStream);
|
|
}
|
|
#endif // #ifdef KERNEL
|
|
|
|
#ifndef KERNEL
|
|
SuspendAllThreads();
|
|
#endif
|
|
|
|
#ifndef KERNEL
|
|
|
|
// If profiling, determine if breakpoint is a profiling breakpoint.
|
|
// If so, skip normal processing of breakpoints, update profiling
|
|
// data structure, and continue execution.
|
|
if (fProfilingDLL)
|
|
{
|
|
UnfreezeThreads();
|
|
ChangeRegContext(pProcessEvent->pThreadEvent);
|
|
GetRegPCValue(&pcaddr);
|
|
|
|
if (fBrkpt && !fProfilingBrkptFound)
|
|
{
|
|
AddrSub(&pcaddr, cbBrkptLength);
|
|
SetRegPCValue(&pcaddr);
|
|
|
|
#ifdef BP_CORRUPTION
|
|
UnlockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
|
|
for (count = 0; count < MAX_NUMBER_OF_BREAKPOINTS; count++)
|
|
{
|
|
NotFlat(brkptlist[count].addr);
|
|
ComputeFlatAddress(&brkptlist[count].addr,NULL);
|
|
|
|
if ( brkptlist[count].status == 'e' &&
|
|
brkptlist[count].pProcess == pProcessEvent &&
|
|
( (brkptlist[count].option == (UCHAR)-1 &&
|
|
AddrEqu(brkptlist[count].addr, pcaddr)) ||
|
|
( brkptlist[count].option != (UCHAR)-1
|
|
#ifdef I386
|
|
&& ((GetDregValue(6) >> brkptlist[count].dregindx) & 1)
|
|
#endif
|
|
)
|
|
)
|
|
)
|
|
{
|
|
if ((brkptlist[count].pThread == NULL ||
|
|
brkptlist[count].pThread == pProcessEvent->pThreadEvent)
|
|
&& --brkptlist[count].passcnt == 0)
|
|
{
|
|
brkptlist[count].passcnt = 1;
|
|
|
|
// Check to see if this is a profiling breakpoint.
|
|
// If breakpoint exists in profiling data structure,
|
|
// check to see if user has manually set a breakpoint
|
|
// there. If so, cannot pass through
|
|
|
|
cBrkptType = GetBrkptType(ps_ProfileDLL, count);
|
|
if (cBrkptType != BRKPT_NOT_FOUND)
|
|
{
|
|
GetSymbolStdCall(
|
|
Flat(brkptlist[count].addr),
|
|
Symbol,
|
|
&displacement,
|
|
NULL);
|
|
prevCmdState = cmdState;
|
|
cmdState = 'd';
|
|
UpdateProfile (&ps_ProfileDLL, Symbol, count);
|
|
cmdState = prevCmdState;
|
|
|
|
// check the type of breakpoint to see if the user
|
|
// manually set the break point as well
|
|
if (cBrkptType == BRKPT_PROFILE)
|
|
{
|
|
// Restore the profiling breakpoint
|
|
pProcessOrig = pProcessCurrent;
|
|
pProcessCurrent = brkptlist[count].pProcess;
|
|
RestoreBreakPoint(brkptlist[count].handle);
|
|
brkptlist[count].fBpSet = FALSE;
|
|
pProcessCurrent = pProcessOrig;
|
|
|
|
// Set the trace event for the profiling
|
|
// breakpoint.
|
|
SetSpecificBrkpt (count);
|
|
|
|
fProfilingBrkptFound = TRUE;
|
|
ProfilingBrkptIndex = count;
|
|
|
|
FreezeThreads();
|
|
ChangeRegContext(0);
|
|
ResumeAllThreads();
|
|
#ifdef BP_CORRUPTION
|
|
LockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} //for
|
|
#ifdef BP_CORRUPTION
|
|
LockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
}
|
|
|
|
else if (fProfilingBrkptFound)
|
|
{
|
|
// write back the profiling breakpoint that we just encountered
|
|
// in the previous exception
|
|
|
|
SetSpecificBrkpt(ProfilingBrkptIndex);
|
|
fProfilingBrkptFound = FALSE;
|
|
|
|
FreezeThreads();
|
|
ChangeRegContext(0);
|
|
ResumeAllThreads();
|
|
return;
|
|
}
|
|
}
|
|
|
|
#endif // #ifndef KERNEL
|
|
|
|
do {
|
|
fSymbol = TRUE;
|
|
if (fHardBrkpt && cmdState == 'g') {
|
|
fPassThrough = TRUE;
|
|
}
|
|
|
|
if (!fHardBrkpt) {
|
|
RestoreBrkpts();
|
|
#ifndef KERNEL
|
|
|
|
if (!fProfilingDLL)
|
|
{
|
|
UnfreezeThreads();
|
|
}
|
|
#else // KERNEL
|
|
// UpdateFirCache((ULONG)pcEntryAddr);
|
|
#endif // KERNEL
|
|
}
|
|
|
|
|
|
#ifndef KERNEL
|
|
|
|
if (!fProfilingDLL) {
|
|
ChangeRegContext(pProcessEvent->pThreadEvent);
|
|
}
|
|
|
|
#ifdef i386
|
|
fVm86 = VM86(GetRegValue(REGEFL));
|
|
#endif // i386
|
|
#endif // KERNEL
|
|
|
|
if (fLoopError) {
|
|
cmdState = 'i';
|
|
} else {
|
|
#ifndef KERNEL
|
|
if (! fProfilingDLL) {
|
|
GetRegPCValue(&pcaddr);
|
|
if (fBrkpt && !fProfilingDLL) {
|
|
#ifdef i386
|
|
AddrSub(&pcaddr, cbBrkptLength);
|
|
#endif // i386
|
|
SetRegPCValue(&pcaddr);
|
|
fBrkpt = FALSE;
|
|
}
|
|
}
|
|
#else // KERNEL
|
|
GetRegPCValue(&pcaddr);
|
|
#endif // KERNEL
|
|
|
|
#ifndef ALPHA
|
|
fHardBrkpt = (BOOLEAN)(GetMemString(&pcaddr, bBrkptInstr,
|
|
cbBrkptLength) &&
|
|
!memcmp(bBrkptInstr, (PUCHAR)&trapInstr,
|
|
(int)cbBrkptLength));
|
|
#else // ALPHA
|
|
if (GetMemString(&pcaddr, bBrkptInstr, cbBrkptLength)) {
|
|
LONG index = 0;
|
|
|
|
//
|
|
// ALPHA has several breakpoint instructions - see
|
|
// if we have hit any of them.
|
|
//
|
|
|
|
fHardBrkpt = FALSE;
|
|
do {
|
|
|
|
if (!memcmp(bBrkptInstr,
|
|
(PUCHAR)&breakInstrs[index],
|
|
(int)cbBrkptLength)) {
|
|
|
|
fHardBrkpt = TRUE;
|
|
break; // from the WHILE loop
|
|
}
|
|
} while (++index < NUMBER_OF_BREAK_INSTRUCTIONS);
|
|
|
|
} // if (GetMemString)
|
|
#endif // ALPHA
|
|
if ((cmdState == 'p' || cmdState == 't') &&
|
|
#ifndef KERNEL
|
|
pProcessStepBrkpt == pProcessEvent &&
|
|
#endif // KERNEL
|
|
(Flat(steptraceaddr) == -1L ||
|
|
AddrEqu(steptraceaddr, pcaddr))) {
|
|
|
|
// step/trace event occurred
|
|
|
|
fSymbol = FALSE;
|
|
|
|
// ignore step/trace event is not the specific
|
|
// thread requested.
|
|
|
|
#ifndef KERNEL
|
|
if (pThreadCmd && pThreadCmd != pProcessEvent->pThreadEvent)
|
|
{
|
|
WatchThreadMismatch++;
|
|
fPassThrough = TRUE;
|
|
}
|
|
else
|
|
#else // KERNEL
|
|
if ((InitialSP
|
|
&&
|
|
((CURRENT_STACK + 0x1500 < InitialSP)
|
|
||
|
|
(InitialSP + 0x1500 < CURRENT_STACK)
|
|
))) {
|
|
fPassThrough = TRUE;
|
|
} else
|
|
#endif // KERNEL
|
|
// test if step/trace range active
|
|
// if so, compute the next offset and pass through
|
|
|
|
if (steptracelow != -1 && Flat(pcaddr) >= steptracelow
|
|
&& Flat(pcaddr) < steptracehigh) {
|
|
ComputeNextOffset(steptraceaddr,
|
|
(BOOLEAN)(cmdState == 'p'));
|
|
#ifdef KERNEL
|
|
if (WatchWhole) {
|
|
BeginCurFunc = Flat(steptraceaddr);
|
|
EndCurFunc = 0;
|
|
}
|
|
#endif // KERNEL
|
|
fPassThrough = TRUE;
|
|
}
|
|
|
|
// active step/trace event - note event if count is zero
|
|
|
|
else if ((fControlC
|
|
#ifdef KERNEL
|
|
&& DbgKdpBreakIn
|
|
#endif // KERNEL
|
|
)
|
|
|| (--steptracepasscnt == 0)
|
|
|| (Watching && AddrEqu(WatchTarget,pcaddr)
|
|
#ifdef KERNEL
|
|
&& (CURRENT_STACK >= InitialSP)
|
|
#endif // KERNEL
|
|
)) {
|
|
#ifndef KERNEL
|
|
ULONG i;
|
|
#endif // KERNEL
|
|
|
|
// dprintf("\n\nFinished trace cc = %d, count = %d, target = %x, pc = %x\n", (LONG)fControlC, steptracepasscnt, Flat(WatchTarget), Flat(pcaddr));
|
|
fPassThrough = FALSE;
|
|
fBrkptHit |= STEPTRACEHIT;
|
|
steptracepasscnt = 0;
|
|
fControlC = 0;
|
|
#ifdef KERNEL
|
|
DbgKdpBreakIn = FALSE; // TEMP TEMP TEMP
|
|
#else // KERNEL
|
|
if ( Timing ) {
|
|
dprintf("%ld us\n",SystemReportedTime);
|
|
Timing = FALSE;
|
|
}
|
|
#endif // KERNEL
|
|
|
|
if ( Watching ) {
|
|
fPassThrough = FALSE;
|
|
#ifdef KERNEL
|
|
if (WatchWhole) {
|
|
PDBGKD_TRACE_DATA td = (PDBGKD_TRACE_DATA)traceData;
|
|
if (td[1].s.Instructions == TRACE_DATA_INSTRUCTIONS_BIG) {
|
|
WatchCount = td[2].LongNumber;
|
|
} else {
|
|
WatchCount = td[1].s.Instructions;
|
|
}
|
|
} else {
|
|
ProcessWatchTraceEvent((PDBGKD_TRACE_DATA)traceData,
|
|
pcaddr);
|
|
}
|
|
#else // KERNEL
|
|
if (CurrentWatchSym) {
|
|
CurrentWatchSym->InstrCount++;
|
|
PrintWatchSym(CurrentWatchSym);
|
|
dprintf("\n");
|
|
}
|
|
#endif // KERNEL
|
|
Watching = FALSE;
|
|
FreeWatchTrace();
|
|
dprintf("%d Instructions were executed %d(%d from other threads) traces %d sums\n",WatchCount, WatchTRCalls, WatchThreadMismatch, WatchSumIt);
|
|
#ifndef KERNEL
|
|
ProcDump(&ps_ProfileTrace);
|
|
|
|
dprintf("%d system calls were executed\n\nSystem Call:\n", KernelCalls);
|
|
for(i = 0; i < NtCalls; i++)
|
|
{
|
|
dprintf("%d %s\n", NtCallTable[i].Count,
|
|
NtCallTable[i].Name);
|
|
}
|
|
#endif // KERNEL
|
|
}
|
|
}
|
|
else {
|
|
|
|
|
|
if ( Watching ) {
|
|
if ( WatchTrace && !fPassThrough ) {
|
|
#ifdef KERNEL
|
|
ProcessWatchTraceEvent((PDBGKD_TRACE_DATA)traceData,
|
|
pcaddr);
|
|
#else // KERNEL
|
|
ProcessWatchTraceEvent(pcaddr);
|
|
#endif // KERNEL
|
|
}
|
|
goto skipit;
|
|
}
|
|
|
|
// more remaining events to occur, but output
|
|
// the instruction (optionally with registers)
|
|
// compute the step/trace address for next event
|
|
|
|
if (fOutputRegs) {
|
|
OutputAllRegs(FALSE);
|
|
}
|
|
#ifndef KERNEL
|
|
OutDisCurrent(TRUE, fSymbol); // output with EA
|
|
#else // KERNEL
|
|
OutDisCurrent(fOutputRegs, fSymbol); // no EA if no regs
|
|
#endif // KERNEL
|
|
skipit:
|
|
if ((cmdState == 'p') || WatchStepOver) {
|
|
ComputeNextOffset(steptraceaddr, TRUE);
|
|
} else {
|
|
ComputeNextOffset(steptraceaddr, FALSE);
|
|
}
|
|
GetCurrentMemoryOffsets(&steptracelow, &steptracehigh);
|
|
fPassThrough = TRUE;
|
|
}
|
|
}
|
|
else if (cmdState == 'g') {
|
|
for (count = 0; count < gocnt; count++) {
|
|
if (AddrEqu(golist[count].addr,pcaddr)) {
|
|
#ifdef KERNEL
|
|
fBrkptHit |= GOLISTHIT;
|
|
#else // KERNEL
|
|
if (pProcessGoBrkpt == pProcessEvent &&
|
|
(pThreadCmd == NULL ||
|
|
pThreadCmd == pProcessEvent->pThreadEvent)) {
|
|
if (fVerboseOutput) {
|
|
dprintf("Hit Breakpoint#%d\n",count);
|
|
}
|
|
|
|
fBrkptHit |= GOLISTHIT;
|
|
} else {
|
|
fPassThrough = TRUE;
|
|
}
|
|
#endif // KERNEL
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#ifdef BP_CORRUPTION
|
|
UnlockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
for (count = 0; count < MAX_NUMBER_OF_BREAKPOINTS; count++) {
|
|
|
|
NotFlat(brkptlist[count].addr);
|
|
ComputeFlatAddress(&brkptlist[count].addr,NULL);
|
|
|
|
if (brkptlist[count].status == 'e'
|
|
#ifndef KERNEL
|
|
&& brkptlist[count].pProcess == pProcessEvent
|
|
#endif // KERNEL
|
|
#ifdef i386
|
|
&& ((brkptlist[count].option == (UCHAR)-1
|
|
&& AddrEqu(brkptlist[count].addr, pcaddr))
|
|
|| (brkptlist[count].option != (UCHAR)-1
|
|
#ifdef KERNEL
|
|
&& ((pCtlReport->Dr6
|
|
#else // KERNEL
|
|
&& ((GetDregValue(6)
|
|
#endif // KERNEL
|
|
>> brkptlist[count].dregindx) & 1))))
|
|
#else // i386
|
|
&& AddrEqu(brkptlist[count].addr,pcaddr))
|
|
#endif // i386
|
|
{
|
|
if (
|
|
#ifndef KERNEL
|
|
(brkptlist[count].pThread == NULL ||
|
|
brkptlist[count].pThread
|
|
== pProcessEvent->pThreadEvent) &&
|
|
#endif // KERNEL
|
|
--brkptlist[count].passcnt == 0) {
|
|
fBrkptHit |= BRKPTHIT;
|
|
brkptlist[count].passcnt = 1;
|
|
}
|
|
else {
|
|
fPassThrough = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#ifdef BP_CORRUPTION
|
|
LockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
|
|
if (fDeferDefined
|
|
#ifndef KERNEL
|
|
&& pProcessDeferBrkpt == pProcessEvent
|
|
#endif // KERNEL
|
|
#if defined(MIPS) || defined(ALPHA) || defined(_PPC_)
|
|
&& (Flat(deferaddr) == -1 || AddrEqu(deferaddr,pcaddr))
|
|
#endif // !i386
|
|
&& fBrkptHit == 0) {
|
|
fPassThrough = TRUE;
|
|
}
|
|
|
|
if (fBrkptHit == BRKPTHIT) {
|
|
if (brkptlist[count].szcommand[0] != '\0') {
|
|
strcpy(chCommand, brkptlist[count].szcommand);
|
|
pchCommand = chCommand;
|
|
|
|
// Don't output any noise while processing breakpoint
|
|
// command strings.
|
|
|
|
fOutputRegs = FALSE;
|
|
fOutputDisCurrent = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cmdState == 'i' || !fPassThrough || fHardBrkpt) {
|
|
|
|
cmdState = 'c';
|
|
|
|
#ifdef KERNEL
|
|
if (fHardBrkpt && pcEntryAddr == vs.BreakpointWithStatus) {
|
|
HandleBPWithStatus();
|
|
}
|
|
if (fOutputRegs) {
|
|
OutputAllRegs(FALSE);
|
|
}
|
|
if (fOutputDisCurrent) {
|
|
OutDisCurrent(fOutputRegs, fSymbol); // no EA if no registers
|
|
}
|
|
#else // KERNEL
|
|
if (fOutputRegs && !fBrkptContinue) {
|
|
OutputAllRegs(FALSE);
|
|
}
|
|
if (fOutputDisCurrent && !fBrkptContinue) {
|
|
OutDisCurrent(TRUE, fSymbol); // output with EA
|
|
}
|
|
#endif // KERNEL
|
|
|
|
GetRegPCValue(&assemDefault);
|
|
dumpDefault = unasmDefault = assemDefault;
|
|
///////////////////////////////
|
|
|
|
#ifdef KERNEL
|
|
|
|
ProcessCommands();
|
|
|
|
#else // KERNEL
|
|
if (fBrkptContinue) {
|
|
fBrkptContinue = FALSE;
|
|
cmdState = (Watching ? oldcmdState : 'g');
|
|
} else {
|
|
ProcessCommands();
|
|
}
|
|
#endif // !KERNEL
|
|
|
|
|
|
///////////////////////////////
|
|
if (cmdState == 'p' || cmdState == 't') {
|
|
#ifndef KERNEL
|
|
if (pThreadCmd == NULL) {
|
|
pThreadCmd = pProcessCurrent->pThreadCurrent;
|
|
}
|
|
ChangeRegContext(pThreadCmd);
|
|
#endif // KERNEL
|
|
ComputeNextOffset(steptraceaddr,
|
|
(BOOLEAN)(cmdState == 'p'));
|
|
|
|
GetCurrentMemoryOffsets(&steptracelow, &steptracehigh);
|
|
#ifndef KERNEL
|
|
pProcessStepBrkpt = pProcessCurrent;
|
|
ChangeRegContext(pProcessEvent->pThreadEvent);
|
|
#endif // KERNEL
|
|
}
|
|
GetRegPCValue(&pcaddr);
|
|
#ifndef ALPHA
|
|
fHardBrkpt = (BOOLEAN)(GetMemString(&pcaddr, bBrkptInstr,
|
|
cbBrkptLength) &&
|
|
!memcmp(bBrkptInstr, (PUCHAR)&trapInstr,
|
|
(int)cbBrkptLength));
|
|
#else // ALPHA
|
|
if (GetMemString(&pcaddr, bBrkptInstr, cbBrkptLength)) {
|
|
LONG index = 0;
|
|
|
|
//
|
|
// ALPHA has several breakpoint instructions - see
|
|
// if we have hit any of them.
|
|
//
|
|
|
|
fHardBrkpt = FALSE;
|
|
do {
|
|
|
|
if (!memcmp(bBrkptInstr,
|
|
(PUCHAR)&breakInstrs[index],
|
|
(int)cbBrkptLength)) {
|
|
|
|
fHardBrkpt = TRUE;
|
|
break; // from the FOR loop
|
|
}
|
|
} while (++index < NUMBER_OF_BREAK_INSTRUCTIONS);
|
|
|
|
} // if (GetMemString)
|
|
#endif // ALPHA
|
|
}
|
|
|
|
#ifdef KERNEL
|
|
if (cmdState == 's') {
|
|
fHardBrkpt = FALSE;
|
|
}
|
|
#endif // KERNEL
|
|
if (fHardBrkpt) {
|
|
fLoopError = FALSE;
|
|
SetRegPCValue(AddrAdd(&pcaddr, cbBrkptLength));
|
|
#ifdef KERNEL
|
|
BeginCurFunc = 1;
|
|
#endif // KERNEL
|
|
} else {
|
|
fLoopError = !(BOOLEAN)(SetBrkpts()
|
|
#ifndef KERNEL
|
|
&& FreezeThreads()
|
|
#endif // KERNEL
|
|
);
|
|
}
|
|
}
|
|
while (fHardBrkpt || fLoopError);
|
|
|
|
#ifndef KERNEL
|
|
ChangeRegContext(0);
|
|
#else // KERNEL
|
|
ChangeKdRegContext(0, NULL);
|
|
#endif // KERNEL
|
|
|
|
#ifndef KERNEL
|
|
ResumeAllThreads();
|
|
#endif // KERNEL
|
|
}
|
|
|
|
/*** ProcessCommands - high-level command processor
|
|
*
|
|
* Purpose:
|
|
* If no command string remains, the user is prompted to
|
|
* input one. Once input, this routine parses the string
|
|
* into commands and their operands. Error checking is done
|
|
* on both commands and operands. Multiple commands may be
|
|
* input by separating them with semicolons. Once a command
|
|
* is parsefd, the appropriate routine (type fnXXXXX) is called
|
|
* to execute the command.
|
|
*
|
|
* Input:
|
|
* pchCommand = pointer to the next command in the string
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* error exit: SYNTAX - command type or operand error
|
|
* normal exit: termination on 'q' command
|
|
*
|
|
*************************************************************************/
|
|
|
|
void ProcessCommands (void)
|
|
{
|
|
UCHAR ch;
|
|
ADDR addr1;
|
|
ADDR addr2;
|
|
ULONG value1;
|
|
ULONG value2;
|
|
ULONG value3;
|
|
PADDR paddr2=&addr2;
|
|
ULONG count;
|
|
ULONG traceType;
|
|
BOOLEAN fLength;
|
|
UCHAR list[STRLISTSIZE];
|
|
ULONG size;
|
|
#ifdef KERNEL
|
|
PUCHAR SavedpchCommand;
|
|
ULONGLONG valueL;
|
|
#endif
|
|
PUCHAR pargstring;
|
|
PPROCESS_INFO pProcessPrevious = NULL;
|
|
BOOLEAN parseProcess=FALSE;
|
|
|
|
if (setjmp(cmd_return) != 0) {
|
|
pchCommand = chCommand;
|
|
chCommand[0] = '\0';
|
|
chLastCommand[0] = '\0';
|
|
}
|
|
|
|
#if defined(KERNEL)
|
|
SetCmdCtrlHandler();
|
|
#endif
|
|
|
|
#ifdef KERNEL
|
|
fSwitched = FALSE;
|
|
#ifdef i386
|
|
fSetGlobalDataBrkpts = FALSE;
|
|
|
|
if (!InitialCommandRead) {
|
|
InitialCommandRead = TRUE;
|
|
if (chCommand[ 0 ] != '\0') {
|
|
dprintf("%s: Reading initial command: '%s'\n",
|
|
DebuggerName,
|
|
chCommand );
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
do {
|
|
ch = *pchCommand++;
|
|
if (ch == '\0' || (ch == ';' && fFlushInput)) {
|
|
fControlC = FALSE;
|
|
fFlushInput = FALSE;
|
|
|
|
#ifdef KERNEL
|
|
BrkpointsSuspended = FALSE;
|
|
#endif
|
|
|
|
if (parseProcess) {
|
|
parseProcess = FALSE;
|
|
pProcessCurrent = pProcessPrevious;
|
|
}
|
|
#ifndef KERNEL
|
|
dprintf("%1ld:%03ld", pProcessCurrent->index,
|
|
pProcessCurrent->pThreadCurrent->index);
|
|
#else
|
|
#ifdef i386
|
|
if (fVm86 = VM86(GetRegValue(REGEFL))) {
|
|
dprintf("vm");
|
|
vm86DefaultSeg = -1L;
|
|
} else
|
|
if (f16pm = !csDesc.Descriptor.HighWord.Bits.Default_Big) {
|
|
dprintf("16");
|
|
vm86DefaultSeg = -1L;
|
|
}
|
|
#endif
|
|
if (NumberProcessors > 1) {
|
|
dprintf("%d: kd", NtsdCurrentProcessor);
|
|
cbPrompt = 5;
|
|
}
|
|
else {
|
|
dprintf("kd");
|
|
cbPrompt = 2;
|
|
}
|
|
|
|
#endif
|
|
fPhysicalAddress = FALSE;
|
|
NtsdPrompt("> ", chCommand, sizeof(chCommand));
|
|
RemoveDelChar(chCommand);
|
|
pchCommand = chCommand;
|
|
if (*chCommand!='r') ExpandUserRegs(pchCommand);
|
|
if (chCommand[0] == '\0')
|
|
strcpy(chCommand, chLastCommand);
|
|
else
|
|
strcpy(chLastCommand, chCommand);
|
|
pchCommand = chCommand;
|
|
ch = *pchCommand++;
|
|
}
|
|
EVALUATE:
|
|
while (ch == ' ' || ch == '\t' || ch == ';')
|
|
ch = *pchCommand++;
|
|
|
|
#ifdef KERNEL
|
|
|
|
//
|
|
// BUGBUG Here we assume no more than 32 processors
|
|
//
|
|
|
|
if (NumberProcessors > 32) {
|
|
dprintf ("WARNING: NumberProcessors corrupted - using 1\n");
|
|
NumberProcessors = 1;
|
|
}
|
|
|
|
if (ch >= '0' && ch <= '9') {
|
|
value1 = 0;
|
|
SavedpchCommand = pchCommand;
|
|
while (ch >= '0' && ch <= '9') {
|
|
value1 = value1 * 10 + (ch - '0');
|
|
ch = *SavedpchCommand++;
|
|
}
|
|
ch = (UCHAR)tolower(ch);
|
|
if (ch == 'r' || ch == 'k' || ch == 'z' || (ch == 'd' && tolower(*SavedpchCommand) == 't')) {
|
|
if (value1 < NumberProcessors) {
|
|
if (value1 != (ULONG)NtsdCurrentProcessor) {
|
|
SaveProcessorState();
|
|
NtsdCurrentProcessor = (USHORT)value1;
|
|
fSwitched = TRUE;
|
|
|
|
}
|
|
} else {
|
|
error(BADRANGE);
|
|
}
|
|
} else {
|
|
error(SYNTAX);
|
|
}
|
|
pchCommand = SavedpchCommand;
|
|
}
|
|
#endif
|
|
fPointerExpression = TRUE;
|
|
switch (ch = (UCHAR)tolower(ch)) {
|
|
case '?':
|
|
fPointerExpression=FALSE;
|
|
if ((ch = PeekChar()) == '\0' || ch == ';')
|
|
OutputHelp();
|
|
else
|
|
fnEvaluateExp();
|
|
break;
|
|
case '$':
|
|
if ( *pchCommand++ == '<')
|
|
parseScriptCmd();
|
|
*pchCommand = 0;
|
|
break;
|
|
#ifndef KERNEL
|
|
case '~':
|
|
parseThreadCmds();
|
|
break;
|
|
case '|':
|
|
if (!parseProcess) {
|
|
parseProcess = TRUE;
|
|
pProcessPrevious = pProcessCurrent;
|
|
}
|
|
parseProcessCmds();
|
|
if (!*pchCommand)
|
|
parseProcess = FALSE;
|
|
else{
|
|
ch = *pchCommand++;
|
|
goto EVALUATE;
|
|
}
|
|
|
|
break;
|
|
#else
|
|
case '~':
|
|
value1 = 0;
|
|
while (*pchCommand >= '0' && *pchCommand <= '9') {
|
|
value1 = value1 * 10 + (*pchCommand - '0');
|
|
pchCommand++;
|
|
}
|
|
*pchCommand = 0;
|
|
if (value1 < NumberProcessors &&
|
|
value1 != (ULONG)NtsdCurrentProcessor) {
|
|
|
|
if (CrashFileName) {
|
|
extern ULONG contextState;
|
|
NtsdCurrentProcessor = (USHORT) value1;
|
|
contextState = CONTEXTFIR;
|
|
} else {
|
|
SwitchProcessor = (USHORT) value1 + 1;
|
|
cmdState = 's';
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
case '.':
|
|
fPointerExpression=FALSE;
|
|
fnDotCommand();
|
|
break;
|
|
|
|
case '!':
|
|
fnBangCmd(pchCommand, &pchCommand);
|
|
break;
|
|
|
|
case '#':
|
|
igrep();
|
|
pargstring = pchCommand;
|
|
while (*pchCommand != '\0') pchCommand++;
|
|
break;
|
|
|
|
case 'a':
|
|
if ((ch = PeekChar()) != '\0' && ch != ';')
|
|
GetAddrExpression(X86REGCS, &assemDefault);
|
|
fnAssemble(&assemDefault);
|
|
break;
|
|
case 'b':
|
|
ch = *pchCommand++;
|
|
#ifdef KERNEL
|
|
if (CrashFileName) {
|
|
dprintf( "you cannot do breakpoints on a crash dump\n" );
|
|
break;
|
|
}
|
|
#endif
|
|
switch (tolower(ch)) {
|
|
#ifdef i386
|
|
case 'a':
|
|
parseBpCmd(TRUE
|
|
#ifdef KERNEL
|
|
, FALSE, 0
|
|
#else
|
|
,NULL
|
|
#endif
|
|
); // data breakpoint
|
|
break;
|
|
#endif
|
|
case 'c':
|
|
case 'd':
|
|
case 'e':
|
|
value1 = GetIdList();
|
|
fnChangeBpState(value1, ch);
|
|
break;
|
|
#ifdef KERNEL
|
|
case 'i':
|
|
case 'w':
|
|
parseBpCmd(FALSE,TRUE,ch); // nondata internal
|
|
break;
|
|
#endif
|
|
case 'l':
|
|
fnListBpState();
|
|
break;
|
|
case 'p':
|
|
parseBpCmd(FALSE // nondata breakpoint
|
|
#ifdef KERNEL
|
|
, FALSE, 0
|
|
#else
|
|
,NULL
|
|
#endif
|
|
);
|
|
break;
|
|
default:
|
|
error(SYNTAX);
|
|
break;
|
|
}
|
|
break;
|
|
case 'c':
|
|
#ifdef i386
|
|
GetRange(&addr1, &value2, &fLength, 1, X86REGDS );
|
|
#else
|
|
GetRange(&addr1, &value2, &fLength, 1 );
|
|
#endif
|
|
GetAddrExpression(X86REGDS, &addr2);
|
|
fnCompareMemory(&addr1, value2, &addr2);
|
|
break;
|
|
case 'd':
|
|
ch = (UCHAR)tolower(*pchCommand);
|
|
if (ch == 'a'
|
|
|| ch == 'b'
|
|
|| ch == 'c'
|
|
|| ch == 'd'
|
|
|| ch == 'g'
|
|
|| ch == 'l'
|
|
|| ch == 'u'
|
|
|| ch == 'w'
|
|
|| ch == 's'
|
|
#ifdef KERNEL
|
|
#ifdef MIPS
|
|
|| ch == 't'
|
|
#endif
|
|
#endif
|
|
) {
|
|
if (ch == 's') {
|
|
dumptype = *pchCommand;
|
|
}
|
|
else {
|
|
dumptype = ch;
|
|
}
|
|
pchCommand++;
|
|
}
|
|
#if defined(KERNEL) && defined(i386)
|
|
if ((fVm86||f16pm) && vm86DefaultSeg==-1L)
|
|
vm86DefaultSeg = (ULONG)GetRegValue(REGDS);
|
|
#endif
|
|
#ifdef _PPC_
|
|
ppcPrefix = FALSE;
|
|
#endif
|
|
// addr1 = dumpDefault; // default starting address
|
|
fLength = TRUE;
|
|
switch (dumptype) {
|
|
case 'a':
|
|
value2 = 384;
|
|
GetRange(&dumpDefault, &value2, &fLength, 1
|
|
#ifdef i386
|
|
, REGDS
|
|
#endif
|
|
);
|
|
fnDumpAsciiMemory(&dumpDefault, value2);
|
|
break;
|
|
case 'b':
|
|
value2 = 128;
|
|
GetRange(&dumpDefault, &value2, &fLength, 1
|
|
#ifdef i386
|
|
, REGDS
|
|
#endif
|
|
);
|
|
fnDumpByteMemory(&dumpDefault, value2);
|
|
break;
|
|
case 'c':
|
|
value2 = 32;
|
|
GetRange(&dumpDefault, &value2, &fLength, 4
|
|
#ifdef i386
|
|
,REGDS
|
|
#endif
|
|
);
|
|
fnDumpDwordAndCharMemory(&dumpDefault, value2);
|
|
break;
|
|
case 'd':
|
|
value2 = 32;
|
|
GetRange(&dumpDefault, &value2, &fLength, 4
|
|
#ifdef i386
|
|
, REGDS
|
|
#endif
|
|
);
|
|
fnDumpDwordMemory(&dumpDefault, value2);
|
|
break;
|
|
#ifdef i386
|
|
case 'g':
|
|
{
|
|
DESCRIPTOR_TABLE_ENTRY desc;
|
|
ULONG base;
|
|
ULONG limit;
|
|
INT type;
|
|
LPSTR lpName;
|
|
|
|
desc.Selector = GetExpression();
|
|
|
|
dprintf("Selector Base Limit Type DPL Size Gran\n");
|
|
dprintf("-------- -------- -------- ------ --- ------- ----\n");
|
|
|
|
DbgKdLookupSelector(NtsdCurrentProcessor, &desc);
|
|
|
|
base = ((ULONG)desc.Descriptor.HighWord.Bytes.BaseHi << 24)
|
|
+ ((ULONG)desc.Descriptor.HighWord.Bytes.BaseMid << 16)
|
|
+ ((ULONG)desc.Descriptor.BaseLow);
|
|
|
|
limit = (ULONG)desc.Descriptor.LimitLow
|
|
+ ((ULONG)desc.Descriptor.HighWord.Bits.LimitHi << 16);
|
|
if ( desc.Descriptor.HighWord.Bits.Granularity ) {
|
|
limit <<= 12;
|
|
limit += 0xFFF;
|
|
}
|
|
type = desc.Descriptor.HighWord.Bits.Type;
|
|
if ( type & 0x10 ) {
|
|
if ( type & 0x8 ) {
|
|
// Code Descriptor
|
|
lpName = " Code ";
|
|
} else {
|
|
// Data Descriptor
|
|
lpName = " Data ";
|
|
}
|
|
} else {
|
|
lpName = " Sys. ";
|
|
}
|
|
|
|
// 1234 12345678 12345678 ?Type? 1 ....... ....
|
|
dprintf(" %04X %08lX %08lX %s %d %s %s\n",
|
|
desc.Selector,
|
|
base,
|
|
limit,
|
|
lpName,
|
|
desc.Descriptor.HighWord.Bits.Dpl,
|
|
desc.Descriptor.HighWord.Bits.Default_Big ? " Big " : "Not Big",
|
|
desc.Descriptor.HighWord.Bits.Granularity ? "Page" : "Byte"
|
|
);
|
|
}
|
|
break;
|
|
#endif
|
|
case 'l':
|
|
value2 = 32;
|
|
value3 = 4;
|
|
if ((ch = PeekChar()) != '\0' && ch != ';') {
|
|
GetAddrExpression(X86REGDS, &dumpDefault);
|
|
if ((ch = PeekChar()) != '\0' && ch != ';') {
|
|
value2 = GetExpression();
|
|
if ((ch = PeekChar()) != '\0' && ch != ';') {
|
|
value3 = GetExpression();
|
|
}
|
|
}
|
|
}
|
|
|
|
fnDumpListMemory(&dumpDefault, value2, value3);
|
|
break;
|
|
#ifdef KERNEL
|
|
#ifdef MIPS
|
|
case 't':
|
|
value2 = 8;
|
|
GetRange(&addr1, &value2, &fLength, 8);
|
|
value1 = Flat(addr1);
|
|
fnDumpTb4000(value1, value2);
|
|
value1 = value1 + value2;
|
|
ADDR32(&addr1,value1);
|
|
break;
|
|
#endif
|
|
#endif
|
|
case 's':
|
|
case 'S':
|
|
{
|
|
UNICODE_STRING UnicodeString;
|
|
ADDR BufferAddr;
|
|
|
|
value2 = 1;
|
|
GetRange(&dumpDefault, &value2, &fLength, 2
|
|
#ifdef i386
|
|
, REGDS
|
|
#endif
|
|
);
|
|
|
|
while (value2--) {
|
|
if (GetMemString(&dumpDefault,
|
|
(PUCHAR)&UnicodeString,
|
|
sizeof( UnicodeString )
|
|
) == sizeof( UnicodeString )
|
|
) {
|
|
#if defined(KERNEL) & defined(i386)
|
|
BufferAddr.seg = (USHORT)GetRegValue(REGDS);
|
|
#endif
|
|
BufferAddr.type = ADDR_32|FLAT_COMPUTED;
|
|
Off(BufferAddr) = Flat(BufferAddr) = (ULONG)UnicodeString.Buffer;
|
|
|
|
if (dumptype == 'S') {
|
|
fnDumpUnicodeMemory( &BufferAddr, UnicodeString.Length );
|
|
}
|
|
else {
|
|
fnDumpAsciiMemory( &BufferAddr, UnicodeString.Length );
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 'u':
|
|
value2 = 384;
|
|
GetRange(&dumpDefault, &value2, &fLength, 2
|
|
#ifdef i386
|
|
, REGDS
|
|
#endif
|
|
);
|
|
fnDumpUnicodeMemory(&dumpDefault, value2);
|
|
break;
|
|
case 'w':
|
|
value2 = 64;
|
|
GetRange(&dumpDefault, &value2, &fLength, 2
|
|
#ifdef i386
|
|
, REGDS
|
|
#endif
|
|
);
|
|
fnDumpWordMemory(&dumpDefault, value2);
|
|
break;
|
|
}
|
|
#ifdef _PPC_
|
|
ppcPrefix = TRUE;
|
|
#endif
|
|
break;
|
|
case 'e':
|
|
ch = (UCHAR)tolower(*pchCommand);
|
|
if (ch == 'a' || ch == 'b' || ch == 'w' || ch == 'd') {
|
|
pchCommand++;
|
|
entertype = ch;
|
|
}
|
|
GetAddrExpression(X86REGDS, &addr1);
|
|
if (entertype == 'a') {
|
|
AsciiList(list, &count);
|
|
if (count == 0)
|
|
error(UNIMPLEMENT); //TEMP
|
|
} else {
|
|
size = 1;
|
|
if (entertype == 'w')
|
|
size = 2;
|
|
else if (entertype == 'd')
|
|
size = 4;
|
|
HexList(list, &count, size);
|
|
if (count == 0) {
|
|
fnInteractiveEnterMemory(&addr1, size);
|
|
} else {
|
|
fnEnterMemory(&addr1, list, count);
|
|
}
|
|
}
|
|
break;
|
|
case 'f':
|
|
NotFlat(addr1);
|
|
GetRange(&addr1, &value2, &fLength, 1
|
|
#ifdef i386
|
|
, REGDS);
|
|
if (fnotFlat(addr1)) error(SYNTAX);
|
|
#else
|
|
);
|
|
#endif
|
|
HexList(list, &count, 1);
|
|
fnFillMemory(&addr1, value2, list, count);
|
|
break;
|
|
case 'g':
|
|
#ifdef KERNEL
|
|
if (CrashFileName) {
|
|
dprintf( "you cannot do a go on a crash dump\n" );
|
|
break;
|
|
}
|
|
#endif
|
|
parseGoCmd(
|
|
#ifndef KERNEL
|
|
NULL, FALSE
|
|
#endif
|
|
);
|
|
break;
|
|
#ifdef KERNEL
|
|
case 'i':
|
|
ch = (UCHAR)tolower(*pchCommand);
|
|
pchCommand++;
|
|
if (ch != 'b' && ch != 'w' && ch != 'd')
|
|
error(SYNTAX);
|
|
fnInputIo(GetExpression(), ch);
|
|
break;
|
|
#endif
|
|
case 'j':{
|
|
PUCHAR pch, start;
|
|
|
|
if (GetExpression()) {
|
|
pch = pchCommand;
|
|
|
|
// Find a semicolon or a quote
|
|
|
|
while(*pch&&*pch!=';'&&*pch!='\'') pch++;
|
|
if (*pch==';') *pch=0;
|
|
else
|
|
if (*pch) {
|
|
*pch=' ';
|
|
// Find the closing quote
|
|
while(*pch&&*pch!='\'') pch++;
|
|
*pch=0;
|
|
}
|
|
}
|
|
else {
|
|
start = pch = pchCommand;
|
|
|
|
// Find a semicolon or a quote
|
|
|
|
while(*pch&&*pch!=';'&&*pch!='\'') pch++;
|
|
if (*pch==';') start = ++pch;
|
|
else
|
|
if (*pch) {
|
|
pch++;
|
|
while(*pch&&*pch++!='\'');
|
|
while(*pch&&(*pch==';'||*pch==' ')) pch++;
|
|
start = pch;
|
|
}
|
|
while(*pch&&*pch!=';'&&*pch!='\'') pch++;
|
|
if (*pch==';') *pch=0;
|
|
else
|
|
if (*pch) {
|
|
*pch=' ';
|
|
// Find the closing quote
|
|
while(*pch&&*pch!='\'') pch++;
|
|
*pch=0;
|
|
}
|
|
pchCommand = start;
|
|
}
|
|
ch = *pchCommand++;
|
|
goto EVALUATE;
|
|
}
|
|
|
|
case 'k':
|
|
value1 = 0;
|
|
#ifdef i386
|
|
value2 = (ULONG)GetRegValue(REGEIP);
|
|
#else
|
|
value2 = 0;
|
|
#endif
|
|
parseStackTrace(&traceType, &paddr2, &value1, &value2, &value3);
|
|
DoStackTrace( paddr2->off, value1, value2, value3, traceType );
|
|
#ifdef KERNEL
|
|
if (fSwitched) {
|
|
RestoreProcessorState();
|
|
fSwitched = FALSE;
|
|
}
|
|
#endif
|
|
break;
|
|
case 'l':
|
|
ch = (UCHAR)tolower(*pchCommand);
|
|
if (ch == 'n') {
|
|
pchCommand++;
|
|
if ((ch = PeekChar()) != '\0' && ch != ';')
|
|
GetAddrExpression(X86REGCS, &addr1);
|
|
else
|
|
GetRegPCValue(&addr1);
|
|
fnListNear(Flat(addr1));
|
|
} else if (ch == 'm') {
|
|
pchCommand++;
|
|
DumpModuleTable( FALSE );
|
|
} else if (ch == 'd') {
|
|
PIMAGE_INFO p;
|
|
pchCommand++;
|
|
while (*pchCommand && *pchCommand == ' ') pchCommand++;
|
|
_strupr( pchCommand );
|
|
for (p=pProcessHead->pImageHead; p; p=p->pImageNext) {
|
|
if (MatchPattern( p->szModuleName, pchCommand )) {
|
|
SymLoadModule(
|
|
pProcessCurrent->hProcess,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
(ULONG)p->lpBaseOfImage,
|
|
0
|
|
);
|
|
}
|
|
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
break;
|
|
}
|
|
}
|
|
while (*pchCommand) pchCommand++;
|
|
|
|
} else {
|
|
error(SYNTAX);
|
|
}
|
|
|
|
break;
|
|
case 'm':
|
|
{
|
|
ADDR TempAddr;
|
|
|
|
GetRange(&addr1, &value2, &fLength, 1
|
|
#ifdef i386
|
|
, REGDS
|
|
#endif
|
|
);
|
|
GetAddrExpression(X86REGDS, &TempAddr);
|
|
fnMoveMemory(&addr1, value2, &TempAddr);
|
|
}
|
|
break;
|
|
case 'n':
|
|
if ((ch = PeekChar()) != '\0' && ch != ';')
|
|
if (ch == '8') {
|
|
pchCommand++;
|
|
baseDefault = 8;
|
|
}
|
|
else if (ch == '1') {
|
|
ch = *++pchCommand;
|
|
if (ch == '0' || ch == '6') {
|
|
pchCommand++;
|
|
baseDefault = 10 + ch - '0';
|
|
}
|
|
else
|
|
error(SYNTAX);
|
|
}
|
|
else
|
|
error(SYNTAX);
|
|
dprintf("base is %ld\n", baseDefault);
|
|
break;
|
|
#ifdef KERNEL
|
|
case 'o':
|
|
ch = (UCHAR)tolower(*pchCommand);
|
|
pchCommand++;
|
|
if (ch == 'b')
|
|
value2 = 1;
|
|
else if (ch == 'w')
|
|
value2 = 2;
|
|
else if (ch == 'd')
|
|
value2 = 4;
|
|
else
|
|
error(SYNTAX);
|
|
value1 = GetExpression();
|
|
value2 = HexValue(value2);
|
|
fnOutputIo(value1, value2, ch);
|
|
break;
|
|
#endif
|
|
case 'w':
|
|
case 'p':
|
|
case 't':
|
|
#ifdef KERNEL
|
|
if (tolower(pchCommand[0]) == 'r' &&
|
|
tolower(pchCommand[1]) == 'm' &&
|
|
tolower(pchCommand[2]) == 's' &&
|
|
tolower(pchCommand[3]) == 'r') {
|
|
|
|
pchCommand +=4;
|
|
value1 = GetExpression();
|
|
if (!NT_SUCCESS(DbgKdWriteMsr (value1, HexValueL(8)))) {
|
|
dprintf ("no such msr\n");
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (CrashFileName) {
|
|
dprintf( "you cannot do a t/p/w on a crash dump\n" );
|
|
break;
|
|
}
|
|
#endif
|
|
parseStepTrace(
|
|
#ifndef KERNEL
|
|
NULL, FALSE,
|
|
#endif
|
|
ch);
|
|
break;
|
|
case 'q':
|
|
if (PeekChar() != '\0')
|
|
error(SYNTAX);
|
|
dprintf("quit:\n");
|
|
|
|
#if defined(KERNEL) && defined(i386)
|
|
// disable and remove any data breakpoints
|
|
|
|
// SetDregValue(6, 0);
|
|
SetDregValue(7, 0);
|
|
ChangeKdRegContext(0, NULL);
|
|
#endif
|
|
|
|
#ifndef KERNEL
|
|
if (dwPidToDebug == 0xffffffff) {
|
|
#ifndef CHICAGO
|
|
UNICODE_STRING UnicodeString;
|
|
ULONG Parameters[ 2 ];
|
|
ULONG Response;
|
|
NTSTATUS Status;
|
|
BOOLEAN WasEnabled;
|
|
|
|
Status = RtlAdjustPrivilege( SE_SHUTDOWN_PRIVILEGE,
|
|
(BOOLEAN)TRUE,
|
|
TRUE,
|
|
&WasEnabled
|
|
);
|
|
|
|
if (Status == STATUS_NO_TOKEN) {
|
|
|
|
//
|
|
// No thread token, use the process token
|
|
//
|
|
|
|
Status = RtlAdjustPrivilege( SE_SHUTDOWN_PRIVILEGE,
|
|
(BOOLEAN)TRUE,
|
|
FALSE,
|
|
&WasEnabled
|
|
);
|
|
}
|
|
RtlInitUnicodeString( &UnicodeString, L"Debug of Windows SubSystem" );
|
|
Parameters[ 0 ] = (ULONG)&UnicodeString;
|
|
Parameters[ 1 ] = (ULONG)STATUS_UNSUCCESSFUL;
|
|
NtRaiseHardError( STATUS_SYSTEM_PROCESS_TERMINATED,
|
|
2,
|
|
1,
|
|
Parameters,
|
|
OptionShutdownSystem,
|
|
&Response
|
|
);
|
|
#else
|
|
OutputDebugString("Subsystem shutdown\n");
|
|
DebugBreak();
|
|
#endif
|
|
}
|
|
|
|
ExitProcess(0);
|
|
#else
|
|
SymCleanup( KD_SYM_HANDLE );
|
|
exit(0);
|
|
#endif
|
|
case 'r':
|
|
#ifdef KERNEL
|
|
if (tolower(pchCommand[0]) == 'd' &&
|
|
tolower(pchCommand[1]) == 'm' &&
|
|
tolower(pchCommand[2]) == 's' &&
|
|
tolower(pchCommand[3]) == 'r') {
|
|
|
|
pchCommand +=4;
|
|
value1 = GetExpression();
|
|
if (NT_SUCCESS(DbgKdReadMsr (value1, &valueL))) {
|
|
dprintf ("msr[%x] = %08x:%08x\n",
|
|
value1, (ULONG) (valueL >> 32), (ULONG) valueL);
|
|
} else {
|
|
dprintf ("no such msr\n");
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
parseRegCmd();
|
|
#ifdef KERNEL
|
|
if (fSwitched) {
|
|
RestoreProcessorState();
|
|
fSwitched = FALSE;
|
|
}
|
|
#endif
|
|
break;
|
|
case 's':
|
|
ch = (UCHAR)tolower(*pchCommand);
|
|
|
|
if (ch == 's') {
|
|
pchCommand++;
|
|
fnSetSuffix();
|
|
}
|
|
#ifdef KERNEL
|
|
else if (ch == 'q') {
|
|
pchCommand++;
|
|
NotStupid = !NotStupid;
|
|
dprintf("Quiet mode is %s\n", NotStupid? "ON" : "OFF");
|
|
}
|
|
#endif
|
|
else if (ch == 'x') {
|
|
pchCommand++;
|
|
fnSetException();
|
|
}
|
|
else {
|
|
GetRange(&addr1, &value2, &fLength, 1
|
|
#ifdef i386
|
|
, REGDS
|
|
#endif
|
|
);
|
|
HexList(list, &count, 1);
|
|
fnSearchMemory(&addr1, value2, list, count);
|
|
}
|
|
break;
|
|
|
|
case 'u':
|
|
ch = (UCHAR)tolower(*pchCommand);
|
|
#ifdef KERNEL
|
|
if (ch == 'x') {
|
|
pchCommand += 1;
|
|
}
|
|
#endif
|
|
value1 = Flat(unasmDefault);
|
|
value2 = 8; // eight instructions
|
|
fLength = TRUE; // length, not ending addr
|
|
addr1 = unasmDefault;
|
|
GetRange(&addr1, &value2, &fLength, 0
|
|
#ifdef i386
|
|
, REGCS
|
|
#endif
|
|
);
|
|
unasmDefault = addr1;
|
|
#ifdef KERNEL
|
|
if (ch == 'x') {
|
|
ADDR addr;
|
|
char text[128];
|
|
if (X86BiosBaseAddress == 0) {
|
|
X86BiosBaseAddress = GetExpressionRoutine("hal!HalpEisaMemoryBase");
|
|
ADDR32(&addr,X86BiosBaseAddress);
|
|
GetMemString( &addr, (PUCHAR)&X86BiosBaseAddress, 4 );
|
|
}
|
|
addr = unasmDefault;
|
|
addr.flat += (X86BiosBaseAddress + (addr.seg<<4));
|
|
addr.off = addr.flat;
|
|
addr.type = ADDR_V86 | INSTR_POINTER;
|
|
for (value2=0; value2<8; value2++) {
|
|
X86disasm( &addr, text, TRUE );
|
|
addr.flat = addr.off;
|
|
dprintf("%s", text );
|
|
}
|
|
unasmDefault = addr;
|
|
unasmDefault.off -= (X86BiosBaseAddress + (addr.seg<<4));
|
|
unasmDefault.flat = unasmDefault.off;
|
|
} else
|
|
#endif
|
|
{
|
|
fnUnassemble(&unasmDefault, value2, fLength,
|
|
(BOOLEAN) (value1 != Flat(unasmDefault)));
|
|
}
|
|
break;
|
|
|
|
case 'v':
|
|
if (_stricmp(pchCommand,"ersion")==0) {
|
|
pchCommand += strlen(pchCommand);
|
|
PrintVersionInformation();
|
|
}
|
|
break;
|
|
|
|
case 'x':
|
|
parseExamine();
|
|
break;
|
|
|
|
case ';':
|
|
case '\0':
|
|
pchCommand--;
|
|
break;
|
|
|
|
default:
|
|
error(SYNTAX);
|
|
break;
|
|
}
|
|
do
|
|
ch = *pchCommand++;
|
|
while (ch == ' ' || ch == '\t');
|
|
if (ch != ';' && ch != '\0')
|
|
error(EXTRACHARS);
|
|
pchCommand--;
|
|
}
|
|
while (cmdState == 'c');
|
|
|
|
#if defined(KERNEL)
|
|
SetWaitCtrlHandler();
|
|
#endif
|
|
}
|
|
|
|
void RemoveDelChar (PUCHAR pBuffer)
|
|
{
|
|
PUCHAR pBufferOld = pBuffer;
|
|
PUCHAR pBufferNew;
|
|
UCHAR ch;
|
|
|
|
do {
|
|
ch = *pBufferOld++;
|
|
if (ch == '\r' || ch == '\n')
|
|
ch = '\0';
|
|
}
|
|
while (ch != '\0' && ch != 0x7f);
|
|
|
|
pBufferNew = pBufferOld - 1;
|
|
|
|
while (ch != '\0') {
|
|
if (ch == 0x7f) {
|
|
if (pBufferNew > pBuffer)
|
|
pBufferNew--;
|
|
}
|
|
else
|
|
*pBufferNew++ = ch;
|
|
ch = *pBufferOld++;
|
|
if (ch == '\r' || ch == '\n')
|
|
ch = '\0';
|
|
}
|
|
|
|
*pBufferNew = '\0';
|
|
}
|
|
|
|
|
|
/*** error - error reporting and recovery
|
|
*
|
|
* Purpose:
|
|
* To output an error message with a location indicator
|
|
* of the problem. Once output, the command line is reset
|
|
* and the command processor is restarted.
|
|
*
|
|
* Input:
|
|
* errorcode - number of error to output
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* Return is made via longjmp to start of command processor.
|
|
*
|
|
*************************************************************************/
|
|
|
|
static char szBlanks[] =
|
|
" "
|
|
" "
|
|
" "
|
|
" ^ ";
|
|
void error (ULONG errcode)
|
|
{
|
|
ULONG count = cbPrompt;
|
|
UCHAR *pchtemp = pchStart;
|
|
|
|
if (fDisableErrorPrint) goto skiperrorprint;
|
|
|
|
while (pchtemp < pchCommand)
|
|
if (*pchtemp++ == '\t')
|
|
count = (count + 7) & ~7;
|
|
else
|
|
count++;
|
|
|
|
dprintf(&szBlanks[sizeof(szBlanks) - (count + 1)]);
|
|
|
|
switch (errcode) {
|
|
case OVERFLOW:
|
|
dprintf("Overflow");
|
|
break;
|
|
case SYNTAX:
|
|
dprintf("Syntax");
|
|
break;
|
|
case BADRANGE:
|
|
dprintf("Range");
|
|
break;
|
|
case VARDEF:
|
|
dprintf("Variable definition");
|
|
break;
|
|
case EXTRACHARS:
|
|
dprintf("Extra character");
|
|
break;
|
|
case LISTSIZE:
|
|
dprintf("List size");
|
|
break;
|
|
case STRINGSIZE:
|
|
dprintf("String size");
|
|
break;
|
|
case MEMORY:
|
|
dprintf("Memory access");
|
|
break;
|
|
case BADREG:
|
|
dprintf("Bad register");
|
|
break;
|
|
case BADOPCODE:
|
|
dprintf("Bad opcode");
|
|
break;
|
|
case SUFFIX:
|
|
dprintf("Opcode suffix");
|
|
break;
|
|
case OPERAND:
|
|
dprintf("Operand");
|
|
break;
|
|
case ALIGNMENT:
|
|
dprintf("Alignment");
|
|
break;
|
|
case PREFIX:
|
|
dprintf("Opcode prefix");
|
|
break;
|
|
case DISPLACEMENT:
|
|
dprintf("Displacement");
|
|
break;
|
|
case BPLISTFULL:
|
|
dprintf("No breakpoint available");
|
|
break;
|
|
case BPDUPLICATE:
|
|
dprintf("Duplicate breakpoint");
|
|
break;
|
|
case UNIMPLEMENT:
|
|
dprintf("Unimplemented");
|
|
break;
|
|
case AMBIGUOUS:
|
|
dprintf("Ambiguous symbol");
|
|
break;
|
|
#ifndef KERNEL
|
|
case BADTHREAD:
|
|
dprintf("Illegal thread");
|
|
break;
|
|
case BADPROCESS:
|
|
dprintf("Illegal process");
|
|
break;
|
|
#endif
|
|
case FILEREAD:
|
|
dprintf("File read");
|
|
break;
|
|
case LINENUMBER:
|
|
dprintf("Line number");
|
|
break;
|
|
case BADSEL:
|
|
dprintf("Bad selector");
|
|
break;
|
|
case BADSEG:
|
|
dprintf("Bad segment");
|
|
break;
|
|
case SYMTOOSMALL:
|
|
dprintf("Symbol only 1 character");
|
|
break;
|
|
#ifdef KERNEL
|
|
case BPIONOTSUP:
|
|
dprintf("I/O breakpoints not supported");
|
|
break;
|
|
#endif
|
|
default:
|
|
dprintf("Unknown");
|
|
break;
|
|
}
|
|
dprintf(" error in '%s'\n", pchStart);
|
|
skiperrorprint:
|
|
if (fJmpBuf) // TEMP TEMP
|
|
longjmp(asm_return,1); // TEMP TEMP
|
|
else // TEMP TEMP
|
|
longjmp(cmd_return, 1); // TEMP TEMP
|
|
}
|
|
|
|
|
|
void fnInteractiveEnterMemory (PADDR Address, ULONG Size)
|
|
{
|
|
UCHAR chEnter[1024];
|
|
PUCHAR pchEnter;
|
|
ULONG Content;
|
|
PUCHAR pchCmdSaved = pchCommand;
|
|
PUCHAR pchStartSaved = pchStart;
|
|
ULONG EnteredValue;
|
|
UCHAR ch;
|
|
|
|
cbPrompt = 9 + 2 * Size;
|
|
|
|
while (TRUE) {
|
|
switch (Size) {
|
|
case 1:
|
|
if (!GetMemByte(Address, (PUCHAR)&Content))
|
|
error(MEMORY);
|
|
dprintAddr(Address);
|
|
dprintf("%02x", (UCHAR)Content);
|
|
break;
|
|
case 2:
|
|
if (!GetMemWord(Address, (PUSHORT)&Content))
|
|
error(MEMORY);
|
|
dprintAddr(Address);
|
|
dprintf("%04x", (USHORT)Content);
|
|
break;
|
|
case 4:
|
|
if (!GetMemDword(Address, &Content))
|
|
error(MEMORY);
|
|
dprintAddr(Address);
|
|
dprintf("%08lx", Content);
|
|
break;
|
|
}
|
|
|
|
NtsdPrompt(" ", chEnter, 15);
|
|
RemoveDelChar(chEnter);
|
|
pchEnter = chEnter;
|
|
|
|
if (*pchEnter == '\0') {
|
|
pchCommand = pchCmdSaved;
|
|
pchStart = pchStartSaved;
|
|
return;
|
|
}
|
|
|
|
ch = *pchEnter;
|
|
while (ch == ' ' || ch == '\t' || ch == ';')
|
|
ch = *++pchEnter;
|
|
|
|
if (*pchEnter == '\0') {
|
|
AddrAdd(Address, Size);
|
|
continue;
|
|
}
|
|
|
|
pchCommand = pchEnter;
|
|
pchStart = pchEnter;
|
|
EnteredValue = HexValue(Size);
|
|
|
|
switch (Size) {
|
|
case 1:
|
|
if (!SetMemByte(Address, (UCHAR)EnteredValue))
|
|
error(MEMORY);
|
|
break;
|
|
case 2:
|
|
if (!SetMemWord(Address, (USHORT)EnteredValue))
|
|
error(MEMORY);
|
|
break;
|
|
case 4:
|
|
if (!SetMemDword(Address, EnteredValue))
|
|
error(MEMORY);
|
|
break;
|
|
}
|
|
AddrAdd(Address, Size);
|
|
}
|
|
}
|
|
|
|
/*** HexList - parse list of hex values
|
|
*
|
|
* Purpose:
|
|
* With the current location of the command string in
|
|
* pchCommand, attempt to parse hex number of byte size
|
|
* bytesize as bytes into the character array pointed by
|
|
* parray. The value pointed by *pcount contains the bytes
|
|
* of the array filled.
|
|
*
|
|
* Input:
|
|
* pchCommand - start of command string
|
|
* bytesize - size of items in bytes
|
|
*
|
|
* Output:
|
|
* parray - pointer to byte array to fill
|
|
* pcount - pointer to value of bytes filled
|
|
*
|
|
* Exceptions:
|
|
* error exit:
|
|
* LISTSIZE: too many items in list
|
|
* SYNTAX: illegal separator of list items
|
|
* OVERFLOW: item value too large
|
|
*
|
|
*************************************************************************/
|
|
|
|
void HexList (PUCHAR parray, ULONG *pcount, ULONG bytesize)
|
|
{
|
|
UCHAR ch;
|
|
ULONG value;
|
|
ULONG count = 0;
|
|
ULONG i;
|
|
|
|
while ((ch = PeekChar()) != '\0' && ch != ';') {
|
|
if (count == STRLISTSIZE)
|
|
error(LISTSIZE);
|
|
value = HexValue(bytesize);
|
|
for (i = 0; i < bytesize; i++) {
|
|
*parray++ = (char) value;
|
|
value >>= 8;
|
|
}
|
|
count += bytesize;
|
|
}
|
|
*pcount = count;
|
|
}
|
|
|
|
ULONGLONG HexValueL (ULONG bytesize)
|
|
{
|
|
UCHAR ch;
|
|
ULONGLONG value = 0;
|
|
ULONGLONG ovfl;
|
|
|
|
// hack for broken x86 compiler which can shift 64bit correctly
|
|
if (bytesize <= 4) {
|
|
ovfl = (1L << (8 * bytesize - 4));
|
|
} else {
|
|
ovfl = (0x100000000L << (8 * (bytesize-4) - 4));
|
|
}
|
|
|
|
if (PeekChar() == '\'') {
|
|
pchCommand++;
|
|
while (TRUE) {
|
|
ch = *pchCommand++;
|
|
if (ch == '\'') {
|
|
if (*pchCommand != '\'') {
|
|
break;
|
|
}
|
|
ch = *pchCommand++;
|
|
}
|
|
else
|
|
if (ch == '\\') {
|
|
ch = *pchCommand++;
|
|
}
|
|
value = (value << 8) | ch;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
while (TRUE) {
|
|
ch = *pchCommand++;
|
|
if (ch >= '0' && ch <= '9')
|
|
ch -= '0';
|
|
else if (ch >= 'a' && ch <= 'f')
|
|
ch -= 'a' - 10;
|
|
else if (ch >= 'A' && ch <= 'F')
|
|
ch -= 'A' - 10;
|
|
else if (ch == ' ' || ch == '\t' || ch == '\0' || ch == ';')
|
|
break;
|
|
else
|
|
error(SYNTAX);
|
|
if (value >= ovfl)
|
|
error(OVERFLOW);
|
|
value = value * 0x10 + ch;
|
|
}
|
|
if (ch == '\0' || ch == ';')
|
|
pchCommand--;
|
|
|
|
return value;
|
|
}
|
|
|
|
/*** AsciiList - process string for ascii list
|
|
*
|
|
* Purpose:
|
|
* With the current location of the command string in
|
|
* pchCommand, attempt to parse ascii characters into the
|
|
* character array pointed by parray. The value pointed
|
|
* by pcount contains the bytes of the array filled. The
|
|
* string must start with a double quote. The string must
|
|
* end with a quote or be at the end of the input line.
|
|
*
|
|
* Input:
|
|
* pchCommand - start of command string
|
|
*
|
|
* Output:
|
|
* parray - pointer to byte array to fill
|
|
* pcount - pointer to value of bytes filled
|
|
*
|
|
* Exceptions:
|
|
* error exit:
|
|
* STRINGSIZE: string length too long
|
|
* SYNTAX: no leading double quote
|
|
*
|
|
*************************************************************************/
|
|
|
|
void AsciiList (PUCHAR parray, ULONG *pcount)
|
|
{
|
|
UCHAR ch;
|
|
ULONG count = 0;
|
|
|
|
if (PeekChar() != '"')
|
|
error(SYNTAX);
|
|
pchCommand++;
|
|
while ((ch = *pchCommand++) != '"' && ch != '\0'
|
|
&& count++ < STRLISTSIZE)
|
|
*parray++ = ch;
|
|
if (count == STRLISTSIZE)
|
|
error(STRINGSIZE);
|
|
if (ch == '\0' || ch == ';')
|
|
pchCommand--;
|
|
*pcount = count;
|
|
}
|
|
|
|
/*** GetIdList - get mask value from item list of values 0-9
|
|
*
|
|
* Purpose:
|
|
* From the current command string in pchCommand, parse single
|
|
* decimal digits and build a 10-bit mask corresponding to the
|
|
* items parsed. The 1-bit denotes a "0" item up to 200-bit for
|
|
* a "9" item. An asterisk (*) is a wildcard value that returns
|
|
* the full 3ff mask.
|
|
*
|
|
* Input:
|
|
* pchCommand - pointer to command string
|
|
*
|
|
* Returns:
|
|
* 10-bit mask - 1-bit for "0" up to 200-bit for "9"
|
|
*
|
|
* Exceptions:
|
|
* error exit:
|
|
* SYNTAX - illegal character or item too large
|
|
*
|
|
*************************************************************************/
|
|
|
|
ULONG GetIdList (void)
|
|
{
|
|
ULONG value = 0;
|
|
UCHAR ch;
|
|
UCHAR digits[5]; // allow up to four digits
|
|
int i;
|
|
|
|
//
|
|
// Change to allow more than 32 break points to be set. Use
|
|
// break point numbers instead of masks.
|
|
//
|
|
|
|
if ((ch = PeekChar()) == '*') {
|
|
value = ALL_BREAKPOINTS;
|
|
pchCommand++;
|
|
} else {
|
|
for (i = 0; i < 4 ; i++) {
|
|
if (ch >= '0' && ch <= '9') {
|
|
digits[i] = ch;
|
|
ch = *++pchCommand;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
digits[i] = '\0';
|
|
|
|
if (ch == '\0' || ch == ';' || ch == ' ' || ch == '\t') {
|
|
value = strtol (digits, NULL, 10);
|
|
} else {
|
|
error (SYNTAX);
|
|
}
|
|
}
|
|
|
|
if (value >= MAX_NUMBER_OF_BREAKPOINTS && value != ALL_BREAKPOINTS) {
|
|
error (SYNTAX);
|
|
}
|
|
|
|
return (value);
|
|
}
|
|
|
|
/*** fnEvaluateExp - evaluate expression
|
|
*
|
|
* Purpose:
|
|
* Function for the "?<exp>" command.
|
|
*
|
|
* Evaluate the expression and output it in both
|
|
* hexadecimal and decimal.
|
|
*
|
|
* Input:
|
|
* pchCommand - pointer to operand in command line
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnEvaluateExp (void)
|
|
{
|
|
LONG value;
|
|
|
|
value = GetExpression();
|
|
dprintf("Evaluate expression: %ld = 0x%08lx\n", value, value);
|
|
}
|
|
|
|
/*** fnDotCommand - parse and execute dot command
|
|
*
|
|
* Purpose:
|
|
* Parse and execute all commands starting with a dot ('.').
|
|
*
|
|
* Input:
|
|
* pchCommand - pointer to character after dot in command line
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnDotCommand(void)
|
|
{
|
|
ULONG index = 0;
|
|
UCHAR chCmd[12];
|
|
UCHAR ch;
|
|
ADDR tempAddr;
|
|
NTSTATUS Status;
|
|
|
|
// if there was nothing after the dot look up a function
|
|
if (!*pchCommand) {
|
|
PSYMBOL pSymbol;
|
|
GetRegPCValue(&tempAddr);
|
|
pSymbol = GetFunctionFromOffset( Flat(tempAddr) );
|
|
return;
|
|
}
|
|
|
|
|
|
// read in up to the first twelve alpha characters into
|
|
// chCmd, converting to lower case
|
|
|
|
while (index < 12) {
|
|
ch = (UCHAR)tolower(*pchCommand);
|
|
if (ch >= 'a' && ch <= 'z') {
|
|
chCmd[index++] = ch;
|
|
pchCommand++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
// if all characters read, then too big, else terminate
|
|
|
|
if (index == 12)
|
|
error(SYNTAX);
|
|
chCmd[index] = '\0';
|
|
|
|
// test for the commands
|
|
|
|
#if defined(KERNEL)
|
|
if (!strcmp(chCmd, "reboot") && ch == '\0') {
|
|
chLastCommand[0] = '\0'; // null out .reboot command
|
|
DbgKdReboot(); // reboot demands trailing null
|
|
longjmp(reboot, 1); // ...and wait for event
|
|
}
|
|
else
|
|
if (!strcmp(chCmd, "cache"))
|
|
fnCache();
|
|
else
|
|
if (!strcmp(chCmd, "crash")) {
|
|
chLastCommand[0] = '\0';
|
|
DbgKdCrash( CRASH_BUGCHECK_CODE );
|
|
longjmp(reboot, 1);
|
|
} else
|
|
if (!strcmp(chCmd, "pagein")) {
|
|
if ((ch = PeekChar()) != '\0') {
|
|
index = GetExpression();
|
|
Status = DbgKdPageIn( index );
|
|
if (Status == STATUS_SUCCESS) {
|
|
dprintf( "paging in 0x%08x\n", index );
|
|
cmdState = 'g';
|
|
} else {
|
|
dprintf( "could not page in 0x%08x\n", index );
|
|
}
|
|
}
|
|
} else
|
|
#endif
|
|
if (!strcmp(chCmd, "logopen"))
|
|
fnLogOpen(FALSE);
|
|
else if (!strcmp(chCmd, "logappend"))
|
|
fnLogOpen(TRUE);
|
|
else if (!strcmp(chCmd, "logclose"))
|
|
fnLogClose();
|
|
else
|
|
dprintf("unknown . command\n");
|
|
}
|
|
|
|
/*** fnLogOpen - open log file
|
|
*
|
|
* Purpose:
|
|
* Closes any open log file and creates or appends a new one.
|
|
*
|
|
* Input:
|
|
* pchCommand - pointer to start of prospective filename
|
|
* fAppend - set if appending, else create new file
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnLogOpen (BOOLEAN fAppend)
|
|
{
|
|
UCHAR ch;
|
|
UCHAR chFile[_MAX_PATH];
|
|
PUCHAR pchFile = chFile;
|
|
|
|
// extract a pathname to use to name file
|
|
|
|
ch = PeekChar();
|
|
while (ch != '\0' && ch != ';' && ch != ' ' && ch != '\t') {
|
|
*pchFile++ = ch;
|
|
ch = *++pchCommand;
|
|
}
|
|
*pchFile = '\0';
|
|
|
|
// close existing opened log file
|
|
|
|
fnLogClose();
|
|
|
|
// open log file with specified or default name
|
|
// either for append or create mode
|
|
|
|
if (!chFile[0]) {
|
|
fAppend = fLogAppend;
|
|
if (!( pchFile = LogFileName )) pchFile =
|
|
#ifdef KERNEL
|
|
"kd.log";
|
|
#else
|
|
"ntsd.log";
|
|
#endif
|
|
}
|
|
else pchFile = chFile;
|
|
|
|
if (fAppend)
|
|
loghandle = _open(pchFile, O_APPEND | O_CREAT | O_RDWR,
|
|
S_IREAD | S_IWRITE);
|
|
else
|
|
loghandle = _open(pchFile, O_APPEND | O_CREAT | O_TRUNC | O_RDWR,
|
|
S_IREAD | S_IWRITE);
|
|
|
|
if (loghandle != -1)
|
|
dprintf("log file opened\n");
|
|
else
|
|
dprintf("log file could not be opened\n");
|
|
}
|
|
|
|
/*** fnLogClose - close log file
|
|
*
|
|
* Purpose:
|
|
* Closes any open log file.
|
|
*
|
|
* Input:
|
|
* loghandler - file handle of open log file
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnLogClose (void)
|
|
{
|
|
if (loghandle != -1) {
|
|
dprintf("closing open log file\n");
|
|
_close(loghandle);
|
|
loghandle = -1;
|
|
}
|
|
}
|
|
|
|
/*** dprintf - debugger printf routine
|
|
*
|
|
* Purpose:
|
|
* Replaces "printf" function for debugger to allow
|
|
* logging of all output to file. If file variable loghandle
|
|
* is not -1, output to it in addition to standard output.
|
|
*
|
|
* Input:
|
|
* loghandle - file variable of log file
|
|
* fDebugOutput - (user-mode only) set if output to debugging screen
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
HANDLE ConsoleInputHandle;
|
|
HANDLE ConsoleOutputHandle;
|
|
|
|
#define DPRINTF_SIZE (1024 * 4)
|
|
unsigned char outbuffer[DPRINTF_SIZE];
|
|
|
|
int _CRTAPI1 dprintf (char *format, ...)
|
|
{
|
|
DWORD cb, whocares;
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, format);
|
|
|
|
__try {
|
|
cb = _vsnprintf(outbuffer, DPRINTF_SIZE-1, format, arg_ptr);
|
|
|
|
if (loghandle != -1) {
|
|
_write(loghandle, outbuffer, cb);
|
|
}
|
|
|
|
#ifndef KERNEL
|
|
if (fDebugOutput) {
|
|
if (DbgPrint("%s", outbuffer) == STATUS_BREAKPOINT) {
|
|
fControlC = TRUE;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
WriteFile(
|
|
ConsoleOutputHandle,
|
|
outbuffer,
|
|
cb,
|
|
&whocares,
|
|
NULL
|
|
);
|
|
}
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
printf( "DPRINTF failed - Exception code == %x\n", GetExceptionCode() );
|
|
}
|
|
|
|
va_end(arg_ptr);
|
|
return cb;
|
|
}
|
|
|
|
/*** lprintf - log file print
|
|
*
|
|
* Purpose:
|
|
* Print line only to log file. Used to echo input line that
|
|
* is already printed on standard output by gets() function.
|
|
*
|
|
* Input:
|
|
* loghandle - file handle variable of log file
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void lprintf (char *string)
|
|
{
|
|
if (loghandle != -1)
|
|
_write(loghandle, string, strlen(string));
|
|
}
|
|
|
|
void parseScriptCmd(void)
|
|
{
|
|
if (!(streamCmd = fopen(pchCommand,"r"))) streamCmd = stdin;
|
|
}
|
|
|
|
#ifndef KERNEL
|
|
void parseProcessCmds (void)
|
|
{
|
|
UCHAR ch;
|
|
PPROCESS_INFO pProcess;
|
|
ULONG index;
|
|
|
|
ch = PeekChar();
|
|
if (ch == '\0' || ch == ';')
|
|
fnOutputProcessInfo(NULL);
|
|
else {
|
|
pProcess = pProcessCurrent;
|
|
pchCommand++;
|
|
if (ch == '.')
|
|
;
|
|
else if (ch == '#')
|
|
pProcess = pProcessEvent;
|
|
else if (ch == '*')
|
|
pProcess = NULL;
|
|
else if (ch >= '0' && ch <= '9') {
|
|
index = 0;
|
|
do {
|
|
index = index * 10 + ch - '0';
|
|
if (index > 1000)
|
|
index = 1000;
|
|
ch = *pchCommand++;
|
|
}
|
|
while (ch >= '0' && ch <= '9');
|
|
pchCommand--;
|
|
if (index >= 1000)
|
|
error(BADPROCESS);
|
|
pProcess = pProcessFromIndex((UCHAR)index);
|
|
if (pProcess == NULL)
|
|
error(BADPROCESS);
|
|
}
|
|
else
|
|
pchCommand--;
|
|
ch = PeekChar();
|
|
if (ch == '\0' || ch == ';')
|
|
fnOutputProcessInfo(pProcess);
|
|
else {
|
|
pchCommand++;
|
|
if (tolower(ch) == 's') {
|
|
if (pProcess == NULL)
|
|
error(BADPROCESS);
|
|
pProcessCurrent = pProcess;
|
|
if (pProcessCurrent->pThreadCurrent == NULL)
|
|
pProcessCurrent->pThreadCurrent =
|
|
pProcessCurrent->pThreadHead;
|
|
ChangeRegContext(pProcessCurrent->pThreadCurrent);
|
|
OutDisCurrent(TRUE, TRUE);
|
|
GetRegPCValue(&assemDefault);
|
|
dumpDefault = unasmDefault = assemDefault;
|
|
}
|
|
else
|
|
pchCommand--;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef KERNEL
|
|
void fnOutputProcessInfo (PPROCESS_INFO pProcessOut)
|
|
{
|
|
PPROCESS_INFO pProcess;
|
|
|
|
pProcess = pProcessHead;
|
|
while (pProcess) {
|
|
if (pProcessOut == NULL || pProcessOut == pProcess)
|
|
dprintf("%3ld\tid: %lx\tname: %s\n", pProcess->index,
|
|
pProcess->dwProcessId, pProcess->pImageHead->szImagePath);
|
|
pProcess = pProcess->pProcessNext;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef KERNEL
|
|
void parseThreadCmds (void)
|
|
{
|
|
UCHAR ch;
|
|
PTHREAD_INFO pThread;
|
|
ULONG index, value1, value3, value4;
|
|
ADDR value2;
|
|
PADDR pvalue2=&value2;
|
|
ULONG traceType;
|
|
BOOLEAN fFreezeThread = FALSE;
|
|
|
|
ch = PeekChar();
|
|
if (ch == '\0' || ch == ';')
|
|
fnOutputThreadInfo(NULL);
|
|
else {
|
|
pThread = pProcessCurrent->pThreadCurrent;
|
|
pchCommand++;
|
|
if (ch == '.')
|
|
fFreezeThread = TRUE;
|
|
else if (ch == '#') {
|
|
fFreezeThread = TRUE;
|
|
pThread = pProcessEvent->pThreadEvent;
|
|
}
|
|
else if (ch == '*')
|
|
pThread = NULL;
|
|
else if (ch >= '0' && ch <= '9') {
|
|
index = 0;
|
|
do {
|
|
index = index * 10 + ch - '0';
|
|
if (index > 1000)
|
|
index = 1000;
|
|
ch = *pchCommand++;
|
|
}
|
|
while (ch >= '0' && ch <= '9');
|
|
pchCommand--;
|
|
if (index >= 1000)
|
|
error(BADTHREAD);
|
|
pThread = pThreadFromIndex((UCHAR)index);
|
|
if (pThread == NULL)
|
|
error(BADTHREAD);
|
|
fFreezeThread = TRUE;
|
|
}
|
|
else
|
|
pchCommand--;
|
|
ch = PeekChar();
|
|
if (ch == '\0' || ch == ';')
|
|
fnOutputThreadInfo(pThread);
|
|
else {
|
|
pchCommand++;
|
|
switch (ch = (UCHAR)tolower(ch)) {
|
|
case 'b':
|
|
ch = (UCHAR)tolower(*pchCommand); pchCommand++;
|
|
if (ch != 'p' && ch != 'a') {
|
|
if (ch == '\0' || ch == ';')
|
|
pchCommand--;
|
|
error(SYNTAX);
|
|
}
|
|
parseBpCmd((BOOLEAN)(ch == 'a'), pThread);
|
|
// TRUE for data bp - FALSE for code
|
|
break;
|
|
case 's':
|
|
if (pThread == NULL)
|
|
error(BADTHREAD);
|
|
pProcessCurrent->pThreadCurrent = pThread;
|
|
ChangeRegContext(pProcessCurrent->pThreadCurrent);
|
|
OutDisCurrent(TRUE, TRUE);
|
|
GetRegPCValue(&assemDefault);
|
|
dumpDefault = unasmDefault = assemDefault;
|
|
break;
|
|
// HACK FOR PDK BUG FIX 3822: Disable ~f and ~u commands
|
|
#if 0
|
|
case 'f':
|
|
case 'u':
|
|
if (pThread == NULL) {
|
|
pThread = pProcessCurrent->pThreadCurrent;
|
|
while (pThread) {
|
|
pThread->fFrozen = (BOOLEAN)(ch == 'f');
|
|
pThread = pThread->pThreadNext;
|
|
}
|
|
}
|
|
else
|
|
pThread->fFrozen = (BOOLEAN)(ch == 'f');
|
|
break;
|
|
#endif
|
|
case 'g':
|
|
parseGoCmd(pThread, fFreezeThread);
|
|
break;
|
|
case 'k':
|
|
if (pThread == NULL)
|
|
error(BADTHREAD);
|
|
ChangeRegContext(pThread);
|
|
value1 = 0;
|
|
#ifdef i386
|
|
value3 = (ULONG)GetRegValue(REGEIP);
|
|
#else
|
|
value3 = 0;
|
|
#endif
|
|
parseStackTrace(&traceType,
|
|
&pvalue2,
|
|
&value1,
|
|
&value3,
|
|
&value4);
|
|
DoStackTrace( pvalue2->off,
|
|
value1,
|
|
value3,
|
|
value4,
|
|
traceType );
|
|
#ifdef KERNEL
|
|
ChangeRegContext(pProcessCurrent->pThreadCurrent);
|
|
#endif
|
|
break;
|
|
case 'r':
|
|
if (pThread == NULL)
|
|
error(BADTHREAD);
|
|
ChangeRegContext(pThread);
|
|
parseRegCmd();
|
|
ChangeRegContext(pProcessCurrent->pThreadCurrent);
|
|
break;
|
|
case 't':
|
|
case 'p':
|
|
parseStepTrace(pThread, fFreezeThread, ch);
|
|
break;
|
|
default:
|
|
error(SYNTAX);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef KERNEL
|
|
void fnOutputThreadInfo (PTHREAD_INFO pThreadOut)
|
|
{
|
|
PTHREAD_INFO pThread;
|
|
THREAD_BASIC_INFORMATION ThreadBasicInfo;
|
|
NTSTATUS Status;
|
|
|
|
pThread = pProcessCurrent->pThreadHead;
|
|
while (pThread) {
|
|
if (pThreadOut == NULL || pThreadOut == pThread) {
|
|
|
|
#ifndef CHICAGO
|
|
Status = NtQueryInformationThread(
|
|
pThread->hThread,
|
|
ThreadBasicInformation,
|
|
&ThreadBasicInfo,
|
|
sizeof(ThreadBasicInfo),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
dprintf("%s: NtQueryInfomationThread failed\n", DebuggerName);
|
|
return;
|
|
}
|
|
#endif
|
|
dprintf("%3ld id: %lx.%lx Teb %lx ", pThread->index,
|
|
pProcessCurrent->dwProcessId,
|
|
pThread->dwThreadId,
|
|
#ifndef CHICAGO
|
|
ThreadBasicInfo.TebBaseAddress
|
|
#else
|
|
0x12345678
|
|
#endif
|
|
);
|
|
if (pThread->fFrozen)
|
|
dprintf("Frozen\n");
|
|
else
|
|
dprintf("Unfrozen\n");
|
|
}
|
|
pThread = pThread->pThreadNext;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*** fnAssemble - interactive assembly routine
|
|
*
|
|
* Purpose:
|
|
* Function of "a<range>" command.
|
|
*
|
|
* Prompt the user with successive assembly addresses until
|
|
* a blank line is input. Assembly errors do not abort the
|
|
* function, but the prompt is output again for a retry.
|
|
* The variables pchStart, pchCommand, cbPrompt, and pjbufReturn
|
|
* are set to make a local error context and restored on routine
|
|
* exit.
|
|
*
|
|
* Input:
|
|
* *addr - starting address for assembly
|
|
*
|
|
* Output:
|
|
* *addr - address after the last assembled instruction.
|
|
*
|
|
* Notes:
|
|
* all error processing is local, no errors are returned.
|
|
*
|
|
*************************************************************************/
|
|
|
|
|
|
void fnAssemble (PADDR paddr)
|
|
{
|
|
char chAssemble[SYMBOLSIZE];
|
|
char ch;
|
|
// jmp_buf asm_return; // TEMP TEMP
|
|
|
|
// save present prompt and error recovery context
|
|
|
|
UCHAR *pchStartSave = pchStart; // saved start of cmd buffer
|
|
UCHAR *pchCommandSave = pchCommand; // current ptr in cmd buffer
|
|
ULONG cbPromptSave = cbPrompt; // size of prompt string
|
|
// jmp_buf *pjbufReturnSave = pjbufReturn; // ptr to error return jmp_buf
|
|
|
|
// set local prompt and error recovery context
|
|
|
|
pchStart = chAssemble;
|
|
pchCommand = chAssemble;
|
|
cbPrompt = 9;
|
|
// pjbufReturn = &asm_return;
|
|
fJmpBuf = TRUE; // TEMP TEMP
|
|
|
|
if (setjmp(asm_return) != 0) {
|
|
;
|
|
}
|
|
chAssemble[0] = '\0';
|
|
|
|
while (TRUE) {
|
|
dprintAddr(paddr);
|
|
NtsdPrompt("", chAssemble, sizeof(chAssemble));
|
|
RemoveDelChar(chAssemble);
|
|
pchCommand = chAssemble;
|
|
do
|
|
ch = *pchCommand++;
|
|
while (ch == ' ' || ch == '\t');
|
|
if (ch == '\0')
|
|
break;
|
|
pchCommand--;
|
|
|
|
assert(fFlat(*paddr) || fInstrPtr(*paddr));
|
|
assem(paddr, pchCommand);
|
|
}
|
|
|
|
// restore entry prompt and error recovery context
|
|
|
|
pchStart = pchStartSave;
|
|
pchCommand = pchCommandSave;
|
|
cbPrompt = cbPromptSave;
|
|
// pjbufReturn = pjbufReturnSave;
|
|
fJmpBuf = FALSE; // TEMP TEMP
|
|
}
|
|
|
|
/*** fnUnassemble - disassembly of an address range
|
|
*
|
|
* Purpose:
|
|
* Function of "u<range>" command.
|
|
*
|
|
* Output the disassembly of the instruction in the given
|
|
* address range. Since some processors have variable
|
|
* instruction lengths, use fLength value to determine if
|
|
* instruction count or inclusive range should be used.
|
|
*
|
|
* Input:
|
|
* *addr - pointer to starting address to disassemble
|
|
* value - if fLength = TRUE, count of instructions to output
|
|
* if fLength = FALSE, ending address of inclusive range
|
|
*
|
|
* Output:
|
|
* *addr - address after last instruction disassembled
|
|
*
|
|
* Exceptions:
|
|
* error exit: MEMORY - memory access error
|
|
*
|
|
* Notes:
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnUnassemble (PADDR paddr, ULONG value, BOOLEAN fLength, BOOLEAN fFirst)
|
|
{
|
|
UCHAR buffer[SYMBOLSIZE + 100];
|
|
BOOLEAN fStatus;
|
|
PADDR pendaddr = (PADDR)value;
|
|
|
|
#if defined(KERNEL) & defined(i386)
|
|
if (TerseLevel < 2) {
|
|
fFirst = TRUE;
|
|
}
|
|
#else
|
|
fFirst = TRUE;
|
|
#endif
|
|
|
|
while ((fLength && value--) || (!fLength && AddrLt(*paddr,*pendaddr))) {
|
|
OutputSymAddr(Flat(*paddr), fFirst, TRUE);
|
|
fStatus = disasm(paddr, buffer, FALSE);
|
|
dprintf("%s", buffer);
|
|
if (!fStatus)
|
|
error(MEMORY);
|
|
fFirst = FALSE;
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*** fnEnterMemory - enter bytes into memory
|
|
*
|
|
* Purpose:
|
|
* Function of "e[b|w|d]<addr>" commands.
|
|
*
|
|
* Write the bytes in the array pointed by *array into
|
|
* memory at address addr for count bytes.
|
|
*
|
|
* Input:
|
|
* addr - memory address to start write
|
|
* *array - pointer to array to read bytes
|
|
* count - number of byte to write
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* error exit: MEMORY - write memory access error
|
|
*
|
|
* Notes:
|
|
* HexList preprocesses the word and dword value lists into
|
|
* a byte array for this routine.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnEnterMemory (PADDR addr, PUCHAR array, ULONG count)
|
|
{
|
|
ULONG index;
|
|
|
|
for (index = 0; index < count; AddrAdd(addr,1), index++)
|
|
if (!SetMemByte(addr, *array++))
|
|
error(MEMORY);
|
|
}
|
|
|
|
/*** fnChangeBpState - change breakpoint state
|
|
*
|
|
* Purpose:
|
|
* Function of "b[c|d|e]<idlist>" commands.
|
|
*
|
|
* To change the state of one or more breakpoints.
|
|
* The mask is produced by IdList and has the breakpoint
|
|
* number mapped, 1-bit for breakpoint 0 through 200-bit
|
|
* for breakpoint 9.
|
|
*
|
|
* Input:
|
|
* mask - breakpoint selection mask
|
|
* type - character 'c'-clear, 'd'-disable, 'e'-enable
|
|
*
|
|
* Output:
|
|
* brkptlist[i].status - changed to new status if i in <idlist>
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnChangeBpState (ULONG mask, UCHAR type)
|
|
{
|
|
ULONG index, start, limit;
|
|
BOOLEAN fProfilingBrkpt = FALSE;
|
|
|
|
if (type == 'c') {
|
|
type = '\0';
|
|
}
|
|
|
|
if (mask == ALL_BREAKPOINTS) {
|
|
start = 0;
|
|
limit = MAX_NUMBER_OF_BREAKPOINTS;
|
|
} else {
|
|
start = mask;
|
|
limit = mask+1;
|
|
}
|
|
|
|
#ifdef BP_CORRUPTION
|
|
UnlockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
for (index = start; index < limit; index++) {
|
|
if (brkptlist[index].status != '\0') {
|
|
#ifndef KERNEL
|
|
// If profiling breakpoint, change breakpoint type accordingly
|
|
if (type == '\0' || type == 'd')
|
|
fProfilingBrkpt = UpdateBrkpt(ps_ProfileDLL, index, BRKPT_PROFILE);
|
|
else if (type == 'e')
|
|
fProfilingBrkpt = UpdateBrkpt(ps_ProfileDLL, index, BRKPT_USER);
|
|
|
|
if (!fProfilingBrkpt || PROFILING) {
|
|
brkptlist[index].status = type;
|
|
}
|
|
#else
|
|
brkptlist[index].status = type;
|
|
|
|
if (brkptlist[index].bpInternal) {
|
|
DbgKdSetInternalBp(Flat(brkptlist[index].addr),
|
|
(type == 'e') ?
|
|
DBGKD_INTERNAL_BP_FLAG_COUNTONLY :
|
|
DBGKD_INTERNAL_BP_FLAG_INVALID);
|
|
}
|
|
#ifdef i386
|
|
if (brkptlist[index].option != (UCHAR)-1) {
|
|
fDataBrkptsChanged = TRUE;
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef BP_CORRUPTION
|
|
LockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
}
|
|
|
|
/*** fnListBpState - list breakpoint information
|
|
*
|
|
* Purpose:
|
|
* Function of "bl" command.
|
|
*
|
|
* Output the number, status, address, thread (opt),
|
|
* and command string (opt) for all defined breakpoints.
|
|
*
|
|
* Input:
|
|
* None.
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnListBpState (void)
|
|
{
|
|
ULONG count;
|
|
UCHAR option;
|
|
#ifndef KERNEL
|
|
PPROCESS_INFO pProcessSaved = pProcessCurrent;
|
|
#endif
|
|
|
|
for (count = 0; count < MAX_NUMBER_OF_BREAKPOINTS; count++)
|
|
if (brkptlist[count].status != '\0') {
|
|
dprintf("%2ld %c ", count, brkptlist[count].status);
|
|
dprintAddr(&brkptlist[count].addr);
|
|
if (brkptlist[count].option != (UCHAR)-1) {
|
|
switch (brkptlist[count].option) {
|
|
case 0: option = 'e'; break;
|
|
case 1: option = 'w'; break;
|
|
case 2: option = 'i'; break;
|
|
case 3: option = 'r'; break;
|
|
default: option = '?'; break;
|
|
}
|
|
dprintf("%c %c", option, brkptlist[count].size + '1');
|
|
} else {
|
|
dprintf(" ");
|
|
}
|
|
dprintf(" %04lx (%04lx) ", brkptlist[count].passcnt,
|
|
brkptlist[count].setpasscnt);
|
|
#ifndef KERNEL
|
|
dprintf("%2ld:", brkptlist[count].pProcess->index);
|
|
if (brkptlist[count].pThread != NULL) {
|
|
dprintf("~%03ld ", brkptlist[count].pThread->index);
|
|
}
|
|
else {
|
|
dprintf("*** ");
|
|
}
|
|
pProcessCurrent = brkptlist[count].pProcess;
|
|
#endif
|
|
OutputSymAddr(Flat(brkptlist[count].addr), TRUE, FALSE);
|
|
|
|
if (brkptlist[count].szcommand[0] != '\0') {
|
|
dprintf("\"%s\"", brkptlist[count].szcommand);
|
|
}
|
|
dprintf("\n");
|
|
}
|
|
#ifndef KERNEL
|
|
pProcessCurrent = pProcessSaved;
|
|
#endif
|
|
|
|
#ifdef KERNEL
|
|
dprintf("\n");
|
|
for (count = 0; count < MAX_NUMBER_OF_BREAKPOINTS; count++) {
|
|
if ((brkptlist[count].status != '\0') && (brkptlist[count].bpInternal)) {
|
|
ULONG flags, calls, minInst, maxInst, totInst, maxCPS;
|
|
DbgKdGetInternalBp(Flat(brkptlist[count].addr),&flags,&calls,
|
|
&minInst,&maxInst,&totInst,&maxCPS);
|
|
dprintf("%8x %6d %8d %8d %8d %2x %4d ",Flat(brkptlist[count].addr),
|
|
calls,minInst,maxInst,
|
|
totInst,flags,maxCPS);
|
|
OutputSymAddr(Flat(brkptlist[count].addr), TRUE, FALSE);
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
/*** fnSetBp - set breakpoint
|
|
*
|
|
* Purpose:
|
|
* Function of "[~[n|*|.|#]]bp[n] addr [passcnt] ["<cmd-string>"]".
|
|
*
|
|
* First determine the breakpoint number. If the number is
|
|
* given in the command and another at the same address exists,
|
|
* return an error. If no number is given and another exists,
|
|
* redefine it and give a warning message. If no number is
|
|
* given and another does not exist, use the first available
|
|
* unassigned one. Once the number is computed, set the
|
|
* appropriate fields in the table and mark as enabled. The
|
|
* thread pointer is nonNULL if the breakpoint is enabled only
|
|
* for a specific thread.
|
|
*
|
|
* Input:
|
|
* bpno - breakpoint number (-1 if none specified)
|
|
* bpaddr - address to set breakpoint
|
|
* passcnt - pass count of breakpoint
|
|
* pThread - if nonNULL, pointer to thread structure
|
|
* *string - pointer to command string of breakpoint
|
|
*
|
|
* Output:
|
|
* Returns breakpoint number.
|
|
*
|
|
* Exceptions:
|
|
* error exit:
|
|
* BPDUPLICATE - existing breakpoint at another number
|
|
* BPLISTFULL - breakpoint table is full
|
|
*
|
|
*************************************************************************/
|
|
|
|
ULONG fnSetBp (ULONG bpno, UCHAR bpoption, UCHAR bpsize,
|
|
PADDR bpaddr, ULONG passcnt,
|
|
#ifdef KERNEL
|
|
BOOLEAN internalBp,
|
|
#else
|
|
PTHREAD_INFO pThread,
|
|
#endif
|
|
PUCHAR string)
|
|
{
|
|
ULONG count;
|
|
|
|
#if defined(MIPS) || defined(ALPHA) || defined(_PPC_)
|
|
PIMAGE_FUNCTION_ENTRY FunctionEntry;
|
|
FunctionEntry = SymFunctionTableAccess( pProcessCurrent->hProcess, Flat(*bpaddr) );
|
|
if (FunctionEntry != NULL) {
|
|
if ( (Flat(*bpaddr) >= FunctionEntry->StartingAddress) &&
|
|
(Flat(*bpaddr) < FunctionEntry->EndOfPrologue)) {
|
|
ADDR32(bpaddr, FunctionEntry->EndOfPrologue );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef BP_CORRUPTION
|
|
UnlockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
|
|
for (count = 0; count < MAX_NUMBER_OF_BREAKPOINTS; count++)
|
|
if (brkptlist[count].status != '\0' &&
|
|
#ifndef KERNEL
|
|
brkptlist[count].pProcess == pProcessCurrent &&
|
|
#endif
|
|
AddrEqu(brkptlist[count].addr,*bpaddr)) {
|
|
if (bpno == -1) {
|
|
bpno = count;
|
|
dprintf("breakpoint %ld redefined\n", count);
|
|
#ifndef KERNEL
|
|
// Update the breakpoint type if breakpoint is
|
|
// a profiling breakpoint
|
|
UpdateBrkpt(ps_ProfileDLL, bpno, BRKPT_USER);
|
|
#endif
|
|
}
|
|
else if (bpno != count)
|
|
error(BPDUPLICATE);
|
|
}
|
|
if (bpno == -1) {
|
|
for (count = 0; count < MAX_NUMBER_OF_BREAKPOINTS; count++)
|
|
if (brkptlist[count].status == '\0') {
|
|
bpno = count;
|
|
break;
|
|
}
|
|
if (bpno == -1)
|
|
error(BPLISTFULL);
|
|
}
|
|
|
|
#ifdef KERNEL
|
|
// Intel P5 processor also supports I/O breakpoints.
|
|
// Make sure they are enabled in CR4.
|
|
|
|
if (bpoption == 2) {
|
|
#ifdef i386
|
|
// bugbug - hardcoded value
|
|
if (!(GetRegValue(REGCR4) & 8)) {
|
|
#else
|
|
if (TRUE) {
|
|
#endif
|
|
brkptlist[bpno].status = '\0';
|
|
error (BPIONOTSUP);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
brkptlist[bpno].status = 'e';
|
|
brkptlist[bpno].option = bpoption;
|
|
brkptlist[bpno].size = bpsize;
|
|
brkptlist[bpno].addr = *bpaddr;
|
|
brkptlist[bpno].passcnt = passcnt;
|
|
brkptlist[bpno].setpasscnt = passcnt;
|
|
#ifdef KERNEL
|
|
brkptlist[bpno].bpInternal = internalBp;
|
|
#else
|
|
brkptlist[bpno].pProcess = pProcessCurrent;
|
|
brkptlist[bpno].pThread = pThread;
|
|
#endif
|
|
strcpy(brkptlist[bpno].szcommand, string);
|
|
#ifdef KERNEL
|
|
#ifdef i386
|
|
if (bpoption != (UCHAR)-1) {
|
|
fDataBrkptsChanged = TRUE;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef BP_CORRUPTION
|
|
LockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
|
|
return (bpno);
|
|
}
|
|
|
|
#ifdef KERNEL
|
|
|
|
NTSTATUS
|
|
DbgKdSetSpecialCalls (
|
|
IN ULONG NumSpecialCalls,
|
|
IN PULONG Calls
|
|
);
|
|
|
|
VOID
|
|
SetupSpecialCalls()
|
|
{
|
|
ULONG SpecialCalls[6];
|
|
|
|
steptracepasscnt = 0xfffffffe;
|
|
|
|
// Set the special calls (overkill, once per boot
|
|
// would be enough, but this is easier).
|
|
|
|
if (!(SpecialCalls[0] = LookupSymbolInDll("KfLowerIrql","hal"))) {
|
|
SpecialCalls[0] = LookupSymbolInDll("KeLowerIrql","hal");
|
|
}
|
|
if (!(SpecialCalls[1] = LookupSymbolInDll("KfReleaseSpinLock","hal"))) {
|
|
SpecialCalls[1] = LookupSymbolInDll("KeReleaseSpinLock","hal");
|
|
}
|
|
SpecialCalls[2] = LookupSymbolInDll("HalRequestSoftwareInterrupt","hal");
|
|
SpecialCalls[3] = LookupSymbolInDll("KiSwapContext","NT");
|
|
SpecialCalls[4] = LookupSymbolInDll("SwapContext","NT");
|
|
SpecialCalls[5] = LookupSymbolInDll("KiUnlockDispatcherDatabase", "NT");
|
|
|
|
DbgKdSetSpecialCalls(6,SpecialCalls);
|
|
}
|
|
#endif
|
|
|
|
/*** parseBpCmd - parse breakpoint command
|
|
*
|
|
* Purpose:
|
|
* Parse the breakpoint commands ("bp" for code, "ba" for data).
|
|
* Once parsed, call fnSetBp to set the breakpoint table entry.
|
|
*
|
|
* Input:
|
|
* pThread - nonNULL for breakpoint on specific thread
|
|
* *pchCommand - pointer to operands in command string
|
|
* internalBp - make this an internal kernel bp?
|
|
* type - iff this is an internalBp, is it countonly ('i') or not ('w')?
|
|
*
|
|
* Output:
|
|
* Returns the breakpoint number.
|
|
*
|
|
* Exceptions:
|
|
* error exit:
|
|
* SYNTAX - character after "bp" not decimal digit
|
|
* STRINGSIZE - command string too large
|
|
*
|
|
*************************************************************************/
|
|
|
|
ULONG parseBpCmd (
|
|
#ifndef KERNEL
|
|
BOOLEAN fDataBp, PTHREAD_INFO pThread
|
|
#else
|
|
BOOLEAN fDataBp, BOOLEAN internalBp, char type
|
|
#endif
|
|
)
|
|
{
|
|
ULONG bpno = (ULONG)-1;
|
|
ADDR addr;
|
|
ULONG passcnt = 1;
|
|
ULONG count = 1;
|
|
UCHAR option = (UCHAR)-1;
|
|
UCHAR size;
|
|
UCHAR szBpCmd[SYMBOLSIZE];
|
|
UCHAR *pchBpCmd = szBpCmd;
|
|
UCHAR ch;
|
|
|
|
// get the breakpoint number in bpno if given
|
|
|
|
GetRegPCValue(&addr);
|
|
ch = *pchCommand;
|
|
if (ch >= '0' && ch <= '9') {
|
|
bpno = ch - '0';
|
|
ch = *++pchCommand;
|
|
if (ch >= '0' && ch <= '9') {
|
|
bpno = bpno * 10 + ch - '0';
|
|
ch = *++pchCommand;
|
|
}
|
|
if (bpno >= MAX_NUMBER_OF_BREAKPOINTS ||
|
|
(ch != ' ' && ch != '\t' && ch != '\0')) {
|
|
error(SYNTAX);
|
|
}
|
|
}
|
|
|
|
// if data breakpoint, get option and size values
|
|
|
|
if (fDataBp) {
|
|
USHORT cntDataBrkpts=0, index;
|
|
|
|
ch = PeekChar();
|
|
ch = (UCHAR)tolower(ch);
|
|
|
|
if (ch == 'e') {
|
|
option = 0;
|
|
} else if (ch == 'w') {
|
|
option = 1;
|
|
#ifdef KERNEL
|
|
} else if (ch == 'i') {
|
|
option = 2;
|
|
#endif
|
|
} else if (ch == 'r') {
|
|
option = 3;
|
|
} else {
|
|
error(SYNTAX);
|
|
}
|
|
|
|
pchCommand++;
|
|
|
|
ch = PeekChar();
|
|
ch = (UCHAR)tolower(ch);
|
|
if (ch == '1') {
|
|
size = 0;
|
|
} else if (ch == '2') {
|
|
size = 1;
|
|
} else if (ch == '4') {
|
|
size = 3;
|
|
} else {
|
|
error(SYNTAX);
|
|
}
|
|
|
|
pchCommand++;
|
|
for (index = 0; index <MAX_NUMBER_OF_BREAKPOINTS; index++) {
|
|
if (brkptlist[index].status=='e' &&
|
|
brkptlist[index].option!=(UCHAR)-1) {
|
|
cntDataBrkpts++;
|
|
}
|
|
}
|
|
if (cntDataBrkpts>3) {
|
|
error(BPLISTFULL);
|
|
}
|
|
}
|
|
|
|
// get the breakpoint address, if given, in addr
|
|
|
|
ch = PeekChar();
|
|
if (ch != '"' && ch != '\0') {
|
|
GetAddrExpression(X86REGCS, &addr);
|
|
ch = PeekChar();
|
|
}
|
|
|
|
// test alignment if data breakpoint
|
|
|
|
if (fDataBp && (size & (UCHAR)Flat(addr))) {
|
|
error(ALIGNMENT);
|
|
}
|
|
|
|
#ifdef KERNEL
|
|
// test alignment and range if I/O breakpoint
|
|
|
|
if (option == 2) {
|
|
if (size != 3) {
|
|
error(ALIGNMENT);
|
|
}
|
|
|
|
if (Flat(addr) > 0xffffL) {
|
|
error(BADRANGE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// get the pass count, if given, in passcnt
|
|
|
|
if (ch != '"' && ch != ';' && ch != '\0') {
|
|
passcnt = GetExpression();
|
|
ch = PeekChar();
|
|
}
|
|
|
|
// if next character is double quote, get the command string
|
|
|
|
if (ch == '"') {
|
|
pchCommand++;
|
|
ch = *pchCommand++;
|
|
while (ch != '"' && ch != '\0' && count < SYMBOLSIZE) {
|
|
*pchBpCmd++ = ch;
|
|
ch = *pchCommand++;
|
|
count++;
|
|
}
|
|
if (count == SYMBOLSIZE) {
|
|
error(STRINGSIZE);
|
|
}
|
|
if (ch == '\0' || ch == ';') {
|
|
pchCommand--;
|
|
}
|
|
}
|
|
*pchBpCmd = '\0';
|
|
bpno = fnSetBp(bpno, option, size, &addr, passcnt,
|
|
#ifdef KERNEL
|
|
internalBp,
|
|
#else
|
|
pThread,
|
|
#endif
|
|
szBpCmd);
|
|
#ifdef KERNEL
|
|
if (internalBp) {
|
|
if (type == 'i') {
|
|
DbgKdSetInternalBp(Flat(addr),DBGKD_INTERNAL_BP_FLAG_COUNTONLY);
|
|
} else {
|
|
DbgKdSetInternalBp(Flat(addr),0);
|
|
SetupSpecialCalls();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return (bpno);
|
|
}
|
|
|
|
/*** fnGoExecution - continue execution with temporary breakpoints
|
|
*
|
|
* Purpose:
|
|
* Function of the "[~[<thrd>]g[=<startaddr>][<bpaddr>]*" command.
|
|
*
|
|
* Set the PC to startaddr. Set the temporary breakpoints in
|
|
* golist with the addresses pointed by *bparray and the count
|
|
* in gocnt to bpcount. Set cmdState to exit the command processor
|
|
* and continue program execution.
|
|
*
|
|
* Input:
|
|
* startaddr - new starting address
|
|
* bpcount - number of temporary breakpoints
|
|
* pThread - thread pointer to qualify <bpaddr>, NULL for all
|
|
* fThreadFreeze - TRUE if freezing all but pThread thread
|
|
* bpaddr - pointer to array of temporary breakpoints
|
|
*
|
|
* Output:
|
|
* cmdState - set to continue execution
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnGoExecution (PADDR startaddr, ULONG bpcount,
|
|
#ifndef KERNEL
|
|
PTHREAD_INFO pThread, BOOLEAN fThreadFreeze,
|
|
#endif
|
|
PADDR bparray)
|
|
{
|
|
ULONG count;
|
|
|
|
SetRegPCValue(startaddr);
|
|
|
|
gocnt = bpcount;
|
|
for (count = 0; count < bpcount; count++) {
|
|
golist[count].addr = (*bparray++);
|
|
//dprintf("Setting temporary breakpoiht @ %08lx\n", Flat(golist[count].addr));
|
|
}
|
|
|
|
cmdState = 'g';
|
|
#ifndef KERNEL
|
|
fFreeze = fThreadFreeze;
|
|
pThreadCmd = pThread;
|
|
pProcessGoBrkpt = pProcessCurrent;
|
|
#endif
|
|
}
|
|
|
|
/*** parseRegCmd - parse register command
|
|
*
|
|
* Purpose:
|
|
* Parse the register ("r") command.
|
|
* if "r", output all registers
|
|
* if "r <reg>", output only the register <reg>
|
|
* if "r <reg> =", output only the register <reg>
|
|
* and prompt for new value
|
|
* if "r <reg> = <value>" or "r <reg> <value>",
|
|
* set <reg> to value <value>
|
|
*
|
|
* if "rt ...", output register(s) and toggle terse flag
|
|
* if set, the terse flag inhibits the display of
|
|
* the less used control registers (kernel debug only)
|
|
*
|
|
* Input:
|
|
* *pchCommand - pointer to operands in command string
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* error exit:
|
|
* SYNTAX - character after "r" not register name
|
|
*
|
|
*************************************************************************/
|
|
|
|
VOID
|
|
parseRegCmd (
|
|
VOID
|
|
)
|
|
{
|
|
UCHAR ch;
|
|
ULONG count;
|
|
UCHAR chValue[_MAX_PATH];
|
|
PUCHAR pchValue;
|
|
ULONG value;
|
|
BOOL Show64 = FALSE;
|
|
|
|
#if defined(KERNEL) & defined(i386)
|
|
// if 't' or 'T' follows 'r', toggle the terse flag
|
|
|
|
if (*pchCommand == 't' || *pchCommand == 'T') {
|
|
pchCommand++;
|
|
TerseLevel = TerseLevel == 1 ? 0 : 1;
|
|
if (*pchCommand >= '0' && *pchCommand <= '3') {
|
|
TerseLevel = *pchCommand - '0';
|
|
pchCommand++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
if (*pchCommand == 'L') {
|
|
pchCommand++;
|
|
Show64 = TRUE;
|
|
}
|
|
|
|
// if just "r", output all the register and disassembly as EIP
|
|
|
|
if ((ch = PeekChar()) == '\0' || ch == ';') {
|
|
#ifdef KERNEL
|
|
if (NtsdCurrentProcessor != DefaultProcessor) {
|
|
DefaultProcessor = NtsdCurrentProcessor;
|
|
lastSelector = -1;
|
|
}
|
|
#endif
|
|
OutputAllRegs(Show64);
|
|
OutDisCurrent(TRUE, TRUE);
|
|
GetRegPCValue(&assemDefault);
|
|
unasmDefault = assemDefault;
|
|
}
|
|
else {
|
|
|
|
#ifdef KERNEL
|
|
// if [processor]r, no register can be specified.
|
|
if (fSwitched) {
|
|
error(SYNTAX);
|
|
}
|
|
#endif
|
|
// parse the register name
|
|
|
|
PARSE:
|
|
|
|
#if defined(ALPHA) || defined(_PPC_) || defined(_MIPS_)
|
|
//
|
|
// Support for floating point number
|
|
//
|
|
|
|
if (*pchCommand == 'F') {
|
|
pchCommand++;
|
|
printFloatReg();
|
|
return;
|
|
}
|
|
|
|
#endif // ALPHA || PPC
|
|
if ((count = GetRegName()) == -1) {
|
|
error(SYNTAX);
|
|
}
|
|
|
|
// if "r <reg>", output value
|
|
|
|
if ((ch = (UCHAR)PeekChar()) == '\0' || ch == ',' || ch == ';') {
|
|
if (UserRegTest(count)) {
|
|
dprintf("%s=%s", RegNameFromIndex(count),
|
|
GetRegFlagValue(count));
|
|
} else {
|
|
dprintf("%s=", RegNameFromIndex(count));
|
|
OutputOneReg(count, Show64);
|
|
}
|
|
if (ch == ',') {
|
|
dprintf(" ");
|
|
while (*pchCommand==' '||*pchCommand==',')
|
|
pchCommand++;
|
|
goto PARSE;
|
|
}
|
|
else {
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
else if (ch == '=') {
|
|
|
|
// if "r <reg> =", output and prompt for new value
|
|
|
|
pchCommand++;
|
|
if ((ch = PeekChar()) == '\0' || ch == ';') {
|
|
dprintf(UserRegTest(count) ? "%s=%s\n" : "%s=%08lx\n",
|
|
RegNameFromIndex(count), GetRegFlagValue(count));
|
|
if (UserRegTest(count)) {
|
|
NtsdPrompt("; new value: ", chValue, sizeof(chValue));
|
|
RemoveDelChar(chValue);
|
|
SetRegFlagValue(count, (LONG)chValue);
|
|
}
|
|
else {
|
|
NtsdPrompt("; new hex value: ", chValue, 20);
|
|
RemoveDelChar(chValue);
|
|
pchValue = chValue;
|
|
do {
|
|
ch = *pchValue++;
|
|
} while (ch == ' ' || ch == '\t');
|
|
if (ch != '\0') {
|
|
value = 0;
|
|
ch = (UCHAR)tolower(ch);
|
|
while ((ch >= '0' && ch <= '9') ||
|
|
(ch >= 'a' && ch <= 'f')) {
|
|
ch -= '0';
|
|
if (ch > 9)
|
|
ch -= 'a' - '0' - 10;
|
|
if (value > 0x10000000)
|
|
error(OVERFLOW);
|
|
value = value * 0x10 + ch;
|
|
ch = (UCHAR)tolower(*pchValue); pchValue++;
|
|
}
|
|
if (ch != '\0') {
|
|
error(SYNTAX);
|
|
}
|
|
SetRegFlagValue(count, (LONG)value);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
// if "r <reg> = <value>", set the value
|
|
|
|
if (UserRegTest(count)) {
|
|
SetRegFlagValue(count, (LONG)pchCommand);
|
|
*pchCommand=0;
|
|
}
|
|
else {
|
|
SetRegFlagValue(count, (LONG)GetExpression());
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
// if "r <reg> <value>", also set the value
|
|
|
|
if (UserRegTest(count)) {
|
|
SetRegFlagValue(count, (LONG)pchCommand);
|
|
*pchCommand=0;
|
|
}
|
|
else {
|
|
SetRegFlagValue(count, (LONG)GetExpression());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*** parseGoCmd - parse go command
|
|
*
|
|
* Purpose:
|
|
* Parse the go ("g") command. Once parsed, call fnGoExecution
|
|
* restart program execution.
|
|
*
|
|
* Input:
|
|
* pThread - nonNULL for breakpoint on specific thread
|
|
* fThreadFreeze - TRUE if all threads except pThread are frozen
|
|
* *pchCommand - pointer to operands in command string
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* error exit:
|
|
* LISTSIZE - breakpoint address list too large
|
|
*
|
|
*************************************************************************/
|
|
|
|
void parseGoCmd (
|
|
#ifndef KERNEL
|
|
PTHREAD_INFO pThread, BOOLEAN fThreadFreeze
|
|
#else
|
|
void
|
|
#endif
|
|
)
|
|
{
|
|
ULONG count;
|
|
ADDR addr[10];
|
|
UCHAR ch;
|
|
ADDR pcaddr;
|
|
UCHAR ch2;
|
|
|
|
chExceptionHandle = '\0';
|
|
ch = (UCHAR)tolower(*pchCommand);
|
|
if (ch == 'h' || ch == 'n') {
|
|
ch2 = *(pchCommand + 1);
|
|
if (ch2 == ' ' || ch2 == '\t' || ch2 == '\0') {
|
|
pchCommand++;
|
|
chExceptionHandle = ch;
|
|
}
|
|
}
|
|
|
|
GetRegPCValue(&pcaddr); // default to current PC
|
|
count = 0;
|
|
if (PeekChar() == '=') {
|
|
*pchCommand++;
|
|
GetAddrExpression(X86REGCS, &pcaddr);
|
|
}
|
|
while ((ch = PeekChar()) != '\0' && ch != ';') {
|
|
if (count == 10)
|
|
error(LISTSIZE);
|
|
GetAddrExpression(X86REGCS, addr+(count++));
|
|
}
|
|
#ifndef KERNEL
|
|
chLastCommand[0] = '\0'; // null out g command UNDONE ?????
|
|
#endif
|
|
fnGoExecution(&pcaddr, count,
|
|
#ifndef KERNEL
|
|
pThread, fThreadFreeze,
|
|
#endif
|
|
addr);
|
|
}
|
|
|
|
VOID
|
|
parseStackTrace (
|
|
PULONG pTraceType,
|
|
PADDR *pStartFP,
|
|
PULONG Esp,
|
|
PULONG Eip,
|
|
PULONG Count
|
|
)
|
|
{
|
|
UCHAR ch;
|
|
|
|
ch = PeekChar();
|
|
*pTraceType = 0;
|
|
|
|
if (tolower(ch) == 'b') {
|
|
pchCommand++;
|
|
*pTraceType = 1;
|
|
} else if (tolower(ch) == 'v') {
|
|
pchCommand++;
|
|
*pTraceType = 2;
|
|
}
|
|
|
|
if (PeekChar() == '=') {
|
|
|
|
#ifdef i386
|
|
pchCommand++;
|
|
GetAddrExpression(REGSS,*pStartFP);
|
|
} else {
|
|
*pStartFP = GetRegFPValue();
|
|
}
|
|
#else
|
|
pchCommand++;
|
|
GetAddrExpression(0,*pStartFP);
|
|
} else {
|
|
ADDR32(*pStartFP, 0);
|
|
}
|
|
#endif
|
|
|
|
*Count = 20;
|
|
#ifdef i386
|
|
STtrace = FALSE;
|
|
if ((ch = PeekChar()) != '\0' && ch != ';') {
|
|
//
|
|
// If only one more value it's the count
|
|
//
|
|
|
|
*Count = GetExpression();
|
|
|
|
if ((ch = PeekChar()) != '\0' && ch != ';') {
|
|
//
|
|
// More then one value, set extra value for special
|
|
// FPO backtrace
|
|
//
|
|
|
|
STebp = (*pStartFP)->off;
|
|
STeip = GetExpression();
|
|
STesp = *Count;
|
|
*Esp = STesp;
|
|
STtrace= TRUE;
|
|
*Eip = STeip;
|
|
*Count = 20;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((ch = PeekChar()) != '\0' && ch != ';') {
|
|
*Count = GetExpression();
|
|
if ((LONG)*Count < 1) {
|
|
pchCommand++;
|
|
error(SYNTAX);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*** fnDumpAsciiMemory - output ascii strings from memory
|
|
*
|
|
* Purpose:
|
|
* Function of "da<range>" command.
|
|
*
|
|
* Outputs the memory in the specified range as ascii
|
|
* strings up to 48 characters per line. The default
|
|
* display is 16 lines for 384 characters total.
|
|
*
|
|
* Input:
|
|
* startaddr - starting address to begin display
|
|
* count - number of characters to display as ascii
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* memory locations not accessible are output as "?",
|
|
* but no errors are returned.
|
|
*
|
|
*************************************************************************/
|
|
|
|
ULONG fnDumpAsciiMemory (PADDR startaddr, ULONG count)
|
|
{
|
|
ULONG rowindex = 0;
|
|
ULONG readcount;
|
|
UCHAR readbuffer[32];
|
|
UCHAR bytestring[33];
|
|
UCHAR ch = 'x';
|
|
ULONG blockindex = 0;
|
|
ULONG blockcount;
|
|
ULONG startcount = count;
|
|
ULONG memOffset = Flat(*startaddr);
|
|
|
|
while (ch != 0x00 && count > 0) {
|
|
blockcount = min(count, 32);
|
|
blockcount = min(blockcount, pageSize - (memOffset & (pageSize - 1)));
|
|
readcount = GetMemString(startaddr, readbuffer, blockcount);
|
|
while (ch != 0x00 && blockindex < blockcount) {
|
|
rowindex = 0;
|
|
while (ch != 0x00 && rowindex < 32 && blockindex < blockcount) {
|
|
if (rowindex == 0)
|
|
dprintAddr(startaddr);
|
|
ch = '?';
|
|
if (blockindex < readcount) {
|
|
ch = readbuffer[blockindex];
|
|
if (ch != 0x00 && (ch < 0x20 || ch > 0x7e))
|
|
ch = '.';
|
|
}
|
|
bytestring[rowindex++] = ch;
|
|
blockindex++;
|
|
AddrAdd(startaddr, 1);
|
|
}
|
|
if (rowindex == 32) {
|
|
bytestring[32] = '\0';
|
|
dprintf(" \"%s\"\n", bytestring);
|
|
rowindex = 0;
|
|
}
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
count -= blockindex;
|
|
memOffset += blockindex;
|
|
blockindex = 0;
|
|
}
|
|
if (rowindex != 0) {
|
|
bytestring[rowindex] = '\0';
|
|
dprintf(" \"%s\"\n", bytestring);
|
|
}
|
|
return startcount - count;
|
|
}
|
|
|
|
/*** fnDumpUnicodeMemory - output unicode strings from memory
|
|
*
|
|
* Purpose:
|
|
* Function of "du<range>" command.
|
|
*
|
|
* Outputs the memory in the specified range as unicode
|
|
* strings up to 48 characters per line. The default
|
|
* display is 16 lines for 384 characters total (768 bytes)
|
|
*
|
|
* Input:
|
|
* startaddr - starting address to begin display
|
|
* count - number of characters to display as ascii
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* memory locations not accessible are output as "?",
|
|
* but no errors are returned.
|
|
*
|
|
*************************************************************************/
|
|
|
|
ULONG fnDumpUnicodeMemory (PADDR startaddr, ULONG count)
|
|
{
|
|
ULONG rowindex = 0;
|
|
ULONG readcount;
|
|
WCHAR readbuffer[32];
|
|
WCHAR bytestring[33];
|
|
WCHAR ch = 'x';
|
|
ULONG blockindex = 0;
|
|
ULONG blockcount;
|
|
ULONG startcount = count;
|
|
ULONG memOffset = Flat(*startaddr);
|
|
|
|
while (ch != 0x00 && count > 0) {
|
|
blockcount = min(count, sizeof(readbuffer) / sizeof( WCHAR ));
|
|
blockcount = min(blockcount,
|
|
(pageSize - (memOffset & (pageSize - 1)) + 1) / sizeof( WCHAR ));
|
|
readcount = (GetMemString(startaddr, (PUCHAR)readbuffer,
|
|
blockcount * sizeof( WCHAR ))) / sizeof( WCHAR );
|
|
while (ch != UNICODE_NULL && blockindex < blockcount) {
|
|
rowindex = 0;
|
|
while (ch != UNICODE_NULL && rowindex < 32 && blockindex < blockcount) {
|
|
if (rowindex == 0)
|
|
dprintAddr(startaddr);
|
|
ch = L'?';
|
|
if (blockindex < readcount) {
|
|
ch = readbuffer[blockindex];
|
|
if (ch != UNICODE_NULL && (ch < L' ' || ch > (WCHAR)0x007e))
|
|
ch = L'.';
|
|
}
|
|
bytestring[rowindex++] = ch;
|
|
blockindex++;
|
|
AddrAdd(startaddr, sizeof( WCHAR ));
|
|
}
|
|
if (rowindex == (sizeof(readbuffer) / sizeof( WCHAR ))) {
|
|
bytestring[rowindex] = UNICODE_NULL;
|
|
dprintf(" \"%ws\"\n", bytestring);
|
|
rowindex = 0;
|
|
}
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
blockindex = 0;
|
|
count -= blockcount;
|
|
memOffset += blockcount * sizeof( WCHAR );
|
|
}
|
|
|
|
if (rowindex != 0) {
|
|
bytestring[rowindex] = UNICODE_NULL;
|
|
dprintf(" \"%ws\"\n", bytestring);
|
|
}
|
|
|
|
return startcount - count;
|
|
}
|
|
|
|
/*** fnDumpByteMemory - output byte values from memory
|
|
*
|
|
* Purpose:
|
|
* Function of "db<range>" command.
|
|
*
|
|
* Output the memory in the specified range as hex
|
|
* byte values and ascii characters up to 16 bytes
|
|
* per line. The default display is 16 lines for
|
|
* 256 byte total.
|
|
*
|
|
* Input:
|
|
* startaddr - starting address to begin display
|
|
* count - number of bytes to display as hex and characters
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* memory location not accessible are output as "??" for
|
|
* byte values and "?" as characters, but no errors are returned.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnDumpByteMemory (PADDR startaddr, ULONG count)
|
|
{
|
|
ULONG rowindex = 0;
|
|
ULONG readcount;
|
|
UCHAR readbuffer[_MAX_PATH];
|
|
UCHAR bytestring[17];
|
|
UCHAR ch;
|
|
ULONG blockindex = 0;
|
|
ULONG blockcount;
|
|
BOOLEAN first = TRUE;
|
|
ULONG memOffset = Flat(*startaddr);
|
|
|
|
while (count > 0) {
|
|
blockcount = min(count, 128);
|
|
blockcount = min(blockcount, pageSize - (memOffset & (pageSize - 1)));
|
|
readcount = GetMemString(startaddr, readbuffer, blockcount);
|
|
if (first && readcount >= 4) {
|
|
first = FALSE;
|
|
EXPRLastDump = (ULONG)readbuffer[0];
|
|
}
|
|
while (blockindex < blockcount) {
|
|
while (rowindex < 16 && blockindex < blockcount) {
|
|
if (rowindex == 0)
|
|
dprintAddr(startaddr);
|
|
if (rowindex == 8)
|
|
dprintf("-");
|
|
else
|
|
dprintf(" ");
|
|
if (blockindex < readcount) {
|
|
ch = readbuffer[blockindex];
|
|
dprintf("%02x", ch);
|
|
if (ch < 0x20 || ch > 0x7e)
|
|
ch = '.';
|
|
}
|
|
else {
|
|
dprintf("??");
|
|
ch = '?';
|
|
}
|
|
bytestring[rowindex++] = ch;
|
|
blockindex++;
|
|
AddrAdd(startaddr, 1);
|
|
}
|
|
if (rowindex == 16) {
|
|
bytestring[16] = '\0';
|
|
if ( (startaddr->type & ADDR_1632) == ADDR_1632 ) {
|
|
dprintf(" %s\n", bytestring);
|
|
} else {
|
|
dprintf(" %s\n", bytestring);
|
|
}
|
|
rowindex = 0;
|
|
}
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
return;
|
|
}
|
|
}
|
|
blockindex = 0;
|
|
count -= blockcount;
|
|
memOffset += blockcount;
|
|
}
|
|
|
|
if (rowindex != 0) {
|
|
bytestring[rowindex] = '\0';
|
|
while (rowindex < 16) {
|
|
dprintf(" ");
|
|
rowindex++;
|
|
}
|
|
dprintf(" %s\n", bytestring);
|
|
}
|
|
}
|
|
|
|
/*** fnDumpWordMemory - output word values from memory
|
|
*
|
|
* Purpose:
|
|
* Function of "dw<range>" command.
|
|
*
|
|
* Output the memory in the specified range as word
|
|
* values up to 8 words per line. The default display
|
|
* is 16 lines for 128 words total.
|
|
*
|
|
* Input:
|
|
* startaddr - starting address to begin display
|
|
* count - number of words to be displayed
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* memory locations not accessible are output as "????",
|
|
* but no errors are returned.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnDumpWordMemory (PADDR startaddr, ULONG count)
|
|
{
|
|
ULONG rowindex = 0;
|
|
ULONG readcount;
|
|
USHORT readbuffer[64];
|
|
ULONG blockindex = 0;
|
|
ULONG blockcount;
|
|
BOOLEAN first = TRUE;
|
|
ULONG memOffset = Flat(*startaddr);
|
|
|
|
while (count > 0) {
|
|
blockcount = min(count, 64);
|
|
blockcount = min(blockcount,
|
|
(pageSize - (memOffset & (pageSize - 1)) + 1) / 2);
|
|
readcount = (GetMemString(startaddr, (PUCHAR)readbuffer,
|
|
blockcount * 2)) / 2;
|
|
if (first && readcount >= 2) {
|
|
first = FALSE;
|
|
EXPRLastDump = (ULONG)readbuffer[0];
|
|
}
|
|
while (blockindex < blockcount) {
|
|
while (rowindex < 8 && blockindex < blockcount) {
|
|
if (rowindex == 0)
|
|
dprintAddr(startaddr);
|
|
if (blockindex < readcount)
|
|
dprintf(" %04x", readbuffer[blockindex]);
|
|
else
|
|
dprintf(" ????");
|
|
rowindex++;
|
|
blockindex++;
|
|
AddrAdd(startaddr, 2);
|
|
}
|
|
if (rowindex == 8) {
|
|
dprintf("\n");
|
|
rowindex = 0;
|
|
}
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
return;
|
|
}
|
|
}
|
|
blockindex = 0;
|
|
count -= blockcount;
|
|
memOffset += blockcount * 2;
|
|
}
|
|
if (rowindex != 0)
|
|
dprintf("\n");
|
|
}
|
|
|
|
/*** fnDumpDwordMemory - output dword value from memory
|
|
*
|
|
* Purpose:
|
|
* Function of "dd<range>" command.
|
|
*
|
|
* Output the memory in the specified range as double
|
|
* word values up to 4 double words per line. The default
|
|
* display is 16 lines for 64 double words total.
|
|
*
|
|
* Input:
|
|
* startaddr - starting address to begin display
|
|
* count - number of double words to be displayed
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* memory locations not accessible are output as "????????",
|
|
* but no errors are returned.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnDumpDwordMemory (PADDR startaddr, ULONG count)
|
|
{
|
|
ULONG rowindex = 0;
|
|
ULONG readcount;
|
|
ULONG readbuffer[32];
|
|
ULONG blockindex = 0;
|
|
ULONG blockcount;
|
|
BOOLEAN first = TRUE;
|
|
ULONG memOffset = Flat(*startaddr);
|
|
|
|
while (count > 0) {
|
|
blockcount = min(count, 32);
|
|
blockcount = min(blockcount,
|
|
(pageSize - (memOffset & (pageSize - 1)) + 3) / 4);
|
|
readcount = (GetMemString(startaddr, (PUCHAR)readbuffer,
|
|
blockcount * 4)) / 4;
|
|
if (first && readcount >= 1) {
|
|
first = FALSE;
|
|
EXPRLastDump = readbuffer[0];
|
|
}
|
|
while (blockindex < blockcount) {
|
|
while (rowindex < 4 && blockindex < blockcount) {
|
|
if (rowindex == 0)
|
|
dprintAddr(startaddr);
|
|
if (blockindex < readcount)
|
|
dprintf(" %08lx", readbuffer[blockindex]);
|
|
else
|
|
dprintf(" ????????");
|
|
rowindex++;
|
|
blockindex++;
|
|
AddrAdd(startaddr, 4);
|
|
}
|
|
if (rowindex == 4) {
|
|
dprintf("\n");
|
|
rowindex = 0;
|
|
}
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
return;
|
|
}
|
|
}
|
|
blockindex = 0;
|
|
count -= blockcount;
|
|
memOffset += blockcount * 4;
|
|
}
|
|
if (rowindex != 0)
|
|
dprintf("\n");
|
|
}
|
|
|
|
/*** fnDumpDwordAndCharMemory - output dword value from memory
|
|
*
|
|
* Purpose:
|
|
* Function of "dc<range>" command.
|
|
*
|
|
* Output the memory in the specified range as double
|
|
* word values up to 4 double words per line, followed by
|
|
* an ASCII character representation of the bytes.
|
|
* The default display is 16 lines for 64 double words total.
|
|
*
|
|
* Input:
|
|
* startaddr - starting address to begin display
|
|
* count - number of double words to be displayed
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* memory locations not accessible are output as "????????",
|
|
* but no errors are returned.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnDumpDwordAndCharMemory (PADDR startaddr, ULONG count)
|
|
{
|
|
ULONG rowindex = 0;
|
|
ULONG readcount;
|
|
ULONG readbuffer[32];
|
|
ULONG blockindex = 0;
|
|
ULONG blockcount;
|
|
BOOLEAN first = TRUE;
|
|
ULONG memOffset = Flat(*startaddr);
|
|
|
|
PUCHAR bytebuffer=(PUCHAR)readbuffer;
|
|
CHAR bytestring[17];
|
|
CHAR ch;
|
|
ULONG charindex;
|
|
|
|
while (count > 0) {
|
|
blockcount = min(count, 32);
|
|
blockcount = min(blockcount,
|
|
(pageSize - (memOffset & (pageSize - 1)) + 3) / 4);
|
|
readcount = (GetMemString(startaddr, (PUCHAR)readbuffer,
|
|
blockcount * 4)) / 4;
|
|
if (first && readcount >= 1) {
|
|
first = FALSE;
|
|
EXPRLastDump = readbuffer[0];
|
|
}
|
|
while (blockindex < blockcount) {
|
|
while (rowindex < 4 && blockindex < blockcount) {
|
|
if (rowindex == 0)
|
|
dprintAddr(startaddr);
|
|
if (blockindex < readcount) {
|
|
dprintf(" %08lx", readbuffer[blockindex]);
|
|
for (charindex=rowindex*4; charindex<(rowindex+1)*4; charindex++) {
|
|
ch = bytebuffer[blockindex*4+(charindex-rowindex*4)];
|
|
if (ch < 0x20 || ch > 0x7a) {
|
|
ch = '.';
|
|
}
|
|
bytestring[charindex] = ch;
|
|
}
|
|
} else {
|
|
dprintf(" ????????");
|
|
for (charindex=rowindex*4; charindex<(rowindex+1)*4; charindex++) {
|
|
ch = '?';
|
|
bytestring[charindex] = ch;
|
|
}
|
|
}
|
|
rowindex++;
|
|
blockindex++;
|
|
AddrAdd(startaddr, 4);
|
|
}
|
|
if (rowindex == 4) {
|
|
bytestring[16] = '\0';
|
|
dprintf(" %s\n", bytestring);
|
|
rowindex = 0;
|
|
}
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
return;
|
|
}
|
|
}
|
|
blockindex = 0;
|
|
count -= blockcount;
|
|
memOffset += blockcount * 4;
|
|
}
|
|
if (rowindex != 0)
|
|
dprintf("\n");
|
|
}
|
|
|
|
/*** fnDumpListMemory - output linked list from memory
|
|
*
|
|
* Purpose:
|
|
* Function of "dl addr length size" command.
|
|
*
|
|
* Output the memory in the specified range as a linked list
|
|
*
|
|
* Input:
|
|
* startaddr - starting address to begin display
|
|
* count - number of list elements to be displayed
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* memory locations not accessible are output as "????????",
|
|
* but no errors are returned.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnDumpListMemory (PADDR startaddr, ULONG elemcount, ULONG size)
|
|
{
|
|
ULONG count;
|
|
ULONG rowindex;
|
|
ULONG readcount;
|
|
ULONG readbuffer[32];
|
|
ULONG blockindex;
|
|
ULONG blockcount;
|
|
BOOLEAN first;
|
|
ULONG memOffset;
|
|
ULONG firstaddr;
|
|
|
|
#ifdef i386
|
|
if (Type(*startaddr) & (ADDR_UNKNOWN | ADDR_V86 | ADDR_16 | ADDR_1632)) {
|
|
dprintf("[%u,%x:%x,%08x] - bogus address type.\n",
|
|
Type(*startaddr),startaddr->seg,Off(*startaddr),Flat(*startaddr)
|
|
);
|
|
return;
|
|
}
|
|
#endif
|
|
firstaddr = Flat(*startaddr);
|
|
while (elemcount-- != 0 && Flat(*startaddr) != 0) {
|
|
memOffset = Flat(*startaddr);
|
|
first = TRUE;
|
|
rowindex = 0;
|
|
blockindex = 0;
|
|
count = size;
|
|
while (count > 0) {
|
|
blockcount = min(count, 32);
|
|
blockcount = min(blockcount,
|
|
(pageSize - (memOffset & (pageSize - 1)) + 3) / 4);
|
|
readcount = (GetMemString(startaddr, (PUCHAR)readbuffer,
|
|
blockcount * 4)) / 4;
|
|
if (first) {
|
|
if (readcount >= 1) {
|
|
first = FALSE;
|
|
EXPRLastDump = readbuffer[0];
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
while (blockindex < blockcount) {
|
|
while (rowindex < 4 && blockindex < blockcount) {
|
|
if (rowindex == 0)
|
|
dprintf("%08lx: ", Flat(*startaddr));
|
|
if (blockindex < readcount)
|
|
dprintf(" %08lx", readbuffer[blockindex]);
|
|
else
|
|
dprintf(" ????????");
|
|
rowindex++;
|
|
blockindex++;
|
|
AddrAdd(startaddr, 4);
|
|
}
|
|
if (rowindex == 4) {
|
|
dprintf("\n");
|
|
rowindex = 0;
|
|
}
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
return;
|
|
}
|
|
}
|
|
blockindex = 0;
|
|
count -= blockcount;
|
|
memOffset += blockcount * 4;
|
|
}
|
|
if (rowindex != 0)
|
|
dprintf("\n");
|
|
dprintf("\n");
|
|
if (first) {
|
|
break;
|
|
}
|
|
|
|
Flat(*startaddr) = EXPRLastDump;
|
|
#if i386
|
|
startaddr->off = EXPRLastDump;
|
|
#endif
|
|
if (Flat(*startaddr) == firstaddr) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef KERNEL
|
|
|
|
/*** fnInputIo - read and output io
|
|
*
|
|
* Purpose:
|
|
* Function of "ib, iw, id <address>" command.
|
|
*
|
|
* Read (input) and print the value at the specified io address.
|
|
*
|
|
* Input:
|
|
* IoAddress - Address to read.
|
|
* InputType - The size type 'b', 'w', or 'd'
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* I/O locations not accessible are output as "??", "????", or
|
|
* "????????", depending on size. No errors are returned.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnInputIo (ULONG IoAddress, UCHAR InputType)
|
|
{
|
|
ULONG InputValue;
|
|
ULONG InputSize = 1;
|
|
NTSTATUS status;
|
|
UCHAR Format[] = "%08lx: %01lx\n";
|
|
|
|
InputValue = 0;
|
|
if (InputType == 'w')
|
|
InputSize = 2;
|
|
else if (InputType == 'd')
|
|
InputSize = 4;
|
|
Format[9] = (UCHAR)('0' + (InputSize * 2));
|
|
|
|
status = DbgKdReadIoSpace((PVOID)IoAddress, &InputValue, InputSize);
|
|
|
|
if (NT_SUCCESS(status))
|
|
dprintf(Format, IoAddress, InputValue);
|
|
else {
|
|
dprintf(" %08lx: ", IoAddress);
|
|
while (InputSize--)
|
|
dprintf("??");
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef KERNEL
|
|
/*** fnOutputIo - output io
|
|
*
|
|
* Purpose:
|
|
* Function of "ob, ow, od <address>" command.
|
|
*
|
|
* Write a value to the specified io address.
|
|
*
|
|
* Input:
|
|
* IoAddress - Address to read.
|
|
* OutputValue - Value to be written
|
|
* OutputType - The output size type 'b', 'w', or 'd'
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* No errors are returned.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnOutputIo (ULONG IoAddress, ULONG OutputValue, UCHAR OutputType)
|
|
{
|
|
ULONG OutputSize = 1;
|
|
|
|
if (OutputType == 'w')
|
|
OutputSize = 2;
|
|
else if (OutputType == 'd')
|
|
OutputSize = 4;
|
|
|
|
DbgKdWriteIoSpace((PVOID)IoAddress, OutputValue, OutputSize);
|
|
}
|
|
#endif
|
|
|
|
/*** parseStepTrace - parse step or trace
|
|
*
|
|
* Purpose:
|
|
* Parse the step ("p") or trace ("t") command. Command
|
|
* syntax is "[~<thread>]p|t[=<addr>][count]. Once parsed,
|
|
* call fnStepTrace to step or trace the program.
|
|
*
|
|
* Input:
|
|
* pThread - nonNULL for breakpoint on specific thread
|
|
* fThreadFreeze - TRUE if all threads except pThread are frozen
|
|
* *pchCommand - pointer to operands in command string
|
|
* chcmd - 'p' for step, 't' for trace
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* error exit:
|
|
* SYNTAX - indirectly through GetExpression
|
|
*
|
|
*************************************************************************/
|
|
|
|
void parseStepTrace (
|
|
#ifndef KERNEL
|
|
PTHREAD_INFO pThread, BOOLEAN fThreadFreeze,
|
|
#endif
|
|
UCHAR chcmd)
|
|
{
|
|
ADDR addr1;
|
|
ULONG value2;
|
|
UCHAR ch;
|
|
#ifdef TARGET_i386
|
|
UCHAR chAddrBuffer[SYMBOLSIZE];
|
|
ULONG displacement;
|
|
ADDR addr2;
|
|
#endif
|
|
|
|
|
|
// if next character is 'r', toggle flag to output registers
|
|
// on display on breakpoint.
|
|
|
|
ch = PeekChar();
|
|
if (tolower(ch) == 'r') {
|
|
pchCommand++;
|
|
fOutputRegs = (BOOLEAN)!fOutputRegs;
|
|
}
|
|
|
|
GetRegPCValue(&addr1); // default to current PC
|
|
if (PeekChar() == '=') {
|
|
pchCommand++;
|
|
GetAddrExpression(X86REGCS, &addr1);
|
|
}
|
|
#ifndef KERNEL
|
|
if (PeekChar() == 't' && chcmd == 'p' ) {
|
|
pchCommand++;
|
|
Timing = TRUE;
|
|
}
|
|
#endif
|
|
if (((PeekChar() == 't') || (PeekChar() == 'w')) && chcmd == 'w' ) {
|
|
#ifdef KERNEL
|
|
InitialSP = (ULONG)CURRENT_STACK;
|
|
WatchWhole = (PeekChar() == 'w');
|
|
BrkpointsSuspended = TRUE;
|
|
ClearTraceDataSyms();
|
|
BeginCurFunc = EndCurFunc = 0;
|
|
#endif
|
|
pchCommand++;
|
|
WatchTrace = TRUE;
|
|
InitializeListHead(&WatchList);
|
|
CurrentWatchSym = NULL;
|
|
WatchLevel = 0;
|
|
WatchTRCalls = 0;
|
|
WatchSumIt = 0;
|
|
WatchThreadMismatch = 0;
|
|
}
|
|
else {
|
|
WatchTrace = FALSE;
|
|
}
|
|
|
|
value2 = 1;
|
|
if ((ch = PeekChar()) != '\0' && ch != ';') {
|
|
value2 = GetExpression();
|
|
}
|
|
#ifdef TARGET_i386
|
|
else if (chcmd == 'w') {
|
|
GetSymbolStdCall(Flat(addr1),
|
|
chAddrBuffer,
|
|
&displacement,
|
|
NULL
|
|
);
|
|
if (displacement == 0 && chAddrBuffer[ 0 ] != '\0') {
|
|
GetReturnAddress(&addr2);
|
|
dprintf("Tracing %s to return address %08x\n", chAddrBuffer, Flat(addr2));
|
|
value2 = Flat(addr2);
|
|
#ifdef KERNEL
|
|
if (WatchWhole) {
|
|
BeginCurFunc = value2;
|
|
EndCurFunc = 0;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (((LONG)value2 <= 0) && (!WatchTrace))
|
|
error(SYNTAX);
|
|
fnStepTrace(&addr1, value2,
|
|
#ifndef KERNEL
|
|
pThread, fThreadFreeze,
|
|
#endif
|
|
chcmd);
|
|
}
|
|
|
|
/*** fnStepTrace - step or trace the program
|
|
*
|
|
* Purpose:
|
|
* To continue execution of the program with a temporary
|
|
* breakpoint set to stop after the next instruction
|
|
* executed (trace - 't') or the instruction in the next
|
|
* memory location (step - 'p'). The PC is also set
|
|
* as well as a pass count variable.
|
|
*
|
|
* Input:
|
|
* addr - new value of PC
|
|
* count - passcount for step or trace
|
|
* pThread - thread pointer to qualify step/trace, NULL for all
|
|
* chStepType - 't' for trace; 'p' for step
|
|
*
|
|
* Output:
|
|
* cmdState - set to 't' for trace; 'p' for step
|
|
* steptracepasscnt - pass count for step/trace
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnStepTrace (PADDR addr, ULONG count,
|
|
#ifndef KERNEL
|
|
PTHREAD_INFO pThread, BOOLEAN fThreadFreeze,
|
|
#endif
|
|
UCHAR chStepType)
|
|
{
|
|
SetRegPCValue(addr);
|
|
steptracepasscnt = count;
|
|
#ifndef KERNEL
|
|
pThreadCmd = pThread;
|
|
fFreeze = fThreadFreeze;
|
|
#endif
|
|
if (chStepType == 'w') {
|
|
WatchTarget = *addr;
|
|
ComputeNextOffset(WatchTarget, TRUE);
|
|
if ( Flat(WatchTarget) != -1 || count != 1) {
|
|
#ifdef KERNEL
|
|
SetupSpecialCalls();
|
|
#else
|
|
KernelCalls = 0;
|
|
NtCalls = 0;
|
|
fDeferredDecrement = FALSE;
|
|
#endif
|
|
WatchCount = 0;
|
|
Watching = TRUE;
|
|
steptracepasscnt = 0xfffffff;
|
|
if ( count != 1 ) {
|
|
Flat(WatchTarget) = count;
|
|
// dprintf("WatchTarget = %x, count = %d\n", count, steptracepasscnt);
|
|
}
|
|
chStepType = 't';
|
|
}
|
|
}
|
|
|
|
#ifndef KERNEL
|
|
oldcmdState = chStepType;
|
|
#endif
|
|
|
|
cmdState = chStepType;
|
|
fControlC = FALSE;
|
|
}
|
|
|
|
/*** OutDisCurrent - output disassembly of current instruction
|
|
*
|
|
* Purpose:
|
|
* The instruction at the current program current is disassembled
|
|
* with any effective address displayed.
|
|
*
|
|
* Input:
|
|
* None.
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* If the disassembly is of a delayed control instruction, the
|
|
* delay slot instruction is also output.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void OutDisCurrent(BOOLEAN fEA, BOOLEAN fSymbol)
|
|
{
|
|
ADDR pcvalue;
|
|
UCHAR buffer[SYMBOLSIZE+100];
|
|
BOOLEAN fSourceOutput;
|
|
|
|
GetRegPCValue(&pcvalue);
|
|
|
|
if (fSymbol)
|
|
OutputSymAddr(Flat(pcvalue), TRUE, TRUE);
|
|
disasm(&pcvalue, buffer, fEA);
|
|
dprintf("%s", buffer);
|
|
if (fDelayInstruction()) {
|
|
disasm(&pcvalue, buffer, fEA);
|
|
dprintf("%s", buffer);
|
|
}
|
|
}
|
|
|
|
void OutputSymAddr (ULONG offset, BOOLEAN fForce, BOOLEAN fLabel)
|
|
{
|
|
UCHAR chAddrBuffer[SYMBOLSIZE+100];
|
|
ULONG displacement;
|
|
|
|
GetSymbolStdCall(offset, chAddrBuffer, &displacement, NULL);
|
|
if ((!displacement || fForce) && chAddrBuffer[0]) {
|
|
dprintf("%s", chAddrBuffer);
|
|
if (displacement)
|
|
dprintf("+0x%lx", displacement);
|
|
if (fLabel)
|
|
dprintf(":\n");
|
|
else
|
|
dprintf(" ");
|
|
}
|
|
}
|
|
|
|
/*** fnCompareMemory - compare two ranges of memory
|
|
*
|
|
* Purpose:
|
|
* Function of "c<range><addr>" command.
|
|
*
|
|
* To compare two ranges of memory, starting at offsets
|
|
* src1addr and src2addr, respectively, for length bytes.
|
|
* Bytes that mismatch are displayed with their offsets
|
|
* and contents.
|
|
*
|
|
* Input:
|
|
* src1addr - start of first memory region
|
|
* length - count of bytes to compare
|
|
* src2addr - start of second memory region
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* error exit: MEMORY - memory read access failure
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnCompareMemory (PADDR src1addr, ULONG length, PADDR src2addr)
|
|
{
|
|
ULONG compindex;
|
|
UCHAR src1ch;
|
|
UCHAR src2ch;
|
|
|
|
for (compindex = 0; compindex < length; compindex++) {
|
|
if (!GetMemByte(src1addr, &src1ch))
|
|
error(MEMORY);
|
|
if (!GetMemByte(src2addr, &src2ch))
|
|
error(MEMORY);
|
|
if (src1ch != src2ch) {
|
|
dprintAddr(src1addr); dprintf(" %02x - ", src1ch);
|
|
dprintAddr(src2addr); dprintf(" %02x\n", src2ch);
|
|
}
|
|
AddrAdd(src1addr,1);
|
|
AddrAdd(src2addr,1);
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*** fnMoveMemory - move a range of memory to another
|
|
*
|
|
* Purpose:
|
|
* Function of "m<range><addr>" command.
|
|
*
|
|
* To move a range of memory starting at srcaddr to memory
|
|
* starting at destaddr for length bytes.
|
|
*
|
|
* Input:
|
|
* srcaddr - start of source memory region
|
|
* length - count of bytes to move
|
|
* destaddr - start of destination memory region
|
|
*
|
|
* Output:
|
|
* memory at destaddr has moved values
|
|
*
|
|
* Exceptions:
|
|
* error exit: MEMORY - memory reading or writing access failure
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnMoveMemory (PADDR srcaddr, ULONG length, PADDR destaddr)
|
|
{
|
|
UCHAR ch;
|
|
ULONG incr = 1;
|
|
|
|
if (AddrLt(*srcaddr, *destaddr)) {
|
|
AddrAdd(srcaddr, length - 1);
|
|
AddrAdd(destaddr, length - 1);
|
|
incr = (ULONG)-1;
|
|
}
|
|
while (length--) {
|
|
if (!GetMemByte(srcaddr, &ch))
|
|
error(MEMORY);
|
|
if (!SetMemByte(destaddr, ch))
|
|
error(MEMORY);
|
|
AddrAdd(srcaddr, incr);
|
|
AddrAdd(destaddr, incr);
|
|
}
|
|
}
|
|
|
|
/*** fnFillMemory - fill memory with a byte list
|
|
*
|
|
* Purpose:
|
|
* Function of "f<range><bytelist>" command.
|
|
*
|
|
* To fill a range of memory with the byte list specified.
|
|
* The pattern repeats if the range size is larger than the
|
|
* byte list size.
|
|
*
|
|
* Input:
|
|
* startaddr - offset of memory to fill
|
|
* length - number of bytes to fill
|
|
* *plist - pointer to byte array to define values to set
|
|
* length - size of *plist array
|
|
*
|
|
* Exceptions:
|
|
* error exit: MEMORY - memory write access failure
|
|
*
|
|
* Output:
|
|
* memory at startaddr filled.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnFillMemory (PADDR startaddr, ULONG length, PUCHAR plist, ULONG count)
|
|
{
|
|
ULONG fillindex;
|
|
ULONG listindex = 0;
|
|
|
|
for (fillindex = 0; fillindex < length; fillindex++) {
|
|
if (!SetMemByte(startaddr , *(plist + listindex++)))
|
|
error(MEMORY);
|
|
if (listindex == count)
|
|
listindex = 0;
|
|
AddrAdd(startaddr, 1);
|
|
}
|
|
}
|
|
|
|
/*** fnSearchMemory - search memory with for a byte list
|
|
*
|
|
* Purpose:
|
|
* Function of "s<range><bytelist>" command.
|
|
*
|
|
* To search a range of memory with the byte list specified.
|
|
* If a match occurs, the offset of memory is output.
|
|
*
|
|
* Input:
|
|
* startaddr - offset of memory to start search
|
|
* length - size of range to search
|
|
* *plist - pointer to byte array to define values to search
|
|
* length - size of *plist array
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* error exit: MEMORY - memory read access failure
|
|
*
|
|
*************************************************************************/
|
|
|
|
void fnSearchMemory (PADDR startaddr, ULONG length, PUCHAR plist, ULONG count)
|
|
{
|
|
ULONG searchindex;
|
|
ULONG listindex;
|
|
UCHAR ch;
|
|
ADDR tAddr = *startaddr;
|
|
|
|
for (searchindex=0;searchindex<length;tAddr=*startaddr, searchindex++) {
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
return;
|
|
}
|
|
AddrAdd(&tAddr, searchindex);
|
|
for (listindex = 0; listindex<count;AddrAdd(&tAddr,1), listindex++) {
|
|
if (!GetMemByte(&tAddr, &ch))
|
|
error(MEMORY);
|
|
if (ch != *(plist + listindex))
|
|
break;
|
|
}
|
|
if (listindex == count) {
|
|
tAddr = *startaddr;
|
|
AddrAdd(&tAddr, searchindex);
|
|
dprintAddr(&tAddr);
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void fnSetSuffix (void)
|
|
{
|
|
UCHAR ch;
|
|
|
|
ch = PeekChar();
|
|
ch = (UCHAR)tolower(ch);
|
|
|
|
if (ch == ';' || ch == '\0') {
|
|
if (chSymbolSuffix == 'n') {
|
|
dprintf("n - no suffix\n");
|
|
} else if (chSymbolSuffix == 'a') {
|
|
dprintf("a - ascii\n");
|
|
} else {
|
|
dprintf("w - wide\n");
|
|
}
|
|
}
|
|
else if (ch == 'n' || ch == 'a' || ch == 'w') {
|
|
chSymbolSuffix = ch;
|
|
pchCommand++;
|
|
} else {
|
|
error(SYNTAX);
|
|
}
|
|
}
|
|
|
|
/*** GetMemByte - get byte memory value
|
|
*
|
|
* Purpose:
|
|
* To return the byte value of the memory offset specified.
|
|
*
|
|
* Input:
|
|
* addr - offset of memory to get
|
|
* *pvalue - pointer to byte to set with memory value
|
|
*
|
|
* Output:
|
|
* byte at *pvalue set if read successful
|
|
*
|
|
* Returns:
|
|
* TRUE if read successful else FALSE
|
|
*
|
|
*************************************************************************/
|
|
|
|
BOOLEAN GetMemByte (PADDR addr, PUCHAR pvalue)
|
|
{
|
|
return (BOOLEAN)(GetMemString(addr, pvalue, sizeof(UCHAR)) ==
|
|
sizeof(UCHAR));
|
|
}
|
|
|
|
/*** GetMemWord - get word memory value
|
|
*
|
|
* Purpose:
|
|
* To return the word value of the memory offset specified.
|
|
*
|
|
* Input:
|
|
* addr - offset of memory to get
|
|
* *pvalue - pointer to word to set with memory value
|
|
*
|
|
* Output:
|
|
* word at *pvalue set if read successful
|
|
*
|
|
* Returns:
|
|
* TRUE if read successful else FALSE
|
|
*
|
|
*************************************************************************/
|
|
|
|
BOOLEAN GetMemWord (PADDR addr, PUSHORT pvalue)
|
|
{
|
|
return (BOOLEAN)(GetMemString(addr, (PUCHAR)pvalue, sizeof(USHORT)) ==
|
|
sizeof(USHORT));
|
|
}
|
|
|
|
/*** GetMemDword - get double word memory value
|
|
*
|
|
* Purpose:
|
|
* To return the double word value of the memory offset specified.
|
|
*
|
|
* Input:
|
|
* addr - offset of memory to get
|
|
* *pvalue - pointer to double word to set with memory value
|
|
*
|
|
* Output:
|
|
* double word at *pvalue set if read successful
|
|
*
|
|
* Returns:
|
|
* TRUE if read successful else FALSE
|
|
*
|
|
*************************************************************************/
|
|
|
|
BOOLEAN GetMemDword (PADDR addr, PULONG pvalue)
|
|
{
|
|
return (BOOLEAN)(GetMemString(addr, (PUCHAR)pvalue, sizeof(ULONG)) ==
|
|
sizeof(ULONG));
|
|
}
|
|
|
|
/*** GetMemString - get memory string values
|
|
*
|
|
* Purpose:
|
|
* To read a string of a specified length with the memory
|
|
* values selected. Break reads across page boundaries -
|
|
* multiples of size pageSize.
|
|
*
|
|
* Input:
|
|
* addr - offset of memory to start reading
|
|
* pBufDest - pointer to byte string to set with memory values
|
|
*
|
|
* Output:
|
|
* bytes at pBufDest set if read successful
|
|
*
|
|
* Returns:
|
|
* number of bytes actually read
|
|
*
|
|
*************************************************************************/
|
|
|
|
ULONG GetMemString (PADDR paddr, PUCHAR pBufDest, ULONG length)
|
|
{
|
|
|
|
ULONG cTotalBytesRead = 0;
|
|
ULONG cBytesRead;
|
|
ULONG readcount;
|
|
PUCHAR pBufSource;
|
|
BOOLEAN fSuccess;
|
|
|
|
assert(fFlat(*paddr) || fInstrPtr(*paddr));
|
|
|
|
#ifdef KERNEL
|
|
// bugbug: check if this readcachedmemory is still needed
|
|
//if (!fSwitched) {
|
|
// if (cBytesRead = ReadCachedMemory(paddr, pBufDest, length))
|
|
// return cBytesRead;
|
|
// }
|
|
//
|
|
// Pass request to ReadVirtualMemory - the lower level API will
|
|
// take care of page boundaries
|
|
//
|
|
|
|
pBufSource = (PUCHAR)(Flat(*paddr));
|
|
fSuccess = (BOOLEAN)NT_SUCCESS(DbgKdReadVirtualMemory(
|
|
(PVOID) pBufSource,
|
|
(PVOID) pBufDest,
|
|
length,
|
|
&cTotalBytesRead ));
|
|
|
|
return fSuccess ? cTotalBytesRead : 0;
|
|
|
|
#endif
|
|
|
|
pBufSource = (PUCHAR)(Flat(*paddr));
|
|
|
|
do {
|
|
// do not perform reads across page boundaries.
|
|
// calculate bytes to read in present page in readcount.
|
|
|
|
readcount = min(length - cTotalBytesRead,
|
|
pageSize - ((ULONG)pBufSource & (pageSize - 1)));
|
|
|
|
fSuccess = ReadVirtualMemory(pBufSource, pBufDest, readcount,
|
|
&cBytesRead);
|
|
|
|
// update total bytes read and new address for next read
|
|
|
|
if (fSuccess) {
|
|
cTotalBytesRead += cBytesRead;
|
|
pBufSource += cBytesRead;
|
|
pBufDest += cBytesRead;
|
|
}
|
|
}
|
|
|
|
// keep reading until failure or all bytes read
|
|
|
|
while (fSuccess && cTotalBytesRead < length);
|
|
|
|
return cTotalBytesRead;
|
|
}
|
|
|
|
/*** SetMemByte - set byte memory value
|
|
*
|
|
* Purpose:
|
|
* To set the byte value of the memory offset specified.
|
|
*
|
|
* Input:
|
|
* addr - offset of memory to set
|
|
* pvalue - byte value to set memory
|
|
*
|
|
* Output:
|
|
* byte at addr set if read successful
|
|
*
|
|
* Returns:
|
|
* TRUE if write successful else FALSE
|
|
*
|
|
*************************************************************************/
|
|
|
|
BOOLEAN SetMemByte (PADDR addr, UCHAR value)
|
|
{
|
|
return (BOOLEAN)(SetMemString(addr, &value, sizeof(UCHAR)) ==
|
|
sizeof(UCHAR));
|
|
}
|
|
|
|
/*** SetMemWord - set ushort memory value
|
|
*
|
|
* Purpose:
|
|
* To set the ushort value of the memory offset specified.
|
|
*
|
|
* Input:
|
|
* addr - offset of memory to set
|
|
* pvalue - ushort value to set memory
|
|
*
|
|
* Output:
|
|
* ushort at addr set if read successful
|
|
*
|
|
* Returns:
|
|
* TRUE if write successful else FALSE
|
|
*
|
|
*************************************************************************/
|
|
|
|
BOOLEAN SetMemWord (PADDR addr, USHORT value)
|
|
{
|
|
return (BOOLEAN)(SetMemString(addr, (PUCHAR)&value, sizeof(USHORT)) ==
|
|
sizeof(USHORT));
|
|
}
|
|
|
|
/*** SetMemDword - set double word memory value
|
|
*
|
|
* Purpose:
|
|
* To set the double word value of the memory offset specified.
|
|
*
|
|
* Input:
|
|
* addr - offset of memory to set
|
|
* pvalue - double word value to set memory
|
|
*
|
|
* Output:
|
|
* double word at addr set if read successful
|
|
*
|
|
* Returns:
|
|
* TRUE if write successful else FALSE
|
|
*
|
|
*************************************************************************/
|
|
|
|
BOOLEAN SetMemDword (PADDR addr, ULONG value)
|
|
{
|
|
return (BOOLEAN)(SetMemString(addr, (PUCHAR)&value, sizeof(ULONG)) ==
|
|
sizeof(ULONG));
|
|
}
|
|
|
|
/*** SetMemString - set memory string values
|
|
*
|
|
* Purpose:
|
|
* To write a string of a specified length with the memory
|
|
* values selected.
|
|
*
|
|
* Input:
|
|
* addr - offset of memory to start writing
|
|
* *pvalue - pointer to byte string to set with memory values
|
|
*
|
|
* Output:
|
|
* bytes at *pvalue set if write successful
|
|
*
|
|
* Returns:
|
|
* number of bytes actually write
|
|
*
|
|
*************************************************************************/
|
|
|
|
ULONG SetMemString (PADDR paddr, PUCHAR pvalue, ULONG length)
|
|
{
|
|
// NTSTATUS status;
|
|
ULONG cBytesWritten;
|
|
|
|
assert(fFlat(*paddr) || fInstrPtr(*paddr));
|
|
|
|
//#ifdef KERNEL
|
|
// WriteCachedMemory(paddr, pvalue, length);
|
|
//#endif
|
|
|
|
// status =
|
|
DbgKdWriteVirtualMemory((PVOID)Flat(*paddr), (PVOID)pvalue,
|
|
length, &cBytesWritten);
|
|
// if (!NT_SUCCESS(status))
|
|
// cBytesWritten = 0;
|
|
|
|
return cBytesWritten;
|
|
}
|
|
|
|
/*** RestoreBrkpts - restore breakpoints
|
|
*
|
|
* Purpose:
|
|
* To restore the original instructions that were replaced
|
|
* by breakpoint instructions in SetBrkpts.
|
|
*
|
|
* Input:
|
|
* None.
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* The order of restoring breakpoints is opposite that of setting
|
|
* them in SetBrkpts in case of duplicate addresses.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void RestoreBrkpts (void)
|
|
{
|
|
ULONG index;
|
|
#ifndef KERNEL
|
|
PPROCESS_INFO pProcessSave;
|
|
#endif
|
|
|
|
#ifdef KERNEL
|
|
if (BrkpointsSuspended) {
|
|
return; // do nothing
|
|
}
|
|
#endif
|
|
|
|
// restore the deferred breakpoint if set
|
|
//dprintf("RestoreBrkpts() called (gocnt=%d)\n", gocnt);
|
|
|
|
if (fDeferBpSet) {
|
|
RestoreBreakPoint(deferhandle);
|
|
fDeferBpSet = FALSE;
|
|
}
|
|
|
|
// restore the step/trace breakpoint if set
|
|
|
|
if (fStepTraceBpSet) {
|
|
RestoreBreakPoint(steptracehandle);
|
|
fStepTraceBpSet = FALSE;
|
|
}
|
|
|
|
// restore any appropriate temporary breakpoints (reverse order)
|
|
|
|
for (index = gocnt - 1; index != -1; index--) {
|
|
//dprintf("Restore bp @%08lx, handle=%08lx ", Flat(golist[index].addr),
|
|
// golist[index].handle);
|
|
if (golist[index].fBpSet) {
|
|
//dprintf("[okay] [status=%s]\n",
|
|
RestoreBreakPoint(golist[index].handle)
|
|
// ==STATUS_SUCCESS?"SUCCESS":"FAILURE")
|
|
;
|
|
golist[index].fBpSet = FALSE;
|
|
}
|
|
//else dprintf("[nope]\n");
|
|
}
|
|
|
|
// restore any appropriate permanent breakpoints (reverse order)
|
|
|
|
#ifndef KERNEL
|
|
pProcessSave = pProcessCurrent;
|
|
#endif
|
|
for (index = (MAX_NUMBER_OF_BREAKPOINTS - 1); index != -1; index--)
|
|
if (brkptlist[index].fBpSet) {
|
|
#ifndef KERNEL
|
|
pProcessCurrent = brkptlist[index].pProcess;
|
|
#endif
|
|
RestoreBreakPoint(brkptlist[index].handle);
|
|
brkptlist[index].fBpSet = FALSE;
|
|
}
|
|
#ifndef KERNEL
|
|
pProcessCurrent = pProcessSave;
|
|
#endif
|
|
}
|
|
|
|
/*** SetBrkpts - set breakpoints
|
|
*
|
|
* Purpose:
|
|
* For each breakpoint set, save the current instruction and set
|
|
* the breakpoint instruction.
|
|
*
|
|
* Input:
|
|
* None.
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* The order of setting breakpoints is opposite that of restoring
|
|
* them in RestoreBrkpts in case of duplicate addresses.
|
|
*
|
|
* If a breakpoint is defined at the current instruction, the
|
|
* breakpoint must be deferred so that instruction can execute.
|
|
* In this case, do not set the breakpoint, but set the fBpDefer
|
|
* flag TRUE. If fBpDefer is TRUE at the end of the routine,
|
|
* and cmdState is 'p' (step) or 'g' (go), set the trace breakpoint
|
|
* and change the cmdState to 'P' or 'G', respectively. This will
|
|
* trace the instruction at the breakpoint location (which is not set).
|
|
* Upon breaking after the trace, the breakpoint can then be set,
|
|
* cmdState is reset to 'p' or 'g' and the command can proceed.
|
|
*
|
|
*************************************************************************/
|
|
|
|
BOOLEAN SetBrkpts (void)
|
|
{
|
|
NTSTATUS ntstatus;
|
|
ULONG index;
|
|
ADDR pcaddr;
|
|
#if defined(KERNEL) && defined(i386)
|
|
ULONG regDR7Value = 0;
|
|
UCHAR cntDataBrkpts = 0;
|
|
#endif
|
|
#ifndef KERNEL
|
|
PPROCESS_INFO pProcessSave;
|
|
#endif
|
|
#if !defined(KERNEL) && defined(i386)
|
|
PPROCESS_INFO pProcess;
|
|
PTHREAD_INFO pThread;
|
|
#endif
|
|
|
|
#if defined(KERNEL) && defined(i386)
|
|
regDR7Value = GetDregValue(7);
|
|
|
|
//
|
|
// Turn off all data breakpoints. (We will turn the enabled ones back
|
|
// on when we restart execution)
|
|
//
|
|
|
|
//regDR7Value &= ~0xff;
|
|
|
|
regDR7Value &= 0xff00L;
|
|
#endif
|
|
|
|
GetRegPCValue(&pcaddr);
|
|
fDeferDefined = FALSE;
|
|
|
|
// set any appropriate permanent breakpoints
|
|
// for i386, set any appropriate data breakpoints
|
|
|
|
#if !defined(KERNEL) && defined(i386)
|
|
|
|
// for each thread in each process, set count and DR7
|
|
|
|
pProcess = pProcessHead;
|
|
while (pProcess) {
|
|
pThread = pProcess->pThreadHead;
|
|
while (pThread) {
|
|
pThread->DReg7 = 0;
|
|
pThread->cntDReg = 0;
|
|
pThread = pThread->pThreadNext;
|
|
}
|
|
pProcess = pProcess->pProcessNext;
|
|
}
|
|
#endif
|
|
|
|
for (index = 0; index < MAX_NUMBER_OF_BREAKPOINTS; index++)
|
|
if (brkptlist[index].status == 'e'
|
|
#ifdef KERNEL
|
|
&& !brkptlist[index].bpInternal
|
|
#endif
|
|
) {
|
|
NotFlat(brkptlist[index].addr);
|
|
ComputeFlatAddress(&brkptlist[index].addr,NULL);
|
|
|
|
if (AddrEqu(brkptlist[index].addr, pcaddr) &&
|
|
brkptlist[index].option != 2)
|
|
fDeferDefined=TRUE;
|
|
else
|
|
#if defined(KERNEL) && defined(i386)
|
|
if (brkptlist[index].option != (UCHAR)-1) {
|
|
if (cntDataBrkpts > 3) {
|
|
dprintf("too many data breakpoints\n");
|
|
return FALSE;
|
|
}
|
|
regDR7Value |= (((ULONG)brkptlist[index].size << 2)
|
|
+ (ULONG)brkptlist[index].option
|
|
<< (16 + cntDataBrkpts * 2))
|
|
+ 2 << (cntDataBrkpts * 2);
|
|
if (fDataBrkptsChanged) {
|
|
SetDregValue(cntDataBrkpts, Flat(brkptlist[index].addr));
|
|
}
|
|
brkptlist[index].dregindx = cntDataBrkpts++;
|
|
fSetGlobalDataBrkpts = TRUE;
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if !defined(KERNEL) && defined(i386)
|
|
if (brkptlist[index].option != (UCHAR)-1) {
|
|
|
|
// for user-mode data breakpoints, if pThread is not
|
|
// NULL, set for that thread, else set all threads
|
|
|
|
pThread = (brkptlist[index].pProcess)->pThreadHead;
|
|
while (pThread) {
|
|
if (brkptlist[index].pThread == NULL ||
|
|
brkptlist[index].pThread == pThread) {
|
|
|
|
if (pThread->cntDReg > 3) {
|
|
dprintf("too many data breakpoints\n");
|
|
return FALSE;
|
|
}
|
|
pThread->DReg7 |=
|
|
(((ULONG)brkptlist[index].size << 2)
|
|
+ (ULONG)brkptlist[index].option
|
|
<< (16 + pThread->cntDReg * 2))
|
|
+ 1 << (pThread->cntDReg * 2); // LE
|
|
pThread->DReg[pThread->cntDReg] =
|
|
Flat(brkptlist[index].addr);
|
|
brkptlist[index].dregindx = pThread->cntDReg++;
|
|
}
|
|
pThread = pThread->pThreadNext;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
|
|
if (AddrEqu(brkptlist[index].addr, pcaddr) &&
|
|
brkptlist[index].option != 2
|
|
#ifndef KERNEL
|
|
&& brkptlist[index].pProcess == pProcessCurrent
|
|
#endif
|
|
) {
|
|
#if 0
|
|
if (fVerboseOutput) {
|
|
dprintf("Defering replacement of bp@");
|
|
dprintAddr(pcaddr);
|
|
dprintf("\n");
|
|
}
|
|
#endif
|
|
fDeferDefined = TRUE;
|
|
}
|
|
else {
|
|
#ifndef KERNEL
|
|
pProcessSave = pProcessCurrent;
|
|
pProcessCurrent = brkptlist[index].pProcess;
|
|
#endif
|
|
#ifdef KERNEL
|
|
if (!brkptlist[index].bpInternal && !BrkpointsSuspended) {
|
|
#endif
|
|
ntstatus = WriteBreakPoint( brkptlist[index].addr,
|
|
&brkptlist[index].handle );
|
|
|
|
#ifndef KERNEL
|
|
pProcessCurrent = pProcessSave;
|
|
#endif
|
|
if (!(brkptlist[index].fBpSet = (BOOLEAN)NT_SUCCESS(ntstatus))) {
|
|
dprintf("bp%d at addr ", index);
|
|
dprintAddr(&brkptlist[index].addr);
|
|
dprintf(" failed\n");
|
|
return FALSE;
|
|
}
|
|
#ifdef KERNEL
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if defined(KERNEL) && defined(i386)
|
|
if (cntDataBrkpts)
|
|
regDR7Value |= 0x100; // local exact match, which is effectively
|
|
// global on NT.
|
|
// SetDregValue(6, 0);
|
|
SetDregValue(7, regDR7Value);
|
|
#endif
|
|
|
|
#if !defined(KERNEL) && defined(i386)
|
|
// for each thread in each process, set registers
|
|
|
|
pProcessSave = pProcessCurrent;
|
|
pProcessCurrent = pProcessHead;
|
|
while (pProcessCurrent) {
|
|
pThread = pProcessCurrent->pThreadHead;
|
|
while (pThread) {
|
|
if (pThread->DReg7) {
|
|
pThread->DReg7 |= 0x100; // local exact match
|
|
|
|
ChangeRegContext(pThread);
|
|
SetDregValue(0, pThread->DReg[0]);
|
|
SetDregValue(1, pThread->DReg[1]);
|
|
SetDregValue(2, pThread->DReg[2]);
|
|
SetDregValue(3, pThread->DReg[3]);
|
|
SetDregValue(6, 0);
|
|
SetDregValue(7, pThread->DReg7);
|
|
} else {
|
|
SetDregValue(7, pThread->DReg7);
|
|
}
|
|
pThread = pThread->pThreadNext;
|
|
}
|
|
pProcessCurrent = pProcessCurrent->pProcessNext;
|
|
}
|
|
pProcessCurrent = pProcessSave;
|
|
ChangeRegContext(pProcessCurrent->pThreadCurrent);
|
|
#endif
|
|
|
|
// set any appropriate temporary breakpoints
|
|
|
|
if (cmdState == 'g')
|
|
for (index = 0; index < gocnt; index++) {
|
|
NotFlat(golist[index].addr);
|
|
ComputeFlatAddress(&golist[index].addr,NULL);
|
|
|
|
if (AddrEqu(golist[index].addr, pcaddr)) {
|
|
#if 0
|
|
if (fVerboseOutput) {
|
|
dprintf("Defering replacement of bp@");
|
|
dprintAddr(pcaddr);
|
|
dprintf("\n");
|
|
}
|
|
#endif
|
|
fDeferDefined = TRUE;
|
|
}else {
|
|
ntstatus=WriteBreakPoint( golist[index].addr,
|
|
&golist[index].handle );
|
|
|
|
//dprintf("Wrote bp @%08lx, handle=%08lx\n", Flat(golist[index].addr),
|
|
// golist[index].handle);
|
|
|
|
if (!(golist[index].fBpSet = (BOOLEAN)NT_SUCCESS(ntstatus))) {
|
|
dprintf("temp bp%d at addr ", index);
|
|
dprintAddr(&golist[index].addr);
|
|
dprintf(" failed \n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// set the step/trace breakpoint if appropriate
|
|
|
|
else if (cmdState == 'p' || cmdState == 't') {
|
|
if (Flat(steptraceaddr) == -1)
|
|
SetTraceFlag();
|
|
else if (AddrEqu(steptraceaddr, pcaddr))
|
|
fDeferDefined = TRUE;
|
|
else {
|
|
|
|
#ifndef KERNEL
|
|
pProcessSave = pProcessCurrent;
|
|
pProcessCurrent = pProcessStepBrkpt;
|
|
#endif
|
|
ntstatus = WriteBreakPoint( steptraceaddr,
|
|
&steptracehandle );
|
|
#ifndef KERNEL
|
|
pProcessCurrent = pProcessSave;
|
|
#endif
|
|
if (!(fStepTraceBpSet = (BOOLEAN)NT_SUCCESS(ntstatus))) {
|
|
dprintf("trace bp at addr ");
|
|
dprintAddr(&steptraceaddr);
|
|
dprintf("failed.\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// process deferred breakpoint
|
|
|
|
if (fDeferDefined) {
|
|
#if defined(MIPS) || defined(ALPHA) || defined(_PPC_)
|
|
ComputeNextOffset(deferaddr, FALSE);
|
|
if (Flat(deferaddr) != -1 ) {
|
|
ntstatus = WriteBreakPoint( deferaddr,
|
|
&deferhandle );
|
|
|
|
if (!(fDeferBpSet = (BOOLEAN)NT_SUCCESS(ntstatus))) {
|
|
dprintf("trace bp at addr %08lx failed\n", deferaddr);
|
|
return FALSE;
|
|
}
|
|
}
|
|
#else
|
|
SetTraceFlag();
|
|
#endif
|
|
#ifndef KERNEL
|
|
pProcessDeferBrkpt = pProcessCurrent;
|
|
#endif
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifndef KERNEL
|
|
|
|
//---------------------------------------------------------------
|
|
//
|
|
// Purpose: Sets a specific CODE breakpoint.
|
|
//
|
|
// Input: BrkptNo - index of the breakpoint to set in brkptlist
|
|
//
|
|
//---------------------------------------------------------------
|
|
BOOLEAN SetSpecificBrkpt (ULONG BrkptNo)
|
|
{
|
|
NTSTATUS ntstatus;
|
|
ADDR pcaddr;
|
|
PPROCESS_INFO pProcessSave;
|
|
PPROCESS_INFO pProcess;
|
|
PTHREAD_INFO pThread;
|
|
|
|
#ifdef BP_CORRUPTION
|
|
UnlockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
|
|
GetRegPCValue(&pcaddr);
|
|
fDeferDefined = FALSE;
|
|
|
|
pProcess = pProcessHead;
|
|
#ifdef I386
|
|
while (pProcess) {
|
|
pThread = pProcess->pThreadHead;
|
|
while (pThread) {
|
|
pThread->DReg7 = 0;
|
|
pThread->cntDReg = 0;
|
|
pThread = pThread->pThreadNext;
|
|
}
|
|
pProcess = pProcess->pProcessNext;
|
|
}
|
|
#endif
|
|
|
|
if (brkptlist[BrkptNo].status == 'e') {
|
|
NotFlat(brkptlist[BrkptNo].addr);
|
|
ComputeFlatAddress(&brkptlist[BrkptNo].addr,NULL);
|
|
|
|
if (AddrEqu(brkptlist[BrkptNo].addr, pcaddr) &&
|
|
(brkptlist[BrkptNo].option != 2)) {
|
|
fDeferDefined=TRUE;
|
|
} else {
|
|
pProcessSave = pProcessCurrent;
|
|
pProcessCurrent = brkptlist[BrkptNo].pProcess;
|
|
|
|
ntstatus = WriteBreakPoint( brkptlist[BrkptNo].addr,
|
|
&brkptlist[BrkptNo].handle );
|
|
|
|
pProcessCurrent = pProcessSave;
|
|
if (!(brkptlist[BrkptNo].fBpSet = (BOOLEAN)NT_SUCCESS(ntstatus))) {
|
|
dprintf("bp%d at addr ", BrkptNo);
|
|
dprintAddr(&brkptlist[BrkptNo].addr);
|
|
dprintf(" failed\n");
|
|
#ifdef BP_CORRUPTION
|
|
LockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// for each thread in each process, set registers
|
|
pProcessSave = pProcessCurrent;
|
|
pProcessCurrent = pProcessHead;
|
|
#ifdef I386
|
|
while (pProcessCurrent) {
|
|
pThread = pProcessCurrent->pThreadHead;
|
|
while (pThread) {
|
|
if (pThread->DReg7)
|
|
pThread->DReg7 |= 0x100; // local exact match
|
|
|
|
ChangeRegContext(pThread);
|
|
SetDregValue(0, pThread->DReg[0]);
|
|
SetDregValue(1, pThread->DReg[1]);
|
|
SetDregValue(2, pThread->DReg[2]);
|
|
SetDregValue(3, pThread->DReg[3]);
|
|
SetDregValue(6, 0);
|
|
SetDregValue(7, pThread->DReg7);
|
|
|
|
pThread = pThread->pThreadNext;
|
|
}
|
|
pProcessCurrent = pProcessCurrent->pProcessNext;
|
|
}
|
|
#endif
|
|
pProcessCurrent = pProcessSave;
|
|
ChangeRegContext(pProcessCurrent->pThreadCurrent);
|
|
|
|
// process deferred breakpoint
|
|
if (fDeferDefined) {
|
|
SetTraceFlag();
|
|
pProcessDeferBrkpt = pProcessCurrent;
|
|
}
|
|
|
|
#ifdef BP_CORRUPTION
|
|
LockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
return TRUE;
|
|
}
|
|
|
|
void RemoveProcessBps (PPROCESS_INFO pProcess)
|
|
{
|
|
ULONG index;
|
|
|
|
#ifdef BP_CORRUPTION
|
|
UnlockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
for (index = 0; index < MAX_NUMBER_OF_BREAKPOINTS; index++) {
|
|
if (brkptlist[index].status != '\0' && (brkptlist[index].pProcess == pProcess)) {
|
|
brkptlist[index].status = '\0';
|
|
}
|
|
}
|
|
#ifdef BP_CORRUPTION
|
|
LockBreakpointList();
|
|
#endif // BP_CORRUPTION
|
|
}
|
|
|
|
void RemoveThreadBps (PTHREAD_INFO pThread)
|
|
{
|
|
ULONG index;
|
|
|
|
for (index = 0; index < MAX_NUMBER_OF_BREAKPOINTS; index++)
|
|
if (brkptlist[index].status != '\0'
|
|
&& brkptlist[index].pProcess == pProcessCurrent
|
|
&& brkptlist[index].pThread == pThread)
|
|
brkptlist[index].status = '\0';
|
|
}
|
|
|
|
void SuspendAllThreads (void)
|
|
{
|
|
PPROCESS_INFO pProcess;
|
|
PTHREAD_INFO pThread;
|
|
|
|
pProcess = pProcessHead;
|
|
while (pProcess) {
|
|
pThread = pProcess->pThreadHead;
|
|
while (pThread) {
|
|
SuspendThread(pThread->hThread);
|
|
pThread = pThread->pThreadNext;
|
|
}
|
|
pProcess = pProcess->pProcessNext;
|
|
}
|
|
}
|
|
|
|
|
|
void ResumeAllThreads (void)
|
|
{
|
|
PPROCESS_INFO pProcess;
|
|
PTHREAD_INFO pThread;
|
|
|
|
pProcess = pProcessHead;
|
|
while (pProcess) {
|
|
pThread = pProcess->pThreadHead;
|
|
while (pThread) {
|
|
|
|
// dprintf("** resuming thread id: %08lx handle: %08lx\n",
|
|
// pThread->dwThreadId, (ULONG)pThread->hThread);
|
|
|
|
if (pThread->hThread != NULL &&
|
|
ResumeThread(pThread->hThread) == -1)
|
|
dprintf("%s: ResumeThread failed\n", DebuggerName);
|
|
pThread = pThread->pThreadNext;
|
|
}
|
|
pProcess = pProcess->pProcessNext;
|
|
}
|
|
}
|
|
|
|
/*** ChangeRegContext - change thread register context
|
|
*
|
|
* Purpose:
|
|
* Update the current register context to the thread specified.
|
|
* The NULL value implies no context. Update pActiveThread
|
|
* to point to the thread in context.
|
|
*
|
|
* Input:
|
|
* pNewContext - pointer to new thread context (NULL if none).
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* failed register context call (get or set)
|
|
*
|
|
* Notes:
|
|
* Call with NULL argument to flush current register context
|
|
* before continuing with program.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void ChangeRegContext (PTHREAD_INFO pThreadNew)
|
|
{
|
|
static PTHREAD_INFO pThreadContext = NULL;
|
|
BOOL b;
|
|
|
|
if (pThreadNew != pThreadContext) {
|
|
if (pThreadContext != NULL && pThreadContext->hThread != NULL) {
|
|
#ifdef i386
|
|
RegisterContext = VDMRegisterContext;
|
|
#endif
|
|
b = DbgSetThreadContext(pThreadContext->hThread, &RegisterContext);
|
|
if (!b) {
|
|
dprintf("%s: SetThreadContext failed - pThread: %x Handle: %x Id: %x - Error == %u\n",
|
|
DebuggerName,
|
|
pThreadContext,
|
|
pThreadContext->hThread,
|
|
pThreadContext->dwThreadId,
|
|
GetLastError()
|
|
);
|
|
pThreadContext = NULL;
|
|
}
|
|
}
|
|
pThreadContext = pThreadNew;
|
|
if (pThreadContext != NULL && pThreadContext->hThread != NULL) {
|
|
RegisterContext.ContextFlags = ContextType;
|
|
b = DbgGetThreadContext(pThreadContext->hThread, &RegisterContext);
|
|
#ifdef i386
|
|
VDMRegisterContext = RegisterContext;
|
|
#endif
|
|
if (!b) {
|
|
dprintf("%s: GetThreadContext failed - pThread: %x Handle: %x Id: %x - Error == %u\n",
|
|
DebuggerName,
|
|
pThreadContext,
|
|
pThreadContext->hThread,
|
|
pThreadContext->dwThreadId,
|
|
GetLastError()
|
|
);
|
|
pThreadContext = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*** FreezeThreads - freeze threads before execution
|
|
*
|
|
* Purpose:
|
|
* Freeze program threads before execution. If fFreeze is
|
|
* FALSE, freeze threads marked as frozen in PTHREAD_INFO.
|
|
* If fFreeze is TRUE, freeze all threads but pThread.
|
|
*
|
|
* Input:
|
|
* None.
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* failed suspend thread call
|
|
* all nonterminating threads frozen
|
|
*
|
|
*************************************************************************/
|
|
|
|
BOOLEAN FreezeThreads (void)
|
|
{
|
|
BOOL b;
|
|
BOOLEAN fActive = FALSE;
|
|
BOOLEAN fNonTerm = FALSE;
|
|
PTHREAD_INFO pThread;
|
|
|
|
pThread = pProcessEvent->pThreadHead;
|
|
while (pThread) {
|
|
if (!pThread->fTerminating) {
|
|
fNonTerm = TRUE;
|
|
if ((fFreeze && pThread != pThreadCmd)
|
|
|| (!fFreeze && pThread->fFrozen)) {
|
|
dprintf("thread %d suspended\n", pThread->index);
|
|
b = SuspendThread(pThread->hThread);
|
|
if (!b) {
|
|
dprintf("%s: SuspendThread failed\n", DebuggerName);
|
|
return FALSE;
|
|
}
|
|
pThread->fSuspend = TRUE;
|
|
}
|
|
else
|
|
fActive = TRUE;
|
|
}
|
|
pThread = pThread->pThreadNext;
|
|
}
|
|
if (fNonTerm && !fActive) {
|
|
dprintf("No active threads to run\n");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*** UnfreezeThreads - unfreeze all frozen threads
|
|
*
|
|
* Purpose:
|
|
* Unfreeze all threads frozen by last FreezeThreads call.
|
|
*
|
|
* Input:
|
|
* None.
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* failed suspend thread call
|
|
*
|
|
*************************************************************************/
|
|
|
|
void UnfreezeThreads (void)
|
|
{
|
|
BOOL b;
|
|
BOOLEAN fActive = FALSE;
|
|
PTHREAD_INFO pThread;
|
|
|
|
pThread = pProcessEvent->pThreadHead;
|
|
while (pThread) {
|
|
if (pThread->fSuspend) {
|
|
dprintf("thread %d resumed\n", pThread->index);
|
|
b = ResumeThread(pThread->hThread);
|
|
if (!b) {
|
|
dprintf("%s: ResumeThread failed\n", DebuggerName);
|
|
return;
|
|
}
|
|
// assert(b);
|
|
pThread->fSuspend = FALSE;
|
|
}
|
|
pThread = pThread->pThreadNext;
|
|
}
|
|
}
|
|
|
|
#endif // ifndef KERNEL
|
|
|
|
#ifdef CHICAGO
|
|
ULONG DbgPrompt( char *Prompt, char *buffer, ULONG cb)
|
|
{
|
|
gets(buffer);
|
|
|
|
return strlen(buffer);
|
|
}
|
|
|
|
ULONG DbgPrint( char *Text, ... )
|
|
{
|
|
char Temp[1024];
|
|
va_list valist;
|
|
|
|
va_start(valist, Text);
|
|
wvsprintf(Temp,Text,valist);
|
|
OutputDebugString(Temp);
|
|
va_end(valist);
|
|
|
|
return 0;
|
|
|
|
}
|
|
#endif
|
|
|
|
int
|
|
NtsdPrompt (
|
|
char *Prompt,
|
|
char *Buffer,
|
|
int cb
|
|
)
|
|
{
|
|
#ifdef KERNEL
|
|
dprintf(Prompt);
|
|
if (streamCmd) {
|
|
if (!fgets(Buffer, cb, streamCmd))
|
|
streamCmd = NULL;
|
|
else {
|
|
dprintf("%s", Buffer);
|
|
return strlen(Buffer);
|
|
}
|
|
}
|
|
DbgKdGets(Buffer, (USHORT)cb);
|
|
lprintf(Buffer);
|
|
return strlen(Buffer);
|
|
#else
|
|
int s;
|
|
DWORD whocares;
|
|
|
|
if (fDebugOutput) {
|
|
if (streamCmd == stdin ) {
|
|
s = (int)DbgPrompt(Prompt, Buffer, cb);
|
|
} else {
|
|
if (!fgets(Buffer, cb, streamCmd)) {
|
|
streamCmd = stdin;
|
|
s = (int)DbgPrompt(Prompt, Buffer, cb);
|
|
} else {
|
|
DbgPrint("%s%s", Prompt, Buffer);
|
|
s = strlen(Buffer);
|
|
}
|
|
}
|
|
} else {
|
|
dprintf("%s", Prompt);
|
|
if ( streamCmd == stdin ) {
|
|
BOOL b;
|
|
b = ReadFile(
|
|
ConsoleInputHandle,
|
|
Buffer,
|
|
cb,
|
|
&whocares,
|
|
NULL
|
|
);
|
|
if (!b) {
|
|
ExitProcess(1);
|
|
}
|
|
Buffer[whocares-2] = '\0'; /* Remove CR LF */
|
|
}
|
|
else {
|
|
if (!fgets(Buffer, cb, streamCmd)) {
|
|
streamCmd=stdin;
|
|
ReadFile(
|
|
ConsoleInputHandle,
|
|
Buffer,
|
|
cb,
|
|
&whocares,
|
|
NULL
|
|
);
|
|
Buffer[whocares-2] = '\0'; /* Remove CR LF */
|
|
}
|
|
}
|
|
s = strlen(Buffer);
|
|
}
|
|
lprintf(Buffer);
|
|
lprintf("\n");
|
|
return s;
|
|
#endif
|
|
}
|
|
|
|
#ifndef KERNEL
|
|
extern ULONG segtable[];
|
|
#endif
|
|
|
|
ULONG
|
|
GetExpressionRoutine(char * CommandString)
|
|
{
|
|
ULONG ReturnValue;
|
|
PUCHAR pchTemp;
|
|
PUCHAR pchStartSave = pchStart;
|
|
jmp_buf savejmpbuf;
|
|
|
|
#ifndef KERNEL
|
|
if ( strcmp(CommandString,"WOW_BIG_BDE_HACK") == 0 ) {
|
|
return( (ULONG)(&segtable[0]) );
|
|
}
|
|
|
|
//
|
|
// this is because the kdexts MUST include the address-of operator
|
|
// on all getexpression calls for windbg/c expression evaluators
|
|
//
|
|
if (*CommandString=='&') {
|
|
CommandString++;
|
|
}
|
|
#endif
|
|
|
|
pchTemp = pchCommand;
|
|
pchStart = pchCommand = CommandString;
|
|
fDisableErrorPrint = TRUE;
|
|
memcpy( savejmpbuf, cmd_return, sizeof( cmd_return ) );
|
|
if (setjmp(cmd_return) == 0) {
|
|
ReturnValue = GetExpression();
|
|
} else {
|
|
ReturnValue = 0;
|
|
}
|
|
fDisableErrorPrint = FALSE;
|
|
pchCommand = pchTemp;
|
|
pchStart = pchStartSave;
|
|
memcpy( cmd_return, savejmpbuf, sizeof( cmd_return ) );
|
|
return ReturnValue;
|
|
}
|
|
|
|
void
|
|
GetSymbolRoutine (
|
|
LPVOID offset,
|
|
PUCHAR pchBuffer,
|
|
PULONG pDisplacement
|
|
)
|
|
{
|
|
GetSymbolStdCall((ULONG)offset, pchBuffer, pDisplacement, NULL);
|
|
}
|
|
|
|
#ifndef KERNEL
|
|
DWORD disasmExportRoutine(LPDWORD lpOffset, LPSTR lpBuffer, BOOL fShowEA)
|
|
{
|
|
return (DWORD)disasmRoutine((PULONG)lpOffset, (PUCHAR)lpBuffer,
|
|
(BOOLEAN)fShowEA);
|
|
}
|
|
#endif
|
|
|
|
ULONG disasmRoutine(PULONG lpOffset, PUCHAR lpBuffer, BOOLEAN fShowEA)
|
|
{
|
|
ADDR tempAddr;
|
|
BOOLEAN ret;
|
|
#if MULTIMODE
|
|
Type(tempAddr) = ADDR_32|FLAT_COMPUTED;
|
|
#endif
|
|
Off(tempAddr) = Flat(tempAddr) = *lpOffset;
|
|
ret = disasm(&tempAddr, (PUCHAR) lpBuffer, (BOOLEAN) fShowEA);
|
|
*lpOffset = Flat(tempAddr);
|
|
return ret;
|
|
}
|
|
|
|
DWORD CheckControlC (VOID)
|
|
{
|
|
if (fControlC) {
|
|
fControlC = 0;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifndef KERNEL
|
|
|
|
LONG
|
|
fnBangCommandExceptionFilter(
|
|
struct _EXCEPTION_POINTERS *ExceptionInfo,
|
|
LPSTR modname,
|
|
LPSTR pname
|
|
)
|
|
{
|
|
dprintf("%s: %08x Exception in %s.%s debugger extension.\n",
|
|
DebuggerName,
|
|
ExceptionInfo->ExceptionRecord->ExceptionCode,
|
|
modname,
|
|
pname
|
|
);
|
|
|
|
dprintf(" PC: %08x VA: %08x R/W: %x Parameter: %x\n",
|
|
ExceptionInfo->ExceptionRecord->ExceptionAddress,
|
|
ExceptionInfo->ExceptionRecord->ExceptionInformation[1],
|
|
ExceptionInfo->ExceptionRecord->ExceptionInformation[0],
|
|
ExceptionInfo->ExceptionRecord->ExceptionInformation[2]
|
|
);
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
VOID
|
|
fnBangCmd(
|
|
PUCHAR argstring,
|
|
PUCHAR *pNext
|
|
)
|
|
{
|
|
PUCHAR pc;
|
|
PUCHAR modname;
|
|
PUCHAR pname;
|
|
PNTSD_EXTENSION_ROUTINE ExtensionRoutine = NULL;
|
|
HANDLE hMod;
|
|
BOOLEAN LoadingDefault;
|
|
ADDR TempAddr;
|
|
UCHAR string[_MAX_PATH];
|
|
PUCHAR pc1;
|
|
|
|
LoadingDefault = FALSE;
|
|
|
|
if ( NtsdExtensions.nSize == 0 ) {
|
|
NtsdExtensions.nSize = sizeof(NtsdExtensions);
|
|
NtsdExtensions.lpOutputRoutine = dprintf;
|
|
NtsdExtensions.lpGetExpressionRoutine = GetExpressionRoutine;
|
|
NtsdExtensions.lpGetSymbolRoutine = GetSymbolRoutine;
|
|
NtsdExtensions.lpDisasmRoutine = disasmExportRoutine;
|
|
NtsdExtensions.lpCheckControlCRoutine = CheckControlC;
|
|
}
|
|
|
|
|
|
//
|
|
// copy the command into a local buffer. Consume until
|
|
// a ';' or '\0'. Leave the original string so that
|
|
// commands may consume the ';' as well if they want to.
|
|
//
|
|
|
|
//
|
|
// copy command until a ';' into local buffer
|
|
// leave argstring pointing to original command.
|
|
//
|
|
|
|
pc = string;
|
|
pc1 = argstring;
|
|
while (*pc1 && *pc1 != ';') {
|
|
*pc++ = *pc1++;
|
|
}
|
|
*pc = '\0';
|
|
|
|
//
|
|
// point to next command:
|
|
//
|
|
if (pNext) {
|
|
*pNext = pc1;
|
|
}
|
|
|
|
//
|
|
// syntax is module.function argument-string
|
|
//
|
|
|
|
pc = string;
|
|
|
|
while ((*pc == ' ' ) || (*pc == '\t')) {
|
|
pc++;
|
|
}
|
|
modname = pc;
|
|
pname = NULL;
|
|
|
|
while ((*pc != '.') && (*pc != ' ') && (*pc != '\t') && (*pc != '\0')) {
|
|
pc++;
|
|
}
|
|
if ( *pc == '.' ) {
|
|
if (*pc != '\0') {
|
|
*pc = '\0';
|
|
pc++;
|
|
}
|
|
} else {
|
|
if (*pc != '\0') {
|
|
*pc = '\0';
|
|
pc++;
|
|
}
|
|
pname = modname;
|
|
modname = "ntsdexts";
|
|
LoadingDefault = TRUE;
|
|
}
|
|
|
|
if ( !pname ) {
|
|
pname = pc;
|
|
|
|
while ( (*pc != ' ') && (*pc != '\t') && (*pc != '\0')) {
|
|
pc++;
|
|
}
|
|
|
|
|
|
if (*pc != '\0') {
|
|
*pc = '\0';
|
|
pc++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// modname -> Name of module
|
|
// pname -> Name of command to process
|
|
// pc -> argument to command
|
|
//
|
|
|
|
//
|
|
// Do a load library of modname and a getprocaddress of pname
|
|
//
|
|
if ( LoadingDefault ) {
|
|
if ( !hNtsdDefaultLibrary ) {
|
|
hNtsdDefaultLibrary = LoadLibrary(modname);
|
|
}
|
|
if (DefaultExtDllName) {
|
|
if ( !hNtsdUserDefaultLibrary ) {
|
|
hNtsdUserDefaultLibrary = LoadLibrary(DefaultExtDllName);
|
|
}
|
|
modname = DefaultExtDllName;
|
|
hMod = hNtsdUserDefaultLibrary;
|
|
} else
|
|
hMod = hNtsdDefaultLibrary;
|
|
} else {
|
|
hMod = LoadLibrary(modname);
|
|
}
|
|
if ( !hMod ) {
|
|
dprintf("LoadLibrary(\"%s\") failed\n", modname );
|
|
|
|
} else {
|
|
|
|
ExtensionRoutine = (PNTSD_EXTENSION_ROUTINE)GetProcAddress(hMod,pname);
|
|
if ( !ExtensionRoutine ) {
|
|
if (DefaultExtDllName) {
|
|
ExtensionRoutine =
|
|
(PNTSD_EXTENSION_ROUTINE)GetProcAddress(hNtsdDefaultLibrary,
|
|
pname);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !ExtensionRoutine ) {
|
|
if (!_stricmp( pname, "unload" )) {
|
|
FreeLibrary(hMod);
|
|
if ( LoadingDefault ) {
|
|
if (hMod == hNtsdDefaultLibrary) {
|
|
hNtsdDefaultLibrary = NULL;
|
|
} else {
|
|
hNtsdUserDefaultLibrary = NULL;
|
|
}
|
|
}
|
|
} else if (!_stricmp( pname, "setdll" )) {
|
|
dprintf("Setting default dll extension to '%s'\n",pc);
|
|
SetDefaultExtDllName(pc);
|
|
FreeLibrary(hMod);
|
|
if ( LoadingDefault ) {
|
|
if (hMod == hNtsdDefaultLibrary) {
|
|
hNtsdDefaultLibrary = NULL;
|
|
} else {
|
|
hNtsdUserDefaultLibrary = NULL;
|
|
}
|
|
}
|
|
} else {
|
|
dprintf("GetProcAddress(\"%s\",\"%s\") failed\n",modname,pname);
|
|
if ( !LoadingDefault ) {
|
|
FreeLibrary(hMod);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
GetRegPCValue(&TempAddr);
|
|
__try {
|
|
(ExtensionRoutine)(
|
|
pProcessCurrent->hProcess,
|
|
pProcessCurrent->pThreadCurrent->hThread,
|
|
Flat(TempAddr),
|
|
&NtsdExtensions,
|
|
pc
|
|
);
|
|
}
|
|
__except (fnBangCommandExceptionFilter(GetExceptionInformation(),modname,pname)) {
|
|
}
|
|
|
|
#ifdef BP_CORRUPTION
|
|
ValidateBreakpointTable(__FILE__, __LINE__);
|
|
#endif //BP_CORRUPTION
|
|
|
|
if ( !LoadingDefault ) {
|
|
FreeLibrary(hMod);
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
ExpandUserRegs (PUCHAR sz)
|
|
{
|
|
PUCHAR szSearch = "$ux", szReg, szRegValue;
|
|
CHAR cs, cch, tempBuffer[_MAX_PATH];
|
|
|
|
while (TRUE) {
|
|
ULONG index = 0L;
|
|
ULONG pointer = 0L;
|
|
|
|
// scan the line for $ux (where x is a digit from 0 to 9)
|
|
|
|
for (szReg = sz; (cs = szSearch[index]) && (cch = sz[pointer]);
|
|
pointer++)
|
|
if ((cs == 'x' && isdigit(cch))
|
|
|| (cs != 'x' && cs == (CHAR)tolower(cch)))
|
|
index++;
|
|
else {
|
|
szReg = sz + pointer + 1;
|
|
index = 0L;
|
|
}
|
|
if (szSearch[index])
|
|
return;
|
|
|
|
// copy everything past the user reg into a temporary buffer
|
|
|
|
strcpy(tempBuffer, sz + pointer);
|
|
szReg[0] = '$';
|
|
szReg[1] = 'u';
|
|
szReg[3] = 0;
|
|
|
|
// get the value of the user register and copy it over the user reg
|
|
|
|
index = GetRegString(szReg);
|
|
szRegValue = (PUCHAR)GetRegFlagValue(index);
|
|
if (szRegValue)
|
|
strcpy(szReg, szRegValue);
|
|
else
|
|
*szReg = 0;
|
|
|
|
// now concatenate the rest of the original string
|
|
|
|
strcat(sz, tempBuffer);
|
|
}
|
|
}
|
|
|
|
NTSTATUS WriteBreakPoint(
|
|
ADDR addr,
|
|
PULONG phBKPT
|
|
) {
|
|
#ifdef ADDR_BKPTS
|
|
return( AddrWriteBreakPoint( addr, phBKPT ) );
|
|
#else
|
|
return( DbgKdWriteBreakPoint( (PVOID)Flat(addr), phBKPT ) );
|
|
#endif
|
|
}
|
|
|
|
NTSTATUS RestoreBreakPoint(
|
|
ULONG hBKPT
|
|
) {
|
|
#ifdef ADDR_BKPTS
|
|
return( AddrRestoreBreakPoint( hBKPT ) );
|
|
#else
|
|
return( DbgKdRestoreBreakPoint( hBKPT ) );
|
|
#endif
|
|
}
|
|
|
|
static ULONG igrepSearchStartAddress = 0L;
|
|
static ULONG igrepLastPc;
|
|
static CHAR igrepLastPattern[256];
|
|
|
|
static void igrep (void)
|
|
{
|
|
ULONG dwNextGrepAddr;
|
|
ULONG dwCurrGrepAddr;
|
|
CHAR SourceLine[2 * SYMBOLSIZE];
|
|
BOOLEAN NewPc;
|
|
ULONG d;
|
|
PUCHAR pc = pchCommand;
|
|
PUCHAR Pattern;
|
|
PUCHAR Expression;
|
|
CHAR Symbol[SYMBOLSIZE];
|
|
ULONG Displacement;
|
|
ADDR TempAddr;
|
|
ULONG dwCurrentPc;
|
|
|
|
GetRegPCValue(&TempAddr);
|
|
dwCurrentPc = Flat(TempAddr);
|
|
if ( igrepLastPc && igrepLastPc == dwCurrentPc ) {
|
|
NewPc = FALSE;
|
|
}
|
|
else {
|
|
igrepLastPc = dwCurrentPc;
|
|
NewPc = TRUE;
|
|
}
|
|
|
|
//
|
|
// check for pattern.
|
|
//
|
|
|
|
Pattern = NULL;
|
|
Expression = NULL;
|
|
if (*pc) {
|
|
Pattern = pc;
|
|
while (*pc > ' ')
|
|
pc++;
|
|
|
|
//
|
|
// check for an expression
|
|
//
|
|
|
|
if (*pc != '\0') {
|
|
*pc = '\0';
|
|
pc++;
|
|
if (*pc <= ' ') {
|
|
while (*pc <= ' ')
|
|
pc++;
|
|
}
|
|
if (*pc)
|
|
Expression = pc;
|
|
}
|
|
}
|
|
|
|
|
|
if (Pattern) {
|
|
for (pc = Pattern; *pc; pc++) *pc = (UCHAR)toupper(*pc);
|
|
igrepLastPattern[0] = '*';
|
|
strcpy(igrepLastPattern+1,Pattern);
|
|
if (Pattern[0] == '*') {
|
|
strcpy(igrepLastPattern,Pattern);
|
|
}
|
|
if (Pattern[strlen(Pattern)] != '*') {
|
|
strcat(igrepLastPattern, "*");
|
|
}
|
|
}
|
|
|
|
if (Expression) {
|
|
igrepSearchStartAddress = GetExpressionRoutine(Expression);
|
|
}
|
|
if (!igrepSearchStartAddress) {
|
|
igrepSearchStartAddress = igrepLastPc;
|
|
return;
|
|
}
|
|
dwNextGrepAddr = igrepSearchStartAddress;
|
|
dwCurrGrepAddr = dwNextGrepAddr;
|
|
|
|
d = disasmRoutine(&dwNextGrepAddr, SourceLine, FALSE);
|
|
while (d) {
|
|
for (pc = SourceLine; *pc; pc++)
|
|
*pc = (UCHAR)tolower(*pc);
|
|
if (MatchPattern(SourceLine, igrepLastPattern)) {
|
|
EXPRLastExpression = dwCurrGrepAddr;
|
|
igrepSearchStartAddress = dwNextGrepAddr;
|
|
GetSymbolRoutine((LPVOID)dwCurrGrepAddr, Symbol, &Displacement);
|
|
disasmRoutine(&dwCurrGrepAddr, SourceLine, FALSE);
|
|
dprintf("%s", SourceLine);
|
|
return;
|
|
}
|
|
|
|
if (CheckControlC()) {
|
|
return;
|
|
}
|
|
|
|
dwCurrGrepAddr = dwNextGrepAddr;
|
|
d = disasmRoutine(&dwNextGrepAddr, SourceLine, FALSE);
|
|
}
|
|
}
|
|
|
|
void dprintAddr(PADDR paddr)
|
|
{
|
|
#if MULTIMODE
|
|
switch (paddr->type & (~(FLAT_COMPUTED | INSTR_POINTER))) {
|
|
case ADDR_V86:
|
|
case ADDR_16:
|
|
dprintf("%04x:%04x ", paddr->seg, (USHORT)paddr->off);
|
|
break;
|
|
case ADDR_32:
|
|
dprintf("%08lx ", paddr->off);
|
|
break;
|
|
case ADDR_1632:
|
|
dprintf("%04x:%08lx ", paddr->seg, paddr->off);
|
|
break;
|
|
}
|
|
#else
|
|
dprintf("%08lx ", Flat(*paddr));
|
|
#endif
|
|
}
|
|
|
|
void sprintAddr(PUCHAR *buffer, PADDR paddr)
|
|
{
|
|
#if MULTIMODE
|
|
switch (paddr->type & (~(FLAT_COMPUTED | INSTR_POINTER))) {
|
|
case ADDR_V86:
|
|
case ADDR_16:
|
|
sprintf(*buffer,"%04x:%04x ", paddr->seg, (USHORT)paddr->off);
|
|
break;
|
|
case ADDR_32:
|
|
sprintf(*buffer,"%08lx ", paddr->off);
|
|
break;
|
|
case ADDR_1632:
|
|
sprintf(*buffer,"%04x:%08lx ", paddr->seg, paddr->off);
|
|
break;
|
|
}
|
|
#else
|
|
sprintf(*buffer,"%08lx ", *paddr);
|
|
#endif
|
|
while (**buffer)
|
|
(*buffer)++;
|
|
}
|
|
|
|
|
|
void FormAddress (PADDR paddr, ULONG seg, ULONG off)
|
|
{
|
|
paddr->seg = (USHORT)seg;
|
|
paddr->off = off;
|
|
if (fVm86) {
|
|
paddr->type = ADDR_V86;
|
|
ComputeFlatAddress(paddr, NULL);
|
|
}
|
|
else {
|
|
|
|
if (seg) {
|
|
#ifdef i386
|
|
DESCRIPTOR_TABLE_ENTRY desc;
|
|
|
|
desc.Selector = seg;
|
|
DbgKdLookupSelector(DefaultProcessor, &desc);
|
|
paddr->type = (UCHAR)desc.Descriptor.HighWord.Bits.Default_Big
|
|
? ADDR_1632 : ADDR_16;
|
|
ComputeFlatAddress(paddr, &desc);
|
|
#else
|
|
paddr->type = ADDR_16;
|
|
ComputeFlatAddress(paddr, NULL);
|
|
#endif
|
|
}
|
|
else
|
|
paddr->type = ADDR_32;
|
|
ComputeFlatAddress(paddr, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef MULTIMODE
|
|
void ComputeNativeAddress (PADDR paddr)
|
|
{
|
|
switch (paddr->type & (~(FLAT_COMPUTED | INSTR_POINTER))) {
|
|
#ifdef i386
|
|
case ADDR_V86:
|
|
paddr->off = Flat(*paddr) - ((ULONG)paddr->seg << 4);
|
|
if (paddr->off > 0xffff) {
|
|
ULONG excess = 1 + (paddr->off - 0xffffL) >> 4;
|
|
paddr->seg += (USHORT)excess;
|
|
paddr->off -= excess << 4;
|
|
}
|
|
break;
|
|
|
|
case ADDR_16:
|
|
case ADDR_1632: {
|
|
DESCRIPTOR_TABLE_ENTRY desc;
|
|
|
|
if (paddr->seg != (USHORT)lastSelector) {
|
|
lastSelector = paddr->seg;
|
|
desc.Selector = (ULONG)paddr->seg;
|
|
DbgKdLookupSelector(DefaultProcessor, &desc);
|
|
lastBaseOffset =
|
|
((ULONG)desc.Descriptor.HighWord.Bytes.BaseHi << 24) |
|
|
((ULONG)desc.Descriptor.HighWord.Bytes.BaseMid << 16) |
|
|
(ULONG)desc.Descriptor.BaseLow;
|
|
}
|
|
paddr->off = Flat(*paddr) - lastBaseOffset;
|
|
}
|
|
break;
|
|
#endif
|
|
case ADDR_32:
|
|
paddr->off = Flat(*paddr);
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
void ComputeFlatAddress (PADDR paddr, PDESCRIPTOR_TABLE_ENTRY pdesc)
|
|
{
|
|
if (paddr->type&FLAT_COMPUTED)
|
|
return;
|
|
|
|
switch (paddr->type & (~INSTR_POINTER)) {
|
|
#ifdef i386
|
|
case ADDR_V86:
|
|
paddr->off &= 0xffff;
|
|
Flat(*paddr) = ((ULONG)paddr->seg << 4) + paddr->off;
|
|
break;
|
|
|
|
case ADDR_16:
|
|
paddr->off &= 0xffff;
|
|
|
|
case ADDR_1632: {
|
|
DESCRIPTOR_TABLE_ENTRY desc;
|
|
|
|
if (paddr->seg!=(USHORT)lastSelector) {
|
|
lastSelector = paddr->seg;
|
|
desc.Selector = (ULONG)paddr->seg;
|
|
if (!pdesc)
|
|
DbgKdLookupSelector(DefaultProcessor, pdesc = &desc);
|
|
lastBaseOffset =
|
|
((ULONG)pdesc->Descriptor.HighWord.Bytes.BaseHi << 24) |
|
|
((ULONG)pdesc->Descriptor.HighWord.Bytes.BaseMid << 16) |
|
|
(ULONG)pdesc->Descriptor.BaseLow;
|
|
}
|
|
Flat(*paddr) = paddr->off + lastBaseOffset;
|
|
}
|
|
break;
|
|
#endif
|
|
case ADDR_32:
|
|
Flat(*paddr) = paddr->off;
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
paddr->type |= FLAT_COMPUTED;
|
|
}
|
|
|
|
PADDR AddrAdd(PADDR paddr, ULONG scalar)
|
|
{
|
|
// assert(fFlat(paddr));
|
|
if (fnotFlat(*paddr))
|
|
ComputeFlatAddress(paddr, NULL);
|
|
Flat(*paddr) += scalar;
|
|
paddr->off += scalar;
|
|
return paddr;
|
|
}
|
|
|
|
PADDR AddrSub(PADDR paddr, ULONG scalar)
|
|
{
|
|
// assert(fFlat(paddr));
|
|
if (fnotFlat(*paddr))
|
|
ComputeFlatAddress(paddr, NULL);
|
|
Flat(*paddr) -= scalar;
|
|
paddr->off -= scalar;
|
|
return paddr;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef KERNEL
|
|
|
|
NTSTATUS GetClientId()
|
|
{
|
|
#ifndef CHICAGO
|
|
PTEB Teb;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING LinkRecord;
|
|
STRING Os2RootDirectoryName;
|
|
HANDLE Os2RootDirectory;
|
|
STRING DirectoryName;
|
|
UNICODE_STRING DirectoryName_U;
|
|
HANDLE DirectoryHandle;
|
|
CHAR localSecurityDescriptor[SECURITY_DESCRIPTOR_MIN_LENGTH];
|
|
PSECURITY_DESCRIPTOR securityDescriptor;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
RtlInitAnsiString( &Os2RootDirectoryName, "\\OS2SS");
|
|
|
|
Status = RtlAnsiStringToUnicodeString(&DirectoryName_U,
|
|
&Os2RootDirectoryName,
|
|
TRUE);
|
|
assert (NT_SUCCESS(Status));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(Status);
|
|
}
|
|
|
|
Status = RtlCreateSecurityDescriptor((PSECURITY_DESCRIPTOR)
|
|
&localSecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION);
|
|
assert( NT_SUCCESS( Status ) );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = RtlSetDaclSecurityDescriptor((PSECURITY_DESCRIPTOR)
|
|
&localSecurityDescriptor,
|
|
TRUE,
|
|
(PACL) NULL,
|
|
FALSE);
|
|
|
|
assert( NT_SUCCESS( Status ) );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
securityDescriptor = (PSECURITY_DESCRIPTOR) &localSecurityDescriptor;
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DirectoryName_U,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
securityDescriptor);
|
|
|
|
Status = NtOpenDirectoryObject(&Os2RootDirectory,
|
|
DIRECTORY_ALL_ACCESS,
|
|
&ObjectAttributes);
|
|
RtlFreeUnicodeString (&DirectoryName_U);
|
|
|
|
assert( NT_SUCCESS( Status ) );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
RtlInitAnsiString( &DirectoryName, "DebugClientId" );
|
|
Status = RtlAnsiStringToUnicodeString( &DirectoryName_U,
|
|
&DirectoryName,
|
|
TRUE);
|
|
assert (NT_SUCCESS(Status));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&DirectoryName_U,
|
|
OBJ_CASE_INSENSITIVE,
|
|
Os2RootDirectory,
|
|
securityDescriptor
|
|
);
|
|
Teb = NtCurrentTeb();
|
|
|
|
LinkRecord.Length = sizeof( Teb->ClientId );
|
|
LinkRecord.MaximumLength = LinkRecord.Length;
|
|
LinkRecord.Buffer = (PWSTR)&Teb->ClientId;
|
|
Status = NtCreateSymbolicLinkObject( &DirectoryHandle,
|
|
SYMBOLIC_LINK_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
&LinkRecord
|
|
);
|
|
RtlFreeUnicodeString (&DirectoryName_U);
|
|
|
|
assert( NT_SUCCESS( Status ) );
|
|
|
|
return Status;
|
|
#else
|
|
return STATUS_UNSUCCESSFUL;
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef KERNEL
|
|
|
|
typedef struct _TRACE_DATA_SYM {
|
|
ULONG SymMin;
|
|
ULONG SymMax;
|
|
} TRACE_DATA_SYM, *PTRACE_DATA_SYM;
|
|
|
|
TRACE_DATA_SYM TraceDataSyms[2 * SYMBOLSIZE];
|
|
UCHAR NextTraceDataSym = 0; // what's the next one to be replaced
|
|
UCHAR NumTraceDataSyms = 0; // how many are valid?
|
|
|
|
LONG
|
|
SymNumFor (
|
|
ULONG Pc
|
|
)
|
|
{
|
|
long index;
|
|
|
|
for ( index = 0; index < NumTraceDataSyms; index++ ) {
|
|
if ( (TraceDataSyms[index].SymMin <= Pc) &&
|
|
(TraceDataSyms[index].SymMax > Pc) ) {
|
|
return index;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
VOID
|
|
ClearTraceDataSyms (
|
|
VOID
|
|
)
|
|
{
|
|
NextTraceDataSym = 0;
|
|
NumTraceDataSyms = 0;
|
|
}
|
|
|
|
VOID
|
|
PotentialNewSymbol (
|
|
ULONG Pc
|
|
)
|
|
{
|
|
if ( -1 != SymNumFor(Pc) ) {
|
|
return; // we've already seen this one
|
|
}
|
|
|
|
TraceDataSyms[NextTraceDataSym].SymMin = BeginCurFunc;
|
|
TraceDataSyms[NextTraceDataSym].SymMax = EndCurFunc;
|
|
|
|
//
|
|
// Bump the "next" pointer, wrapping if necessary. Also bump the
|
|
// "valid" pointer if we need to.
|
|
//
|
|
|
|
NextTraceDataSym = (NextTraceDataSym + 1) % 256;
|
|
if ( NumTraceDataSyms < NextTraceDataSym ) {
|
|
NumTraceDataSyms = NextTraceDataSym;
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef KERNEL
|
|
VOID
|
|
ProcessWatchTraceEvent(
|
|
PDBGKD_TRACE_DATA TraceData,
|
|
ADDR PcAddr
|
|
)
|
|
|
|
{
|
|
//
|
|
// All of the real information is captured in the TraceData unions
|
|
// sent to us by the kernel. Here we have two main jobs:
|
|
//
|
|
// 1) Print out the data in the TraceData record.
|
|
// 2) See if we need up update the SymNum table before
|
|
// returning to the kernel.
|
|
//
|
|
|
|
WATCH_SYM Sym;
|
|
ULONG index;
|
|
|
|
if ( AddrEqu(WatchTarget,PcAddr) && (CURRENT_STACK >= InitialSP) ) {
|
|
|
|
//
|
|
// HACK HACK HACK
|
|
//
|
|
// fix up the last trace entry.
|
|
//
|
|
|
|
ULONG lastEntry = TraceData[0].LongNumber;
|
|
if (lastEntry != 0) {
|
|
TraceData[lastEntry].s.LevelChange = -1;
|
|
TraceData[lastEntry].s.SymbolNumber = 0; // this is wrong if we
|
|
// filled the symbol table!
|
|
}
|
|
}
|
|
|
|
for ( index = 1; index < TraceData[0].LongNumber; index++ ) {
|
|
PWATCH_SYM pSym;
|
|
int instructions_popped = 0;
|
|
/* Fake up sym for printing */
|
|
|
|
GetSymbolStdCall(TraceDataSyms[TraceData[index].s.SymbolNumber].SymMin,
|
|
&Sym.Symbol[0],&Sym.Level, NULL);
|
|
|
|
WatchLevel += TraceData[index].s.LevelChange;
|
|
if (WatchLevel < 0) {
|
|
WatchLevel = 0; // in case of screwups
|
|
}
|
|
|
|
if (IsListEmpty(&WatchList) || (TraceData[index].s.LevelChange > 0)) {
|
|
if (IsListEmpty(&WatchList)) {
|
|
TraceData[index].s.LevelChange = 1; // hack
|
|
}
|
|
while (TraceData[index].s.LevelChange != 0) {
|
|
pSym = calloc(1,sizeof(*pSym));
|
|
pSym->SubordinateInstrCount = 0;
|
|
InsertTailList(&WatchList,&pSym->Links);
|
|
TraceData[index].s.LevelChange--;
|
|
}
|
|
} else if (TraceData[index].s.LevelChange < 0) {
|
|
while (!IsListEmpty(&WatchList) && TraceData[index].s.LevelChange != 0) {
|
|
pSym = (PWATCH_SYM)WatchList.Blink;
|
|
instructions_popped += pSym->SubordinateInstrCount;
|
|
RemoveEntryList(&(pSym->Links));
|
|
free(pSym);
|
|
TraceData[index].s.LevelChange++;
|
|
}
|
|
pSym = (PWATCH_SYM)WatchList.Blink;
|
|
} else {
|
|
// We just made a horizontal call.
|
|
pSym = (PWATCH_SYM)WatchList.Blink;
|
|
}
|
|
|
|
Sym.Level = WatchLevel;
|
|
if (TraceData[index].s.Instructions == TRACE_DATA_INSTRUCTIONS_BIG) {
|
|
WatchCount += Sym.InstrCount = TraceData[index+1].LongNumber;
|
|
index++;
|
|
} else {
|
|
WatchCount += Sym.InstrCount = TraceData[index].s.Instructions;
|
|
}
|
|
|
|
if (pSym) {
|
|
Sym.SubordinateInstrCount =
|
|
pSym->SubordinateInstrCount +=
|
|
instructions_popped + Sym.InstrCount;
|
|
} else {
|
|
Sym.SubordinateInstrCount = 0;
|
|
}
|
|
|
|
PrintWatchSym(&Sym);
|
|
|
|
}
|
|
|
|
//
|
|
// now see if we need to add a new symbol
|
|
//
|
|
|
|
if (-1 == SymNumFor(Flat(PcAddr))) {
|
|
/* yup, add the symbol */
|
|
|
|
GetAdjacentSymOffsets(Flat(PcAddr),&BeginCurFunc,&EndCurFunc);
|
|
|
|
if ((BeginCurFunc == 0) || (EndCurFunc == 0xffffffff)) {
|
|
/* Couldn't determine function; single step */
|
|
BeginCurFunc = EndCurFunc = 0;
|
|
} else if ((BeginCurFunc <= Flat(WatchTarget)) &&
|
|
(Flat(WatchTarget) < EndCurFunc)) {
|
|
/* The "exit" address is in the symbol range;
|
|
* fix it so this isn't the case.
|
|
*/
|
|
if (Flat(PcAddr) < Flat(WatchTarget)) {
|
|
EndCurFunc = Flat(WatchTarget);
|
|
} else {
|
|
BeginCurFunc = Flat(WatchTarget) + 1;
|
|
}
|
|
}
|
|
|
|
PotentialNewSymbol(Flat(PcAddr));
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
void
|
|
KdDumpVersion( void )
|
|
{
|
|
ULONG Result;
|
|
ADDR Addr;
|
|
ULONG CmNtCSDVersion;
|
|
|
|
ADDR32( &Addr, LookupSymbolInDll("CmNtCSDVersion", "NT") );
|
|
if ( !Addr.off || !GetMemDword( &Addr, &CmNtCSDVersion ) ) {
|
|
CmNtCSDVersion = 0;
|
|
}
|
|
|
|
dprintf( "Kernel Version %d", vs.MinorVersion );
|
|
if (CmNtCSDVersion != 0) {
|
|
dprintf( ": " );
|
|
if (CmNtCSDVersion & 0xFFFF) {
|
|
dprintf( " Service Pack %u%c",
|
|
(CmNtCSDVersion & 0xFF00) >> 8,
|
|
(CmNtCSDVersion & 0xFF) ? 'A' + (CmNtCSDVersion & 0xFF) - 1 : '\0'
|
|
);
|
|
}
|
|
|
|
if (CmNtCSDVersion & 0xFFFF0000) {
|
|
if (CmNtCSDVersion & 0xFFFF) {
|
|
dprintf( ", " );
|
|
}
|
|
dprintf( "RC %u", (CmNtCSDVersion >> 24) & 0xFF );
|
|
if (CmNtCSDVersion & 0x00FF0000) {
|
|
dprintf( ".%u", (CmNtCSDVersion >> 16) & 0xFF );
|
|
}
|
|
}
|
|
}
|
|
|
|
dprintf( " %s %s\nKernel base = 0x%08x PsLoadedModuleList = 0x%08x\n",
|
|
(vs.Flags & DBGKD_VERS_FLAG_MP)? "MP" : "UP",
|
|
vs.MajorVersion == 0xC ? "Checked" : "Free",
|
|
(DWORD)vs.KernBase,
|
|
(DWORD)vs.PsLoadedModuleList
|
|
);
|
|
}
|
|
|
|
#else // KERNEL
|
|
VOID
|
|
ProcessWatchTraceEvent(
|
|
ADDR PcAddr
|
|
)
|
|
{
|
|
WATCH_SYM Sym;
|
|
PWATCH_SYM pSym;
|
|
|
|
//
|
|
// get current function and see if it matches current. If so, bump
|
|
// count in current, otherwise, update to new level
|
|
//
|
|
|
|
WatchTRCalls++;
|
|
GetSymbolStdCall(Flat(PcAddr),&Sym.Symbol[0],&Sym.Level, NULL);
|
|
if (!CurrentWatchSym ) {
|
|
|
|
/*
|
|
// first symbol in the list
|
|
*/
|
|
|
|
pSym = calloc(1,sizeof(*pSym));
|
|
*pSym = Sym;
|
|
pSym->InstrCount = 1;
|
|
pSym->Level = WatchLevel;
|
|
pSym->fQueued = FALSE;
|
|
CurrentWatchSym = pSym;
|
|
|
|
} else {
|
|
|
|
CHAR buffer[164];
|
|
|
|
disasm(&PcAddr, buffer, FALSE);
|
|
|
|
if(fDeferredDecrement)
|
|
{
|
|
PWATCH_SYM pw;
|
|
|
|
// We have to see if this is really returning to a call site.
|
|
// We do this because of try-finally funnies
|
|
|
|
for(pw = (PWATCH_SYM)WatchList.Blink;
|
|
pw != (PWATCH_SYM)&WatchList;
|
|
pw = (PWATCH_SYM)pw->Links.Blink)
|
|
{
|
|
if((Flat(PcAddr) - Flat(pw->PCPointer)) < MAXPCOFFSET)
|
|
{
|
|
PWATCH_SYM pw1;
|
|
|
|
if(--WatchLevel != pw->Level)
|
|
{
|
|
dprintf(">>>>More than one level popped %d %d\n",
|
|
WatchLevel, pw->Level);
|
|
WatchLevel = pw->Level;
|
|
}
|
|
if(pw != CurrentWatchSym)
|
|
{
|
|
RemoveEntryList(&pw->Links); // remove it
|
|
free(pw);
|
|
}
|
|
|
|
// Now prune the list ...
|
|
|
|
for(pw1 = (PWATCH_SYM)WatchList.Blink;
|
|
(pw1 != (PWATCH_SYM)&WatchList) && (pw1->Level >= WatchLevel);
|
|
)
|
|
{
|
|
PWATCH_SYM pw2 = pw1;
|
|
|
|
pw1 = (PWATCH_SYM)pw1->Links.Blink;
|
|
if(pw2 != CurrentWatchSym)
|
|
{
|
|
RemoveEntryList(&pw2->Links)
|
|
free(pw2);
|
|
}
|
|
}
|
|
if ( WatchLevel < 0 ) {
|
|
WatchLevel = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(pw == (PWATCH_SYM)&WatchList)
|
|
{
|
|
dprintf(">>No match on ret %s\n", buffer);
|
|
}
|
|
fDeferredDecrement = FALSE;
|
|
}
|
|
if ( !_stricmp(Sym.Symbol,CurrentWatchSym->Symbol) ||
|
|
(*Sym.Symbol == 0)) {
|
|
CurrentWatchSym->InstrCount++;
|
|
} else {
|
|
PrintWatchSym(CurrentWatchSym);
|
|
if(!CurrentWatchSym->fQueued)
|
|
{
|
|
free(CurrentWatchSym);
|
|
}
|
|
pSym = calloc(1,sizeof(*pSym));
|
|
*pSym = Sym;
|
|
pSym->InstrCount = 1;
|
|
pSym->Level = WatchLevel;
|
|
pSym->fQueued = FALSE;
|
|
CurrentWatchSym = pSym;
|
|
}
|
|
|
|
#ifndef KERNEL
|
|
//
|
|
// Adjust watch level to compensate for kernel-mode callbacks
|
|
//
|
|
if (CurrentWatchSym->InstrCount == 1) {
|
|
if (!_stricmp(CurrentWatchSym->Symbol, "ntdll!_KiUserCallBackDispatcher")) {
|
|
WatchLevel++;
|
|
CurrentWatchSym->Level = WatchLevel;
|
|
} else if (!strcmp(CurrentWatchSym->Symbol, "ntdll!_ZwCallbackReturn")) {
|
|
WatchLevel -= 2;
|
|
CurrentWatchSym->Level = WatchLevel;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (INCREMENT_LEVEL(buffer)) {
|
|
// check if already queued. If so, this is a "local" procedure
|
|
// call, such as for an inline that is not inlined.
|
|
|
|
if(CurrentWatchSym->fQueued)
|
|
{
|
|
CurrentWatchSym->InstrCount--;
|
|
PrintWatchSym(CurrentWatchSym);
|
|
pSym = calloc(1,sizeof(*pSym));
|
|
*pSym = Sym;
|
|
pSym->InstrCount = 1;
|
|
pSym->Level = WatchLevel;
|
|
pSym->fQueued = FALSE;
|
|
CurrentWatchSym = pSym;
|
|
}
|
|
CurrentWatchSym->PCPointer = PcAddr;
|
|
InsertTailList(&WatchList, &CurrentWatchSym->Links);
|
|
WatchLevel++;
|
|
CurrentWatchSym->fQueued = TRUE;
|
|
} else if ( DECREMENT_LEVEL(buffer) ) {
|
|
fDeferredDecrement = TRUE;
|
|
} else if ( SYSTEM_CALL(buffer) ) {
|
|
PCHAR ptr;
|
|
ULONG i;
|
|
|
|
ptr = strchr(CurrentWatchSym->Symbol, '!');
|
|
if(!ptr)
|
|
{
|
|
ptr = CurrentWatchSym->Symbol;
|
|
}
|
|
for(i = 0; i < NtCalls; i++)
|
|
{
|
|
if(!strcmp(ptr, NtCallTable[i].Name))
|
|
{
|
|
NtCallTable[i].Count++;
|
|
break;
|
|
}
|
|
}
|
|
if((i >= NtCalls) && (NtCalls < MAXNTCALLS))
|
|
{
|
|
strcpy(NtCallTable[NtCalls].Name, ptr);
|
|
NtCallTable[NtCalls++].Count = 1;
|
|
}
|
|
KernelCalls++;
|
|
WatchLevel--;
|
|
if ( WatchLevel < 0 ) {
|
|
WatchLevel = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif // KERNEL
|
|
|
|
void
|
|
PrintVersionInformation(
|
|
void
|
|
)
|
|
{
|
|
extern HANDLE hDefaultLibrary;
|
|
CHAR buf[MAX_PATH];
|
|
DWORD tstamp;
|
|
LPSTR p;
|
|
LPAPI_VERSION lpav;
|
|
#ifdef KERNEL
|
|
PWINDBG_EXTENSION_API_VERSION ExtensionApiVersion;
|
|
|
|
KdDumpVersion();
|
|
#else
|
|
OSVERSIONINFO OsVerInfo;
|
|
|
|
OsVerInfo.dwOSVersionInfoSize = sizeof( OsVerInfo );
|
|
if (GetVersionEx( &OsVerInfo )) {
|
|
dprintf( "Windows NT %u.%u Build %u",
|
|
OsVerInfo.dwMajorVersion,
|
|
OsVerInfo.dwMinorVersion,
|
|
OsVerInfo.dwBuildNumber
|
|
);
|
|
if (OsVerInfo.szCSDVersion[0]) {
|
|
dprintf( ": %s", OsVerInfo.szCSDVersion );
|
|
}
|
|
dprintf( "\n" );
|
|
}
|
|
#endif
|
|
|
|
GetModuleFileName( NULL, buf, sizeof(buf) );
|
|
_strlwr( buf );
|
|
tstamp = GetTimestampForLoadedLibrary( GetModuleHandle( NULL ) );
|
|
p = ctime( &tstamp );
|
|
p[strlen(p)-1] = 0;
|
|
dprintf(
|
|
"debugger version: %d.%d.%d, built: %s [name: %s]\n",
|
|
ApiVersion.MajorVersion,
|
|
ApiVersion.MinorVersion,
|
|
ApiVersion.Revision,
|
|
p,
|
|
buf );
|
|
GetModuleFileName( GetModuleHandle("imagehlp.dll"), buf, sizeof(buf) );
|
|
tstamp = GetTimestampForLoadedLibrary( GetModuleHandle( "imagehlp.dll" ) );
|
|
p = ctime( &tstamp );
|
|
p[strlen(p)-1] = 0;
|
|
_strlwr( buf );
|
|
dprintf(
|
|
"imagehlp version: %d.%d.%d, built: %s [name: %s]\n",
|
|
ImagehlpAV.MajorVersion,
|
|
ImagehlpAV.MinorVersion,
|
|
ImagehlpAV.Revision,
|
|
p,
|
|
buf );
|
|
|
|
#ifdef KERNEL
|
|
|
|
fnBangCmd( "getloaded", NULL );
|
|
if (hDefaultLibrary) {
|
|
ExtensionApiVersion = (PWINDBG_EXTENSION_API_VERSION)
|
|
GetProcAddress( hDefaultLibrary, "ExtensionApiVersion" );
|
|
if (ExtensionApiVersion) {
|
|
lpav = (LPAPI_VERSION)ExtensionApiVersion();
|
|
GetModuleFileName( hDefaultLibrary, buf, sizeof(buf) );
|
|
tstamp = GetTimestampForLoadedLibrary( hDefaultLibrary );
|
|
p = ctime( &tstamp );
|
|
p[strlen(p)-1] = 0;
|
|
_strlwr( buf );
|
|
dprintf(
|
|
"kdext version: %d.%d.%d, built: %s [name: %s]\n",
|
|
lpav->MajorVersion,
|
|
lpav->MinorVersion,
|
|
lpav->Revision,
|
|
p,
|
|
buf );
|
|
}
|
|
fnBangCmd( "version", NULL );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
VerifyVersionInformation(
|
|
void
|
|
)
|
|
{
|
|
extern HANDLE hDefaultLibrary;
|
|
LPAPI_VERSION lpav;
|
|
#ifdef KERNEL
|
|
PWINDBG_EXTENSION_API_VERSION ExtensionApiVersion;
|
|
#endif
|
|
|
|
ImagehlpAV = *ImagehlpApiVersion();
|
|
if ((ImagehlpAV.MinorVersion != ApiVersion.MinorVersion) ||
|
|
(ImagehlpAV.MajorVersion != ApiVersion.MajorVersion) ||
|
|
(ImagehlpAV.Revision != ApiVersion.Revision)) {
|
|
//
|
|
// bad version match
|
|
//
|
|
dprintf( "imagehlp.dll has a version mismatch with the debugger\n\n" );
|
|
if (!MYOB) {
|
|
PrintVersionInformation();
|
|
ExitProcess( 1 );
|
|
}
|
|
}
|
|
|
|
#ifdef KERNEL
|
|
fnBangCmd( "getloaded", NULL );
|
|
if (hDefaultLibrary) {
|
|
ExtensionApiVersion = (PWINDBG_EXTENSION_API_VERSION)
|
|
GetProcAddress( hDefaultLibrary, "ExtensionApiVersion" );
|
|
if (ExtensionApiVersion) {
|
|
lpav = (LPAPI_VERSION)ExtensionApiVersion();
|
|
if ((lpav->MinorVersion != ApiVersion.MinorVersion) ||
|
|
(lpav->MajorVersion != ApiVersion.MajorVersion) ||
|
|
(lpav->Revision != ApiVersion.Revision)) {
|
|
//
|
|
// bad version match
|
|
//
|
|
dprintf( "kdext.dll has a version mismatch with the debugger:\n" );
|
|
dprintf( " ext dbg\n");
|
|
dprintf( "major: %04x %04x\nminor: %04x %04x\nrev: %04x %04x\n\n",
|
|
lpav->MinorVersion, ApiVersion.MinorVersion,
|
|
lpav->MajorVersion, ApiVersion.MajorVersion,
|
|
lpav->Revision , ApiVersion.Revision);
|
|
|
|
if (!MYOB) {
|
|
PrintVersionInformation();
|
|
ExitProcess( 1 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
DWORD
|
|
GetContinueStatus (
|
|
DWORD fFirstChance,
|
|
BOOLEAN fDefault
|
|
)
|
|
{
|
|
if (cmdState == 'g') {
|
|
if (chExceptionHandle == 'h')
|
|
return (DWORD)DBG_EXCEPTION_HANDLED;
|
|
if (chExceptionHandle == 'n')
|
|
return (DWORD)DBG_EXCEPTION_NOT_HANDLED;
|
|
}
|
|
if (!fFirstChance || fDefault)
|
|
return (DWORD)DBG_EXCEPTION_HANDLED;
|
|
else
|
|
return (DWORD)DBG_EXCEPTION_NOT_HANDLED;
|
|
}
|