From b7baeda99eb546b55e9d9f0545cb0dc59c606f1c Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 22 Aug 2024 09:41:20 -0700 Subject: [PATCH] Remove Helper Method Frames from all `StubHelpers` (#106793) * Convert GetCOMIPFromRCW * Convert AllocateInternal * Convert GetHRExceptionObject and GetCOMHRExceptionObject. * Convert MarshalToManagedVaListInternal and MarshalToUnmanagedVaListInternal. * Convert ValidateObject and ValidateByref. --- .../src/System/StubHelpers.cs | 69 ++-- src/coreclr/vm/corelib.h | 5 +- src/coreclr/vm/dllimport.cpp | 12 +- src/coreclr/vm/ecalllist.h | 7 - src/coreclr/vm/ilmarshalers.cpp | 13 +- src/coreclr/vm/qcallentrypoints.cpp | 7 + src/coreclr/vm/stubhelpers.cpp | 367 +++++++++--------- src/coreclr/vm/stubhelpers.h | 44 +-- 8 files changed, 256 insertions(+), 268 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs index bf3fd65a5cdb2f..e689e2d114e4e1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs @@ -1353,25 +1353,26 @@ internal static void DestroyCleanupList(ref CleanupWorkListElement? pCleanupWork internal static Exception GetHRExceptionObject(int hr) { - Exception ex = InternalGetHRExceptionObject(hr); - ex.InternalPreserveStackTrace(); - return ex; + Exception? ex = null; + GetHRExceptionObject(hr, ObjectHandleOnStack.Create(ref ex)); + ex!.InternalPreserveStackTrace(); + return ex!; } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Exception InternalGetHRExceptionObject(int hr); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "StubHelpers_GetHRExceptionObject")] + private static partial void GetHRExceptionObject(int hr, ObjectHandleOnStack throwable); #if FEATURE_COMINTEROP internal static Exception GetCOMHRExceptionObject(int hr, IntPtr pCPCMD, object pThis) { - Exception ex = InternalGetCOMHRExceptionObject(hr, pCPCMD, pThis); - ex.InternalPreserveStackTrace(); - return ex; + Exception? ex = null; + GetCOMHRExceptionObject(hr, pCPCMD, ObjectHandleOnStack.Create(ref pThis), ObjectHandleOnStack.Create(ref ex)); + ex!.InternalPreserveStackTrace(); + return ex!; } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern Exception InternalGetCOMHRExceptionObject(int hr, IntPtr pCPCMD, object? pThis); - + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "StubHelpers_GetCOMHRExceptionObject")] + private static partial void GetCOMHRExceptionObject(int hr, IntPtr pCPCMD, ObjectHandleOnStack pThis, ObjectHandleOnStack throwable); #endif // FEATURE_COMINTEROP [ThreadStatic] @@ -1426,7 +1427,27 @@ internal static void SafeHandleRelease(SafeHandle pHandle) #if FEATURE_COMINTEROP [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr GetCOMIPFromRCW(object objSrc, IntPtr pCPCMD, out IntPtr ppTarget, out bool pfNeedsRelease); + private static extern IntPtr GetCOMIPFromRCW(object objSrc, IntPtr pCPCMD, out IntPtr ppTarget); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "StubHelpers_GetCOMIPFromRCWSlow")] + private static partial IntPtr GetCOMIPFromRCWSlow(ObjectHandleOnStack objSrc, IntPtr pCPCMD, out IntPtr ppTarget); + + internal static IntPtr GetCOMIPFromRCW(object objSrc, IntPtr pCPCMD, out IntPtr ppTarget, out bool pfNeedsRelease) + { + IntPtr rcw = GetCOMIPFromRCW(objSrc, pCPCMD, out ppTarget); + if (rcw == IntPtr.Zero) + { + // If we didn't find the COM interface pointer in the cache we need to release the pointer. + pfNeedsRelease = true; + return GetCOMIPFromRCWWorker(objSrc, pCPCMD, out ppTarget); + } + pfNeedsRelease = false; + return rcw; + + [MethodImpl(MethodImplOptions.NoInlining)] + static IntPtr GetCOMIPFromRCWWorker(object objSrc, IntPtr pCPCMD, out IntPtr ppTarget) + => GetCOMIPFromRCWSlow(ObjectHandleOnStack.Create(ref objSrc), pCPCMD, out ppTarget); + } #endif // FEATURE_COMINTEROP #if PROFILING_SUPPORTED @@ -1522,26 +1543,26 @@ internal static unsafe void LayoutDestroyNativeInternal(object obj, byte* pNativ } } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object AllocateInternal(IntPtr typeHandle); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint="StubHelpers_MarshalToManagedVaList")] + internal static partial void MarshalToManagedVaList(IntPtr va_list, IntPtr pArgIterator); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void MarshalToUnmanagedVaListInternal(IntPtr va_list, uint vaListSize, IntPtr pArgIterator); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void MarshalToManagedVaListInternal(IntPtr va_list, IntPtr pArgIterator); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint="StubHelpers_MarshalToUnmanagedVaList")] + internal static partial void MarshalToUnmanagedVaList(IntPtr va_list, uint vaListSize, IntPtr pArgIterator); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern uint CalcVaListSize(IntPtr va_list); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void ValidateObject(object obj, IntPtr pMD, object pThis); - [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void LogPinnedArgument(IntPtr localDesc, IntPtr nativeArg); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void ValidateByref(IntPtr byref, IntPtr pMD, object pThis); // the byref is pinned so we can safely "cast" it to IntPtr + [LibraryImport(RuntimeHelpers.QCall, EntryPoint="StubHelpers_ValidateObject")] + private static partial void ValidateObject(ObjectHandleOnStack obj, IntPtr pMD); + + internal static void ValidateObject(object obj, IntPtr pMD) + => ValidateObject(ObjectHandleOnStack.Create(ref obj), pMD); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint="StubHelpers_ValidateByref")] + internal static partial void ValidateByref(IntPtr byref, IntPtr pMD); // the byref is pinned so we can safely "cast" it to IntPtr [Intrinsic] [MethodImpl(MethodImplOptions.InternalCall)] diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index d3e37da7a02ee7..671ae48e7ee7c5 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -940,9 +940,8 @@ DEFINE_METHOD(STUBHELPERS, CHECK_STRING_LENGTH, CheckStringLength, DEFINE_METHOD(STUBHELPERS, FMT_CLASS_UPDATE_NATIVE_INTERNAL, FmtClassUpdateNativeInternal, SM_Obj_PtrByte_RefCleanupWorkListElement_RetVoid) DEFINE_METHOD(STUBHELPERS, FMT_CLASS_UPDATE_CLR_INTERNAL, FmtClassUpdateCLRInternal, SM_Obj_PtrByte_RetVoid) DEFINE_METHOD(STUBHELPERS, LAYOUT_DESTROY_NATIVE_INTERNAL, LayoutDestroyNativeInternal, SM_Obj_PtrByte_RetVoid) -DEFINE_METHOD(STUBHELPERS, ALLOCATE_INTERNAL, AllocateInternal, SM_IntPtr_RetObj) -DEFINE_METHOD(STUBHELPERS, MARSHAL_TO_MANAGED_VA_LIST_INTERNAL,MarshalToManagedVaListInternal, SM_IntPtr_IntPtr_RetVoid) -DEFINE_METHOD(STUBHELPERS, MARSHAL_TO_UNMANAGED_VA_LIST_INTERNAL,MarshalToUnmanagedVaListInternal,SM_IntPtr_UInt_IntPtr_RetVoid) +DEFINE_METHOD(STUBHELPERS, MARSHAL_TO_MANAGED_VA_LIST, MarshalToManagedVaList, SM_IntPtr_IntPtr_RetVoid) +DEFINE_METHOD(STUBHELPERS, MARSHAL_TO_UNMANAGED_VA_LIST, MarshalToUnmanagedVaList, SM_IntPtr_UInt_IntPtr_RetVoid) DEFINE_METHOD(STUBHELPERS, CALC_VA_LIST_SIZE, CalcVaListSize, SM_IntPtr_RetUInt) DEFINE_METHOD(STUBHELPERS, VALIDATE_OBJECT, ValidateObject, SM_Obj_IntPtr_Obj_RetVoid) DEFINE_METHOD(STUBHELPERS, VALIDATE_BYREF, ValidateByref, SM_IntPtr_IntPtr_Obj_RetVoid) diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index ff89bbd81848b8..33966400d5a189 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -2277,30 +2277,28 @@ void NDirectStubLinker::EmitValidateLocal(ILCodeStream* pcsEmit, DWORD dwLocalNu if (SF_IsDelegateStub(dwStubFlags)) { - pcsEmit->EmitLoadNullPtr(); pcsEmit->EmitLoadThis(); + pcsEmit->EmitCALL(METHOD__DELEGATE__GET_INVOKE_METHOD, 1, 1); } else if (SF_IsCALLIStub(dwStubFlags)) { pcsEmit->EmitLoadNullPtr(); - pcsEmit->EmitLDNULL(); } else { // P/Invoke, CLR->COM EmitLoadStubContext(pcsEmit, dwStubFlags); - pcsEmit->EmitLDNULL(); } if (fIsByref) { - // StubHelpers.ValidateByref(byref, pMD, pThis) - pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_BYREF, 3, 0); + // StubHelpers.ValidateByref(byref, pMD) + pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_BYREF, 2, 0); } else { - // StubHelpers.ValidateObject(obj, pMD, pThis) - pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_OBJECT, 3, 0); + // StubHelpers.ValidateObject(obj, pMD) + pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_OBJECT, 2, 0); } } diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 7575f6ba9adb69..9cadd51c06ae4b 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -447,17 +447,10 @@ FCFuncStart(gStubHelperFuncs) FCFuncElement("TryGetStringTrailByte", StubHelpers::TryGetStringTrailByte) FCFuncElement("SetLastError", StubHelpers::SetLastError) FCFuncElement("ClearLastError", StubHelpers::ClearLastError) - FCFuncElement("InternalGetHRExceptionObject", StubHelpers::GetHRExceptionObject) #ifdef FEATURE_COMINTEROP - FCFuncElement("InternalGetCOMHRExceptionObject", StubHelpers::GetCOMHRExceptionObject) FCFuncElement("GetCOMIPFromRCW", StubHelpers::GetCOMIPFromRCW) #endif // FEATURE_COMINTEROP - FCFuncElement("AllocateInternal", StubHelpers::AllocateInternal) - FCFuncElement("MarshalToUnmanagedVaListInternal", StubHelpers::MarshalToUnmanagedVaListInternal) - FCFuncElement("MarshalToManagedVaListInternal", StubHelpers::MarshalToManagedVaListInternal) FCFuncElement("CalcVaListSize", StubHelpers::CalcVaListSize) - FCFuncElement("ValidateObject", StubHelpers::ValidateObject) - FCFuncElement("ValidateByref", StubHelpers::ValidateByref) FCFuncElement("LogPinnedArgument", StubHelpers::LogPinnedArgument) FCFuncElement("GetStubContext", StubHelpers::GetStubContext) FCFuncElement("MulticastDebuggerTraceHelper", StubHelpers::MulticastDebuggerTraceHelper) diff --git a/src/coreclr/vm/ilmarshalers.cpp b/src/coreclr/vm/ilmarshalers.cpp index e7e59cc82bca6a..6d38d94a46004c 100644 --- a/src/coreclr/vm/ilmarshalers.cpp +++ b/src/coreclr/vm/ilmarshalers.cpp @@ -2233,9 +2233,10 @@ void ILLayoutClassPtrMarshalerBase::EmitConvertSpaceNativeToCLR(ILCodeStream* ps pslILEmit->EmitBRFALSE(pNullRefLabel); pslILEmit->EmitLDTOKEN(pslILEmit->GetToken(m_pargs->m_pMT)); - pslILEmit->EmitCALL(METHOD__RT_TYPE_HANDLE__TO_INTPTR, 1, 1); - // static object AllocateInternal(IntPtr typeHandle); - pslILEmit->EmitCALL(METHOD__STUBHELPERS__ALLOCATE_INTERNAL, 1, 1); + // static Type Type.GetTypeFromHandle(RuntimeTypeHandle handle) + pslILEmit->EmitCALL(METHOD__TYPE__GET_TYPE_FROM_HANDLE, 1, 1); + // static object RuntimeHelpers.GetUninitializedObject(Type type) + pslILEmit->EmitCALL(METHOD__RUNTIME_HELPERS__GET_UNINITIALIZED_OBJECT, 1, 1); EmitStoreManagedValue(pslILEmit); pslILEmit->EmitLabel(pNullRefLabel); } @@ -3522,11 +3523,11 @@ void ILArgIteratorMarshaler::EmitConvertSpaceAndContentsCLRToNative(ILCodeStream pslILEmit->EmitLOCALLOC(); EmitStoreNativeValue(pslILEmit); - // void MarshalToUnmanagedVaListInternal(va_list, uint vaListSize, VARARGS* data) + // void MarshalToUnmanagedVaList(va_list, uint vaListSize, VARARGS* data) EmitLoadNativeValue(pslILEmit); pslILEmit->EmitLDLOC(dwVaListSizeLocal); EmitLoadManagedHomeAddr(pslILEmit); - pslILEmit->EmitCALL(METHOD__STUBHELPERS__MARSHAL_TO_UNMANAGED_VA_LIST_INTERNAL, 3, 0); + pslILEmit->EmitCALL(METHOD__STUBHELPERS__MARSHAL_TO_UNMANAGED_VA_LIST, 3, 0); } void ILArgIteratorMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslILEmit) @@ -3535,7 +3536,7 @@ void ILArgIteratorMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslILE EmitLoadManagedHomeAddr(pslILEmit); // void MarshalToManagedVaList(va_list va, VARARGS *dataout) - pslILEmit->EmitCALL(METHOD__STUBHELPERS__MARSHAL_TO_MANAGED_VA_LIST_INTERNAL, 2, 0); + pslILEmit->EmitCALL(METHOD__STUBHELPERS__MARSHAL_TO_MANAGED_VA_LIST, 2, 0); } LocalDesc ILArrayWithOffsetMarshaler::GetNativeType() diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 1cd3b5d7704c24..108738af6b9cca 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -425,11 +425,18 @@ static const Entry s_QCall[] = DllImportEntry(StubHelpers_CreateCustomMarshalerHelper) DllImportEntry(StubHelpers_SetStringTrailByte) DllImportEntry(StubHelpers_ThrowInteropParamException) + DllImportEntry(StubHelpers_MarshalToManagedVaList) + DllImportEntry(StubHelpers_MarshalToUnmanagedVaList) + DllImportEntry(StubHelpers_ValidateObject) + DllImportEntry(StubHelpers_ValidateByref) #ifdef PROFILING_SUPPORTED DllImportEntry(StubHelpers_ProfilerBeginTransitionCallback) DllImportEntry(StubHelpers_ProfilerEndTransitionCallback) #endif + DllImportEntry(StubHelpers_GetHRExceptionObject) #if defined(FEATURE_COMINTEROP) + DllImportEntry(StubHelpers_GetCOMHRExceptionObject) + DllImportEntry(StubHelpers_GetCOMIPFromRCWSlow) DllImportEntry(ObjectMarshaler_ConvertToNative) DllImportEntry(ObjectMarshaler_ConvertToManaged) DllImportEntry(InterfaceMarshaler_ConvertToNative) diff --git a/src/coreclr/vm/stubhelpers.cpp b/src/coreclr/vm/stubhelpers.cpp index b4673dfbb78af6..775c965c7a93a5 100644 --- a/src/coreclr/vm/stubhelpers.cpp +++ b/src/coreclr/vm/stubhelpers.cpp @@ -28,77 +28,58 @@ #ifdef VERIFY_HEAP -CQuickArray StubHelpers::s_ByrefValidationEntries; -SIZE_T StubHelpers::s_ByrefValidationIndex = 0; -CrstStatic StubHelpers::s_ByrefValidationLock; - -// static -void StubHelpers::Init() +struct ByrefValidationEntry final { - WRAPPER_NO_CONTRACT; - s_ByrefValidationLock.Init(CrstPinnedByrefValidation); -} + void *pByref; // pointer to GC heap + MethodDesc *pMD; // interop MD this byref was passed to +}; -// static -void StubHelpers::ValidateObjectInternal(Object *pObjUNSAFE, BOOL fValidateNextObj) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; -} - CONTRACTL_END; - - _ASSERTE(GCHeapUtilities::GetGCHeap()->RuntimeStructuresValid()); - - // validate the object - there's no need to validate next object's - // header since we validate the next object explicitly below - if (pObjUNSAFE) - { - pObjUNSAFE->Validate(/*bDeep=*/ TRUE, /*bVerifyNextHeader=*/ FALSE, /*bVerifySyncBlock=*/ TRUE); - } - - // and the next object as required - if (fValidateNextObj) - { - Object *nextObj = GCHeapUtilities::GetGCHeap()->NextObj(pObjUNSAFE); - if (nextObj != NULL) - { - // Note that the MethodTable of the object (i.e. the pointer at offset 0) can change from - // g_pFreeObjectMethodTable to NULL, from NULL to , or possibly also from - // g_pFreeObjectMethodTable to concurrently while executing this function. - // Once is seen, we believe that the object should pass the Validate check. - // We have to be careful and read the pointer only once to avoid "phantom reads". - MethodTable *pMT = VolatileLoad(nextObj->GetMethodTablePtr()); - if (pMT != NULL && pMT != g_pFreeObjectMethodTable) - { - // do *not* verify the next object's syncblock - the next object is not guaranteed to - // be "alive" so the finalizer thread may have already released its syncblock - nextObj->Validate(/*bDeep=*/ TRUE, /*bVerifyNextHeader=*/ FALSE, /*bVerifySyncBlock=*/ FALSE); - } - } - } -} +static CQuickArray s_ByrefValidationEntries; +static SIZE_T s_ByrefValidationIndex = 0; +static CrstStatic s_ByrefValidationLock; -// static -MethodDesc *StubHelpers::ResolveInteropMethod(Object *pThisUNSAFE, MethodDesc *pMD) +static void ValidateObjectInternal(Object *pObjUNSAFE, BOOL fValidateNextObj) { - WRAPPER_NO_CONTRACT; + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + _ASSERTE(GCHeapUtilities::GetGCHeap()->RuntimeStructuresValid()); - if (pMD == NULL && pThisUNSAFE != NULL) + // validate the object - there's no need to validate next object's + // header since we validate the next object explicitly below + if (pObjUNSAFE) { - // if this is a call via delegate, get its Invoke method - MethodTable *pMT = pThisUNSAFE->GetMethodTable(); + pObjUNSAFE->Validate(/*bDeep=*/ TRUE, /*bVerifyNextHeader=*/ FALSE, /*bVerifySyncBlock=*/ TRUE); + } - _ASSERTE(pMT->IsDelegate()); - return ((DelegateEEClass *)pMT->GetClass())->GetInvokeMethod(); + // and the next object as required + if (fValidateNextObj) + { + Object *nextObj = GCHeapUtilities::GetGCHeap()->NextObj(pObjUNSAFE); + if (nextObj != NULL) + { + // Note that the MethodTable of the object (i.e. the pointer at offset 0) can change from + // g_pFreeObjectMethodTable to NULL, from NULL to , or possibly also from + // g_pFreeObjectMethodTable to concurrently while executing this function. + // Once is seen, we believe that the object should pass the Validate check. + // We have to be careful and read the pointer only once to avoid "phantom reads". + MethodTable *pMT = VolatileLoad(nextObj->GetMethodTablePtr()); + if (pMT != NULL && pMT != g_pFreeObjectMethodTable) + { + // do *not* verify the next object's syncblock - the next object is not guaranteed to + // be "alive" so the finalizer thread may have already released its syncblock + nextObj->Validate(/*bDeep=*/ TRUE, /*bVerifyNextHeader=*/ FALSE, /*bVerifySyncBlock=*/ FALSE); + } + } } - return pMD; } -// static -void StubHelpers::FormatValidationMessage(MethodDesc *pMD, SString &ssErrorString) +static void FormatValidationMessage(MethodDesc *pMD, SString &ssErrorString) { CONTRACTL { @@ -108,25 +89,25 @@ void StubHelpers::FormatValidationMessage(MethodDesc *pMD, SString &ssErrorStrin } CONTRACTL_END; - ssErrorString.Append(W("Detected managed heap corruption, likely culprit is interop call through ")); + ssErrorString.AppendUTF8("Detected managed heap corruption, likely culprit is interop call through "); if (pMD == NULL) { // the only case where we don't have interop MD is CALLI - ssErrorString.Append(W("CALLI.")); + ssErrorString.AppendUTF8("CALLI."); } else { - ssErrorString.Append(W("method '")); + ssErrorString.AppendUTF8("method '"); StackSString ssClassName; pMD->GetMethodTable()->_GetFullyQualifiedNameForClass(ssClassName); ssErrorString.Append(ssClassName); - ssErrorString.Append(NAMESPACE_SEPARATOR_CHAR); + ssErrorString.AppendUTF8(NAMESPACE_SEPARATOR_CHAR); ssErrorString.AppendUTF8(pMD->GetName()); - ssErrorString.Append(W("'.")); + ssErrorString.AppendUTF8("'."); } } @@ -178,6 +159,15 @@ void StubHelpers::ProcessByrefValidationList() #endif // VERIFY_HEAP +// static +void StubHelpers::Init() +{ + WRAPPER_NO_CONTRACT; +#ifdef VERIFY_HEAP + s_ByrefValidationLock.Init(CrstPinnedByrefValidation); +#endif // VERIFY_HEAP +} + #ifdef FEATURE_COMINTEROP FORCEINLINE static void GetCOMIPFromRCW_ClearFP() @@ -246,39 +236,10 @@ FORCEINLINE static void *GetCOMIPFromRCW_GetTarget(IUnknown *pUnk, CLRToCOMCallI { LIMITED_METHOD_CONTRACT; - LPVOID *lpVtbl = *(LPVOID **)pUnk; return lpVtbl[pComInfo->m_cachedComSlot]; } -NOINLINE static IUnknown* GetCOMIPFromRCWHelper(LPVOID pFCall, OBJECTREF pSrc, MethodDesc* pMD, void **ppTarget) -{ - FC_INNER_PROLOG(pFCall); - - IUnknown *pIntf = NULL; - - // This is only called in IL stubs which are in CER, so we don't need to worry about ThreadAbort - HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_NO_THREAD_ABORT|Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, pSrc); - - SafeComHolder pRetUnk; - - CLRToCOMCallInfo *pComInfo = CLRToCOMCallInfo::FromMethodDesc(pMD); - pRetUnk = ComObject::GetComIPFromRCWThrowing(&pSrc, pComInfo->m_pInterfaceMT); - - *ppTarget = GetCOMIPFromRCW_GetTarget(pRetUnk, pComInfo); - _ASSERTE(*ppTarget != NULL); - - GetCOMIPFromRCW_ClearFP(); - - pIntf = pRetUnk.Extract(); - - // No exception will be thrown here (including thread abort as it is delayed in IL stubs) - HELPER_METHOD_FRAME_END(); - - FC_INNER_EPILOG(); - return pIntf; -} - //================================================================================================================== // The GetCOMIPFromRCW helper exists in four specialized versions to optimize CLR->COM perf. Please be careful when // changing this code as one of these methods is executed as part of every CLR->COM call so every instruction counts. @@ -289,7 +250,7 @@ NOINLINE static IUnknown* GetCOMIPFromRCWHelper(LPVOID pFCall, OBJECTREF pSrc, M // This helper can handle any CLR->COM call, it supports hosting, // and clears FP state on x86 for compatibility with VB6. -FCIMPL4(IUnknown*, StubHelpers::GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* pMD, void **ppTarget, CLR_BOOL* pfNeedsRelease) +FCIMPL3(IUnknown*, StubHelpers::GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* pMD, void **ppTarget) { CONTRACTL { @@ -299,13 +260,10 @@ FCIMPL4(IUnknown*, StubHelpers::GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* CONTRACTL_END; OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); - *pfNeedsRelease = false; - CLRToCOMCallInfo *pComInfo = CLRToCOMCallInfo::FromMethodDesc(pMD); RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); if (pRCW != NULL) { - IUnknown * pUnk = GetCOMIPFromRCW_GetIUnknownFromRCWCache(pRCW, pComInfo->m_pInterfaceMT); if (pUnk != NULL) { @@ -317,15 +275,41 @@ FCIMPL4(IUnknown*, StubHelpers::GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* } } } - - /* if we didn't find the COM interface pointer in the cache we will have to erect an HMF */ - *pfNeedsRelease = true; - FC_INNER_RETURN(IUnknown*, GetCOMIPFromRCWHelper(StubHelpers::GetCOMIPFromRCW, pSrc, pMD, ppTarget)); + return NULL; } FCIMPLEND #include +extern "C" IUnknown* QCALLTYPE StubHelpers_GetCOMIPFromRCWSlow(QCall::ObjectHandleOnStack pSrc, MethodDesc* pMD, void** ppTarget) +{ + QCALL_CONTRACT; + _ASSERTE(pMD != NULL); + _ASSERTE(ppTarget != NULL); + + IUnknown *pIntf = NULL; + BEGIN_QCALL; + + GCX_COOP(); + + OBJECTREF objRef = pSrc.Get(); + GCPROTECT_BEGIN(objRef); + + CLRToCOMCallInfo* pComInfo = CLRToCOMCallInfo::FromMethodDesc(pMD); + SafeComHolder pRetUnk = ComObject::GetComIPFromRCWThrowing(&objRef, pComInfo->m_pInterfaceMT); + *ppTarget = GetCOMIPFromRCW_GetTarget(pRetUnk, pComInfo); + _ASSERTE(*ppTarget != NULL); + + GetCOMIPFromRCW_ClearFP(); + pIntf = pRetUnk.Extract(); + + GCPROTECT_END(); + + END_QCALL; + + return pIntf; +} + extern "C" void QCALLTYPE ObjectMarshaler_ConvertToNative(QCall::ObjectHandleOnStack pSrcUNSAFE, VARIANT* pDest) { QCALL_CONTRACT; @@ -531,149 +515,152 @@ extern "C" void QCALLTYPE StubHelpers_ProfilerEndTransitionCallback(MethodDesc* } #endif // PROFILING_SUPPORTED -FCIMPL1(Object*, StubHelpers::GetHRExceptionObject, HRESULT hr) +extern "C" void QCALLTYPE StubHelpers_GetHRExceptionObject(HRESULT hr, QCall::ObjectHandleOnStack result) { - FCALL_CONTRACT; + QCALL_CONTRACT; + + BEGIN_QCALL; + + GCX_COOP(); OBJECTREF oThrowable = NULL; + GCPROTECT_BEGIN(oThrowable); - HELPER_METHOD_FRAME_BEGIN_RET_1(oThrowable); - { - // GetExceptionForHR uses equivalant logic as COMPlusThrowHR - GetExceptionForHR(hr, &oThrowable); - } - HELPER_METHOD_FRAME_END(); + // GetExceptionForHR uses equivalant logic as COMPlusThrowHR + GetExceptionForHR(hr, &oThrowable); + result.Set(oThrowable); + + GCPROTECT_END(); - return OBJECTREFToObject(oThrowable); + END_QCALL; } -FCIMPLEND #ifdef FEATURE_COMINTEROP -FCIMPL3(Object*, StubHelpers::GetCOMHRExceptionObject, HRESULT hr, MethodDesc *pMD, Object *unsafe_pThis) +extern "C" void QCALLTYPE StubHelpers_GetCOMHRExceptionObject( + HRESULT hr, + MethodDesc* pMD, + QCall::ObjectHandleOnStack pThis, + QCall::ObjectHandleOnStack result) { - FCALL_CONTRACT; + QCALL_CONTRACT; - OBJECTREF oThrowable = NULL; + BEGIN_QCALL; - // get 'this' - OBJECTREF oref = ObjectToOBJECTREF(unsafe_pThis); + GCX_COOP(); - HELPER_METHOD_FRAME_BEGIN_RET_2(oref, oThrowable); + struct { - IErrorInfo *pErrInfo = NULL; + OBJECTREF oThrowable; + OBJECTREF oref; + } gc; + gc.oThrowable = NULL; + gc.oref = NULL; + GCPROTECT_BEGIN(gc); + + IErrorInfo* pErrorInfo = NULL; + if (pMD != NULL) + { + // Retrieve the interface method table. + MethodTable* pItfMT = CLRToCOMCallInfo::FromMethodDesc(pMD)->m_pInterfaceMT; - if (pMD != NULL) - { - // Retrieve the interface method table. - MethodTable *pItfMT = CLRToCOMCallInfo::FromMethodDesc(pMD)->m_pInterfaceMT; + // get 'this' + gc.oref = ObjectToOBJECTREF(pThis.Get()); - // Get IUnknown pointer for this interface on this object - IUnknown* pUnk = ComObject::GetComIPFromRCW(&oref, pItfMT); - if (pUnk != NULL) - { - // Check to see if the component supports error information for this interface. - IID ItfIID; - pItfMT->GetGuid(&ItfIID, TRUE); - pErrInfo = GetSupportedErrorInfo(pUnk, ItfIID); + // Get IUnknown pointer for this interface on this object + IUnknown* pUnk = ComObject::GetComIPFromRCW(&gc.oref, pItfMT); + if (pUnk != NULL) + { + // Check to see if the component supports error information for this interface. + IID ItfIID; + pItfMT->GetGuid(&ItfIID, TRUE); + pErrorInfo = GetSupportedErrorInfo(pUnk, ItfIID); - DWORD cbRef = SafeRelease(pUnk); - LogInteropRelease(pUnk, cbRef, "IUnk to QI for ISupportsErrorInfo"); - } + DWORD cbRef = SafeRelease(pUnk); + LogInteropRelease(pUnk, cbRef, "IUnk to QI for ISupportsErrorInfo"); } - - GetExceptionForHR(hr, pErrInfo, &oThrowable); } - HELPER_METHOD_FRAME_END(); - return OBJECTREFToObject(oThrowable); + // GetExceptionForHR will handle lifetime of IErrorInfo. + GetExceptionForHR(hr, pErrorInfo, &gc.oThrowable); + result.Set(gc.oThrowable); + + GCPROTECT_END(); + + END_QCALL; } -FCIMPLEND #endif // FEATURE_COMINTEROP -FCIMPL1(Object*, StubHelpers::AllocateInternal, EnregisteredTypeHandle pRegisteredTypeHnd) +extern "C" void QCALLTYPE StubHelpers_MarshalToManagedVaList(va_list va, VARARGS* pArgIterator) { - FCALL_CONTRACT; - - TypeHandle typeHnd = TypeHandle::FromPtr(pRegisteredTypeHnd); - OBJECTREF objRet = NULL; - HELPER_METHOD_FRAME_BEGIN_RET_1(objRet); - - MethodTable* pMT = typeHnd.GetMethodTable(); - objRet = pMT->Allocate(); - - HELPER_METHOD_FRAME_END(); + QCALL_CONTRACT; - return OBJECTREFToObject(objRet); + BEGIN_QCALL; + VARARGS::MarshalToManagedVaList(va, pArgIterator); + END_QCALL; } -FCIMPLEND -FCIMPL3(void, StubHelpers::MarshalToUnmanagedVaListInternal, va_list va, DWORD cbVaListSize, const VARARGS* pArgIterator) +extern "C" void QCALLTYPE StubHelpers_MarshalToUnmanagedVaList(va_list va, DWORD cbVaListSize, const VARARGS* pArgIterator) { - FCALL_CONTRACT; + QCALL_CONTRACT; - HELPER_METHOD_FRAME_BEGIN_0(); + BEGIN_QCALL; VARARGS::MarshalToUnmanagedVaList(va, cbVaListSize, pArgIterator); - HELPER_METHOD_FRAME_END(); + END_QCALL; } -FCIMPLEND -FCIMPL2(void, StubHelpers::MarshalToManagedVaListInternal, va_list va, VARARGS* pArgIterator) +extern "C" void QCALLTYPE StubHelpers_ValidateObject(QCall::ObjectHandleOnStack pObj, MethodDesc *pMD) { - FCALL_CONTRACT; - - VARARGS::MarshalToManagedVaList(va, pArgIterator); -} -FCIMPLEND + QCALL_CONTRACT; -FCIMPL3(void, StubHelpers::ValidateObject, Object *pObjUNSAFE, MethodDesc *pMD, Object *pThisUNSAFE) -{ - FCALL_CONTRACT; + BEGIN_QCALL; #ifdef VERIFY_HEAP - HELPER_METHOD_FRAME_BEGIN_0(); + GCX_COOP(); StackSString errorString; EX_TRY { AVInRuntimeImplOkayHolder AVOkay; - // don't validate the next object if a BGC is in progress. we can race with background - // sweep which could make the next object a Free object underneath us if it's dead. - ValidateObjectInternal(pObjUNSAFE, !(GCHeapUtilities::GetGCHeap()->IsConcurrentGCInProgress())); + // don't validate the next object if a BGC is in progress. we can race with background + // sweep which could make the next object a Free object underneath us if it's dead. + ValidateObjectInternal(OBJECTREFToObject(pObj.Get()), !(GCHeapUtilities::GetGCHeap()->IsConcurrentGCInProgress())); } EX_CATCH { - FormatValidationMessage(ResolveInteropMethod(pThisUNSAFE, pMD), errorString); + FormatValidationMessage(pMD, errorString); EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, errorString.GetUnicode()); } EX_END_CATCH_UNREACHABLE; - HELPER_METHOD_FRAME_END(); #else // VERIFY_HEAP - FCUnique(0xa3); - UNREACHABLE_MSG("No validation support without VERIFY_HEAP"); + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_FAILFAST, "No validation support without VERIFY_HEAP"); #endif // VERIFY_HEAP + + END_QCALL; } -FCIMPLEND -FCIMPL3(void, StubHelpers::ValidateByref, void *pByref, MethodDesc *pMD, Object *pThisUNSAFE) +extern "C" void QCALLTYPE StubHelpers_ValidateByref(void *pByref, MethodDesc *pMD) { - FCALL_CONTRACT; - -#ifdef VERIFY_HEAP - // We cannot validate byrefs at this point as code:GCHeap.GetContainingObject could potentially race - // with allocations on other threads. We'll just remember this byref along with the interop MD and - // perform the validation on next GC (see code:StubHelpers.ProcessByrefValidationList). + QCALL_CONTRACT; // Skip byref if is not pointing inside managed heap if (!GCHeapUtilities::GetGCHeap()->IsHeapPointer(pByref)) { return; } + + BEGIN_QCALL; + +#ifdef VERIFY_HEAP + GCX_COOP(); + + // We cannot validate byrefs at this point as code:GCHeap.GetContainingObject could potentially race + // with allocations on other threads. We'll just remember this byref along with the interop MD and + // perform the validation on next GC (see code:StubHelpers.ProcessByrefValidationList). + ByrefValidationEntry entry; entry.pByref = pByref; - entry.pMD = ResolveInteropMethod(pThisUNSAFE, pMD); - - HELPER_METHOD_FRAME_BEGIN_0(); + entry.pMD = pMD; SIZE_T NumOfEntries = 0; { @@ -702,14 +689,12 @@ FCIMPL3(void, StubHelpers::ValidateByref, void *pByref, MethodDesc *pMD, Object // if the list is too big, trigger GC now GCHeapUtilities::GetGCHeap()->GarbageCollect(0); } - - HELPER_METHOD_FRAME_END(); #else // VERIFY_HEAP - FCUnique(0xa4); - UNREACHABLE_MSG("No validation support without VERIFY_HEAP"); + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_FAILFAST, "No validation support without VERIFY_HEAP"); #endif // VERIFY_HEAP + + END_QCALL; } -FCIMPLEND FCIMPL0(void*, StubHelpers::GetStubContext) { diff --git a/src/coreclr/vm/stubhelpers.h b/src/coreclr/vm/stubhelpers.h index 9529bdc80deaca..5b0bb00d684ba7 100644 --- a/src/coreclr/vm/stubhelpers.h +++ b/src/coreclr/vm/stubhelpers.h @@ -15,27 +15,10 @@ class StubHelpers { -#ifdef VERIFY_HEAP - struct ByrefValidationEntry - { - void *pByref; // pointer to GC heap - MethodDesc *pMD; // interop MD this byref was passed to - }; - - static CQuickArray s_ByrefValidationEntries; - static SIZE_T s_ByrefValidationIndex; - static CrstStatic s_ByrefValidationLock; - - static void ValidateObjectInternal(Object *pObjUNSAFE, BOOL fValidateNextObj); - static MethodDesc *ResolveInteropMethod(Object *pThisUNSAFE, MethodDesc *pMD); - static void FormatValidationMessage(MethodDesc *pMD, SString &ssErrorString); - public: static void Init(); +#ifdef VERIFY_HEAP static void ProcessByrefValidationList(); -#else // VERIFY_HEAP -public: - static void Init() { LIMITED_METHOD_CONTRACT; } #endif // VERIFY_HEAP //------------------------------------------------------- @@ -43,7 +26,7 @@ class StubHelpers //------------------------------------------------------- #ifdef FEATURE_COMINTEROP - static FCDECL4(IUnknown*, GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* pMD, void **ppTarget, CLR_BOOL* pfNeedsRelease); + static FCDECL3(IUnknown*, GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* pMD, void **ppTarget); #endif // FEATURE_COMINTEROP static FCDECL0(void, SetLastError ); @@ -52,20 +35,9 @@ class StubHelpers static FCDECL2(FC_BOOL_RET, TryGetStringTrailByte, StringObject* thisRefUNSAFE, UINT8 *pbData); - static FCDECL1(Object*, GetHRExceptionObject, HRESULT hr); - -#ifdef FEATURE_COMINTEROP - static FCDECL3(Object*, GetCOMHRExceptionObject, HRESULT hr, MethodDesc *pMD, Object *unsafe_pThis); -#endif // FEATURE_COMINTEROP - - static FCDECL1(Object*, AllocateInternal, EnregisteredTypeHandle typeHnd); - static FCDECL3(void, MarshalToUnmanagedVaListInternal, va_list va, DWORD cbVaListSize, const VARARGS* pArgIterator); - static FCDECL2(void, MarshalToManagedVaListInternal, va_list va, VARARGS* pArgIterator); static FCDECL0(void*, GetStubContext); static FCDECL2(void, LogPinnedArgument, MethodDesc *localDesc, Object *nativeArg); static FCDECL1(DWORD, CalcVaListSize, VARARGS *varargs); - static FCDECL3(void, ValidateObject, Object *pObjUNSAFE, MethodDesc *pMD, Object *pThisUNSAFE); - static FCDECL3(void, ValidateByref, void *pByref, MethodDesc *pMD, Object *pThisUNSAFE); static FCDECL2(void, MulticastDebuggerTraceHelper, Object*, INT32); @@ -79,7 +51,13 @@ extern "C" void* QCALLTYPE StubHelpers_ProfilerBeginTransitionCallback(MethodDes extern "C" void QCALLTYPE StubHelpers_ProfilerEndTransitionCallback(MethodDesc* pTargetMD); #endif +extern "C" void QCALLTYPE StubHelpers_GetHRExceptionObject(HRESULT hr, QCall::ObjectHandleOnStack result); + #ifdef FEATURE_COMINTEROP +extern "C" void QCALLTYPE StubHelpers_GetCOMHRExceptionObject(HRESULT hr, MethodDesc *pMD, QCall::ObjectHandleOnStack pThis, QCall::ObjectHandleOnStack result); + +extern "C" IUnknown* QCALLTYPE StubHelpers_GetCOMIPFromRCWSlow(QCall::ObjectHandleOnStack pSrc, MethodDesc* pMD, void** ppTarget); + extern "C" void QCALLTYPE ObjectMarshaler_ConvertToNative(QCall::ObjectHandleOnStack pSrcUNSAFE, VARIANT* pDest); extern "C" void QCALLTYPE ObjectMarshaler_ConvertToManaged(VARIANT* pSrc, QCall::ObjectHandleOnStack retObject); @@ -90,4 +68,10 @@ extern "C" void QCALLTYPE InterfaceMarshaler_ConvertToManaged(IUnknown** ppUnk, extern "C" void QCALLTYPE StubHelpers_SetStringTrailByte(QCall::StringHandleOnStack str, UINT8 bData); extern "C" void QCALLTYPE StubHelpers_ThrowInteropParamException(INT resID, INT paramIdx); +extern "C" void QCALLTYPE StubHelpers_MarshalToManagedVaList(va_list va, VARARGS* pArgIterator); +extern "C" void QCALLTYPE StubHelpers_MarshalToUnmanagedVaList(va_list va, DWORD cbVaListSize, const VARARGS* pArgIterator); + +extern "C" void QCALLTYPE StubHelpers_ValidateObject(QCall::ObjectHandleOnStack pObj, MethodDesc *pMD); +extern "C" void QCALLTYPE StubHelpers_ValidateByref(void *pByref, MethodDesc *pMD); + #endif // __STUBHELPERS_h__