From 51c1f2beee2ffae2f8341f996c7fcafc05104a42 Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Tue, 9 Jun 2026 21:23:06 -0700
Subject: [PATCH 1/7] Basic packaging changes
---
eng/CdacPackageItems.props | 24 ------
eng/InstallNativePackages.targets | 8 +-
eng/Versions.props | 8 ++
.../GenerateManifest/Directory.Build.props | 13 ++++
.../GenerateManifest/Directory.Build.targets | 3 +
src/SOS/SOS.Package/SOS.Package.csproj | 2 +-
src/SOS/SOS.Package/SOS.Symbol.Package.csproj | 60 ++++++++++-----
.../pkg/Microsoft.Diagnostics.DbgShim.props | 11 +++
src/sos-packaging.props | 74 ++++++++++++++-----
9 files changed, 137 insertions(+), 66 deletions(-)
delete mode 100644 eng/CdacPackageItems.props
create mode 100644 src/SOS/SOS.Package/GenerateManifest/Directory.Build.props
create mode 100644 src/SOS/SOS.Package/GenerateManifest/Directory.Build.targets
diff --git a/eng/CdacPackageItems.props b/eng/CdacPackageItems.props
deleted file mode 100644
index 65e548d057..0000000000
--- a/eng/CdacPackageItems.props
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
- <_cdacPackageVersion Condition="'$(TargetRid)' == 'win-x64'">$(runtimewinx64MicrosoftDotNetCdacTransportVersion)
- <_cdacPackageVersion Condition="'$(TargetRid)' == 'win-arm64'">$(runtimewinarm64MicrosoftDotNetCdacTransportVersion)
- <_cdacPackageVersion Condition="'$(TargetRid)' == 'linux-x64'">$(runtimelinuxx64MicrosoftDotNetCdacTransportVersion)
- <_cdacPackageVersion Condition="'$(TargetRid)' == 'linux-arm64'">$(runtimelinuxarm64MicrosoftDotNetCdacTransportVersion)
- <_cdacPackageVersion Condition="'$(TargetRid)' == 'osx-x64'">$(runtimeosxx64MicrosoftDotNetCdacTransportVersion)
- <_cdacPackageVersion Condition="'$(TargetRid)' == 'osx-arm64'">$(runtimeosxarm64MicrosoftDotNetCdacTransportVersion)
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/eng/InstallNativePackages.targets b/eng/InstallNativePackages.targets
index f8804fd7ab..938fc1d743 100644
--- a/eng/InstallNativePackages.targets
+++ b/eng/InstallNativePackages.targets
@@ -21,10 +21,14 @@
-
+
+
+
+
diff --git a/eng/Versions.props b/eng/Versions.props
index 7f2d6dfefb..c39d6d3c7d 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -14,6 +14,14 @@
true
true
+
+ true
+
+
diff --git a/src/SOS/SOS.Package/GenerateManifest/Directory.Build.targets b/src/SOS/SOS.Package/GenerateManifest/Directory.Build.targets
new file mode 100644
index 0000000000..333e562a8b
--- /dev/null
+++ b/src/SOS/SOS.Package/GenerateManifest/Directory.Build.targets
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/SOS/SOS.Package/SOS.Package.csproj b/src/SOS/SOS.Package/SOS.Package.csproj
index f9688559c9..121655af78 100644
--- a/src/SOS/SOS.Package/SOS.Package.csproj
+++ b/src/SOS/SOS.Package/SOS.Package.csproj
@@ -41,7 +41,7 @@
-
+
diff --git a/src/SOS/SOS.Package/SOS.Symbol.Package.csproj b/src/SOS/SOS.Package/SOS.Symbol.Package.csproj
index dbb9daae4b..eb76c10f46 100644
--- a/src/SOS/SOS.Package/SOS.Symbol.Package.csproj
+++ b/src/SOS/SOS.Package/SOS.Symbol.Package.csproj
@@ -11,6 +11,12 @@
tools
true
false
+
+
+ <_PackAllTargetRids Condition="'$(SingleTargetRidPackage)' != 'true'">true
@@ -19,65 +25,79 @@
$(SOSPackagePathPrefix)/lib
-
+
$(SOSPackagePathPrefix)/win-x64
-
-
-
-
+
$(SOSPackagePathPrefix)/win-x86
-
+
$(SOSPackagePathPrefix)/win-arm64
-
+
$(SOSPackagePathPrefix)/linux-x64
-
+
$(SOSPackagePathPrefix)/linux-x64
-
+
$(SOSPackagePathPrefix)/linux-musl-x64
-
+
$(SOSPackagePathPrefix)/linux-musl-x64
-
+
$(SOSPackagePathPrefix)/linux-arm
-
+
$(SOSPackagePathPrefix)/linux-arm
-
+
$(SOSPackagePathPrefix)/linux-arm64
-
+
$(SOSPackagePathPrefix)/linux-arm64
-
+
$(SOSPackagePathPrefix)/linux-musl-arm64
-
+
$(SOSPackagePathPrefix)/linux-musl-arm64
-
+
$(SOSPackagePathPrefix)/osx-x64
-
+
$(SOSPackagePathPrefix)/osx-x64
-
+
$(SOSPackagePathPrefix)/osx-arm64
-
+
$(SOSPackagePathPrefix)/osx-arm64
diff --git a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.props b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.props
index 9c22d95636..ed22809dbd 100644
--- a/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.props
+++ b/src/dbgshim/pkg/Microsoft.Diagnostics.DbgShim.props
@@ -18,6 +18,17 @@
+
+
diff --git a/src/sos-packaging.props b/src/sos-packaging.props
index 95e0d090ef..04b151341f 100644
--- a/src/sos-packaging.props
+++ b/src/sos-packaging.props
@@ -1,6 +1,17 @@
@@ -8,56 +19,81 @@
true
-
-
+
+
+
-
+
-
+
+
+
+
-
+
+
+
-
+
+
-
-
-
+
+
+
+
+
+
+
+
-
+
+
-
-
-
+
+
+
+
+
-
-
-
+
+
+
+
+
+
-
+
+
+
-
+
+
+
$([MSBuild]::ValueOrDefault('%(FullPath)', '').Replace('linux-musl','linux'))
From 2b5f735de5f173bd7f889051fe38e29ea121cf5d Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Wed, 10 Jun 2026 00:50:51 -0700
Subject: [PATCH 2/7] Change cDAC loading paths and policies
---
.../Host.cs | 4 +-
.../Runtime.cs | 105 +++++++--
.../ServiceManager.cs | 3 +-
.../IHostAssetResolver.cs | 29 +++
.../IRuntime.cs | 8 +-
.../ISettingsService.cs | 17 +-
.../Host/CommandFormatHelpers.cs | 3 +-
.../Host/RuntimesCommand.cs | 24 +-
src/SOS/SOS.Extensions/HostServices.cs | 2 +-
src/SOS/SOS.Hosting/HostAssetResolver.cs | 61 +++++
src/SOS/SOS.Hosting/RuntimeWrapper.cs | 54 +++--
src/SOS/SOS.Hosting/SOS.Hosting.csproj | 1 -
src/SOS/SOS.Hosting/SOSLibrary.cs | 52 +++--
src/SOS/SOS.Hosting/SOSPackageLayout.cs | 107 +++++++++
src/SOS/SOS.InstallHelper/InstallHelper.cs | 46 +---
.../SOS.InstallHelper.csproj | 4 +
src/SOS/Strike/clrma/managedanalysis.cpp | 6 +-
src/SOS/Strike/platform/runtimeimpl.cpp | 219 +++++++++++++++---
src/SOS/Strike/platform/runtimeimpl.h | 15 ++
src/SOS/Strike/util.cpp | 6 +-
20 files changed, 586 insertions(+), 180 deletions(-)
create mode 100644 src/Microsoft.Diagnostics.DebugServices/IHostAssetResolver.cs
create mode 100644 src/SOS/SOS.Hosting/HostAssetResolver.cs
create mode 100644 src/SOS/SOS.Hosting/SOSPackageLayout.cs
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Host.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Host.cs
index 212714d6b5..0b066c5309 100644
--- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Host.cs
+++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Host.cs
@@ -118,9 +118,7 @@ public string GetTempDirectory()
public virtual bool DacSignatureVerificationEnabled { get; set; }
- public bool UseContractReader { get; set; }
-
- public bool ForceUseContractReader { get; set; }
+ public bool? UseCDac { get; set; }
#endregion
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
index 05db19b715..ab236ab87a 100644
--- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
+++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
@@ -20,6 +20,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
public class Runtime : IRuntime, IDisposable
{
private readonly ClrInfo _clrInfo;
+ private readonly IHostAssetResolver _hostAssetResolver;
private readonly ISettingsService _settingsService;
private readonly ISymbolService _symbolService;
private Version _runtimeVersion;
@@ -36,6 +37,7 @@ public Runtime(IServiceProvider services, int id, ClrInfo clrInfo)
Target = services.GetService() ?? throw new DiagnosticsException("Dump or live session target required");
Id = id;
_clrInfo = clrInfo ?? throw new ArgumentNullException(nameof(clrInfo));
+ _hostAssetResolver = services.GetService() ?? throw new ArgumentException("IHostAssetResolver required");
_settingsService = services.GetService() ?? throw new ArgumentException("ISettingsService required");
_symbolService = services.GetService() ?? throw new ArgumentException("ISymbolService required");
@@ -100,27 +102,8 @@ public Version RuntimeVersion
}
}
- public string GetCDacFilePath()
- {
- if (_cdacFilePath is null)
- {
- if (_settingsService.UseContractReader || _settingsService.ForceUseContractReader)
- {
- _cdacFilePath = GetLibraryPath(DebugLibraryKind.CDac);
- }
- }
- return _cdacFilePath;
- }
-
public string GetDacFilePath(out bool verifySignature)
{
- if (_settingsService.ForceUseContractReader)
- {
- // Don't verify signature when using the CDAC and don't change the cached value
- // because it only applies to the regular DAC in _dacFilePath.
- verifySignature = false;
- return GetCDacFilePath();
- }
if (_dacFilePath is null)
{
_dacFilePath = GetLibraryPath(DebugLibraryKind.Dac);
@@ -133,6 +116,26 @@ public string GetDacFilePath(out bool verifySignature)
return _dacFilePath;
}
+ public string GetCDacFilePath()
+ {
+ // ShouldUseCDac() evaluates the cDAC loading policy. When it returns false the caller
+ // uses the in-box DAC from GetDacFilePath instead.
+ if (!ShouldUseCDac())
+ {
+ return null;
+ }
+
+ // The cDAC is bundled with the diagnostics tool and is never downloaded, so a missing
+ // path means it isn't available for this host.
+ _cdacFilePath ??= GetLibraryPath(DebugLibraryKind.CDac);
+ if (_cdacFilePath is null && _settingsService.UseCDac == true)
+ {
+ // The cDAC was explicitly forced but isn't bundled with this tool.
+ throw new DiagnosticsException($"The cDAC was explicitly requested but no matching cDAC is available for this runtime: {RuntimeModule.FileName}");
+ }
+ return _cdacFilePath;
+ }
+
public string GetDbiFilePath()
{
_dbiFilePath ??= GetLibraryPath(DebugLibraryKind.Dbi);
@@ -141,12 +144,59 @@ public string GetDbiFilePath()
#endregion
+ ///
+ /// The minimum runtime major version that supports the cDAC.
+ ///
+ private const int MinCDacRuntimeMajorVersion = 11;
+
+ ///
+ /// Evaluates the cDAC loading policy for this runtime. This is the single place that
+ /// decides whether the diagnostics tool should load the cDAC itself in place of the
+ /// in-box DAC, based on the setting and the
+ /// target runtime version.
+ ///
+ private bool ShouldUseCDac()
+ {
+ return _settingsService.UseCDac switch
+ {
+ false => false, // Never load the cDAC.
+ true => true, // Always use the cDAC, regardless of the runtime version. Availability is
+ // checked by the caller (a missing forced cDAC is a hard error).
+ _ => ShouldUseCDacByDefault(), // No explicit setting: evaluate the default policy.
+ };
+ }
+
+ ///
+ /// The default cDAC policy used when is not set.
+ ///
+ private bool ShouldUseCDacByDefault()
+ {
+ // When DOTNET_ENABLE_CDAC is requested, the in-box (legacy) DAC loads and drives the
+ // cDAC contract reader itself, including its own dac-vs-cdac fallback/comparison
+ // (see CDAC_NO_FALLBACK). Defer to that mechanism rather than loading the cDAC
+ // directly so those scenarios (for example, the runtime's cDAC test pipeline that
+ // points at a freshly built cDAC via -liveruntimedir) keep working.
+ if (Environment.GetEnvironmentVariable("DOTNET_ENABLE_CDAC") == "1"
+ || Environment.GetEnvironmentVariable("COMPlus_ENABLE_CDAC") == "1")
+ {
+ return false;
+ }
+
+ // Default policy: use the cDAC only for runtimes that support it. This needs to be
+ // changed to consider native AOT and singlefile. This is a dummy policy for work
+ // we will offload to dbgshim.
+ return RuntimeVersion is not null && RuntimeVersion.Major >= MinCDacRuntimeMajorVersion;
+ }
+
///
/// Create ClrRuntime instance
///
private ClrRuntime CreateRuntime()
{
- string dacFilePath = GetDacFilePath(out _);
+ // Prefer the cDAC for the ClrMD data-access path when policy selects it; fall back to the in-box DAC.
+ // We ignore the dac verification param since it's already set as part of the CLRMD DataTarget creation
+ // now (it's a global setting to the session).
+ string dacFilePath = GetCDacFilePath() ?? GetDacFilePath(out _);
if (dacFilePath is not null)
{
Trace.TraceInformation($"Creating ClrRuntime #{Id} {dacFilePath}");
@@ -187,6 +237,13 @@ private string GetLibraryPath(DebugLibraryKind kind)
{
break;
}
+ // The cDAC is an analyzer-host artifact shipped inside the diagnostics tool
+ // (next to sos.dll, matching the host's RID). It is not symbol-store indexed
+ // by the target runtime, so never attempt to download it.
+ if (libraryInfo.Kind == DebugLibraryKind.CDac)
+ {
+ continue;
+ }
if (libraryInfo.ArchivedUnder != SymbolProperties.None)
{
libraryPath = DownloadFile(libraryInfo);
@@ -206,7 +263,11 @@ private string GetLocalPath(DebugLibraryInfo libraryInfo)
string localFilePath;
if (libraryInfo.Kind == DebugLibraryKind.CDac)
{
- localFilePath = libraryInfo.FileName;
+ // The cDAC ships next to the native sos module. Ask the host asset resolver where it
+ // is rather than reasoning about layouts here (ClrMD's DebuggingLibraries entry points
+ // at the managed-assembly base directory, so it is ignored). The shared existence
+ // check below verifies the path, so the in-box DAC is used when the cDAC isn't bundled.
+ localFilePath = _hostAssetResolver?.GetCDacPath();
}
else
{
@@ -219,7 +280,7 @@ private string GetLocalPath(DebugLibraryInfo libraryInfo)
localFilePath = Path.Combine(Path.GetDirectoryName(RuntimeModule.FileName), Path.GetFileName(libraryInfo.FileName));
}
}
- if (!File.Exists(localFilePath))
+ if (localFilePath is null || !File.Exists(localFilePath))
{
localFilePath = null;
}
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceManager.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceManager.cs
index ae69ea69ef..2e42bfc3f5 100644
--- a/src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceManager.cs
+++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceManager.cs
@@ -360,8 +360,7 @@ private sealed class ExtensionLoadContext : AssemblyLoadContext
"Microsoft.FileFormats",
"Microsoft.SymbolStore",
"SOS.Extensions",
- "SOS.Hosting",
- "SOS.InstallHelper"
+ "SOS.Hosting"
};
private static readonly string _defaultAssembliesPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
diff --git a/src/Microsoft.Diagnostics.DebugServices/IHostAssetResolver.cs b/src/Microsoft.Diagnostics.DebugServices/IHostAssetResolver.cs
new file mode 100644
index 0000000000..deb660a6b8
--- /dev/null
+++ b/src/Microsoft.Diagnostics.DebugServices/IHostAssetResolver.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+ ///
+ /// Answers questions about where the host's assets live — the native binaries the host ships
+ /// (the native sos module, the cDAC, DiaSymReader, …). The directory of those assets is
+ /// host-specific: a native debugger host supplies it (the SOS hosting layer feeds it from the
+ /// host's sos module location), while in-process hosts (dotnet-dump) derive it from the tool's
+ /// package layout. Runtimes and other services query this resolver instead of reasoning about
+ /// layouts themselves.
+ ///
+ public interface IHostAssetResolver
+ {
+ ///
+ /// The directory containing the host's native binaries (the native sos module and the
+ /// cDAC that ships next to it).
+ ///
+ string NativeBinariesDirectory { get; }
+
+ ///
+ /// The full path to where the cDAC native library (mscordaccore_universal) ships for the
+ /// current host (next to the native sos module). The path is not probed; the caller is
+ /// expected to check existence (the cDAC is not bundled in, for example, release builds).
+ ///
+ string GetCDacPath();
+ }
+}
diff --git a/src/Microsoft.Diagnostics.DebugServices/IRuntime.cs b/src/Microsoft.Diagnostics.DebugServices/IRuntime.cs
index 6bf7f05ef6..9400736d17 100644
--- a/src/Microsoft.Diagnostics.DebugServices/IRuntime.cs
+++ b/src/Microsoft.Diagnostics.DebugServices/IRuntime.cs
@@ -60,13 +60,15 @@ public interface IRuntime
string RuntimeModuleDirectory { get; set; }
///
- /// Returns the DAC file path
+ /// Returns the DAC file path to use for this runtime.
///
- /// returns if the DAC signature should be verified
+ /// returns whether the returned DAC requires signature verification.
string GetDacFilePath(out bool verifySignature);
///
- /// Returns the CDac file path if enabled by global settings
+ /// Returns the cDAC (mscordaccore_universal) file path to use for this runtime, or null
+ /// when the cDAC should not be used (policy disabled or unsupported runtime) or isn't
+ /// available.
///
string GetCDacFilePath();
diff --git a/src/Microsoft.Diagnostics.DebugServices/ISettingsService.cs b/src/Microsoft.Diagnostics.DebugServices/ISettingsService.cs
index 60ad98c338..2e6c42bc32 100644
--- a/src/Microsoft.Diagnostics.DebugServices/ISettingsService.cs
+++ b/src/Microsoft.Diagnostics.DebugServices/ISettingsService.cs
@@ -17,13 +17,16 @@ public interface ISettingsService
bool DacSignatureVerificationEnabled { get; set; }
///
- /// If true, uses the CDAC contract reader if available.
+ /// Controls whether the cDAC is used in place of the in-box DAC:
+ ///
+ /// - null (default): evaluate policy and fall back. The cDAC is used
+ /// when the target runtime supports it and a matching cDAC is available next
+ /// to the diagnostics tool; otherwise the in-box DAC is used.
+ /// - true: always use the cDAC. Runtime construction fails if no
+ /// matching cDAC is available.
+ /// - false: always use the in-box DAC. The cDAC is never loaded.
+ ///
///
- bool UseContractReader { get; set; }
-
- ///
- /// If true, always use the CDAC contract reader even when not requested
- ///
- bool ForceUseContractReader { get; set; }
+ bool? UseCDac { get; set; }
}
}
diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Host/CommandFormatHelpers.cs b/src/Microsoft.Diagnostics.ExtensionCommands/Host/CommandFormatHelpers.cs
index 7b55377c45..15c3ef2860 100644
--- a/src/Microsoft.Diagnostics.ExtensionCommands/Host/CommandFormatHelpers.cs
+++ b/src/Microsoft.Diagnostics.ExtensionCommands/Host/CommandFormatHelpers.cs
@@ -20,8 +20,7 @@ public static void DisplaySettingService(this CommandBase command)
{
ISettingsService settingsService = command.Services.GetService() ?? throw new DiagnosticsException("Settings service required");
command.Console.WriteLine("Settings:");
- command.Console.WriteLine($"-> Use CDAC contract reader: {settingsService.UseContractReader}");
- command.Console.WriteLine($"-> Force use CDAC contract reader: {settingsService.ForceUseContractReader}");
+ command.Console.WriteLine($"-> Use cDAC: {settingsService.UseCDac switch { true => "true", false => "false", _ => "policy (default)" }}");
command.Console.WriteLine($"-> DAC signature verification check enabled: {settingsService.DacSignatureVerificationEnabled}");
}
diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Host/RuntimesCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/Host/RuntimesCommand.cs
index 83c70cef29..53b1aa49f2 100644
--- a/src/Microsoft.Diagnostics.ExtensionCommands/Host/RuntimesCommand.cs
+++ b/src/Microsoft.Diagnostics.ExtensionCommands/Host/RuntimesCommand.cs
@@ -36,11 +36,8 @@ public class RuntimesCommand : CommandBase
[Option(Name = "--all", Aliases = new string[] { "-a" }, Help = "Forces all runtimes to be enumerated.")]
public bool All { get; set; }
- [Option(Name = "--usecdac", Help = "Use the CDAC if available and requested (true/false).")]
- public bool? UseContractReader { get; set; }
-
- [Option(Name = "--forceusecdac", Help = "Always use the CDAC (true/false).")]
- public bool? ForceUseContractReader { get; set; }
+ [Option(Name = "--usecdac", Help = "Controls cDAC usage: true (always), false (never), or policy (default: use for supported runtimes when bundled).")]
+ public string UseCDac { get; set; }
[Option(Name = "--DacSignatureVerification", Aliases = new string[] { "-v" }, Help = "Enforce the proper DAC certificate signing when loaded (true/false).")]
public bool? DacSignatureVerification { get; set; }
@@ -53,16 +50,15 @@ public override void Invoke()
}
bool flush = false;
- if (UseContractReader.HasValue)
- {
- SettingsService.UseContractReader = UseContractReader.Value;
- flush = true;
- }
-
- if (ForceUseContractReader.HasValue)
+ if (UseCDac is not null)
{
- SettingsService.UseContractReader = ForceUseContractReader.Value;
- SettingsService.ForceUseContractReader = ForceUseContractReader.Value;
+ SettingsService.UseCDac = UseCDac.ToLowerInvariant() switch
+ {
+ "true" => true,
+ "false" => false,
+ "policy" or "default" => (bool?)null,
+ _ => throw new DiagnosticsException($"Invalid --usecdac value '{UseCDac}'. Expected true, false, or policy."),
+ };
flush = true;
}
diff --git a/src/SOS/SOS.Extensions/HostServices.cs b/src/SOS/SOS.Extensions/HostServices.cs
index be9e5f89ee..af4c6dc5e9 100644
--- a/src/SOS/SOS.Extensions/HostServices.cs
+++ b/src/SOS/SOS.Extensions/HostServices.cs
@@ -22,7 +22,7 @@ namespace SOS.Extensions
///
/// The extension services Wrapper the native hosts are given
///
- public sealed unsafe class HostServices : COMCallableIUnknown, SOSLibrary.ISOSModule
+ public sealed class HostServices : COMCallableIUnknown, SOSLibrary.ISOSModule
{
private static readonly Guid IID_IHostServices = new("27B2CB8D-BDEE-4CBD-B6EF-75880D76D46F");
diff --git a/src/SOS/SOS.Hosting/HostAssetResolver.cs b/src/SOS/SOS.Hosting/HostAssetResolver.cs
new file mode 100644
index 0000000000..968a105078
--- /dev/null
+++ b/src/SOS/SOS.Hosting/HostAssetResolver.cs
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using System.Runtime.InteropServices;
+using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.Shared;
+
+namespace SOS.Hosting;
+
+///
+/// The host-registered . Resolves assets (native binaries, the
+/// bundled cDAC) to the directory the host actually loaded sos from. When a native debugger host
+/// supplies its sos module location () that location wins;
+/// otherwise the host loaded sos itself (e.g. dotnet-dump) and the directory comes from this
+/// tool's package layout. Either way the cDAC ships in that same directory.
+///
+public sealed class HostAssetResolver : IHostAssetResolver
+{
+ private HostAssetResolver(string nativeBinariesDirectory)
+ {
+ NativeBinariesDirectory = nativeBinariesDirectory;
+ }
+
+ public string NativeBinariesDirectory { get; }
+
+ private static readonly string s_cDACBinaryName = ComputeCDacHostBinaryName();
+
+ private static string ComputeCDacHostBinaryName()
+ {
+ const string baseName = "mscordaccore_universal";
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return baseName + ".dll";
+ }
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return "lib" + baseName + ".dylib";
+ }
+ return "lib" + baseName + ".so";
+ }
+
+ public string GetCDacPath()
+ {
+ return Path.Combine(NativeBinariesDirectory, s_cDACBinaryName);
+ }
+
+ [ServiceExport(Scope = ServiceScope.Global)]
+ public static IHostAssetResolver Create([ServiceImport(Optional = true)] SOSLibrary.ISOSModule sosModule)
+ {
+ // A native debugger host that loaded sos for us is the authoritative source for where
+ // the native binaries (and the cDAC next to them) live. Otherwise fall back to this
+ // tool's package layout.
+ string nativeBinariesDirectory = sosModule?.SOSPath;
+ if (string.IsNullOrEmpty(nativeBinariesDirectory))
+ {
+ nativeBinariesDirectory = SOSPackageLayout.GetNativeBinariesDirectory();
+ }
+ return new HostAssetResolver(nativeBinariesDirectory);
+ }
+}
diff --git a/src/SOS/SOS.Hosting/RuntimeWrapper.cs b/src/SOS/SOS.Hosting/RuntimeWrapper.cs
index ac37d23e01..708161c99d 100644
--- a/src/SOS/SOS.Hosting/RuntimeWrapper.cs
+++ b/src/SOS/SOS.Hosting/RuntimeWrapper.cs
@@ -238,22 +238,22 @@ private int GetClrDataProcess(
return HResult.E_INVALIDARG;
}
*ppClrDataProcess = IntPtr.Zero;
- if ((flags & ClrDataProcessFlags.UseCDac) != 0)
+ // Prefer the cDAC for the data-access (IXCLRDataProcess) path when the runtime policy
+ // selects it (GetCDacFilePath returns non-null); fall back to the in-box DAC otherwise.
+ // The ICorDebug/DBI path (CreateCorDebugProcess) always uses the in-box DAC. The flags
+ // parameter is retained for the native IRuntime contract but no longer consulted here.
+ if (_cdacDataProcess == IntPtr.Zero)
{
- if (_cdacDataProcess == IntPtr.Zero)
+ try
{
- try
- {
- _cdacDataProcess = CreateClrDataProcess(GetCDacHandle());
- }
- catch (Exception ex)
- {
- Trace.TraceError(ex.ToString());
- }
+ _cdacDataProcess = CreateClrDataProcess(GetCDacHandle());
+ }
+ catch (Exception ex)
+ {
+ Trace.TraceError(ex.ToString());
}
- *ppClrDataProcess = _cdacDataProcess;
}
- // Fallback to regular DAC instance if CDac isn't enabled or there where errors creating the instance
+ *ppClrDataProcess = _cdacDataProcess;
if (*ppClrDataProcess == IntPtr.Zero)
{
if (_clrDataProcess == IntPtr.Zero)
@@ -518,7 +518,13 @@ private IntPtr GetDacHandle()
{
if (_dacHandle == IntPtr.Zero)
{
- _dacHandle = GetDacHandle(useCDac: false);
+ string dacFilePath = _runtime.GetDacFilePath(out bool verifySignature);
+ if (dacFilePath == null)
+ {
+ Trace.TraceError($"Could not find matching DAC for this runtime: {_runtime.RuntimeModule.FileName}");
+ return IntPtr.Zero;
+ }
+ _dacHandle = LoadDacLibrary(dacFilePath, verifySignature);
}
return _dacHandle;
}
@@ -527,27 +533,27 @@ private IntPtr GetCDacHandle()
{
if (_cdacHandle == IntPtr.Zero)
{
- _cdacHandle = GetDacHandle(useCDac: true);
+ string cdacFilePath = _runtime.GetCDacFilePath();
+ if (cdacFilePath == null)
+ {
+ // The cDAC isn't selected for this runtime; the caller falls back to the in-box DAC.
+ return IntPtr.Zero;
+ }
+ // The cDAC ships in the signed tool install directory, so it is never signature-verified.
+ _cdacHandle = LoadDacLibrary(cdacFilePath, verifySignature: false);
}
return _cdacHandle;
}
- private IntPtr GetDacHandle(bool useCDac)
+ private static IntPtr LoadDacLibrary(string dacFilePath, bool verifySignature)
{
- bool verifySignature = false;
- string dacFilePath = useCDac ? _runtime.GetCDacFilePath() : _runtime.GetDacFilePath(out verifySignature);
- if (dacFilePath == null)
- {
- Trace.TraceError($"Could not find matching DAC {dacFilePath ?? ""} {useCDac} for this runtime: {_runtime.RuntimeModule.FileName}");
- return IntPtr.Zero;
- }
IntPtr dacHandle = IntPtr.Zero;
IDisposable fileLock = null;
try
{
if (verifySignature)
{
- Trace.TraceInformation($"Verifying DAC signing and cert {dacFilePath} {useCDac}");
+ Trace.TraceInformation($"Verifying DAC signing and cert {dacFilePath}");
// Check if the DAC cert is valid before loading
if (!AuthenticodeUtil.VerifyDacDll(dacFilePath, out fileLock))
@@ -561,7 +567,7 @@ private IntPtr GetDacHandle(bool useCDac)
}
catch (Exception ex) when (ex is DllNotFoundException or BadImageFormatException)
{
- Trace.TraceError($"LoadLibrary({dacFilePath}) {useCDac} FAILED {ex}");
+ Trace.TraceError($"LoadLibrary({dacFilePath}) FAILED {ex}");
return IntPtr.Zero;
}
}
diff --git a/src/SOS/SOS.Hosting/SOS.Hosting.csproj b/src/SOS/SOS.Hosting/SOS.Hosting.csproj
index bb438ecf78..54f1132d8a 100644
--- a/src/SOS/SOS.Hosting/SOS.Hosting.csproj
+++ b/src/SOS/SOS.Hosting/SOS.Hosting.csproj
@@ -17,7 +17,6 @@
-
diff --git a/src/SOS/SOS.Hosting/SOSLibrary.cs b/src/SOS/SOS.Hosting/SOSLibrary.cs
index d7bed24b2e..e9e0f8927d 100644
--- a/src/SOS/SOS.Hosting/SOSLibrary.cs
+++ b/src/SOS/SOS.Hosting/SOSLibrary.cs
@@ -4,10 +4,10 @@
using System;
using System.Diagnostics;
using System.IO;
-using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.Runtime.Utilities;
+using Microsoft.Diagnostics.Shared;
namespace SOS.Hosting
{
@@ -16,21 +16,24 @@ namespace SOS.Hosting
///
public sealed class SOSLibrary : IDisposable
{
- ///
- /// Provides the SOS module path and handle
- ///
- public interface ISOSModule
- {
- ///
- /// The SOS module path
- ///
- string SOSPath { get; }
+ ///
+ /// Provided by a native debugger host to tell SOS hosting where the native sos module was
+ /// loaded from and its handle. This is the source where sos (and the cDAC that
+ /// ships next to it) comes from. When absent (in-process hosts such as dotnet-dump that load
+ /// sos themselves), the directory is derived from the tool's package layout instead.
+ ///
+ public interface ISOSModule
+ {
+ ///
+ /// The directory containing the native sos module (and the cDAC next to it).
+ ///
+ string SOSPath { get; }
- ///
- /// The SOS module handle
- ///
- IntPtr SOSHandle { get; }
- }
+ ///
+ /// The native sos module handle.
+ ///
+ IntPtr SOSHandle { get; }
+ }
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate int SOSCommandDelegate(
@@ -67,12 +70,12 @@ private delegate int SOSInitializeDelegate(
public string SOSPath { get; set; }
[ServiceExport(Scope = ServiceScope.Global)]
- public static SOSLibrary TryCreate(IHost host, [ServiceImport(Optional = true)] ISOSModule sosModule)
+ public static SOSLibrary TryCreate(IHost host, IHostAssetResolver assetResolver, [ServiceImport(Optional = true)] ISOSModule sosModule)
{
SOSLibrary sosLibrary = null;
try
{
- sosLibrary = new SOSLibrary(host, sosModule);
+ sosLibrary = new SOSLibrary(host, assetResolver, sosModule);
sosLibrary.Initialize();
}
catch
@@ -86,10 +89,16 @@ public static SOSLibrary TryCreate(IHost host, [ServiceImport(Optional = true)]
///
/// Create an instance of the hosting class
///
- /// target instance
- /// sos library info or null
- private SOSLibrary(IHost host, ISOSModule sosModule)
+ /// the host instance
+ /// resolves where the native sos binaries live (the host's sos
+ /// directory, or this tool's package layout)
+ /// the host-loaded sos module (handle/ownership), or null when this
+ /// host loads sos itself (dotnet-dump)
+ private SOSLibrary(IHost host, IHostAssetResolver assetResolver, ISOSModule sosModule)
{
+ // The asset resolver is the single source of truth for the native binaries directory; it
+ // already accounts for a host-supplied sos location. ISOSModule, when present, only tells
+ // us the host already loaded sos so we reuse its handle instead of loading/unloading it.
if (sosModule is not null)
{
SOSPath = sosModule.SOSPath;
@@ -97,8 +106,7 @@ private SOSLibrary(IHost host, ISOSModule sosModule)
}
else
{
- string rid = InstallHelper.GetRid();
- SOSPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), rid);
+ SOSPath = assetResolver.NativeBinariesDirectory;
_uninitializeLibrary = true;
}
_hostWrapper = new HostWrapper(host);
diff --git a/src/SOS/SOS.Hosting/SOSPackageLayout.cs b/src/SOS/SOS.Hosting/SOSPackageLayout.cs
new file mode 100644
index 0000000000..7d0c7b9c48
--- /dev/null
+++ b/src/SOS/SOS.Hosting/SOSPackageLayout.cs
@@ -0,0 +1,107 @@
+// 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.IO;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Diagnostics.Shared;
+
+///
+/// Describes the on-disk layout of the diagnostics tool packages (dotnet-dump,
+/// dotnet-sos, SOS.Package, dbgshim) relative to the assembly this type is compiled
+/// into.
+///
+/// A package contains two kinds of binaries that are loaded by the analyzer host:
+///
+/// -
+/// Native binaries that are loaded directly into the analyzer host process and
+/// therefore must match the host's OS and processor architecture (sos.dll /
+/// libsosplugin, mscordaccore_universal, DiaSymReader, …). They live
+/// in a host-specific subfolder of the managed assembly directory (e.g.
+/// tools/<tfm>/any/win-x64/). The folder name is the analyzer host's
+/// runtime identifier — that's the layout the build emits.
+///
+/// -
+/// SOS managed extension assemblies that dotnet-sos install copies into the
+/// SOS install directory (Microsoft.Diagnostics.ExtensionCommands.dll, etc.). They
+/// live in the lib/ sibling subfolder and are not OS/arch specific.
+///
+///
+///
+/// This file is compile-included by SOS.Hosting and SOS.InstallHelper
+/// so they agree on the layout. Because it is compiled into each consumer, the
+/// package base directory is the directory of the consuming assembly.
+///
+internal static class SOSPackageLayout
+{
+ ///
+ /// The directory of the package this type is compiled into. All package-relative
+ /// paths are rooted here.
+ ///
+ private static string s_packageBaseDirectory = ComputePackageBaseDirectory();
+
+ ///
+ /// Returns the directory containing the native binaries for this package,
+ /// targeting (or the current host's architecture
+ /// when is null). Used when the host needs
+ /// to address binaries for a non-current architecture (for example, when
+ /// dotnet-sos install -a arm64 is invoked from an x64 host).
+ ///
+ public static string GetNativeBinariesDirectory(Architecture? architecture = null)
+ => Path.Combine(s_packageBaseDirectory, GetHostNativeBinariesFolderName(architecture));
+
+ ///
+ /// Returns the directory containing the SOS managed extension assemblies for this
+ /// package.
+ ///
+ public static string GetManagedBinariesDirectory()
+ => Path.Combine(s_packageBaseDirectory, "lib");
+
+ private static string ComputePackageBaseDirectory()
+ {
+ string location = typeof(SOSPackageLayout).Assembly.Location;
+ return Path.GetDirectoryName(location)
+ ?? throw new InvalidOperationException($"Cannot resolve package base directory: {typeof(SOSPackageLayout).Assembly.GetName().Name} has no on-disk location.");
+ }
+
+ ///
+ /// The package-relative subfolder for native binaries targeting the current host
+ /// OS and the supplied (or the current process
+ /// architecture when is null).
+ ///
+ private static string GetHostNativeBinariesFolderName(Architecture? architecture)
+ => ComputeHostNativeBinariesFolderName(architecture);
+
+ private static string ComputeHostNativeBinariesFolderName(Architecture? architecture)
+ {
+ string os;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ os = "win";
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ os = "osx";
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ os = "linux";
+ try
+ {
+ if (File.ReadAllText("/etc/os-release").Contains("ID=alpine"))
+ {
+ os = "linux-musl";
+ }
+ }
+ catch (Exception ex) when (ex is FileNotFoundException or DirectoryNotFoundException or IOException)
+ {
+ }
+ }
+ else
+ {
+ throw new PlatformNotSupportedException($"Unsupported operating system: {RuntimeInformation.OSDescription}");
+ }
+ return $"{os}-{(architecture ?? RuntimeInformation.ProcessArchitecture).ToString().ToLowerInvariant()}";
+ }
+}
diff --git a/src/SOS/SOS.InstallHelper/InstallHelper.cs b/src/SOS/SOS.InstallHelper/InstallHelper.cs
index ed72e4b8c9..1f72dfaecb 100644
--- a/src/SOS/SOS.InstallHelper/InstallHelper.cs
+++ b/src/SOS/SOS.InstallHelper/InstallHelper.cs
@@ -5,9 +5,9 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
-using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
+using Microsoft.Diagnostics.Shared;
namespace SOS
{
@@ -55,7 +55,6 @@ public sealed class InstallHelper
public InstallHelper(Action writeLine, Architecture? architecture = null)
{
m_writeLine = writeLine;
- string rid = GetRid(architecture);
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@@ -63,8 +62,9 @@ public InstallHelper(Action writeLine, Architecture? architecture = null
LLDBInitFile = Path.Combine(home, ".lldbinit");
}
InstallLocation = Path.GetFullPath(Path.Combine(home, ".dotnet", "sos"));
- SOSNativeSourcePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), rid);
- SOSManagedSourcePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "lib");
+
+ SOSNativeSourcePath = SOSPackageLayout.GetNativeBinariesDirectory(architecture);
+ SOSManagedSourcePath = SOSPackageLayout.GetManagedBinariesDirectory();
}
///
@@ -306,44 +306,6 @@ private static void RetryOperation(string errorMessage, Action operation)
}
}
- ///
- /// Returns the RID
- ///
- /// architecture to install or if null using the current process architecture
- public static string GetRid(Architecture? architecture = null)
- {
- string os = null;
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- os = "win";
- }
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
- {
- os = "osx";
- }
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
- {
- os = "linux";
- try
- {
- string ostype = File.ReadAllText("/etc/os-release");
- if (ostype.Contains("ID=alpine"))
- {
- os = "linux-musl";
- }
- }
- catch (Exception ex) when (ex is FileNotFoundException or DirectoryNotFoundException or IOException)
- {
- }
- }
- if (os == null)
- {
- throw new SOSInstallerException($"Unsupported operating system {RuntimeInformation.OSDescription}");
- }
- string architectureString = (architecture.HasValue ? architecture : RuntimeInformation.ProcessArchitecture).ToString().ToLowerInvariant();
- return $"{os}-{architectureString}";
- }
-
private static void CopyFiles(string sourcePath, string destinationPath)
{
foreach (string path in Directory.EnumerateDirectories(sourcePath))
diff --git a/src/SOS/SOS.InstallHelper/SOS.InstallHelper.csproj b/src/SOS/SOS.InstallHelper/SOS.InstallHelper.csproj
index 315833e657..7f336113b2 100644
--- a/src/SOS/SOS.InstallHelper/SOS.InstallHelper.csproj
+++ b/src/SOS/SOS.InstallHelper/SOS.InstallHelper.csproj
@@ -10,4 +10,8 @@
true
false
+
+
+
+
diff --git a/src/SOS/Strike/clrma/managedanalysis.cpp b/src/SOS/Strike/clrma/managedanalysis.cpp
index 365cc39c4d..9aee1f0762 100644
--- a/src/SOS/Strike/clrma/managedanalysis.cpp
+++ b/src/SOS/Strike/clrma/managedanalysis.cpp
@@ -7,8 +7,8 @@ extern bool IsWindowsTarget();
extern "C" IXCLRDataProcess * GetClrDataFromDbgEng();
_Use_decl_annotations_
-ClrmaManagedAnalysis::ClrmaManagedAnalysis() :
- m_lRefs(1),
+ClrmaManagedAnalysis::ClrmaManagedAnalysis() :
+ m_lRefs(1),
m_pointerSize(0),
m_fileSeparator(0),
m_processorType(0),
@@ -266,6 +266,8 @@ ClrmaManagedAnalysis::AssociateClient(
}
if (FAILED(hr = runtime->GetClrDataProcess(IRuntime::ClrDataProcessFlags::UseCDac, &m_clrData)))
{
+ TraceInformation("AssociateClient Runtime based DAC retrieval failed with code %08x, falling back to CLRMA\n", hr);
+
m_clrData = GetClrDataFromDbgEng();
if (m_clrData == nullptr)
{
diff --git a/src/SOS/Strike/platform/runtimeimpl.cpp b/src/SOS/Strike/platform/runtimeimpl.cpp
index 3f356315a9..0826075013 100644
--- a/src/SOS/Strike/platform/runtimeimpl.cpp
+++ b/src/SOS/Strike/platform/runtimeimpl.cpp
@@ -27,7 +27,7 @@
#define CORDBG_E_NO_IMAGE_AVAILABLE EMAKEHR(0x1c64)
-typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImpl2FnPtr)(ULONG64 clrInstanceId,
+typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImpl2FnPtr)(ULONG64 clrInstanceId,
IUnknown * pDataTarget,
LPCWSTR pDacModulePath,
CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion,
@@ -35,7 +35,7 @@ typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImpl2FnPtr)(ULONG64 clrInsta
IUnknown ** ppInstance,
CLR_DEBUGGING_PROCESS_FLAGS * pdwFlags);
-typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImplFnPtr)(ULONG64 clrInstanceId,
+typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImplFnPtr)(ULONG64 clrInstanceId,
IUnknown * pDataTarget,
HMODULE hDacDll,
CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion,
@@ -43,7 +43,7 @@ typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImplFnPtr)(ULONG64 clrInstan
IUnknown ** ppInstance,
CLR_DEBUGGING_PROCESS_FLAGS * pdwFlags);
-typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcess2FnPtr)(ULONG64 clrInstanceId,
+typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcess2FnPtr)(ULONG64 clrInstanceId,
IUnknown * pDataTarget,
HMODULE hDacDll,
REFIID riid,
@@ -104,7 +104,7 @@ static HRESULT GetSingleFileInfo(ITarget* target, PULONG pModuleIndex, PULONG64
continue;
}
}
- else
+ else
{
hr = debuggerServices->GetOffsetBySymbol(index, symbolName, &symbolAddress);
if (FAILED(hr)) {
@@ -176,11 +176,11 @@ HRESULT Runtime::CreateInstance(ITarget* target, RuntimeConfiguration configurat
// If the previous operations were successful, create the Runtime instance
if (SUCCEEDED(hr))
{
- if (moduleSize > 0)
+ if (moduleSize > 0)
{
*ppRuntime = new Runtime(target, configuration, moduleIndex, moduleAddress, moduleSize, runtimeInfo);
}
- else
+ else
{
ExtOut("Runtime (%s) module size == 0\n", runtimeModuleName);
hr = E_INVALIDARG;
@@ -204,8 +204,10 @@ Runtime::Runtime(ITarget* target, RuntimeConfiguration configuration, ULONG inde
m_runtimeInfo(runtimeInfo),
m_runtimeDirectory(nullptr),
m_dacFilePath(nullptr),
+ m_cdacFilePath(nullptr),
m_dbiFilePath(nullptr),
m_clrDataProcess(nullptr),
+ m_cdacDataProcess(nullptr),
m_pCorDebugProcess(nullptr)
{
_ASSERTE(index != -1);
@@ -240,6 +242,11 @@ Runtime::~Runtime()
free((void*)m_dacFilePath);
m_dacFilePath = nullptr;
}
+ if (m_cdacFilePath != nullptr)
+ {
+ free((void*)m_cdacFilePath);
+ m_cdacFilePath = nullptr;
+ }
if (m_dbiFilePath != nullptr)
{
free((void*)m_dbiFilePath);
@@ -256,6 +263,11 @@ Runtime::~Runtime()
m_clrDataProcess->Release();
m_clrDataProcess = nullptr;
}
+ if (m_cdacDataProcess != nullptr)
+ {
+ m_cdacDataProcess->Release();
+ m_cdacDataProcess = nullptr;
+ }
}
/**********************************************************************\
@@ -299,6 +311,65 @@ LPCSTR Runtime::GetDacFilePath()
return m_dacFilePath;
}
+#ifndef FEATURE_PAL
+extern HMODULE g_hInstance;
+#else
+// A file-local anchor used to resolve the directory of the SOS module via dladdr.
+static void CDacModuleAnchor() {}
+#endif
+
+/**********************************************************************\
+ * Returns the cDAC (mscordaccore_universal) module path bundled next to
+ * sos in the diagnostics tool package, or nullptr when it isn't present.
+ * The cDAC is shipped with the tool and is never downloaded.
+\**********************************************************************/
+LPCSTR Runtime::GetCDacFilePath()
+{
+ if (m_cdacFilePath == nullptr)
+ {
+ // The cDAC lives in the same directory as the loaded sos module (the host's platform
+ // subfolder of the package), not in the target runtime directory.
+ ArrayHolder szSOSModulePath = new char[MAX_LONGPATH + 1];
+#ifdef FEATURE_PAL
+ Dl_info info;
+ if (dladdr((void*)&CDacModuleAnchor, &info) == 0 || info.dli_fname == nullptr)
+ {
+ ExtDbgOut("GetCDacFilePath: dladdr failed to locate the sos module\n");
+ return nullptr;
+ }
+ strcpy_s(szSOSModulePath.GetPtr(), MAX_LONGPATH, info.dli_fname);
+#else
+ if (GetModuleFileNameA(g_hInstance, szSOSModulePath, MAX_LONGPATH) == 0)
+ {
+ ExtDbgOut("GetCDacFilePath: GetModuleFileNameA failed %08x\n", HRESULT_FROM_WIN32(GetLastError()));
+ return nullptr;
+ }
+#endif
+ std::string cdacModulePath(szSOSModulePath.GetPtr());
+ size_t lastSlash = cdacModulePath.rfind(DIRECTORY_SEPARATOR_CHAR_A);
+ if (lastSlash == std::string::npos)
+ {
+ ExtDbgOut("GetCDacFilePath: failed to parse sos module directory from %s\n", cdacModulePath.c_str());
+ return nullptr;
+ }
+ cdacModulePath.erase(lastSlash + 1);
+ cdacModulePath.append(NETCORE_CDAC_DLL_NAME_A);
+
+ // The cDAC must exist on disk next to sos; it is never downloaded. When it is not
+ // bundled (for example, RIDs without a cDAC), callers fall back to the in-box DAC.
+#ifdef FEATURE_PAL
+ bool exists = access(cdacModulePath.c_str(), F_OK) == 0;
+#else
+ bool exists = GetFileAttributesA(cdacModulePath.c_str()) != INVALID_FILE_ATTRIBUTES;
+#endif
+ if (exists)
+ {
+ m_cdacFilePath = _strdup(cdacModulePath.c_str());
+ }
+ }
+ return m_cdacFilePath;
+}
+
/**********************************************************************\
* Returns the DBI module path to the rest of SOS
\**********************************************************************/
@@ -333,6 +404,10 @@ void Runtime::Flush()
{
m_clrDataProcess->Flush();
}
+ if (m_cdacDataProcess != nullptr)
+ {
+ m_cdacDataProcess->Flush();
+ }
}
//----------------------------------------------------------------------------
@@ -360,7 +435,7 @@ HRESULT Runtime::QueryInterface(
ULONG Runtime::AddRef()
{
- LONG ref = InterlockedIncrement(&m_ref);
+ LONG ref = InterlockedIncrement(&m_ref);
return ref;
}
@@ -423,6 +498,28 @@ LPCSTR Runtime::GetRuntimeDirectory()
\**********************************************************************/
HRESULT Runtime::GetClrDataProcess(ClrDataProcessFlags flags, IXCLRDataProcess** ppClrDataProcess)
{
+ // When the cDAC is requested (e.g. by CLRMA or the main SOS DAC-load path) and the policy
+ // selects it (supported runtime version, DOTNET_ENABLE_CDAC not deferring to the in-box DAC),
+ // prefer it for the data-access path and fall back to the in-box DAC if it isn't bundled or
+ // fails to initialize.
+ if ((flags & ClrDataProcessFlags::UseCDac) != 0 && ShouldUseCDac())
+ {
+ if (m_cdacDataProcess == nullptr)
+ {
+ LPCSTR cdacFilePath = GetCDacFilePath();
+ if (cdacFilePath != nullptr)
+ {
+ m_cdacDataProcess = CreateClrDataProcessInstance(cdacFilePath);
+ }
+ }
+ if (m_cdacDataProcess != nullptr)
+ {
+ *ppClrDataProcess = m_cdacDataProcess;
+ return S_OK;
+ }
+ // Fall through to the DAC.
+ }
+
if (m_clrDataProcess == nullptr)
{
*ppClrDataProcess = nullptr;
@@ -432,39 +529,97 @@ HRESULT Runtime::GetClrDataProcess(ClrDataProcessFlags flags, IXCLRDataProcess**
{
return CORDBG_E_NO_IMAGE_AVAILABLE;
}
- HMODULE hdac = LoadLibraryA(dacFilePath);
- if (hdac == NULL)
+ m_clrDataProcess = CreateClrDataProcessInstance(dacFilePath);
+ if (m_clrDataProcess == nullptr)
{
- ExtDbgOut("LoadLibraryA(%s) FAILED %08x\n", dacFilePath, HRESULT_FROM_WIN32(GetLastError()));
return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
}
- PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance");
- if (pfnCLRDataCreateInstance == nullptr)
- {
- FreeLibrary(hdac);
- return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
- }
- ICLRDataTarget *target = new DataTarget(GetModuleAddress());
- HRESULT hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&m_clrDataProcess);
- if (FAILED(hr))
- {
- m_clrDataProcess = nullptr;
- return hr;
- }
- ULONG32 flags = 0;
- m_clrDataProcess->GetOtherNotificationFlags(&flags);
- flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD | CLRDATA_NOTIFY_ON_EXCEPTION);
- m_clrDataProcess->SetOtherNotificationFlags(flags);
}
*ppClrDataProcess = m_clrDataProcess;
return S_OK;
}
+// The minimum runtime major version that supports the cDAC.
+static const DWORD MinCDacRuntimeMajorVersion = 11;
+
+// Returns true if the named environment variable is set to "1".
+static bool IsEnvironmentVariableSetToOne(const char* name)
+{
+ char buffer[16];
+ DWORD length = GetEnvironmentVariableA(name, buffer, ARRAY_SIZE(buffer));
+ return length > 0 && length < ARRAY_SIZE(buffer) && strcmp(buffer, "1") == 0;
+}
+
+/**********************************************************************\
+ * Evaluates the cDAC loading policy for this runtime.
+\**********************************************************************/
+bool Runtime::ShouldUseCDac()
+{
+ // When DOTNET_ENABLE_CDAC is requested, the in-box (legacy) DAC loads and drives the cDAC
+ // contract reader itself (including its own dac-vs-cdac fallback/comparison). Defer to that
+ // mechanism rather than loading the cDAC directly so those scenarios keep working.
+ if (IsEnvironmentVariableSetToOne("DOTNET_ENABLE_CDAC") || IsEnvironmentVariableSetToOne("COMPlus_ENABLE_CDAC"))
+ {
+ return false;
+ }
+
+ // Use the cDAC only for runtimes that support it (.NET 11+).
+ VS_FIXEDFILEINFO fileInfo;
+ if (FAILED(GetEEVersion(&fileInfo, nullptr, 0)))
+ {
+ return false;
+ }
+ DWORD majorVersion = (fileInfo.dwFileVersionMS >> 16) & 0xFFFF;
+ return majorVersion >= MinCDacRuntimeMajorVersion;
+}
+
+/**********************************************************************\
+ * Loads the given DAC/cDAC module and creates an IXCLRDataProcess from it.
+ * Returns nullptr on failure.
+\**********************************************************************/
+IXCLRDataProcess* Runtime::CreateClrDataProcessInstance(LPCSTR dacFilePath)
+{
+ HMODULE hdac = LoadLibraryA(dacFilePath);
+ if (hdac == NULL)
+ {
+ ExtDbgOut("LoadLibraryA(%s) FAILED %08x\n", dacFilePath, HRESULT_FROM_WIN32(GetLastError()));
+ return nullptr;
+ }
+ PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance");
+ if (pfnCLRDataCreateInstance == nullptr)
+ {
+ FreeLibrary(hdac);
+ return nullptr;
+ }
+ ICLRDataTarget *target = new DataTarget(GetModuleAddress());
+ IXCLRDataProcess* clrDataProcess = nullptr;
+ HRESULT hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&clrDataProcess);
+ if (FAILED(hr))
+ {
+ // CLRDataCreateInstance only AddRefs the data target on success; release our reference
+ // (created at ref count 0) to delete it, and unload the module.
+ target->AddRef();
+ target->Release();
+ FreeLibrary(hdac);
+ return nullptr;
+ }
+ // Best-effort: enable module load/unload and exception notifications so SOS flushes its caches
+ // across stop states when the cDAC/DAC is used against a live target. Ignore failures (the
+ // cDAC may not implement these yet).
+ ULONG32 notificationFlags = 0;
+ if (SUCCEEDED(clrDataProcess->GetOtherNotificationFlags(¬ificationFlags)))
+ {
+ notificationFlags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD | CLRDATA_NOTIFY_ON_EXCEPTION);
+ clrDataProcess->SetOtherNotificationFlags(notificationFlags);
+ }
+ return clrDataProcess;
+}
+
/**********************************************************************\
- * Loads and initializes the public ICorDebug interfaces. This should be
- * called at least once per debugger stop state to ensure that the
+ * Loads and initializes the public ICorDebug interfaces. This should be
+ * called at least once per debugger stop state to ensure that the
* interface is available and that it doesn't hold stale data. Calling
- * it more than once isn't an error, but does have perf overhead from
+ * it more than once isn't an error, but does have perf overhead from
* needlessly flushing memory caches.
\**********************************************************************/
HRESULT Runtime::GetCorDebugInterface(ICorDebugProcess** ppCorDebugProcess)
@@ -516,7 +671,7 @@ HRESULT Runtime::GetCorDebugInterface(ICorDebugProcess** ppCorDebugProcess)
return hr;
}
const char* dbiFilePath = GetDbiFilePath();
- if (dbiFilePath == nullptr)
+ if (dbiFilePath == nullptr)
{
ExtErr("Could not find matching DBI\n");
return CORDBG_E_NO_IMAGE_AVAILABLE;
@@ -553,7 +708,7 @@ HRESULT Runtime::GetCorDebugInterface(ICorDebugProcess** ppCorDebugProcess)
}
#ifdef FEATURE_PAL
// On Linux/MacOS the DAC module handle needs to be re-created using the DAC PAL instance
- // before being passed to DBI's OpenVirtualProcess* implementation. The DBI and DAC share
+ // before being passed to DBI's OpenVirtualProcess* implementation. The DBI and DAC share
// the same PAL where dbgshim has it's own.
LoadLibraryWFnPtr loadLibraryWFn = (LoadLibraryWFnPtr)GetProcAddress(hDac, "LoadLibraryW");
if (loadLibraryWFn != nullptr)
diff --git a/src/SOS/Strike/platform/runtimeimpl.h b/src/SOS/Strike/platform/runtimeimpl.h
index 6d8994eaeb..2d6e664327 100644
--- a/src/SOS/Strike/platform/runtimeimpl.h
+++ b/src/SOS/Strike/platform/runtimeimpl.h
@@ -38,6 +38,10 @@
#define DESKTOP_DAC_DLL_NAME_W MAKEDLLNAME_W(W("mscordacwks"))
#define DESKTOP_DAC_DLL_NAME_A MAKEDLLNAME_A("mscordacwks")
+// The cDAC (mscordaccore_universal) ships next to sos in the diagnostics tool package.
+// MAKEDLLNAME_A applies the platform-specific prefix/suffix (e.g. .dll, lib*.so, lib*.dylib).
+#define NETCORE_CDAC_DLL_NAME_A MAKEDLLNAME_A("mscordaccore_universal")
+
extern IRuntime* g_pRuntime;
// Returns the runtime configuration as a string
@@ -121,8 +125,10 @@ class Runtime : public IRuntime
RuntimeInfo* m_runtimeInfo;
LPCSTR m_runtimeDirectory;
LPCSTR m_dacFilePath;
+ LPCSTR m_cdacFilePath;
LPCSTR m_dbiFilePath;
IXCLRDataProcess* m_clrDataProcess;
+ IXCLRDataProcess* m_cdacDataProcess;
ICorDebugProcess* m_pCorDebugProcess;
Runtime(ITarget* target, RuntimeConfiguration configuration, ULONG index, ULONG64 address, ULONG64 size, RuntimeInfo* runtimeInfo);
@@ -143,6 +149,13 @@ class Runtime : public IRuntime
}
}
+ // Loads the given DAC/cDAC module and creates an IXCLRDataProcess from it (nullptr on failure).
+ IXCLRDataProcess* CreateClrDataProcessInstance(LPCSTR dacFilePath);
+
+ // Evaluates the cDAC loading policy: cDAC is used for supported runtimes (.NET 11+) unless
+ // DOTNET_ENABLE_CDAC requests that the in-box DAC drive the cDAC contract reader itself.
+ bool ShouldUseCDac();
+
public:
static HRESULT CreateInstance(ITarget* target, RuntimeConfiguration configuration, Runtime** ppRuntime);
@@ -150,6 +163,8 @@ class Runtime : public IRuntime
LPCSTR GetDacFilePath();
+ LPCSTR GetCDacFilePath();
+
LPCSTR GetDbiFilePath();
void DisplayStatus();
diff --git a/src/SOS/Strike/util.cpp b/src/SOS/Strike/util.cpp
index 8ead614499..5b7e172097 100644
--- a/src/SOS/Strike/util.cpp
+++ b/src/SOS/Strike/util.cpp
@@ -3795,7 +3795,7 @@ class SOSDacInterface15Simulator : public ISOSDacInterface15
HRESULT LoadClrDebugDll(void)
{
_ASSERTE(g_pRuntime != nullptr);
- HRESULT hr = g_pRuntime->GetClrDataProcess(IRuntime::ClrDataProcessFlags::None, &g_clrData);
+ HRESULT hr = g_pRuntime->GetClrDataProcess(IRuntime::ClrDataProcessFlags::UseCDac, &g_clrData);
if (FAILED(hr))
{
g_clrData = GetClrDataFromDbgEng();
@@ -5322,7 +5322,7 @@ WString DmlEscape(const WString &input)
const WCHAR *str = input.c_str();
size_t len = input.length();
WString result;
-
+
for (size_t i = 0; i < len; i++)
{
// Ampersand must be escaped FIRST to avoid double-escaping
@@ -5346,7 +5346,7 @@ WString DmlEscape(const WString &input)
result += temp;
}
}
-
+
return result;
}
From ecdd2b407f33f9f8f6980da7a081f9de5e77cd84 Mon Sep 17 00:00:00 2001
From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Date: Thu, 11 Jun 2026 04:17:39 -0700
Subject: [PATCH 3/7] Implement ICLRContractLocator in native runtime and
signing override in CLRMD for cdac + dac scenarios
---
.../Runtime.cs | 5 ++-
.../RuntimeProvider.cs | 27 ++++++++++++-
src/SOS/Strike/platform/datatarget.cpp | 32 ++++++++++++++-
src/SOS/Strike/platform/datatarget.h | 10 ++++-
src/SOS/Strike/platform/runtimeimpl.cpp | 39 +++++++++++++++++--
src/SOS/Strike/platform/runtimeimpl.h | 8 +++-
6 files changed, 110 insertions(+), 11 deletions(-)
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
index ab236ab87a..1739fce5dc 100644
--- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
+++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
@@ -37,7 +37,10 @@ public Runtime(IServiceProvider services, int id, ClrInfo clrInfo)
Target = services.GetService() ?? throw new DiagnosticsException("Dump or live session target required");
Id = id;
_clrInfo = clrInfo ?? throw new ArgumentNullException(nameof(clrInfo));
- _hostAssetResolver = services.GetService() ?? throw new ArgumentException("IHostAssetResolver required");
+ // IHostAssetResolver is optional: it is registered by the SOS hosting layer to locate
+ // the bundled cDAC. When absent (hosts without SOS.Hosting, e.g. some test hosts), cDAC
+ // resolution returns null and the in-box DAC is used.
+ _hostAssetResolver = services.GetService();
_settingsService = services.GetService() ?? throw new ArgumentException("ISettingsService required");
_symbolService = services.GetService() ?? throw new ArgumentException("ISymbolService required");
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/RuntimeProvider.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/RuntimeProvider.cs
index 00eb74c6cd..84572786c1 100644
--- a/src/Microsoft.Diagnostics.DebugServices.Implementation/RuntimeProvider.cs
+++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/RuntimeProvider.cs
@@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
using Microsoft.Diagnostics.Runtime;
namespace Microsoft.Diagnostics.DebugServices.Implementation
@@ -33,10 +35,33 @@ public IEnumerable EnumerateRuntimes(int startingRuntimeId, RuntimeEnu
// not flushed when the Target/RuntimeService is flushed; they are all disposed and the list cleared. They are
// all re-created the next time the IRuntime or ClrRuntime instance is queried.
ISettingsService settingsService = _services.GetService();
+ bool verifyDac = settingsService?.DacSignatureVerificationEnabled ?? true;
+
+ // The cDAC (mscordaccore_universal) ships inside the (signed) diagnostics tool package and
+ // carries no individual DAC signature, so it cannot satisfy ClrMD's signature check. Trust it
+ // the same way the native and SOS-hosting cDAC load paths do (load without verification), while
+ // still verifying the in-box DAC. We trust ONLY the exact cDAC path the host resolver provides
+ // (the bundled binary next to sos); matching by file name alone would let a name-hijacked DLL
+ // loaded from elsewhere (target runtime dir, symbol cache, ...) bypass verification.
+ string trustedCDacPath = _services.GetService()?.GetCDacPath();
+ string normalizedTrustedCDacPath = string.IsNullOrEmpty(trustedCDacPath) ? null : Path.GetFullPath(trustedCDacPath);
+ StringComparison pathComparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
+
DataTarget dataTarget = new(_services.GetService(), new DataTargetOptions()
{
ForceCompleteRuntimeEnumeration = (flags & RuntimeEnumerationFlags.All) != 0,
- VerifyDacOnWindows = settingsService?.DacSignatureVerificationEnabled ?? true
+ VerifyDacOnWindows = verifyDac,
+ // Takes priority over VerifyDacOnWindows: skip verification only for the exact bundled cDAC.
+ DacSignatureVerificationOverride = (dacFilePath) =>
+ {
+ if (normalizedTrustedCDacPath is not null
+ && !string.IsNullOrEmpty(dacFilePath)
+ && string.Equals(Path.GetFullPath(dacFilePath), normalizedTrustedCDacPath, pathComparison))
+ {
+ return false;
+ }
+ return verifyDac;
+ }
});
for (int i = 0; i < dataTarget.ClrVersions.Length; i++)
{
diff --git a/src/SOS/Strike/platform/datatarget.cpp b/src/SOS/Strike/platform/datatarget.cpp
index 63a059b536..b0068fb8e6 100644
--- a/src/SOS/Strike/platform/datatarget.cpp
+++ b/src/SOS/Strike/platform/datatarget.cpp
@@ -11,9 +11,10 @@
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
-DataTarget::DataTarget(ULONG64 baseAddress) :
+DataTarget::DataTarget(ULONG64 baseAddress, ULONG64 contractDescriptorAddress) :
m_ref(0),
- m_baseAddress(baseAddress)
+ m_baseAddress(baseAddress),
+ m_contractDescriptorAddress(contractDescriptorAddress)
{
}
@@ -55,6 +56,12 @@ DataTarget::QueryInterface(
AddRef();
return S_OK;
}
+ else if (InterfaceId == IID_ICLRContractLocator)
+ {
+ *Interface = (ICLRContractLocator*)this;
+ AddRef();
+ return S_OK;
+ }
else
{
*Interface = NULL;
@@ -385,3 +392,24 @@ DataTarget::GetRuntimeBase(
*baseAddress = m_baseAddress;
return S_OK;
}
+
+// ICLRContractLocator
+
+HRESULT STDMETHODCALLTYPE
+DataTarget::GetContractDescriptor(
+ /* [out] */ CLRDATA_ADDRESS* contractAddress)
+{
+ if (contractAddress == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+ // The contract descriptor address is resolved by the runtime (which knows the target OS, the
+ // runtime module index, and has a memory-reading callback) and handed to us at construction.
+ // The cDAC requires it via ICLRContractLocator; the legacy DAC never queries this interface.
+ if (m_contractDescriptorAddress == 0)
+ {
+ return E_FAIL;
+ }
+ *contractAddress = m_contractDescriptorAddress;
+ return S_OK;
+}
diff --git a/src/SOS/Strike/platform/datatarget.h b/src/SOS/Strike/platform/datatarget.h
index 369cc290ce..418f8f0e2d 100644
--- a/src/SOS/Strike/platform/datatarget.h
+++ b/src/SOS/Strike/platform/datatarget.h
@@ -1,14 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-class DataTarget : public ICLRDataTarget2, ICorDebugDataTarget4, ICLRMetadataLocator, ICLRRuntimeLocator
+class DataTarget : public ICLRDataTarget2, ICorDebugDataTarget4, ICLRMetadataLocator, ICLRRuntimeLocator, ICLRContractLocator
{
private:
LONG m_ref; // Reference count.
ULONG64 m_baseAddress; // Runtime base address
+ ULONG64 m_contractDescriptorAddress; // cDAC contract descriptor address (0 if not applicable/resolved)
public:
- DataTarget(ULONG64 baseAddress);
+ DataTarget(ULONG64 baseAddress, ULONG64 contractDescriptorAddress = 0);
virtual ~DataTarget() {}
// IUnknown.
@@ -120,4 +121,9 @@ class DataTarget : public ICLRDataTarget2, ICorDebugDataTarget4, ICLRMetadataLoc
virtual HRESULT STDMETHODCALLTYPE GetRuntimeBase(
/* [out] */ CLRDATA_ADDRESS* baseAddress);
+
+ // ICLRContractLocator
+
+ virtual HRESULT STDMETHODCALLTYPE GetContractDescriptor(
+ /* [out] */ CLRDATA_ADDRESS* contractAddress);
};
\ No newline at end of file
diff --git a/src/SOS/Strike/platform/runtimeimpl.cpp b/src/SOS/Strike/platform/runtimeimpl.cpp
index 0826075013..b51b9cdc68 100644
--- a/src/SOS/Strike/platform/runtimeimpl.cpp
+++ b/src/SOS/Strike/platform/runtimeimpl.cpp
@@ -509,7 +509,7 @@ HRESULT Runtime::GetClrDataProcess(ClrDataProcessFlags flags, IXCLRDataProcess**
LPCSTR cdacFilePath = GetCDacFilePath();
if (cdacFilePath != nullptr)
{
- m_cdacDataProcess = CreateClrDataProcessInstance(cdacFilePath);
+ m_cdacDataProcess = CreateClrDataProcessInstance(cdacFilePath, GetContractDescriptorAddress());
}
}
if (m_cdacDataProcess != nullptr)
@@ -529,7 +529,7 @@ HRESULT Runtime::GetClrDataProcess(ClrDataProcessFlags flags, IXCLRDataProcess**
{
return CORDBG_E_NO_IMAGE_AVAILABLE;
}
- m_clrDataProcess = CreateClrDataProcessInstance(dacFilePath);
+ m_clrDataProcess = CreateClrDataProcessInstance(dacFilePath, 0);
if (m_clrDataProcess == nullptr)
{
return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
@@ -577,7 +577,7 @@ bool Runtime::ShouldUseCDac()
* Loads the given DAC/cDAC module and creates an IXCLRDataProcess from it.
* Returns nullptr on failure.
\**********************************************************************/
-IXCLRDataProcess* Runtime::CreateClrDataProcessInstance(LPCSTR dacFilePath)
+IXCLRDataProcess* Runtime::CreateClrDataProcessInstance(LPCSTR dacFilePath, ULONG64 contractDescriptorAddress)
{
HMODULE hdac = LoadLibraryA(dacFilePath);
if (hdac == NULL)
@@ -591,7 +591,7 @@ IXCLRDataProcess* Runtime::CreateClrDataProcessInstance(LPCSTR dacFilePath)
FreeLibrary(hdac);
return nullptr;
}
- ICLRDataTarget *target = new DataTarget(GetModuleAddress());
+ ICLRDataTarget *target = new DataTarget(GetModuleAddress(), contractDescriptorAddress);
IXCLRDataProcess* clrDataProcess = nullptr;
HRESULT hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&clrDataProcess);
if (FAILED(hr))
@@ -615,6 +615,37 @@ IXCLRDataProcess* Runtime::CreateClrDataProcessInstance(LPCSTR dacFilePath)
return clrDataProcess;
}
+/**********************************************************************\
+ * Resolves the address of the cDAC contract descriptor export
+ * (DotNetRuntimeContractDescriptor) in the runtime module, or 0 if it
+ * can't be located. Mirrors the export lookup in GetSingleFileInfo: the
+ * cross-platform reader-based lookup for ELF/Mach-O targets and the
+ * debugger's symbol resolution for Windows (PE) targets.
+\**********************************************************************/
+ULONG64 Runtime::GetContractDescriptorAddress()
+{
+ const char* symbolName = "DotNetRuntimeContractDescriptor";
+ ULONG64 symbolAddress = 0;
+ if (m_target->GetOperatingSystem() == ITarget::OperatingSystem::Linux ||
+ m_target->GetOperatingSystem() == ITarget::OperatingSystem::OSX)
+ {
+ if (!::TryGetSymbolWithCallback(ReaderReadMemory, m_address, symbolName, &symbolAddress))
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ IDebuggerServices* debuggerServices = GetDebuggerServices();
+ if (debuggerServices == nullptr ||
+ FAILED(debuggerServices->GetOffsetBySymbol(m_index, symbolName, &symbolAddress)))
+ {
+ return 0;
+ }
+ }
+ return symbolAddress;
+}
+
/**********************************************************************\
* Loads and initializes the public ICorDebug interfaces. This should be
* called at least once per debugger stop state to ensure that the
diff --git a/src/SOS/Strike/platform/runtimeimpl.h b/src/SOS/Strike/platform/runtimeimpl.h
index 2d6e664327..5c4e4d3fa9 100644
--- a/src/SOS/Strike/platform/runtimeimpl.h
+++ b/src/SOS/Strike/platform/runtimeimpl.h
@@ -150,7 +150,13 @@ class Runtime : public IRuntime
}
// Loads the given DAC/cDAC module and creates an IXCLRDataProcess from it (nullptr on failure).
- IXCLRDataProcess* CreateClrDataProcessInstance(LPCSTR dacFilePath);
+ // contractDescriptorAddress is the cDAC contract descriptor address (0 for the in-box DAC, which
+ // does not use it).
+ IXCLRDataProcess* CreateClrDataProcessInstance(LPCSTR dacFilePath, ULONG64 contractDescriptorAddress);
+
+ // Resolves the address of the cDAC contract descriptor export (DotNetRuntimeContractDescriptor)
+ // in the runtime module, or 0 if it can't be found. The cDAC requires this via ICLRContractLocator.
+ ULONG64 GetContractDescriptorAddress();
// Evaluates the cDAC loading policy: cDAC is used for supported runtimes (.NET 11+) unless
// DOTNET_ENABLE_CDAC requests that the in-box DAC drive the cDAC contract reader itself.
From d0cf0f85cb6de6599b31e3fc5daa75d9d9124118 Mon Sep 17 00:00:00 2001
From: Juan Sebastian Hoyos Ayala
Date: Wed, 17 Jun 2026 10:57:15 -0700
Subject: [PATCH 4/7] Disable tests failing from dotnet/diagnostics#5883
---
src/tests/SOS.UnitTests/SOS.cs | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/tests/SOS.UnitTests/SOS.cs b/src/tests/SOS.UnitTests/SOS.cs
index a8eb0ddb61..110c8ab27f 100644
--- a/src/tests/SOS.UnitTests/SOS.cs
+++ b/src/tests/SOS.UnitTests/SOS.cs
@@ -332,11 +332,8 @@ await SOSTestHelpers.RunTest(
[SkippableTheory, MemberData(nameof(SOSTestHelpers.Configurations), MemberType = typeof(SOSTestHelpers))]
public async Task StackTests(TestConfiguration config)
{
- if (config.RuntimeFrameworkVersionMajor == 10)
- {
- // The clrstack -i command regressed on .NET 10 win-x86, so skip this test for now.
- SOSTestHelpers.SkipIfWinX86(config);
- }
+ // Tracking: https://github.com/dotnet/diagnostics/issues/5883 (dotnet/runtime#129456)
+ SOSTestHelpers.SkipIfWinX86(config);
await SOSTestHelpers.RunTest(
config,
@@ -507,6 +504,8 @@ public SOSGCTests(ITestOutputHelper output)
public async Task GCTests(TestConfiguration config)
{
SOSTestHelpers.SkipIfArm(config);
+ // Tracking: https://github.com/dotnet/diagnostics/issues/5883 (dotnet/runtime#129456)
+ SOSTestHelpers.SkipIfWinX86(config);
// Live only
await SOSTestHelpers.RunTest(
@@ -525,6 +524,8 @@ public async Task GCPOHTests(TestConfiguration config)
{
throw new SkipTestException("This test validates POH behavior, which was introduced in .net 5");
}
+ // Tracking: https://github.com/dotnet/diagnostics/issues/5883 (dotnet/runtime#129456)
+ SOSTestHelpers.SkipIfWinX86(config);
await SOSTestHelpers.RunTest(
config,
debuggeeName: "GCPOH",
@@ -709,6 +710,8 @@ public async Task VarargPInvokeInteropMD(TestConfiguration config)
{
throw new SkipTestException("Test only supports CDB and therefore only runs on Windows");
}
+ // Tracking: https://github.com/dotnet/diagnostics/issues/5883 (dotnet/runtime#129456)
+ SOSTestHelpers.SkipIfWinX86(config);
await SOSTestHelpers.RunTest(
config,
@@ -862,11 +865,8 @@ public async Task StackAndOtherTests(TestConfiguration config)
{
throw new SkipTestException("Single-file DAC signature verification failure with CDB (https://github.com/dotnet/diagnostics/issues/5757)");
}
- if (config.RuntimeFrameworkVersionMajor == 10)
- {
- // The clrstack -i -a command regressed on .NET 10 win-x86, so skip this test for now.
- SOSTestHelpers.SkipIfWinX86(config);
- }
+ // Tracking: https://github.com/dotnet/diagnostics/issues/5883 (dotnet/runtime#129456)
+ SOSTestHelpers.SkipIfWinX86(config);
foreach (TestConfiguration currentConfig in TestRunner.EnumeratePdbTypeConfigs(config))
{
From 63250349245bfb1cff6c649011ee7cdda0038382 Mon Sep 17 00:00:00 2001
From: Juan Sebastian Hoyos Ayala
Date: Wed, 17 Jun 2026 11:14:15 -0700
Subject: [PATCH 5/7] Relax line number against issue dotnet/diagnostics#5884
---
src/tests/SOS.UnitTests/Scripts/StackAndOtherTests.script | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/tests/SOS.UnitTests/Scripts/StackAndOtherTests.script b/src/tests/SOS.UnitTests/Scripts/StackAndOtherTests.script
index 0db28bdd9e..fa3336be07 100644
--- a/src/tests/SOS.UnitTests/Scripts/StackAndOtherTests.script
+++ b/src/tests/SOS.UnitTests/Scripts/StackAndOtherTests.script
@@ -67,7 +67,7 @@ SOSCOMMAND:SOSStatus
SOSCOMMAND:ClrStack
VERIFY:.*OS Thread Id:\s+0x\s+.*
VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+
-VERIFY:.*\s+\s+.*\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 57\]\s*
+VERIFY:.*\s+\s+.*\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ (53|57)\]\s*
VERIFY:\s+\s+\s+SymbolTestApp\.Program\.Foo2\(.*\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 32\]\s*
VERIFY:\s+\s+\s+SymbolTestApp\.Program\.Foo1\(.*\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 27\]\s*
VERIFY:\s+\s+\s+SymbolTestApp\.Program\.Main\(.*\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 22\]\s*
@@ -89,7 +89,7 @@ VERIFY:\s+....*
SOSCOMMAND:ClrStack -f
VERIFY:.*OS Thread Id:\s+0x\s+.*
VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+
-VERIFY:\s+\s+\s+SymbolTestApp\.(dll|exe)!SymbolTestApp\.Program\.Foo4\(System\.String\)\s+\+\s+\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 57\]\s*
+VERIFY:\s+\s+\s+SymbolTestApp\.(dll|exe)!SymbolTestApp\.Program\.Foo4\(System\.String\)\s+\+\s+\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ (53|57)\]\s*
VERIFY:\s+\s+\s+SymbolTestApp\.(dll|exe)!SymbolTestApp\.Program\.Foo2\(.*\)\s+\+\s+\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 32\]\s*
VERIFY:\s+\s+\s+SymbolTestApp\.(dll|exe)!SymbolTestApp\.Program\.Foo1\(.*\)\s+\+\s+\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 27\]\s*
VERIFY:\s+\s+\s+SymbolTestApp\.(dll|exe)!SymbolTestApp\.Program\.Main\(.*\)\s+\+\s+\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 22\]\s*
@@ -98,7 +98,7 @@ VERIFY:\s+\s+\s+SymbolTestApp\.(dll|exe)!SymbolTestApp\.Program\
SOSCOMMAND:ClrStack -a
VERIFY:.*OS Thread Id:\s+0x\s+.*
VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+
-VERIFY:.*\s+\s+\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 57\]\s*
+VERIFY:.*\s+\s+\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ (53|57)\]\s*
VERIFY:\s+PARAMETERS:\s+
VERIFY:\s+dllPath \(0x\) = 0x\s+
VERIFY:.*\s+LOCALS:\s+
@@ -112,7 +112,7 @@ SOSCOMMAND:ClrStack -r
VERIFY:.*OS Thread Id:\s+0x\s+.*
VERIFY:\s+Child\s+SP\s+IP\s+Call Site\s+
-VERIFY:.*\s+\s+\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ 57\]\s*
+VERIFY:.*\s+\s+\s+SymbolTestApp\.Program\.Foo4\(System\.String\)\s+\[(?i:.*[\\|/]SymbolTestApp\.cs) @ (53|57)\]\s*
IFDEF:ARM
VERIFY:\s+r0=\s+r1=\s+r2=\s+
ENDIF:ARM
From 9559aef332abb48dc8fb4e1c80ff371363b66e8f Mon Sep 17 00:00:00 2001
From: Juan Sebastian Hoyos Ayala
Date: Wed, 17 Jun 2026 21:39:20 -0700
Subject: [PATCH 6/7] Refactor cDAC handling: replace UseCDac property with
CDacLoadPolicy enum
---
.../Host.cs | 2 +-
.../Runtime.cs | 16 +++++------
.../CDacLoadPolicy.cs | 28 +++++++++++++++++++
.../ISettingsService.cs | 13 ++-------
.../Host/CommandFormatHelpers.cs | 2 +-
.../Host/RuntimesCommand.cs | 8 +++---
6 files changed, 45 insertions(+), 24 deletions(-)
create mode 100644 src/Microsoft.Diagnostics.DebugServices/CDacLoadPolicy.cs
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Host.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Host.cs
index 0b066c5309..d502b96943 100644
--- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Host.cs
+++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Host.cs
@@ -118,7 +118,7 @@ public string GetTempDirectory()
public virtual bool DacSignatureVerificationEnabled { get; set; }
- public bool? UseCDac { get; set; }
+ public CDacLoadPolicy CDacLoadPolicy { get; set; }
#endregion
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
index 1739fce5dc..4c362a4de8 100644
--- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
+++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
@@ -131,7 +131,7 @@ public string GetCDacFilePath()
// The cDAC is bundled with the diagnostics tool and is never downloaded, so a missing
// path means it isn't available for this host.
_cdacFilePath ??= GetLibraryPath(DebugLibraryKind.CDac);
- if (_cdacFilePath is null && _settingsService.UseCDac == true)
+ if (_cdacFilePath is null && _settingsService.CDacLoadPolicy == CDacLoadPolicy.UseCDac)
{
// The cDAC was explicitly forced but isn't bundled with this tool.
throw new DiagnosticsException($"The cDAC was explicitly requested but no matching cDAC is available for this runtime: {RuntimeModule.FileName}");
@@ -155,22 +155,22 @@ public string GetDbiFilePath()
///
/// Evaluates the cDAC loading policy for this runtime. This is the single place that
/// decides whether the diagnostics tool should load the cDAC itself in place of the
- /// in-box DAC, based on the setting and the
+ /// in-box DAC, based on the setting and the
/// target runtime version.
///
private bool ShouldUseCDac()
{
- return _settingsService.UseCDac switch
+ return _settingsService.CDacLoadPolicy switch
{
- false => false, // Never load the cDAC.
- true => true, // Always use the cDAC, regardless of the runtime version. Availability is
- // checked by the caller (a missing forced cDAC is a hard error).
- _ => ShouldUseCDacByDefault(), // No explicit setting: evaluate the default policy.
+ CDacLoadPolicy.UseLegacyDac => false, // Never load the cDAC.
+ CDacLoadPolicy.UseCDac => true, // Always use the cDAC, regardless of the runtime version. Availability is
+ // checked by the caller (a missing forced cDAC is a hard error).
+ _ => ShouldUseCDacByDefault(), // No explicit setting: evaluate the default policy.
};
}
///
- /// The default cDAC policy used when is not set.
+ /// The default cDAC policy used when is not set.
///
private bool ShouldUseCDacByDefault()
{
diff --git a/src/Microsoft.Diagnostics.DebugServices/CDacLoadPolicy.cs b/src/Microsoft.Diagnostics.DebugServices/CDacLoadPolicy.cs
new file mode 100644
index 0000000000..ff187888fd
--- /dev/null
+++ b/src/Microsoft.Diagnostics.DebugServices/CDacLoadPolicy.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+ ///
+ /// Controls whether the cDAC is used in place of the in-box DAC.
+ ///
+ public enum CDacLoadPolicy
+ {
+ ///
+ /// Evaluate policy and fall back. The cDAC is used when the target runtime supports it
+ /// and a matching cDAC is available next to the diagnostics tool; otherwise the in-box
+ /// DAC is used.
+ ///
+ Default,
+
+ ///
+ /// Always use the cDAC. Runtime construction fails if no matching cDAC is available.
+ ///
+ UseCDac,
+
+ ///
+ /// Always use the in-box DAC. The cDAC is never loaded.
+ ///
+ UseLegacyDac,
+ }
+}
diff --git a/src/Microsoft.Diagnostics.DebugServices/ISettingsService.cs b/src/Microsoft.Diagnostics.DebugServices/ISettingsService.cs
index 2e6c42bc32..031d28a8b9 100644
--- a/src/Microsoft.Diagnostics.DebugServices/ISettingsService.cs
+++ b/src/Microsoft.Diagnostics.DebugServices/ISettingsService.cs
@@ -17,16 +17,9 @@ public interface ISettingsService
bool DacSignatureVerificationEnabled { get; set; }
///
- /// Controls whether the cDAC is used in place of the in-box DAC:
- ///
- /// - null (default): evaluate policy and fall back. The cDAC is used
- /// when the target runtime supports it and a matching cDAC is available next
- /// to the diagnostics tool; otherwise the in-box DAC is used.
- /// - true: always use the cDAC. Runtime construction fails if no
- /// matching cDAC is available.
- /// - false: always use the in-box DAC. The cDAC is never loaded.
- ///
+ /// Controls whether the cDAC is used in place of the in-box DAC. See
+ /// for the individual policy values.
///
- bool? UseCDac { get; set; }
+ CDacLoadPolicy CDacLoadPolicy { get; set; }
}
}
diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Host/CommandFormatHelpers.cs b/src/Microsoft.Diagnostics.ExtensionCommands/Host/CommandFormatHelpers.cs
index 15c3ef2860..9eb92edcd8 100644
--- a/src/Microsoft.Diagnostics.ExtensionCommands/Host/CommandFormatHelpers.cs
+++ b/src/Microsoft.Diagnostics.ExtensionCommands/Host/CommandFormatHelpers.cs
@@ -20,7 +20,7 @@ public static void DisplaySettingService(this CommandBase command)
{
ISettingsService settingsService = command.Services.GetService() ?? throw new DiagnosticsException("Settings service required");
command.Console.WriteLine("Settings:");
- command.Console.WriteLine($"-> Use cDAC: {settingsService.UseCDac switch { true => "true", false => "false", _ => "policy (default)" }}");
+ command.Console.WriteLine($"-> Use cDAC: {settingsService.CDacLoadPolicy switch { CDacLoadPolicy.UseCDac => "true", CDacLoadPolicy.UseLegacyDac => "false", _ => "policy (default)" }}");
command.Console.WriteLine($"-> DAC signature verification check enabled: {settingsService.DacSignatureVerificationEnabled}");
}
diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/Host/RuntimesCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/Host/RuntimesCommand.cs
index 53b1aa49f2..c65b18d206 100644
--- a/src/Microsoft.Diagnostics.ExtensionCommands/Host/RuntimesCommand.cs
+++ b/src/Microsoft.Diagnostics.ExtensionCommands/Host/RuntimesCommand.cs
@@ -52,11 +52,11 @@ public override void Invoke()
bool flush = false;
if (UseCDac is not null)
{
- SettingsService.UseCDac = UseCDac.ToLowerInvariant() switch
+ SettingsService.CDacLoadPolicy = UseCDac.ToLowerInvariant() switch
{
- "true" => true,
- "false" => false,
- "policy" or "default" => (bool?)null,
+ "true" => CDacLoadPolicy.UseCDac,
+ "false" => CDacLoadPolicy.UseLegacyDac,
+ "policy" or "default" => CDacLoadPolicy.Default,
_ => throw new DiagnosticsException($"Invalid --usecdac value '{UseCDac}'. Expected true, false, or policy."),
};
flush = true;
From e8921a942859ee6107eb247fd4b1903ef43a3b3c Mon Sep 17 00:00:00 2001
From: Juan Sebastian Hoyos Ayala
Date: Wed, 17 Jun 2026 21:41:33 -0700
Subject: [PATCH 7/7] Front load DAC to work around ordering issue
---
src/SOS/SOS.Hosting/RuntimeWrapper.cs | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/src/SOS/SOS.Hosting/RuntimeWrapper.cs b/src/SOS/SOS.Hosting/RuntimeWrapper.cs
index 708161c99d..a704534190 100644
--- a/src/SOS/SOS.Hosting/RuntimeWrapper.cs
+++ b/src/SOS/SOS.Hosting/RuntimeWrapper.cs
@@ -389,6 +389,20 @@ private IntPtr CreateCorDebugProcess()
Trace.TraceError($"Could not find matching DBI {dbiFilePath ?? ""} for this runtime: {_runtime.RuntimeModule.FileName}");
return IntPtr.Zero;
}
+
+ // Load the in-box DAC before the DBI. The DBI has a hard load-time dependency on the in-box DAC
+ // (libmscordaccore.so / mscordaccore.dll is a NEEDED import resolved next to the runtime).
+ // as it's the PAL provider for the debugger process. For senarios where the DBI is not collocated with the DAC
+ // (e.x. single-file), each is downloaded into its own symbol-cache directory, so the loader can only satisfy the DBI's dependency if the DAC is
+ // already resident in the process. When the cDAC serves the data-access path the in-box DAC is otherwise never loaded, so load it explicitly here first.
+ // This also verifies the DAC signature before the DBI is passed the DAC path or handle.
+ IntPtr dacHandle = GetDacHandle();
+ if (dacHandle == IntPtr.Zero)
+ {
+ return IntPtr.Zero;
+ }
+ string dacFilePath = _runtime.GetDacFilePath(out bool _);
+
if (_dbiHandle == IntPtr.Zero)
{
try
@@ -415,16 +429,6 @@ private IntPtr CreateCorDebugProcess()
int hresult = 0;
try
{
- // This will verify the DAC signature if needed before DBI is passed the DAC path or handle
- IntPtr dacHandle = GetDacHandle();
- if (dacHandle == IntPtr.Zero)
- {
- return IntPtr.Zero;
- }
-
- // The DAC was verified in the GetDacHandle call above. Ignore the verifySignature parameter here.
- string dacFilePath = _runtime.GetDacFilePath(out bool _);
-
OpenVirtualProcessImpl2Delegate openVirtualProcessImpl2 = SOSHost.GetDelegateFunction(_dbiHandle, "OpenVirtualProcessImpl2");
if (openVirtualProcessImpl2 != null)
{