Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions docs/design/datacontracts/ComWrappers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -15,19 +23,88 @@ 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
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<long>(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
}
```
11 changes: 10 additions & 1 deletion docs/design/datacontracts/Loader.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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)
Expand Down
10 changes: 8 additions & 2 deletions src/coreclr/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 0 additions & 20 deletions src/coreclr/debug/ee/dactable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<typename T, template<typename> class U>
struct is_type_template_instantiation
{
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/inc/dacvars.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/inc/gfunc_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
84 changes: 45 additions & 39 deletions src/coreclr/interop/comwrappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<ManagedObjectWrapper> 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;
Expand All @@ -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<ManagedObjectWrapper> 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);
Expand Down Expand Up @@ -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
};
}
}
10 changes: 9 additions & 1 deletion src/coreclr/interop/inc/interoplibabi.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <stddef.h>
#include <interoplib.h>
#include "../../vm/cdacdata.h"

namespace InteropLib
{
Expand All @@ -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*).");

Expand Down Expand Up @@ -55,6 +56,7 @@ namespace InteropLib
// This is designed to codify the binary layout.
struct ManagedObjectWrapperLayout
{
friend struct ::cdac_data<InteropLib::ABI::ManagedObjectWrapperLayout>;
public:
LONGLONG GetRawRefCount() const
{
Expand All @@ -81,4 +83,10 @@ namespace InteropLib
}
}

template<>
struct cdac_data<InteropLib::ABI::ManagedObjectWrapperLayout>
{
static constexpr size_t RefCount = offsetof(InteropLib::ABI::ManagedObjectWrapperLayout, _refCount);
};

#endif // _INTEROP_INC_INTEROPLIBABI_H_
1 change: 1 addition & 0 deletions src/coreclr/vm/appdomain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1882,6 +1882,7 @@ struct cdac_data<SystemDomain>
{
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

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading
Loading