From 10c60fcdbe2d8173c62154ed80b5f71b564733a0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 14 May 2020 16:27:35 -0700 Subject: [PATCH] Implement unwrapping a ComWrappers CCW when dumping a stowed exception. (#36360) --- src/coreclr/src/debug/daccess/CMakeLists.txt | 1 + src/coreclr/src/debug/daccess/dacimpl.h | 4 ++ src/coreclr/src/debug/daccess/enummem.cpp | 22 +++++- src/coreclr/src/debug/daccess/request.cpp | 72 ++++++++++++++++++++ src/coreclr/src/inc/daccess.h | 3 + src/coreclr/src/interop/comwrappers.cpp | 27 +++++--- src/coreclr/src/interop/inc/interoplibabi.h | 14 ++++ 7 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 src/coreclr/src/interop/inc/interoplibabi.h diff --git a/src/coreclr/src/debug/daccess/CMakeLists.txt b/src/coreclr/src/debug/daccess/CMakeLists.txt index 6d18ad64fded8..b683d762885b4 100644 --- a/src/coreclr/src/debug/daccess/CMakeLists.txt +++ b/src/coreclr/src/debug/daccess/CMakeLists.txt @@ -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}) diff --git a/src/coreclr/src/debug/daccess/dacimpl.h b/src/coreclr/src/debug/daccess/dacimpl.h index 958adb1fd9178..29183751fb53e 100644 --- a/src/coreclr/src/debug/daccess/dacimpl.h +++ b/src/coreclr/src/debug/daccess/dacimpl.h @@ -1456,6 +1456,10 @@ class ClrDataAccess 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: diff --git a/src/coreclr/src/debug/daccess/enummem.cpp b/src/coreclr/src/debug/daccess/enummem.cpp index 6a3feec6cf9f5..5c5f402b99ad0 100644 --- a/src/coreclr/src/debug/daccess/enummem.cpp +++ b/src/coreclr/src/debug/daccess/enummem.cpp @@ -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; diff --git a/src/coreclr/src/debug/daccess/request.cpp b/src/coreclr/src/debug/daccess/request.cpp index 79880ff064e5e..8fc4896abdcd1 100644 --- a/src/coreclr/src/debug/daccess/request.cpp +++ b/src/coreclr/src/debug/daccess/request.cpp @@ -20,6 +20,8 @@ #include #endif // FEATURE_COMINTEROP +#include + #ifndef TARGET_UNIX // It is unfortunate having to include this header just to get the definition of GenericModeBlock #include @@ -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) { diff --git a/src/coreclr/src/inc/daccess.h b/src/coreclr/src/inc/daccess.h index 2c68abff92c18..f2b37c32db749 100644 --- a/src/coreclr/src/inc/daccess.h +++ b/src/coreclr/src/inc/daccess.h @@ -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. diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index 60d80910a5078..3fb1b7941ed75 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. #include "comwrappers.hpp" +#include #include #include // 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 index 0000000000000..19a1e0c01e755 --- /dev/null +++ b/src/coreclr/src/interop/inc/interoplibabi.h @@ -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 + +namespace InteropLib +{ + namespace ABI + { + const size_t DispatchAlignmentThisPtr = 16; // Should be a power of 2. + const intptr_t DispatchThisPtrMask = ~(DispatchAlignmentThisPtr - 1); + } +}