Skip to content

fix(runtime): make UnsafeBlit trimming and NativeAOT consumption safe - #763

Merged
SunSi12138 merged 84 commits into
devfrom
fix/761-package-aot-unsafe-blit
Oct 2, 2026
Merged

SunSi12138 merged 84 commits into
devfrom
fix/761-package-aot-unsafe-blit

Conversation

@SunSi12138

@SunSi12138 SunSi12138 commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Closes #761

3.x structural follow-up: #762.

Problem

The final application decides whether it is trimmed or NativeAOT, but SharpLink.Runtime may arrive through either a precompiled PackageReference or a source ProjectReference.

  • PackageReference consumers use an already-built Runtime DLL.
  • ProjectReference consumers commonly put PublishTrimmed / PublishAot only in the application project; those ordinary project properties are not automatically forwarded to referenced projects as MSBuild global properties.

A child-project #if PublishTrimmed/PublishAot therefore cannot be the only correctness boundary.

2.x support contract

  • PackageReference: trimming and NativeAOT are supported through the packaged feature switch plus generated UnsafeBlit ABI metadata.
  • ProjectReference: NativeAOT and TrimMode=full are supported through the embedded linker substitution.
  • ProjectReference + TrimMode=partial: not part of the 2.x support contract. A prototype using IsTrimmable=true made partial trimming link Runtime, but caused a real iOS CoreCLR runtime regression in the blocking mobile evidence. That broader linker-policy change is deferred to the 3.x structural work in [3.x / NativeAOT / Architecture] Separate generated UnsafeBlit ABI validation from reflective fallback #762.

This preserves the existing mobile TrimMode=copy behavior instead of changing the whole Runtime assembly's linker semantics for a narrow source-consumer scenario.

2.x fix

  1. Embedded linker substitution

    • SharpLink.Runtime.dll embeds ILLink.Substitutions.xml.
    • When Runtime is actually linked, RpcUnsafeBlitPlatform.ReflectionFallbackEnabled is stubbed to false.
    • This covers ProjectReference NativeAOT and full trimming without depending on publish-property propagation.
  2. Package feature switch at final execution time

    • buildTransitive/SharpLink.Runtime.targets uses an execution-time target rather than an evaluation-time ItemGroup.
    • It runs after Apple workload _ComputePublishTrimmed when that target exists, and before runtimeconfig/ILLink consume RuntimeHostConfigurationOption.
    • It sets SharpLink.Runtime.UnsafeBlitReflectionFallback=false for packaged trimming / NativeAOT consumers even when PublishTrimmed is computed late.
    • For PackageReference TrimMode=partial, where Runtime remains a copy assembly, the same execution-time target adds a package-scoped ILLink warning-suppression file. This keeps unsupported ProjectReference partial trimming warning-visible.
    • The _ILLinkSuppressions hook is an SDK-internal .NET 10 coupling, so the nupkg verifier and package smoke explicitly lock that integration.
  3. No blanket trim opt-in

    • Runtime does not set IsTrimmable=true in 2.x.
    • The reflection-only helpers remain RequiresUnreferencedCode.
    • No blanket trim-warning suppression is used.

Untrimmed JIT keeps the reflective fallback. Supported trimmed/NativeAOT consumers require generated ABI metadata and fail closed when it is missing.

Regression coverage

PR Package Smoke triggers on Runtime, Generator, SDK, package smoke projects, and the source publish smoke scripts. On every relevant synchronize it executes:

Build Release
-> ProjectReference full-trim smoke
-> isolated ProjectReference NativeAOT smoke
-> Pack --no-build
-> ordinary JIT package smoke
-> packaged full-trim smoke
-> packaged partial-trim smoke
-> packaged NativeAOT smoke

Acceptance details:

  • ProjectReference full-trim has PublishTrimmed=true, PublishAot=false, TrimMode=full only in the app project.
  • The publish command does not inject /p:PublishTrimmed=true.
  • Source NativeAOT projects have PublishAot=true only in their project files; the smoke script no longer injects -p:PublishAot=true.
  • Generated UnsafeBlit must work; an unmanaged type without generated ABI metadata must fail closed.
  • Package verification requires buildTransitive/SharpLink.Runtime.targets to be present in the Runtime nupkg and verifies the packaged feature-switch setting.
  • PackageReference partial-trim has its own project-fixed TrimMode=partial consumer, so the feature-switch path is exercised independently of the full-trim linker substitution.
  • A separate late-set trimming configuration regression starts with PublishTrimmed unset, simulates the Apple workload _ComputePublishTrimmed target setting it to true, and requires both the runtime feature switch and partial-trim suppression to be added afterward.
  • NativeAOT SDK/intermediate output is isolated so it cannot contaminate the Release output later consumed by pack --no-build.
  • The trim smoke only deletes its own mktemp -d directory under $ROOT/artifacts.

Release blocking

Release Gate now calls the reusable Codec Mobile Evidence workflow and includes its result in release-summary.needs. The existing mobile summary requires Android CoreCLR and iOS CoreCLR evidence to succeed; iOS x64 is the blocking non-experimental lane.

Because the Release Gate itself only auto-runs for PRs targeting main, a narrow PR Release Mobile Wiring check runs when either release/mobile workflow file changes. It invokes the same reusable mobile workflow from a caller workflow and then verifies that release-gate.yml still wires codec-mobile-evidence into release-summary.needs. This gives the caller integration a persistent PR regression check without running the entire Release Gate on every dev PR.

Non-goals

  • no wire/CodecHash change;
  • no blanket field preservation;
  • no blanket trim-warning suppression;
  • no global IsTrimmable=true change in 2.x;
  • no ProjectReference partial-trim support in 2.x;
  • no generated ABI/public API change in 2.x.

@SunSi12138
SunSi12138 marked this pull request as ready for review September 29, 2026 12:24
@SunSi12138
SunSi12138 marked this pull request as draft September 30, 2026 00:38
@SunSi12138
SunSi12138 marked this pull request as ready for review September 30, 2026 00:38

Copy link
Copy Markdown
Owner Author

Review follow-up addressed:

  • P1 artifact contamination: fixed in eng/run-shared-memory-aot-process-smoke.sh by routing all NativeAOT publish SDK build/intermediate outputs through an isolated --artifacts-path under the smoke artifact directory. The normal Release bin/obj tree is no longer overwritten by ProjectReference AOT builds.
  • P2 package regression coverage: PR Package Smoke now triggers on src/SharpLink.Runtime/**, test/SharpLink.PackageSmoke/**, and test/SharpLink.PackageAotSmoke/**.

Validation on head 0d4d9121a5a397c8bb30b763f58905f252eec181:

The JIT fallback assertion was intentionally retained and now passes after the AOT smoke, proving the packaged Runtime DLL is no longer contaminated.

@SunSi12138
SunSi12138 marked this pull request as draft September 30, 2026 05:46
@SunSi12138
SunSi12138 marked this pull request as ready for review October 1, 2026 23:26

Copy link
Copy Markdown
Owner Author

Final validation on head 48579b7bf4ac86681324479424fb4ccd7859f222 is green.

src/SharpLink.Shared/** is also included in the PR Package Smoke trigger paths.

The PR is Ready for Review again.

@SunSi12138
SunSi12138 marked this pull request as draft October 1, 2026 23:53
@SunSi12138
SunSi12138 marked this pull request as ready for review October 2, 2026 01:51
@SunSi12138
SunSi12138 merged commit d7a57d9 into dev Oct 2, 2026
50 checks passed
@SunSi12138
SunSi12138 deleted the fix/761-package-aot-unsafe-blit branch October 2, 2026 02:04
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