Skip to content

Commit 064f23f

Browse files
grendellojonathanpeppers
authored andcommitted
Use Android shared library loader for JNI libraries (#10376)
Fixes: #10324 Fixes: #7616 Context: https://docs.oracle.com/en/java/javase/17/docs/specs/jni/invocation.html#library-and-version-management Context: #7616 (comment) Java language/virtual machine supports implementing portions of the API in a language (like C, C++ or Rust) which compiles into native binary code instead of being JIT-ed or interpreted at run time. Such implementations are contained in native shared libraries which have to conform to a set of rules laid out in the JNI (Java Native Interface) documentation. Part of the specification describes a function (`JNI_OnLoad`) which may be present in the shared library and, if it's there, it will be called by the JavaVM when the library is loaded. For this to happen, however, the load must be initiated by calling the `System.loadLibrary(string)` Java method. This method will find the named shared library, load it using an OS-specific mechanism and then call all the exported functions described in the JNI specification, if they are present. Until this PR, .NET for Android (and Xamarin.Android before it) were loading all the shared libraries in the same way, via `dlopen(2)` instead of by using `System.loadLibrary(string)` which resulted in some of those libraries not being initialized properly. This PR fixes the issue by identifying shared libraries which contain the `JNI_OnLoad` function and loading them at run time by calling `System.loadLibrary(string)` instead of just `dlopen(2)`. This makes it certain that the libraries are properly initialized. However, Android environment is quite complex and not everything in the PR is implement the way it was intended to. The problem lies in the ability of `System.loadLibrary(string)` to find the actual shared library file. The file can be found in a number of locations, among them two application-specific ones: * The APK archive's `lib/{ABI}/` directory, when shared libraries are not extracted from the archive on installation. * The application-specific library location on the file system, when shared libraries are extracted from the archive on installation. In either case, the location is not known beforehand as each time the application is installed, it will get a different path where both its archive and extracted files are located. This requires the Java runtime to provide that information to the application in some way. The way ART (the Java runtime on Android) does it is via class loaders, which are special classes that know how to find and load Java components as well as the native libraries. `System.loadLibrary(string)` uses that information to locate the .so files with JavaVM extensions. The mechanism described above works well as long as the `System.loadLibrary` call is made from a thread that's fully attached to the Java VM, which is to say that the VM environment sets up the class loaders correctly, so that they contain information about the application-specific shared library locations. With the correctly configured class loaders, we can see a similar message when loading the shared library with `System.loadLibrary`: ``` 08-13 12:06:48.269 11989 11989 D nativeloader: Load /data/app/~~Xy-UIVle34c_VksRd2_LEg==/com.xamarin.XAPerfTest.net10-GbhwYcau77FAjV_FW1uZwg==/split_config.arm64_v8a.apk!/lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so using class loader ns clns-9 (caller=/data/app/~~Xy-UIVle34c_VksRd2_LEg==/com.xamarin.XAPerfTest.net10-GbhwYcau77FAjV_FW1uZwg==/base.apk): ok ``` The bits to note above are the `class loader ns clns-9` information - it's a dynamically configured loader that is fully informed on application-specific shared library locations and the name of the caller (cryptic-looking path ending with `base.apk`). This loader is used during, for instance, our native runtime configuration - when it is being intialized from our (Java) package manager at application startup. However, the problem is that the above class loader is no longer around when we call `System.loadLibrary` on a thread that's not fully attached to the Java VM: ``` 08-13 12:16:10.659 12472 12472 D nativeloader: Load libSystem.Security.Cryptography.Native.Android.so using system ns (caller=/system/framework/framework.jar!classes3.dex): dlopen failed: library "libSystem.Security.Cryptography.Native.Android.so" not found ``` In this case note that both the class loader (named here just `system ns`) and the caller are generic, they have no knowledge of the application-specific shared library locations. This observation lead to the idea of using the native looper (`ALooper`) interface to post the shared library load request to the main thread from native code, and then call `System.loadLibrary` on it. This assumed that the main thread, which originally had the application-specific class loader, would still be around and able to handle the load properly. Unfortunately, this doesn't appear to be the case. Despite us attaching to the Java thread with JNI API (`AttachCurrentThread`), the application-specific class loader isn't there. This was discovered a few years ago already (see the link to issue #7616 comment) but we haven't been able to find a way to fully attach the thread to the Java VM so that the class loaders are correctly set up. This, unfortunately, leads us to our only remaining option - preloading of the JNI libraries at application started, while we're still in the properly configured main thread. This PR implements just that, but it also implements and uses code to load JNI shared libraries on-demand from any thread by posting the request to the main thread, as it may just happen that a request to load a shared library will happen on a separate managed thread during startup and we might get lucky to run the loading code on a still-attached main thread. In the future more work is required (much more) to investigate the internals of the ART runtime in order to try to find a way to fully attach managed threads so that class loaders are set up properly.
1 parent 4ce12a7 commit 064f23f

30 files changed

+844
-263
lines changed

src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ sealed class DSOCacheEntry
6161
[NativeAssembler (NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)]
6262
public ulong real_name_hash;
6363
public bool ignore;
64+
public bool is_jni_library;
6465

6566
[NativeAssembler (UsesDataProvider = true)]
6667
public string? name;
@@ -156,14 +157,19 @@ sealed class XamarinAndroidBundledAssembly
156157
}
157158
#pragma warning restore CS0649
158159

160+
sealed class DsoCacheState
161+
{
162+
public List<StructureInstance<DSOCacheEntry>> DsoCache = [];
163+
public List<DSOCacheEntry> JniPreloadDSOs = [];
164+
public List<StructureInstance<DSOCacheEntry>> AotDsoCache = [];
165+
}
166+
159167
// Keep in sync with FORMAT_TAG in src/monodroid/jni/xamarin-app.hh
160168
const ulong FORMAT_TAG = 0x00025E6972616D58; // 'Xmari^XY' where XY is the format version
161169

162170
SortedDictionary <string, string>? environmentVariables;
163171
SortedDictionary <string, string>? systemProperties;
164172
StructureInstance? application_config;
165-
List<StructureInstance<DSOCacheEntry>>? dsoCache;
166-
List<StructureInstance<DSOCacheEntry>>? aotDsoCache;
167173
List<StructureInstance<XamarinAndroidBundledAssembly>>? xamarinAndroidBundledAssemblies;
168174

169175
StructureInfo? applicationConfigStructureInfo;
@@ -229,7 +235,7 @@ protected override void Construct (LlvmIrModule module)
229235
};
230236
module.Add (sysProps, stringGroupName: "sysprop", stringGroupComment: " System properties name:value pairs");
231237

232-
(dsoCache, aotDsoCache) = InitDSOCache ();
238+
DsoCacheState dsoState = InitDSOCache ();
233239
var app_cfg = new ApplicationConfig {
234240
uses_mono_llvm = UsesMonoLLVM,
235241
uses_mono_aot = UsesMonoAOT,
@@ -249,8 +255,8 @@ protected override void Construct (LlvmIrModule module)
249255
number_of_assemblies_in_apk = (uint)NumberOfAssembliesInApk,
250256
number_of_shared_libraries = (uint)NativeLibraries.Count,
251257
bundled_assembly_name_width = (uint)BundledAssemblyNameWidth,
252-
number_of_dso_cache_entries = (uint)dsoCache.Count,
253-
number_of_aot_cache_entries = (uint)aotDsoCache.Count,
258+
number_of_dso_cache_entries = (uint)dsoState.DsoCache.Count,
259+
number_of_aot_cache_entries = (uint)dsoState.AotDsoCache.Count,
254260
android_runtime_jnienv_class_token = (uint)AndroidRuntimeJNIEnvToken,
255261
jnienv_initialize_method_token = (uint)JNIEnvInitializeToken,
256262
jnienv_registerjninatives_method_token = (uint)JNIEnvRegisterJniNativesToken,
@@ -262,13 +268,23 @@ protected override void Construct (LlvmIrModule module)
262268
application_config = new StructureInstance<ApplicationConfig> (applicationConfigStructureInfo, app_cfg);
263269
module.AddGlobalVariable ("application_config", application_config);
264270

265-
var dso_cache = new LlvmIrGlobalVariable (dsoCache, "dso_cache", LlvmIrVariableOptions.GlobalWritable) {
271+
var dso_cache = new LlvmIrGlobalVariable (dsoState.DsoCache, "dso_cache", LlvmIrVariableOptions.GlobalWritable) {
266272
Comment = " DSO cache entries",
267273
BeforeWriteCallback = HashAndSortDSOCache,
268274
};
269275
module.Add (dso_cache);
270276

271-
var aot_dso_cache = new LlvmIrGlobalVariable (aotDsoCache, "aot_dso_cache", LlvmIrVariableOptions.GlobalWritable) {
277+
// This variable MUST be written after `dso_cache` since it relies on sorting performed by HashAndSortDSOCache
278+
var dso_jni_preloads_idx = new LlvmIrGlobalVariable (new List<uint> (), "dso_jni_preloads_idx", LlvmIrVariableOptions.GlobalConstant) {
279+
Comment = " Indices into dso_cache[] of DSO libraries to preload because of JNI use",
280+
ArrayItemCount = (uint)dsoState.JniPreloadDSOs.Count,
281+
BeforeWriteCallback = PopulatePreloadIndices,
282+
BeforeWriteCallbackCallerState = dsoState,
283+
};
284+
module.AddGlobalVariable ("dso_jni_preloads_idx_count", dso_jni_preloads_idx.ArrayItemCount);
285+
module.Add (dso_jni_preloads_idx);
286+
287+
var aot_dso_cache = new LlvmIrGlobalVariable (dsoState.AotDsoCache, "aot_dso_cache", LlvmIrVariableOptions.GlobalWritable) {
272288
Comment = " AOT DSO cache entries",
273289
BeforeWriteCallback = HashAndSortDSOCache,
274290
};
@@ -330,6 +346,36 @@ void AddAssemblyStores (LlvmIrModule module)
330346
module.Add (assembly_store);
331347
}
332348

349+
void PopulatePreloadIndices (LlvmIrVariable variable, LlvmIrModuleTarget target, object? state)
350+
{
351+
var indices = variable.Value as List<uint>;
352+
if (indices == null) {
353+
throw new InvalidOperationException ($"Internal error: DSO preload indices list instance not present.");
354+
}
355+
356+
var dsoState = state as DsoCacheState;
357+
if (dsoState == null) {
358+
throw new InvalidOperationException ($"Internal error: DSO state not present.");
359+
}
360+
361+
foreach (DSOCacheEntry preload in dsoState.JniPreloadDSOs) {
362+
int dsoIdx = dsoState.DsoCache.FindIndex (entry => {
363+
if (entry.Instance == null) {
364+
return false;
365+
}
366+
367+
return entry.Instance.hash == preload.hash && entry.Instance.real_name_hash == preload.real_name_hash;
368+
});
369+
370+
if (dsoIdx == -1) {
371+
throw new InvalidOperationException ($"Internal error: DSO entry in JNI preload list not found in the DSO cache list.");
372+
}
373+
374+
indices.Add ((uint)dsoIdx);
375+
}
376+
indices.Sort ();
377+
}
378+
333379
void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, object? state)
334380
{
335381
var cache = variable.Value as List<StructureInstance<DSOCacheEntry>>;
@@ -358,9 +404,9 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
358404
});
359405
}
360406

361-
(List<StructureInstance<DSOCacheEntry>> dsoCache, List<StructureInstance<DSOCacheEntry>> aotDsoCache) InitDSOCache ()
407+
DsoCacheState InitDSOCache ()
362408
{
363-
var dsos = new List<(string name, string nameLabel, bool ignore)> ();
409+
var dsos = new List<(string name, string nameLabel, bool ignore, ITaskItem item)> ();
364410
var nameCache = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
365411

366412
foreach (ITaskItem item in NativeLibraries) {
@@ -374,26 +420,37 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
374420
continue;
375421
}
376422

377-
dsos.Add ((name, $"dsoName{dsos.Count.ToString (CultureInfo.InvariantCulture)}", ELFHelper.IsEmptyAOTLibrary (Log, item.ItemSpec)));
423+
dsos.Add ((name, $"dsoName{dsos.Count.ToString (CultureInfo.InvariantCulture)}", ELFHelper.IsEmptyAOTLibrary (Log, item.ItemSpec), item));
378424
}
379425

380426
var dsoCache = new List<StructureInstance<DSOCacheEntry>> ();
427+
var jniPreloads = new List<DSOCacheEntry> ();
381428
var aotDsoCache = new List<StructureInstance<DSOCacheEntry>> ();
382429
var nameMutations = new List<string> ();
383430

384431
for (int i = 0; i < dsos.Count; i++) {
385432
string name = dsos[i].name;
433+
434+
bool isJniLibrary = ELFHelper.IsJniLibrary (Log, dsos[i].item.ItemSpec);
435+
bool ignore = dsos[i].ignore;
436+
386437
nameMutations.Clear();
387438
AddNameMutations (name);
439+
388440
// All mutations point to the actual library name, but have hash of the mutated one
389441
foreach (string entryName in nameMutations) {
390442
var entry = new DSOCacheEntry {
391443
HashedName = entryName,
392444
hash = 0, // Hash is arch-specific, we compute it before writing
393-
ignore = dsos[i].ignore,
445+
ignore = ignore,
446+
is_jni_library = isJniLibrary,
394447
name = name,
395448
};
396449

450+
if (entry.is_jni_library && entry.HashedName == name && !ApplicationConfigNativeAssemblyGeneratorCLR.DsoCacheJniPreloadIgnore.Contains (name)) {
451+
jniPreloads.Add (entry);
452+
}
453+
397454
var item = new StructureInstance<DSOCacheEntry> (dsoCacheEntryStructureInfo, entry);
398455
if (name.StartsWith ("libaot-", StringComparison.OrdinalIgnoreCase)) {
399456
aotDsoCache.Add (item);
@@ -403,7 +460,11 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
403460
}
404461
}
405462

406-
return (dsoCache, aotDsoCache);
463+
return new DsoCacheState {
464+
DsoCache = dsoCache,
465+
AotDsoCache = aotDsoCache,
466+
JniPreloadDSOs = jniPreloads,
467+
};
407468

408469
void AddNameMutations (string name)
409470
{

src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGeneratorCLR.cs

Lines changed: 82 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ sealed class DSOCacheEntry
5959
[NativeAssembler (NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)]
6060
public ulong real_name_hash;
6161
public bool ignore;
62+
public bool is_jni_library;
6263

6364
[NativeAssembler (UsesDataProvider = true)]
6465
public uint name_index;
@@ -238,15 +239,27 @@ sealed class XamarinAndroidBundledAssembly
238239
}
239240
#pragma warning restore CS0649
240241

242+
sealed class DsoCacheState
243+
{
244+
public List<StructureInstance<DSOCacheEntry>> DsoCache = [];
245+
public List<DSOCacheEntry> JniPreloadDSOs = [];
246+
public List<StructureInstance<DSOCacheEntry>> AotDsoCache = [];
247+
public LlvmIrStringBlob NamesBlob = null!;
248+
}
249+
241250
// Keep in sync with FORMAT_TAG in src/monodroid/jni/xamarin-app.hh
242251
const ulong FORMAT_TAG = 0x00025E6972616D58; // 'Xmari^XY' where XY is the format version
243252

253+
// List of library names to ignore when generating the list of JNI-using libraries to preload
254+
internal static readonly HashSet<string> DsoCacheJniPreloadIgnore = new (StringComparer.OrdinalIgnoreCase) {
255+
"libmonodroid.so",
256+
};
257+
244258
SortedDictionary <string, string>? environmentVariables;
245259
SortedDictionary <string, string>? systemProperties;
246260
SortedDictionary <string, string>? runtimeProperties;
247261
StructureInstance? application_config;
248-
List<StructureInstance<DSOCacheEntry>>? dsoCache;
249-
List<StructureInstance<DSOCacheEntry>>? aotDsoCache;
262+
250263
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value - assigned conditionally by build process
251264
List<StructureInstance<XamarinAndroidBundledAssembly>>? xamarinAndroidBundledAssemblies;
252265
#pragma warning restore CS0649
@@ -261,10 +274,10 @@ sealed class XamarinAndroidBundledAssembly
261274
StructureInfo? assemblyStoreRuntimeDataStructureInfo;
262275
StructureInfo? runtimePropertyStructureInfo;
263276
StructureInfo? runtimePropertyIndexEntryStructureInfo;
264-
#pragma warning disable CS0169 // Field is never used - might be used in future versions
277+
#pragma warning disable CS0169 // Field is never used - might be used in future versions
265278
StructureInfo? hostConfigurationPropertyStructureInfo;
266279
#pragma warning restore CS0169
267-
#pragma warning disable CS0169 // Field is never used - might be used in future versions
280+
#pragma warning disable CS0169 // Field is never used - might be used in future versions
268281
StructureInfo? hostConfigurationPropertiesStructureInfo;
269282
#pragma warning restore CS0169
270283
StructureInfo? appEnvironmentVariableStructureInfo;
@@ -349,7 +362,7 @@ protected override void Construct (LlvmIrModule module)
349362
};
350363
module.Add (sysProps, stringGroupName: "sysprop", stringGroupComment: " System properties name:value pairs");
351364

352-
(dsoCache, aotDsoCache, LlvmIrStringBlob dsoNamesBlob) = InitDSOCache ();
365+
DsoCacheState dsoState = InitDSOCache ();
353366
var app_cfg = new ApplicationConfigCLR {
354367
uses_assembly_preload = UsesAssemblyPreload,
355368
jni_add_native_method_registration_attribute_present = JniAddNativeMethodRegistrationAttributePresent,
@@ -363,8 +376,8 @@ protected override void Construct (LlvmIrModule module)
363376
number_of_assemblies_in_apk = (uint)NumberOfAssembliesInApk,
364377
number_of_shared_libraries = (uint)NativeLibraries.Count,
365378
bundled_assembly_name_width = (uint)BundledAssemblyNameWidth,
366-
number_of_dso_cache_entries = (uint)dsoCache.Count,
367-
number_of_aot_cache_entries = (uint)aotDsoCache.Count,
379+
number_of_dso_cache_entries = (uint)dsoState.DsoCache.Count,
380+
number_of_aot_cache_entries = (uint)dsoState.AotDsoCache.Count,
368381
android_runtime_jnienv_class_token = (uint)AndroidRuntimeJNIEnvToken,
369382
jnienv_initialize_method_token = (uint)JNIEnvInitializeToken,
370383
jnienv_registerjninatives_method_token = (uint)JNIEnvRegisterJniNativesToken,
@@ -375,18 +388,28 @@ protected override void Construct (LlvmIrModule module)
375388
application_config = new StructureInstance<ApplicationConfigCLR> (applicationConfigStructureInfo, app_cfg);
376389
module.AddGlobalVariable ("application_config", application_config);
377390

378-
var dso_cache = new LlvmIrGlobalVariable (dsoCache, "dso_cache", LlvmIrVariableOptions.GlobalWritable) {
391+
var dso_cache = new LlvmIrGlobalVariable (dsoState.DsoCache, "dso_cache", LlvmIrVariableOptions.GlobalWritable) {
379392
Comment = " DSO cache entries",
380393
BeforeWriteCallback = HashAndSortDSOCache,
381394
};
382395
module.Add (dso_cache);
383396

384-
var aot_dso_cache = new LlvmIrGlobalVariable (aotDsoCache, "aot_dso_cache", LlvmIrVariableOptions.GlobalWritable) {
397+
// This variable MUST be written after `dso_cache` since it relies on sorting performed by HashAndSortDSOCache
398+
var dso_jni_preloads_idx = new LlvmIrGlobalVariable (new List<uint> (), "dso_jni_preloads_idx", LlvmIrVariableOptions.GlobalConstant) {
399+
Comment = " Indices into dso_cache[] of DSO libraries to preload because of JNI use",
400+
ArrayItemCount = (uint)dsoState.JniPreloadDSOs.Count,
401+
BeforeWriteCallback = PopulatePreloadIndices,
402+
BeforeWriteCallbackCallerState = dsoState,
403+
};
404+
module.AddGlobalVariable ("dso_jni_preloads_idx_count", dso_jni_preloads_idx.ArrayItemCount);
405+
module.Add (dso_jni_preloads_idx);
406+
407+
var aot_dso_cache = new LlvmIrGlobalVariable (dsoState.AotDsoCache, "aot_dso_cache", LlvmIrVariableOptions.GlobalWritable) {
385408
Comment = " AOT DSO cache entries",
386409
BeforeWriteCallback = HashAndSortDSOCache,
387410
};
388411
module.Add (aot_dso_cache);
389-
module.AddGlobalVariable ("dso_names_data", dsoNamesBlob, LlvmIrVariableOptions.GlobalConstant);
412+
module.AddGlobalVariable ("dso_names_data", dsoState.NamesBlob, LlvmIrVariableOptions.GlobalConstant);
390413

391414
var dso_apk_entries = new LlvmIrGlobalVariable (typeof(List<StructureInstance<DSOApkEntry>>), "dso_apk_entries") {
392415
ArrayItemCount = (ulong)NativeLibraries.Count,
@@ -543,6 +566,36 @@ void AddAssemblyStores (LlvmIrModule module)
543566
module.Add (assembly_store);
544567
}
545568

569+
void PopulatePreloadIndices (LlvmIrVariable variable, LlvmIrModuleTarget target, object? state)
570+
{
571+
var indices = variable.Value as List<uint>;
572+
if (indices == null) {
573+
throw new InvalidOperationException ($"Internal error: DSO preload indices list instance not present.");
574+
}
575+
576+
var dsoState = state as DsoCacheState;
577+
if (dsoState == null) {
578+
throw new InvalidOperationException ($"Internal error: DSO state not present.");
579+
}
580+
581+
foreach (DSOCacheEntry preload in dsoState.JniPreloadDSOs) {
582+
int dsoIdx = dsoState.DsoCache.FindIndex (entry => {
583+
if (entry.Instance == null) {
584+
return false;
585+
}
586+
587+
return entry.Instance.hash == preload.hash && entry.Instance.real_name_hash == preload.real_name_hash;
588+
});
589+
590+
if (dsoIdx == -1) {
591+
throw new InvalidOperationException ($"Internal error: DSO entry in JNI preload list not found in the DSO cache list.");
592+
}
593+
594+
indices.Add ((uint)dsoIdx);
595+
}
596+
indices.Sort ();
597+
}
598+
546599
void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, object? state)
547600
{
548601
var cache = variable.Value as List<StructureInstance<DSOCacheEntry>>;
@@ -571,10 +624,9 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
571624
});
572625
}
573626

574-
(List<StructureInstance<DSOCacheEntry>> dsoCache, List<StructureInstance<DSOCacheEntry>> aotDsoCache, LlvmIrStringBlob namesBlob)
575-
InitDSOCache ()
627+
DsoCacheState InitDSOCache ()
576628
{
577-
var dsos = new List<(string name, string nameLabel, bool ignore)> ();
629+
var dsos = new List<(string name, string nameLabel, bool ignore, ITaskItem item)> ();
578630
var nameCache = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
579631

580632
foreach (ITaskItem item in NativeLibraries) {
@@ -588,10 +640,11 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
588640
continue;
589641
}
590642

591-
dsos.Add ((name, $"dsoName{dsos.Count.ToString (CultureInfo.InvariantCulture)}", ELFHelper.IsEmptyAOTLibrary (Log, item.ItemSpec)));
643+
dsos.Add ((name, $"dsoName{dsos.Count.ToString (CultureInfo.InvariantCulture)}", ELFHelper.IsEmptyAOTLibrary (Log, item.ItemSpec), item));
592644
}
593645

594646
var dsoCache = new List<StructureInstance<DSOCacheEntry>> ();
647+
var jniPreloads = new List<DSOCacheEntry> ();
595648
var aotDsoCache = new List<StructureInstance<DSOCacheEntry>> ();
596649
var nameMutations = new List<string> ();
597650
var dsoNamesBlob = new LlvmIrStringBlob ();
@@ -600,6 +653,9 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
600653
string name = dsos[i].name;
601654
(int nameOffset, _) = dsoNamesBlob.Add (name);
602655

656+
bool isJniLibrary = ELFHelper.IsJniLibrary (Log, dsos[i].item.ItemSpec);
657+
bool ignore = dsos[i].ignore;
658+
603659
nameMutations.Clear();
604660
AddNameMutations (name);
605661
// All mutations point to the actual library name, but have hash of the mutated one
@@ -609,10 +665,15 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
609665
RealName = name,
610666

611667
hash = 0, // Hash is arch-specific, we compute it before writing
612-
ignore = dsos[i].ignore,
668+
ignore = ignore,
669+
is_jni_library = isJniLibrary,
613670
name_index = (uint)nameOffset,
614671
};
615672

673+
if (entry.is_jni_library && entry.HashedName == name && !DsoCacheJniPreloadIgnore.Contains (name)) {
674+
jniPreloads.Add (entry);
675+
}
676+
616677
var item = new StructureInstance<DSOCacheEntry> (dsoCacheEntryStructureInfo, entry);
617678
if (name.StartsWith ("libaot-", StringComparison.OrdinalIgnoreCase)) {
618679
aotDsoCache.Add (item);
@@ -622,7 +683,12 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
622683
}
623684
}
624685

625-
return (dsoCache, aotDsoCache, dsoNamesBlob);
686+
return new DsoCacheState {
687+
DsoCache = dsoCache,
688+
JniPreloadDSOs = jniPreloads,
689+
AotDsoCache = aotDsoCache,
690+
NamesBlob = dsoNamesBlob,
691+
};
626692

627693
void AddNameMutations (string name)
628694
{

0 commit comments

Comments
 (0)