diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 5f96d98b6e228d..18560e3df9b016 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -4886,7 +4886,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::Hijack(VMPTR_Thread vmThread, ULO // // The debugger could always re-enable the single-step flag if it wants to. #ifndef FEATURE_EMULATE_SINGLESTEP - UnsetSSFlag(reinterpret_cast(&ctx)); + UnsetSSFlag(&ctx); #endif // Push pointers @@ -5027,7 +5027,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::Hijack(VMPTR_Thread vmThread, ULO } // Return the filter CONTEXT on the LS. -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetManagedStoppedContext(VMPTR_Thread vmThread, OUT VMPTR_CONTEXT * pRetVal) +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetManagedStoppedContext(VMPTR_Thread vmThread, OUT CORDB_ADDRESS * pRetVal) { DD_ENTER_MAY_THROW; @@ -5035,13 +5035,13 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetManagedStoppedContext(VMPTR_Th EX_TRY { - VMPTR_CONTEXT vmContext = VMPTR_CONTEXT::NullPtr(); + CORDB_ADDRESS contextAddr = (CORDB_ADDRESS)NULL; Thread * pThread = vmThread.GetDacPtr(); if (pThread->GetInteropDebuggingHijacked()) { _ASSERTE(!ISREDIRECTEDTHREAD(pThread)); - vmContext = VMPTR_CONTEXT::NullPtr(); + contextAddr = (CORDB_ADDRESS)NULL; } else { @@ -5049,7 +5049,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetManagedStoppedContext(VMPTR_Th if (pLSContext != NULL) { _ASSERTE(!ISREDIRECTEDTHREAD(pThread)); - vmContext.SetHostPtr(pLSContext); + contextAddr = (CORDB_ADDRESS)PTR_HOST_TO_TADDR(pLSContext); } else if (ISREDIRECTEDTHREAD(pThread)) { @@ -5058,12 +5058,12 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetManagedStoppedContext(VMPTR_Th if (pLSContext != NULL) { - vmContext.SetHostPtr(pLSContext); + contextAddr = (CORDB_ADDRESS)PTR_HOST_TO_TADDR(pLSContext); } } } - *pRetVal = vmContext; + *pRetVal = contextAddr; } EX_CATCH_HRESULT(hr); return hr; @@ -5420,15 +5420,20 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDebuggerControlBlockAddress(OU } // DacDbi API: Get the context for a particular thread of the target process -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pContextBuffer) +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, ContextBuffer contextBuffer) { DD_ENTER_MAY_THROW + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < sizeof(DT_CONTEXT))) + { + return E_INVALIDARG; + } + HRESULT hr = S_OK; EX_TRY { - _ASSERTE(pContextBuffer != NULL); + DT_CONTEXT * pContextBuffer = reinterpret_cast(contextBuffer.pContextBytes); Thread * pThread = vmThread.GetDacPtr(); @@ -5509,6 +5514,714 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, return hr; } +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetTargetContextSize(ULONG32 contextFlags, OUT ULONG32 * pSize) +{ + DD_ENTER_MAY_THROW; + + if (pSize == NULL) + return E_INVALIDARG; + +#if defined(DT_CONTEXT_EXTENDED_REGISTERS) + if ((contextFlags & DT_CONTEXT_EXTENDED_REGISTERS) != DT_CONTEXT_EXTENDED_REGISTERS) + { + *pSize = offsetof(DT_CONTEXT, ExtendedRegisters); + } + else +#endif + { + *pSize = sizeof(DT_CONTEXT); + } + + return S_OK; +} + +static UINT_PTR * GetRegisterSlotFromContext(BYTE * ctxBuf, CorDebugRegister reg) +{ + DT_CONTEXT * pCtx = (DT_CONTEXT *)ctxBuf; + +#if defined(TARGET_X86) + switch (reg) + { + case REGISTER_INSTRUCTION_POINTER: return (UINT_PTR *)&pCtx->Eip; + case REGISTER_STACK_POINTER: return (UINT_PTR *)&pCtx->Esp; + case REGISTER_FRAME_POINTER: return (UINT_PTR *)&pCtx->Ebp; + case REGISTER_X86_EAX: return (UINT_PTR *)&pCtx->Eax; + case REGISTER_X86_ECX: return (UINT_PTR *)&pCtx->Ecx; + case REGISTER_X86_EDX: return (UINT_PTR *)&pCtx->Edx; + case REGISTER_X86_EBX: return (UINT_PTR *)&pCtx->Ebx; + case REGISTER_X86_ESI: return (UINT_PTR *)&pCtx->Esi; + case REGISTER_X86_EDI: return (UINT_PTR *)&pCtx->Edi; + default: return NULL; + } +#elif defined(TARGET_AMD64) + switch (reg) + { + case REGISTER_INSTRUCTION_POINTER: return (UINT_PTR *)&pCtx->Rip; + case REGISTER_STACK_POINTER: return (UINT_PTR *)&pCtx->Rsp; + case REGISTER_AMD64_RBP: return (UINT_PTR *)&pCtx->Rbp; + case REGISTER_AMD64_RAX: return (UINT_PTR *)&pCtx->Rax; + case REGISTER_AMD64_RCX: return (UINT_PTR *)&pCtx->Rcx; + case REGISTER_AMD64_RDX: return (UINT_PTR *)&pCtx->Rdx; + case REGISTER_AMD64_RBX: return (UINT_PTR *)&pCtx->Rbx; + case REGISTER_AMD64_RSI: return (UINT_PTR *)&pCtx->Rsi; + case REGISTER_AMD64_RDI: return (UINT_PTR *)&pCtx->Rdi; + case REGISTER_AMD64_R8: return (UINT_PTR *)&pCtx->R8; + case REGISTER_AMD64_R9: return (UINT_PTR *)&pCtx->R9; + case REGISTER_AMD64_R10: return (UINT_PTR *)&pCtx->R10; + case REGISTER_AMD64_R11: return (UINT_PTR *)&pCtx->R11; + case REGISTER_AMD64_R12: return (UINT_PTR *)&pCtx->R12; + case REGISTER_AMD64_R13: return (UINT_PTR *)&pCtx->R13; + case REGISTER_AMD64_R14: return (UINT_PTR *)&pCtx->R14; + case REGISTER_AMD64_R15: return (UINT_PTR *)&pCtx->R15; + default: return NULL; + } +#elif defined(TARGET_ARM) + switch (reg) + { + case REGISTER_INSTRUCTION_POINTER: return (UINT_PTR *)&pCtx->Pc; + case REGISTER_STACK_POINTER: return (UINT_PTR *)&pCtx->Sp; + case REGISTER_ARM_R0: return (UINT_PTR *)&pCtx->R0; + case REGISTER_ARM_R1: return (UINT_PTR *)&pCtx->R1; + case REGISTER_ARM_R2: return (UINT_PTR *)&pCtx->R2; + case REGISTER_ARM_R3: return (UINT_PTR *)&pCtx->R3; + case REGISTER_ARM_R4: return (UINT_PTR *)&pCtx->R4; + case REGISTER_ARM_R5: return (UINT_PTR *)&pCtx->R5; + case REGISTER_ARM_R6: return (UINT_PTR *)&pCtx->R6; + case REGISTER_ARM_R7: return (UINT_PTR *)&pCtx->R7; + case REGISTER_ARM_R8: return (UINT_PTR *)&pCtx->R8; + case REGISTER_ARM_R9: return (UINT_PTR *)&pCtx->R9; + case REGISTER_ARM_R10: return (UINT_PTR *)&pCtx->R10; + case REGISTER_ARM_R11: return (UINT_PTR *)&pCtx->R11; + case REGISTER_ARM_R12: return (UINT_PTR *)&pCtx->R12; + case REGISTER_ARM_LR: return (UINT_PTR *)&pCtx->Lr; + default: return NULL; + } +#elif defined(TARGET_ARM64) + switch (reg) + { + case REGISTER_INSTRUCTION_POINTER: return (UINT_PTR *)&pCtx->Pc; + case REGISTER_STACK_POINTER: return (UINT_PTR *)&pCtx->Sp; + case REGISTER_ARM64_FP: return (UINT_PTR *)&pCtx->Fp; + case REGISTER_ARM64_LR: return (UINT_PTR *)&pCtx->Lr; + default: + if (reg >= REGISTER_ARM64_X0 && reg <= REGISTER_ARM64_X28) + return (UINT_PTR *)&pCtx->X[reg - REGISTER_ARM64_X0]; + return NULL; + } +#elif defined(TARGET_LOONGARCH64) + switch (reg) + { + case REGISTER_INSTRUCTION_POINTER: return (UINT_PTR *)&pCtx->Pc; + case REGISTER_STACK_POINTER: return (UINT_PTR *)&pCtx->Sp; + case REGISTER_LOONGARCH64_FP: return (UINT_PTR *)&pCtx->Fp; + case REGISTER_LOONGARCH64_RA: return (UINT_PTR *)&pCtx->Ra; + case REGISTER_LOONGARCH64_TP: return (UINT_PTR *)&pCtx->Tp; + case REGISTER_LOONGARCH64_X0: return (UINT_PTR *)&pCtx->X0; + default: + if (reg >= REGISTER_LOONGARCH64_A0 && reg <= REGISTER_LOONGARCH64_A7) + return (UINT_PTR *)&pCtx->A0 + (reg - REGISTER_LOONGARCH64_A0); + if (reg >= REGISTER_LOONGARCH64_T0 && reg <= REGISTER_LOONGARCH64_T8) + return (UINT_PTR *)&pCtx->T0 + (reg - REGISTER_LOONGARCH64_T0); + if (reg >= REGISTER_LOONGARCH64_S0 && reg <= REGISTER_LOONGARCH64_S8) + return (UINT_PTR *)&pCtx->S0 + (reg - REGISTER_LOONGARCH64_S0); + return NULL; + } +#elif defined(TARGET_RISCV64) + switch (reg) + { + case REGISTER_INSTRUCTION_POINTER: return (UINT_PTR *)&pCtx->Pc; + case REGISTER_STACK_POINTER: return (UINT_PTR *)&pCtx->Sp; + case REGISTER_RISCV64_FP: return (UINT_PTR *)&pCtx->Fp; + case REGISTER_RISCV64_RA: return (UINT_PTR *)&pCtx->Ra; + case REGISTER_RISCV64_GP: return (UINT_PTR *)&pCtx->Gp; + case REGISTER_RISCV64_TP: return (UINT_PTR *)&pCtx->Tp; + case REGISTER_RISCV64_S1: return (UINT_PTR *)&pCtx->S1; + default: + if (reg >= REGISTER_RISCV64_T0 && reg <= REGISTER_RISCV64_T2) + return (UINT_PTR *)&pCtx->T0 + (reg - REGISTER_RISCV64_T0); + if (reg >= REGISTER_RISCV64_A0 && reg <= REGISTER_RISCV64_A7) + return (UINT_PTR *)&pCtx->A0 + (reg - REGISTER_RISCV64_A0); + if (reg >= REGISTER_RISCV64_S2 && reg <= REGISTER_RISCV64_S11) + return (UINT_PTR *)&pCtx->S2 + (reg - REGISTER_RISCV64_S2); + if (reg >= REGISTER_RISCV64_T3 && reg <= REGISTER_RISCV64_T6) + return (UINT_PTR *)&pCtx->T3 + (reg - REGISTER_RISCV64_T3); + return NULL; + } +#else + return NULL; +#endif +} + +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::WriteRegistersToContext( + IN ContextBuffer contextBuffer, + IN const CorDebugRegister * regs, + IN ULONG32 nRegs, + IN const TADDR * values) +{ + DD_ENTER_MAY_THROW; + + if (contextBuffer.pContextBytes == NULL || (nRegs > 0 && (regs == NULL || values == NULL))) + return E_INVALIDARG; + if (contextBuffer.contextSize < sizeof(DT_CONTEXT)) + return E_INVALIDARG; + + for (ULONG32 i = 0; i < nRegs; i++) + { + // Registers with no CONTEXT slot (e.g. float / SIMD registers) are skipped. + UINT_PTR * pSlot = GetRegisterSlotFromContext(contextBuffer.pContextBytes, regs[i]); + if (pSlot != NULL) + *pSlot = (UINT_PTR)values[i]; + } + return S_OK; +} + +namespace +{ + bool TryReadFloatRegisterFromContext(BYTE * ctxBuf, CorDebugRegister reg, DOUBLE * pValue); +} + +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::ReadRegistersFromContext( + IN ContextBuffer contextBuffer, + IN const CorDebugRegister * regs, + IN ULONG32 nRegs, + OUT CORDB_REGISTER * pValues) +{ + DD_ENTER_MAY_THROW; + + if (contextBuffer.pContextBytes == NULL || (nRegs > 0 && (regs == NULL || pValues == NULL))) + return E_INVALIDARG; + if (contextBuffer.contextSize < sizeof(DT_CONTEXT)) + return E_INVALIDARG; + + for (ULONG32 i = 0; i < nRegs; i++) + { + UINT_PTR * pSlot = GetRegisterSlotFromContext(contextBuffer.pContextBytes, regs[i]); + if (pSlot != NULL) + { + pValues[i] = (CORDB_REGISTER)(UINT_PTR)*pSlot; + continue; + } + + DOUBLE floatValue = 0.0; + if (TryReadFloatRegisterFromContext(contextBuffer.pContextBytes, regs[i], &floatValue)) + memcpy(&pValues[i], &floatValue, sizeof(CORDB_REGISTER)); + else + pValues[i] = (CORDB_REGISTER)0; + } + return S_OK; +} + +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetAvailableRegistersMask( + IN BOOL fActive, + IN BOOL fQuickUnwind, + IN ULONG32 regCount, + OUT BYTE pAvailable[]) +{ + DD_ENTER_MAY_THROW; + + if (pAvailable == NULL || regCount == 0) + return E_INVALIDARG; + + ZeroMemory(pAvailable, regCount); + + auto setBit = [pAvailable, regCount](int reg) + { + ULONG32 byteIdx = (ULONG32)reg / 8; + if (byteIdx < regCount) + pAvailable[byteIdx] |= (BYTE)(1 << (reg % 8)); + }; + +#if defined(TARGET_X86) + setBit(REGISTER_INSTRUCTION_POINTER); + setBit(REGISTER_STACK_POINTER); + setBit(REGISTER_FRAME_POINTER); + if (!fQuickUnwind || fActive) + { + setBit(REGISTER_X86_EAX); + setBit(REGISTER_X86_ECX); + setBit(REGISTER_X86_EDX); + setBit(REGISTER_X86_EBX); + setBit(REGISTER_X86_ESI); + setBit(REGISTER_X86_EDI); + } + if (fActive) + { + for (int r = REGISTER_X86_FPSTACK_0; r <= REGISTER_X86_FPSTACK_7; r++) + setBit(r); + } +#elif defined(TARGET_AMD64) + setBit(REGISTER_INSTRUCTION_POINTER); + setBit(REGISTER_STACK_POINTER); + if (!fQuickUnwind || fActive) + { + setBit(REGISTER_AMD64_RBP); + setBit(REGISTER_AMD64_RAX); + setBit(REGISTER_AMD64_RCX); + setBit(REGISTER_AMD64_RDX); + setBit(REGISTER_AMD64_RBX); + setBit(REGISTER_AMD64_RSI); + setBit(REGISTER_AMD64_RDI); + for (int r = REGISTER_AMD64_R8; r <= REGISTER_AMD64_R15; r++) + setBit(r); + } + if (fActive) + { + for (int r = REGISTER_AMD64_XMM0; r <= REGISTER_AMD64_XMM15; r++) + setBit(r); + } +#elif defined(TARGET_ARM) + UNREFERENCED_PARAMETER(fActive); + UNREFERENCED_PARAMETER(fQuickUnwind); + setBit(REGISTER_INSTRUCTION_POINTER); + setBit(REGISTER_STACK_POINTER); + for (int r = REGISTER_ARM_R0; r <= REGISTER_ARM_R12; r++) + setBit(r); + setBit(REGISTER_ARM_LR); +#elif defined(TARGET_ARM64) + UNREFERENCED_PARAMETER(fActive); + UNREFERENCED_PARAMETER(fQuickUnwind); + setBit(REGISTER_ARM64_PC); + setBit(REGISTER_ARM64_SP); + setBit(REGISTER_ARM64_FP); + setBit(REGISTER_ARM64_LR); + for (int r = REGISTER_ARM64_X0; r <= REGISTER_ARM64_X28; r++) + setBit(r); + for (int r = REGISTER_ARM64_V0; r <= REGISTER_ARM64_V31; r++) + setBit(r); +#elif defined(TARGET_LOONGARCH64) + UNREFERENCED_PARAMETER(fActive); + UNREFERENCED_PARAMETER(fQuickUnwind); + setBit(REGISTER_LOONGARCH64_PC); + setBit(REGISTER_LOONGARCH64_SP); + setBit(REGISTER_LOONGARCH64_FP); + setBit(REGISTER_LOONGARCH64_RA); + setBit(REGISTER_LOONGARCH64_TP); + setBit(REGISTER_LOONGARCH64_X0); + for (int r = REGISTER_LOONGARCH64_A0; r <= REGISTER_LOONGARCH64_A7; r++) + setBit(r); + for (int r = REGISTER_LOONGARCH64_T0; r <= REGISTER_LOONGARCH64_T8; r++) + setBit(r); + for (int r = REGISTER_LOONGARCH64_S0; r <= REGISTER_LOONGARCH64_S8; r++) + setBit(r); + for (int r = REGISTER_LOONGARCH64_F0; r <= REGISTER_LOONGARCH64_F31; r++) + setBit(r); +#elif defined(TARGET_RISCV64) + UNREFERENCED_PARAMETER(fActive); + UNREFERENCED_PARAMETER(fQuickUnwind); + setBit(REGISTER_RISCV64_PC); + setBit(REGISTER_RISCV64_RA); + setBit(REGISTER_RISCV64_SP); + setBit(REGISTER_RISCV64_GP); + setBit(REGISTER_RISCV64_TP); + setBit(REGISTER_RISCV64_FP); + setBit(REGISTER_RISCV64_S1); + for (int r = REGISTER_RISCV64_T0; r <= REGISTER_RISCV64_T2; r++) + setBit(r); + for (int r = REGISTER_RISCV64_A0; r <= REGISTER_RISCV64_A7; r++) + setBit(r); + for (int r = REGISTER_RISCV64_S2; r <= REGISTER_RISCV64_S11; r++) + setBit(r); + for (int r = REGISTER_RISCV64_T3; r <= REGISTER_RISCV64_T6; r++) + setBit(r); + for (int r = REGISTER_RISCV64_F0; r <= REGISTER_RISCV64_F31; r++) + setBit(r); +#else + UNREFERENCED_PARAMETER(fActive); + UNREFERENCED_PARAMETER(fQuickUnwind); + return E_NOTIMPL; +#endif + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::ContextHasExtendedRegisters( + IN ContextBuffer contextBuffer, + OUT BOOL * pResult) +{ + DD_ENTER_MAY_THROW; + + if (contextBuffer.pContextBytes == NULL || pResult == NULL) + return E_INVALIDARG; + if (contextBuffer.contextSize < sizeof(DT_CONTEXT)) + return E_INVALIDARG; + + *pResult = FALSE; +#if defined(DT_CONTEXT_EXTENDED_REGISTERS) + DT_CONTEXT * pCtx = (DT_CONTEXT *)contextBuffer.pContextBytes; + *pResult = (pCtx->ContextFlags & DT_CONTEXT_EXTENDED_REGISTERS) == DT_CONTEXT_EXTENDED_REGISTERS; +#endif + return S_OK; +} + +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::CompareControlRegisters( + IN ContextBuffer contextBuffer1, + IN ContextBuffer contextBuffer2, + OUT BOOL * pResult) +{ + DD_ENTER_MAY_THROW; + + if (contextBuffer1.pContextBytes == NULL || contextBuffer2.pContextBytes == NULL || pResult == NULL) + return E_INVALIDARG; + if (contextBuffer1.contextSize < sizeof(DT_CONTEXT) || contextBuffer2.contextSize < sizeof(DT_CONTEXT)) + return E_INVALIDARG; + + *pResult = ::CompareControlRegisters( + reinterpret_cast(contextBuffer1.pContextBytes), + reinterpret_cast(contextBuffer2.pContextBytes)); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::CopyContext( + IN ContextBuffer destinationContext, + IN ContextBuffer sourceContext, + IN ContextCopyMode copyMode, + IN ULONG32 flags) +{ + DD_ENTER_MAY_THROW; + + if (destinationContext.pContextBytes == NULL || sourceContext.pContextBytes == NULL) + return E_INVALIDARG; + + // The chunk-wise copy below only touches the CONTEXT regions selected by the + // ContextFlags. + if (!CheckContextSizeForBuffer(sourceContext.contextSize, sourceContext.pContextBytes)) + return E_INVALIDARG; + if (copyMode != kCopyContextUseExplicitFlags && flags != 0) + return E_INVALIDARG; + + switch (copyMode) + { + case kCopyContextPreserveDestinationFlags: + if (!CheckContextSizeForBuffer(destinationContext.contextSize, destinationContext.pContextBytes)) + return E_INVALIDARG; + break; + + case kCopyContextMergeSourceFlags: + { + if (!CheckContextSizeForBuffer(destinationContext.contextSize, destinationContext.pContextBytes)) + return E_INVALIDARG; + + DT_CONTEXT * pDestination = reinterpret_cast(destinationContext.pContextBytes); + const DT_CONTEXT * pSource = reinterpret_cast(sourceContext.pContextBytes); + DWORD mergedFlags = pDestination->ContextFlags | pSource->ContextFlags; + if (!CheckContextSizeForFlags(destinationContext.contextSize, mergedFlags)) + return E_INVALIDARG; + pDestination->ContextFlags = mergedFlags; + break; + } + + case kCopyContextUseExplicitFlags: + if (!CheckContextSizeForFlags(destinationContext.contextSize, flags)) + return E_INVALIDARG; + reinterpret_cast(destinationContext.pContextBytes)->ContextFlags = flags; + break; + + default: + return E_INVALIDARG; + } + + CORDbgCopyThreadContext( + destinationContext.pContextBytes, destinationContext.contextSize, + sourceContext.pContextBytes, sourceContext.contextSize); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::ConvertJitRegNumToCorDebugRegister( + IN ULONG32 jitRegNum, + OUT CorDebugRegister * pReg) +{ + DD_ENTER_MAY_THROW; + + if (pReg == NULL) + return E_INVALIDARG; + if (jitRegNum >= ARRAY_SIZE(g_JITToCorDbgReg)) + return E_INVALIDARG; + + *pReg = ConvertRegNumToCorDebugRegister(static_cast(jitRegNum)); + return S_OK; +} + +namespace +{ + double BitsToDouble(UINT64 bits) + { + double d; + memcpy(&d, &bits, sizeof(double)); + return d; + } + + // Convert an x87 80-bit double-extended value to IEEE-754 binary64 with + // round-to-nearest-even, matching the hardware FSTP conversion the legacy + // x86 path used (gradual underflow to subnormals, overflow to infinity). + // Layout (Intel SDM Vol 1 sec 8.2.2): bytes 0-7 = 64-bit significand with an + // explicit integer bit at 63; bytes 8-9 = 15-bit biased exponent (bias + // 16383) in bits 0-14 and the sign in bit 15. + double X87ExtendedToDouble(const BYTE * slot10) + { + UINT64 significand = 0; + memcpy(&significand, slot10, sizeof(UINT64)); + UINT16 signExp = 0; + memcpy(&signExp, slot10 + 8, sizeof(UINT16)); + + UINT64 sign = (UINT64)((signExp >> 15) & 1) << 63; + UINT32 exp80 = (UINT32)(signExp & 0x7FFF); + + if (exp80 == 0x7FFF) + { + UINT64 frac = significand & ~(1ULL << 63); + if (frac == 0) + return BitsToDouble(sign | ((UINT64)0x7FF << 52)); + // Force a quiet NaN (keeping the high payload bits) so a signaling + // NaN is never misencoded as an infinity. + UINT64 payload = (frac >> 12) & ((1ULL << 51) - 1); + return BitsToDouble(sign | ((UINT64)0x7FF << 52) | (1ULL << 51) | payload); + } + + // Zero, extended denormals and unnormals all fall below the binary64 + // subnormal floor (2^-1074) and map to signed zero. + if (exp80 == 0 || (significand & (1ULL << 63)) == 0) + return BitsToDouble(sign); + + int expD = (int)exp80 - 16383 + 1023; // pre-rounding binary64 biased exponent + + if (expD >= 0x7FF) + return BitsToDouble(sign | ((UINT64)0x7FF << 52)); // overflow -> infinity + + if (expD > 0) + { + // Normal: keep the top 52 fraction bits and round the low 11 to even. + UINT64 frac = significand & ~(1ULL << 63); // 63 fraction bits + UINT64 result = frac >> 11; // top 52 bits + UINT64 discarded = frac & 0x7FF; // low 11 bits + if (discarded > 0x400 || (discarded == 0x400 && (result & 1))) + { + result++; + if (result >> 52) // carry out of the fraction + { + result = 0; + expD++; + if (expD >= 0x7FF) + return BitsToDouble(sign | ((UINT64)0x7FF << 52)); + } + } + return BitsToDouble(sign | ((UINT64)expD << 52) | (result & ((1ULL << 52) - 1))); + } + + // Subnormal / underflow (expD <= 0): right-shift the significand by + // (12 - expD) and round to nearest-even. A carry into bit 52 promotes the + // value to the smallest normal, which the encoding produces for free. + int shift = 12 - expD; // >= 12 + if (shift > 64) // below half the smallest subnormal + return BitsToDouble(sign); + + UINT64 result; + UINT64 discarded; + UINT64 half; + if (shift == 64) + { + result = 0; + discarded = significand; + half = 1ULL << 63; + } + else + { + result = significand >> shift; + discarded = significand & ((1ULL << shift) - 1); + half = 1ULL << (shift - 1); + } + if (discarded > half || (discarded == half && (result & 1))) + result++; + return BitsToDouble(sign | result); + } + + UINT64 DoubleToBits(double d) + { + UINT64 bits; + memcpy(&bits, &d, sizeof(UINT64)); + return bits; + } + + void X87DoubleToExtended(double value, BYTE * slot10) + { + UINT64 bits = DoubleToBits(value); + UINT64 sign = (bits >> 63) & 1; + UINT32 exp = (UINT32)((bits >> 52) & 0x7FF); + UINT64 frac = bits & ((1ULL << 52) - 1); + + UINT64 mant; // 64-bit significand with explicit integer bit at 63 + UINT16 signExp; // sign (bit 15) + 15-bit biased exponent + + if (exp == 0x7FF) + { + // Infinity or NaN: max exponent, integer bit set. Preserve the NaN + // payload (shifted into the extended fraction) and force it quiet. + mant = (1ULL << 63) | (frac << 11); + if (frac != 0) + mant |= (1ULL << 62); // quiet bit + signExp = (UINT16)((sign << 15) | 0x7FFF); + } + else if (exp == 0 && frac == 0) + { + // Signed zero. + mant = 0; + signExp = (UINT16)(sign << 15); + } + else if (exp == 0) + { + // Binary64 subnormal: normalize into the wider extended exponent range. + // value = frac * 2^(-1074); shift the leading fraction bit up to bit 63. + int leadingZeros = 0; + UINT64 m = frac; + while ((m & (1ULL << 51)) == 0) + { + m <<= 1; + leadingZeros++; + } + mant = m << 12; // integer bit lands at 63 + int e = -1023 - leadingZeros + 16383; // rebias to extended + signExp = (UINT16)((sign << 15) | (UINT32)(e & 0x7FFF)); + } + else + { + // Normal: prepend the implicit integer bit, align to bit 63, rebias. + mant = ((1ULL << 52) | frac) << 11; + int e = (int)exp - 1023 + 16383; + signExp = (UINT16)((sign << 15) | (UINT32)(e & 0x7FFF)); + } + + memcpy(slot10, &mant, sizeof(UINT64)); + memcpy(slot10 + 8, &signExp, sizeof(UINT16)); + } + + // Read a single floating-point / SIMD register from a target CONTEXT buffer. + bool TryReadFloatRegisterFromContext(BYTE * ctxBuf, CorDebugRegister reg, DOUBLE * pValue) + { + DT_CONTEXT * pCtx = (DT_CONTEXT *)ctxBuf; + +#if defined(TARGET_X86) + if ((int)reg < REGISTER_X86_FPSTACK_0 || (int)reg > REGISTER_X86_FPSTACK_7) + return false; + + const DT_FLOATING_SAVE_AREA * pFp = &pCtx->FloatSave; + ULONG32 rawTop = (pFp->StatusWord >> 11) & 0x7; + ULONG32 floatStackTop = 7 - rawTop; + ULONG32 logical = (ULONG32)((int)reg - REGISTER_X86_FPSTACK_0); + + // The availability mask exposes all 8 FPSTACK registers even when the + // live stack is shallower; those out-of-depth slots read 0. + if (logical > floatStackTop) + { + *pValue = 0.0; + return true; + } + + // REGISTER_X86_FPSTACK_0 names the bottom of the logical stack, so a + // logical index counts up from the bottom: ST(i) with i = top - logical. + ULONG32 physIdx = (rawTop + (floatStackTop - logical)) & 0x7; + *pValue = X87ExtendedToDouble((const BYTE *)pFp->RegisterArea + physIdx * 10); + return true; +#elif defined(TARGET_AMD64) + if ((int)reg < REGISTER_AMD64_XMM0 || (int)reg > REGISTER_AMD64_XMM0 + 15) + return false; + memcpy(pValue, (const BYTE *)&pCtx->Xmm0 + (ULONG32)((int)reg - REGISTER_AMD64_XMM0) * 16, sizeof(DOUBLE)); + return true; +#elif defined(TARGET_ARM64) + if ((int)reg < REGISTER_ARM64_V0 || (int)reg > REGISTER_ARM64_V0 + 31) + return false; + memcpy(pValue, (const BYTE *)&pCtx->V + (ULONG32)((int)reg - REGISTER_ARM64_V0) * 16, sizeof(DOUBLE)); + return true; +#elif defined(TARGET_LOONGARCH64) + if ((int)reg < REGISTER_LOONGARCH64_F0 || (int)reg > REGISTER_LOONGARCH64_F0 + 31) + return false; + memcpy(pValue, (const BYTE *)&pCtx->F + (ULONG32)((int)reg - REGISTER_LOONGARCH64_F0) * 32, sizeof(DOUBLE)); + return true; +#elif defined(TARGET_RISCV64) + if ((int)reg < REGISTER_RISCV64_F0 || (int)reg > REGISTER_RISCV64_F0 + 31) + return false; + memcpy(pValue, (const BYTE *)&pCtx->F + (ULONG32)((int)reg - REGISTER_RISCV64_F0) * 8, sizeof(DOUBLE)); + return true; +#elif defined(TARGET_ARM) + if ((int)reg < REGISTER_ARM_D0 || (int)reg > REGISTER_ARM_D0 + 31) + return false; + memcpy(pValue, (const BYTE *)&pCtx->D + (ULONG32)((int)reg - REGISTER_ARM_D0) * 8, sizeof(DOUBLE)); + return true; +#else + UNREFERENCED_PARAMETER(pCtx); + UNREFERENCED_PARAMETER(reg); + UNREFERENCED_PARAMETER(pValue); + return false; +#endif + } +} + +// Write a single floating-point / SIMD register into a target CONTEXT buffer. +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::WriteFloatRegisterToContext( + IN ContextBuffer contextBuffer, + IN CorDebugRegister reg, + IN const BYTE * pValue, + IN ULONG32 valueSize) +{ + DD_ENTER_MAY_THROW; + + if (contextBuffer.pContextBytes == NULL || pValue == NULL || + (valueSize != sizeof(float) && valueSize != sizeof(double))) + { + return E_INVALIDARG; + } + if (contextBuffer.contextSize < sizeof(DT_CONTEXT)) + return E_INVALIDARG; + + DT_CONTEXT * pCtx = reinterpret_cast(contextBuffer.pContextBytes); + +#if defined(TARGET_X86) + if ((int)reg < REGISTER_X86_FPSTACK_0 || (int)reg > REGISTER_X86_FPSTACK_7) + return E_INVALIDARG; + + DT_FLOATING_SAVE_AREA * pFp = &pCtx->FloatSave; + ULONG32 rawTop = (pFp->StatusWord >> 11) & 0x7; + ULONG32 floatStackTop = 7 - rawTop; + ULONG32 logical = (ULONG32)((int)reg - REGISTER_X86_FPSTACK_0); + if (logical > floatStackTop) + return E_INVALIDARG; + + // REGISTER_X86_FPSTACK_0 names the bottom of the logical stack: ST(i) with + // i = top - logical. Map to the physical RegisterArea slot and re-encode. + ULONG32 physIdx = (rawTop + (floatStackTop - logical)) & 0x7; + BYTE * slot = (BYTE *)pFp->RegisterArea + physIdx * 10; + double d = (valueSize == sizeof(float)) ? (double)(*(const float *)pValue) + : *(const double *)pValue; + X87DoubleToExtended(d, slot); +#elif defined(TARGET_AMD64) + if ((int)reg < REGISTER_AMD64_XMM0 || (int)reg > REGISTER_AMD64_XMM0 + 15) + return E_INVALIDARG; + memcpy((BYTE *)&pCtx->Xmm0 + (ULONG32)((int)reg - REGISTER_AMD64_XMM0) * 16, pValue, valueSize); +#elif defined(TARGET_ARM64) + if ((int)reg < REGISTER_ARM64_V0 || (int)reg > REGISTER_ARM64_V0 + 31) + return E_INVALIDARG; + memcpy((BYTE *)&pCtx->V + (ULONG32)((int)reg - REGISTER_ARM64_V0) * 16, pValue, valueSize); +#elif defined(TARGET_LOONGARCH64) + if ((int)reg < REGISTER_LOONGARCH64_F0 || (int)reg > REGISTER_LOONGARCH64_F0 + 31) + return E_INVALIDARG; + memcpy((BYTE *)&pCtx->F + (ULONG32)((int)reg - REGISTER_LOONGARCH64_F0) * 32, pValue, valueSize); +#elif defined(TARGET_RISCV64) + if ((int)reg < REGISTER_RISCV64_F0 || (int)reg > REGISTER_RISCV64_F0 + 31) + return E_INVALIDARG; + memcpy((BYTE *)&pCtx->F + (ULONG32)((int)reg - REGISTER_RISCV64_F0) * 8, pValue, valueSize); +#elif defined(TARGET_ARM) + if ((int)reg < REGISTER_ARM_D0 || (int)reg > REGISTER_ARM_D0 + 31) + return E_INVALIDARG; + memcpy((BYTE *)&pCtx->D + (ULONG32)((int)reg - REGISTER_ARM_D0) * 8, pValue, valueSize); +#else + UNREFERENCED_PARAMETER(pCtx); + UNREFERENCED_PARAMETER(reg); + UNREFERENCED_PARAMETER(pValue); + UNREFERENCED_PARAMETER(valueSize); + return E_NOTIMPL; +#endif + + return S_OK; +} + // Create a VMPTR_Object from a target object address // @dbgtodo validate the VMPTR_Object is in fact a object, possibly by DACizing // Object::Validate diff --git a/src/coreclr/debug/daccess/dacdbiimpl.h b/src/coreclr/debug/daccess/dacdbiimpl.h index ec0aa77dfc66af..40873fc5aa6345 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.h +++ b/src/coreclr/debug/daccess/dacdbiimpl.h @@ -151,6 +151,28 @@ class DacDbiInterfaceImpl : HRESULT STDMETHODCALLTYPE EnumerateAsyncLocals(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeAddr, UINT32 state, FP_ASYNC_LOCAL_CALLBACK fpCallback, CALLBACK_DATA pUserData); HRESULT STDMETHODCALLTYPE GetGenericArgTokenIndex(VMPTR_MethodDesc vmMethod, OUT UINT32* pIndex); + HRESULT STDMETHODCALLTYPE GetTargetContextSize(ULONG32 contextFlags, OUT ULONG32 * pSize); + + HRESULT STDMETHODCALLTYPE WriteRegistersToContext(IN ContextBuffer contextBuffer, IN const CorDebugRegister * regs, IN ULONG32 nRegs, IN const TADDR * values); + HRESULT STDMETHODCALLTYPE ReadRegistersFromContext(IN ContextBuffer contextBuffer, IN const CorDebugRegister * regs, IN ULONG32 nRegs, OUT CORDB_REGISTER * pValues); + HRESULT STDMETHODCALLTYPE GetAvailableRegistersMask(IN BOOL fActive, IN BOOL fQuickUnwind, IN ULONG32 regCount, OUT BYTE pAvailable[]); + HRESULT STDMETHODCALLTYPE ConvertJitRegNumToCorDebugRegister(IN ULONG32 jitRegNum, OUT CorDebugRegister * pReg); + HRESULT STDMETHODCALLTYPE WriteFloatRegisterToContext( + IN ContextBuffer contextBuffer, + IN CorDebugRegister reg, + IN const BYTE * pValue, + IN ULONG32 valueSize); + + HRESULT STDMETHODCALLTYPE ContextHasExtendedRegisters(IN ContextBuffer contextBuffer, OUT BOOL * pResult); + + HRESULT STDMETHODCALLTYPE CompareControlRegisters(IN ContextBuffer contextBuffer1, IN ContextBuffer contextBuffer2, OUT BOOL * pResult); + + HRESULT STDMETHODCALLTYPE CopyContext( + IN ContextBuffer destinationContext, + IN ContextBuffer sourceContext, + IN ContextCopyMode copyMode, + IN ULONG32 flags); + private: void TypeHandleToExpandedTypeInfoImpl(AreValueTypesBoxed boxed, TypeHandle typeHandle, @@ -667,27 +689,26 @@ class DacDbiInterfaceImpl : HRESULT STDMETHODCALLTYPE Hijack(VMPTR_Thread vmThread, ULONG32 dwThreadId, const EXCEPTION_RECORD * pRecord, T_CONTEXT * pOriginalContext, ULONG32 cbSizeContext, EHijackReason::EHijackReason reason, void * pUserData, CORDB_ADDRESS * pRemoteContextAddr); // Return the filter CONTEXT on the LS. - HRESULT STDMETHODCALLTYPE GetManagedStoppedContext(VMPTR_Thread vmThread, OUT VMPTR_CONTEXT * pRetVal); + HRESULT STDMETHODCALLTYPE GetManagedStoppedContext(VMPTR_Thread vmThread, OUT CORDB_ADDRESS * pRetVal); // Create and return a stackwalker on the specified thread. - HRESULT STDMETHODCALLTYPE CreateStackWalk(VMPTR_Thread vmThread, DT_CONTEXT * pInternalContextBuffer, OUT StackWalkHandle * ppSFIHandle); + HRESULT STDMETHODCALLTYPE CreateStackWalk(VMPTR_Thread vmThread, ContextBuffer contextBuffer, OUT StackWalkHandle * ppSFIHandle); // Delete the stackwalk object HRESULT STDMETHODCALLTYPE DeleteStackWalk(StackWalkHandle ppSFIHandle); // Get the CONTEXT of the current frame at which the stackwalker is stopped. - HRESULT STDMETHODCALLTYPE GetStackWalkCurrentContext(StackWalkHandle pSFIHandle, DT_CONTEXT * pContext); + HRESULT STDMETHODCALLTYPE GetStackWalkCurrentContext(StackWalkHandle pSFIHandle, ContextBuffer contextBuffer); void GetStackWalkCurrentContext(StackFrameIterator * pIter, DT_CONTEXT * pContext); // Set the stackwalker to the specified CONTEXT. - HRESULT STDMETHODCALLTYPE SetStackWalkCurrentContext(VMPTR_Thread vmThread, StackWalkHandle pSFIHandle, CorDebugSetContextFlag flag, DT_CONTEXT * pContext); + HRESULT STDMETHODCALLTYPE SetStackWalkCurrentContext(VMPTR_Thread vmThread, StackWalkHandle pSFIHandle, CorDebugSetContextFlag flag, ContextBuffer contextBuffer); // Unwind the stackwalker to the next frame. HRESULT STDMETHODCALLTYPE UnwindStackWalkFrame(StackWalkHandle pSFIHandle, OUT BOOL * pResult); - HRESULT STDMETHODCALLTYPE CheckContext(VMPTR_Thread vmThread, - const DT_CONTEXT * pContext); + HRESULT STDMETHODCALLTYPE CheckContext(VMPTR_Thread vmThread, ContextBuffer contextBuffer); // Retrieve information about the current frame from the stackwalker. HRESULT STDMETHODCALLTYPE GetStackWalkCurrentFrameInfo(StackWalkHandle pSFIHandle, OPTIONAL Debugger_STRData * pFrameData, OUT FrameType * pRetVal); @@ -708,10 +729,10 @@ class DacDbiInterfaceImpl : // Return TRUE if the specified CONTEXT is the CONTEXT of the leaf frame. // @dbgtodo filter CONTEXT - Currently we check for the filter CONTEXT first. - HRESULT STDMETHODCALLTYPE IsLeafFrame(VMPTR_Thread vmThread, const DT_CONTEXT * pContext, OUT BOOL * pResult); + HRESULT STDMETHODCALLTYPE IsLeafFrame(VMPTR_Thread vmThread, ContextBuffer contextBuffer, OUT BOOL * pResult); // DacDbi API: Get the context for a particular thread of the target process - HRESULT STDMETHODCALLTYPE GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pContextBuffer); + HRESULT STDMETHODCALLTYPE GetContext(VMPTR_Thread vmThread, ContextBuffer contextBuffer); // Check if the given method is a DiagnosticHidden or an LCG method. HRESULT STDMETHODCALLTYPE IsDiagnosticsHiddenOrLCGMethod(VMPTR_MethodDesc vmMethodDesc, OUT DynamicMethodType * pRetVal); diff --git a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp index 532482de1ec7a4..66ab549439fc6c 100644 --- a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp +++ b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp @@ -109,10 +109,20 @@ T_CONTEXT * GetContextBufferFromHandle(StackWalkHandle pSFIHandle) // Create and return a stackwalker on the specified thread. -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::CreateStackWalk(VMPTR_Thread vmThread, DT_CONTEXT * pInternalContextBuffer, OUT StackWalkHandle * ppSFIHandle) +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::CreateStackWalk(VMPTR_Thread vmThread, ContextBuffer contextBuffer, OUT StackWalkHandle * ppSFIHandle) { DD_ENTER_MAY_THROW; + if (ppSFIHandle == NULL) + { + return E_INVALIDARG; + } + *ppSFIHandle = NULL; + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < sizeof(DT_CONTEXT))) + { + return E_INVALIDARG; + } + HRESULT hr = S_OK; EX_TRY { @@ -131,13 +141,13 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::CreateStackWalk(VMPTR_Thread vmTh // initialize the CONTEXT. // SetStackWalk will initial the RegDisplay from this context. - IfFailThrow(GetContext(vmThread, pInternalContextBuffer)); + IfFailThrow(GetContext(vmThread, contextBuffer)); // initialize the stackwalker IfFailThrow(SetStackWalkCurrentContext(vmThread, *ppSFIHandle, SET_CONTEXT_FLAG_ACTIVE_FRAME, - pInternalContextBuffer)); + contextBuffer)); } EX_CATCH_HRESULT(hr); return hr; @@ -156,17 +166,22 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::DeleteStackWalk(StackWalkHandle p } // Get the CONTEXT of the current frame at which the stackwalker is stopped. -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetStackWalkCurrentContext(StackWalkHandle pSFIHandle, DT_CONTEXT * pContext) +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetStackWalkCurrentContext(StackWalkHandle pSFIHandle, ContextBuffer contextBuffer) { DD_ENTER_MAY_THROW; + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < sizeof(DT_CONTEXT))) + { + return E_INVALIDARG; + } + HRESULT hr = S_OK; EX_TRY { StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle); - GetStackWalkCurrentContext(pIter, pContext); + GetStackWalkCurrentContext(pIter, reinterpret_cast(contextBuffer.pContextBytes)); } EX_CATCH_HRESULT(hr); return hr; @@ -189,20 +204,27 @@ void DacDbiInterfaceImpl::GetStackWalkCurrentContext(StackFrameIterator * pIter, // Set the stackwalker to the specified CONTEXT. -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::SetStackWalkCurrentContext(VMPTR_Thread vmThread, StackWalkHandle pSFIHandle, CorDebugSetContextFlag flag, DT_CONTEXT * pContext) +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::SetStackWalkCurrentContext(VMPTR_Thread vmThread, StackWalkHandle pSFIHandle, CorDebugSetContextFlag flag, ContextBuffer contextBuffer) { DD_ENTER_MAY_THROW; + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < sizeof(DT_CONTEXT))) + { + return E_INVALIDARG; + } + HRESULT hr = S_OK; EX_TRY { + DT_CONTEXT * pContext = reinterpret_cast(contextBuffer.pContextBytes); + StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle); REGDISPLAY * pRD = GetRegDisplayFromHandle(pSFIHandle); #if defined(_DEBUG) // The caller should have checked this already. - _ASSERTE(CheckContext(vmThread, pContext) == S_OK); + _ASSERTE(CheckContext(vmThread, contextBuffer) == S_OK); #endif // _DEBUG // DD can't keep pointers back into the RS address space. @@ -341,11 +363,17 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::UnwindStackWalkFrame(StackWalkHan // Check whether the specified CONTEXT is valid. The only check we perform right now is whether the // SP in the specified CONTEXT is in the stack range of the thread. -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::CheckContext(VMPTR_Thread vmThread, - const DT_CONTEXT * pContext) +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::CheckContext(VMPTR_Thread vmThread, ContextBuffer contextBuffer) { DD_ENTER_MAY_THROW; + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < sizeof(DT_CONTEXT))) + { + return E_INVALIDARG; + } + + const DT_CONTEXT * pContext = reinterpret_cast(contextBuffer.pContextBytes); + // If the SP in the CONTEXT isn't valid, then there's no point in checking. if ((pContext->ContextFlags & CONTEXT_CONTROL) == 0) { @@ -551,9 +579,9 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::EnumerateInternalFrames(VMPTR_Thr Frame * pFrame = pThread->GetFrame(); AppDomain * pAppDomain = AppDomain::GetCurrentDomain(); - // cStubFrame entries have no DT_CONTEXT buffer; leave ctx as NULL so consumers + // cStubFrame entries have no DT_CONTEXT buffer; leave ctx empty so consumers // (and the cDAC cross-check, which sets ctx = 0) don't observe a garbage value. - frameData.ctx = NULL; + frameData.ctx = {}; frameData.eType = Debugger_STRData::cStubFrame; while (pFrame != FRAME_TOP) @@ -722,14 +750,26 @@ FramePointer DacDbiInterfaceImpl::GetFramePointerWorker(StackFrameIterator * pIt } // Return TRUE if the specified CONTEXT is the CONTEXT of the leaf frame. -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::IsLeafFrame(VMPTR_Thread vmThread, const DT_CONTEXT * pContext, OUT BOOL * pResult) +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::IsLeafFrame(VMPTR_Thread vmThread, ContextBuffer contextBuffer, OUT BOOL * pResult) { DD_ENTER_MAY_THROW; + if (pResult == NULL) + { + return E_INVALIDARG; + } + *pResult = FALSE; + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < sizeof(DT_CONTEXT))) + { + return E_INVALIDARG; + } + HRESULT hr = S_OK; EX_TRY { + const DT_CONTEXT * pContext = reinterpret_cast(contextBuffer.pContextBytes); + DT_CONTEXT ctxLeaf; Thread * pThread = vmThread.GetDacPtr(); ctxLeaf.ContextFlags = DT_CONTEXT_ALL; @@ -739,7 +779,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::IsLeafFrame(VMPTR_Thread vmThread reinterpret_cast(&ctxLeaf))); // Call a platform-specific helper to compare the two contexts. - *pResult = CompareControlRegisters(pContext, &ctxLeaf); + *pResult = ::CompareControlRegisters(pContext, &ctxLeaf); } EX_CATCH_HRESULT(hr); return hr; @@ -772,8 +812,8 @@ void DacDbiInterfaceImpl::InitFrameData(StackFrameIterator * pIter, { pFrameData->eType = Debugger_STRData::cRuntimeNativeFrame; - _ASSERTE(pFrameData->ctx != NULL); - GetStackWalkCurrentContext(pIter, pFrameData->ctx); + _ASSERTE(pFrameData->ctx.pContextBytes != NULL); + GetStackWalkCurrentContext(pIter, reinterpret_cast(pFrameData->ctx.pContextBytes)); } else if (ft == kManagedStackFrame) { @@ -803,8 +843,8 @@ void DacDbiInterfaceImpl::InitFrameData(StackFrameIterator * pIter, pFrameData->eType = Debugger_STRData::cMethodFrame; - _ASSERTE(pFrameData->ctx != NULL); - GetStackWalkCurrentContext(pIter, pFrameData->ctx); + _ASSERTE(pFrameData->ctx.pContextBytes != NULL); + GetStackWalkCurrentContext(pIter, reinterpret_cast(pFrameData->ctx.pContextBytes)); // // initialize the fields in Debugger_STRData::v diff --git a/src/coreclr/debug/di/CMakeLists.txt b/src/coreclr/debug/di/CMakeLists.txt index e9c6b14af9300c..690d8f0a446c32 100644 --- a/src/coreclr/debug/di/CMakeLists.txt +++ b/src/coreclr/debug/di/CMakeLists.txt @@ -13,6 +13,7 @@ set(CORDBDI_SOURCES shimstackwalk.cpp breakpoint.cpp cordb.cpp + cordbregisterset.cpp divalue.cpp dbgtransportmanager.cpp hash.cpp @@ -50,24 +51,6 @@ set(CORDBDI_HEADERS if(CLR_CMAKE_HOST_WIN32) #use static crt set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded) - - if (CLR_CMAKE_TARGET_ARCH_AMD64 OR ((CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_ARM) - AND NOT DEFINED CLR_CROSS_COMPONENTS_BUILD)) - set(CORDBDI_SOURCES_ASM_FILE ${ARCH_SOURCES_DIR}/floatconversion.asm) - endif() - - if ((CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_ARM64) AND NOT DEFINED CLR_CROSS_COMPONENTS_BUILD) - convert_to_absolute_path(CORDBDI_SOURCES_ASM_FILE ${CORDBDI_SOURCES_ASM_FILE}) - preprocess_files(CORDBDI_SOURCES_ASM_FILE ${CORDBDI_SOURCES_ASM_FILE}) - endif() -elseif(CLR_CMAKE_HOST_UNIX) - - if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64 OR CLR_CMAKE_TARGET_ARCH_RISCV64) - set(CORDBDI_SOURCES_ASM_FILE - ${ARCH_SOURCES_DIR}/floatconversion.S - ) - endif() - endif(CLR_CMAKE_HOST_WIN32) if (CLR_CMAKE_TARGET_WIN32) @@ -75,8 +58,6 @@ if (CLR_CMAKE_TARGET_WIN32) list(APPEND CORDBDI_SOURCES ${CORDBDI_HEADERS}) endif (CLR_CMAKE_TARGET_WIN32) -list(APPEND CORDBDI_SOURCES ${CORDBDI_SOURCES_ASM_FILE}) - add_library_clr(cordbdi STATIC ${CORDBDI_SOURCES}) target_precompile_headers(cordbdi PRIVATE stdafx.h) add_dependencies(cordbdi eventing_headers) diff --git a/src/coreclr/debug/di/amd64/FloatConversion.asm b/src/coreclr/debug/di/amd64/FloatConversion.asm deleted file mode 100644 index a9f4fbc6f82aef..00000000000000 --- a/src/coreclr/debug/di/amd64/FloatConversion.asm +++ /dev/null @@ -1,19 +0,0 @@ -; Licensed to the .NET Foundation under one or more agreements. -; The .NET Foundation licenses this file to you under the MIT license. - -;// @dbgtodo Microsoft inspection: remove the implementation from vm\amd64\getstate.asm when we remove the -;// ipc event to load the float state. - -;// this is the same implementation as the function of the same name in vm\amd64\getstate.asm and they must -;// remain in sync. -;// Arguments -;// input: (in rcx) the M128 value to be converted to a double -;// output: the double corresponding to the M128 input value - -_TEXT segment para 'CODE' -FPFillR8 proc - movdqa xmm0, [rcx] - ret -FPFillR8 endp - -END diff --git a/src/coreclr/debug/di/amd64/cordbregisterset.cpp b/src/coreclr/debug/di/amd64/cordbregisterset.cpp deleted file mode 100644 index 22e4521dbc12c6..00000000000000 --- a/src/coreclr/debug/di/amd64/cordbregisterset.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -//***************************************************************************** -// File: CordbRegisterSet.cpp -// - -// -//***************************************************************************** -#include "primitives.h" - - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG64* pAvailable) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT(pAvailable, ULONG64*); - - (*pAvailable) = SETBITULONG64( REGISTER_INSTRUCTION_POINTER ) - | SETBITULONG64( REGISTER_STACK_POINTER ); - - if (!m_quickUnwind || m_active) - (*pAvailable) |= SETBITULONG64( REGISTER_AMD64_RBP ) - | SETBITULONG64( REGISTER_AMD64_RAX ) - | SETBITULONG64( REGISTER_AMD64_RCX ) - | SETBITULONG64( REGISTER_AMD64_RDX ) - | SETBITULONG64( REGISTER_AMD64_RBX ) - | SETBITULONG64( REGISTER_AMD64_RSI ) - | SETBITULONG64( REGISTER_AMD64_RDI ) - | SETBITULONG64( REGISTER_AMD64_R8 ) - | SETBITULONG64( REGISTER_AMD64_R9 ) - | SETBITULONG64( REGISTER_AMD64_R10 ) - | SETBITULONG64( REGISTER_AMD64_R11 ) - | SETBITULONG64( REGISTER_AMD64_R12 ) - | SETBITULONG64( REGISTER_AMD64_R13 ) - | SETBITULONG64( REGISTER_AMD64_R14 ) - | SETBITULONG64( REGISTER_AMD64_R15 ); - - if (m_active) - (*pAvailable) |= SETBITULONG64( REGISTER_AMD64_XMM0 ) - | SETBITULONG64( REGISTER_AMD64_XMM1 ) - | SETBITULONG64( REGISTER_AMD64_XMM2 ) - | SETBITULONG64( REGISTER_AMD64_XMM3 ) - | SETBITULONG64( REGISTER_AMD64_XMM4 ) - | SETBITULONG64( REGISTER_AMD64_XMM5 ) - | SETBITULONG64( REGISTER_AMD64_XMM6 ) - | SETBITULONG64( REGISTER_AMD64_XMM7 ) - | SETBITULONG64( REGISTER_AMD64_XMM8 ) - | SETBITULONG64( REGISTER_AMD64_XMM9 ) - | SETBITULONG64( REGISTER_AMD64_XMM10 ) - | SETBITULONG64( REGISTER_AMD64_XMM11 ) - | SETBITULONG64( REGISTER_AMD64_XMM12 ) - | SETBITULONG64( REGISTER_AMD64_XMM13 ) - | SETBITULONG64( REGISTER_AMD64_XMM14 ) - | SETBITULONG64( REGISTER_AMD64_XMM15 ); - - return S_OK; -} - -HRESULT CordbRegisterSet::GetRegisters(ULONG64 mask, ULONG32 regCount, - CORDB_REGISTER regBuffer[]) -{ - PUBLIC_REENTRANT_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); - UINT iRegister = 0; - - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - if ( mask & ( SETBITULONG64( REGISTER_AMD64_XMM0 ) - | SETBITULONG64( REGISTER_AMD64_XMM1 ) - | SETBITULONG64( REGISTER_AMD64_XMM2 ) - | SETBITULONG64( REGISTER_AMD64_XMM3 ) - | SETBITULONG64( REGISTER_AMD64_XMM4 ) - | SETBITULONG64( REGISTER_AMD64_XMM5 ) - | SETBITULONG64( REGISTER_AMD64_XMM6 ) - | SETBITULONG64( REGISTER_AMD64_XMM7 ) - | SETBITULONG64( REGISTER_AMD64_XMM8 ) - | SETBITULONG64( REGISTER_AMD64_XMM9 ) - | SETBITULONG64( REGISTER_AMD64_XMM10 ) - | SETBITULONG64( REGISTER_AMD64_XMM11 ) - | SETBITULONG64( REGISTER_AMD64_XMM12 ) - | SETBITULONG64( REGISTER_AMD64_XMM13 ) - | SETBITULONG64( REGISTER_AMD64_XMM14 ) - | SETBITULONG64( REGISTER_AMD64_XMM15 ) ) ) - { - HRESULT hr = S_OK; - - if (!m_active) - return E_INVALIDARG; - - if (!m_thread->m_fFloatStateValid) - { - EX_TRY - { - m_thread->LoadFloatState(); - } - EX_CATCH_HRESULT(hr); - - if ( !SUCCEEDED(hr) ) - { - return hr; - } - LOG( ( LF_CORDB, LL_INFO1000, "CRS::GR: Loaded float state\n" ) ); - } - } - - // Make sure that the registers are really available - if ( mask & ( SETBITULONG64( REGISTER_AMD64_RBP ) - | SETBITULONG64( REGISTER_AMD64_RAX ) - | SETBITULONG64( REGISTER_AMD64_RCX ) - | SETBITULONG64( REGISTER_AMD64_RDX ) - | SETBITULONG64( REGISTER_AMD64_RBX ) - | SETBITULONG64( REGISTER_AMD64_RSI ) - | SETBITULONG64( REGISTER_AMD64_RDI ) - | SETBITULONG64( REGISTER_AMD64_R8 ) - | SETBITULONG64( REGISTER_AMD64_R9 ) - | SETBITULONG64( REGISTER_AMD64_R10 ) - | SETBITULONG64( REGISTER_AMD64_R11 ) - | SETBITULONG64( REGISTER_AMD64_R12 ) - | SETBITULONG64( REGISTER_AMD64_R13 ) - | SETBITULONG64( REGISTER_AMD64_R14 ) - | SETBITULONG64( REGISTER_AMD64_R15 ) ) ) - { - if (!m_active && m_quickUnwind) - return E_INVALIDARG; - } - - for ( int i = REGISTER_INSTRUCTION_POINTER - ; i<=REGISTER_AMD64_XMM15 && iRegister < regCount - ; i++) - { - if( mask & SETBITULONG64(i) ) - { - switch( i ) - { - case REGISTER_INSTRUCTION_POINTER: - regBuffer[iRegister++] = m_context.Rip; break; - case REGISTER_STACK_POINTER: - regBuffer[iRegister++] = m_context.Rsp; break; - case REGISTER_AMD64_RBP: - regBuffer[iRegister++] = m_context.Rbp; break; - case REGISTER_AMD64_RAX: - regBuffer[iRegister++] = m_context.Rax; break; - case REGISTER_AMD64_RBX: - regBuffer[iRegister++] = m_context.Rbx; break; - case REGISTER_AMD64_RCX: - regBuffer[iRegister++] = m_context.Rcx; break; - case REGISTER_AMD64_RDX: - regBuffer[iRegister++] = m_context.Rdx; break; - case REGISTER_AMD64_RSI: - regBuffer[iRegister++] = m_context.Rsi; break; - case REGISTER_AMD64_RDI: - regBuffer[iRegister++] = m_context.Rdi; break; - case REGISTER_AMD64_R8: - regBuffer[iRegister++] = m_context.R8; break; - case REGISTER_AMD64_R9: - regBuffer[iRegister++] = m_context.R9; break; - case REGISTER_AMD64_R10: - regBuffer[iRegister++] = m_context.R10; break; - case REGISTER_AMD64_R11: - regBuffer[iRegister++] = m_context.R11; break; - case REGISTER_AMD64_R12: - regBuffer[iRegister++] = m_context.R12; break; - case REGISTER_AMD64_R13: - regBuffer[iRegister++] = m_context.R13; break; - case REGISTER_AMD64_R14: - regBuffer[iRegister++] = m_context.R14; break; - case REGISTER_AMD64_R15: - regBuffer[iRegister++] = m_context.R15; break; - - case REGISTER_AMD64_XMM0: - case REGISTER_AMD64_XMM1: - case REGISTER_AMD64_XMM2: - case REGISTER_AMD64_XMM3: - case REGISTER_AMD64_XMM4: - case REGISTER_AMD64_XMM5: - case REGISTER_AMD64_XMM6: - case REGISTER_AMD64_XMM7: - case REGISTER_AMD64_XMM8: - case REGISTER_AMD64_XMM9: - case REGISTER_AMD64_XMM10: - case REGISTER_AMD64_XMM11: - case REGISTER_AMD64_XMM12: - case REGISTER_AMD64_XMM13: - case REGISTER_AMD64_XMM14: - case REGISTER_AMD64_XMM15: - regBuffer[iRegister++] = *(CORDB_REGISTER*) - &(m_thread->m_floatValues[(i - REGISTER_AMD64_XMM0)]); - break; - } - } - } - - _ASSERTE( iRegister <= regCount ); - return S_OK; -} - - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG32 regCount, - BYTE pAvailable[]) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(pAvailable, CORDB_REGISTER, regCount, true, true); - - // Defer to adapter for v1.0 interface - return GetRegistersAvailableAdapter(regCount, pAvailable); -} - - -HRESULT CordbRegisterSet::GetRegisters(ULONG32 maskCount, BYTE mask[], - ULONG32 regCount, CORDB_REGISTER regBuffer[]) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - // Defer to adapter for v1.0 interface - return GetRegistersAdapter(maskCount, mask, regCount, regBuffer); -} diff --git a/src/coreclr/debug/di/amd64/floatconversion.S b/src/coreclr/debug/di/amd64/floatconversion.S deleted file mode 100644 index d43658175addaa..00000000000000 --- a/src/coreclr/debug/di/amd64/floatconversion.S +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -.intel_syntax noprefix -#include - -LEAF_ENTRY FPFillR8, _TEXT - movdqa xmm0, xmmword ptr [rdi] - ret -LEAF_END FPFillR8, _TEXT diff --git a/src/coreclr/debug/di/arm/cordbregisterset.cpp b/src/coreclr/debug/di/arm/cordbregisterset.cpp deleted file mode 100644 index 781947a6e45e82..00000000000000 --- a/src/coreclr/debug/di/arm/cordbregisterset.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -//***************************************************************************** -// File: CordbRegisterSet.cpp -// - -// -//***************************************************************************** -#include "primitives.h" - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG64 *pAvailable) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT(pAvailable, ULONG64 *); - - *pAvailable = SETBITULONG64(REGISTER_INSTRUCTION_POINTER) - | SETBITULONG64(REGISTER_STACK_POINTER) - | SETBITULONG64(REGISTER_ARM_R0) - | SETBITULONG64(REGISTER_ARM_R1) - | SETBITULONG64(REGISTER_ARM_R2) - | SETBITULONG64(REGISTER_ARM_R3) - | SETBITULONG64(REGISTER_ARM_R4) - | SETBITULONG64(REGISTER_ARM_R5) - | SETBITULONG64(REGISTER_ARM_R6) - | SETBITULONG64(REGISTER_ARM_R7) - | SETBITULONG64(REGISTER_ARM_R8) - | SETBITULONG64(REGISTER_ARM_R9) - | SETBITULONG64(REGISTER_ARM_R10) - | SETBITULONG64(REGISTER_ARM_R11) - | SETBITULONG64(REGISTER_ARM_R12) - | SETBITULONG64(REGISTER_ARM_LR); - - return S_OK; -} - -HRESULT CordbRegisterSet::GetRegisters(ULONG64 mask, ULONG32 regCount, CORDB_REGISTER regBuffer[]) -{ - PUBLIC_REENTRANT_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); - - UINT iRegister = 0; - - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - // @ARMTODO: floating point support - - for (int i = REGISTER_INSTRUCTION_POINTER; - i <= REGISTER_ARM_LR && iRegister < regCount; - i++) - { - if (mask & SETBITULONG64(i)) - { - switch (i) - { - case REGISTER_INSTRUCTION_POINTER: - regBuffer[iRegister++] = m_context.Pc; break; - case REGISTER_STACK_POINTER: - regBuffer[iRegister++] = m_context.Sp; break; - case REGISTER_ARM_R0: - regBuffer[iRegister++] = m_context.R0; break; - case REGISTER_ARM_R1: - regBuffer[iRegister++] = m_context.R1; break; - case REGISTER_ARM_R2: - regBuffer[iRegister++] = m_context.R2; break; - case REGISTER_ARM_R3: - regBuffer[iRegister++] = m_context.R3; break; - case REGISTER_ARM_R4: - regBuffer[iRegister++] = m_context.R4; break; - case REGISTER_ARM_R5: - regBuffer[iRegister++] = m_context.R5; break; - case REGISTER_ARM_R6: - regBuffer[iRegister++] = m_context.R6; break; - case REGISTER_ARM_R7: - regBuffer[iRegister++] = m_context.R7; break; - case REGISTER_ARM_R8: - regBuffer[iRegister++] = m_context.R8; break; - case REGISTER_ARM_R9: - regBuffer[iRegister++] = m_context.R9; break; - case REGISTER_ARM_R10: - regBuffer[iRegister++] = m_context.R10; break; - case REGISTER_ARM_R11: - regBuffer[iRegister++] = m_context.R11; break; - case REGISTER_ARM_R12: - regBuffer[iRegister++] = m_context.R12; break; - case REGISTER_ARM_LR: - regBuffer[iRegister++] = m_context.Lr; break; - } - } - } - - _ASSERTE (iRegister <= regCount); - return S_OK; -} - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG32 regCount, - BYTE pAvailable[]) -{ - PUBLIC_REENTRANT_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(pAvailable, CORDB_REGISTER, regCount, true, true); - - // Defer to adapter for v1.0 interface - return GetRegistersAvailableAdapter(regCount, pAvailable); -} - - -HRESULT CordbRegisterSet::GetRegisters(ULONG32 maskCount, BYTE mask[], - ULONG32 regCount, CORDB_REGISTER regBuffer[]) -{ - PUBLIC_REENTRANT_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - // Defer to adapter for v1.0 interface - return GetRegistersAdapter(maskCount, mask, regCount, regBuffer); -} diff --git a/src/coreclr/debug/di/arm/floatconversion.S b/src/coreclr/debug/di/arm/floatconversion.S deleted file mode 100644 index feea0db7abed7f..00000000000000 --- a/src/coreclr/debug/di/arm/floatconversion.S +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include - -// Arguments -// input: (in R0) the address of the ULONGLONG to be converted to a double -// output: the double corresponding to the ULONGLONG input value - -LEAF_ENTRY FPFillR8, .TEXT - .thumb - vldr D0, [R0] - bx lr -LEAF_END FPFillR8, .TEXT diff --git a/src/coreclr/debug/di/arm64/cordbregisterset.cpp b/src/coreclr/debug/di/arm64/cordbregisterset.cpp deleted file mode 100644 index ca11ec8020aabc..00000000000000 --- a/src/coreclr/debug/di/arm64/cordbregisterset.cpp +++ /dev/null @@ -1,241 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -//***************************************************************************** -// File: CordbRegisterSet.cpp -// - -// -//***************************************************************************** -#include "primitives.h" - - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG64* pAvailable) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT(pAvailable, ULONG64 *); - - *pAvailable = SETBITULONG64(REGISTER_ARM64_PC) - | SETBITULONG64(REGISTER_ARM64_SP) - | SETBITULONG64(REGISTER_ARM64_X0) - | SETBITULONG64(REGISTER_ARM64_X1) - | SETBITULONG64(REGISTER_ARM64_X2) - | SETBITULONG64(REGISTER_ARM64_X3) - | SETBITULONG64(REGISTER_ARM64_X4) - | SETBITULONG64(REGISTER_ARM64_X5) - | SETBITULONG64(REGISTER_ARM64_X6) - | SETBITULONG64(REGISTER_ARM64_X7) - | SETBITULONG64(REGISTER_ARM64_X8) - | SETBITULONG64(REGISTER_ARM64_X9) - | SETBITULONG64(REGISTER_ARM64_X10) - | SETBITULONG64(REGISTER_ARM64_X11) - | SETBITULONG64(REGISTER_ARM64_X12) - | SETBITULONG64(REGISTER_ARM64_X13) - | SETBITULONG64(REGISTER_ARM64_X14) - | SETBITULONG64(REGISTER_ARM64_X15) - | SETBITULONG64(REGISTER_ARM64_X16) - | SETBITULONG64(REGISTER_ARM64_X17) - | SETBITULONG64(REGISTER_ARM64_X18) - | SETBITULONG64(REGISTER_ARM64_X19) - | SETBITULONG64(REGISTER_ARM64_X20) - | SETBITULONG64(REGISTER_ARM64_X21) - | SETBITULONG64(REGISTER_ARM64_X22) - | SETBITULONG64(REGISTER_ARM64_X23) - | SETBITULONG64(REGISTER_ARM64_X24) - | SETBITULONG64(REGISTER_ARM64_X25) - | SETBITULONG64(REGISTER_ARM64_X26) - | SETBITULONG64(REGISTER_ARM64_X27) - | SETBITULONG64(REGISTER_ARM64_X28) - | SETBITULONG64(REGISTER_ARM64_FP) - | SETBITULONG64(REGISTER_ARM64_LR) - | SETBITULONG64(REGISTER_ARM64_V0) - | SETBITULONG64(REGISTER_ARM64_V1) - | SETBITULONG64(REGISTER_ARM64_V2) - | SETBITULONG64(REGISTER_ARM64_V3) - | SETBITULONG64(REGISTER_ARM64_V4) - | SETBITULONG64(REGISTER_ARM64_V5) - | SETBITULONG64(REGISTER_ARM64_V6) - | SETBITULONG64(REGISTER_ARM64_V7) - | SETBITULONG64(REGISTER_ARM64_V8) - | SETBITULONG64(REGISTER_ARM64_V9) - | SETBITULONG64(REGISTER_ARM64_V10) - | SETBITULONG64(REGISTER_ARM64_V11) - | SETBITULONG64(REGISTER_ARM64_V12) - | SETBITULONG64(REGISTER_ARM64_V13) - | SETBITULONG64(REGISTER_ARM64_V14) - | SETBITULONG64(REGISTER_ARM64_V15) - | SETBITULONG64(REGISTER_ARM64_V16) - | SETBITULONG64(REGISTER_ARM64_V17) - | SETBITULONG64(REGISTER_ARM64_V18) - | SETBITULONG64(REGISTER_ARM64_V19) - | SETBITULONG64(REGISTER_ARM64_V20) - | SETBITULONG64(REGISTER_ARM64_V21) - | SETBITULONG64(REGISTER_ARM64_V22) - | SETBITULONG64(REGISTER_ARM64_V23) - | SETBITULONG64(REGISTER_ARM64_V24) - | SETBITULONG64(REGISTER_ARM64_V25) - | SETBITULONG64(REGISTER_ARM64_V26) - | SETBITULONG64(REGISTER_ARM64_V27) - | SETBITULONG64(REGISTER_ARM64_V28) - | SETBITULONG64(REGISTER_ARM64_V29) - | SETBITULONG64(REGISTER_ARM64_V30); - - return S_OK; -} - -HRESULT CordbRegisterSet::GetRegisters(ULONG64 mask, ULONG32 regCount, - CORDB_REGISTER regBuffer[]) -{ - PUBLIC_REENTRANT_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); - - UINT iRegister = 0; - - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - for (int i = REGISTER_ARM64_PC; - i <= REGISTER_ARM64_V30 && iRegister < regCount; - i++) - { - if (mask & SETBITULONG64(i)) - { - _ASSERTE (iRegister < regCount); - - if ((i >= REGISTER_ARM64_X0) && (i <= REGISTER_ARM64_X28)) - { - regBuffer[iRegister++] = m_context.X[i - REGISTER_ARM64_X0]; - continue; - } - - if ((i >= REGISTER_ARM64_V0) && (i <= REGISTER_ARM64_V30)) - { - if (!m_thread->m_fFloatStateValid) - { - HRESULT hr = S_OK; - EX_TRY - { - m_thread->LoadFloatState(); - } - EX_CATCH_HRESULT(hr); - - if ( !SUCCEEDED(hr) ) - { - return hr; - } - LOG( ( LF_CORDB, LL_INFO1000, "CRS::GR: Loaded float state\n" ) ); - } - - regBuffer[iRegister++] = *(CORDB_REGISTER*) - &(m_thread->m_floatValues[(i - REGISTER_ARM64_V0)]); - continue; - } - - switch (i) - { - case REGISTER_ARM64_PC: - regBuffer[iRegister++] = m_context.Pc; break; - case REGISTER_ARM64_SP: - regBuffer[iRegister++] = m_context.Sp; break; - case REGISTER_ARM64_FP: - regBuffer[iRegister++] = m_context.Fp; break; - case REGISTER_ARM64_LR: - regBuffer[iRegister++] = m_context.Lr; break; - default: - _ASSERTE(false); break; - } - } - } - - _ASSERTE (iRegister <= regCount); - return S_OK; -} - - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG32 regCount, - BYTE pAvailable[]) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(pAvailable, CORDB_REGISTER, regCount, true, true); - - for (int i = 0 ; i < (int)regCount ; ++i) - { - if (i * 8 <= REGISTER_ARM64_V31) - { - pAvailable[i] = (i * 8 == REGISTER_ARM64_V31) ? BYTE(0x1) : BYTE(0xff); - } - else - { - pAvailable[i] = 0; - } - } - - return S_OK; -} - - -HRESULT CordbRegisterSet::GetRegisters(ULONG32 maskCount, BYTE mask[], - ULONG32 regCount, CORDB_REGISTER regBuffer[]) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - UINT iRegister = 0; - - for (int m = 0 ; m < (int)maskCount ; ++m) - { - for (int bit = 0 ; bit < 8 ; ++bit) - { - if (mask[m] & SETBITULONG64(bit)) - { - _ASSERTE (iRegister < regCount); - - int i = m * 8 + bit; - - if ((i >= REGISTER_ARM64_X0) && (i <= REGISTER_ARM64_X28)) - { - regBuffer[iRegister++] = m_context.X[i - REGISTER_ARM64_X0]; - continue; - } - - if ((i >= REGISTER_ARM64_V0) && (i <= REGISTER_ARM64_V31)) - { - if (!m_thread->m_fFloatStateValid) - { - HRESULT hr = S_OK; - EX_TRY - { - m_thread->LoadFloatState(); - } - EX_CATCH_HRESULT(hr); - - if ( !SUCCEEDED(hr) ) - { - return hr; - } - LOG( ( LF_CORDB, LL_INFO1000, "CRS::GR: Loaded float state\n" ) ); - } - - regBuffer[iRegister++] = *(CORDB_REGISTER*) - &(m_thread->m_floatValues[(i - REGISTER_ARM64_V0)]); - continue; - } - - switch (i) - { - case REGISTER_ARM64_PC: - regBuffer[iRegister++] = m_context.Pc; break; - case REGISTER_ARM64_SP: - regBuffer[iRegister++] = m_context.Sp; break; - case REGISTER_ARM64_FP: - regBuffer[iRegister++] = m_context.Fp; break; - case REGISTER_ARM64_LR: - regBuffer[iRegister++] = m_context.Lr; break; - default: - _ASSERTE(false); break; - } - } - } - } - - return S_OK; -} diff --git a/src/coreclr/debug/di/arm64/floatconversion.S b/src/coreclr/debug/di/arm64/floatconversion.S deleted file mode 100644 index c6370f28e8f811..00000000000000 --- a/src/coreclr/debug/di/arm64/floatconversion.S +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include - -// Arguments -// input: (in X0) the _NEON128 value to be converted to a double -// output: the double corresponding to the _NEON128 input value - -LEAF_ENTRY FPFillR8, .TEXT - LDR Q0, [X0] - ret lr -LEAF_END FPFillR8, .TEXT diff --git a/src/coreclr/debug/di/arm64/floatconversion.asm b/src/coreclr/debug/di/arm64/floatconversion.asm deleted file mode 100644 index 8ad73c8884fb41..00000000000000 --- a/src/coreclr/debug/di/arm64/floatconversion.asm +++ /dev/null @@ -1,16 +0,0 @@ -; Licensed to the .NET Foundation under one or more agreements. -; The .NET Foundation licenses this file to you under the MIT license. - -#include "ksarm64.h" - -;; Arguments -;; input: (in X0) the _NEON128 value to be converted to a double -;; output: the double corresponding to the _NEON128 input value - - LEAF_ENTRY FPFillR8 - LDR Q0, [X0] - ret lr - LEAF_END - -;; Must be at very end of file - END diff --git a/src/coreclr/debug/di/cordb.cpp b/src/coreclr/debug/di/cordb.cpp index 036ae1fb217491..debabd9cb98831 100644 --- a/src/coreclr/debug/di/cordb.cpp +++ b/src/coreclr/debug/di/cordb.cpp @@ -431,18 +431,17 @@ STDAPI GetRequestedRuntimeInfo(LPCWSTR pExe, return E_NOTIMPL; } -#ifdef TARGET_ARM BOOL DbiGetThreadContext(HANDLE hThread, - DT_CONTEXT *lpContext) + T_CONTEXT *lpContext) { // if we aren't local debugging this isn't going to work -#if !defined(HOST_ARM) || defined(FEATURE_DBGIPC_TRANSPORT_DI) || !SUPPORT_LOCAL_DEBUGGING +#if defined(FEATURE_DBGIPC_TRANSPORT_DI) || !SUPPORT_LOCAL_DEBUGGING _ASSERTE(!"Can't use local GetThreadContext remotely, this needed to go to datatarget"); return FALSE; #else BOOL res = FALSE; - if (((ULONG)lpContext) & ~0x10) + if (((ULONG_PTR)lpContext) & 0xF) { CONTEXT *ctx = (CONTEXT*)_aligned_malloc(sizeof(CONTEXT), 16); if (ctx) @@ -450,7 +449,7 @@ DbiGetThreadContext(HANDLE hThread, ctx->ContextFlags = lpContext->ContextFlags; if (::GetThreadContext(hThread, ctx)) { - *lpContext = *(DT_CONTEXT*)ctx; + memcpy(lpContext, ctx, ContextSizeForFlags(ctx->ContextFlags)); res = TRUE; } @@ -474,19 +473,19 @@ DbiGetThreadContext(HANDLE hThread, BOOL DbiSetThreadContext(HANDLE hThread, - const DT_CONTEXT *lpContext) + const T_CONTEXT *lpContext) { -#if !defined(HOST_ARM) || defined(FEATURE_DBGIPC_TRANSPORT_DI) || !SUPPORT_LOCAL_DEBUGGING +#if defined(FEATURE_DBGIPC_TRANSPORT_DI) || !SUPPORT_LOCAL_DEBUGGING _ASSERTE(!"Can't use local GetThreadContext remotely, this needed to go to datatarget"); return FALSE; #else BOOL res = FALSE; - if (((ULONG)lpContext) & ~0x10) + if (((ULONG_PTR)lpContext) & 0xF) { CONTEXT *ctx = (CONTEXT*)_aligned_malloc(sizeof(CONTEXT), 16); if (ctx) { - *ctx = *(CONTEXT*)lpContext; + memcpy(ctx, lpContext, ContextSizeForFlags(lpContext->ContextFlags)); res = ::SetThreadContext(hThread, ctx); _aligned_free(ctx); } @@ -505,4 +504,3 @@ DbiSetThreadContext(HANDLE hThread, return res; #endif } -#endif diff --git a/src/coreclr/debug/di/cordbregisterset.cpp b/src/coreclr/debug/di/cordbregisterset.cpp new file mode 100644 index 00000000000000..a06f6956e9fda3 --- /dev/null +++ b/src/coreclr/debug/di/cordbregisterset.cpp @@ -0,0 +1,162 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// File: CordbRegisterSet.cpp +// +// Cross-platform implementation of CordbRegisterSet's ICorDebugRegisterSet / +// ICorDebugRegisterSet2 surface. +// +// Availability bits (for both GPRs and float / SIMD registers) come from a +// single byte-form DDI: IDacDbiInterface::GetAvailableRegistersMask. The DAC +// owns the per-arch policy that gates float visibility (active-only on +// x86 / amd64, suppressed on arm, always on for arm64 / loongarch64 / +// riscv64). +// +// Register VALUES (both GPR and float / SIMD) come from the single target +// CONTEXT byte buffer (m_contextBuffer) via one batched +// IDacDbiInterface::ReadRegistersFromContext call. +// +//***************************************************************************** +#include "stdafx.h" +#include "primitives.h" + +namespace +{ + // Buffer size for the byte-form availability mask. arm64 needs 9 bytes + // (REGISTER_ARM64_V31 is bit 64); 16 bytes covers every arch with room + // to spare without forcing dynamic allocation. + constexpr ULONG32 kAvailMaskBytes = 16; + + // Upper bound on distinct register bit positions (kAvailMaskBytes * 8), + // used to size the batched request array on the stack. + constexpr ULONG32 kMaxRegisters = kAvailMaskBytes * 8; + + inline bool MaskBitSet(const BYTE * mask, ULONG32 maskCount, int bit) + { + ULONG32 byteIdx = (ULONG32)bit / 8; + if (byteIdx >= maskCount) + return false; + return (mask[byteIdx] & (BYTE)(1 << (bit % 8))) != 0; + } + + // Shared GetRegisters core. Validates that every bit set in the caller's + // request is also set in the available mask (E_INVALIDARG otherwise), then + // fills regBuffer in ascending bit order by reading the target CONTEXT. + HRESULT FillRegisters(IDacDbiInterface * pDAC, + ContextBuffer contextBuffer, + const BYTE * requestMask, ULONG32 requestMaskCount, + const BYTE * availMask, ULONG32 availMaskCount, + ULONG32 regCount, CORDB_REGISTER regBuffer[]) + { + // Validate the request is a subset of the available registers. + for (ULONG32 b = 0; b < requestMaskCount * 8; b++) + { + if (MaskBitSet(requestMask, requestMaskCount, (int)b) && + !MaskBitSet(availMask, availMaskCount, (int)b)) + { + return E_INVALIDARG; + } + } + + // Collect the requested register ids (ascending bit order), bounded by + // the caller's buffer, then read them all from the context at once. + CorDebugRegister requestedRegs[kMaxRegisters]; + ULONG32 requestedCount = 0; + for (ULONG32 b = 0; b < requestMaskCount * 8 && requestedCount < regCount; b++) + { + if (!MaskBitSet(requestMask, requestMaskCount, (int)b)) + continue; + if (requestedCount >= kMaxRegisters) + return E_FAIL; + requestedRegs[requestedCount++] = (CorDebugRegister)b; + } + + if (requestedCount > 0) + return pDAC->ReadRegistersFromContext(contextBuffer, requestedRegs, requestedCount, regBuffer); + + return S_OK; + } +} // anonymous namespace + +HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG64 * pAvailable) +{ + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT(pAvailable, ULONG64 *); + + BYTE availBytes[kAvailMaskBytes] = {}; + IDacDbiInterface * pDAC = GetProcess()->GetDAC(); + HRESULT hr = pDAC->GetAvailableRegistersMask( + m_active, m_quickUnwind, kAvailMaskBytes, availBytes); + if (FAILED(hr)) + return hr; + + // Fold the low 64 bits into the ULONG64 surface. Bits at positions >= 64 + // (e.g. arm64 V31) are only reachable through the v2 byte-mask overload. + ULONG64 mask = 0; + for (int b = 0; b < 64; b++) + { + if (availBytes[b / 8] & (BYTE)(1 << (b % 8))) + mask |= SETBITULONG64(b); + } + *pAvailable = mask; + return S_OK; +} + +HRESULT CordbRegisterSet::GetRegisters(ULONG64 mask, ULONG32 regCount, + CORDB_REGISTER regBuffer[]) +{ + PUBLIC_REENTRANT_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + + VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); + + BYTE availBytes[kAvailMaskBytes] = {}; + IDacDbiInterface * pDAC = GetProcess()->GetDAC(); + HRESULT hr = pDAC->GetAvailableRegistersMask( + m_active, m_quickUnwind, kAvailMaskBytes, availBytes); + if (FAILED(hr)) + return hr; + + BYTE requestBytes[sizeof(ULONG64)] = {}; + for (int b = 0; b < 64; b++) + { + if (mask & SETBITULONG64(b)) + requestBytes[b / 8] |= (BYTE)(1 << (b % 8)); + } + + return FillRegisters(pDAC, m_contextBuffer, + requestBytes, (ULONG32)sizeof(requestBytes), + availBytes, kAvailMaskBytes, regCount, regBuffer); +} + +HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG32 regCount, BYTE pAvailable[]) +{ + PUBLIC_REENTRANT_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + VALIDATE_POINTER_TO_OBJECT_ARRAY(pAvailable, CORDB_REGISTER, regCount, true, true); + + IDacDbiInterface * pDAC = GetProcess()->GetDAC(); + return pDAC->GetAvailableRegistersMask(m_active, m_quickUnwind, regCount, pAvailable); +} + +HRESULT CordbRegisterSet::GetRegisters(ULONG32 maskCount, BYTE mask[], + ULONG32 regCount, CORDB_REGISTER regBuffer[]) +{ + PUBLIC_REENTRANT_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); + + VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); + + BYTE availBytes[kAvailMaskBytes] = {}; + IDacDbiInterface * pDAC = GetProcess()->GetDAC(); + HRESULT hr = pDAC->GetAvailableRegistersMask( + m_active, m_quickUnwind, kAvailMaskBytes, availBytes); + if (FAILED(hr)) + return hr; + + return FillRegisters(pDAC, m_contextBuffer, + mask, maskCount, + availBytes, kAvailMaskBytes, regCount, regBuffer); +} diff --git a/src/coreclr/debug/di/i386/cordbregisterset.cpp b/src/coreclr/debug/di/i386/cordbregisterset.cpp deleted file mode 100644 index 1bf9056912536d..00000000000000 --- a/src/coreclr/debug/di/i386/cordbregisterset.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -//***************************************************************************** -// File: CordbRegisterSet.cpp -// - -// -//***************************************************************************** -#include "primitives.h" - - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG64 *pAvailable) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT(pAvailable, ULONG64 *); - - (*pAvailable) = SETBITULONG64( REGISTER_INSTRUCTION_POINTER ) - | SETBITULONG64( REGISTER_STACK_POINTER ) - | SETBITULONG64( REGISTER_FRAME_POINTER ); - - if (!m_quickUnwind || m_active) - (*pAvailable) |= SETBITULONG64( REGISTER_X86_EAX ) - | SETBITULONG64( REGISTER_X86_ECX ) - | SETBITULONG64( REGISTER_X86_EDX ) - | SETBITULONG64( REGISTER_X86_EBX ) - | SETBITULONG64( REGISTER_X86_ESI ) - | SETBITULONG64( REGISTER_X86_EDI ); - - if (m_active) - (*pAvailable) |= SETBITULONG64( REGISTER_X86_FPSTACK_0 ) - | SETBITULONG64( REGISTER_X86_FPSTACK_1 ) - | SETBITULONG64( REGISTER_X86_FPSTACK_2 ) - | SETBITULONG64( REGISTER_X86_FPSTACK_3 ) - | SETBITULONG64( REGISTER_X86_FPSTACK_4 ) - | SETBITULONG64( REGISTER_X86_FPSTACK_5 ) - | SETBITULONG64( REGISTER_X86_FPSTACK_6 ) - | SETBITULONG64( REGISTER_X86_FPSTACK_7 ); - - return S_OK; -} - - -#define FPSTACK_FROM_INDEX( _index ) (m_thread->m_floatValues[m_thread->m_floatStackTop -( (REGISTER_X86_FPSTACK_##_index)-REGISTER_X86_FPSTACK_0 ) ] ) - -HRESULT CordbRegisterSet::GetRegisters(ULONG64 mask, ULONG32 regCount, - CORDB_REGISTER regBuffer[]) -{ - PUBLIC_REENTRANT_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); - - UINT iRegister = 0; - - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - //If we need some floating point value, tell the thread to get it - if ( mask & ( SETBITULONG64(REGISTER_X86_FPSTACK_0) - | SETBITULONG64(REGISTER_X86_FPSTACK_1) - | SETBITULONG64(REGISTER_X86_FPSTACK_2) - | SETBITULONG64(REGISTER_X86_FPSTACK_3) - | SETBITULONG64(REGISTER_X86_FPSTACK_4) - | SETBITULONG64(REGISTER_X86_FPSTACK_5) - | SETBITULONG64(REGISTER_X86_FPSTACK_6) - | SETBITULONG64(REGISTER_X86_FPSTACK_7 ) ) ) - { - HRESULT hr = S_OK; - - if (!m_active) - return E_INVALIDARG; - - if (!m_thread->m_fFloatStateValid) - { - EX_TRY - { - m_thread->LoadFloatState(); - } - EX_CATCH_HRESULT(hr); - if ( !SUCCEEDED(hr) ) - { - return hr; - } - LOG( ( LF_CORDB, LL_INFO1000, "CRS::GR: Loaded float state\n" ) ); - } - } - - // Make sure that the registers are really available - if ( mask & ( SETBITULONG64( REGISTER_X86_EAX ) - | SETBITULONG64( REGISTER_X86_ECX ) - | SETBITULONG64( REGISTER_X86_EDX ) - | SETBITULONG64( REGISTER_X86_EBX ) - | SETBITULONG64( REGISTER_X86_ESI ) - | SETBITULONG64( REGISTER_X86_EDI ) ) ) - { - if (!m_active && m_quickUnwind) - return E_INVALIDARG; - } - - for ( int i = REGISTER_INSTRUCTION_POINTER - ; i<=REGISTER_X86_FPSTACK_7 && iRegister < regCount - ; i++) - { - if( mask & SETBITULONG64(i) ) - { - switch( i ) - { - case REGISTER_INSTRUCTION_POINTER: - regBuffer[iRegister++] = m_context.Eip; break; - case REGISTER_STACK_POINTER: - regBuffer[iRegister++] = m_context.Esp; break; - case REGISTER_FRAME_POINTER: - regBuffer[iRegister++] = m_context.Ebp; break; - case REGISTER_X86_EAX: - regBuffer[iRegister++] = m_context.Eax; break; - case REGISTER_X86_EBX: - regBuffer[iRegister++] = m_context.Ebx; break; - case REGISTER_X86_ECX: - regBuffer[iRegister++] = m_context.Ecx; break; - case REGISTER_X86_EDX: - regBuffer[iRegister++] = m_context.Edx; break; - case REGISTER_X86_ESI: - regBuffer[iRegister++] = m_context.Esi; break; - case REGISTER_X86_EDI: - regBuffer[iRegister++] = m_context.Edi; break; - - //for floats, copy the bits, not the integer part of - //the value, into the register - case REGISTER_X86_FPSTACK_0: - memcpy(®Buffer[iRegister++], - &(FPSTACK_FROM_INDEX(0)), - sizeof(CORDB_REGISTER)); - break; - case REGISTER_X86_FPSTACK_1: - memcpy( ®Buffer[iRegister++], - & (FPSTACK_FROM_INDEX( 1 ) ), - sizeof(CORDB_REGISTER) ); - break; - case REGISTER_X86_FPSTACK_2: - memcpy( ®Buffer[iRegister++], - & (FPSTACK_FROM_INDEX( 2 ) ), - sizeof(CORDB_REGISTER) ); break; - case REGISTER_X86_FPSTACK_3: - memcpy( ®Buffer[iRegister++], - & (FPSTACK_FROM_INDEX( 3 ) ), - sizeof(CORDB_REGISTER) ); break; - case REGISTER_X86_FPSTACK_4: - memcpy( ®Buffer[iRegister++], - & (FPSTACK_FROM_INDEX( 4 ) ), - sizeof(CORDB_REGISTER) ); break; - case REGISTER_X86_FPSTACK_5: - memcpy( ®Buffer[iRegister++], - & (FPSTACK_FROM_INDEX( 5 ) ), - sizeof(CORDB_REGISTER) ); break; - case REGISTER_X86_FPSTACK_6: - memcpy( ®Buffer[iRegister++], - & (FPSTACK_FROM_INDEX( 6 ) ), - sizeof(CORDB_REGISTER) ); break; - case REGISTER_X86_FPSTACK_7: - memcpy( ®Buffer[iRegister++], - & (FPSTACK_FROM_INDEX( 7 ) ), - sizeof(CORDB_REGISTER) ); break; - } - } - } - - _ASSERTE( iRegister <= regCount ); - return S_OK; -} - - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG32 regCount, - BYTE pAvailable[]) -{ - PUBLIC_REENTRANT_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(pAvailable, CORDB_REGISTER, regCount, true, true); - - // Defer to adapter for v1.0 interface - return GetRegistersAvailableAdapter(regCount, pAvailable); -} - - -HRESULT CordbRegisterSet::GetRegisters(ULONG32 maskCount, BYTE mask[], - ULONG32 regCount, CORDB_REGISTER regBuffer[]) -{ - PUBLIC_REENTRANT_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - // Defer to adapter for v1.0 interface - return GetRegistersAdapter(maskCount, mask, regCount, regBuffer); -} diff --git a/src/coreclr/debug/di/loongarch64/cordbregisterset.cpp b/src/coreclr/debug/di/loongarch64/cordbregisterset.cpp deleted file mode 100644 index faec1079c1b059..00000000000000 --- a/src/coreclr/debug/di/loongarch64/cordbregisterset.cpp +++ /dev/null @@ -1,269 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -//***************************************************************************** -// File: CordbRegisterSet.cpp -// - -// -//***************************************************************************** -#include "primitives.h" - - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG64* pAvailable) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT(pAvailable, ULONG64 *); - - *pAvailable = SETBITULONG64(REGISTER_LOONGARCH64_PC) - | SETBITULONG64(REGISTER_LOONGARCH64_SP) - | SETBITULONG64(REGISTER_LOONGARCH64_FP) - | SETBITULONG64(REGISTER_LOONGARCH64_RA) - | SETBITULONG64(REGISTER_LOONGARCH64_TP) - | SETBITULONG64(REGISTER_LOONGARCH64_A0) - | SETBITULONG64(REGISTER_LOONGARCH64_A1) - | SETBITULONG64(REGISTER_LOONGARCH64_A2) - | SETBITULONG64(REGISTER_LOONGARCH64_A3) - | SETBITULONG64(REGISTER_LOONGARCH64_A4) - | SETBITULONG64(REGISTER_LOONGARCH64_A5) - | SETBITULONG64(REGISTER_LOONGARCH64_A6) - | SETBITULONG64(REGISTER_LOONGARCH64_A7) - | SETBITULONG64(REGISTER_LOONGARCH64_T0) - | SETBITULONG64(REGISTER_LOONGARCH64_T1) - | SETBITULONG64(REGISTER_LOONGARCH64_T2) - | SETBITULONG64(REGISTER_LOONGARCH64_T3) - | SETBITULONG64(REGISTER_LOONGARCH64_T4) - | SETBITULONG64(REGISTER_LOONGARCH64_T5) - | SETBITULONG64(REGISTER_LOONGARCH64_T6) - | SETBITULONG64(REGISTER_LOONGARCH64_T7) - | SETBITULONG64(REGISTER_LOONGARCH64_T8) - | SETBITULONG64(REGISTER_LOONGARCH64_X0) - | SETBITULONG64(REGISTER_LOONGARCH64_S0) - | SETBITULONG64(REGISTER_LOONGARCH64_S1) - | SETBITULONG64(REGISTER_LOONGARCH64_S2) - | SETBITULONG64(REGISTER_LOONGARCH64_S3) - | SETBITULONG64(REGISTER_LOONGARCH64_S4) - | SETBITULONG64(REGISTER_LOONGARCH64_S5) - | SETBITULONG64(REGISTER_LOONGARCH64_S6) - | SETBITULONG64(REGISTER_LOONGARCH64_S7) - | SETBITULONG64(REGISTER_LOONGARCH64_S8) - | SETBITULONG64(REGISTER_LOONGARCH64_F0) - | SETBITULONG64(REGISTER_LOONGARCH64_F1) - | SETBITULONG64(REGISTER_LOONGARCH64_F2) - | SETBITULONG64(REGISTER_LOONGARCH64_F3) - | SETBITULONG64(REGISTER_LOONGARCH64_F4) - | SETBITULONG64(REGISTER_LOONGARCH64_F5) - | SETBITULONG64(REGISTER_LOONGARCH64_F6) - | SETBITULONG64(REGISTER_LOONGARCH64_F7) - | SETBITULONG64(REGISTER_LOONGARCH64_F8) - | SETBITULONG64(REGISTER_LOONGARCH64_F9) - | SETBITULONG64(REGISTER_LOONGARCH64_F10) - | SETBITULONG64(REGISTER_LOONGARCH64_F11) - | SETBITULONG64(REGISTER_LOONGARCH64_F12) - | SETBITULONG64(REGISTER_LOONGARCH64_F13) - | SETBITULONG64(REGISTER_LOONGARCH64_F14) - | SETBITULONG64(REGISTER_LOONGARCH64_F15) - | SETBITULONG64(REGISTER_LOONGARCH64_F16) - | SETBITULONG64(REGISTER_LOONGARCH64_F17) - | SETBITULONG64(REGISTER_LOONGARCH64_F18) - | SETBITULONG64(REGISTER_LOONGARCH64_F19) - | SETBITULONG64(REGISTER_LOONGARCH64_F20) - | SETBITULONG64(REGISTER_LOONGARCH64_F21) - | SETBITULONG64(REGISTER_LOONGARCH64_F22) - | SETBITULONG64(REGISTER_LOONGARCH64_F23) - | SETBITULONG64(REGISTER_LOONGARCH64_F24) - | SETBITULONG64(REGISTER_LOONGARCH64_F25) - | SETBITULONG64(REGISTER_LOONGARCH64_F26) - | SETBITULONG64(REGISTER_LOONGARCH64_F27) - | SETBITULONG64(REGISTER_LOONGARCH64_F28) - | SETBITULONG64(REGISTER_LOONGARCH64_F29) - | SETBITULONG64(REGISTER_LOONGARCH64_F30) - | SETBITULONG64(REGISTER_LOONGARCH64_F31); - - return S_OK; -} - -// Reads the value of a single register (identified by a CorDebugRegister index) into *pValue. -// Shared by both GetRegisters overloads to avoid duplicating the per-register mapping. -static HRESULT GetRegisterValue(CordbThread * pThread, const DT_CONTEXT * pContext, int regIndex, CORDB_REGISTER * pValue) -{ - if ((regIndex >= REGISTER_LOONGARCH64_F0) && (regIndex <= REGISTER_LOONGARCH64_F31)) - { - if (!pThread->m_fFloatStateValid) - { - HRESULT hr = S_OK; - EX_TRY - { - pThread->LoadFloatState(); - } - EX_CATCH_HRESULT(hr); - - if ( !SUCCEEDED(hr) ) - { - return hr; - } - LOG( ( LF_CORDB, LL_INFO1000, "CRS::GR: Loaded float state\n" ) ); - } - - *pValue = *(CORDB_REGISTER*)&(pThread->m_floatValues[(regIndex - REGISTER_LOONGARCH64_F0)]); - return S_OK; - } - - switch (regIndex) - { - case REGISTER_LOONGARCH64_A0: - *pValue = pContext->A0; break; - case REGISTER_LOONGARCH64_A1: - *pValue = pContext->A1; break; - case REGISTER_LOONGARCH64_A2: - *pValue = pContext->A2; break; - case REGISTER_LOONGARCH64_A3: - *pValue = pContext->A3; break; - case REGISTER_LOONGARCH64_A4: - *pValue = pContext->A4; break; - case REGISTER_LOONGARCH64_A5: - *pValue = pContext->A5; break; - case REGISTER_LOONGARCH64_A6: - *pValue = pContext->A6; break; - case REGISTER_LOONGARCH64_A7: - *pValue = pContext->A7; break; - case REGISTER_LOONGARCH64_S0: - *pValue = pContext->S0; break; - case REGISTER_LOONGARCH64_S1: - *pValue = pContext->S1; break; - case REGISTER_LOONGARCH64_S2: - *pValue = pContext->S2; break; - case REGISTER_LOONGARCH64_S3: - *pValue = pContext->S3; break; - case REGISTER_LOONGARCH64_S4: - *pValue = pContext->S4; break; - case REGISTER_LOONGARCH64_S5: - *pValue = pContext->S5; break; - case REGISTER_LOONGARCH64_S6: - *pValue = pContext->S6; break; - case REGISTER_LOONGARCH64_S7: - *pValue = pContext->S7; break; - case REGISTER_LOONGARCH64_S8: - *pValue = pContext->S8; break; - case REGISTER_LOONGARCH64_T0: - *pValue = pContext->T0; break; - case REGISTER_LOONGARCH64_T1: - *pValue = pContext->T1; break; - case REGISTER_LOONGARCH64_T2: - *pValue = pContext->T2; break; - case REGISTER_LOONGARCH64_T3: - *pValue = pContext->T3; break; - case REGISTER_LOONGARCH64_T4: - *pValue = pContext->T4; break; - case REGISTER_LOONGARCH64_T5: - *pValue = pContext->T5; break; - case REGISTER_LOONGARCH64_T6: - *pValue = pContext->T6; break; - case REGISTER_LOONGARCH64_T7: - *pValue = pContext->T7; break; - case REGISTER_LOONGARCH64_T8: - *pValue = pContext->T8; break; - case REGISTER_LOONGARCH64_X0: - *pValue = pContext->X0; break; - case REGISTER_LOONGARCH64_PC: - *pValue = pContext->Pc; break; - case REGISTER_LOONGARCH64_SP: - *pValue = pContext->Sp; break; - case REGISTER_LOONGARCH64_FP: - *pValue = pContext->Fp; break; - case REGISTER_LOONGARCH64_RA: - *pValue = pContext->Ra; break; - case REGISTER_LOONGARCH64_TP: - *pValue = pContext->Tp; break; - default: - _ASSERTE(false); break; - } - - return S_OK; -} - -HRESULT CordbRegisterSet::GetRegisters(ULONG64 mask, ULONG32 regCount, - CORDB_REGISTER regBuffer[]) -{ - PUBLIC_REENTRANT_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); - - UINT iRegister = 0; - - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - for (int i = REGISTER_LOONGARCH64_PC; - i <= REGISTER_LOONGARCH64_F31 && iRegister < regCount; - i++) - { - if (mask & SETBITULONG64(i)) - { - _ASSERTE (iRegister < regCount); - - HRESULT hr = GetRegisterValue(m_thread, &m_context, i, ®Buffer[iRegister]); - if (FAILED(hr)) - { - return hr; - } - iRegister++; - } - } - - _ASSERTE (iRegister <= regCount); - return S_OK; -} - - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG32 regCount, - BYTE pAvailable[]) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(pAvailable, CORDB_REGISTER, regCount, true, true); - - for (int i = 0 ; i < (int)regCount ; ++i) - { - if (i * 8 <= REGISTER_LOONGARCH64_F31) - { - pAvailable[i] = (i * 8 == REGISTER_LOONGARCH64_F31) ? BYTE(0x1) : BYTE(0xff); - } - else - { - pAvailable[i] = 0; - } - } - - return S_OK; -} - - -HRESULT CordbRegisterSet::GetRegisters(ULONG32 maskCount, BYTE mask[], - ULONG32 regCount, CORDB_REGISTER regBuffer[]) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - UINT iRegister = 0; - - for (int m = 0 ; m < (int)maskCount ; ++m) - { - for (int bit = 0 ; bit < 8 ; ++bit) - { - if (mask[m] & SETBITULONG64(bit)) - { - _ASSERTE (iRegister < regCount); - - int i = m * 8 + bit; - - HRESULT hr = GetRegisterValue(m_thread, &m_context, i, ®Buffer[iRegister]); - if (FAILED(hr)) - { - return hr; - } - iRegister++; - } - } - } - - return S_OK; -} diff --git a/src/coreclr/debug/di/loongarch64/floatconversion.S b/src/coreclr/debug/di/loongarch64/floatconversion.S deleted file mode 100644 index 6b5c7125ed4018..00000000000000 --- a/src/coreclr/debug/di/loongarch64/floatconversion.S +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include - -// Arguments -// input: (in A0) the address of the ULONGLONG to be converted to a double -// output: the double corresponding to the ULONGLONG input value -LEAF_ENTRY FPFillR8, .TEXT - fld.d $fa0, $a0, 0 - jirl $r0, $ra, 0 -LEAF_END FPFillR8, .TEXT diff --git a/src/coreclr/debug/di/module.cpp b/src/coreclr/debug/di/module.cpp index c261d88df78c6b..06216b3c5c1bea 100644 --- a/src/coreclr/debug/di/module.cpp +++ b/src/coreclr/debug/di/module.cpp @@ -3842,15 +3842,14 @@ HRESULT CordbVariableHome::GetRegister(CorDebugRegister *pRegister) switch (m_nativeVarInfo.loc.vlType) { case ICorDebugInfo::VLT_REG: - *pRegister = ConvertRegNumToCorDebugRegister(m_nativeVarInfo.loc.vlReg.vlrReg); - break; + return m_pCode->GetProcess()->ConvertJitRegNumToCorDebugRegister( + m_nativeVarInfo.loc.vlReg.vlrReg, pRegister); case ICorDebugInfo::VLT_STK: - *pRegister = ConvertRegNumToCorDebugRegister(m_nativeVarInfo.loc.vlStk.vlsBaseReg); - break; + return m_pCode->GetProcess()->ConvertJitRegNumToCorDebugRegister( + m_nativeVarInfo.loc.vlStk.vlsBaseReg, pRegister); default: return E_FAIL; } - return S_OK; } //----------------------------------------------------------------------------- diff --git a/src/coreclr/debug/di/platformspecific.cpp b/src/coreclr/debug/di/platformspecific.cpp index cd690dccc2fd25..f57c9db0cf66fe 100644 --- a/src/coreclr/debug/di/platformspecific.cpp +++ b/src/coreclr/debug/di/platformspecific.cpp @@ -22,22 +22,16 @@ #endif #if TARGET_X86 -#include "i386/cordbregisterset.cpp" #include "i386/primitives.cpp" #elif TARGET_AMD64 -#include "amd64/cordbregisterset.cpp" #include "amd64/primitives.cpp" #elif TARGET_ARM -#include "arm/cordbregisterset.cpp" #include "arm/primitives.cpp" #elif TARGET_ARM64 -#include "arm64/cordbregisterset.cpp" #include "arm64/primitives.cpp" #elif TARGET_LOONGARCH64 -#include "loongarch64/cordbregisterset.cpp" #include "loongarch64/primitives.cpp" #elif TARGET_RISCV64 -#include "riscv64/cordbregisterset.cpp" #include "riscv64/primitives.cpp" #else #error Unsupported platform diff --git a/src/coreclr/debug/di/process.cpp b/src/coreclr/debug/di/process.cpp index 66d33e4ff16f5c..45ff58221055a5 100644 --- a/src/coreclr/debug/di/process.cpp +++ b/src/coreclr/debug/di/process.cpp @@ -648,6 +648,14 @@ IDacDbiInterface * CordbProcess::GetDAC() return m_pDacPrimitives; } +HRESULT CordbProcess::ConvertJitRegNumToCorDebugRegister(ULONG32 jitRegNum, CorDebugRegister * pReg) +{ + if (pReg == NULL) + return E_INVALIDARG; + + return GetDAC()->ConvertJitRegNumToCorDebugRegister(jitRegNum, pReg); +} + //--------------------------------------------------------------------------------------- // Get the Data-Target // @@ -855,7 +863,8 @@ CordbProcess::CordbProcess(ULONG64 clrInstanceId, m_pEventChannel(NULL), m_fAssertOnTargetInconsistency(false), m_runtimeOffsetsInitialized(false), - m_writableMetadataUpdateMode(LegacyCompatPolicy) + m_writableMetadataUpdateMode(LegacyCompatPolicy), + m_ctxSize(0) #ifdef OUT_OF_PROCESS_SETTHREADCONTEXT , m_dwOutOfProcessStepping(0), @@ -1445,6 +1454,19 @@ void CordbProcess::FreeDac() LOG((LF_CORDB, LL_INFO1000, "Unloading DAC\n")); m_hDacModule.Free(); } + + m_ctxSize = 0; +} + +ULONG32 CordbProcess::GetTargetContextSize() +{ + if (m_ctxSize == 0) + { + ULONG32 size = 0; + IfFailThrow(GetDAC()->GetTargetContextSize(0x00000020L, &size)); // CONTEXT_EXTENDED_REGISTERS + m_ctxSize = size; + } + return m_ctxSize; } IEventChannel * CordbProcess::GetEventChannel() @@ -5788,10 +5810,9 @@ HRESULT CordbProcess::IsOSSuspended(DWORD threadID, BOOL *pbSuspended) // // This routine reads a thread context from the process being debugged, taking into account the fact that the context -// record may be a different size than the one we compiled with. On systems < NT5, then OS doesn't usually allocate -// space for the extended registers. However, the CONTEXT struct that we compile with does have this space. +// record may be a different size than the one we compiled with. // -HRESULT CordbProcess::SafeReadThreadContext(LSPTR_CONTEXT pContext, DT_CONTEXT * pCtx) +HRESULT CordbProcess::SafeReadThreadContext(CORDB_ADDRESS remoteContextAddr, ContextBuffer contextBuffer) { HRESULT hr = S_OK; @@ -5800,9 +5821,13 @@ HRESULT CordbProcess::SafeReadThreadContext(LSPTR_CONTEXT pContext, DT_CONTEXT * EX_TRY { + ULONG32 fullContextSize = GetTargetContextSize(); + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < fullContextSize)) + { + ThrowHR(E_INVALIDARG); + } - void *pRemoteContext = pContext.UnsafeGet(); - TargetBuffer tbFull(pRemoteContext, sizeof(DT_CONTEXT)); + TargetBuffer tbFull(remoteContextAddr, fullContextSize); // The context may have 2 parts: // 1. Base register, which are always present. @@ -5810,32 +5835,30 @@ HRESULT CordbProcess::SafeReadThreadContext(LSPTR_CONTEXT pContext, DT_CONTEXT * // in the flags. // At a minimum we have room for a whole context up to the extended registers. - #if defined(DT_CONTEXT_EXTENDED_REGISTERS) - ULONG32 minContextSize = offsetof(DT_CONTEXT, ExtendedRegisters); - #else - ULONG32 minContextSize = sizeof(DT_CONTEXT); - #endif + ULONG32 minContextSize; + IfFailThrow(GetDAC()->GetTargetContextSize(0, &minContextSize)); // Read the minimum part. TargetBuffer tbMin = tbFull.SubBuffer(0, minContextSize); - SafeReadBuffer(tbMin, (BYTE*) pCtx); + SafeReadBuffer(tbMin, contextBuffer.pContextBytes); - #if defined(DT_CONTEXT_EXTENDED_REGISTERS) - void *pCurExtReg = (void*)((UINT_PTR)pCtx + minContextSize); - TargetBuffer tbExtended = tbFull.SubBuffer(minContextSize); - - // Now, read the extended registers if the context contains them. If the context does not have extended registers, - // just set them to zero. - if (SUCCEEDED(hr) && (pCtx->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS) - { - SafeReadBuffer(tbExtended, (BYTE*) pCurExtReg); - } - else + if (fullContextSize > minContextSize) { - memset(pCurExtReg, 0, tbExtended.cbSize); - } - #endif + void *pCurExtReg = (void*)((UINT_PTR)contextBuffer.pContextBytes + minContextSize); + TargetBuffer tbExtended = tbFull.SubBuffer(minContextSize); + + BOOL hasExtendedRegisters = FALSE; + IfFailThrow(GetDAC()->ContextHasExtendedRegisters(contextBuffer, &hasExtendedRegisters)); + if (hasExtendedRegisters) + { + SafeReadBuffer(tbExtended, (BYTE*) pCurExtReg); + } + else + { + memset(pCurExtReg, 0, tbExtended.cbSize); + } + } } EX_CATCH_HRESULT(hr); return hr; @@ -5843,46 +5866,74 @@ HRESULT CordbProcess::SafeReadThreadContext(LSPTR_CONTEXT pContext, DT_CONTEXT * // // This routine writes a thread context to the process being debugged, taking into account the fact that the context -// record may be a different size than the one we compiled with. On systems < NT5, then OS doesn't usually allocate -// space for the extended registers. However, the CONTEXT struct that we compile with does have this space. +// record may be a different size than the one we compiled with. // -HRESULT CordbProcess::SafeWriteThreadContext(LSPTR_CONTEXT pContext, const DT_CONTEXT * pCtx) +HRESULT CordbProcess::SafeWriteThreadContext(CORDB_ADDRESS remoteContextAddr, ContextBuffer contextBuffer) +{ + INTERNAL_API_ENTRY(this); + FAIL_IF_NEUTERED(this); + + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < GetTargetContextSize())) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + EX_TRY + { + ULONG32 sizeToWrite; + BOOL hasExtendedRegisters = FALSE; + IfFailThrow(GetDAC()->ContextHasExtendedRegisters(contextBuffer, &hasExtendedRegisters)); + IfFailThrow(GetDAC()->GetTargetContextSize( + hasExtendedRegisters ? 0x00000020L : 0, // CONTEXT_EXTENDED_REGISTERS + &sizeToWrite)); + + CORDB_ADDRESS pRemoteContext = remoteContextAddr; + BYTE * pCtxSource = contextBuffer.pContextBytes; + + // 64 bit windows puts space for the first 6 stack parameters in the CONTEXT structure so that + // kernel to usermode transitions don't have to allocate a CONTEXT and do a separate sub rsp + // to allocate stack spill space for the arguments. This means that writing to P1Home - P6Home + // will overwrite the arguments of some function higher on the stack, very bad. Conceptually you + // can think of these members as not being part of the context, ie they don't represent something + // which gets saved or restored on context switches. They are just space we shouldn't overwrite. + // See issue 630276 for more details. +#if defined TARGET_AMD64 + pRemoteContext += offsetof(CONTEXT, ContextFlags); // immediately follows the 6 parameters P1-P6 + pCtxSource += offsetof(CONTEXT, ContextFlags); + sizeToWrite -= offsetof(CONTEXT, ContextFlags); +#endif + + // Write the context. + TargetBuffer tb(pRemoteContext, sizeToWrite); + SafeWriteBuffer(tb, (const BYTE*) pCtxSource); + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +HRESULT CordbProcess::SafeWriteThreadContext(CORDB_ADDRESS remoteContextAddr, T_CONTEXT * pCtx) { INTERNAL_API_ENTRY(this); FAIL_IF_NEUTERED(this); HRESULT hr = S_OK; - DWORD sizeToWrite = sizeof(DT_CONTEXT); + DWORD sizeToWrite = sizeof(T_CONTEXT); - BYTE * pRemoteContext = (BYTE*) pContext.UnsafeGet(); BYTE * pCtxSource = (BYTE*) pCtx; -#if defined(DT_CONTEXT_EXTENDED_REGISTERS) - // If our context has extended registers, then write the whole thing. Otherwise, just write the minimum part. - if ((pCtx->ContextFlags & DT_CONTEXT_EXTENDED_REGISTERS) != DT_CONTEXT_EXTENDED_REGISTERS) +#if defined(TARGET_X86) + if ((pCtx->ContextFlags & CONTEXT_EXTENDED_REGISTERS) != CONTEXT_EXTENDED_REGISTERS) { - sizeToWrite = offsetof(DT_CONTEXT, ExtendedRegisters); + sizeToWrite = offsetof(T_CONTEXT, SegSs) + sizeof(pCtx->SegSs); } #endif -// 64 bit windows puts space for the first 6 stack parameters in the CONTEXT structure so that -// kernel to usermode transitions don't have to allocate a CONTEXT and do a separate sub rsp -// to allocate stack spill space for the arguments. This means that writing to P1Home - P6Home -// will overwrite the arguments of some function higher on the stack, very bad. Conceptually you -// can think of these members as not being part of the context, ie they don't represent something -// which gets saved or restored on context switches. They are just space we shouldn't overwrite. -// See issue 630276 for more details. -#if defined TARGET_AMD64 - pRemoteContext += offsetof(CONTEXT, ContextFlags); // immediately follows the 6 parameters P1-P6 - pCtxSource += offsetof(CONTEXT, ContextFlags); - sizeToWrite -= offsetof(CONTEXT, ContextFlags); -#endif - EX_TRY { - // Write the context. - TargetBuffer tb(pRemoteContext, sizeToWrite); + TargetBuffer tb(remoteContextAddr, sizeToWrite); SafeWriteBuffer(tb, (const BYTE*) pCtxSource); } EX_CATCH_HRESULT(hr); @@ -5897,16 +5948,6 @@ HRESULT CordbProcess::GetThreadContext(DWORD threadID, ULONG32 contextSize, BYTE FAIL_IF_NEUTERED(this); LOG((LF_CORDB, LL_INFO10000, "CP::GTC: thread=0x%x\n", threadID)); - DT_CONTEXT * pContext; - - if (contextSize < sizeof(DT_CONTEXT)) - { - LOG((LF_CORDB, LL_INFO10000, "CP::GTC: thread=0x%x, context size is invalid.\n", threadID)); - return E_INVALIDARG; - } - - pContext = reinterpret_cast(context); - VALIDATE_POINTER_TO_OBJECT_ARRAY(context, BYTE, contextSize, true, true); if (this->IsInteropDebugging()) @@ -5924,7 +5965,13 @@ HRESULT CordbProcess::GetThreadContext(DWORD threadID, ULONG32 contextSize, BYTE return E_INVALIDARG; } - return ut->GetThreadContext((DT_CONTEXT*)context); + if (contextSize < sizeof(T_CONTEXT)) + { + LOG((LF_CORDB, LL_INFO10000, "CP::GTC: thread=0x%x, context size is invalid.\n", threadID)); + return E_INVALIDARG; + } + + return ut->GetThreadContext(reinterpret_cast(context)); #else return E_NOTIMPL; #endif @@ -5937,6 +5984,11 @@ HRESULT CordbProcess::GetThreadContext(DWORD threadID, ULONG32 contextSize, BYTE HRESULT hr = S_OK; EX_TRY { + if (contextSize < GetTargetContextSize()) + { + LOG((LF_CORDB, LL_INFO10000, "CP::GTC: thread=0x%x, context size is invalid.\n", threadID)); + return E_INVALIDARG; + } CordbThread* thread = this->TryLookupThreadByVolatileOSId(threadID); if (thread == NULL) { @@ -5946,9 +5998,13 @@ HRESULT CordbProcess::GetThreadContext(DWORD threadID, ULONG32 contextSize, BYTE } else { - DT_CONTEXT* managedContext; - hr = thread->GetManagedContext(&managedContext); - *pContext = *managedContext; + ContextBuffer managedContext = {}; + IfFailThrow(thread->GetManagedContext(&managedContext)); + if (managedContext.contextSize > contextSize) + { + ThrowHR(E_INVALIDARG); + } + memcpy(context, managedContext.pContextBytes, managedContext.contextSize); } } EX_CATCH_HRESULT(hr) @@ -5969,14 +6025,12 @@ HRESULT CordbProcess::SetThreadContext(DWORD threadID, ULONG32 contextSize, BYTE FAIL_IF_NEUTERED(this); VALIDATE_POINTER_TO_OBJECT_ARRAY(context, BYTE, contextSize, true, true); - if (contextSize != sizeof(DT_CONTEXT)) + if (contextSize != GetTargetContextSize()) { LOG((LF_CORDB, LL_INFO10000, "CP::STC: thread=0x%x, context size is invalid.\n", threadID)); return E_INVALIDARG; } - DT_CONTEXT* pContext = (DT_CONTEXT*)context; - if (this->IsInteropDebugging()) { #ifdef FEATURE_INTEROP_DEBUGGING @@ -5993,7 +6047,13 @@ HRESULT CordbProcess::SetThreadContext(DWORD threadID, ULONG32 contextSize, BYTE return E_INVALIDARG; } - hr = ut->SetThreadContext(pContext); + if (contextSize < sizeof(T_CONTEXT)) + { + LOG((LF_CORDB, LL_INFO10000, "CP::STC: thread=0x%x, context size is invalid.\n", threadID)); + return E_INVALIDARG; + } + + hr = ut->SetThreadContext(reinterpret_cast(context)); // Update the register set for the leaf-unmanaged chain so that it's consistent w/ the context. // We may not necessarily be synchronized, and so these frames may be stale. Even so, no harm done. @@ -6035,7 +6095,8 @@ HRESULT CordbProcess::SetThreadContext(DWORD threadID, ULONG32 contextSize, BYTE hr = E_INVALIDARG; } - hr = thread->SetManagedContext(pContext); + ContextBuffer contextBuffer = { context, contextSize }; + hr = thread->SetManagedContext(contextBuffer); } EX_CATCH { @@ -10365,7 +10426,7 @@ bool CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId) PRD_TYPE m_opcode = 0; public: - void Update(DT_CONTEXT * pContext) + void Update(T_CONTEXT * pContext) { this->m_lsContextAddr = (TADDR)pContext->Rcx; this->m_contextSize = (DWORD)pContext->Rdx; @@ -10444,13 +10505,13 @@ bool CordbProcess::HandleSetThreadContextNeeded(DWORD dwThreadId) return CORDBG_E_BAD_THREAD_STATE; } - DT_CONTEXT context = { 0 }; + T_CONTEXT context = { 0 }; context.ContextFlags = CONTEXT_FULL; // we originally used GetDataTarget()->GetThreadContext, but // the implementation uses ShimLocalDataTarget::GetThreadContext which // depends on OpenThread which might fail with an Access Denied error (see note above) - BOOL success = ::GetThreadContext(m_hThread, (CONTEXT*)(&context)); + BOOL success = ::GetThreadContext(m_hThread, &context); if (!success) { return HRESULT_FROM_WIN32(GetLastError()); @@ -11953,9 +12014,9 @@ Reaction CordbProcess::TriageExcep1stChanceAndInit(CordbUnmanagedThread * pUnman // WFDE will pick it up) but before we realize it's one of ours. STRESS_LOG2(LF_CORDB, LL_INFO1000, "CP::TE1stCAI: Phantom Int3: Tid=0x%x, addr=%p\n", pEvent->dwThreadId, pExAddress); - DT_CONTEXT context; + T_CONTEXT context; - context.ContextFlags = DT_CONTEXT_FULL; + context.ContextFlags = CONTEXT_FULL; BOOL fSuccess = DbiGetThreadContext(pUnmanagedThread->m_handle, &context); @@ -12447,10 +12508,9 @@ void CordbProcess::HandleDebugEventForInteropDebugging(const DEBUG_EVENT * pEven //Verify that GetThreadContext agrees with the exception address if (pEvent->dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { - DT_CONTEXT tempDebugContext; - tempDebugContext.ContextFlags = DT_CONTEXT_FULL; + T_CONTEXT tempDebugContext; + tempDebugContext.ContextFlags = CONTEXT_FULL; DbiGetThreadContext(pUnmanagedThread->m_handle, &tempDebugContext); - CordbUnmanagedThread::LogContext(&tempDebugContext); #if defined(TARGET_X86) || defined(TARGET_AMD64) const ULONG_PTR breakpointOpcodeSize = 1; #elif defined(TARGET_ARM64) @@ -12655,11 +12715,11 @@ void CordbProcess::HandleDebugEventForInteropDebugging(const DEBUG_EVENT * pEven if(pUnmanagedThread->IsFirstChanceHijacked()) { LOG((LF_CORDB, LL_INFO100000, "W32ET::W32EL: hijack complete will restore context...\n")); - DT_CONTEXT tempContext = { 0 }; -#if defined(DT_CONTEXT_EXTENDED_REGISTERS) - tempContext.ContextFlags = DT_CONTEXT_FULL | DT_CONTEXT_FLOATING_POINT | DT_CONTEXT_EXTENDED_REGISTERS; + T_CONTEXT tempContext = { 0 }; +#if defined(TARGET_X86) + tempContext.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS; #else - tempContext.ContextFlags = DT_CONTEXT_FULL | DT_CONTEXT_FLOATING_POINT; + tempContext.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; #endif HRESULT hr = pUnmanagedThread->GetThreadContext(&tempContext); _ASSERTE(SUCCEEDED(hr)); @@ -12739,7 +12799,7 @@ void CordbProcess::HandleDebugEventForInteropDebugging(const DEBUG_EVENT * pEven _ASSERTE(fcd.pLeftSideContext != NULL); LOG((LF_CORDB, LL_INFO10000, "W32ET::W32EL: updating LS context at 0x%p\n", fcd.pLeftSideContext)); // write the new context over the old one on the LS - SafeWriteThreadContext(fcd.pLeftSideContext, &tempContext); + SafeWriteThreadContext(fcd.pLeftSideContext.UnsafeGetAddr(), &tempContext); } // Write the new Fcd data to the LS @@ -13092,8 +13152,8 @@ void EnableDebugTrace(CordbUnmanagedThread *ut) // Get the context HRESULT hr = S_OK; - DT_CONTEXT context; - context.ContextFlags = DT_CONTEXT_FULL; + T_CONTEXT context; + context.ContextFlags = CONTEXT_FULL; hr = pProcess->GetThreadContext((DWORD) ut->m_id, sizeof(context), (BYTE*)&context); diff --git a/src/coreclr/debug/di/riscv64/cordbregisterset.cpp b/src/coreclr/debug/di/riscv64/cordbregisterset.cpp deleted file mode 100644 index 469e8fe21359db..00000000000000 --- a/src/coreclr/debug/di/riscv64/cordbregisterset.cpp +++ /dev/null @@ -1,269 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -//***************************************************************************** -// File: CordbRegisterSet.cpp -// - -// -//***************************************************************************** -#include "primitives.h" - - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG64* pAvailable) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT(pAvailable, ULONG64 *); - - *pAvailable = SETBITULONG64(REGISTER_RISCV64_PC) - | SETBITULONG64(REGISTER_RISCV64_RA) - | SETBITULONG64(REGISTER_RISCV64_SP) - | SETBITULONG64(REGISTER_RISCV64_GP) - | SETBITULONG64(REGISTER_RISCV64_TP) - | SETBITULONG64(REGISTER_RISCV64_T0) - | SETBITULONG64(REGISTER_RISCV64_T1) - | SETBITULONG64(REGISTER_RISCV64_T2) - | SETBITULONG64(REGISTER_RISCV64_FP) - | SETBITULONG64(REGISTER_RISCV64_S1) - | SETBITULONG64(REGISTER_RISCV64_A0) - | SETBITULONG64(REGISTER_RISCV64_A1) - | SETBITULONG64(REGISTER_RISCV64_A2) - | SETBITULONG64(REGISTER_RISCV64_A3) - | SETBITULONG64(REGISTER_RISCV64_A4) - | SETBITULONG64(REGISTER_RISCV64_A5) - | SETBITULONG64(REGISTER_RISCV64_A6) - | SETBITULONG64(REGISTER_RISCV64_A7) - | SETBITULONG64(REGISTER_RISCV64_S2) - | SETBITULONG64(REGISTER_RISCV64_S3) - | SETBITULONG64(REGISTER_RISCV64_S4) - | SETBITULONG64(REGISTER_RISCV64_S5) - | SETBITULONG64(REGISTER_RISCV64_S6) - | SETBITULONG64(REGISTER_RISCV64_S7) - | SETBITULONG64(REGISTER_RISCV64_S8) - | SETBITULONG64(REGISTER_RISCV64_S9) - | SETBITULONG64(REGISTER_RISCV64_S10) - | SETBITULONG64(REGISTER_RISCV64_S11) - | SETBITULONG64(REGISTER_RISCV64_T3) - | SETBITULONG64(REGISTER_RISCV64_T4) - | SETBITULONG64(REGISTER_RISCV64_T5) - | SETBITULONG64(REGISTER_RISCV64_T6) - | SETBITULONG64(REGISTER_RISCV64_F0) - | SETBITULONG64(REGISTER_RISCV64_F1) - | SETBITULONG64(REGISTER_RISCV64_F2) - | SETBITULONG64(REGISTER_RISCV64_F3) - | SETBITULONG64(REGISTER_RISCV64_F4) - | SETBITULONG64(REGISTER_RISCV64_F5) - | SETBITULONG64(REGISTER_RISCV64_F6) - | SETBITULONG64(REGISTER_RISCV64_F7) - | SETBITULONG64(REGISTER_RISCV64_F8) - | SETBITULONG64(REGISTER_RISCV64_F9) - | SETBITULONG64(REGISTER_RISCV64_F10) - | SETBITULONG64(REGISTER_RISCV64_F11) - | SETBITULONG64(REGISTER_RISCV64_F12) - | SETBITULONG64(REGISTER_RISCV64_F13) - | SETBITULONG64(REGISTER_RISCV64_F14) - | SETBITULONG64(REGISTER_RISCV64_F15) - | SETBITULONG64(REGISTER_RISCV64_F16) - | SETBITULONG64(REGISTER_RISCV64_F17) - | SETBITULONG64(REGISTER_RISCV64_F18) - | SETBITULONG64(REGISTER_RISCV64_F19) - | SETBITULONG64(REGISTER_RISCV64_F20) - | SETBITULONG64(REGISTER_RISCV64_F21) - | SETBITULONG64(REGISTER_RISCV64_F22) - | SETBITULONG64(REGISTER_RISCV64_F23) - | SETBITULONG64(REGISTER_RISCV64_F24) - | SETBITULONG64(REGISTER_RISCV64_F25) - | SETBITULONG64(REGISTER_RISCV64_F26) - | SETBITULONG64(REGISTER_RISCV64_F27) - | SETBITULONG64(REGISTER_RISCV64_F28) - | SETBITULONG64(REGISTER_RISCV64_F29) - | SETBITULONG64(REGISTER_RISCV64_F30) - | SETBITULONG64(REGISTER_RISCV64_F31); - - return S_OK; -} - -// Reads the value of a single register (identified by a CorDebugRegister index) into *pValue. -// Shared by both GetRegisters overloads to avoid duplicating the per-register mapping. -static HRESULT GetRegisterValue(CordbThread * pThread, const DT_CONTEXT * pContext, int regIndex, CORDB_REGISTER * pValue) -{ - if ((regIndex >= REGISTER_RISCV64_F0) && (regIndex <= REGISTER_RISCV64_F31)) - { - if (!pThread->m_fFloatStateValid) - { - HRESULT hr = S_OK; - EX_TRY - { - pThread->LoadFloatState(); - } - EX_CATCH_HRESULT(hr); - - if ( !SUCCEEDED(hr) ) - { - return hr; - } - LOG( ( LF_CORDB, LL_INFO1000, "CRS::GR: Loaded float state\n" ) ); - } - - *pValue = *(CORDB_REGISTER*)&(pThread->m_floatValues[(regIndex - REGISTER_RISCV64_F0)]); - return S_OK; - } - - switch (regIndex) - { - case REGISTER_RISCV64_PC: - *pValue = pContext->Pc; break; - case REGISTER_RISCV64_RA: - *pValue = pContext->Ra; break; - case REGISTER_RISCV64_SP: - *pValue = pContext->Sp; break; - case REGISTER_RISCV64_GP: - *pValue = pContext->Gp; break; - case REGISTER_RISCV64_TP: - *pValue = pContext->Tp; break; - case REGISTER_RISCV64_T0: - *pValue = pContext->T0; break; - case REGISTER_RISCV64_T1: - *pValue = pContext->T1; break; - case REGISTER_RISCV64_T2: - *pValue = pContext->T2; break; - case REGISTER_RISCV64_FP: - *pValue = pContext->Fp; break; - case REGISTER_RISCV64_S1: - *pValue = pContext->S1; break; - case REGISTER_RISCV64_A0: - *pValue = pContext->A0; break; - case REGISTER_RISCV64_A1: - *pValue = pContext->A1; break; - case REGISTER_RISCV64_A2: - *pValue = pContext->A2; break; - case REGISTER_RISCV64_A3: - *pValue = pContext->A3; break; - case REGISTER_RISCV64_A4: - *pValue = pContext->A4; break; - case REGISTER_RISCV64_A5: - *pValue = pContext->A5; break; - case REGISTER_RISCV64_A6: - *pValue = pContext->A6; break; - case REGISTER_RISCV64_A7: - *pValue = pContext->A7; break; - case REGISTER_RISCV64_S2: - *pValue = pContext->S2; break; - case REGISTER_RISCV64_S3: - *pValue = pContext->S3; break; - case REGISTER_RISCV64_S4: - *pValue = pContext->S4; break; - case REGISTER_RISCV64_S5: - *pValue = pContext->S5; break; - case REGISTER_RISCV64_S6: - *pValue = pContext->S6; break; - case REGISTER_RISCV64_S7: - *pValue = pContext->S7; break; - case REGISTER_RISCV64_S8: - *pValue = pContext->S8; break; - case REGISTER_RISCV64_S9: - *pValue = pContext->S9; break; - case REGISTER_RISCV64_S10: - *pValue = pContext->S10; break; - case REGISTER_RISCV64_S11: - *pValue = pContext->S11; break; - case REGISTER_RISCV64_T3: - *pValue = pContext->T3; break; - case REGISTER_RISCV64_T4: - *pValue = pContext->T4; break; - case REGISTER_RISCV64_T5: - *pValue = pContext->T5; break; - case REGISTER_RISCV64_T6: - *pValue = pContext->T6; break; - default: - _ASSERTE(false); break; - } - - return S_OK; -} - -HRESULT CordbRegisterSet::GetRegisters(ULONG64 mask, ULONG32 regCount, - CORDB_REGISTER regBuffer[]) -{ - PUBLIC_REENTRANT_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); - - UINT iRegister = 0; - - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - for (int i = REGISTER_RISCV64_PC; - i <= REGISTER_RISCV64_F31 && iRegister < regCount; - i++) - { - if (mask & SETBITULONG64(i)) - { - _ASSERTE (iRegister < regCount); - - HRESULT hr = GetRegisterValue(m_thread, &m_context, i, ®Buffer[iRegister]); - if (FAILED(hr)) - { - return hr; - } - iRegister++; - } - } - - _ASSERTE (iRegister <= regCount); - return S_OK; -} - - -HRESULT CordbRegisterSet::GetRegistersAvailable(ULONG32 regCount, - BYTE pAvailable[]) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(pAvailable, CORDB_REGISTER, regCount, true, true); - - for (int i = 0 ; i < (int)regCount ; ++i) - { - if (i * 8 <= REGISTER_RISCV64_F31) - { - pAvailable[i] = (i * 8 == REGISTER_RISCV64_F31) ? BYTE(0x1) : BYTE(0xff); - } - else - { - pAvailable[i] = 0; - } - } - - return S_OK; -} - - -HRESULT CordbRegisterSet::GetRegisters(ULONG32 maskCount, BYTE mask[], - ULONG32 regCount, CORDB_REGISTER regBuffer[]) -{ - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, regCount, true, true); - - UINT iRegister = 0; - - for (int m = 0 ; m < (int)maskCount ; ++m) - { - for (int bit = 0 ; bit < 8 ; ++bit) - { - if (mask[m] & SETBITULONG64(bit)) - { - _ASSERTE (iRegister < regCount); - - int i = m * 8 + bit; - - HRESULT hr = GetRegisterValue(m_thread, &m_context, i, ®Buffer[iRegister]); - if (FAILED(hr)) - { - return hr; - } - iRegister++; - } - } - } - - return S_OK; -} diff --git a/src/coreclr/debug/di/riscv64/floatconversion.S b/src/coreclr/debug/di/riscv64/floatconversion.S deleted file mode 100644 index 138db0bc9dd243..00000000000000 --- a/src/coreclr/debug/di/riscv64/floatconversion.S +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include - -// Arguments -// input: (in A0) the address of the ULONGLONG to be converted to a double -// output: the double corresponding to the ULONGLONG input value -LEAF_ENTRY FPFillR8, .TEXT - fld fa0, 0(a0) - ret -LEAF_END FPFillR8, .TEXT diff --git a/src/coreclr/debug/di/rspriv.h b/src/coreclr/debug/di/rspriv.h index d4d9537c78f660..bcf4a18b431846 100644 --- a/src/coreclr/debug/di/rspriv.h +++ b/src/coreclr/debug/di/rspriv.h @@ -3583,8 +3583,9 @@ class CordbProcess : void AddToLeftSideResourceCleanupList(CordbBase * pObject); // Routines to read and write thread context records between the processes safely. - HRESULT SafeReadThreadContext(LSPTR_CONTEXT pRemoteContext, DT_CONTEXT * pCtx); - HRESULT SafeWriteThreadContext(LSPTR_CONTEXT pRemoteContext, const DT_CONTEXT * pCtx); + HRESULT SafeReadThreadContext(CORDB_ADDRESS remoteContextAddr, ContextBuffer contextBuffer); + HRESULT SafeWriteThreadContext(CORDB_ADDRESS remoteContextAddr, ContextBuffer contextBuffer); + HRESULT SafeWriteThreadContext(CORDB_ADDRESS remoteContextAddr, T_CONTEXT * pCtx); #ifdef FEATURE_INTEROP_DEBUGGING // Record a win32 event for debugging purposes. @@ -3598,6 +3599,8 @@ class CordbProcess : // Get the DAC interface. IDacDbiInterface * GetDAC(); + HRESULT ConvertJitRegNumToCorDebugRegister(ULONG32 jitRegNum, CorDebugRegister * pReg); + // Get the data-target, which provides access to the debuggee. ICorDebugDataTarget * GetDataTarget(); @@ -4003,6 +4006,7 @@ class CordbProcess : #endif // FEATURE_INTEROP_DEBUGGING bool IsBreakOpcodeAtAddress(const void * address); + ULONG32 GetTargetContextSize(); private: // @@ -4097,6 +4101,7 @@ class CordbProcess : // controls how metadata updated in the target is handled WriteableMetadataUpdateMode m_writableMetadataUpdateMode; + ULONG32 m_ctxSize; COM_METHOD GetObjectInternal(CORDB_ADDRESS addr, ICorDebugObjectValue **pObject); @@ -6076,21 +6081,6 @@ class CordbThread : public CordbBase, public ICorDebugThread, void MarkStackFramesDirty(); -#if defined(TARGET_X86) - // Converts the values in the floating point register area of the context to real number values. - void Get32bitFPRegisters(CONTEXT * pContext); - -#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) - // Converts the values in the floating point register area of the context to real number values. - void Get64bitFPRegisters(FPRegister64 * rgContextFPRegisters, int start, int nRegisters); - -#endif // TARGET_X86 - - // Initializes the float state members of this instance of CordbThread. This function gets the context and - // converts the floating point values from their context representation to real number values. - void LoadFloatState(); - - HRESULT SetIP( bool fCanSetIPOnly, CordbNativeCode * pNativeCode, SIZE_T offset, @@ -6133,8 +6123,8 @@ class CordbThread : public CordbBase, public ICorDebugThread, // // ////////////////////////////////////////////////////////////////////////// - HRESULT GetManagedContext( DT_CONTEXT ** ppContext ); - HRESULT SetManagedContext( DT_CONTEXT * pContext ); + HRESULT GetManagedContext(ContextBuffer * pContextBuffer); + HRESULT SetManagedContext(ContextBuffer contextBuffer); // API to retrieve the thread handle from the LS. void InternalGetHandle(HANDLE * phThread); @@ -6177,7 +6167,7 @@ class CordbThread : public CordbBase, public ICorDebugThread, BOOL IsThreadExceptionManaged(); // This is a private hook for the shim to create a CordbRegisterSet for a ShimChain. - void CreateCordbRegisterSet(DT_CONTEXT * pContext, + void CreateCordbRegisterSet(ContextBuffer contextBuffer, BOOL fActive, CorDebugChainReason reason, ICorDebugRegisterSet ** ppRegSet); @@ -6193,15 +6183,14 @@ class CordbThread : public CordbBase, public ICorDebugThread, public: // RS Cache for LS context. - // NULL if we haven't allocated memory for a Right side context - DT_CONTEXT * m_pContext; + NewArrayHolder m_pContext; // Set to the CONTEXT pointer in the LS if this LS thread is // stopped in managed code. This may be either stopped for execution control // (breakpoint / single-step exception) or hijacked w/ a redirected frame because // another thread synced the LS. // This context is used by the RS to set enregistered vars. - VMPTR_CONTEXT m_vmLeftSideContext; + CORDB_ADDRESS m_vmLeftSideContext; // indicates whether m_pContext is up-to-date bool m_fContextFresh; @@ -6233,10 +6222,6 @@ class CordbThread : public CordbBase, public ICorDebugThread, // Instead, we mark m_fFramesFresh in CleanupStack() and clear the cache only when it is used next time. CDynArray m_stackFrames; - bool m_fFloatStateValid; - unsigned int m_floatStackTop; - double m_floatValues[DebuggerIPCE_FloatCount]; - private: // True for the window after an Exception callback, but before it's been continued. // We dispatch two exception events in a row (ICDManagedCallback::Exception and ICDManagedCallback2::Exception), @@ -6368,14 +6353,15 @@ class CordbStackWalk : public CordbBase, public ICorDebugStackWalk // unwind the frame and update m_context with the new context BOOL UnwindStackFrame(); + ContextBuffer GetContextBuffer() const { return { m_pContextBuffer, GetProcess()->GetTargetContextSize() }; } + // the thread on which this CordbStackWalk is created CordbThread * m_pCordbThread; // This is the same iterator used by the runtime itself. IDacDbiInterface::StackWalkHandle m_pSFIHandle; - // buffers used for stackwalking - DT_CONTEXT m_context; + NewArrayHolder m_pContextBuffer; // Used to figure out if we have to refresh any reference objects // on the left side. We set it to CordbProcess::m_flushCounter on @@ -6474,7 +6460,14 @@ class CordbFrame : public CordbBase, public ICorDebugFrame // This is basically a complicated cast function. We are casting from an ICorDebugFrame to a CordbFrame. static CordbFrame* GetCordbFrameFromInterface(ICorDebugFrame *pFrame); - virtual const DT_CONTEXT * GetContext() const { return NULL; } + virtual const ContextBuffer GetContext() const { return ContextBuffer{}; } + + // Reads an integer register from this frame's CONTEXT buffer via the DAC. + HRESULT ReadContextRegister(CorDebugRegister reg, TADDR * pValue) const; + + // Reads a floating-point / SIMD register's scalar value (as a double bit + // pattern) from this frame's CONTEXT buffer via the DAC. + HRESULT ReadFloatContextRegister(CorDebugRegister reg, double * pValue) const; public: // this represents the IL offset for a CordbJITILFrame, the native offset for a CordbNativeFrame, @@ -6671,7 +6664,7 @@ class CordbRuntimeUnwindableFrame : public CordbFrame, public ICorDebugRuntimeUn CordbRuntimeUnwindableFrame(CordbThread * pThread, FramePointer fp, CordbAppDomain * pCurrentAppDomain, - DT_CONTEXT * pContext); + ContextBuffer contextBuffer); virtual void Neuter(); @@ -6753,10 +6746,11 @@ class CordbRuntimeUnwindableFrame : public CordbFrame, public ICorDebugRuntimeUn return NULL; } - virtual const DT_CONTEXT * GetContext() const; + virtual const ContextBuffer GetContext() const; private: - DT_CONTEXT m_context; + NewArrayHolder m_pContextBuffer; + }; // Function signature for retrieving a value at a specific index @@ -6843,7 +6837,7 @@ class CordbNativeFrame : public CordbFrame, public ICorDebugNativeFrame, public TADDR addrAmbientESP, CordbAppDomain * pCurrentAppDomain, CordbMiscFrame * pMisc = NULL, - DT_CONTEXT * pContext = NULL); + ContextBuffer contextBuffer = {}); virtual ~CordbNativeFrame(); virtual void Neuter(); @@ -6954,7 +6948,7 @@ class CordbNativeFrame : public CordbFrame, public ICorDebugNativeFrame, public CordbFunction * GetFunction(); CordbNativeCode * GetNativeCode(); - virtual const DT_CONTEXT * GetContext() const; + virtual const ContextBuffer GetContext() const; // Given the native variable information of a variable, return its value. // This function assumes that the value is either in a register or on the stack @@ -6982,7 +6976,6 @@ class CordbNativeFrame : public CordbFrame, public ICorDebugNativeFrame, public CorDebugRegister lowWordRegister, CordbType * pType, ICorDebugValue **ppValue); - UINT_PTR * GetAddressOfRegister(CorDebugRegister regNum) const; CORDB_ADDRESS GetLeftSideAddressOfRegister(CorDebugRegister regNum) const; HRESULT GetLocalFloatingPointValue(DWORD index, CordbType * pType, @@ -7001,6 +6994,12 @@ class CordbNativeFrame : public CordbFrame, public ICorDebugNativeFrame, public CordbType * pType, ICorDebugValue **ppValue); + HRESULT ReadJitRegFromContext(ULONG32 jitRegNum, TADDR * pValue) const; + + CorDebugRegister ConvertJitRegToCorDebugRegister(ULONG32 jitRegNum) const; + + CORDB_ADDRESS GetStackPointer() const; + CORDB_ADDRESS GetLSStackAddress(ICorDebugInfo::RegNum regNum, signed offset); @@ -7020,7 +7019,6 @@ class CordbNativeFrame : public CordbFrame, public ICorDebugNativeFrame, public SIZE_T GetParentIP(); TADDR GetAmbientESP() { return m_taAmbientESP; } - TADDR GetReturnRegisterValue(); // accessor for the shim private hook code:CordbThread::ConvertFrameForILMethodWithoutMetadata BOOL ConvertNativeFrameForILMethodWithoutMetadata(ICorDebugInternalFrame2 ** ppInternalFrame2); @@ -7030,7 +7028,6 @@ class CordbNativeFrame : public CordbFrame, public ICorDebugNativeFrame, public //----------------------------------------------------------- public: - // each CordbNativeFrame corresponds to exactly one CordbJITILFrame and one CordbNativeCode RSSmartPtr m_JITILFrame; RSSmartPtr m_nativeCode; @@ -7042,8 +7039,7 @@ class CordbNativeFrame : public CordbFrame, public ICorDebugNativeFrame, public // the ambient SP value only used on x86 to retrieve sp-relative local variables // (most likely in a frameless method) TADDR m_taAmbientESP; - - DT_CONTEXT m_context; + NewArrayHolder m_pContextBuffer; }; @@ -7065,10 +7061,11 @@ class CordbNativeFrame : public CordbFrame, public ICorDebugNativeFrame, public class CordbRegisterSet : public CordbBase, public ICorDebugRegisterSet, public ICorDebugRegisterSet2 { public: - CordbRegisterSet(CordbThread * pThread, - DT_CONTEXT * pContext, + CordbRegisterSet(ContextBuffer contextBuffer, + CordbThread * pThread, bool fActive, - bool fQuickUnwind); + bool fQuickUnwind, + bool fTakeOwnershipOfContext = false); ~CordbRegisterSet(); @@ -7164,15 +7161,12 @@ class CordbRegisterSet : public CordbBase, public ICorDebugRegisterSet, public I } protected: - - // Adapters to impl v2.0 interfaces on top of v1.0 interfaces. - HRESULT GetRegistersAvailableAdapter(ULONG32 regCount, BYTE pAvailable[]); - HRESULT GetRegistersAdapter(ULONG32 maskCount, BYTE mask[], ULONG32 regCount, CORDB_REGISTER regBuffer[]); - - DT_CONTEXT m_context; + ContextBuffer m_contextBuffer; CordbThread *m_thread; bool m_active; // true if we're the leafmost register set. bool m_quickUnwind; + + bool m_fOwnsContext; } ; @@ -7719,7 +7713,7 @@ class EnregisteredValueHome // set a remote enregistered location to a new value // Arguments: // input: pNewValue - buffer containing the new value along with its size - // pContext - context from which the value comes + // contextBuffer - context from which the value comes // fIsSigned - indicates whether the value is signed or not. The value provided may be smaller than // a register, in which case we'll need to extend it to a full register width. To do this // correctly, we need to know whether to sign extend or zero extend. Currently, only @@ -7729,7 +7723,7 @@ class EnregisteredValueHome // Note: Throws E_FAIL for invalid input or various HRESULTs from an // unsuccessful call to WriteProcessMemory virtual - void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned) = 0; + void SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned) = 0; // Gets an enregistered value and returns it to the caller // Arguments: @@ -7781,7 +7775,7 @@ class RegValueHome: public EnregisteredValueHome EnregisteredValueHome(pFrame), m_reg1Info(regNum, pFrame->GetLeftSideAddressOfRegister(regNum), - *(pFrame->GetAddressOfRegister(regNum))) + ReadFrameRegister(pFrame, regNum)) {}; // copy constructor @@ -7798,14 +7792,13 @@ class RegValueHome: public EnregisteredValueHome virtual RegValueHome * Clone() const { return new RegValueHome(*this); }; - // updates a register in a given context, and in the regdisplay of a given frame. - void SetContextRegister(DT_CONTEXT * pContext, + void SetContextRegister(ContextBuffer contextBuffer, CorDebugRegister regNum, SIZE_T newVal); // set the value of a remote enregistered value virtual - void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned); + void SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned); // Gets an enregistered value and returns it to the caller virtual @@ -7815,6 +7808,15 @@ class RegValueHome: public EnregisteredValueHome virtual void CopyToIPCEType(RemoteAddress * pRegAddr); +protected: + static SIZE_T ReadFrameRegister(const CordbNativeFrame * pFrame, CorDebugRegister regNum) + { + TADDR v = 0; + HRESULT hr = pFrame->ReadContextRegister(regNum, &v); + IfFailThrow(hr); + return (SIZE_T)v; + } + //------------------------------------- // data members //------------------------------------- @@ -7846,7 +7848,7 @@ class RegRegValueHome: public RegValueHome RegValueHome(pFrame, reg1Num), m_reg2Info(reg2Num, pFrame->GetLeftSideAddressOfRegister(reg2Num), - *(pFrame->GetAddressOfRegister(reg2Num))) + ReadFrameRegister(pFrame, reg2Num)) {}; // copy constructor @@ -7865,7 +7867,7 @@ class RegRegValueHome: public RegValueHome // set the value of a remote enregistered value virtual - void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned); + void SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned); // Gets an enregistered value and returns it to the caller virtual @@ -7888,7 +7890,7 @@ class RegRegValueHome: public RegValueHome // EnregisteredValueHome for a value that lives in two registers where at least one is a // floating-point register (e.g. a 16-byte struct returned in XMM0+XMM1 on Unix x64, or a // mixed int/fp multi-register return). -// Floating-point register contents are not reachable through the integer register display, so +// Floating-point register contents are stored separately from the frame's integer context, so // rather than referencing live registers this home captures a snapshot of the 16-byte value // (low 8 bytes followed by high 8 bytes) when it is created. The snapshot is used to populate // the value's local object copy and is cloned for field access. Writing back to a @@ -7925,7 +7927,7 @@ class TwoRegisterValueHome: public EnregisteredValueHome // writing back to a multi-register return value is not supported virtual - void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned) + void SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned) { ThrowHR(CORDBG_E_SET_VALUE_NOT_ALLOWED_ON_NONLEAF_FRAME); }; @@ -7987,7 +7989,7 @@ class RegAndMemBaseValueHome: public RegValueHome // set the value of a remote enregistered value virtual - void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * DT_pContext, bool fIsSigned) = 0; + void SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned) = 0; // Gets an enregistered value and returns it to the caller virtual @@ -8045,7 +8047,7 @@ class RegMemValueHome: public RegAndMemBaseValueHome // set the value of a remote enregistered value virtual - void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned); + void SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned); // Gets an enregistered value and returns it to the caller virtual @@ -8095,7 +8097,7 @@ class MemRegValueHome: public RegAndMemBaseValueHome // set the value of a remote enregistered value virtual - void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned); + void SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned); // Gets an enregistered value and returns it to the caller virtual @@ -8117,11 +8119,14 @@ class FloatRegValueHome: public EnregisteredValueHome // Arguments: // input: pFrame - frame to which the value belongs // index - index into the floating point stack where the value resides + // regNum - CorDebugRegister naming the fp / SIMD register // output: no out parameters, but the instance has been initialized FloatRegValueHome(const CordbNativeFrame * pFrame, - DWORD index): + DWORD index, + CorDebugRegister regNum): EnregisteredValueHome(pFrame), - m_floatIndex(index) + m_floatIndex(index), + m_regNum(regNum) {}; // copy constructor @@ -8131,7 +8136,8 @@ class FloatRegValueHome: public EnregisteredValueHome // output: no out parameters, but the instance has been initialized FloatRegValueHome(const FloatRegValueHome * pRemoteRegAddr): EnregisteredValueHome(pRemoteRegAddr->m_pFrame), - m_floatIndex(pRemoteRegAddr->m_floatIndex) + m_floatIndex(pRemoteRegAddr->m_floatIndex), + m_regNum(pRemoteRegAddr->m_regNum) {}; // make a copy of this instance of FloatRegValueHome @@ -8140,7 +8146,7 @@ class FloatRegValueHome: public EnregisteredValueHome // set the value of a remote enregistered value virtual - void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned); + void SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned); // Gets an enregistered value and returns it to the caller virtual @@ -8158,6 +8164,10 @@ class FloatRegValueHome: public EnregisteredValueHome protected: // index into the FP registers for the register in which the floating point value resides const DWORD m_floatIndex; + + // CorDebugRegister naming the fp / SIMD register, used to write the value + // back into the target CONTEXT via the DAC. + const CorDebugRegister m_regNum; }; // class FloatRegValueHome // ---------------------------------------------------------------------------- @@ -10433,8 +10443,8 @@ class CordbUnmanagedThread : public CordbBase // These are wrappers for the OS calls which hide // the effects of hijacking and internal SS flag usage - HRESULT GetThreadContext(DT_CONTEXT * pContext); - HRESULT SetThreadContext(DT_CONTEXT * pContext); + HRESULT GetThreadContext(T_CONTEXT * pContext); + HRESULT SetThreadContext(T_CONTEXT * pContext); // Turns on and off the internal usage of the SS flag VOID BeginStepping(); @@ -10442,7 +10452,7 @@ class CordbUnmanagedThread : public CordbBase // An accessor for &m_context, this value generally stores // a context we may need to restore after a hijack completes - DT_CONTEXT * GetHijackCtx(); + T_CONTEXT * GetHijackCtx(); private: CORDB_ADDRESS m_stackBase; @@ -10479,9 +10489,7 @@ class CordbUnmanagedThread : public CordbBase } void ClearState(CordbUnmanagedThreadState state) {LIMITED_METHOD_CONTRACT; m_state = (CordbUnmanagedThreadState)(m_state & ~state); } - void HijackToRaiseException(); void RestoreFromRaiseExceptionHijack(); - void SaveRaiseExceptionEntryContext(); void ClearRaiseExceptionEntryContext(); BOOL IsExceptionFromLastRaiseException(const EXCEPTION_RECORD* pExceptionRecord); @@ -10501,9 +10509,6 @@ class CordbUnmanagedThread : public CordbBase HRESULT RestoreLeafSeh(); #endif - // Logs basic data about a context to the debugging log - static VOID LogContext(DT_CONTEXT* pContext); - public: HANDLE m_handle; @@ -10531,10 +10536,7 @@ class CordbUnmanagedThread : public CordbBase private: // Spare context used for various purposes. // See CordbUnmanagedThread::GetThreadContext for details - DT_CONTEXT m_context; - - // The context of the thread the last time it called into kernel32!RaiseException - DT_CONTEXT m_raiseExceptionEntryContext; + T_CONTEXT m_context; DWORD m_raiseExceptionExceptionCode; DWORD m_raiseExceptionExceptionFlags; diff --git a/src/coreclr/debug/di/rsregsetcommon.cpp b/src/coreclr/debug/di/rsregsetcommon.cpp index c5f7e85ee3cca4..82835e6c7bb10d 100644 --- a/src/coreclr/debug/di/rsregsetcommon.cpp +++ b/src/coreclr/debug/di/rsregsetcommon.cpp @@ -4,9 +4,10 @@ // File: RSRegSetCommon.cpp // -// Common cross-platform behavior of reg sets. -// Platform specific stuff is in CordbRegisterSet.cpp located in -// the platform sub-dir. +// Common cross-platform behavior of reg sets. The ICorDebugRegisterSet / +// ICorDebugRegisterSet2 surface is implemented in CordbRegisterSet.cpp using the +// ReadRegistersFromContext / WriteRegistersToContext / GetAvailableRegistersMask +// DDIs, which cover both integer (GPR) and floating-point / SIMD registers. // //***************************************************************************** #include "stdafx.h" @@ -18,21 +19,27 @@ CordbRegisterSet::CordbRegisterSet( + ContextBuffer contextBuffer, CordbThread * pThread, - DT_CONTEXT * pContext, bool fActive, - bool fQuickUnwind) + bool fQuickUnwind, + bool fTakeOwnershipOfContext /*= false*/) : CordbBase(pThread->GetProcess(), 0, enumCordbRegisterSet) { - _ASSERTE( pContext != NULL ); - _ASSERTE( pThread != NULL ); - m_thread = pThread; - m_context = *pContext; - m_active = fActive; - m_quickUnwind = fQuickUnwind; + _ASSERTE(pThread != NULL); + if ((contextBuffer.pContextBytes == NULL) || + (contextBuffer.contextSize < pThread->GetProcess()->GetTargetContextSize())) + { + ThrowHR(E_INVALIDARG); + } - // Add to our parent thread's neuter list. + m_contextBuffer = contextBuffer; + m_fOwnsContext = fTakeOwnershipOfContext; + m_thread = pThread; + m_active = fActive; + m_quickUnwind = fQuickUnwind; + // Add to our parent thread's neuter list. HRESULT hr = S_OK; EX_TRY { @@ -45,6 +52,14 @@ CordbRegisterSet::CordbRegisterSet( void CordbRegisterSet::Neuter() { m_thread = NULL; + + if (m_fOwnsContext) + { + delete[] m_contextBuffer.pContextBytes; + } + m_contextBuffer.pContextBytes = NULL; + m_contextBuffer.contextSize = 0; + CordbBase::Neuter(); } @@ -108,127 +123,52 @@ HRESULT CordbRegisterSet::GetThreadContext(ULONG32 contextSize, BYTE context[]) EX_TRY { _ASSERTE( m_thread != NULL ); - if( contextSize < sizeof( DT_CONTEXT )) + if( contextSize < GetProcess()->GetTargetContextSize()) { ThrowHR(E_INVALIDARG); } ValidateOrThrow(context); - DT_CONTEXT *pInputContext = reinterpret_cast (context); - - // Just to be safe, zero out the buffer we got in while preserving the ContextFlags. - // On X64 the ContextFlags field is not the first 4 bytes of the DT_CONTEXT. - DWORD dwContextFlags = pInputContext->ContextFlags; - ZeroMemory(context, contextSize); - pInputContext->ContextFlags = dwContextFlags; + IDacDbiInterface * pDAC = GetProcess()->GetDAC(); + ULONG32 targetContextSize = GetProcess()->GetTargetContextSize(); - // Augment the leafmost (active) register w/ information from the current context. - DT_CONTEXT * pLeafContext = NULL; + ContextBuffer leafContext = {}; if (m_active) { EX_TRY { // This may fail, but it is not a disastrous failure in this case. All we care is whether - // pLeafContext is updated to a non-NULL value. - m_thread->GetManagedContext( &pLeafContext); + // leafContext is updated to contain a non-NULL value. + m_thread->GetManagedContext(&leafContext); } EX_CATCH { } EX_END_CATCH - if (pLeafContext != NULL) + if (leafContext.pContextBytes != NULL) { - // @todo - shouldn't this be a context-flags sensitive copy? - memmove( pInputContext, pLeafContext, sizeof( DT_CONTEXT) ); + if (leafContext.contextSize < targetContextSize) + { + ThrowHR(E_INVALIDARG); + } + + // Raw byte copy of the leaf context, which carries the leaf's ContextFlags into the + // destination so the flag-sensitive overlay below is gated on those flags. + memcpy(context, leafContext.pContextBytes, targetContextSize); } } - CORDbgCopyThreadContext(pInputContext, &m_context); + + // Overlay this frame's registers from the cached CONTEXT buffer, honoring the destination's + // ContextFlags (the leaf's if copied above, otherwise the caller's incoming flags). + ContextBuffer destinationContext = { context, contextSize }; + IfFailThrow(pDAC->CopyContext( + destinationContext, + m_contextBuffer, + IDacDbiInterface::kCopyContextPreserveDestinationFlags, + 0)); } EX_CATCH_HRESULT(hr); return hr; } - -//----------------------------------------------------------------------------- -// Helpers to impl IRegSet2 on top of original IRegSet. -// These are useful on platforms that don't need IRegSet2 (like x86 + amd64). -// See CorDebug.idl for details. -// -// Inputs: -// regCount - size of pAvailable buffer in bytes -// pAvailable - buffer to hold bitvector of available registers. -// On success, bit at position CorDebugRegister is 1 iff that -// register is available. -// Returns S_OK on success. -//----------------------------------------------------------------------------- -HRESULT CordbRegisterSet::GetRegistersAvailableAdapter( - ULONG32 regCount, - BYTE pAvailable[]) -{ - // Defer to call on v1.0 interface - HRESULT hr = S_OK; - - if (regCount < sizeof(ULONG64)) - { - return E_INVALIDARG; - } - - _ASSERTE(pAvailable != NULL); - - ULONG64 availRegs; - hr = this->GetRegistersAvailable(&availRegs); - if (FAILED(hr)) - { - return hr; - } - - // Nor marshal our 64-bit value into the outgoing byte array. - for(int iBit = 0; iBit < (int) sizeof(availRegs) * 8; iBit++) - { - ULONG64 test = SETBITULONG64(iBit); - if (availRegs & test) - { - SET_BIT_MASK(pAvailable, iBit); - } - else - { - RESET_BIT_MASK(pAvailable, iBit); - } - } - return S_OK; -} - -//----------------------------------------------------------------------------- -// Helpers to impl IRegSet2 on top of original IRegSet. -// These are useful on platforms that don't need IRegSet2 (like x86 + amd64). -// See CorDebug.idl for details. -// -// Inputs: -// maskCount - size of mask buffer in bytes. -// mask - input buffer specifying registers to request -// regCount - size of regBuffer in bytes -// regBuffer - output buffer, regBuffer[n] = value of register at n-th active -// bit in mask. -// Returns S_OK on success. -//----------------------------------------------------------------------------- - -// mask input request registers, which get written to regCount buffer. -HRESULT CordbRegisterSet::GetRegistersAdapter( - ULONG32 maskCount, BYTE mask[], - ULONG32 regCount, CORDB_REGISTER regBuffer[]) -{ - // Convert input mask to orig mask. - ULONG64 maskOrig = 0; - - for(UINT iBit = 0; iBit < maskCount * 8; iBit++) - { - if (IS_SET_BIT_MASK(mask, iBit)) - { - maskOrig |= SETBITULONG64(iBit); - } - } - - return this->GetRegisters(maskOrig, - regCount, regBuffer); -} diff --git a/src/coreclr/debug/di/rsstackwalk.cpp b/src/coreclr/debug/di/rsstackwalk.cpp index 4e155e73b5c833..ad50faf8379435 100644 --- a/src/coreclr/debug/di/rsstackwalk.cpp +++ b/src/coreclr/debug/di/rsstackwalk.cpp @@ -35,9 +35,12 @@ void CordbStackWalk::Init() m_lastSyncFlushCounter = pProcess->m_flushCounter; IDacDbiInterface * pDAC = pProcess->GetDAC(); + ULONG32 contextSize = pProcess->GetTargetContextSize(); + m_pContextBuffer = new BYTE[contextSize]; + IfFailThrow(pDAC->CreateStackWalk(m_pCordbThread->m_vmThreadToken, - &m_context, - &m_pSFIHandle)); + GetContextBuffer(), + &m_pSFIHandle)); // see the function header of code:CordbStackWalk::CheckForLegacyHijackCase CheckForLegacyHijackCase(); @@ -74,13 +77,18 @@ void CordbStackWalk::CheckForLegacyHijackCase() if (pUT->IsFirstChanceHijacked() || pUT->IsGenericHijacked()) { // The GetThreadContext function hides the effects of hijacking and returns the unhijacked context - m_context.ContextFlags = DT_CONTEXT_FULL; - pUT->GetThreadContext(&m_context); IDacDbiInterface * pDAC = GetProcess()->GetDAC(); + if (GetProcess()->GetTargetContextSize() < sizeof(T_CONTEXT)) + { + ThrowHR(E_FAIL); + } + T_CONTEXT * pContext = reinterpret_cast(m_pContextBuffer.GetValue()); + pContext->ContextFlags = CONTEXT_FULL; + pUT->GetThreadContext(pContext); IfFailThrow(pDAC->SetStackWalkCurrentContext(m_pCordbThread->m_vmThreadToken, m_pSFIHandle, SET_CONTEXT_FLAG_ACTIVE_FRAME, - &m_context)); + GetContextBuffer())); } } } @@ -194,19 +202,21 @@ void CordbStackWalk::RefreshIfNeeded() // check if we need to refresh if (m_lastSyncFlushCounter != pProcess->m_flushCounter) { - // Make a local copy of the CONTEXT here. - DT_CONTEXT ctx = m_context; + // Make a local copy of the CONTEXT here. DeleteAll() will delete the CONTEXT on the cached frame, + // and CreateStackWalk() actually uses the CONTEXT buffer we pass to it. + NewArrayHolder ctx(new BYTE[pProcess->GetTargetContextSize()]); + memcpy(ctx, m_pContextBuffer, pProcess->GetTargetContextSize()); // clear all the state DeleteAll(); // create a new stackwalk handle IfFailThrow(pProcess->GetDAC()->CreateStackWalk(m_pCordbThread->m_vmThreadToken, - &m_context, - &m_pSFIHandle)); + GetContextBuffer(), + &m_pSFIHandle)); // advance the stackwalker to where we originally were - SetContextWorker(m_cachedSetContextFlag, sizeof(DT_CONTEXT), reinterpret_cast(&ctx)); + SetContextWorker(m_cachedSetContextFlag, pProcess->GetTargetContextSize(), ctx); // update the sync counter m_lastSyncFlushCounter = pProcess->m_flushCounter; @@ -265,9 +275,9 @@ HRESULT CordbStackWalk::GetContext(ULONG32 contextFlags, ThrowWin32(ERROR_INSUFFICIENT_BUFFER); } - // We have to call the DDI. IDacDbiInterface * pDAC = GetProcess()->GetDAC(); + // We have to call the DDI. IDacDbiInterface::FrameType ft; IfFailThrow(pDAC->GetStackWalkCurrentFrameInfo(m_pSFIHandle, NULL, &ft)); if (ft == IDacDbiInterface::kInvalid) @@ -285,7 +295,14 @@ HRESULT CordbStackWalk::GetContext(ULONG32 contextFlags, else { // We always store the current CONTEXT, so just copy it into the buffer. - CORDbgCopyThreadContext(pContext, &m_context); + // Stamp the requested contextFlags on the destination so the copy + // pulls exactly those chunks from the source. + ContextBuffer destinationContext = { pbContextBuf, contextBufSize }; + IfFailThrow(pDAC->CopyContext( + destinationContext, + GetContextBuffer(), + IDacDbiInterface::kCopyContextUseExplicitFlags, + contextFlags)); } } } @@ -338,28 +355,27 @@ void CordbStackWalk::SetContextWorker(CorDebugSetContextFlag flag, ULONG32 conte ThrowWin32(ERROR_INSUFFICIENT_BUFFER); } - DT_CONTEXT * pSrcContext = reinterpret_cast(context); - - // Check the incoming CONTEXT using a temporary CONTEXT buffer before updating our real CONTEXT buffer. - // The incoming CONTEXT is not required to have all the bits set in its CONTEXT flags, so only update - // the registers specified by the CONTEXT flags. Note that CORDbgCopyThreadContext() honours the CONTEXT - // flags on both the source and the destination CONTEXTs when it copies them. - DT_CONTEXT tmpCtx = m_context; - tmpCtx.ContextFlags |= pSrcContext->ContextFlags; - CORDbgCopyThreadContext(&tmpCtx, pSrcContext); - IDacDbiInterface * pDAC = GetProcess()->GetDAC(); - IfFailThrow(pDAC->CheckContext(m_pCordbThread->m_vmThreadToken, &tmpCtx)); - // At this point we have done all of our checks to verify that the incoming CONTEXT is sane, so we can - // update our internal CONTEXT buffer. - m_context = tmpCtx; + ULONG32 cbCtx = GetProcess()->GetTargetContextSize(); + NewArrayHolder tmpCtx(new BYTE[cbCtx]); + memcpy(tmpCtx, m_pContextBuffer, cbCtx); + ContextBuffer temporaryContext = { tmpCtx, cbCtx }; + ContextBuffer sourceContext = { context, contextSize }; + IfFailThrow(pDAC->CopyContext( + temporaryContext, + sourceContext, + IDacDbiInterface::kCopyContextMergeSourceFlags, + 0)); + IfFailThrow(pDAC->CheckContext(m_pCordbThread->m_vmThreadToken, temporaryContext)); + + memcpy(m_pContextBuffer, tmpCtx, cbCtx); m_cachedSetContextFlag = flag; IfFailThrow(pDAC->SetStackWalkCurrentContext(m_pCordbThread->m_vmThreadToken, m_pSFIHandle, flag, - &m_context)); + GetContextBuffer())); } //--------------------------------------------------------------------------------------- @@ -385,7 +401,9 @@ BOOL CordbStackWalk::UnwindStackFrame() // Now that we have unwound, make sure we update the CONTEXT buffer to reflect the current stack frame. if (retVal) - IfFailThrow(pDAC->GetStackWalkCurrentContext(m_pSFIHandle, &m_context)); + { + IfFailThrow(pDAC->GetStackWalkCurrentContext(m_pSFIHandle, GetContextBuffer())); + } return retVal; } // CordbStackWalk::UnwindStackWalkFrame @@ -488,20 +506,20 @@ HRESULT CordbStackWalk::GetFrameWorker(ICorDebugFrame ** ppFrame) RSInitHolder pResultFrame(NULL); - IDacDbiInterface * pDAC = NULL; + IDacDbiInterface * pDAC = GetProcess()->GetDAC(); Debugger_STRData frameData; ZeroMemory(&frameData, sizeof(frameData)); - // Allocate the DT_CONTEXT buffer on the dbi stack and - // hand the address to the DAC via Debugger_STRData. The DAC writes the - // populated context through this pointer. - DT_CONTEXT frameCtx; - ZeroMemory(&frameCtx, sizeof(frameCtx)); - frameData.ctx = &frameCtx; + // Allocate the CONTEXT buffer (sized to the target's CONTEXT) on the dbi heap + // and hand its address to the DAC via Debugger_STRData. The DAC writes the + // populated context through this pointer. The buffer is opaque target bytes; + // see the comment on Debugger_STRData in dbgipcevents.h for the protocol. + NewArrayHolder frameCtx(new BYTE[GetProcess()->GetTargetContextSize()]); + ZeroMemory(frameCtx, GetProcess()->GetTargetContextSize()); + frameData.ctx = { frameCtx, GetProcess()->GetTargetContextSize() }; IDacDbiInterface::FrameType ft = IDacDbiInterface::kInvalid; - pDAC = GetProcess()->GetDAC(); IfFailThrow(pDAC->GetStackWalkCurrentFrameInfo(m_pSFIHandle, &frameData, &ft)); if (ft == IDacDbiInterface::kInvalid) @@ -585,6 +603,7 @@ HRESULT CordbStackWalk::GetFrameWorker(ICorDebugFrame ** ppFrame) // initialize the auxiliary info required for funclets CordbMiscFrame miscFrame(pJITFuncData); + ContextBuffer contextBuffer = { frameCtx, GetProcess()->GetTargetContextSize() }; // Create the native frame. CordbNativeFrame* pNativeFrame = new CordbNativeFrame(m_pCordbThread, @@ -594,7 +613,7 @@ HRESULT CordbStackWalk::GetFrameWorker(ICorDebugFrame ** ppFrame) (TADDR)frameData.v.taAmbientESP, pCurrentAppDomain, &miscFrame, - &frameCtx); + contextBuffer); pResultFrame.Assign(static_cast(pNativeFrame)); @@ -716,11 +735,12 @@ HRESULT CordbStackWalk::GetFrameWorker(ICorDebugFrame ** ppFrame) // to the frame when we create it so we can properly resolve locals in that frame later. CordbAppDomain * pCurrentAppDomain = GetProcess()->GetAppDomain(); _ASSERTE(pCurrentAppDomain != NULL); + ContextBuffer contextBuffer = { frameCtx, GetProcess()->GetTargetContextSize() }; CordbRuntimeUnwindableFrame * pRuntimeFrame = new CordbRuntimeUnwindableFrame(m_pCordbThread, FramePointer::MakeFramePointer(CORDB_ADDRESS_TO_PTR(frameData.fp)), pCurrentAppDomain, - &frameCtx); + contextBuffer); pResultFrame.Assign(static_cast(pRuntimeFrame)); diff --git a/src/coreclr/debug/di/rsthread.cpp b/src/coreclr/debug/di/rsthread.cpp index 08c760735f91a1..4ca74e24f18827 100644 --- a/src/coreclr/debug/di/rsthread.cpp +++ b/src/coreclr/debug/di/rsthread.cpp @@ -77,8 +77,6 @@ CordbThread::CordbThread(CordbProcess * pProcess, VMPTR_Thread vmThread) : m_pAppDomain(NULL), m_debugState(THREAD_RUN), m_fFramesFresh(false), - m_fFloatStateValid(false), - m_floatStackTop(0), m_fException(false), m_EnCRemapFunctionIP(0), m_userState(kInvalidUserState), @@ -101,18 +99,9 @@ CordbThread::CordbThread(CordbProcess * pProcess, VMPTR_Thread vmThread) : // Unique ID should never be 0. _ASSERTE(m_dwUniqueID != 0); - m_vmLeftSideContext = VMPTR_CONTEXT::NullPtr(); + m_vmLeftSideContext = (CORDB_ADDRESS)NULL; m_vmExcepObjHandle = VMPTR_OBJECTHANDLE::NullPtr(); -#if defined(_DEBUG) - for (unsigned int i = 0; - i < (sizeof(m_floatValues) / sizeof(m_floatValues[0])); - i++) - { - m_floatValues[i] = 0; - } -#endif - // Set AppDomain m_pAppDomain = pProcess->GetAppDomain(); _ASSERTE(m_pAppDomain != NULL); @@ -154,11 +143,9 @@ void CordbThread::Neuter() m_hCachedThread = INVALID_HANDLE_VALUE; } - if( m_pContext != NULL ) - { - delete [] m_pContext; - m_pContext = NULL; - } + // Free the RS context cache buffer now (Neuter), so the destructor's + // m_pContext == NULL invariant holds. Clear() releases and resets to NULL. + m_pContext.Clear(); ClearStackFrameCache(); @@ -302,7 +289,7 @@ BOOL CordbThread::IsThreadExceptionManaged() // This is a private hook for the shim to create a CordbRegisterSet for a ShimChain. // // Arguments: -// * pContext - the CONTEXT to be converted; this must be the leaf CONTEXT of a chain +// * contextBuffer - the CONTEXT to be converted; this must be the leaf CONTEXT of a chain // * fLeaf - whether the chain is the leaf chain or not // * reason - the chain reason; this is needed for legacy reasons (see below) // * ppRegSet - out parameter; return the newly created ICDRegisterSet @@ -313,7 +300,7 @@ BOOL CordbThread::IsThreadExceptionManaged() // chain reason. // -void CordbThread::CreateCordbRegisterSet(DT_CONTEXT * pContext, +void CordbThread::CreateCordbRegisterSet(ContextBuffer contextBuffer, BOOL fLeaf, CorDebugChainReason reason, ICorDebugRegisterSet ** ppRegSet) @@ -327,12 +314,26 @@ void CordbThread::CreateCordbRegisterSet(DT_CONTEXT * pContext, PUBLIC_REENTRANT_API_ENTRY_FOR_SHIM(GetProcess()); IfFailThrow(EnsureThreadIsAlive()); + ULONG32 expectedContextSize = GetProcess()->GetTargetContextSize(); + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < expectedContextSize)) + { + ThrowHR(E_INVALIDARG); + } + + // Allocate and populate a CONTEXT buffer that the CordbRegisterSet will own. + // The caller passes the target's CONTEXT size (queried from the DAC). + NewArrayHolder pContextBuffer(new BYTE[expectedContextSize]); + memcpy(pContextBuffer, contextBuffer.pContextBytes, expectedContextSize); // create the CordbRegisterSet - RSInitHolder pRS(new CordbRegisterSet(this, - pContext, + ContextBuffer registerContext = { pContextBuffer, expectedContextSize }; + RSInitHolder pRS(new CordbRegisterSet(registerContext, + this, (fLeaf == TRUE), - (reason == CHAIN_ENTER_MANAGED))); + (reason == CHAIN_ENTER_MANAGED), + true)); + pContextBuffer.SuppressRelease(); + pRS.TransferOwnershipExternal(ppRegSet); } @@ -1146,15 +1147,21 @@ HRESULT CordbThread::GetRegisterSet(ICorDebugRegisterSet ** ppRegisters) IfFailThrow(hr); // retrieve the leaf CONTEXT - DT_CONTEXT ctx; - hr = pSW->GetContext(CONTEXT_FULL, sizeof(ctx), NULL, reinterpret_cast(&ctx)); - IfFailThrow(hr); + + // Allocate and populate a CONTEXT buffer that the CordbRegisterSet will own. + // The buffer is sized to the target's CONTEXT layout. + ULONG32 contextSize = GetProcess()->GetTargetContextSize(); + NewArrayHolder pContextBuffer(new BYTE[contextSize]); + IfFailThrow(pSW->GetContext(CONTEXT_FULL, contextSize, NULL, pContextBuffer)); // create the CordbRegisterSet - RSInitHolder pRS(new CordbRegisterSet(this, - &ctx, + ContextBuffer contextBuffer = { pContextBuffer, contextSize }; + RSInitHolder pRS(new CordbRegisterSet(contextBuffer, + this, true, // active - false)); + false, // !fQuickUnwind + true)); // own context buffer + pContextBuffer.SuppressRelease(); pRS.TransferOwnershipExternal(ppRegisters); } @@ -1290,7 +1297,7 @@ void CordbThread::CleanupStack() m_RefreshStackNeuterList.NeuterAndClear(GetProcess()); m_fContextFresh = false; // invalidate the cached active CONTEXT - m_vmLeftSideContext = VMPTR_CONTEXT::NullPtr(); // set the LS pointer to the active CONTEXT to NULL + m_vmLeftSideContext = (CORDB_ADDRESS)NULL; // set the LS pointer to the active CONTEXT to NULL m_fFramesFresh = false; // invalidate the cached stack trace (frames & chains) m_userState = kInvalidUserState; // clear the cached user state @@ -1309,9 +1316,6 @@ void CordbThread::MarkStackFramesDirty() _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); - // invalidate the cached floating point state - m_fFloatStateValid = false; - // This flag is only true between the window when we get an exception callback and // when we call continue. Since this function is only called when we continue, we // need to reset this flag here. Note that in the case of an outstanding funceval, @@ -1324,7 +1328,7 @@ void CordbThread::MarkStackFramesDirty() m_EnCRemapFunctionIP = 0; m_fContextFresh = false; // invalidate the cached active CONTEXT - m_vmLeftSideContext = VMPTR_CONTEXT::NullPtr(); // set the LS pointer to the active CONTEXT to NULL + m_vmLeftSideContext = (CORDB_ADDRESS)NULL; // set the LS pointer to the active CONTEXT to NULL m_fFramesFresh = false; // invalidate the cached stack trace (frames & chains) m_userState = kInvalidUserState; // clear the cached user state @@ -1413,183 +1417,6 @@ HRESULT CordbThread::FindFrame(ICorDebugFrame ** ppFrame, FramePointer fp) -#if defined(CROSS_COMPILE) && (defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64)) -extern "C" double FPFillR8(void* pFillSlot) -{ - _ASSERTE(!"nyi for platform"); - return 0; -} -#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) -extern "C" double FPFillR8(void* pFillSlot); -#endif - - -#if defined(TARGET_X86) - -// CordbThread::Get32bitFPRegisters -// Converts the values in the floating point register area of the context to real number values. See -// code:CordbThread::LoadFloatState for more details. -// Arguments: -// input: pContext -// output: none (initializes m_floatValues) - -void CordbThread::Get32bitFPRegisters(CONTEXT * pContext) -{ - // On X86, we get the values by saving our current FPU state, loading - // the other thread's FPU state into our own, saving out each - // value off the FPU stack, and then restoring our FPU state. - // - FLOATING_SAVE_AREA floatarea = pContext->FloatSave; // copy FloatSave - - // - // Take the TOP out of the FPU status word. Note, our version of the - // stack runs from 0->7, not 7->0... - // - unsigned int floatStackTop = 7 - ((floatarea.StatusWord & 0x3800) >> 11); - - FLOATING_SAVE_AREA currentFPUState; - -#ifdef _MSC_VER - __asm fnsave currentFPUState // save the current FPU state. -#else - __asm__ __volatile__ - ( - " fnsave %0\n" \ - : "=m"(currentFPUState) - ); -#endif - - floatarea.StatusWord &= 0xFF00; // remove any error codes. - floatarea.ControlWord |= 0x3F; // mask all exceptions. - - // the x86 FPU stores real numbers as 10 byte values in IEEE format. Here we use - // the hardware to convert these to doubles. - - // @dbgtodo Microsoft crossplat: the conversion from a series of bytes to a floating - // point value will need to be done with an explicit conversion routine to unpack - // the IEEE format and compute the real number value represented. - -#ifdef _MSC_VER - __asm - { - fninit - frstor floatarea ;; reload the threads FPU state. - } -#else - __asm__ - ( - " fninit\n" \ - " frstor %0\n" \ - : /* no outputs */ - : "m"(floatarea) - ); -#endif - - unsigned int i; - - for (i = 0; i <= floatStackTop; i++) - { - double td = 0.0; -#ifdef _MSC_VER - __asm fstp td // copy out the double -#else - __asm("fstpl %0" : "=m" (td)); -#endif - m_floatValues[i] = td; - } - -#ifdef _MSC_VER - __asm - { - fninit - frstor currentFPUState ;; restore our saved FPU state. - } -#else - __asm__ - ( - " fninit\n" \ - " frstor %0\n" \ - : /* no outputs */ - : "m"(currentFPUState) - ); -#endif - - m_fFloatStateValid = true; - m_floatStackTop = floatStackTop; -} // CordbThread::Get32bitFPRegisters - -#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) - -// CordbThread::Get64bitFPRegisters -// Converts the values in the floating point register area of the context to real number values. See -// code:CordbThread::LoadFloatState for more details. The size of FPRegister64 is per-architecture and -// determines the stride between consecutive registers in the context (8 bytes on Arm/RISC-V64, 16 on -// Amd64/Arm64, and 32 on LoongArch64, whose FPR64/LSX/LASX slot holds the scalar value in its first -// 64 bits); FPFillR8 reads that scalar value. -// Arguments: -// input: rgContextFPRegisters - starting address of the floating point register storage of the CONTEXT -// start - the index into m_floatValues where we start initializing -// nRegisters - the number of registers to be initialized -// output: none (initializes m_floatValues) - -void CordbThread::Get64bitFPRegisters(FPRegister64 * rgContextFPRegisters, int start, int nRegisters) -{ - // We convert and copy all the fp registers. - for (int reg = start; reg < nRegisters; reg++) - { - // @dbgtodo Microsoft crossplat: the conversion from a FLOAT128 or M128A struct to a floating - // point value will need to be done with an explicit conversion routine instead - // of the call to FPFillR8 - m_floatValues[reg] = FPFillR8(&rgContextFPRegisters[reg - start]); - } -} // CordbThread::Get64bitFPRegisters - -#endif // TARGET_X86 - -// CordbThread::LoadFloatState -// Initializes the float state members of this instance of CordbThread. This function gets the context and -// converts the floating point values from their context representation to a real number value. Floating -// point numbers are represented in IEEE format on all current platforms. We store them in the context as a -// pair of 64-bit integers (IA64 and AMD64) or a series of bytes (x86). Rather than unpack them explicitly -// and do the appropriate mathematical operations to produce the corresponding floating point value, we let -// the hardware do it instead. We load a floating point register with the representation from the context -// and then store it in m_floatValues. Using the hardware is obviously a huge perf win. If/when we make -// cross-plat work, we should at least code necessary conversion routines in assembly. Even with cross-plat, -// we can probably still use the hardware in most cases, as long as the size is appropriate. -// -// Arguments: none -// Return Value: none (initializes data members) -// Note: Throws - -void CordbThread::LoadFloatState() -{ - THROW_IF_NEUTERED(this); - INTERNAL_SYNC_API_ENTRY(GetProcess()); - - DT_CONTEXT tempContext; - IfFailThrow(GetProcess()->GetDAC()->GetContext(m_vmThreadToken, &tempContext)); - -#if defined(TARGET_X86) - Get32bitFPRegisters((CONTEXT*) &tempContext); -#elif defined(TARGET_AMD64) - // we have no fixed-value registers, so we begin with the first one and initialize all 16 - Get64bitFPRegisters((FPRegister64*) &(tempContext.Xmm0), 0, 16); -#elif defined(TARGET_ARM64) - Get64bitFPRegisters((FPRegister64*) &(tempContext.V), 0, 32); -#elif defined (TARGET_ARM) - Get64bitFPRegisters((FPRegister64*) &(tempContext.D), 0, 32); -#elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) - // The size of FPRegister64 selects the per-register stride in the context: a single 64-bit slot - // on RISC-V64, and a four-slot FPR64/LSX/LASX group on LoongArch64 (scalar value in the first slot). - Get64bitFPRegisters((FPRegister64*) &(tempContext.F), 0, 32); -#else - _ASSERTE(!"nyi for platform"); -#endif // !TARGET_X86 - - m_fFloatStateValid = true; -} // CordbThread::LoadFloatState - - const bool SetIP_fCanSetIPOnly = TRUE; const bool SetIP_fSetIP = FALSE; @@ -1674,25 +1501,27 @@ HRESULT CordbThread::SetIP(bool fCanSetIPOnly, // Get the context from a thread in managed code. // This thread should be stopped gracefully by the LS in managed code. -HRESULT CordbThread::GetManagedContext(DT_CONTEXT ** ppContext) +HRESULT CordbThread::GetManagedContext(ContextBuffer * pContextBuffer) { FAIL_IF_NEUTERED(this); INTERNAL_SYNC_API_ENTRY(GetProcess()); - if (ppContext == NULL) + if (pContextBuffer == NULL) { ThrowHR(E_INVALIDARG); } - *ppContext = NULL; + pContextBuffer->pContextBytes = NULL; + pContextBuffer->contextSize = 0; ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); - // Each CordbThread object allocates the m_pContext's DT_CONTEXT structure only once, the first time GetContext is - // invoked. - if(m_pContext == NULL) + // Each CordbThread object allocates m_pContext only once, the first time + // GetContext is invoked. The buffer is sized to the target's CONTEXT + // (queried from the DAC); callers treat it as opaque target bytes. + if (m_pContext == NULL) { // Throw if the allocation fails. - m_pContext = reinterpret_cast(new BYTE[sizeof(DT_CONTEXT)]); + m_pContext = new BYTE[GetProcess()->GetTargetContextSize()]; } HRESULT hr = S_OK; @@ -1702,7 +1531,7 @@ HRESULT CordbThread::GetManagedContext(DT_CONTEXT ** ppContext) IDacDbiInterface * pDAC = GetProcess()->GetDAC(); IfFailThrow(pDAC->GetManagedStoppedContext(m_vmThreadToken, &m_vmLeftSideContext)); - if (m_vmLeftSideContext.IsNull()) + if (m_vmLeftSideContext == (CORDB_ADDRESS)NULL) { // We don't have a context in managed code. ThrowHR(CORDBG_E_CONTEXT_UNVAILABLE); @@ -1713,7 +1542,8 @@ HRESULT CordbThread::GetManagedContext(DT_CONTEXT ** ppContext) // The thread we're examining IS handling an exception, So grab the CONTEXT of the exception, NOT the // currently executing thread's CONTEXT (which would be the context of the exception handler.) - hr = GetProcess()->SafeReadThreadContext(m_vmLeftSideContext.ToLsPtr(), m_pContext); + ContextBuffer managedContext = { m_pContext.GetValue(), GetProcess()->GetTargetContextSize() }; + hr = GetProcess()->SafeReadThreadContext(m_vmLeftSideContext, managedContext); IfFailThrow(hr); } @@ -1722,17 +1552,19 @@ HRESULT CordbThread::GetManagedContext(DT_CONTEXT ** ppContext) } _ASSERTE(SUCCEEDED(hr)); - (*ppContext) = m_pContext; + pContextBuffer->pContextBytes = m_pContext; + pContextBuffer->contextSize = GetProcess()->GetTargetContextSize(); return hr; } -HRESULT CordbThread::SetManagedContext(DT_CONTEXT * pContext) +HRESULT CordbThread::SetManagedContext(ContextBuffer contextBuffer) { INTERNAL_API_ENTRY(this); FAIL_IF_NEUTERED(this); - if(pContext == NULL) + ULONG32 contextSize = GetProcess()->GetTargetContextSize(); + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < contextSize)) { ThrowHR(E_INVALIDARG); } @@ -1744,7 +1576,7 @@ HRESULT CordbThread::SetManagedContext(DT_CONTEXT * pContext) IDacDbiInterface * pDAC = GetProcess()->GetDAC(); IfFailThrow(pDAC->GetManagedStoppedContext(m_vmThreadToken, &m_vmLeftSideContext)); - if (m_vmLeftSideContext.IsNull()) + if (m_vmLeftSideContext == (CORDB_ADDRESS)NULL) { ThrowHR(CORDBG_E_CONTEXT_UNVAILABLE); } @@ -1755,13 +1587,18 @@ HRESULT CordbThread::SetManagedContext(DT_CONTEXT * pContext) // // Note: we read the remote context and merge the new one in, then write it back. This ensures that we don't // write too much information into the remote process. - DT_CONTEXT tempContext = { 0 }; - hr = GetProcess()->SafeReadThreadContext(m_vmLeftSideContext.ToLsPtr(), &tempContext); + NewArrayHolder tempContext(new BYTE[contextSize]); + ContextBuffer temporaryContext = { tempContext, contextSize }; + hr = GetProcess()->SafeReadThreadContext(m_vmLeftSideContext, temporaryContext); IfFailThrow(hr); - CORDbgCopyThreadContext(&tempContext, pContext); + IfFailThrow(GetProcess()->GetDAC()->CopyContext( + temporaryContext, + contextBuffer, + IDacDbiInterface::kCopyContextPreserveDestinationFlags, + 0)); - hr = GetProcess()->SafeWriteThreadContext(m_vmLeftSideContext.ToLsPtr(), &tempContext); + hr = GetProcess()->SafeWriteThreadContext(m_vmLeftSideContext, temporaryContext); IfFailThrow(hr); // @todo - who's updating the regdisplay to guarantee that's in sync w/ our new context? @@ -1770,7 +1607,7 @@ HRESULT CordbThread::SetManagedContext(DT_CONTEXT * pContext) _ASSERTE(SUCCEEDED(hr)); if (m_fContextFresh && (m_pContext != NULL)) { - *m_pContext = *pContext; + memcpy(m_pContext, contextBuffer.pContextBytes, contextSize); } return hr; @@ -2809,70 +2646,6 @@ HRESULT CordbUnmanagedThread::LoadTLSArrayPtr(void) return hr; } -/* -VOID CordbUnmanagedThread::VerifyFSChain() -{ -#if defined(TARGET_X86) - DT_CONTEXT temp; - temp.ContextFlags = DT_CONTEXT_FULL; - DbiGetThreadContext(m_handle, &temp); - LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: 0x%x fs=0x%x TIB=0x%x\n", - m_id, temp.SegFs, m_threadLocalBase)); - REMOTE_PTR pExceptionRegRecordPtr; - HRESULT hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(m_threadLocalBase), &pExceptionRegRecordPtr); - if(FAILED(hr)) - { - LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x failed to read fs:0 value: computed addr=0x%p err=%x\n", - m_id, m_threadLocalBase, hr)); - _ASSERTE(FALSE); - return; - } - while(pExceptionRegRecordPtr != EXCEPTION_CHAIN_END && pExceptionRegRecordPtr != NULL) - { - REMOTE_PTR prev; - REMOTE_PTR handler; - hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pExceptionRegRecordPtr), &prev); - if(FAILED(hr)) - { - LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x failed to read prev value: computed addr=0x%p err=%x\n", - m_id, pExceptionRegRecordPtr, hr)); - return; - } - hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS( (VOID*)((DWORD)pExceptionRegRecordPtr+4) ), &handler); - if(FAILED(hr)) - { - LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x failed to read handler value: computed addr=0x%p err=%x\n", - m_id, (DWORD)pExceptionRegRecordPtr+4, hr)); - return; - } - LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: OK 0x%x record=0x%x prev=0x%x handler=0x%x\n", - m_id, pExceptionRegRecordPtr, prev, handler)); - if(handler == NULL) - { - LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x NULL handler found\n", m_id)); - _ASSERTE(FALSE); - return; - } - if(prev == NULL) - { - LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x NULL prev found\n", m_id)); - _ASSERTE(FALSE); - return; - } - if(prev == pExceptionRegRecordPtr) - { - LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x cyclic prev found\n", m_id)); - _ASSERTE(FALSE); - return; - } - pExceptionRegRecordPtr = prev; - } - - LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: OK 0x%x\n", m_id)); -#endif - return; -}*/ - #ifdef TARGET_X86 HRESULT CordbUnmanagedThread::SaveCurrentLeafSeh() { @@ -3418,7 +3191,7 @@ bool CordbUnmanagedThread::GetEEFrame() // Gets the thread context as if the thread were unhijacked, regardless // of whether it really is -HRESULT CordbUnmanagedThread::GetThreadContext(DT_CONTEXT* pContext) +HRESULT CordbUnmanagedThread::GetThreadContext(T_CONTEXT* pContext) { // While hijacked there are 3 potential contexts we could be resuming back to // 1) A context provided in SetThreadContext that we defered applying @@ -3451,8 +3224,8 @@ HRESULT CordbUnmanagedThread::GetThreadContext(DT_CONTEXT* pContext) "HijackedForSync=%d RaiseExceptionHijacked=%d.\n", IsContextSet(), IsGenericHijacked(), IsBlockingForSync(), IsRaiseExceptionHijacked())); LOG((LF_CORDB, LL_INFO10000, "CUT::GTC: hijackCtx is:\n")); - LogContext(GetHijackCtx()); - CORDbgCopyThreadContext(pContext, GetHijackCtx()); + CORDbgCopyThreadContext(reinterpret_cast(pContext), sizeof(T_CONTEXT), + reinterpret_cast(GetHijackCtx()), sizeof(T_CONTEXT)); } // use the LS for M2UHandoff else if (IsFirstChanceHijacked() && !IsBlockingForSync()) @@ -3461,12 +3234,14 @@ HRESULT CordbUnmanagedThread::GetThreadContext(DT_CONTEXT* pContext) m_pLeftSideContext.UnsafeGet())); // Read the context into a temp context then copy to the out param. - DT_CONTEXT tempContext = { 0 }; + T_CONTEXT tempContext = { 0 }; + ContextBuffer contextBuffer = { reinterpret_cast(&tempContext), sizeof(tempContext) }; - hr = GetProcess()->SafeReadThreadContext(m_pLeftSideContext, &tempContext); + hr = GetProcess()->SafeReadThreadContext(m_pLeftSideContext.UnsafeGetAddr(), contextBuffer); if (SUCCEEDED(hr)) - CORDbgCopyThreadContext(pContext, &tempContext); + CORDbgCopyThreadContext(reinterpret_cast(pContext), sizeof(T_CONTEXT), + reinterpret_cast(&tempContext), sizeof(tempContext)); } // no hijack in place so just call straight through else @@ -3483,7 +3258,6 @@ HRESULT CordbUnmanagedThread::GetThreadContext(DT_CONTEXT* pContext) { UnsetSSFlag(pContext); } - LogContext(pContext); return hr; } @@ -3491,15 +3265,13 @@ HRESULT CordbUnmanagedThread::GetThreadContext(DT_CONTEXT* pContext) // Sets the thread context as if the thread were unhijacked, regardless // of whether it really is. See GetThreadContext above for more details // on this abstraction -HRESULT CordbUnmanagedThread::SetThreadContext(DT_CONTEXT* pContext) +HRESULT CordbUnmanagedThread::SetThreadContext(T_CONTEXT* pContext) { HRESULT hr = S_OK; LOG((LF_CORDB, LL_INFO10000, "CUT::STC: thread=0x%p, flags=0x%x.\n", this, pContext->ContextFlags)); - LogContext(pContext); - // If the thread is first chance hijacked, then write the context into the remote process. If the thread is generic // hijacked, then update the copy of the context that we already have. Otherwise call the normal Win32 function. @@ -3518,7 +3290,8 @@ HRESULT CordbUnmanagedThread::SetThreadContext(DT_CONTEXT* pContext) LOG((LF_CORDB, LL_INFO10000, "CUT::STC: setting context from RaiseException hijack.\n")); } SetState(CUTS_HasContextSet); - CORDbgCopyThreadContext(GetHijackCtx(), pContext); + CORDbgCopyThreadContext(reinterpret_cast(GetHijackCtx()), sizeof(T_CONTEXT), + reinterpret_cast(pContext), sizeof(T_CONTEXT)); } else { @@ -3556,8 +3329,8 @@ VOID CordbUnmanagedThread::BeginStepping() _ASSERTE(!IsSSFlagNeeded()); _ASSERTE(!IsSSFlagHidden()); - DT_CONTEXT tempContext; - tempContext.ContextFlags = DT_CONTEXT_FULL; + T_CONTEXT tempContext; + tempContext.ContextFlags = CONTEXT_FULL; BOOL succ = DbiGetThreadContext(m_handle, &tempContext); _ASSERTE(succ); @@ -3579,8 +3352,8 @@ VOID CordbUnmanagedThread::EndStepping() _ASSERTE(!IsGenericHijacked() && !IsFirstChanceHijacked()); _ASSERTE(IsSSFlagNeeded()); - DT_CONTEXT tempContext; - tempContext.ContextFlags = DT_CONTEXT_FULL; + T_CONTEXT tempContext; + tempContext.ContextFlags = CONTEXT_FULL; BOOL succ = DbiGetThreadContext(m_handle, &tempContext); _ASSERTE(succ); @@ -3595,32 +3368,6 @@ VOID CordbUnmanagedThread::EndStepping() _ASSERTE(succ); } - -// Writes some details of the given context into the debugger log -VOID CordbUnmanagedThread::LogContext(DT_CONTEXT* pContext) -{ -#if defined(TARGET_X86) - LOG((LF_CORDB, LL_INFO10000, - "CUT::LC: Eip=0x%08x, Esp=0x%08x, Eflags=0x%08x\n", pContext->Eip, pContext->Esp, - pContext->EFlags)); -#elif defined(TARGET_AMD64) - LOG((LF_CORDB, LL_INFO10000, - "CUT::LC: Rip=" FMT_ADDR ", Rsp=" FMT_ADDR ", Eflags=0x%08x\n", - DBG_ADDR(pContext->Rip), - DBG_ADDR(pContext->Rsp), - pContext->EFlags)); // EFlags is still 32bits on AMD64 -#elif defined(TARGET_ARM64) - LOG((LF_CORDB, LL_INFO10000, - "CUT::LC: Pc=" FMT_ADDR ", Sp=" FMT_ADDR ", Lr=" FMT_ADDR ", Cpsr=" FMT_ADDR "\n", - DBG_ADDR(pContext->Pc), - DBG_ADDR(pContext->Sp), - DBG_ADDR(pContext->Lr), - DBG_ADDR(pContext->Cpsr))); -#else // TARGET_X86 - PORTABILITY_ASSERT("LogContext needs a PC and stack pointer."); -#endif // TARGET_X86 -} - // Hijacks this thread using the FirstChanceSuspend hijack HRESULT CordbUnmanagedThread::SetupFirstChanceHijackForSync() { @@ -3651,15 +3398,14 @@ HRESULT CordbUnmanagedThread::SetupFirstChanceHijackForSync() // snapshot the current context so we can start spoofing it LOG((LF_CORDB, LL_INFO10000, "CUT::SFCHFS: hijackCtx started as:\n")); - LogContext(GetHijackCtx()); - // Save the thread's full context + DT_CONTEXT_EXTENDED_REGISTERS + // Save the thread's full context + CONTEXT_EXTENDED_REGISTERS // to avoid getting incomplete information and corrupt the thread context - DT_CONTEXT context; -#if defined(DT_CONTEXT_EXTENDED_REGISTERS) - context.ContextFlags = DT_CONTEXT_FULL | DT_CONTEXT_FLOATING_POINT | DT_CONTEXT_EXTENDED_REGISTERS; + T_CONTEXT context; +#if defined(TARGET_X86) + context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS; #else - context.ContextFlags = DT_CONTEXT_FULL | DT_CONTEXT_FLOATING_POINT; + context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; #endif BOOL succ = DbiGetThreadContext(m_handle, &context); _ASSERTE(succ); @@ -3669,14 +3415,14 @@ HRESULT CordbUnmanagedThread::SetupFirstChanceHijackForSync() DWORD error = GetLastError(); LOG((LF_CORDB, LL_ERROR, "CUT::SFCHFS: DbiGetThreadContext error=0x%x\n", error)); } -#if defined(DT_CONTEXT_EXTENDED_REGISTERS) - GetHijackCtx()->ContextFlags = DT_CONTEXT_FULL | DT_CONTEXT_FLOATING_POINT | DT_CONTEXT_EXTENDED_REGISTERS; +#if defined(TARGET_X86) + GetHijackCtx()->ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS; #else - GetHijackCtx()->ContextFlags = DT_CONTEXT_FULL | DT_CONTEXT_FLOATING_POINT; + GetHijackCtx()->ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; #endif - CORDbgCopyThreadContext(GetHijackCtx(), &context); + CORDbgCopyThreadContext(reinterpret_cast(GetHijackCtx()), sizeof(T_CONTEXT), + reinterpret_cast(&context), sizeof(context)); LOG((LF_CORDB, LL_INFO10000, "CUT::SFCHFS: thread=0x%x Hijacking for sync. Original context is:\n", this)); - LogContext(GetHijackCtx()); // We're hijacking now... SetState(CUTS_FirstChanceHijacked); @@ -3804,7 +3550,7 @@ HRESULT CordbUnmanagedThread::SetupGenericHijack(DWORD eventCode, const EXCEPTIO _ASSERTE(!IsContextSet()); // Save the thread's full context. - GetHijackCtx()->ContextFlags = DT_CONTEXT_FULL; + GetHijackCtx()->ContextFlags = CONTEXT_FULL; BOOL succ = DbiGetThreadContext(m_handle, GetHijackCtx()); @@ -3932,7 +3678,7 @@ HRESULT CordbUnmanagedThread::FixupFromGenericHijack() return S_OK; } -DT_CONTEXT * CordbUnmanagedThread::GetHijackCtx() +T_CONTEXT * CordbUnmanagedThread::GetHijackCtx() { return &m_context; } @@ -3942,8 +3688,8 @@ DT_CONTEXT * CordbUnmanagedThread::GetHijackCtx() // This can only be called after a bp. (because we assume that we executed a bp when we adjust the eip). HRESULT CordbUnmanagedThread::EnableSSAfterBP() { - DT_CONTEXT c; - c.ContextFlags = DT_CONTEXT_FULL; + T_CONTEXT c; + c.ContextFlags = CONTEXT_FULL; BOOL succ = DbiGetThreadContext(m_handle, &c); @@ -4078,7 +3824,7 @@ void CordbUnmanagedThread::FixupForSkipBreakpoint() m_pPatchSkipAddress = NULL; } -inline TADDR GetSP(DT_CONTEXT* context) +inline TADDR GetSP(T_CONTEXT* context) { #if defined(TARGET_X86) return (TADDR)context->Esp; @@ -4098,10 +3844,10 @@ BOOL CordbUnmanagedThread::GetStackRange(CORDB_ADDRESS *pBase, CORDB_ADDRESS *pL if (m_stackBase == 0 && m_stackLimit == 0) { HANDLE hProc; - DT_CONTEXT tempContext; + T_CONTEXT tempContext; MEMORY_BASIC_INFORMATION mbi; - tempContext.ContextFlags = DT_CONTEXT_FULL; + tempContext.ContextFlags = CONTEXT_FULL; if (SUCCEEDED(GetProcess()->GetHandle(&hProc)) && SUCCEEDED(GetThreadContext(&tempContext)) && ::VirtualQueryEx(hProc, (LPCVOID)GetSP(&tempContext), &mbi, sizeof(mbi)) != 0) @@ -4164,26 +3910,6 @@ BOOL CordbUnmanagedThread::GetStackRange(CORDB_ADDRESS *pBase, CORDB_ADDRESS *pL #endif // FEATURE_DBGIPC_TRANSPORT } -//----------------------------------------------------------------------------- -// Returns the thread context to the state it was in when it last entered RaiseException -// This allows the thread to retrigger an exception caused by RaiseException -//----------------------------------------------------------------------------- -void CordbUnmanagedThread::HijackToRaiseException() -{ - LOG((LF_CORDB, LL_INFO1000, "CP::HTRE: hijacking to RaiseException\n")); - _ASSERTE(HasRaiseExceptionEntryCtx()); - _ASSERTE(!IsRaiseExceptionHijacked()); - _ASSERTE(!IsGenericHijacked()); - _ASSERTE(!IsFirstChanceHijacked()); - _ASSERTE(!IsContextSet()); - - BOOL succ = DbiGetThreadContext(m_handle, GetHijackCtx()); - _ASSERTE(succ); - succ = DbiSetThreadContext(m_handle, &m_raiseExceptionEntryContext); - _ASSERTE(succ); - SetState(CUTS_IsRaiseExceptionHijacked); -} - //---------------------------------------------------------------------------- // Returns the context to its unhijacked state. //---------------------------------------------------------------------------- @@ -4192,8 +3918,8 @@ void CordbUnmanagedThread::RestoreFromRaiseExceptionHijack() LOG((LF_CORDB, LL_INFO1000, "CP::RFREH: ending RaiseException hijack\n")); _ASSERTE(IsRaiseExceptionHijacked()); - DT_CONTEXT restoreContext; - restoreContext.ContextFlags = DT_CONTEXT_FULL; + T_CONTEXT restoreContext; + restoreContext.ContextFlags = CONTEXT_FULL; HRESULT hr = GetThreadContext(&restoreContext); _ASSERTE(SUCCEEDED(hr)); @@ -4202,90 +3928,6 @@ void CordbUnmanagedThread::RestoreFromRaiseExceptionHijack() _ASSERTE(SUCCEEDED(hr)); } -//----------------------------------------------------------------------------- -// Attempts to store the state of a thread currently entering RaiseException -// This grabs both a full context and enough state to determine what exception -// RaiseException should be raising. If any of the state can not be retrieved -// then this entrance to RaiseException is silently ignored -//----------------------------------------------------------------------------- -void CordbUnmanagedThread::SaveRaiseExceptionEntryContext() -{ - _ASSERTE(FALSE); // should be unused now - LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: saving raise exception context.\n")); - _ASSERTE(!HasRaiseExceptionEntryCtx()); - _ASSERTE(!IsRaiseExceptionHijacked()); - HRESULT hr = S_OK; - DT_CONTEXT context; - context.ContextFlags = DT_CONTEXT_FULL; - DbiGetThreadContext(m_handle, &context); - // if the flag is set, unset it - // we don't want to be single stepping through RaiseException the second time - // sending out OOB SS events. Ultimately we will rethrow the exception which would - // cleared the SS flag anyways. - UnsetSSFlag(&context); - memcpy(&m_raiseExceptionEntryContext, &context, sizeof(DT_CONTEXT)); - - // calculate the exception that we would expect to come from this invocation of RaiseException - REMOTE_PTR pExceptionInformation = NULL; -#if defined(TARGET_AMD64) - m_raiseExceptionExceptionCode = (DWORD)m_raiseExceptionEntryContext.Rcx; - m_raiseExceptionExceptionFlags = (DWORD)m_raiseExceptionEntryContext.Rdx; - m_raiseExceptionNumberParameters = (DWORD)m_raiseExceptionEntryContext.R8; - pExceptionInformation = (REMOTE_PTR)m_raiseExceptionEntryContext.R9; -#elif defined(TARGET_ARM64) - m_raiseExceptionExceptionCode = (DWORD)m_raiseExceptionEntryContext.X0; - m_raiseExceptionExceptionFlags = (DWORD)m_raiseExceptionEntryContext.X1; - m_raiseExceptionNumberParameters = (DWORD)m_raiseExceptionEntryContext.X2; - pExceptionInformation = (REMOTE_PTR)m_raiseExceptionEntryContext.X3; -#elif defined(TARGET_X86) - hr = m_pProcess->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_raiseExceptionEntryContext.Esp+4), &m_raiseExceptionExceptionCode); - if(FAILED(hr)) - { - LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: failed to read exception code.\n")); - return; - } - hr = m_pProcess->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_raiseExceptionEntryContext.Esp+8), &m_raiseExceptionExceptionFlags); - if(FAILED(hr)) - { - LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: failed to read exception flags.\n")); - return; - } - hr = m_pProcess->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_raiseExceptionEntryContext.Esp+12), &m_raiseExceptionNumberParameters); - if(FAILED(hr)) - { - LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: failed to read number of parameters.\n")); - return; - } - hr = m_pProcess->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_raiseExceptionEntryContext.Esp+16), &pExceptionInformation); - if(FAILED(hr)) - { - LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: failed to read exception information pointer.\n")); - return; - } -#else - _ASSERTE(!"Implement this for your platform"); - return; -#endif - LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: RaiseException parameters are 0x%x 0x%x 0x%x 0x%p.\n", - m_raiseExceptionExceptionCode, m_raiseExceptionExceptionFlags, - m_raiseExceptionNumberParameters, pExceptionInformation)); - TargetBuffer exceptionInfoTargetBuffer(pExceptionInformation, sizeof(REMOTE_PTR)*m_raiseExceptionNumberParameters); - EX_TRY - { - m_pProcess->SafeReadBuffer(exceptionInfoTargetBuffer, (BYTE*)m_raiseExceptionExceptionInformation); - } - EX_CATCH_HRESULT(hr); - if(FAILED(hr)) - { - LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: failed to read exception information.\n")); - return; - } - - // If everything was successful then set this flag, otherwise none of the above data is considered valid - SetState(CUTS_HasRaiseExceptionEntryCtx); - return; -} - //----------------------------------------------------------------------------- // Clears all the state saved in SaveRaiseExceptionContext and returns the thread // to the state as if RaiseException has yet to be called. This is typically called @@ -4764,6 +4406,43 @@ bool CordbFrame::IsContainedInFrame(FramePointer fp) } } +// Reads an integer register from this frame's CONTEXT buffer via the DAC. +HRESULT CordbFrame::ReadContextRegister(CorDebugRegister reg, TADDR * pValue) const +{ + if (pValue == NULL) + return E_POINTER; + + IDacDbiInterface * pDAC = GetProcess()->GetDAC(); + CORDB_REGISTER value = 0; + HRESULT hr = pDAC->ReadRegistersFromContext( + GetContext(), + ®, + 1, + &value); + if (SUCCEEDED(hr)) + *pValue = (TADDR)value; + return hr; +} + +// Reads a floating-point / SIMD register's scalar value (as a double bit +// pattern) from this frame's CONTEXT buffer via the DAC. +HRESULT CordbFrame::ReadFloatContextRegister(CorDebugRegister reg, double * pValue) const +{ + if (pValue == NULL) + return E_POINTER; + + IDacDbiInterface * pDAC = GetProcess()->GetDAC(); + CORDB_REGISTER value = 0; + HRESULT hr = pDAC->ReadRegistersFromContext( + GetContext(), + ®, + 1, + &value); + if (SUCCEEDED(hr)) + memcpy(pValue, &value, sizeof(double)); + return hr; +} + //--------------------------------------------------------------------------------------- // // Given an ICorDebugFrame interface pointer, return a pointer to the base class CordbFrame. @@ -5300,7 +4979,7 @@ BOOL CordbInternalFrame::IsCloserToLeafWorker(ICorDebugFrame * pFrameToCompare) // Compare the address of the "this" internal frame to the SP of the stack frame. // We can't compare frame pointers because the frame pointer means different things on // different platforms. - CORDB_ADDRESS stackFrameSP = CORDbgGetSP(pCNativeFrame->GetContext()); + CORDB_ADDRESS stackFrameSP = pCNativeFrame->GetStackPointer(); return (thisFrameAddr < stackFrameSP); } @@ -5312,8 +4991,9 @@ BOOL CordbInternalFrame::IsCloserToLeafWorker(ICorDebugFrame * pFrameToCompare) CordbRuntimeUnwindableFrame * pCRUFrame = static_cast(pRUFrame.GetValue()); - DT_CONTEXT * pResumeContext = const_cast(pCRUFrame->GetContext()); - CORDB_ADDRESS stackFrameSP = CORDbgGetSP(pResumeContext); + TADDR sp = 0; + IfFailThrow(pCRUFrame->ReadContextRegister(REGISTER_STACK_POINTER, &sp)); + CORDB_ADDRESS stackFrameSP = PTR_TO_CORDB_ADDRESS(sp); return (thisFrameAddr < stackFrameSP); } @@ -5372,10 +5052,17 @@ HRESULT CordbInternalFrame::IsCloserToLeaf(ICorDebugFrame * pFrameToCompare, CordbRuntimeUnwindableFrame::CordbRuntimeUnwindableFrame(CordbThread * pThread, FramePointer fp, CordbAppDomain * pCurrentAppDomain, - DT_CONTEXT * pContext) - : CordbFrame(pThread, fp, 0, pCurrentAppDomain), - m_context(*pContext) + ContextBuffer contextBuffer) + : CordbFrame(pThread, fp, 0, pCurrentAppDomain) { + ULONG32 expectedContextSize = pThread->GetProcess()->GetTargetContextSize(); + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < expectedContextSize)) + { + ThrowHR(E_INVALIDARG); + } + + m_pContextBuffer = new BYTE[expectedContextSize]; + memcpy(m_pContextBuffer, contextBuffer.pContextBytes, expectedContextSize); } void CordbRuntimeUnwindableFrame::Neuter() @@ -5415,9 +5102,9 @@ HRESULT CordbRuntimeUnwindableFrame::QueryInterface(REFIID id, void ** ppInterfa // Return a pointer to the CONTEXT. // -const DT_CONTEXT * CordbRuntimeUnwindableFrame::GetContext() const +const ContextBuffer CordbRuntimeUnwindableFrame::GetContext() const { - return &m_context; + return { m_pContextBuffer, GetProcess()->GetTargetContextSize() }; } @@ -5449,7 +5136,7 @@ CordbNativeFrame::CordbNativeFrame(CordbThread * pThread, TADDR taAmbientESP, CordbAppDomain * pCurrentAppDomain, CordbMiscFrame * pMisc /*= NULL*/, - DT_CONTEXT * pContext /*= NULL*/) + ContextBuffer contextBuffer /*= {}*/) : CordbFrame(pThread, fp, ip, pCurrentAppDomain), m_JITILFrame(NULL), m_nativeCode(pNativeCode), // implicit InternalAddRef @@ -5458,8 +5145,14 @@ CordbNativeFrame::CordbNativeFrame(CordbThread * pThread, m_misc = *pMisc; // Only new CordbNativeFrames created by the new stackwalk contain a CONTEXT. - _ASSERTE(pContext != NULL); - m_context = *pContext; + ULONG32 expectedContextSize = GetProcess()->GetTargetContextSize(); + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < expectedContextSize)) + { + ThrowHR(E_INVALIDARG); + } + + m_pContextBuffer = new BYTE[expectedContextSize]; + memcpy(m_pContextBuffer, contextBuffer.pContextBytes, expectedContextSize); } /* @@ -5576,9 +5269,9 @@ HRESULT CordbNativeFrame::GetCode(ICorDebugCode **ppCode) // Return a pointer to the CONTEXT. // -const DT_CONTEXT * CordbNativeFrame::GetContext() const +const ContextBuffer CordbNativeFrame::GetContext() const { - return &m_context; + return ContextBuffer{ m_pContextBuffer, GetProcess()->GetTargetContextSize() }; } //--------------------------------------------------------------------------------------- @@ -5628,20 +5321,43 @@ ULONG32 CordbNativeFrame::GetIPOffset() return (ULONG32)m_ip; } -TADDR CordbNativeFrame::GetReturnRegisterValue() +// Reads an integer register from this frame's CONTEXT buffer via the DAC, using +// the JIT register number. Composes the JIT-RegNum -> CorDebugRegister +// translation with the generic register read. +HRESULT CordbNativeFrame::ReadJitRegFromContext(ULONG32 jitRegNum, TADDR * pValue) const { -#if defined(TARGET_X86) - return (TADDR)m_context.Eax; -#elif defined(TARGET_AMD64) - return (TADDR)m_context.Rax; -#elif defined(TARGET_ARM) - return (TADDR)m_context.R0; -#elif defined(TARGET_ARM64) - return (TADDR)m_context.X0; -#else - _ASSERTE(!"nyi for platform"); - return 0; -#endif + if (pValue == NULL) + return E_POINTER; + + CorDebugRegister reg; + HRESULT hr = GetProcess()->ConvertJitRegNumToCorDebugRegister(jitRegNum, ®); + if (FAILED(hr)) + return hr; + + return ReadContextRegister(reg, pValue); +} + +// Translates a JIT ICorDebugInfo::RegNum to its CorDebugRegister via the DAC, +// throwing on failure. See declaration in rspriv.h for full contract. +CorDebugRegister CordbNativeFrame::ConvertJitRegToCorDebugRegister(ULONG32 jitRegNum) const +{ + CorDebugRegister reg = (CorDebugRegister)0; + HRESULT hr = GetProcess()->ConvertJitRegNumToCorDebugRegister(jitRegNum, ®); + IfFailThrow(hr); + return reg; +} + +// Convenience wrapper around ReadContextRegister for the stack pointer. +CORDB_ADDRESS CordbNativeFrame::GetStackPointer() const +{ + TADDR sp = 0; + HRESULT hr = ReadContextRegister(REGISTER_STACK_POINTER, &sp); + if (FAILED(hr)) + { + _ASSERTE(!"ReadContextRegister(REGISTER_STACK_POINTER) failed"); + return (CORDB_ADDRESS)0; + } + return PTR_TO_CORDB_ADDRESS(sp); } // Determine if we can set IP at this point. The specified offset is the native offset. @@ -5704,8 +5420,6 @@ CORDB_ADDRESS CordbNativeFrame::GetLSStackAddress( ICorDebugInfo::RegNum regNum, signed offset) { - UINT_PTR *pRegAddr; - CORDB_ADDRESS pRemoteValue; if (regNum != DBG_TARGET_REGNUM_AMBIENT_SP) @@ -5716,15 +5430,13 @@ CORDB_ADDRESS CordbNativeFrame::GetLSStackAddress( // funclet prolog using the PSP. Thus, we just look up the frame pointer in the // current native frame. - pRegAddr = this->GetAddressOfRegister( - ConvertRegNumToCorDebugRegister(regNum)); - - // This should never be null as long as regNum is a member of the RegNum enum. - // If it is, an AV dereferencing a null-pointer in retail builds, or an assert in debug - // builds is exactly the behavior we want. - _ASSERTE(pRegAddr != NULL); + TADDR regVal = 0; + HRESULT hr = this->ReadJitRegFromContext(regNum, ®Val); + // This should never fail as long as regNum is a member of the RegNum enum. + _ASSERTE(SUCCEEDED(hr)); + IfFailThrow(hr); - pRemoteValue = PTR_TO_CORDB_ADDRESS(*pRegAddr + offset); + pRemoteValue = PTR_TO_CORDB_ADDRESS(regVal + offset); } else { @@ -5772,7 +5484,7 @@ HRESULT CordbNativeFrame::GetStackRange(CORDB_ADDRESS *pStart, if (pStart) { // From register set. - *pStart = CORDbgGetSP(&m_context); + *pStart = GetStackPointer(); } if (pEnd) @@ -5812,8 +5524,8 @@ HRESULT CordbNativeFrame::GetRegisterSet(ICorDebugRegisterSet **ppRegisters) EX_TRY { // allocate a new CordbRegisterSet object - RSInitHolder pRegisterSet(new CordbRegisterSet(m_pThread, - &m_context, + RSInitHolder pRegisterSet(new CordbRegisterSet(GetContext(), + m_pThread, IsLeafFrame(), false)); @@ -5939,7 +5651,9 @@ HRESULT CordbNativeFrame::GetStackParameterSize(ULONG32 * pSize) #if defined(TARGET_X86) IDacDbiInterface * pDAC = GetProcess()->GetDAC(); - IfFailThrow(pDAC->GetStackParameterSize(PTR_TO_CORDB_ADDRESS(CORDbgGetIP(&m_context)), pSize)); + TADDR ip = 0; + IfFailThrow(ReadContextRegister(REGISTER_INSTRUCTION_POINTER, &ip)); + IfFailThrow(pDAC->GetStackParameterSize(PTR_TO_CORDB_ADDRESS(ip), pSize)); #else // !TARGET_X86 hr = S_FALSE; *pSize = 0; @@ -5950,462 +5664,6 @@ HRESULT CordbNativeFrame::GetStackParameterSize(ULONG32 * pSize) return hr; } -// -// GetAddressOfRegister returns the address of the given register in the -// frame's current register display (eg, a local address). This is usually used to build a -// ICorDebugValue from. -// -UINT_PTR * CordbNativeFrame::GetAddressOfRegister(CorDebugRegister regNum) const -{ - UINT_PTR* ret = NULL; - - switch (regNum) - { -#if !defined(TARGET_WASM) - case REGISTER_STACK_POINTER: - ret = (UINT_PTR*)GetSPAddress(&m_context); - break; -#endif - -#if !defined(TARGET_AMD64) && !defined(TARGET_ARM) && !defined(TARGET_WASM) // @ARMTODO - case REGISTER_FRAME_POINTER: - ret = (UINT_PTR*)GetFPAddress(&m_context); - break; -#endif - -#if defined(TARGET_X86) - case REGISTER_X86_EAX: - ret = (UINT_PTR*)&m_context.Eax; - break; - - case REGISTER_X86_ECX: - ret = (UINT_PTR*)&m_context.Ecx; - break; - - case REGISTER_X86_EDX: - ret = (UINT_PTR*)&m_context.Edx; - break; - - case REGISTER_X86_EBX: - ret = (UINT_PTR*)&m_context.Ebx; - break; - - case REGISTER_X86_ESI: - ret = (UINT_PTR*)&m_context.Esi; - break; - - case REGISTER_X86_EDI: - ret = (UINT_PTR*)&m_context.Edi; - break; - -#elif defined(TARGET_AMD64) - case REGISTER_AMD64_RBP: - ret = (UINT_PTR*)&m_context.Rbp; - break; - - case REGISTER_AMD64_RAX: - ret = (UINT_PTR*)&m_context.Rax; - break; - - case REGISTER_AMD64_RCX: - ret = (UINT_PTR*)&m_context.Rcx; - break; - - case REGISTER_AMD64_RDX: - ret = (UINT_PTR*)&m_context.Rdx; - break; - - case REGISTER_AMD64_RBX: - ret = (UINT_PTR*)&m_context.Rbx; - break; - - case REGISTER_AMD64_RSI: - ret = (UINT_PTR*)&m_context.Rsi; - break; - - case REGISTER_AMD64_RDI: - ret = (UINT_PTR*)&m_context.Rdi; - break; - - case REGISTER_AMD64_R8: - ret = (UINT_PTR*)&m_context.R8; - break; - - case REGISTER_AMD64_R9: - ret = (UINT_PTR*)&m_context.R9; - break; - - case REGISTER_AMD64_R10: - ret = (UINT_PTR*)&m_context.R10; - break; - - case REGISTER_AMD64_R11: - ret = (UINT_PTR*)&m_context.R11; - break; - - case REGISTER_AMD64_R12: - ret = (UINT_PTR*)&m_context.R12; - break; - - case REGISTER_AMD64_R13: - ret = (UINT_PTR*)&m_context.R13; - break; - - case REGISTER_AMD64_R14: - ret = (UINT_PTR*)&m_context.R14; - break; - - case REGISTER_AMD64_R15: - ret = (UINT_PTR*)&m_context.R15; - break; -#elif defined(TARGET_ARM) - case REGISTER_ARM_R0: - ret = (UINT_PTR*)&m_context.R0; - break; - - case REGISTER_ARM_R1: - ret = (UINT_PTR*)&m_context.R1; - break; - - case REGISTER_ARM_R2: - ret = (UINT_PTR*)&m_context.R2; - break; - - case REGISTER_ARM_R3: - ret = (UINT_PTR*)&m_context.R3; - break; - - case REGISTER_ARM_R4: - ret = (UINT_PTR*)&m_context.R4; - break; - - case REGISTER_ARM_R5: - ret = (UINT_PTR*)&m_context.R5; - break; - - case REGISTER_ARM_R6: - ret = (UINT_PTR*)&m_context.R6; - break; - - case REGISTER_ARM_R7: - ret = (UINT_PTR*)&m_context.R7; - break; - - case REGISTER_ARM_R8: - ret = (UINT_PTR*)&m_context.R8; - break; - - case REGISTER_ARM_R9: - ret = (UINT_PTR*)&m_context.R9; - break; - - case REGISTER_ARM_R10: - ret = (UINT_PTR*)&m_context.R10; - break; - - case REGISTER_ARM_R11: - ret = (UINT_PTR*)&m_context.R11; - break; - - case REGISTER_ARM_R12: - ret = (UINT_PTR*)&m_context.R12; - break; - - case REGISTER_ARM_LR: - ret = (UINT_PTR*)&m_context.Lr; - break; - - case REGISTER_ARM_PC: - ret = (UINT_PTR*)&m_context.Pc; - break; -#elif defined(TARGET_ARM64) - case REGISTER_ARM64_X0: - case REGISTER_ARM64_X1: - case REGISTER_ARM64_X2: - case REGISTER_ARM64_X3: - case REGISTER_ARM64_X4: - case REGISTER_ARM64_X5: - case REGISTER_ARM64_X6: - case REGISTER_ARM64_X7: - case REGISTER_ARM64_X8: - case REGISTER_ARM64_X9: - case REGISTER_ARM64_X10: - case REGISTER_ARM64_X11: - case REGISTER_ARM64_X12: - case REGISTER_ARM64_X13: - case REGISTER_ARM64_X14: - case REGISTER_ARM64_X15: - case REGISTER_ARM64_X16: - case REGISTER_ARM64_X17: - case REGISTER_ARM64_X18: - case REGISTER_ARM64_X19: - case REGISTER_ARM64_X20: - case REGISTER_ARM64_X21: - case REGISTER_ARM64_X22: - case REGISTER_ARM64_X23: - case REGISTER_ARM64_X24: - case REGISTER_ARM64_X25: - case REGISTER_ARM64_X26: - case REGISTER_ARM64_X27: - case REGISTER_ARM64_X28: - ret = (UINT_PTR*)&m_context.X[regNum - REGISTER_ARM64_X0]; - break; - - case REGISTER_ARM64_LR: - ret = (UINT_PTR*)&m_context.Lr; - break; - - case REGISTER_ARM64_PC: - ret = (UINT_PTR*)&m_context.Pc; - break; -#elif defined(TARGET_RISCV64) - case REGISTER_RISCV64_PC: - ret = (UINT_PTR*)&m_context.Pc; - break; - - case REGISTER_RISCV64_RA: - ret = (UINT_PTR*)&m_context.Ra; - break; - - case REGISTER_RISCV64_GP: - ret = (UINT_PTR*)&m_context.Gp; - break; - - case REGISTER_RISCV64_TP: - ret = (UINT_PTR*)&m_context.Tp; - break; - - case REGISTER_RISCV64_T0: - ret = (UINT_PTR*)&m_context.T0; - break; - - case REGISTER_RISCV64_T1: - ret = (UINT_PTR*)&m_context.T1; - break; - - case REGISTER_RISCV64_T2: - ret = (UINT_PTR*)&m_context.T2; - break; - - case REGISTER_RISCV64_S1: - ret = (UINT_PTR*)&m_context.S1; - break; - - case REGISTER_RISCV64_A0: - ret = (UINT_PTR*)&m_context.A0; - break; - - case REGISTER_RISCV64_A1: - ret = (UINT_PTR*)&m_context.A1; - break; - - case REGISTER_RISCV64_A2: - ret = (UINT_PTR*)&m_context.A2; - break; - - case REGISTER_RISCV64_A3: - ret = (UINT_PTR*)&m_context.A3; - break; - - case REGISTER_RISCV64_A4: - ret = (UINT_PTR*)&m_context.A4; - break; - - case REGISTER_RISCV64_A5: - ret = (UINT_PTR*)&m_context.A5; - break; - - case REGISTER_RISCV64_A6: - ret = (UINT_PTR*)&m_context.A6; - break; - - case REGISTER_RISCV64_A7: - ret = (UINT_PTR*)&m_context.A7; - break; - - case REGISTER_RISCV64_S2: - ret = (UINT_PTR*)&m_context.S2; - break; - - case REGISTER_RISCV64_S3: - ret = (UINT_PTR*)&m_context.S3; - break; - - case REGISTER_RISCV64_S4: - ret = (UINT_PTR*)&m_context.S4; - break; - - case REGISTER_RISCV64_S5: - ret = (UINT_PTR*)&m_context.S5; - break; - - case REGISTER_RISCV64_S6: - ret = (UINT_PTR*)&m_context.S6; - break; - - case REGISTER_RISCV64_S7: - ret = (UINT_PTR*)&m_context.S7; - break; - - case REGISTER_RISCV64_S8: - ret = (UINT_PTR*)&m_context.S8; - break; - - case REGISTER_RISCV64_S9: - ret = (UINT_PTR*)&m_context.S9; - break; - - case REGISTER_RISCV64_S10: - ret = (UINT_PTR*)&m_context.S10; - break; - - case REGISTER_RISCV64_S11: - ret = (UINT_PTR*)&m_context.S11; - break; - - case REGISTER_RISCV64_T3: - ret = (UINT_PTR*)&m_context.T3; - break; - - case REGISTER_RISCV64_T4: - ret = (UINT_PTR*)&m_context.T4; - break; - - case REGISTER_RISCV64_T5: - ret = (UINT_PTR*)&m_context.T5; - break; - - case REGISTER_RISCV64_T6: - ret = (UINT_PTR*)&m_context.T6; - break; -#elif defined(TARGET_LOONGARCH64) - case REGISTER_LOONGARCH64_PC: - ret = (UINT_PTR*)&m_context.Pc; - break; - - case REGISTER_LOONGARCH64_RA: - ret = (UINT_PTR*)&m_context.Ra; - break; - - case REGISTER_LOONGARCH64_TP: - ret = (UINT_PTR*)&m_context.Tp; - break; - - case REGISTER_LOONGARCH64_A0: - ret = (UINT_PTR*)&m_context.A0; - break; - - case REGISTER_LOONGARCH64_A1: - ret = (UINT_PTR*)&m_context.A1; - break; - - case REGISTER_LOONGARCH64_A2: - ret = (UINT_PTR*)&m_context.A2; - break; - - case REGISTER_LOONGARCH64_A3: - ret = (UINT_PTR*)&m_context.A3; - break; - - case REGISTER_LOONGARCH64_A4: - ret = (UINT_PTR*)&m_context.A4; - break; - - case REGISTER_LOONGARCH64_A5: - ret = (UINT_PTR*)&m_context.A5; - break; - - case REGISTER_LOONGARCH64_A6: - ret = (UINT_PTR*)&m_context.A6; - break; - - case REGISTER_LOONGARCH64_A7: - ret = (UINT_PTR*)&m_context.A7; - break; - - case REGISTER_LOONGARCH64_T0: - ret = (UINT_PTR*)&m_context.T0; - break; - - case REGISTER_LOONGARCH64_T1: - ret = (UINT_PTR*)&m_context.T1; - break; - - case REGISTER_LOONGARCH64_T2: - ret = (UINT_PTR*)&m_context.T2; - break; - - case REGISTER_LOONGARCH64_T3: - ret = (UINT_PTR*)&m_context.T3; - break; - - case REGISTER_LOONGARCH64_T4: - ret = (UINT_PTR*)&m_context.T4; - break; - - case REGISTER_LOONGARCH64_T5: - ret = (UINT_PTR*)&m_context.T5; - break; - - case REGISTER_LOONGARCH64_T6: - ret = (UINT_PTR*)&m_context.T6; - break; - - case REGISTER_LOONGARCH64_T7: - ret = (UINT_PTR*)&m_context.T7; - break; - - case REGISTER_LOONGARCH64_T8: - ret = (UINT_PTR*)&m_context.T8; - break; - - case REGISTER_LOONGARCH64_X0: - ret = (UINT_PTR*)&m_context.X0; - break; - - case REGISTER_LOONGARCH64_S0: - ret = (UINT_PTR*)&m_context.S0; - break; - - case REGISTER_LOONGARCH64_S1: - ret = (UINT_PTR*)&m_context.S1; - break; - - case REGISTER_LOONGARCH64_S2: - ret = (UINT_PTR*)&m_context.S2; - break; - - case REGISTER_LOONGARCH64_S3: - ret = (UINT_PTR*)&m_context.S3; - break; - - case REGISTER_LOONGARCH64_S4: - ret = (UINT_PTR*)&m_context.S4; - break; - - case REGISTER_LOONGARCH64_S5: - ret = (UINT_PTR*)&m_context.S5; - break; - - case REGISTER_LOONGARCH64_S6: - ret = (UINT_PTR*)&m_context.S6; - break; - - case REGISTER_LOONGARCH64_S7: - ret = (UINT_PTR*)&m_context.S7; - break; - - case REGISTER_LOONGARCH64_S8: - ret = (UINT_PTR*)&m_context.S8; - break; -#endif - - default: - _ASSERT(!"Invalid register number!"); - } - - return ret; -} // // GetLeftSideAddressOfRegister returns the Left Side address of the given register in the frames current register @@ -6450,8 +5708,9 @@ SIZE_T CordbNativeFrame::GetRegisterOrStackValue(const ICorDebugInfo::NativeVarI if (pNativeVarInfo->loc.vlType == ICorDebugInfo::VLT_REG) { - CorDebugRegister reg = ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlReg.vlrReg); - uResult = *(reinterpret_cast(GetAddressOfRegister(reg))); + TADDR regVal = 0; + IfFailThrow(ReadJitRegFromContext(pNativeVarInfo->loc.vlReg.vlrReg, ®Val)); + uResult = (SIZE_T)regVal; } else if (pNativeVarInfo->loc.vlType == ICorDebugInfo::VLT_STK) { @@ -6721,10 +5980,10 @@ HRESULT CordbNativeFrame::GetLocalRegisterValue(CorDebugRegister reg, } #endif - // The address of the given register is the address of the value - // in this process. We have no remote address here. - void *pLocalValue = (void*)GetAddressOfRegister(reg); - HRESULT hr = S_OK; + TADDR regVal = 0; + HRESULT hr = ReadContextRegister(reg, ®Val); + if (FAILED(hr)) + return hr; EX_TRY { @@ -6738,7 +5997,7 @@ HRESULT CordbNativeFrame::GetLocalRegisterValue(CorDebugRegister reg, pType, false, EMPTY_BUFFER, - MemoryRange(pLocalValue, REG_SIZE), + MemoryRange(®Val, REG_SIZE), pRegHolder, &pValue); // throws @@ -6958,6 +6217,8 @@ HRESULT CordbNativeFrame::GetLocalFloatingPointValue(DWORD index, (et != ELEMENT_TYPE_R8)) return E_INVALIDARG; + CorDebugRegister regNum = (CorDebugRegister)index; + #if defined(TARGET_AMD64) if (!((index >= REGISTER_AMD64_XMM0) && (index <= REGISTER_AMD64_XMM15))) @@ -6987,61 +6248,35 @@ HRESULT CordbNativeFrame::GetLocalFloatingPointValue(DWORD index, ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); - - // Make sure the thread's floating point stack state is loaded - // over from the left side. - // - CordbThread *pThread = m_pThread; - EX_TRY { - if (!pThread->m_fFloatStateValid) - { - pThread->LoadFloatState(); - } - } - EX_CATCH_HRESULT(hr); - if (SUCCEEDED(hr)) - { -#if !defined(TARGET_64BIT) - // This is needed on x86 because we are dealing with a stack. - index = pThread->m_floatStackTop - index; -#endif - - if (index >= (sizeof(pThread->m_floatValues) / - sizeof(pThread->m_floatValues[0]))) - return E_INVALIDARG; + // Read the register's scalar value from this frame's context. The DAC + // performs the arch-specific float / SIMD and x87 stack decoding. + double floatValue = 0.0; + IfFailThrow(ReadFloatContextRegister(regNum, &floatValue)); #ifdef TARGET_X86 // A workaround (sort of) to get around the difference in format between // a float value and a double value. We can't simply cast a double pointer to // a float pointer. Instead, we have to cast the double itself to a float. if (pType->m_elementType == ELEMENT_TYPE_R4) - *(float *)&(pThread->m_floatValues[index]) = (float)pThread->m_floatValues[index]; + *(float *)&floatValue = (float)floatValue; #endif - ICorDebugValue* pValue; - - EX_TRY - { - // Provide the register info as we create the value. CreateValueByType will transfer ownership of this to - // the new instance of CordbValue. - EnregisteredValueHomeHolder pRemoteReg(new FloatRegValueHome(this, index)); - EnregisteredValueHomeHolder * pRegHolder = pRemoteReg.GetAddr(); - - CordbValue::CreateValueByType(GetCurrentAppDomain(), - pType, - false, - EMPTY_BUFFER, - MemoryRange(&(pThread->m_floatValues[index]), sizeof(double)), - pRegHolder, - &pValue); // throws - - *ppValue = pValue; - } - EX_CATCH_HRESULT(hr); + // Provide the register info as we create the value. CreateValueByType will transfer ownership of this to + // the new instance of CordbValue. + EnregisteredValueHomeHolder pRemoteReg(new FloatRegValueHome(this, index, regNum)); + EnregisteredValueHomeHolder * pRegHolder = pRemoteReg.GetAddr(); + CordbValue::CreateValueByType(GetCurrentAppDomain(), + pType, + false, + EMPTY_BUFFER, + MemoryRange(&floatValue, sizeof(double)), + pRegHolder, + ppValue); // throws } + EX_CATCH_HRESULT(hr); return hr; } @@ -7053,11 +6288,9 @@ HRESULT CordbNativeFrame::GetLocalFloatingPointValue(DWORD index, // contiguous local snapshot, then the value is built from that snapshot. // // Arguments: -// lowReg - the register holding the low 8 bytes. When lowIsFloat is true -// this is a 0-based fp register index, otherwise a CorDebugRegister. +// lowReg - the CorDebugRegister holding the low 8 bytes. // lowIsFloat - whether the low half is in a floating-point register. -// highReg - the register holding the high 8 bytes. When highIsFloat is true -// this is a 0-based fp register index, otherwise a CorDebugRegister. +// highReg - the CorDebugRegister holding the high 8 bytes. // highIsFloat - whether the high half is in a floating-point register. // pType - the type of the value. // ppValue - [out] the newly created value. @@ -7083,53 +6316,37 @@ HRESULT CordbNativeFrame::GetLocalTwoRegisterValue(DWORD lowReg, EX_TRY { - CordbThread * pThread = m_pThread; - - // Ensure the floating-point state is loaded if either half lives in an fp register. - if (lowIsFloat || highIsFloat) - { - if (!pThread->m_fFloatStateValid) - { - pThread->LoadFloatState(); - } - } - - const DWORD numFloatValues = - (DWORD)(sizeof(pThread->m_floatValues) / sizeof(pThread->m_floatValues[0])); - // Gather the low 8 bytes. if (lowIsFloat) { - if (lowReg >= numFloatValues) - ThrowHR(E_INVALIDARG); - memcpy(valueBuffer, &pThread->m_floatValues[lowReg], sizeof(double)); + double floatValue = 0.0; + IfFailThrow(ReadFloatContextRegister((CorDebugRegister)lowReg, &floatValue)); + memcpy(valueBuffer, &floatValue, sizeof(double)); } else { - UINT_PTR * pReg = GetAddressOfRegister((CorDebugRegister)lowReg); - if (pReg == NULL) - ThrowHR(E_INVALIDARG); - memcpy(valueBuffer, pReg, sizeof(UINT_PTR)); + TADDR regValue = 0; + IfFailThrow(ReadContextRegister((CorDebugRegister)lowReg, ®Value)); + memcpy(valueBuffer, ®Value, sizeof(regValue)); } // Gather the high 8 bytes. if (highIsFloat) { - if (highReg >= numFloatValues) - ThrowHR(E_INVALIDARG); - memcpy(valueBuffer + sizeof(double), &pThread->m_floatValues[highReg], sizeof(double)); + double floatValue = 0.0; + IfFailThrow(ReadFloatContextRegister((CorDebugRegister)highReg, &floatValue)); + memcpy(valueBuffer + sizeof(double), &floatValue, sizeof(double)); } else { - UINT_PTR * pReg = GetAddressOfRegister((CorDebugRegister)highReg); - if (pReg == NULL) - ThrowHR(E_INVALIDARG); - memcpy(valueBuffer + sizeof(double), pReg, sizeof(UINT_PTR)); + TADDR regValue = 0; + IfFailThrow(ReadContextRegister((CorDebugRegister)highReg, ®Value)); + memcpy(valueBuffer + sizeof(double), ®Value, sizeof(regValue)); } // Build the value from the local snapshot. The value lives in two registers, at // least one of which is a floating-point register, so its contents cannot be - // reached through the integer register display. TwoRegisterValueHome captures the + // read from a single context source. TwoRegisterValueHome captures the // 16-byte snapshot so the value's object copy can be populated and so the home can // be cloned for read-only field access (writing back is not supported). EnregisteredValueHomeHolder pRemoteReg(new TwoRegisterValueHome(this, valueBuffer, sizeof(valueBuffer))); @@ -7196,7 +6413,12 @@ bool CordbNativeFrame::IsLeafFrame() const if (pNFrame != NULL) { // check if the leaf frame in the leaf chain is "this" - if (CompareControlRegisters(GetContext(), pNFrame->GetContext())) + BOOL fSameControlRegisters = FALSE; + IfFailThrow(GetProcess()->GetDAC()->CompareControlRegisters( + GetContext(), + pNFrame->GetContext(), + &fSameControlRegisters)); + if (fSameControlRegisters) { m_optfIsLeafFrame = TRUE; } @@ -7213,7 +6435,10 @@ bool CordbNativeFrame::IsLeafFrame() const { IDacDbiInterface * pDAC = GetProcess()->GetDAC(); BOOL isLeaf; - IfFailThrow(pDAC->IsLeafFrame(m_pThread->m_vmThreadToken, &m_context, &isLeaf)); + IfFailThrow(pDAC->IsLeafFrame( + m_pThread->m_vmThreadToken, + GetContext(), + &isLeaf)); m_optfIsLeafFrame = (isLeaf == TRUE); } } @@ -8340,14 +7565,17 @@ HRESULT CordbJITILFrame::GetNativeVariable(CordbType *type, { case ICorDebugInfo::VLT_REG: hr = m_nativeFrame->GetLocalRegisterValue( - ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlReg.vlrReg), + m_nativeFrame->ConvertJitRegToCorDebugRegister(pNativeVarInfo->loc.vlReg.vlrReg), type, ppValue); break; case ICorDebugInfo::VLT_REG_BYREF: { - CORDB_ADDRESS pRemoteByRefAddr = PTR_TO_CORDB_ADDRESS( - *( m_nativeFrame->GetAddressOfRegister(ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlReg.vlrReg))) ); + TADDR regVal = 0; + IfFailThrow(m_nativeFrame->ReadJitRegFromContext( + pNativeVarInfo->loc.vlReg.vlrReg, + ®Val)); + CORDB_ADDRESS pRemoteByRefAddr = PTR_TO_CORDB_ADDRESS(regVal); hr = m_nativeFrame->GetLocalMemoryValue(pRemoteByRefAddr, type, @@ -8413,10 +7641,10 @@ HRESULT CordbJITILFrame::GetNativeVariable(CordbType *type, // VLT_REG_REG can represent mixed int/fp pairs. FP register // indices for GetLocalTwoRegisterValue are 0-based. hr = m_nativeFrame->GetLocalTwoRegisterValue( - lowIsFloat ? lowReg - ICorDebugInfo::REGNUM_FP_FIRST + lowIsFloat ? (CorDebugRegister)(REGISTER_AMD64_XMM0 + (lowReg - ICorDebugInfo::REGNUM_FP_FIRST)) : ConvertRegNumToCorDebugRegister(lowReg), lowIsFloat, - highIsFloat ? highReg - ICorDebugInfo::REGNUM_FP_FIRST + highIsFloat ? (CorDebugRegister)(REGISTER_AMD64_XMM0 + (highReg - ICorDebugInfo::REGNUM_FP_FIRST)) : ConvertRegNumToCorDebugRegister(highReg), highIsFloat, type, @@ -8426,8 +7654,8 @@ HRESULT CordbJITILFrame::GetNativeVariable(CordbType *type, } #endif hr = m_nativeFrame->GetLocalDoubleRegisterValue( - ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlRegReg.vlrrReg2), - ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlRegReg.vlrrReg1), + m_nativeFrame->ConvertJitRegToCorDebugRegister(pNativeVarInfo->loc.vlRegReg.vlrrReg2), + m_nativeFrame->ConvertJitRegToCorDebugRegister(pNativeVarInfo->loc.vlRegReg.vlrrReg1), type, ppValue); break; @@ -8438,7 +7666,7 @@ HRESULT CordbJITILFrame::GetNativeVariable(CordbType *type, hr = m_nativeFrame->GetLocalMemoryRegisterValue( pRemoteValue, - ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlRegStk.vlrsReg), + m_nativeFrame->ConvertJitRegToCorDebugRegister(pNativeVarInfo->loc.vlRegStk.vlrsReg), type, ppValue); } break; @@ -8449,7 +7677,7 @@ HRESULT CordbJITILFrame::GetNativeVariable(CordbType *type, pNativeVarInfo->loc.vlStkReg.vlsrStk.vlsrsBaseReg, pNativeVarInfo->loc.vlStkReg.vlsrStk.vlsrsOffset); hr = m_nativeFrame->GetLocalRegisterMemoryValue( - ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlStkReg.vlsrReg), + m_nativeFrame->ConvertJitRegToCorDebugRegister(pNativeVarInfo->loc.vlStkReg.vlsrReg), pRemoteValue, type, ppValue); } break; @@ -11531,4 +10759,4 @@ void CordbAsyncFrame::LoadGenericArgs() m_genericArgsLoaded = true; ppGenericArgs.SuppressRelease(); -} +} \ No newline at end of file diff --git a/src/coreclr/debug/di/shimlocaldatatarget.cpp b/src/coreclr/debug/di/shimlocaldatatarget.cpp index 8bad40ee735e29..fb2317a0ff9e2e 100644 --- a/src/coreclr/debug/di/shimlocaldatatarget.cpp +++ b/src/coreclr/debug/di/shimlocaldatatarget.cpp @@ -380,7 +380,7 @@ ShimLocalDataTarget::GetThreadContext( HRESULT hr = E_FAIL; - if (!CheckContextSizeForBuffer(contextSize, pContext)) + if ((pContext == NULL) || !CheckContextSizeForFlags(contextSize, contextFlags)) { return E_INVALIDARG; } @@ -392,7 +392,7 @@ ShimLocalDataTarget::GetThreadContext( if (hThread != NULL) { - DT_CONTEXT * pCtx = reinterpret_cast(pContext); + T_CONTEXT * pCtx = reinterpret_cast(pContext); pCtx->ContextFlags = contextFlags; if (DbiGetThreadContext(hThread, pCtx)) @@ -428,7 +428,7 @@ ShimLocalDataTarget::SetThreadContext( if (hThread != NULL) { - if (DbiSetThreadContext(hThread, reinterpret_cast(pContext))) + if (DbiSetThreadContext(hThread, reinterpret_cast(pContext))) { hr = S_OK; } diff --git a/src/coreclr/debug/di/shimpriv.h b/src/coreclr/debug/di/shimpriv.h index acbf2b5da4b522..04e29e98d62df1 100644 --- a/src/coreclr/debug/di/shimpriv.h +++ b/src/coreclr/debug/di/shimpriv.h @@ -638,6 +638,10 @@ class ShimStackWalk ShimChain * GetChain(UINT32 index); ICorDebugFrame * GetFrame(UINT32 index); + // The target's CONTEXT size in bytes (forwards to CordbProcess::GetTargetContextSize, + // which memoizes it). ShimStackWalk owns no context buffer, so it caches nothing itself. + ULONG32 GetContextSize(); + // Get the number of frames and chains. ULONG GetChainCount(); ULONG GetFrameCount(); @@ -684,13 +688,25 @@ class ShimStackWalk struct ChainInfo { public: - ChainInfo() : m_rootFP(LEAF_MOST_FRAME), m_reason(CHAIN_NONE), m_fNeedEnterManagedChain(FALSE), m_fLeafNativeContextIsValid(FALSE) {} + ChainInfo(ULONG32 contextSize) + : m_leafNativeContext(new BYTE[contextSize]), + m_leafManagedContext(new BYTE[contextSize]), + m_contextSize(contextSize), + m_rootFP(LEAF_MOST_FRAME), + m_reason(CHAIN_NONE), + m_fNeedEnterManagedChain(FALSE), + m_fLeafNativeContextIsValid(FALSE) {} void CancelUMChain() { m_reason = CHAIN_NONE; } BOOL IsTrackingUMChain() { return (m_reason == CHAIN_ENTER_UNMANAGED); } - DT_CONTEXT m_leafNativeContext; - DT_CONTEXT m_leafManagedContext; + ContextBuffer GetLeafNativeContext() { return { m_leafNativeContext, m_contextSize }; } + ContextBuffer GetLeafManagedContext() { return { m_leafManagedContext, m_contextSize }; } + + // Leaf CONTEXT buffers, sized at construction to the target's CONTEXT size. + NewArrayHolder m_leafNativeContext; + NewArrayHolder m_leafManagedContext; + ULONG32 m_contextSize; FramePointer m_rootFP; CorDebugChainReason m_reason; bool m_fNeedEnterManagedChain; @@ -776,7 +792,7 @@ class ShimStackWalk void Clear(); // Get a FramePointer to mark the root boundary of a chain. - FramePointer GetFramePointerForChain(DT_CONTEXT * pContext); + FramePointer GetFramePointerForChain(ContextBuffer contextBuffer); FramePointer GetFramePointerForChain(ICorDebugInternalFrame2 * pInternalFrame2); CorDebugInternalFrameType GetInternalFrameType(ICorDebugInternalFrame2 * pFrame2); @@ -787,14 +803,14 @@ class ShimStackWalk // Append a chain to the array. void AppendChainWorker(StackWalkInfo * pStackWalkInfo, - DT_CONTEXT * pLeafContext, + ContextBuffer contextBuffer, FramePointer fpRoot, CorDebugChainReason chainReason, BOOL fIsManagedChain); void AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo); // Save information on the ChainInfo regarding the current chain. - void SaveChainContext(ICorDebugStackWalk * pSW, ChainInfo * pChainInfo, DT_CONTEXT * pContext); + void SaveChainContext(ICorDebugStackWalk * pSW, ChainInfo * pChainInfo, ContextBuffer contextBuffer); // Check what we are process next, a internal frame or a stack frame. BOOL CheckInternalFrame(ICorDebugFrame * pNextStackFrame, @@ -842,7 +858,7 @@ class ShimChain : public ICorDebugChain { public: ShimChain(ShimStackWalk * pSW, - DT_CONTEXT * pContext, + ContextBuffer contextBuffer, FramePointer fpRoot, UINT32 chainIndex, UINT32 frameStartIndex, @@ -898,7 +914,10 @@ class ShimChain : public ICorDebugChain // end of the chain, and a frame pointer where the chain ends (rootmost). This stack range is exposed // publicly via ICDChain::GetStackRange(), and can be used to stitch managed and native stack frames // together into a unified stack. - DT_CONTEXT m_context; // the leaf end of the chain + // The CONTEXT is held as an opaque byte buffer sized to the target's CONTEXT (GetTargetContextSize), + // so ShimChain keeps no per-arch knowledge of the CONTEXT layout. + NewArrayHolder m_pContext; // the leaf end of the chain + ULONG32 m_contextSize; FramePointer m_fpRoot; // the root end of the chain ShimStackWalk * m_pStackWalk; // the owning ShimStackWalk @@ -1046,4 +1065,3 @@ class ShimFrameEnum : public ICorDebugFrameEnum #endif // SHIMPRIV_H - diff --git a/src/coreclr/debug/di/shimstackwalk.cpp b/src/coreclr/debug/di/shimstackwalk.cpp index f289788507a54e..ab4cdcf9cfcaa0 100644 --- a/src/coreclr/debug/di/shimstackwalk.cpp +++ b/src/coreclr/debug/di/shimstackwalk.cpp @@ -22,6 +22,23 @@ static const ULONG32 REGISTER_AMD64_MAX = REGISTER_AMD64_XMM15 + 1; static const ULONG32 MAX_MASK_COUNT = (REGISTER_AMD64_MAX + 7) >> 3; #endif +namespace +{ + TADDR ReadCtxReg(CordbThread * pThread, + ContextBuffer contextBuffer, + CorDebugRegister reg) + { + CORDB_REGISTER value = 0; + IDacDbiInterface * pDAC = pThread->GetProcess()->GetDAC(); + IfFailThrow(pDAC->ReadRegistersFromContext( + contextBuffer, + ®, + 1, + &value)); + return (TADDR)value; + } +} + ShimStackWalk::ShimStackWalk(ShimProcess * pProcess, ICorDebugThread * pThread) : m_pChainEnumList(NULL), m_pFrameEnumList(NULL) @@ -33,6 +50,11 @@ ShimStackWalk::ShimStackWalk(ShimProcess * pProcess, ICorDebugThread * pThread) Populate(); } +ULONG32 ShimStackWalk::GetContextSize() +{ + return static_cast(m_pThread.GetValue())->GetProcess()->GetTargetContextSize(); +} + ShimStackWalk::~ShimStackWalk() { Clear(); @@ -184,7 +206,7 @@ void ShimStackWalk::Populate() IfFailThrow(hr); // structs used to store information during the stackwalk - ChainInfo chainInfo; + ChainInfo chainInfo(GetContextSize()); StackWalkInfo swInfo; // use the ICDStackWalk to retrieve the internal frames @@ -356,7 +378,7 @@ void ShimStackWalk::Populate() { // If we have hit any managed stack frame, then we may need to send // an enter-managed chain later. Save the CONTEXT now. - SaveChainContext(pSW, &chainInfo, &(chainInfo.m_leafManagedContext)); + SaveChainContext(pSW, &chainInfo, chainInfo.GetLeafManagedContext()); chainInfo.m_fNeedEnterManagedChain = true; } @@ -411,7 +433,7 @@ void ShimStackWalk::Populate() // we have exhausted all the stack frames. // We need to save the CONTEXT to start tracking an unmanaged chain. - SaveChainContext(pSW, &chainInfo, &(chainInfo.m_leafNativeContext)); + SaveChainContext(pSW, &chainInfo, chainInfo.GetLeafNativeContext()); chainInfo.m_fLeafNativeContextIsValid = true; // begin tracking UM chain if we're supposed to @@ -954,9 +976,11 @@ void ShimStackWalk::GetCalleeForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** } } -FramePointer ShimStackWalk::GetFramePointerForChain(DT_CONTEXT * pContext) +FramePointer ShimStackWalk::GetFramePointerForChain(ContextBuffer contextBuffer) { - return FramePointer::MakeFramePointer(CORDB_ADDRESS_TO_PTR(CORDbgGetSP(pContext))); + CordbThread * pThread = static_cast(m_pThread.GetValue()); + TADDR sp = ReadCtxReg(pThread, contextBuffer, REGISTER_STACK_POINTER); + return FramePointer::MakeFramePointer(reinterpret_cast(sp)); } FramePointer ShimStackWalk::GetFramePointerForChain(ICorDebugInternalFrame2 * pInternalFrame2) @@ -1050,14 +1074,14 @@ void ShimStackWalk::AppendFrame(ICorDebugInternalFrame2 * pInternalFrame2, Stack // void ShimStackWalk::AppendChainWorker(StackWalkInfo * pStackWalkInfo, - DT_CONTEXT * pLeafContext, + ContextBuffer contextBuffer, FramePointer fpRoot, CorDebugChainReason chainReason, BOOL fIsManagedChain) { // first, create the chain NewHolder pChain(new ShimChain(this, - pLeafContext, + contextBuffer, fpRoot, pStackWalkInfo->m_cChain, pStackWalkInfo->m_firstFrameInChain, @@ -1104,12 +1128,12 @@ void ShimStackWalk::AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWa fManagedChain = TRUE; } - DT_CONTEXT * pChainContext = NULL; + ContextBuffer pChainContext = { NULL, 0 }; if (fManagedChain) { // The chain to be added is managed itself. So we don't need to send an enter-managed chain. pChainInfo->m_fNeedEnterManagedChain = false; - pChainContext = &(pChainInfo->m_leafManagedContext); + pChainContext = pChainInfo->GetLeafManagedContext(); } else { @@ -1118,7 +1142,10 @@ void ShimStackWalk::AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWa { // We need to send an extra enter-managed chain. _ASSERTE(pChainInfo->m_fLeafNativeContextIsValid); - BYTE * sp = reinterpret_cast(CORDB_ADDRESS_TO_PTR(CORDbgGetSP(&(pChainInfo->m_leafNativeContext)))); + CordbThread * pThread = static_cast(m_pThread.GetValue()); + ContextBuffer nativeContext = pChainInfo->GetLeafNativeContext(); + BYTE * sp = reinterpret_cast( + ReadCtxReg(pThread, nativeContext, REGISTER_STACK_POINTER)); #if !defined(TARGET_ARM) && !defined(TARGET_ARM64) // Dev11 324806: on ARM we use the caller's SP for a frame's ending delimiter so we cannot // subtract 4 bytes from the chain's ending delimiter else the frame might never be in range. @@ -1127,8 +1154,9 @@ void ShimStackWalk::AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWa #endif FramePointer fp = FramePointer::MakeFramePointer(sp); + ContextBuffer managedContext = pChainInfo->GetLeafManagedContext(); AppendChainWorker(pStackWalkInfo, - &(pChainInfo->m_leafManagedContext), + managedContext, fp, CHAIN_ENTER_MANAGED, TRUE); @@ -1136,7 +1164,7 @@ void ShimStackWalk::AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWa pChainInfo->m_fNeedEnterManagedChain = false; } _ASSERTE(pChainInfo->m_fLeafNativeContextIsValid); - pChainContext = &(pChainInfo->m_leafNativeContext); + pChainContext = pChainInfo->GetLeafNativeContext(); } // Add the actual chain. @@ -1157,18 +1185,23 @@ void ShimStackWalk::AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWa // Arguments: // * pSW - the ICDStackWalk for the current stackwalk // * pChainInfo - the ChainInfo keeping track of the current chain -// * pContext - the destination CONTEXT +// * contextBuffer - the destination CONTEXT // -void ShimStackWalk::SaveChainContext(ICorDebugStackWalk * pSW, ChainInfo * pChainInfo, DT_CONTEXT * pContext) +void ShimStackWalk::SaveChainContext(ICorDebugStackWalk * pSW, ChainInfo * pChainInfo, ContextBuffer contextBuffer) { + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < GetContextSize())) + { + ThrowHR(E_INVALIDARG); + } + HRESULT hr = pSW->GetContext(CONTEXT_FULL, - sizeof(*pContext), + contextBuffer.contextSize, NULL, - reinterpret_cast(pContext)); + contextBuffer.pContextBytes); IfFailThrow(hr); - pChainInfo->m_rootFP = GetFramePointerForChain(pContext); + pChainInfo->m_rootFP = GetFramePointerForChain(contextBuffer); } // ---------------------------------------------------------------------------- @@ -1209,14 +1242,14 @@ BOOL ShimStackWalk::CheckInternalFrame(ICorDebugFrame * pNextStackFrame, hr = pThread3->CreateStackWalk(&pTmpSW); IfFailThrow(hr); - // retrieve the current CONTEXT - DT_CONTEXT ctx; - ctx.ContextFlags = DT_CONTEXT_FULL; - hr = pSW->GetContext(ctx.ContextFlags, sizeof(ctx), NULL, reinterpret_cast(&ctx)); + // retrieve the current CONTEXT into an opaque target-sized buffer + ULONG32 ctxSize = GetContextSize(); + NewArrayHolder ctx(new BYTE[ctxSize]); + hr = pSW->GetContext(CONTEXT_FULL, ctxSize, NULL, ctx); IfFailThrow(hr); // set the CONTEXT on the temporary ICDStackWalk - hr = pTmpSW->SetContext(SET_CONTEXT_FLAG_ACTIVE_FRAME, sizeof(ctx), reinterpret_cast(&ctx)); + hr = pTmpSW->SetContext(SET_CONTEXT_FLAG_ACTIVE_FRAME, ctxSize, ctx); IfFailThrow(hr); // unwind the temporary ICDStackWalk by one frame @@ -1225,11 +1258,14 @@ BOOL ShimStackWalk::CheckInternalFrame(ICorDebugFrame * pNextStackFrame, // Unwinding from a managed stack frame will land us either in a managed stack frame or a native // stack frame. In either case, we have a CONTEXT. - hr = pTmpSW->GetContext(ctx.ContextFlags, sizeof(ctx), NULL, reinterpret_cast(&ctx)); + hr = pTmpSW->GetContext(CONTEXT_FULL, ctxSize, NULL, ctx); IfFailThrow(hr); // Get the SP from the CONTEXT. This is the caller SP. - CORDB_ADDRESS sp = CORDbgGetSP(&ctx); + CordbThread * pThread = static_cast(m_pThread.GetValue()); + ContextBuffer contextBuffer = { ctx, ctxSize }; + CORDB_ADDRESS sp = PTR_TO_CORDB_ADDRESS( + ReadCtxReg(pThread, contextBuffer, REGISTER_STACK_POINTER)); // get the frame address CORDB_ADDRESS frameAddr = 0; @@ -1428,7 +1464,8 @@ void ShimStackWalk::TrackUMChain(ChainInfo * pChainInfo, StackWalkInfo * pStackW { // check whether we get any stack range _ASSERTE(pChainInfo->m_fLeafNativeContextIsValid); - FramePointer fpLeaf = GetFramePointerForChain(&(pChainInfo->m_leafNativeContext)); + ContextBuffer contextBuffer = pChainInfo->GetLeafNativeContext(); + FramePointer fpLeaf = GetFramePointerForChain(contextBuffer); // Don't bother creating an unmanaged chain if the stack range is empty. if (fpLeaf != pChainInfo->m_rootFP) @@ -1572,7 +1609,7 @@ BOOL ShimStackWalk::StackWalkInfo::HasConvertedFrame() ShimChain::ShimChain(ShimStackWalk * pSW, - DT_CONTEXT * pContext, + ContextBuffer contextBuffer, FramePointer fpRoot, UINT32 chainIndex, UINT32 frameStartIndex, @@ -1580,7 +1617,7 @@ ShimChain::ShimChain(ShimStackWalk * pSW, CorDebugChainReason chainReason, BOOL fIsManaged, RSLock * pShimLock) - : m_context(*pContext), + : m_contextSize(pSW->GetContextSize()), m_fpRoot(fpRoot), m_pStackWalk(pSW), m_refCount(0), @@ -1592,6 +1629,14 @@ ShimChain::ShimChain(ShimStackWalk * pSW, m_fIsNeutered(FALSE), m_pShimLock(pShimLock) { + if ((contextBuffer.pContextBytes == NULL) || (contextBuffer.contextSize < m_contextSize)) + { + ThrowHR(E_INVALIDARG); + } + + // Own a copy of the leaf CONTEXT as an opaque target-sized byte buffer. + m_pContext = new BYTE[m_contextSize]; + memcpy(m_pContext, contextBuffer.pContextBytes, m_contextSize); } ShimChain::~ShimChain() @@ -1677,7 +1722,10 @@ HRESULT ShimChain::GetStackRange(CORDB_ADDRESS * pStart, CORDB_ADDRESS * pEnd) // The leafmost end is represented by the register set. if (pStart) { - *pStart = CORDbgGetSP(&m_context); + CordbThread * pThread = static_cast(m_pStackWalk->GetThread()); + ContextBuffer contextBuffer = { m_pContext, m_contextSize }; + *pStart = PTR_TO_CORDB_ADDRESS( + ReadCtxReg(pThread, contextBuffer, REGISTER_STACK_POINTER)); } // Return the rootmost end of the stack range. It is represented by the frame pointer of the chain. @@ -1830,7 +1878,8 @@ HRESULT ShimChain::GetRegisterSet(ICorDebugRegisterSet ** ppRegisters) // This is a private hook for calling back into the RS. Alternatively, we could have created a // ShimRegisterSet, but that's too much work for now. - pThread->CreateCordbRegisterSet(&m_context, + ContextBuffer contextBuffer = { m_pContext, m_contextSize }; + pThread->CreateCordbRegisterSet(contextBuffer, (m_chainIndex == 0), m_chainReason, ppRegisters); diff --git a/src/coreclr/debug/di/stdafx.h b/src/coreclr/debug/di/stdafx.h index 0f1ae48265b9de..44b03d8b240507 100644 --- a/src/coreclr/debug/di/stdafx.h +++ b/src/coreclr/debug/di/stdafx.h @@ -44,10 +44,5 @@ using std::max; #include "utilcode.h" #endif -#ifndef TARGET_ARM -#define DbiGetThreadContext(hThread, lpContext) ::GetThreadContext(hThread, (CONTEXT*)(lpContext)) -#define DbiSetThreadContext(hThread, lpContext) ::SetThreadContext(hThread, (CONTEXT*)(lpContext)) -#else -BOOL DbiGetThreadContext(HANDLE hThread, DT_CONTEXT *lpContext); -BOOL DbiSetThreadContext(HANDLE hThread, const DT_CONTEXT *lpContext); -#endif +BOOL DbiGetThreadContext(HANDLE hThread, T_CONTEXT *lpContext); +BOOL DbiSetThreadContext(HANDLE hThread, const T_CONTEXT *lpContext); diff --git a/src/coreclr/debug/di/valuehome.cpp b/src/coreclr/debug/di/valuehome.cpp index 24230bb6c85cd1..5814ca5fbdd757 100644 --- a/src/coreclr/debug/di/valuehome.cpp +++ b/src/coreclr/debug/di/valuehome.cpp @@ -37,144 +37,25 @@ void RegValueHome::CopyToIPCEType(RemoteAddress * pRegAddr) // RegValueHome::SetContextRegister // This will update a register in a given context, and in the regdisplay of a given frame. // Arguments: -// input: pContext - context from which the register comes +// updates a register in a given context buffer using the DAC. +// Arguments: +// input: contextBuffer - context buffer in which the register is to be updated // regnum - enumeration constant indicating which register is to be updated // newVal - the new value for the register contents -// output: no out parameters, but the new value will be written to the context and the frame -// Notes: We don't take a data target here because we are directly writing process memory and passing -// in a context, which has the location to update. -// Throws -void RegValueHome::SetContextRegister(DT_CONTEXT * pContext, +// output: no out parameters, but the new value will be written to the context buffer +void RegValueHome::SetContextRegister(ContextBuffer contextBuffer, CorDebugRegister regNum, SIZE_T newVal) { - LPVOID rdRegAddr; - -#define _UpdateFrame() \ - if (m_pFrame != NULL) \ - { \ - rdRegAddr = m_pFrame->GetAddressOfRegister(regNum); \ - *(SIZE_T *)rdRegAddr = newVal; \ - } - - switch(regNum) + IDacDbiInterface * pDAC = m_pFrame->GetProcess()->GetDAC(); + TADDR value = (TADDR)newVal; + HRESULT hr = pDAC->WriteRegistersToContext( + contextBuffer, + ®Num, + 1, + &value); + if (FAILED(hr)) { - case REGISTER_INSTRUCTION_POINTER: CORDbgSetIP(pContext, (LPVOID)newVal); break; - case REGISTER_STACK_POINTER: CORDbgSetSP(pContext, (LPVOID)newVal); break; - -#if defined(TARGET_X86) - case REGISTER_FRAME_POINTER: CORDbgSetFP(pContext, (LPVOID)newVal); - _UpdateFrame(); break; - - case REGISTER_X86_EAX: pContext->Eax = newVal; - _UpdateFrame(); break; - case REGISTER_X86_ECX: pContext->Ecx = newVal; - _UpdateFrame(); break; - case REGISTER_X86_EDX: pContext->Edx = newVal; - _UpdateFrame(); break; - case REGISTER_X86_EBX: pContext->Ebx = newVal; - _UpdateFrame(); break; - case REGISTER_X86_ESI: pContext->Esi = newVal; - _UpdateFrame(); break; - case REGISTER_X86_EDI: pContext->Edi = newVal; - _UpdateFrame(); break; - -#elif defined(TARGET_AMD64) - case REGISTER_AMD64_RBP: pContext->Rbp = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_RAX: pContext->Rax = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_RCX: pContext->Rcx = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_RDX: pContext->Rdx = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_RBX: pContext->Rbx = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_RSI: pContext->Rsi = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_RDI: pContext->Rdi = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_R8: pContext->R8 = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_R9: pContext->R9 = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_R10: pContext->R10 = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_R11: pContext->R11 = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_R12: pContext->R12 = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_R13: pContext->R13 = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_R14: pContext->R14 = newVal; - _UpdateFrame(); break; - case REGISTER_AMD64_R15: pContext->R15 = newVal; - _UpdateFrame(); break; -#elif defined(TARGET_ARM) - case REGISTER_ARM_R0: pContext->R0 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R1: pContext->R1 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R2: pContext->R2 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R3: pContext->R3 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R4: pContext->R4 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R5: pContext->R5 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R6: pContext->R6 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R7: pContext->R7 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R8: pContext->R8 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R9: pContext->R9 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R10: pContext->R10 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R11: pContext->R11 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_R12: pContext->R12 = newVal; - _UpdateFrame(); break; - case REGISTER_ARM_LR: pContext->Lr = newVal; - _UpdateFrame(); break; -#elif defined(TARGET_ARM64) - case REGISTER_ARM64_X0: - case REGISTER_ARM64_X1: - case REGISTER_ARM64_X2: - case REGISTER_ARM64_X3: - case REGISTER_ARM64_X4: - case REGISTER_ARM64_X5: - case REGISTER_ARM64_X6: - case REGISTER_ARM64_X7: - case REGISTER_ARM64_X8: - case REGISTER_ARM64_X9: - case REGISTER_ARM64_X10: - case REGISTER_ARM64_X11: - case REGISTER_ARM64_X12: - case REGISTER_ARM64_X13: - case REGISTER_ARM64_X14: - case REGISTER_ARM64_X15: - case REGISTER_ARM64_X16: - case REGISTER_ARM64_X17: - case REGISTER_ARM64_X18: - case REGISTER_ARM64_X19: - case REGISTER_ARM64_X20: - case REGISTER_ARM64_X21: - case REGISTER_ARM64_X22: - case REGISTER_ARM64_X23: - case REGISTER_ARM64_X24: - case REGISTER_ARM64_X25: - case REGISTER_ARM64_X26: - case REGISTER_ARM64_X27: - case REGISTER_ARM64_X28: pContext->X[regNum - REGISTER_ARM64_X0] = newVal; - _UpdateFrame(); break; - - case REGISTER_ARM64_LR: pContext->Lr = newVal; - _UpdateFrame(); break; -#endif - default: _ASSERTE(!"Invalid register number!"); ThrowHR(E_FAIL); } @@ -183,7 +64,7 @@ void RegValueHome::SetContextRegister(DT_CONTEXT * pContext, // RegValueHome::SetEnregisteredValue // set a remote enregistered location to a new value (see code:EnregisteredValueHome::SetEnregisteredValue // for full header comment) -void RegValueHome::SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned) +void RegValueHome::SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned) { SIZE_T extendedVal = 0; @@ -229,7 +110,7 @@ void RegValueHome::SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pCont } } - SetContextRegister(pContext, m_reg1Info.m_kRegNumber, extendedVal); // throws + SetContextRegister(contextBuffer, m_reg1Info.m_kRegNumber, extendedVal); // throws } // RegValueHome::SetEnregisteredValue // RegValueHome::GetEnregisteredValue @@ -237,11 +118,13 @@ void RegValueHome::SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pCont // for full header comment) void RegValueHome::GetEnregisteredValue(MemoryRange valueOutBuffer) { - UINT_PTR* reg = m_pFrame->GetAddressOfRegister(m_reg1Info.m_kRegNumber); - _ASSERTE(reg != NULL); - _ASSERTE(sizeof(*reg) == valueOutBuffer.Size()); + _ASSERTE(sizeof(TADDR) == valueOutBuffer.Size()); + + TADDR value = 0; + HRESULT hr = m_pFrame->ReadContextRegister(m_reg1Info.m_kRegNumber, &value); + IfFailThrow(hr); - memcpy(valueOutBuffer.StartAddress(), reg, sizeof(*reg)); + memcpy(valueOutBuffer.StartAddress(), &value, sizeof(value)); } // RegValueHome::GetEnregisteredValue @@ -266,7 +149,7 @@ void RegRegValueHome::CopyToIPCEType(RemoteAddress * pRegAddr) // RegRegValueHome::SetEnregisteredValue // set a remote enregistered location to a new value (see EnregisteredValueHome::SetEnregisteredValue // for full header comment) -void RegRegValueHome::SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned) +void RegRegValueHome::SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned) { // A two-register value occupies more than one register's worth of space // and at most two registers' worth. On x86 this is 8 bytes (2*4), on @@ -290,23 +173,10 @@ void RegRegValueHome::SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pC memcpy(&highPart, (BYTE *)newValue.StartAddress() + REG_SIZE, newValue.Size() - REG_SIZE); } - // Update the proper registers. - SetContextRegister(pContext, m_reg1Info.m_kRegNumber, highPart); // throws - SetContextRegister(pContext, m_reg2Info.m_kRegNumber, lowPart); // throws - - // Update the frame's register display for each register individually. - // We must not do a single memcpy of the full value into reg1's address - // because the two registers may not be contiguous in the CONTEXT layout. - UINT_PTR * pReg1 = m_pFrame->GetAddressOfRegister(m_reg1Info.m_kRegNumber); - UINT_PTR * pReg2 = m_pFrame->GetAddressOfRegister(m_reg2Info.m_kRegNumber); - if (pReg1 != NULL) - { - *pReg1 = highPart; - } - if (pReg2 != NULL) - { - *pReg2 = lowPart; - } + // Update the proper registers. SetContextRegister writes through to the + // active CONTEXT - which is the only source of truth now that REGDISPLAY is gone. + SetContextRegister(contextBuffer, m_reg1Info.m_kRegNumber, highPart); // throws + SetContextRegister(contextBuffer, m_reg2Info.m_kRegNumber, lowPart); // throws } // RegRegValueHome::SetEnregisteredValue // RegRegValueHome::GetEnregisteredValue @@ -314,26 +184,29 @@ void RegRegValueHome::SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pC // for full header comment) void RegRegValueHome::GetEnregisteredValue(MemoryRange valueOutBuffer) { - UINT_PTR* highWordAddr = m_pFrame->GetAddressOfRegister(m_reg1Info.m_kRegNumber); - _ASSERTE(highWordAddr != NULL); - - UINT_PTR* lowWordAddr = m_pFrame->GetAddressOfRegister(m_reg2Info.m_kRegNumber); - _ASSERTE(lowWordAddr != NULL); + TADDR highWord = 0; + TADDR lowWord = 0; + IfFailThrow(m_pFrame->ReadContextRegister(m_reg1Info.m_kRegNumber, &highWord)); + IfFailThrow(m_pFrame->ReadContextRegister(m_reg2Info.m_kRegNumber, &lowWord)); // The low half occupies the first register-sized chunk; the high half the second. // The out buffer may be smaller than two registers (e.g. a 12-byte struct returned // in two 8-byte registers), so clamp each copy to the bytes that actually remain. - const SIZE_T cbReg = sizeof(*lowWordAddr); + const SIZE_T cbReg = sizeof(lowWord); const SIZE_T cbTotal = valueOutBuffer.Size(); _ASSERTE(cbTotal <= 2 * cbReg); + if (cbTotal > 2 * cbReg) + { + ThrowHR(E_INVALIDARG); + } const SIZE_T cbLow = (cbTotal < cbReg) ? cbTotal : cbReg; - memcpy(valueOutBuffer.StartAddress(), lowWordAddr, cbLow); + memcpy(valueOutBuffer.StartAddress(), &lowWord, cbLow); if (cbTotal > cbReg) { const SIZE_T cbHigh = cbTotal - cbReg; - memcpy((BYTE *)valueOutBuffer.StartAddress() + cbReg, highWordAddr, cbHigh); + memcpy((BYTE *)valueOutBuffer.StartAddress() + cbReg, &highWord, cbHigh); } } // RegRegValueHome::GetEnregisteredValue @@ -375,7 +248,7 @@ void RegMemValueHome::CopyToIPCEType(RemoteAddress * pRegAddr) // RegMemValueHome::SetEnregisteredValue // set a remote enregistered location to a new value (see EnregisteredValueHome::SetEnregisteredValue // for full header comment) -void RegMemValueHome::SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned) +void RegMemValueHome::SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned) { _ASSERTE(newValue.Size() == REG_SIZE >> 1); // make sure we have bytes for two registers _ASSERTE(REG_SIZE == sizeof(void*)); @@ -388,7 +261,7 @@ void RegMemValueHome::SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pC memcpy(&highPart, (BYTE *)newValue.StartAddress() + REG_SIZE, REG_SIZE); // Update the proper registers. - SetContextRegister(pContext, m_reg1Info.m_kRegNumber, highPart); // throws + SetContextRegister(contextBuffer, m_reg1Info.m_kRegNumber, highPart); // throws _ASSERTE(REG_SIZE == sizeof(lowPart)); HRESULT hr = m_pFrame->GetProcess()->SafeReadStruct(m_memAddr, &lowPart); @@ -402,18 +275,18 @@ void RegMemValueHome::SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pC void RegMemValueHome::GetEnregisteredValue(MemoryRange valueOutBuffer) { // Read the high bits from the register... - UINT_PTR* highBitsAddr = m_pFrame->GetAddressOfRegister(m_reg1Info.m_kRegNumber); - _ASSERTE(highBitsAddr != NULL); + TADDR highBits = 0; + IfFailThrow(m_pFrame->ReadContextRegister(m_reg1Info.m_kRegNumber, &highBits)); // ... and the low bits from the remote process DWORD lowBits; HRESULT hr = m_pFrame->GetProcess()->SafeReadStruct(m_memAddr, &lowBits); IfFailThrow(hr); - _ASSERTE(sizeof(lowBits)+sizeof(*highBitsAddr) == valueOutBuffer.Size()); + _ASSERTE(sizeof(lowBits) + sizeof(highBits) == valueOutBuffer.Size()); memcpy(valueOutBuffer.StartAddress(), &lowBits, sizeof(lowBits)); - memcpy((BYTE *)valueOutBuffer.StartAddress() + sizeof(lowBits), highBitsAddr, sizeof(*highBitsAddr)); + memcpy((BYTE *)valueOutBuffer.StartAddress() + sizeof(lowBits), &highBits, sizeof(highBits)); } // RegMemValueHome::GetEnregisteredValue @@ -437,7 +310,7 @@ void MemRegValueHome::CopyToIPCEType(RemoteAddress * pRegAddr) // MemRegValueHome::SetEnregisteredValue // set a remote enregistered location to a new value (see EnregisteredValueHome::SetEnregisteredValue // for full header comment) -void MemRegValueHome::SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned) +void MemRegValueHome::SetEnregisteredValue(MemoryRange newValue, ContextBuffer contextBuffer, bool fIsSigned) { _ASSERTE(newValue.Size() == REG_SIZE << 1); // make sure we have bytes for two registers _ASSERTE(REG_SIZE == sizeof(void *)); @@ -450,7 +323,7 @@ void MemRegValueHome::SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pC memcpy(&highPart, (BYTE *)newValue.StartAddress() + REG_SIZE, REG_SIZE); // Update the proper registers. - SetContextRegister(pContext, m_reg1Info.m_kRegNumber, lowPart); // throws + SetContextRegister(contextBuffer, m_reg1Info.m_kRegNumber, lowPart); // throws _ASSERTE(REG_SIZE == sizeof(highPart)); HRESULT hr = m_pFrame->GetProcess()->SafeWriteStruct(m_memAddr, &highPart); @@ -467,15 +340,14 @@ void MemRegValueHome::GetEnregisteredValue(MemoryRange valueOutBuffer) HRESULT hr = m_pFrame->GetProcess()->SafeReadStruct(m_memAddr, &highBits); IfFailThrow(hr); - // and the low bits from a register - UINT_PTR* lowBitsAddr = m_pFrame->GetAddressOfRegister(m_reg1Info.m_kRegNumber); - _ASSERTE(lowBitsAddr != NULL); + TADDR lowBits = 0; + IfFailThrow(m_pFrame->ReadContextRegister(m_reg1Info.m_kRegNumber, &lowBits)); - _ASSERTE(sizeof(*lowBitsAddr)+sizeof(highBits) == valueOutBuffer.Size()); + _ASSERTE(sizeof(lowBits) + sizeof(highBits) == valueOutBuffer.Size()); - memcpy(valueOutBuffer.StartAddress(), lowBitsAddr, sizeof(*lowBitsAddr)); - memcpy((BYTE *)valueOutBuffer.StartAddress() + sizeof(*lowBitsAddr), &highBits, sizeof(highBits)); + memcpy(valueOutBuffer.StartAddress(), &lowBits, sizeof(lowBits)); + memcpy((BYTE *)valueOutBuffer.StartAddress() + sizeof(lowBits), &highBits, sizeof(highBits)); } // MemRegValueHome::GetEnregisteredValue @@ -495,130 +367,24 @@ void FloatRegValueHome::CopyToIPCEType(RemoteAddress * pRegAddr) } // FloatRegValueHome::CopyToIPCEType // FloatValueHome::SetEnregisteredValue -// set a remote enregistered location to a new value (see EnregisteredValueHome::SetEnregisteredValue -// for full header comment) +// set a remote enregistered location to a new value. void FloatRegValueHome::SetEnregisteredValue(MemoryRange newValue, - DT_CONTEXT * pContext, + ContextBuffer contextBuffer, bool fIsSigned) { _ASSERTE((newValue.Size() == 4) || (newValue.Size() == 8)); - // Convert the input to a double. - double newVal = 0.0; - - memcpy(&newVal, newValue.StartAddress(), newValue.Size()); - -#if defined(TARGET_X86) - - // What a pain, on X86 take the floating - // point state in the context and make it our current FP - // state, set the value into the current FP state, then - // save out the FP state into the context again and - // restore our original state. - DT_FLOATING_SAVE_AREA currentFPUState; - - #ifdef _MSC_VER - __asm fnsave currentFPUState // save the current FPU state. - #else - __asm__ __volatile__ - ( - " fnsave %0\n" \ - : "=m"(currentFPUState) - ); - #endif - - // Copy the state out of the context. - DT_FLOATING_SAVE_AREA floatarea = pContext->FloatSave; - floatarea.StatusWord &= 0xFF00; // remove any error codes. - floatarea.ControlWord |= 0x3F; // mask all exceptions. - - #ifdef _MSC_VER - __asm - { - fninit - frstor floatarea ;; reload the threads FPU state. - } - #else - __asm__ - ( - " fninit\n" \ - " frstor %0\n" \ - : /* no outputs */ - : "m"(floatarea) - ); - #endif - - double td; // temp double - double popArea[DebuggerIPCE_FloatCount]; - - // Pop off until we reach the value we want to change. - DWORD i = 0; - - while (i <= m_floatIndex) - { - #ifdef _MSC_VER - __asm fstp td - #else - __asm("fstpl %0" : "=m" (td)); - #endif - popArea[i++] = td; - } - - #ifdef _MSC_VER - __asm fld newVal; // push on the new value. - #else - __asm("fldl %0" : "=m" (newVal)); - #endif - - // Push any values that we popled off back onto the stack, - // _except_ the last one, which was the one we changed. - i--; - - while (i > 0) - { - td = popArea[--i]; - #ifdef _MSC_VER - __asm fld td - #else - __asm("fldl %0" : "=m" (td)); - #endif - } - - // Save out the modified float area. - #ifdef _MSC_VER - __asm fnsave floatarea - #else - __asm__ __volatile__ - ( - " fnsave %0\n" \ - : "=m"(floatarea) - ); - #endif - - // Put it into the context. - pContext->FloatSave= floatarea; - - // Restore our FPU state - #ifdef _MSC_VER - __asm + IDacDbiInterface * pDAC = m_pFrame->GetProcess()->GetDAC(); + HRESULT hr = pDAC->WriteFloatRegisterToContext( + contextBuffer, + m_regNum, + (const BYTE *)newValue.StartAddress(), + (ULONG32)newValue.Size()); + if (FAILED(hr)) { - fninit - frstor currentFPUState ;; restore our saved FPU state. + _ASSERTE(!"Failed to write floating point register to context"); + ThrowHR(E_FAIL); } - #else - __asm__ - ( - " fninit\n" \ - " frstor %0\n" \ - : /* no outputs */ - : "m"(currentFPUState) - ); - #endif -#endif // TARGET_X86 - - // update the thread's floating point stack - void * valueAddress = (void *) &(m_pFrame->m_pThread->m_floatValues[m_floatIndex]); - memcpy(valueAddress, newValue.StartAddress(), newValue.Size()); } // FloatValueHome::SetEnregisteredValue // FloatRegValueHome::GetEnregisteredValue @@ -852,8 +618,6 @@ void RegisterValueHome::CopyToIPCEType(RemoteAddress * pRegAddr) void RegisterValueHome::SetEnregisteredValue(MemoryRange src, bool fIsSigned) { _ASSERTE(m_pRemoteRegAddr != NULL); - // Get the thread's context so we can update it. - DT_CONTEXT * cTemp = NULL; const CordbNativeFrame * frame = m_pRemoteRegAddr->GetFrame(); // Can't set an enregistered value unless the frame the value was @@ -865,11 +629,15 @@ void RegisterValueHome::SetEnregisteredValue(MemoryRange src, bool fIsSigned) ThrowHR(CORDBG_E_SET_VALUE_NOT_ALLOWED_ON_NONLEAF_FRAME); } + ULONG32 cbCtx = frame->GetProcess()->GetTargetContextSize(); + + // Get the thread's context so we can update it. + ContextBuffer contextBuffer = {}; HRESULT hr = S_OK; EX_TRY { // This may throw, in which case we want to return our own HRESULT. - hr = frame->m_pThread->GetManagedContext(&cTemp); + hr = frame->m_pThread->GetManagedContext(&contextBuffer); } EX_CATCH_HRESULT(hr); if (FAILED(hr)) @@ -877,15 +645,23 @@ void RegisterValueHome::SetEnregisteredValue(MemoryRange src, bool fIsSigned) // If we failed to get the context, then we must not be in a leaf frame. ThrowHR(CORDBG_E_SET_VALUE_NOT_ALLOWED_ON_NONLEAF_FRAME); } + if (contextBuffer.contextSize < cbCtx) + { + ThrowHR(E_INVALIDARG); + } - // Its important to copy this context that we're given a ptr to. - DT_CONTEXT c; - c = *cTemp; + // Work on a local copy so failures in the chain below don't corrupt the + // thread's cached context. The subclass mutates this buffer to apply the + // new register value, and SetManagedContext then ships it to the LS and + // updates the cache. + NewArrayHolder ctxBuf(new BYTE[cbCtx]); + memcpy(ctxBuf, contextBuffer.pContextBytes, cbCtx); + ContextBuffer updatedContext = { ctxBuf, cbCtx }; - m_pRemoteRegAddr->SetEnregisteredValue(src, &c, fIsSigned); + m_pRemoteRegAddr->SetEnregisteredValue(src, updatedContext, fIsSigned); // Set the thread's modified context. - IfFailThrow(frame->m_pThread->SetManagedContext(&c)); + IfFailThrow(frame->m_pThread->SetManagedContext(updatedContext)); } // RegisterValueHome::SetEnregisteredValue @@ -1145,4 +921,3 @@ RefValueHome::RefValueHome(CordbProcess * pProcess, } // RefValueHome::RefValueHome - diff --git a/src/coreclr/debug/ee/arm/primitives.cpp b/src/coreclr/debug/ee/arm/primitives.cpp index 4c085699e06ba9..a8cd675c5884c7 100644 --- a/src/coreclr/debug/ee/arm/primitives.cpp +++ b/src/coreclr/debug/ee/arm/primitives.cpp @@ -13,14 +13,14 @@ void CopyREGDISPLAY(REGDISPLAY* pDst, REGDISPLAY* pSrc) CopyRegDisplay(pSrc, pDst, &tmp); } -void SetSSFlag(DT_CONTEXT *, Thread *pThread) +void SetSSFlag(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); pThread->EnableSingleStep(); } -void UnsetSSFlag(DT_CONTEXT *, Thread *pThread) +void UnsetSSFlag(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); @@ -28,7 +28,7 @@ void UnsetSSFlag(DT_CONTEXT *, Thread *pThread) } // Check if single stepping is enabled. -bool IsSSFlagEnabled(DT_CONTEXT *, Thread *pThread) +bool IsSSFlagEnabled(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); diff --git a/src/coreclr/debug/ee/arm64/primitives.cpp b/src/coreclr/debug/ee/arm64/primitives.cpp index 3cfee11c692bd4..d5dadbf5e665e3 100644 --- a/src/coreclr/debug/ee/arm64/primitives.cpp +++ b/src/coreclr/debug/ee/arm64/primitives.cpp @@ -14,14 +14,14 @@ void CopyREGDISPLAY(REGDISPLAY* pDst, REGDISPLAY* pSrc) } #ifdef FEATURE_EMULATE_SINGLESTEP -void SetSSFlag(DT_CONTEXT *, Thread *pThread) +void SetSSFlag(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); pThread->EnableSingleStep(); } -void UnsetSSFlag(DT_CONTEXT *, Thread *pThread) +void UnsetSSFlag(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); @@ -29,25 +29,25 @@ void UnsetSSFlag(DT_CONTEXT *, Thread *pThread) } // Check if single stepping is enabled. -bool IsSSFlagEnabled(DT_CONTEXT *, Thread *pThread) +bool IsSSFlagEnabled(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); return pThread->IsSingleStepEnabled(); } #else // FEATURE_EMULATE_SINGLESTEP -void SetSSFlag(DT_CONTEXT *pContext, Thread *) +void SetSSFlag(T_CONTEXT *pContext, Thread *) { SetSSFlag(pContext); } -void UnsetSSFlag(DT_CONTEXT *pContext, Thread *) +void UnsetSSFlag(T_CONTEXT *pContext, Thread *) { UnsetSSFlag(pContext); } // Check if single stepping is enabled. -bool IsSSFlagEnabled(DT_CONTEXT *pContext, Thread *) +bool IsSSFlagEnabled(T_CONTEXT *pContext, Thread *) { return IsSSFlagEnabled(pContext); } diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 9b5f3e15620108..1ac0825ff1eee3 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -3496,7 +3496,7 @@ void DebuggerController::ApplyTraceFlag(Thread *thread) g_pEEInterface->MarkThreadForDebugStepping(thread, true); LOG((LF_CORDB,LL_INFO1000, "DC::ApplyTraceFlag marked thread for debug stepping\n")); - SetSSFlag(reinterpret_cast(context) ARM_ARG(thread) ARM64_ARG(thread) RISCV64_ARG(thread) LOONGARCH64_ARG(thread)); + SetSSFlag(context ARM_ARG(thread) ARM64_ARG(thread) RISCV64_ARG(thread) LOONGARCH64_ARG(thread)); } // @@ -3546,7 +3546,7 @@ void DebuggerController::UnapplyTraceFlag(Thread *thread) // Always need to unmark for stepping g_pEEInterface->MarkThreadForDebugStepping(thread, false); - UnsetSSFlag(reinterpret_cast(context) ARM_ARG(thread) ARM64_ARG(thread) RISCV64_ARG(thread) LOONGARCH64_ARG(thread)); + UnsetSSFlag(context ARM_ARG(thread) ARM64_ARG(thread) RISCV64_ARG(thread) LOONGARCH64_ARG(thread)); } void DebuggerController::EnableExceptionHook() @@ -8322,9 +8322,9 @@ void DebuggerStepper::PrepareForSendEvent(StackTraceTicket ticket) { // If we're at either a patch or SS, we'll have a context. CONTEXT *context = g_pEEInterface->GetThreadFilterContext(GetThread()); - if (context == NULL) + if (context != NULL) { - void * pIP = CORDbgGetIP(reinterpret_cast(context)); + void * pIP = CORDbgGetIP(context); DebuggerJitInfo * dji = g_pDebugger->GetJitInfoFromAddr((TADDR) pIP); DebuggerMethodInfo * dmi = NULL; @@ -9250,8 +9250,8 @@ TP_RESULT DebuggerFuncEvalComplete::TriggerPatch(DebuggerControllerPatch *patch, #error Not supported #endif #endif - CORDbgCopyThreadContext(reinterpret_cast(pCtx), - reinterpret_cast(&(m_pDE->m_context))); + CORDbgCopyThreadContext(reinterpret_cast(pCtx), sizeof(T_CONTEXT), + reinterpret_cast(&(m_pDE->m_context)), sizeof(T_CONTEXT)); // We've hit our patch, so simply disable all (which removes the // patch) and trigger the event. diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 5ce9928ed2fec3..1480e27393f257 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -13315,7 +13315,7 @@ VOID Debugger::M2UHandoffHijackWorker(CONTEXT *pContext, //win32 has a weird property where EIP points after the BP in the debug event //so we are adjusting it to point at the BP - CORDbgAdjustPCForBreakInstruction((DT_CONTEXT*)pContext); + CORDbgAdjustPCForBreakInstruction(pContext); LOG((LF_CORDB, LL_INFO1000, "D::M2UHHW: Context ip set to 0x%p\n", GetIP(pContext))); _ASSERTE(!ISREDIRECTEDTHREAD(pEEThread)); @@ -13431,7 +13431,7 @@ LONG Debugger::FirstChanceSuspendHijackWorker(CONTEXT *pContext, Thread *pEEThread = debuggerBreakInThread ? NULL : g_pEEInterface->GetThread(); // Hook up the memory so RS can get to it - fcd.pLeftSideContext.Set((DT_CONTEXT*)pContext); + fcd.pLeftSideContext.Set((T_CONTEXT*)pContext); fcd.action = HIJACK_ACTION_EXIT_UNHANDLED; fcd.debugCounter = 0; @@ -14952,7 +14952,7 @@ BOOL Debugger::IsThreadContextInvalid(Thread *pThread, CONTEXT *pCtx) if (success) { // Check single-step flag - if (IsSSFlagEnabled(reinterpret_cast(pCtx) ARM_ARG(pThread) ARM64_ARG(pThread) RISCV64_ARG(pThread) LOONGARCH64_ARG(pThread))) + if (IsSSFlagEnabled(pCtx ARM_ARG(pThread) ARM64_ARG(pThread) RISCV64_ARG(pThread) LOONGARCH64_ARG(pThread))) { // Can't hijack a thread whose SS-flag is set. This could lead to races // with the thread taking the SS-exception. diff --git a/src/coreclr/debug/ee/loongarch64/primitives.cpp b/src/coreclr/debug/ee/loongarch64/primitives.cpp index 981809a4bac21a..206f2789cf014f 100644 --- a/src/coreclr/debug/ee/loongarch64/primitives.cpp +++ b/src/coreclr/debug/ee/loongarch64/primitives.cpp @@ -11,14 +11,14 @@ void CopyREGDISPLAY(REGDISPLAY* pDst, REGDISPLAY* pSrc) CopyRegDisplay(pSrc, pDst, &tmp); } -void SetSSFlag(DT_CONTEXT *, Thread *pThread) +void SetSSFlag(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); pThread->EnableSingleStep(); } -void UnsetSSFlag(DT_CONTEXT *, Thread *pThread) +void UnsetSSFlag(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); @@ -26,7 +26,7 @@ void UnsetSSFlag(DT_CONTEXT *, Thread *pThread) } // Check if single stepping is enabled. -bool IsSSFlagEnabled(DT_CONTEXT *, Thread *pThread) +bool IsSSFlagEnabled(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); diff --git a/src/coreclr/debug/ee/riscv64/primitives.cpp b/src/coreclr/debug/ee/riscv64/primitives.cpp index 65f4c586238f3f..0e9021a91ba3d2 100644 --- a/src/coreclr/debug/ee/riscv64/primitives.cpp +++ b/src/coreclr/debug/ee/riscv64/primitives.cpp @@ -13,14 +13,14 @@ void CopyREGDISPLAY(REGDISPLAY* pDst, REGDISPLAY* pSrc) CopyRegDisplay(pSrc, pDst, &tmp); } -void SetSSFlag(DT_CONTEXT *, Thread *pThread) +void SetSSFlag(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); pThread->EnableSingleStep(); } -void UnsetSSFlag(DT_CONTEXT *, Thread *pThread) +void UnsetSSFlag(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); @@ -28,7 +28,7 @@ void UnsetSSFlag(DT_CONTEXT *, Thread *pThread) } // Check if single stepping is enabled. -bool IsSSFlagEnabled(DT_CONTEXT *, Thread *pThread) +bool IsSSFlagEnabled(T_CONTEXT *, Thread *pThread) { _ASSERTE(pThread != NULL); diff --git a/src/coreclr/debug/inc/amd64/primitives.h b/src/coreclr/debug/inc/amd64/primitives.h index 41ce1bfe060f70..36068e62261461 100644 --- a/src/coreclr/debug/inc/amd64/primitives.h +++ b/src/coreclr/debug/inc/amd64/primitives.h @@ -21,8 +21,6 @@ typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE; #define PRD_TYPE DWORD_PTR #endif -typedef M128A FPRegister64; - // From section 1.1 of AMD64 Programmers Manual Vol 3. #define MAX_INSTRUCTION_LENGTH 15 @@ -104,7 +102,7 @@ inline CorDebugRegister ConvertRegNumToCorDebugRegister(ICorDebugInfo::RegNum re // // inline function to access/modify the CONTEXT // -inline LPVOID CORDbgGetIP(DT_CONTEXT* context) +inline LPVOID CORDbgGetIP(T_CONTEXT* context) { CONTRACTL { @@ -118,7 +116,7 @@ inline LPVOID CORDbgGetIP(DT_CONTEXT* context) return (LPVOID) context->Rip; } -inline void CORDbgSetIP(DT_CONTEXT* context, LPVOID rip) +inline void CORDbgSetIP(T_CONTEXT* context, LPVOID rip) { CONTRACTL { @@ -132,7 +130,7 @@ inline void CORDbgSetIP(DT_CONTEXT* context, LPVOID rip) context->Rip = (DWORD64) rip; } -inline CORDB_ADDRESS CORDbgGetSP(const DT_CONTEXT * context) +inline CORDB_ADDRESS CORDbgGetSP(const T_CONTEXT * context) { CONTRACTL { @@ -219,7 +217,7 @@ inline void CORDbgSetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address, } -inline void CORDbgAdjustPCForBreakInstruction(DT_CONTEXT* pContext) +inline void CORDbgAdjustPCForBreakInstruction(T_CONTEXT* pContext) { LIMITED_METHOD_CONTRACT; @@ -233,19 +231,19 @@ inline bool AddressIsBreakpoint(CORDB_ADDRESS_TYPE *address) return *address == CORDbg_BREAK_INSTRUCTION; } -inline void SetSSFlag(DT_CONTEXT *pContext) +inline void SetSSFlag(T_CONTEXT *pContext) { _ASSERTE(pContext != NULL); pContext->EFlags |= 0x100; } -inline void UnsetSSFlag(DT_CONTEXT *pContext) +inline void UnsetSSFlag(T_CONTEXT *pContext) { _ASSERTE(pContext != NULL); pContext->EFlags &= ~0x100; } -inline bool IsSSFlagEnabled(DT_CONTEXT * context) +inline bool IsSSFlagEnabled(T_CONTEXT * context) { _ASSERTE(context != NULL); return (context->EFlags & 0x100) != 0; diff --git a/src/coreclr/debug/inc/arm/primitives.h b/src/coreclr/debug/inc/arm/primitives.h index 431d223fc73b39..93e9fd115ef92d 100644 --- a/src/coreclr/debug/inc/arm/primitives.h +++ b/src/coreclr/debug/inc/arm/primitives.h @@ -16,7 +16,6 @@ #define THUMB_CODE 1 #endif -typedef ULONGLONG FPRegister64; typedef const BYTE CORDB_ADDRESS_TYPE; typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE; @@ -86,13 +85,13 @@ constexpr CorDebugRegister g_JITToCorDbgReg[] = // // inline function to access/modify the CONTEXT // -inline void CORDbgSetIP(DT_CONTEXT *context, LPVOID eip) { +inline void CORDbgSetIP(T_CONTEXT *context, LPVOID eip) { LIMITED_METHOD_CONTRACT; context->Pc = (UINT32)(size_t)eip; } -inline CORDB_ADDRESS CORDbgGetSP(const DT_CONTEXT * context) { +inline CORDB_ADDRESS CORDbgGetSP(const T_CONTEXT * context) { LIMITED_METHOD_CONTRACT; return (CORDB_ADDRESS)(context->Sp); @@ -168,13 +167,13 @@ inline void CORDbgSetInstruction(CORDB_ADDRESS_TYPE* address, class Thread; // Enable single stepping. -void SetSSFlag(DT_CONTEXT *pCtx, Thread *pThread); +void SetSSFlag(T_CONTEXT *pCtx, Thread *pThread); // Disable single stepping -void UnsetSSFlag(DT_CONTEXT *pCtx, Thread *pThread); +void UnsetSSFlag(T_CONTEXT *pCtx, Thread *pThread); // Check if single stepping is enabled. -bool IsSSFlagEnabled(DT_CONTEXT *pCtx, Thread *pThread); +bool IsSSFlagEnabled(T_CONTEXT *pCtx, Thread *pThread); #include "arm_primitives.h" #endif // PRIMITIVES_H_ diff --git a/src/coreclr/debug/inc/arm64/primitives.h b/src/coreclr/debug/inc/arm64/primitives.h index 4a7c89ae67920e..8c1fad92caa6e6 100644 --- a/src/coreclr/debug/inc/arm64/primitives.h +++ b/src/coreclr/debug/inc/arm64/primitives.h @@ -16,7 +16,6 @@ #include "executableallocator.h" #endif -typedef NEON128 FPRegister64; typedef const BYTE CORDB_ADDRESS_TYPE; typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE; @@ -132,13 +131,13 @@ constexpr CorDebugRegister g_JITToCorDbgReg[] = REGISTER_ARM64_V31, }; -inline void CORDbgSetIP(DT_CONTEXT *context, LPVOID eip) { +inline void CORDbgSetIP(T_CONTEXT *context, LPVOID eip) { LIMITED_METHOD_CONTRACT; context->Pc = (DWORD64)eip; } -inline CORDB_ADDRESS CORDbgGetSP(const DT_CONTEXT * context) { +inline CORDB_ADDRESS CORDbgGetSP(const T_CONTEXT * context) { LIMITED_METHOD_CONTRACT; return (CORDB_ADDRESS)(context->Sp); @@ -204,19 +203,19 @@ inline PRD_TYPE CORDbgGetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address) } -inline void SetSSFlag(DT_CONTEXT *pContext) +inline void SetSSFlag(T_CONTEXT *pContext) { _ASSERTE(pContext != NULL); pContext->Cpsr |= 0x00200000; } -inline void UnsetSSFlag(DT_CONTEXT *pContext) +inline void UnsetSSFlag(T_CONTEXT *pContext) { _ASSERTE(pContext != NULL); pContext->Cpsr &= ~0x00200000; } -inline bool IsSSFlagEnabled(DT_CONTEXT * pContext) +inline bool IsSSFlagEnabled(T_CONTEXT * pContext) { _ASSERTE(pContext != NULL); return (pContext->Cpsr & 0x00200000) != 0; diff --git a/src/coreclr/debug/inc/arm_primitives.h b/src/coreclr/debug/inc/arm_primitives.h index 8c21a44fb3e76e..0f322643df2587 100644 --- a/src/coreclr/debug/inc/arm_primitives.h +++ b/src/coreclr/debug/inc/arm_primitives.h @@ -24,7 +24,7 @@ inline CorDebugRegister ConvertRegNumToCorDebugRegister(ICorDebugInfo::RegNum re return g_JITToCorDbgReg[reg]; } -inline LPVOID CORDbgGetIP(DT_CONTEXT *context) +inline LPVOID CORDbgGetIP(T_CONTEXT *context) { LIMITED_METHOD_CONTRACT; @@ -66,11 +66,11 @@ inline void CORDbgInsertBreakpointExImpl(UNALIGNED CORDB_ADDRESS_TYPE *address) // After a breakpoint exception, the CPU points to _after_ the break instruction. // Adjust the IP so that it points at the break instruction. This lets us patch that // opcode and re-execute what was underneath the bp. -inline void CORDbgAdjustPCForBreakInstruction(DT_CONTEXT* pContext) +inline void CORDbgAdjustPCForBreakInstruction(T_CONTEXT* pContext) { LIMITED_METHOD_CONTRACT; -#if defined(TARGET_ARM64) +#if defined(HOST_ARM64) pContext->Pc -= CORDbg_BREAK_INSTRUCTION_SIZE; #else // @ARMTODO: ARM appears to leave the PC at the start of the breakpoint (at least according to Windbg, @@ -88,13 +88,13 @@ inline bool AddressIsBreakpoint(CORDB_ADDRESS_TYPE* address) class Thread; // Enable single stepping. -void SetSSFlag(DT_CONTEXT *pCtx, Thread *pThread); +void SetSSFlag(T_CONTEXT *pCtx, Thread *pThread); // Disable single stepping -void UnsetSSFlag(DT_CONTEXT *pCtx, Thread *pThread); +void UnsetSSFlag(T_CONTEXT *pCtx, Thread *pThread); // Check if single stepping is enabled. -bool IsSSFlagEnabled(DT_CONTEXT *pCtx, Thread *pThread); +bool IsSSFlagEnabled(T_CONTEXT *pCtx, Thread *pThread); inline bool PRDIsEqual(PRD_TYPE p1, PRD_TYPE p2) { diff --git a/src/coreclr/debug/inc/common.h b/src/coreclr/debug/inc/common.h index 5e4000839846f8..4a33f749502f3b 100644 --- a/src/coreclr/debug/inc/common.h +++ b/src/coreclr/debug/inc/common.h @@ -58,8 +58,13 @@ void InitEventForDebuggerNotification(DEBUG_EVENT * pDebugEvent, // CORDbgCopyThreadContext() does an intelligent copy from pSrc to pDst, // respecting the ContextFlags of both contexts. // +// The source and destination are opaque byte buffers with an associated size. +// They only need to be at least sizeof(DT_CONTEXT) bytes and may be larger +// (for example a full T_CONTEXT that carries trailing XSTATE/SVE state which is +// not part of DT_CONTEXT); only the DT_CONTEXT portion is ever read or written. +// -extern void CORDbgCopyThreadContext(DT_CONTEXT* pDst, const DT_CONTEXT* pSrc); +extern void CORDbgCopyThreadContext(BYTE* pDst, ULONG32 cbDst, const BYTE* pSrc, ULONG32 cbSrc); //--------------------------------------------------------------------------------------- // diff --git a/src/coreclr/debug/inc/dacdbiinterface.h b/src/coreclr/debug/inc/dacdbiinterface.h index e739a2e98395b7..397b99f592cbac 100644 --- a/src/coreclr/debug/inc/dacdbiinterface.h +++ b/src/coreclr/debug/inc/dacdbiinterface.h @@ -1051,7 +1051,7 @@ IDacDbiInterface : public IUnknown // we don't have a filter CONTEXT on the LS anymore. // - virtual HRESULT STDMETHODCALLTYPE GetManagedStoppedContext(VMPTR_Thread vmThread, OUT VMPTR_CONTEXT * pRetVal) = 0; + virtual HRESULT STDMETHODCALLTYPE GetManagedStoppedContext(VMPTR_Thread vmThread, OUT CORDB_ADDRESS * pRetVal) = 0; typedef enum { @@ -1076,7 +1076,7 @@ IDacDbiInterface : public IUnknown // // Arguments: // vmThread - the specified thread - // pInternalContextBuffer - a CONTEXT buffer for the stackwalker to work with + // contextBuffer - a CONTEXT buffer for the stackwalker to work with // ppSFIHandle - out parameter; return a handle to the stackwalker // // Notes: @@ -1084,7 +1084,7 @@ IDacDbiInterface : public IUnknown // This is a special case that violates the 'no state' tenant. // - virtual HRESULT STDMETHODCALLTYPE CreateStackWalk(VMPTR_Thread vmThread, DT_CONTEXT * pInternalContextBuffer, OUT StackWalkHandle * ppSFIHandle) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateStackWalk(VMPTR_Thread vmThread, ContextBuffer contextBuffer, OUT StackWalkHandle * ppSFIHandle) = 0; // Delete the stackwalk object created from CreateStackWalk. virtual HRESULT STDMETHODCALLTYPE DeleteStackWalk(StackWalkHandle ppSFIHandle) = 0; @@ -1094,10 +1094,10 @@ IDacDbiInterface : public IUnknown // // Arguments: // pSFIHandle - the handle to the stackwalker - // pContext - OUT: the CONTEXT to be filled out. The context control flags are ignored. + // contextBuffer - OUT: the CONTEXT to be filled out. The context control flags are ignored. // - virtual HRESULT STDMETHODCALLTYPE GetStackWalkCurrentContext(StackWalkHandle pSFIHandle, DT_CONTEXT * pContext) = 0; + virtual HRESULT STDMETHODCALLTYPE GetStackWalkCurrentContext(StackWalkHandle pSFIHandle, ContextBuffer contextBuffer) = 0; // // Set the stackwalker to the given CONTEXT. The CorDebugSetContextFlag indicates whether @@ -1108,10 +1108,10 @@ IDacDbiInterface : public IUnknown // vmThread - the current thread // pSFIHandle - the handle to the stackwalker // flag - flag to indicate whether the specified CONTEXT is "active" - // pContext - the specified CONTEXT. This may make correctional adjustments to the context's IP. + // contextBuffer - the specified CONTEXT. This may make correctional adjustments to the context's IP. // - virtual HRESULT STDMETHODCALLTYPE SetStackWalkCurrentContext(VMPTR_Thread vmThread, StackWalkHandle pSFIHandle, CorDebugSetContextFlag flag, DT_CONTEXT * pContext) = 0; + virtual HRESULT STDMETHODCALLTYPE SetStackWalkCurrentContext(VMPTR_Thread vmThread, StackWalkHandle pSFIHandle, CorDebugSetContextFlag flag, ContextBuffer contextBuffer) = 0; // // Unwind the stackwalker to the next frame. The next frame could be any actual stack frame, @@ -1134,14 +1134,13 @@ IDacDbiInterface : public IUnknown // // Arguments: // vmThread - the specified thread - // pContext - the CONTEXT to be checked + // contextBuffer - the CONTEXT to be checked // // Return Value: // S_OK on success; otherwise, an appropriate failure HRESULT. // - virtual HRESULT STDMETHODCALLTYPE CheckContext(VMPTR_Thread vmThread, - const DT_CONTEXT * pContext) = 0; + virtual HRESULT STDMETHODCALLTYPE CheckContext(VMPTR_Thread vmThread, ContextBuffer contextBuffer) = 0; // // Fill in the Debugger_STRData structure with information about the current frame @@ -1222,7 +1221,7 @@ IDacDbiInterface : public IUnknown // // Arguments: // vmThread - the specified thread - // pContext - the CONTEXT to check + // contextBuffer - the CONTEXT to check // pResult - [out] TRUE if the specified CONTEXT is the leaf CONTEXT. // // Return Value: @@ -1233,15 +1232,15 @@ IDacDbiInterface : public IUnknown // This will be deprecated in V3. // - virtual HRESULT STDMETHODCALLTYPE IsLeafFrame(VMPTR_Thread vmThread, const DT_CONTEXT * pContext, OUT BOOL * pResult) = 0; + virtual HRESULT STDMETHODCALLTYPE IsLeafFrame(VMPTR_Thread vmThread, ContextBuffer contextBuffer, OUT BOOL * pResult) = 0; // Get the context for a particular thread of the target process. // Arguments: // input: vmThread - the thread for which the context is required - // output: pContextBuffer - the address of the CONTEXT to be initialized. - // The memory for this belongs to the caller. It must not be NULL. + // output: contextBuffer - the CONTEXT buffer to be initialized. + // The memory for this belongs to the caller. // Note: returns an appropriate failure HRESULT on error - virtual HRESULT STDMETHODCALLTYPE GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pContextBuffer) = 0; + virtual HRESULT STDMETHODCALLTYPE GetContext(VMPTR_Thread vmThread, ContextBuffer contextBuffer) = 0; typedef enum { @@ -2208,6 +2207,61 @@ IDacDbiInterface : public IUnknown VMPTR_MethodDesc vmMethod, OUT UINT32* pTokenIndex) = 0; + virtual HRESULT STDMETHODCALLTYPE GetTargetContextSize(ULONG32 contextFlags, OUT ULONG32 * pSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE WriteRegistersToContext( + IN ContextBuffer contextBuffer, + IN const CorDebugRegister * regs, + IN ULONG32 nRegs, + IN const TADDR * values) = 0; + + virtual HRESULT STDMETHODCALLTYPE ReadRegistersFromContext( + IN ContextBuffer contextBuffer, + IN const CorDebugRegister * regs, + IN ULONG32 nRegs, + OUT CORDB_REGISTER * pValues) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAvailableRegistersMask( + IN BOOL fActive, + IN BOOL fQuickUnwind, + IN ULONG32 regCount, + OUT BYTE pAvailable[]) = 0; + + virtual HRESULT STDMETHODCALLTYPE ConvertJitRegNumToCorDebugRegister( + IN ULONG32 jitRegNum, + OUT CorDebugRegister * pReg) = 0; + + virtual HRESULT STDMETHODCALLTYPE WriteFloatRegisterToContext( + IN ContextBuffer contextBuffer, + IN CorDebugRegister reg, + IN const BYTE * pValue, + IN ULONG32 valueSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE ContextHasExtendedRegisters( + IN ContextBuffer contextBuffer, + OUT BOOL * pResult) = 0; + + virtual HRESULT STDMETHODCALLTYPE CompareControlRegisters( + IN ContextBuffer contextBuffer1, + IN ContextBuffer contextBuffer2, + OUT BOOL * pResult) = 0; + + typedef enum + { + kCopyContextPreserveDestinationFlags = 0, + kCopyContextMergeSourceFlags, + kCopyContextUseExplicitFlags, + } ContextCopyMode; + + // Copies sourceContext into destinationContext, respecting the ContextFlags + // selected by copyMode. flags supplies the destination ContextFlags when + // copyMode is kCopyContextUseExplicitFlags. + virtual HRESULT STDMETHODCALLTYPE CopyContext( + IN ContextBuffer destinationContext, + IN ContextBuffer sourceContext, + IN ContextCopyMode copyMode, + IN ULONG32 flags) = 0; + // The following tag tells the DD-marshalling tool to stop scanning. // END_MARSHAL diff --git a/src/coreclr/debug/inc/dacdbistructures.h b/src/coreclr/debug/inc/dacdbistructures.h index 16ceee4d503774..a2095abc5267c1 100644 --- a/src/coreclr/debug/inc/dacdbistructures.h +++ b/src/coreclr/debug/inc/dacdbistructures.h @@ -160,6 +160,18 @@ struct MSLAYOUT TargetBuffer ULONG cbSize; }; +// A variable sized host memory context buffer in target architecture specific layout. +// The currently supported layout matches the CONTEXT structure for the +// target architecture and OS. +// Not (yet) supported: +// - Optional trailing XState feature data in the buffer +// - Add an architecture enum field to make the struct self-describing +struct MSLAYOUT ContextBuffer +{ + BYTE * pContextBytes; + ULONG32 contextSize; +}; + // Module properties, retrieved by DAC. struct MSLAYOUT ModuleInfo { @@ -539,11 +551,10 @@ struct MSLAYOUT Debugger_JITFuncData #endif // ARM context structures have a 16-byte alignment requirement struct MSLAYOUT Debugger_STRData { - // fp is a CORDB_ADDRESS (fixed 64-bit) rather than a host-sized FramePointer; the RS - // converts to/from FramePointer at the boundary. ctx is deliberately a host-sized - // pointer to a dbi-allocated DT_CONTEXT buffer that the DAC writes through. + // fp is fixed-width across target architectures. ctx wraps a DBI-owned buffer + // (pointer plus size) holding the target's opaque CONTEXT byte image. CORDB_ADDRESS fp; - DT_CONTEXT * ctx; + ContextBuffer ctx; VMPTR_AppDomain vmCurrentAppDomainToken; diff --git a/src/coreclr/debug/inc/dbgipcevents.h b/src/coreclr/debug/inc/dbgipcevents.h index 8a087492ca3d17..00e5505f880db1 100644 --- a/src/coreclr/debug/inc/dbgipcevents.h +++ b/src/coreclr/debug/inc/dbgipcevents.h @@ -424,6 +424,11 @@ class MSLAYOUT LsPointer : public GeneralLsPointer return CORDB_ADDRESS_TO_PTR((CORDB_ADDRESS)m_ptr); } + CORDB_ADDRESS UnsafeGetAddr() + { + return (CORDB_ADDRESS)m_ptr; + } + static LsPointer NullPtr() { return MakePtr(NULL); @@ -592,7 +597,7 @@ DEFINE_LSPTR_TYPE(class DebuggerEval, LSPTR_DEBUGGEREVAL); DEFINE_LSPTR_TYPE(class DebuggerStepper, LSPTR_STEPPER); // Need to be careful not to annoy the compiler here since DT_CONTEXT is a typedef, not a struct. -typedef LsPointer LSPTR_CONTEXT; +typedef LsPointer LSPTR_CONTEXT; DEFINE_LSPTR_TYPE(struct OBJECTHANDLE__, LSPTR_OBJECTHANDLE); DEFINE_LSPTR_TYPE(class TypeHandleDummyPtr, LSPTR_TYPEHANDLE); // TypeHandle in the LS is not a direct pointer. @@ -762,11 +767,6 @@ DEFINE_VMPTR(class AppDomain, PTR_AppDomain, VMPTR_AppDomain); // Need to be careful not to annoy the compiler here since DT_CONTEXT is a typedef, not a struct. // DEFINE_VMPTR(struct _CONTEXT, PTR_CONTEXT, VMPTR_CONTEXT); -#if defined(ALLOW_VMPTR_ACCESS) -typedef VMPTR_Base VMPTR_CONTEXT; -#else -typedef VMPTR_Base VMPTR_CONTEXT; -#endif DEFINE_VMPTR(class Module, PTR_Module, VMPTR_Module); @@ -958,51 +958,6 @@ struct MSLAYOUT IPCENames // We use a class/struct so that the function can rema #endif // !DACCESS_COMPILE -// -// NOTE: CPU-specific values below! -// DebuggerIPCE_FloatCount is the number of doubles in the processor's -// floating point stack. - -#if defined(TARGET_X86) -#define DebuggerIPCE_FloatCount 8 -#elif defined(TARGET_AMD64) -#define DebuggerIPCE_FloatCount 16 -#elif defined(TARGET_ARM) -#define DebuggerIPCE_FloatCount 32 -#elif defined(TARGET_ARM64) -#define DebuggerIPCE_FloatCount 32 -#elif defined(TARGET_LOONGARCH64) -#define DebuggerIPCE_FloatCount 32 -#elif defined(TARGET_RISCV64) -#define DebuggerIPCE_FloatCount 32 -#else -#define DebuggerIPCE_FloatCount 1 -#endif - -#if !defined(TARGET_WASM) -inline LPVOID GetSPAddress(const DT_CONTEXT * context) -{ -#if defined(TARGET_X86) - return (LPVOID)&context->Esp; -#elif defined(TARGET_AMD64) - return (LPVOID)&context->Rsp; -#else - return (LPVOID)&context->Sp; -#endif -} -#endif // !TARGET_WASM - -#if !defined(TARGET_AMD64) && !defined(TARGET_ARM) && !defined(TARGET_WASM) -inline LPVOID GetFPAddress(const DT_CONTEXT * context) -{ -#if defined(TARGET_X86) - return (LPVOID)&context->Ebp; -#else - return (LPVOID)&context->Fp; -#endif -} -#endif // !TARGET_AMD64 && !TARGET_ARM && !TARGET_WASM - class MSLAYOUT FramePointer { diff --git a/src/coreclr/debug/inc/i386/primitives.h b/src/coreclr/debug/inc/i386/primitives.h index aa7d35a73a9bfa..d5ef47b4e2451a 100644 --- a/src/coreclr/debug/inc/i386/primitives.h +++ b/src/coreclr/debug/inc/i386/primitives.h @@ -72,19 +72,19 @@ inline CorDebugRegister ConvertRegNumToCorDebugRegister(ICorDebugInfo::RegNum re // // inline function to access/modify the CONTEXT // -inline LPVOID CORDbgGetIP(DT_CONTEXT *context) { +inline LPVOID CORDbgGetIP(T_CONTEXT *context) { LIMITED_METHOD_CONTRACT; return (LPVOID)(size_t)(context->Eip); } -inline void CORDbgSetIP(DT_CONTEXT *context, LPVOID eip) { +inline void CORDbgSetIP(T_CONTEXT *context, LPVOID eip) { LIMITED_METHOD_CONTRACT; context->Eip = (UINT32)(size_t)eip; } -inline CORDB_ADDRESS CORDbgGetSP(const DT_CONTEXT * context) { +inline CORDB_ADDRESS CORDbgGetSP(const T_CONTEXT * context) { LIMITED_METHOD_CONTRACT; return (CORDB_ADDRESS)(context->Esp); @@ -163,7 +163,7 @@ inline void CORDbgSetInstruction(CORDB_ADDRESS_TYPE* address, // After a breakpoint exception, the CPU points to _after_ the break instruction. // Adjust the IP so that it points at the break instruction. This lets us patch that // opcode and re-execute what was underneath the bp. -inline void CORDbgAdjustPCForBreakInstruction(DT_CONTEXT* pContext) +inline void CORDbgAdjustPCForBreakInstruction(T_CONTEXT* pContext) { LIMITED_METHOD_CONTRACT; @@ -178,14 +178,14 @@ inline bool AddressIsBreakpoint(CORDB_ADDRESS_TYPE* address) } // Set the hardware trace flag. -inline void SetSSFlag(DT_CONTEXT *context) +inline void SetSSFlag(T_CONTEXT *context) { _ASSERTE(context != NULL); context->EFlags |= 0x100; } // Unset the hardware trace flag. -inline void UnsetSSFlag(DT_CONTEXT *context) +inline void UnsetSSFlag(T_CONTEXT *context) { SUPPORTS_DAC; _ASSERTE(context != NULL); @@ -193,7 +193,7 @@ inline void UnsetSSFlag(DT_CONTEXT *context) } // return true if the hardware trace flag applied. -inline bool IsSSFlagEnabled(DT_CONTEXT * context) +inline bool IsSSFlagEnabled(T_CONTEXT * context) { _ASSERTE(context != NULL); return (context->EFlags & 0x100) != 0; diff --git a/src/coreclr/debug/inc/loongarch64/primitives.h b/src/coreclr/debug/inc/loongarch64/primitives.h index 230056ddc25073..90d1714052342d 100644 --- a/src/coreclr/debug/inc/loongarch64/primitives.h +++ b/src/coreclr/debug/inc/loongarch64/primitives.h @@ -16,11 +16,6 @@ typedef const BYTE CORDB_ADDRESS_TYPE; typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE; -// Floating point registers are stored in a SIMD-capable layout (FPR64/LSX/LASX) where each register -// occupies four 64-bit slots. FPRegister64 spans that slot so Get64bitFPRegisters strides correctly; -// FPFillR8 reads the scalar value from the first 64 bits. -typedef struct { ULONGLONG slots[4]; } FPRegister64; - #define MAX_INSTRUCTION_LENGTH 4 // Given a return address retrieved during stackwalk, @@ -88,13 +83,13 @@ constexpr CorDebugRegister g_JITToCorDbgReg[] = REGISTER_LOONGARCH64_PC }; -inline void CORDbgSetIP(DT_CONTEXT *context, LPVOID ip) { +inline void CORDbgSetIP(T_CONTEXT *context, LPVOID ip) { LIMITED_METHOD_CONTRACT; context->Pc = (DWORD64)ip; } -inline CORDB_ADDRESS CORDbgGetSP(const DT_CONTEXT * context) { +inline CORDB_ADDRESS CORDbgGetSP(const T_CONTEXT * context) { LIMITED_METHOD_CONTRACT; return (CORDB_ADDRESS)(context->Sp); @@ -165,7 +160,7 @@ inline CorDebugRegister ConvertRegNumToCorDebugRegister(ICorDebugInfo::RegNum re return g_JITToCorDbgReg[reg]; } -inline LPVOID CORDbgGetIP(DT_CONTEXT *context) +inline LPVOID CORDbgGetIP(T_CONTEXT *context) { LIMITED_METHOD_CONTRACT; @@ -207,7 +202,7 @@ inline void CORDbgInsertBreakpointExImpl(UNALIGNED CORDB_ADDRESS_TYPE *address) // After a breakpoint exception, the CPU points to _after_ the break instruction. // Adjust the IP so that it points at the break instruction. This lets us patch that // opcode and re-execute what was underneath the bp. -inline void CORDbgAdjustPCForBreakInstruction(DT_CONTEXT* pContext) +inline void CORDbgAdjustPCForBreakInstruction(T_CONTEXT* pContext) { LIMITED_METHOD_CONTRACT; @@ -224,13 +219,13 @@ inline bool AddressIsBreakpoint(CORDB_ADDRESS_TYPE* address) class Thread; // Enable single stepping. -void SetSSFlag(DT_CONTEXT *pCtx, Thread *pThread); +void SetSSFlag(T_CONTEXT *pCtx, Thread *pThread); // Disable single stepping -void UnsetSSFlag(DT_CONTEXT *pCtx, Thread *pThread); +void UnsetSSFlag(T_CONTEXT *pCtx, Thread *pThread); // Check if single stepping is enabled. -bool IsSSFlagEnabled(DT_CONTEXT *pCtx, Thread *pThread); +bool IsSSFlagEnabled(T_CONTEXT *pCtx, Thread *pThread); inline bool PRDIsEqual(PRD_TYPE p1, PRD_TYPE p2) diff --git a/src/coreclr/debug/inc/riscv64/primitives.h b/src/coreclr/debug/inc/riscv64/primitives.h index 3fb15769b9cb3e..f30477a4821e70 100644 --- a/src/coreclr/debug/inc/riscv64/primitives.h +++ b/src/coreclr/debug/inc/riscv64/primitives.h @@ -16,10 +16,6 @@ typedef const BYTE CORDB_ADDRESS_TYPE; typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE; -// Each floating point register occupies a single 64-bit slot in the context, so FPRegister64 is one -// ULONGLONG and Get64bitFPRegisters strides one slot per register. -typedef ULONGLONG FPRegister64; - // TODO-RISCV64-CQ: Update when it supports c and other extensions #define MAX_INSTRUCTION_LENGTH 4 @@ -88,13 +84,13 @@ constexpr CorDebugRegister g_JITToCorDbgReg[] = REGISTER_RISCV64_PC }; -inline void CORDbgSetIP(DT_CONTEXT *context, LPVOID ip) { +inline void CORDbgSetIP(T_CONTEXT *context, LPVOID ip) { LIMITED_METHOD_CONTRACT; context->Pc = (DWORD64)ip; } -inline CORDB_ADDRESS CORDbgGetSP(const DT_CONTEXT * context) { +inline CORDB_ADDRESS CORDbgGetSP(const T_CONTEXT * context) { LIMITED_METHOD_CONTRACT; return (CORDB_ADDRESS)(context->Sp); @@ -165,7 +161,7 @@ inline CorDebugRegister ConvertRegNumToCorDebugRegister(ICorDebugInfo::RegNum re return g_JITToCorDbgReg[reg]; } -inline LPVOID CORDbgGetIP(DT_CONTEXT *context) +inline LPVOID CORDbgGetIP(T_CONTEXT *context) { LIMITED_METHOD_CONTRACT; @@ -207,7 +203,7 @@ inline void CORDbgInsertBreakpointExImpl(UNALIGNED CORDB_ADDRESS_TYPE *address) // After a breakpoint exception, the CPU points to _after_ the break instruction. // Adjust the IP so that it points at the break instruction. This lets us patch that // opcode and re-execute what was underneath the bp. -inline void CORDbgAdjustPCForBreakInstruction(DT_CONTEXT* pContext) +inline void CORDbgAdjustPCForBreakInstruction(T_CONTEXT* pContext) { LIMITED_METHOD_CONTRACT; @@ -224,13 +220,13 @@ inline bool AddressIsBreakpoint(CORDB_ADDRESS_TYPE* address) class Thread; // Enable single stepping. -void SetSSFlag(DT_CONTEXT *pCtx, Thread *pThread); +void SetSSFlag(T_CONTEXT *pCtx, Thread *pThread); // Disable single stepping -void UnsetSSFlag(DT_CONTEXT *pCtx, Thread *pThread); +void UnsetSSFlag(T_CONTEXT *pCtx, Thread *pThread); // Check if single stepping is enabled. -bool IsSSFlagEnabled(DT_CONTEXT *pCtx, Thread *pThread); +bool IsSSFlagEnabled(T_CONTEXT *pCtx, Thread *pThread); inline bool PRDIsEqual(PRD_TYPE p1, PRD_TYPE p2) diff --git a/src/coreclr/debug/shared/amd64/primitives.cpp b/src/coreclr/debug/shared/amd64/primitives.cpp index ff23ab99b1003d..4069f9a38ee911 100644 --- a/src/coreclr/debug/shared/amd64/primitives.cpp +++ b/src/coreclr/debug/shared/amd64/primitives.cpp @@ -16,8 +16,14 @@ // CopyThreadContext() does an intelligent copy from pSrc to pDst, // respecting the ContextFlags of both contexts. // -void CORDbgCopyThreadContext(DT_CONTEXT* pDst, const DT_CONTEXT* pSrc) +void CORDbgCopyThreadContext(BYTE* pDstBuffer, ULONG32 cbDst, const BYTE* pSrcBuffer, ULONG32 cbSrc) { + _ASSERTE(cbDst >= sizeof(DT_CONTEXT)); + _ASSERTE(cbSrc >= sizeof(DT_CONTEXT)); + + DT_CONTEXT* pDst = reinterpret_cast(pDstBuffer); + const DT_CONTEXT* pSrc = reinterpret_cast(pSrcBuffer); + ULONG dstFlags = pDst->ContextFlags; ULONG srcFlags = pSrc->ContextFlags; LOG((LF_CORDB, LL_INFO1000000, diff --git a/src/coreclr/debug/shared/arm/primitives.cpp b/src/coreclr/debug/shared/arm/primitives.cpp index 4cdf1e82af643d..bae8f734d46098 100644 --- a/src/coreclr/debug/shared/arm/primitives.cpp +++ b/src/coreclr/debug/shared/arm/primitives.cpp @@ -16,28 +16,34 @@ // CopyThreadContext() does an intelligent copy from pSrc to pDst, // respecting the ContextFlags of both contexts. // -void CORDbgCopyThreadContext(DT_CONTEXT* pDst, const DT_CONTEXT* pSrc) +void CORDbgCopyThreadContext(BYTE* pDstBuffer, ULONG32 cbDst, const BYTE* pSrcBuffer, ULONG32 cbSrc) { + _ASSERTE(cbDst >= sizeof(DT_CONTEXT)); + _ASSERTE(cbSrc >= sizeof(DT_CONTEXT)); + + DT_CONTEXT* pDst = reinterpret_cast(pDstBuffer); + const DT_CONTEXT* pSrc = reinterpret_cast(pSrcBuffer); + DWORD dstFlags = pDst->ContextFlags; DWORD srcFlags = pSrc->ContextFlags; LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: pDst=0x%08x dstFlags=0x%x, pSrc=0x%08x srcFlags=0x%x\n", pDst, dstFlags, pSrc, srcFlags)); - if ((dstFlags & srcFlags & DT_CONTEXT_CONTROL) == DT_CONTEXT_CONTROL) + if ((dstFlags & srcFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) CopyContextChunk(&(pDst->Sp), &(pSrc->Sp), &(pDst->Fpscr), DT_CONTEXT_CONTROL); - if ((dstFlags & srcFlags & DT_CONTEXT_INTEGER) == DT_CONTEXT_INTEGER) + if ((dstFlags & srcFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) CopyContextChunk(&(pDst->R0), &(pSrc->R0), &(pDst->Sp), - DT_CONTEXT_INTEGER); + CONTEXT_INTEGER); - if ((dstFlags & srcFlags & DT_CONTEXT_FLOATING_POINT) == DT_CONTEXT_FLOATING_POINT) + if ((dstFlags & srcFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) CopyContextChunk(&(pDst->Fpscr), &(pSrc->Fpscr), &(pDst->Bvr[0]), - DT_CONTEXT_FLOATING_POINT); + CONTEXT_FLOATING_POINT); - if ((dstFlags & srcFlags & DT_CONTEXT_DEBUG_REGISTERS) == - DT_CONTEXT_DEBUG_REGISTERS) - CopyContextChunk(&(pDst->Bvr[0]), &(pSrc->Bvr[0]), &(pDst->Wcr[DT_ARM_MAX_WATCHPOINTS]), - DT_CONTEXT_DEBUG_REGISTERS); + if ((dstFlags & srcFlags & CONTEXT_DEBUG_REGISTERS) == + CONTEXT_DEBUG_REGISTERS) + CopyContextChunk(&(pDst->Bvr[0]), &(pSrc->Bvr[0]), &(pDst->Wcr[ARM_MAX_WATCHPOINTS]), + CONTEXT_DEBUG_REGISTERS); } diff --git a/src/coreclr/debug/shared/arm64/primitives.cpp b/src/coreclr/debug/shared/arm64/primitives.cpp index a381891b02d83d..b3441fa912db05 100644 --- a/src/coreclr/debug/shared/arm64/primitives.cpp +++ b/src/coreclr/debug/shared/arm64/primitives.cpp @@ -16,32 +16,38 @@ // CopyThreadContext() does an intelligent copy from pSrc to pDst, // respecting the ContextFlags of both contexts. // -void CORDbgCopyThreadContext(DT_CONTEXT* pDst, const DT_CONTEXT* pSrc) +void CORDbgCopyThreadContext(BYTE* pDstBuffer, ULONG32 cbDst, const BYTE* pSrcBuffer, ULONG32 cbSrc) { + _ASSERTE(cbDst >= sizeof(DT_CONTEXT)); + _ASSERTE(cbSrc >= sizeof(DT_CONTEXT)); + + DT_CONTEXT* pDst = reinterpret_cast(pDstBuffer); + const DT_CONTEXT* pSrc = reinterpret_cast(pSrcBuffer); + DWORD dstFlags = pDst->ContextFlags; DWORD srcFlags = pSrc->ContextFlags; LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: pDst=0x%08x dstFlags=0x%x, pSrc=0x%08x srcFlags=0x%x\n", pDst, dstFlags, pSrc, srcFlags)); - if ((dstFlags & srcFlags & DT_CONTEXT_CONTROL) == DT_CONTEXT_CONTROL) + if ((dstFlags & srcFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) { CopyContextChunk(&(pDst->Fp), &(pSrc->Fp), &(pDst->V), - DT_CONTEXT_CONTROL); + CONTEXT_CONTROL); CopyContextChunk(&(pDst->Cpsr), &(pSrc->Cpsr), &(pDst->X), - DT_CONTEXT_CONTROL); + CONTEXT_CONTROL); } - if ((dstFlags & srcFlags & DT_CONTEXT_INTEGER) == DT_CONTEXT_INTEGER) + if ((dstFlags & srcFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) CopyContextChunk(&(pDst->X[0]), &(pSrc->X[0]), &(pDst->Fp), - DT_CONTEXT_INTEGER); + CONTEXT_INTEGER); - if ((dstFlags & srcFlags & DT_CONTEXT_FLOATING_POINT) == DT_CONTEXT_FLOATING_POINT) + if ((dstFlags & srcFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) CopyContextChunk(&(pDst->V[0]), &(pSrc->V[0]), &(pDst->Bcr[0]), - DT_CONTEXT_FLOATING_POINT); + CONTEXT_FLOATING_POINT); - if ((dstFlags & srcFlags & DT_CONTEXT_DEBUG_REGISTERS) == - DT_CONTEXT_DEBUG_REGISTERS) + if ((dstFlags & srcFlags & CONTEXT_DEBUG_REGISTERS) == + CONTEXT_DEBUG_REGISTERS) CopyContextChunk(&(pDst->Bcr[0]), &(pSrc->Bcr[0]), &(pDst->Wvr[ARM64_MAX_WATCHPOINTS]), - DT_CONTEXT_DEBUG_REGISTERS); + CONTEXT_DEBUG_REGISTERS); } diff --git a/src/coreclr/debug/shared/i386/primitives.cpp b/src/coreclr/debug/shared/i386/primitives.cpp index 96a5a8f9aea90f..a91503b13b8673 100644 --- a/src/coreclr/debug/shared/i386/primitives.cpp +++ b/src/coreclr/debug/shared/i386/primitives.cpp @@ -16,40 +16,57 @@ // CopyThreadContext() does an intelligent copy from pSrc to pDst, // respecting the ContextFlags of both contexts. // -void CORDbgCopyThreadContext(DT_CONTEXT* pDst, const DT_CONTEXT* pSrc) +void CORDbgCopyThreadContext(BYTE* pDstBuffer, ULONG32 cbDst, const BYTE* pSrcBuffer, ULONG32 cbSrc) { + // The buffers must at least be large enough to hold the ContextFlags field, which + // selects how much of the rest of the CONTEXT the copy below reads/writes. + _ASSERTE(cbDst >= offsetof(DT_CONTEXT, ContextFlags) + sizeof(DWORD)); + _ASSERTE(cbSrc >= offsetof(DT_CONTEXT, ContextFlags) + sizeof(DWORD)); + + DT_CONTEXT* pDst = reinterpret_cast(pDstBuffer); + const DT_CONTEXT* pSrc = reinterpret_cast(pSrcBuffer); + DWORD dstFlags = pDst->ContextFlags; DWORD srcFlags = pSrc->ContextFlags; + + // The copy only touches the trailing ExtendedRegisters region when + // CONTEXT_EXTENDED_REGISTERS is set, so a smaller CONTEXT buffer that omits that + // array (e.g. CONTEXT_FULL without extended registers) is valid. + _ASSERTE(cbDst >= (((dstFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS) + ? sizeof(DT_CONTEXT) : offsetof(DT_CONTEXT, ExtendedRegisters))); + _ASSERTE(cbSrc >= (((srcFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS) + ? sizeof(DT_CONTEXT) : offsetof(DT_CONTEXT, ExtendedRegisters))); + LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: pDst=0x%08x dstFlags=0x%x, pSrc=0x%08x srcFlags=0x%x\n", pDst, dstFlags, pSrc, srcFlags)); - if ((dstFlags & srcFlags & DT_CONTEXT_CONTROL) == DT_CONTEXT_CONTROL) + if ((dstFlags & srcFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) CopyContextChunk(&(pDst->Ebp), &(pSrc->Ebp), pDst->ExtendedRegisters, - DT_CONTEXT_CONTROL); + CONTEXT_CONTROL); - if ((dstFlags & srcFlags & DT_CONTEXT_INTEGER) == DT_CONTEXT_INTEGER) + if ((dstFlags & srcFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) CopyContextChunk(&(pDst->Edi), &(pSrc->Edi), &(pDst->Ebp), - DT_CONTEXT_INTEGER); + CONTEXT_INTEGER); - if ((dstFlags & srcFlags & DT_CONTEXT_SEGMENTS) == DT_CONTEXT_SEGMENTS) + if ((dstFlags & srcFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) CopyContextChunk(&(pDst->SegGs), &(pSrc->SegGs), &(pDst->Edi), - DT_CONTEXT_SEGMENTS); + CONTEXT_SEGMENTS); - if ((dstFlags & srcFlags & DT_CONTEXT_FLOATING_POINT) == DT_CONTEXT_FLOATING_POINT) + if ((dstFlags & srcFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) CopyContextChunk(&(pDst->FloatSave), &(pSrc->FloatSave), (&pDst->FloatSave)+1, - DT_CONTEXT_FLOATING_POINT); + CONTEXT_FLOATING_POINT); - if ((dstFlags & srcFlags & DT_CONTEXT_DEBUG_REGISTERS) == - DT_CONTEXT_DEBUG_REGISTERS) + if ((dstFlags & srcFlags & CONTEXT_DEBUG_REGISTERS) == + CONTEXT_DEBUG_REGISTERS) CopyContextChunk(&(pDst->Dr0), &(pSrc->Dr0), &(pDst->FloatSave), - DT_CONTEXT_DEBUG_REGISTERS); + CONTEXT_DEBUG_REGISTERS); - if ((dstFlags & srcFlags & DT_CONTEXT_EXTENDED_REGISTERS) == - DT_CONTEXT_EXTENDED_REGISTERS) + if ((dstFlags & srcFlags & CONTEXT_EXTENDED_REGISTERS) == + CONTEXT_EXTENDED_REGISTERS) CopyContextChunk(pDst->ExtendedRegisters, pSrc->ExtendedRegisters, &(pDst->ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]), - DT_CONTEXT_EXTENDED_REGISTERS); + CONTEXT_EXTENDED_REGISTERS); } diff --git a/src/coreclr/debug/shared/loongarch64/primitives.cpp b/src/coreclr/debug/shared/loongarch64/primitives.cpp index 11801fea05c106..df79d064249775 100644 --- a/src/coreclr/debug/shared/loongarch64/primitives.cpp +++ b/src/coreclr/debug/shared/loongarch64/primitives.cpp @@ -17,49 +17,55 @@ // CopyThreadContext() does an intelligent copy from pSrc to pDst, // respecting the ContextFlags of both contexts. // -void CORDbgCopyThreadContext(DT_CONTEXT* pDst, const DT_CONTEXT* pSrc) +void CORDbgCopyThreadContext(BYTE* pDstBuffer, ULONG32 cbDst, const BYTE* pSrcBuffer, ULONG32 cbSrc) { + _ASSERTE(cbDst >= sizeof(DT_CONTEXT)); + _ASSERTE(cbSrc >= sizeof(DT_CONTEXT)); + + DT_CONTEXT* pDst = reinterpret_cast(pDstBuffer); + const DT_CONTEXT* pSrc = reinterpret_cast(pSrcBuffer); + DWORD dstFlags = pDst->ContextFlags; DWORD srcFlags = pSrc->ContextFlags; LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: pDst=0x%08x dstFlags=0x%x, pSrc=0x%08x srcFlags=0x%x\n", pDst, dstFlags, pSrc, srcFlags)); - if ((dstFlags & srcFlags & DT_CONTEXT_CONTROL) == DT_CONTEXT_CONTROL) + if ((dstFlags & srcFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) { LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: RA: pDst=0x%lx, pSrc=0x%lx, Flags=0x%x\n", - pDst->Ra, pSrc->Ra, DT_CONTEXT_CONTROL)); + pDst->Ra, pSrc->Ra, CONTEXT_CONTROL)); pDst->Ra = pSrc->Ra; LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: SP: pDst=0x%lx, pSrc=0x%lx, Flags=0x%x\n", - pDst->Sp, pSrc->Sp, DT_CONTEXT_CONTROL)); + pDst->Sp, pSrc->Sp, CONTEXT_CONTROL)); pDst->Sp = pSrc->Sp; LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: FP: pDst=0x%lx, pSrc=0x%lx, Flags=0x%x\n", - pDst->Fp, pSrc->Fp, DT_CONTEXT_CONTROL)); + pDst->Fp, pSrc->Fp, CONTEXT_CONTROL)); pDst->Fp = pSrc->Fp; LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: PC: pDst=0x%lx, pSrc=0x%lx, Flags=0x%x\n", - pDst->Pc, pSrc->Pc, DT_CONTEXT_CONTROL)); + pDst->Pc, pSrc->Pc, CONTEXT_CONTROL)); pDst->Pc = pSrc->Pc; } - if ((dstFlags & srcFlags & DT_CONTEXT_INTEGER) == DT_CONTEXT_INTEGER) + if ((dstFlags & srcFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) { CopyContextChunk(&pDst->A0, &pSrc->A0, &pDst->Fp, - DT_CONTEXT_INTEGER); + CONTEXT_INTEGER); CopyContextChunk(&pDst->S0, &pSrc->S0, &pDst->Pc, - DT_CONTEXT_INTEGER); + CONTEXT_INTEGER); } - if ((dstFlags & srcFlags & DT_CONTEXT_FLOATING_POINT) == DT_CONTEXT_FLOATING_POINT) + if ((dstFlags & srcFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) { CopyContextChunk(&pDst->F[0], &pSrc->F[0], &pDst->F[32*4], - DT_CONTEXT_FLOATING_POINT); + CONTEXT_FLOATING_POINT); pDst->Fcsr = pSrc->Fcsr; pDst->Fcc = pSrc->Fcc; } diff --git a/src/coreclr/debug/shared/riscv64/primitives.cpp b/src/coreclr/debug/shared/riscv64/primitives.cpp index cf306684ada4cd..bd1ffa7286fdfb 100644 --- a/src/coreclr/debug/shared/riscv64/primitives.cpp +++ b/src/coreclr/debug/shared/riscv64/primitives.cpp @@ -15,53 +15,59 @@ // CopyThreadContext() does an intelligent copy from pSrc to pDst, // respecting the ContextFlags of both contexts. // -void CORDbgCopyThreadContext(DT_CONTEXT* pDst, const DT_CONTEXT* pSrc) +void CORDbgCopyThreadContext(BYTE* pDstBuffer, ULONG32 cbDst, const BYTE* pSrcBuffer, ULONG32 cbSrc) { + _ASSERTE(cbDst >= sizeof(DT_CONTEXT)); + _ASSERTE(cbSrc >= sizeof(DT_CONTEXT)); + + DT_CONTEXT* pDst = reinterpret_cast(pDstBuffer); + const DT_CONTEXT* pSrc = reinterpret_cast(pSrcBuffer); + DWORD dstFlags = pDst->ContextFlags; DWORD srcFlags = pSrc->ContextFlags; LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: pDst=0x%08x dstFlags=0x%x, pSrc=0x%08x srcFlags=0x%x\n", pDst, dstFlags, pSrc, srcFlags)); - if ((dstFlags & srcFlags & DT_CONTEXT_CONTROL) == DT_CONTEXT_CONTROL) + if ((dstFlags & srcFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) { LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: RA: pDst=0x%lx, pSrc=0x%lx, Flags=0x%x\n", - pDst->Ra, pSrc->Ra, DT_CONTEXT_CONTROL)); + pDst->Ra, pSrc->Ra, CONTEXT_CONTROL)); pDst->Ra = pSrc->Ra; LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: SP: pDst=0x%lx, pSrc=0x%lx, Flags=0x%x\n", - pDst->Sp, pSrc->Sp, DT_CONTEXT_CONTROL)); + pDst->Sp, pSrc->Sp, CONTEXT_CONTROL)); pDst->Sp = pSrc->Sp; LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: FP: pDst=0x%lx, pSrc=0x%lx, Flags=0x%x\n", - pDst->Fp, pSrc->Fp, DT_CONTEXT_CONTROL)); + pDst->Fp, pSrc->Fp, CONTEXT_CONTROL)); pDst->Fp = pSrc->Fp; LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: PC: pDst=0x%lx, pSrc=0x%lx, Flags=0x%x\n", - pDst->Pc, pSrc->Pc, DT_CONTEXT_CONTROL)); + pDst->Pc, pSrc->Pc, CONTEXT_CONTROL)); pDst->Pc = pSrc->Pc; } - if ((dstFlags & srcFlags & DT_CONTEXT_INTEGER) == DT_CONTEXT_INTEGER) + if ((dstFlags & srcFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) { CopyContextChunk(&pDst->Gp, &pSrc->Gp, &pDst->Fp, - DT_CONTEXT_INTEGER); + CONTEXT_INTEGER); CopyContextChunk(&pDst->S1, &pSrc->S1, &pDst->Pc, - DT_CONTEXT_INTEGER); + CONTEXT_INTEGER); LOG((LF_CORDB, LL_INFO1000000, "CP::CTC: T0: pDst=0x%lx, pSrc=0x%lx, Flags=0x%x\n", - pDst->R0, pSrc->R0, DT_CONTEXT_INTEGER)); + pDst->R0, pSrc->R0, CONTEXT_INTEGER)); pDst->R0 = pSrc->R0; } - if ((dstFlags & srcFlags & DT_CONTEXT_FLOATING_POINT) == DT_CONTEXT_FLOATING_POINT) + if ((dstFlags & srcFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) { CopyContextChunk(&pDst->F[0], &pSrc->F[0], &pDst->F[32], - DT_CONTEXT_FLOATING_POINT); + CONTEXT_FLOATING_POINT); pDst->Fcsr = pSrc->Fcsr; } } diff --git a/src/coreclr/inc/dacdbi.idl b/src/coreclr/inc/dacdbi.idl index 73610b87d4a3d4..5b256da9b5e8fa 100644 --- a/src/coreclr/inc/dacdbi.idl +++ b/src/coreclr/inc/dacdbi.idl @@ -50,7 +50,6 @@ typedef ULONG64 VMPTR_Assembly; typedef ULONG64 VMPTR_Module; typedef ULONG64 VMPTR_Thread; typedef ULONG64 VMPTR_MethodDesc; -typedef ULONG64 VMPTR_CONTEXT; typedef ULONG64 VMPTR_TypeHandle; typedef ULONG64 VMPTR_FieldDesc; typedef ULONG64 VMPTR_Object; @@ -64,6 +63,7 @@ typedef ULONG64 VMPTR_NativeCodeVersionNode; // Address and token types typedef ULONG64 CORDB_ADDRESS; +typedef ULONG64 CORDB_REGISTER; typedef ULONG64 GENERICS_TYPE_TOKEN; typedef ULONG64 PCODE; typedef ULONG64 TADDR; @@ -75,8 +75,12 @@ typedef int mdMethodDef; // Context types typedef int T_CONTEXT; -typedef int DT_CONTEXT; typedef int EXCEPTION_RECORD; +typedef struct +{ + BYTE * pContextBytes; + ULONG32 contextSize; +} ContextBuffer; // Metadata interface typedef int IMDInternalImport; @@ -93,6 +97,7 @@ typedef int DynamicMethodType; typedef int FrameType; typedef int EHijackReason; typedef int CLR_DEBUGGING_PROCESS_FLAGS; +typedef int ContextCopyMode; // Size type typedef unsigned int SIZE_T; @@ -272,19 +277,19 @@ interface IDacDbiInterface : IUnknown [in] CALLBACK_DATA pUserData); // Context and Stack Walking - HRESULT GetManagedStoppedContext([in] VMPTR_Thread vmThread, [out] VMPTR_CONTEXT * pRetVal); - HRESULT CreateStackWalk([in] VMPTR_Thread vmThread, [in, out] DT_CONTEXT * pInternalContextBuffer, [out] StackWalkHandle * ppSFIHandle); + HRESULT GetManagedStoppedContext([in] VMPTR_Thread vmThread, [out] CORDB_ADDRESS * pRetVal); + HRESULT CreateStackWalk([in] VMPTR_Thread vmThread, [in] ContextBuffer contextBuffer, [out] StackWalkHandle * ppSFIHandle); HRESULT DeleteStackWalk([in] StackWalkHandle ppSFIHandle); - HRESULT GetStackWalkCurrentContext([in] StackWalkHandle pSFIHandle, [out] DT_CONTEXT * pContext); - HRESULT SetStackWalkCurrentContext([in] VMPTR_Thread vmThread, [in] StackWalkHandle pSFIHandle, [in] CorDebugSetContextFlag flag, [in] DT_CONTEXT * pContext); + HRESULT GetStackWalkCurrentContext([in] StackWalkHandle pSFIHandle, [in] ContextBuffer contextBuffer); + HRESULT SetStackWalkCurrentContext([in] VMPTR_Thread vmThread, [in] StackWalkHandle pSFIHandle, [in] CorDebugSetContextFlag flag, [in] ContextBuffer contextBuffer); HRESULT UnwindStackWalkFrame([in] StackWalkHandle pSFIHandle, [out] BOOL * pResult); - HRESULT CheckContext([in] VMPTR_Thread vmThread, [in] const DT_CONTEXT * pContext); + HRESULT CheckContext([in] VMPTR_Thread vmThread, [in] ContextBuffer contextBuffer); HRESULT GetStackWalkCurrentFrameInfo([in] StackWalkHandle pSFIHandle, [out] struct Debugger_STRData * pFrameData, [out] FrameType * pRetVal); HRESULT GetCountOfInternalFrames([in] VMPTR_Thread vmThread, [out] ULONG32 * pRetVal); HRESULT EnumerateInternalFrames([in] VMPTR_Thread vmThread, [in] FP_INTERNAL_FRAME_ENUMERATION_CALLBACK fpCallback, [in] CALLBACK_DATA pUserData); HRESULT GetStackParameterSize([in] CORDB_ADDRESS controlPC, [out] ULONG32 * pRetVal); - HRESULT IsLeafFrame([in] VMPTR_Thread vmThread, [in] const DT_CONTEXT * pContext, [out] BOOL * pResult); - HRESULT GetContext([in] VMPTR_Thread vmThread, [out] DT_CONTEXT * pContextBuffer); + HRESULT IsLeafFrame([in] VMPTR_Thread vmThread, [in] ContextBuffer contextBuffer, [out] BOOL * pResult); + HRESULT GetContext([in] VMPTR_Thread vmThread, [in] ContextBuffer contextBuffer); // Method HRESULT IsDiagnosticsHiddenOrLCGMethod([in] VMPTR_MethodDesc vmMethodDesc, [out] DynamicMethodType * pRetVal); @@ -440,4 +445,46 @@ interface IDacDbiInterface : IUnknown // Generic Arg Token HRESULT GetGenericArgTokenIndex([in] VMPTR_MethodDesc vmMethod, [out] UINT32 * pTokenIndex); + + // Context size queries + HRESULT GetTargetContextSize([in] ULONG32 contextFlags, [out] ULONG32 * pSize); + + // CONTEXT register accessors. + HRESULT WriteRegistersToContext([in] ContextBuffer contextBuffer, [in] const int * regs, [in] ULONG32 nRegs, [in] const TADDR * values); + HRESULT ReadRegistersFromContext([in] ContextBuffer contextBuffer, [in] const int * regs, [in] ULONG32 nRegs, [out] CORDB_REGISTER * pValues); + + // Returns the full availability bitmask for the target architecture in + // CorDebugRegister bit positions, including both integer (GPR) and + // floating-point / SIMD registers. + HRESULT GetAvailableRegistersMask( + [in] BOOL fActive, + [in] BOOL fQuickUnwind, + [in] ULONG32 regCount, + [out, size_is(regCount)] BYTE pAvailable[]); + + // Translates a JIT ICorDebugInfo::RegNum to a CorDebugRegister value + HRESULT ConvertJitRegNumToCorDebugRegister([in] ULONG32 jitRegNum, [out] int * pReg); + + // Writes a single floating-point / SIMD register into a DBI-side context + // buffer. SIMD register files receive a raw byte copy into the low bytes of + // the slot; x87 slots are numerically re-encoded into 80-bit double-extended + // form. valueSize is the natural width of the value (4 for R4, 8 for R8). + HRESULT WriteFloatRegisterToContext( + [in] ContextBuffer contextBuffer, + [in] int reg, + [in] const BYTE * pValue, + [in] ULONG32 valueSize); + + HRESULT ContextHasExtendedRegisters([in] ContextBuffer contextBuffer, [out] BOOL * pResult); + + HRESULT CompareControlRegisters([in] ContextBuffer contextBuffer1, [in] ContextBuffer contextBuffer2, [out] BOOL * pResult); + + // Copies sourceContext into destinationContext, respecting the ContextFlags + // selected by copyMode. flags supplies the destination ContextFlags when + // copyMode requests explicit flags. + HRESULT CopyContext( + [in] ContextBuffer destinationContext, + [in] ContextBuffer sourceContext, + [in] ContextCopyMode copyMode, + [in] ULONG32 flags); }; diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 970d29fc769d78..8840784c72f28f 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -870,19 +870,19 @@ typedef FLOATING_SAVE_AREA *PFLOATING_SAVE_AREA; typedef struct _CONTEXT { ULONG ContextFlags; - ULONG Dr0_PAL_Undefined; - ULONG Dr1_PAL_Undefined; - ULONG Dr2_PAL_Undefined; - ULONG Dr3_PAL_Undefined; - ULONG Dr6_PAL_Undefined; - ULONG Dr7_PAL_Undefined; + ULONG Dr0; // UNDEFINED + ULONG Dr1; // UNDEFINED + ULONG Dr2; // UNDEFINED + ULONG Dr3; // UNDEFINED + ULONG Dr6; // UNDEFINED + ULONG Dr7; // UNDEFINED FLOATING_SAVE_AREA FloatSave; - ULONG SegGs_PAL_Undefined; - ULONG SegFs_PAL_Undefined; - ULONG SegEs_PAL_Undefined; - ULONG SegDs_PAL_Undefined; + ULONG SegGs; // UNDEFINED + ULONG SegFs; // UNDEFINED + ULONG SegEs; // UNDEFINED + ULONG SegDs; // UNDEFINED ULONG Edi; ULONG Esi; diff --git a/src/coreclr/vm/amd64/getstate.asm b/src/coreclr/vm/amd64/getstate.asm index 7033b7bf0c3d66..7487ca4e7c7ea1 100644 --- a/src/coreclr/vm/amd64/getstate.asm +++ b/src/coreclr/vm/amd64/getstate.asm @@ -29,9 +29,6 @@ LEAF_ENTRY GetRBP, _TEXT LEAF_END GetRBP, _TEXT -;// this is the same implementation as the function of the same name in di\amd64\floatconversion.asm and they must -;// remain in sync. - ;// @dbgtodo inspection: remove this function when we remove the ipc event to load the float state ; extern "C" double FPFillR8(void* fpContextSlot); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs index 4603d6f1f1f2ec..ba95a0513fe690 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs @@ -196,6 +196,60 @@ public bool TryReadRegister(int number, out TargetNUInt value) } } + // XMM0-XMM15 occupy the FltSave area with a 16-byte (128-bit) stride. + private const int XmmRegisterCount = 16; + private const int XmmRegisterStride = 16; + + public readonly bool TryReadFloatingPointRegister(ReadOnlySpan context, int index, out double value) + => SimdRegisterAccess.TryReadRegister(context, (int)Marshal.OffsetOf(nameof(Xmm)), XmmRegisterStride, XmmRegisterCount, index, out value); + + public readonly bool TryWriteFloatingPointRegister(Span context, int index, ReadOnlySpan value) + => SimdRegisterAccess.TryWriteRegister(context, (int)Marshal.OffsetOf(nameof(Xmm)), XmmRegisterStride, XmmRegisterCount, index, value); + + + public readonly (uint Flag, string Name)[] GetScalarRegisters() => s_scalarRegisters; + public readonly (uint Flag, int Start, int End)[] GetWideSpans() => s_wideSpans; + + private static readonly (uint Flag, string Name)[] s_scalarRegisters = + [ + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "cs"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "ss"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "eflags"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "rsp"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "rax"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "rcx"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "rdx"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "rbx"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "rbp"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "rsi"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "rdi"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r8"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r9"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r10"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r11"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r12"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r13"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r14"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r15"), + ((uint)ContextFlagsValues.CONTEXT_SEGMENTS, "ds"), + ((uint)ContextFlagsValues.CONTEXT_SEGMENTS, "es"), + ((uint)ContextFlagsValues.CONTEXT_SEGMENTS, "fs"), + ((uint)ContextFlagsValues.CONTEXT_SEGMENTS, "gs"), + ]; + + private static readonly (uint Flag, int Start, int End)[] s_wideSpans = + [ + // Matches native CORDbgCopyThreadContext's CONTROL chunk [Rip, Xmm0), which + // copies Rip together with the legacy x87 save area that has no named field here. + ((uint)ContextFlagsValues.CONTEXT_CONTROL, + (int)Marshal.OffsetOf(nameof(Rip)), (int)Marshal.OffsetOf(nameof(Xmm))), + ((uint)ContextFlagsValues.CONTEXT_DEBUG_REGISTERS, + (int)Marshal.OffsetOf(nameof(Dr0)), (int)Marshal.OffsetOf(nameof(Rax))), + ((uint)ContextFlagsValues.CONTEXT_FLOATING_POINT, + (int)Marshal.OffsetOf(nameof(MxCsr)), (int)Marshal.OffsetOf(nameof(Cs))), + ((uint)ContextFlagsValues.CONTEXT_FLOATING_POINT, + (int)Marshal.OffsetOf(nameof(Xmm)), (int)Marshal.OffsetOf(nameof(Xmm)) + (16 * 2 * sizeof(ulong))), + ]; [FieldOffset(0x0)] public ulong P1Home; @@ -218,12 +272,13 @@ public bool TryReadRegister(int number, out TargetNUInt value) [FieldOffset(0x30)] public uint ContextFlags; + [Register(RegisterType.FloatingPoint)] [FieldOffset(0x34)] public uint MxCsr; #region Segment registers - [Register(RegisterType.Segments)] + [Register(RegisterType.Control)] [FieldOffset(0x38)] public ushort Cs; @@ -243,13 +298,13 @@ public bool TryReadRegister(int number, out TargetNUInt value) [FieldOffset(0x40)] public ushort Gs; - [Register(RegisterType.Segments)] + [Register(RegisterType.Control)] [FieldOffset(0x42)] public ushort Ss; #endregion - [Register(RegisterType.General)] + [Register(RegisterType.Control)] [FieldOffset(0x44)] public int EFlags; @@ -303,7 +358,7 @@ public bool TryReadRegister(int number, out TargetNUInt value) [FieldOffset(0x98)] public ulong Rsp; - [Register(RegisterType.Control | RegisterType.FramePointer)] + [Register(RegisterType.General | RegisterType.FramePointer)] [FieldOffset(0xa0)] public ulong Rbp; @@ -355,33 +410,24 @@ public bool TryReadRegister(int number, out TargetNUInt value) #region Floating point registers - // [Register(RegisterType.FloatPoint)] - // [FieldOffset(0x100)] - // public XmmSaveArea FltSave; - - // [Register(RegisterType.FloatPoint)] - // [FieldOffset(0x300)] - // public VectorRegisterArea VectorRegisters; + [Register(RegisterType.FloatingPoint)] + [FieldOffset(0x1a0)] + public unsafe fixed ulong Xmm[16 * 2]; #endregion - [Register(RegisterType.Debug)] [FieldOffset(0x4a8)] public ulong DebugControl; - [Register(RegisterType.Debug)] [FieldOffset(0x4b0)] public ulong LastBranchToRip; - [Register(RegisterType.Debug)] [FieldOffset(0x4b8)] public ulong LastBranchFromRip; - [Register(RegisterType.Debug)] [FieldOffset(0x4c0)] public ulong LastExceptionToRip; - [Register(RegisterType.Debug)] [FieldOffset(0x4c8)] public ulong LastExceptionFromRip; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs index 65b9d9b2208ff9..ec8a8e4882fef9 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs @@ -244,6 +244,65 @@ public bool TryReadRegister(int number, out TargetNUInt value) } } + // V0-V31 are the 128-bit NEON registers, a 16-byte stride within the context. + private const int VRegisterCount = 32; + private const int VRegisterStride = 16; + + public readonly bool TryReadFloatingPointRegister(ReadOnlySpan context, int index, out double value) + => SimdRegisterAccess.TryReadRegister(context, (int)Marshal.OffsetOf(nameof(V)), VRegisterStride, VRegisterCount, index, out value); + + public readonly bool TryWriteFloatingPointRegister(Span context, int index, ReadOnlySpan value) + => SimdRegisterAccess.TryWriteRegister(context, (int)Marshal.OffsetOf(nameof(V)), VRegisterStride, VRegisterCount, index, value); + + public readonly (uint Flag, string Name)[] GetScalarRegisters() => s_scalarRegisters; + public readonly (uint Flag, int Start, int End)[] GetWideSpans() => s_wideSpans; + + private static readonly (uint Flag, string Name)[] s_scalarRegisters = + [ + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "fp"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "lr"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "sp"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "pc"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "cpsr"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x0"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x1"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x2"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x3"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x4"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x5"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x6"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x7"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x8"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x9"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x10"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x11"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x12"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x13"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x14"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x15"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x16"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x17"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x18"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x19"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x20"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x21"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x22"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x23"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x24"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x25"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x26"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x27"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x28"), + ]; + + private static readonly (uint Flag, int Start, int End)[] s_wideSpans = + [ + ((uint)ContextFlagsValues.CONTEXT_FLOATING_POINT, + (int)Marshal.OffsetOf(nameof(V)), (int)Marshal.OffsetOf(nameof(Bcr))), + ((uint)ContextFlagsValues.CONTEXT_DEBUG_REGISTERS, + (int)Marshal.OffsetOf(nameof(Bcr)), (int)Marshal.OffsetOf(nameof(Wvr)) + (ARM64_MAX_WATCHPOINTS * sizeof(ulong))), + ]; + private readonly unsafe TargetNUInt ReadVectorRegister(int index) { // V indexes into the low 64 bits of each 128-bit register, so double the index. @@ -257,7 +316,7 @@ private readonly unsafe TargetNUInt ReadVectorRegister(int index) #region General registers - [Register(RegisterType.General)] + [Register(RegisterType.Control)] [FieldOffset(0x4)] public uint Cpsr; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs index cde8ee0c0ce6f4..c29730b28b0f05 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs @@ -162,6 +162,48 @@ public bool TryReadRegister(int number, out TargetNUInt value) } } + // D0-D31 are the 64-bit views of the VFP/NEON register file, an 8-byte stride. + private const int DRegisterCount = 32; + private const int DRegisterStride = 8; + + public readonly bool TryReadFloatingPointRegister(ReadOnlySpan context, int index, out double value) + => SimdRegisterAccess.TryReadRegister(context, (int)Marshal.OffsetOf(nameof(D)), DRegisterStride, DRegisterCount, index, out value); + + public readonly bool TryWriteFloatingPointRegister(Span context, int index, ReadOnlySpan value) + => SimdRegisterAccess.TryWriteRegister(context, (int)Marshal.OffsetOf(nameof(D)), DRegisterStride, DRegisterCount, index, value); + + public readonly (uint Flag, string Name)[] GetScalarRegisters() => s_scalarRegisters; + public readonly (uint Flag, int Start, int End)[] GetWideSpans() => s_wideSpans; + + private static readonly (uint Flag, string Name)[] s_scalarRegisters = + [ + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "sp"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "lr"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "pc"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "cpsr"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r0"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r1"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r2"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r3"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r4"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r5"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r6"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r7"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r8"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r9"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r10"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r11"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "r12"), + ]; + + private static readonly (uint Flag, int Start, int End)[] s_wideSpans = + [ + ((uint)ContextFlagsValues.CONTEXT_FLOATING_POINT, + (int)Marshal.OffsetOf(nameof(Fpscr)), (int)Marshal.OffsetOf(nameof(Bvr))), + ((uint)ContextFlagsValues.CONTEXT_DEBUG_REGISTERS, + (int)Marshal.OffsetOf(nameof(Bvr)), (int)Marshal.OffsetOf(nameof(Wcr)) + sizeof(uint)), + ]; + // Control flags [FieldOffset(0x0)] @@ -237,7 +279,7 @@ public bool TryReadRegister(int number, out TargetNUInt value) [FieldOffset(0x40)] public uint Pc; - [Register(RegisterType.General)] + [Register(RegisterType.Control)] [FieldOffset(0x44)] public uint Cpsr; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs index 01457bab25b56a..85575024a662e7 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs @@ -12,6 +12,8 @@ public sealed class ContextHolder : IPlatformAgnosticContext, IEquatable Context.Size; + public uint SizeWithoutExtendedRegisters => Context.SizeWithoutExtendedRegisters; + public uint ExtendedRegistersFlag => Context.ExtendedRegistersFlag; public uint ContextControlFlags => Context.ContextControlFlags; public uint FullContextFlags => Context.FullContextFlags; public uint AllContextFlags => Context.AllContextFlags; @@ -58,6 +60,11 @@ public unsafe byte[] GetBytes() public bool TryReadRegister(string fieldName, out TargetNUInt value) => Context.TryReadRegister(fieldName, out value); public bool TrySetRegister(int number, TargetNUInt value) => Context.TrySetRegister(number, value); public bool TryReadRegister(int number, out TargetNUInt value) => Context.TryReadRegister(number, out value); + public bool TryReadFloatingPointRegister(ReadOnlySpan context, int index, out double value) => Context.TryReadFloatingPointRegister(context, index, out value); + public bool TryWriteFloatingPointRegister(Span context, int index, ReadOnlySpan value) => Context.TryWriteFloatingPointRegister(context, index, value); + + public (uint Flag, string Name)[] GetScalarRegisters() => Context.GetScalarRegisters(); + public (uint Flag, int Start, int End)[] GetWideSpans() => Context.GetWideSpans(); public bool Equals(ContextHolder? other) { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/FloatConversion.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/FloatConversion.cs new file mode 100644 index 00000000000000..a6320bb44fb258 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/FloatConversion.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers.Binary; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +internal static class FloatConversion +{ + // Convert an x87 80-bit double-extended value to IEEE-754 binary64 with + // round-to-nearest-even, matching the hardware FSTP conversion the legacy + // x86 path used. Layout (Intel SDM Vol 1 sec 8.2.2): bytes 0-7 = 64-bit + // significand with an explicit integer bit at 63; bytes 8-9 = 15-bit biased + // exponent (bias 16383) in bits 0-14 and the sign in bit 15. + public static double X87ExtendedToDouble(ReadOnlySpan slot10) + { + ulong significand = BinaryPrimitives.ReadUInt64LittleEndian(slot10); + ushort signExp = BinaryPrimitives.ReadUInt16LittleEndian(slot10.Slice(8)); + + ulong sign = (ulong)((signExp >> 15) & 1) << 63; + uint exp80 = (uint)(signExp & 0x7FFF); + + if (exp80 == 0x7FFF) + { + ulong frac = significand & ~(1UL << 63); + if (frac == 0) + return BitConverter.Int64BitsToDouble(unchecked((long)(sign | ((ulong)0x7FF << 52)))); + // Force a quiet NaN (keeping the high payload bits) so a signaling + // NaN is never misencoded as an infinity. + ulong payload = (frac >> 12) & ((1UL << 51) - 1); + return BitConverter.Int64BitsToDouble(unchecked((long)(sign | ((ulong)0x7FF << 52) | (1UL << 51) | payload))); + } + + // Zero, extended denormals and unnormals all fall below the binary64 + // subnormal floor (2^-1074) and map to signed zero. + if (exp80 == 0 || (significand & (1UL << 63)) == 0) + return BitConverter.Int64BitsToDouble(unchecked((long)sign)); + + int expD = (int)exp80 - 16383 + 1023; // pre-rounding binary64 biased exponent + + if (expD >= 0x7FF) + return BitConverter.Int64BitsToDouble(unchecked((long)(sign | ((ulong)0x7FF << 52)))); // overflow -> infinity + + if (expD > 0) + { + // Normal: keep the top 52 fraction bits and round the low 11 to even. + ulong frac = significand & ~(1UL << 63); // 63 fraction bits + ulong result = frac >> 11; // top 52 bits + ulong discarded = frac & 0x7FF; // low 11 bits + if (discarded > 0x400 || (discarded == 0x400 && (result & 1) != 0)) + { + result++; + if ((result >> 52) != 0) // carry out of the fraction + { + result = 0; + expD++; + if (expD >= 0x7FF) + return BitConverter.Int64BitsToDouble(unchecked((long)(sign | ((ulong)0x7FF << 52)))); + } + } + return BitConverter.Int64BitsToDouble(unchecked((long)(sign | ((ulong)expD << 52) | (result & ((1UL << 52) - 1))))); + } + + // Subnormal / underflow (expD <= 0): right-shift the significand by + // (12 - expD) and round to nearest-even. A carry into bit 52 promotes the + // value to the smallest normal, which the encoding produces for free. + int shift = 12 - expD; // >= 12 + if (shift > 64) // below half the smallest subnormal + return BitConverter.Int64BitsToDouble(unchecked((long)sign)); + + ulong res; + ulong disc; + ulong half; + if (shift == 64) + { + res = 0; + disc = significand; + half = 1UL << 63; + } + else + { + res = significand >> shift; + disc = significand & ((1UL << shift) - 1); + half = 1UL << (shift - 1); + } + if (disc > half || (disc == half && (res & 1) != 0)) + res++; + return BitConverter.Int64BitsToDouble(unchecked((long)(sign | res))); + } + + // Encode an IEEE-754 binary64 value into an x87 80-bit double-extended slot + // (the inverse of X87ExtendedToDouble). Every finite binary64 is represented + // exactly, since the 64-bit extended significand and 15-bit exponent strictly + // contain binary64's 52-bit fraction and 11-bit exponent. Layout: bytes 0-7 = + // 64-bit significand with an explicit integer bit at 63; bytes 8-9 = 15-bit + // biased exponent (bias 16383) in bits 0-14 and the sign in bit 15. + public static void X87DoubleToExtended(double value, Span slot10) + { + ulong bits = (ulong)BitConverter.DoubleToInt64Bits(value); + ulong sign = (bits >> 63) & 1; + uint exp = (uint)((bits >> 52) & 0x7FF); + ulong frac = bits & ((1UL << 52) - 1); + + ulong mant; // 64-bit significand with explicit integer bit at 63 + ushort signExp; // sign (bit 15) + 15-bit biased exponent + + if (exp == 0x7FF) + { + // Infinity or NaN: max exponent, integer bit set. Preserve the NaN + // payload (shifted into the extended fraction) and force it quiet. + mant = (1UL << 63) | (frac << 11); + if (frac != 0) + mant |= 1UL << 62; // quiet bit + signExp = (ushort)((sign << 15) | 0x7FFF); + } + else if (exp == 0 && frac == 0) + { + // Signed zero. + mant = 0; + signExp = (ushort)(sign << 15); + } + else if (exp == 0) + { + // Binary64 subnormal: normalize into the wider extended exponent range. + // value = frac * 2^(-1074); shift the leading fraction bit up to bit 63. + int leadingZeros = 0; + ulong m = frac; + while ((m & (1UL << 51)) == 0) + { + m <<= 1; + leadingZeros++; + } + mant = m << 12; // integer bit lands at 63 + int e = -1023 - leadingZeros + 16383; // rebias to extended + signExp = (ushort)((sign << 15) | (uint)(e & 0x7FFF)); + } + else + { + // Normal: prepend the implicit integer bit, align to bit 63, rebias. + mant = ((1UL << 52) | frac) << 11; + int e = (int)exp - 1023 + 16383; + signExp = (ushort)((sign << 15) | (uint)(e & 0x7FFF)); + } + + BinaryPrimitives.WriteUInt64LittleEndian(slot10, mant); + BinaryPrimitives.WriteUInt16LittleEndian(slot10.Slice(8), signExp); + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs index d0cd2264d1772d..adbfb9e5aab43c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs @@ -8,6 +8,8 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; public interface IPlatformAgnosticContext { public abstract uint Size { get; } + public abstract uint SizeWithoutExtendedRegisters { get; } + public abstract uint ExtendedRegistersFlag { get; } public abstract uint ContextControlFlags { get; } public abstract uint FullContextFlags { get; } public abstract uint AllContextFlags { get; } @@ -29,6 +31,15 @@ public interface IPlatformAgnosticContext public abstract bool TryReadRegister(string fieldName, out TargetNUInt value); public abstract bool TrySetRegister(int number, TargetNUInt value); public abstract bool TryReadRegister(int number, out TargetNUInt value); + public abstract bool TryReadFloatingPointRegister(ReadOnlySpan context, int index, out double value); + public abstract bool TryWriteFloatingPointRegister(Span context, int index, ReadOnlySpan value); + + // The register copy set derived from the context's [Register]-tagged fields, used to + // implement CONTEXT-flag-gated copies. Integer/control/segment registers are named and + // copied through the register accessors; floating-point/debug/extended state is copied + // as a raw [Start, End) byte span. + public abstract (uint Flag, string Name)[] GetScalarRegisters(); + public abstract (uint Flag, int Start, int End)[] GetWideSpans(); public abstract void Unwind(Target target); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformContext.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformContext.cs index 2e71424e3d2191..c345182832b7fb 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformContext.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformContext.cs @@ -1,11 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; + namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; public interface IPlatformContext { uint Size { get; } + + uint SizeWithoutExtendedRegisters => Size; + + uint ExtendedRegistersFlag => 0; + uint ContextControlFlags { get; } uint FullContextFlags { get; } uint AllContextFlags { get; } @@ -32,4 +39,9 @@ public interface IPlatformContext bool TrySetRegister(int number, TargetNUInt value); bool TryReadRegister(int number, out TargetNUInt value); + bool TryReadFloatingPointRegister(ReadOnlySpan context, int index, out double value); + bool TryWriteFloatingPointRegister(Span context, int index, ReadOnlySpan value); + + (uint Flag, string Name)[] GetScalarRegisters(); + (uint Flag, int Start, int End)[] GetWideSpans(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64Context.cs index bf0081fa0033b7..75ba2e786c07a7 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64Context.cs @@ -10,7 +10,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; /// /// LoongArch64-specific thread context. /// -[StructLayout(LayoutKind.Explicit, Pack = 1)] +[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x520)] internal struct LoongArch64Context : IPlatformContext { [Flags] @@ -34,7 +34,7 @@ public enum ContextFlagsValues : uint CONTEXT_AREA_MASK = 0xFFFF, } - public readonly uint Size => 0x320; + public readonly uint Size => 0x520; public readonly uint ContextControlFlags => (uint)ContextFlagsValues.CONTEXT_CONTROL; @@ -105,7 +105,7 @@ public bool TrySetRegister(string name, TargetNUInt value) if (name.Equals("s7", StringComparison.OrdinalIgnoreCase)) { S7 = value.Value; return true; } if (name.Equals("s8", StringComparison.OrdinalIgnoreCase)) { S8 = value.Value; return true; } if (name.Equals("pc", StringComparison.OrdinalIgnoreCase)) { Pc = value.Value; return true; } - if (name.Equals("fcc", StringComparison.OrdinalIgnoreCase)) { Fcc = (uint)value.Value; return true; } + if (name.Equals("fcc", StringComparison.OrdinalIgnoreCase)) { Fcc = value.Value; return true; } if (name.Equals("fcsr", StringComparison.OrdinalIgnoreCase)) { Fcsr = (uint)value.Value; return true; } return false; } @@ -231,6 +231,60 @@ public bool TryReadRegister(int number, out TargetNUInt value) } } + // F0-F31 hold the 256-bit LASX vector registers, a 32-byte stride within the context. + private const int FRegisterCount = 32; + private const int FRegisterStride = 32; + + public readonly bool TryReadFloatingPointRegister(ReadOnlySpan context, int index, out double value) + => SimdRegisterAccess.TryReadRegister(context, (int)Marshal.OffsetOf(nameof(F)), FRegisterStride, FRegisterCount, index, out value); + + public readonly bool TryWriteFloatingPointRegister(Span context, int index, ReadOnlySpan value) + => SimdRegisterAccess.TryWriteRegister(context, (int)Marshal.OffsetOf(nameof(F)), FRegisterStride, FRegisterCount, index, value); + + public readonly (uint Flag, string Name)[] GetScalarRegisters() => s_scalarRegisters; + public readonly (uint Flag, int Start, int End)[] GetWideSpans() => s_wideSpans; + + private static readonly (uint Flag, string Name)[] s_scalarRegisters = + [ + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "ra"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "sp"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "fp"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "pc"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a0"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a1"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a2"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a3"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a4"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a5"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a6"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a7"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t0"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t1"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t2"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t3"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t4"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t5"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t6"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t7"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t8"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "x0"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s0"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s1"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s2"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s3"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s4"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s5"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s6"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s7"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s8"), + ]; + + private static readonly (uint Flag, int Start, int End)[] s_wideSpans = + [ + ((uint)ContextFlagsValues.CONTEXT_FLOATING_POINT, + (int)Marshal.OffsetOf(nameof(F)), (int)Marshal.OffsetOf(nameof(Fcsr)) + sizeof(uint)), + ]; + // Control flags [FieldOffset(0x0)] @@ -238,19 +292,17 @@ public bool TryReadRegister(int number, out TargetNUInt value) #region General registers - [Register(RegisterType.General)] [FieldOffset(0x8)] public ulong R0; - [Register(RegisterType.General)] + [Register(RegisterType.Control)] [FieldOffset(0x10)] public ulong Ra; - [Register(RegisterType.General)] [FieldOffset(0x18)] public ulong Tp; - [Register(RegisterType.General | RegisterType.StackPointer)] + [Register(RegisterType.Control | RegisterType.StackPointer)] [FieldOffset(0x20)] public ulong Sp; @@ -326,7 +378,7 @@ public bool TryReadRegister(int number, out TargetNUInt value) [FieldOffset(0xb0)] public ulong X0; - [Register(RegisterType.General | RegisterType.FramePointer)] + [Register(RegisterType.Control | RegisterType.FramePointer)] [FieldOffset(0xb8)] public ulong Fp; @@ -380,14 +432,14 @@ public bool TryReadRegister(int number, out TargetNUInt value) [Register(RegisterType.FloatingPoint)] [FieldOffset(0x110)] - public unsafe fixed ulong F[32]; + public unsafe fixed ulong F[4 * 32]; [Register(RegisterType.FloatingPoint)] - [FieldOffset(0x210)] - public uint Fcc; + [FieldOffset(0x510)] + public ulong Fcc; [Register(RegisterType.FloatingPoint)] - [FieldOffset(0x214)] + [FieldOffset(0x518)] public uint Fcsr; #endregion diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64Context.cs index f829c897e1c5c2..a59552648aca12 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64Context.cs @@ -10,7 +10,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; /// /// RISC-V 64-bit specific thread context. /// -[StructLayout(LayoutKind.Explicit, Pack = 1)] +[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x220)] internal struct RISCV64Context : IPlatformContext { [Flags] @@ -228,6 +228,61 @@ public bool TryReadRegister(int number, out TargetNUInt value) } } + // F0-F31 are the 64-bit floating-point registers, an 8-byte stride within the context. + private const int FRegisterCount = 32; + private const int FRegisterStride = 8; + + public readonly bool TryReadFloatingPointRegister(ReadOnlySpan context, int index, out double value) + => SimdRegisterAccess.TryReadRegister(context, (int)Marshal.OffsetOf(nameof(F)), FRegisterStride, FRegisterCount, index, out value); + + public readonly bool TryWriteFloatingPointRegister(Span context, int index, ReadOnlySpan value) + => SimdRegisterAccess.TryWriteRegister(context, (int)Marshal.OffsetOf(nameof(F)), FRegisterStride, FRegisterCount, index, value); + + public readonly (uint Flag, string Name)[] GetScalarRegisters() => s_scalarRegisters; + public readonly (uint Flag, int Start, int End)[] GetWideSpans() => s_wideSpans; + + private static readonly (uint Flag, string Name)[] s_scalarRegisters = + [ + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "ra"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "sp"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "fp"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "pc"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "gp"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "tp"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t0"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t1"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t2"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s1"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a0"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a1"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a2"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a3"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a4"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a5"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a6"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "a7"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s2"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s3"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s4"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s5"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s6"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s7"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s8"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s9"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s10"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "s11"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t3"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t4"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t5"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "t6"), + ]; + + private static readonly (uint Flag, int Start, int End)[] s_wideSpans = + [ + ((uint)ContextFlagsValues.CONTEXT_FLOATING_POINT, + (int)Marshal.OffsetOf(nameof(F)), (int)Marshal.OffsetOf(nameof(Fcsr)) + sizeof(uint)), + ]; + // Control flags [FieldOffset(0x0)] @@ -235,11 +290,11 @@ public bool TryReadRegister(int number, out TargetNUInt value) #region General registers - [Register(RegisterType.General)] + [Register(RegisterType.Control)] [FieldOffset(0x8)] public ulong Ra; - [Register(RegisterType.General | RegisterType.StackPointer)] + [Register(RegisterType.Control | RegisterType.StackPointer)] [FieldOffset(0x10)] public ulong Sp; @@ -263,7 +318,7 @@ public bool TryReadRegister(int number, out TargetNUInt value) [FieldOffset(0x38)] public ulong T2; - [Register(RegisterType.General | RegisterType.FramePointer)] + [Register(RegisterType.Control | RegisterType.FramePointer)] [FieldOffset(0x40)] public ulong Fp; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RegisterAttribute.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RegisterAttribute.cs index 1ae0c32bf7ffa4..b37c30f09736b7 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RegisterAttribute.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RegisterAttribute.cs @@ -13,6 +13,7 @@ public enum RegisterType : byte Segments = 0x03, FloatingPoint = 0x04, Debug = 0x05, + Extended = 0x06, TypeMask = 0x0f, ProgramCounter = 0x10, diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/SimdRegisterAccess.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/SimdRegisterAccess.cs new file mode 100644 index 00000000000000..0ef4f58f2f7c44 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/SimdRegisterAccess.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers.Binary; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; + +/// +/// Shared helpers for reading and writing a scalar value from a fixed-stride floating-point / SIMD +/// register file (the V/XMM/F/D areas) laid out contiguously within a platform CONTEXT buffer. +/// +internal static class SimdRegisterAccess +{ + /// + /// Reads the low 64 bits (interpreted as a ) of the -th + /// register in the file that starts at with a + /// byte stride. Returns false when is outside the register file. + /// + public static bool TryReadRegister(ReadOnlySpan context, int fileOffset, int registerStride, int registerCount, int index, out double value) + { + value = 0.0; + if ((uint)index >= (uint)registerCount) + return false; + int offset = fileOffset + (index * registerStride); + if (offset + sizeof(ulong) <= context.Length) + value = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(context.Slice(offset))); + return true; + } + + /// + /// Writes (4 or 8 bytes) into the low bytes of the -th + /// register in the file that starts at with a + /// byte stride. Returns false when is outside the register file. + /// + public static bool TryWriteRegister(Span context, int fileOffset, int registerStride, int registerCount, int index, ReadOnlySpan value) + { + if ((uint)index >= (uint)registerCount) + return false; + int offset = fileOffset + (index * registerStride); + if (offset + value.Length > context.Length) + return false; + value.CopyTo(context.Slice(offset, value.Length)); + return true; + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/WasmContext.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/WasmContext.cs index 24ac092843f18b..1b4ed25fadf091 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/WasmContext.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/WasmContext.cs @@ -159,4 +159,15 @@ public readonly bool TryReadRegister(int number, out TargetNUInt value) value = default; return false; } + + public readonly bool TryReadFloatingPointRegister(ReadOnlySpan context, int index, out double value) + { + value = default; + return false; + } + + public readonly bool TryWriteFloatingPointRegister(Span context, int index, ReadOnlySpan value) => false; + + public readonly (uint Flag, string Name)[] GetScalarRegisters() => Array.Empty<(uint Flag, string Name)>(); + public readonly (uint Flag, int Start, int End)[] GetWideSpans() => Array.Empty<(uint Flag, int Start, int End)>(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs index d637dbe636426b..32b574952ccf5e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.InteropServices; +using System.Buffers.Binary; using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers.X86; namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; @@ -39,6 +40,10 @@ public enum ContextFlagsValues : uint public readonly uint Size => 0x2cc; + public readonly uint SizeWithoutExtendedRegisters => 0xcc; + + public readonly uint ExtendedRegistersFlag => (uint)ContextFlagsValues.CONTEXT_EXTENDED_REGISTERS; + public readonly uint ContextControlFlags => (uint)ContextFlagsValues.CONTEXT_CONTROL; public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; @@ -177,6 +182,94 @@ public bool TryReadRegister(int number, out TargetNUInt value) } } + // The x87 FP stack exposes 8 logical registers ST(0)-ST(7); each occupies a 10-byte + // 80-bit slot in the FloatSave register area. + private const int X87RegisterCount = 8; + + private static int Float80Size + => (int)Marshal.OffsetOf(nameof(ST1)) - (int)Marshal.OffsetOf(nameof(ST0)); + + public readonly bool TryReadFloatingPointRegister(ReadOnlySpan context, int index, out double value) + { + value = 0.0; + if ((uint)index >= X87RegisterCount) + return false; + + // The availability mask exposes all 8 slots even when the live stack is shallower; + // out-of-depth slots read as 0. + if (!TryGetX87SlotOffset(context, index, out int offset)) + return true; + + if (offset + Float80Size <= context.Length) + value = FloatConversion.X87ExtendedToDouble(context.Slice(offset, Float80Size)); + return true; + } + + public readonly bool TryWriteFloatingPointRegister(Span context, int index, ReadOnlySpan value) + { + if ((uint)index >= X87RegisterCount) + return false; + + // Writing an out-of-depth (or otherwise unresolvable) slot is not supported. + if (!TryGetX87SlotOffset(context, index, out int offset) || offset + Float80Size > context.Length) + return false; + + double d = value.Length == sizeof(float) + ? BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(value)) + : BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(value)); + FloatConversion.X87DoubleToExtended(d, context.Slice(offset, Float80Size)); + return true; + } + + // Resolves the byte offset of the 80-bit slot backing logical register ST(). + // REGISTER_X86_FPSTACK_0 names the bottom of the logical stack: ST(i) with i = top - logicalIndex. + // Returns false when logicalIndex is beyond the live stack depth. + private static bool TryGetX87SlotOffset(ReadOnlySpan context, int logicalIndex, out int offset) + { + offset = 0; + uint statusWord = BinaryPrimitives.ReadUInt32LittleEndian(context.Slice((int)Marshal.OffsetOf(nameof(StatusWord)))); + uint rawTop = (statusWord >> 11) & 0x7; + uint floatStackTop = 7 - rawTop; + if ((uint)logicalIndex > floatStackTop) + return false; + uint physIdx = (rawTop + (floatStackTop - (uint)logicalIndex)) & 0x7; + offset = (int)Marshal.OffsetOf(nameof(ST0)) + ((int)physIdx * Float80Size); + return true; + } + + public readonly (uint Flag, string Name)[] GetScalarRegisters() => s_scalarRegisters; + public readonly (uint Flag, int Start, int End)[] GetWideSpans() => s_wideSpans; + + private static readonly (uint Flag, string Name)[] s_scalarRegisters = + [ + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "ebp"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "eip"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "cs"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "eflags"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "esp"), + ((uint)ContextFlagsValues.CONTEXT_CONTROL, "ss"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "edi"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "esi"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "ebx"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "edx"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "ecx"), + ((uint)ContextFlagsValues.CONTEXT_INTEGER, "eax"), + ((uint)ContextFlagsValues.CONTEXT_SEGMENTS, "gs"), + ((uint)ContextFlagsValues.CONTEXT_SEGMENTS, "fs"), + ((uint)ContextFlagsValues.CONTEXT_SEGMENTS, "es"), + ((uint)ContextFlagsValues.CONTEXT_SEGMENTS, "ds"), + ]; + + private static readonly (uint Flag, int Start, int End)[] s_wideSpans = + [ + ((uint)ContextFlagsValues.CONTEXT_DEBUG_REGISTERS, + (int)Marshal.OffsetOf(nameof(Dr0)), (int)Marshal.OffsetOf(nameof(ControlWord))), + ((uint)ContextFlagsValues.CONTEXT_FLOATING_POINT, + (int)Marshal.OffsetOf(nameof(ControlWord)), (int)Marshal.OffsetOf(nameof(Gs))), + ((uint)ContextFlagsValues.CONTEXT_EXTENDED_REGISTERS, + (int)Marshal.OffsetOf(nameof(ExtendedRegisters)), (int)Marshal.OffsetOf(nameof(ExtendedRegisters)) + 512), + ]; + // Control flags [FieldOffset(0x0)] @@ -336,11 +429,11 @@ public bool TryReadRegister(int number, out TargetNUInt value) [FieldOffset(0xb8)] public uint Eip; - [Register(RegisterType.Segments)] + [Register(RegisterType.Control)] [FieldOffset(0xbc)] public uint Cs; - [Register(RegisterType.General)] + [Register(RegisterType.Control)] [FieldOffset(0xc0)] public uint EFlags; @@ -348,12 +441,13 @@ public bool TryReadRegister(int number, out TargetNUInt value) [FieldOffset(0xc4)] public uint Esp; - [Register(RegisterType.Segments)] + [Register(RegisterType.Control)] [FieldOffset(0xc8)] public uint Ss; #endregion + [Register(RegisterType.Extended)] [FieldOffset(0xcc)] public unsafe fixed byte ExtendedRegisters[512]; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index dfdd0766072cb6..57deac2a4c4187 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Buffers.Binary; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -1577,10 +1576,13 @@ public int GetManagedStoppedContext(ulong vmThread, ulong* pRetVal) return hr; } - private void SeedHandleFromNativeContext(StackWalkHandleData handleData, byte* pContext, bool isFirst) + private void SeedHandleFromNativeContext(StackWalkHandleData handleData, ContextBuffer contextBuffer, bool isFirst) { - uint contextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size; - byte[] contextBuf = new Span(pContext, (int)contextSize).ToArray(); + uint expectedContextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size; + if (contextBuffer.pContextBytes is null || contextBuffer.contextSize < expectedContextSize) + throw new ArgumentException("The context buffer is invalid.", nameof(contextBuffer)); + + byte[] contextBuf = new Span(contextBuffer.pContextBytes, (int)expectedContextSize).ToArray(); handleData.Reset(contextBuf, isFirst); } @@ -1619,13 +1621,16 @@ private string DescribeContextDiff(ReadOnlySpan cdac, ReadOnlySpan l } #endif - public int CreateStackWalk(ulong vmThread, byte* pInternalContextBuffer, nuint* ppSFIHandle) + public int CreateStackWalk(ulong vmThread, ContextBuffer contextBuffer, nuint* ppSFIHandle) { if (ppSFIHandle is null) return HResults.E_POINTER; - if (pInternalContextBuffer == null) - return HResults.E_POINTER; *ppSFIHandle = 0; + if (contextBuffer.pContextBytes == null) + return HResults.E_POINTER; + uint expectedContextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size; + if (contextBuffer.contextSize < expectedContextSize) + return HResults.E_INVALIDARG; int hr = HResults.S_OK; StackWalkHandleData? handleData = null; @@ -1635,10 +1640,10 @@ public int CreateStackWalk(ulong vmThread, byte* pInternalContextBuffer, nuint* uint allFlags = ctx.AllContextFlags; ThreadData threadData = _target.Contracts.Thread.GetThreadData(new TargetPointer(vmThread)); byte[] seedContext = _target.Contracts.StackWalk.GetContext(threadData, ThreadContextSource.Debugger, allFlags); - seedContext.AsSpan().CopyTo(new Span(pInternalContextBuffer, seedContext.Length)); + seedContext.AsSpan().CopyTo(new Span(contextBuffer.pContextBytes, (int)expectedContextSize)); handleData = new StackWalkHandleData(_target.Contracts.StackWalk, threadData); - SeedHandleFromNativeContext(handleData, pInternalContextBuffer, isFirst: true); + SeedHandleFromNativeContext(handleData, contextBuffer, isFirst: true); *ppSFIHandle = handleData.GetHandle(); } catch (System.Exception ex) @@ -1648,20 +1653,20 @@ public int CreateStackWalk(ulong vmThread, byte* pInternalContextBuffer, nuint* // Mirror the create onto the legacy DBI if (_legacy is not null && LegacyFallbackHelper.CanFallback()) { - uint contextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size; nuint legacyHandle = 0; - byte* pLocal = (byte*)NativeMemory.AlignedAlloc(contextSize, 16); + byte* pLocal = (byte*)NativeMemory.AlignedAlloc(expectedContextSize, 16); try { - new Span(pLocal, (int)contextSize).Clear(); - int hrLocal = _legacy.CreateStackWalk(vmThread, pLocal, &legacyHandle); + new Span(pLocal, (int)expectedContextSize).Clear(); + ContextBuffer legacyContext = new() { pContextBytes = pLocal, contextSize = expectedContextSize }; + int hrLocal = _legacy.CreateStackWalk(vmThread, legacyContext, &legacyHandle); Debug.ValidateHResult(hr, hrLocal); if (hr == HResults.S_OK && hrLocal == HResults.S_OK) { #if DEBUG - ReadOnlySpan cdacBytes = new(pInternalContextBuffer, (int)contextSize); - ReadOnlySpan legacyBytes = new(pLocal, (int)contextSize); + ReadOnlySpan cdacBytes = new(contextBuffer.pContextBytes, (int)expectedContextSize); + ReadOnlySpan legacyBytes = new(pLocal, (int)expectedContextSize); if (!cdacBytes.SequenceEqual(legacyBytes)) Debug.Fail(DescribeContextDiff(cdacBytes, legacyBytes)); #endif @@ -1709,16 +1714,18 @@ public int DeleteStackWalk(nuint ppSFIHandle) return hr; } - public int GetStackWalkCurrentContext(nuint pSFIHandle, byte* pContext) + public int GetStackWalkCurrentContext(nuint pSFIHandle, ContextBuffer contextBuffer) { if (pSFIHandle == 0) return HResults.E_INVALIDARG; - if (pContext == null) + if (contextBuffer.pContextBytes == null) return HResults.E_POINTER; int hr = HResults.S_OK; nuint legacyHandle = 0; - uint contextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size; + uint expectedContextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size; + if (contextBuffer.contextSize < expectedContextSize) + return HResults.E_INVALIDARG; try { GCHandle gcHandle = GCHandle.FromIntPtr((nint)pSFIHandle); @@ -1741,7 +1748,7 @@ public int GetStackWalkCurrentContext(nuint pSFIHandle, byte* pContext) context = stripped.GetBytes(); } - context.AsSpan().CopyTo(new Span(pContext, context.Length)); + context.AsSpan().CopyTo(new Span(contextBuffer.pContextBytes, (int)expectedContextSize)); } else { @@ -1755,16 +1762,17 @@ public int GetStackWalkCurrentContext(nuint pSFIHandle, byte* pContext) #if DEBUG if (_legacy is not null && legacyHandle != 0) { - byte* pLocal = (byte*)NativeMemory.AlignedAlloc(contextSize, 16); + byte* pLocal = (byte*)NativeMemory.AlignedAlloc(expectedContextSize, 16); try { - new Span(pLocal, (int)contextSize).Clear(); - int hrLocal = _legacy.GetStackWalkCurrentContext(legacyHandle, pLocal); + new Span(pLocal, (int)expectedContextSize).Clear(); + ContextBuffer legacyContext = new() { pContextBytes = pLocal, contextSize = expectedContextSize }; + int hrLocal = _legacy.GetStackWalkCurrentContext(legacyHandle, legacyContext); Debug.ValidateHResult(hr, hrLocal); if (hr == HResults.S_OK) { - ReadOnlySpan cdacBytes = new(pContext, (int)contextSize); - ReadOnlySpan legacyBytes = new(pLocal, (int)contextSize); + ReadOnlySpan cdacBytes = new(contextBuffer.pContextBytes, (int)expectedContextSize); + ReadOnlySpan legacyBytes = new(pLocal, (int)expectedContextSize); if (!cdacBytes.SequenceEqual(legacyBytes)) Debug.Fail(DescribeContextDiff(cdacBytes, legacyBytes)); } @@ -1778,12 +1786,14 @@ public int GetStackWalkCurrentContext(nuint pSFIHandle, byte* pContext) return hr; } - public int SetStackWalkCurrentContext(ulong vmThread, nuint pSFIHandle, int flag, byte* pContext) + public int SetStackWalkCurrentContext(ulong vmThread, nuint pSFIHandle, int flag, ContextBuffer contextBuffer) { if (pSFIHandle == 0) return HResults.E_INVALIDARG; - if (pContext == null) + if (contextBuffer.pContextBytes == null) return HResults.E_POINTER; + if (contextBuffer.contextSize < IPlatformAgnosticContext.GetContextForPlatform(_target).Size) + return HResults.E_INVALIDARG; int hr = HResults.S_OK; nuint legacyHandle = 0; @@ -1794,7 +1804,7 @@ public int SetStackWalkCurrentContext(ulong vmThread, nuint pSFIHandle, int flag throw new ArgumentException("Invalid stack walk handle", nameof(pSFIHandle)); legacyHandle = handleData.LegacyHandle; - SeedHandleFromNativeContext(handleData, pContext, isFirst: flag == (int)CorDebugSetContextFlags.SET_CONTEXT_FLAG_ACTIVE_FRAME); + SeedHandleFromNativeContext(handleData, contextBuffer, isFirst: flag == (int)CorDebugSetContextFlags.SET_CONTEXT_FLAG_ACTIVE_FRAME); } catch (System.Exception ex) { @@ -1803,7 +1813,7 @@ public int SetStackWalkCurrentContext(ulong vmThread, nuint pSFIHandle, int flag // Mirror to legacy DBI in all builds so the legacy walker tracks the cDAC walker if (_legacy is not null && LegacyFallbackHelper.CanFallback() && legacyHandle != 0) { - int hrLocal = _legacy.SetStackWalkCurrentContext(vmThread, legacyHandle, flag, pContext); + int hrLocal = _legacy.SetStackWalkCurrentContext(vmThread, legacyHandle, flag, contextBuffer); Debug.ValidateHResult(hr, hrLocal); } return hr; @@ -1861,13 +1871,20 @@ public int UnwindStackWalkFrame(nuint pSFIHandle, Interop.BOOL* pResult) return hr; } - public int CheckContext(ulong vmThread, byte* pContext) + public int CheckContext(ulong vmThread, ContextBuffer contextBuffer) { + if (contextBuffer.pContextBytes == null) + return HResults.E_POINTER; + + uint expectedContextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size; + if (contextBuffer.contextSize < expectedContextSize) + return HResults.E_INVALIDARG; + int hr = HResults.S_OK; try { IPlatformAgnosticContext ctx = IPlatformAgnosticContext.GetContextForPlatform(_target); - ctx.FillFromBuffer(new Span(pContext, (int)ctx.Size)); + ctx.FillFromBuffer(new Span(contextBuffer.pContextBytes, (int)ctx.Size)); if ((ctx.RawContextFlags & ctx.ContextControlFlags) != 0) { @@ -1886,7 +1903,7 @@ public int CheckContext(ulong vmThread, byte* pContext) #if DEBUG if (_legacy is not null) { - int hrLocal = _legacy.CheckContext(vmThread, pContext); + int hrLocal = _legacy.CheckContext(vmThread, contextBuffer); Debug.ValidateHResult(hr, hrLocal); } #endif @@ -2122,9 +2139,16 @@ private static nuint TryGetLegacyHandle(nuint pSFIHandle) } } - public int IsLeafFrame(ulong vmThread, byte* pContext, Interop.BOOL* pResult) + public int IsLeafFrame(ulong vmThread, ContextBuffer contextBuffer, Interop.BOOL* pResult) { + if (contextBuffer.pContextBytes == null || pResult == null) + return HResults.E_POINTER; + *pResult = Interop.BOOL.FALSE; + uint expectedContextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size; + if (contextBuffer.contextSize < expectedContextSize) + return HResults.E_INVALIDARG; + int hr = HResults.S_OK; try { @@ -2136,7 +2160,7 @@ public int IsLeafFrame(ulong vmThread, byte* pContext, Interop.BOOL* pResult) // Read the given context from the native buffer. IPlatformAgnosticContext givenCtx = IPlatformAgnosticContext.GetContextForPlatform(_target); - givenCtx.FillFromBuffer(new Span(pContext, leafContext.Length)); + givenCtx.FillFromBuffer(new Span(contextBuffer.pContextBytes, (int)expectedContextSize)); *pResult = givenCtx.StackPointer == leafCtx.StackPointer && givenCtx.InstructionPointer == leafCtx.InstructionPointer @@ -2150,7 +2174,7 @@ public int IsLeafFrame(ulong vmThread, byte* pContext, Interop.BOOL* pResult) if (_legacy is not null) { Interop.BOOL resultLocal; - int hrLocal = _legacy.IsLeafFrame(vmThread, pContext, &resultLocal); + int hrLocal = _legacy.IsLeafFrame(vmThread, contextBuffer, &resultLocal); Debug.ValidateHResult(hr, hrLocal); if (hr == HResults.S_OK) Debug.Assert(*pResult == resultLocal, $"cDAC: {*pResult}, DAC: {resultLocal}"); @@ -2159,8 +2183,15 @@ public int IsLeafFrame(ulong vmThread, byte* pContext, Interop.BOOL* pResult) return hr; } - public int GetContext(ulong vmThread, byte* pContextBuffer) + public int GetContext(ulong vmThread, ContextBuffer contextBuffer) { + if (contextBuffer.pContextBytes == null) + return HResults.E_POINTER; + + uint expectedContextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size; + if (contextBuffer.contextSize < expectedContextSize) + return HResults.E_INVALIDARG; + int hr = HResults.S_OK; try { @@ -2168,7 +2199,7 @@ public int GetContext(ulong vmThread, byte* pContextBuffer) ThreadData threadData = _target.Contracts.Thread.GetThreadData(new TargetPointer(vmThread)); byte[] context = _target.Contracts.StackWalk.GetContext(threadData, ThreadContextSource.Debugger, allFlags); - context.AsSpan().CopyTo(new Span(pContextBuffer, context.Length)); + context.AsSpan().CopyTo(new Span(contextBuffer.pContextBytes, (int)expectedContextSize)); } catch (System.Exception ex) { @@ -2177,18 +2208,18 @@ public int GetContext(ulong vmThread, byte* pContextBuffer) #if DEBUG if (_legacy is not null) { - uint contextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size; - byte[] localContextBuf = new byte[contextSize]; + byte[] localContextBuf = new byte[expectedContextSize]; fixed (byte* pLocal = localContextBuf) { - int hrLocal = _legacy.GetContext(vmThread, pLocal); + ContextBuffer legacyContext = new() { pContextBytes = pLocal, contextSize = expectedContextSize }; + int hrLocal = _legacy.GetContext(vmThread, legacyContext); Debug.ValidateHResult(hr, hrLocal); if (hr == HResults.S_OK) { IPlatformAgnosticContext contextStruct = IPlatformAgnosticContext.GetContextForPlatform(_target); IPlatformAgnosticContext localContextStruct = IPlatformAgnosticContext.GetContextForPlatform(_target); - contextStruct.FillFromBuffer(new Span(pContextBuffer, (int)contextSize)); + contextStruct.FillFromBuffer(new Span(contextBuffer.pContextBytes, (int)expectedContextSize)); localContextStruct.FillFromBuffer(localContextBuf); Debug.Assert(contextStruct.Equals(localContextStruct)); @@ -5788,6 +5819,712 @@ public int GetGenericArgTokenIndex(ulong vmMethod, uint* pIndex) return hr; } + public int GetTargetContextSize(uint contextFlags, uint* pSize) + { + int hr = HResults.S_OK; + try + { + if (pSize is null) + throw new ArgumentNullException(nameof(pSize)); + + IPlatformAgnosticContext ctx = IPlatformAgnosticContext.GetContextForPlatform(_target); + + // The CONTEXT size varies with the flags only on platforms that support variable-sized + // CONTEXTs (currently just x86's extended registers). On every other platform + // ExtendedRegistersFlag is 0 and SizeWithoutExtendedRegisters == Size, so the full size + // is always returned. This mirrors the native DAC's TARGET_X86 build-time gate. + *pSize = ctx.ExtendedRegistersFlag != 0 && (contextFlags & ctx.ExtendedRegistersFlag) != ctx.ExtendedRegistersFlag + ? ctx.SizeWithoutExtendedRegisters + : ctx.Size; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + uint sizeLocal; + int hrLocal = _legacy.GetTargetContextSize(contextFlags, &sizeLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + Debug.Assert(*pSize == sizeLocal, $"cDAC: {*pSize}, DAC: {sizeLocal}"); + } +#endif + return hr; + } + + public int ContextHasExtendedRegisters(ContextBuffer contextBuffer, Interop.BOOL* pResult) + { + int hr = HResults.S_OK; + try + { + if (contextBuffer.pContextBytes is null) + throw new ArgumentNullException(nameof(contextBuffer)); + if (pResult is null) + throw new ArgumentNullException(nameof(pResult)); + + IPlatformAgnosticContext ctx = IPlatformAgnosticContext.GetContextForPlatform(_target); + if (contextBuffer.contextSize < ctx.Size) + throw new ArgumentException("Context buffer too small", nameof(contextBuffer)); + + if (ctx.ExtendedRegistersFlag == 0) + { + *pResult = Interop.BOOL.FALSE; + } + else + { + ctx.FillFromBuffer(new Span(contextBuffer.pContextBytes, (int)ctx.Size)); + bool hasExtended = (ctx.RawContextFlags & ctx.ExtendedRegistersFlag) == ctx.ExtendedRegistersFlag; + *pResult = hasExtended ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + Interop.BOOL resultLocal; + int hrLocal = _legacy.ContextHasExtendedRegisters(contextBuffer, &resultLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + Debug.Assert(*pResult == resultLocal, $"cDAC: {*pResult}, DAC: {resultLocal}"); + } +#endif + return hr; + } + + public int CompareControlRegisters(ContextBuffer contextBuffer1, ContextBuffer contextBuffer2, Interop.BOOL* pResult) + { + int hr = HResults.S_OK; + try + { + if (contextBuffer1.pContextBytes is null || contextBuffer2.pContextBytes is null) + throw new ArgumentNullException(contextBuffer1.pContextBytes is null ? nameof(contextBuffer1) : nameof(contextBuffer2)); + if (pResult is null) + throw new ArgumentNullException(nameof(pResult)); + + IPlatformAgnosticContext ctx1 = IPlatformAgnosticContext.GetContextForPlatform(_target); + IPlatformAgnosticContext ctx2 = IPlatformAgnosticContext.GetContextForPlatform(_target); + if (contextBuffer1.contextSize < ctx1.Size || contextBuffer2.contextSize < ctx2.Size) + throw new ArgumentException("Context buffer too small"); + + ctx1.FillFromBuffer(new Span(contextBuffer1.pContextBytes, (int)ctx1.Size)); + ctx2.FillFromBuffer(new Span(contextBuffer2.pContextBytes, (int)ctx2.Size)); + + *pResult = ctx1.StackPointer == ctx2.StackPointer + && ctx1.InstructionPointer == ctx2.InstructionPointer + ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + Interop.BOOL resultLocal; + int hrLocal = _legacy.CompareControlRegisters(contextBuffer1, contextBuffer2, &resultLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + Debug.Assert(*pResult == resultLocal, $"cDAC: {*pResult}, DAC: {resultLocal}"); + } +#endif + return hr; + } + + public int CopyContext(ContextBuffer destinationContext, ContextBuffer sourceContext, ContextCopyMode copyMode, uint flags) + { + int hr = HResults.S_OK; + IPlatformAgnosticContext ctx = IPlatformAgnosticContext.GetContextForPlatform(_target); +#if DEBUG + // Capture the untouched destination image so the legacy cross-check below can + // replay the same copy from an identical starting state. + byte[]? originalDst = (destinationContext.pContextBytes is not null && destinationContext.contextSize >= ctx.Size) + ? new ReadOnlySpan(destinationContext.pContextBytes, (int)ctx.Size).ToArray() + : null; +#endif + try + { + if (destinationContext.pContextBytes is null || sourceContext.pContextBytes is null) + throw new ArgumentNullException(destinationContext.pContextBytes is null ? nameof(destinationContext) : nameof(sourceContext)); + + if (destinationContext.contextSize < ctx.Size || sourceContext.contextSize < ctx.Size) + throw new ArgumentException("Context buffer too small"); + + Span dst = new Span(destinationContext.pContextBytes, (int)ctx.Size); + Span src = new Span(sourceContext.pContextBytes, (int)ctx.Size); + + IPlatformAgnosticContext dstCtx = IPlatformAgnosticContext.GetContextForPlatform(_target); + dstCtx.FillFromBuffer(dst); + IPlatformAgnosticContext srcCtx = IPlatformAgnosticContext.GetContextForPlatform(_target); + srcCtx.FillFromBuffer(src); + + if (copyMode != ContextCopyMode.UseExplicitFlags && flags != 0) + throw new ArgumentException("Flags are only valid when explicit context flags are requested.", nameof(flags)); + + switch (copyMode) + { + case ContextCopyMode.PreserveDestinationFlags: + break; + case ContextCopyMode.MergeSourceFlags: + dstCtx.RawContextFlags |= srcCtx.RawContextFlags; + break; + case ContextCopyMode.UseExplicitFlags: + dstCtx.RawContextFlags = flags; + break; + default: + throw new ArgumentException("Invalid context copy mode.", nameof(copyMode)); + } + + uint dstFlags = dstCtx.RawContextFlags; + uint srcFlags = srcCtx.RawContextFlags; + + // Integer/control/segment registers copy by name; wide floating-point/debug/ + // extended state copies as a raw byte span. A group is copied only when its flag + // is set in both the source and destination contexts. + foreach ((uint flag, string name) in dstCtx.GetScalarRegisters()) + { + if ((dstFlags & srcFlags & flag) == flag && srcCtx.TryReadRegister(name, out TargetNUInt value)) + dstCtx.TrySetRegister(name, value); + } + + dstCtx.GetBytes().AsSpan(0, (int)ctx.Size).CopyTo(dst); + + foreach ((uint flag, int start, int end) in dstCtx.GetWideSpans()) + { + if ((dstFlags & srcFlags & flag) == flag) + src.Slice(start, end - start).CopyTo(dst.Slice(start)); + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null && originalDst is not null && + sourceContext.pContextBytes is not null && sourceContext.contextSize >= ctx.Size) + { + byte[] scratchDst = (byte[])originalDst.Clone(); + byte[] scratchSrc = new ReadOnlySpan(sourceContext.pContextBytes, (int)ctx.Size).ToArray(); + fixed (byte* pScratchDst = scratchDst) + fixed (byte* pScratchSrc = scratchSrc) + { + ContextBuffer scratchDestination = new() { pContextBytes = pScratchDst, contextSize = ctx.Size }; + ContextBuffer scratchSource = new() { pContextBytes = pScratchSrc, contextSize = ctx.Size }; + int hrLocal = _legacy.CopyContext(scratchDestination, scratchSource, copyMode, flags); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + Debug.Assert(new ReadOnlySpan(destinationContext.pContextBytes, (int)ctx.Size).SequenceEqual(scratchDst.AsSpan(0, (int)ctx.Size)), + "cDAC and DAC produced different copied contexts"); + } + } +#endif + return hr; + } + + public int WriteRegistersToContext(ContextBuffer contextBuffer, CorDebugRegister* regs, uint nRegs, nuint* values) + { + int hr = HResults.S_OK; + IPlatformAgnosticContext ctx = IPlatformAgnosticContext.GetContextForPlatform(_target); + try + { + if (contextBuffer.pContextBytes is null) + throw new ArgumentNullException(nameof(contextBuffer)); + if (nRegs > 0 && (regs is null || values is null)) + throw new ArgumentNullException(regs is null ? nameof(regs) : nameof(values)); + if (contextBuffer.contextSize < ctx.Size) + throw new ArgumentException("Context buffer too small", nameof(contextBuffer)); + + Span bufSpan = new Span(contextBuffer.pContextBytes, (int)ctx.Size); + ctx.FillFromBuffer(bufSpan); + + RuntimeInfoArchitecture arch = _target.Contracts.RuntimeInfo.GetTargetArchitecture(); + for (uint i = 0; i < nRegs; i++) + { + // Registers with no CONTEXT slot (float / SIMD) are skipped. + string? name = MapToCdacName(arch, regs[i]); + if (name is not null) + ctx.TrySetRegister(name, new TargetNUInt(values[i])); + } + + ctx.GetBytes().AsSpan(0, (int)ctx.Size).CopyTo(bufSpan); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null && contextBuffer.pContextBytes is not null && contextBuffer.contextSize >= ctx.Size) + { + byte[] scratch = new byte[ctx.Size]; + new ReadOnlySpan(contextBuffer.pContextBytes, (int)ctx.Size).CopyTo(scratch); + fixed (byte* pScratch = scratch) + { + ContextBuffer scratchContext = new() { pContextBytes = pScratch, contextSize = ctx.Size }; + int hrLocal = _legacy.WriteRegistersToContext(scratchContext, regs, nRegs, values); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + Debug.Assert(new ReadOnlySpan(contextBuffer.pContextBytes, (int)ctx.Size).SequenceEqual(scratch), + "cDAC and DAC produced different mutated contexts"); + } + } +#endif + return hr; + } + + public int ReadRegistersFromContext(ContextBuffer contextBuffer, CorDebugRegister* regs, uint nRegs, ulong* pValues) + { + int hr = HResults.S_OK; + try + { + if (contextBuffer.pContextBytes is null) + throw new ArgumentNullException(nameof(contextBuffer)); + if (nRegs > 0 && (regs is null || pValues is null)) + throw new ArgumentNullException(regs is null ? nameof(regs) : nameof(pValues)); + + IPlatformAgnosticContext ctx = IPlatformAgnosticContext.GetContextForPlatform(_target); + if (contextBuffer.contextSize < ctx.Size) + throw new ArgumentException("Context buffer too small", nameof(contextBuffer)); + + ctx.FillFromBuffer(new Span(contextBuffer.pContextBytes, (int)ctx.Size)); + ReadOnlySpan buf = new ReadOnlySpan(contextBuffer.pContextBytes, (int)ctx.Size); + + RuntimeInfoArchitecture arch = _target.Contracts.RuntimeInfo.GetTargetArchitecture(); + for (uint i = 0; i < nRegs; i++) + { + string? name = MapToCdacName(arch, regs[i]); + if (name is not null && ctx.TryReadRegister(name, out TargetNUInt v)) + { + // Integer register: zero-extend the pointer-sized value to 64 bits. + pValues[i] = (ulong)v.Value; + } + else if (TryReadFloatRegister(ctx, buf, arch, regs[i], out double floatValue)) + { + // Float / SIMD register: its scalar 64-bit double bit pattern. + pValues[i] = (ulong)BitConverter.DoubleToInt64Bits(floatValue); + } + else + { + // Registers with no CONTEXT slot at all read as zero. + pValues[i] = 0; + } + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + ulong[] valuesLocal = new ulong[nRegs]; + int hrLocal; + fixed (ulong* pValuesLocal = valuesLocal) + hrLocal = _legacy.ReadRegistersFromContext(contextBuffer, regs, nRegs, pValuesLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + { + for (uint i = 0; i < nRegs; i++) + Debug.Assert(pValues[i] == valuesLocal[i], $"reg[{i}] cDAC: {pValues[i]:x}, DAC: {valuesLocal[i]:x}"); + } + } +#endif + return hr; + } + + // Reads a single float / SIMD register's scalar 64-bit value (as a double) from the target context. + private static bool TryReadFloatRegister(IPlatformAgnosticContext ctx, ReadOnlySpan context, + RuntimeInfoArchitecture arch, CorDebugRegister reg, out double value) + { + value = 0.0; + if (!TryGetFloatRegisterIndex(arch, reg, out int index)) + return false; + return ctx.TryReadFloatingPointRegister(context, index, out value); + } + + private static bool TryGetFloatRegisterIndex(RuntimeInfoArchitecture arch, CorDebugRegister reg, out int index) + { + int first = arch switch + { + RuntimeInfoArchitecture.X86 => (int)CorDebugRegister.REGISTER_X86_FPSTACK_0, + RuntimeInfoArchitecture.X64 => (int)CorDebugRegister.REGISTER_AMD64_XMM0, + RuntimeInfoArchitecture.Arm64 => (int)CorDebugRegister.REGISTER_ARM64_V0, + RuntimeInfoArchitecture.Arm => (int)CorDebugRegister.REGISTER_ARM_D0, + RuntimeInfoArchitecture.LoongArch64 => (int)CorDebugRegister.REGISTER_LOONGARCH64_F0, + RuntimeInfoArchitecture.RiscV64 => (int)CorDebugRegister.REGISTER_RISCV64_F0, + _ => -1, + }; + index = first < 0 ? -1 : (int)reg - first; + return index >= 0; + } + + public int GetAvailableRegistersMask(Interop.BOOL fActive, Interop.BOOL fQuickUnwind, uint regCount, byte* pAvailable) + { + int hr = HResults.S_OK; + try + { + if (pAvailable is null) + throw new ArgumentNullException(nameof(pAvailable)); + if (regCount == 0) + throw new ArgumentException("regCount must be non-zero", nameof(regCount)); + + new Span(pAvailable, (int)regCount).Clear(); + + RuntimeInfoArchitecture arch = _target.Contracts.RuntimeInfo.GetTargetArchitecture(); + WriteAvailableRegistersMask(arch, fActive != Interop.BOOL.FALSE, fQuickUnwind != Interop.BOOL.FALSE, + pAvailable, regCount); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null && pAvailable is not null && regCount > 0) + { + byte[] localBuf = new byte[regCount]; + int hrLocal; + fixed (byte* pLocal = localBuf) + hrLocal = _legacy.GetAvailableRegistersMask(fActive, fQuickUnwind, regCount, pLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + Debug.Assert(new ReadOnlySpan(pAvailable, (int)regCount).SequenceEqual(localBuf), + "cDAC and DAC produced different availability masks"); + } +#endif + return hr; + } + + public int ConvertJitRegNumToCorDebugRegister(uint jitRegNum, CorDebugRegister* pReg) + { + int hr = HResults.S_OK; + try + { + if (pReg is null) + throw new ArgumentNullException(nameof(pReg)); + + RuntimeInfoArchitecture arch = _target.Contracts.RuntimeInfo.GetTargetArchitecture(); + CorDebugRegister? reg = MapJitRegNumToCorDebugRegister(arch, jitRegNum); + if (reg is null) + throw new ArgumentException($"Unsupported JIT register {jitRegNum}", nameof(jitRegNum)); + + *pReg = reg.Value; + } + catch (System.Exception ex) + { + hr = ex.HResult; + if (pReg is not null) *pReg = default; + } +#if DEBUG + if (_legacy is not null) + { + CorDebugRegister regLocal; + int hrLocal = _legacy.ConvertJitRegNumToCorDebugRegister(jitRegNum, ®Local); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + Debug.Assert(*pReg == regLocal, $"cDAC: {(int)(*pReg)}, DAC: {(int)regLocal}"); + } +#endif + return hr; + } + + public int WriteFloatRegisterToContext(ContextBuffer contextBuffer, CorDebugRegister reg, byte* pValue, uint valueSize) + { +#if DEBUG + byte[]? originalContext = null; + if (contextBuffer.pContextBytes is not null && contextBuffer.contextSize > 0) + originalContext = new ReadOnlySpan(contextBuffer.pContextBytes, (int)contextBuffer.contextSize).ToArray(); +#endif + int hr = HResults.S_OK; + try + { + if (contextBuffer.pContextBytes is null || pValue is null || + (valueSize != sizeof(float) && valueSize != sizeof(double))) + { + throw new ArgumentException("Invalid argument to WriteFloatRegisterToContext"); + } + + IPlatformAgnosticContext ctx = IPlatformAgnosticContext.GetContextForPlatform(_target); + if (contextBuffer.contextSize < ctx.Size) + throw new ArgumentException("Context buffer too small", nameof(contextBuffer)); + + Span buf = new Span(contextBuffer.pContextBytes, (int)ctx.Size); + + RuntimeInfoArchitecture arch = _target.Contracts.RuntimeInfo.GetTargetArchitecture(); + ReadOnlySpan valueBytes = new ReadOnlySpan(pValue, (int)valueSize); + + if (!TryGetFloatRegisterIndex(arch, reg, out int index) || + !ctx.TryWriteFloatingPointRegister(buf, index, valueBytes)) + { + throw new ArgumentException($"Register {reg} is not a float register", nameof(reg)); + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null && originalContext is not null && pValue is not null && + (valueSize == sizeof(float) || valueSize == sizeof(double))) + { + // Replay the same write through the legacy DAC on a copy of the + // pre-write context bytes and confirm the resulting buffers match. + byte[] legacyContext = originalContext; + int hrLocal; + fixed (byte* pLegacy = legacyContext) + { + ContextBuffer legacyBuffer = new ContextBuffer { pContextBytes = pLegacy, contextSize = contextBuffer.contextSize }; + hrLocal = _legacy.WriteFloatRegisterToContext(legacyBuffer, reg, pValue, valueSize); + } + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + { + ReadOnlySpan cdacResult = new ReadOnlySpan(contextBuffer.pContextBytes, (int)contextBuffer.contextSize); + Debug.Assert(cdacResult.SequenceEqual(legacyContext), + "cDAC and DAC produced different context bytes after WriteFloatRegisterToContext"); + } + } +#endif + return hr; + } + + private static void WriteAvailableRegistersMask(RuntimeInfoArchitecture arch, bool fActive, bool fQuickUnwind, + byte* pAvailable, uint regCount) + { + void SetBit(int reg) + { + uint byteIdx = (uint)reg / 8; + if (byteIdx < regCount) + pAvailable[byteIdx] |= (byte)(1 << (reg % 8)); + } + void SetRange(CorDebugRegister first, CorDebugRegister last) + { + for (int r = (int)first; r <= (int)last; r++) + SetBit(r); + } + switch (arch) + { + case RuntimeInfoArchitecture.X86: + SetBit((int)CorDebugRegister.REGISTER_X86_EIP); + SetBit((int)CorDebugRegister.REGISTER_X86_ESP); + SetBit((int)CorDebugRegister.REGISTER_X86_EBP); + if (!fQuickUnwind || fActive) + SetRange(CorDebugRegister.REGISTER_X86_EAX, CorDebugRegister.REGISTER_X86_EDI); + if (fActive) + SetRange(CorDebugRegister.REGISTER_X86_FPSTACK_0, CorDebugRegister.REGISTER_X86_FPSTACK_7); + break; + + case RuntimeInfoArchitecture.X64: + SetBit((int)CorDebugRegister.REGISTER_AMD64_RIP); + SetBit((int)CorDebugRegister.REGISTER_AMD64_RSP); + if (!fQuickUnwind || fActive) + SetRange(CorDebugRegister.REGISTER_AMD64_RBP, CorDebugRegister.REGISTER_AMD64_R15); + if (fActive) + SetRange(CorDebugRegister.REGISTER_AMD64_XMM0, CorDebugRegister.REGISTER_AMD64_XMM15); + break; + + case RuntimeInfoArchitecture.Arm: + SetBit((int)CorDebugRegister.REGISTER_ARM_PC); + SetBit((int)CorDebugRegister.REGISTER_ARM_SP); + SetRange(CorDebugRegister.REGISTER_ARM_R0, CorDebugRegister.REGISTER_ARM_LR); + break; + + case RuntimeInfoArchitecture.Arm64: + SetBit((int)CorDebugRegister.REGISTER_ARM64_PC); + SetBit((int)CorDebugRegister.REGISTER_ARM64_SP); + SetBit((int)CorDebugRegister.REGISTER_ARM64_FP); + SetRange(CorDebugRegister.REGISTER_ARM64_X0, CorDebugRegister.REGISTER_ARM64_X28); + SetBit((int)CorDebugRegister.REGISTER_ARM64_LR); + SetRange(CorDebugRegister.REGISTER_ARM64_V0, CorDebugRegister.REGISTER_ARM64_V31); + break; + + case RuntimeInfoArchitecture.LoongArch64: + for (int r = (int)CorDebugRegister.REGISTER_LOONGARCH64_PC; + r <= (int)CorDebugRegister.REGISTER_LOONGARCH64_S8; r++) + SetBit(r); + SetRange(CorDebugRegister.REGISTER_LOONGARCH64_F0, CorDebugRegister.REGISTER_LOONGARCH64_F31); + break; + + case RuntimeInfoArchitecture.RiscV64: + for (int r = (int)CorDebugRegister.REGISTER_RISCV64_PC; + r <= (int)CorDebugRegister.REGISTER_RISCV64_T6; r++) + SetBit(r); + SetRange(CorDebugRegister.REGISTER_RISCV64_F0, CorDebugRegister.REGISTER_RISCV64_F31); + break; + + default: + throw new NotImplementedException($"GetAvailableRegistersMask: unsupported architecture {arch}"); + } + } + + private static CorDebugRegister? MapJitRegNumToCorDebugRegister(RuntimeInfoArchitecture arch, uint jitRegNum) + { + switch (arch) + { + case RuntimeInfoArchitecture.X86: + return jitRegNum switch + { + 0 => CorDebugRegister.REGISTER_X86_EAX, + 1 => CorDebugRegister.REGISTER_X86_ECX, + 2 => CorDebugRegister.REGISTER_X86_EDX, + 3 => CorDebugRegister.REGISTER_X86_EBX, + 4 => CorDebugRegister.REGISTER_X86_ESP, + 5 => CorDebugRegister.REGISTER_X86_EBP, + 6 => CorDebugRegister.REGISTER_X86_ESI, + 7 => CorDebugRegister.REGISTER_X86_EDI, + _ => null, + }; + + case RuntimeInfoArchitecture.X64: + return jitRegNum switch + { + 0 => CorDebugRegister.REGISTER_AMD64_RAX, + 1 => CorDebugRegister.REGISTER_AMD64_RCX, + 2 => CorDebugRegister.REGISTER_AMD64_RDX, + 3 => CorDebugRegister.REGISTER_AMD64_RBX, + 4 => CorDebugRegister.REGISTER_AMD64_RSP, + 5 => CorDebugRegister.REGISTER_AMD64_RBP, + 6 => CorDebugRegister.REGISTER_AMD64_RSI, + 7 => CorDebugRegister.REGISTER_AMD64_RDI, + >= 8 and <= 15 => (CorDebugRegister)((int)CorDebugRegister.REGISTER_AMD64_R8 + (int)(jitRegNum - 8)), + _ => null, + }; + + case RuntimeInfoArchitecture.Arm: + return jitRegNum switch + { + <= 12 => (CorDebugRegister)((int)CorDebugRegister.REGISTER_ARM_R0 + (int)jitRegNum), + 13 => CorDebugRegister.REGISTER_ARM_SP, + 14 => CorDebugRegister.REGISTER_ARM_LR, + 15 => CorDebugRegister.REGISTER_ARM_PC, + _ => null, + }; + + case RuntimeInfoArchitecture.Arm64: + return jitRegNum switch + { + <= 28 => (CorDebugRegister)((int)CorDebugRegister.REGISTER_ARM64_X0 + (int)jitRegNum), + 29 => CorDebugRegister.REGISTER_ARM64_FP, + 30 => CorDebugRegister.REGISTER_ARM64_LR, + 31 => CorDebugRegister.REGISTER_ARM64_SP, + 32 => CorDebugRegister.REGISTER_ARM64_PC, + _ => null, + }; + + case RuntimeInfoArchitecture.LoongArch64: + return jitRegNum switch + { + 0 => null, + 1 => CorDebugRegister.REGISTER_LOONGARCH64_RA, + 2 => CorDebugRegister.REGISTER_LOONGARCH64_TP, + 3 => CorDebugRegister.REGISTER_LOONGARCH64_SP, + >= 4 and <= 11 => (CorDebugRegister)((int)CorDebugRegister.REGISTER_LOONGARCH64_A0 + (int)(jitRegNum - 4)), + >= 12 and <= 20 => (CorDebugRegister)((int)CorDebugRegister.REGISTER_LOONGARCH64_T0 + (int)(jitRegNum - 12)), + 21 => CorDebugRegister.REGISTER_LOONGARCH64_X0, + 22 => CorDebugRegister.REGISTER_LOONGARCH64_FP, + >= 23 and <= 31 => (CorDebugRegister)((int)CorDebugRegister.REGISTER_LOONGARCH64_S0 + (int)(jitRegNum - 23)), + 32 => CorDebugRegister.REGISTER_LOONGARCH64_PC, + _ => null, + }; + + case RuntimeInfoArchitecture.RiscV64: + return jitRegNum switch + { + 0 => null, + 1 => CorDebugRegister.REGISTER_RISCV64_RA, + 2 => CorDebugRegister.REGISTER_RISCV64_SP, + 3 => CorDebugRegister.REGISTER_RISCV64_GP, + 4 => CorDebugRegister.REGISTER_RISCV64_TP, + >= 5 and <= 7 => (CorDebugRegister)((int)CorDebugRegister.REGISTER_RISCV64_T0 + (int)(jitRegNum - 5)), + 8 => CorDebugRegister.REGISTER_RISCV64_FP, + 9 => CorDebugRegister.REGISTER_RISCV64_S1, + >= 10 and <= 17 => (CorDebugRegister)((int)CorDebugRegister.REGISTER_RISCV64_A0 + (int)(jitRegNum - 10)), + >= 18 and <= 27 => (CorDebugRegister)((int)CorDebugRegister.REGISTER_RISCV64_S2 + (int)(jitRegNum - 18)), + >= 28 and <= 31 => (CorDebugRegister)((int)CorDebugRegister.REGISTER_RISCV64_T3 + (int)(jitRegNum - 28)), + 32 => CorDebugRegister.REGISTER_RISCV64_PC, + _ => null, + }; + + default: + return null; + } + } + + private static string? MapToCdacName(RuntimeInfoArchitecture arch, CorDebugRegister reg) + { + int n = (int)reg; + switch (arch) + { + case RuntimeInfoArchitecture.X86: + return n switch + { + 0 => "eip", 1 => "esp", 2 => "ebp", + 3 => "eax", 4 => "ecx", 5 => "edx", + 6 => "ebx", 7 => "esi", 8 => "edi", + _ => null, + }; + + case RuntimeInfoArchitecture.X64: + return n switch + { + 0 => "rip", 1 => "rsp", 2 => "rbp", + 3 => "rax", 4 => "rcx", 5 => "rdx", + 6 => "rbx", 7 => "rsi", 8 => "rdi", + >= 9 and <= 16 => "r" + (n - 9 + 8).ToString(), + _ => null, + }; + + case RuntimeInfoArchitecture.Arm: + return n switch + { + 0 => "pc", + 1 => "sp", + >= 2 and <= 14 => "r" + (n - 2).ToString(), + 15 => "lr", + _ => null, + }; + + case RuntimeInfoArchitecture.Arm64: + return n switch + { + 0 => "pc", + 1 => "sp", + 2 => "fp", + >= 3 and <= 31 => "x" + (n - 3).ToString(), + 32 => "lr", + _ => null, + }; + + case RuntimeInfoArchitecture.LoongArch64: + return n switch + { + 0 => "pc", 1 => "sp", 2 => "fp", 3 => "ra", 4 => "tp", + >= 5 and <= 12 => "a" + (n - 5).ToString(), + >= 13 and <= 21 => "t" + (n - 13).ToString(), + 22 => "x0", + >= 23 and <= 31 => "s" + (n - 23).ToString(), + _ => null, + }; + + case RuntimeInfoArchitecture.RiscV64: + return n switch + { + 0 => "pc", 1 => "sp", 2 => "fp", 3 => "ra", 4 => "gp", 5 => "tp", + >= 6 and <= 8 => "t" + (n - 6).ToString(), + 9 => "s1", + >= 10 and <= 17 => "a" + (n - 10).ToString(), + >= 18 and <= 27 => "s" + (n - 18 + 2).ToString(), + >= 28 and <= 31 => "t" + (n - 28 + 3).ToString(), + _ => null, + }; + + default: + return null; + } + } + // Fills a DebuggerIPCE_ExpandedTypeData entry for a single type parameter, falling back to System.__Canon on failure. private void FillExpandedTypeDataWithCanonFallback(IRuntimeTypeSystem rts, ITypeHandle typeHandle, ITypeHandle thCanon, DebuggerIPCE_ExpandedTypeData* pTypeInfo) { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs index cc96dd9fc20abd..eb175946717dff 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs @@ -29,6 +29,13 @@ public struct COR_TYPEID public ulong token2; } +[StructLayout(LayoutKind.Sequential)] +public unsafe struct ContextBuffer +{ + public byte* pContextBytes; + public uint contextSize; +} + [StructLayout(LayoutKind.Sequential)] public struct FieldData { @@ -326,8 +333,8 @@ public struct DebuggerIPCE_STRData_StubFrame // Holds data for each stack frame or chain, passed from the RC to the DI during a // stack walk. Mirrors the native Debugger_STRData in src/coreclr/debug/inc/dacdbistructures.h. -// ctx is a host-sized pointer to a dbi-allocated DT_CONTEXT buffer the DAC writes through; -// paths that produce no context (e.g. EnumerateInternalFrames cStubFrame entries) leave it 0. +// ctx wraps a host-sized pointer to a DBI-owned buffer (plus its size) holding the +// target's opaque CONTEXT byte image; paths that produce no context leave it zeroed. [StructLayout(LayoutKind.Explicit)] public struct Debugger_STRData { @@ -338,13 +345,13 @@ public enum EType cRuntimeNativeFrame = 2, } - [FieldOffset(0)] public ulong fp; // FramePointer (CORDB_ADDRESS) - [FieldOffset(8)] public nuint ctx; // DT_CONTEXT* (host-sized) - [FieldOffset(16)] public ulong vmCurrentAppDomainToken; // VMPTR_AppDomain - [FieldOffset(24)] public EType eType; + [FieldOffset(0)] public ulong fp; // CORDB_ADDRESS + [FieldOffset(8)] public ContextBuffer ctx; // ContextBuffer (host-sized pointer + size) + [FieldOffset(24)] public ulong vmCurrentAppDomainToken; // VMPTR_AppDomain + [FieldOffset(32)] public EType eType; // v (method frame) and stubFrame overlap, mirroring the native anonymous union. - [FieldOffset(32)] public DebuggerIPCE_STRData_MethodFrame v; - [FieldOffset(32)] public DebuggerIPCE_STRData_StubFrame stubFrame; + [FieldOffset(40)] public DebuggerIPCE_STRData_MethodFrame v; + [FieldOffset(40)] public DebuggerIPCE_STRData_StubFrame stubFrame; } #pragma warning restore CS0649 @@ -507,6 +514,154 @@ public enum CorDebugSetContextFlags SET_CONTEXT_FLAG_UNWIND_FRAME = 0x2, } +public enum CorDebugRegister : int +{ + REGISTER_INSTRUCTION_POINTER = 0, + REGISTER_STACK_POINTER = 1, + REGISTER_FRAME_POINTER = 2, + + REGISTER_X86_EIP = 0, + REGISTER_X86_ESP = 1, + REGISTER_X86_EBP = 2, + REGISTER_X86_EAX = 3, + REGISTER_X86_ECX = 4, + REGISTER_X86_EDX = 5, + REGISTER_X86_EBX = 6, + REGISTER_X86_ESI = 7, + REGISTER_X86_EDI = 8, + REGISTER_X86_FPSTACK_0 = 9, + REGISTER_X86_FPSTACK_1 = 10, + REGISTER_X86_FPSTACK_2 = 11, + REGISTER_X86_FPSTACK_3 = 12, + REGISTER_X86_FPSTACK_4 = 13, + REGISTER_X86_FPSTACK_5 = 14, + REGISTER_X86_FPSTACK_6 = 15, + REGISTER_X86_FPSTACK_7 = 16, + + REGISTER_AMD64_RIP = 0, + REGISTER_AMD64_RSP = 1, + REGISTER_AMD64_RBP = 2, + REGISTER_AMD64_RAX = 3, + REGISTER_AMD64_RCX = 4, + REGISTER_AMD64_RDX = 5, + REGISTER_AMD64_RBX = 6, + REGISTER_AMD64_RSI = 7, + REGISTER_AMD64_RDI = 8, + REGISTER_AMD64_R8 = 9, + REGISTER_AMD64_R9 = 10, + REGISTER_AMD64_R10 = 11, + REGISTER_AMD64_R11 = 12, + REGISTER_AMD64_R12 = 13, + REGISTER_AMD64_R13 = 14, + REGISTER_AMD64_R14 = 15, + REGISTER_AMD64_R15 = 16, + REGISTER_AMD64_XMM0 = 17, + REGISTER_AMD64_XMM1 = 18, + REGISTER_AMD64_XMM2 = 19, + REGISTER_AMD64_XMM3 = 20, + REGISTER_AMD64_XMM4 = 21, + REGISTER_AMD64_XMM5 = 22, + REGISTER_AMD64_XMM6 = 23, + REGISTER_AMD64_XMM7 = 24, + REGISTER_AMD64_XMM8 = 25, + REGISTER_AMD64_XMM9 = 26, + REGISTER_AMD64_XMM10 = 27, + REGISTER_AMD64_XMM11 = 28, + REGISTER_AMD64_XMM12 = 29, + REGISTER_AMD64_XMM13 = 30, + REGISTER_AMD64_XMM14 = 31, + REGISTER_AMD64_XMM15 = 32, + + REGISTER_ARM_PC = 0, + REGISTER_ARM_SP = 1, + REGISTER_ARM_R0 = 2, + REGISTER_ARM_R1 = 3, + REGISTER_ARM_R2 = 4, + REGISTER_ARM_R3 = 5, + REGISTER_ARM_R4 = 6, + REGISTER_ARM_R5 = 7, + REGISTER_ARM_R6 = 8, + REGISTER_ARM_R7 = 9, + REGISTER_ARM_R8 = 10, + REGISTER_ARM_R9 = 11, + REGISTER_ARM_R10 = 12, + REGISTER_ARM_R11 = 13, + REGISTER_ARM_R12 = 14, + REGISTER_ARM_LR = 15, + REGISTER_ARM_D0 = 16, + REGISTER_ARM_D31 = 47, + + REGISTER_ARM64_PC = 0, + REGISTER_ARM64_SP = 1, + REGISTER_ARM64_FP = 2, + REGISTER_ARM64_X0 = 3, + REGISTER_ARM64_X1 = 4, + REGISTER_ARM64_X2 = 5, + REGISTER_ARM64_X3 = 6, + REGISTER_ARM64_X4 = 7, + REGISTER_ARM64_X5 = 8, + REGISTER_ARM64_X6 = 9, + REGISTER_ARM64_X7 = 10, + REGISTER_ARM64_X8 = 11, + REGISTER_ARM64_X9 = 12, + REGISTER_ARM64_X10 = 13, + REGISTER_ARM64_X11 = 14, + REGISTER_ARM64_X12 = 15, + REGISTER_ARM64_X13 = 16, + REGISTER_ARM64_X14 = 17, + REGISTER_ARM64_X15 = 18, + REGISTER_ARM64_X16 = 19, + REGISTER_ARM64_X17 = 20, + REGISTER_ARM64_X18 = 21, + REGISTER_ARM64_X19 = 22, + REGISTER_ARM64_X20 = 23, + REGISTER_ARM64_X21 = 24, + REGISTER_ARM64_X22 = 25, + REGISTER_ARM64_X23 = 26, + REGISTER_ARM64_X24 = 27, + REGISTER_ARM64_X25 = 28, + REGISTER_ARM64_X26 = 29, + REGISTER_ARM64_X27 = 30, + REGISTER_ARM64_X28 = 31, + REGISTER_ARM64_LR = 32, + REGISTER_ARM64_V0 = 33, + REGISTER_ARM64_V31 = 64, + + REGISTER_LOONGARCH64_PC = 0, + REGISTER_LOONGARCH64_SP = 1, + REGISTER_LOONGARCH64_FP = 2, + REGISTER_LOONGARCH64_RA = 3, + REGISTER_LOONGARCH64_TP = 4, + REGISTER_LOONGARCH64_A0 = 5, + REGISTER_LOONGARCH64_A7 = 12, + REGISTER_LOONGARCH64_T0 = 13, + REGISTER_LOONGARCH64_T8 = 21, + REGISTER_LOONGARCH64_X0 = 22, + REGISTER_LOONGARCH64_S0 = 23, + REGISTER_LOONGARCH64_S8 = 31, + REGISTER_LOONGARCH64_F0 = 32, + REGISTER_LOONGARCH64_F31 = 63, + + REGISTER_RISCV64_PC = 0, + REGISTER_RISCV64_SP = 1, + REGISTER_RISCV64_FP = 2, + REGISTER_RISCV64_RA = 3, + REGISTER_RISCV64_GP = 4, + REGISTER_RISCV64_TP = 5, + REGISTER_RISCV64_T0 = 6, + REGISTER_RISCV64_T1 = 7, + REGISTER_RISCV64_T2 = 8, + REGISTER_RISCV64_S1 = 9, + REGISTER_RISCV64_A0 = 10, + REGISTER_RISCV64_A7 = 17, + REGISTER_RISCV64_S2 = 18, + REGISTER_RISCV64_S11 = 27, + REGISTER_RISCV64_T3 = 28, + REGISTER_RISCV64_T6 = 31, + REGISTER_RISCV64_F0 = 32, + REGISTER_RISCV64_F31 = 63, +} + // Name-surface projection of IDacDbiInterface in native method order for COM binding validation. // Parameter shapes are intentionally coarse placeholders and will be refined with method implementation work. [GeneratedComInterface] @@ -640,22 +795,22 @@ public unsafe partial interface IDacDbiInterface int GetManagedStoppedContext(ulong vmThread, ulong* pRetVal); [PreserveSig] - int CreateStackWalk(ulong vmThread, byte* pInternalContextBuffer, nuint* ppSFIHandle); + int CreateStackWalk(ulong vmThread, ContextBuffer contextBuffer, nuint* ppSFIHandle); [PreserveSig] int DeleteStackWalk(nuint ppSFIHandle); [PreserveSig] - int GetStackWalkCurrentContext(nuint pSFIHandle, byte* pContext); + int GetStackWalkCurrentContext(nuint pSFIHandle, ContextBuffer contextBuffer); [PreserveSig] - int SetStackWalkCurrentContext(ulong vmThread, nuint pSFIHandle, int flag, byte* pContext); + int SetStackWalkCurrentContext(ulong vmThread, nuint pSFIHandle, int flag, ContextBuffer contextBuffer); [PreserveSig] int UnwindStackWalkFrame(nuint pSFIHandle, Interop.BOOL* pResult); [PreserveSig] - int CheckContext(ulong vmThread, byte* pContext); + int CheckContext(ulong vmThread, ContextBuffer contextBuffer); [PreserveSig] int GetStackWalkCurrentFrameInfo(nuint pSFIHandle, nint pFrameData, int* pRetVal); @@ -670,10 +825,10 @@ public unsafe partial interface IDacDbiInterface int GetStackParameterSize(ulong controlPC, uint* pRetVal); [PreserveSig] - int IsLeafFrame(ulong vmThread, byte* pContext, Interop.BOOL* pResult); + int IsLeafFrame(ulong vmThread, ContextBuffer contextBuffer, Interop.BOOL* pResult); [PreserveSig] - int GetContext(ulong vmThread, byte* pContextBuffer); + int GetContext(ulong vmThread, ContextBuffer contextBuffer); [PreserveSig] int IsDiagnosticsHiddenOrLCGMethod(ulong vmMethodDesc, int* pRetVal); @@ -896,4 +1051,38 @@ int EnumerateAsyncLocals(ulong vmMethod, ulong codeAddr, uint state, [PreserveSig] int GetGenericArgTokenIndex(ulong vmMethod, uint* pIndex); + + [PreserveSig] + int GetTargetContextSize(uint contextFlags, uint* pSize); + + [PreserveSig] + int WriteRegistersToContext(ContextBuffer contextBuffer, CorDebugRegister* regs, uint nRegs, nuint* values); + + [PreserveSig] + int ReadRegistersFromContext(ContextBuffer contextBuffer, CorDebugRegister* regs, uint nRegs, ulong* pValues); + + [PreserveSig] + int GetAvailableRegistersMask(Interop.BOOL fActive, Interop.BOOL fQuickUnwind, uint regCount, byte* pAvailable); + + [PreserveSig] + int ConvertJitRegNumToCorDebugRegister(uint jitRegNum, CorDebugRegister* pReg); + + [PreserveSig] + int WriteFloatRegisterToContext(ContextBuffer contextBuffer, CorDebugRegister reg, byte* pValue, uint valueSize); + + [PreserveSig] + int ContextHasExtendedRegisters(ContextBuffer contextBuffer, Interop.BOOL* pResult); + + [PreserveSig] + int CompareControlRegisters(ContextBuffer contextBuffer1, ContextBuffer contextBuffer2, Interop.BOOL* pResult); + + [PreserveSig] + int CopyContext(ContextBuffer destinationContext, ContextBuffer sourceContext, ContextCopyMode copyMode, uint flags); +} + +public enum ContextCopyMode +{ + PreserveDestinationFlags = 0, + MergeSourceFlags, + UseExplicitFlags, } diff --git a/src/native/managed/cdac/tests/DumpTests/DacDbi/DacDbiStackWalkDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/DacDbi/DacDbiStackWalkDumpTests.cs index 3b932d54ba7894..622d042c59eeb4 100644 --- a/src/native/managed/cdac/tests/DumpTests/DacDbi/DacDbiStackWalkDumpTests.cs +++ b/src/native/managed/cdac/tests/DumpTests/DacDbi/DacDbiStackWalkDumpTests.cs @@ -35,7 +35,8 @@ public unsafe void GetContext_Succeeds_ForCrashingThread(TestConfiguration confi fixed (byte* pContext = contextBuffer) { - int hr = dbi.GetContext(crashingThread.ThreadAddress, pContext); + ContextBuffer buffer = new() { pContextBytes = pContext, contextSize = contextSize }; + int hr = dbi.GetContext(crashingThread.ThreadAddress, buffer); Assert.Equal(System.HResults.S_OK, hr); } @@ -58,7 +59,8 @@ public unsafe void GetContext_MatchesContractGetContext(TestConfiguration config byte[] dbiContextBuffer = new byte[contextSize]; fixed (byte* pContext = dbiContextBuffer) { - int hr = dbi.GetContext(crashingThread.ThreadAddress, pContext); + ContextBuffer buffer = new() { pContextBytes = pContext, contextSize = contextSize }; + int hr = dbi.GetContext(crashingThread.ThreadAddress, buffer); Assert.Equal(System.HResults.S_OK, hr); } @@ -90,7 +92,8 @@ public unsafe void IsLeafFrame_TrueForLeafContext(TestConfiguration config) Interop.BOOL result; fixed (byte* pContext = leafContext) { - int hr = dbi.IsLeafFrame(crashingThread.ThreadAddress, pContext, &result); + ContextBuffer contextBuffer = new() { pContextBytes = pContext, contextSize = (uint)leafContext.Length }; + int hr = dbi.IsLeafFrame(crashingThread.ThreadAddress, contextBuffer, &result); Assert.Equal(System.HResults.S_OK, hr); } @@ -130,7 +133,8 @@ public unsafe void IsLeafFrame_FalseForNonLeafContext(TestConfiguration config) Interop.BOOL result; fixed (byte* pContext = nonLeafContext) { - int hr = dbi.IsLeafFrame(crashingThread.ThreadAddress, pContext, &result); + ContextBuffer contextBuffer = new() { pContextBytes = pContext, contextSize = (uint)nonLeafContext.Length }; + int hr = dbi.IsLeafFrame(crashingThread.ThreadAddress, contextBuffer, &result); Assert.Equal(System.HResults.S_OK, hr); } diff --git a/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs b/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs index 17f7e687d1b0e9..e70cf51d6053e0 100644 --- a/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs @@ -847,6 +847,90 @@ public static IEnumerable TargetArchitectures_SpRange() } } + [Theory] + [MemberData(nameof(TargetArchitectures))] + public void CopyContext_CopyModeControlsContextFlags(MockTarget.Architecture arch, string targetArch) + { + var target = new TestPlaceholderTarget.Builder(arch) + .AddGlobalStrings((Constants.Globals.Architecture, targetArch)) + .AddContract(version: "c1") + .Build(); + DacDbiImpl dacDbi = new(target, legacyObj: null); + + IPlatformAgnosticContext destinationContext = IPlatformAgnosticContext.GetContextForPlatform(target); + IPlatformAgnosticContext sourceContext = IPlatformAgnosticContext.GetContextForPlatform(target); + destinationContext.RawContextFlags = destinationContext.ContextControlFlags; + sourceContext.RawContextFlags = sourceContext.FullContextFlags; + + string? sourceOnlyRegister = null; + foreach ((uint flag, string name) in sourceContext.GetScalarRegisters()) + { + if ((sourceContext.FullContextFlags & flag) == flag && + (destinationContext.ContextControlFlags & flag) != flag) + { + sourceOnlyRegister = name; + break; + } + } + + Assert.NotNull(sourceOnlyRegister); + TargetNUInt expectedValue = new(0x1234); + Assert.True(sourceContext.TrySetRegister(sourceOnlyRegister, expectedValue)); + + byte[] destinationBytes = destinationContext.GetBytes(); + byte[] sourceBytes = sourceContext.GetBytes(); + fixed (byte* pDestination = destinationBytes) + fixed (byte* pSource = sourceBytes) + { + ContextBuffer destinationBuffer = new() { pContextBytes = pDestination, contextSize = (uint)destinationBytes.Length }; + ContextBuffer sourceBuffer = new() { pContextBytes = pSource, contextSize = (uint)sourceBytes.Length }; + int hr = dacDbi.CopyContext(destinationBuffer, sourceBuffer, ContextCopyMode.PreserveDestinationFlags, flags: 0); + Assert.Equal(System.HResults.S_OK, hr); + } + + destinationContext.FillFromBuffer(destinationBytes); + Assert.Equal(destinationContext.ContextControlFlags, destinationContext.RawContextFlags); + Assert.True(destinationContext.TryReadRegister(sourceOnlyRegister, out TargetNUInt preservedValue)); + Assert.NotEqual(expectedValue, preservedValue); + + fixed (byte* pDestination = destinationBytes) + fixed (byte* pSource = sourceBytes) + { + ContextBuffer destinationBuffer = new() { pContextBytes = pDestination, contextSize = (uint)destinationBytes.Length }; + ContextBuffer sourceBuffer = new() { pContextBytes = pSource, contextSize = (uint)sourceBytes.Length }; + int hr = dacDbi.CopyContext(destinationBuffer, sourceBuffer, ContextCopyMode.MergeSourceFlags, flags: 0); + Assert.Equal(System.HResults.S_OK, hr); + } + + destinationContext.FillFromBuffer(destinationBytes); + Assert.Equal(sourceContext.FullContextFlags, destinationContext.RawContextFlags); + Assert.True(destinationContext.TryReadRegister(sourceOnlyRegister, out TargetNUInt actualValue)); + Assert.Equal(expectedValue, actualValue); + + TargetNUInt destinationValue = new(0x5678); + destinationContext.RawContextFlags = destinationContext.FullContextFlags; + Assert.True(destinationContext.TrySetRegister(sourceOnlyRegister, destinationValue)); + destinationBytes = destinationContext.GetBytes(); + + fixed (byte* pDestination = destinationBytes) + fixed (byte* pSource = sourceBytes) + { + ContextBuffer destinationBuffer = new() { pContextBytes = pDestination, contextSize = (uint)destinationBytes.Length }; + ContextBuffer sourceBuffer = new() { pContextBytes = pSource, contextSize = (uint)sourceBytes.Length }; + int hr = dacDbi.CopyContext( + destinationBuffer, + sourceBuffer, + ContextCopyMode.UseExplicitFlags, + flags: 0); + Assert.Equal(System.HResults.S_OK, hr); + } + + destinationContext.FillFromBuffer(destinationBytes); + Assert.Equal(0u, destinationContext.RawContextFlags); + Assert.True(destinationContext.TryReadRegister(sourceOnlyRegister, out actualValue)); + Assert.Equal(destinationValue, actualValue); + } + [Theory] [MemberData(nameof(TargetArchitectures_SpRange))] public void CheckContext_WithControlFlag_ValidatesSpRange(MockTarget.Architecture arch, string targetArch, ulong sp, int expectedHr) @@ -861,11 +945,29 @@ public void CheckContext_WithControlFlag_ValidatesSpRange(MockTarget.Architectur fixed (byte* pCtx = bytes) { - int hr = dacDbi.CheckContext(ThreadAddr, pCtx); + ContextBuffer contextBuffer = new() { pContextBytes = pCtx, contextSize = (uint)bytes.Length }; + int hr = dacDbi.CheckContext(ThreadAddr, contextBuffer); Assert.Equal(expectedHr, hr); } } + [Theory] + [MemberData(nameof(TargetArchitectures))] + public void CheckContext_UndersizedBuffer_ReturnsInvalidArgument(MockTarget.Architecture arch, string targetArch) + { + const ulong ThreadAddr = 0x1000; + var (dacDbi, target) = CreateCheckContextDacDbi(arch, targetArch, ThreadAddr, stackBase: 0x8000, stackLimit: 0x4000); + uint contextSize = IPlatformAgnosticContext.GetContextForPlatform(target).Size; + byte[] bytes = new byte[(int)contextSize - 1]; + + fixed (byte* pCtx = bytes) + { + ContextBuffer contextBuffer = new() { pContextBytes = pCtx, contextSize = (uint)bytes.Length }; + int hr = dacDbi.CheckContext(ThreadAddr, contextBuffer); + Assert.Equal(System.HResults.E_INVALIDARG, hr); + } + } + [Theory] [MemberData(nameof(TargetArchitectures))] public void CheckContext_NoControlFlag_SkipsSpCheck(MockTarget.Architecture arch, string targetArch) @@ -887,7 +989,8 @@ public void CheckContext_NoControlFlag_SkipsSpCheck(MockTarget.Architecture arch fixed (byte* pCtx = bytes) { - int hr = dacDbi.CheckContext(ThreadAddr, pCtx); + ContextBuffer contextBuffer = new() { pContextBytes = pCtx, contextSize = (uint)bytes.Length }; + int hr = dacDbi.CheckContext(ThreadAddr, contextBuffer); Assert.Equal(System.HResults.S_OK, hr); }