Skip to content
Draft
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
5 changes: 4 additions & 1 deletion src/coreclr/vm/callconvbuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ bool CallConv::TryGetCallingConventionFromUnmanagedCallersOnly(_In_ MethodDesc*

// UnmanagedCallersOnly each
// have optional named arguments.
CaNamedArg namedArgs[2];
CaNamedArg namedArgs[3];

// For the UnmanagedCallersOnly scenario.
CaType caCallConvs;
Expand All @@ -563,6 +563,9 @@ bool CallConv::TryGetCallingConventionFromUnmanagedCallersOnly(_In_ MethodDesc*
CaTypeCtor caEntryPoint(SERIALIZATION_TYPE_STRING);
namedArgs[1].Init("EntryPoint", SERIALIZATION_TYPE_STRING, caEntryPoint);

CaTypeCtor caAssociatedSourceType(SERIALIZATION_TYPE_TYPE);
namedArgs[2].Init("AssociatedSourceType", SERIALIZATION_TYPE_TYPE, caAssociatedSourceType);

InlineFactory<SArray<CaValue>, 4> caValueArrayFactory;
Assembly* assembly = pMD->GetLoaderModule()->GetAssembly();
IfFailThrow(CustomAttribute::ParseArgumentValues(
Expand Down
65 changes: 62 additions & 3 deletions src/mono/mono/metadata/marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -3450,6 +3450,61 @@ mono_marshal_set_signature_callconv_from_attribute(MonoMethodSignature *sig, Mon
// - Adjust the code above with 'anding' the attribute parameter value
}

static gboolean
mono_marshal_unmanaged_callers_only_attribute_has_only_associated_source_type (MonoCustomAttrEntry *attr)
{
const guint8 *data = attr->data;
const guint8 custom_attribute_prolog = 0x01;
const guint8 named_arg_count = 0x01;
const guint8 field_named_arg = 0x53;
const guint8 system_type = 0x50;
const guint32 associated_source_type_name_len = sizeof ("AssociatedSourceType") - 1;
/* Prolog, named argument count, named argument kind, data type, and single-byte name length. */
const guint32 minimum_data_size = 2 + 2 + 1 + 1 + 1 + associated_source_type_name_len;
const char *value_start;
const char *value_after_size;
const char *end;
guint32 value_len;

if (attr->data_size < minimum_data_size)
return FALSE;
if (data [0] != custom_attribute_prolog || data [1] != 0x00)
return FALSE;
if (data [2] != named_arg_count || data [3] != 0x00)
return FALSE;
if (data [4] != field_named_arg || data [5] != system_type)
return FALSE;
if (data [6] != associated_source_type_name_len)
return FALSE;
if (memcmp (&data [7], "AssociatedSourceType", associated_source_type_name_len))
return FALSE;

value_start = (const char *)&data [7 + associated_source_type_name_len];
end = (const char *)data + attr->data_size;
if (value_start >= end)
return FALSE;
MONO_DISABLE_WARNING (4310) // cast truncates constant value
if (*value_start == (char)0xFF)
return value_start + 1 == end;
MONO_RESTORE_WARNING

if ((*value_start & 0x80) == 0) {
if (value_start + 1 > end)
return FALSE;
} else if ((*value_start & 0xC0) == 0x80) {
if (value_start + 2 > end)
return FALSE;
} else if ((*value_start & 0xE0) == 0xC0) {
if (value_start + 4 > end)
return FALSE;
} else {
return FALSE;
}

value_len = mono_metadata_decode_blob_size (value_start, &value_after_size);
return value_after_size <= end && value_len <= (guint32)(end - value_after_size);
}

static void
mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *method, MonoMethodSignature *csig)
{
Expand All @@ -3473,18 +3528,22 @@ mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *met
}
}

if (attr != NULL && mono_marshal_unmanaged_callers_only_attribute_has_only_associated_source_type (attr))
attr = NULL;

if (attr != NULL) {
MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, error);
mono_error_assert_ok (error);
for (int i = 0; i < decoded_args->named_args_num; ++i) {
if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) {
g_assertf(decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute parameter %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name);
g_assertf (decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "Expected UnmanagedCallersOnlyAttribute field %s to be SZARRAY type, specified for method %s. This indicates a malformed attribute or incorrect usage pattern.", decoded_args->named_args_info [i].field->name, method->name);
MonoCustomAttrValueArray *calling_conventions = decoded_args->named_args[i]->value.array;
if (calling_conventions->len > 0) {
if (calling_conventions->len > 1)
g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name);
g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute field %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name);
// TODO: Support multiple conventions?
MonoType* calling_convention = (MonoType*)calling_conventions->values[0].value.primitive;
// System.Type values are stored in the primitive union member by load_cattr_value_noalloc.
MonoType *calling_convention = (MonoType*)calling_conventions->values[0].value.primitive;
mono_marshal_set_signature_callconv_from_attribute (csig, calling_convention, error);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ public static int ManagedDoubleCallback(int n)
return DoubleImpl(n);
}

[UnmanagedCallersOnly(AssociatedSourceType = typeof(UnmanagedCallersOnlyBasicTest))]
public static int ManagedDoubleCallback_AssociatedSourceType(int n)
{
return DoubleImpl(n);
}

[ActiveIssue("https://github.com/dotnet/runtime/issues/64127", typeof(PlatformDetection), nameof(PlatformDetection.PlatformDoesNotSupportNativeTestAssets))]
[ActiveIssue("Needs coreclr build", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoFULLAOT))]
[ActiveIssue("needs triage", TestPlatforms.Android)]
Expand All @@ -60,7 +66,21 @@ public static void TestUnmanagedCallersOnlyValid()
Assert.Equal(expected, UnmanagedCallersOnlyDll.CallManagedProc((IntPtr)(delegate* unmanaged<int, int>)&ManagedDoubleCallback, n));
}

[UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
[ActiveIssue("https://github.com/dotnet/runtime/issues/64127", typeof(PlatformDetection), nameof(PlatformDetection.PlatformDoesNotSupportNativeTestAssets))]
[ActiveIssue("Needs coreclr build", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoFULLAOT))]
[ActiveIssue("needs triage", TestPlatforms.Android)]
[ActiveIssue("needs triage", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
[Fact]
public static void TestUnmanagedCallersOnlyValid_AssociatedSourceType()
{
Console.WriteLine($"Running {nameof(TestUnmanagedCallersOnlyValid_AssociatedSourceType)}...");

int n = 12345;
int expected = DoubleImpl(n);
Assert.Equal(expected, ((delegate* unmanaged<int, int>)&ManagedDoubleCallback_AssociatedSourceType)(n));
}

[UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
public static int ManagedDoubleCallback_Stdcall(int n)
{
return DoubleImpl(n);
Expand Down