[native] Remove std::format from the CoreCLR host - #12534
Open
simonrozsival wants to merge 4 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR continues the CoreCLR-host libc++ reduction work by removing std::format usage from CoreCLR-reachable native host code paths and switching logging/abort call sites to the existing printf-annotated log_*f / Helpers::abort_applicationf APIs. This reduces libc++ symbol pull-in while also making format/argument mismatches compiler-checked.
Changes:
- Replaced
std::format-style logging withlog_debugf/log_infof/log_warnf/log_errorfacross CoreCLR host/runtime-base code and shared native headers. - Added
Helpers::abort_applicationfdefinition to the Mono shared lane and migrated CoreCLR abort call sites to theabort_applicationfoverload. - Adjusted a few debug-only type/name helpers and log strings to avoid
std::string_view/std::formatdependencies in CoreCLR code paths.
Show a summary per file
| File | Description |
|---|---|
| src/native/mono/shared/helpers.cc | Adds Helpers::abort_applicationf implementation for the Mono lane. |
| src/native/common/include/runtime-base/timing-internal.hh | Switches warning logs to log_warnf (printf-style). |
| src/native/common/include/runtime-base/system-loadlibrary-wrapper.hh | Converts debug logs to log_debugf. |
| src/native/common/include/runtime-base/mainthread-dso-loader.hh | Removes <format> usage and converts logs/abort to printf-style formatting. |
| src/native/common/include/runtime-base/dso-loader.hh | Converts loader tracing/error logs to printf-style formatting. |
| src/native/clr/runtime-base/android-system.cc | Converts environment/system-property diagnostics to printf-style logs. |
| src/native/clr/pinvoke-override/precompiled.cc | Removes <format> usage and converts abort/log statements to abort_applicationf/log_debugf. |
| src/native/clr/include/runtime-base/monodroid-dl.hh | Converts DSO cache and symbol-lookup logs to printf-style formatting. |
| src/native/clr/include/runtime-base/android-system.hh | Converts debug log to log_debugf. |
| src/native/clr/include/host/typemap.hh | Replaces debug label std::string_view constants with const char* and updates debug signatures. |
| src/native/clr/include/host/pinvoke-override-impl.hh | Converts p/invoke override diagnostics to printf-style logging. |
| src/native/clr/host/typemap.cc | Converts typemap debug/release tracing to printf-style logging and updates helper signatures. |
| src/native/clr/host/host.cc | Converts CoreCLR host logging/abort paths to printf-style formatting and adds <cinttypes> usage. |
| src/native/clr/host/fastdev-assemblies.cc | Converts FastDev logs and aborts to printf-style formatting. |
| src/native/clr/host/bridge-processing.cc | Converts GC-related logs to log_*f formatting (including NativeAOT-shared TU). |
| src/native/clr/host/assembly-store.cc | Removes std::format usage (including hex formatting) and converts cache/store logging and aborts to printf-style formatting. |
Review details
- Files reviewed: 16/16 changed files
- Comments generated: 2
- Review effort level: Lite
simonrozsival
force-pushed
the
dev/simonrozsival/clr-remove-std-format
branch
from
August 27, 2026 07:16
b4e9cd8 to
01a4b88
Compare
simonrozsival
changed the base branch from
main
to
dev/simonrozsival/nativeaot-drop-libcpp-packs
August 27, 2026 07:16
simonrozsival
force-pushed
the
dev/simonrozsival/clr-remove-std-format
branch
from
August 27, 2026 09:41
ef156f8 to
dfe614c
Compare
simonrozsival
force-pushed
the
dev/simonrozsival/clr-remove-std-format
branch
from
August 27, 2026 10:21
0aa1be2 to
05f8061
Compare
jonathanpeppers
force-pushed
the
dev/simonrozsival/clr-remove-std-format
branch
from
August 27, 2026 14:25
05f8061 to
0d6ad82
Compare
simonrozsival
force-pushed
the
dev/simonrozsival/clr-remove-std-format
branch
2 times, most recently
from
August 27, 2026 16:09
03be5c7 to
f48725d
Compare
This was referenced Aug 27, 2026
simonrozsival
force-pushed
the
dev/simonrozsival/clr-remove-std-format
branch
2 times, most recently
from
August 28, 2026 07:54
8daa480 to
828dfe6
Compare
simonrozsival
force-pushed
the
dev/simonrozsival/clr-remove-std-format
branch
from
August 28, 2026 08:47
828dfe6 to
c6a73e6
Compare
simonrozsival
force-pushed
the
dev/simonrozsival/clr-remove-std-format
branch
from
August 28, 2026 08:55
c6a73e6 to
5bae941
Compare
simonrozsival
force-pushed
the
dev/simonrozsival/clr-remove-std-format
branch
from
August 28, 2026 09:51
5bae941 to
71c8a79
Compare
simonrozsival
force-pushed
the
dev/simonrozsival/clr-remove-std-format
branch
from
August 28, 2026 10:29
71c8a79 to
018724f
Compare
Part of the "drop libc++" effort (#12533). `std::format` is by far the biggest single contributor of libc++ symbols in the native host: each translation unit that formats a value pulls in `std::to_chars` for `float`/`double`/`long double`, `std::locale`, `std::numpunct` and `std::use_facet`. This converts every `std::format`-based logging call site in the CoreCLR lane to the printf-style `log_debugf`/`log_infof`/`log_warnf`/`log_errorf` functions that already exist in `common/include/shared/log_functions.hh`. Unlike the `std::format` macros, those are annotated with `__attribute__((format(printf, ...)))`, so the compiler now type-checks every format specifier against its argument. `Helpers::abort_application (CAT, std::format (...))` call sites move to the previously unused `Helpers::abort_applicationf` overload. That overload was only defined in the CoreCLR lane, so an identical definition is added to `mono/shared/helpers.cc`. Also fixes a latent bug: `clr/host/bridge-processing.cc` is compiled into both the CoreCLR and the NativeAOT hosts, but NativeAOT's `log_types.hh` is a stub, so those calls fell through to the printf-style macros in `java-interop-logger.h` and logged a literal `{}` instead of the value. Measured on the arm64 Release CoreCLR archive (`libnet-android.release-static-release.a`), undefined libc++ symbols drop from 144 to 78 (-46%), and the `std::format` fingerprint is gone from every object file. Verified by building the CoreCLR, MonoVM and NativeAOT lanes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e
- Retry the main-thread DSO loader pipe write on EINTR and treat any incomplete write as a failure, rather than only checking for -1. - Drop the duplicated word in the "Trying to load loading shared JNI library" log message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e
With every CoreCLR-lane call site converted to the printf-style `log_*f` functions, the `std::format`-based macros and templates in `clr/include/shared/log_types.hh` have no users left. They generated no code (the templates were never instantiated), but they kept `<format>` and the whole `std::format` API reachable from every CoreCLR translation unit. The file is now identical to the NativeAOT stub. The `std::string_view` overload of `log_write` goes away as well. It was duplicated in the CoreCLR and MonoVM copies of `log_types.hh` and existed only so that call sites using a string literal wouldn't have to write `.data ()`. It had just four users, all in `timing-internal.cc`: three pass a literal and now rely on the plain `const char*` overload, and the fourth passes a `std::string_view` produced by `FastTiming::dump ()` and now uses `log_writef ()` with `%.*s`. That last one also removes a fragile invariant: the overload called `.data ()` and so required a NUL-terminated string, but `dump ()` builds its views from a buffer and an explicit length. They happen to be NUL-terminated today, and nothing enforced it. `%.*s` honours the length. `std::format` is now entirely absent from the `clr/`, `common/` and `nativeaot/` trees; only MonoVM still uses it. Verified by building the CoreCLR, MonoVM and NativeAOT lanes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e
The `log_time` helper in `FastTiming::dump ()` formatted into a fixed 256-byte buffer and silently clamped the length when the message didn't fit, so an oversized line would have been quietly cut short. `snprintf` already reports how much room the message needs, so use it: format into a stack buffer, and on overflow allocate a heap buffer of exactly the required size and format again. This matches the existing `format_message`/`build_message` pair used for the individual timing events, including freeing the buffer only when it differs from the stack one. The stack buffer is now `Constants::MAX_LOGCAT_MESSAGE_LENGTH`, the same size `dump ()` already uses for each event message, so the heap path is not expected to be taken in practice. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e
simonrozsival
force-pushed
the
dev/simonrozsival/clr-remove-std-format
branch
from
August 28, 2026 12:06
018724f to
e3baa71
Compare
Base automatically changed from
dev/simonrozsival/nativeaot-drop-libcpp-packs
to
dev/simonrozsival/nativeaot-remove-libcpp
August 28, 2026 12:42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #12533 (the CoreCLR follow-up to #12139). Stacked on top of #12524.
Why
std::formatis by far the biggest single contributor of libc++ symbols in the native host. Every translation unit that formats any value pulls instd::to_charsforfloat,doubleandlong double, plusstd::locale,std::numpunctandstd::use_facet— about 15 symbols per object file, whether or not the code ever formats a floating point number.What
Converts every
std::format-based logging call site reachable from the CoreCLR lane to the printf-stylelog_debugf/log_infof/log_warnf/log_errorffunctions that already exist incommon/include/shared/log_functions.hh.This is not just a mechanical swap — unlike the
std::formatmacros, these are annotated with__attribute__((format(printf, ...))), so the compiler now type-checks every format specifier against its argument.-Wformat/-Werror=format-securityare already enabled, and the build is clean.Helpers::abort_application (CAT, std::format (…))call sites move to the previously unusedHelpers::abort_applicationfoverload. That overload was only defined in the CoreCLR lane, so an identical definition is added tomono/shared/helpers.cc.Several of the converted files live in
common/rather thanclr/. Those are header-inlined into CoreCLR objects (dso-loader.hh,mainthread-dso-loader.hh,monodroid-dl.hh, …), so leaving them alone would have left thestd::formatpayload inhost.cc.oregardless.log_*fis defined in both the CoreCLR and MonoVM lanes, so converting them is safe for Mono too — verified by building it.Bonus: fixes a latent NativeAOT logging bug
clr/host/bridge-processing.ccis compiled into both the CoreCLR and the NativeAOT hosts. NativeAOT'slog_types.hhis a 5-line stub, so those calls fell through to the printf-style macros injava-interop-logger.h— which have noformat(printf)attribute, so nothing warned. In NativeAOT builds they printed a literal{}instead of the value. Both sites are now correct in both lanes.Results
Measured on the arm64 Release CoreCLR archive (
libnet-android.release-static-release.a), counting undefinedstd::__ndk1::*/operator new/operator delete/__cxa_*symbols. Baseline is this PR's base, #12524:std::formatfingerprintPer-object, before → after:
host.cc.oassembly-store.cc.otypemap.cc.obridge-processing.cc.oThe
std::formatfingerprint (co-occurrence ofto_chars<float/double/long double>withlocale/numpunct/use_facet) is now absent from every object file in the archive.Deleting the machinery
With the last call site gone, the
std::formatmacros and templates inclr/include/shared/log_types.hhhave no users left, so this PR deletes them too. That file is now identical to the existing NativeAOT stub. This generates no code change on its own — the templates were never instantiated, which is why the counts above already show thestd::formatfingerprint gone — but it meansstd::formatcan no longer be reintroduced into the CoreCLR host by accident.The
std::string_viewoverload oflog_writegoes with it. It was duplicated in the CoreCLR and MonoVM copies oflog_types.hhand existed only so call sites passing a string literal wouldn't have to write.data (). It had four users, all intiming-internal.cc: three pass a literal and now bind to the plainconst char*overload, and the fourth passes a view produced byFastTiming::dump ()and now useslog_writef ()with%.*s.That last one also retires a fragile invariant. The overload called
.data (), so it required a NUL-terminated string — butdump ()builds its views from a buffer plus an explicit length. They are all NUL-terminated today and nothing enforced it.%.*shonours the length instead.std::formatis now completely absent from theclr/,common/andnativeaot/trees:std::format#include <format>clr/common/(shared)nativeaot/mono/Verification
Built all three runtime lanes locally for
arm64-v8aRelease — CoreCLR, MonoVM and NativeAOT — with zero errors and zero new warnings. Building MonoVM caught a real link error (the missingabort_applicationfdefinition) that a CoreCLR-only build would have missed.Not in this PR
mono/-only call sites, and deleting thestd::formatmachinery frommono/shared/log_types.hh.std::string/std::function/std::mutexusage fromhost.ccandassembly-store.cc(the 76 remaining refs).CplusPlusArchiveentries fromNativeRuntimeComponents.cs— the last step, once the archives are genuinely unreferenced.