Skip to content

Commit 5219f86

Browse files
kotlarmilosjkotas
andauthored
[clr-ios] Use IL unboxing stubs when FEATURE_JIT isn't available (#121115)
This change prevents the runtime from emitting native machine code at execution time through StubLinkerCPU on platforms where dynamic code generation is not allowed due to W^X and code-signing restrictions. Currently, unboxing and generic instantiating stubs still generate arm64 code via CPUSTUBLINKER that works as a lightweight JIT, because FEATURE_PORTABLE_SHUFFLE_THUNKS is enabled on all non-x86 targets. Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent b02019a commit 5219f86

File tree

11 files changed

+113
-176
lines changed

11 files changed

+113
-176
lines changed

src/coreclr/inc/switches.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,11 @@
165165
#define CHAIN_LOOKUP
166166
#endif // FEATURE_VIRTUAL_STUB_DISPATCH
167167

168-
#if !defined(FEATURE_PORTABLE_ENTRYPOINTS) && !defined(TARGET_X86)
168+
// FEATURE_PORTABLE_SHUFFLE_THUNKS depends on CPUSTUBLINKER that is de-facto JIT
169+
#if defined(FEATURE_JIT) && !defined(TARGET_X86)
169170
#define FEATURE_PORTABLE_SHUFFLE_THUNKS
170171
#endif
171172

172-
#if defined(TARGET_UNIX) || !defined(TARGET_X86)
173-
#define FEATURE_INSTANTIATINGSTUB_AS_IL
174-
#endif
175-
176173
// If this is uncommented, leaves a file "StubLog_<pid>.log" with statistics on the behavior
177174
// of stub-based interface dispatch.
178175
//#define STUB_LOGGING

src/coreclr/vm/arm64/cgencpu.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,9 +501,7 @@ class StubLinkerCPU : public StubLinker
501501

502502
void EmitShuffleThunk(struct ShuffleEntry *pShuffleEntryArray);
503503

504-
#if defined(FEATURE_SHARE_GENERIC_CODE)
505504
void EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg);
506-
#endif // FEATURE_SHARE_GENERIC_CODE
507505

508506
void EmitMovConstant(IntReg target, UINT64 constant);
509507
void EmitJumpRegister(IntReg regTarget);

src/coreclr/vm/comdelegate.cpp

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ static UINT16 ShuffleOfs(INT ofs, UINT stackSizeDelta = 0)
5656

5757
return static_cast<UINT16>(ofs);
5858
}
59-
#endif
59+
#endif // defined(TARGET_X86)
6060

6161
#ifdef FEATURE_PORTABLE_SHUFFLE_THUNKS
6262

@@ -635,7 +635,7 @@ BOOL GenerateShuffleArrayPortable(MethodDesc* pMethodSrc, MethodDesc *pMethodDst
635635
}
636636
#endif // FEATURE_PORTABLE_SHUFFLE_THUNKS
637637

638-
#ifndef FEATURE_PORTABLE_ENTRYPOINTS
638+
#if defined(FEATURE_PORTABLE_SHUFFLE_THUNKS) || defined(TARGET_X86)
639639
BOOL GenerateShuffleArray(MethodDesc* pInvoke, MethodDesc *pTargetMeth, SArray<ShuffleEntry> * pShuffleEntryArray)
640640
{
641641
STANDARD_VM_CONTRACT;
@@ -775,7 +775,7 @@ BOOL GenerateShuffleArray(MethodDesc* pInvoke, MethodDesc *pTargetMeth, SArray<S
775775
return TRUE;
776776
}
777777
static ShuffleThunkCache* s_pShuffleThunkCache = NULL;
778-
#endif // !FEATURE_PORTABLE_ENTRYPOINTS
778+
#endif // FEATURE_PORTABLE_SHUFFLE_THUNKS || TARGET_X86
779779

780780
// One time init.
781781
void COMDelegate::Init()
@@ -787,7 +787,7 @@ void COMDelegate::Init()
787787
MODE_ANY;
788788
}
789789
CONTRACTL_END;
790-
#ifndef FEATURE_PORTABLE_ENTRYPOINTS
790+
#if defined(FEATURE_PORTABLE_SHUFFLE_THUNKS) || defined(TARGET_X86)
791791
s_pShuffleThunkCache = new ShuffleThunkCache(SystemDomain::GetGlobalLoaderAllocator()->GetStubHeap());
792792
#endif
793793
}
@@ -829,7 +829,6 @@ LoaderHeap *DelegateEEClass::GetStubHeap()
829829
return GetInvokeMethod()->GetLoaderAllocator()->GetStubHeap();
830830
}
831831

832-
#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) || defined(FEATURE_PORTABLE_ENTRYPOINTS)
833832
static Stub* CreateILDelegateShuffleThunk(MethodDesc* pDelegateMD, bool callTargetWithThis)
834833
{
835834
SigTypeContext typeContext(pDelegateMD);
@@ -889,7 +888,6 @@ static Stub* CreateILDelegateShuffleThunk(MethodDesc* pDelegateMD, bool callTarg
889888

890889
return Stub::NewStub(JitILStub(pStubMD), NEWSTUB_FL_SHUFFLE_THUNK);
891890
}
892-
#endif // TARGET_RISCV64 || TARGET_LOONGARCH64
893891

894892
static PCODE SetupShuffleThunk(MethodTable * pDelMT, MethodDesc *pTargetMeth)
895893
{
@@ -915,7 +913,7 @@ static PCODE SetupShuffleThunk(MethodTable * pDelMT, MethodDesc *pTargetMeth)
915913

916914
MethodDesc *pMD = pClass->GetInvokeMethod();
917915

918-
#ifndef FEATURE_PORTABLE_ENTRYPOINTS
916+
#if defined(FEATURE_PORTABLE_SHUFFLE_THUNKS) || defined(TARGET_X86)
919917
// We haven't already setup a shuffle thunk, go do it now (which will cache the result automatically).
920918
StackSArray<ShuffleEntry> rShuffleEntryArray;
921919
if (GenerateShuffleArray(pMD, pTargetMeth, &rShuffleEntryArray))
@@ -931,14 +929,9 @@ static PCODE SetupShuffleThunk(MethodTable * pDelMT, MethodDesc *pTargetMeth)
931929
pShuffleThunk = pShuffleThunkCache->Canonicalize((const BYTE *)&rShuffleEntryArray[0], "DelegateShuffleThunk");
932930
}
933931
else
934-
#endif // !FEATURE_PORTABLE_ENTRYPOINTS
932+
#endif // FEATURE_PORTABLE_SHUFFLE_THUNKS || TARGET_X86
935933
{
936-
#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) || defined(FEATURE_PORTABLE_ENTRYPOINTS)
937934
pShuffleThunk = CreateILDelegateShuffleThunk(pMD, isInstRetBuff);
938-
#else
939-
_ASSERTE(FALSE);
940-
return (PCODE)NULL;
941-
#endif // TARGET_RISCV64 || TARGET_LOONGARCH64
942935
}
943936

944937
if (!pShuffleThunk)

src/coreclr/vm/dllimport.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,8 @@ enum ILStubTypes
194194
ILSTUB_ARRAYOP_SET = 0x80000002,
195195
ILSTUB_ARRAYOP_ADDRESS = 0x80000003,
196196
ILSTUB_MULTICASTDELEGATE_INVOKE = 0x80000004,
197-
#ifdef FEATURE_INSTANTIATINGSTUB_AS_IL
198197
ILSTUB_UNBOXINGILSTUB = 0x80000005,
199198
ILSTUB_INSTANTIATINGSTUB = 0x80000006,
200-
#endif // FEATURE_INSTANTIATINGSTUB_AS_IL
201199
ILSTUB_WRAPPERDELEGATE_INVOKE = 0x80000007,
202200
ILSTUB_TAILCALL_STOREARGS = 0x80000008,
203201
ILSTUB_TAILCALL_CALLTARGET = 0x80000009,
@@ -231,10 +229,8 @@ inline bool SF_IsMulticastDelegateStub (DWORD dwStubFlags) { LIMITED_METHOD_CON
231229
inline bool SF_IsDelegateInvokeMethod (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_DELEGATE_INVOKE_METHOD); }
232230

233231
inline bool SF_IsWrapperDelegateStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_WRAPPERDELEGATE_INVOKE); }
234-
#ifdef FEATURE_INSTANTIATINGSTUB_AS_IL
235232
inline bool SF_IsUnboxingILStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_UNBOXINGILSTUB); }
236233
inline bool SF_IsInstantiatingStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_INSTANTIATINGSTUB); }
237-
#endif // FEATURE_INSTANTIATINGSTUB_AS_IL
238234
inline bool SF_IsTailCallStoreArgsStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_TAILCALL_STOREARGS); }
239235
inline bool SF_IsTailCallCallTargetStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_TAILCALL_CALLTARGET); }
240236
inline bool SF_IsDelegateShuffleThunk (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_DELEGATE_SHUFFLE_THUNK); }

src/coreclr/vm/i386/stublinkerx86.cpp

Lines changed: 82 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ VOID StubLinkerCPU::X64EmitMovXmmXmm(X86Reg destXmmreg, X86Reg srcXmmReg)
374374
Emit8(REX_PREFIX_BASE | rex);
375375

376376
Emit16(0x280F);
377-
Emit8(static_cast<UINT8>(0300 | (destXmmreg << 3) | srcXmmReg));
377+
Emit8(static_cast<UINT8>(0300 | (destXmmreg << 3) | srcXmmReg));
378378
}
379379

380380
//---------------------------------------------------------------
@@ -852,7 +852,7 @@ static const X86Reg c_argRegs[] = {
852852
// and pass it as an extra argument. Thus this stub generator really covers both
853853
// - Unboxing, non-instantiating stubs
854854
// - Unboxing, method-table-instantiating stubs
855-
VOID StubLinkerCPU::EmitUnboxMethodStub(MethodDesc* pUnboxMD)
855+
bool StubLinkerCPU::EmitUnboxMethodStub(MethodDesc* pUnboxMD)
856856
{
857857
CONTRACTL
858858
{
@@ -861,25 +861,96 @@ VOID StubLinkerCPU::EmitUnboxMethodStub(MethodDesc* pUnboxMD)
861861
}
862862
CONTRACTL_END;
863863

864-
#ifdef FEATURE_INSTANTIATINGSTUB_AS_IL
865-
_ASSERTE(!pUnboxMD->RequiresInstMethodTableArg());
866-
#else
867864
if (pUnboxMD->RequiresInstMethodTableArg())
868865
{
869-
EmitInstantiatingMethodStub(pUnboxMD, NULL);
870-
return;
866+
return EmitInstantiatingMethodStub(pUnboxMD, NULL);
871867
}
872-
#endif
873868

874869
//
875870
// unboxing a value class simply means adding sizeof(void*) to the THIS pointer
876871
//
877872
X86EmitAddReg(THIS_kREG, sizeof(void*));
878873
EmitTailJumpToMethod(pUnboxMD);
874+
return true;
875+
}
876+
877+
// The stub generated by this method passes an extra dictionary argument before jumping to
878+
// shared-instantiation generic code.
879+
//
880+
// pMD is either
881+
// * An InstantiatedMethodDesc for a generic method whose code is shared across instantiations.
882+
// In this case, the extra argument is the InstantiatedMethodDesc for the instantiation-specific stub itself.
883+
// or * A MethodDesc for a static method in a generic class whose code is shared across instantiations.
884+
// In this case, the extra argument is the MethodTable pointer of the instantiated type.
885+
// or * A MethodDesc for unboxing stub. In this case, the extra argument is null.
886+
bool StubLinkerCPU::EmitInstantiatingMethodStub(MethodDesc* pMD, void* extra)
887+
{
888+
CONTRACTL
889+
{
890+
STANDARD_VM_CHECK;
891+
PRECONDITION(pMD->RequiresInstArg());
892+
}
893+
CONTRACTL_END;
894+
895+
#ifdef UNIX_X86_ABI
896+
// Unix x86 ABI requires stack alignment
897+
return false;
898+
#else // UNIX_X86_ABI
899+
MetaSig msig(pMD);
900+
ArgIterator argit(&msig);
901+
902+
int paramTypeArgOffset = argit.GetParamTypeArgOffset();
903+
904+
// It's on the stack
905+
if (TransitionBlock::IsStackArgumentOffset(paramTypeArgOffset))
906+
{
907+
// Pop return address into AX
908+
X86EmitPopReg(kEAX);
909+
910+
if (extra != NULL)
911+
{
912+
// Push extra dictionary argument
913+
X86EmitPushImmPtr(extra);
914+
}
915+
else
916+
{
917+
// Push the vtable pointer from "this"
918+
X86EmitIndexPush(THIS_kREG, 0);
919+
}
920+
921+
// Put return address back
922+
X86EmitPushReg(kEAX);
923+
}
924+
// It's in a register
925+
else
926+
{
927+
X86Reg paramReg = GetX86ArgumentRegisterFromOffset(paramTypeArgOffset - TransitionBlock::GetOffsetOfArgumentRegisters());
928+
929+
if (extra != NULL)
930+
{
931+
X86EmitRegLoad(paramReg, (UINT_PTR)extra);
932+
}
933+
else
934+
{
935+
// Just extract the vtable pointer from "this"
936+
X86EmitIndexRegLoad(paramReg, THIS_kREG);
937+
}
938+
}
939+
940+
if (extra == NULL)
941+
{
942+
// Unboxing stub case.
943+
X86EmitAddReg(THIS_kREG, sizeof(void*));
944+
}
945+
946+
EmitTailJumpToMethod(pMD);
947+
948+
return true;
949+
#endif // UNIX_X86_ABI
879950
}
880-
#endif //TARGET_X86
951+
#endif // TARGET_X86
881952

882-
#if defined(FEATURE_SHARE_GENERIC_CODE) && defined(TARGET_AMD64)
953+
#ifdef FEATURE_PORTABLE_SHUFFLE_THUNKS
883954
VOID StubLinkerCPU::EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg)
884955
{
885956
STANDARD_VM_CONTRACT;
@@ -939,7 +1010,7 @@ VOID StubLinkerCPU::EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, s
9391010
EmitTailJumpToMethod(pSharedMD);
9401011
SetTargetMethod(pSharedMD);
9411012
}
942-
#endif // defined(FEATURE_SHARE_GENERIC_CODE) && defined(TARGET_AMD64)
1013+
#endif // FEATURE_PORTABLE_SHUFFLE_THUNKS
9431014

9441015
#ifdef TARGET_AMD64
9451016
VOID StubLinkerCPU::EmitLoadMethodAddressIntoAX(MethodDesc *pMD)
@@ -984,76 +1055,6 @@ VOID StubLinkerCPU::EmitTailJumpToMethod(MethodDesc *pMD)
9841055
#endif
9851056
}
9861057

987-
#if defined(FEATURE_SHARE_GENERIC_CODE) && !defined(FEATURE_INSTANTIATINGSTUB_AS_IL) && defined(TARGET_X86)
988-
// The stub generated by this method passes an extra dictionary argument before jumping to
989-
// shared-instantiation generic code.
990-
//
991-
// pMD is either
992-
// * An InstantiatedMethodDesc for a generic method whose code is shared across instantiations.
993-
// In this case, the extra argument is the InstantiatedMethodDesc for the instantiation-specific stub itself.
994-
// or * A MethodDesc for a static method in a generic class whose code is shared across instantiations.
995-
// In this case, the extra argument is the MethodTable pointer of the instantiated type.
996-
// or * A MethodDesc for unboxing stub. In this case, the extra argument is null.
997-
VOID StubLinkerCPU::EmitInstantiatingMethodStub(MethodDesc* pMD, void* extra)
998-
{
999-
CONTRACTL
1000-
{
1001-
STANDARD_VM_CHECK;
1002-
PRECONDITION(pMD->RequiresInstArg());
1003-
}
1004-
CONTRACTL_END;
1005-
1006-
MetaSig msig(pMD);
1007-
ArgIterator argit(&msig);
1008-
1009-
int paramTypeArgOffset = argit.GetParamTypeArgOffset();
1010-
1011-
// It's on the stack
1012-
if (TransitionBlock::IsStackArgumentOffset(paramTypeArgOffset))
1013-
{
1014-
// Pop return address into AX
1015-
X86EmitPopReg(kEAX);
1016-
1017-
if (extra != NULL)
1018-
{
1019-
// Push extra dictionary argument
1020-
X86EmitPushImmPtr(extra);
1021-
}
1022-
else
1023-
{
1024-
// Push the vtable pointer from "this"
1025-
X86EmitIndexPush(THIS_kREG, 0);
1026-
}
1027-
1028-
// Put return address back
1029-
X86EmitPushReg(kEAX);
1030-
}
1031-
// It's in a register
1032-
else
1033-
{
1034-
X86Reg paramReg = GetX86ArgumentRegisterFromOffset(paramTypeArgOffset - TransitionBlock::GetOffsetOfArgumentRegisters());
1035-
1036-
if (extra != NULL)
1037-
{
1038-
X86EmitRegLoad(paramReg, (UINT_PTR)extra);
1039-
}
1040-
else
1041-
{
1042-
// Just extract the vtable pointer from "this"
1043-
X86EmitIndexRegLoad(paramReg, THIS_kREG);
1044-
}
1045-
}
1046-
1047-
if (extra == NULL)
1048-
{
1049-
// Unboxing stub case.
1050-
X86EmitAddReg(THIS_kREG, sizeof(void*));
1051-
}
1052-
1053-
EmitTailJumpToMethod(pMD);
1054-
}
1055-
#endif // defined(FEATURE_SHARE_GENERIC_CODE) && !defined(FEATURE_INSTANTIATINGSTUB_AS_IL) && defined(TARGET_X86)
1056-
10571058
VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray)
10581059
{
10591060
STANDARD_VM_CONTRACT;

src/coreclr/vm/i386/stublinkerx86.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,17 +198,17 @@ class StubLinkerCPU : public StubLinker
198198
}
199199

200200
#ifdef TARGET_X86
201-
VOID EmitUnboxMethodStub(MethodDesc* pRealMD);
201+
bool EmitUnboxMethodStub(MethodDesc* pRealMD);
202+
bool EmitInstantiatingMethodStub(MethodDesc* pSharedMD, void* extra);
202203
#endif // TARGET_X86
203204
VOID EmitTailJumpToMethod(MethodDesc *pMD);
204205
#ifdef TARGET_AMD64
205206
VOID EmitLoadMethodAddressIntoAX(MethodDesc *pMD);
206207
#endif
207208

208-
#if defined(FEATURE_SHARE_GENERIC_CODE)
209-
VOID EmitInstantiatingMethodStub(MethodDesc* pSharedMD, void* extra);
210-
#endif // FEATURE_SHARE_GENERIC_CODE
209+
#ifdef FEATURE_PORTABLE_SHUFFLE_THUNKS
211210
VOID EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg);
211+
#endif
212212

213213
//===========================================================================
214214
// Emits code to adjust for a static delegate target.

src/coreclr/vm/ilstubcache.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,8 @@ namespace
129129
case DynamicMethodDesc::StubArrayOp: return "IL_STUB_Array";
130130
case DynamicMethodDesc::StubMulticastDelegate: return "IL_STUB_MulticastDelegate_Invoke";
131131
case DynamicMethodDesc::StubDelegateInvokeMethod: return "IL_STUB_Delegate_Invoke";
132-
#ifdef FEATURE_INSTANTIATINGSTUB_AS_IL
133132
case DynamicMethodDesc::StubUnboxingIL: return "IL_STUB_UnboxingStub";
134133
case DynamicMethodDesc::StubInstantiating: return "IL_STUB_InstantiatingStub";
135-
#endif
136134
case DynamicMethodDesc::StubWrapperDelegate: return "IL_STUB_WrapperDelegate_Invoke";
137135
case DynamicMethodDesc::StubTailCallStoreArgs: return "IL_STUB_StoreTailCallArgs";
138136
case DynamicMethodDesc::StubTailCallCallTarget: return "IL_STUB_CallTailCallTarget";
@@ -243,7 +241,6 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa
243241
pMD->SetILStubType(DynamicMethodDesc::StubWrapperDelegate);
244242
}
245243
else
246-
#ifdef FEATURE_INSTANTIATINGSTUB_AS_IL
247244
if (SF_IsUnboxingILStub(dwStubFlags))
248245
{
249246
pMD->SetILStubType(DynamicMethodDesc::StubUnboxingIL);
@@ -254,7 +251,6 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa
254251
pMD->SetILStubType(DynamicMethodDesc::StubInstantiating);
255252
}
256253
else
257-
#endif
258254
if (SF_IsTailCallStoreArgsStub(dwStubFlags))
259255
{
260256
pMD->SetILStubType(DynamicMethodDesc::StubTailCallStoreArgs);

src/coreclr/vm/loongarch64/cgencpu.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,7 @@ class StubLinkerCPU : public StubLinker
411411
void EmitCallLabel(CodeLabel *target, BOOL fTailCall, BOOL fIndirect);
412412

413413
void EmitShuffleThunk(struct ShuffleEntry *pShuffleEntryArray);
414-
415-
#if defined(FEATURE_SHARE_GENERIC_CODE)
416414
void EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg);
417-
#endif // FEATURE_SHARE_GENERIC_CODE
418415

419416
void EmitMovConstant(IntReg Rd, UINT64 constant);
420417
void EmitJumpRegister(IntReg regTarget);

0 commit comments

Comments
 (0)