Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Objective-C msgSend* support for pending exceptions in Release #52849

Merged
merged 19 commits into from
May 20, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,11 @@ internal static void SetPendingExceptionObject(Exception? exception)
s_pendingExceptionObject = exception;
}

internal static void ClearPendingExceptionObject()
{
s_pendingExceptionObject = null;
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern IntPtr CreateCustomMarshalerHelper(IntPtr pMD, int paramToken, IntPtr hndManagedType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -797,5 +797,23 @@ private static MarshallerKind GetArrayElementMarshallerKind(
return MarshallerKind.Invalid;
}
}

internal static bool ShouldCheckForPendingException(TargetDetails target, PInvokeMetadata metadata)
{
if (!target.IsOSX)
return false;

const string ObjectiveCLibrary = "/usr/lib/libobjc.dylib";
jkotas marked this conversation as resolved.
Show resolved Hide resolved
const string ObjectiveCMsgSend = "objc_msgSend";

// This is for the objc_msgSend suite of functions.
// objc_msgSend
// objc_msgSend_fpret
// objc_msgSend_stret
// objc_msgSendSuper
// objc_msgSendSuper_stret
return metadata.Module.Equals(ObjectiveCLibrary)
&& metadata.Name.StartsWith(ObjectiveCMsgSend);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams)

MetadataType stubHelpersType = InteropTypes.GetStubHelpers(context);

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

if (MarshalHelpers.ShouldCheckForPendingException(_targetMethod.Context.Target, _importMetadata))
throw new NotSupportedException();

if (_targetMethod.IsUnmanagedCallersOnly)
throw new NotSupportedException();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,18 @@ public static bool IsMarshallingRequired(MethodDesc targetMethod)
if (targetMethod.IsUnmanagedCallersOnly)
return true;

PInvokeFlags flags = targetMethod.GetPInvokeMethodMetadata().Flags;
PInvokeMetadata metadata = targetMethod.GetPInvokeMethodMetadata();
PInvokeFlags flags = metadata.Flags;

if (flags.SetLastError)
return true;

if (!flags.PreserveSig)
return true;

if (MarshalHelpers.ShouldCheckForPendingException(targetMethod.Context.Target, metadata))
return true;

var marshallers = GetMarshallersForMethod(targetMethod);
for (int i = 0; i < marshallers.Length; i++)
{
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,8 @@ DEFINE_METHOD(STUBHELPERS, ADD_TO_CLEANUP_LIST_SAFEHANDLE, AddToClea
DEFINE_METHOD(STUBHELPERS, KEEP_ALIVE_VIA_CLEANUP_LIST, KeepAliveViaCleanupList, SM_RefCleanupWorkListElement_Obj_RetVoid)
DEFINE_METHOD(STUBHELPERS, DESTROY_CLEANUP_LIST, DestroyCleanupList, SM_RefCleanupWorkListElement_RetVoid)
DEFINE_METHOD(STUBHELPERS, GET_HR_EXCEPTION_OBJECT, GetHRExceptionObject, SM_Int_RetException)
DEFINE_METHOD(STUBHELPERS, GET_PENDING_EXCEPTION_OBJECT, GetPendingExceptionObject, SM_RetException)
DEFINE_METHOD(STUBHELPERS, GET_PENDING_EXCEPTION_OBJECT, GetPendingExceptionObject, SM_RetException)
DEFINE_METHOD(STUBHELPERS, CLEAR_PENDING_EXCEPTION_OBJECT, ClearPendingExceptionObject, SM_RetVoid)
DEFINE_METHOD(STUBHELPERS, CREATE_CUSTOM_MARSHALER_HELPER, CreateCustomMarshalerHelper, SM_IntPtr_Int_IntPtr_RetIntPtr)

DEFINE_METHOD(STUBHELPERS, CHECK_STRING_LENGTH, CheckStringLength, SM_Int_RetVoid)
Expand Down
64 changes: 47 additions & 17 deletions src/coreclr/vm/dllimport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,12 @@ class ILStubState : public StubState
}
#endif // PROFILING_SUPPORTED

if (SF_IsCheckPendingException(m_dwStubFlags)
&& SF_IsForwardStub(m_dwStubFlags))
{
pcsDispatch->EmitCALL(METHOD__STUBHELPERS__CLEAR_PENDING_EXCEPTION_OBJECT, 0, 0);
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
}

// For CoreClr, clear the last error before calling the target that returns last error.
// There isn't always a way to know the function have failed without checking last error,
// in particular on Unix.
Expand Down Expand Up @@ -2657,7 +2663,9 @@ void PInvokeStaticSigInfo::PreInit(MethodDesc* pMD)
}

PInvokeStaticSigInfo::PInvokeStaticSigInfo(
_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8 *pLibName, _Outptr_opt_ LPCUTF8 *pEntryPointName)
_In_ MethodDesc* pMD,
_Outptr_opt_ LPCUTF8* pLibName,
_Outptr_opt_ LPCUTF8* pEntryPointName)
{
CONTRACTL
{
Expand Down Expand Up @@ -2755,7 +2763,10 @@ PInvokeStaticSigInfo::PInvokeStaticSigInfo(
InitCallConv(CallConvWinApiSentinel, FALSE);
}

void PInvokeStaticSigInfo::DllImportInit(_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8 *ppLibName, _Outptr_opt_ LPCUTF8 *ppEntryPointName)
void PInvokeStaticSigInfo::DllImportInit(
_In_ MethodDesc* pMD,
_Outptr_opt_ LPCUTF8* ppLibName,
_Outptr_opt_ LPCUTF8* ppEntryPointName)
{
CONTRACTL
{
Expand Down Expand Up @@ -2784,7 +2795,6 @@ void PInvokeStaticSigInfo::DllImportInit(_In_ MethodDesc* pMD, _Outptr_opt_ LPCU
return;
}

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

Expand Down Expand Up @@ -3098,6 +3108,11 @@ HRESULT NDirect::HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken t


// Either MD or signature & module must be given.
//
// N.B. This method can be called at a time when the associated NDirectMethodDesc
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
// has not been fully populated. This means the optimized path for this call is to rely
// on the most basic P/Invoke metadata. An example when this can happen is when the JIT
// is compiling a method containing a P/Invoke that is being considered for inlining.
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
/*static*/
BOOL NDirect::MarshalingRequired(
_In_opt_ MethodDesc* pMD,
Expand Down Expand Up @@ -3128,16 +3143,12 @@ BOOL NDirect::MarshalingRequired(
return TRUE;
}

// SetLastError is handled by stub
PInvokeStaticSigInfo sigInfo(pMD);
if (sigInfo.GetLinkFlags() & nlfLastError)
return TRUE;

// LCID argument is handled by stub
if (GetLCIDParameterIndex(pMD) != -1)
return TRUE;

if (pMD->IsNDirect())
PInvokeStaticSigInfo sigInfo;
if (!pMD->IsNDirect())
{
new (&sigInfo) PInvokeStaticSigInfo(pMD);
}
else
{
// A P/Invoke marked with UnmanagedCallersOnlyAttribute
// doesn't technically require marshalling. However, we
Expand All @@ -3154,13 +3165,23 @@ BOOL NDirect::MarshalingRequired(
if (pNMD->IsClassConstructorTriggeredByILStub())
return TRUE;

InitializeSigInfoAndPopulateNDirectMethodDesc(pNMD, &sigInfo);

#ifndef CROSSGEN_COMPILE
// Pending exceptions are handled by stub
if (Interop::ShouldCheckForPendingException(pNMD))
return TRUE;
#endif // !CROSSGEN_COMPILE
}

// SetLastError is handled by stub
if (sigInfo.GetLinkFlags() & nlfLastError)
return TRUE;

// LCID argument is handled by stub
if (GetLCIDParameterIndex(pMD) != -1)
return TRUE;

callConv = sigInfo.GetCallConv();
}

Expand Down Expand Up @@ -4225,7 +4246,11 @@ static void CreateNDirectStubAccessMetadata(

namespace
{
void PopulateNDirectMethodDescImpl(_Inout_ NDirectMethodDesc* pNMD, _In_ const PInvokeStaticSigInfo& sigInfo, _In_opt_z_ LPCUTF8 libName, _In_opt_z_ LPCUTF8 entryPointName)
void PopulateNDirectMethodDescImpl(
_Inout_ NDirectMethodDesc* pNMD,
_In_ const PInvokeStaticSigInfo& sigInfo,
_In_opt_z_ LPCUTF8 libName,
_In_opt_z_ LPCUTF8 entryPointName)
{
CONTRACTL
{
Expand Down Expand Up @@ -4263,9 +4288,8 @@ namespace
pNMD->ndirect.m_pszEntrypointName.SetValueMaybeNull(entryPointName);
}

// Call this exactly ONCE per thread. Do not publish incomplete prestub flags
// or you will introduce a race condition.
pNMD->InterlockedSetNDirectFlags(ndirectflags);
// Do not publish incomplete prestub flags or you will introduce a race condition.
pNMD->InterlockedSetNDirectFlags(ndirectflags | NDirectMethodDesc::kNDirectPopulated);
}
}

Expand All @@ -4281,6 +4305,9 @@ void NDirect::PopulateNDirectMethodDesc(_Inout_ NDirectMethodDesc* pNMD)
if (pNMD->IsSynchronized())
COMPlusThrow(kTypeLoadException, IDS_EE_NOSYNCHRONIZED);

if (pNMD->IsPopulated())
return;

LPCUTF8 szLibName = NULL, szEntryPointName = NULL;
PInvokeStaticSigInfo sigInfo(pNMD, &szLibName, &szEntryPointName);
PopulateNDirectMethodDescImpl(pNMD, sigInfo, szLibName, szEntryPointName);
Expand All @@ -4302,6 +4329,9 @@ void NDirect::InitializeSigInfoAndPopulateNDirectMethodDesc(_Inout_ NDirectMetho
LPCUTF8 szLibName = NULL, szEntryPointName = NULL;
new (pSigInfo) PInvokeStaticSigInfo(pNMD, &szLibName, &szEntryPointName);

if (pNMD->IsPopulated())
return;

PopulateNDirectMethodDescImpl(pNMD, *pSigInfo, szLibName, szEntryPointName);
}

Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/dllimport.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,12 +312,12 @@ struct PInvokeStaticSigInfo

PInvokeStaticSigInfo(_In_ MethodDesc* pMdDelegate);

PInvokeStaticSigInfo(_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8 *pLibName, _Outptr_opt_ LPCUTF8 *pEntryPointName);
PInvokeStaticSigInfo(_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8* pLibName, _Outptr_opt_ LPCUTF8* pEntryPointName);

private:
void ThrowError(WORD errorResourceID);
void InitCallConv(CorInfoCallConvExtension callConv, BOOL bIsVarArg);
void DllImportInit(_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8 *pLibName, _Outptr_opt_ LPCUTF8 *pEntryPointName);
void DllImportInit(_In_ MethodDesc* pMD, _Outptr_opt_ LPCUTF8* pLibName, _Outptr_opt_ LPCUTF8* pEntryPointName);
void PreInit(Module* pModule, MethodTable *pClass);
void PreInit(MethodDesc* pMD);

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/interoplibinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class ObjCMarshalNative
static bool IsTrackedReference(_In_ OBJECTREF object, _Out_ bool* isReferenced);

public: // Identification
static bool IsRuntimeMsgSendFunctionOverridden(
static bool IsRuntimeMessageSendFunction(
_In_z_ const char* libraryName,
_In_z_ const char* entrypointName);

Expand Down
18 changes: 10 additions & 8 deletions src/coreclr/vm/interoplibinterface_objc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,16 @@ namespace
OBJC_MSGSEND "Super_stret",
};

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

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

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

bool ObjCMarshalNative::IsRuntimeMsgSendFunctionOverridden(
bool ObjCMarshalNative::IsRuntimeMessageSendFunction(
_In_z_ const char* libraryName,
_In_z_ const char* entrypointName)
{
Expand All @@ -240,7 +242,7 @@ bool ObjCMarshalNative::IsRuntimeMsgSendFunctionOverridden(
}
CONTRACTL_END;

return MessageSendPInvokeOverride(libraryName, entrypointName) != NULL;
return IsObjectiveCMessageSendFunction(libraryName, entrypointName);
}

namespace
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/vm/interoplibinterface_shared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

// Runtime headers
#include "common.h"
#include "dllimport.h"

#include "interoplibinterface.h"

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

if (ObjCMarshalNative::IsRuntimeMsgSendFunctionOverridden(libraryName, entrypointName))
if (ObjCMarshalNative::IsRuntimeMessageSendFunction(libraryName, entrypointName))
return true;
#endif // FEATURE_OBJCMARSHAL

Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/vm/method.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3027,6 +3027,8 @@ class NDirectMethodDesc : public MethodDesc
kIsQCall = 0x1000,

kDefaultDllImportSearchPathsStatus = 0x2000, // either method has custom attribute or not.

kNDirectPopulated = 0x8000, // Indicate if the NDirect has been fully populated.
};

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

BOOL IsPopulated()
{
LIMITED_METHOD_CONTRACT;
return (ndirect.m_wFlags & kNDirectPopulated) != 0;
}

ULONG DefaultDllImportSearchPathsAttributeCachedValue()
{
LIMITED_METHOD_CONTRACT;
Expand Down
Loading