Description
A .NET 11 Preview 5 MAUI Android app using CoreCLR + ReadyToRun with a startup MIBC profile crashes during process startup in the default/non-trimmable-typemap registration path.
The crash looks like the Java __md_methods string was corrupted, but generated Java/class/dex data is clean. The corruption appears after CoreCLR enters the managed JNI registration parser (JNIEnvInit.RegisterJniNatives / AndroidTypeManager.RegisterNativeMembers).
This appears to be a regression from the trimmable type map work leaking into the default configuration: the default CoreCLR registration path now routes through the raw jchar* -> ReadOnlySpan<char> parser that was introduced as part of the trimmable type map runtime work.
Crash
On Android emulator startup:
Failed to register native method crc6488302ad6e9e4df1a.MauiApplication.<garbage> in .../base.apk!classes2.dex
java.lang.NoSuchMethodError: no static or non-static method "Lcrc6488302ad6e9e4df1a/MauiApplication;.\u0011l"
at mono.android.Runtime.register(Native Method)
at net.dot.android.ApplicationRegistration.registerApplications(ApplicationRegistration.java:10)
at mono.MonoPackageManager.LoadApplication(MonoPackageManager.java:86)
at mono.MonoRuntimeProvider.attachInfo(MonoRuntimeProvider.java:21)
Earlier, with ManagedPeer native registration enabled, the same broad MIBC also crashed in Java.Interop.ManagedPeer. Disabling that feature switch moves the crash to the default mono.android.Runtime.register path shown above.
Environment
- SDK:
11.0.100-preview.5.26302.115
- Android workload pack:
Microsoft.Android.Sdk.Darwin/36.99.0-preview.5.308
- Target:
net11.0-android, android-arm64
- Runtime: CoreCLR
- Device used for repro: Android API 36 arm64 emulator (
emulator-5554)
- App type: .NET MAUI app
- Package:
com.companyname.demotimetracker
Project has a startup MIBC wired via:
<ItemGroup Condition="$(TargetFramework.Contains('-android'))">
<_ReadyToRunPgoFiles Include="pgo/android/*.mibc" />
</ItemGroup>
and ManagedPeer native registration disabled to bypass the separate ManagedPeer crash:
<ItemGroup>
<RuntimeHostConfigurationOption Include="Java.Interop.RuntimeFeature.ManagedPeerNativeRegistration"
Value="false" Trim="true" />
</ItemGroup>
Evidence gathered
Generated inputs are clean:
// obj/Release/net11.0-android/android-arm64/android/src/net/dot/android/ApplicationRegistration.java
mono.android.Runtime.register(
"Microsoft.Maui.MauiApplication, Microsoft.Maui, Version=11.0.0.0, Culture=neutral, PublicKeyToken=null",
crc6488302ad6e9e4df1a.MauiApplication.class,
crc6488302ad6e9e4df1a.MauiApplication.__md_methods);
// MauiApplication.__md_methods
n_onCreate:()V:GetOnCreateHandler
n_onLowMemory:()V:GetOnLowMemoryHandler
n_onTrimMemory:(I)V:GetOnTrimMemory_IHandler
n_onConfigurationChanged:(Landroid/content/res/Configuration;)V:GetOnConfigurationChanged_Landroid_content_res_Configuration_Handler
javap/dex inspection also shows clean constants. So the malformed JNI method name is not coming from Java source generation or dexing.
The crashing MIBC contains these methods:
[Mono.Android]Android.Runtime.JNIEnvInit.RegisterJniNatives(native int,int32,native int,native int,int32)
[Mono.Android]Android.Runtime.JNIEnvInit.<RegisterJniNatives>g__TypeGetType|11_0(string)
[Mono.Android]Android.Runtime.AndroidTypeManager.RegisterNativeMembers(JniType,Type,ReadOnlySpan`1<char>)
[Mono.Android]Android.Runtime.AndroidTypeManager.FastRegisterNativeMembers(JniType,Type,ReadOnlySpan`1<char>)
[Mono.Android]Android.Runtime.AndroidTypeManager.CountMethods(ReadOnlySpan`1<char>)
[Mono.Android]Android.Runtime.AndroidTypeManager.SplitMethodLine(ReadOnlySpan`1<char>,ReadOnlySpan`1<char>&,ReadOnlySpan`1<char>&,ReadOnlySpan`1<char>&,ReadOnlySpan`1<char>&)
The relevant path is:
Host::Java_mono_android_Runtime_register()
-> env->GetStringChars(methods)
-> JNIEnvInit.RegisterJniNatives(typeName_ptr, typeName_len, jniClass, methods_ptr, methods_len)
-> new ReadOnlySpan<char>((void*) methods_ptr, methods_len)
-> AndroidTypeManager.RegisterNativeMembers(..., ReadOnlySpan<char> methods)
-> SplitMethodLine(...)
-> JniNativeMethodRegistration(name.ToString(), signature.ToString(), callback)
Isolation result
I filtered the existing startup MIBC to exclude the Mono.Android assembly while keeping the rest of the profile. That removes the parser methods listed above.
Result:
- Original broad MIBC: repros startup crash with corrupted method name.
- MIBC excluding
Mono.Android: clean rebuild starts successfully on the emulator and remains running.
The first incremental rebuild after changing the MIBC hit an unrelated packaging abort:
Compressed assembly 'DemoTimeTracker.r2r.dll' is larger than when the application was built ... Assemblies don't grow just like that!
A dotnet clean + rebuild fixed that, and the no-Mono.Android MIBC APK then launched successfully.
Relevant history
The default parser code itself has not changed post Preview 5 on origin/main. The relevant mainline change appears to be older and already in Preview 5:
aa0f6d9f3 [TrimmableTypeMap] Implement runtime TypeManager, ValueManager, and JavaConvert (#10967)
git blame points the current default CoreCLR registration handoff in host.cc and the null-check/use in JNIEnvInit.cs to that trimmable type map runtime work.
Post-Preview5 origin/main did not show a direct change to RegisterJniNatives, AndroidTypeManager.RegisterNativeMembers, SplitMethodLine, or the native Host::Java_mono_android_Runtime_register() parser handoff.
Expected behavior
Applying a startup MIBC profile should not make the default/non-trimmable-typemap JNI registration path corrupt method names. The app should start, and generated __md_methods should parse into the same method names present in Java/dex.
Actual behavior
When the default Mono.Android JNI registration parser methods are included in MIBC-driven R2R, startup registration tries to register a garbage JNI method name for MauiApplication and fails with NoSuchMethodError.
Workaround
Exclude Mono.Android from the MIBC profile, or at minimum exclude:
Android.Runtime.JNIEnvInit.RegisterJniNatives
Android.Runtime.JNIEnvInit.<RegisterJniNatives>g__TypeGetType|11_0
Android.Runtime.AndroidTypeManager.RegisterNativeMembers
Android.Runtime.AndroidTypeManager.FastRegisterNativeMembers
Android.Runtime.AndroidTypeManager.CountMethods
Android.Runtime.AndroidTypeManager.SplitMethodLine
Description
A .NET 11 Preview 5 MAUI Android app using CoreCLR + ReadyToRun with a startup MIBC profile crashes during process startup in the default/non-trimmable-typemap registration path.
The crash looks like the Java
__md_methodsstring was corrupted, but generated Java/class/dex data is clean. The corruption appears after CoreCLR enters the managed JNI registration parser (JNIEnvInit.RegisterJniNatives/AndroidTypeManager.RegisterNativeMembers).This appears to be a regression from the trimmable type map work leaking into the default configuration: the default CoreCLR registration path now routes through the raw
jchar*->ReadOnlySpan<char>parser that was introduced as part of the trimmable type map runtime work.Crash
On Android emulator startup:
Earlier, with ManagedPeer native registration enabled, the same broad MIBC also crashed in
Java.Interop.ManagedPeer. Disabling that feature switch moves the crash to the defaultmono.android.Runtime.registerpath shown above.Environment
11.0.100-preview.5.26302.115Microsoft.Android.Sdk.Darwin/36.99.0-preview.5.308net11.0-android,android-arm64emulator-5554)com.companyname.demotimetrackerProject has a startup MIBC wired via:
and ManagedPeer native registration disabled to bypass the separate ManagedPeer crash:
Evidence gathered
Generated inputs are clean:
javap/dex inspection also shows clean constants. So the malformed JNI method name is not coming from Java source generation or dexing.The crashing MIBC contains these methods:
The relevant path is:
Isolation result
I filtered the existing startup MIBC to exclude the
Mono.Androidassembly while keeping the rest of the profile. That removes the parser methods listed above.Result:
Mono.Android: clean rebuild starts successfully on the emulator and remains running.The first incremental rebuild after changing the MIBC hit an unrelated packaging abort:
A
dotnet clean+ rebuild fixed that, and the no-Mono.AndroidMIBC APK then launched successfully.Relevant history
The default parser code itself has not changed post Preview 5 on
origin/main. The relevant mainline change appears to be older and already in Preview 5:git blamepoints the current default CoreCLR registration handoff inhost.ccand the null-check/use inJNIEnvInit.csto that trimmable type map runtime work.Post-Preview5
origin/maindid not show a direct change toRegisterJniNatives,AndroidTypeManager.RegisterNativeMembers,SplitMethodLine, or the nativeHost::Java_mono_android_Runtime_register()parser handoff.Expected behavior
Applying a startup MIBC profile should not make the default/non-trimmable-typemap JNI registration path corrupt method names. The app should start, and generated
__md_methodsshould parse into the same method names present in Java/dex.Actual behavior
When the default
Mono.AndroidJNI registration parser methods are included in MIBC-driven R2R, startup registration tries to register a garbage JNI method name forMauiApplicationand fails withNoSuchMethodError.Workaround
Exclude
Mono.Androidfrom the MIBC profile, or at minimum exclude: