Fix JNI preload cache generation#10547
Merged
jonathanpeppers merged 1 commit intomainfrom Oct 17, 2025
Merged
Conversation
jonathanpeppers
approved these changes
Oct 17, 2025
jonathanpeppers
pushed a commit
that referenced
this pull request
Oct 17, 2025
Fixes: #10544 Context: 1a62af3 Context: cba39dc cba39dc introduced code to preload at startup native libraries which use JNI, to work around an issue in Android which prevents such libraries from being properly loaded at the later time of application life. Part of the workaround was support for updating handle of such a library in our shared library cache. Since every library has different entries in the cache (because we search the array using xxHash generated from various forms of the library name), after preloading it we had to update every entry in the DSO cache with the correct handle, so that the library is never loaded again. The code which generates the caches and indexes at application build time worked fine in my testing (using `dotnet build`) but it turns out that using `dotnet publish` instead breaks the code in a subtle, but nasty way. The issue is that the code which generated the index of shared libraries to preload reused an array which stored the indexes, while generating code for different RIDs. This resulted in the very first RID to process to contain valid indexes, the second one would **append** its own indexes to the preceding RID's data, the third RID would further append its own data etc. This would result in the following index code generated for the subsequend RIDs: ``` ; android-arm ; Indices into dso_cache[] of DSO libraries to preload because of JNI use @dso_jni_preloads_idx = dso_local local_unnamed_addr constant [4 x i32] [ i32 15, ; libSystem.Security.Cryptography.Native.Android.so i32 0, ; libSystem.Security.Cryptography.Native.Android i32 7, ; System.Security.Cryptography.Native.Android.so i32 8 ; System.Security.Cryptography.Native.Android ], align 4 ``` ``` ; android-arm64 ; Indices into dso_cache[] of DSO libraries to preload because of JNI use @dso_jni_preloads_idx = dso_local local_unnamed_addr constant [8 x i32] [ i32 15, ; libSystem.Security.Cryptography.Native.Android.so i32 0, ; libSystem.Security.Cryptography.Native.Android i32 7, ; System.Security.Cryptography.Native.Android.so i32 8, ; System.Security.Cryptography.Native.Android i32 10, ; Invalid index 4 i32 0, ; Invalid index 5 i32 1, ; Invalid index 6 i32 14 ; Invalid index 7 ], align 4 ``` ``` ; android-x64 ; Indices into dso_cache[] of DSO libraries to preload because of JNI use @dso_jni_preloads_idx = dso_local local_unnamed_addr constant [12 x i32] [ i32 15, ; libSystem.Security.Cryptography.Native.Android.so i32 0, ; libSystem.Security.Cryptography.Native.Android i32 7, ; System.Security.Cryptography.Native.Android.so i32 8, ; System.Security.Cryptography.Native.Android i32 10, ; Invalid index 4 i32 0, ; Invalid index 5 i32 1, ; Invalid index 6 i32 14, ; Invalid index 7 i32 10, ; Invalid index 8 i32 0, ; Invalid index 9 i32 1, ; Invalid index 10 i32 14 ; Invalid index 11 ], align 16 ``` In effect, when running on an arm64 device, we would try to load, and cache the handle, of an entirely different shared library, leading to further problems to find a requested symbol: ``` 10-17 11:25:18.062 27900 27900 F monodroid-assembly: Failed to load symbol 'AndroidCryptoNative_EcKeyCreateByKeyParameters' from shared library 'libSystem.Security.Cryptography.Native.Android' 10-17 11:25:18.110 1444 4298 I ActivityManager: Process com.companyname.test_jwt (pid 27900) has died: fg TOP 10-17 11:25:18.110 1444 1712 I libprocessgroup: Removed cgroup /sys/fs/cgroup/apps/uid_10361/pid_27900 10-17 11:25:18.111 913 913 I Zygote : Process 27900 exited cleanly (0) ``` Native code attempted to load the symbol from a library that happened to be stored at the index valid for `android-arm` but not for e.g. `android-arm64`, which was not `libSystem.Security.Cryptography.Native.Android`, leading to the above red herring error. Note: if **all** of RIDs enabled for the application are 32-bit or **all** of them are 64-bit, things would work even though the generated code would be technically incorrect. This is because all of the hashes and, thus, sort order of the `dso_cache` entries would be the same. Fix this by making sure that the index array is not shared between different RIDs when generating the DSO cache code.
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
Fixes: #10544
Context: 1a62af3
Context: cba39dc
cba39dc introduced code to preload at startup native libraries which use JNI,
to work around an issue in Android which prevents such libraries from being
properly loaded at the later time of application life.
Part of the workaround was support for updating handle of such a library in
our shared library cache. Since every library has different entries in the
cache (because we search the array using xxHash generated from various forms
of the library name), after preloading it we had to update every entry in the
DSO cache with the correct handle, so that the library is never loaded again.
The code which generates the caches and indexes at application build time worked
fine in my testing (using
dotnet build) but it turns out that usingdotnet publishinstead breaks the code in a subtle, but nasty way.
The issue is that the code which generated the index of shared libraries to preload
reused an array which stored the indexes, while generating code for different RIDs.
This resulted in the very first RID to process to contain valid indexes, the
second one would append its own indexes to the preceding RID's data, the third
RID would further append its own data etc.
This would result in the following index code generated for the subsequend RIDs:
In effect, when running on an arm64 device, we would try to load, and cache the
handle, of an entirely different shared library, leading to further problems to
find a requested symbol:
Native code attempted to load the symbol from a library that happened to be
stored at the index valid for
android-armbut not for e.g.android-arm64,which was not
libSystem.Security.Cryptography.Native.Android, leading tothe above red herring error.
Note: if all of RIDs enabled for the application are 32-bit or all
of them are 64-bit, things would work even though the generated code would
be technically incorrect. This is because all of the hashes and, thus, sort
order of the
dso_cacheentries would be the same.Fix this by making sure that the index array is not shared between different
RIDs when generating the DSO cache code.