Skip to content

Enable opt-in R8 JNI name obfuscation for CoreCLR and NativeAOT - #12575

Draft
simonrozsival wants to merge 13 commits into
mainfrom
simonrozsival-prototype-r8-jni-remapping
Draft

Enable opt-in R8 JNI name obfuscation for CoreCLR and NativeAOT#12575
simonrozsival wants to merge 13 commits into
mainfrom
simonrozsival-prototype-r8-jni-remapping

Conversation

@simonrozsival

@simonrozsival simonrozsival commented Aug 28, 2026

Copy link
Copy Markdown
Member

Context

Advances #12535.

.NET for Android currently disables R8 name obfuscation because managed bindings embed Java/JNI class, method, field, and descriptor names. If R8 renames the Java side while managed metadata and IL retain the original names, JNI lookup and native registration fail.

This change implements build-time rewriting without a runtime lookup table. R8 remains the naming authority: a naming-only pass establishes names, managed assemblies are rewritten before ILLink or NativeAOT ILC, and final R8 performs normal shrinking and optimization with -applymapping.

The feature is opt-in through AndroidEnableR8JniNameObfuscation. It requires AndroidLinkTool=r8, AndroidTypeMapImplementation=trimmable, and either CoreCLR or NativeAOT.

Pipeline

  1. Generate the pre-trim trimmable typemap and Java callable wrappers.
  2. Compile the seed Java graph and run naming-only R8.
  3. Rewrite managed JNI metadata, IL strings, descriptors, typemap attributes, aliases, and FieldRVA data using the seed mapping.
  4. Feed collision-safe, path-hashed rewritten assemblies to ILLink or NativeAOT ILC.
  5. Generate backend-specific final keep rules and a post-link reachability manifest.
  6. Run final R8 with normal shrinking/optimization and -applymapping.
  7. Validate seed/final name stability and required Java-symbol reachability.
  8. Package the resulting DEX and optimized managed output normally.

NativeAOT's prebuilt host performs fixed native lookups against mono.android.Runtime, so that small bridge remains deliberately preserved.

Production hardening

  • Rewritten assemblies are staged under source-path hashes, preventing duplicate-filename and ABI collisions while preserving downstream item metadata.
  • Multi-RID builds use isolated per-RID rewrite outputs and manifests; the shared final Java/R8 pass validates against the first RID's equivalent managed manifest.
  • Strong-named linker/compiler inputs retain their public key and assembly identity plus re-signable signature-directory space, while the stale StrongNameSigned flag is cleared. These private rewritten inputs are intentionally delay-signed because the original private keys are unavailable.
  • NativeAOT multi-RID inner builds retain their normal parallelism.
  • Generated ACW keep rules deduplicate repeated map values; one measured seed configuration decreased from 21,875 rules to 7,271.
  • Seed compilation now runs after intermediate-directory cleanup/setup so clean inner builds have valid stamp directories.

Validation coverage

Automated Release integration tests cover CoreCLR and NativeAOT and verify:

  • clean, no-op, missing-output recovery, and JNI-name invalidation builds;
  • arm64 plus x64 multi-ABI builds with isolated rewritten inputs and manifests;
  • referenced Android libraries containing registered Java peers;
  • custom ProGuard configuration changes invalidating the seed pass, managed rewrite, and final R8;
  • collision-safe hashed staging paths;
  • seed/final mappings plus rewrite and reachability manifests;
  • preservation of public-key assembly identity and delay-signature space.

The focused hardening matrix passes all 7 CoreCLR, NativeAOT, multi-ABI, project-reference/custom-rule, incrementality, and strong-name tests. Existing JNI mapping, rewriting, scanning, mapping-consistency, and reachability coverage remains in place.

Measurements

Metric Baseline Two-pass Delta
CoreCLR signed APK 15,681,455 bytes 15,394,735 bytes -286,720 bytes (-1.83%)
NativeAOT signed APK 19,490,367 bytes 19,138,111 bytes -352,256 bytes (-1.81%)

Across 86 generated typemap assemblies, rewriting 13,031 real typemap entries reduced raw PE size by 1,016,832 bytes (9.42%) and gzip-equivalent size by 107,446 bytes (6.61%). NativeAOT's packaged saving came primarily from classes.dex, because those managed assemblies are compiled into the application native library.

Naming-only R8 remains the dominant added clean-build cost. Replacing it with a managed allocator would reproduce R8 inheritance, interface dispatch, override, keep-rule, and minifier semantics, so this design intentionally keeps R8 as the naming authority.

Remaining follow-up

Additional ACW class-name obfuscation is intentionally deferred. Allowing those classes to be renamed caused CoreCLR final R8 to remove reachable generated native declarations such as nctor_0; the reachability validator correctly rejected the output. Supporting this safely requires a separate native registration/retention design rather than weaker validation or another superficial ProGuard modifier.

simonrozsival and others added 2 commits August 28, 2026 16:07
Rebuild managed PE metadata and IL with obfuscated JNI class, method, field, descriptor, RegisterNatives, and FieldRVA string data. Preserve compression descriptor ordering when rewritten assembly sizes change.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Run a naming-only R8 pass before managed optimization, stage rewritten inputs for ILLink and ILC, and reuse the mapping during final Java shrinking.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival changed the title [prototype] Rewrite managed JNI names after R8 obfuscation [prototype] Two-pass R8 JNI name rewriting before ILLink and ILC Aug 31, 2026
simonrozsival and others added 4 commits August 31, 2026 11:51
Use descriptor-aware reverse member mappings and regenerate the post-link ACW reachability map while retaining the original Java input set.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Record the exact seed mapping entries consumed while rewriting managed assemblies and verify that final R8 preserves every surviving name. Removed entries remain valid linker or R8 trimming outcomes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival changed the title [prototype] Two-pass R8 JNI name rewriting before ILLink and ILC [prototype] Enable R8 JNI name obfuscation for CoreCLR and NativeAOT Aug 31, 2026
simonrozsival and others added 5 commits August 31, 2026 14:38
Generate post-link manifests from the CoreCLR and NativeAOT keep-rule inputs, then fail final R8 when a required Java class or member disappears. Cover registered fields and overload-aware methods in the CoreCLR rules.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reuse the full JNI rewrite planner in reverse over linked CoreCLR assemblies so final R8 keep rules cover attributes, direct lookups, descriptors, interfaces, typemap metadata, and FieldRVA data. Preserve every ambiguous reverse member candidate and index exact lookups.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Normalize expected multiline output to the host line ending so the new test passes on Windows as well as Unix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add a supported opt-in, collision-safe staging, multi-ABI and project-reference coverage, identity-preserving strong-name handling, and deduplicated ACW keep rules.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival changed the title [prototype] Enable R8 JNI name obfuscation for CoreCLR and NativeAOT Enable opt-in R8 JNI name obfuscation for CoreCLR and NativeAOT Sep 1, 2026
simonrozsival and others added 2 commits September 1, 2026 12:07
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

🚀 Android PR Reviewer has started processing this issue comment

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.

1 participant