[CoreCLR] Stop serving BCL p/invokes from the precompiled override#11537
Open
simonrozsival wants to merge 3 commits into
Open
[CoreCLR] Stop serving BCL p/invokes from the precompiled override#11537simonrozsival wants to merge 3 commits into
simonrozsival wants to merge 3 commits into
Conversation
The p/invoke override's precompiled path (used by the default separate-`.so` runtime layout) served the .NET BCL native libraries — `libSystem.Native`, `libSystem.Globalization.Native`, `libSystem.Security.Cryptography.Native.Android` and `libSystem.IO.Compression.Native` — from a hand-maintained static table (`dotnet_pinvokes`). That table has to be regenerated by hand whenever the runtime adds or renames a p/invoke, and it silently drifts from the runtime's real exports. A missing entry aborts the application at startup, e.g. #11530: Abort message: 'Missing pinvoke SystemNative_IsAtomicNonInheritablePipeCreationSupported@libSystem.Native' In the default layout these libraries are shipped as standalone `.so` files in the APK, so CoreCLR can resolve their entry points itself. We now return `nullptr` for them and let CoreCLR's own DllImport resolution handle them, removing the whole class of drift bugs. Measured on a physical Samsung SM-A165F (CoreCLR Release, arm64, `dotnet new maui --sample-content`, 25 cold starts each, back-to-back): override ON (static table): median 1930 ms / p90 1970 ms override OFF (this change): median 1922 ms / p90 1966 ms The difference (+8 ms, in favour of removing it) is within run-to-run noise: the override is invoked ~70 times during the entire cold start, each symbol exactly once, so the static table is a one-time path with no measurable startup benefit. The host shared library also shrinks slightly (the unused `dotnet_pinvokes` table is dead-stripped). The change is scoped to the precompiled path: * The internal-symbols branch (`xa-internal-api`, `java-interop`, `liblog`) is unchanged — those symbols are linked into the host itself and have no separate `.so` to resolve. * `handle_other_pinvoke_request` is unchanged — it resolves arbitrary app and third-party native libraries through dotnet/android's own loader (`MonodroidDl::monodroid_dlopen`, which loads APK-embedded DSOs), carries no static table, and is therefore not subject to drift. * The unified-DSO layout uses `dynamic.cc` (the generated `find_pinvoke` table), which is independent of `dotnet_pinvokes` and unaffected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the CoreCLR precompiled p/invoke override so it no longer serves BCL native-library p/invokes (libSystem.Native, libSystem.Globalization.Native, libSystem.Security.Cryptography.Native.Android, libSystem.IO.Compression.Native) from the hand-maintained dotnet_pinvokes static table, and instead returns nullptr to let CoreCLR’s default DllImport resolution handle them. This aims to eliminate “table drift” failures like #11530 where missing entries can abort the app at startup.
Changes:
- Remove the BCL-library fast path that looked up p/invokes in the generated
dotnet_pinvokestable and loaded symbols via cached DSO handles. - For the four BCL libraries, return
nullptrso CoreCLR resolves their symbols through its own resolver in the separate-.solayout. - Keep the existing “other libraries” path (
handle_other_pinvoke_request) for app/third-party DSOs that requireMonodroidDlbehaviors (APK-embedded DSOs, name normalization, runtime lib directories).
The previous commit stopped serving the .NET BCL native libraries from the precompiled override's static `dotnet_pinvokes` table, returning `nullptr` so CoreCLR resolves them itself. That made the generated table dead code (it was already dead-stripped from the host binary). Remove the CoreCLR generator's `dotnet_pinvoke_names` list and the `dotnet_pinvokes`/`dotnet_pinvokes_count` emission, and regenerate `src/native/clr/pinvoke-override/pinvoke-tables.include`. This deletes the hand-maintained symbol list that drifted from the runtime's real exports (the root cause of #11530), so it can no longer go stale. The four BCL library-name hashes are intentionally kept: the precompiled override still uses them to identify those libraries and return `nullptr`. The internal-symbols table and all library-name hashes are unchanged. This only touches the CoreCLR generator; the MonoVM generator and its generated table are untouched and regenerate byte-for-byte identically. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
Author
|
/azp run Xamarin.Android-PR |
|
No pipelines are associated with this pull request. |
Member
Author
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
`Ping.Send()` calls `SystemNative_IsAtomicNonInheritablePipeCreationSupported` in `libSystem.Native`, which was missing from the precompiled override's static BCL p/invoke table and aborted the app at startup (#11530). Add a device test exercising `Ping.Send("127.0.0.1")` so this class of regression is caught regardless of how the BCL p/invokes are resolved. Authored-by: Jonathan Peppers <jonathan.peppers@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
Author
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
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.
Summary
The p/invoke override's precompiled path (used by the default separate-
.soruntime layout) serves the .NET BCL native libraries —libSystem.Native,libSystem.Globalization.Native,libSystem.Security.Cryptography.Native.Android,libSystem.IO.Compression.Native— from a hand-maintained static table (dotnet_pinvokes, generated from hardcoded name lists). That table must be regenerated by hand whenever the runtime adds/renames a p/invoke and silently drifts from the runtime's real exports. A missing entry aborts the app at startup — this is #11530:In the default layout these libraries ship as standalone
.sofiles in the APK, so CoreCLR can resolve their entry points itself. This PR returnsnullptrfor them and lets CoreCLR's ownDllImportresolution handle them, removing the whole class of drift bugs. The now-deaddotnet_pinvokessymbol list is also deleted from the CoreCLR generator so it can never go stale again.Commits
monodroid_pinvoke_overridereturnsnullptrfor the four BCL libraries instead of looking them up indotnet_pinvokes(and aborting on a miss).dotnet_pinvokesgeneration — deletes thedotnet_pinvoke_nameslist and thedotnet_pinvokes/dotnet_pinvokes_countemission from the CoreCLR generator, and regeneratespinvoke-tables.include. The four BCL library-name hashes are kept (the override still uses them to identify those libraries and returnnullptr); the internal-symbols table is unchanged.PingTestregression guard — a device test exercisingPing.Send("127.0.0.1")(the exact call that hit the missingSystemNative_IsAtomicNonInheritablePipeCreationSupportedsymbol in CoreCLR Ping causes SIGABRT on Android #11530), so this class of regression is caught regardless of how BCL p/invokes are resolved. Ported from Sync SystemNative pinvoke tables with dotnet/runtime #11531.Why this is safe (and why it doesn't cost us startup time)
Measured on a physical Samsung SM-A165F (CoreCLR Release, arm64,
dotnet new maui --sample-content, 25 cold starts each, back-to-back same session):The +8 ms is well within run-to-run noise. Direct instrumentation explains why: the override is invoked ~70 times during the entire cold start, each native symbol exactly once (CoreCLR caches the resolved pointer per call site). The static table is a one-time path, not a hot one, so it provides no measurable startup benefit. The host shared library also shrinks slightly (1,368,256 → 1,325,936 bytes) as the now-unused
dotnet_pinvokestable is dead-stripped.Correctness check: the override-off build boots and renders identically. During startup the 43 BCL p/invokes that the table used to serve (
libSystem.Native×29,libSystem.Globalization.Native×13) were resolved by CoreCLR's own resolver — noMissing pinvoke, nocannot locate symbol, nodlopen failed.Scope — what is intentionally not changed
xa-internal-api,java-interop,liblog): unchanged. These are linked into the host itself and have no separate.so, so the override must keep serving them.handle_other_pinvoke_request(e_sqlite3, app/third-party native libs): unchanged. It resolves arbitrary libraries through dotnet/android's own loader (MonodroidDl::monodroid_dlopen, which loads APK-embedded DSOs and normalizes[DllImport("log")]-style names) — behavior CoreCLR's default resolver does not replicate. It carries no static table, so it does not drift.dynamic.cc, the generatedfind_pinvoketable): independent ofdotnet_pinvokesand unaffected. There the BCL symbols are hidden in a single DSO and must still be resolved by the override.Validation status / asking reviewers
-Werror).pinvoke-tables.includeregenerated viabuild-tools/scripts/generate-pinvoke-tables.sh; host relinks cleanly.PingTest(device regression test). Not run locally in this change; CI device tests will execute it.libSystem.Security.Cryptography.Native.Android(TLS/X509) andlibSystem.IO.Compression.Native(Deflate/Zip). They are listed as CoreCLR known runtime native libs and load the same way aslibSystem.Native, but a crypto/compression device smoke test would be good to add.extractNativeLibs=false/ fast-deploy layouts.armeabi-v7a).Relationship to #11531
#11531 fixes #11530 by re-syncing the
SystemNativestatic table (CLR and MonoVM) and addsPingTest. This PR takes a different, more durable approach for CoreCLR: it removes the static BCL table entirely so CoreCLR resolves those symbols itself, so the CLR table can't drift again. It does not touch MonoVM — MonoVM still needs #11531's sync (or its own follow-up). ThePingTesthere is ported from #11531 so #11530 keeps a regression guard either way.Fixes #11530.