From e5c4f94c46993732419b1bab015b58acde5da94d Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 25 Mar 2025 16:33:27 -0400 Subject: [PATCH 1/5] Add ICLRContractLocator --- src/shared/inc/clrdata.idl | 14 ++++ src/shared/pal/prebuilt/idl/clrdata_i.cpp | 3 + src/shared/pal/prebuilt/inc/clrdata.h | 93 ++++++++++++++++++++++- 3 files changed, 109 insertions(+), 1 deletion(-) diff --git a/src/shared/inc/clrdata.idl b/src/shared/inc/clrdata.idl index 81b0e0bc7f..cfc57ba212 100644 --- a/src/shared/inc/clrdata.idl +++ b/src/shared/inc/clrdata.idl @@ -211,6 +211,20 @@ interface ICLRRuntimeLocator : IUnknown HRESULT GetRuntimeBase([out] CLRDATA_ADDRESS* baseAddress); }; +[ + object, + local, + uuid(17d5b8c6-34a9-407f-af4f-a930201d4e02), + pointer_default(unique) +] +interface ICLRContractLocator : IUnknown +{ + /* + Returns the address of the runtime's contract descriptor. + */ + HRESULT GetContractDescriptor([out] CLRDATA_ADDRESS* contractAddress); +} + /* * Interface used by the data access services layer to locate metadata * of assemblies in a target. diff --git a/src/shared/pal/prebuilt/idl/clrdata_i.cpp b/src/shared/pal/prebuilt/idl/clrdata_i.cpp index 26d36c133b..d42237a780 100644 --- a/src/shared/pal/prebuilt/idl/clrdata_i.cpp +++ b/src/shared/pal/prebuilt/idl/clrdata_i.cpp @@ -78,6 +78,9 @@ MIDL_DEFINE_GUID(IID, IID_ICLRDataTarget3,0xa5664f95,0x0af4,0x4a1b,0x96,0x0e,0x2 MIDL_DEFINE_GUID(IID, IID_ICLRRuntimeLocator,0xb760bf44,0x9377,0x4597,0x8b,0xe7,0x58,0x08,0x3b,0xdc,0x51,0x46); +MIDL_DEFINE_GUIDE(IID, IID_ICLRContractLocator,0x17d5b8c6,0x34a9,0x407f,0xaf4f,0xa9,0x30,0x20,0x1d,0x4e,0x02); + + MIDL_DEFINE_GUID(IID, IID_ICLRMetadataLocator,0xaa8fa804,0xbc05,0x4642,0xb2,0xc5,0xc3,0x53,0xed,0x22,0xfc,0x63); diff --git a/src/shared/pal/prebuilt/inc/clrdata.h b/src/shared/pal/prebuilt/inc/clrdata.h index 802c34a87b..e8e3cfbb6b 100644 --- a/src/shared/pal/prebuilt/inc/clrdata.h +++ b/src/shared/pal/prebuilt/inc/clrdata.h @@ -77,7 +77,14 @@ typedef interface ICLRDataTarget3 ICLRDataTarget3; #define __ICLRRuntimeLocator_FWD_DEFINED__ typedef interface ICLRRuntimeLocator ICLRRuntimeLocator; -#endif /* __ICLRRuntimeLocator_FWD_DEFINED__ */ + +#endif /* __ICLRContractLocator_FWD_DEFINED__ */ + +#ifndef __ICLRContractLocator_FWD_DEFINED__ +#define __ICLRContractLocator_FWD_DEFINED__ +typedef interface ICLRContractLocator ICLRContractLocator; + +#endif /* __ICLRContractLocator_FWD_DEFINED__ */ #ifndef __ICLRMetadataLocator_FWD_DEFINED__ @@ -919,6 +926,90 @@ EXTERN_C const IID IID_ICLRRuntimeLocator; +#endif /* __ICLRRuntimeLocator_INTERFACE_DEFINED__ */ + + +#ifndef __ICLRContractLocator_INTERFACE_DEFINED__ +#define __ICLRContractLocator_INTERFACE_DEFINED__ + +/* interface ICLRContractLocator */ +/* [unique][uuid][local][object] */ + + +EXTERN_C const IID IID_ICLRContractLocator; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("17d5b8c6-34a9-407f-af4f-a930201d4e02") + ICLRContractLocator : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetContractDescriptor( + /* [out] */ CLRDATA_ADDRESS *contractAddress) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICLRContractLocatorVtbl + { + BEGIN_INTERFACE + + DECLSPEC_XFGVIRT(IUnknown, QueryInterface) + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICLRContractLocator * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + DECLSPEC_XFGVIRT(IUnknown, AddRef) + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICLRContractLocator * This); + + DECLSPEC_XFGVIRT(IUnknown, Release) + ULONG ( STDMETHODCALLTYPE *Release )( + ICLRContractLocator * This); + + DECLSPEC_XFGVIRT(ICLRContractLocator, GetContractDescriptor) + HRESULT ( STDMETHODCALLTYPE *GetContractDescriptor )( + ICLRContractLocator * This, + /* [out] */ CLRDATA_ADDRESS *contractAddress); + + END_INTERFACE + } ICLRRuntimeLocatorVtbl; + + interface ICLRContractLocator + { + CONST_VTBL struct ICLRRuntimeLocatorVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICLRContractLocator_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICLRContractLocator_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICLRContractLocator_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICLRContractLocator_GetContractDescriptor(This,contractAddress) \ + ( (This)->lpVtbl -> GetContractDescriptor(This,contractAddress) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + #endif /* __ICLRRuntimeLocator_INTERFACE_DEFINED__ */ From aade030047203c7ba5813afd31a9c6d248828080 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 25 Mar 2025 16:33:45 -0400 Subject: [PATCH 2/5] implement cDAC loading through managed path --- .../Runtime.cs | 35 ++++++++++++++++- src/SOS/SOS.Hosting/DataTargetWrapper.cs | 38 ++++++++++++++++++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs index 26513a095c..b051ef02af 100644 --- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs +++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs @@ -98,7 +98,40 @@ public Version RuntimeVersion public string GetDacFilePath() { - _dacFilePath ??= GetLibraryPath(DebugLibraryKind.Dac); + if (_dacFilePath is not null) + { + return _dacFilePath; + } + + // Optionally load the DAC through the cDAC + string str = Environment.GetEnvironmentVariable("DOTNET_SOS_LOAD_CDAC"); + int.TryParse(str ?? string.Empty, out int val); + + if (val == 0) + { + _dacFilePath ??= GetLibraryPath(DebugLibraryKind.Dac); + } + else + { + OSPlatform platform = Target.OperatingSystem; + if (platform == OSPlatform.Windows) + { + _dacFilePath ??= GetLocalPath("cdacreader.dll"); + } + else if (platform == OSPlatform.Linux) + { + _dacFilePath ??= GetLocalPath("libcdacreader.so"); + } + else if (platform == OSPlatform.OSX) + { + _dacFilePath ??= GetLocalPath("libcdacreader.dylib"); + } + else + { + Trace.TraceError($"GetCdacFilePath: platform not supported - {platform}"); + } + } + return _dacFilePath; } diff --git a/src/SOS/SOS.Hosting/DataTargetWrapper.cs b/src/SOS/SOS.Hosting/DataTargetWrapper.cs index 3feade5d10..8683292960 100644 --- a/src/SOS/SOS.Hosting/DataTargetWrapper.cs +++ b/src/SOS/SOS.Hosting/DataTargetWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; @@ -17,10 +17,14 @@ internal sealed unsafe class DataTargetWrapper : COMCallableIUnknown private static readonly Guid IID_ICLRDataTarget4 = new("E799DC06-E099-4713-BDD9-906D3CC02CF2"); private static readonly Guid IID_ICLRMetadataLocator = new("aa8fa804-bc05-4642-b2c5-c353ed22fc63"); private static readonly Guid IID_ICLRRuntimeLocator = new("b760bf44-9377-4597-8be7-58083bdc5146"); + private static readonly Guid IID_ICLRContractLocator = new("17d5b8c6-34a9-407f-af4f-a930201d4e02"); // For ClrMD's magic hand shake private const ulong MagicCallbackConstant = 0x43; + // cDAC Contract Descriptor export symbol name + private const string ContractDescriptorExport = "DotNetRuntimeContractDescriptor"; + private readonly IRuntime _runtime; private readonly IContextService _contextService; private readonly ISymbolService _symbolService; @@ -67,6 +71,10 @@ public DataTargetWrapper(IServiceProvider services, IRuntime runtime) builder.AddMethod(new GetRuntimeBaseDelegate(GetRuntimeBase)); builder.Complete(); + builder = AddInterface(IID_ICLRContractLocator, false); + builder.AddMethod(new GetContractDescriptorDelegate(GetContractDescriptor)); + builder.Complete(); + AddRef(); } @@ -350,6 +358,26 @@ private int GetRuntimeBase( #endregion + #region ICLRContractLocator + + private int GetContractDescriptor( + IntPtr self, + out ulong address) + { + address = 0; + IExportSymbols exportSymbols = _runtime.RuntimeModule.Services.GetService(); + if (exportSymbols is not null) + { + if (exportSymbols.TryGetSymbolAddress(ContractDescriptorExport, out address)) + { + return HResult.S_OK; + } + } + return HResult.E_FAIL; + } + + #endregion + #region ICLRDataTarget delegates [UnmanagedFunctionPointer(CallingConvention.Winapi)] @@ -485,5 +513,13 @@ private delegate int GetRuntimeBaseDelegate( [Out] out ulong address); #endregion + + #region ICLRContractLocator delegate + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int GetContractDescriptorDelegate( + [In] IntPtr self, + [Out] out ulong address); + #endregion } } From 26756208d41338b5aea0509e5540e47bb3dd8f62 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 25 Mar 2025 16:35:45 -0400 Subject: [PATCH 3/5] partially implement cDAC loading in native path --- src/SOS/Strike/platform/datatarget.cpp | 16 +++++++++ src/SOS/Strike/platform/datatarget.h | 7 +++- src/SOS/Strike/platform/runtimeimpl.cpp | 47 ++++++++++++++++++++++++- src/SOS/Strike/platform/runtimeimpl.h | 9 +++++ 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/SOS/Strike/platform/datatarget.cpp b/src/SOS/Strike/platform/datatarget.cpp index 63a059b536..2311cb8671 100644 --- a/src/SOS/Strike/platform/datatarget.cpp +++ b/src/SOS/Strike/platform/datatarget.cpp @@ -55,6 +55,12 @@ DataTarget::QueryInterface( AddRef(); return S_OK; } + else if (InterfaceId == IID_ICLRContractLocator) + { + *Interface = (ICLRContractLocator*)this; + AddRef(); + return S_OK; + } else { *Interface = NULL; @@ -385,3 +391,13 @@ DataTarget::GetRuntimeBase( *baseAddress = m_baseAddress; return S_OK; } + +// ICLRContractLocator + +HRESULT STDMETHODCALLTYPE +DataTarget::GetContractDescriptor( + /* out */ CLRDATA_ADDRESS* contractAddress) +{ + // TODO: IMPLEMENT + return E_FAIL; +} diff --git a/src/SOS/Strike/platform/datatarget.h b/src/SOS/Strike/platform/datatarget.h index 369cc290ce..bd65afe6e2 100644 --- a/src/SOS/Strike/platform/datatarget.h +++ b/src/SOS/Strike/platform/datatarget.h @@ -120,4 +120,9 @@ class DataTarget : public ICLRDataTarget2, ICorDebugDataTarget4, ICLRMetadataLoc virtual HRESULT STDMETHODCALLTYPE GetRuntimeBase( /* [out] */ CLRDATA_ADDRESS* baseAddress); -}; \ No newline at end of file + + // ICLRContractLocator + + virtual HRESULT STDMETHODCALLTYPE GetContractDescriptor( + /* [out] */ CLRDATA_ADDRESS* contractAddress); +}; diff --git a/src/SOS/Strike/platform/runtimeimpl.cpp b/src/SOS/Strike/platform/runtimeimpl.cpp index 546c28ddad..8fd798dbd4 100644 --- a/src/SOS/Strike/platform/runtimeimpl.cpp +++ b/src/SOS/Strike/platform/runtimeimpl.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "runtimeimpl.h" #include "datatarget.h" #include "cordebugdatatarget.h" @@ -299,6 +300,43 @@ LPCSTR Runtime::GetDacFilePath() return m_dacFilePath; } +/**********************************************************************\ + * Returns the cDAC module path to the rest of SOS. +\**********************************************************************/ +LPCSTR Runtime::GetCdacFilePath() +{ + if (m_cdacFilePath == nullptr) + { + // No debugger service instance means that SOS is hosted by dotnet-dump, + // which does runtime enumeration in CLRMD. We should never get here. + IDebuggerServices* debuggerServices = GetDebuggerServices(); + if (debuggerServices == nullptr) + { + ExtDbgOut("GetCdacFilePath: GetDebuggerServices returned nullptr\n"); + return nullptr; + } + + // TODO: Signature verification + + LPCSTR directory = GetRuntimeDirectory(); + if (directory != nullptr) + { + std::string dacModulePath(directory); + dacModulePath.append(DIRECTORY_SEPARATOR_STR_A); + dacModulePath.append(GetCdacDllName()); + #ifdef FEATURE_PAL + // If DAC file exists in the runtime directory + if (access(dacModulePath.c_str(), F_OK) == 0) + #endif + { + m_cdacFilePath = _strdup(dacModulePath.c_str()); + } + } + } + return m_cdacFilePath; +} + + /**********************************************************************\ * Returns the DBI module path to the rest of SOS \**********************************************************************/ @@ -427,7 +465,14 @@ HRESULT Runtime::GetClrDataProcess(IXCLRDataProcess** ppClrDataProcess) { *ppClrDataProcess = nullptr; - LPCSTR dacFilePath = GetDacFilePath(); + CLRConfigNoCache enable = CLRConfigNoCache::Get("SOS_LOAD_CDAC"); + DWORD val = 0; + if (enable.IsSet()) + { + enable.TryAsInteger(10, val); + } + + LPCSTR dacFilePath = val != 0 ? GetCdacFilePath() : GetDacFilePath(); if (dacFilePath == nullptr) { return CORDBG_E_NO_IMAGE_AVAILABLE; diff --git a/src/SOS/Strike/platform/runtimeimpl.h b/src/SOS/Strike/platform/runtimeimpl.h index 27b3069407..f4d4a25a2e 100644 --- a/src/SOS/Strike/platform/runtimeimpl.h +++ b/src/SOS/Strike/platform/runtimeimpl.h @@ -105,6 +105,12 @@ inline const char* GetDacDllName() return (g_pRuntime->GetRuntimeConfiguration() == IRuntime::WindowsDesktop) ? DESKTOP_DAC_DLL_NAME_A : NETCORE_DAC_DLL_NAME_A; } +// Returns the cDAC module name +inline const char* GetCdacDllName() +{ + return MAKEDLLNAME_A("cdacreader"); +} + /**********************************************************************\ * Local Runtime interface implementation \**********************************************************************/ @@ -121,6 +127,7 @@ class Runtime : public IRuntime RuntimeInfo* m_runtimeInfo; LPCSTR m_runtimeDirectory; LPCSTR m_dacFilePath; + LPCSTR m_cdacFilePath; LPCSTR m_dbiFilePath; IXCLRDataProcess* m_clrDataProcess; ICorDebugProcess* m_pCorDebugProcess; @@ -150,6 +157,8 @@ class Runtime : public IRuntime LPCSTR GetDacFilePath(); + LPCSTR GetCdacFilePath(); + LPCSTR GetDbiFilePath(); void DisplayStatus(); From 3113923926d4f58f7c0718222de1f76cecb10a8c Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 25 Mar 2025 17:00:55 -0400 Subject: [PATCH 4/5] fix typo --- src/shared/pal/prebuilt/idl/clrdata_i.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/shared/pal/prebuilt/idl/clrdata_i.cpp b/src/shared/pal/prebuilt/idl/clrdata_i.cpp index d42237a780..147be0b8b1 100644 --- a/src/shared/pal/prebuilt/idl/clrdata_i.cpp +++ b/src/shared/pal/prebuilt/idl/clrdata_i.cpp @@ -8,10 +8,10 @@ /* File created by MIDL compiler version 8.01.0626 */ /* Compiler settings for clrdata.idl: - Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0626 + Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0626 protocol : dce , ms_ext, c_ext, robust - error checks: allocation ref bounds_check enum stub_data - VC __declspec() decoration level: + error checks: allocation ref bounds_check enum stub_data + VC __declspec() decoration level: __declspec(uuid()), __declspec(selectany), __declspec(novtable) DECLSPEC_UUID(), MIDL_INTERFACE() */ @@ -22,7 +22,7 @@ #ifdef __cplusplus extern "C"{ -#endif +#endif #include @@ -78,7 +78,7 @@ MIDL_DEFINE_GUID(IID, IID_ICLRDataTarget3,0xa5664f95,0x0af4,0x4a1b,0x96,0x0e,0x2 MIDL_DEFINE_GUID(IID, IID_ICLRRuntimeLocator,0xb760bf44,0x9377,0x4597,0x8b,0xe7,0x58,0x08,0x3b,0xdc,0x51,0x46); -MIDL_DEFINE_GUIDE(IID, IID_ICLRContractLocator,0x17d5b8c6,0x34a9,0x407f,0xaf4f,0xa9,0x30,0x20,0x1d,0x4e,0x02); +MIDL_DEFINE_GUID(IID, IID_ICLRContractLocator,0x17d5b8c6,0x34a9,0x407f,0xaf4f,0xa9,0x30,0x20,0x1d,0x4e,0x02); MIDL_DEFINE_GUID(IID, IID_ICLRMetadataLocator,0xaa8fa804,0xbc05,0x4642,0xb2,0xc5,0xc3,0x53,0xed,0x22,0xfc,0x63); From c0e808a02202ea83be9c141a2dd83cb389ed95b2 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 25 Mar 2025 17:32:53 -0400 Subject: [PATCH 5/5] fix another typo --- src/shared/pal/prebuilt/idl/clrdata_i.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/pal/prebuilt/idl/clrdata_i.cpp b/src/shared/pal/prebuilt/idl/clrdata_i.cpp index 147be0b8b1..a638dd3f75 100644 --- a/src/shared/pal/prebuilt/idl/clrdata_i.cpp +++ b/src/shared/pal/prebuilt/idl/clrdata_i.cpp @@ -78,7 +78,7 @@ MIDL_DEFINE_GUID(IID, IID_ICLRDataTarget3,0xa5664f95,0x0af4,0x4a1b,0x96,0x0e,0x2 MIDL_DEFINE_GUID(IID, IID_ICLRRuntimeLocator,0xb760bf44,0x9377,0x4597,0x8b,0xe7,0x58,0x08,0x3b,0xdc,0x51,0x46); -MIDL_DEFINE_GUID(IID, IID_ICLRContractLocator,0x17d5b8c6,0x34a9,0x407f,0xaf4f,0xa9,0x30,0x20,0x1d,0x4e,0x02); +MIDL_DEFINE_GUID(IID, IID_ICLRContractLocator,0x17d5b8c6,0x34a9,0x407f,0xaf,0x4f,0xa9,0x30,0x20,0x1d,0x4e,0x02); MIDL_DEFINE_GUID(IID, IID_ICLRMetadataLocator,0xaa8fa804,0xbc05,0x4642,0xb2,0xc5,0xc3,0x53,0xed,0x22,0xfc,0x63);