From 34827b4280e0b3b876ca6a8d11c6663e43a016d3 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 13 May 2020 11:19:23 -0700 Subject: [PATCH 1/3] Implement unwrapping a ComWrappers CCW when dumping a stowed exception. --- 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 | 71 ++++++++++++++++++++ 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(+), 13 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 2be663ebd77ea..c04b6ccbb9a58 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,75 @@ 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; + CLRDATA_ADDRESS vTableAddress = NULL; + IfFailGo(m_pTarget->ReadVirtual(ccwPtr, (PBYTE)&vTableAddress, sizeof(CLRDATA_ADDRESS), &bytesRead)); + if (bytesRead != sizeof(CLRDATA_ADDRESS) + || vTableAddress == NULL) + { + hr = S_FALSE; + goto ErrExit; + } + + TADDR qiAddress = NULL; + IfFailGo(m_pTarget->ReadVirtual(vTableAddress, (PBYTE)&qiAddress, sizeof(TADDR), &bytesRead)); + if (bytesRead != sizeof(TADDR) + || qiAddress == NULL) + { + hr = S_FALSE; + 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 = S_FALSE; + goto ErrExit; + } + + // Mask the "dispatch pointer" to get a double pointer to the ManagedObjectWrapper + CLRDATA_ADDRESS managedObjectWrapperPtrPtr = ccwPtr & InteropLib::ABI::DispatchThisPtrMask; + + // Return ManagedObjectWrapper as an OBJECTHANDLE. (The OBJECTHANDLE is guaranteed to live at offset 0). + CLRDATA_ADDRESS managedObjectWrapperPtr; + IfFailGo(m_pTarget->ReadVirtual(managedObjectWrapperPtrPtr, (PBYTE)&managedObjectWrapperPtr, sizeof(CLRDATA_ADDRESS), &bytesRead)); + if (bytesRead != sizeof(CLRDATA_ADDRESS)) + { + hr = S_FALSE; + goto ErrExit; + } + + OBJECTHANDLE handle; + IfFailGo(m_pTarget->ReadVirtual(managedObjectWrapperPtr, (PBYTE)&handle, sizeof(OBJECTHANDLE), &bytesRead)); + if (bytesRead != sizeof(OBJECTHANDLE)) + { + hr = S_FALSE; + 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..39d317c201064 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,16 +180,21 @@ namespace ABI } } +// 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) +{ + ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); + return wrapper->QueryInterface(riid, ppvObject); +} + 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); - } ULONG STDMETHODCALLTYPE ManagedObjectWrapper_AddRef(_In_ ABI::ComInterfaceDispatch* disp) { @@ -310,6 +316,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); + } +} From 552e8c82d6e68ad10357a421e6f04b77430a3eff Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 13 May 2020 12:04:12 -0700 Subject: [PATCH 2/3] Remove extra line. --- src/coreclr/src/interop/comwrappers.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp index 39d317c201064..3fb1b7941ed75 100644 --- a/src/coreclr/src/interop/comwrappers.cpp +++ b/src/coreclr/src/interop/comwrappers.cpp @@ -183,7 +183,6 @@ namespace ABI // 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, @@ -195,7 +194,6 @@ HRESULT STDMETHODCALLTYPE ManagedObjectWrapper_QueryInterface( namespace { - ULONG STDMETHODCALLTYPE ManagedObjectWrapper_AddRef(_In_ ABI::ComInterfaceDispatch* disp) { ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp); From 14b34da0780c206bc151801599fc4eb437f5c2db Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 14 May 2020 10:59:00 -0700 Subject: [PATCH 3/3] Change usage of CLRDATA_ADDRESS to TADDR. --- src/coreclr/src/debug/daccess/request.cpp | 25 ++++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/coreclr/src/debug/daccess/request.cpp b/src/coreclr/src/debug/daccess/request.cpp index c04b6ccbb9a58..80606ce093e48 100644 --- a/src/coreclr/src/debug/daccess/request.cpp +++ b/src/coreclr/src/debug/daccess/request.cpp @@ -4077,12 +4077,13 @@ HRESULT ClrDataAccess::DACTryGetComWrappersObjectFromCCW(CLRDATA_ADDRESS ccwPtr, // Read CCWs QI address and compare it to the managed object wrapper's implementation. ULONG32 bytesRead = 0; - CLRDATA_ADDRESS vTableAddress = NULL; - IfFailGo(m_pTarget->ReadVirtual(ccwPtr, (PBYTE)&vTableAddress, sizeof(CLRDATA_ADDRESS), &bytesRead)); - if (bytesRead != sizeof(CLRDATA_ADDRESS) + 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 = S_FALSE; + hr = E_FAIL; goto ErrExit; } @@ -4091,7 +4092,7 @@ HRESULT ClrDataAccess::DACTryGetComWrappersObjectFromCCW(CLRDATA_ADDRESS ccwPtr, if (bytesRead != sizeof(TADDR) || qiAddress == NULL) { - hr = S_FALSE; + hr = E_FAIL; goto ErrExit; } @@ -4103,19 +4104,19 @@ HRESULT ClrDataAccess::DACTryGetComWrappersObjectFromCCW(CLRDATA_ADDRESS ccwPtr, if (qiAddress != GetEEFuncEntryPoint(ManagedObjectWrapper_QueryInterface)) { - hr = S_FALSE; + hr = E_FAIL; goto ErrExit; } // Mask the "dispatch pointer" to get a double pointer to the ManagedObjectWrapper - CLRDATA_ADDRESS managedObjectWrapperPtrPtr = ccwPtr & InteropLib::ABI::DispatchThisPtrMask; + TADDR managedObjectWrapperPtrPtr = ccw & InteropLib::ABI::DispatchThisPtrMask; // Return ManagedObjectWrapper as an OBJECTHANDLE. (The OBJECTHANDLE is guaranteed to live at offset 0). - CLRDATA_ADDRESS managedObjectWrapperPtr; - IfFailGo(m_pTarget->ReadVirtual(managedObjectWrapperPtrPtr, (PBYTE)&managedObjectWrapperPtr, sizeof(CLRDATA_ADDRESS), &bytesRead)); - if (bytesRead != sizeof(CLRDATA_ADDRESS)) + TADDR managedObjectWrapperPtr; + IfFailGo(m_pTarget->ReadVirtual(managedObjectWrapperPtrPtr, (PBYTE)&managedObjectWrapperPtr, sizeof(TADDR), &bytesRead)); + if (bytesRead != sizeof(TADDR)) { - hr = S_FALSE; + hr = E_FAIL; goto ErrExit; } @@ -4123,7 +4124,7 @@ HRESULT ClrDataAccess::DACTryGetComWrappersObjectFromCCW(CLRDATA_ADDRESS ccwPtr, IfFailGo(m_pTarget->ReadVirtual(managedObjectWrapperPtr, (PBYTE)&handle, sizeof(OBJECTHANDLE), &bytesRead)); if (bytesRead != sizeof(OBJECTHANDLE)) { - hr = S_FALSE; + hr = E_FAIL; goto ErrExit; }