/*** ntcmd.c - command processor for NT debugger * * Copyright 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 #include #ifndef NT_HOST #include #endif #include #include #include #include #include #include #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 "?" 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" 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" 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]" 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]" 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 * *************************************************************************/ 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] [""]". * * 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 "[~[]g[=][]*" 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 , 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 ", output only the register * if "r =", output only the register * and prompt for new value * if "r = " or "r ", * set to 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 ", 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 =", 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 = ", set the value if (UserRegTest(count)) { SetRegFlagValue(count, (ULONG)pchCommand); *pchCommand=0; } else SetRegFlagValue(count, GetExpression()); } else // if "r ", 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" 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" 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" 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" 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
" 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
" 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 "[~]p|t[=][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" 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" 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" 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" 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;searchindexpThreadHead; 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 */