From 55576acda1dc75e2e24dd64791f49b511aeb6ee8 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Tue, 11 Aug 2026 14:21:05 +0300 Subject: [PATCH 1/3] [XABT] Fix stale shrunk assemblies in the llvm-ir typemap path `_RemoveRegisterAttribute` copies `@(_ResolvedAssemblies)` into `@(_ShrunkAssemblies)`. Packaging reads the shrunk copies, while `mono-aot-cross` compiles the resolved assemblies. Two defects let those two sets diverge: * Ordering: the target ran before `_GenerateJavaStubs`, which invokes `RewriteMarshalMethods` and rewrites `@(_ResolvedAssemblies)` in place, removing the `cb_*` JNI callback fields. `_PostTrimmingPipeline` rewrites them again `AfterTargets="ILLink"`. * Invalidation: `Inputs` tracked `$(_AndroidLinkFlag)`, which is only touched by ILLink and so never changes when those post-link rewrites modify the assemblies. The result is an APK whose AOT images were built from assemblies with a different metadata `Field` table than the assemblies actually packaged. AOT encodes a static field reference as a class reference plus a `Field` table row (`encode_field_info` / `decode_field_info`), so one added or removed field shifts every later row. In #12267 the packaged `Mono.Android.dll` retained `Android.Widget.AbsListView::cb_getAdapter` while the AOT input did not, moving `Android.Runtime.AndroidTypeManager` from rows 984-987 to 985-988. `AndroidTypeManager..cctor` then resolved its `SFLDA` patch to row 985 - the instance field `jniAddNativeMethodRegistrationAttributePresent` instead of the static `prevent_delegate_gc_lock` - and Mono aborted during startup, before any test ran: * Assertion at mono/metadata/object.c:3109, condition `field->type->attrs & FIELD_ATTRIBUTE_STATIC' not met Mono cannot detect the mismatch on its own: its AOT staleness check compares the assembly MVID, and Cecil preserves the MVID across rewrites, so both copies look identical to it. `_RemoveRegisterAttributeCoreClr` in Microsoft.Android.Sdk.TypeMap.Trimmable.targets already keys `Inputs` off `@(_ResolvedAssemblies)`. This brings the llvm-ir path in line, which is why only the Mono leg regressed while CoreCLR passed. Validated on a Pixel 7a (arm64, API 36) with the Mono.Android.NET-Tests instrumentation, Release + `-p:UseMonoRuntime=true`, each run from a clean `obj`: * before: crash (5 runs, deterministic), linked/ 1767 rows vs linked/shrunk/ 1768 rows * after: 281 tests, 0 failed (2 runs), linked/ and shrunk/ both 1767 rows * control: reverting only these two attributes on the same built SDK restores both the crash and the 1767/1768 mismatch The shrunk copies were already rewritten on every build before this change, so tracking `@(_ResolvedAssemblies)` does not regress the incremental work in #12229. Fixes https://github.com/dotnet/runtime/issues/131760 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 56a10519-c924-45fd-a767-83cd4e5f947a --- ...crosoft.Android.Sdk.TypeMap.LlvmIr.targets | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets index 85bbf54a0a4..9316f40db68 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets @@ -377,10 +377,25 @@ - + + + + From 164057c731314d5cc41f69c5dfb74f51905a6637 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Tue, 11 Aug 2026 07:44:42 -0500 Subject: [PATCH 2/3] [ci] Re-enable Mono APK smoke test Restore the Release MonoVM instrumentation lane now that the llvm-ir typemap path keeps packaged shrunk assemblies in sync with the AOT inputs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- build-tools/automation/yaml-templates/stage-package-tests.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/build-tools/automation/yaml-templates/stage-package-tests.yaml b/build-tools/automation/yaml-templates/stage-package-tests.yaml index 632529d2883..7cbed90d579 100644 --- a/build-tools/automation/yaml-templates/stage-package-tests.yaml +++ b/build-tools/automation/yaml-templates/stage-package-tests.yaml @@ -158,8 +158,6 @@ stages: extraBuildArgs: -p:UseMonoRuntime=true -p:_DisableCheckForUnsupportedMonoMobileRuntime=true artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab artifactFolder: $(DotNetTargetFramework)-Mono - # https://github.com/dotnet/runtime/issues/131760 - condition: false - template: /build-tools/automation/yaml-templates/apk-instrumentation.yaml parameters: From 692ec45fb38d27e7829c12724d4968a31e1b162d Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Tue, 11 Aug 2026 09:33:16 -0500 Subject: [PATCH 3/3] [XABT] Preserve ILLink invalidation for shrunk assemblies Keep the existing link flag input while also tracking final rewritten assemblies and the resolved assembly set hash. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Microsoft.Android.Sdk.TypeMap.LlvmIr.targets | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets index 9316f40db68..994a38cb374 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets @@ -385,17 +385,17 @@ `cb_*` JNI callback fields), and `_PostTrimmingPipeline` rewrites them again after ILLink. Depending on `_GenerateJavaStubs` keeps this copy ordered after those rewrites. - Inputs track the assemblies themselves rather than `$(_AndroidLinkFlag)`: the link flag is - only touched by ILLink, so it does not change when the post-link rewrites modify the - assemblies. Keying on the flag let the shrunk copies go stale while @(_ResolvedAssemblies) - moved on, so mono-aot-cross compiled the rewritten assemblies while packaging shipped the - pre-rewrite copies. Their metadata field tables then disagreed, and AOT `SFLDA` patches - resolved to the wrong MonoClassField at runtime. This mirrors `_RemoveRegisterAttributeCoreClr` - in Microsoft.Android.Sdk.TypeMap.Trimmable.targets. + `$(_AndroidLinkFlag)` tracks ILLink, while @(_ResolvedAssemblies) tracks the later in-place + rewrites that the link flag cannot observe. The assembly hash also invalidates the target when + the resolved assembly set changes. Without the final assemblies as inputs, the shrunk copies + could go stale while @(_ResolvedAssemblies) moved on, so mono-aot-cross compiled the rewritten + assemblies while packaging shipped the pre-rewrite copies. Their metadata field tables then + disagreed, and AOT `SFLDA` patches resolved to the wrong MonoClassField at runtime. This mirrors + `_RemoveRegisterAttributeCoreClr` in Microsoft.Android.Sdk.TypeMap.Trimmable.targets. -->