Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
17 changes: 10 additions & 7 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4623,29 +4623,32 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes)

if (this.MethodBeingCompiled.IsUnmanagedCallersOnly)
{
#if READYTORUN
const bool isFallbackBodyCompilation = false;
#else
bool isFallbackBodyCompilation = _isFallbackBodyCompilation;
#endif

// Validate UnmanagedCallersOnlyAttribute usage
if (!this.MethodBeingCompiled.Signature.IsStatic) // Must be a static method
if (!isFallbackBodyCompilation && !this.MethodBeingCompiled.Signature.IsStatic) // Must be a static method
{
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramNonStaticMethod, this.MethodBeingCompiled);
}

if (this.MethodBeingCompiled.HasInstantiation || this.MethodBeingCompiled.OwningType.HasInstantiation) // No generics involved
if (!isFallbackBodyCompilation && (this.MethodBeingCompiled.HasInstantiation || this.MethodBeingCompiled.OwningType.HasInstantiation)) // No generics involved
{
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramGenericMethod, this.MethodBeingCompiled);
}

if (this.MethodBeingCompiled.IsAsync)
if (!isFallbackBodyCompilation && this.MethodBeingCompiled.IsAsync)
{
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramAsync, this.MethodBeingCompiled);
}

#if READYTORUN
// TODO: enable this check in full AOT
if (Marshaller.IsMarshallingRequired(this.MethodBeingCompiled.Signature, ((MetadataType)this.MethodBeingCompiled.OwningType).Module, this.MethodBeingCompiled.GetUnmanagedCallersOnlyMethodCallingConventions())) // Only blittable arguments
if (!isFallbackBodyCompilation && MarshalHelpers.IsMarshallingRequired(this.MethodBeingCompiled.Signature, ((MetadataType)this.MethodBeingCompiled.OwningType).Module)) // Only blittable arguments
{
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramNonBlittableTypes, this.MethodBeingCompiled);
}
#endif

flags.Set(CorJitFlag.CORJIT_FLAG_REVERSE_PINVOKE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -971,5 +971,10 @@ public static bool IsRuntimeMarshallingEnabled(ModuleDesc module)
{
return module.Assembly is not EcmaAssembly assembly || !assembly.HasAssemblyCustomAttribute("System.Runtime.CompilerServices", "DisableRuntimeMarshallingAttribute");
}

public static bool IsMarshallingRequired(MethodSignature methodSig, ModuleDesc moduleContext)
{
return Marshaller.IsMarshallingRequired(methodSig, moduleContext);
}
Comment on lines +975 to +978
}
}
131 changes: 131 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,137 @@ private static bool IsMarshallingRequired(MarshallerKind kind)
return true;
}

private static Marshaller[] GetMarshallers(
MethodSignature methodSig,
PInvokeFlags flags,
ParameterMetadata[] parameterMetadataArray,
bool runtimeMarshallingEnabled)
{
Marshaller[] marshallers = new Marshaller[methodSig.Length + 1];

for (int i = 0, parameterIndex = 0; i < marshallers.Length; i++)
{
Debug.Assert(parameterIndex == parameterMetadataArray.Length || i <= parameterMetadataArray[parameterIndex].Index);

ParameterMetadata parameterMetadata;
if (parameterIndex == parameterMetadataArray.Length || i < parameterMetadataArray[parameterIndex].Index)
{
// if we don't have metadata for the parameter, create a dummy one
parameterMetadata = new ParameterMetadata(i, ParameterMetadataAttributes.None, null);
}
else
{
Debug.Assert(i == parameterMetadataArray[parameterIndex].Index);
parameterMetadata = parameterMetadataArray[parameterIndex++];
}

TypeDesc parameterType = (i == 0) ? methodSig.ReturnType : methodSig[i - 1]; //first item is the return type
if (runtimeMarshallingEnabled)
{
marshallers[i] = CreateMarshaller(parameterType,
parameterIndex,
methodSig.GetEmbeddedSignatureData(),
MarshallerType.Argument,
parameterMetadata.MarshalAsDescriptor,
MarshalDirection.Forward,
marshallers,
#if !READYTORUN
null,
#endif
parameterMetadata.Index,
flags,
parameterMetadata.In,
parameterMetadata.Out,
parameterMetadata.Return);
}
else
{
marshallers[i] = CreateDisabledMarshaller(
parameterType,
MarshallerType.Argument,
MarshalDirection.Forward,
marshallers,
parameterMetadata.Index,
flags,
parameterMetadata.Return);
}
}

return marshallers;
}

public static Marshaller[] GetMarshallersForMethod(MethodDesc targetMethod)
{
Debug.Assert(targetMethod.IsPInvoke);
return GetMarshallers(
targetMethod.Signature,
targetMethod.GetPInvokeMethodMetadata().Flags,
targetMethod.GetParameterMetadata(),
MarshalHelpers.IsRuntimeMarshallingEnabled(((MetadataType)targetMethod.OwningType).Module));
}

public static Marshaller[] GetMarshallersForSignature(MethodSignature methodSig, ParameterMetadata[] paramMetadata, ModuleDesc moduleContext)
{
return GetMarshallers(
methodSig,
new PInvokeFlags(PInvokeAttributes.None),
paramMetadata,
MarshalHelpers.IsRuntimeMarshallingEnabled(moduleContext));
}

public static bool IsMarshallingRequired(MethodDesc targetMethod)
{
Debug.Assert(targetMethod.IsPInvoke);

if (targetMethod.IsUnmanagedCallersOnly)
return true;

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++)
{
if (marshallers[i].IsMarshallingRequired())
return true;
}

return false;
}

public static bool IsMarshallingRequired(MethodSignature methodSig, ModuleDesc moduleContext)
{
Marshaller[] marshallers = GetMarshallersForSignature(methodSig, Array.Empty<ParameterMetadata>(), moduleContext);
for (int i = 0; i < marshallers.Length; i++)
{
if (marshallers[i].IsMarshallingRequired())
return true;
}

return false;
}

public static bool IsMarshallingRequired(MethodSignature methodSig, ParameterMetadata[] paramMetadata, ModuleDesc moduleContext)
{
Marshaller[] marshallers = GetMarshallersForSignature(methodSig, paramMetadata, moduleContext);
for (int i = 0; i < marshallers.Length; i++)
{
if (marshallers[i].IsMarshallingRequired())
return true;
}

return false;
}

public bool IsMarshallingRequired()
{
return Out || IsManagedByRef || IsMarshallingRequired(MarshallerKind);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Debug = System.Diagnostics.Debug;

namespace Internal.TypeSystem.Interop
{
partial class Marshaller
Expand All @@ -26,132 +24,5 @@ protected static Marshaller CreateMarshaller(MarshallerKind kind)
}
}

private static Marshaller[] GetMarshallers(
MethodSignature methodSig,
PInvokeFlags flags,
ParameterMetadata[] parameterMetadataArray,
bool runtimeMarshallingEnabled)
{
Marshaller[] marshallers = new Marshaller[methodSig.Length + 1];

for (int i = 0, parameterIndex = 0; i < marshallers.Length; i++)
{
Debug.Assert(parameterIndex == parameterMetadataArray.Length || i <= parameterMetadataArray[parameterIndex].Index);

ParameterMetadata parameterMetadata;
if (parameterIndex == parameterMetadataArray.Length || i < parameterMetadataArray[parameterIndex].Index)
{
// if we don't have metadata for the parameter, create a dummy one
parameterMetadata = new ParameterMetadata(i, ParameterMetadataAttributes.None, null);
}
else
{
Debug.Assert(i == parameterMetadataArray[parameterIndex].Index);
parameterMetadata = parameterMetadataArray[parameterIndex++];
}

TypeDesc parameterType = (i == 0) ? methodSig.ReturnType : methodSig[i - 1]; //first item is the return type
if (runtimeMarshallingEnabled)
{
marshallers[i] = CreateMarshaller(parameterType,
parameterIndex,
methodSig.GetEmbeddedSignatureData(),
MarshallerType.Argument,
parameterMetadata.MarshalAsDescriptor,
MarshalDirection.Forward,
marshallers,
parameterMetadata.Index,
flags,
parameterMetadata.In,
parameterMetadata.Out,
parameterMetadata.Return);
}
else
{
marshallers[i] = CreateDisabledMarshaller(
parameterType,
MarshallerType.Argument,
MarshalDirection.Forward,
marshallers,
parameterMetadata.Index,
flags,
parameterMetadata.Return);
}
}

return marshallers;
}

public static Marshaller[] GetMarshallersForMethod(MethodDesc targetMethod)
{
Debug.Assert(targetMethod.IsPInvoke);
return GetMarshallers(
targetMethod.Signature,
targetMethod.GetPInvokeMethodMetadata().Flags,
targetMethod.GetParameterMetadata(),
MarshalHelpers.IsRuntimeMarshallingEnabled(((MetadataType)targetMethod.OwningType).Module));
}

public static Marshaller[] GetMarshallersForSignature(MethodSignature methodSig, ParameterMetadata[] paramMetadata, ModuleDesc moduleContext)
{
return GetMarshallers(
methodSig,
new PInvokeFlags(PInvokeAttributes.None),
paramMetadata,
MarshalHelpers.IsRuntimeMarshallingEnabled(moduleContext));
}

public static bool IsMarshallingRequired(MethodDesc targetMethod)
{
Debug.Assert(targetMethod.IsPInvoke);

if (targetMethod.IsUnmanagedCallersOnly)
return true;

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++)
{
if (marshallers[i].IsMarshallingRequired())
return true;
}

return false;
}

public static bool IsMarshallingRequired(MethodSignature methodSig, ModuleDesc moduleContext, UnmanagedCallingConventions callingConvention)
{
Marshaller[] marshallers = GetMarshallersForSignature(methodSig, System.Array.Empty<ParameterMetadata>(), moduleContext);
for (int i = 0; i < marshallers.Length; i++)
{
if (marshallers[i].IsMarshallingRequired())
return true;
}

return false;
}

public static bool IsMarshallingRequired(MethodSignature methodSig, ParameterMetadata[] paramMetadata, ModuleDesc moduleContext)
{
Marshaller[] marshallers = GetMarshallersForSignature(methodSig, paramMetadata, moduleContext);
for (int i = 0; i < marshallers.Length; i++)
{
if (marshallers[i].IsMarshallingRequired())
return true;
}

return false;
}
}
}
1 change: 0 additions & 1 deletion src/tests/Interop/DisabledRuntimeMarshalling/AutoLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
namespace DisabledRuntimeMarshalling;

[ActiveIssue("https://github.com/dotnet/runtime/issues/91388", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.PlatformDoesNotSupportNativeTestAssets))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/81676", typeof(Utilities), nameof(Utilities.IsNativeAot))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/64127", TestRuntimes.Mono)]
public unsafe class PInvokes_AutoLayout
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
<RequiresProcessIsolation>true</RequiresProcessIsolation>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<MonoAotIncompatible>true</MonoAotIncompatible>
<!-- https://github.com/dotnet/runtime/issues/81676 -->
<NativeAotIncompatible>true</NativeAotIncompatible>
</PropertyGroup>
<ItemGroup>
<Compile Include="PInvokeAssemblyMarshallingEnabled/*.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ namespace DisabledRuntimeMarshalling;


[ActiveIssue("https://github.com/dotnet/runtime/issues/91388", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.PlatformDoesNotSupportNativeTestAssets))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/81676", typeof(Utilities), nameof(Utilities.IsNativeAot))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/64127", TestRuntimes.Mono)]
public unsafe class FunctionPointers
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
namespace DisabledRuntimeMarshalling.PInvokeAssemblyMarshallingDisabled;

[ActiveIssue("https://github.com/dotnet/runtime/issues/91388", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.PlatformDoesNotSupportNativeTestAssets))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/81676", typeof(Utilities), nameof(Utilities.IsNativeAot))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/64127", TestRuntimes.Mono)]
public unsafe class DelegatesFromExternalAssembly
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
namespace DisabledRuntimeMarshalling;

[ActiveIssue("https://github.com/dotnet/runtime/issues/91388", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.PlatformDoesNotSupportNativeTestAssets))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/81676", typeof(Utilities), nameof(Utilities.IsNativeAot))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/64127", TestRuntimes.Mono)]
public unsafe class Generics
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
namespace DisabledRuntimeMarshalling.PInvokeAssemblyMarshallingDisabled;

[ActiveIssue("https://github.com/dotnet/runtime/issues/91388", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.PlatformDoesNotSupportNativeTestAssets))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/81676", typeof(Utilities), nameof(Utilities.IsNativeAot))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/64127", TestRuntimes.Mono)]
public class PInvokes
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ namespace DisabledRuntimeMarshalling.PInvokeAssemblyMarshallingDisabled;
public unsafe class UnmanagedCallersOnly
{

[Fact]
// On NativeAOT, this failfasts because the exception escapes the native UnmanagedCallersOnly transition.
[ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNotNativeAot))]
Comment on lines +16 to +17

@MichalStrehovsky MichalStrehovsky Jul 3, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how/why this works (throws a catchable exception) with CoreCLR, but I guess we don't care about this specific behavior too much.

I validated this test now fails fast and that's what I would expect.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should work on Windows thanks to SEH, but I would not expect this to work on Unix. The exception should become unhandled on Unix at the boundary. Open a bug on this?

public static void UnmanagedCallersOnly_Defined_InDisabledAssembly_WithNonBlittableParameters_Fails()
{
short s = 41;
Expand Down
2 changes: 2 additions & 0 deletions src/tests/nativeaot/SmokeTests/SharedLibrary/SharedLibrary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Runtime.InteropServices;
using System.Threading;

[assembly:DisableRuntimeMarshalling]

namespace SharedLibrary
{
public class ClassLibrary
Expand Down
Loading