diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/HostSymbolProvider.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/HostSymbolProvider.cs
new file mode 100644
index 0000000000..e5f337b57d
--- /dev/null
+++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/HostSymbolProvider.cs
@@ -0,0 +1,104 @@
+// 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 Microsoft.Diagnostics.Runtime;
+
+namespace Microsoft.Diagnostics.DebugServices.Implementation
+{
+ ///
+ /// Adapter exposing the host's /
+ /// through ClrMD's
+ /// contract.
+ ///
+ [ServiceExport(Type = typeof(IClrSymbolProvider), Scope = ServiceScope.Target)]
+ public sealed class HostSymbolProvider : IClrSymbolProvider
+ {
+ private readonly IModuleService _moduleService;
+ private readonly ulong _signExtensionMask;
+
+ public HostSymbolProvider(IModuleService moduleService, IMemoryService memoryService)
+ {
+ _moduleService = moduleService ?? throw new ArgumentNullException(nameof(moduleService));
+ _signExtensionMask = memoryService?.SignExtensionMask() ?? ulong.MaxValue;
+ }
+
+ public bool TryGetSymbolName(ulong address, out string symbolName, out ulong displacement)
+ {
+ symbolName = null;
+ displacement = 0;
+
+ address &= _signExtensionMask;
+
+ IModule module;
+ try
+ {
+ module = _moduleService.GetModuleFromAddress(address);
+ }
+ catch (DiagnosticsException)
+ {
+ return false;
+ }
+ if (module is null)
+ {
+ return false;
+ }
+
+ IModuleSymbols symbols = module.Services.GetService();
+ if (symbols is null)
+ {
+ return false;
+ }
+
+ if (!symbols.TryGetSymbolName(address, out string bareName, out displacement)
+ || string.IsNullOrEmpty(bareName))
+ {
+ return false;
+ }
+
+ // Strip any module! qualifier the lower-level service might have
+ // prepended — the new contract returns bare names only.
+ int bang = bareName.IndexOf('!');
+ symbolName = bang >= 0 && bang + 1 < bareName.Length ? bareName.Substring(bang + 1) : bareName;
+ return true;
+ }
+
+ public bool TryGetSymbolAddress(ulong moduleBase, string name, out ulong address)
+ {
+ address = 0;
+ if (string.IsNullOrEmpty(name))
+ {
+ return false;
+ }
+
+ moduleBase &= _signExtensionMask;
+
+ IModule scopedModule;
+ try
+ {
+ scopedModule = _moduleService.GetModuleFromBaseAddress(moduleBase);
+ }
+ catch (DiagnosticsException)
+ {
+ return false;
+ }
+ if (scopedModule is null)
+ {
+ return false;
+ }
+
+ IModuleSymbols scopedSymbols = scopedModule.Services.GetService();
+ if (scopedSymbols is null)
+ {
+ return false;
+ }
+
+ if (scopedSymbols.TryGetSymbolAddress(name, out ulong scopedAddr) && scopedAddr != 0)
+ {
+ address = scopedAddr;
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
index 4c362a4de8..28c444bb62 100644
--- a/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
+++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
@@ -44,15 +44,7 @@ public Runtime(IServiceProvider services, int id, ClrInfo clrInfo)
_settingsService = services.GetService() ?? throw new ArgumentException("ISettingsService required");
_symbolService = services.GetService() ?? throw new ArgumentException("ISymbolService required");
- RuntimeType = RuntimeType.Unknown;
- if (clrInfo.Flavor == ClrFlavor.Core)
- {
- RuntimeType = RuntimeType.NetCore;
- }
- else if (clrInfo.Flavor == ClrFlavor.Desktop)
- {
- RuntimeType = RuntimeType.Desktop;
- }
+ RuntimeType = GetRuntimeType(clrInfo.Flavor);
RuntimeModule = services.GetService().GetModuleFromBaseAddress(clrInfo.ModuleInfo.ImageBase);
ServiceContainerFactory containerFactory = services.GetService().CreateServiceContainerFactory(ServiceScope.Runtime, services);
@@ -382,9 +374,18 @@ public override int GetHashCode()
"Desktop .NET Framework",
".NET Core",
".NET Core (single-file)",
+ "Native AOT",
"Other"
};
+ private static RuntimeType GetRuntimeType(ClrFlavor flavor) => flavor switch
+ {
+ ClrFlavor.Core => RuntimeType.NetCore,
+ ClrFlavor.Desktop => RuntimeType.Desktop,
+ ClrFlavor.NativeAOT => RuntimeType.NativeAOT,
+ _ => RuntimeType.Unknown,
+ };
+
public override string ToString()
{
StringBuilder sb = new();
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/RuntimeProvider.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/RuntimeProvider.cs
index 84572786c1..3d077346df 100644
--- a/src/Microsoft.Diagnostics.DebugServices.Implementation/RuntimeProvider.cs
+++ b/src/Microsoft.Diagnostics.DebugServices.Implementation/RuntimeProvider.cs
@@ -61,7 +61,8 @@ public IEnumerable EnumerateRuntimes(int startingRuntimeId, RuntimeEnu
return false;
}
return verifyDac;
- }
+ },
+ SymbolProvider = _services.GetService(),
});
for (int i = 0; i < dataTarget.ClrVersions.Length; i++)
{
diff --git a/src/SOS/SOS.Hosting/DataTargetWrapper.cs b/src/SOS/SOS.Hosting/DataTargetWrapper.cs
index 0a01ceea22..0a6690a9b2 100644
--- a/src/SOS/SOS.Hosting/DataTargetWrapper.cs
+++ b/src/SOS/SOS.Hosting/DataTargetWrapper.cs
@@ -19,6 +19,7 @@ internal sealed unsafe class DataTargetWrapper : COMCallableIUnknown
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");
+ private static readonly Guid IID_ICLRSymbolProvider = new("c4f8b7e2-9d3a-4f6c-b1e5-8a2d7c3f9b1e");
// For ClrMD's magic hand shake
private const ulong MagicCallbackConstant = 0x43;
@@ -31,6 +32,7 @@ internal sealed unsafe class DataTargetWrapper : COMCallableIUnknown
private readonly IModuleService _moduleService;
private readonly IThreadUnwindService _threadUnwindService;
private readonly IRemoteMemoryService _remoteMemoryService;
+ private readonly IClrSymbolProvider _symbolProvider;
private readonly ulong _ignoreAddressBitsMask;
public IntPtr IDataTarget { get; }
@@ -47,6 +49,7 @@ public DataTargetWrapper(IServiceProvider services, IRuntime runtime)
_threadUnwindService = services.GetService();
_moduleService = services.GetService();
_remoteMemoryService = services.GetService();
+ _symbolProvider = services.GetService();
_ignoreAddressBitsMask = _memoryService.SignExtensionMask();
VTableBuilder builder = AddInterface(IID_ICLRDataTarget, false);
@@ -73,6 +76,11 @@ public DataTargetWrapper(IServiceProvider services, IRuntime runtime)
builder.AddMethod(new GetContractDescriptorDelegate(GetContractDescriptor));
builder.Complete();
+ builder = AddInterface(IID_ICLRSymbolProvider, false);
+ builder.AddMethod(new TryGetSymbolNameDelegate(TryGetSymbolName));
+ builder.AddMethod(new TryGetSymbolAddressDelegate(TryGetSymbolAddress));
+ builder.Complete();
+
AddRef();
}
@@ -378,6 +386,113 @@ private int GetContractDescriptor(
#endregion
+ #region ICLRSymbolProvider
+
+ private int TryGetSymbolName(
+ IntPtr self,
+ ulong address,
+ uint cchName,
+ char* pName,
+ uint* pcchNameActual,
+ ulong* pDisplacement)
+ {
+ if (cchName > int.MaxValue)
+ {
+ return HResult.E_INVALIDARG;
+ }
+
+ address &= _ignoreAddressBitsMask;
+
+ try
+ {
+ if (_symbolProvider is null)
+ {
+ return HResult.E_NOTIMPL;
+ }
+
+ if (!_symbolProvider.TryGetSymbolName(address, out string symbolName, out ulong displacement)
+ || string.IsNullOrEmpty(symbolName))
+ {
+ return HResult.E_FAIL;
+ }
+
+ if (pcchNameActual != null)
+ {
+ *pcchNameActual = (uint)symbolName.Length + 1;
+ }
+ if (pDisplacement != null)
+ {
+ *pDisplacement = displacement;
+ }
+
+ if (cchName == 0 || pName == null)
+ {
+ return HResult.S_OK;
+ }
+
+ int copy = Math.Min(symbolName.Length, (int)cchName - 1);
+ for (int i = 0; i < copy; i++)
+ {
+ pName[i] = symbolName[i];
+ }
+ pName[copy] = '\0';
+ return copy < symbolName.Length ? HResult.S_FALSE : HResult.S_OK;
+ }
+ catch
+ {
+ return HResult.E_FAIL;
+ }
+ }
+
+ private int TryGetSymbolAddress(
+ IntPtr self,
+ ulong moduleBase,
+ string name,
+ ulong* pAddress)
+ {
+ if (pAddress == null)
+ {
+ return HResult.E_INVALIDARG;
+ }
+ *pAddress = 0;
+
+ moduleBase &= _ignoreAddressBitsMask;
+
+ try
+ {
+ if (_symbolProvider is null)
+ {
+ return HResult.E_NOTIMPL;
+ }
+
+ if (string.IsNullOrEmpty(name))
+ {
+ return HResult.E_INVALIDARG;
+ }
+
+ // Bare symbol names only — '!' is reserved as the SOS module
+ // separator and is not produced by any of the mangling toolchains
+ // we target.
+ if (name.IndexOf('!') >= 0)
+ {
+ return HResult.E_INVALIDARG;
+ }
+
+ if (_symbolProvider.TryGetSymbolAddress(moduleBase, name, out ulong address) && address != 0)
+ {
+ *pAddress = address;
+ return HResult.S_OK;
+ }
+ return HResult.E_FAIL;
+ }
+ catch
+ {
+ return HResult.E_FAIL;
+ }
+ }
+
+ #endregion
+
#region ICLRDataTarget delegates
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
@@ -522,5 +637,25 @@ private delegate int GetContractDescriptorDelegate(
[Out] out ulong address);
#endregion
+
+ #region ICLRSymbolProvider delegates
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate int TryGetSymbolNameDelegate(
+ [In] IntPtr self,
+ [In] ulong address,
+ [In] uint cchName,
+ [Out] char* pName,
+ [Out] uint* pcchNameActual,
+ [Out] ulong* pDisplacement);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate int TryGetSymbolAddressDelegate(
+ [In] IntPtr self,
+ [In] ulong moduleBase,
+ [In][MarshalAs(UnmanagedType.LPWStr)] string name,
+ [Out] ulong* pAddress);
+
+ #endregion
}
}