//---------------------------------------------------------------------------- // // Abstraction of processor-specific information. // // Copyright (C) Microsoft Corporation, 1999-2001. // //---------------------------------------------------------------------------- #include "ntsdp.hpp" ULONG g_EffMachine = IMAGE_FILE_MACHINE_UNKNOWN; MachineIndex g_EffMachineIndex = MACHIDX_COUNT; MachineInfo* g_Machine = NULL; MachineInfo* g_TargetMachine; // Leave one extra slot at the end so indexing // MACHIDX_COUNT returns NULL for the undefined case. MachineInfo* g_AllMachines[MACHIDX_COUNT + 1]; // TRUE when symbol prefixing should be done. BOOL g_PrefixSymbols; // TRUE if context changed while processing BOOL g_ContextChanged; DEBUG_PROCESSOR_IDENTIFICATION_ALL g_InitProcessorId; //---------------------------------------------------------------------------- // // MachineInfo. // //---------------------------------------------------------------------------- HRESULT MachineInfo::InitializeConstants(void) { m_TraceMode = TRACE_NONE; m_ContextState = MCTX_NONE; m_ContextIsReadOnly = FALSE; m_NumberRegs = 0; // Every machine supports basic integer and FP registers. m_AllMaskBits = DEBUG_REGISTERS_ALL; m_SymPrefixLen = m_SymPrefix != NULL ? strlen(m_SymPrefix) : 0; ZeroMemory(m_PageDirectories, sizeof(m_PageDirectories)); m_Translating = FALSE; ULONG i; for (i = 0; i < SEGREG_COUNT; i++) { m_SegRegDesc[i].Flags = SEGDESC_INVALID; } return S_OK; } HRESULT MachineInfo::InitializeForTarget(void) { InitializeContextFlags(&m_Context, m_SverCanonicalContext); return S_OK; } HRESULT MachineInfo::InitializeForProcessor(void) { DBG_ASSERT(m_MaxDataBreakpoints <= MAX_DATA_BREAKS); // Count register definitions. RegisterGroup* Group; for (Group = m_Groups; Group != NULL; Group = Group->Next) { Group->NumberRegs = 0; REGDEF* Def = Group->Regs; while (Def->psz != NULL) { Group->NumberRegs++; Def++; } m_NumberRegs += Group->NumberRegs; REGALLDESC* Desc = Group->AllExtraDesc; if (Desc != NULL) { while (Desc->Bit != 0) { m_AllMaskBits |= Desc->Bit; Desc++; } } } return S_OK; } HRESULT MachineInfo::GetContextState(ULONG State) { if (g_RegContextThread == NULL) { // No error message here as this can get hit during // Reload("NT") initialization when accessing paged-out memory. // It's also noisy in other failure cases, so rely // on higher-level error output. return E_UNEXPECTED; } if (State == MCTX_DIRTY) { g_ContextChanged = TRUE; } if (m_ContextState >= State) { return S_OK; } HRESULT Status = E_UNEXPECTED; // Dump support is built into the Ud/Kd routines. if (IS_USER_TARGET()) { Status = UdGetContextState(State); } else if (IS_KERNEL_TARGET()) { Status = KdGetContextState(State); } if (Status != S_OK) { ErrOut("GetContextState failed, 0x%X\n", Status); return Status; } if (State == MCTX_DIRTY) { DBG_ASSERT(m_ContextState >= MCTX_FULL); m_ContextState = State; } DBG_ASSERT(State <= m_ContextState); return S_OK; } HRESULT MachineInfo::SetContext(void) { if (m_ContextState != MCTX_DIRTY) { // Nothing to write. return S_OK; } if (g_RegContextThread == NULL) { ErrOut("No current thread in SetContext\n"); return E_UNEXPECTED; } if (m_ContextIsReadOnly) { ErrOut("Context cannot be modified\n"); return E_UNEXPECTED; } HRESULT Status = E_UNEXPECTED; if (IS_DUMP_TARGET()) { ErrOut("Can't set dump file contexts\n"); return E_UNEXPECTED; } else if (IS_USER_TARGET()) { Status = UdSetContext(); } else if (IS_KERNEL_TARGET()) { Status = KdSetContext(); } if (Status != S_OK) { ErrOut("SetContext failed, 0x%X\n", Status); return Status; } // No longer dirty. m_ContextState = MCTX_FULL; return S_OK; } HRESULT MachineInfo::UdGetContextState(ULONG State) { // MCTX_CONTEXT and MCTX_FULL are the same in user mode. if (State >= MCTX_CONTEXT && m_ContextState < MCTX_FULL) { HRESULT Status = g_Target->GetContext(g_RegContextThread->Handle, &m_Context); if (Status != S_OK) { return Status; } Status = g_Target->GetTargetSegRegDescriptors (g_RegContextThread->Handle, 0, SEGREG_COUNT, m_SegRegDesc); if (Status != S_OK) { return Status; } m_ContextState = MCTX_FULL; } return S_OK; } HRESULT MachineInfo::UdSetContext(void) { return g_Target->SetContext(g_RegContextThread->Handle, &m_Context); } void MachineInfo::InvalidateContext(void) { m_ContextState = MCTX_NONE; g_Target->InvalidateTargetContext(); ULONG i; for (i = 0; i < SEGREG_COUNT; i++) { m_SegRegDesc[i].Flags = SEGDESC_INVALID; } } HRESULT MachineInfo::GetExdiContext(IUnknown* Exdi, PEXDI_CONTEXT Context) { return E_NOTIMPL; } HRESULT MachineInfo::SetExdiContext(IUnknown* Exdi, PEXDI_CONTEXT Context) { return E_NOTIMPL; } void MachineInfo::ConvertExdiContextFromContext(PCROSS_PLATFORM_CONTEXT Context, PEXDI_CONTEXT ExdiContext) { // Nothing to do. } void MachineInfo::ConvertExdiContextToContext(PEXDI_CONTEXT ExdiContext, PCROSS_PLATFORM_CONTEXT Context) { // Nothing to do. } void MachineInfo::ConvertExdiContextToSegDescs(PEXDI_CONTEXT ExdiContext, ULONG Start, ULONG Count, PDESCRIPTOR64 Descs) { // Nothing to do. } void MachineInfo::ConvertExdiContextFromSpecial (PCROSS_PLATFORM_KSPECIAL_REGISTERS Special, PEXDI_CONTEXT ExdiContext) { // Nothing to do. } void MachineInfo::ConvertExdiContextToSpecial (PEXDI_CONTEXT ExdiContext, PCROSS_PLATFORM_KSPECIAL_REGISTERS Special) { // Nothing to do. } ULONG MachineInfo::GetSegRegNum(ULONG SegReg) { return 0; } HRESULT MachineInfo::GetSegRegDescriptor(ULONG SegReg, PDESCRIPTOR64 Desc) { return E_UNEXPECTED; } void MachineInfo::KdUpdateControlSet(PDBGKD_ANY_CONTROL_SET ControlSet) { // Nothing to do. } void MachineInfo::KdSaveProcessorState( void ) { m_SavedContext = m_Context; m_SavedContextState = m_ContextState; memcpy(m_SavedSegRegDesc, m_SegRegDesc, sizeof(m_SegRegDesc)); m_ContextState = MCTX_NONE; g_Target->InvalidateTargetContext(); } void MachineInfo::KdRestoreProcessorState( void ) { DBG_ASSERT(m_ContextState != MCTX_DIRTY); m_Context = m_SavedContext; m_ContextState = m_SavedContextState; memcpy(m_SegRegDesc, m_SavedSegRegDesc, sizeof(m_SegRegDesc)); g_Target->InvalidateTargetContext(); } HRESULT MachineInfo::SetDefaultPageDirectories(ULONG Mask) { HRESULT Status; ULONG i; ULONG64 OldDirs[PAGE_DIR_COUNT]; memcpy(OldDirs, m_PageDirectories, sizeof(m_PageDirectories)); i = 0; while (i < PAGE_DIR_COUNT) { // Pass on the set to machine-specific code. if (Mask & (1 << i)) { if ((Status = SetPageDirectory(i, 0, &i)) != S_OK) { memcpy(m_PageDirectories, OldDirs, sizeof(m_PageDirectories)); return Status; } } else { i++; } } // Try and validate that the new kernel page directory is // valid by checking an address that should always // be available. if ((Mask & (1 << PAGE_DIR_KERNEL)) && IS_KERNEL_TARGET() && KdDebuggerData.PsLoadedModuleList) { LIST_ENTRY64 List; if ((Status = g_Target-> ReadListEntry(this, KdDebuggerData.PsLoadedModuleList, &List)) != S_OK) { // This page directory doesn't seem valid so restore // the previous setting and fail. memcpy(m_PageDirectories, OldDirs, sizeof(m_PageDirectories)); } } return Status; } HRESULT MachineInfo::NewBreakpoint(DebugClient* Client, ULONG Type, ULONG Id, Breakpoint** RetBp) { return E_NOINTERFACE; } void MachineInfo::InsertAllDataBreakpoints(void) { // Nothing to do. } void MachineInfo::RemoveAllDataBreakpoints(void) { // Nothing to do. } ULONG MachineInfo::IsBreakpointOrStepException(PEXCEPTION_RECORD64 Record, ULONG FirstChance, PADDR BpAddr, PADDR RelAddr) { return Record->ExceptionCode == STATUS_BREAKPOINT ? EXBS_BREAKPOINT_ANY : EXBS_NONE; } void MachineInfo::GetRetAddr(PADDR Addr) { DEBUG_STACK_FRAME StackFrame; if (StackTrace(0, 0, 0, &StackFrame, 1, 0, 0, FALSE) > 0) { ADDRFLAT(Addr, StackFrame.ReturnOffset); } else { ErrOut("StackTrace failed\n"); ADDRFLAT(Addr, 0); } } BOOL MachineInfo::GetPrefixedSymbolOffset(ULONG64 SymOffset, ULONG Flags, PULONG64 PrefixedSymOffset) { DBG_ASSERT(m_SymPrefix == NULL); // This routine should never be called since there's no prefix. return FALSE; } HRESULT MachineInfo::ReadDynamicFunctionTable(ULONG64 Table, PULONG64 NextTable, PULONG64 MinAddress, PULONG64 MaxAddress, PULONG64 BaseAddress, PULONG64 TableData, PULONG TableSize, PWSTR OutOfProcessDll, PCROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE RawTable) { // No dynamic function table support. return E_UNEXPECTED; } PVOID MachineInfo::FindDynamicFunctionEntry(PCROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE Table, ULONG64 Address, PVOID TableData, ULONG TableSize) { // No dynamic function tables so no match. return NULL; } void MachineInfo::FlushPerExecutionCaches(void) { ZeroMemory(m_PageDirectories, sizeof(m_PageDirectories)); m_Translating = FALSE; } void MachineInfo::FormAddr(ULONG SegOrReg, ULONG64 Off, ULONG Flags, PADDR Address) { PDESCRIPTOR64 SegDesc = NULL; DESCRIPTOR64 Desc; Address->off = Off; if (Flags & FORM_SEGREG) { ULONG SegRegNum = GetSegRegNum(SegOrReg); if (SegRegNum) { Address->seg = GetReg16(SegRegNum); } else { Address->seg = 0; } } else { Address->seg = (USHORT)SegOrReg; } if (Flags & FORM_VM86) { Address->type = ADDR_V86; } else if (Address->seg == 0) { // A segment wasn't used or segmentation doesn't exist. Address->type = ADDR_FLAT; } else { HRESULT Status; if (Flags & FORM_SEGREG) { Status = GetSegRegDescriptor(SegOrReg, &Desc); } else { Status = g_Target-> GetSelDescriptor(this, g_CurrentProcess->CurrentThread->Handle, SegOrReg, &Desc); } if (Status == S_OK) { static USHORT MainCodeSeg = 0; SegDesc = &Desc; if (((Flags & FORM_CODE) && (Desc.Flags & X86_DESC_LONG_MODE)) || ((Flags & FORM_CODE) == 0 && g_Amd64InCode64)) { Address->type = ADDR_1664; } else if (Desc.Flags & X86_DESC_DEFAULT_BIG) { Address->type = ADDR_1632; } else { Address->type = ADDR_16; } if ((Flags & FORM_CODE) && ((g_EffMachine == IMAGE_FILE_MACHINE_I386 && Address->type == ADDR_1632) || (g_EffMachine == IMAGE_FILE_MACHINE_AMD64 && Address->type == ADDR_1664))) { if ( MainCodeSeg == 0 ) { if ( Desc.Base == 0 ) { MainCodeSeg = Address->seg; } } if ( Address->seg == MainCodeSeg ) { Address->type = ADDR_FLAT; } } } else { Address->type = ADDR_16; } } ComputeFlatAddress(Address, SegDesc); } void MachineInfo::PrintStackFrameAddressesTitle(ULONG Flags) { if (!(Flags & DEBUG_STACK_FRAME_ADDRESSES_RA_ONLY)) { PrintMultiPtrTitle("Child-SP", 1); } PrintMultiPtrTitle("RetAddr", 1); } void MachineInfo::PrintStackFrameAddresses(ULONG Flags, PDEBUG_STACK_FRAME StackFrame) { if (!(Flags & DEBUG_STACK_FRAME_ADDRESSES_RA_ONLY)) { dprintf("%s ", FormatAddr64(StackFrame->StackOffset)); } dprintf("%s ", FormatAddr64(StackFrame->ReturnOffset)); } void MachineInfo::PrintStackArgumentsTitle(ULONG Flags) { dprintf(": "); PrintMultiPtrTitle("Args to Child", 4); dprintf(": "); } void MachineInfo::PrintStackArguments(ULONG Flags, PDEBUG_STACK_FRAME StackFrame) { dprintf(": %s %s %s %s : ", FormatAddr64(StackFrame->Params[0]), FormatAddr64(StackFrame->Params[1]), FormatAddr64(StackFrame->Params[2]), FormatAddr64(StackFrame->Params[3])); } void MachineInfo::PrintStackCallSiteTitle(ULONG Flags) { dprintf("Call Site"); } void MachineInfo::PrintStackCallSite(ULONG Flags, PDEBUG_STACK_FRAME StackFrame, CHAR SymBuf[], DWORD64 Displacement, USHORT StdCallArgs) { if (*SymBuf) { dprintf("%s", SymBuf); if (!(Flags & DEBUG_STACK_PARAMETERS) || !ShowFunctionParameters(StackFrame, SymBuf, Displacement)) { // We dont see the parameters } if (Displacement) { dprintf("+"); } } if (Displacement || !*SymBuf) { dprintf("0x%s", FormatDisp64(Displacement)); } } void MachineInfo::PrintStackNonvolatileRegisters(ULONG Flags, PDEBUG_STACK_FRAME StackFrame, PCROSS_PLATFORM_CONTEXT Context, ULONG FrameNum) { // Empty base implementation. } //---------------------------------------------------------------------------- // // Functions. // //---------------------------------------------------------------------------- HRESULT InitializeMachines(ULONG TargetMachine) { HRESULT Status; ULONG i; if (DbgKdApi64 != (g_SystemVersion > NT_SVER_NT4)) { WarnOut("Debug API version does not match system version\n"); } if (IsImageMachineType64(TargetMachine) && !DbgKdApi64) { WarnOut("64-bit machine not using 64-bit API\n"); } memset(g_AllMachines, 0, sizeof(g_AllMachines)); // There are several different X86 machines due to // the emulations available on various systems and CPUs. switch(TargetMachine) { case IMAGE_FILE_MACHINE_IA64: g_AllMachines[MACHIDX_I386] = &g_X86OnIa64Machine; break; default: g_AllMachines[MACHIDX_I386] = &g_X86Machine; break; } g_AllMachines[MACHIDX_ALPHA] = &g_Axp32Machine; g_AllMachines[MACHIDX_AXP64] = &g_Axp64Machine; g_AllMachines[MACHIDX_IA64] = &g_Ia64Machine; g_AllMachines[MACHIDX_AMD64] = &g_Amd64Machine; g_TargetMachineType = TargetMachine; g_TargetMachine = MachineTypeInfo(TargetMachine); ZeroMemory(&g_InitProcessorId, sizeof(g_InitProcessorId)); for (i = 0; i < MACHIDX_COUNT; i++) { DBG_ASSERT(g_AllMachines[i] != NULL); if ((Status = g_AllMachines[i]->InitializeConstants()) != S_OK) { return Status; } } if (!IS_TARGET_SET()) { return S_OK; } for (i = 0; i < MACHIDX_COUNT; i++) { if ((Status = g_AllMachines[i]->InitializeForTarget()) != S_OK) { return Status; } } if (g_TargetMachineType == IMAGE_FILE_MACHINE_UNKNOWN) { return S_OK; } // Get the base processor ID for determing what // kind of features a processor supports. The // assumption is that the processors in a machine // will be similar enough that retrieving this // for one processor is sufficient. // If this fails we continue on without a processor ID. if (!IS_DUMP_TARGET()) { g_Target->GetProcessorId(0, &g_InitProcessorId); } for (i = 0; i < MACHIDX_COUNT; i++) { if ((Status = g_AllMachines[i]->InitializeForProcessor()) != S_OK) { return Status; } } return S_OK; } void SetEffMachine(ULONG Machine, BOOL Notify) { BOOL Changed = g_EffMachine != Machine; if (Changed && g_EffMachine != IMAGE_FILE_MACHINE_UNKNOWN && g_EffMachine != g_TargetMachineType) { // If the previous machine was not the target machine // it may be an emulated machine that uses the // target machine's context. In that case we need to // make sure that any dirty registers it has get flushed // so that if the new effective machine is the target // machine it'll show changes due to changes through // the emulated machine. if (g_Machine->SetContext() != S_OK) { // Error already displayed. return; } } g_EffMachine = Machine; g_EffMachineIndex = MachineTypeIndex(Machine); DBG_ASSERT(g_EffMachineIndex <= MACHIDX_COUNT); g_Machine = g_AllMachines[g_EffMachineIndex]; if (Changed && Notify) { NotifyChangeEngineState(DEBUG_CES_EFFECTIVE_PROCESSOR, g_EffMachine, TRUE); } } MachineIndex MachineTypeIndex(ULONG Machine) { switch(Machine) { case IMAGE_FILE_MACHINE_I386: return MACHIDX_I386; case IMAGE_FILE_MACHINE_ALPHA: return MACHIDX_ALPHA; case IMAGE_FILE_MACHINE_AXP64: return MACHIDX_AXP64; case IMAGE_FILE_MACHINE_IA64: return MACHIDX_IA64; case IMAGE_FILE_MACHINE_AMD64: return MACHIDX_AMD64; default: return MACHIDX_COUNT; } } void CacheReportInstructions(ULONG64 Pc, ULONG Count, PUCHAR Stream) { // There was a long-standing bug in the kernel // where it didn't properly remove all breakpoints // present in the instruction stream reported to // the debugger. If this kernel suffers from the // problem just ignore the stream contents. if (Count == 0 || g_TargetBuildNumber < 2300) { return; } g_VirtualCache.Add(Pc, Stream, Count); } void FlushMachinePerExecutionCaches(void) { ULONG i; for (i = 0; i < MACHIDX_COUNT; i++) { g_AllMachines[i]->FlushPerExecutionCaches(); } } //---------------------------------------------------------------------------- // // Common code and constants. // //---------------------------------------------------------------------------- /* OutputHex - output hex value * * Purpose: * Output the value in outvalue into the buffer * pointed by *pBuf. The value may be signed * or unsigned depending on the value fSigned. * * Input: * outvalue - value to output * length - length in digits * fSigned - TRUE if signed else FALSE * * Output: * None. * ***********************************************************************/ UCHAR g_HexDigit[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; void MachineInfo::BufferHex ( ULONG64 outvalue, ULONG length, BOOL fSigned ) { UCHAR digit[32]; LONG index = 0; DBG_ASSERT(length <= 32); if (fSigned && (LONGLONG)outvalue < 0) { *m_Buf++ = '-'; outvalue = - (LONGLONG)outvalue; } do { digit[index++] = g_HexDigit[outvalue & 0xf]; outvalue >>= 4; } while ((fSigned && outvalue) || (!fSigned && index < (LONG)length)); while (--index >= 0) { *m_Buf++ = digit[index]; } } /* BlankFill - blank-fill buffer * * Purpose: * To fill the buffer at *pBuf with blanks until * position count is reached. * * Input: * None. * * Output: * None. * ***********************************************************************/ void MachineInfo::BufferBlanks(ULONG count) { do { *m_Buf++ = ' '; } while (m_Buf < m_BufStart + count); } /* OutputString - output string * * Purpose: * Copy the string into the buffer pointed by pBuf. * * Input: * *pStr - pointer to string * * Output: * None. * ***********************************************************************/ void MachineInfo::BufferString(PCSTR String) { while (*String) { *m_Buf++ = *String++; } } void MachineInfo::PrintMultiPtrTitle(const CHAR* Title, USHORT PtrNum) { size_t PtrLen = (strlen(FormatAddr64(0)) + 1) * PtrNum; size_t TitleLen = strlen(Title); if (PtrLen < TitleLen) { // Extremly rare case so keep it simple while slow for (size_t i = 0; i < PtrLen - 1; ++i) { dprintf("%c", Title[i]); } dprintf(" "); } else { dprintf(Title); if (PtrLen > TitleLen) { char Format[16]; _snprintf(Format, sizeof(Format) - 1, "%% %ds", PtrLen - TitleLen); dprintf(Format, ""); } } } CHAR g_F0[] = "f0"; CHAR g_F1[] = "f1"; CHAR g_F2[] = "f2"; CHAR g_F3[] = "f3"; CHAR g_F4[] = "f4"; CHAR g_F5[] = "f5"; CHAR g_F6[] = "f6"; CHAR g_F7[] = "f7"; CHAR g_F8[] = "f8"; CHAR g_F9[] = "f9"; CHAR g_F10[] = "f10"; CHAR g_F11[] = "f11"; CHAR g_F12[] = "f12"; CHAR g_F13[] = "f13"; CHAR g_F14[] = "f14"; CHAR g_F15[] = "f15"; CHAR g_F16[] = "f16"; CHAR g_F17[] = "f17"; CHAR g_F18[] = "f18"; CHAR g_F19[] = "f19"; CHAR g_F20[] = "f20"; CHAR g_F21[] = "f21"; CHAR g_F22[] = "f22"; CHAR g_F23[] = "f23"; CHAR g_F24[] = "f24"; CHAR g_F25[] = "f25"; CHAR g_F26[] = "f26"; CHAR g_F27[] = "f27"; CHAR g_F28[] = "f28"; CHAR g_F29[] = "f29"; CHAR g_F30[] = "f30"; CHAR g_F31[] = "f31"; CHAR g_R0[] = "r0"; CHAR g_R1[] = "r1"; CHAR g_R2[] = "r2"; CHAR g_R3[] = "r3"; CHAR g_R4[] = "r4"; CHAR g_R5[] = "r5"; CHAR g_R6[] = "r6"; CHAR g_R7[] = "r7"; CHAR g_R8[] = "r8"; CHAR g_R9[] = "r9"; CHAR g_R10[] = "r10"; CHAR g_R11[] = "r11"; CHAR g_R12[] = "r12"; CHAR g_R13[] = "r13"; CHAR g_R14[] = "r14"; CHAR g_R15[] = "r15"; CHAR g_R16[] = "r16"; CHAR g_R17[] = "r17"; CHAR g_R18[] = "r18"; CHAR g_R19[] = "r19"; CHAR g_R20[] = "r20"; CHAR g_R21[] = "r21"; CHAR g_R22[] = "r22"; CHAR g_R23[] = "r23"; CHAR g_R24[] = "r24"; CHAR g_R25[] = "r25"; CHAR g_R26[] = "r26"; CHAR g_R27[] = "r27"; CHAR g_R28[] = "r28"; CHAR g_R29[] = "r29"; CHAR g_R30[] = "r30"; CHAR g_R31[] = "r31";