-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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][wasm] TrimMode=full is trimming UnmanagedCallersOnly #101434
Comments
Tagging subscribers to 'arch-wasm': @lewing |
Tagging subscribers to this area: @dotnet/interop-contrib |
NativeAOT appears to use UnmanagedCallersOnlyAssembly which feeds into runtime/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UnmanagedEntryPointsRootProvider.cs Line 15 in dd24e9c
|
Tagging subscribers to this area: @dotnet/interop-contrib |
Tagging subscribers to this area: @agocke, @sbomer, @vitek-karas |
ILLink doesn't have such support. Do we need to add support for UnmanagedCallersOnlyAssembly for this scenario to work? Or could the tooling that generates the interop logic for the UnmanagedCallersOnly method be made to pass along some extra roots to ILLink? |
NativeAOT would only export these if we're building a library. Is this building an executable that also acts as a library? Feels like a pretty special thing and I agree with Sven that maybe whatever is generating |
Alternatively if we expect to support trimmed native libraries at some point, a general purpose mechanism could be added to illink in preparation for that - I could see a new command argument allowing to specify a list of assemblies where UnmanagedCallersOnly methods with a non-empty |
Tagging subscribers to this area: @agocke, @sbomer, @vitek-karas |
It seems this is gap in ILink, please re-assign |
Is #101434 (comment) not feasible? |
My understanding is that Binlog But overall it feels like the Those methods are created
We could mandate that it's not enough, for those tools to do For a library in a nuget, we still don't have solution for running What are the challenges to implement it in ILLInk ? What makes it "pretty special thing" ? Why this is not a problem for NAOT on windows when they want to publish native DLL with exported native methods ? Are they not trimming it ? |
If I understand it correctly, WASM is hitting this when building an executable. ILLink currently doesn't support trimming If I understand it correctly, this would be a one-off ILLink feature for WASM only. That's why the question of whether we can do it elsewhere so that WASM specific things stay together with WASM. |
After discussing it offline, I understood that NAOT doesn't need to trim native DLL, because DLLs are never self-contained.
For WASI The next question is should we protect all UCOs ? And if we need to express more nuance to trim some UCOs, I think that ILLInk's
I see that now, thanks. It still feels to me that ILLink is the right place to implement UCO rooting. @sbomer do you have objections ? And guidance ? |
My main objection is that keeping all UCO in the app and its dependencies would lead to unnecessary bloat. Are you aware of any scenarios where the managed code genuinely has no insight into whether the native code will call back into managed? In the sample app above, I think a better solution would be for UnmanagedFunc to have a DynamicDependencyAttribute pointing to ManagedFunc. Using that approach in a library would allow the both methods to be removed if the library code path is unused in a trimmed app.
How will this work if I'm trying to implement a WASI component, and I have some nuget dependencies with UCO, but I don't want those to be exports of my WASI Component? |
I guess that's true with any WASI component which has more than one export. At build time, you don't know which of them would be used by other WASI component (for example written in python) that is consuming your component at runtime. Also in case that there is nuget/assembly with UCOs + WasmImportLinkageAttribute. More specific example I could imagine is WASI HTTP (server) handler, wrapper translating WASI API into C# API.
I suggest that UCOs should be protected by default and that trimming them would be opt-in via ILLink substitution/stub. |
Thank you, that helps me understand the scenarios a little better. A few more questions/comments: I agree that when building a library, UCO should be preserved, so that they exist when building an application that depends on the library (but whether they should be trimmed as part of the app build is a separate question). If we added a library trimming mode, I think it would make sense to preserve UCO in that mode - so for the rest of my comments I'll assume we're talking about trimming an app or final WASI component.
This isn't entirely clear to me. Could it not be the case that a library has UCO for interop with library-specific native code, but doesn't want to contribute it to the exports of the final WASI component?
We don't currently have a way to do this. The feature substitutions are able to:
But there's not a way to conditionally remove a method (other than making sure it's not called). We could probably come up with a way, but the model that currently makes most sense to me is to allow UCO methods to be removed by default. This allows libraries to preserve UCO methods only when certain code paths are reachable (for example via DynamicDependencyAttribute), allowing them to be removed if the library is unused or partially unused. |
Yes, you are right that there could be UCOs aimed at native code and we don't know if that native code uses it or not. I don't know how bad it would be if we rooted all of them. Are there (native) interop scenarios which generate UCOs opportunistically assuming it would be trimmed later ?
That should be enough to trim extra functionality which UCO rooted.
@lewing suggested that wit-bindgen is in better position to categorize the methods into groups by interface they implement. So that the app developer would have easier time to express what is to be preserved or trimmed by that group/interface. Let's continue thinking about it. |
NAOT does "trim" code from DLLs, because from the NAOT perspective, the output is always a self-contained native artifact. The difference between executables and libraries then becomes only about what are the entrypoints. The current NAOT model is that you need to specify the assemblies for which It works less well for WASI components, where it's expected that you can have exported UCOs in referenced assemblies. It does not work for JSExports. The NativeAOT-LLVM experience with this problem suggests that some more automatic solution is needed; this is easily the top recurring problem people open issues about. See e. g. dotnet/runtimelab#2626. |
This is to above point about the need for some UnmanagedEntryPointsAssembly-like mechanism: We've talked a bit with Pavel on Discord, and it was pointed out that it would be undesirable to specify the capability for rooting UCOs on an "unconditional" basis in a way that would force the trimmer (or ILC) to look at all of the referenced assemblies for unmanaged exports, many of which don't need to be looked at today. |
My understanding of the xamarin-macios managed static registrar, after reading through some of the discussion and code around #80912, is that it:
|
@sbomer my understanding was that we want to move away from custom linker passes as a solution, are you suggesting that is the correct approach? |
Notes from discussion with @SingleAccretion earlier this week
|
Description
After #100288 still for browser
UnmanagedCallersOnly
is not called after publish.Reproduction Steps
create browser sample app
and in
local.c
Expected behavior
ManagedFunc
should be calledActual behavior
No build errors, in
pinvoke-table.h
wrapper is there, butManagedFunc
is not being called.Regression?
No response
Known Workarounds
Calling it explicitly
((IntPtr)(delegate* unmanaged<int,int>)&ManagedFunc).ToString();
Configuration
No response
Other information
No response
The text was updated successfully, but these errors were encountered: