Implement unwrapping a ComWrappers CCW when dumping a stowed exception. (#36360)
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Thu, 14 May 2020 23:27:35 +0000 (16:27 -0700)
committerGitHub <noreply@github.com>
Thu, 14 May 2020 23:27:35 +0000 (16:27 -0700)
src/coreclr/src/debug/daccess/CMakeLists.txt
src/coreclr/src/debug/daccess/dacimpl.h
src/coreclr/src/debug/daccess/enummem.cpp
src/coreclr/src/debug/daccess/request.cpp
src/coreclr/src/inc/daccess.h
src/coreclr/src/interop/comwrappers.cpp
src/coreclr/src/interop/inc/interoplibabi.h [new file with mode: 0644]

index 6d18ad6..b683d76 100644 (file)
@@ -5,6 +5,7 @@ include_directories(BEFORE ${VM_DIR}/${ARCH_SOURCES_DIR})
 include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
 include_directories(${CLR_DIR}/src/debug/ee)
 include_directories(${CLR_DIR}/src/gcdump)
+include_directories(${CLR_DIR}/src/interop/inc)
 
 if(CLR_CMAKE_HOST_UNIX)
   include_directories(${GENERATED_INCLUDE_DIR})
index 958adb1..2918375 100644 (file)
@@ -1456,6 +1456,10 @@ private:
     PTR_IUnknown DACGetCOMIPFromCCW(PTR_ComCallWrapper pCCW, int vtableIndex);
 #endif
 
+#ifdef FEATURE_COMWRAPPERS
+    HRESULT DACTryGetComWrappersObjectFromCCW(CLRDATA_ADDRESS ccwPtr, OBJECTREF* objRef);
+#endif
+
     static LONG s_procInit;
 
 public:
index 6a3feec..5c5f402 100644 (file)
@@ -1298,7 +1298,7 @@ HRESULT ClrDataAccess::EnumMemDumpAllThreadsStack(CLRDataEnumMemoryFlags flags)
 }
 
 
-#ifdef FEATURE_COMINTEROP
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 //
 // WinRT stowed exception holds the (CCW)pointer to a managed exception object.
@@ -1431,11 +1431,26 @@ HRESULT ClrDataAccess::DumpStowedExceptionObject(CLRDataEnumMemoryFlags flags, C
     if (ccwPtr == NULL)
         return S_OK;
 
+    OBJECTREF managedExceptionObject = NULL;
+
+#ifdef FEATURE_COMINTEROP
     // dump the managed exception object wrapped in CCW
     // memory of the CCW object itself is dumped later by DacInstanceManager::DumpAllInstances
     DacpCCWData ccwData;
     GetCCWData(ccwPtr, &ccwData);   // this call collects some memory implicitly
-    DumpManagedExcepObject(flags, OBJECTREF(TO_TADDR(ccwData.managedObject)));
+    managedExceptionObject = OBJECTREF(CLRDATA_ADDRESS_TO_TADDR(ccwData.managedObject));
+#endif
+#ifdef FEATURE_COMWRAPPERS
+    if (managedExceptionObject == NULL)
+    {
+        OBJECTREF wrappedObjAddress;
+        if (DACTryGetComWrappersObjectFromCCW(ccwPtr, &wrappedObjAddress) == S_OK)
+        {
+            managedExceptionObject = wrappedObjAddress;
+        }
+    }
+#endif
+    DumpManagedExcepObject(flags, managedExceptionObject);
 
     // dump memory of the 2nd slot in the CCW's vtable
     // this is used in DACGetCCWFromAddress to identify if the passed in pointer is a valid CCW.
@@ -1450,7 +1465,8 @@ HRESULT ClrDataAccess::DumpStowedExceptionObject(CLRDataEnumMemoryFlags flags, C
 
     CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED
     (
-        ReportMem(vTableAddress + sizeof(PBYTE)* TEAR_OFF_SLOT, sizeof(TADDR));
+        ReportMem(vTableAddress, sizeof(TADDR)); // Report the QI slot on the vtable for ComWrappers
+        ReportMem(vTableAddress + sizeof(PBYTE) * TEAR_OFF_SLOT, sizeof(TADDR)); // Report the AddRef slot on the vtable for built-in CCWs
     );
 
     return S_OK;
index 79880ff..8fc4896 100644 (file)
@@ -20,6 +20,8 @@
 #include <comcallablewrapper.h>
 #endif // FEATURE_COMINTEROP
 
+#include <interoplibabi.h>
+
 #ifndef TARGET_UNIX
 // It is unfortunate having to include this header just to get the definition of GenericModeBlock
 #include <msodw.h>
@@ -4065,6 +4067,76 @@ PTR_IUnknown ClrDataAccess::DACGetCOMIPFromCCW(PTR_ComCallWrapper pCCW, int vtab
 }
 #endif
 
+#ifdef FEATURE_COMWRAPPERS
+HRESULT ClrDataAccess::DACTryGetComWrappersObjectFromCCW(CLRDATA_ADDRESS ccwPtr, OBJECTREF* objRef)
+{
+    if (ccwPtr == 0 || objRef == NULL)
+        return E_INVALIDARG;
+
+    SOSDacEnter();
+
+    // Read CCWs QI address and compare it to the managed object wrapper's implementation.
+    ULONG32 bytesRead = 0;
+    TADDR ccw = CLRDATA_ADDRESS_TO_TADDR(ccwPtr);
+    TADDR vTableAddress = NULL;
+    IfFailGo(m_pTarget->ReadVirtual(ccw, (PBYTE)&vTableAddress, sizeof(TADDR), &bytesRead));
+    if (bytesRead != sizeof(TADDR)
+        || vTableAddress == NULL)
+    {
+        hr = E_FAIL;
+        goto ErrExit;
+    }
+
+    TADDR qiAddress = NULL;
+    IfFailGo(m_pTarget->ReadVirtual(vTableAddress, (PBYTE)&qiAddress, sizeof(TADDR), &bytesRead));
+    if (bytesRead != sizeof(TADDR)
+        || qiAddress == NULL)
+    {
+        hr = E_FAIL;
+        goto ErrExit;
+    }
+
+
+#ifdef TARGET_ARM
+    // clear the THUMB bit on qiAddress before comparing with known vtable entry
+    qiAddress &= ~THUMB_CODE;
+#endif
+
+    if (qiAddress != GetEEFuncEntryPoint(ManagedObjectWrapper_QueryInterface))
+    {
+        hr = E_FAIL;
+        goto ErrExit;
+    }
+
+    // Mask the "dispatch pointer" to get a double pointer to the ManagedObjectWrapper
+    TADDR managedObjectWrapperPtrPtr = ccw & InteropLib::ABI::DispatchThisPtrMask;
+
+    // Return ManagedObjectWrapper as an OBJECTHANDLE. (The OBJECTHANDLE is guaranteed to live at offset 0).
+    TADDR managedObjectWrapperPtr;
+    IfFailGo(m_pTarget->ReadVirtual(managedObjectWrapperPtrPtr, (PBYTE)&managedObjectWrapperPtr, sizeof(TADDR), &bytesRead));
+    if (bytesRead != sizeof(TADDR))
+    {
+        hr = E_FAIL;
+        goto ErrExit;
+    }
+
+    OBJECTHANDLE handle;
+    IfFailGo(m_pTarget->ReadVirtual(managedObjectWrapperPtr, (PBYTE)&handle, sizeof(OBJECTHANDLE), &bytesRead));
+    if (bytesRead != sizeof(OBJECTHANDLE))
+    {
+        hr = E_FAIL;
+        goto ErrExit;
+    }
+
+    *objRef = ObjectFromHandle(handle);
+
+    SOSDacLeave();
+
+    return S_OK;
+
+ErrExit: return hr;
+}
+#endif
 
 HRESULT ClrDataAccess::GetCCWData(CLRDATA_ADDRESS ccw, struct DacpCCWData *ccwData)
 {
index 2c68abf..f2b37c3 100644 (file)
@@ -629,6 +629,9 @@ typedef struct _DacGlobals
     ULONG fn__Unknown_AddRefSpecial;
     ULONG fn__Unknown_AddRefInner;
 #endif
+#ifdef FEATURE_COMWRAPPERS
+    ULONG fn__ManagedObjectWrapper_QueryInterface;
+#endif
 
     // Vtable pointer values for all classes that must
     // be instanted using vtable pointers as the identity.
index 60d8091..3fb1b79 100644 (file)
@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 #include "comwrappers.hpp"
+#include <interoplibabi.h>
 #include <interoplibimports.h>
 
 #include <new> // placement new
@@ -47,8 +48,8 @@ namespace ABI
     };
     ABI_ASSERT(sizeof(ComInterfaceDispatch) == sizeof(void*));
 
-    const size_t DispatchAlignmentThisPtr = 16; // Should be a power of 2.
-    const intptr_t DispatchThisPtrMask = ~(DispatchAlignmentThisPtr - 1);
+    using InteropLib::ABI::DispatchAlignmentThisPtr;
+    using InteropLib::ABI::DispatchThisPtrMask;
     ABI_ASSERT(sizeof(void*) < DispatchAlignmentThisPtr);
 
     const intptr_t AlignmentThisPtrMaxPadding = DispatchAlignmentThisPtr - sizeof(void*);
@@ -179,17 +180,20 @@ namespace ABI
     }
 }
 
-namespace
+// ManagedObjectWrapper_QueryInterface needs to be visible outside of this compilation unit
+// to support the DAC. See code:ClrDataAccess::DACTryGetComWrappersObjectFromCCW for the
+// usage in the DAC (look for the GetEEFuncEntryPoint call).
+HRESULT STDMETHODCALLTYPE ManagedObjectWrapper_QueryInterface(
+    _In_ ABI::ComInterfaceDispatch* disp,
+    /* [in] */ REFIID riid,
+    /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
 {
-    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);
@@ -310,6 +314,7 @@ void ManagedObjectWrapper::GetIUnknownImpl(
     *fpRelease = ManagedObjectWrapper_IUnknownImpl.Release;
 }
 
+// The logic here should match code:ClrDataAccess::DACTryGetComWrappersObjectFromCCW in daccess/request.cpp
 ManagedObjectWrapper* ManagedObjectWrapper::MapFromIUnknown(_In_ IUnknown* pUnk)
 {
     _ASSERTE(pUnk != nullptr);
diff --git a/src/coreclr/src/interop/inc/interoplibabi.h b/src/coreclr/src/interop/inc/interoplibabi.h
new file mode 100644 (file)
index 0000000..19a1e0c
--- /dev/null
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <stddef.h>
+
+namespace InteropLib
+{
+    namespace ABI
+    {
+        const size_t DispatchAlignmentThisPtr = 16; // Should be a power of 2.
+        const intptr_t DispatchThisPtrMask = ~(DispatchAlignmentThisPtr - 1);
+    }
+}