|
// 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.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.Metadata;
using System.Collections.Generic;
using Microsoft.Diagnostics.DataContractReader.Contracts;
namespace Microsoft.Diagnostics.DataContractReader.Legacy;
[GeneratedComClass]
public sealed unsafe partial class ClrDataModule : ICustomQueryInterface, IXCLRDataModule, IXCLRDataModule2
{
private readonly TargetPointer _address;
private readonly Target _target;
internal TargetPointer Address => _address;
private bool _extentsSet;
private CLRDataModuleExtent[] _extents = new CLRDataModuleExtent[2];
#if DEBUG
private ulong legacyExtentHandle;
#endif
private readonly IXCLRDataModule? _legacyModule;
private readonly IXCLRDataModule2? _legacyModule2;
// This is an IUnknown pointer for the legacy implementation
private readonly nint _legacyModulePointer;
private MetaDataImportImpl? _metaDataImportImpl;
public ClrDataModule(TargetPointer address, Target target, IXCLRDataModule? legacyImpl)
{
_address = address;
_target = target;
_legacyModule = legacyImpl;
_legacyModule2 = legacyImpl as IXCLRDataModule2;
if (legacyImpl is not null && System.Runtime.InteropServices.ComWrappers.TryGetComInstance(legacyImpl, out _legacyModulePointer))
{
// Release the AddRef from TryGetComInstance. We rely on the ref-count from holding on to IXCLRDataModule.
Marshal.Release(_legacyModulePointer);
}
}
private const uint CORDEBUG_JIT_DEFAULT = 0x1;
private const uint CORDEBUG_JIT_DISABLE_OPTIMIZATION = 0x3;
CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out nint ppv)
{
ppv = default;
// Legacy DAC implementation of IXCLRDataModule handles QIs for IMetaDataImport by creating and
// passing out an implementation of IMetaDataImport. Note that it does not do COM aggregation.
// It simply returns a completely separate object. See ClrDataModule::QueryInterface in task.cpp
// The returned MetaDataImportImpl also implements IMetaDataImport2 and IMetaDataAssemblyImport,
// so consumers can QI the returned object for those interfaces as well.
//
// IMPORTANT: Some consumers (e.g. ClrMD) QI for IMetaDataImport but then access IMetaDataImport2
// vtable slots beyond the IMetaDataImport vtable boundary. This works with native C++ COM objects
// (where the vtable for IMetaDataImport and IMetaDataImport2 is unified) but breaks with managed
// [GeneratedComInterface] CCWs which create separate vtables per interface. To handle this, we
// always return the IMetaDataImport2 vtable pointer when asked for IMetaDataImport. Since
// IMetaDataImport2 inherits from IMetaDataImport, the first slots are identical.
if (iid == typeof(IMetaDataImport).GUID)
{
MetaDataImportImpl? wrapper = _metaDataImportImpl;
if (wrapper is null)
{
MetadataReader? reader = null;
IMetaDataImport? legacyImport = null;
try
{
ILoader loader = _target.Contracts.Loader;
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(_address);
reader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle);
}
catch
{
}
try
{
Guid iidMetaDataImport = typeof(IMetaDataImport).GUID;
if (_legacyModulePointer != 0 && Marshal.QueryInterface(_legacyModulePointer, iidMetaDataImport, out nint ppMdi) >= 0)
{
legacyImport = ComInterfaceMarshaller<IMetaDataImport>.ConvertToManaged((void*)ppMdi);
Marshal.Release(ppMdi);
}
}
catch
{
}
if (reader is null)
return CustomQueryInterfaceResult.NotHandled;
wrapper = new MetaDataImportImpl(reader, legacyImport);
_metaDataImportImpl ??= wrapper;
wrapper = _metaDataImportImpl;
}
nint pUnk = (nint)ComInterfaceMarshaller<IMetaDataImport2>.ConvertToUnmanaged(wrapper);
// ConvertToUnmanaged returns a COM pointer for IMetaDataImport2.
// We return this directly as ppv so that consumers (e.g. ClrMD) that QI for
// IMetaDataImport but access IMetaDataImport2 vtable slots get the full vtable.
ppv = pUnk;
return CustomQueryInterfaceResult.Handled;
}
return CustomQueryInterfaceResult.NotHandled;
}
internal sealed class EnumMethodDefinitions : IEnum<uint>
{
private readonly uint _flags;
private readonly MetadataReader _reader;
private TypeDefinitionHandle? _typeHandle;
private string? _methodName;
public IEnumerator<uint> Enumerator { get; set; } = Enumerable.Empty<uint>().GetEnumerator();
public TargetPointer LegacyHandle { get; set; } = TargetPointer.Null;
public EnumMethodDefinitions(MetadataReader reader, uint flags, TargetPointer legacyHandle)
{
_reader = reader;
_flags = flags;
LegacyHandle = legacyHandle;
}
public void Start(string fullName)
{
// start: find the type.
int parenIndex = fullName.IndexOf('(');
if (parenIndex >= 0)
fullName = fullName[..parenIndex];
int lastNestingSep = Math.Max(fullName.LastIndexOf('+'), fullName.LastIndexOf('/'));
int searchFrom = fullName.Length - 1;
while (searchFrom > 0)
{
int dotPos = fullName.LastIndexOf('.', searchFrom);
if (dotPos <= 0)
break;
while (dotPos > 0 && fullName[dotPos - 1] == '.')
dotPos--;
if (dotPos <= lastNestingSep || dotPos <= 0)
break;
string typePortion = fullName[..dotPos];
string methodName = fullName[Math.Min(dotPos + 1, fullName.Length - 1)..];
_typeHandle = ResolveType(_reader, typePortion);
if (_typeHandle != null)
{
_methodName = methodName;
break;
}
searchFrom = dotPos - 1;
}
if (_typeHandle == null)
throw new ArgumentException();
Enumerator = IterateMethodDefinitions().GetEnumerator();
}
private bool StringEquals(string a, string b)
{
StringComparison comparison = (_flags & (uint)CLRDataByNameFlag.CLRDATA_BYNAME_CASE_INSENSITIVE) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
return string.Equals(a, b, comparison);
}
private IEnumerable<uint> IterateMethodDefinitions()
{
foreach (MethodDefinitionHandle mh in _reader.GetTypeDefinition(_typeHandle!.Value).GetMethods())
{
if (StringEquals(_reader.GetString(_reader.GetMethodDefinition(mh).Name), _methodName!))
yield return (uint)MetadataTokens.GetToken(mh);
}
yield break;
}
private TypeDefinitionHandle? ResolveType(MetadataReader reader, string typeFullName)
{
string[] nestingParts = typeFullName.Split('+', '/');
string firstPart = nestingParts[0];
int lastDot = firstPart.LastIndexOf('.');
string ns = lastDot >= 0 ? firstPart[..lastDot] : "";
string outerName = lastDot >= 0 ? firstPart[(lastDot + 1)..] : firstPart;
TypeDefinitionHandle? current = FindTopLevelType(reader, ns, outerName);
for (int i = 1; i < nestingParts.Length && current != null; i++)
current = FindNestedType(reader, current.Value, nestingParts[i]);
return current;
}
private TypeDefinitionHandle? FindTopLevelType(MetadataReader reader, string @namespace, string name)
{
foreach (TypeDefinitionHandle handle in reader.TypeDefinitions)
{
TypeDefinition td = reader.GetTypeDefinition(handle);
if (td.IsNested)
continue;
if ((string.IsNullOrEmpty(@namespace) || StringEquals(reader.GetString(td.Namespace), @namespace)) &&
StringEquals(reader.GetString(td.Name), name))
return handle;
}
return null;
}
private TypeDefinitionHandle? FindNestedType(MetadataReader reader, TypeDefinitionHandle enclosing, string name)
{
foreach (TypeDefinitionHandle nh in reader.GetTypeDefinition(enclosing).GetNestedTypes())
{
if (StringEquals(reader.GetString(reader.GetTypeDefinition(nh).Name), name))
return nh;
}
return null;
}
}
int IXCLRDataModule.StartEnumAssemblies(ulong* handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumAssemblies(handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.EnumAssembly(ulong* handle, DacComNullableByRef<IXCLRDataAssembly> assembly)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumAssembly(handle, assembly) : HResults.E_NOTIMPL;
int IXCLRDataModule.EndEnumAssemblies(ulong handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumAssemblies(handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.StartEnumTypeDefinitions(ulong* handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumTypeDefinitions(handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.EnumTypeDefinition(ulong* handle, DacComNullableByRef<IXCLRDataTypeDefinition> typeDefinition)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumTypeDefinition(handle, typeDefinition) : HResults.E_NOTIMPL;
int IXCLRDataModule.EndEnumTypeDefinitions(ulong handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumTypeDefinitions(handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.StartEnumTypeInstances(IXCLRDataAppDomain? appDomain, ulong* handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumTypeInstances(appDomain, handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.EnumTypeInstance(ulong* handle, DacComNullableByRef<IXCLRDataTypeInstance> typeInstance)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumTypeInstance(handle, typeInstance) : HResults.E_NOTIMPL;
int IXCLRDataModule.EndEnumTypeInstances(ulong handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumTypeInstances(handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.StartEnumTypeDefinitionsByName(char* name, uint flags, ulong* handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumTypeDefinitionsByName(name, flags, handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.EnumTypeDefinitionByName(ulong* handle, DacComNullableByRef<IXCLRDataTypeDefinition> type)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumTypeDefinitionByName(handle, type) : HResults.E_NOTIMPL;
int IXCLRDataModule.EndEnumTypeDefinitionsByName(ulong handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumTypeDefinitionsByName(handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.StartEnumTypeInstancesByName(char* name, uint flags, IXCLRDataAppDomain? appDomain, ulong* handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumTypeInstancesByName(name, flags, appDomain, handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.EnumTypeInstanceByName(ulong* handle, DacComNullableByRef<IXCLRDataTypeInstance> type)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumTypeInstanceByName(handle, type) : HResults.E_NOTIMPL;
int IXCLRDataModule.EndEnumTypeInstancesByName(ulong handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumTypeInstancesByName(handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.GetTypeDefinitionByToken(/*mdTypeDef*/ uint token, DacComNullableByRef<IXCLRDataTypeDefinition> typeDefinition)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.GetTypeDefinitionByToken(token, typeDefinition) : HResults.E_NOTIMPL;
int IXCLRDataModule.StartEnumMethodDefinitionsByName(char* name, uint flags, ulong* handle)
{
int hr = HResults.S_OK;
*handle = 0;
// Start the legacy enumeration to keep it in sync with the cDAC enumeration.
ulong handleLocal = default;
int hrLocal = default;
try
{
if (_legacyModule is not null)
{
hrLocal = _legacyModule.StartEnumMethodDefinitionsByName(name, flags, &handleLocal);
}
if (name == null || *name == '\0')
throw new ArgumentException();
if ((flags & ~((uint)CLRDataByNameFlag.CLRDATA_BYNAME_CASE_SENSITIVE | (uint)CLRDataByNameFlag.CLRDATA_BYNAME_CASE_INSENSITIVE)) != 0)
throw new ArgumentException();
string fullName = new string(name);
// start: find the type.
ILoader loader = _target.Contracts.Loader;
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(_address);
MetadataReader reader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle)!;
EnumMethodDefinitions emd = new(reader, flags, handleLocal);
emd.Start(fullName);
*handle = (ulong)((IEnum<uint>)emd).GetHandle();
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacyModule is not null)
{
Debug.ValidateHResult(hr, hrLocal);
}
#endif
return hr;
}
int IXCLRDataModule.EnumMethodDefinitionByName(ulong* handle, DacComNullableByRef<IXCLRDataMethodDefinition> method)
{
int hr = HResults.S_OK;
EnumMethodDefinitions emd;
try
{
if (method.IsNullRef)
throw new NullReferenceException();
GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)(*handle));
if (gcHandle.Target is not EnumMethodDefinitions emdLocal)
throw new ArgumentException();
emd = emdLocal;
}
catch (System.Exception ex)
{
return ex.HResult;
}
// Advance the legacy enumeration to keep it in sync with the cDAC enumeration.
IXCLRDataMethodDefinition? legacyMethod = null;
int hrLocal = HResults.S_OK;
if (_legacyModule is not null)
{
ulong legacyHandle = emd.LegacyHandle;
DacComNullableByRef<IXCLRDataMethodDefinition> legacyMethodOut = new(isNullRef: false);
hrLocal = _legacyModule.EnumMethodDefinitionByName(&legacyHandle, legacyMethodOut);
legacyMethod = legacyMethodOut.Interface;
emd.LegacyHandle = legacyHandle;
}
try
{
if (emd.Enumerator.MoveNext())
{
uint token = emd.Enumerator.Current;
method.Interface = new ClrDataMethodDefinition(_target, _address, token, legacyMethod);
}
else
{
hr = HResults.S_FALSE;
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacyModule is not null)
{
Debug.ValidateHResult(hr, hrLocal);
}
#endif
return hr;
}
int IXCLRDataModule.EndEnumMethodDefinitionsByName(ulong handle)
{
int hr = HResults.S_OK;
EnumMethodDefinitions emd;
try
{
GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)handle);
if (gcHandle.Target is not EnumMethodDefinitions emdLocal)
throw new ArgumentException();
emd = emdLocal;
((IEnum<uint>)emd).Dispose();
gcHandle.Free();
}
catch (System.Exception ex)
{
return ex.HResult;
}
if (_legacyModule != null && emd.LegacyHandle != TargetPointer.Null)
{
int hrLocal = _legacyModule.EndEnumMethodDefinitionsByName(emd.LegacyHandle);
if (hrLocal < 0)
return hrLocal;
}
return hr;
}
int IXCLRDataModule.StartEnumMethodInstancesByName(char* name, uint flags, IXCLRDataAppDomain? appDomain, ulong* handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumMethodInstancesByName(name, flags, appDomain, handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.EnumMethodInstanceByName(ulong* handle, DacComNullableByRef<IXCLRDataMethodInstance> method)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumMethodInstanceByName(handle, method) : HResults.E_NOTIMPL;
int IXCLRDataModule.EndEnumMethodInstancesByName(ulong handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumMethodInstancesByName(handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.GetMethodDefinitionByToken(/*mdMethodDef*/ uint token, DacComNullableByRef<IXCLRDataMethodDefinition> methodDefinition)
{
int hr = HResults.S_OK;
int hrLocal = HResults.S_OK;
IXCLRDataMethodDefinition? legacyMethod = null;
try
{
if (_legacyModule is not null)
{
DacComNullableByRef<IXCLRDataMethodDefinition> legacyMethodOut = new(isNullRef: false);
hrLocal = _legacyModule.GetMethodDefinitionByToken(token, legacyMethodOut);
legacyMethod = legacyMethodOut.Interface;
}
if ((EcmaMetadataUtils.TokenType)(token & EcmaMetadataUtils.TokenTypeMask) != EcmaMetadataUtils.TokenType.mdtMethodDef)
throw new ArgumentException();
methodDefinition.Interface = new ClrDataMethodDefinition(_target, _address, token, legacyMethod);
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacyModule is not null)
{
Debug.ValidateHResult(hr, hrLocal);
}
#endif
return hr;
}
int IXCLRDataModule.StartEnumDataByName(char* name, uint flags, IXCLRDataAppDomain? appDomain, IXCLRDataTask? tlsTask, ulong* handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumDataByName(name, flags, appDomain, tlsTask, handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.EnumDataByName(ulong* handle, DacComNullableByRef<IXCLRDataValue> value)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumDataByName(handle, value) : HResults.E_NOTIMPL;
int IXCLRDataModule.EndEnumDataByName(ulong handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumDataByName(handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.GetName(uint bufLen, uint* nameLen, char* name)
{
int hr = HResults.S_OK;
int E_INSUFFICIENT_BUFFER = unchecked((int)0x8007007A);
try
{
if (nameLen != null)
*nameLen = 0;
Contracts.ILoader loader = _target.Contracts.Loader;
Contracts.ModuleHandle handle = loader.GetModuleHandleFromModulePtr(_address);
string result = loader.GetSimpleName(handle);
uint nameLenLocal = 0;
OutputBufferHelpers.CopyStringToBuffer(name, bufLen, &nameLenLocal, result);
if (nameLen != null)
*nameLen = nameLenLocal;
// throw on insufficient buffer
if (nameLenLocal > bufLen)
throw Marshal.GetExceptionForHR(E_INSUFFICIENT_BUFFER)!;
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacyModule is not null)
{
char[] nameLocal = new char[bufLen];
uint nameLenLocal;
int hrLocal;
fixed (char* ptr = nameLocal)
{
hrLocal = _legacyModule.GetName(bufLen, &nameLenLocal, ptr);
}
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
{
Debug.Assert(nameLen == null || *nameLen == nameLenLocal);
Debug.Assert(name == null || new ReadOnlySpan<char>(nameLocal, 0, (int)nameLenLocal - 1).SequenceEqual(new string(name)));
}
}
#endif
return hr;
}
int IXCLRDataModule.GetFileName(uint bufLen, uint* nameLen, char* name)
{
try
{
Contracts.ILoader contract = _target.Contracts.Loader;
Contracts.ModuleHandle handle = contract.GetModuleHandleFromModulePtr(_address);
string result = string.Empty;
try
{
result = contract.GetPath(handle);
}
catch (VirtualReadException)
{
// The memory for the path may not be enumerated - for example, in triage dumps
// In this case, GetPath will throw VirtualReadException
}
if (string.IsNullOrEmpty(result))
{
result = contract.GetFileName(handle);
}
if (string.IsNullOrEmpty(result))
return HResults.E_FAIL;
OutputBufferHelpers.CopyStringToBuffer(name, bufLen, nameLen, result);
}
catch (System.Exception ex)
{
return ex.HResult;
}
#if DEBUG
if (_legacyModule is not null)
{
char[] nameLocal = new char[bufLen];
uint nameLenLocal;
int hrLocal;
fixed (char* ptr = nameLocal)
{
hrLocal = _legacyModule.GetFileName(bufLen, &nameLenLocal, ptr);
}
Debug.Assert(hrLocal == HResults.S_OK);
Debug.Assert(nameLen == null || *nameLen == nameLenLocal);
Debug.Assert(name == null || new ReadOnlySpan<char>(nameLocal, 0, (int)nameLenLocal - 1).SequenceEqual(new string(name)));
}
#endif
return HResults.S_OK;
}
int IXCLRDataModule.GetFlags(uint* flags)
{
*flags = 0;
try
{
Contracts.ILoader contract = _target.Contracts.Loader;
Contracts.ModuleHandle handle = contract.GetModuleHandleFromModulePtr(_address);
ModuleFlags moduleFlags = contract.GetFlags(handle);
if ((moduleFlags & ModuleFlags.ReflectionEmit) != 0)
{
*flags |= 0x1; // CLRDATA_MODULE_IS_DYNAMIC
}
if (contract.GetAssembly(handle) == contract.GetRootAssembly())
{
*flags |= 0x4; // CLRDATA_MODULE_FLAGS_ROOT_ASSEMBLY
}
}
catch (System.Exception ex)
{
return ex.HResult;
}
#if DEBUG
if (_legacyModule is not null)
{
uint flagsLocal;
int hrLocal = _legacyModule.GetFlags(&flagsLocal);
Debug.Assert(hrLocal == HResults.S_OK, $"cDAC: {HResults.S_OK}, DAC: {hrLocal}");
Debug.Assert(flagsLocal == *flags, $"cDAC: {*flags}, DAC: {flagsLocal}");
}
#endif
return HResults.S_OK;
}
int IXCLRDataModule.IsSameObject(IXCLRDataModule* mod)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.IsSameObject(mod) : HResults.E_NOTIMPL;
int IXCLRDataModule.StartEnumExtents(ulong* handle)
{
int hr = HResults.S_OK;
try
{
if (!_extentsSet)
{
Contracts.ILoader contract = _target.Contracts.Loader;
Contracts.ModuleHandle moduleHandle = contract.GetModuleHandleFromModulePtr(_address);
TargetPointer peAssembly = contract.GetPEAssembly(moduleHandle);
if (peAssembly == 0)
{
*handle = 0;
hr = HResults.E_INVALIDARG;
}
else
{
if (contract.TryGetLoadedImageContents(moduleHandle, out TargetPointer baseAddress, out uint size, out _))
{
_extents[0].baseAddress = baseAddress.ToClrDataAddress(_target);
_extents[0].length = size;
_extents[0].type = 0x0; // CLRDATA_MODULE_PE_FILE
}
_extentsSet = true;
}
}
*handle = 1;
hr = _extents[0].baseAddress != 0 ? HResults.S_OK : HResults.S_FALSE;
}
catch (System.Exception ex)
{
return ex.HResult;
}
#if DEBUG
if (_legacyModule is not null)
{
ulong handleLocal = 0;
int hrLocal = _legacyModule.StartEnumExtents(&handleLocal);
legacyExtentHandle = handleLocal;
Debug.Assert(hrLocal == HResults.S_OK, $"cDAC: {HResults.S_OK}, DAC: {hrLocal}");
}
#endif
return hr;
}
int IXCLRDataModule.EnumExtent(ulong* handle, /*CLRDATA_MODULE_EXTENT*/ void* extent)
{
int hr = HResults.S_OK;
try
{
Contracts.ILoader contract = _target.Contracts.Loader;
Contracts.ModuleHandle moduleHandle = contract.GetModuleHandleFromModulePtr(_address);
if (!_extentsSet)
{
hr = HResults.E_INVALIDARG;
}
else if (*handle == 1)
{
*handle += 1;
CLRDataModuleExtent* dataModuleExtent = (CLRDataModuleExtent*)extent;
dataModuleExtent->baseAddress = _extents[0].baseAddress;
dataModuleExtent->length = _extents[0].length;
dataModuleExtent->type = _extents[0].type;
}
else
{
hr = HResults.S_FALSE;
}
}
catch (System.Exception ex)
{
return ex.HResult;
}
#if DEBUG
if (_legacyModule is not null)
{
ulong handleLocal = legacyExtentHandle;
CLRDataModuleExtent dataModuleExtentLocal = default;
int hrLocal = _legacyModule.EnumExtent(&handleLocal, &dataModuleExtentLocal);
legacyExtentHandle = handleLocal;
Debug.Assert(hr == hrLocal, $"cDAC: {hr}, DAC: {hrLocal}");
if (hr == HResults.S_OK)
{
CLRDataModuleExtent* dataModuleExtent = (CLRDataModuleExtent*)extent;
Debug.Assert(dataModuleExtent->baseAddress == dataModuleExtentLocal.baseAddress, $"cDAC: {dataModuleExtent->baseAddress}, DAC: {dataModuleExtentLocal.baseAddress}");
Debug.Assert(dataModuleExtent->length == dataModuleExtentLocal.length, $"cDAC: {dataModuleExtent->length}, DAC: {dataModuleExtentLocal.length}");
Debug.Assert(dataModuleExtent->type == dataModuleExtentLocal.type, $"cDAC: {dataModuleExtent->type}, DAC: {dataModuleExtentLocal.type}");
}
}
#endif
return hr;
}
int IXCLRDataModule.EndEnumExtents(ulong handle)
{
#if DEBUG
if (_legacyModule is not null)
{
int hrLocal = _legacyModule.EndEnumExtents(handle);
Debug.Assert(hrLocal == HResults.S_OK, $"cDAC: {HResults.S_OK}, DAC: {hrLocal}");
}
#endif
return HResults.S_OK;
}
int IXCLRDataModule.Request(uint reqCode, uint inBufferSize, byte* inBuffer, uint outBufferSize, byte* outBuffer)
{
int hr = HResults.S_OK;
try
{
if (inBufferSize != 0 || inBuffer is not null || outBuffer is null)
throw new ArgumentException();
switch (reqCode)
{
case (uint)CLRDataGeneralRequest.CLRDATA_REQUEST_REVISION:
if (outBufferSize != sizeof(uint))
throw new ArgumentException();
*(uint*)outBuffer = 3;
break;
case 0xf0000000 /*DACDATAMODULEPRIV_REQUEST_GET_MODULEPTR*/:
if (outBufferSize != sizeof(DacpGetModuleAddress))
throw new ArgumentException();
((DacpGetModuleAddress*)outBuffer)->ModulePtr = _address.ToClrDataAddress(_target);
break;
case 0xf0000001 /*DACDATAMODULEPRIV_REQUEST_GET_MODULEDATA*/:
if (outBufferSize != sizeof(DacpGetModuleData))
throw new ArgumentException();
PopulateModuleData((DacpGetModuleData*)outBuffer);
break;
default:
throw new ArgumentException();
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacyModule is not null)
{
byte[] localBuffer = new byte[(int)outBufferSize];
fixed (byte* localOutBuffer = localBuffer)
{
int hrLocal = _legacyModule.Request(reqCode, inBufferSize, inBuffer, outBufferSize, localOutBuffer);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(new ReadOnlySpan<byte>(outBuffer, (int)outBufferSize).SequenceEqual(localBuffer));
}
}
#endif
return hr;
}
private void PopulateModuleData(DacpGetModuleData* getModuleData)
{
*getModuleData = default;
Contracts.ILoader contract = _target.Contracts.Loader;
Contracts.ModuleHandle moduleHandle = contract.GetModuleHandleFromModulePtr(_address);
TargetPointer peAssembly = contract.GetPEAssembly(moduleHandle);
bool isReflectionEmit = (contract.GetFlags(moduleHandle) & ModuleFlags.ReflectionEmit) != 0;
getModuleData->PEAssembly = _address.ToClrDataAddress(_target);
getModuleData->IsDynamic = isReflectionEmit ? 1u : 0u;
if (peAssembly != TargetPointer.Null)
{
// If isReflectionEmit or ProbeExtension is valid, the path is not valid.
if (isReflectionEmit || contract.IsProbeExtensionResultValid(moduleHandle))
{
getModuleData->IsInMemory = 1u;
}
else
{
getModuleData->IsInMemory = contract.GetPath(moduleHandle).Length == 0 ? 1u : 0u;
}
contract.TryGetLoadedImageContents(moduleHandle, out TargetPointer baseAddress, out uint size, out uint flags);
getModuleData->LoadedPEAddress = baseAddress.ToClrDataAddress(_target);
getModuleData->LoadedPESize = size;
// Can not get the assembly layout for a dynamic module
if (getModuleData->IsDynamic == 0u)
{
getModuleData->IsFileLayout = ((flags & /*FLAG_CONTENTS*/0x2) != 0) && !((flags & /*FLAG_MAPPED*/0x1) != 0) ? 1u : 0u; // HasContents && !IsMapped
}
}
if (contract.TryGetSymbolStream(moduleHandle, out TargetPointer symbolBuffer, out uint symbolBufferSize))
{
getModuleData->InMemoryPdbAddress = symbolBuffer.ToClrDataAddress(_target);
getModuleData->InMemoryPdbSize = symbolBufferSize;
}
}
int IXCLRDataModule.StartEnumAppDomains(ulong* handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.StartEnumAppDomains(handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.EnumAppDomain(ulong* handle, /*IXCLRDataAppDomain*/ void** appDomain)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EnumAppDomain(handle, appDomain) : HResults.E_NOTIMPL;
int IXCLRDataModule.EndEnumAppDomains(ulong handle)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.EndEnumAppDomains(handle) : HResults.E_NOTIMPL;
int IXCLRDataModule.GetVersionId(Guid* vid)
=> LegacyFallbackHelper.CanFallback() && _legacyModule is not null ? _legacyModule.GetVersionId(vid) : HResults.E_NOTIMPL;
int IXCLRDataModule2.SetJITCompilerFlags(uint flags)
{
int hr = HResults.S_OK;
try
{
if ((flags != CORDEBUG_JIT_DEFAULT) && (flags != CORDEBUG_JIT_DISABLE_OPTIMIZATION))
throw new ArgumentException();
Contracts.ILoader loader = _target.Contracts.Loader;
Contracts.ModuleHandle handle = loader.GetModuleHandleFromModulePtr(_address);
bool allowJitOpts = (flags & CORDEBUG_JIT_DISABLE_OPTIMIZATION) != CORDEBUG_JIT_DISABLE_OPTIMIZATION;
DebuggerAssemblyControlFlags bits = loader.GetDebuggerInfoBits(handle)
& ~(DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS | DebuggerAssemblyControlFlags.DACF_ENC_ENABLED);
bits &= DebuggerAssemblyControlFlags.DACF_CONTROL_FLAGS_MASK;
if (allowJitOpts)
{
bits |= DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS;
}
loader.SetDebuggerInfoBits(handle, bits);
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacyModule2 is not null)
{
int hrLocal = _legacyModule2.SetJITCompilerFlags(flags);
Debug.ValidateHResult(hr, hrLocal);
}
#endif
return hr;
}
}
|