dotnet / dotnet/runtime

[iOS] .NET 9.0.13 broke NativeAOT builds with classic linker when AesGcm is used

Open
#124,609 21 comments 1 reaction 1 assignee Claimed by @akoeplinger View on GitHub
area-Infrastructure os-ios
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

There's a Swift shim in `libSystem.Security.Cryptography.Native.Apple.a`. The build tools were likely upgraded between .NET 9.0.12 an .NET 9.0.13 which resulted in additional dependencies inside the Swift shim:
```
U __swift_FORCE_LOAD_$_swiftXPC
00000000000022d0 S __swift_FORCE_LOAD_$_swiftXPC_$_pal_swiftbindings
U __swift_FORCE_LOAD_$_swift_Builtin_float
00000000000022a8 S __swift_FORCE_LOAD_$_swift_Builtin_float_$_pal_swiftbindings
U __swift_FORCE_LOAD_$_swift_errno
0000000000002270 S __swift_FORCE_LOAD_$_swift_errno_$_pal_swiftbindings
U __swift_FORCE_LOAD_$_swift_math
00000000000022a0 S __swift_FORCE_LOAD_$_swift_math_$_pal_swiftbindings
U __swift_FORCE_LOAD_$_swift_signal
0000000000002278 S __swift_FORCE_LOAD_$_swift_signal_$_pal_swiftbindings
U __swift_FORCE_LOAD_$_swift_stdio
0000000000002298 S __swift_FORCE_LOAD_$_swift_stdio_$_pal_swiftbindings
U __swift_FORCE_LOAD_$_swift_time
0000000000002288 S __swift_FORCE_LOAD_$_swift_time_$_pal_swiftbindings
U __swift_FORCE_LOAD_$_swiftsys_time
0000000000002280 S __swift_FORCE_LOAD_$_swiftsys_time_$_pal_swiftbindings
U __swift_FORCE_LOAD_$_swiftunistd
0000000000002290 S __swift_FORCE_LOAD_$_swiftunistd_$_pal_swiftbindings
```

The new Apple linker has some logic to handle some backwards compatibility but that doesn't work with classic linker. The classic linker fails to compile the project now:
```
/usr/local/share/dotnet/packs/Microsoft.iOS.Sdk.net9.0_26.0/26.0.9785/targets/Xamarin.Shared.Sdk.targets(1835,3): error :
clang++ exited with code 1:
0 0x100d81788 __assert_rtn + 160
1 0x100d84570 ld::tool::SymbolTableAtom::classicOrdinalForProxy(ld::Atom const*) (.cold.3) + 0
2 0x100cbbdb0 ld::tool::SymbolTableAtom::classicOrdinalForProxy(ld::Atom const*) + 172
3 0x100cbcc24 ld::tool::SymbolTableAtom::addImport(ld::Atom const*, ld::tool::StringPoolAtom*) + 140
4 0x100cbe508 ld::tool::SymbolTableAtom::encode() + 396
5 0x100cb03b0 ___ZN2ld4tool10OutputFile20buildLINKEDITContentERNS_8InternalE_block_invoke.413 + 36
6 0x18814ca28 _dispatch_call_block_and_release + 32
7 0x1881664b0 _dispatch_client_callout + 16
8 0x188183d9c _dispatch_channel_invoke.cold.9 + 76
9 0x18815eadc _dispatch_root_queue_drain + 708
10 0x18815f120 _dispatch_worker_thread2 + 184
11 0x188303e84 _pthread_wqthread + 232
A linker snapshot was created at:
/tmp/AesGcmAppleNAOT-2026-02-19-191403.ld-snapshot
ld: Assertion failed: (it != _dylibToOrdinal.end()), function dylibToOrdinal, file OutputFile.cpp, line 5196.
```

We cannot use the new linker due to #119380.

Minimal repro:
[AesGcmAppleNAOT.zip](https://github.com/user-attachments/files/25424151/AesGcmAppleNAOT.zip) - Run `dotnet publish`

The repro simply uses the `AesGcm` class which depends on the Swift shim. For simplicity, any code like this is sufficient to reference the class and the related shims:
```csharp
using (var aes = new AesGcm(new byte[32], 16))
aes.Encrypt([], [], [], [], []);
```

The project includes global.json which pins the SDK version to `9.0.311`. Downgrading to `9.0.310` (9.0.12 runtime) makes the error go away.

Minimal supported platform version is set with `15.0`. Raising it to `18.0` also makes the problem go away because the OS deployed Swift version is new enough.

Finally, `<_UseClassicLinker>true` (dotnet/macios workload) switch is used to force the usage of the old linker.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.