diff --git a/docs/design/datacontracts/ComWrappers.md b/docs/design/datacontracts/ComWrappers.md index 73c3bcd9930728..2a83a8044ec5b6 100644 --- a/docs/design/datacontracts/ComWrappers.md +++ b/docs/design/datacontracts/ComWrappers.md @@ -7,6 +7,14 @@ This contract is for getting information related to COM wrappers. ``` csharp // Get the address of the external COM object TargetPointer GetComWrappersIdentity(TargetPointer rcw); +// Given a ccw pointer, return the managed object wrapper +public TargetPointer GetManagedObjectWrapperFromCCW(TargetPointer ccw); +// Given a managed object wrapper, return the comwrappers pointer +public TargetPointer GetComWrappersObjectFromMOW(TargetPointer mow); +// Given a managed object wrapper, return its reference count +public long GetMOWReferenceCount(TargetPointer mow); +// Determine if a pointer represents a ComWrappers RCW +public bool IsComWrappersRCW(TargetPointer rcw); ``` ## Version 1 @@ -15,14 +23,28 @@ Data descriptors used: | Data Descriptor Name | Field | Meaning | | --- | --- | --- | | `NativeObjectWrapperObject` | `ExternalComObject` | Address of the external COM object | +| `ManagedObjectWrapperHolderObject` | `WrappedObject` | Address of the wrapped object | +| `ManagedObjectWrapperLayout` | `RefCount` | Reference count of the managed object wrapper | +| `ComWrappersVtablePtrs` | `Size` | Size of vtable pointers array | Global variables used: | Global Name | Type | Purpose | | --- | --- | --- | +| `ComWrappersVtablePtrs` | TargetPointer | Pointer to struct containing ComWrappers-related function pointers | +| `DispatchThisPtrMask` | TargetPointer | Used to mask low bits of CCW pointer to the nearest valid address from which to read a managed object wrapper | + +### Contract Constants: +| Name | Type | Purpose | Value | +| --- | --- | --- | --- | +| `NativeObjectWrapperNamespace` | string | Namespace of System.Runtime.InteropServices.ComWrappers+NativeObjectWrapper | `System.Runtime.InteropServices` | +| `NativeObjectWrapperName` | string | Name of System.Runtime.InteropServices.ComWrappers+NativeObjectWrapper | `ComWrappers+NativeObjectWrapper` | Contracts used: | Contract Name | | --- | +| `Object` | +| `RuntimeTypeSystem` | +| `Loader` | ``` csharp @@ -30,4 +52,59 @@ public TargetPointer GetComWrappersIdentity(TargetPointer address) { return _target.ReadPointer(address + /* NativeObjectWrapperObject::ExternalComObject offset */); } + +private bool GetComWrappersCCWVTableQIAddress(TargetPointer ccw, out TargetPointer vtable, out TargetPointer qiAddress) +{ + vtable = TargetPointer.Null; + qiAddress = TargetPointer.Null; + + // read two levels of indirection from the ccw to get the code pointer into qiAddress + // if read fails, return false + + qiAddress = CodePointerUtils.AddressFromCodePointer(qiAddress, _target); + return true; +} + +private bool IsComWrappersCCW(TargetPointer ccw) +{ + if (!GetComWrappersCCWVTableQIAddress(ccw, out _, out TargetPointer qiAddress)) + return false; + + TargetPointer comWrappersVtablePtrs = _target.ReadGlobalPointer("ComWrappersVtablePtrs"); + + return /* qiAddress matches any entry in ComWrappersVtablePtrs */ ; +} + +public TargetPointer GetManagedObjectWrapperFromCCW(TargetPointer ccw) +{ + if (!IsComWrappersCCW(ccw)) + return TargetPointer.Null; + try + { + return _target.ReadPointer(ccw & _target.ReadGlobalPointer("DispatchThisPtrMask")); + } + catch (VirtualReadException) + { + return TargetPointer.Null; + } +} + +public TargetPointer GetComWrappersObjectFromMOW(TargetPointer mow) +{ + TargetPointer mowHolderObject = /* read two layers of indirection from MOW */; + return mowHolderObject + /* ManagedObjectWrapperHolderObject::WrappedObject offset */; +} + +public long GetMOWReferenceCount(TargetPointer mow) +{ + return target.Read(mow + /* ManagedObjectWrapperLayout::RefCount offset */); +} + +public bool IsComWrappersRCW(TargetPointer rcw) +{ + // Get method table from rcw using Object contract GetMethodTableAddress + // Find module from the system assembly + // Then use RuntimeTypeSystem contract to look up type handle by name/namespace hardcoded in contract + // Then compare the rcw method table with the method table found by name/namespace/module +} ``` diff --git a/docs/design/datacontracts/Loader.md b/docs/design/datacontracts/Loader.md index caa97a1ce68373..2634c5a27a807e 100644 --- a/docs/design/datacontracts/Loader.md +++ b/docs/design/datacontracts/Loader.md @@ -79,6 +79,7 @@ IEnumerable<(TargetPointer, uint)> EnumerateModuleLookupMap(TargetPointer table) bool IsCollectible(ModuleHandle handle); bool IsAssemblyLoaded(ModuleHandle handle); TargetPointer GetGlobalLoaderAllocator(); +TargetPointer GetSystemAssembly(); TargetPointer GetHighFrequencyHeap(TargetPointer loaderAllocatorPointer); TargetPointer GetLowFrequencyHeap(TargetPointer loaderAllocatorPointer); TargetPointer GetStubHeap(TargetPointer loaderAllocatorPointer); @@ -134,6 +135,7 @@ TargetPointer GetDynamicIL(ModuleHandle handle, uint token); | `AppDomain` | `DomainAssemblyList` | ArrayListBase of assemblies in the AppDomain | | `AppDomain` | `FriendlyName` | Friendly name of the AppDomain | | `SystemDomain` | `GlobalLoaderAllocator` | global LoaderAllocator | +| `SystemDomain` | `SystemAssembly` | pointer to the system Assembly | | `LoaderAllocator` | `ReferenceCount` | Reference count of LoaderAllocator | | `LoaderAllocator` | `HighFrequencyHeap` | High-frequency heap of LoaderAllocator | | `LoaderAllocator` | `LowFrequencyHeap` | Low-frequency heap of LoaderAllocator | @@ -619,7 +621,14 @@ TargetPointer GetGlobalLoaderAllocator() { TargetPointer systemDomainPointer = target.ReadGlobalPointer("SystemDomain"); TargetPointer systemDomain = target.ReadPointer(systemDomainPointer); - return target.ReadPointer(systemDomain + /* SystemDomain::GlobalLoaderAllocator offset */); + return systemDomain + /* SystemDomain::GlobalLoaderAllocator offset */; +} + +TargetPointer GetSystemAssembly() +{ + TargetPointer systemDomainPointer = target.ReadGlobalPointer("SystemDomain"); + TargetPointer systemDomain = target.ReadPointer(systemDomainPointer); + return target.ReadPointer(systemDomain + /* SystemDomain::SystemAssembly offset */); } TargetPointer GetHighFrequencyHeap(TargetPointer loaderAllocatorPointer) diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index c1a59f690d307c..6cee27c4adf2d1 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -4361,8 +4361,14 @@ BOOL ClrDataAccess::DACIsComWrappersCCW(CLRDATA_ADDRESS ccwPtr) return FALSE; } - return (qiAddress == GetEEFuncEntryPoint(ManagedObjectWrapper_QueryInterface) - || qiAddress == GetEEFuncEntryPoint(TrackerTarget_QueryInterface)); + for (unsigned int i = 0; i < g_numKnownQueryInterfaceImplementations; i++) + { + if (PINSTRToPCODE(qiAddress) == g_knownQueryInterfaceImplementations[i]) + { + return TRUE; + } + } + return FALSE; } TADDR ClrDataAccess::DACGetManagedObjectWrapperFromCCW(CLRDATA_ADDRESS ccwPtr) diff --git a/src/coreclr/debug/ee/dactable.cpp b/src/coreclr/debug/ee/dactable.cpp index 9c65b163cf2799..4c8fe9ab7bc0d7 100644 --- a/src/coreclr/debug/ee/dactable.cpp +++ b/src/coreclr/debug/ee/dactable.cpp @@ -25,26 +25,6 @@ extern PCODE g_FCDynamicallyAssignedImplementations[ECall::NUM_DYNAMICALLY_ASSIGNED_FCALL_IMPLEMENTATIONS]; extern "C" void STDCALL ThePreStubPatchLabel(void); -#ifdef FEATURE_COMWRAPPERS -// Keep these forward declarations in sync with the method definitions in interop/comwrappers.cpp -namespace InteropLib -{ - namespace ABI - { - struct ComInterfaceDispatch; - } -} -HRESULT STDMETHODCALLTYPE ManagedObjectWrapper_QueryInterface( - _In_ InteropLib::ABI::ComInterfaceDispatch* disp, - /* [in] */ REFIID riid, - /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject); -HRESULT STDMETHODCALLTYPE TrackerTarget_QueryInterface( - _In_ InteropLib::ABI::ComInterfaceDispatch* disp, - /* [in] */ REFIID riid, - /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject); - -#endif - template class U> struct is_type_template_instantiation { diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 5803ae1f33b3b5..25395995074281 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -208,6 +208,10 @@ DEFINE_DACVAR(PTR_SyncTableEntry, dac__g_pSyncTable, ::g_pSyncTable) DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pRCWCleanupList, ::g_pRCWCleanupList) #endif // FEATURE_COMINTEROP +#ifdef FEATURE_COMWRAPPERS +DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_knownQueryInterfaceImplementations, InteropLib::ABI::g_knownQueryInterfaceImplementations) +#endif // FEATURE_COMWRAPPERS + #ifndef TARGET_UNIX DEFINE_DACVAR(SIZE_T, dac__g_runtimeLoadedBaseAddress, ::g_runtimeLoadedBaseAddress) DEFINE_DACVAR(SIZE_T, dac__g_runtimeVirtualSize, ::g_runtimeVirtualSize) diff --git a/src/coreclr/inc/gfunc_list.h b/src/coreclr/inc/gfunc_list.h index b7bfa5dc6a5ebd..9631099ff92f46 100644 --- a/src/coreclr/inc/gfunc_list.h +++ b/src/coreclr/inc/gfunc_list.h @@ -19,7 +19,3 @@ DEFINE_DACGFN(Unknown_AddRef) DEFINE_DACGFN(Unknown_AddRefSpecial) DEFINE_DACGFN(Unknown_AddRefInner) #endif -#ifdef FEATURE_COMWRAPPERS -DEFINE_DACGFN(ManagedObjectWrapper_QueryInterface) -DEFINE_DACGFN(TrackerTarget_QueryInterface) -#endif diff --git a/src/coreclr/interop/comwrappers.cpp b/src/coreclr/interop/comwrappers.cpp index 05fb629cf2282f..414fc6670547ea 100644 --- a/src/coreclr/interop/comwrappers.cpp +++ b/src/coreclr/interop/comwrappers.cpp @@ -59,19 +59,17 @@ namespace ABI } } -// ManagedObjectWrapper_QueryInterface needs to be visible outside of this compilation unit -// to support the DAC (look for the GetEEFuncEntryPoint call). -HRESULT STDMETHODCALLTYPE ManagedObjectWrapper_QueryInterface( +namespace +{ + HRESULT STDMETHODCALLTYPE ManagedObjectWrapper_QueryInterface( _In_ ABI::ComInterfaceDispatch* disp, /* [in] */ REFIID riid, /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) -{ - ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); - return wrapper->QueryInterface(riid, ppvObject); -} + { + ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); + return wrapper->QueryInterface(riid, ppvObject); + } -namespace -{ ULONG STDMETHODCALLTYPE ManagedObjectWrapper_AddRef(_In_ ABI::ComInterfaceDispatch* disp) { ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); @@ -99,36 +97,6 @@ namespace static_assert(sizeof(ManagedObjectWrapper_IUnknownImpl) == (3 * sizeof(void*)), "Unexpected vtable size"); } -// TrackerTarget_QueryInterface needs to be visible outside of this compilation unit -// to support the DAC (look for the GetEEFuncEntryPoint call). -HRESULT STDMETHODCALLTYPE TrackerTarget_QueryInterface( - _In_ ABI::ComInterfaceDispatch* disp, - /* [in] */ REFIID riid, - /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) -{ - ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); - - // AddRef is "safe" at this point because since it is a MOW with an outstanding - // Reference Tracker reference, we know for sure the MOW is not claimed yet - // but the managed object could be. If the managed object is alive at this - // moment the AddRef will ensure it remains alive for the duration of the - // QueryInterface. - ComHolder ensureStableLifetime{ wrapper }; - - // For MOWs that have outstanding Reference Tracker reference, they could be either: - // 1. Marked to Destroy - in this case it is unsafe to touch wrapper. - // 2. Object Handle target has been NULLed out by GC. - if (wrapper->IsMarkedToDestroy() - || !InteropLibImports::HasValidTarget(wrapper->GetTarget())) - { - // It is unsafe to proceed with a QueryInterface call. The MOW has been - // marked destroyed or the associated managed object has been collected. - return COR_E_ACCESSING_CCW; - } - - return wrapper->QueryInterface(riid, ppvObject); -} - namespace { const int32_t TrackerRefShift = 32; @@ -152,6 +120,34 @@ namespace return (c & DestroySentinel) != 0; } + HRESULT STDMETHODCALLTYPE TrackerTarget_QueryInterface( + _In_ ABI::ComInterfaceDispatch* disp, + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) + { + ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); + + // AddRef is "safe" at this point because since it is a MOW with an outstanding + // Reference Tracker reference, we know for sure the MOW is not claimed yet + // but the managed object could be. If the managed object is alive at this + // moment the AddRef will ensure it remains alive for the duration of the + // QueryInterface. + ComHolder ensureStableLifetime{ wrapper }; + + // For MOWs that have outstanding Reference Tracker reference, they could be either: + // 1. Marked to Destroy - in this case it is unsafe to touch wrapper. + // 2. Object Handle target has been NULLed out by GC. + if (wrapper->IsMarkedToDestroy() + || !InteropLibImports::HasValidTarget(wrapper->GetTarget())) + { + // It is unsafe to proceed with a QueryInterface call. The MOW has been + // marked destroyed or the associated managed object has been collected. + return COR_E_ACCESSING_CCW; + } + + return wrapper->QueryInterface(riid, ppvObject); + } + ULONG STDMETHODCALLTYPE TrackerTarget_AddRefFromReferenceTracker(_In_ ABI::ComInterfaceDispatch* disp) { _ASSERTE(disp != nullptr && disp->vtable != nullptr); @@ -527,3 +523,13 @@ InteropLib::OBJECTHANDLE ManagedObjectWrapper::GetTarget() const { return _target; } + +using QueryInterfaceMethod = HRESULT (STDMETHODCALLTYPE *)(InteropLib::ABI::ComInterfaceDispatch*, REFIID, void**); +namespace InteropLib { namespace ABI { + struct ComInterfaceDispatch; + QueryInterfaceMethod g_knownQueryInterfaceImplementations[] = { + &ManagedObjectWrapper_QueryInterface, + &TrackerTarget_QueryInterface + }; + } +} diff --git a/src/coreclr/interop/inc/interoplibabi.h b/src/coreclr/interop/inc/interoplibabi.h index 217ecda8b73e28..bf066c92a47b50 100644 --- a/src/coreclr/interop/inc/interoplibabi.h +++ b/src/coreclr/interop/inc/interoplibabi.h @@ -6,6 +6,7 @@ #include #include +#include "../../vm/cdacdata.h" namespace InteropLib { @@ -22,7 +23,7 @@ namespace InteropLib constexpr size_t DispatchAlignmentThisPtr = 16; // Should be a power of 2. #endif - constexpr intptr_t DispatchThisPtrMask = ~(DispatchAlignmentThisPtr - 1); + constexpr uintptr_t DispatchThisPtrMask = ~(DispatchAlignmentThisPtr - 1u); static_assert(sizeof(void*) < DispatchAlignmentThisPtr, "DispatchAlignmentThisPtr must be larger than sizeof(void*)."); @@ -55,6 +56,7 @@ namespace InteropLib // This is designed to codify the binary layout. struct ManagedObjectWrapperLayout { + friend struct ::cdac_data; public: LONGLONG GetRawRefCount() const { @@ -81,4 +83,10 @@ namespace InteropLib } } +template<> +struct cdac_data +{ + static constexpr size_t RefCount = offsetof(InteropLib::ABI::ManagedObjectWrapperLayout, _refCount); +}; + #endif // _INTEROP_INC_INTEROPLIBABI_H_ diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 0109c2d5b8798a..18ba1c41a962e0 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -1882,6 +1882,7 @@ struct cdac_data { static constexpr PTR_SystemDomain* SystemDomainPtr = &SystemDomain::m_pSystemDomain; static constexpr size_t GlobalLoaderAllocator = offsetof(SystemDomain, m_GlobalAllocator); + static constexpr size_t SystemAssembly = offsetof(SystemDomain, m_pSystemAssembly); }; #endif // DACCESS_COMPILE diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 185cae0dd920bc..6d14f5b05a8575 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -418,7 +418,7 @@ END_ILLINK_FEATURE_SWITCH() DEFINE_CLASS(COMWRAPPERS, Interop, ComWrappers) DEFINE_CLASS(CREATEOBJECTFLAGS, Interop, CreateObjectFlags) DEFINE_CLASS(MANAGED_OBJECT_WRAPPER_HOLDER,Interop, ComWrappers+ManagedObjectWrapperHolder) -DEFINE_CLASS(NATIVE_OBJECT_WRAPPER, Interop, ComWrappers+NativeObjectWrapper) +DEFINE_CLASS(NATIVE_OBJECT_WRAPPER, Interop, ComWrappers+NativeObjectWrapper) // cDAC depends on the exact namespace and name DEFINE_METHOD(COMWRAPPERS, CALL_ICUSTOMQUERYINTERFACE, CallICustomQueryInterface, SM_ManagedObjectWrapperHolder_RefGuid_RefIntPtr_RetInt) DEFINE_METHOD(COMWRAPPERS, GET_OR_CREATE_COM_INTERFACE_FOR_OBJECT_WITH_GLOBAL_MARSHALLING_INSTANCE, GetOrCreateComInterfaceForObjectWithGlobalMarshallingInstance, SM_Obj_RetIntPtr) DEFINE_METHOD(COMWRAPPERS, GET_OR_CREATE_OBJECT_FOR_COM_INSTANCE_WITH_GLOBAL_MARSHALLING_INSTANCE, GetOrCreateObjectForComInstanceWithGlobalMarshallingInstance, SM_IntPtr_CreateObjectFlags_RetObj) diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 660f79cf77edcb..1e61487e26f921 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -153,6 +153,16 @@ CDAC_TYPE_BEGIN(NativeObjectWrapperObject) CDAC_TYPE_INDETERMINATE(NativeObjectWrapperObject) CDAC_TYPE_FIELD(NativeObjectWrapperObject, /*pointer*/, ExternalComObject, cdac_data::ExternalComObject) CDAC_TYPE_END(NativeObjectWrapperObject) + +CDAC_TYPE_BEGIN(ManagedObjectWrapperHolderObject) +CDAC_TYPE_INDETERMINATE(ManagedObjectWrapperHolderObject) +CDAC_TYPE_FIELD(ManagedObjectWrapperHolderObject, /*pointer*/, WrappedObject, cdac_data::WrappedObject) +CDAC_TYPE_END(ManagedObjectWrapperHolderObject) + +CDAC_TYPE_BEGIN(ManagedObjectWrapperLayout) +CDAC_TYPE_INDETERMINATE(ManagedObjectWrapperLayout) +CDAC_TYPE_FIELD(ManagedObjectWrapperLayout, /*int64*/, RefCount, cdac_data::RefCount) +CDAC_TYPE_END(ManagedObjectWrapperLayout) #endif // FEATURE_COMWRAPPERS CDAC_TYPE_BEGIN(SyncTableEntry) @@ -261,6 +271,7 @@ CDAC_TYPE_END(AppDomain) CDAC_TYPE_BEGIN(SystemDomain) CDAC_TYPE_INDETERMINATE(SystemDomain) CDAC_TYPE_FIELD(SystemDomain, /*GlobalLoaderAllocator*/, GlobalLoaderAllocator, cdac_data::GlobalLoaderAllocator) +CDAC_TYPE_FIELD(SystemDomain, /*pointer*/, SystemAssembly, cdac_data::SystemAssembly) CDAC_TYPE_END(SystemDomain) CDAC_TYPE_BEGIN(ArrayListBase) @@ -1006,6 +1017,12 @@ CDAC_TYPE_FIELD(DynamicILBlobTable, /*uint32*/, EntryMethodToken, cdac_data::EntryIL) CDAC_TYPE_END(DynamicILBlobTable) +#ifdef FEATURE_COMWRAPPERS +CDAC_TYPE_BEGIN(ComWrappersVtablePtrs) +CDAC_TYPE_SIZE(g_numKnownQueryInterfaceImplementations * sizeof(PCODE)) +CDAC_TYPE_END(ComWrappersVtablePtrs) +#endif + CDAC_TYPES_END() CDAC_GLOBALS_BEGIN() @@ -1111,6 +1128,10 @@ CDAC_GLOBAL_POINTER(MiniMetaDataBuffMaxSize, &::g_MiniMetaDataBuffMaxSize) CDAC_GLOBAL_POINTER(DacNotificationFlags, &::g_dacNotificationFlags) CDAC_GLOBAL_POINTER(OffsetOfCurrentThreadInfo, &::g_offsetOfCurrentThreadInfo) CDAC_GLOBAL_POINTER(ThinlockThreadIdDispenser, &::g_pThinLockThreadIdDispenser) +#ifdef FEATURE_COMWRAPPERS +CDAC_GLOBAL(DispatchThisPtrMask, uintptr_t, InteropLib::ABI::DispatchThisPtrMask) +CDAC_GLOBAL_POINTER(ComWrappersVtablePtrs, InteropLib::ABI::g_knownQueryInterfaceImplementations) +#endif // FEATURE_COMWRAPPERS CDAC_GLOBAL_POINTER(GcNotificationFlags, &::g_gcNotificationFlags) CDAC_GLOBAL_POINTER(CoreLib, &::g_CoreLib) #ifdef TARGET_WINDOWS diff --git a/src/coreclr/vm/interoplibinterface_comwrappers.h b/src/coreclr/vm/interoplibinterface_comwrappers.h index ce19830fa98119..2defdae7b5931f 100644 --- a/src/coreclr/vm/interoplibinterface_comwrappers.h +++ b/src/coreclr/vm/interoplibinterface_comwrappers.h @@ -77,6 +77,13 @@ class ManagedObjectWrapperHolderObject : public Object public: OBJECTREF _wrappedObject; DPTR(InteropLib::ABI::ManagedObjectWrapperLayout) _wrapper; + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t WrappedObject = offsetof(ManagedObjectWrapperHolderObject, _wrappedObject); }; class NativeObjectWrapperObject : public Object diff --git a/src/coreclr/vm/vars.cpp b/src/coreclr/vm/vars.cpp index f6fd1eac774627..02e97870b61671 100644 --- a/src/coreclr/vm/vars.cpp +++ b/src/coreclr/vm/vars.cpp @@ -94,6 +94,10 @@ GPTR_IMPL_INIT(StressLog, g_pStressLog, &StressLog::theLog); GPTR_IMPL(RCWCleanupList,g_pRCWCleanupList); #endif // FEATURE_COMINTEROP +#ifdef FEATURE_COMWRAPPERS +GARY_IMPL(TADDR, g_knownQueryInterfaceImplementations, g_numKnownQueryInterfaceImplementations); +#endif // FEATURE_COMWRAPPERS + #ifdef FEATURE_INTEROP_DEBUGGING GVAL_IMPL_INIT(DWORD, g_debuggerWordTLSIndex, TLS_OUT_OF_INDEXES); #endif diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp index 246291fdeab900..e3c065bb9d4710 100644 --- a/src/coreclr/vm/vars.hpp +++ b/src/coreclr/vm/vars.hpp @@ -40,6 +40,19 @@ class SyncBlockCache; class SyncTableEntry; class ThreadStore; namespace ETW { class CEtwTracer; }; +#ifdef FEATURE_COMWRAPPERS +inline constexpr size_t g_numKnownQueryInterfaceImplementations = 2; +namespace InteropLib { namespace ABI { + struct ComInterfaceDispatch; + using QueryInterfaceMethod = HRESULT (STDMETHODCALLTYPE *)(InteropLib::ABI::ComInterfaceDispatch*, REFIID, void**); +#ifndef DACCESS_COMPILE + extern QueryInterfaceMethod g_knownQueryInterfaceImplementations[g_numKnownQueryInterfaceImplementations]; +#endif // !DACCESS_COMPILE +} } + +GARY_DECL(TADDR, g_knownQueryInterfaceImplementations, g_numKnownQueryInterfaceImplementations); + +#endif // FEATURE_COMWRAPPERS class DebugInterface; class DebugInfoManager; class EEDbgInterfaceImpl; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IComWrappers.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IComWrappers.cs index 257012cb5528e0..2904e98ccda794 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IComWrappers.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IComWrappers.cs @@ -9,6 +9,10 @@ public interface IComWrappers : IContract { static string IContract.Name { get; } = nameof(ComWrappers); TargetPointer GetComWrappersIdentity(TargetPointer address) => throw new NotImplementedException(); + TargetPointer GetManagedObjectWrapperFromCCW(TargetPointer ccw) => throw new NotImplementedException(); + TargetPointer GetComWrappersObjectFromMOW(TargetPointer mow) => throw new NotImplementedException(); + long GetMOWReferenceCount(TargetPointer mow) => throw new NotImplementedException(); + bool IsComWrappersRCW(TargetPointer rcw) => throw new NotImplementedException(); } public readonly struct ComWrappers : IComWrappers diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs index 24481bf29c7505..794fc78a10cb2e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs @@ -104,6 +104,7 @@ public interface ILoader : IContract bool IsAssemblyLoaded(ModuleHandle handle) => throw new NotImplementedException(); TargetPointer GetGlobalLoaderAllocator() => throw new NotImplementedException(); + TargetPointer GetSystemAssembly() => throw new NotImplementedException(); TargetPointer GetHighFrequencyHeap(TargetPointer loaderAllocatorPointer) => throw new NotImplementedException(); TargetPointer GetLowFrequencyHeap(TargetPointer loaderAllocatorPointer) => throw new NotImplementedException(); TargetPointer GetStubHeap(TargetPointer loaderAllocatorPointer) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index 4c6d5a94291017..b750abb30a753c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -143,6 +143,7 @@ public interface IRuntimeTypeSystem : IContract TypeHandle GetTypeParam(TypeHandle typeHandle) => throw new NotImplementedException(); TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray typeArguments) => throw new NotImplementedException(); TypeHandle GetPrimitiveType(CorElementType typeCode) => throw new NotImplementedException(); + TypeHandle GetTypeByNameAndModule(string name, string nameSpace, ModuleHandle moduleHandle) => throw new NotImplementedException(); bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token) => throw new NotImplementedException(); bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan retAndArgTypes, out byte callConv) => throw new NotImplementedException(); bool IsPointer(TypeHandle typeHandle) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index 35f243961ee539..6be64a591afb9d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -69,6 +69,9 @@ public enum DataType StressMsgHeader, Object, NativeObjectWrapperObject, + ManagedObjectWrapperHolderObject, + ManagedObjectWrapperLayout, + ComWrappersVtablePtrs, String, MethodDesc, MethodDescChunk, diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Target.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Target.cs index e6bb21976d84b3..850aab0f488320 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Target.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Target.cs @@ -59,6 +59,14 @@ public abstract class Target /// Thrown when the read operation fails public abstract TargetPointer ReadPointer(ulong address); + /// + /// Read a pointer from the target in target endianness + /// + /// Address to start reading from + /// Pointer read from the target + /// True if read succeeds, false otherwise. + public abstract bool TryReadPointer(ulong address, out TargetPointer value); + /// /// Read a code pointer from the target in target endianness /// @@ -67,6 +75,14 @@ public abstract class Target /// Thrown when the read operation fails public abstract TargetCodePointer ReadCodePointer(ulong address); + /// + /// Read a code pointer from the target in target endianness + /// + /// Address to start reading from + /// Pointer read from the target + /// True if read succeeds, false otherwise. + public abstract bool TryReadCodePointer(ulong address, out TargetCodePointer value); + /// /// Read some bytes from the target /// diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs index 1903486453ce0d..427ef634e7726a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -32,6 +32,8 @@ public static class Globals public const string OffsetOfCurrentThreadInfo = nameof(OffsetOfCurrentThreadInfo); public const string TlsIndexBase = nameof(TlsIndexBase); public const string ThinlockThreadIdDispenser = nameof(ThinlockThreadIdDispenser); + public const string DispatchThisPtrMask = nameof(DispatchThisPtrMask); + public const string ComWrappersVtablePtrs = nameof(ComWrappersVtablePtrs); public const string GcNotificationFlags = nameof(GcNotificationFlags); public const string StressLogEnabled = nameof(StressLogEnabled); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ComWrappers_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ComWrappers_1.cs index 98b0a54dc0c416..0b9ce1d310cdff 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ComWrappers_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ComWrappers_1.cs @@ -10,6 +10,8 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; internal readonly struct ComWrappers_1 : IComWrappers { + private const string NativeObjectWrapperNamespace = "System.Runtime.InteropServices"; + private const string NativeObjectWrapperName = "ComWrappers+NativeObjectWrapper"; private readonly Target _target; public ComWrappers_1(Target target) @@ -22,4 +24,63 @@ public TargetPointer GetComWrappersIdentity(TargetPointer address) Data.NativeObjectWrapperObject wrapper = _target.ProcessedData.GetOrAdd(address); return wrapper.ExternalComObject; } + + private bool GetComWrappersCCWVTableQIAddress(TargetPointer ccw, out TargetPointer vtable, out TargetPointer qiAddress) + { + qiAddress = TargetPointer.Null; + if (!_target.TryReadPointer(ccw, out vtable)) + return false; + if (!_target.TryReadCodePointer(vtable, out TargetCodePointer qiCodePtr)) + return false; + qiAddress = CodePointerUtils.AddressFromCodePointer(qiCodePtr, _target); + return true; + } + + private bool IsComWrappersCCW(TargetPointer ccw) + { + if (!GetComWrappersCCWVTableQIAddress(ccw, out _, out TargetPointer qiAddress)) + return false; + + TargetPointer comWrappersVtablePtrs = _target.ReadGlobalPointer(Constants.Globals.ComWrappersVtablePtrs); + Data.ComWrappersVtablePtrs comWrappersVtableStruct = _target.ProcessedData.GetOrAdd(comWrappersVtablePtrs); + return comWrappersVtableStruct.ComWrappersInterfacePointers.Contains(CodePointerUtils.CodePointerFromAddress(qiAddress, _target)); + } + + public TargetPointer GetManagedObjectWrapperFromCCW(TargetPointer ccw) + { + if (!IsComWrappersCCW(ccw)) + return TargetPointer.Null; + if (!_target.TryReadPointer(ccw & _target.ReadGlobalPointer(Constants.Globals.DispatchThisPtrMask), out TargetPointer MOWWrapper)) + return TargetPointer.Null; + return MOWWrapper; + } + + public TargetPointer GetComWrappersObjectFromMOW(TargetPointer mow) + { + TargetPointer objHandle = _target.ReadPointer(mow); + Data.ObjectHandle handle = _target.ProcessedData.GetOrAdd(objHandle); + Data.ManagedObjectWrapperHolderObject mowHolderObject = _target.ProcessedData.GetOrAdd(handle.Object); + return mowHolderObject.WrappedObject; + } + + public long GetMOWReferenceCount(TargetPointer mow) + { + Data.ManagedObjectWrapperLayout layout = _target.ProcessedData.GetOrAdd(mow); + return layout.RefCount; + } + + public bool IsComWrappersRCW(TargetPointer rcw) + { + TargetPointer mt = _target.Contracts.Object.GetMethodTableAddress(rcw); + + // get system module + ILoader loader = _target.Contracts.Loader; + TargetPointer systemAssembly = loader.GetSystemAssembly(); + ModuleHandle moduleHandle = loader.GetModuleHandleFromAssemblyPtr(systemAssembly); + + // lookup by name + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + TargetPointer typeHandlePtr = rts.GetTypeByNameAndModule(NativeObjectWrapperName, NativeObjectWrapperNamespace, moduleHandle).Address; + return mt == typeHandlePtr; + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs index ce3d398d053da4..3dc6d559eb8557 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs @@ -506,6 +506,13 @@ TargetPointer ILoader.GetGlobalLoaderAllocator() return systemDomain.GlobalLoaderAllocator; } + TargetPointer ILoader.GetSystemAssembly() + { + TargetPointer systemDomainPointer = _target.ReadGlobalPointer(Constants.Globals.SystemDomain); + Data.SystemDomain systemDomain = _target.ProcessedData.GetOrAdd(_target.ReadPointer(systemDomainPointer)); + return systemDomain.SystemAssembly; + } + TargetPointer ILoader.GetHighFrequencyHeap(TargetPointer loaderAllocatorPointer) { Data.LoaderAllocator loaderAllocator = _target.ProcessedData.GetOrAdd(loaderAllocatorPointer); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index 58318207cf47d1..fc8e5628905669 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -9,6 +9,7 @@ using Microsoft.Diagnostics.DataContractReader.Data; using System.Reflection.Metadata; using System.Collections.Immutable; +using System.Reflection; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -26,6 +27,7 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem private readonly Dictionary _methodTables = new(); private readonly Dictionary _methodDescs = new(); private readonly Dictionary _typeHandles = new(); + private readonly Dictionary _typeHandlesByName = new(); internal struct MethodTable { @@ -97,6 +99,22 @@ public override int GetHashCode() } } + private readonly struct TypeKeyByName : IEquatable + { + public TypeKeyByName(string name, string namespaceName, TargetPointer module) + { + Name = name; + Namespace = namespaceName; + Module = module; + } + public string Name { get; } + public string Namespace { get; } + public TargetPointer Module { get; } + public bool Equals(TypeKeyByName other) => Name == other.Name && Namespace == other.Namespace && Module == other.Module; + public override bool Equals(object? obj) => obj is TypeKeyByName other && Equals(other); + public override int GetHashCode() => HashCode.Combine(Name, Namespace, Module); + } + // Low order bits of TypeHandle address. // If the low bits contain a 2, then it is a TypeDesc [Flags] @@ -801,6 +819,121 @@ TypeHandle IRuntimeTypeSystem.GetPrimitiveType(CorElementType typeCode) return GetTypeHandle(typeHandlePtr); } + private static bool ModuleMatch(AssemblyReference assemblyRef, AssemblyDefinition assemblyDef) + { + AssemblyName assemblyRefName = assemblyRef.GetAssemblyName(); + AssemblyName assemblyDefName = assemblyDef.GetAssemblyName(); + if ((assemblyRefName.Name != assemblyDefName.Name) || + (assemblyRefName.Version != assemblyDefName.Version) || + (assemblyRefName.CultureName != assemblyDefName.CultureName)) + { + return false; + } + + ReadOnlySpan refToken = assemblyRefName.GetPublicKeyToken(); + ReadOnlySpan defToken = assemblyDefName.GetPublicKeyToken(); + return refToken.SequenceEqual(defToken); + } + + private MetadataReader? LookForHandle(AssemblyReference exportedAssemblyRef) + { + TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain); + TargetPointer appDomain = _target.ReadPointer(appDomainPointer); + foreach (ModuleHandle mdhandle in _target.Contracts.Loader.GetModuleHandles(appDomain, AssemblyIterationFlags.IncludeLoaded)) + { + MetadataReader? md2 = _target.Contracts.EcmaMetadata.GetMetadata(mdhandle); + if (md2 == null) + continue; + AssemblyDefinition assemblyDefinition = md2.GetAssemblyDefinition(); + if (ModuleMatch(exportedAssemblyRef, assemblyDefinition)) + { + return md2; + } + } + return null; + } + + TypeHandle IRuntimeTypeSystem.GetTypeByNameAndModule(string name, string nameSpace, ModuleHandle moduleHandle) + { + ILoader loader = _target.Contracts.Loader; + TargetPointer modulePtr = loader.GetModule(moduleHandle); + if (_typeHandlesByName.TryGetValue(new TypeKeyByName(name, nameSpace, modulePtr), out TypeHandle existing)) + return existing; + string[] parts = name.Split('+'); + string outerName = parts[0]; + MetadataReader? md = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle); + TypeDefinitionHandle currentHandle = default; + // create a hash set of MDs and if we come across the same one more than once in a loop then we return null typehandle + HashSet seenMDs = new(); + // 1. find the outer type + while (md != null && seenMDs.Add(md)) + { + foreach (TypeDefinitionHandle typeDefHandle in md.TypeDefinitions) + { + TypeDefinition typedef = md.GetTypeDefinition(typeDefHandle); + if (md.GetString(typedef.Name) == outerName && md.GetString(typedef.Namespace) == nameSpace) + { + // found our outermost type, remember it + currentHandle = typeDefHandle; + break; + } + } + + if (currentHandle == default) + { + // look for forwarded types + foreach (ExportedTypeHandle exportedTypeHandle in md.ExportedTypes) + { + ExportedType exportedType = md.GetExportedType(exportedTypeHandle); + if (exportedType.Implementation.Kind != HandleKind.AssemblyReference || !exportedType.IsForwarder) + continue; + if (md.GetString(exportedType.Name) == outerName && md.GetString(exportedType.Namespace) == nameSpace) + { + // get the assembly ref for target + AssemblyReferenceHandle arefHandle = (AssemblyReferenceHandle)exportedType.Implementation; + AssemblyReference exportedAssemblyRef = md.GetAssemblyReference(arefHandle); + md = LookForHandle(exportedAssemblyRef); + break; + } + } + } + else break; // if we found our typedef without forwarding break out of the while loop + } + + if (currentHandle == default) + return new TypeHandle(TargetPointer.Null); + + // 2. Walk down the nested types + for (int i = 1; i < parts.Length; i++) + { + string nestedName = parts[i]; + bool found = false; + foreach (TypeDefinitionHandle nestedHandle in md!.GetTypeDefinition(currentHandle).GetNestedTypes()) + { + TypeDefinition nestedDef = md.GetTypeDefinition(nestedHandle); + if (md.GetString(nestedDef.Name) == nestedName) + { + currentHandle = nestedHandle; + found = true; + break; + } + } + if (!found) + return new TypeHandle(TargetPointer.Null); + } + + // 3. We have the handle, look up the type handle + int token = MetadataTokens.GetToken((EntityHandle)currentHandle); + TargetPointer typeDefToMethodTable = loader.GetLookupTables(moduleHandle).TypeDefToMethodTable; + TargetPointer typeHandlePtr = loader.GetModuleLookupMapElement(typeDefToMethodTable, (uint)token, out _); + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + if (typeHandlePtr == TargetPointer.Null) + return new TypeHandle(TargetPointer.Null); + TypeHandle foundTypeHandle = rts.GetTypeHandle(typeHandlePtr); + _ = _typeHandlesByName.TryAdd(new TypeKeyByName(name, nameSpace, modulePtr), foundTypeHandle); + return foundTypeHandle; + } + public bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token) { module = TargetPointer.Null; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ComWrappersVtablePtrs.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ComWrappersVtablePtrs.cs new file mode 100644 index 00000000000000..36826c9baaf2c5 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ComWrappersVtablePtrs.cs @@ -0,0 +1,22 @@ +// 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.Collections.Generic; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class ComWrappersVtablePtrs : IData +{ + static ComWrappersVtablePtrs IData.Create(Target target, TargetPointer address) => new ComWrappersVtablePtrs(target, address); + public ComWrappersVtablePtrs(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.ComWrappersVtablePtrs); + for (int i = 0; i < type.Size / target.PointerSize; i++) + { + ComWrappersInterfacePointers.Add(target.ReadCodePointer(address + (ulong)(i * target.PointerSize))); + } + } + + public List ComWrappersInterfacePointers { get; init; } = new List(); +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ManagedObjectWrapperHolderObject.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ManagedObjectWrapperHolderObject.cs new file mode 100644 index 00000000000000..39c4851d87790c --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ManagedObjectWrapperHolderObject.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class ManagedObjectWrapperHolderObject : IData +{ + static ManagedObjectWrapperHolderObject IData.Create(Target target, TargetPointer address) => new ManagedObjectWrapperHolderObject(target, address); + public ManagedObjectWrapperHolderObject(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.ManagedObjectWrapperHolderObject); + WrappedObject = target.ReadPointer(address + (ulong)type.Fields[nameof(WrappedObject)].Offset); + } + + public TargetPointer WrappedObject { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ManagedObjectWrapperLayout.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ManagedObjectWrapperLayout.cs new file mode 100644 index 00000000000000..0dfbc1d0bc124b --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ManagedObjectWrapperLayout.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class ManagedObjectWrapperLayout : IData +{ + static ManagedObjectWrapperLayout IData.Create(Target target, TargetPointer address) => new ManagedObjectWrapperLayout(target, address); + public ManagedObjectWrapperLayout(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.ManagedObjectWrapperLayout); + RefCount = target.Read(address + (ulong)type.Fields[nameof(RefCount)].Offset); + } + + public long RefCount { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/SystemDomain.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/SystemDomain.cs index e3596be4def1bb..526ff3cdda71d0 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/SystemDomain.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/SystemDomain.cs @@ -12,7 +12,9 @@ public SystemDomain(Target target, TargetPointer address) { Target.TypeInfo type = target.GetTypeInfo(DataType.SystemDomain); GlobalLoaderAllocator = address + (ulong)type.Fields[nameof(GlobalLoaderAllocator)].Offset; + SystemAssembly = target.ReadPointer(address + (ulong)type.Fields[nameof(SystemAssembly)].Offset); } public TargetPointer GlobalLoaderAllocator { get; init; } + public TargetPointer SystemAssembly { get; init; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs index 4ac39286bb3dad..58b62628fe68be 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -41,6 +41,7 @@ public sealed unsafe partial class SOSDacImpl // They should be set when actually requested via other DAC APIs, so we lazily read the global pointers. private readonly Lazy _stringMethodTable; private readonly Lazy _objectMethodTable; + private readonly ulong _rcwMask = 1UL; private readonly ISOSDacInterface? _legacyImpl; private readonly ISOSDacInterface2? _legacyImpl2; @@ -4049,25 +4050,134 @@ int ISOSDacInterface9.GetBreakingChangeVersion() int ISOSDacInterface10.GetObjectComWrappersData(ClrDataAddress objAddr, ClrDataAddress* rcw, uint count, ClrDataAddress* mowList, uint* pNeeded) => _legacyImpl10 is not null ? _legacyImpl10.GetObjectComWrappersData(objAddr, rcw, count, mowList, pNeeded) : HResults.E_NOTIMPL; int ISOSDacInterface10.IsComWrappersCCW(ClrDataAddress ccw, Interop.BOOL* isComWrappersCCW) - => _legacyImpl10 is not null ? _legacyImpl10.IsComWrappersCCW(ccw, isComWrappersCCW) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + Contracts.IComWrappers comWrappersContract = _target.Contracts.ComWrappers; + if (ccw == 0) + throw new ArgumentException(); + + if (isComWrappersCCW != null) + { + TargetPointer ccwPtr = comWrappersContract.GetManagedObjectWrapperFromCCW(ccw.ToTargetPointer(_target)); + *isComWrappersCCW = (ccwPtr != TargetPointer.Null) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + hr = (ccwPtr != TargetPointer.Null) ? HResults.S_OK : HResults.S_FALSE; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacyImpl10 is not null) + { + Interop.BOOL isComWrappersCCWLocal; + int hrLocal = _legacyImpl10.IsComWrappersCCW(ccw, &isComWrappersCCWLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK || hr == HResults.S_FALSE) + { + Debug.Assert(*isComWrappersCCW == isComWrappersCCWLocal); + } + } +#endif + return hr; + } int ISOSDacInterface10.GetComWrappersCCWData(ClrDataAddress ccw, ClrDataAddress* managedObject, int* refCount) - => _legacyImpl10 is not null ? _legacyImpl10.GetComWrappersCCWData(ccw, managedObject, refCount) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + if (ccw == 0) + throw new ArgumentException(); + TargetPointer ccwPtr = ccw.ToTargetPointer(_target); + Contracts.IComWrappers comWrappersContract = _target.Contracts.ComWrappers; + TargetPointer managedObjectPtr = comWrappersContract.GetManagedObjectWrapperFromCCW(ccwPtr); + if (managedObjectPtr == TargetPointer.Null) + throw new ArgumentException(); + + if (managedObject != null) + { + *managedObject = 0; + *managedObject = comWrappersContract.GetComWrappersObjectFromMOW(managedObjectPtr).ToClrDataAddress(_target); + } + + if (refCount != null) + *refCount = (int)comWrappersContract.GetMOWReferenceCount(managedObjectPtr); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacyImpl10 is not null) + { + ClrDataAddress managedObjectLocal; + int refCountLocal; + int hrLocal = _legacyImpl10.GetComWrappersCCWData(ccw, &managedObjectLocal, &refCountLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + if (managedObject != null) + Debug.Assert(*managedObject == managedObjectLocal); + if (refCount != null) + Debug.Assert(*refCount == refCountLocal); + } + } +#endif + return hr; + } int ISOSDacInterface10.IsComWrappersRCW(ClrDataAddress rcw, Interop.BOOL* isComWrappersRCW) - => _legacyImpl10 is not null ? _legacyImpl10.IsComWrappersRCW(rcw, isComWrappersRCW) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + Contracts.IComWrappers comWrappersContract = _target.Contracts.ComWrappers; + if (rcw == 0) + throw new ArgumentException(); + else if (isComWrappersRCW != null) + { + if ((rcw & _rcwMask) == 0) + *isComWrappersRCW = Interop.BOOL.FALSE; + else + { + TargetPointer rcwPtr = rcw.ToTargetPointer(_target) & ~_rcwMask; + *isComWrappersRCW = comWrappersContract.IsComWrappersRCW(rcwPtr) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + } + hr = (*isComWrappersRCW != Interop.BOOL.FALSE) ? HResults.S_OK : HResults.S_FALSE; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacyImpl10 is not null) + { + Interop.BOOL isComWrappersRCWLocal; + int hrLocal = _legacyImpl10.IsComWrappersRCW(rcw, &isComWrappersRCWLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK || hr == HResults.S_FALSE) + { + Debug.Assert(*isComWrappersRCW == isComWrappersRCWLocal); + } + } +#endif + return hr; + } int ISOSDacInterface10.GetComWrappersRCWData(ClrDataAddress rcw, ClrDataAddress* identity) { int hr = HResults.S_OK; try { - ulong rcwMask = 1UL; Contracts.IComWrappers comWrappersContract = _target.Contracts.ComWrappers; if (rcw == 0 || identity == null) throw new ArgumentException(); - else if ((rcw & rcwMask) == 0) + else if ((rcw & _rcwMask) == 0) *identity = 0; - else if (identity != null) + else { - TargetPointer identityPtr = comWrappersContract.GetComWrappersIdentity((rcw.ToTargetPointer(_target) & ~rcwMask)); + TargetPointer identityPtr = comWrappersContract.GetComWrappersIdentity(rcw.ToTargetPointer(_target) & ~_rcwMask); *identity = identityPtr.ToClrDataAddress(_target); } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/ContractDescriptorTarget.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/ContractDescriptorTarget.cs index 50d138a7c507df..db0ff00bdcd7f5 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/ContractDescriptorTarget.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/ContractDescriptorTarget.cs @@ -526,6 +526,9 @@ public override TargetPointer ReadPointer(ulong address) return pointer; } + public override bool TryReadPointer(ulong address, out TargetPointer value) + => TryReadPointer(address, _config, _dataTargetDelegates, out value); + public override TargetPointer ReadPointerFromSpan(ReadOnlySpan bytes) { if (_config.PointerSize == sizeof(uint)) @@ -552,6 +555,29 @@ public override TargetCodePointer ReadCodePointer(ulong address) throw new VirtualReadException($"Failed to read code pointer at 0x{address:x8} because CodePointer size is not 4 or 8"); } + public override bool TryReadCodePointer(ulong address, out TargetCodePointer value) + { + TypeInfo codePointerTypeInfo = GetTypeInfo(DataType.CodePointer); + if (codePointerTypeInfo.Size is sizeof(uint)) + { + if (TryRead(address, out uint val)) + { + value = new TargetCodePointer(val); + return true; + } + } + else if (codePointerTypeInfo.Size is sizeof(ulong)) + { + if (TryRead(address, out ulong val)) + { + value = new TargetCodePointer(val); + return true; + } + } + value = default; + return false; + } + public void ReadPointers(ulong address, Span buffer) { // TODO(cdac) - This could do a single read, and then swizzle in place if it is useful for performance diff --git a/src/native/managed/cdac/tests/TestPlaceholderTarget.cs b/src/native/managed/cdac/tests/TestPlaceholderTarget.cs index 289873f6222ca2..f503c9eeb5adc1 100644 --- a/src/native/managed/cdac/tests/TestPlaceholderTarget.cs +++ b/src/native/managed/cdac/tests/TestPlaceholderTarget.cs @@ -77,7 +77,16 @@ public override TargetPointer ReadGlobalPointer(string name) } public override TargetPointer ReadPointer(ulong address) => DefaultReadPointer(address); + public override bool TryReadPointer(ulong address, out TargetPointer value) => DefaultTryReadPointer(address, out value); public override TargetCodePointer ReadCodePointer(ulong address) => DefaultReadCodePointer(address); + public override bool TryReadCodePointer(ulong address, out TargetCodePointer value) + { + value = default; + if (!DefaultTryReadPointer(address, out TargetPointer ptr)) + return false; + value = new TargetCodePointer(ptr); + return true; + } public override void ReadBuffer(ulong address, Span buffer) { if (_dataReader(address, buffer) < 0)