Skip to content

Commit 6e1e6b1

Browse files
Add support in WASM for calling UnmanagedCallersOnly functions (#121359)
Fixes #121006 This provides an entry point, similar to P/Invokes thunks, for generated reverse P/Invoke stubs. A commented out example of the generated code was provided.
1 parent 663e792 commit 6e1e6b1

File tree

20 files changed

+330
-81
lines changed

20 files changed

+330
-81
lines changed

src/coreclr/hosts/corerun/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,17 @@ else()
7474
-sMAXIMUM_MEMORY=2147483648
7575
-sALLOW_MEMORY_GROWTH=1
7676
-sSTACK_SIZE=5MB
77-
-sWASM_BIGINT=1
7877
-sEXPORTED_RUNTIME_METHODS=cwrap,ccall,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAP64,HEAPU64,HEAPF32,HEAPF64,safeSetTimeout,maybeExit,exitJS,abort,lengthBytesUTF8,UTF8ToString,stringToUTF8Array
7978
-sEXPORTED_FUNCTIONS=_main,_GetDotNetRuntimeContractDescriptor
8079
-sENVIRONMENT=node,shell,web
8180
-Wl,-error-limit=0)
8281

8382
if (CORERUN_IN_BROWSER)
83+
# Node.js doesn't have good support for WASM_BIGINT
84+
# so it only is added when running in the browser.
85+
target_link_options(corerun PRIVATE
86+
-sWASM_BIGINT=1)
87+
8488
# Include the virtual file system data for the
8589
# browser scenario.
8690
set(WASM_PRELOAD_DIR "${CMAKE_INSTALL_PREFIX}/IL")

src/coreclr/inc/corinfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,7 @@ enum CORINFO_ACCESS_FLAGS
808808
CORINFO_ACCESS_NONNULL = 0x0004, // Instance is guaranteed non-null
809809

810810
CORINFO_ACCESS_LDFTN = 0x0010, // Accessed via ldftn
811+
CORINFO_ACCESS_UNMANAGED_CALLER_MAYBE = 0x0020, // Method might be attributed with UnmanagedCallersOnlyAttribute.
811812

812813
// Field access flags
813814
CORINFO_ACCESS_GET = 0x0100, // Field get (ldfld)

src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ public enum CORINFO_ACCESS_FLAGS
549549
CORINFO_ACCESS_NONNULL = 0x0004, // Instance is guaranteed non-null
550550

551551
CORINFO_ACCESS_LDFTN = 0x0010, // Accessed via ldftn
552+
CORINFO_ACCESS_UNMANAGED_CALLER_MAYBE = 0x0020, // Method might be attributed with UnmanagedCallersOnlyAttribute.
552553

553554
// Field access flags
554555
CORINFO_ACCESS_GET = 0x0100, // Field get (ldfld)

src/coreclr/vm/comdelegate.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,17 +1277,22 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis,
12771277
// We can get virtual delegates closed over null through this code path, so be careful to handle that case (no need to
12781278
// virtualize since we're just going to throw NullRefException at invocation time).
12791279
// </TODO>
1280-
if (pTargetMethod->IsVirtual() &&
1281-
*pRefFirstArg != NULL &&
1282-
pTargetMethod->GetMethodTable() != (*pRefFirstArg)->GetMethodTable())
1280+
if (pTargetMethod->IsVirtual()
1281+
&& *pRefFirstArg != NULL
1282+
&& pTargetMethod->GetMethodTable() != (*pRefFirstArg)->GetMethodTable())
1283+
{
12831284
pTargetCode = pTargetMethod->GetMultiCallableAddrOfVirtualizedCode(pRefFirstArg, pTargetMethod->GetMethodTable());
1284-
else
1285+
}
12851286
#ifdef HAS_THISPTR_RETBUF_PRECODE
1286-
if (pTargetMethod->IsStatic() && pTargetMethod->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1287+
else if (pTargetMethod->IsStatic() && pTargetMethod->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1288+
{
12871289
pTargetCode = pTargetMethod->GetLoaderAllocator()->GetFuncPtrStubs()->GetFuncPtrStub(pTargetMethod, PRECODE_THISPTR_RETBUF);
1288-
else
1290+
}
12891291
#endif // HAS_THISPTR_RETBUF_PRECODE
1292+
else
1293+
{
12901294
pTargetCode = pTargetMethod->GetMultiCallableAddrOfCode();
1295+
}
12911296
_ASSERTE(pTargetCode);
12921297

12931298
refRealDelegate->SetTarget(*pRefFirstArg);

src/coreclr/vm/corhost.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -694,8 +694,9 @@ HRESULT CorHost2::CreateDelegate(
694694
EMPTY_STRING_TO_NULL(wszClassName);
695695
EMPTY_STRING_TO_NULL(wszMethodName);
696696

697-
if (fnPtr == 0)
697+
if (fnPtr == NULL)
698698
return E_POINTER;
699+
699700
*fnPtr = 0;
700701

701702
if(wszAssemblyName == NULL)
@@ -714,10 +715,6 @@ HRESULT CorHost2::CreateDelegate(
714715
HRESULT hr = S_OK;
715716
BEGIN_EXTERNAL_ENTRYPOINT(&hr);
716717

717-
#ifdef FEATURE_PORTABLE_ENTRYPOINTS
718-
hr = E_NOTIMPL;
719-
720-
#else // !FEATURE_PORTABLE_ENTRYPOINTS
721718
GCX_COOP_THREAD_EXISTS(GET_THREAD());
722719

723720
MAKE_UTF8PTR_FROMWIDE(szClassName, wszClassName);
@@ -754,15 +751,19 @@ HRESULT CorHost2::CreateDelegate(
754751

755752
if (pMD->HasUnmanagedCallersOnlyAttribute())
756753
{
757-
*fnPtr = pMD->GetMultiCallableAddrOfCode();
754+
pMD->PrepareForUseAsAFunctionPointer();
755+
*fnPtr = pMD->GetMultiCallableAddrOfCode(CORINFO_ACCESS_UNMANAGED_CALLER_MAYBE);
758756
}
759757
else
760758
{
759+
#ifdef FEATURE_PORTABLE_ENTRYPOINTS
760+
ThrowHR(COR_E_NOTSUPPORTED);
761+
#else // !FEATURE_PORTABLE_ENTRYPOINTS
761762
UMEntryThunkData* pUMEntryThunk = pMD->GetLoaderAllocator()->GetUMEntryThunkCache()->GetUMEntryThunk(pMD);
762763
*fnPtr = (INT_PTR)pUMEntryThunk->GetCode();
764+
#endif // FEATURE_PORTABLE_ENTRYPOINTS
763765
}
764766
}
765-
#endif // FEATURE_PORTABLE_ENTRYPOINTS
766767

767768
END_EXTERNAL_ENTRYPOINT;
768769

src/coreclr/vm/dllimport.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6165,6 +6165,26 @@ EXTERN_C void STDCALL GenericPInvokeCalliStubWorker(TransitionBlock * pTransitio
61656165
pFrame->Pop(CURRENT_THREAD);
61666166
}
61676167

6168+
EXTERN_C void LookupMethodByName(const char* fullQualifiedTypeName, const char* methodName, MethodDesc** ppMD)
6169+
{
6170+
CONTRACTL
6171+
{
6172+
STANDARD_VM_CHECK;
6173+
ENTRY_POINT;
6174+
}
6175+
CONTRACTL_END;
6176+
6177+
_ASSERTE(fullQualifiedTypeName != nullptr);
6178+
_ASSERTE(methodName != nullptr);
6179+
_ASSERTE(ppMD != nullptr);
6180+
6181+
SString fullQualifiedTypeNameUtf8(SString::Utf8, fullQualifiedTypeName);
6182+
TypeHandle type = TypeName::GetTypeFromAsmQualifiedName(fullQualifiedTypeNameUtf8.GetUnicode(), /*bThrowIfNotFound*/ TRUE);
6183+
_ASSERTE(!type.IsTypeDesc());
6184+
6185+
*ppMD = MemberLoader::FindMethodByName(type.GetMethodTable(), methodName);
6186+
}
6187+
61686188
namespace
61696189
{
61706190
//-------------------------------------------------------------------------------------

src/coreclr/vm/interpexec.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ void InvokeCalliStub(PCODE ftn, void *cookie, int8_t *pArgs, int8_t *pRet)
324324
pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize);
325325
}
326326

327-
LPVOID GetCookieForCalliSig(MetaSig metaSig)
327+
void* GetCookieForCalliSig(MetaSig metaSig)
328328
{
329329
STANDARD_VM_CONTRACT;
330330

src/coreclr/vm/interpexec.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ struct ExceptionClauseArgs
7979

8080
void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFrame *pFrame, InterpThreadContext *pThreadContext, ExceptionClauseArgs *pExceptionClauseArgs = NULL);
8181

82+
extern "C" void LookupMethodByName(const char* fullQualifiedTypeName, const char* methodName, MethodDesc** ppMD);
83+
extern "C" void ExecuteInterpretedMethodFromUnmanaged(MethodDesc* pMD, int8_t* args, size_t argSize, int8_t* ret);
84+
8285
CallStubHeader *CreateNativeToInterpreterCallStub(InterpMethod* pInterpMethod);
8386

84-
#endif
87+
#endif // _INTERPEXEC_H_

src/coreclr/vm/jitinterface.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9158,15 +9158,15 @@ void CEEInfo::getFunctionEntryPoint(CORINFO_METHOD_HANDLE ftnHnd,
91589158
}
91599159
else
91609160
{
9161-
ret = (void *)ftn->TryGetMultiCallableAddrOfCode(accessFlags);
9161+
ret = (void*)ftn->TryGetMultiCallableAddrOfCode((CORINFO_ACCESS_FLAGS)(accessFlags | CORINFO_ACCESS_UNMANAGED_CALLER_MAYBE));
91629162

91639163
// TryGetMultiCallableAddrOfCode returns NULL if indirect access is desired
91649164
if (ret == NULL)
91659165
{
91669166
// should never get here for EnC methods or if interception via remoting stub is required
91679167
_ASSERTE(!ftn->InEnCEnabledModule());
91689168

9169-
ret = (void *)ftn->GetAddrOfSlot();
9169+
ret = (void*)ftn->GetAddrOfSlot();
91709170

91719171
accessType = IAT_PVALUE;
91729172
}
@@ -9205,7 +9205,7 @@ void CEEInfo::getFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn,
92059205
pMD->PrepareForUseAsAFunctionPointer();
92069206

92079207
pResult->accessType = IAT_VALUE;
9208-
pResult->addr = (void*)pMD->GetMultiCallableAddrOfCode();
9208+
pResult->addr = (void*)pMD->GetMultiCallableAddrOfCode(CORINFO_ACCESS_UNMANAGED_CALLER_MAYBE);
92099209

92109210
EE_TO_JIT_TRANSITION();
92119211
}
@@ -11317,7 +11317,7 @@ LPVOID CEEInfo::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig)
1131711317
#ifdef FEATURE_INTERPRETER
1131811318

1131911319
// Forward declare the function for mapping MetaSig to a cookie.
11320-
LPVOID GetCookieForCalliSig(MetaSig metaSig);
11320+
void* GetCookieForCalliSig(MetaSig metaSig);
1132111321

1132211322
LPVOID CInterpreterJitInfo::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig)
1132311323
{
@@ -13961,7 +13961,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule,
1396113961
}
1396213962

1396313963
MethodEntry:
13964-
result = pMD->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY);
13964+
result = pMD->GetMultiCallableAddrOfCode(CORINFO_ACCESS_UNMANAGED_CALLER_MAYBE);
1396513965
}
1396613966
break;
1396713967

src/coreclr/vm/method.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2104,12 +2104,13 @@ PCODE MethodDesc::TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags
21042104
COMPlusThrow(kInvalidOperationException, IDS_EE_CODEEXECUTION_CONTAINSGENERICVAR);
21052105
}
21062106

2107+
#ifdef _DEBUG
21072108
if (accessFlags & CORINFO_ACCESS_LDFTN)
21082109
{
21092110
// Whenever we use LDFTN on shared-generic-code-which-requires-an-extra-parameter
2110-
// we need to give out the address of an instantiating stub. This is why we give
2111-
// out GetStableEntryPoint() for the IsInstantiatingStub() case: this is
2112-
// safe. But first we assert that we only use GetMultiCallableAddrOfCode on
2111+
// we need to give out the address of an instantiating stub. This is why we give
2112+
// out GetStableEntryPoint() for IsInstantiatingStub() via the IsWrapperStub() case: this is
2113+
// safe. But first we assert that we only use GetMultiCallableAddrOfCode on
21132114
// the instantiating stubs and not on the shared code itself.
21142115
_ASSERTE(!RequiresInstArg());
21152116
_ASSERTE(!IsSharedByGenericMethodInstantiations());
@@ -2118,8 +2119,26 @@ PCODE MethodDesc::TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags
21182119
_ASSERTE((accessFlags & ~CORINFO_ACCESS_LDFTN) == 0);
21192120
}
21202121

2122+
#ifndef FEATURE_PORTABLE_ENTRYPOINTS
2123+
// If HasUnmanagedCallersOnlyAttribute() is true, then CORINFO_ACCESS_UNMANAGED_CALLER_MAYBE must be set.
2124+
// We only validate this on non portable entrypoints because HasUnmanagedCallersOnlyAttribute()
2125+
// is an unnecessary cost, even in non-Release builds, on portable entrypoint platforms.
2126+
if (HasUnmanagedCallersOnlyAttribute())
2127+
{
2128+
_ASSERTE((accessFlags & CORINFO_ACCESS_UNMANAGED_CALLER_MAYBE) != 0);
2129+
}
2130+
#endif // !FEATURE_PORTABLE_ENTRYPOINTS
2131+
#endif // _DEBUG
2132+
21212133
#ifdef FEATURE_PORTABLE_ENTRYPOINTS
2122-
return GetPortableEntryPoint();
2134+
PCODE entryPoint = GetPortableEntryPoint();
2135+
if (accessFlags & CORINFO_ACCESS_UNMANAGED_CALLER_MAYBE
2136+
&& PortableEntryPoint::ToPortableEntryPoint(entryPoint)->HasUnmanagedCallersOnlyAttribute())
2137+
{
2138+
entryPoint = (PCODE)PortableEntryPoint::GetActualCode(entryPoint);
2139+
}
2140+
2141+
return entryPoint;
21232142

21242143
#else // !FEATURE_PORTABLE_ENTRYPOINTS
21252144
if (RequiresStableEntryPoint() && !HasStableEntryPoint())

0 commit comments

Comments
 (0)