[native] Remove the libc++ dependencies from the CoreCLR host's startup path - #12570
Open
simonrozsival wants to merge 1 commit into
Open
Conversation
Part of #12533. This clears `host.cc` entirely, taking the CoreCLR host from 21 undefined libc++ references to 12 - all of which now live in `assembly-store.cc`. Three separate uses, two of which only compile in Release: * `scan_filesystem_for_assemblies_and_libraries` built the assembly store path with a `std::string`. It now uses `Util::join_paths`, added earlier in this stack, which keeps the path on the stack unless it does not fit and frees only when the returned pointer differs from the stack buffer. * `APP_CONTEXT_BASE_DIRECTORY` was held in a function-local `static std::string`. The static needs process lifetime because the value has to outlive `coreclr_initialize`, and a `std::string` has a non-trivial constructor, so it forced a guard variable - this was the only source of `__cxa_guard_acquire`/`__cxa_guard_release` in the file. A plain `char*` is constant-initialized, so the guard pair disappears. * The FastDev block used a `std::string` plus two `std::vector<const char*>`. It is inside `if constexpr (Constants::is_debug_build)`, so it contributes nothing to Release, but it has to go before libc++ can be dropped from Debug builds too. The property arrays are a fixed `prop_count + 1` entries, so they become `calloc`ed arrays. `FastDevAssemblies::build_tpa_list` consequently returns a `malloc`ed string the caller owns rather than filling a `std::string&` out-parameter. The TPA list has no useful upper bound - one absolute path per assembly in the override directory - so it is accumulated through a small growable buffer that doubles on demand. Allocation failure there falls back to the probe-only path rather than aborting, since FastDev is a debug convenience. `open_assembly` swaps `new uint8_t[]`/`delete[]` for `malloc`/`free`. That buffer is handed to CoreCLR and never freed (see the existing TODO), so this is not a behavioural change. It also gains a null check, which fixes a file descriptor leak on the allocation-failure path that `new` would have turned into a `std::bad_alloc` abort. `<string>` and `<vector>` are no longer included by `host.cc`, and `<string>` is gone from `fastdev-assemblies.hh`, which `host.cc` was picking it up from. ### Results Undefined libc++ references in the CoreCLR host, Release: | object | before | after | |---|---:|---:| | `host.cc.o` | 9 | **0** | | `assembly-store.cc.o` | 12 | 12 | | **total** | **21** | **12** | `libnet-android.release.so` goes from 523,224 to 520,112 bytes (-3,112). Debug is not built locally, so `host.cc` and `fastdev-assemblies.cc` were compiled standalone with `-DDEBUG -DDEBUG_BUILD` using the flags from `compile_commands.json`. Both compile clean and report 0 undefined libc++ references. ### Verification * CoreCLR, MonoVM and NativeAOT all build clean. * Debug-mode compilation of both affected files verified as described above, which matters because `fastdev-assemblies.cc` is only added to the build under `if(DEBUG_BUILD)` and is therefore never compiled in Release. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e
Contributor
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 2
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
src/native/clr/host/fastdev-assemblies.cc — ERROR: FastDevAssemblies::tpa_in_use is set to true on success but never reset on failure. If… |
|
src/native/clr/host/host.cc — WARNING: This Debug-only FastDev path aborts the process if allocating the extended runtime… |
What changed in this PR
This PR refactors the CoreCLR native host FastDev/initialization code to remove dependencies on libc++/STL types (e.g., std::string, std::vector) by switching to C allocation patterns and existing in-tree path helpers.
Changes:
- Change FastDev TPA list construction to return a
malloc-ownedchar*instead of filling astd::string. - Replace STL-based path/property list handling in
host.ccwithUtil::join_paths,malloc/calloc, and explicit lifetime management. - Update FastDev assembly loading buffer allocation to use
malloc/freeinstead ofnew[]/delete[].
| File | Description |
|---|---|
| src/native/clr/include/host/fastdev-assemblies.hh | Updates the FastDev API to return an owned char* TPA list (no STL types). |
| src/native/clr/host/host.cc | Reworks CoreCLR init property handling and path building to avoid STL and manage memory explicitly. |
| src/native/clr/host/fastdev-assemblies.cc | Implements a growable C buffer for TPA list building and switches FastDev file buffer allocation to malloc. |
Comment on lines
191
to
194
| const char *override_dir_path = AndroidSystem::get_primary_override_dir (); | ||
| if (!Util::dir_exists (override_dir_path)) { | ||
| return false; | ||
| return nullptr; | ||
| } |
Comment on lines
+427
to
444
| std::free (fastdev_prop_names); | ||
| std::free (fastdev_prop_values); | ||
| fastdev_prop_names = static_cast<const char**> (std::calloc (new_count, sizeof (const char*))); | ||
| fastdev_prop_values = static_cast<const char**> (std::calloc (new_count, sizeof (const char*))); | ||
| abort_unless ( | ||
| fastdev_prop_names != nullptr && fastdev_prop_values != nullptr, | ||
| "Unable to allocate memory for the FastDev runtime properties" | ||
| ); | ||
|
|
||
| memcpy (fastdev_prop_names, prop_names, old_count * sizeof (const char*)); | ||
| memcpy (fastdev_prop_values, prop_values, old_count * sizeof (const char*)); | ||
| fastdev_prop_names [old_count] = HOST_PROPERTY_TRUSTED_PLATFORM_ASSEMBLIES; | ||
| fastdev_prop_values [old_count] = fastdev_tpa_list; | ||
|
|
||
| prop_names = fastdev_prop_names; | ||
| prop_values = fastdev_prop_values; | ||
| prop_count = static_cast<int>(new_count); | ||
| } |
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 (drop
libc++from the CoreCLR host). Stacked on top of #12568.This clears
host.ccentirely. The CoreCLR host goes from 21 undefined libc++ references to 12, and every one that remains is now in a single file,assembly-store.cc.The three uses
1. The assembly store path
scan_filesystem_for_assemblies_and_librariesjoined the native library directory and the store filename with astd::string. It now usesUtil::join_paths, added earlier in this stack, which keeps the path in a stack buffer unless it does not fit and frees only when the returned pointer differs from that buffer.2.
APP_CONTEXT_BASE_DIRECTORYstatic std::string app_context_base_directory;The static is needed because the value has to outlive
coreclr_initialize— CoreCLR keeps the pointer. Butstd::stringhas a non-trivial constructor, so a function-local static of that type forces a guard variable, and this was the only source of__cxa_guard_acquire/__cxa_guard_releasein the file. A plainchar*is constant-initialized, so the guard pair disappears along with the string.3. The FastDev TPA list
This block is inside
if constexpr (Constants::is_debug_build), so it contributes nothing to the Release numbers below — but it has to go before libc++ can be dropped from Debug builds, which is why it is included here rather than left for later. The two property arrays are a fixedprop_count + 1entries, so they becomecalloced arrays.build_tpa_listnow returns the stringIt previously filled a
std::string&out-parameter and returnedbool; it now returns amalloced string the caller owns, ornullptr.The TPA list is the one place in this PR with no useful upper bound — it holds one absolute path per assembly in the override directory — so a fixed buffer is not appropriate. It is accumulated through a small growable buffer that doubles on demand. Allocation failure falls back to the probe-only path rather than aborting, since FastDev is a debug convenience and must not take the application down.
open_assemblynew uint8_t[]/delete[]becomemalloc/free. That buffer is handed to CoreCLR and never freed — see the existingTODOabove it about ownership — so this is not a behavioural change.It also gains a null check. That is a small bug fix:
new uint8_t[]would have thrownstd::bad_allocand aborted, whereas the new path logs, closes the file descriptor and returnsnullptr. The previous code would have leakedasm_fdhad it ever returned rather than aborted.Header cleanup
<string>and<vector>are no longer included byhost.cc, and<string>is gone fromfastdev-assemblies.hh— which is wherehost.ccwas picking it up from in the first place.Results
Undefined libc++ references in the CoreCLR host, Release:
host.cc.oassembly-store.cc.oThe nine removed from
host.cc.o:__cxa_guard_acquire,__cxa_guard_release,operator new,operator delete,std::string::append,std::string::push_back,std::string::__grow_by_and_replace,~basic_stringand__libcpp_verbose_abort.libnet-android.release.soVerification
fastdev-assemblies.ccis only added to the build underif(DEBUG_BUILD)(src/native/clr/host/CMakeLists.txt), so a Release build never compiles it and would not have caught a mistake in the FastDev work. There is no Debug CoreCLR build configured locally, so bothhost.ccandfastdev-assemblies.ccwere compiled standalone with-DDEBUG -DDEBUG_BUILDusing the exact flags fromcompile_commands.json. Both compile clean andllvm-nm --undefined-onlyreports 0 libc++ references for each.Not covered
Only
android-arm64was built locally; the other ABIs rely on CI. The FastDev path is Debug-only and was not exercised on a device as part of this change.