Skip to content

[XABT] Fix stale shrunk assemblies in the llvm-ir typemap path - #12342

Merged
jonathanpeppers merged 3 commits into
dotnet:mainfrom
BrzVlad:fix-mono-testing2
Aug 11, 2026
Merged

[XABT] Fix stale shrunk assemblies in the llvm-ir typemap path#12342
jonathanpeppers merged 3 commits into
dotnet:mainfrom
BrzVlad:fix-mono-testing2

Conversation

@BrzVlad

@BrzVlad BrzVlad commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

Fix stale shrunk assemblies in the llvm-ir typemap path and restore the Release MonoVM APK smoke test disabled in #12267.

_RemoveRegisterAttribute copies @(_ResolvedAssemblies) into @(_ShrunkAssemblies). Packaging reads the shrunk copies, while mono-aot-cross compiles the resolved assemblies. The two sets could diverge because:

  • Ordering: _RemoveRegisterAttribute ran before _GenerateJavaStubs, which invokes RewriteMarshalMethods and rewrites @(_ResolvedAssemblies) in place, removing the cb_* JNI callback fields. _PostTrimmingPipeline can also rewrite the linked assemblies after ILLink.
  • Invalidation: $(_AndroidLinkFlag) tracks ILLink, but it does not observe those later in-place rewrites. Tracking only the link flag allowed the shrunk copies to remain up to date while the resolved assemblies changed.

Changes

  • Make _RemoveRegisterAttribute depend on _GenerateJavaStubs so it copies the final rewritten assemblies.
  • Preserve $(_AndroidLinkFlag) as an input for ILLink invalidation and additionally track:
    • @(_ResolvedAssemblies) for post-link in-place rewrites.
    • $(_ResolvedUserAssembliesHashFile) for changes to the resolved assembly set.
    • $(_AndroidBuildPropertiesCache) and @(_AndroidMSBuildAllProjects) for relevant build/project changes.
  • Add $(_RemoveRegisterFlag) to @(FileWrites) so clean/incremental-clean behavior tracks the stamp.
  • Re-enable the Mono.Android.NET_Tests-Mono Release MonoVM instrumentation lane that [main] Update dependencies from dotnet/dotnet, microsoft/testfx #12267 temporarily disabled.

This aligns the llvm-ir path with the incremental-input model used by _RemoveRegisterAttributeCoreClr in Microsoft.Android.Sdk.TypeMap.Trimmable.targets, while retaining the existing ILLink sentinel.

Failure mechanism

In #12267, the packaged Mono.Android.dll retained Android.Widget.AbsListView::cb_getAdapter while the AOT input did not. This shifted the metadata Field table rows for Android.Runtime.AndroidTypeManager. AndroidTypeManager..cctor then resolved an AOT SFLDA patch to the instance field jniAddNativeMethodRegistrationAttributePresent instead of the static prevent_delegate_gc_lock, causing Mono to abort during startup before any tests ran:

Assertion at mono/metadata/object.c:3109,
condition `field->type->attrs & FIELD_ATTRIBUTE_STATIC' not met

Mono's AOT staleness check did not catch the mismatch because it compares assembly MVIDs, and Cecil preserves the MVID across these rewrites.

Validation

Validated on a Pixel 7a (arm64, API 36) with Mono.Android.NET-Tests, Release + -p:UseMonoRuntime=true, from clean obj directories:

  • Before: deterministic crash in 5 runs; linked assembly had 1767 field rows while the shrunk copy had 1768.
  • After: 281 tests passed with 0 failures in 2 runs; linked and shrunk assemblies both had 1767 field rows.
  • Control: reverting the target ordering/input changes restored both the crash and the 1767/1768 mismatch.
  • Azure DevOps: all 44 PR checks pass, including the restored Mono APK smoke lane.

Fixes dotnet/runtime#131760

`_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 dotnet#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 dotnet#12229.

Fixes dotnet/runtime#131760

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 56a10519-c924-45fd-a767-83cd4e5f947a

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a build-order and incremental-build invalidation issue in the llvm-ir typemap pipeline that could cause “shrunk” assemblies (used for packaging) to diverge from the assemblies compiled by mono-aot-cross, leading to runtime AOT failures due to mismatched metadata field-table row indices.

Changes:

  • Move _RemoveRegisterAttribute behind _GenerateJavaStubs so it copies from the final, post-rewrite linked assemblies.
  • Improve incremental invalidation by keying _RemoveRegisterAttribute off @(_ResolvedAssemblies) (plus project/build cache inputs) instead of $(_AndroidLinkFlag).
  • Track the stamp file in @(FileWrites) so it is cleaned reliably.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

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>
Comment thread build-tools/automation/yaml-templates/stage-package-tests.yaml
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>
@jonathanpeppers

Copy link
Copy Markdown
Member

/review

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer failed. Please review the logs for details.

@jonathanpeppers

Copy link
Copy Markdown
Member

@dalexsoto review

@dalexsoto dalexsoto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The target ordering and invalidation changes keep packaged shrunk assemblies aligned with the Mono AOT inputs, and the stamp/output cleanup is coherent. No blocking issues found.

@jonathanpeppers
jonathanpeppers merged commit 6308658 into dotnet:main Aug 11, 2026
44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Mono] [Android] Assertion in mono_static_field_get_addr during app startup after dotnet/dotnet update

4 participants