Skip to content

Commit 4a782d5

Browse files
Objective-C msgSend* support for pending exceptions in Release (#52849)
* Support checking for overridden methods during P/Invoke inline pass * Objective-C runtime msgSend* functions are never inlinable. * Update CrossGen2. * Add comment on details of how we are testing the SetMessageSendPendingException API. * Disable the crossgen2smoke on OSX ARM64
1 parent 478571c commit 4a782d5

17 files changed

Lines changed: 146 additions & 65 deletions

File tree

src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace System.Runtime.InteropServices.ObjectiveC
1010
public static partial class ObjectiveCMarshal
1111
{
1212
/// <summary>
13-
/// Sets a pending exception to be thrown the next time the runtime is entered from an overridden msgSend P/Invoke.
13+
/// Sets a pending exception to be thrown the next time the runtime is entered from an Objective-C msgSend P/Invoke.
1414
/// </summary>
1515
/// <param name="exception">The exception.</param>
1616
/// <remarks>

src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,5 +797,23 @@ private static MarshallerKind GetArrayElementMarshallerKind(
797797
return MarshallerKind.Invalid;
798798
}
799799
}
800+
801+
internal static bool ShouldCheckForPendingException(TargetDetails target, PInvokeMetadata metadata)
802+
{
803+
if (!target.IsOSX)
804+
return false;
805+
806+
const string ObjectiveCLibrary = "/usr/lib/libobjc.dylib";
807+
const string ObjectiveCMsgSend = "objc_msgSend";
808+
809+
// This is for the objc_msgSend suite of functions.
810+
// objc_msgSend
811+
// objc_msgSend_fpret
812+
// objc_msgSend_stret
813+
// objc_msgSendSuper
814+
// objc_msgSendSuper_stret
815+
return metadata.Module.Equals(ObjectiveCLibrary)
816+
&& metadata.Name.StartsWith(ObjectiveCMsgSend);
817+
}
800818
}
801819
}

src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams)
4343

4444
MetadataType stubHelpersType = InteropTypes.GetStubHelpers(context);
4545

46-
// if the SetLastError flag is set in DllImport, clear the error code before doing P/Invoke
46+
// if the SetLastError flag is set in DllImport, clear the error code before doing P/Invoke
4747
if (_importMetadata.Flags.SetLastError)
4848
{
4949
callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(
@@ -78,6 +78,9 @@ private MethodIL EmitIL()
7878
if (!_importMetadata.Flags.PreserveSig)
7979
throw new NotSupportedException();
8080

81+
if (MarshalHelpers.ShouldCheckForPendingException(_targetMethod.Context.Target, _importMetadata))
82+
throw new NotSupportedException();
83+
8184
if (_targetMethod.IsUnmanagedCallersOnly)
8285
throw new NotSupportedException();
8386

src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,18 @@ public static bool IsMarshallingRequired(MethodDesc targetMethod)
9393
if (targetMethod.IsUnmanagedCallersOnly)
9494
return true;
9595

96-
PInvokeFlags flags = targetMethod.GetPInvokeMethodMetadata().Flags;
96+
PInvokeMetadata metadata = targetMethod.GetPInvokeMethodMetadata();
97+
PInvokeFlags flags = metadata.Flags;
9798

9899
if (flags.SetLastError)
99100
return true;
100101

101102
if (!flags.PreserveSig)
102103
return true;
103104

105+
if (MarshalHelpers.ShouldCheckForPendingException(targetMethod.Context.Target, metadata))
106+
return true;
107+
104108
var marshallers = GetMarshallersForMethod(targetMethod);
105109
for (int i = 0; i < marshallers.Length; i++)
106110
{

src/coreclr/vm/dllimport.cpp

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2657,7 +2657,9 @@ void PInvokeStaticSigInfo::PreInit(MethodDesc* pMD)
26572657
}
26582658

26592659
PInvokeStaticSigInfo::PInvokeStaticSigInfo(
2660-
_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8 *pLibName, _Outptr_opt_ LPCUTF8 *pEntryPointName)
2660+
_In_ MethodDesc* pMD,
2661+
_Outptr_opt_ LPCUTF8* pLibName,
2662+
_Outptr_opt_ LPCUTF8* pEntryPointName)
26612663
{
26622664
CONTRACTL
26632665
{
@@ -2755,7 +2757,10 @@ PInvokeStaticSigInfo::PInvokeStaticSigInfo(
27552757
InitCallConv(CallConvWinApiSentinel, FALSE);
27562758
}
27572759

2758-
void PInvokeStaticSigInfo::DllImportInit(_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8 *ppLibName, _Outptr_opt_ LPCUTF8 *ppEntryPointName)
2760+
void PInvokeStaticSigInfo::DllImportInit(
2761+
_In_ MethodDesc* pMD,
2762+
_Outptr_opt_ LPCUTF8* ppLibName,
2763+
_Outptr_opt_ LPCUTF8* ppEntryPointName)
27592764
{
27602765
CONTRACTL
27612766
{
@@ -2784,7 +2789,6 @@ void PInvokeStaticSigInfo::DllImportInit(_In_ MethodDesc* pMD, _Outptr_opt_ LPCU
27842789
return;
27852790
}
27862791

2787-
// out parameter pEntryPointName
27882792
if (ppEntryPointName && *ppEntryPointName == NULL)
27892793
*ppEntryPointName = pMD->GetName();
27902794

@@ -3128,16 +3132,12 @@ BOOL NDirect::MarshalingRequired(
31283132
return TRUE;
31293133
}
31303134

3131-
// SetLastError is handled by stub
3132-
PInvokeStaticSigInfo sigInfo(pMD);
3133-
if (sigInfo.GetLinkFlags() & nlfLastError)
3134-
return TRUE;
3135-
3136-
// LCID argument is handled by stub
3137-
if (GetLCIDParameterIndex(pMD) != -1)
3138-
return TRUE;
3139-
3140-
if (pMD->IsNDirect())
3135+
PInvokeStaticSigInfo sigInfo;
3136+
if (!pMD->IsNDirect())
3137+
{
3138+
new (&sigInfo) PInvokeStaticSigInfo(pMD);
3139+
}
3140+
else
31413141
{
31423142
// A P/Invoke marked with UnmanagedCallersOnlyAttribute
31433143
// doesn't technically require marshalling. However, we
@@ -3154,13 +3154,23 @@ BOOL NDirect::MarshalingRequired(
31543154
if (pNMD->IsClassConstructorTriggeredByILStub())
31553155
return TRUE;
31563156

3157+
InitializeSigInfoAndPopulateNDirectMethodDesc(pNMD, &sigInfo);
3158+
31573159
#ifndef CROSSGEN_COMPILE
31583160
// Pending exceptions are handled by stub
31593161
if (Interop::ShouldCheckForPendingException(pNMD))
31603162
return TRUE;
31613163
#endif // !CROSSGEN_COMPILE
31623164
}
31633165

3166+
// SetLastError is handled by stub
3167+
if (sigInfo.GetLinkFlags() & nlfLastError)
3168+
return TRUE;
3169+
3170+
// LCID argument is handled by stub
3171+
if (GetLCIDParameterIndex(pMD) != -1)
3172+
return TRUE;
3173+
31643174
callConv = sigInfo.GetCallConv();
31653175
}
31663176

@@ -4225,7 +4235,11 @@ static void CreateNDirectStubAccessMetadata(
42254235

42264236
namespace
42274237
{
4228-
void PopulateNDirectMethodDescImpl(_Inout_ NDirectMethodDesc* pNMD, _In_ const PInvokeStaticSigInfo& sigInfo, _In_opt_z_ LPCUTF8 libName, _In_opt_z_ LPCUTF8 entryPointName)
4238+
void PopulateNDirectMethodDescImpl(
4239+
_Inout_ NDirectMethodDesc* pNMD,
4240+
_In_ const PInvokeStaticSigInfo& sigInfo,
4241+
_In_opt_z_ LPCUTF8 libName,
4242+
_In_opt_z_ LPCUTF8 entryPointName)
42294243
{
42304244
CONTRACTL
42314245
{
@@ -4263,9 +4277,8 @@ namespace
42634277
pNMD->ndirect.m_pszEntrypointName.SetValueMaybeNull(entryPointName);
42644278
}
42654279

4266-
// Call this exactly ONCE per thread. Do not publish incomplete prestub flags
4267-
// or you will introduce a race condition.
4268-
pNMD->InterlockedSetNDirectFlags(ndirectflags);
4280+
// Do not publish incomplete prestub flags or you will introduce a race condition.
4281+
pNMD->InterlockedSetNDirectFlags(ndirectflags | NDirectMethodDesc::kNDirectPopulated);
42694282
}
42704283
}
42714284

@@ -4281,6 +4294,9 @@ void NDirect::PopulateNDirectMethodDesc(_Inout_ NDirectMethodDesc* pNMD)
42814294
if (pNMD->IsSynchronized())
42824295
COMPlusThrow(kTypeLoadException, IDS_EE_NOSYNCHRONIZED);
42834296

4297+
if (pNMD->IsPopulated())
4298+
return;
4299+
42844300
LPCUTF8 szLibName = NULL, szEntryPointName = NULL;
42854301
PInvokeStaticSigInfo sigInfo(pNMD, &szLibName, &szEntryPointName);
42864302
PopulateNDirectMethodDescImpl(pNMD, sigInfo, szLibName, szEntryPointName);
@@ -4302,6 +4318,9 @@ void NDirect::InitializeSigInfoAndPopulateNDirectMethodDesc(_Inout_ NDirectMetho
43024318
LPCUTF8 szLibName = NULL, szEntryPointName = NULL;
43034319
new (pSigInfo) PInvokeStaticSigInfo(pNMD, &szLibName, &szEntryPointName);
43044320

4321+
if (pNMD->IsPopulated())
4322+
return;
4323+
43054324
PopulateNDirectMethodDescImpl(pNMD, *pSigInfo, szLibName, szEntryPointName);
43064325
}
43074326

src/coreclr/vm/dllimport.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ class NDirect
7575
static HRESULT HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken token, DWORD dwMemberAttrs);
7676

7777
// Either MD or signature & module must be given.
78+
// Note: This method can be called at a time when the associated NDirectMethodDesc
79+
// has not been fully populated. This means the optimized path for this call is to rely
80+
// on the most basic P/Invoke metadata. An example when this can happen is when the JIT
81+
// is compiling a method containing a P/Invoke that is being considered for inlining.
7882
static BOOL MarshalingRequired(
7983
_In_opt_ MethodDesc* pMD,
8084
_In_opt_ PCCOR_SIGNATURE pSig = NULL,
@@ -312,12 +316,12 @@ struct PInvokeStaticSigInfo
312316

313317
PInvokeStaticSigInfo(_In_ MethodDesc* pMdDelegate);
314318

315-
PInvokeStaticSigInfo(_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8 *pLibName, _Outptr_opt_ LPCUTF8 *pEntryPointName);
319+
PInvokeStaticSigInfo(_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8* pLibName, _Outptr_opt_ LPCUTF8* pEntryPointName);
316320

317321
private:
318322
void ThrowError(WORD errorResourceID);
319323
void InitCallConv(CorInfoCallConvExtension callConv, BOOL bIsVarArg);
320-
void DllImportInit(_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8 *pLibName, _Outptr_opt_ LPCUTF8 *pEntryPointName);
324+
void DllImportInit(_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8* pLibName, _Outptr_opt_ LPCUTF8* pEntryPointName);
321325
void PreInit(Module* pModule, MethodTable *pClass);
322326
void PreInit(MethodDesc* pMD);
323327

src/coreclr/vm/interoplibinterface.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class ObjCMarshalNative
147147
static bool IsTrackedReference(_In_ OBJECTREF object, _Out_ bool* isReferenced);
148148

149149
public: // Identification
150-
static bool IsRuntimeMsgSendFunctionOverridden(
150+
static bool IsRuntimeMessageSendFunction(
151151
_In_z_ const char* libraryName,
152152
_In_z_ const char* entrypointName);
153153

src/coreclr/vm/interoplibinterface_objc.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,16 @@ namespace
127127
OBJC_MSGSEND "Super_stret",
128128
};
129129

130-
const void* STDMETHODCALLTYPE MessageSendPInvokeOverride(_In_z_ const char* libraryName, _In_z_ const char* entrypointName)
130+
bool IsObjectiveCMessageSendFunction(_In_z_ const char* libraryName, _In_z_ const char* entrypointName)
131131
{
132-
// All overrides are in libobjc
133-
if (strcmp(libraryName, ObjectiveCLibrary) != 0)
134-
return nullptr;
132+
// Is the function in libobjc and named appropriately.
133+
return ((strcmp(libraryName, ObjectiveCLibrary) == 0)
134+
&& (strncmp(entrypointName, OBJC_MSGSEND, _countof(OBJC_MSGSEND) -1) == 0));
135+
}
135136

136-
// All overrides start with objc_msgSend
137-
if (strncmp(entrypointName, OBJC_MSGSEND, _countof(OBJC_MSGSEND) -1) != 0)
137+
const void* STDMETHODCALLTYPE MessageSendPInvokeOverride(_In_z_ const char* libraryName, _In_z_ const char* entrypointName)
138+
{
139+
if (!IsObjectiveCMessageSendFunction(libraryName, entrypointName))
138140
return nullptr;
139141

140142
for (int i = 0; i < _countof(MsgSendEntryPoints); ++i)
@@ -227,7 +229,7 @@ bool ObjCMarshalNative::IsTrackedReference(_In_ OBJECTREF object, _Out_ bool* is
227229
return true;
228230
}
229231

230-
bool ObjCMarshalNative::IsRuntimeMsgSendFunctionOverridden(
232+
bool ObjCMarshalNative::IsRuntimeMessageSendFunction(
231233
_In_z_ const char* libraryName,
232234
_In_z_ const char* entrypointName)
233235
{
@@ -240,7 +242,7 @@ bool ObjCMarshalNative::IsRuntimeMsgSendFunctionOverridden(
240242
}
241243
CONTRACTL_END;
242244

243-
return MessageSendPInvokeOverride(libraryName, entrypointName) != NULL;
245+
return IsObjectiveCMessageSendFunction(libraryName, entrypointName);
244246
}
245247

246248
namespace

src/coreclr/vm/interoplibinterface_shared.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
// Runtime headers
55
#include "common.h"
6+
#include "dllimport.h"
67

78
#include "interoplibinterface.h"
89

@@ -24,7 +25,7 @@ bool Interop::ShouldCheckForPendingException(_In_ NDirectMethodDesc* md)
2425
if (libraryName == NULL || entrypointName == NULL)
2526
return false;
2627

27-
if (ObjCMarshalNative::IsRuntimeMsgSendFunctionOverridden(libraryName, entrypointName))
28+
if (ObjCMarshalNative::IsRuntimeMessageSendFunction(libraryName, entrypointName))
2829
return true;
2930
#endif // FEATURE_OBJCMARSHAL
3031

src/coreclr/vm/method.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3027,6 +3027,8 @@ class NDirectMethodDesc : public MethodDesc
30273027
kIsQCall = 0x1000,
30283028

30293029
kDefaultDllImportSearchPathsStatus = 0x2000, // either method has custom attribute or not.
3030+
3031+
kNDirectPopulated = 0x8000, // Indicate if the NDirect has been fully populated.
30303032
};
30313033

30323034
// Resolve the import to the NDirect target and set it on the NDirectMethodDesc.
@@ -3159,6 +3161,12 @@ class NDirectMethodDesc : public MethodDesc
31593161
return (ndirect.m_wFlags & kDefaultDllImportSearchPathsIsCached) != 0;
31603162
}
31613163

3164+
BOOL IsPopulated()
3165+
{
3166+
LIMITED_METHOD_CONTRACT;
3167+
return (ndirect.m_wFlags & kNDirectPopulated) != 0;
3168+
}
3169+
31623170
ULONG DefaultDllImportSearchPathsAttributeCachedValue()
31633171
{
31643172
LIMITED_METHOD_CONTRACT;

0 commit comments

Comments
 (0)