diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 5f96d98b6e228d..a444706edf0b10 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -32,7 +32,6 @@ #include "conditionalweaktable.h" #ifndef USE_DAC_TABLE_RVA -extern "C" bool TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress); #include #define CAN_USE_CDAC #endif @@ -252,10 +251,16 @@ DLLEXPORT DacDbiInterfaceInstance( ICorDebugDataTarget * pTarget, CORDB_ADDRESS baseAddress, + CLRDATA_ADDRESS contractDescriptorAddress, IDacDbiInterface::IAllocator * pAllocator, IDacDbiInterface::IMetaDataLookup * pMetaDataLookup, IDacDbiInterface ** ppInterface) { +#ifndef CAN_USE_CDAC + // Consumed only by the cDAC path, which is compiled out here. + (void)contractDescriptorAddress; +#endif + // No marshalling is done by the instantiationf function - we just need to setup the infrastructure. // We don't want to warn if this involves creating and accessing undacized data structures, // because it's for the infrastructure, not DACized code itself. @@ -293,8 +298,8 @@ DacDbiInterfaceInstance( DWORD val; if (enable.TryAsInteger(10, val) && val == 1) { - uint64_t contractDescriptorAddr = 0; - if (TryGetSymbol(pDac->m_pTarget, pDac->m_globalBase, "DotNetRuntimeContractDescriptor", &contractDescriptorAddr)) + uint64_t contractDescriptorAddr = contractDescriptorAddress; + if (contractDescriptorAddr != 0) { IUnknown* legacyImpl; HRESULT qiRes = pDac->QueryInterface(IID_IUnknown, (void**)&legacyImpl); diff --git a/src/coreclr/debug/daccess/dacdbiimpl.h b/src/coreclr/debug/daccess/dacdbiimpl.h index ec0aa77dfc66af..e733e666cc497e 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.h +++ b/src/coreclr/debug/daccess/dacdbiimpl.h @@ -24,6 +24,7 @@ DLLEXPORT DacDbiInterfaceInstance( ICorDebugDataTarget * pTarget, CORDB_ADDRESS baseAddress, + CLRDATA_ADDRESS contractDescriptorAddress, IDacDbiInterface::IAllocator * pAllocator, IDacDbiInterface::IMetaDataLookup * pMetaDataLookup, IDacDbiInterface ** ppInterface); diff --git a/src/coreclr/debug/di/process.cpp b/src/coreclr/debug/di/process.cpp index 66d33e4ff16f5c..787c0f874af3b9 100644 --- a/src/coreclr/debug/di/process.cpp +++ b/src/coreclr/debug/di/process.cpp @@ -28,6 +28,13 @@ #include "readonlydatatargetfacade.h" #include "metahost.h" +// Defined in dbgutil; resolves a runtime export address from the data target. +extern "C" bool TryGetSymbol( + ICorDebugDataTarget* dataTarget, + uint64_t baseAddress, + const char* symbolName, + uint64_t* symbolAddress); + // Keep this around for retail debugging. It's very very useful because // it's global state that we can always find, regardless of how many locals the compiler // optimizes away ;) @@ -577,10 +584,21 @@ CordbProcess::CreateDacDbiInterface() IDacDbiInterface::IAllocator * pAllocator = this; IDacDbiInterface::IMetaDataLookup * pMetaDataLookup = this; + // The cDAC needs the address of the runtime's contract descriptor to initialize. The legacy + // DAC only needs it if it wants to route through cdac or to compare results. + CLRDATA_ADDRESS contractDescriptorAddress = 0; + { + uint64_t address = 0; + if (TryGetSymbol(m_pDACDataTarget, m_clrInstanceId, "DotNetRuntimeContractDescriptor", &address)) + { + contractDescriptorAddress = address; + } + } typedef HRESULT (STDAPICALLTYPE * PFN_DacDbiInterfaceInstance)( ICorDebugDataTarget *, CORDB_ADDRESS, + CLRDATA_ADDRESS, IDacDbiInterface::IAllocator *, IDacDbiInterface::IMetaDataLookup *, IDacDbiInterface **); @@ -592,7 +610,7 @@ CordbProcess::CreateDacDbiInterface() ThrowLastError(); } - hrStatus = pfnEntry(m_pDACDataTarget, m_clrInstanceId, pAllocator, pMetaDataLookup, &pInterfacePtr); + hrStatus = pfnEntry(m_pDACDataTarget, m_clrInstanceId, contractDescriptorAddress, pAllocator, pMetaDataLookup, &pInterfacePtr); IfFailThrow(hrStatus); // We now have a resource, pInterfacePtr, that needs to be freed. diff --git a/src/coreclr/debug/di/shimdatatarget.cpp b/src/coreclr/debug/di/shimdatatarget.cpp index a7eca2dacc404c..332fa4d5fca22e 100644 --- a/src/coreclr/debug/di/shimdatatarget.cpp +++ b/src/coreclr/debug/di/shimdatatarget.cpp @@ -15,7 +15,6 @@ #include "shimpriv.h" - // Standard impl of IUnknown::QueryInterface HRESULT STDMETHODCALLTYPE ShimDataTarget::QueryInterface( REFIID InterfaceId, diff --git a/src/coreclr/debug/di/shimdatatarget.h b/src/coreclr/debug/di/shimdatatarget.h index c2e92c217dd6c1..1786671c8d354c 100644 --- a/src/coreclr/debug/di/shimdatatarget.h +++ b/src/coreclr/debug/di/shimdatatarget.h @@ -11,17 +11,16 @@ #ifndef SHIMDATATARGET_H_ #define SHIMDATATARGET_H_ - // Function to invoke for typedef HRESULT (*FPContinueStatusChanged)(void * pUserData, DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus); - //--------------------------------------------------------------------------------------- // Data target for a live process. This is used by Shim. // class ShimDataTarget : public ICorDebugMutableDataTarget, ICorDebugDataTarget4 { public: + ShimDataTarget() {} virtual ~ShimDataTarget() {} // Allow hooking an implementation for ContinueStatusChanged. @@ -129,4 +128,3 @@ HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo, ShimDataTarget ** ppDataTarget); #endif // SHIMDATATARGET_H_ - diff --git a/src/coreclr/debug/di/shimpriv.h b/src/coreclr/debug/di/shimpriv.h index acbf2b5da4b522..fbc1bb0a5b6f1f 100644 --- a/src/coreclr/debug/di/shimpriv.h +++ b/src/coreclr/debug/di/shimpriv.h @@ -1046,4 +1046,3 @@ class ShimFrameEnum : public ICorDebugFrameEnum #endif // SHIMPRIV_H - diff --git a/src/coreclr/dlls/mscordbi/CMakeLists.txt b/src/coreclr/dlls/mscordbi/CMakeLists.txt index 13a0b2ac4aa228..d199a5a48ff04a 100644 --- a/src/coreclr/dlls/mscordbi/CMakeLists.txt +++ b/src/coreclr/dlls/mscordbi/CMakeLists.txt @@ -69,6 +69,7 @@ endif(CLR_CMAKE_HOST_UNIX) set(COREDBI_LIBRARIES debug-pal cordbdi + dbgutil utilcodestaticnohost mdcompiler-dbi mdruntime-dbi diff --git a/src/coreclr/inc/clrdata.idl b/src/coreclr/inc/clrdata.idl index 81b0e0bc7fc653..76c55520315ebe 100644 --- a/src/coreclr/inc/clrdata.idl +++ b/src/coreclr/inc/clrdata.idl @@ -340,4 +340,3 @@ interface ICLRDataEnumMemoryRegions : IUnknown [in] ULONG32 miniDumpFlags, [in] CLRDataEnumMemoryFlags clrFlags); } - diff --git a/src/coreclr/pal/prebuilt/idl/clrdata_i.cpp b/src/coreclr/pal/prebuilt/idl/clrdata_i.cpp index 26d36c133bf46c..a234e6e8eb363f 100644 --- a/src/coreclr/pal/prebuilt/idl/clrdata_i.cpp +++ b/src/coreclr/pal/prebuilt/idl/clrdata_i.cpp @@ -99,4 +99,3 @@ MIDL_DEFINE_GUID(IID, IID_ICLRDataEnumMemoryRegions,0x471c35b4,0x7c2f,0x4ef0,0xa #endif - diff --git a/src/coreclr/pal/prebuilt/inc/clrdata.h b/src/coreclr/pal/prebuilt/inc/clrdata.h index 802c34a87bf0cb..7a575eae0cf932 100644 --- a/src/coreclr/pal/prebuilt/inc/clrdata.h +++ b/src/coreclr/pal/prebuilt/inc/clrdata.h @@ -146,7 +146,7 @@ extern RPC_IF_HANDLE __MIDL_itf_clrdata_0000_0000_v0_0_s_ifspec; #define __ICLRDataTarget_INTERFACE_DEFINED__ /* interface ICLRDataTarget */ -/* [unique][uuid][local][object] */ +/* [unique][uuid][local][object] */ EXTERN_C const IID IID_ICLRDataTarget; @@ -1405,5 +1405,3 @@ EXTERN_C const IID IID_ICLRDataEnumMemoryRegions; #endif #endif - - diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs index e9677b6b92b48e..b834bdd3651b45 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs @@ -12,6 +12,7 @@ public static class CorDbgHResults public const int CORDBG_E_CLASS_NOT_LOADED = unchecked((int)0x80131303); public const int CORDBG_E_FUNCTION_NOT_IL = unchecked((int)0x8013130a); public const int CORDBG_E_TARGET_INCONSISTENT = unchecked((int)0x80131c36); + public const int CORDBG_E_TARGET_READONLY = unchecked((int)0x80131c38); public const int CORDBG_S_NOT_ALL_BITS_SET = unchecked((int)0x00131c13); public const int CORDBG_E_NON_MATCHING_CONTEXT = unchecked((int)0x80131327); public const int CORDBG_E_UNSUPPORTED_DELEGATE = unchecked((int)0x80131c68); 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..e8165a5a50006e 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 @@ -22,6 +22,20 @@ public unsafe partial interface ICorDebugDataTarget int GetThreadContext(uint threadId, uint contextFlags, uint contextSize, byte* pContext); } +[GeneratedComInterface] +[Guid("A1B8A756-3CB6-4CCB-979F-3DF999673A59")] +public unsafe partial interface ICorDebugMutableDataTarget : ICorDebugDataTarget +{ + [PreserveSig] + int WriteVirtual(ulong address, byte* pBuffer, uint bytesRequested); + + [PreserveSig] + int SetThreadContext(uint threadId, uint contextSize, byte* pContext); + + [PreserveSig] + int ContinueStatusChanged(uint threadId, uint continueStatus); +} + [StructLayout(LayoutKind.Sequential)] public struct COR_TYPEID { diff --git a/src/native/managed/cdac/mscordaccore_universal/Entrypoints.cs b/src/native/managed/cdac/mscordaccore_universal/Entrypoints.cs index fb7983b7154987..11b392d635da3a 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Entrypoints.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Entrypoints.cs @@ -12,6 +12,9 @@ internal static class Entrypoints { private const string CDAC = "cdac_reader_"; + // Native CONTEXT and DT_CONTEXT declarations require 16-byte alignment. + private const nuint ContextAlignment = 16; + [UnmanagedCallersOnly(EntryPoint = $"{CDAC}init")] private static unsafe int Init( ulong descriptor, @@ -61,15 +64,14 @@ private static unsafe int Init( { setThreadContextDelegate = (uint threadId, ReadOnlySpan context) => { - const nuint RequiredAlignment = 16; fixed (byte* contextPtr = context) { - if (((nuint)contextPtr & (RequiredAlignment - 1)) == 0) + if (((nuint)contextPtr & (ContextAlignment - 1)) == 0) { return writeThreadContext(threadId, (uint)context.Length, contextPtr, delegateContext); } - byte* alignedBuffer = (byte*)NativeMemory.AlignedAlloc((nuint)context.Length, RequiredAlignment); + byte* alignedBuffer = (byte*)NativeMemory.AlignedAlloc((nuint)context.Length, ContextAlignment); try { context.CopyTo(new Span(alignedBuffer, context.Length)); @@ -102,15 +104,14 @@ private static unsafe int Init( }, (threadId, contextFlags, buffer) => { - const nuint RequiredAlignment = 16; fixed (byte* bufferPtr = buffer) { - if (((nuint)bufferPtr & (RequiredAlignment - 1)) == 0) + if (((nuint)bufferPtr & (ContextAlignment - 1)) == 0) { return readThreadContext(threadId, contextFlags, (uint)buffer.Length, bufferPtr, delegateContext); } - byte* alignedBuffer = (byte*)NativeMemory.AlignedAlloc((nuint)buffer.Length, RequiredAlignment); + byte* alignedBuffer = (byte*)NativeMemory.AlignedAlloc((nuint)buffer.Length, ContextAlignment); NativeMemory.Clear(alignedBuffer, (nuint)buffer.Length); try { @@ -257,15 +258,16 @@ private static unsafe int CLRDataCreateInstanceWithFallback(Guid* pIID, IntPtr / private static unsafe int DacDbiInterfaceInstance( IntPtr /*ICorDebugDataTarget*/ pTarget, ulong runtimeBase, + ulong contractDescriptorAddress, IntPtr /*IDacDbiInterface::IAllocator*/ pAllocator, IntPtr /*IDacDbiInterface::IMetaDataLookup*/ pMetaDataLookup, void** iface) { - // Match the native DAC export (DacDbiInterfaceInstance in dacdbiimpl.cpp), which only - // validates the target, base address, and out parameter. The allocator and metadata - // lookup pointers are not used by the managed implementation, so don't require them. + // The allocator and metadata lookup pointers are not used by the managed implementation, + // so, unlike the native DAC export, they are not required. if (pTarget == IntPtr.Zero || runtimeBase == 0 + || contractDescriptorAddress == 0 || iface == null) { return HResults.E_INVALIDARG; @@ -276,17 +278,7 @@ private static unsafe int DacDbiInterfaceInstance( try { object dataTarget = ComInterfaceMarshaller.ConvertToManaged((void*)pTarget)!; - if (dataTarget is ICLRRuntimeLocator runtimeLocator) - { - ulong locatedRuntimeBase; - int hr = runtimeLocator.GetRuntimeBase(&locatedRuntimeBase); - if (hr < 0) - return hr; - if (locatedRuntimeBase != runtimeBase) - return HResults.E_INVALIDARG; - } - - ContractDescriptorTarget target = CreateTargetFromCorDebugDataTarget(dataTarget); + ContractDescriptorTarget target = CreateTargetFromCorDebugDataTarget(dataTarget, contractDescriptorAddress); Legacy.DacDbiImpl impl = new(target, legacyObj: null); *iface = ComInterfaceMarshaller.ConvertToUnmanaged(impl); return HResults.S_OK; @@ -395,15 +387,14 @@ private static unsafe int CLRDataCreateInstanceCore(Guid* pIID, IntPtr /*ICLRDat }, (threadId, context) => { - const nuint RequiredAlignment = 16; fixed (byte* contextPtr = context) { - if (((nuint)contextPtr & (RequiredAlignment - 1)) == 0) + if (((nuint)contextPtr & (ContextAlignment - 1)) == 0) { return dataTarget.SetThreadContext(threadId, (uint)context.Length, contextPtr); } - byte* alignedBuffer = (byte*)NativeMemory.AlignedAlloc((nuint)context.Length, RequiredAlignment); + byte* alignedBuffer = (byte*)NativeMemory.AlignedAlloc((nuint)context.Length, ContextAlignment); try { context.CopyTo(new Span(alignedBuffer, context.Length)); @@ -433,20 +424,11 @@ private static unsafe int CLRDataCreateInstanceCore(Guid* pIID, IntPtr /*ICLRDat return 0; } - private static unsafe ContractDescriptorTarget CreateTargetFromCorDebugDataTarget(object targetObject) + private static unsafe ContractDescriptorTarget CreateTargetFromCorDebugDataTarget(object targetObject, ulong contractAddress) { ICorDebugDataTarget dataTarget = targetObject as ICorDebugDataTarget ?? throw new ArgumentException( $"Data target does not implement {nameof(ICorDebugDataTarget)}", nameof(targetObject)); - ICLRContractLocator contractLocator = targetObject as ICLRContractLocator ?? throw new ArgumentException( - $"Data target does not implement {nameof(ICLRContractLocator)}", nameof(targetObject)); - - ulong contractAddress; - int hr = contractLocator.GetContractDescriptor(&contractAddress); - if (hr != 0) - { - throw new InvalidOperationException( - $"{nameof(ICLRContractLocator)} failed to fetch the contract descriptor with HRESULT: 0x{hr:x}."); - } + ICorDebugMutableDataTarget? mutableDataTarget = targetObject as ICorDebugMutableDataTarget; if (!ContractDescriptorTarget.TryCreate( contractAddress, @@ -455,18 +437,76 @@ private static unsafe ContractDescriptorTarget CreateTargetFromCorDebugDataTarge fixed (byte* bufferPtr = buffer) { uint bytesRead; - return dataTarget.ReadVirtual(address, bufferPtr, (uint)buffer.Length, &bytesRead); + int hr = dataTarget.ReadVirtual(address, bufferPtr, (uint)buffer.Length, &bytesRead); + return hr >= 0 && bytesRead != (uint)buffer.Length + ? CorDbgHResults.CORDBG_E_READVIRTUAL_FAILURE + : hr; + } + }, + (address, buffer) => + { + if (mutableDataTarget is null) + return CorDbgHResults.CORDBG_E_TARGET_READONLY; + + fixed (byte* bufferPtr = buffer) + { + return mutableDataTarget.WriteVirtual(address, bufferPtr, (uint)buffer.Length); } }, - (address, buffer) => HResults.E_NOTIMPL, (threadId, contextFlags, bufferToFill) => { fixed (byte* bufferPtr = bufferToFill) { - return dataTarget.GetThreadContext(threadId, contextFlags, (uint)bufferToFill.Length, bufferPtr); + if (((nuint)bufferPtr & (ContextAlignment - 1)) == 0) + { + return dataTarget.GetThreadContext(threadId, contextFlags, (uint)bufferToFill.Length, bufferPtr); + } + + byte* alignedBuffer = (byte*)NativeMemory.AlignedAlloc((nuint)bufferToFill.Length, ContextAlignment); + NativeMemory.Clear(alignedBuffer, (nuint)bufferToFill.Length); + try + { + int hr = dataTarget.GetThreadContext( + threadId, + contextFlags, + (uint)bufferToFill.Length, + alignedBuffer); + if (hr >= 0) + { + new ReadOnlySpan(alignedBuffer, bufferToFill.Length).CopyTo(bufferToFill); + } + return hr; + } + finally + { + NativeMemory.AlignedFree(alignedBuffer); + } + } + }, + (threadId, context) => + { + if (mutableDataTarget is null) + return CorDbgHResults.CORDBG_E_TARGET_READONLY; + + fixed (byte* contextPtr = context) + { + if (((nuint)contextPtr & (ContextAlignment - 1)) == 0) + { + return mutableDataTarget.SetThreadContext(threadId, (uint)context.Length, contextPtr); + } + + byte* alignedBuffer = (byte*)NativeMemory.AlignedAlloc((nuint)context.Length, ContextAlignment); + try + { + context.CopyTo(new Span(alignedBuffer, context.Length)); + return mutableDataTarget.SetThreadContext(threadId, (uint)context.Length, alignedBuffer); + } + finally + { + NativeMemory.AlignedFree(alignedBuffer); + } } }, - (threadId, context) => HResults.E_NOTIMPL, (ulong size, out ulong allocatedAddress) => { allocatedAddress = 0;