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 4 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";
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
50 changes: 30 additions & 20 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);
}

// 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 @@ -2613,8 +2619,10 @@ void PInvokeStaticSigInfo::PreInit(Module* pModule, MethodTable * pMT)

// initialize data members
m_wFlags = 0;
m_entryPointName = NULL;
m_pModule = pModule;
m_callConv = CallConvWinApiSentinel;
m_externModref = mdModuleRefNil;
SetBestFitMapping (TRUE);
SetThrowOnUnmappableChar (FALSE);
SetLinkFlags (nlfNone);
Expand Down Expand Up @@ -2657,7 +2665,7 @@ 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)
{
CONTRACTL
{
Expand All @@ -2666,7 +2674,7 @@ PInvokeStaticSigInfo::PInvokeStaticSigInfo(
}
CONTRACTL_END;

DllImportInit(pMD, pLibName, pEntryPointName);
DllImportInit(pMD, pLibName);
}

PInvokeStaticSigInfo::PInvokeStaticSigInfo(_In_ MethodDesc* pMD)
Expand All @@ -2684,7 +2692,7 @@ PInvokeStaticSigInfo::PInvokeStaticSigInfo(_In_ MethodDesc* pMD)

if (!pMT->IsDelegate())
{
DllImportInit(pMD, NULL, NULL);
DllImportInit(pMD, NULL);
return;
}

Expand Down Expand Up @@ -2755,7 +2763,7 @@ 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)
{
CONTRACTL
{
Expand All @@ -2767,7 +2775,6 @@ void PInvokeStaticSigInfo::DllImportInit(_In_ MethodDesc* pMD, _Outptr_opt_ LPCU
// where pMD->ndirect.m_szLibName was passed in directly, cleared
// by this API, then accessed on another thread before being reset here.
PRECONDITION(CheckPointer(ppLibName, NULL_OK) && (!ppLibName || *ppLibName == NULL));
PRECONDITION(CheckPointer(ppEntryPointName, NULL_OK) && (!ppEntryPointName || *ppEntryPointName == NULL));
}
CONTRACTL_END;

Expand All @@ -2777,21 +2784,19 @@ void PInvokeStaticSigInfo::DllImportInit(_In_ MethodDesc* pMD, _Outptr_opt_ LPCU
// System.Runtime.InteropServices.DllImportAttribute
IMDInternalImport *pInternalImport = pMD->GetMDImport();
CorPinvokeMap mappingFlags = pmMaxValue;
mdModuleRef modref = mdModuleRefNil;
if (FAILED(pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, ppEntryPointName, &modref)))
if (FAILED(pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, &m_entryPointName, &m_externModref)))
{
InitCallConv(CallConvWinApiSentinel, pMD->IsVarArg());
return;
}

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

// out parameter pLibName
if (ppLibName != NULL)
{
if (FAILED(pInternalImport->GetModuleRefProps(modref, ppLibName)))
if (FAILED(pInternalImport->GetModuleRefProps(m_externModref, ppLibName)))
{
ThrowError(IDS_CLASSLOAD_BADFORMAT);
}
Expand Down Expand Up @@ -3098,6 +3103,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
// 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.
/*static*/
BOOL NDirect::MarshalingRequired(
_In_opt_ MethodDesc* pMD,
Expand Down Expand Up @@ -3156,7 +3166,7 @@ BOOL NDirect::MarshalingRequired(

#ifndef CROSSGEN_COMPILE
// Pending exceptions are handled by stub
if (Interop::ShouldCheckForPendingException(pNMD))
if (Interop::ShouldCheckForPendingException(pNMD, &sigInfo))
return TRUE;
#endif // !CROSSGEN_COMPILE
}
Expand Down Expand Up @@ -4225,7 +4235,7 @@ 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)
{
CONTRACTL
{
Expand Down Expand Up @@ -4260,7 +4270,7 @@ namespace
else
{
pNMD->ndirect.m_pszLibName.SetValueMaybeNull(libName);
pNMD->ndirect.m_pszEntrypointName.SetValueMaybeNull(entryPointName);
pNMD->ndirect.m_pszEntrypointName.SetValueMaybeNull(sigInfo.GetEntryPointName());
}

// Call this exactly ONCE per thread. Do not publish incomplete prestub flags
Expand All @@ -4281,9 +4291,9 @@ void NDirect::PopulateNDirectMethodDesc(_Inout_ NDirectMethodDesc* pNMD)
if (pNMD->IsSynchronized())
COMPlusThrow(kTypeLoadException, IDS_EE_NOSYNCHRONIZED);

LPCUTF8 szLibName = NULL, szEntryPointName = NULL;
PInvokeStaticSigInfo sigInfo(pNMD, &szLibName, &szEntryPointName);
PopulateNDirectMethodDescImpl(pNMD, sigInfo, szLibName, szEntryPointName);
LPCUTF8 szLibName = NULL;
PInvokeStaticSigInfo sigInfo(pNMD, &szLibName);
PopulateNDirectMethodDescImpl(pNMD, sigInfo, szLibName);
}

void NDirect::InitializeSigInfoAndPopulateNDirectMethodDesc(_Inout_ NDirectMethodDesc* pNMD, _Inout_ PInvokeStaticSigInfo* pSigInfo)
Expand All @@ -4299,10 +4309,10 @@ void NDirect::InitializeSigInfoAndPopulateNDirectMethodDesc(_Inout_ NDirectMetho
if (pNMD->IsSynchronized())
COMPlusThrow(kTypeLoadException, IDS_EE_NOSYNCHRONIZED);

LPCUTF8 szLibName = NULL, szEntryPointName = NULL;
new (pSigInfo) PInvokeStaticSigInfo(pNMD, &szLibName, &szEntryPointName);
LPCUTF8 szLibName = NULL;
new (pSigInfo) PInvokeStaticSigInfo(pNMD, &szLibName);

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

#ifdef FEATURE_COMINTEROP
Expand Down
8 changes: 6 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);

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);
void PreInit(Module* pModule, MethodTable *pClass);
void PreInit(MethodDesc* pMD);

Expand Down Expand Up @@ -350,6 +350,8 @@ struct PInvokeStaticSigInfo
BOOL IsDelegateInterop() const { LIMITED_METHOD_CONTRACT; return m_wFlags & PINVOKE_STATIC_SIGINFO_IS_DELEGATE_INTEROP; }
CorInfoCallConvExtension GetCallConv() const { LIMITED_METHOD_CONTRACT; return m_callConv; }
Signature GetSignature() const { LIMITED_METHOD_CONTRACT; return m_sig; }
LPCUTF8 GetEntryPointName() const { LIMITED_METHOD_CONTRACT; return m_entryPointName; }
mdModuleRef GetExternModuleRefToken() const { LIMITED_METHOD_CONTRACT; return m_externModref; }
CorNativeLinkType GetCharSet() const { LIMITED_METHOD_CONTRACT; return (CorNativeLinkType)((m_wFlags & COR_NATIVE_LINK_TYPE_MASK) >> COR_NATIVE_LINK_TYPE_SHIFT); }
CorNativeLinkFlags GetLinkFlags() const { LIMITED_METHOD_CONTRACT; return (CorNativeLinkFlags)((m_wFlags & COR_NATIVE_LINK_FLAGS_MASK) >> COR_NATIVE_LINK_FLAGS_SHIFT); }

Expand Down Expand Up @@ -404,7 +406,9 @@ struct PInvokeStaticSigInfo
private:
Module* m_pModule;
Signature m_sig;
LPCUTF8 m_entryPointName;
CorInfoCallConvExtension m_callConv;
mdModuleRef m_externModref;
WORD m_wFlags;
};

Expand Down
9 changes: 7 additions & 2 deletions 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 All @@ -165,11 +165,16 @@ class ObjCMarshalNative

#endif // FEATURE_OBJCMARSHAL

// Forward declaration of type.
struct PInvokeStaticSigInfo;

class Interop
{
public:
// Check if pending exceptions are possible for the following native export.
static bool ShouldCheckForPendingException(_In_ NDirectMethodDesc* md);
static bool ShouldCheckForPendingException(
_In_ NDirectMethodDesc* md,
_In_opt_ PInvokeStaticSigInfo* sigInfo = NULL);

// A no return callback that is designed to help propagate a managed
// exception going from Managed to Native.
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
32 changes: 29 additions & 3 deletions src/coreclr/vm/interoplibinterface_shared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@

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

#include "interoplibinterface.h"

using ManagedToNativeExceptionCallback = Interop::ManagedToNativeExceptionCallback;

bool Interop::ShouldCheckForPendingException(_In_ NDirectMethodDesc* md)
bool Interop::ShouldCheckForPendingException(
_In_ NDirectMethodDesc* md,
_In_opt_ PInvokeStaticSigInfo* sigInfo)
{
CONTRACTL
{
Expand All @@ -22,9 +25,32 @@ bool Interop::ShouldCheckForPendingException(_In_ NDirectMethodDesc* md)
PTR_CUTF8 libraryName = md->GetLibNameRaw();
PTR_CUTF8 entrypointName = md->GetEntrypointName();
if (libraryName == NULL || entrypointName == NULL)
return false;
{
// If no sig info was supplied we have no recourse.
if (sigInfo == NULL)
return false;

if (libraryName == NULL)
{
// If the library name is unknown, see if we can get the token
// from the sig info.
mdModuleRef modRef = sigInfo->GetExternModuleRefToken();
if (modRef != mdModuleRefNil)
{
IMDInternalImport* mdImport = md->GetMDImport();
(void)mdImport->GetModuleRefProps(modRef, &libraryName);
}
}

if (entrypointName == NULL)
entrypointName = sigInfo->GetEntryPointName();

// Still no luck so return false.
if (libraryName == NULL || entrypointName == NULL)
return false;
}

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

Expand Down
Loading