Skip to content

[native] Replace the bundled properties map with a linked list - #12551

Open
simonrozsival wants to merge 1 commit into
dev/simonrozsival/clr-timing-free-listfrom
dev/simonrozsival/clr-bundled-properties
Open

[native] Replace the bundled properties map with a linked list#12551
simonrozsival wants to merge 1 commit into
dev/simonrozsival/clr-timing-free-listfrom
dev/simonrozsival/clr-bundled-properties

Conversation

@simonrozsival

Copy link
Copy Markdown
Member

Part of the drop-libc++ work for CoreCLR.

AndroidSystem::bundled_properties was an std::unordered_map<std::string, std::string> and the only user of <unordered_map> in the CoreCLR host.

A sorted static array + binary search isn't an option here: bundled properties are read from the environment override files at run time, so the set isn't known at build time. But the map isn't buying us anything either — the entries are added once during startup, looked up a handful of times, and there are only a few of them.

So this uses the same malloc'd singly linked list that MonoVM has always used for exactly this purpose (BundledProperty), keeping the two runtimes consistent:

struct BundledProperty
{
	BundledProperty *next;
	char            *name;
	char            *value;
	size_t           value_len;
};

The name is allocated together with the node (one allocation covers both); the value is allocated separately so that setting the same property twice can replace it. Allocation failure aborts, as elsewhere in this stack.

This also fixes a real bug

The lookup returned the map key instead of the value:

value_len = prop_iter->second.length ();   // the value's length
return prop_iter->first.c_str ();          // ...but the *name*

So in Debug builds every bundled property resolved to its own name, reported with the value's length. When the value is longer than the name that's an over-read past the end of the name's buffer. This is pre-existing on main, and falls out of the rewrite for free.

Results

This code is #if defined (DEBUG) only, so Release builds are completely unaffected — I verified the undefined-libc++-symbol list per object file is byte-for-byte identical before and after (58 both ways).

The win is in Debug builds. Compiling android-system.cc with the real build flags plus -DDEBUG:

before after
undefined libc++ symbols 13 12
object size 83,400 77,984 (−5,416)

The symbol that disappears is std::__ndk1::__next_prime() — the bucket-count helper that is unordered_map's only out-of-line dependency.

Testing

  • CoreCLR and MonoVM Release builds are clean.
  • The Debug path was compile-checked with the real build flags plus -DDEBUG, and I confirmed via llvm-nm that the new code is genuinely being compiled (find_bundled_property and add_system_property are present in the object) rather than silently skipped.

Copilot AI lite review requested due to automatic review settings August 27, 2026 21:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

Review tier: Lite
Findings: 1 Low severity

New issues introduced by this change (1)
Severity Finding
Low severity src/​native/​clr/​runtime-base/​android-system.cc — 💡 suggestion (performance/alloc churn)add_system_property() always allocates a new value
What changed in this PR

This PR updates the CoreCLR host’s debug-only bundled system property storage to remove the last std::unordered_map usage (and its <unordered_map> dependency), aligning the implementation with MonoVM’s long-standing BundledProperty singly linked list approach. It also corrects a pre-existing debug-only lookup bug where the property name was returned instead of the value, which could cause buffer over-reads.

Changes:

  • Replace AndroidSystem::bundled_properties from std::unordered_map<std::string, std::string> to a malloc’d BundledProperty singly linked list (DEBUG-only).
  • Add AndroidSystem::find_bundled_property() helper for linear lookup in the linked list.
  • Fix debug-only property lookup to return the correct value pointer and length.
File Description
src/​native/​clr/​runtime-base/​android-system.cc Implements linked-list storage and correct lookup of bundled properties in DEBUG builds.
src/​native/​clr/​include/​runtime-base/​android-system.hh Removes <unordered_map> and introduces the DEBUG-only BundledProperty definition + storage pointer.

Comment thread src/native/clr/runtime-base/android-system.cc
@simonrozsival simonrozsival added the drop-libcpp Work to remove the libc++ dependency from Android NativeAOT label Aug 28, 2026
@simonrozsival
simonrozsival force-pushed the dev/simonrozsival/clr-bundled-properties branch from ae48813 to c5ad8c6 Compare August 28, 2026 06:10
@simonrozsival
simonrozsival force-pushed the dev/simonrozsival/clr-bundled-properties branch 2 times, most recently from 5bcebb9 to ecc2418 Compare August 28, 2026 07:54
@simonrozsival
simonrozsival force-pushed the dev/simonrozsival/clr-bundled-properties branch from ecc2418 to 1f23c98 Compare August 28, 2026 08:47
@simonrozsival
simonrozsival force-pushed the dev/simonrozsival/clr-bundled-properties branch from 1f23c98 to a3d78dc Compare August 28, 2026 08:56
@simonrozsival
simonrozsival force-pushed the dev/simonrozsival/clr-bundled-properties branch from a3d78dc to a1bee56 Compare August 28, 2026 09:51
@simonrozsival
simonrozsival force-pushed the dev/simonrozsival/clr-bundled-properties branch from a1bee56 to d202466 Compare August 28, 2026 10:29
`AndroidSystem::bundled_properties` was an
`std::unordered_map<std::string, std::string>`, which is the only user of
`<unordered_map>` in the CoreCLR host.  The properties are read from the
environment override files at run time, so the set is not known at build time
and cannot be a static sorted array - but the map buys us nothing either: the
entries are added once at startup, looked up a handful of times and there are
only a few of them.

Use the same malloc'd singly linked list MonoVM has always used for this
(`BundledProperty`), with the name allocated together with the node and the
value allocated separately so that setting a property twice can replace it.

This also fixes a real bug.  The lookup returned the map key rather than the
value:

    value_len = prop_iter->second.length ();
    return prop_iter->first.c_str ();

so every bundled property resolved to its own *name*, reported with the
*value's* length - which over-reads past the end of the name whenever the value
is longer than the name.

Release builds are unaffected, this code is `#if defined (DEBUG)` only.  In a
Debug build of android-system.cc it removes the last reference to
`std::__next_prime()` (12 undefined libc++ symbols instead of 13) and shrinks
the object file from 83,400 to 77,984 bytes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0a35a0db-502d-48c0-8468-e73b5dd0ab2e
@simonrozsival
simonrozsival force-pushed the dev/simonrozsival/clr-bundled-properties branch from d202466 to a0ae195 Compare August 28, 2026 12:06
Base automatically changed from dev/simonrozsival/clr-drop-chrono to dev/simonrozsival/clr-timing-free-list August 28, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

drop-libcpp Work to remove the libc++ dependency from Android NativeAOT

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants