Skip to content

Commit

Permalink
[release/9.0] [mono] inflate function pointer types; hang indirect ca…
Browse files Browse the repository at this point in the history
…ll wrappers on the <Module> (#106926)

* [mono] inflate function pointer types; hang indirect call wrappers on the <Module>

1. We were missing a case for `MONO_TYPE_FNPTR` in `inflate_generic_type`

2. When an indirect call was inside of a method that was part of a
generic class, we were creating wrapper methods for the indirect calls
parented by the GTD type of the caller.  This ended up making
common_call_trampoline consider the wrapper as needing an mrgctx.  But
if the caller was actually a normal closed instance type no mrgctx is
set up.  So we end up dereferencing a null pointer.  Instead, when we
create an indirect call wrapper method (which we asserted has no type
params and which is always just sets up the GC transition and forwards
the arguments to the indirect callee - so it never depends on the
generic context), make its parent be the <Module> class of the calling
assembly.  This is consistent with how we cache these indirect
methods (in the calling image, by the signature of the indirect
callee) - it has nothing to do with the caller class.

Fixes #106811

* pay attention to `changed` when inflating

if the cmods changed, but the sig didn't we want to return `type`.
return NULL only if there's an error or if nothing else changed

* add regression test

---------

Co-authored-by: Aleksey Kliger <alklig@microsoft.com>
Co-authored-by: Aleksey Kliger <aleksey@lambdageek.org>
  • Loading branch information
3 people committed Aug 26, 2024
1 parent 6f23d04 commit e107da8
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 1 deletion.
25 changes: 25 additions & 0 deletions src/mono/mono/metadata/class.c
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,31 @@ inflate_generic_type (MonoImage *image, MonoType *type, MonoGenericContext *cont
nt->data.type = inflated;
return nt;
}
case MONO_TYPE_FNPTR: {
MonoMethodSignature *in_sig = type->data.method;
// quick bail out - if there are no type variables anywhere in the signature,
// there's nothing that could get inflated.
if (!in_sig->has_type_parameters) {
if (!changed)
return NULL;
else
return type;
}
MonoMethodSignature *new_sig = mono_inflate_generic_signature (in_sig, context, error);
if ((!new_sig && !changed) || !is_ok (error)) {
return NULL;
} else if (!new_sig && changed)
return type;
if (new_sig == in_sig) {
if (!changed)
return NULL;
else
return type;
}
MonoType *nt = mono_metadata_type_dup (image, type);
nt->data.method = new_sig;
return nt;
}
default:
if (!changed)
return NULL;
Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/metadata/marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -3963,7 +3963,7 @@ mono_marshal_get_native_func_wrapper_indirect (MonoClass *caller_class, MonoMeth
return res;

char *name = mono_signature_to_name (sig, "wrapper_native_indirect");
MonoMethodBuilder *mb = mono_mb_new (caller_class, name, MONO_WRAPPER_MANAGED_TO_NATIVE);
MonoMethodBuilder *mb = mono_mb_new (get_wrapper_target_class (image), name, MONO_WRAPPER_MANAGED_TO_NATIVE);
mb->method->save_lmf = 1;

WrapperInfo *info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NATIVE_FUNC_INDIRECT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ internal static unsafe void NonGenericCalli<U>(void* fnptr, ref int val, float a
{
((delegate* unmanaged<ref int, float, void>)fnptr)(ref val, arg);
}

internal static unsafe int NonGenericCalliInNonGenericMethod(void* fnptr, float arg)
{
return ((delegate* unmanaged<float, int>)fnptr)(arg);
}
}

struct BlittableGeneric<T>
Expand Down Expand Up @@ -99,6 +104,12 @@ public static void RunGenericFunctionPointerTest(float inVal)
GenericCaller<string>.NonGenericCalli<string>((delegate* unmanaged<int*, float, void>)&UnmanagedExportedFunctionRefInt, ref outVar, inVal);
}
Assert.Equal(expectedValue, outVar);

unsafe
{
outVar = GenericCaller<string>.NonGenericCalliInNonGenericMethod((delegate* unmanaged<float, int>)&UnmanagedExportedFunction, inVal);
}
Assert.Equal(expectedValue, outVar);
}

[ConditionalFact(nameof(CanRunInvalidGenericFunctionPointerTest))]
Expand Down

0 comments on commit e107da8

Please sign in to comment.