Skip to content

Drop libc++ from Android NativeAOT linking#11311

Open
simonrozsival wants to merge 8 commits into
mainfrom
dev/simonrozsival/nativeaot-drop-libcpp
Open

Drop libc++ from Android NativeAOT linking#11311
simonrozsival wants to merge 8 commits into
mainfrom
dev/simonrozsival/nativeaot-drop-libcpp

Conversation

@simonrozsival
Copy link
Copy Markdown
Member

@simonrozsival simonrozsival commented May 8, 2026

Summary

This draft explores removing the Android NativeAOT link-time dependency on libc++.

The branch now:

  • removes the explicit NativeAOT libc++_static.a and libc++abi.a link inputs;
  • removes libc++ packaging from the Android native runtime component list;
  • keeps the final NativeAOT app link on the direct ld.lld/NativeLinker path from main;
  • reduces NativeAOT-reachable host code that pulled in C++ runtime/STL symbols;
  • adds NativeAOT-local C++ allocation/nothrow shims for the remaining runtime-pack allocation references;
  • fixes trimmable NativeAOT startup/linking by making generated per-assembly __ArrayMapRank* typemap anchors public, because the root typemap assembly references them across assembly boundaries.

Context

This is related to #9926 and the NDK r29 NativeAOT linking work.

The relevant background is that the NativeAOT Android runtime should avoid depending on libc++ instead of working around duplicate libunwind symbols or switching to shared libc++. Related references:

Size impact

Earlier measurements on this branch used samples/NativeAOT/NativeAOT.csproj built in Release with _AndroidTypeMapImplementation=trimmable.

Artifact Parent libc++ baseline No-libc++ build Difference
arm64 APK 1,575,849 B 1,296,320 B -279,529 B (-17.74%)
x64 APK 1,639,459 B 1,353,661 B -285,798 B (-17.43%)
arm64 libNativeAOT.so 3,481,880 B 2,727,936 B -753,944 B (-21.65%)
x64 libNativeAOT.so 3,404,896 B 2,664,048 B -740,848 B (-21.76%)

libNativeAOT.so is the per-ABI native shared library packaged in the APK, for example lib/arm64-v8a/libNativeAOT.so. It is not the whole Android app package; the APK also contains manifest, resources, Java stubs/classes, signatures, and packaging assets.

Validation

Validated locally during the latest iteration:

  • dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj --no-restore — 457 passed.

Previous branch validation:

  • rebuilt src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj;
  • rebuilt NativeAOT runtime archives for android-arm64 and android-x64;
  • built samples/NativeAOT/NativeAOT.csproj with _AndroidTypeMapImplementation=trimmable for android-arm64 and android-x64;
  • verified generated NativeAOT link response files contain no libc++/libc++abi inputs;
  • verified produced APKs contain no libc++ entries;
  • verified llvm-nm -u reports no undefined C++ runtime-looking symbols in the final libNativeAOT.so outputs;
  • smoke-tested the arm64 APK on an arm64 emulator: libNativeAOT.so loaded without libc++, MainApplication and MainActivity native callbacks ran, MainActivity.OnCreate() logged, and the process remained alive.

Current status

The PR is still draft/WIP. A CI follow-up fixed the NativeAOT trimmable typemap build failures caused by private __ArrayMapRank* anchors producing invalid NativeAOT metadata and unresolved link symbols.

The x64 APK builds and packages without libc++, but local x64 runtime validation still needs an x64 emulator host because Android emulator x86_64 system images are not accepted on Apple Silicon/aarch64 hosts.

@simonrozsival
Copy link
Copy Markdown
Member Author

Emulator validation update

I tested the signed NativeAOT + trimmable typemap sample APK on an arm64 emulator after rebuilding/overlaying the local validation packs.

What works:

  • The arm64 APK installs and launches on emulator-5554.
  • lib/arm64-v8a/libNativeAOT.so loads successfully with no libc++ packaged in the APK.
  • Startup reaches managed code: MainApplication and MainActivity native callbacks run, MainActivity.OnCreate() logs, and the process remains alive.

Important caveats:

  • This arm64 smoke currently requires trimmable NativeAOT startup fixes plus two validation-only workarounds:
    • pointing ILC TypeMapEntryAssembly at the app typemap (_NativeAOT.TypeMap) instead of the root _Microsoft.Android.TypeMaps, because scanning _Mono.Android.TypeMap currently fails on Android.Text.IInputType being present in the ref assembly but absent from the runtime assembly;
    • skipping UncaughtExceptionMarshaler setup for trimmable NativeAOT to avoid the IUncaughtExceptionHandler/JavaProxyThrowable startup path.
  • I do not consider those two workarounds shippable as-is; they need separate root-cause fixes before this can be more than a feasibility proof.
  • x64 builds and packages without libc++ (-nostdlib++ is in the link response and no libc++ entries are in the APK), but I could not run the x64 APK locally: the Android emulator rejects x86_64 system images on this Apple Silicon host (Avd's CPU Architecture 'x86_64' is not supported by the QEMU2 emulator on aarch64 host). x64 runtime behavior still needs validation on an x64 emulator host.

@simonrozsival simonrozsival changed the title [WIP] Explore dropping libc++ from NativeAOT linking [WIP] Drop libc++ from Android NativeAOT linking May 12, 2026
@simonrozsival
Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request

Copy link
Copy Markdown
Contributor

Copilot AI commented May 23, 2026

@copilot resolve the merge conflicts in this pull request

Resolved by merging origin/main into this branch and fixing all reported conflicts in commit 6e8b48a.

@simonrozsival
Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request

Copy link
Copy Markdown
Contributor

Copilot AI commented May 26, 2026

@copilot resolve the merge conflicts in this pull request

Resolved locally by merging origin/main and fixing the conflict hunks in commit 0199a84. Push from the agent is currently blocked by branch protection (GH013: Cannot update this protected ref), so this commit is ready but not yet on the remote branch.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 26, 2026

@copilot resolve the merge conflicts in this pull request

Done in 1c6205f. I merged latest origin/main into this branch and resolved all merge conflicts in the affected typemap generator/targets/tests files.

simonrozsival and others added 5 commits May 27, 2026 17:35
Remove the explicit NativeAOT final-link dependency on libc++/libc++abi and keep the Android NativeAOT link guarded with -nostdlib++.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Centralize gref log message formatting in OSBridge so call sites can pass printf-style arguments directly and formatting is skipped when gref logging is disabled.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep gref file guards at the call sites, allow literal single-argument gref log calls, and centralize printf-style log formatting helpers for CLR native logging.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/nativeaot-drop-libcpp branch from 1c6205f to 6a4feea Compare May 27, 2026 16:12
@simonrozsival simonrozsival changed the title [WIP] Drop libc++ from Android NativeAOT linking Drop libc++ from Android NativeAOT linking May 27, 2026
@simonrozsival simonrozsival marked this pull request as ready for review May 27, 2026 16:20
Copilot AI review requested due to automatic review settings May 27, 2026 16:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 (draft/WIP) explores removing the Android NativeAOT link-time dependency on libc++/libc++abi by adjusting MSBuild NativeAOT linking inputs and runtime component packaging, and by refactoring native runtime code paths to avoid pulling in C++ standard library symbols (including introducing minimal C++ allocation/nothrow shims).

Changes:

  • Remove explicit libc++/libc++abi static link inputs and runtime component entries for NativeAOT Android builds.
  • Add NativeAOT-local C++ runtime shims (operator new/delete + std::nothrow) and refactor multiple native components to avoid std::format/other STL usage.
  • Refactor CLR/native logging and GC bridge processing (including callback plumbing for bridge processing and temporary peer map implementation changes).

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeComponents.cs Drops libc++/libc++abi from the known runtime archive list.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets Removes libc++/libc++abi from NativeAOT link inputs; updates linker/stdlib commentary.
src/native/nativeaot/include/host/host.hh Adds NativeAOT host header shim include.
src/native/nativeaot/include/host/bridge-processing.hh Adapts NativeAOT bridge-processing to callback-based shared implementation.
src/native/nativeaot/host/internal-pinvoke-stubs.cc Simplifies abort path for unimplemented pinvokes to avoid extra std deps.
src/native/nativeaot/host/host.cc Removes std::format usage from NativeAOT host logging path.
src/native/nativeaot/host/cxx-shims.cc Adds minimal C++ allocation / nothrow shims to avoid libc++ dependency.
src/native/nativeaot/host/CMakeLists.txt Adds cxx-shims.cc to NativeAOT host build.
src/native/nativeaot/host/bridge-processing.cc Removes std::format usage and wires callbacks for NativeAOT bridge processing.
src/native/common/include/shared/helpers.hh Adds Helpers::abort_applicationf formatted abort helper.
src/native/common/include/shared/cpp-util.hh Removes C++ ranges/string allocations from diagnostics helpers and logging.
src/native/common/include/runtime-base/timing.hh Replaces std::format timing log formatting with snprintf.
src/native/common/include/runtime-base/timing-internal.hh Replaces std::format timing/internal warnings with snprintf-based logging.
src/native/common/include/runtime-base/strings.hh Replaces some std-container usage and updates formatting/logging to avoid STL pulls.
src/native/common/include/runtime-base/jni-wrappers.hh Replaces new[]/delete[] usage with malloc + placement-new where needed.
src/native/clr/shared/log_functions.cc Introduces vprintf-style log helpers (log_writev, log_*_fmt).
src/native/clr/shared/helpers.cc Adds implementation of Helpers::abort_applicationf; switches some fatal logging to printf-style.
src/native/clr/runtime-base/util.cc Updates logging calls to new printf-style logging helpers.
src/native/clr/runtime-base/logger.cc Refactors gref/lref log path storage away from std::string; updates open/log messages.
src/native/clr/runtime-base/android-system-shared.cc Replaces some new[]/delete[] allocations and formatting with malloc/snprintf and fmt logging.
src/native/clr/include/shared/log_types.hh Adds printf-style logging APIs and adjusts macro behavior for XA_HOST_NATIVEAOT builds.
src/native/clr/include/runtime-base/util.hh Switches to formatted abort/log helpers; adds mmap failure formatted aborts and info logging.
src/native/clr/include/runtime-base/android-system.hh Changes override-dir storage away from std::string for NativeAOT compatibility; API now returns const char*.
src/native/clr/include/host/os-bridge.hh Adds formatted gref logging overloads and refactors internal logging helpers signatures.
src/native/clr/include/host/host-environment.hh Updates debug/warn logging calls to avoid std::format and skip work when disabled.
src/native/clr/include/host/gc-bridge.hh Replaces std::thread/semaphore with pthread/sem_t for compatibility and reduced STL usage.
src/native/clr/include/host/bridge-processing.hh Removes now-unneeded CoreCLR no-op overrides after shared callback refactor.
src/native/clr/include/host/bridge-processing-shared.hh Introduces callback plumbing and switches temporary peer map to robin_map.
src/native/clr/host/os-bridge.cc Refactors stack-trace/gref logging to avoid std::format and centralize formatted logging.
src/native/clr/host/internal-pinvokes-shared.cc Updates managed->native log forwarding to respect category enablement and use log_write.
src/native/clr/host/host-shared.cc Replaces std::format/string_view literal usage in error logging with printf-style.
src/native/clr/host/gc-bridge.cc Migrates to sem_wait/atomic builtins and updates logging formatting.
src/native/clr/host/fastdev-assemblies.cc Updates override-dir handling to match AndroidSystem API change to const char*.
src/native/clr/host/bridge-processing.cc Implements callback-based bridge processing shared logic and temporary peer lifecycle handling.
Comments suppressed due to low confidence (1)

src/native/clr/host/os-bridge.cc:203

  • OSBridge::log_it() logs the main line to logcat unconditionally via log_write(...), even when logcat_enabled is false. This makes gref/lref logging emit to logcat even in the “file-only” configuration, and the logcat_enabled parameter only affects stack traces. Consider only calling log_write when logcat_enabled is true (and keep file writes independent), so behavior matches Logger::{gref,lref}_to_logcat().
void OSBridge::log_it (LogCategories category, const char *line, FILE *to, const char *const from, bool logcat_enabled) noexcept
{
	log_write (category, LogLevel::Info, line);

	// We skip logcat here when logging to file is enabled because _write_stack_trace will output to logcat as well, if enabled
	if (to == nullptr) {
		if (logcat_enabled) {
			_write_stack_trace (nullptr, from, category);
		}

Comment thread src/native/clr/include/runtime-base/util.hh
Comment thread src/native/clr/include/runtime-base/util.hh
Comment thread src/native/clr/include/runtime-base/util.hh Outdated
simonrozsival and others added 3 commits May 27, 2026 20:58
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add the missing standard and logging declarations needed by the native runtime headers when they are compiled directly by the Android runtime ninja builds.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Avoid ambiguous gref log overload resolution, insert temporary GC bridge peers into robin_map without mutating through the iterator proxy, and include robin_map headers in the NativeAOT host build.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

3 participants