/*** 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 <setjmp.h>
#ifndef NT_HOST
#include <signal.h>
#endif
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <stdarg.h>
#undef NULL
#include "ntsdp.h"
#ifndef NT_SAPI
PUCHAR Version_String =
"\n"
#ifdef KERNEL
"Microsoft(R) Windows NT Kernel Debugger\n"
#else
"Microsoft(R) Windows NT Debugger\n"
#endif
"Version 1.00\n"
"(C) 1991 Microsoft Corp.\n"
"\n";
extern ulong EXPRLastExpression; // from module ntexpr.c
ULONG EXPRLastDump = 0L;
jmp_buf cmd_return;
CONTEXT RegisterContext;
void InitNtCmd(void);
#ifndef KERNEL
NTSD_EXTENSION_APIS NtsdExtensions;
HANDLE hNtsdDefaultLibrary;
void ProcessStateChange(BOOLEAN, BOOLEAN);
#else
#define BOOL BOOLEAN
void DelImages(void);
void ProcessStateChange(ULONG, PDBGKD_CONTROL_REPORT);
extern ULONG NumberProcessors;
#endif
extern PUCHAR LogFileName;
extern BOOLEAN fLogAppend;
extern BOOLEAN UserRegTest(ULONG);
#endif /* NT_SAPI */
extern BOOLEAN ReadVirtualMemory(PUCHAR, PUCHAR, ULONG, PULONG);
#ifndef NT_SAPI
void error(ULONG);
void RemoveDelChar(PUCHAR);
#if defined(i386)
#include "i386\ntreg.h"
#endif
#if defined(KERNEL)
extern void SetWaitCtrlHandler(void);
extern void SetCmdCtrlHandler(void);
#endif
#if defined(i386) && defined(KERNEL)
extern ULONG GetRegValue(ULONG);
extern int G_mode_32;
#endif
void HexList(PUCHAR, ULONG *, ULONG);
ULONG HexValue(ULONG);
void AsciiList(PUCHAR, ULONG *);
ULONG GetIdList(void);
void GetRange(NT_PADDR, PULONG, PBOOLEAN, ULONG
#ifdef MULTIMODE
, ULONG
#endif
);
void ProcessCommands(void);
void OutDisCurrent(BOOLEAN, BOOLEAN);
void RestoreBrkpts(void);
BOOLEAN SetBrkpts(void);
#ifndef KERNEL
void RemoveProcessBps(PPROCESS_INFO);
void RemoveThreadBps(PTHREAD_INFO);
void ChangeRegContext(PTHREAD_INFO);
BOOLEAN FreezeThreads(void);
void UnfreezeThreads(void);
#else
#ifdef i386
void ChangeKdRegContext(ULONG, ULONG, ULONG);
#else
void ChangeKdRegContext(ULONG);
#endif
void InitFirCache(ULONG, PUCHAR);
void UpdateFirCache(ULONG);
#endif
#ifndef KERNEL
void fnOutputProcessInfo(PPROCESS_INFO);
void fnOutputThreadInfo(PTHREAD_INFO);
void fnSetBp(ULONG, UCHAR, UCHAR, NT_PADDR, ULONG, PTHREAD_INFO, PUCHAR);
void fnGoExecution(NT_PADDR, ULONG, PTHREAD_INFO, BOOLEAN, NT_PADDR);
void fnStepTrace(NT_PADDR, ULONG, PTHREAD_INFO, BOOLEAN, UCHAR);
#else
void fnSetBp(ULONG, UCHAR, UCHAR, NT_PADDR, ULONG, PUCHAR);
void fnGoExecution(NT_PADDR, ULONG, NT_PADDR);
void fnStepTrace(NT_PADDR, ULONG, UCHAR);
#endif
void fnBangCmd(PUCHAR);
void fnInteractiveEnterMemory(NT_PADDR, ULONG);
void fnDotCommand(void);
void fnEvaluateExp(void);
void fnAssemble(NT_PADDR);
void fnViewLines(void);
void fnUnassemble(NT_PADDR, ULONG, BOOLEAN);
void fnEnterMemory(NT_PADDR, PUCHAR, ULONG);
void fnChangeBpState(ULONG, UCHAR);
void fnListBpState(void);
ULONG fnDumpAsciiMemory(NT_PADDR, ULONG);
void fnDumpByteMemory(NT_PADDR, ULONG);
void fnDumpWordMemory(NT_PADDR, ULONG);
void fnDumpDwordMemory(NT_PADDR, ULONG);
#ifdef KERNEL
void fnInputIo(ULONG, UCHAR);
void fnOutputIo (ULONG, ULONG, UCHAR);
#endif
void fnCompareMemory(NT_PADDR, ULONG, NT_PADDR);
void fnMoveMemory(NT_PADDR, ULONG, NT_PADDR);
void fnFillMemory(NT_PADDR, ULONG, PUCHAR, ULONG);
void fnSearchMemory(NT_PADDR, ULONG, PUCHAR, ULONG);
void parseScriptCmd(void);
#ifndef KERNEL
void parseThreadCmds(void);
void parseProcessCmds(void);
void parseBpCmd(BOOLEAN, PTHREAD_INFO);
void parseGoCmd(PTHREAD_INFO, BOOLEAN);
void parseStepTrace(PTHREAD_INFO, BOOLEAN, UCHAR);
#else
void parseBpCmd(BOOLEAN);
void parseGoCmd(void);
void parseStepTrace(UCHAR);
#endif
void parseRegCmd(void);
#ifdef i386
ULONG parseStackTrace(PBOOLEAN, NT_PADDR*);
#else
ULONG parseStackTrace(void);
#endif
#if defined(i386) && !defined(KERNEL)
BOOLEAN fOutputRegs = TRUE; // set if output regs on breakpoint
#else
BOOLEAN fOutputRegs = FALSE; // set if output regs on breakpoint
#endif
void fnSetSuffix(void);
BOOLEAN GetMemByte(NT_PADDR, PUCHAR);
BOOLEAN GetMemWord(NT_PADDR, PUSHORT);
BOOLEAN GetMemDword(NT_PADDR, PULONG);
ULONG GetMemString(NT_PADDR, PUCHAR, ULONG);
BOOLEAN SetMemByte(NT_PADDR, UCHAR);
BOOLEAN SetMemWord(NT_PADDR, USHORT);
BOOLEAN SetMemDword(NT_PADDR, ULONG);
ULONG SetMemString(NT_PADDR, PUCHAR, ULONG);
void OutputSymAddr(ULONG, BOOLEAN, BOOLEAN);
static void ExpandUserRegs(PUCHAR);
#if defined(KERNEL) && defined(i386)
static DESCRIPTOR_TABLE_ENTRY csDesc;
#endif
#ifndef KERNEL
void GetSymbolRoutine(LPVOID, PUCHAR, PULONG);
DWORD disasmExportRoutine(LPDWORD, LPSTR, BOOL);
NTSTATUS GetClientId(void);
#endif
ULONG disasmRoutine(PULONG, PUCHAR, BOOLEAN);
#if defined(i386) && defined(KERNEL)
extern ULONG contextState;
extern jmp_buf main_return;
extern jmp_buf reboot;
static USHORT lastSel = 0xFFFF;
static ULONG lastOffset;
#endif
#if defined(i386)
extern ULONG GetDregValue(ULONG);
extern void SetDregValue(ULONG, ULONG);
#endif
void fnLogOpen(BOOLEAN);
void fnLogClose(void);
void lprintf(char *);
void cdecl dprintf(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 CurrentProcessor;
extern void SaveProcessorState(void);
extern void RestoreProcessorState(void);
#ifdef i386
BOOLEAN fSetGlobalDataBrkpts;
BOOLEAN fDataBrkptsChanged;
#endif
#endif
UCHAR chCommand[512]; // top-level command buffer
UCHAR chLastCommand[80] = {0}; // 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 = FALSE; // TEMP TEMP - workaround
BOOLEAN fPhysicalAddress=FALSE;
jmp_buf asm_return; // TEMP TEMP
NT_ADDR assemDefaultS, dumpDefaultS, unasmDefaultS;
UCHAR dumptype = 'b'; // 'a' - ascii; 'b' - byte...
UCHAR entertype = 'b'; // ...'w' - word; 'd' - dword
NT_PADDR dumpDefault =&dumpDefaultS; // default dump address
NT_PADDR unasmDefault=&unasmDefaultS; // default unassembly address
NT_PADDR assemDefault=&assemDefaultS; // 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;
NT_ADDR addr[1];
ULONG handle;
ULONG passcnt;
ULONG setpasscnt;
char szcommand[60];
#ifndef KERNEL
PPROCESS_INFO pProcess;
PTHREAD_INFO pThread;
#endif
} brkptlist[32];
extern int fControlC;
extern int fFlushInput;
#endif /* NT_SAPI */
ULONG pageSize;
#ifndef NT_SAPI
#ifndef KERNEL
extern void fnSetException(void);
#endif
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
PTHREAD_INFO pThreadCmd = NULL; // pointer to thread to qualify any
// temporary breakpoint using 'g', 't', 'p'
BOOLEAN fFreeze = FALSE; // TRUE if suspending all threads except
// that pointed by pThreadCmd
#endif
NT_ADDR steptraceaddrS; // defined if cmdState is 't' or 'p'
NT_PADDR steptraceaddr=&steptraceaddrS; // address of trace breakpoint
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
ULONG deferaddr; // address of deferred breakpoint
ULONG deferhandle; // handle of deferred breakpoint
BOOLEAN fDeferBpSet; // TRUE if trace breakpoint set
BOOLEAN fDeferDefined = FALSE; // TRUE if deferred breakpoint is used
#ifndef KERNEL
PPROCESS_INFO pProcessDeferBrkpt; // process of deferred breakpoint
#endif
// defined if cmdState is 'g'
ULONG gocnt; // number of "go" breakpoints active
struct GoBrkpt {
NT_ADDR addr[1]; // 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
extern UCHAR chExceptionHandle;
extern PUCHAR pszScriptFile;
static FILE *streamCmd;
static void igrep(void);
int ntsdstricmp(PUCHAR, PUCHAR);
BOOLEAN fPointerExpression;
/*** 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;
for (count = 0; count < 32; count++) {
brkptlist[count].status = '\0';
brkptlist[count].fBpSet = FALSE;
}
for (count = 0; count < 10; count++)
golist[count].fBpSet = FALSE;
#ifndef KERNEL
BrkptInit();
#endif
fStepTraceBpSet = FALSE;
fDeferBpSet = FALSE;
steptracelow = -1;
chCommand[0] = '\0';
if (!pszScriptFile)
pszScriptFile = "ntsd.ini";
if (!(streamCmd = fopen(pszScriptFile, "r")))
#ifdef KERNEL
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
#endif
)
{
ULONG count;
BOOLEAN fBrkptHit = 0;
BOOLEAN fPassThrough = FALSE;
BOOLEAN fHardBrkpt = FALSE;
BOOLEAN fLoopError = FALSE;
NT_ADDR pcaddrS, *pcaddr=&pcaddrS;
UCHAR bBrkptInstr[4];
BOOLEAN fSymbol;
BOOLEAN fOutputDisCurrent = TRUE;
#ifdef KERNEL
ChangeKdRegContext(pcEntryAddr
#ifdef i386
, pCtlReport->Dr6, pCtlReport->Dr7
#endif
);
ClearTraceFlag();
#ifdef i386
fDataBrkptsChanged = FALSE;
fVm86 = VM86(GetRegValue(REGEFL));
if (!fVm86) {
csDesc.Selector = GetRegValue(REGCS);
DbgKdLookupSelector((USHORT)0, &csDesc);
f16pm = !csDesc.Descriptor.HighWord.Bits.Default_Big;
}
#endif
InitFirCache(pCtlReport->InstructionCount,
pCtlReport->InstructionStream);
#endif // #ifdef KERNEL
do {
fSymbol = TRUE;
if (fHardBrkpt && cmdState == 'g')
fPassThrough = TRUE;
if (!fHardBrkpt) {
RestoreBrkpts();
#ifndef KERNEL
UnfreezeThreads();
#else
// UpdateFirCache((ULONG)pcEntryAddr);
#endif
}
#ifndef KERNEL
ChangeRegContext(pProcessEvent->pThreadEvent);
#endif
if (fLoopError)
cmdState = 'i';
else {
*pcaddr = *GetRegPCValue();
#ifndef KERNEL
if (fBrkpt) {
#ifdef i386
AddrSub(pcaddr, cbBrkptLength);
#endif
SetRegPCValue(pcaddr);
fBrkpt = FALSE;
}
#endif
fHardBrkpt = (BOOLEAN)(GetMemString(pcaddr, bBrkptInstr,
cbBrkptLength) &&
!memcmp(bBrkptInstr, (PUCHAR)&trapInstr,
(int)cbBrkptLength));
if ((cmdState == 'p' || cmdState == 't') &&
#ifndef KERNEL
pProcessStepBrkpt == pProcessEvent &&
#endif
(Flat(steptraceaddr) == -1L ||
AddrEqu(steptraceaddr, pcaddr))) {
// step/trace event occurred
fSymbol = FALSE;
#ifndef KERNEL
// ignore step/trace event is not the specific
// thread requested.
if (pThreadCmd && pThreadCmd != pProcessEvent->pThreadEvent)
fPassThrough = TRUE;
else
#endif
// test if step/trace range active
// if so, compute the next offset and pass through
if (steptracelow != -1 && Flat(pcaddr) >= steptracelow
&& Flat(pcaddr) < steptracehigh) {
#ifdef MIPS
Flat(steptraceaddr)=
#endif
GetNextOffset(
#ifndef MIPS
steptraceaddr,
#endif
(BOOLEAN)(cmdState == 'p'));
fPassThrough = TRUE;
}
// active step/trace event - note event if count is zero
else if ((fControlC
#ifdef KERNEL
&& DbgKdpBreakIn
#endif
) ||
(fPassThrough = (BOOLEAN)(--steptracepasscnt != 0))
== 0) {
fBrkptHit |= STEPTRACEHIT;
steptracepasscnt = 0;
fControlC = 0;
#ifdef KERNEL
DbgKdpBreakIn = FALSE; // TEMP TEMP TEMP
#endif
}
else {
// more remaining events to occur, but output
// the instruction (optionally with registers)
// compute the step/trace address for next event
if (fOutputRegs)
OutputAllRegs();
#ifndef KERNEL
OutDisCurrent(TRUE, fSymbol); // output with EA
#else
OutDisCurrent(fOutputRegs, fSymbol); // no EA if no regs
#endif
#ifdef MIPS
Flat(steptraceaddr)=
#endif
GetNextOffset(
#ifndef MIPS
steptraceaddr,
#endif
(BOOLEAN)(cmdState == 'p'));
GetCurrentMemoryOffsets(&steptracelow, &steptracehigh);
fPassThrough = TRUE;
}
}
else if (cmdState == 'g') {
for (count = 0; count < gocnt; count++) {
if (AddrEqu(golist[count].addr,pcaddr)) {
#ifndef KERNEL
if (pProcessGoBrkpt == pProcessEvent &&
(pThreadCmd == NULL ||
pThreadCmd == pProcessEvent->pThreadEvent)) {
if (fVerboseOutput)
dprintf("Hit Breakpoint#%d\n",count);
#endif
fBrkptHit |= GOLISTHIT;
#ifndef KERNEL
}
else
fPassThrough = TRUE;
#endif
break;
}
}
}
for (count = 0; count < 32; count++) {
if (brkptlist[count].status == 'e'
#ifndef KERNEL
&& brkptlist[count].pProcess == pProcessEvent
#endif
#ifdef i386
&& ((brkptlist[count].option == (UCHAR)-1
&& AddrEqu(brkptlist[count].addr, pcaddr))
|| (brkptlist[count].option != (UCHAR)-1
#ifdef KERNEL
&& ((pCtlReport->Dr6
#else
&& ((GetDregValue(6)
#endif
>> brkptlist[count].dregindx) & 1)))) {
#else
&& AddrEqu(brkptlist[count].addr,pcaddr)) {
#endif
if (
#ifndef KERNEL
(brkptlist[count].pThread == NULL ||
brkptlist[count].pThread
== pProcessEvent->pThreadEvent) &&
#endif
--brkptlist[count].passcnt == 0) {
fBrkptHit |= BRKPTHIT;
brkptlist[count].passcnt = 1;
}
else
fPassThrough = TRUE;
break;
}
}
if (fDeferDefined
#ifndef KERNEL
&& pProcessDeferBrkpt == pProcessEvent
#endif
&& (deferaddr == -1 || deferaddr == Flat(pcaddr))
&& 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 (fOutputRegs)
OutputAllRegs();
if (fOutputDisCurrent)
OutDisCurrent(fOutputRegs, fSymbol); // no EA if no registers
#else
if (fOutputRegs && !fBrkptContinue)
OutputAllRegs();
if (fOutputDisCurrent && !fBrkptContinue)
OutDisCurrent(TRUE, fSymbol); // output with EA
#endif
*dumpDefault = *unasmDefault = *assemDefault
= *GetRegPCValue();
///////////////////////////////
#ifndef KERNEL
if (fBrkptContinue)
cmdState = 'g';
else
ProcessCommands();
fBrkptContinue = FALSE;
#else
ProcessCommands();
#endif
///////////////////////////////
if (cmdState == 'p' || cmdState == 't') {
#ifndef KERNEL
if (pThreadCmd == NULL)
pThreadCmd = pProcessCurrent->pThreadCurrent;
ChangeRegContext(pThreadCmd);
#endif
#ifdef MIPS
Flat(steptraceaddr)=
#endif
GetNextOffset(
#ifndef MIPS
steptraceaddr,
#endif
(BOOLEAN)(cmdState == 'p'));
GetCurrentMemoryOffsets(&steptracelow, &steptracehigh);
#ifndef KERNEL
pProcessStepBrkpt = pProcessCurrent;
ChangeRegContext(pProcessEvent->pThreadEvent);
#endif
}
*pcaddr = *GetRegPCValue();
fHardBrkpt = (BOOLEAN)(GetMemString(pcaddr, bBrkptInstr,
cbBrkptLength) &&
!memcmp(bBrkptInstr, (PUCHAR)&trapInstr,
(int)cbBrkptLength));
}
if (fHardBrkpt) {
fLoopError = FALSE;
SetRegPCValue(AddrAdd(pcaddr, cbBrkptLength));
}
else
fLoopError = (BOOLEAN)(SetBrkpts()
#ifndef KERNEL
&& FreezeThreads()
#endif
);
}
while (fHardBrkpt || !fLoopError);
#ifndef KERNEL
ChangeRegContext(0);
#else
#ifdef i386
ChangeKdRegContext(0, 0, 0);
#else
ChangeKdRegContext(0);
#endif
#endif
}
/*** 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;
NT_ADDR addr1;
NT_ADDR addr2;
ULONG value1;
ULONG value2;
ULONG count;
BOOLEAN fLength;
UCHAR list[STRLISTSIZE];
ULONG size;
#ifdef KERNEL
PUCHAR SavedpchCommand;
#endif
PUCHAR pargstring;
PPROCESS_INFO pProcessPrevious;
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;
#endif
#endif
do {
ch = *pchCommand++;
if (ch == '\0' || (ch == ';' && fFlushInput)) {
fControlC = FALSE;
fFlushInput = FALSE;
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", CurrentProcessor);
cbPrompt = 5;
}
else {
dprintf("kd");
cbPrompt = 2;
}
#endif
fPhysicalAddress = FALSE;
NtsdPrompt("> ", chCommand, 80);
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 8 processors
//
ASSERT(NumberProcessors < 8);
if (ch >= '0' && ch <= '9') {
value1 = 0;
SavedpchCommand = pchCommand;
while (ch >= '0' && ch <= '9') {
value1 = value1 * baseDefault + (ch - '0');
ch = *SavedpchCommand++;
}
ch = (UCHAR)tolower(ch);
if (ch == 'r' || ch == 'k') {
if (value1 < NumberProcessors) {
if (value1 != (ULONG)CurrentProcessor) {
SaveProcessorState();
CurrentProcessor = (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;
#endif
case '.':
fPointerExpression=FALSE;
fnDotCommand();
break;
case '!':
pargstring = pchCommand;
while (*pchCommand != '\0') {
pchCommand++;
}
fnBangCmd(pargstring);
break;
case '#':
igrep();
pargstring = pchCommand;
while (*pchCommand != '\0') pchCommand++;
break;
case 'a':
if ((ch = PeekChar()) != '\0' && ch != ';')
*assemDefault = *(GetAddrExpression(REGCS));
fnAssemble(assemDefault);
break;
case 'b':
ch = *pchCommand++;
switch (tolower(ch)) {
#ifdef i386
case 'a':
parseBpCmd(TRUE
#ifndef KERNEL
,NULL
#endif
); // data breakpoint
break;
#endif
case 'c':
case 'd':
case 'e':
value1 = GetIdList();
fnChangeBpState(value1, ch);
break;
case 'l':
fnListBpState();
break;
case 'p':
parseBpCmd(FALSE // nondata breakpoint
#ifndef KERNEL
,NULL
#endif
);
break;
default:
error(SYNTAX);
break;
}
break;
case 'c':
GetRange(&addr1, &value2, &fLength, 1
#ifdef MULTIMODE
, REGDS
#endif
);
addr2 = *GetAddrExpression(REGDS);
fnCompareMemory(&addr1, value2, &addr2);
break;
case 'd':
ch = (UCHAR)tolower(*pchCommand);
if (ch == 'a' || ch == 'b' || ch == 'w' || ch == 'd'
#if defined(KERNEL) && defined(MIPS)
|| ch == 't'
#endif
) {
pchCommand++;
dumptype = ch;
}
#if defined(KERNEL) && defined(i386)
if ((fVm86||f16pm) && vm86DefaultSeg==-1L)
vm86DefaultSeg = GetRegValue(REGDS);
#endif
// addr1 = *dumpDefault; // default starting address
fLength = TRUE;
switch (dumptype) {
case 'a':
value2 = 384;
GetRange(dumpDefault, &value2, &fLength, 1
#ifdef MULTIMODE
, REGDS
#endif
);
fnDumpAsciiMemory(dumpDefault, value2);
break;
case 'b':
value2 = 128;
GetRange(dumpDefault, &value2, &fLength, 1
#ifdef MULTIMODE
, REGDS
#endif
);
fnDumpByteMemory(dumpDefault, value2);
break;
case 'w':
value2 = 64;
GetRange(dumpDefault, &value2, &fLength, 2
#ifdef MULTIMODE
, REGDS
#endif
);
fnDumpWordMemory(dumpDefault, value2);
break;
case 'd':
value2 = 32;
GetRange(dumpDefault, &value2, &fLength, 4
#ifdef MULTIMODE
, REGDS
#endif
);
fnDumpDwordMemory(dumpDefault, value2);
break;
#if defined(KERNEL) && defined(MIPS)
case 't':
value2 = 8;
GetRange(&value1, &value2, &fLength, 8);
fnDumpTb4000(value1, value2);
value1 = value1 + value2;
break;
#endif
}
break;
case 'e':
ch = (UCHAR)tolower(*pchCommand);
if (ch == 'a' || ch == 'b' || ch == 'w' || ch == 'd') {
pchCommand++;
entertype = ch;
}
addr1 = *GetAddrExpression(REGDS);
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 MULTIMODE
, REGDS);
if (fnotFlat(&addr1)) error(SYNTAX);
#else
);
#endif
HexList(list, &count, 1);
fnFillMemory(&addr1, value2, list, count);
break;
case 'g':
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':
#ifdef i386
value1 = parseStackTrace(&fLength, (NT_PADDR*)&value2);
fnStackTrace(value1, (NT_PADDR)value2, fLength);
#else
if (tolower(*(chCommand+1)) == 'b') {
fnStackTrace(parseStackTrace(),TRUE);
} else {
fnStackTrace(parseStackTrace(),FALSE);
}
#endif
#ifdef KERNEL
if (fSwitched) {
RestoreProcessorState();
fSwitched = FALSE;
}
#endif
break;
case 'l':
ch = (UCHAR)tolower(*pchCommand);
if (ch != 'n')
error(SYNTAX);
pchCommand++;
if ((ch = PeekChar()) != '\0' && ch != ';')
addr1 = *GetAddrExpression(REGCS);
else
addr1 = *GetRegPCValue();
fnListNear(Flat(&addr1));
break;
case 'm':
GetRange(&addr1, &value2, &fLength, 1
#ifdef MULTIMODE
, REGDS
#endif
);
fnMoveMemory(&addr1, value2, GetAddrExpression(REGDS));
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 'p':
case 't':
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, 0, 0);
#endif
#ifndef KERNEL
ExitProcess(0);
#else
exit(0);
#endif
case 'r':
parseRegCmd();
#ifdef KERNEL
if (fSwitched) {
RestoreProcessorState();
fSwitched = FALSE;
}
#endif
break;
case 's':
ch = (UCHAR)tolower(*pchCommand);
// 's' by itself outputs the current display mode
if (ch == ';' || ch == '\0') {
if (fSourceOnly)
dprintf("source");
else if (fSourceMixed)
dprintf("mixed");
else
dprintf("assembly");
dprintf(" mode\n");
}
// 's+', 's&', 's-' set source, mixed, and assembly modes
else if (ch == '+') {
pchCommand++;
fSourceOnly = TRUE;
fSourceMixed = FALSE;
}
else if (ch == '&') {
pchCommand++;
fSourceOnly = FALSE;
fSourceMixed = TRUE;
}
else if (ch == '-') {
pchCommand++;
fSourceOnly = FALSE;
fSourceMixed = FALSE;
}
else if (ch == 's') {
pchCommand++;
fnSetSuffix();
}
#ifndef KERNEL
else if (ch == 'x') {
pchCommand++;
fnSetException();
}
#endif
else {
GetRange(&addr1, &value2, &fLength, 1
#ifdef MULTIMODE
, REGDS
#endif
);
HexList(list, &count, 1);
fnSearchMemory(&addr1, value2, list, count);
}
break;
case 'u':
// addr1 = *unasmDefault;
value2 = 8; // eight instructions
fLength = TRUE; // length, not ending addr
GetRange(unasmDefault, &value2, &fLength, 0
#ifdef MULTIMODE
, REGCS
#endif
);
fnUnassemble(unasmDefault, value2, fLength);
break;
case 'v':
fnViewLines();
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;
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;
default:
dprintf("Unknown");
break;
}
dprintf(" error\n");
// longjmp(*pjbufReturn, 1); // TEMP TEMP
if (fJmpBuf) // TEMP TEMP
longjmp(asm_return,1); // TEMP TEMP
else // TEMP TEMP
longjmp(cmd_return, 1); // TEMP TEMP
}
void fnInteractiveEnterMemory (NT_PADDR Address, ULONG Size)
{
UCHAR chEnter[80];
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;
}
ULONG HexValue (ULONG bytesize)
{
UCHAR ch;
ULONG value = 0;
PeekChar();
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 >= (ULONG)(1L << (8 * bytesize - 4)))
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 id_mask = 0;
UCHAR ch;
ULONG shiftcnt;
if ((ch = PeekChar()) == '*') {
id_mask = 0xffffffff;
pchCommand++;
}
else {
while (TRUE) {
if (ch >= '0' && ch <= '9') {
shiftcnt = ch - '0';
ch = *++pchCommand;
if (ch >= '0' && ch <= '9') {
shiftcnt = shiftcnt * 10 + ch - '0';
ch = *++pchCommand;
}
if (shiftcnt > 31 ||
(ch != ' ' && ch != '\t' && ch != '\0' && ch != ';'))
error(SYNTAX);
id_mask |= (ULONG)1 << shiftcnt;
if ((ch = PeekChar()) == '\0' || ch == ';')
break;
}
else
error(SYNTAX);
}
}
return id_mask;
}
/*** 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;
// if there was nothing after the dot look up a function
if (!*pchCommand) {
PSYMFILE pSymfile;
PSYMBOL pSymbol =
GetFunctionFromOffset(&pSymfile,Flat(GetRegPCValue()));
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(i386) & defined(KERNEL)
if (!strcmp(chCmd, "reboot") && ch == '\0') {
DbgKdReboot(); // reboot demands trailing null
DelImages();
chLastCommand[0] = '\0'; // null out .reboot command
contextState = CONTEXTFIR; // reset context state...
// exit(1);
longjmp(reboot, 1); // ...and wait for event
}
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[80];
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.
*
*************************************************************************/
void cdecl dprintf (char *format, ...)
{
unsigned char outbuffer[512];
va_list arg_ptr;
va_start(arg_ptr, format);
vsprintf(outbuffer, format, arg_ptr);
if (loghandle != -1) {
vsprintf(outbuffer, format, arg_ptr);
write(loghandle, outbuffer, strlen(outbuffer));
}
#ifndef KERNEL
if (fDebugOutput)
DbgPrint("%s", outbuffer);
else
#endif
printf("%s", outbuffer);
va_end(arg_ptr);
}
/*** 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);
dumpDefault = unasmDefault = assemDefault
= GetRegPCValue();
}
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->pszName);
pProcess = pProcess->pProcessNext;
}
}
#endif
#ifndef KERNEL
void parseThreadCmds (void)
{
UCHAR ch;
PTHREAD_INFO pThread;
ULONG index, value1, value2;
BOOLEAN fLength;
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++);
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);
*dumpDefault = *unasmDefault = *assemDefault
= *GetRegPCValue();
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);
#ifdef i386
value1 = parseStackTrace(&fLength, (NT_PADDR*)&value2);
fnStackTrace(value1, (NT_PADDR)value2, fLength);
#else
if (tolower(*(chCommand+1)) == 'b') {
fnStackTrace(parseStackTrace(),TRUE);
} else {
fnStackTrace(parseStackTrace(),FALSE);
}
#endif
ChangeRegContext(pProcessCurrent->pThreadCurrent);
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) {
Status = NtQueryInformationThread(
pThread->hThread,
ThreadBasicInformation,
&ThreadBasicInfo,
sizeof(ThreadBasicInfo),
NULL
);
if (!NT_SUCCESS(Status)) {
dprintf("NTSD: NtQueryInfomationThread failed\n");
return;
}
dprintf("%3ld id: %lx.%lx Teb %lx ", pThread->index,
pProcessCurrent->dwProcessId,
pThread->dwThreadId,
ThreadBasicInfo.TebBaseAddress
);
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 (NT_PADDR paddr)
{
char chAssemble[80];
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, 60);
RemoveDelChar(chAssemble);
pchCommand = chAssemble;
do
ch = *pchCommand++;
while (ch == ' ' || ch == '\t');
if (ch == '\0')
break;
pchCommand--;
ASSERT(fFlat(paddr));
assem(paddr, pchCommand);
}
// restore entry prompt and error recovery context
pchStart = pchStartSave;
pchCommand = pchCommandSave;
cbPrompt = cbPromptSave;
// pjbufReturn = pjbufReturnSave;
fJmpBuf = FALSE; // TEMP TEMP
}
void fnViewLines(void)
{
USHORT lineNum;
USHORT lineNum2;
PSYMFILE pSymfile;
PSYMFILE pSymfile2;
PLINENO pLineno;
PLINENO pLineno2;
ULONG count = 10;
UCHAR ch;
ULONG baseSaved;
ch = PeekChar();
// if no arguments, then output the next 10 lines
if (ch == ';' || ch == '\0') {
pLineno = GetLastLineno(&pSymfile, &lineNum);
if (!pLineno)
error(LINENUMBER);
}
else {
// parse the command line to get symbol file and line
// number structures.
lineNum = GetSrcExpression(&pSymfile, &pLineno);
ch = (UCHAR)tolower(PeekChar());
// if the next character is 'l', get expression for count
if (ch == 'l') {
pchCommand++;
count = (ULONG)GetExpression();
if (count == 0 || count > 0xffff)
error(LINENUMBER);
}
// if the next character is '.', use the difference plus one
// of the line number values for the line count.
else if (ch == '.') {
pchCommand++;
// get line number - default is decimal
baseSaved = baseDefault;
baseDefault = 10;
count = (ULONG)GetExpression();
baseDefault = baseSaved;
if (count > 0xffff || count == 0 || (USHORT)count < lineNum)
error(LINENUMBER);
count = count - (ULONG)lineNum + 1;
}
// if a nonNULL character, try to evaluate as an expression.
// output an error if not the same module and filename
// and a later line number.
else if (ch != ';' && ch != '\0') {
lineNum2 = GetSrcExpression(&pSymfile2, &pLineno2);
if (!pSymfile2 || pSymfile->modIndex != pSymfile2->modIndex
|| strcmp(pSymfile->pchName, pSymfile2->pchName)
|| lineNum > lineNum2)
error(LINENUMBER);
count = (ULONG)(lineNum2 - lineNum + 1);
}
}
// if line number is before first breakpoint top line,
// move LINENO pointer to get start of file
UpdateLineno(pSymfile, pLineno);
if (lineNum < pLineno->topLineNumber)
pLineno--;
if (!OutputLines(pSymfile, pLineno, lineNum, (USHORT)count))
dprintf("no source file to view\n");
}
/*** 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 (NT_PADDR paddr, ULONG value, BOOLEAN fLength)
{
BOOLEAN fFirst = TRUE;
UCHAR buffer[164];
BOOLEAN fStatus;
NT_PADDR pendaddr = (NT_PADDR)value;
while ((fLength && value--) || (!fLength && AddrLt(paddr,pendaddr))) {
if (fSourceOnly || fSourceMixed)
OutputSourceFromOffset(Flat(paddr), TRUE);
OutputSymAddr(Flat(paddr), fFirst, TRUE);
fStatus = disasm(paddr, buffer, FALSE);
dprintf("%s", buffer);
if (!fStatus)
error(MEMORY);
fFirst = FALSE;
//#ifndef KERNEL
if (fControlC) {
fControlC = 0;
return;
}
//#endif
}
}
/*** 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 (NT_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;
if (type == 'c')
type = '\0';
for (index = 0; index < 32; index++) {
if (mask & 1 && brkptlist[index].status != '\0') {
brkptlist[index].status = type;
#ifdef KERNEL
#ifdef i386
if (brkptlist[index].option != (UCHAR)-1) {
fDataBrkptsChanged = TRUE;
}
#endif
#endif
}
mask >>= 1;
}
}
/*** 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
PSYMFILE pSymfile;
PLINENO pLineno;
for (count = 0; count < 32; count++)
if (brkptlist[count].status != '\0') {
dprintf("%2ld %c ", count, brkptlist[count].status);
dprintAddr(brkptlist[count].addr);
if (brkptlist[count].option != (UCHAR)-1) {
if (brkptlist[count].option == 0)
option = 'e';
else if (brkptlist[count].option == 1)
option = 'w';
else
option = 'r';
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);
pLineno=GetLinenoFromOffset(&pSymfile,Flat(brkptlist[count].addr));
if (pLineno && pLineno->memoryOffset==Flat(brkptlist[count].addr))
dprintf("(%s.%d) ", pSymfile->pchName,
pLineno->breakLineNumber);
if (brkptlist[count].szcommand[0] != '\0')
dprintf("\"%s\"", brkptlist[count].szcommand);
dprintf("\n");
}
#ifndef KERNEL
pProcessCurrent = pProcessSaved;
#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:
* None.
*
* Exceptions:
* error exit:
* BPDUPLICATE - existing breakpoint at another number
* BPLISTFULL - breakpoint table is full
*
*************************************************************************/
void fnSetBp (ULONG bpno, UCHAR bpoption, UCHAR bpsize,
NT_PADDR bpaddr, ULONG passcnt,
#ifndef KERNEL
PTHREAD_INFO pThread,
#endif
PUCHAR string)
{
ULONG count;
#ifdef MIPS
PFUNCTION_ENTRY FunctionEntry;
#endif
#ifdef MIPS
FunctionEntry = LookupFunctionEntry(Flat(bpaddr));
if (FunctionEntry != NULL) {
if ( (Flat(bpaddr) >= FunctionEntry->StartingAddress) &&
(Flat(bpaddr) < FunctionEntry->EndOfPrologue)) {
Flat(bpaddr) = (NT_ADDR) FunctionEntry->EndOfPrologue;
}
}
#endif
for (count = 0; count < 32; 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);
}
else if (bpno != count)
error(BPDUPLICATE);
}
if (bpno == -1) {
for (count = 0; count < 32; count++)
if (brkptlist[count].status == '\0') {
bpno = count;
break;
}
if (bpno == -1)
error(BPLISTFULL);
}
brkptlist[bpno].status = 'e';
brkptlist[bpno].option = bpoption;
brkptlist[bpno].size = bpsize;
*brkptlist[bpno].addr = *bpaddr;
brkptlist[bpno].passcnt = passcnt;
brkptlist[bpno].setpasscnt = passcnt;
#ifndef KERNEL
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
}
/*** 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
*
* Output:
* None.
*
* Exceptions:
* error exit:
* SYNTAX - character after "bp" not decimal digit
* STRINGSIZE - command string too large
*
*************************************************************************/
void parseBpCmd (
#ifndef KERNEL
BOOLEAN fDataBp, PTHREAD_INFO pThread
#else
BOOLEAN fDataBp
#endif
)
{
ULONG bpno = -1;
NT_ADDR addrS = *GetRegPCValue();
NT_PADDR addr = &addrS;
ULONG passcnt = 1;
ULONG count = 1;
UCHAR option = -1;
UCHAR size;
UCHAR szBpCmd[60];
UCHAR *pchBpCmd = szBpCmd;
UCHAR ch;
// get the breakpoint number in bpno if given
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 > 31 || (ch != ' ' && ch != '\t' && ch != '\0'))
error(SYNTAX);
}
// if data breakpoint, get option and size values
if (fDataBp) {
USHORT cntDataBrkpts=0, index;
ch = (UCHAR)tolower(PeekChar());
if (ch == 'e')
option = 0;
else if (ch == 'w')
option = 1;
else if (ch == 'r')
option = 3;
else
error(SYNTAX);
pchCommand++;
ch = (UCHAR)tolower(PeekChar());
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 <32; 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') {
*addr = *GetAddrExpression(REGCS);
ch = PeekChar();
}
// test alignment if data breakpoint
if (fDataBp && (size & (UCHAR)Flat(addr)))
error(ALIGNMENT);
// 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 < 60) {
*pchBpCmd++ = ch;
ch = *pchCommand++;
count++;
}
if (count == 60)
error(STRINGSIZE);
if (ch == '\0' || ch == ';')
pchCommand--;
}
*pchBpCmd = '\0';
fnSetBp(bpno, option, size, addr, passcnt,
#ifndef KERNEL
pThread,
#endif
szBpCmd);
}
/*** 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 (NT_PADDR startaddr, ULONG bpcount,
#ifndef KERNEL
PTHREAD_INFO pThread, BOOLEAN fThreadFreeze,
#endif
NT_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[128];
PUCHAR pchValue;
ULONG value;
#if defined(KERNEL) & defined(i386)
// if 't' or 'T' follows 'r', toggle the terse flag
if (*pchCommand == 't' || *pchCommand == 'T') {
pchCommand++;
fTerseReg = (BOOLEAN)!fTerseReg;
}
#endif
// if just "r", output all the register and disassembly as EIP
if ((ch = PeekChar()) == '\0' || ch == ';') {
OutputAllRegs();
OutDisCurrent(TRUE, TRUE);
unasmDefault = assemDefault = GetRegPCValue();
}
else {
#ifdef KERNEL
// if [processor]r, no register can be specified.
if (fSwitched) {
error(SYNTAX);
}
#endif
// parse the register name
PARSE:
if ((count = GetRegName()) == -1)
error(SYNTAX);
// if "r <reg>", output value
if ((ch = (UCHAR)PeekChar()) == '\0' || ch == ',' || ch == ';') {
dprintf(UserRegTest(count) ? "%s=%s" : "%s=%08lx",
RegNameFromIndex(count), GetRegFlagValue(count));
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, 128);
RemoveDelChar(chValue);
SetRegFlagValue(count, (ULONG)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++);
}
if (ch != '\0')
error(SYNTAX);
SetRegFlagValue(count, value);
}
}
}
else
// if "r <reg> = <value>", set the value
if (UserRegTest(count)) {
SetRegFlagValue(count, (ULONG)pchCommand);
*pchCommand=0;
}
else
SetRegFlagValue(count, GetExpression());
}
else
// if "r <reg> <value>", also set the value
if (UserRegTest(count)) {
SetRegFlagValue(count, (ULONG)pchCommand);
*pchCommand=0;
}
else
SetRegFlagValue(count, 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;
NT_ADDR addr[10];
UCHAR ch;
NT_ADDR pcaddr;
#ifndef KERNEL
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;
}
}
#endif
pcaddr = *GetRegPCValue(); // default to current PC
count = 0;
if (PeekChar() == '=') {
*pchCommand++;
pcaddr = *GetAddrExpression(REGCS);
}
while ((ch = PeekChar()) != '\0' && ch != ';') {
if (count == 10)
error(LISTSIZE);
addr[count++] = *GetAddrExpression(REGCS);
}
#ifndef KERNEL
chLastCommand[0] = '\0'; // null out g command UNDONE ?????
#endif
fnGoExecution(&pcaddr, count,
#ifndef KERNEL
pThread, fThreadFreeze,
#endif
addr);
}
ULONG parseStackTrace (
#ifdef i386
PBOOLEAN pfOutputArgs, NT_PADDR *pStartFP
#else
void
#endif
)
{
UCHAR ch;
ULONG value;
#ifdef i386
*pfOutputArgs = FALSE;
if (tolower(PeekChar()) == 'b') {
pchCommand++;
*pfOutputArgs = TRUE;
}
if (PeekChar() == '=') {
pchCommand++;
*pStartFP = GetAddrExpression(REGSS);
}
else
*pStartFP = GetRegFPValue();
#else
if (tolower(PeekChar()) == 'b')
pchCommand++;
#endif
if ((ch = PeekChar()) != '\0' && ch != ';') {
value = GetExpression();
if ((LONG)value < 1) {
pchCommand++;
error(SYNTAX);
}
}
else
value = 10;
return value;
}
/*** 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 (NT_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;
}
/*** 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 (NT_PADDR startaddr, ULONG count)
{
ULONG rowindex = 0;
ULONG readcount;
UCHAR readbuffer[32];
UCHAR bytestring[17];
UCHAR ch;
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)));
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';
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 (NT_PADDR startaddr, ULONG count)
{
ULONG rowindex = 0;
ULONG readcount;
USHORT readbuffer[16];
ULONG blockindex = 0;
ULONG blockcount;
BOOLEAN first = TRUE;
ULONG memOffset = Flat(startaddr);
while (count > 0) {
blockcount = min(count, 16);
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 (NT_PADDR startaddr, ULONG count)
{
ULONG rowindex = 0;
ULONG readcount;
ULONG readbuffer[8];
ULONG blockindex = 0;
ULONG blockcount;
BOOLEAN first = TRUE;
ULONG memOffset = Flat(startaddr);
while (count > 0) {
blockcount = min(count, 8);
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");
}
#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)
{
NT_ADDR addr1;
ULONG value2;
UCHAR ch;
// if next character is 'r', toggle flag to output registers
// on display on breakpoint.
if (tolower(PeekChar() == 'r')) {
pchCommand++;
fOutputRegs = (BOOLEAN)!fOutputRegs;
}
addr1 = *GetRegPCValue(); // default to current PC
value2 = 1;
if (PeekChar() == '=') {
pchCommand++;
addr1 = *GetAddrExpression(REGCS);
}
if ((ch = PeekChar()) != '\0' && ch != ';')
value2 = GetExpression();
if ((LONG)value2 <= 0)
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 (NT_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
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)
{
NT_PADDR pcvalue;
UCHAR buffer[164];
BOOLEAN fSourceOutput;
pcvalue = GetRegPCValue();
if (fSourceOnly || fSourceMixed)
fSourceOutput = OutputSourceFromOffset(Flat(pcvalue), fSourceMixed);
if (!fSourceOnly || !fSourceOutput) {
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[120];
ULONG displacement;
GetSymbol(offset, chAddrBuffer, &displacement);
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 (NT_PADDR src1addr, ULONG length, NT_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 (NT_PADDR srcaddr, ULONG length, NT_PADDR destaddr)
{
UCHAR ch;
ULONG incr = 1;
if (AddrLt(srcaddr, destaddr)) {
AddrAdd(srcaddr, length - 1);
AddrAdd(destaddr, length - 1);
incr = -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 (NT_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 (NT_PADDR startaddr, ULONG length, PUCHAR plist, ULONG count)
{
ULONG searchindex;
ULONG listindex;
UCHAR ch;
NT_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) {
dprintAddr(AddrAdd(startaddr, searchindex));
dprintf("\n");
}
}
}
void fnSetSuffix (void)
{
UCHAR ch;
ch = (UCHAR)tolower(PeekChar());
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 (NT_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 (NT_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 (NT_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 (NT_PADDR paddr, PUCHAR pBufDest, ULONG length)
{
ULONG cTotalBytesRead = 0;
ULONG cBytesRead;
ULONG readcount;
PUCHAR pBufSource;
BOOLEAN fSuccess;
ASSERT(fFlat(paddr));
#if defined ( KERNEL ) && !defined ( NT_SAPI )
if (!fSwitched) {
if (cBytesRead = ReadCachedMemory(paddr, pBufDest, length))
return cBytesRead;
}
#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 (NT_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 (NT_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 (NT_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 (NT_PADDR paddr, PUCHAR pvalue, ULONG length)
{
// NTSTATUS status;
ULONG cBytesWritten;
ASSERT(fFlat(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
// restore the deferred breakpoint if set
//dprintf("RestoreBrkpts() called (gocnt=%d)\n", gocnt);
if (fDeferBpSet) {
DbgKdRestoreBreakPoint(deferhandle);
fDeferBpSet = FALSE;
}
// restore the step/trace breakpoint if set
if (fStepTraceBpSet) {
DbgKdRestoreBreakPoint(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",
DbgKdRestoreBreakPoint(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 = 31; index != -1; index--)
if (brkptlist[index].fBpSet) {
#ifndef KERNEL
pProcessCurrent = brkptlist[index].pProcess;
#endif
DbgKdRestoreBreakPoint(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;
NT_PADDR 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
pcaddr = GetRegPCValue();
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 < 32; index++)
if (brkptlist[index].status == 'e') {
if (AddrEqu(brkptlist[index].addr, pcaddr)) 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)
#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
ntstatus = DbgKdWriteBreakPoint
((PVOID)Flat(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;
}
}
}
#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);
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++) {
if (AddrEqu(golist[index].addr, pcaddr)) {
#if 0
if (fVerboseOutput) {
dprintf("Defering replacement of bp@");
dprintAddr(pcaddr);
dprintf("\n");
}
#endif
fDeferDefined = TRUE;
}else {
ntstatus=DbgKdWriteBreakPoint((PVOID)Flat(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 {
ntstatus = DbgKdWriteBreakPoint((PVOID)Flat(steptraceaddr),
&steptracehandle);
if (!(fStepTraceBpSet = (BOOLEAN)NT_SUCCESS(ntstatus))) {
dprintf("trace bp at addr ");
dprintAddr(steptraceaddr);
dprintf("failed.\n");
return FALSE;
}
}
}
// process deferred breakpoint
if (fDeferDefined) {
#ifdef MIPS
deferaddr = GetNextOffset(FALSE);
#else
deferaddr = -1;
#endif
if (deferaddr == -1)
SetTraceFlag();
else {
ntstatus = DbgKdWriteBreakPoint((PVOID)deferaddr, &deferhandle);
if (!(fDeferBpSet = (BOOLEAN)NT_SUCCESS(ntstatus))) {
dprintf("trace bp at addr %08lx failed\n", deferaddr);
return FALSE;
}
}
#ifndef KERNEL
pProcessDeferBrkpt = pProcessCurrent;
#endif
}
return TRUE;
}
#ifndef KERNEL
void RemoveProcessBps (PPROCESS_INFO pProcess)
{
UCHAR index;
for (index = 0; index < 32; index++)
if (brkptlist[index].status != '\0'
&& brkptlist[index].pProcess == pProcess)
brkptlist[index].status = '\0';
}
#endif
#ifndef KERNEL
void RemoveThreadBps (PTHREAD_INFO pThread)
{
UCHAR index;
for (index = 0; index < 32; index++)
if (brkptlist[index].status != '\0'
&& brkptlist[index].pProcess == pProcessCurrent
&& brkptlist[index].pThread == pThread)
brkptlist[index].status = '\0';
}
#endif
#ifndef KERNEL
/*** 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) {
b = SetThreadContext(pThreadContext->hThread, &RegisterContext);
if (!b) {
dprintf("NTSD: SetThreadContext failed\n");
ExitProcess(1);
}
}
pThreadContext = pThreadNew;
if (pThreadContext != NULL) {
RegisterContext.ContextFlags = ContextType;
b = GetThreadContext(pThreadContext->hThread, &RegisterContext);
if (!b) {
dprintf("NTSD: GetThreadContext failed\n");
ExitProcess(1);
}
}
}
}
/*** 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("NTSD: SuspendThread failed\n");
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("NTSD: ResumeThread failed\n");
return;
}
// ASSERT(b);
pThread->fSuspend = FALSE;
}
pThread = pThread->pThreadNext;
}
}
#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;
if (fDebugOutput)
s = (int)DbgPrompt(Prompt, Buffer, cb);
else {
dprintf("%s", Prompt);
if (!fgets(Buffer, cb, streamCmd)) {
streamCmd=stdin;
fgets(Buffer, cb, streamCmd);
}
s = strlen(Buffer);
}
lprintf(Buffer);
return s;
#endif
}
ULONG
GetExpressionRoutine(char * CommandString)
{
ULONG ReturnValue;
PUCHAR pchTemp;
ReturnValue = (ULONG)NULL;
pchTemp = pchCommand;
pchCommand = CommandString;
ReturnValue = GetExpression();
pchCommand = pchTemp;
return ReturnValue;
}
void GetSymbolRoutine (LPVOID offset, PUCHAR pchBuffer, PULONG pDisplacement)
{
GetSymbol((ULONG)offset, pchBuffer, pDisplacement);
}
#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)
{
NT_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;
}
//#ifndef KERNEL
BOOL CheckControlC (VOID)
{
if (fControlC) {
fControlC = 0;
return 1;
}
return 0;
}
#ifndef KERNEL
VOID
fnBangCmd(PUCHAR argstring)
{
PUCHAR pc;
PUCHAR modname;
PUCHAR pname;
// USHORT i;
// int (*pfunc)();
PNTSD_EXTENSION_ROUTINE ExtensionRoutine;
HANDLE hMod;
BOOLEAN LoadingDefault;
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;
}
//
// syntax is module.function argument-string
//
pc = argstring;
while ((*pc == ' ' ) || (*pc == '\t')) {
pc++;
}
modname = pc;
pname = NULL;
while ((*pc != '.') && (*pc != ' ') && (*pc != '\t') && (*pc != '\0')) {
pc++;
}
if ( *pc == '.' ) {
*pc = '\0';
pc++;
}
else {
*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);
}
hMod = hNtsdDefaultLibrary;
}
else {
hMod = LoadLibrary(modname);
}
if ( !hMod ) {
return;
}
ExtensionRoutine = (PNTSD_EXTENSION_ROUTINE)GetProcAddress(hMod,pname);
if ( !ExtensionRoutine ) {
dprintf("GetProcAddress(%s,%s) failed\n",modname,pname);
if ( !LoadingDefault ) {
FreeLibrary(hMod);
}
return;
}
(ExtensionRoutine)(
pProcessCurrent->hProcess,
pProcessCurrent->pThreadCurrent->hThread,
Flat(GetRegPCValue()),
&NtsdExtensions,
pc
);
if ( !LoadingDefault ) {
FreeLibrary(hMod);
}
return;
}
#endif
static void
ExpandUserRegs (PUCHAR sz)
{
PUCHAR szSearch = "$ux", szReg, szRegValue;
CHAR cs, cch, tempBuffer[512];
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);
}
}
static ULONG igrepSearchStartAddress = 0L;
static ULONG igrepLastPc;
static CHAR igrepLastPattern[256];
static void igrep (void)
{
ULONG dwNextGrepAddr;
ULONG dwCurrGrepAddr;
CHAR SourceLine[256];
BOOLEAN NewPc;
ULONG d;
PUCHAR pc = pchCommand;
PUCHAR Pattern;
PUCHAR Expression;
CHAR Symbol[64];
ULONG Displacement;
ULONG dwCurrentPc = Flat(GetRegPCValue());
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)tolower(*pc);
strcpy(igrepLastPattern,Pattern);
}
else {
Pattern = 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 (strstr(SourceLine, igrepLastPattern)) {
EXPRLastExpression = dwCurrGrepAddr;
igrepSearchStartAddress = dwNextGrepAddr;
GetSymbolRoutine((LPVOID)dwCurrGrepAddr, Symbol, &Displacement);
disasmRoutine(&dwCurrGrepAddr, SourceLine, FALSE);
dprintf("%s", SourceLine);
return;
}
#ifndef KERNEL
if (CheckControlC()) {
return;
}
#endif
dwCurrGrepAddr = dwNextGrepAddr;
d = disasmRoutine(&dwNextGrepAddr, SourceLine, FALSE);
}
}
void dprintAddr(NT_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, NT_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)++;
}
static SHORT lastSelector = -1;
static ULONG lastBaseOffset;
#ifdef MULTIMODE
void FormAddress (NT_PADDR paddr, ULONG seg, ULONG off)
{
paddr->seg = (USHORT)seg;
paddr->off = off;
if (fVm86) {
paddr->type = ADDR_V86;
ComputeFlatAddress(paddr, NULL);
}
else {
DESCRIPTOR_TABLE_ENTRY desc;
if (seg) {
desc.Selector = seg;
DbgKdLookupSelector((USHORT)0, &desc);
paddr->type = (UCHAR)desc.Descriptor.HighWord.Bits.Default_Big
? ADDR_1632 : ADDR_16;
}
else
paddr->type = ADDR_32;
ComputeFlatAddress(paddr, &desc);
}
}
void ComputeNativeAddress (NT_PADDR paddr)
{
switch (paddr->type & (~(FLAT_COMPUTED | INSTR_POINTER))) {
case ADDR_V86:
paddr->off = Flat(paddr) - ((ULONG)paddr->seg << 4);
if (paddr->off > 0xffff) {
ULONG excess = 1 + (paddr->off - 0xffffL) >> 4;
paddr->seg += 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((USHORT)0, &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;
case ADDR_32:
paddr->off = Flat(paddr);
break;
default:
return;
}
}
void ComputeFlatAddress (NT_PADDR paddr, PDESCRIPTOR_TABLE_ENTRY pdesc)
{
if (paddr->type&FLAT_COMPUTED)
return;
switch (paddr->type & (~INSTR_POINTER)) {
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((USHORT)0, 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;
case ADDR_32:
Flat(paddr) = paddr->off;
break;
default:
return;
}
paddr->type |= FLAT_COMPUTED;
}
NT_PADDR AddrAdd(NT_PADDR paddr, ULONG scalar)
{
// ASSERT(fFlat(paddr));
if (fnotFlat(paddr))
ComputeFlatAddress(paddr, NULL);
Flat(paddr) += scalar;
paddr->off += scalar;
return paddr;
}
NT_PADDR AddrSub(NT_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()
{
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.Maxmimum = 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;
}
#endif
#endif /* NT_SAPI */