[NativeAOT] Stop linking and shipping libc++ and libunwind - #12523
[NativeAOT] Stop linking and shipping libc++ and libunwind#12523simonrozsival wants to merge 3 commits into
Conversation
6c0530e to
75d714c
Compare
There was a problem hiding this comment.
Pull request overview
This PR advances the NativeAOT size/startup work in #12139 by removing libc++/libunwind from the NativeAOT link, while also refactoring several native runtime paths to avoid local-string helpers and reduce incidental C++ runtime dependencies. It also updates test baselines for resulting native library/package size changes.
Changes:
- Update NativeAOT MSBuild targets to stop linking
libc++_static.a,libc++abi.a, andlibunwind.a, and to stop removinglibstdc++compat.a. - Refactor multiple native runtime components (Mono + CoreCLR hosts) to use explicit bounded buffers /
snprintf/ heap fallback helpers instead of local-string types. - Update
.apkdesctest resource baselines to reflect newlibmonodroid.soand package sizes.
Show a summary per file
| File | Description |
|---|---|
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.R8.apkdesc | Update expected APK contents/sizes after native size change. |
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc | Update expected APK contents/sizes after native size change. |
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.R8.apkdesc | Update expected APK contents/sizes after native size change. |
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc | Update expected APK contents/sizes after native size change. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets | Stop linking libc++/libunwind for NativeAOT and keep libstdc++compat.a. |
| src/native/mono/runtime-base/util.hh | Add joined-path formatting/join helpers and shared helpers include. |
| src/native/mono/runtime-base/android-system.hh | Add string_view system property overload returning into caller buffer. |
| src/native/mono/runtime-base/android-system.cc | Implement new buffer-based system property retrieval fallback path. |
| src/native/mono/monodroid/monodroid-glue.cc | Replace local-string timing “more info” assembly strings with bounded formatting + heap fallback. |
| src/native/common/runtime-base/timing-internal.cc | Replace property parsing and timing log path composition to use char buffers + join helper. |
| src/native/common/include/runtime-base/timing-internal.hh | Replace dynamic_local_string formatting with snprintf-based message building and add_more_info overloads. |
| src/native/clr/runtime-base/util.cc | Replace dynamic_local_string directory creation with manual mutable buffer + heap fallback. |
| src/native/clr/runtime-base/logger.cc | Switch various path and category parsing to buffer-based helpers and string_view. |
| src/native/clr/runtime-base/android-system.cc | Replace local-string path building with join helpers; refactor DSO path formatting to explicit buffers. |
| src/native/clr/runtime-base/android-system-shared.cc | Change system property retrieval to direct buffer writes; remove small-buffer heap shim in __system_property_get wrapper. |
| src/native/clr/include/runtime-base/util.hh | Add join_paths / format_dso_name helpers; remove path-combine concepts/local-string overloads. |
| src/native/clr/include/runtime-base/logger.hh | Update set_category signature to accept std::string_view. |
| src/native/clr/include/runtime-base/android-system.hh | Introduce buffer-based path formatting helpers; refactor primary override dir formatting. |
| src/native/clr/include/host/pinvoke-override-impl.hh | Build lib*.so pinvoke names via new DSO-name formatter with heap fallback. |
| src/native/clr/include/host/os-bridge.hh | Add missing include for shared utilities used by the header. |
| src/native/clr/include/host/host-environment.hh | Refactor XDG path building to use buffer join logic (and environment setup changes). |
| src/native/clr/include/host/assembly-store.hh | Remove dependency on local-string header. |
| src/native/clr/host/typemap.cc | Build managed type names via bounded formatting + heap fallback. |
| src/native/clr/host/host.cc | Avoid temporary local-string allocations for logging/timing type names. |
| src/native/clr/host/bridge-processing.cc | Add missing includes needed after refactors. |
| src/native/clr/host/assembly-store.cc | Replace local-string timing messages with bounded snprintf messages. |
Review details
- Files reviewed: 6/6 changed files
- Comments generated: 0
- Review effort level: Lite
75d714c to
8021d9f
Compare
dd62525 to
4126bc1
Compare
Linker evidence for keeping
|
| probe | before | after |
|---|---|---|
libc++abi |
2 | 0 |
std::bad_alloc |
1 | 0 |
terminate_handler |
2 | 0 |
_Unwind_Resume |
2 | 0 |
Size impact
Default MAUI app, android-arm64, Release, PublishAot:
| before | after | delta | |
|---|---|---|---|
.so |
25,229,320 | 25,029,728 | −199,592 (−0.79%) |
.so deflated |
9,829,143 | 9,761,291 | −67,852 (−0.69%) |
| APK | 14,903,624 | 14,833,992 | −69,632 (−0.47%) |
Why there is no CMake change in this PR
src/native/**/CMakeLists.txt only produces a static archive (libnaot-android.release-static-release.a) — ar bundling, no linking, so there is nothing there to stop linking. The application link is driven entirely by Microsoft.Android.Sdk.NativeAOT.targets, which is what this PR changes.
4126bc1 to
4dab0f8
Compare
4dab0f8 to
5ac0b54
Compare
5ac0b54 to
31a6570
Compare
4bc321d to
7f9f586
Compare
ea894f2 to
d292114
Compare
d292114 to
935ec9d
Compare
935ec9d to
0b54728
Compare
0b54728 to
495d51f
Compare
495d51f to
051dee2
Compare
NativeAOT applications no longer need the C++ standard library. The host sources compiled into `libnaot-android` do not use any libc++ facility, and the five C++ allocation-ABI symbols the NativeAOT runtime itself references (`operator delete`, `operator delete[]`, the two `nothrow` `operator new` overloads and `std::nothrow`) are already supplied by `libstdc++compat.a`, which ships in the ILC SDK. We used to remove `libstdc++compat.a` from the link because it conflicts with static libc++. With libc++ gone there is no conflict, so keep it and drop `libc++_static.a`, `libc++abi.a` and `libunwind.a` instead. Measured on a default MAUI app (arm64, Release, `PublishAot`): | | before | after | delta | |---|---|---|---| | `.so` | 25,229,320 | 25,029,728 | -199,592 (-0.79%) | | `.so` deflated in APK | 9,829,143 | 9,761,291 | -67,852 (-0.69%) | | APK | 14,903,624 | 14,833,992 | -69,632 (-0.47%) | Contributes to #12139. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e
`LinkStandardCPlusPlusLibrary` is no longer about avoiding interference with static libc++ -- it now keeps the compiler driver from adding a library we do not link at all. Also fix the `clang++` note to mention `-nostdlib++`, which is the flag that suppresses the C++ standard library, rather than `-nostdlib`, which also drops the CRT. Note that no explicit `-nostdlib++` is needed today: the final `.so` is linked by `LinkNativeAotSharedLibrary` via `ld.lld` directly, with fully explicit inputs and `AllowUndefinedSymbols = false`, so no compiler driver is in a position to add libc++ implicitly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e
Now that NativeAOT applications no longer link libc++, the NativeAOT runtime packs still carry `libc++_static.a`, `libc++abi.a` and `libunwind.a` as dead weight. The NDK redistributables were split into just `System` and `Toolchain`, and the `Toolchain` group was shipped to both CoreCLR and NativeAOT. NativeAOT still needs `crtbegin_so.o`, `crtend_so.o` and `libclang_rt.builtins-*.a` from that group, so the group cannot simply be dropped. Introduce a third `CplusPlus` kind for the three C++ archives and ship it only for CoreCLR, which still links libc++. Per ABI this removes 18,398,848 bytes from the NativeAOT runtime pack: | Archive | Size | | --- | ---: | | `libc++_static.a` | 15,182,348 | | `libc++abi.a` | 3,125,348 | | `libunwind.a` | 91,152 | Across the three shipped ABIs (`android-arm`, `android-arm64`, `android-x64`) that is roughly 55 MB. Contributes to #12139. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e
051dee2 to
1f93d26
Compare
NativeAOT already had zero references to the C++ standard library after #12523, so switching `nativeaot-default-common` to `ANDROID_STL=none` costs nothing and turns any future libc++ use into a link error instead of silently pulling the archive back in. Verified on `android-arm64` Release with a clean configure: all three NativeAOT artifacts -- `libnaot-android.release.so`, `libnaot-android.release-static-release.a` and `libxa-java-interop-release.a` -- are byte-for-byte identical to the `c++_static` build. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e
Closes #12139
Closes #12146
Removes
libc++_static.a,libc++abi.aandlibunwind.afrom the NativeAOT link. This is the payoff for the local-string removal work in the rest of this stack: withstrings.hh,dynamic_local_stringandstatic_local_stringgone, nothing inlibnaot-androiduses libc++ any more.Who was actually using the C++ runtime?
Linking with libc++ removed leaves exactly six undefined symbols. Only one of them came from our code:
operator new[](size_t)android-system-shared.cc— oursoperator delete(void*)gcenv.ee.cpp,UnixNativeCodeManager.cppoperator delete[](void*)gcenv.ee.cpp,interoplibinterface_java.cppoperator new(size_t, nothrow_t const&)gcenv.ee.cpp,TypeManager.cppoperator new[](size_t, nothrow_t const&)gcenv.ee.cpp,RhConfig.cppstd::nothrowgcenv.ee.cpp,UnixNativeCodeManager.cppThe five runtime-owned ones belong to the NativeAOT runtime from dotnet/runtime, and the ILC SDK already ships definitions for all of them in
libstdc++compat.a. Our targets were unconditionally removing that archive with the comment "This library conflicts with static libc++" — which is only true while libc++ is linked. With libc++ gone there is no conflict, so we simply stop removing it.The one symbol that was ours came from a dead code path in
monodroid__system_property_get, which is now removed earlier in the stack by #12517 (the PR that makes that path unreachable in the first place).The result is no shim code at all in this repo, and a link with zero undefined symbols.
Size impact
Default MAUI app (
dotnet new maui),net11.0-android,android-arm64, Release,PublishAot=true. Both sides built clean from the same tree..so.sodeflated in APKCommits
Notes
std::unordered_map, which drags in__next_primeand__libcpp_verbose_abortfrom libc++ internals. The rest of this stack is already rebased on top of it, so nothing further is needed.NativeRuntimeComponents.cs(the unified-runtime archive list) is deliberately untouched.libunwind.adrops out with zero undefined symbols — the NativeAOT runtime never referenced it.Testing
Built, installed and launched a default MAUI app on an API 36 arm64 emulator. Cold start with no crashes.
llvm-nm --undefined-onlyon the resultinglibnaot-android.release-static-release.areports nooperator new/operator delete,__cxa_*,_Unwind_*or__libcpp_*references. The only remainingstd::symbols arestring_viewappearing in mangled names, which is header-only and carries no runtime dependency.Not covered: only the arm64 + workload-linker path was exercised locally. The NDK linker path and the x64/arm ABIs rely on CI.
Also: stop shipping the archives in the runtime packs
Previously a separate PR stacked directly on this one; folded in here because "stop linking it" and "stop shipping it" are the same change to the reader, and reviewing them apart means reading the same targets twice.
The NativeAOT runtime packs still shipped
libc++_static.a,libc++abi.aandlibunwind.aeven though, after the change above, nothing links them any more.Why this needs a new item kind
_AndroidNdkRedistributable(inbuild-tools/scripts/Ndk.targets) tagged NDK files with just two kinds:System—libc.so,libdl.so,liblog.so,libm.so,libz.so— shipped to every runtime.Toolchain—crtbegin_so.o,crtend_so.o,libc++_static.a,libc++abi.a,libclang_rt.builtins-*.a,libunwind.a— shipped to CoreCLR and NativeAOT, since both do native linking.NativeAOT still needs
crtbegin_so.o,crtend_so.oandlibclang_rt.builtins-*.a, so theToolchaingroup cannot just be dropped for NativeAOT.This adds a third kind,
CplusPlus, for the three C++ archives, and ships it only for CoreCLR. Both packaging sites are updated:src/native/native.targets— the localbin/<Config>/lib/packslayout.build-tools/create-packs/Microsoft.Android.Runtime.proj— the shipped NuGet packs.Size
Per ABI, removed from the NativeAOT runtime pack:
libc++_static.alibc++abi.alibunwind.aAcross
android-arm,android-arm64andandroid-x64that is roughly 55 MB of pack content. This does not change application size — that is the linker change above — but it shrinks what users restore.Testing
Deleted each pack directory and regenerated it via
_CopyToPackDirs, rather than checking a pack that could still contain stale files.NativeAOT (
android-arm64) — the three archives are gone, and everything NativeAOT links is still present:CoreCLR (
android-arm64) — all three are still shipped:Mono is unaffected — it only ever received the
Systemkind.