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

[mono] Add support for UnmanagedCallersOnlyAttribute #38728

Merged
merged 27 commits into from
Jul 10, 2020

Conversation

lambdageek
Copy link
Member

@lambdageek lambdageek commented Jul 2, 2020

When we see a method tagged with UnmanagedCallersOnlyAttribute in an ldtn instruction, load a pointer to a native-to-managed wrapper instead of a pointer to the managed method.

See https://github.com/dotnet/csharplang/blob/master/proposals/functionpointers.md#systemruntimeinteropservicesunmanagedcallersonlyattribute

TODO:

@Dotnet-GitSync-Bot
Copy link
Collaborator

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@vargaz
Copy link
Contributor

vargaz commented Jul 2, 2020

Looks ok.

Copy link
Contributor

@alexischr alexischr left a comment

Choose a reason for hiding this comment

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

LGTM, documenting that this will increase AOT binaries sizes by 1 byte per native-to-managed wrapper AFAICT

src/mono/mono/metadata/marshal.c Outdated Show resolved Hide resolved
@lambdageek
Copy link
Member Author

this will increase AOT binaries sizes by 2 bytes per native-to-managed wrapper AFAICT

1 byte, I think - AOT uses the same value encoding as CIL images - small values (<127) encode to one byte.

@lambdageek lambdageek marked this pull request as ready for review July 6, 2020 22:04
@lambdageek
Copy link
Member Author

Should pass the positive tests now. Negative tests will still fail.

@lambdageek
Copy link
Member Author

lambdageek commented Jul 6, 2020

I think preventing calls via calli will be difficult using Mono's current design - the native to managed wrapper can't really tell it's being called from a managed method.

I misunderstood the test - it's in fact just crashing. So there's nothing to detect. Mono's behavior of not crashing is ok.

@lambdageek
Copy link
Member Author

Some progress on the negative tests. The InvalidProgramException ones should be in good shape.

@lambdageek
Copy link
Member Author

The TestUnmanagedCallersOnlyValid test is failing with the interpreter due to #38897

@lambdageek lambdageek force-pushed the hack-unmanaged-callers-only branch from 9317eb0 to 6e85742 Compare July 8, 2020 13:50
@lambdageek
Copy link
Member Author

JIT is passing all the tests except TestPInvokeMarkedWithUnmanagedCallersOnly. Working on that one now.

@lambdageek
Copy link
Member Author

Once I see the JIT pass all the tests, I'll bump the test back to Pri1 and then the PR is basically complete.

@lambdageek lambdageek requested a review from vargaz July 8, 2020 18:52
@lambdageek
Copy link
Member Author

From https://helix.dot.net/api/2019-06-17/jobs/51ac422f-058f-4fa3-aa34-f41bb363a4fb/workitems/Interop/console the "runtime (Mono Pri0 Runtime Tests Run OSX x64 release)" JIT job:

Interop.UnmanagedCallersOnly.XUnitWrapper           Total:  1, Errors: 0, Failed: 0, Skipped: 0, Time: 0.252s

As previously mentioned, the interpreter job is failing due to #38897, so this is the best we'll get for now.

Copy link
Contributor

@CoffeeFlux CoffeeFlux left a comment

Choose a reason for hiding this comment

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

Lots of questions but looks fine to me. Would be really good if Zoltan gets a chance to look at this before it's merged since I'm not terribly confident in my review of the code generation parts. Thanks!

csig = mono_metadata_signature_dup_full (get_method_image (method), sig);
csig->pinvoke = 0;
res = mono_mb_create_and_cache_full (cache, method, mb, csig,
csig->param_count + 16, info, NULL);
Copy link
Contributor

Choose a reason for hiding this comment

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

Where is this 16 coming from? It looks like it's used elsewhere, but I'm not familiar enough with this code to understand why.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't know. I'm cargo-culting the other mono_mb_create_and_cache_full calls in this function.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ugh, that's what I feared. @vargaz any idea? For my own learning if nothing else :)

Copy link
Contributor

Choose a reason for hiding this comment

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

No idea, it probably comes from the fact that these are wrappers so the deepest stack will be calling the 'wrapped' method, so param_count + is good for max stack.

src/mono/mono/metadata/marshal.c Outdated Show resolved Hide resolved
return NULL;
}
if (!method_signature_is_blittable (invoke_sig)) {
mono_error_set_invalid_program (error, "method %s with UnmanagedCallersOnlyAttribute has non-blittable parameters", mono_method_full_name (method, TRUE));
Copy link
Contributor

Choose a reason for hiding this comment

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

It could also have a non-blittable return type. I'd change this error slightly to reflect that.

{
ERROR_DECL (attr_error);
MonoClass *attr_klass = NULL;
#ifdef ENABLE_NETCORE
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this accomplishes the same thing, but wouldn't it be clearer to immediately return false on non-netcore?

src/mono/mono/mini/jit-icalls.c Outdated Show resolved Hide resolved
src/mono/mono/metadata/marshal.c Show resolved Hide resolved
src/mono/mono/metadata/marshal.c Show resolved Hide resolved
case MONO_TYPE_R8:
case MONO_TYPE_I:
case MONO_TYPE_U:
case MONO_TYPE_CHAR:
Copy link
Member Author

Choose a reason for hiding this comment

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

I think char shouldn't be here. Doesn't look like it's supposed to be blittable. (mono_class_setup_mono_type doesn't set the blittable bit on it)

Copy link
Contributor

@CoffeeFlux CoffeeFlux Jul 8, 2020

Choose a reason for hiding this comment

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

Ah yeah, good catch. Yes, it's definitely not for the same reason as string—ambiguity over converting to ANSI, UTF-8, or UTF-16.

@safern safern mentioned this pull request Jul 8, 2020
@@ -3940,10 +3958,54 @@ emit_managed_wrapper_noilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke
}
#endif

static gboolean
type_is_blittable (MonoType *type)
Copy link
Contributor

Choose a reason for hiding this comment

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

There is already a class->blittable flag.

Copy link
Contributor

Choose a reason for hiding this comment

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

We discussed this, but it's not the same because of byref types.

@lambdageek lambdageek merged commit 1e3c959 into dotnet:master Jul 10, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 8, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Interop-mono runtime-mono specific to the Mono runtime
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants