Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Implement unwrapping a ComWrappers CCW when dumping a stowed exception.
  • Loading branch information
jkoritzinsky committed May 13, 2020
commit 34827b4280e0b3b876ca6a8d11c6663e43a016d3
1 change: 1 addition & 0 deletions src/coreclr/src/debug/daccess/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/src/debug/daccess/dacimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
22 changes: 19 additions & 3 deletions src/coreclr/src/debug/daccess/enummem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Lets add #ifdef FEATURE_COMWRAPPERS for consistency

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't add an #ifdef in the parentheses because this is a macro invocation. I can refactor out just these two ReportMem calls so we can correctly ifdef the calls if you'd prefer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I wasn't thinking about that. Don't worry about it, the comment is already a pretty good marker and if we captured one extra pointer in the future in a config with this feature disabled its not a big deal : )

ReportMem(vTableAddress + sizeof(PBYTE) * TEAR_OFF_SLOT, sizeof(TADDR)); // Report the AddRef slot on the vtable for built-in CCWs
);

return S_OK;
Expand Down
71 changes: 71 additions & 0 deletions src/coreclr/src/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
{
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/src/inc/daccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
27 changes: 17 additions & 10 deletions src/coreclr/src/interop/comwrappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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*);
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 14 additions & 0 deletions src/coreclr/src/interop/inc/interoplibabi.h
Original file line number Diff line number Diff line change
@@ -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);
}
}