Skip to content

[CoreCLR] MIBC/R2R of default JNI registration path corrupts __md_methods names #11633

@simonrozsival

Description

@simonrozsival

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

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions