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 0481f2c9bd129e..7469d332124916 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 @@ -72,7 +72,9 @@ public int FlushCache() } public int DacSetTargetConsistencyChecks(Interop.BOOL fEnableAsserts) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.DacSetTargetConsistencyChecks(fEnableAsserts) : HResults.E_NOTIMPL; + => LegacyFallbackHelper.CanFallback() && _legacy is not null + ? _legacy.DacSetTargetConsistencyChecks(fEnableAsserts) + : HResults.S_OK; public int IsLeftSideInitialized(Interop.BOOL* pResult) { 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 66cdc0ca6c121e..cc96dd9fc20abd 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 @@ -8,6 +8,20 @@ namespace Microsoft.Diagnostics.DataContractReader.Legacy; +[GeneratedComInterface] +[Guid("FE06DC28-49FB-4636-A4A3-E80DB4AE116C")] +public unsafe partial interface ICorDebugDataTarget +{ + [PreserveSig] + int GetPlatform(int* pTargetPlatform); + + [PreserveSig] + int ReadVirtual(ulong address, byte* pBuffer, uint bytesRequested, uint* pBytesRead); + + [PreserveSig] + int GetThreadContext(uint threadId, uint contextFlags, uint contextSize, byte* pContext); +} + [StructLayout(LayoutKind.Sequential)] public struct COR_TYPEID { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ICLRData.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ICLRData.cs index 838180d4323edd..2e81d2e00a9f63 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ICLRData.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ICLRData.cs @@ -81,6 +81,14 @@ public unsafe partial interface ICLRDataTarget3 : ICLRDataTarget2 int GetExceptionThreadID(uint* threadID); } +[GeneratedComInterface] +[Guid("b760bf44-9377-4597-8be7-58083bdc5146")] +public unsafe partial interface ICLRRuntimeLocator +{ + [PreserveSig] + int GetRuntimeBase(ulong* baseAddress); +} + [GeneratedComInterface] [Guid("17d5b8c6-34a9-407f-af4f-a930201d4e02")] public unsafe partial interface ICLRContractLocator diff --git a/src/native/managed/cdac/mscordaccore_universal/Entrypoints.cs b/src/native/managed/cdac/mscordaccore_universal/Entrypoints.cs index 26fd7bdec35152..fb7983b7154987 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Entrypoints.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Entrypoints.cs @@ -253,6 +253,53 @@ private static unsafe int CLRDataCreateInstanceWithFallback(Guid* pIID, IntPtr / return CLRDataCreateInstanceImpl(pIID, pLegacyTarget, pLegacyImpl, iface); } + [UnmanagedCallersOnly(EntryPoint = "DacDbiInterfaceInstance")] + private static unsafe int DacDbiInterfaceInstance( + IntPtr /*ICorDebugDataTarget*/ pTarget, + ulong runtimeBase, + 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. + if (pTarget == IntPtr.Zero + || runtimeBase == 0 + || iface == null) + { + return HResults.E_INVALIDARG; + } + + *iface = null; + + 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); + Legacy.DacDbiImpl impl = new(target, legacyObj: null); + *iface = ComInterfaceMarshaller.ConvertToUnmanaged(impl); + return HResults.S_OK; + } + catch (Exception ex) + { + if (iface != null) + *iface = null; + int hr = ex.HResult; + return hr < 0 ? hr : HResults.E_FAIL; + } + } + // Same export name and signature as DAC CLRDataCreateInstance in daccess.cpp [UnmanagedCallersOnly(EntryPoint = "CLRDataCreateInstance")] private static unsafe int CLRDataCreateInstance(Guid* pIID, IntPtr /*ICLRDataTarget*/ pLegacyTarget, void** iface) @@ -385,4 +432,53 @@ private static unsafe int CLRDataCreateInstanceCore(Guid* pIID, IntPtr /*ICLRDat return 0; } + + private static unsafe ContractDescriptorTarget CreateTargetFromCorDebugDataTarget(object targetObject) + { + 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}."); + } + + if (!ContractDescriptorTarget.TryCreate( + contractAddress, + (address, buffer) => + { + fixed (byte* bufferPtr = buffer) + { + uint bytesRead; + return dataTarget.ReadVirtual(address, bufferPtr, (uint)buffer.Length, &bytesRead); + } + }, + (address, buffer) => HResults.E_NOTIMPL, + (threadId, contextFlags, bufferToFill) => + { + fixed (byte* bufferPtr = bufferToFill) + { + return dataTarget.GetThreadContext(threadId, contextFlags, (uint)bufferToFill.Length, bufferPtr); + } + }, + (threadId, context) => HResults.E_NOTIMPL, + (ulong size, out ulong allocatedAddress) => + { + allocatedAddress = 0; + return HResults.E_NOTIMPL; + }, + [Contracts.CoreCLRContracts.Register], + out ContractDescriptorTarget? target)) + { + throw new InvalidOperationException( + $"Failed to create a {nameof(ContractDescriptorTarget)} from the contract descriptor at 0x{contractAddress:x}."); + } + + return target!; + } } diff --git a/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs b/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs index b5ebff64043f28..1f91dd724dc280 100644 --- a/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs @@ -37,6 +37,17 @@ private static (DacDbiImpl DacDbi, TestPlaceholderTarget Target) CreateDacDbiWit return (dacDbi, target); } + [Fact] + public void DacSetTargetConsistencyChecks_Standalone_ReturnsSuccess() + { + MockTarget.Architecture architecture = new() { IsLittleEndian = true, Is64Bit = true }; + TestPlaceholderTarget target = new TestPlaceholderTarget.Builder(architecture).Build(); + DacDbiImpl dacDbi = new(target, legacyObj: null); + + Assert.Equal(System.HResults.S_OK, dacDbi.DacSetTargetConsistencyChecks(Interop.BOOL.TRUE)); + Assert.Equal(System.HResults.S_OK, dacDbi.DacSetTargetConsistencyChecks(Interop.BOOL.FALSE)); + } + [Theory] [ClassData(typeof(MockTarget.StdArch))] public void SetCompilerFlags_BothFlagsSet_EncCapable(MockTarget.Architecture arch)