Skip to content

Commit c9b272f

Browse files
jkoritzinskyjkotas
andauthored
Convert constructor invocations from the runtime to use UnmanagedCallersOnly (#124920)
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent ac8e763 commit c9b272f

13 files changed

Lines changed: 92 additions & 204 deletions

File tree

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,27 @@ private static void InitInstantiatedClass(MethodTable* mt, MethodDesc* methodDes
4949
else
5050
InitClassSlow(pMT);
5151
}
52+
53+
[DebuggerHidden]
54+
[UnmanagedCallersOnly]
55+
internal static void CallClassConstructor(void* cctor, void* instantiatingArg, Exception* pException)
56+
{
57+
try
58+
{
59+
if (instantiatingArg == null)
60+
{
61+
((delegate*<void>)cctor)();
62+
}
63+
else
64+
{
65+
// Explicitly pass the instantiating argument as a regular argument to match the ABI of a non-instantiating stub.
66+
((delegate*<void*, void>)cctor)(instantiatingArg);
67+
}
68+
}
69+
catch (Exception ex)
70+
{
71+
*pException = ex;
72+
}
73+
}
5274
}
5375
}

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,19 @@ internal static unsafe void CallToString(object* pObj, string* pResult, Exceptio
654654
*pException = ex;
655655
}
656656
}
657+
658+
[UnmanagedCallersOnly]
659+
internal static unsafe void CallDefaultConstructor(object* pObj, delegate*<object, void> pCtor, Exception* pException)
660+
{
661+
try
662+
{
663+
pCtor(*pObj);
664+
}
665+
catch (Exception ex)
666+
{
667+
*pException = ex;
668+
}
669+
}
657670
}
658671
// Helper class to assist with unsafe pinning of arbitrary objects.
659672
// It's used by VM code.

src/coreclr/md/datablob.inl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,11 @@ void
7070
DataBlob::Clear()
7171
{
7272
m_cbSize = 0;
73+
#ifndef TARGET_WASM
7374
// For debugging purposes let's put invalid non-NULL pointer here
75+
// Emscripten doesn't handle this case well in memcpy, so don't do it for WASM target.
7476
INDEBUG_MD(m_pbData = const_pbBadFood);
77+
#endif // !TARGET_WASM
7578
} // DataBlob::Clear
7679

7780
#undef const_pbBadFood

src/coreclr/vm/callhelpers.cpp

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
#include "common.h"
99
#include "dbginterface.h"
1010

11-
// To include declaration of "AppDomainTransitionExceptionFilter"
12-
#include "excep.h"
1311
#include "invokeutil.h"
1412
#include "argdestination.h"
1513

@@ -110,40 +108,6 @@ void CallDescrWorker(CallDescrData * pCallDescrData)
110108
}
111109
#endif // !defined(HOST_64BIT) && defined(_DEBUG)
112110

113-
void DispatchCallDebuggerWrapper(
114-
CallDescrData * pCallDescrData,
115-
BOOL fCriticalCall
116-
)
117-
{
118-
// Use static contracts b/c we have SEH.
119-
STATIC_CONTRACT_THROWS;
120-
STATIC_CONTRACT_GC_TRIGGERS;
121-
STATIC_CONTRACT_MODE_COOPERATIVE;
122-
123-
struct Param : NotifyOfCHFFilterWrapperParam
124-
{
125-
CallDescrData * pCallDescrData;
126-
BOOL fCriticalCall;
127-
} param;
128-
129-
param.pFrame = NULL;
130-
param.pCallDescrData = pCallDescrData;
131-
param.fCriticalCall = fCriticalCall;
132-
133-
PAL_TRY(Param *, pParam, &param)
134-
{
135-
CallDescrWorkerWithHandler(
136-
pParam->pCallDescrData,
137-
pParam->fCriticalCall);
138-
}
139-
PAL_EXCEPT_FILTER(AppDomainTransitionExceptionFilter)
140-
{
141-
// Should never reach here b/c handler should always continue search.
142-
_ASSERTE(!"Unreachable");
143-
}
144-
PAL_ENDTRY
145-
}
146-
147111
#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64)
148112
void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStructInRegistersInfo info,
149113
bool handleGcRefs)
@@ -180,7 +144,7 @@ void* DispatchCallSimple(
180144
SIZE_T *pSrc,
181145
DWORD numStackSlotsToCopy,
182146
PCODE pTargetAddress,
183-
DWORD dwDispatchCallSimpleFlags)
147+
BOOL fCriticalCall)
184148
{
185149
CONTRACTL
186150
{
@@ -232,16 +196,7 @@ void* DispatchCallSimple(
232196
}
233197
#endif // TARGET_WASM
234198

235-
if ((dwDispatchCallSimpleFlags & DispatchCallSimple_CatchHandlerFoundNotification) != 0)
236-
{
237-
DispatchCallDebuggerWrapper(
238-
&callDescrData,
239-
dwDispatchCallSimpleFlags & DispatchCallSimple_CriticalCall);
240-
}
241-
else
242-
{
243-
CallDescrWorkerWithHandler(&callDescrData, dwDispatchCallSimpleFlags & DispatchCallSimple_CriticalCall);
244-
}
199+
CallDescrWorkerWithHandler(&callDescrData, fCriticalCall);
245200

246201
return *(void **)(&callDescrData.returnValue);
247202
}
@@ -606,13 +561,9 @@ void CallDefaultConstructor(OBJECTREF ref)
606561

607562
MethodDesc *pMD = pMT->GetDefaultConstructor();
608563

609-
PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(pMD);
610-
DECLARE_ARGHOLDER_ARRAY(CtorArgs, 1);
611-
CtorArgs[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ref);
564+
UnmanagedCallersOnlyCaller defaultCtorInvoker{METHOD__RUNTIME_HELPERS__CALL_DEFAULT_CONSTRUCTOR};
612565

613-
// Call the ctor...
614-
CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE;
615-
CALL_MANAGED_METHOD_NORET(CtorArgs);
566+
defaultCtorInvoker.InvokeThrowing(&ref, pMD->GetSingleCallableAddrOfCode());
616567

617568
GCPROTECT_END ();
618569
}

src/coreclr/vm/callhelpers.h

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ void* DispatchCallSimple(
7474
SIZE_T *pSrc,
7575
DWORD numStackSlotsToCopy,
7676
PCODE pTargetAddress,
77-
DWORD dwDispatchCallSimpleFlags);
77+
BOOL fCriticalCall);
7878

7979
#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64)
8080
// Copy structs returned according to floating-point calling convention from 'returnRegs' containing struct fields
@@ -499,12 +499,6 @@ enum EEToManagedCallFlags
499499
/* Macros that provide abstraction to the usage of DispatchCallSimple */
500500
/***********************************************************************/
501501

502-
enum DispatchCallSimpleFlags
503-
{
504-
DispatchCallSimple_CriticalCall = 0x0001,
505-
DispatchCallSimple_CatchHandlerFoundNotification = 0x0002,
506-
};
507-
508502
#define ARGHOLDER_TYPE LPVOID
509503
#define OBJECTREF_TO_ARGHOLDER(x) (LPVOID)OBJECTREFToObject(x)
510504
#define STRINGREF_TO_ARGHOLDER(x) (LPVOID)STRINGREFToObject(x)
@@ -514,7 +508,7 @@ enum DispatchCallSimpleFlags
514508

515509
#define INIT_VARIABLES(count) \
516510
DWORD __numArgs = count; \
517-
DWORD __dwDispatchCallSimpleFlags = 0; \
511+
BOOL __criticalDispatchCall = FALSE; \
518512

519513
#define PREPARE_NONVIRTUAL_CALLSITE(id) \
520514
static PCODE s_pAddr##id = 0; \
@@ -527,39 +521,18 @@ enum DispatchCallSimpleFlags
527521
VolatileStore(&s_pAddr##id, __pSlot); \
528522
}
529523

530-
#define PREPARE_VIRTUAL_CALLSITE(id, objref) \
531-
MethodDesc *__pMeth = CoreLibBinder::GetMethod(id); \
532-
PCODE __pSlot = __pMeth->GetCallTarget(&objref);
533-
534-
#define PREPARE_VIRTUAL_CALLSITE_USING_METHODDESC(pMD, objref) \
535-
PCODE __pSlot = pMD->GetCallTarget(&objref);
536-
537-
#define PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(pMD) \
538-
PCODE __pSlot = (pMD)->GetSingleCallableAddrOfCode();
539-
540524
#define PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pCode) \
541525
PCODE __pSlot = pCode;
542526

543527
#define CRITICAL_CALLSITE \
544-
__dwDispatchCallSimpleFlags |= DispatchCallSimple_CriticalCall;
545-
546-
// This flag should be used for callsites that catch exception up the stack inside the VM. The most common causes are
547-
// such as END_DOMAIN_TRANSITION or EX_CATCH. Catching exceptions in the managed code is properly instrumented and
548-
// does not need this notification.
549-
//
550-
// The notification is what enables both the managed 'unhandled exception' dialog and the 'user unhandled' dialog when
551-
// JMC is turned on. Many things that VS puts up the unhandled exception dialog for are actually cases where the native
552-
// exception was caught, for example catching exceptions at the thread base. JMC requires further accuracy - in that case
553-
// VS is checking to see if an exception escaped particular ranges of managed code frames.
554-
#define CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE \
555-
__dwDispatchCallSimpleFlags |= DispatchCallSimple_CatchHandlerFoundNotification;
528+
__criticalDispatchCall = TRUE;
556529

557530
#define PERFORM_CALL \
558531
void * __retval = NULL; \
559532
__retval = DispatchCallSimple(__pArgs, \
560533
__numStackSlotsToCopy, \
561534
__pSlot, \
562-
__dwDispatchCallSimpleFlags);\
535+
__criticalDispatchCall); \
563536

564537
#ifdef CALLDESCR_ARGREGS
565538

src/coreclr/vm/cominterfacemarshaler.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,9 @@ void COMInterfaceMarshaler::CreateObjectRef(BOOL fDuplicate, OBJECTREF *pComObj,
165165
MethodDesc *pCtorMD = m_typeHandle.GetMethodTable()->GetDefaultConstructor();
166166
if (pCtorMD)
167167
{
168-
PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(pCtorMD);
169-
DECLARE_ARGHOLDER_ARRAY(CtorArgs, 1);
170-
CtorArgs[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*pComObj);
168+
UnmanagedCallersOnlyCaller defaultCtorInvoker{METHOD__RUNTIME_HELPERS__CALL_DEFAULT_CONSTRUCTOR};
171169

172-
// Call the ctor...
173-
CALL_MANAGED_METHOD_NORET(CtorArgs);
170+
defaultCtorInvoker.InvokeThrowing(pComObj, pCtorMD->GetSingleCallableAddrOfCode());
174171
}
175172
}
176173
}

src/coreclr/vm/corelib.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,7 @@ DEFINE_METHOD(RUNTIME_HELPERS, COPY_CONSTRUCT, CopyConstruct, NoSig
690690
DEFINE_METHOD(RUNTIME_HELPERS, SET_NEXT_CALL_GENERIC_CONTEXT, SetNextCallGenericContext, NoSig)
691691
DEFINE_METHOD(RUNTIME_HELPERS, SET_NEXT_CALL_ASYNC_CONTINUATION, SetNextCallAsyncContinuation, NoSig)
692692
DEFINE_METHOD(RUNTIME_HELPERS, CALL_TO_STRING, CallToString, SM_PtrObj_PtrStr_PtrException_RetVoid)
693+
DEFINE_METHOD(RUNTIME_HELPERS, CALL_DEFAULT_CONSTRUCTOR, CallDefaultConstructor, NoSig)
693694

694695
DEFINE_CLASS(ASYNC_HELPERS, CompilerServices, AsyncHelpers)
695696
DEFINE_METHOD(ASYNC_HELPERS, ALLOC_CONTINUATION, AllocContinuation, NoSig)
@@ -1311,6 +1312,7 @@ DEFINE_METHOD(GENERICSHELPERS, CLASSWITHSLOTANDMODULE, ClassWithSlotAndModule, N
13111312
DEFINE_CLASS(INITHELPERS, CompilerServices, InitHelpers)
13121313
DEFINE_METHOD(INITHELPERS, INITCLASS, InitClass, NoSig)
13131314
DEFINE_METHOD(INITHELPERS, INITINSTANTIATEDCLASS, InitInstantiatedClass, NoSig)
1315+
DEFINE_METHOD(INITHELPERS, CALLCLASSCONSTRUCTOR, CallClassConstructor, SM_PtrVoid_PtrVoid_PtrException_RetVoid)
13141316

13151317
DEFINE_CLASS(STATICSHELPERS, CompilerServices, StaticsHelpers)
13161318
DEFINE_METHOD(STATICSHELPERS, GET_NONGC_STATIC, GetNonGCStaticBase, NoSig)

src/coreclr/vm/excep.cpp

Lines changed: 5 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -4929,7 +4929,7 @@ LPVOID COMPlusCheckForAbort(UINT_PTR uTryCatchResumeAddress)
49294929
// we should maintain the original exception point's bucket details.
49304930
if (pUEWatsonBucketTracker->RetrieveWatsonBucketIp() != NULL)
49314931
{
4932-
_ASSERTE(pUEWatsonBucketTracker->CapturedForThreadAbort() || pUEWatsonBucketTracker->CapturedAtADTransition());
4932+
_ASSERTE(pUEWatsonBucketTracker->CapturedForThreadAbort());
49334933
fClearUEWatsonBucketTracker = FALSE;
49344934
}
49354935
#ifdef _DEBUG
@@ -6948,57 +6948,6 @@ LONG NotifyOfCHFFilterWrapper(
69486948
return ret;
69496949
} // LONG NotifyOfCHFFilterWrapper()
69506950

6951-
// This filter will be used process exceptions escaping out of AD transition boundaries
6952-
// that are not at the base of the managed thread. Those are handled in ThreadBaseRedirectingFilter.
6953-
// This will be invoked when an exception is going unhandled from the called AppDomain.
6954-
//
6955-
// This can be used to do last moment work before the exception gets caught by the EX_CATCH setup
6956-
// at the AD transition point.
6957-
LONG AppDomainTransitionExceptionFilter(
6958-
EXCEPTION_POINTERS *pExceptionInfo, // the pExceptionInfo passed to a filter function.
6959-
PVOID pParam)
6960-
{
6961-
// Ideally, we would be NOTHROW here. However, NotifyOfCHFFilterWrapper calls into
6962-
// NotifyOfCHFFilter that is THROWS. Thus, to prevent contract violation,
6963-
// we abide by the rules and be THROWS.
6964-
//
6965-
// Same rationale for GC_TRIGGERS as well.
6966-
CONTRACTL
6967-
{
6968-
GC_TRIGGERS;
6969-
MODE_ANY;
6970-
THROWS;
6971-
}
6972-
CONTRACTL_END;
6973-
6974-
ULONG ret = EXCEPTION_CONTINUE_SEARCH;
6975-
6976-
// First, call into NotifyOfCHFFilterWrapper
6977-
ret = NotifyOfCHFFilterWrapper(pExceptionInfo, pParam);
6978-
6979-
#ifndef TARGET_UNIX
6980-
// Setup the watson bucketing details if the escaping
6981-
// exception is preallocated.
6982-
if (SetupWatsonBucketsForEscapingPreallocatedExceptions())
6983-
{
6984-
// Set the flag that these were captured at AD Transition
6985-
DEBUG_STMT(GetThread()->GetExceptionState()->GetUEWatsonBucketTracker()->SetCapturedAtADTransition());
6986-
}
6987-
6988-
// Attempt to capture buckets for non-preallocated exceptions just before the AppDomain transition boundary
6989-
{
6990-
GCX_COOP();
6991-
OBJECTREF oThrowable = GetThread()->GetThrowable();
6992-
if ((oThrowable != NULL) && (CLRException::IsPreallocatedExceptionObject(oThrowable) == FALSE))
6993-
{
6994-
SetupWatsonBucketsForNonPreallocatedExceptions();
6995-
}
6996-
}
6997-
#endif // !TARGET_UNIX
6998-
6999-
return ret;
7000-
} // LONG AppDomainTransitionExceptionFilter()
7001-
70026951
// This filter will be used process exceptions escaping out of dynamic reflection invocation as
70036952
// unhandled and will eventually be caught in the VM to be made as inner exception of
70046953
// TargetInvocationException that will be thrown from the VM.
@@ -7911,25 +7860,7 @@ PTR_EHWatsonBucketTracker GetWatsonBucketTrackerForPreallocatedException(OBJECTR
79117860
doValidation:
79127861
_ASSERTE(pWBTracker != NULL);
79137862

7914-
// Incase of an OOM, we may not have an IP in the Watson bucket tracker. A scenario
7915-
// would be default domain calling to AD 2 that calls into AD 3.
7916-
//
7917-
// AD 3 has an exception that is represented by a preallocated exception object. The
7918-
// exception goes unhandled and reaches AD2/AD3 transition boundary. The bucketing details
7919-
// from AD3 are copied to UETracker and once the exception is reraised in AD2, we will
7920-
// enter SetupInitialThrowBucketingDetails to copy the bucketing details to the active
7921-
// exception tracker.
7922-
//
7923-
// This copy operation could fail due to OOM and the active exception tracker in AD 2,
7924-
// for the preallocated exception object, will not have any bucketing details. If the
7925-
// exception remains unhandled in AD 2, then just before it reaches DefDomain/AD2 boundary,
7926-
// we will attempt to capture the bucketing details in AppDomainTransitionExceptionFilter,
7927-
// that will bring us here.
7928-
//
7929-
// In such a case, the active exception tracker will not have any bucket details for the
7930-
// preallocated exception. In such a case, if the IP does not exist, we will return NULL
7931-
// indicating that we couldnt find the Watson bucket tracker, since returning a tracker
7932-
// that does not have any bucketing details will be of no use to the caller.
7863+
// In case of an OOM, we may not have an IP in the Watson bucket tracker.
79337864
if (pWBTracker->RetrieveWatsonBucketIp() != NULL)
79347865
{
79357866
// Check if the buckets exist or not..
@@ -8321,11 +8252,6 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)
83218252

83228253
// If we are here, then this was a new exception raised
83238254
// from outside the managed EH clauses (fault/finally/catch).
8324-
//
8325-
// The throwable *may* have the bucketing details already
8326-
// if this exception was raised when it was crossing over
8327-
// an AD transition boundary. Those are stored in UE watson bucket
8328-
// tracker by AppDomainTransitionExceptionFilter.
83298255
if (fIsPreallocatedException)
83308256
{
83318257
PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
@@ -8351,10 +8277,8 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)
83518277
}
83528278
}
83538279
#endif // _DEBUG
8354-
// These should have been captured at AD transition OR
8355-
// could be bucketing details of preallocated [rude] thread abort exception.
8356-
_ASSERTE(pUEWatsonBucketTracker->CapturedAtADTransition() ||
8357-
((fIsThreadAbortException || fIsPreallocatedOOMExceptionForTA) && pUEWatsonBucketTracker->CapturedForThreadAbort()));
8280+
// These could be bucketing details of preallocated [rude] thread abort exception.
8281+
_ASSERTE((fIsThreadAbortException || fIsPreallocatedOOMExceptionForTA) && pUEWatsonBucketTracker->CapturedForThreadAbort());
83588282

83598283
if (!fIsThreadAbortException)
83608284
{
@@ -8440,7 +8364,7 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)
84408364
if (ip != NULL)
84418365
{
84428366
// Confirm that we had the buckets captured for thread abort
8443-
_ASSERTE(pUEWatsonBucketTracker->CapturedForThreadAbort() || pUEWatsonBucketTracker->CapturedAtADTransition());
8367+
_ASSERTE(pUEWatsonBucketTracker->CapturedForThreadAbort());
84448368

84458369
if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
84468370
{

src/coreclr/vm/excep.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -647,11 +647,6 @@ inline void CopyOSContext(T_CONTEXT* pDest, T_CONTEXT* pSrc)
647647

648648
void SaveCurrentExceptionInfo(PEXCEPTION_RECORD pRecord, PT_CONTEXT pContext);
649649

650-
// See implementation for detailed comments in excep.cpp
651-
LONG AppDomainTransitionExceptionFilter(
652-
EXCEPTION_POINTERS *pExceptionInfo, // the pExceptionInfo passed to a filter function.
653-
PVOID pParam);
654-
655650
// See implementation for detailed comments in excep.cpp
656651
LONG ReflectionInvocationExceptionFilter(
657652
EXCEPTION_POINTERS *pExceptionInfo, // the pExceptionInfo passed to a filter function.

0 commit comments

Comments
 (0)