xamarin / xamarin-macios

.NET for iOS, Mac Catalyst, macOS, and tvOS provide open-source bindings of the Apple SDKs for use with .NET managed languages such as C#
Other
2.42k stars 507 forks source link

BadImageFormatException when mixing AOT and Interpreter assemblies #20652

Closed AndreyFromGomel closed 1 month ago

AndreyFromGomel commented 1 month ago

Hi, I'm working on porting game from Xamarin to .Net 8 and faced problem with increased application size (for Xamarin it is around 190 Mb, for .Net 8 it is 230 Mb), after migration application size exceed cellular download limit, this means build could be downloaded only via Wi-Fi unless user specified higher download limit in system settings. In https://github.com/xamarin/xamarin-macios/issues/19381 this problem is discussed more closely and as far as I understood there is no straightforward solution for this, NativeAOT is experimental so I don't consider this option at this moment. Switching to full interpreter mode is also not an option since it is game and there is noticeable performance degradation, so I decided to experiment with project settings. For now <MtouchInterpreter>-all,System.Linq.Expressions</MtouchInterpreter> is specified in Release configuration (application relies on Autofac as DI container and there were problems with resolution of certain types like Func<T> - Autofac uses System.Linq.Expressions under the hood for this). I selected the largest assembly in project and added it to MtouchInterpreter (<MtouchInterpreter>-all,System.Linq.Expressions,MyInterpreterAssembly</MtouchInterpreter>), it significantly reduced application size and for sure it will allows app to pass download limit restriction, in general app is working, but during the testing application crashes with BadImageFormatException in (at least) one specific place.

System.BadImageFormatException: Method has no body
IO_FileName_Name, MyAOTAssemblyName
   at NamespaceFromMyInterpreterAssembly.MyFactory.<>c__DisplayClass6_1.<<WithCallback>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at ObjCRuntime.Runtime.ThrowException(IntPtr gchandle)
   at UIKit.UIApplication.UIApplicationMain(Int32 argc, String[] argv, IntPtr principalClassName, IntPtr delegateClassName)
   at UIKit.UIApplication.Main(String[] args, Type principalClass, Type delegateClass)
   at MyApp.AppDelegate.Main(String[] args)

I have no explanation yet why it crashes in this specific place. Looks like function call from AOT to Interpreter assembly may lead to exception in certain conditions. My question if it is OK to use Interpreter for some specific assembly as I did or full app should be in interpreter mode? I will try to create reproduce for this, but it may take time since it I'm not allowed to share project code, now I'm identifying preconditions to reproduce this issue.

Steps to Reproduce

I haven't managed to create reproduce yet, working on it.

Expected Behavior

It is possible to call Interpreter code from AOT and vice versa.

Actual Behavior

BadImageFormatException is thrown.

Environment

Xcode: 15.2 Dotnet version: 8.0.202 ios 17.2.8043/8.0.100 SDK 8.0.200

Build Logs

Example Project (If Possible)

Working on it.

rolfbjarne commented 1 month ago

My question if it is OK to use Interpreter for some specific assembly

Yes, it should work, so something's going wrong somewhere.

I'm not allowed to share project code

Can you share your csproj and/or a binary build log (https://github.com/xamarin/xamarin-macios/wiki/Diagnosis#binary-build-logs)?

AndreyFromGomel commented 1 month ago

@rolfbjarne first of all big thanks for fast reply. I managed to create reproduce, main problem with reproduce was that I tried to do it on Simulator, looks like issue is relevant only for physical device. I attached sample archive to this message. Archive.zip

rolfbjarne commented 1 month ago

Thanks for the test project, I can reproduce this.

This looks like a the interpreter and the AOT compiler disagree what each of them should do when some assemblies are interpreted and some are AOT compiled.

I'm moving to dotnet/runtime, since it looks like a runtime issue.

rolfbjarne commented 1 month ago

This issue was moved to dotnet/runtime#102867