@@ -239,15 +239,27 @@ sealed class XamarinAndroidBundledAssembly
239239 }
240240#pragma warning restore CS0649
241241
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+
242250 // Keep in sync with FORMAT_TAG in src/monodroid/jni/xamarin-app.hh
243251 const ulong FORMAT_TAG = 0x00025E6972616D58 ; // 'Xmari^XY' where XY is the format version
244252
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+
245258 SortedDictionary < string , string > ? environmentVariables ;
246259 SortedDictionary < string , string > ? systemProperties ;
247260 SortedDictionary < string , string > ? runtimeProperties ;
248261 StructureInstance ? application_config ;
249- List < StructureInstance < DSOCacheEntry > > ? dsoCache ;
250- List < StructureInstance < DSOCacheEntry > > ? aotDsoCache ;
262+
251263#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value - assigned conditionally by build process
252264 List < StructureInstance < XamarinAndroidBundledAssembly > > ? xamarinAndroidBundledAssemblies ;
253265#pragma warning restore CS0649
@@ -350,7 +362,7 @@ protected override void Construct (LlvmIrModule module)
350362 } ;
351363 module . Add ( sysProps , stringGroupName : "sysprop" , stringGroupComment : " System properties name:value pairs" ) ;
352364
353- ( dsoCache , aotDsoCache , LlvmIrStringBlob dsoNamesBlob ) = InitDSOCache ( ) ;
365+ DsoCacheState dsoState = InitDSOCache ( ) ;
354366 var app_cfg = new ApplicationConfigCLR {
355367 uses_assembly_preload = UsesAssemblyPreload ,
356368 jni_add_native_method_registration_attribute_present = JniAddNativeMethodRegistrationAttributePresent ,
@@ -364,8 +376,8 @@ protected override void Construct (LlvmIrModule module)
364376 number_of_assemblies_in_apk = ( uint ) NumberOfAssembliesInApk ,
365377 number_of_shared_libraries = ( uint ) NativeLibraries . Count ,
366378 bundled_assembly_name_width = ( uint ) BundledAssemblyNameWidth ,
367- number_of_dso_cache_entries = ( uint ) dsoCache . Count ,
368- 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 ,
369381 android_runtime_jnienv_class_token = ( uint ) AndroidRuntimeJNIEnvToken ,
370382 jnienv_initialize_method_token = ( uint ) JNIEnvInitializeToken ,
371383 jnienv_registerjninatives_method_token = ( uint ) JNIEnvRegisterJniNativesToken ,
@@ -376,18 +388,27 @@ protected override void Construct (LlvmIrModule module)
376388 application_config = new StructureInstance < ApplicationConfigCLR > ( applicationConfigStructureInfo , app_cfg ) ;
377389 module . AddGlobalVariable ( "application_config" , application_config ) ;
378390
379- var dso_cache = new LlvmIrGlobalVariable ( dsoCache , "dso_cache" , LlvmIrVariableOptions . GlobalWritable ) {
391+ var dso_cache = new LlvmIrGlobalVariable ( dsoState . DsoCache , "dso_cache" , LlvmIrVariableOptions . GlobalWritable ) {
380392 Comment = " DSO cache entries" ,
381393 BeforeWriteCallback = HashAndSortDSOCache ,
382394 } ;
383395 module . Add ( dso_cache ) ;
384396
385- 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 of DSO libraries to preload because of JNI use" ,
400+ ArrayItemCount = ( uint ) dsoState . JniPreloadDSOs . Count ,
401+ BeforeWriteCallback = PopulatePreloadIndices ,
402+ BeforeWriteCallbackCallerState = dsoState ,
403+ } ;
404+ module . Add ( dso_jni_preloads_idx ) ;
405+
406+ var aot_dso_cache = new LlvmIrGlobalVariable ( dsoState . AotDsoCache , "aot_dso_cache" , LlvmIrVariableOptions . GlobalWritable ) {
386407 Comment = " AOT DSO cache entries" ,
387408 BeforeWriteCallback = HashAndSortDSOCache ,
388409 } ;
389410 module . Add ( aot_dso_cache ) ;
390- module . AddGlobalVariable ( "dso_names_data" , dsoNamesBlob , LlvmIrVariableOptions . GlobalConstant ) ;
411+ module . AddGlobalVariable ( "dso_names_data" , dsoState . NamesBlob , LlvmIrVariableOptions . GlobalConstant ) ;
391412
392413 var dso_apk_entries = new LlvmIrGlobalVariable ( typeof ( List < StructureInstance < DSOApkEntry > > ) , "dso_apk_entries" ) {
393414 ArrayItemCount = ( ulong ) NativeLibraries . Count ,
@@ -544,6 +565,36 @@ void AddAssemblyStores (LlvmIrModule module)
544565 module . Add ( assembly_store ) ;
545566 }
546567
568+ void PopulatePreloadIndices ( LlvmIrVariable variable , LlvmIrModuleTarget target , object ? state )
569+ {
570+ var indices = variable . Value as List < uint > ;
571+ if ( indices == null ) {
572+ throw new InvalidOperationException ( $ "Internal error: DSO preload indices list instance not present.") ;
573+ }
574+
575+ var dsoState = state as DsoCacheState ;
576+ if ( dsoState == null ) {
577+ throw new InvalidOperationException ( $ "Internal error: DSO state not present.") ;
578+ }
579+
580+ foreach ( DSOCacheEntry preload in dsoState . JniPreloadDSOs ) {
581+ int dsoIdx = dsoState . DsoCache . FindIndex ( entry => {
582+ if ( entry . Instance == null ) {
583+ return false ;
584+ }
585+
586+ return entry . Instance . hash == preload . hash && entry . Instance . real_name_hash == preload . real_name_hash ;
587+ } ) ;
588+
589+ if ( dsoIdx == - 1 ) {
590+ throw new InvalidOperationException ( $ "Internal error: DSO entry in JNI preload list not found in the DSO cache list.") ;
591+ }
592+
593+ indices . Add ( ( uint ) dsoIdx ) ;
594+ }
595+ indices . Sort ( ) ;
596+ }
597+
547598 void HashAndSortDSOCache ( LlvmIrVariable variable , LlvmIrModuleTarget target , object ? state )
548599 {
549600 var cache = variable . Value as List < StructureInstance < DSOCacheEntry > > ;
@@ -572,8 +623,7 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
572623 } ) ;
573624 }
574625
575- ( List < StructureInstance < DSOCacheEntry > > dsoCache , List < StructureInstance < DSOCacheEntry > > aotDsoCache , LlvmIrStringBlob namesBlob )
576- InitDSOCache ( )
626+ DsoCacheState InitDSOCache ( )
577627 {
578628 var dsos = new List < ( string name , string nameLabel , bool ignore , ITaskItem item ) > ( ) ;
579629 var nameCache = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
@@ -593,6 +643,7 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
593643 }
594644
595645 var dsoCache = new List < StructureInstance < DSOCacheEntry > > ( ) ;
646+ var jniPreloads = new List < DSOCacheEntry > ( ) ;
596647 var aotDsoCache = new List < StructureInstance < DSOCacheEntry > > ( ) ;
597648 var nameMutations = new List < string > ( ) ;
598649 var dsoNamesBlob = new LlvmIrStringBlob ( ) ;
@@ -601,6 +652,9 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
601652 string name = dsos [ i ] . name ;
602653 ( int nameOffset , _ ) = dsoNamesBlob . Add ( name ) ;
603654
655+ bool isJniLibrary = ELFHelper . IsJniLibrary ( Log , dsos [ i ] . item . ItemSpec ) ;
656+ bool ignore = dsos [ i ] . ignore ;
657+
604658 nameMutations . Clear ( ) ;
605659 AddNameMutations ( name ) ;
606660 // All mutations point to the actual library name, but have hash of the mutated one
@@ -610,11 +664,15 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
610664 RealName = name ,
611665
612666 hash = 0 , // Hash is arch-specific, we compute it before writing
613- ignore = dsos [ i ] . ignore ,
614- is_jni_library = ELFHelper . IsJniLibrary ( Log , dsos [ i ] . item . ItemSpec ) ,
667+ ignore = ignore ,
668+ is_jni_library = isJniLibrary ,
615669 name_index = ( uint ) nameOffset ,
616670 } ;
617671
672+ if ( entry . is_jni_library && entry . HashedName == name && ! DsoCacheJniPreloadIgnore . Contains ( name ) ) {
673+ jniPreloads . Add ( entry ) ;
674+ }
675+
618676 var item = new StructureInstance < DSOCacheEntry > ( dsoCacheEntryStructureInfo , entry ) ;
619677 if ( name . StartsWith ( "libaot-" , StringComparison . OrdinalIgnoreCase ) ) {
620678 aotDsoCache . Add ( item ) ;
@@ -624,7 +682,12 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
624682 }
625683 }
626684
627- return ( dsoCache , aotDsoCache , dsoNamesBlob ) ;
685+ return new DsoCacheState {
686+ DsoCache = dsoCache ,
687+ JniPreloadDSOs = jniPreloads ,
688+ AotDsoCache = aotDsoCache ,
689+ NamesBlob = dsoNamesBlob ,
690+ } ;
628691
629692 void AddNameMutations ( string name )
630693 {
0 commit comments