diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateEmptyTypemapStub.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateEmptyTypemapStub.cs index 47d16267cda..7a89cc4e569 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateEmptyTypemapStub.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateEmptyTypemapStub.cs @@ -68,10 +68,9 @@ string GenerateStubLlvmIr (string abi) return header + """ %struct.TypeMap = type { i32, i32, ptr, ptr } %struct.TypeMapManagedTypeInfo = type { i64, i32, i32 } -%struct.TypeMapAssembly = type { i64 } +%struct.TypeMapAssembly = type { [16 x i8], i64, i64 } @type_map = dso_local constant %struct.TypeMap zeroinitializer, align 8 -@typemap_use_hashes = dso_local constant i8 1, align 1 @type_map_managed_type_info = dso_local constant [0 x %struct.TypeMapManagedTypeInfo] zeroinitializer, align 8 @type_map_unique_assemblies = dso_local constant [0 x %struct.TypeMapAssembly] zeroinitializer, align 8 @type_map_assembly_names = dso_local constant [1 x i8] zeroinitializer, align 1 @@ -84,7 +83,7 @@ string GenerateStubLlvmIr (string abi) @managed_to_java_map_module_count = dso_local constant i32 0, align 4 @managed_to_java_map = dso_local constant [0 x i8] zeroinitializer, align 8 @java_to_managed_map = dso_local constant [0 x i8] zeroinitializer, align 8 -@java_to_managed_hashes = dso_local constant [0 x i64] zeroinitializer, align 8 +@java_to_managed_hashes = dso_local constant [0 x i32] zeroinitializer, align 4 @modules_map_data = dso_local constant [0 x i8] zeroinitializer, align 8 @modules_duplicates_data = dso_local constant [0 x i8] zeroinitializer, align 8 @java_type_count = dso_local constant i32 0, align 4 diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs index 927e4ccad70..7571d11757d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs @@ -53,7 +53,6 @@ public sealed class ApplicationConfig_CoreCLR : IApplicationConfig public uint number_of_assemblies_in_apk; public uint bundled_assembly_name_width; public uint number_of_dso_cache_entries; - public uint number_of_aot_cache_entries; public uint number_of_shared_libraries; public uint android_runtime_jnienv_class_token; public uint jnienv_initialize_method_token; @@ -64,7 +63,7 @@ public sealed class ApplicationConfig_CoreCLR : IApplicationConfig public bool managed_marshal_methods_lookup_enabled; } - const uint ApplicationConfigFieldCount_CoreCLR = 20; + const uint ApplicationConfigFieldCount_CoreCLR = 19; // This must be identical to the ApplicationConfig structure in src/native/mono/xamarin-app-stub/xamarin-app.hh public sealed class ApplicationConfig_MonoVM : IApplicationConfig @@ -103,7 +102,7 @@ public sealed class DSOCacheEntry64 { // Hardcoded, by design - we want to know if there are any changes in the // native assembly layout. - public const uint NativeSize_CoreCLR = 32; + public const uint NativeSize_CoreCLR = 24; public const uint NativeSize_MonoVM = 40; public ulong hash; @@ -186,8 +185,6 @@ public sealed class JniPreloads "java_type_count", "managed_to_java_map", "managed_to_java_map_module_count", - "runtime_properties", - "runtime_properties_data", AppEnvironmentVariableContentsSymbolName, AppEnvironmentVariablesSymbolName, ApplicationConfigSymbolName, @@ -363,47 +360,42 @@ static IApplicationConfig ReadApplicationConfig_CoreCLR (EnvironmentFile envFile ret.number_of_dso_cache_entries = ConvertFieldToUInt32 ("number_of_dso_cache_entries", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]); break; - case 11: // number_of_aot_cache_entries: uint32_t / .word | .long - Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}"); - ret.number_of_aot_cache_entries = ConvertFieldToUInt32 ("number_of_aot_cache_entries", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]); - break; - - case 12: // number_of_shared_libraries: uint32_t / .word | .long + case 11: // number_of_shared_libraries: uint32_t / .word | .long Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}"); ret.number_of_shared_libraries = ConvertFieldToUInt32 ("number_of_shared_libraries", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]); break; - case 13: // android_runtime_jnienv_class_token: uint32_t / .word | .long + case 12: // android_runtime_jnienv_class_token: uint32_t / .word | .long Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}"); ret.android_runtime_jnienv_class_token = ConvertFieldToUInt32 ("android_runtime_jnienv_class_token", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]); break; - case 14: // jnienv_initialize_method_token: uint32_t / .word | .long + case 13: // jnienv_initialize_method_token: uint32_t / .word | .long Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}"); ret.jnienv_initialize_method_token = ConvertFieldToUInt32 ("jnienv_initialize_method_token", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]); break; - case 15: // jnienv_registerjninatives_method_token: uint32_t / .word | .long + case 14: // jnienv_registerjninatives_method_token: uint32_t / .word | .long Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}"); ret.jnienv_registerjninatives_method_token = ConvertFieldToUInt32 ("jnienv_registerjninatives_method_token", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]); break; - case 16: // jni_remapping_replacement_type_count: uint32_t / .word | .long + case 15: // jni_remapping_replacement_type_count: uint32_t / .word | .long Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}"); ret.jni_remapping_replacement_type_count = ConvertFieldToUInt32 ("jni_remapping_replacement_type_count", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]); break; - case 17: // jni_remapping_replacement_method_index_entry_count: uint32_t / .word | .long + case 16: // jni_remapping_replacement_method_index_entry_count: uint32_t / .word | .long Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}"); ret.jni_remapping_replacement_method_index_entry_count = ConvertFieldToUInt32 ("jni_remapping_replacement_method_index_entry_count", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]); break; - case 18: // android_package_name: string / [pointer type] + case 17: // android_package_name: string / [pointer type] Assert.IsTrue (expectedPointerTypes.Contains (field [0]), $"Unexpected pointer field type in '{envFile.Path}:{item.LineNumber}': {field [0]}"); pointers.Add (field [1].Trim ()); break; - case 19: // managed_marshal_methods_lookup_enabled: bool / .byte + case 18: // managed_marshal_methods_lookup_enabled: bool / .byte AssertFieldType (envFile.Path, parser.SourceFilePath, ".byte", field [0], item.LineNumber); ret.managed_marshal_methods_lookup_enabled = ConvertFieldToBool ("managed_marshal_methods_lookup_enabled", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]); break; @@ -1135,13 +1127,13 @@ static List ReadDsoCache64_CoreCLR (EnvironmentFile envFile, Na string value; int index = i; - // uint64_t hash - (lineNumber, value) = ReadNextArrayIndex (envFile, parser, dsoCache, index++, expectedUInt64Types); - ulong hash = ConvertFieldToUInt64 ("hash", envFile.Path, parser.SourceFilePath, lineNumber, value); + // uint32_t hash + (lineNumber, value) = ReadNextArrayIndex (envFile, parser, dsoCache, index++, expectedUInt32Types); + ulong hash = ConvertFieldToUInt32 ("hash", envFile.Path, parser.SourceFilePath, lineNumber, value); - // uint64_t real_name_hash - (lineNumber, value) = ReadNextArrayIndex (envFile, parser, dsoCache, index++, expectedUInt64Types); - ulong real_name_hash = ConvertFieldToUInt64 ("real_name_hash", envFile.Path, parser.SourceFilePath, lineNumber, value); + // uint32_t real_name_hash + (lineNumber, value) = ReadNextArrayIndex (envFile, parser, dsoCache, index++, expectedUInt32Types); + ulong real_name_hash = ConvertFieldToUInt32 ("real_name_hash", envFile.Path, parser.SourceFilePath, lineNumber, value); // bool ignore (lineNumber, value) = ReadNextArrayIndex (envFile, parser, dsoCache, index++, ".byte"); @@ -1313,20 +1305,24 @@ static byte ConvertFieldToByte (string fieldName, string llvmAssemblerEnvFile, s return fv; } - // Integers are parsed as signed, since llc will always output signed integers. + // llc usually emits signed decimal integers, but large unsigned fields can also be emitted as + // decimal values above Int32.MaxValue. Accept both forms. static bool TryParseInteger (string value, out uint fv) { if (value.StartsWith ("0x", StringComparison.Ordinal)) { return UInt32.TryParse (value.Substring (2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out fv); } - fv = 0; - if (!Int32.TryParse (value, out int signedFV)) { - return false; + if (UInt32.TryParse (value, NumberStyles.None, CultureInfo.InvariantCulture, out fv)) { + return true; } - fv = (uint)signedFV; - return true; + if (Int32.TryParse (value, NumberStyles.None, CultureInfo.InvariantCulture, out int signedFV)) { + fv = (uint)signedFV; + return true; + } + + return false; } static bool TryParseInteger (string value, out ulong fv) @@ -1335,13 +1331,16 @@ static bool TryParseInteger (string value, out ulong fv) return UInt64.TryParse (value.Substring (2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out fv); } - fv = 0; - if (!Int64.TryParse (value, out long signedFV)) { - return false; + if (UInt64.TryParse (value, NumberStyles.None, CultureInfo.InvariantCulture, out fv)) { + return true; + } + + if (Int64.TryParse (value, NumberStyles.None, CultureInfo.InvariantCulture, out long signedFV)) { + fv = (ulong)signedFV; + return true; } - fv = (ulong)signedFV; - return true; + return false; } static bool TryParseInteger (string value, out byte fv) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc index 065272635fa..dbef4c838e5 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc @@ -5,34 +5,34 @@ "Size": 3036 }, "classes.dex": { - "Size": 402352 + "Size": 402852 }, "lib/arm64-v8a/libassembly-store.so": { - "Size": 2717688 + "Size": 2724560 }, "lib/arm64-v8a/libclrjit.so": { - "Size": 2835128 + "Size": 2757816 }, "lib/arm64-v8a/libcoreclr.so": { - "Size": 4891056 + "Size": 4837240 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1324320 + "Size": 1252840 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72112 }, "lib/arm64-v8a/libSystem.IO.Compression.Native.so": { - "Size": 1262888 + "Size": 1258776 }, "lib/arm64-v8a/libSystem.Native.so": { - "Size": 101888 + "Size": 99664 }, "lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": { - "Size": 168080 + "Size": 163936 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 20968 + "Size": 17896 }, "res/drawable-hdpi-v4/icon.png": { "Size": 2178 @@ -59,5 +59,5 @@ "Size": 1904 } }, - "PackageSize": 7271867 + "PackageSize": 7235003 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc index eb6cece9dff..b08d187284e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc @@ -8,7 +8,7 @@ "Size": 9413828 }, "classes2.dex": { - "Size": 158204 + "Size": 157836 }, "kotlin/annotation/annotation.kotlin_builtins": { "Size": 928 @@ -32,31 +32,31 @@ "Size": 2396 }, "lib/arm64-v8a/libassembly-store.so": { - "Size": 11658648 + "Size": 11696200 }, "lib/arm64-v8a/libclrjit.so": { - "Size": 2824392 + "Size": 2757816 }, "lib/arm64-v8a/libcoreclr.so": { - "Size": 4890432 + "Size": 4837240 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1265504 + "Size": 1252416 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72112 }, "lib/arm64-v8a/libSystem.IO.Compression.Native.so": { - "Size": 1262888 + "Size": 1258776 }, "lib/arm64-v8a/libSystem.Native.so": { - "Size": 101888 + "Size": 99664 }, "lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": { - "Size": 168080 + "Size": 163936 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 147824 + "Size": 159496 }, "META-INF/androidx.activity_activity.version": { "Size": 6 @@ -2234,5 +2234,5 @@ "Size": 794696 } }, - "PackageSize": 20786765 + "PackageSize": 20782669 } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigCLR.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigCLR.cs index b48178218f1..adb7b2fcd2e 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigCLR.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigCLR.cs @@ -36,7 +36,6 @@ sealed class ApplicationConfigCLR public uint number_of_assemblies_in_apk; public uint bundled_assembly_name_width; public uint number_of_dso_cache_entries; - public uint number_of_aot_cache_entries; public uint number_of_shared_libraries; [NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)] diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGeneratorCLR.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGeneratorCLR.cs index f4ef94d75b1..f3434e6e5ea 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGeneratorCLR.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGeneratorCLR.cs @@ -55,10 +55,10 @@ sealed class DSOCacheEntry public string? RealName; [NativeAssembler (UsesDataProvider = true, NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)] - public ulong hash; + public uint hash; [NativeAssembler (NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)] - public ulong real_name_hash; + public uint real_name_hash; public bool ignore; public bool is_jni_library; @@ -90,7 +90,7 @@ sealed class DSOApkEntry public string Name = String.Empty; [NativeAssembler (UsesDataProvider = true, NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)] - public ulong name_hash; + public uint name_hash; public uint offset; // offset into the APK public int fd; // apk file descriptor }; @@ -139,54 +139,6 @@ sealed class AssemblyStoreRuntimeData public AssemblyStoreAssemblyDescriptor? assemblies; } - sealed class RuntimePropertyContextDataProvider : NativeAssemblerStructContextDataProvider - { - public override string GetComment (object data, string fieldName) - { - var runtimeProp = EnsureType (data); - if (MonoAndroidHelper.StringEquals ("key_index", fieldName)) { - return $" '{runtimeProp.Key}'"; - } - - if (MonoAndroidHelper.StringEquals ("value_index", fieldName)) { - return $" '{runtimeProp.Value}'"; - } - - return String.Empty; - } - } - - // Order of fields and their types must correspond *exactly* to that in - // src/native/clr/include/xamarin-app.hh RuntimeProperty structure - [NativeAssemblerStructContextDataProvider (typeof (RuntimePropertyContextDataProvider))] - sealed class RuntimeProperty - { - [NativeAssembler (Ignore = true)] - public string? Key; - - [NativeAssembler (Ignore = true)] - public string? Value; - - [NativeAssembler (UsesDataProvider = true)] - public uint key_index; - - [NativeAssembler (UsesDataProvider = true)] - public uint value_index; - public uint value_size; - } - - // Order of fields and their types must correspond *exactly* to that in - // src/native/clr/include/xamarin-app.hh RuntimePropertyIndexEntry structure - sealed class RuntimePropertyIndexEntry - { - [NativeAssembler (Ignore = true)] - public string? HashedKey; - - [NativeAssembler (NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)] - public ulong key_hash; - public uint index; - } - sealed class XamarinAndroidBundledAssemblyContextDataProvider : NativeAssemblerStructContextDataProvider { public override ulong GetBufferSize (object data, string fieldName) @@ -230,7 +182,6 @@ sealed class DsoCacheState public List> DsoCache = []; public List JniPreloadDSOs = []; public List JniPreloadNames = []; - public List> AotDsoCache = []; public LlvmIrStringBlob NamesBlob = null!; public uint NameMutationsCount = 1; } @@ -251,8 +202,6 @@ sealed class DsoCacheState #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value - assigned conditionally by build process List>? xamarinAndroidBundledAssemblies; #pragma warning restore CS0649 - List>? runtimePropertiesData; - List>? runtimePropertyIndex; StructureInfo? applicationConfigStructureInfo; StructureInfo? dsoCacheEntryStructureInfo; @@ -260,14 +209,6 @@ sealed class DsoCacheState StructureInfo? xamarinAndroidBundledAssemblyStructureInfo; StructureInfo? assemblyStoreSingleAssemblyRuntimeDataStructureinfo; StructureInfo? assemblyStoreRuntimeDataStructureInfo; - StructureInfo? runtimePropertyStructureInfo; - StructureInfo? runtimePropertyIndexEntryStructureInfo; -#pragma warning disable CS0169 // Field is never used - might be used in future versions - StructureInfo? hostConfigurationPropertyStructureInfo; -#pragma warning restore CS0169 -#pragma warning disable CS0169 // Field is never used - might be used in future versions - StructureInfo? hostConfigurationPropertiesStructureInfo; -#pragma warning restore CS0169 StructureInfo? appEnvironmentVariableStructureInfo; public bool UsesAssemblyPreload { get; set; } @@ -366,7 +307,6 @@ protected override void Construct (LlvmIrModule module) number_of_shared_libraries = (uint)NativeLibraries.Count, bundled_assembly_name_width = (uint)BundledAssemblyNameWidth, number_of_dso_cache_entries = (uint)dsoState.DsoCache.Count, - number_of_aot_cache_entries = (uint)dsoState.AotDsoCache.Count, android_runtime_jnienv_class_token = (uint)AndroidRuntimeJNIEnvToken, jnienv_initialize_method_token = (uint)JNIEnvInitializeToken, jnienv_registerjninatives_method_token = (uint)JNIEnvRegisterJniNativesToken, @@ -397,11 +337,6 @@ protected override void Construct (LlvmIrModule module) module.AddGlobalVariable ("dso_jni_preloads_idx_count", dso_jni_preloads_idx.ArrayItemCount); module.Add (dso_jni_preloads_idx); - var aot_dso_cache = new LlvmIrGlobalVariable (dsoState.AotDsoCache, "aot_dso_cache", LlvmIrVariableOptions.GlobalWritable) { - Comment = " AOT DSO cache entries", - BeforeWriteCallback = HashAndSortDSOCache, - }; - module.Add (aot_dso_cache); module.AddGlobalVariable ("dso_names_data", dsoState.NamesBlob, LlvmIrVariableOptions.GlobalConstant); var dso_apk_entries = new LlvmIrGlobalVariable (new List> (NativeLibraries.Count), "dso_apk_entries") { @@ -417,23 +352,6 @@ protected override void Construct (LlvmIrModule module) }; module.Add (bundled_assemblies); - (runtimePropertiesData, runtimePropertyIndex, LlvmIrStringBlob runtimePropsBlob) = InitRuntimeProperties (); - var runtime_properties = new LlvmIrGlobalVariable (runtimePropertiesData, "runtime_properties", LlvmIrVariableOptions.GlobalConstant) { - Comment = "Runtime config properties", - }; - module.Add (runtime_properties); - - var runtime_properties_data = new LlvmIrGlobalVariable (runtimePropsBlob, "runtime_properties_data", LlvmIrVariableOptions.GlobalConstant) { - Comment = "Runtime config properties data", - }; - module.Add (runtime_properties_data); - - var runtime_property_index = new LlvmIrGlobalVariable (runtimePropertyIndex, "runtime_property_index", LlvmIrVariableOptions.GlobalConstant) { - Comment = "Runtime config property index, sorted on property key hash", - BeforeWriteCallback = HashAndSortRuntimePropertiesIndex, - }; - module.Add (runtime_property_index); - // HOST_PROPERTY_RUNTIME_CONTRACT will come first, our native runtime requires that since it needs // to set its value in the values array and we don't want to spend time searching for the index, nor // we want to add yet another variable storing the index to the entry. KISS. @@ -467,75 +385,6 @@ protected override void Construct (LlvmIrModule module) AddAssemblyStores (module); } - void HashAndSortRuntimePropertiesIndex (LlvmIrVariable variable, LlvmIrModuleTarget target, object? state) - { - var index = variable.Value as List>; - if (index == null) { - return; - } - - bool is64Bit = target.Is64Bit; - foreach (StructureInstance instance in index) { - if (instance.Obj == null) { - throw new InvalidOperationException ("Internal error: runtime property index must not contain null entries"); - } - - var entry = instance.Obj as RuntimePropertyIndexEntry; - if (entry == null) { - throw new InvalidOperationException ($"Internal error: runtime property index entry has unexpected type {instance.Obj.GetType ()}"); - } - - entry.key_hash = MonoAndroidHelper.GetXxHash (entry.HashedKey ?? "", is64Bit); - }; - - index.Sort ((StructureInstance a, StructureInstance b) => { - if (a.Instance == null || b.Instance == null) return 0; - return a.Instance.key_hash.CompareTo (b.Instance.key_hash); - }); - } - - ( - List> runtimeProps, - List> runtimePropsIndex, - LlvmIrStringBlob - ) InitRuntimeProperties () - { - var runtimeProps = new List> (); - var runtimePropsIndex = new List> (); - var propsBlob = new LlvmIrStringBlob (); - - if (runtimeProperties == null || runtimeProperties.Count == 0) { - return (runtimeProps, runtimePropsIndex, propsBlob); - } - - foreach (var kvp in runtimeProperties) { - string name = kvp.Key; - string value = kvp.Value; - (int name_index, _) = propsBlob.Add (name); - (int value_index, int value_size) = propsBlob.Add (value); - - var prop = new RuntimeProperty { - Key = name, - Value = value, - - key_index = (uint)name_index, - value_index = (uint)value_index, - - // Includes the terminating NUL - value_size = (uint)value_size, - }; - runtimeProps.Add (new StructureInstance (runtimePropertyStructureInfo, prop)); - - var indexEntry = new RuntimePropertyIndexEntry { - HashedKey = prop.Key, - index = (uint)(runtimeProps.Count - 1), - }; - runtimePropsIndex.Add (new StructureInstance (runtimePropertyIndexEntryStructureInfo, indexEntry)); - } - - return (runtimeProps, runtimePropsIndex, propsBlob); - } - void AddAssemblyStores (LlvmIrModule module) { ulong itemCount = (ulong)(NumberOfAssembliesInApk); @@ -584,13 +433,12 @@ void PopulateDsoApkEntries (LlvmIrVariable variable, LlvmIrModuleTarget target, throw new InvalidOperationException ($"Internal error: DSO apk entries count ({dso_apk_entries.Count}) is different to the native libraries count ({NativeLibraries.Count})."); } - bool is64Bit = target.Is64Bit; foreach (ITaskItem item in NativeLibraries) { string name = Path.GetFileName (item.ItemSpec); var entry = new DSOApkEntry { Name = name, - name_hash = MonoAndroidHelper.GetXxHash (name, is64Bit), + name_hash = TypeMapHelper.HashNameForCLR (name), offset = 0, fd = -1, }; @@ -640,7 +488,6 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob throw new InvalidOperationException ($"Internal error: DSO cache must not be empty"); } - bool is64Bit = target.Is64Bit; foreach (StructureInstance instance in cache) { if (instance.Obj == null) { throw new InvalidOperationException ("Internal error: DSO cache must not contain null entries"); @@ -651,8 +498,8 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob throw new InvalidOperationException ($"Internal error: DSO cache entry has unexpected type {instance.Obj.GetType ()}"); } - entry.hash = MonoAndroidHelper.GetXxHash (entry.HashedName ?? "", is64Bit); - entry.real_name_hash = MonoAndroidHelper.GetXxHash (entry.RealName ?? "", is64Bit); + entry.hash = TypeMapHelper.HashNameForCLR (entry.HashedName ?? ""); + entry.real_name_hash = TypeMapHelper.HashNameForCLR (entry.RealName ?? ""); } cache.Sort ((StructureInstance a, StructureInstance b) => { @@ -682,7 +529,6 @@ DsoCacheState InitDSOCache () var dsoCache = new List> (); var jniPreloads = new List (); - var aotDsoCache = new List> (); var nameMutations = new List (); var dsoNamesBlob = new LlvmIrStringBlob (); int nameMutationsCount = -1; @@ -690,6 +536,10 @@ DsoCacheState InitDSOCache () for (int i = 0; i < dsos.Count; i++) { string name = dsos[i].name; + if (name.StartsWith ("libaot-", StringComparison.OrdinalIgnoreCase)) { + throw new InvalidOperationException ($"Internal error: CoreCLR native library configuration must not use AOT DSO cache entries ('{name}')."); + } + (int nameOffset, _) = dsoNamesBlob.Add (name); bool isJniLibrary = ELFHelper.IsJniLibrary (Log, dsos[i].item.ItemSpec); @@ -715,10 +565,6 @@ DsoCacheState InitDSOCache () }; var item = new StructureInstance (dsoCacheEntryStructureInfo, entry); - if (name.StartsWith ("libaot-", StringComparison.OrdinalIgnoreCase)) { - aotDsoCache.Add (item); - continue; - } // We must add all aliases to the preloads indices array so that all of them have their handle // set when the library is preloaded. @@ -733,7 +579,6 @@ DsoCacheState InitDSOCache () return new DsoCacheState { DsoCache = dsoCache, JniPreloadDSOs = jniPreloads, - AotDsoCache = aotDsoCache, NamesBlob = dsoNamesBlob, NameMutationsCount = (uint)(nameMutationsCount <= 0 ? 1 : nameMutationsCount), }; @@ -754,11 +599,6 @@ void AddNameMutations (string name) nameMutations.Add (Path.GetFileNameWithoutExtension (name)!); } - const string aotPrefix = "libaot-"; - if (name.StartsWith (aotPrefix, StringComparison.OrdinalIgnoreCase)) { - AddNameMutations (name.Substring (aotPrefix.Length)); - } - const string libPrefix = "lib"; if (name.StartsWith (libPrefix, StringComparison.OrdinalIgnoreCase)) { AddNameMutations (name.Substring (libPrefix.Length)); @@ -775,8 +615,6 @@ void MapStructures (LlvmIrModule module) xamarinAndroidBundledAssemblyStructureInfo = module.MapStructure (); dsoCacheEntryStructureInfo = module.MapStructure (); dsoApkEntryStructureInfo = module.MapStructure (); - runtimePropertyStructureInfo = module.MapStructure (); - runtimePropertyIndexEntryStructureInfo = module.MapStructure (); appEnvironmentVariableStructureInfo = module.MapStructure (); } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreBuilder.cs b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreBuilder.cs index 2b976876b86..28bbef25a10 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreBuilder.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreBuilder.cs @@ -20,7 +20,7 @@ public AssemblyStoreBuilder (TaskLoggingHelper log, AndroidRuntime targetRuntime { this.log = log; this.targetRuntime = targetRuntime; - storeGenerator = new (log); + storeGenerator = new (log, targetRuntime); } public void AddAssembly (string assemblySourcePath, ITaskItem assemblyItem, bool includeDebugSymbols) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreGenerator.cs index bad4cacb3f2..42dace7c726 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyStoreGenerator.cs @@ -30,7 +30,7 @@ namespace Xamarin.Android.Tasks; // [INDEX_SIZE] uint; index size in bytes // // INDEX (variable size, HEADER.ENTRY_COUNT*2 entries, for assembly names with and without the extension) -// [NAME_HASH] uint on 32-bit platforms, ulong on 64-bit platforms; xxhash of the assembly name +// [NAME_HASH] uint CRC32 for CoreCLR; uint/ulong xxhash for MonoVM depending on target bitness // [DESCRIPTOR_INDEX] uint; index into in-store assembly descriptor array // [IGNORE] byte; if set to anything other than 0, the assembly is to be ignored when loading // @@ -49,12 +49,14 @@ namespace Xamarin.Android.Tasks; // partial class AssemblyStoreGenerator { - // The two constants below must match their counterparts in src/monodroid/jni/xamarin-app.hh + // The constants below must match their counterparts in src/native/*/include/xamarin-app.hh const uint ASSEMBLY_STORE_MAGIC = 0x41424158; // 'XABA', little-endian, must match the BUNDLED_ASSEMBLIES_BLOB_MAGIC native constant // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones - const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000003; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant - const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000003; + const uint ASSEMBLY_STORE_FORMAT_VERSION_MONOVM_64BIT = 0x80000003; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant + const uint ASSEMBLY_STORE_FORMAT_VERSION_MONOVM_32BIT = 0x00000003; + const uint ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_64BIT = 0x80000004; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant + const uint ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_32BIT = 0x00000004; const uint ASSEMBLY_STORE_ABI_AARCH64 = 0x00010000; const uint ASSEMBLY_STORE_ABI_ARM = 0x00020000; @@ -63,10 +65,12 @@ partial class AssemblyStoreGenerator readonly TaskLoggingHelper log; readonly Dictionary> assemblies; + readonly AndroidRuntime targetRuntime; - public AssemblyStoreGenerator (TaskLoggingHelper log) + public AssemblyStoreGenerator (TaskLoggingHelper log, AndroidRuntime targetRuntime) { this.log = log; + this.targetRuntime = targetRuntime; assemblies = new Dictionary> (); } @@ -110,6 +114,9 @@ string Generate (string baseOutputDirectory, AndroidTargetArch arch, List (); var descriptors = new List (); + bool useCrc32NameHashes = targetRuntime == AndroidRuntime.CoreCLR; + bool use64BitNameHashes = is64Bit && !useCrc32NameHashes; + uint indexEntrySize = use64BitNameHashes ? AssemblyStoreIndexEntry.NativeSize64 : AssemblyStoreIndexEntry.NativeSize32; ulong namesSize = 0; foreach (AssemblyStoreAssemblyInfo info in infos) { @@ -117,7 +124,7 @@ string Generate (string baseOutputDirectory, AndroidTargetArch arch, List is64Bit ? AssemblyStoreIndexEntry.NativeSize64 : AssemblyStoreIndexEntry.NativeSize32; + return is64Bit ? ASSEMBLY_STORE_FORMAT_VERSION_MONOVM_64BIT : ASSEMBLY_STORE_FORMAT_VERSION_MONOVM_32BIT; } void CopyData (FileInfo? src, Stream dest, string storePath) @@ -254,12 +277,12 @@ AssemblyStoreHeader ReadHeader (BinaryReader reader) } #endif - void WriteIndex (BinaryWriter writer, StreamWriter manifestWriter, List index, List descriptors, bool is64Bit) + void WriteIndex (BinaryWriter writer, StreamWriter manifestWriter, List index, List descriptors, bool use64BitNameHashes) { index.Sort ((AssemblyStoreIndexEntry a, AssemblyStoreIndexEntry b) => a.name_hash.CompareTo (b.name_hash)); foreach (AssemblyStoreIndexEntry entry in index) { - if (is64Bit) { + if (use64BitNameHashes) { writer.Write (entry.name_hash); manifestWriter.Write ($"0x{entry.name_hash:x}"); } else { @@ -295,13 +318,15 @@ List ReadIndex (BinaryReader reader, AssemblyStoreHeade var index = new List ((int)header.index_entry_count); reader.BaseStream.Seek (AssemblyStoreHeader.NativeSize, SeekOrigin.Begin); - bool is64Bit = (header.version & ASSEMBLY_STORE_FORMAT_VERSION_64BIT) == ASSEMBLY_STORE_FORMAT_VERSION_64BIT; + uint indexEntrySize = GetIndexEntrySize (header); for (int i = 0; i < (int)header.index_entry_count; i++) { ulong name_hash; - if (is64Bit) { + if (indexEntrySize == AssemblyStoreIndexEntry.NativeSize64) { name_hash = reader.ReadUInt64 (); - } else { + } else if (indexEntrySize == AssemblyStoreIndexEntry.NativeSize32) { name_hash = reader.ReadUInt32 (); + } else { + throw new InvalidOperationException ($"Assembly store index entry size {indexEntrySize} is not supported"); } uint descriptor_index = reader.ReadUInt32 (); @@ -313,6 +338,19 @@ List ReadIndex (BinaryReader reader, AssemblyStoreHeade return index; } + static uint GetIndexEntrySize (AssemblyStoreHeader header) + { + if (header.index_entry_count == 0) { + return 0; + } + + if (header.index_size % header.index_entry_count != 0) { + throw new InvalidOperationException ($"Assembly store index is corrupted: index size {header.index_size} is not evenly divisible by entry count {header.index_entry_count}."); + } + + return header.index_size / header.index_entry_count; + } + void WriteDescriptors (BinaryWriter writer, List descriptors) { foreach (AssemblyStoreEntryDescriptor desc in descriptors) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/PreservePinvokesNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/PreservePinvokesNativeAssemblyGenerator.cs index 3e6d46b104b..08308157d70 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/PreservePinvokesNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/PreservePinvokesNativeAssemblyGenerator.cs @@ -18,12 +18,12 @@ sealed class PInvoke { public readonly LlvmIrFunction NativeFunction; public readonly PinvokeScanner.PinvokeEntryInfo Info; - public readonly ulong Hash; + public readonly uint Hash; - public PInvoke (LlvmIrModule module, PinvokeScanner.PinvokeEntryInfo pinfo, bool is64Bit) + public PInvoke (LlvmIrModule module, PinvokeScanner.PinvokeEntryInfo pinfo) { Info = pinfo; - Hash = MonoAndroidHelper.GetXxHash (pinfo.EntryName, is64Bit); + Hash = TypeMapHelper.HashNameForCLR (pinfo.EntryName); // All the p/invoke functions use the same dummy signature. The only thing we care about is // a way to reference to the symbol at build time so that we can return pointer to it. For @@ -36,21 +36,19 @@ public PInvoke (LlvmIrModule module, PinvokeScanner.PinvokeEntryInfo pinfo, bool sealed class Component { public readonly string Name; - public readonly ulong NameHash; + public readonly uint NameHash; public readonly List PInvokes; - public bool Is64Bit; - public Component (string name, bool is64Bit) + public Component (string name) { Name = name; - NameHash = MonoAndroidHelper.GetXxHash (name, is64Bit); + NameHash = TypeMapHelper.HashNameForCLR (name); PInvokes = new (); - Is64Bit = is64Bit; } public void Add (LlvmIrModule module, PinvokeScanner.PinvokeEntryInfo pinfo) { - PInvokes.Add (new PInvoke (module, pinfo, Is64Bit)); + PInvokes.Add (new PInvoke (module, pinfo)); } public void Sort () @@ -132,13 +130,15 @@ protected override void Construct (LlvmIrModule module) return; } - bool is64Bit = state.TargetArch switch { - AndroidTargetArch.Arm64 => true, - AndroidTargetArch.X86_64 => true, - AndroidTargetArch.Arm => false, - AndroidTargetArch.X86 => false, - _ => throw new NotSupportedException ($"Architecture {state.TargetArch} is not supported here") - }; + switch (state.TargetArch) { + case AndroidTargetArch.Arm64: + case AndroidTargetArch.X86_64: + case AndroidTargetArch.Arm: + case AndroidTargetArch.X86: + break; + default: + throw new NotSupportedException ($"Architecture {state.TargetArch} is not supported here"); + } Log.LogDebugMessage (" Checking discovered p/invokes against the list of components"); var preservedPerComponent = new Dictionary (StringComparer.OrdinalIgnoreCase); @@ -182,7 +182,7 @@ protected override void Construct (LlvmIrModule module) } if (!preservedPerComponent.TryGetValue (pinfo.LibraryName, out Component? component)) { - component = new Component (pinfo.LibraryName, is64Bit); + component = new Component (pinfo.LibraryName); preservedPerComponent.Add (component.Name, component); } component.Add (module, pinfo); @@ -192,11 +192,7 @@ protected override void Construct (LlvmIrModule module) module.AddGlobalVariable ("__explicitly_preserved_symbols", symbolsToExplicitlyPreserve, LlvmIrVariableOptions.GlobalConstant); var components = new List (preservedPerComponent.Values); - if (is64Bit) { - AddFindPinvoke (module, components, is64Bit); - } else { - AddFindPinvoke (module, components, is64Bit); - } + AddFindPinvoke (module, components, is64Bit: false); } void AddFindPinvoke (LlvmIrModule module, List components, bool is64Bit) where T: struct diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapHelper.cs index 6f8b7b541f9..3f5bc917b6f 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapHelper.cs @@ -21,15 +21,35 @@ public static ulong HashJavaName (string name, bool is64Bit) } /// - /// Hash the given Java type name for use in java-to-managed typemap array (CoreCLR version) + /// Hash the given type name for use in CoreCLR native typemap arrays. /// - public static ulong HashJavaNameForCLR (string name, bool is64Bit) + public static unsafe uint HashNameForCLR (string name) { if (name.Length == 0) { - return UInt64.MaxValue; + return UInt32.MaxValue; + } + + int byteCount = Encoding.UTF8.GetByteCount (name); + Span buffer = byteCount <= 256 + ? stackalloc byte [byteCount] + : new byte [byteCount]; + fixed (char* pChars = name) + fixed (byte* pBuffer = buffer) { + Encoding.UTF8.GetBytes (pChars, name.Length, pBuffer, byteCount); + } + return Crc32.HashToUInt32 (buffer); + } + + /// + /// Hash the given bytes for use in CoreCLR native lookup tables. + /// + public static uint HashBytesForCLR (ReadOnlySpan bytes) + { + if (bytes.Length == 0) { + return UInt32.MaxValue; } - return HashString (name, Encoding.UTF8, is64Bit); + return Crc32.HashToUInt32 (bytes); } // Java type names are always ASCII and typically 20-100 characters, diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.cs index 4ef17765d10..db3640f204c 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.cs @@ -18,7 +18,6 @@ class TypeMappingDebugNativeAssemblyGeneratorCLR : LlvmIrComposer const string AssemblyNamesBlobSymbol = "type_map_assembly_names"; const string ManagedTypeNamesBlobSymbol = "type_map_managed_type_names"; const string JavaTypeNamesBlobSymbol = "type_map_java_type_names"; - const string TypeMapUsesHashesSymbol = "typemap_use_hashes"; const string TypeMapManagedTypeInfoSymbol = "type_map_managed_type_info"; sealed class TypeMapContextDataProvider : NativeAssemblerStructContextDataProvider @@ -73,7 +72,7 @@ public override string GetComment (object data, string fieldName) { var entry = EnsureType (data); - if (MonoAndroidHelper.StringEquals ("mvid_hash", fieldName)) { + if (MonoAndroidHelper.StringEquals ("module_uuid", fieldName)) { return $" MVID: {entry.MVID}"; } @@ -118,7 +117,7 @@ sealed class TypeMapEntry public uint from; [NativeAssembler (NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)] - public ulong from_hash; + public uint from_hash; [NativeAssembler (UsesDataProvider = true)] public uint to; @@ -174,8 +173,9 @@ sealed class TypeMapAssembly [NativeAssembler (Ignore = true)] public Guid MVID; - [NativeAssembler (UsesDataProvider = true, NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)] - public ulong mvid_hash; + [NativeAssembler (UsesDataProvider = true, InlineArray = true, InlineArraySize = 16)] + public byte[] module_uuid = []; + public ulong name_length; [NativeAssembler (UsesDataProvider = true)] @@ -224,12 +224,6 @@ protected override void Construct (LlvmIrModule module) // CoreCLR supports only 64-bit targets, so we can make things simpler by hashing all the things here instead of // in a callback during code generation - // Probability of xxHash clashes on managed type names is very low, it might be hard to find such type names that - // would create collision, so in order to be able to test the string-based managed-to-java typemaps, we check whether - // the `CI_TYPEMAP_DEBUG_USE_STRINGS` environment variable is present and not empty. If it's not in the environment - // or its value is an empty string, we default to using hashes for the managed-to-java type maps. - bool typemap_uses_hashes = String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("CI_TYPEMAP_DEBUG_USE_STRINGS")); - var usedHashes = new Dictionary (); foreach (TypeMapGenerator.TypeMapDebugEntry entry in data.ManagedToJavaMap) { (int managedTypeNameOffset, int _) = managedTypeNames.Add (entry.ManagedName); (int javaTypeNameOffset, int _) = javaTypeNames.Add (entry.JavaName); @@ -238,41 +232,24 @@ protected override void Construct (LlvmIrModule module) To = entry.JavaName, from = (uint)managedTypeNameOffset, - from_hash = typemap_uses_hashes ? MonoAndroidHelper.GetXxHash (entry.ManagedName, is64Bit: true) : 0, + from_hash = TypeMapHelper.HashNameForCLR (entry.ManagedName), to = (uint)javaTypeNameOffset, }; managedToJavaMap.Add (new StructureInstance (typeMapEntryStructureInfo, m2j)); + } - if (!typemap_uses_hashes) { - continue; + // Input is sorted on name, we need to re-sort it on hashes. + managedToJavaMap.Sort ((StructureInstance a, StructureInstance b) => { + if (a.Instance == null) { + return b.Instance == null ? 0 : -1; } - if (usedHashes.ContainsKey (m2j.from_hash)) { - typemap_uses_hashes = false; - // It could be a warning, but it's not really actionable - users might not be able to rename the clashing types - Log.LogMessage ($"Detected xxHash conflict between managed type names '{entry.ManagedName}' and '{usedHashes[m2j.from_hash]}' when mapping to Java type '{entry.JavaName}'."); - } else { - usedHashes[m2j.from_hash] = entry.ManagedName; + if (b.Instance == null) { + return 1; } - } - // Input is sorted on name, we need to re-sort it on hashes, if used - if (typemap_uses_hashes) { - managedToJavaMap.Sort ((StructureInstance a, StructureInstance b) => { - if (a.Instance == null) { - return b.Instance == null ? 0 : -1; - } - - if (b.Instance == null) { - return 1; - } - - return a.Instance.from_hash.CompareTo (b.Instance.from_hash); - }); - } - if (!typemap_uses_hashes) { - Log.LogMessage ("Managed-to-java typemaps will use string-based matching."); - } + return a.Instance.from_hash.CompareTo (b.Instance.from_hash); + }); var assemblyNamesBlob = new LlvmIrStringBlob (); foreach (TypeMapGenerator.TypeMapDebugAssembly asm in data.UniqueAssemblies) { @@ -282,7 +259,7 @@ protected override void Construct (LlvmIrModule module) Name = asm.Name, MVID = asm.MVID, - mvid_hash = MonoAndroidHelper.GetXxHash (asm.MVIDBytes, is64Bit: true), + module_uuid = asm.MVIDBytes, name_length = (ulong)assemblyNameLength, // without the trailing NUL name_offset = (ulong)assemblyNameOffset, }; @@ -298,7 +275,7 @@ protected override void Construct (LlvmIrModule module) return 1; } - return a.Instance.mvid_hash.CompareTo (b.Instance.mvid_hash); + return a.Instance.module_uuid.AsSpan ().SequenceCompareTo (b.Instance.module_uuid); }); var managedTypeInfos = new List> (); @@ -346,7 +323,6 @@ protected override void Construct (LlvmIrModule module) module.AddGlobalVariable (ManagedToJavaSymbol, managedToJavaMap, LlvmIrVariableOptions.LocalConstant); module.AddGlobalVariable (JavaToManagedSymbol, javaToManagedMap, LlvmIrVariableOptions.LocalConstant); module.AddGlobalVariable (TypeMapManagedTypeInfoSymbol, managedTypeInfos, LlvmIrVariableOptions.GlobalConstant); - module.AddGlobalVariable (TypeMapUsesHashesSymbol, typemap_uses_hashes, LlvmIrVariableOptions.GlobalConstant); module.AddGlobalVariable (UniqueAssembliesSymbol, uniqueAssemblies, LlvmIrVariableOptions.GlobalConstant); module.AddGlobalVariable (AssemblyNamesBlobSymbol, assemblyNamesBlob, LlvmIrVariableOptions.GlobalConstant); module.AddGlobalVariable (ManagedTypeNamesBlobSymbol, managedTypeNames, LlvmIrVariableOptions.GlobalConstant); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGeneratorCLR.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGeneratorCLR.cs index 284868e2bf8..b60b607d897 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGeneratorCLR.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGeneratorCLR.cs @@ -1,7 +1,6 @@ #nullable disable using System; -using System.Collections; using System.Collections.Generic; using Microsoft.Build.Utilities; @@ -69,8 +68,7 @@ public override string GetComment (object data, string fieldName) { var module_map_entry = EnsureType (data); - if (MonoAndroidHelper.StringEquals ("managed_type_name_hash_32", fieldName) || - MonoAndroidHelper.StringEquals ("managed_type_name_hash_64", fieldName)) { + if (MonoAndroidHelper.StringEquals ("managed_type_name_hash", fieldName)) { return $" managed type name: {module_map_entry.ManagedTypeName}"; } @@ -93,11 +91,13 @@ sealed class TypeMapModuleEntry [NativeAssembler (Ignore = true)] public string ManagedTypeName; - [NativeAssembler (UsesDataProvider = true, ValidTarget = NativeAssemblerValidTarget.ThirtyTwoBit, MemberName = "managed_type_name_hash", NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)] - public uint managed_type_name_hash_32; + [NativeAssembler (NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)] + public uint managed_type_name_hash; - [NativeAssembler (UsesDataProvider = true, ValidTarget = NativeAssemblerValidTarget.SixtyFourBit, MemberName = "managed_type_name_hash", NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)] - public ulong managed_type_name_hash_64; + [NativeAssembler (UsesDataProvider = true)] + public uint managed_type_name_index; + + public uint managed_type_name_length; [NativeAssembler (UsesDataProvider = true)] public uint java_map_index; @@ -145,7 +145,7 @@ sealed class TypeMapJava public string ManagedTypeName; [NativeAssembler (Ignore = true)] - public ulong JavaNameHash; + public uint JavaNameHash; public uint module_index; @@ -175,7 +175,12 @@ sealed class JavaNameHashComparer : IComparer> { public int Compare (StructureInstance a, StructureInstance b) { - return a.Instance.JavaNameHash.CompareTo (b.Instance.JavaNameHash); + int hashCompare = a.Instance.JavaNameHash.CompareTo (b.Instance.JavaNameHash); + if (hashCompare != 0) { + return hashCompare; + } + + return StringComparer.Ordinal.Compare (a.Instance.JavaName, b.Instance.JavaName); } } @@ -241,7 +246,7 @@ protected override void Construct (LlvmIrModule module) // Java hashes are output before Java type map **and** managed modules, because they will also sort the Java map for us. // This is not strictly necessary, as we could do the sorting in the java map BeforeWriteCallback, but this way we save // time sorting only once. - var java_to_managed_hashes = new LlvmIrGlobalVariable (typeof(List), "java_to_managed_hashes") { + var java_to_managed_hashes = new LlvmIrGlobalVariable (typeof(List), "java_to_managed_hashes") { Comment = " Java types name hashes", BeforeWriteCallback = GenerateAndSortJavaHashes, BeforeWriteCallbackCallerState = cs, @@ -278,15 +283,18 @@ void SortEntriesAndUpdateJavaIndexes (LlvmIrVariable variable, LlvmIrModuleTarge var array = (LlvmIrSectionedArray>)variable.Value; foreach (LlvmIrArraySection> section in array.Sections) { - if (target.Is64Bit) { - section.Data.Sort ( - (object a, object b) => ((StructureInstance)a).Instance.managed_type_name_hash_64.CompareTo (((StructureInstance)b).Instance.managed_type_name_hash_64) - ); - } else { - section.Data.Sort ( - (object a, object b) => ((StructureInstance)a).Instance.managed_type_name_hash_32.CompareTo (((StructureInstance)b).Instance.managed_type_name_hash_32) - ); - } + section.Data.Sort ( + (object a, object b) => { + var entryA = ((StructureInstance)a).Instance; + var entryB = ((StructureInstance)b).Instance; + int hashCompare = entryA.managed_type_name_hash.CompareTo (entryB.managed_type_name_hash); + if (hashCompare != 0) { + return hashCompare; + } + + return StringComparer.Ordinal.Compare (entryA.ManagedTypeName, entryB.ManagedTypeName); + } + ); foreach (StructureInstance entry in section.Data) { entry.Instance.java_map_index = GetJavaEntryIndex (entry.Instance.JavaTypeMapEntry); @@ -322,30 +330,17 @@ void GenerateAndSortJavaHashes (LlvmIrVariable variable, LlvmIrModuleTarget targ for (int i = 0; i < cs.JavaMap.Count; i++) { TypeMapJava entry = cs.JavaMap[i].Instance; - entry.JavaNameHash = TypeMapHelper.HashJavaNameForCLR (entry.JavaName, target.Is64Bit); + entry.JavaNameHash = TypeMapHelper.HashNameForCLR (entry.JavaName); } - cs.JavaMap.Sort ((StructureInstance a, StructureInstance b) => a.Instance.JavaNameHash.CompareTo (b.Instance.JavaNameHash)); + cs.JavaMap.Sort (javaNameHashComparer); - Type listType; - IList hashes; - if (target.Is64Bit) { - listType = typeof(List); - var list = new List (); - foreach (StructureInstance si in cs.JavaMap) { - list.Add (si.Instance.JavaNameHash); - } - hashes = list; - } else { - listType = typeof(List); - var list = new List (); - foreach (StructureInstance si in cs.JavaMap) { - list.Add ((uint)si.Instance.JavaNameHash); - } - hashes = list; + var hashes = new List (); + foreach (StructureInstance si in cs.JavaMap) { + hashes.Add (si.Instance.JavaNameHash); } - gv.OverrideTypeAndValue (listType, hashes); + gv.OverrideTypeAndValue (typeof(List), hashes); } ConstructionState EnsureConstructionState (object? callerState) @@ -450,12 +445,14 @@ void PrepareMapModuleData (IEnumerable mod throw new InvalidOperationException ($"Internal error: Java type '{entry.JavaName}' not found in cache"); } + (int managedTypeNameIndex, int managedTypeNameLength) = cs.ManagedTypeNamesBlob.Add (entry.ManagedTypeName); var map_entry = new TypeMapModuleEntry { JavaTypeMapEntry = javaType, ManagedTypeName = entry.ManagedTypeName, - managed_type_name_hash_32 = (uint)MonoAndroidHelper.GetXxHash (entry.ManagedTypeName, is64Bit: false), - managed_type_name_hash_64 = MonoAndroidHelper.GetXxHash (entry.ManagedTypeName, is64Bit: true), + managed_type_name_hash = TypeMapHelper.HashNameForCLR (entry.ManagedTypeName), + managed_type_name_index = (uint)managedTypeNameIndex, + managed_type_name_length = (uint)managedTypeNameLength, java_map_index = UInt32.MaxValue, // will be set later, when the target is known }; moduleSection.Add (new StructureInstance (typeMapModuleEntryStructureInfo, map_entry)); diff --git a/src/native/CMakeLists.txt b/src/native/CMakeLists.txt index 26139682952..5de34c2f2de 100644 --- a/src/native/CMakeLists.txt +++ b/src/native/CMakeLists.txt @@ -248,7 +248,6 @@ endif() set(JAVA_INTEROP_INCLUDE_DIR ${JAVA_INTEROP_SRC_PATH}) set(CONSTEXPR_XXH3_DIR "${EXTERNAL_DIR}/constexpr-xxh3") -set(XXHASH_DIR "${EXTERNAL_DIR}/xxHash") include_directories(common/include) @@ -260,8 +259,6 @@ if(IS_CLR_RUNTIME) ${TARGET} PRIVATE ${XA_RUNTIME_INCLUDE_DIR} - ${EXTERNAL_DIR} - ${CONSTEXPR_XXH3_DIR} ${RUNTIME_INCLUDE_DIR} ) endmacro() diff --git a/src/native/clr/host/CMakeLists.txt b/src/native/clr/host/CMakeLists.txt index e18d1a2531d..9a1beec7b75 100644 --- a/src/native/clr/host/CMakeLists.txt +++ b/src/native/clr/host/CMakeLists.txt @@ -118,7 +118,6 @@ macro(lib_target_options TARGET_NAME) ${TARGET_NAME} BEFORE PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include - ${EXTERNAL_DIR} ${ROBIN_MAP_DIR}/include ) diff --git a/src/native/clr/host/assembly-store.cc b/src/native/clr/host/assembly-store.cc index 2fdc7119d15..45722d29276 100644 --- a/src/native/clr/host/assembly-store.cc +++ b/src/native/clr/host/assembly-store.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -182,7 +183,7 @@ auto AssemblyStore::find_assembly_store_entry (hash_t hash, const AssemblyStoreI { auto equal = [](AssemblyStoreIndexEntry const& entry, hash_t key) -> bool { return entry.name_hash == key; }; auto less_than = [](AssemblyStoreIndexEntry const& entry, hash_t key) -> bool { return entry.name_hash < key; }; - ssize_t idx = Search::binary_search (hash, entries, entry_count); + ssize_t idx = Search::binary_search (hash, entries, entry_count); if (idx >= 0) { return &entries[idx]; } @@ -191,7 +192,7 @@ auto AssemblyStore::find_assembly_store_entry (hash_t hash, const AssemblyStoreI auto AssemblyStore::open_assembly (std::string_view const& name, int64_t &size) noexcept -> void* { - hash_t name_hash = xxhash::hash (name.data (), name.length ()); + hash_t name_hash = crc32_hash (name); if constexpr (Constants::is_debug_build) { // In fastdev mode we might not have any assembly store. diff --git a/src/native/clr/host/host.cc b/src/native/clr/host/host.cc index 3e84ef930d8..b07c54466e2 100644 --- a/src/native/clr/host/host.cc +++ b/src/native/clr/host/host.cc @@ -22,13 +22,12 @@ #include #include #include +#include #include #include #include -#include #include #include -#include #include using namespace xamarin::android; @@ -38,56 +37,6 @@ void Host::clr_error_writer (const char *message) noexcept log_error (LOG_DEFAULT, "CLR error: {}", optional_string (message)); } -size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size_t value_buffer_size, [[maybe_unused]] void *contract_context) noexcept -{ - // NOTE: this code was tested locally, but it's **not** used by CoreCLR yet, so there's been no - // "live" testing. - log_debug (LOG_DEFAULT, "clr_get_runtime_property (\"{}\"...)"sv, optional_string (key)); - if (application_config.number_of_runtime_properties == 0) [[unlikely]] { - log_debug (LOG_DEFAULT, "No runtime properties defined"sv); - return 0; - } - - // value_buffer_size must have enough space for at least 1 character + the terminating NUL - if (key == nullptr || value_buffer == nullptr || value_buffer_size <= 1) [[unlikely]] { - log_warn ( - LOG_DEFAULT, - "runtime property retrieval API called with invalid arguments. key == {:p}; value_buffer == {:p}; value_buffer_size == {}"sv, - static_cast(key), - static_cast(value_buffer), - value_buffer_size - ); - return 0; - } - - hash_t key_hash = xxhash::hash (key, strlen (key)); - - auto equal = [](RuntimePropertyIndexEntry const& entry, hash_t key) -> bool { return entry.key_hash == key; }; - auto less_than = [](RuntimePropertyIndexEntry const& entry, hash_t key) -> bool { return entry.key_hash < key; }; - ssize_t idx = Search::binary_search (key_hash, runtime_property_index, application_config.number_of_runtime_properties); - if (idx < 0) { - log_debug (LOG_DEFAULT, "Runtime property '{}' not found"sv, key); - return 0; - } - - RuntimePropertyIndexEntry const& idx_entry = runtime_property_index[idx]; - RuntimeProperty const& prop = runtime_properties[idx_entry.index]; - - // `value_size` includes the terminating NUL - if (prop.value_size > value_buffer_size) { - log_warn ( - LOG_DEFAULT, - "Value of property '{}' is longer than available buffer space. Need {}b, available {}b"sv, - key, - prop.value_size, - value_buffer_size - ); - } - - strncpy (value_buffer, &runtime_properties_data[prop.value_index], value_buffer_size); - return std::min (static_cast(prop.value_size - 1), value_buffer_size - 1); -} - bool Host::clr_external_assembly_probe (const char *path, void **data_start, int64_t *size) noexcept { // TODO: `path` might be a full path, make sure it isn't @@ -154,7 +103,7 @@ auto Host::zip_scan_callback (std::string_view const& apk_path, int apk_fd, dyna log_debug (LOG_ASSEMBLY, "Found shared library in '{}': {}"sv, apk_path, entry_name.get ()); std::string_view lib_name { entry_name.get () + Zip::lib_prefix.length () }; - hash_t name_hash = xxhash::hash (lib_name.data (), lib_name.length ()); + hash_t name_hash = crc32_hash (lib_name); log_debug (LOG_ASSEMBLY, "Library name is: {}; hash == 0x{:x}", lib_name, name_hash); DSOApkEntry *apk_entry = MonodroidDl::find_dso_apk_entry (name_hash); diff --git a/src/native/clr/host/typemap.cc b/src/native/clr/host/typemap.cc index 8a52077d360..020ec84669a 100644 --- a/src/native/clr/host/typemap.cc +++ b/src/native/clr/host/typemap.cc @@ -1,15 +1,23 @@ #include +#include +#include #include +#include #include #include #include -#include #include using namespace xamarin::android; namespace { + [[gnu::always_inline]] + auto same_string (const char *value, size_t value_length, const char *key, size_t key_length) noexcept -> bool + { + return value_length == key_length && strncmp (value, key, key_length) == 0; + } + class MonoGuidString { static inline constexpr size_t MVID_SIZE = 16; @@ -93,29 +101,30 @@ auto TypeMapper::find_index_by_name (const char *typeName, const TypeMapEntry *m [[gnu::always_inline, gnu::flatten]] auto TypeMapper::find_index_by_hash (const char *typeName, const TypeMapEntry *map, const char (&name_map)[], std::string_view const& from_name, std::string_view const& to_name) noexcept -> ssize_t { - if (!typemap_use_hashes) [[unlikely]] { - return find_index_by_name (typeName, map, name_map, from_name, to_name); - } - log_debug (LOG_ASSEMBLY, "typemap: map {} -> {} uses hashes"sv, from_name, to_name); - auto equal = [](TypeMapEntry const& entry, hash_t key) -> bool { - if (entry.from == std::numeric_limits::max ()) [[unlikely]] { - return 1; - } + size_t type_name_length = strlen (typeName); + hash_t type_name_hash = crc32_hash (typeName, type_name_length); - return entry.from_hash == key; + auto less_than = [](TypeMapEntry const& entry, hash_t key) -> bool { + return entry.from != std::numeric_limits::max () && entry.from_hash < key; }; - auto less_than = [](TypeMapEntry const& entry, hash_t key) -> bool { - if (entry.from == std::numeric_limits::max ()) [[unlikely]] { - return 1; + size_t idx = Search::lower_bound (type_name_hash, map, type_map.entry_count); + while (idx < type_map.entry_count) { + TypeMapEntry const& entry = map[idx]; + if (entry.from == std::numeric_limits::max () || entry.from_hash != type_name_hash) { + break; } - return entry.from_hash < key; - }; - hash_t type_name_hash = xxhash::hash (typeName, strlen (typeName)); - return Search::binary_search (type_name_hash, map, type_map.entry_count); + const char *mapped_type_name = &name_map[entry.from]; + if (same_string (mapped_type_name, strlen (mapped_type_name), typeName, type_name_length)) { + return static_cast(idx); + } + idx++; + } + + return -1z; } [[gnu::always_inline, gnu::flatten]] @@ -146,11 +155,9 @@ auto TypeMapper::managed_to_java_debug (const char *typeName, const uint8_t *mvi dynamic_local_path_string full_type_name; full_type_name.append (typeName); - hash_t mvid_hash = xxhash::hash (reinterpret_cast(mvid), 16z); // we must hope managed land called us with valid data - - auto equal = [](TypeMapAssembly const& entry, hash_t key) -> bool { return entry.mvid_hash == key; }; - auto less_than = [](TypeMapAssembly const& entry, hash_t key) -> bool { return entry.mvid_hash < key; }; - ssize_t idx = Search::binary_search (mvid_hash, type_map_unique_assemblies, type_map.unique_assemblies_count); + auto equal = [](TypeMapAssembly const& entry, const uint8_t *key) -> bool { return memcmp (entry.module_uuid, key, sizeof(entry.module_uuid)) == 0; }; + auto less_than = [](TypeMapAssembly const& entry, const uint8_t *key) -> bool { return memcmp (entry.module_uuid, key, sizeof(entry.module_uuid)) < 0; }; + ssize_t idx = Search::binary_search (mvid, type_map_unique_assemblies, type_map.unique_assemblies_count); if (idx >= 0) [[likely]] { TypeMapAssembly const& assm = type_map_unique_assemblies[idx]; @@ -162,11 +169,8 @@ auto TypeMapper::managed_to_java_debug (const char *typeName, const uint8_t *mvi log_warn (LOG_ASSEMBLY, "typemap: unable to look up assembly name for type '{}', trying without it."sv, typeName); } - // If hashes are used for matching, the type names array is not used. If, however, string-based matching is in - // effect, the managed type name is looked up and then... idx = find_index_by_hash (full_type_name.get (), type_map.managed_to_java, type_map_managed_type_names, MANAGED, JAVA); - // ...either method gives us index into the Java type names array return index_to_name (idx, full_type_name.get (), type_map.managed_to_java, type_map_java_type_names, MANAGED, JAVA); } #endif // def DEBUG @@ -196,17 +200,24 @@ auto TypeMapper::find_module_entry (const uint8_t *mvid, const TypeMapModule *en } [[gnu::always_inline]] -auto TypeMapper::find_managed_to_java_map_entry (hash_t name_hash, const TypeMapModuleEntry *map, size_t entry_count) noexcept -> const TypeMapModuleEntry* +auto TypeMapper::find_managed_to_java_map_entry (hash_t name_hash, const char *type_name, size_t type_name_length, const TypeMapModuleEntry *map, size_t entry_count) noexcept -> const TypeMapModuleEntry* { if (map == nullptr) { return nullptr; }; - auto equal = [](TypeMapModuleEntry const& entry, hash_t key) -> bool { return entry.managed_type_name_hash == key; }; - auto less_than = [](TypeMapModuleEntry const& entry, hash_t key) -> bool { return entry.managed_type_name_hash < key; }; - ssize_t idx = Search::binary_search (name_hash, map, entry_count); - if (idx >= 0) [[likely]] { - return &map[idx]; + auto less_than = [](TypeMapModuleEntry const& entry, hash_t key) -> bool { + return entry.managed_type_name_hash < key; + }; + + size_t idx = Search::lower_bound (name_hash, map, entry_count); + while (idx < entry_count && map[idx].managed_type_name_hash == name_hash) { + TypeMapModuleEntry const& entry = map[idx]; + const char *managed_type_name = &managed_type_names[entry.managed_type_name_index]; + if (same_string (managed_type_name, entry.managed_type_name_length, type_name, type_name_length)) { + return &entry; + } + idx++; } return nullptr; @@ -226,12 +237,13 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m } log_debug (LOG_ASSEMBLY, "typemap: found module matching MVID [{}]"sv, MonoGuidString (mvid).c_str ()); - hash_t name_hash = xxhash::hash (typeName, strlen (typeName)); + size_t type_name_length = strlen (typeName); + hash_t name_hash = crc32_hash (typeName, type_name_length); // We implicitly trust the build process that the indexes are correct. This is by design, the libxamarin-app.so built // with the application is immutable and the build process made sure that the data in it matches the application. const TypeMapModuleEntry *const map = &modules_map_data[match->map_index]; - const TypeMapModuleEntry *entry = find_managed_to_java_map_entry (name_hash, map, match->entry_count); + const TypeMapModuleEntry *entry = find_managed_to_java_map_entry (name_hash, typeName, type_name_length, map, match->entry_count); if (entry == nullptr) [[unlikely]] { if (match->duplicate_count > 0 && match->duplicate_map_index < std::numeric_limitsduplicate_map_index)>::max ()) { log_debug ( @@ -243,7 +255,7 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m ); const TypeMapModuleEntry *const duplicate_map = &modules_duplicates_data[match->duplicate_map_index]; - entry = find_managed_to_java_map_entry (name_hash, duplicate_map, match->duplicate_count); + entry = find_managed_to_java_map_entry (name_hash, typeName, type_name_length, duplicate_map, match->duplicate_count); } if (entry == nullptr) { @@ -375,14 +387,23 @@ auto TypeMapper::java_to_managed_debug (const char *java_type_name, char const** #else // def DEBUG [[gnu::always_inline]] -auto TypeMapper::find_java_to_managed_entry (hash_t name_hash) noexcept -> const TypeMapJava* +auto TypeMapper::find_java_to_managed_entry (hash_t name_hash, const char *java_type_name, size_t java_type_name_length) noexcept -> const TypeMapJava* { - ssize_t idx = Search::binary_search (name_hash, java_to_managed_hashes, java_type_count); - if (idx < 0) [[unlikely]] { - return nullptr; + auto less_than = [](hash_t const& entry, hash_t key) -> bool { + return entry < key; + }; + + size_t idx = Search::lower_bound (name_hash, java_to_managed_hashes, java_type_count); + while (idx < java_type_count && java_to_managed_hashes[idx] == name_hash) { + TypeMapJava const& entry = java_to_managed_map[idx]; + const char *mapped_java_type_name = &java_type_names[entry.java_name_index]; + if (same_string (mapped_java_type_name, entry.java_name_length, java_type_name, java_type_name_length)) { + return &entry; + } + idx++; } - return &java_to_managed_map[idx]; + return nullptr; } [[gnu::flatten]] @@ -419,8 +440,9 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const return false; } - hash_t name_hash = xxhash::hash (java_type_name, strlen (java_type_name)); - TypeMapJava const* java_entry = find_java_to_managed_entry (name_hash); + size_t java_type_name_length = strlen (java_type_name); + hash_t name_hash = crc32_hash (java_type_name, java_type_name_length); + TypeMapJava const* java_entry = find_java_to_managed_entry (name_hash, java_type_name, java_type_name_length); if (java_entry == nullptr) { log_info ( LOG_ASSEMBLY, diff --git a/src/native/clr/include/host/host.hh b/src/native/clr/include/host/host.hh index 3537c9310d7..98e7007725a 100644 --- a/src/native/clr/include/host/host.hh +++ b/src/native/clr/include/host/host.hh @@ -38,7 +38,6 @@ namespace xamarin::android { static void gather_assemblies_and_libraries (jstring_array_wrapper& runtimeApks, bool have_split_apks); static void scan_filesystem_for_assemblies_and_libraries () noexcept; - static size_t clr_get_runtime_property (const char *key, char *value_buffer, size_t value_buffer_size, void *contract_context) noexcept; static bool clr_external_assembly_probe (const char *path, void **data_start, int64_t *size) noexcept; static const void* clr_pinvoke_override (const char *library_name, const char *entry_point_name) noexcept; static void clr_error_writer (const char *message) noexcept; @@ -62,7 +61,7 @@ namespace xamarin::android { static inline host_runtime_contract runtime_contract{ .size = sizeof(host_runtime_contract), .context = nullptr, - .get_runtime_property = clr_get_runtime_property, + .get_runtime_property = nullptr, .bundle_probe = nullptr, .pinvoke_override = clr_pinvoke_override, .external_assembly_probe = clr_external_assembly_probe, diff --git a/src/native/clr/include/host/pinvoke-override-impl.hh b/src/native/clr/include/host/pinvoke-override-impl.hh index 2f2b9498394..c7621108b18 100644 --- a/src/native/clr/include/host/pinvoke-override-impl.hh +++ b/src/native/clr/include/host/pinvoke-override-impl.hh @@ -19,9 +19,6 @@ namespace xamarin::android { void *lib_handle = dso_handle == nullptr ? nullptr : *dso_handle; if (lib_handle == nullptr) { - // We're being called as part of the p/invoke mechanism, we don't need to look in the AOT cache - constexpr bool PREFER_AOT_CACHE = false; - // Handle p/invokes of the form [DllImport ("liblog")] or [DllImport ("log")] // TODO: try modifying the name to contain both the `log` prefix and the `.so` suffix dynamic_local_path_string short_library_name; @@ -35,11 +32,11 @@ namespace xamarin::android { } log_debug (LOG_ASSEMBLY, "Modified p/invoke library name to '{}'", short_library_name.get ()); - lib_handle = MonodroidDl::monodroid_dlopen (short_library_name.get (), microsoft::java_interop::JAVA_INTEROP_LIB_LOAD_LOCALLY); + lib_handle = MonodroidDl::monodroid_dlopen (short_library_name.get (), microsoft::java_interop::JAVA_INTEROP_LIB_LOAD_LOCALLY); } if (lib_handle == nullptr) { - lib_handle = MonodroidDl::monodroid_dlopen (library_name, microsoft::java_interop::JAVA_INTEROP_LIB_LOAD_LOCALLY); + lib_handle = MonodroidDl::monodroid_dlopen (library_name, microsoft::java_interop::JAVA_INTEROP_LIB_LOAD_LOCALLY); } if (lib_handle == nullptr) { @@ -85,9 +82,9 @@ namespace xamarin::android { } PINVOKE_OVERRIDE_INLINE - auto PinvokeOverride::fetch_or_create_pinvoke_map_entry (std::string const& library_name, std::string const& entrypoint_name, hash_t entrypoint_name_hash, pinvoke_api_map_ptr api_map, bool need_lock) noexcept -> void* + auto PinvokeOverride::fetch_or_create_pinvoke_map_entry (std::string const& library_name, std::string const& entrypoint_name, pinvoke_api_map_ptr api_map, bool need_lock) noexcept -> void* { - auto iter = api_map->find (entrypoint_name, entrypoint_name_hash); + auto iter = api_map->find (entrypoint_name); if (iter != api_map->end () && iter->second != nullptr) { return iter->second; } @@ -122,19 +119,19 @@ namespace xamarin::android { } PINVOKE_OVERRIDE_INLINE - auto PinvokeOverride::handle_other_pinvoke_request (std::string_view const& library_name, hash_t library_name_hash, std::string_view const& entrypoint_name, hash_t entrypoint_name_hash) noexcept -> void* + auto PinvokeOverride::handle_other_pinvoke_request (std::string_view const& library_name, std::string_view const& entrypoint_name) noexcept -> void* { std::string lib_name {library_name}; std::string entry_name {entrypoint_name}; - auto iter = other_pinvoke_map.find (lib_name, library_name_hash); + auto iter = other_pinvoke_map.find (lib_name); void *handle = nullptr; if (iter == other_pinvoke_map.end ()) { StartupAwareLock lock (pinvoke_map_write_lock); pinvoke_api_map_ptr lib_map; // Make sure some other thread hasn't just added the map - iter = other_pinvoke_map.find (lib_name, library_name_hash); + iter = other_pinvoke_map.find (lib_name); if (iter == other_pinvoke_map.end () || iter->second == nullptr) { lib_map = new pinvoke_api_map (1); other_pinvoke_map[lib_name] = lib_map; @@ -142,14 +139,14 @@ namespace xamarin::android { lib_map = iter->second; } - handle = fetch_or_create_pinvoke_map_entry (lib_name, entry_name, entrypoint_name_hash, lib_map, /* need_lock */ false); + handle = fetch_or_create_pinvoke_map_entry (lib_name, entry_name, lib_map, /* need_lock */ false); } else { if (iter->second == nullptr) [[unlikely]] { log_warn (LOG_ASSEMBLY, "Internal error: null entry in p/invoke map for key '{}'", library_name); return nullptr; // fall back to `monodroid_dlopen` } - handle = fetch_or_create_pinvoke_map_entry (lib_name, entry_name, entrypoint_name_hash, iter->second, /* need_lock */ true); + handle = fetch_or_create_pinvoke_map_entry (lib_name, entry_name, iter->second, /* need_lock */ true); } return handle; diff --git a/src/native/clr/include/host/pinvoke-override.hh b/src/native/clr/include/host/pinvoke-override.hh index 099c5264669..1a80acdd6e1 100644 --- a/src/native/clr/include/host/pinvoke-override.hh +++ b/src/native/clr/include/host/pinvoke-override.hh @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -31,7 +32,7 @@ #endif #include "../runtime-base/monodroid-dl.hh" -#include +#include namespace xamarin::android { struct PinvokeEntry @@ -44,9 +45,9 @@ namespace xamarin::android { struct string_hash { [[gnu::always_inline]] - xamarin::android::hash_t operator() (std::string const& s) const noexcept + size_t operator() (std::string const& s) const noexcept { - return xamarin::android::xxhash::hash (s.c_str (), s.length ()); + return xamarin::android::crc32_hash (s.c_str (), s.length ()); } }; @@ -76,9 +77,9 @@ namespace xamarin::android { public: static auto load_library_symbol (std::string_view const& library_name, std::string_view const& symbol_name, void **dso_handle = nullptr) noexcept -> void*; static auto load_library_entry (std::string const& library_name, std::string const& entrypoint_name, pinvoke_api_map_ptr api_map) noexcept -> void*; - static auto fetch_or_create_pinvoke_map_entry (std::string const& library_name, std::string const& entrypoint_name, hash_t entrypoint_name_hash, pinvoke_api_map_ptr api_map, bool need_lock) noexcept -> void*; + static auto fetch_or_create_pinvoke_map_entry (std::string const& library_name, std::string const& entrypoint_name, pinvoke_api_map_ptr api_map, bool need_lock) noexcept -> void*; static auto find_pinvoke_address (hash_t hash, const PinvokeEntry *entries, size_t entry_count) noexcept -> PinvokeEntry*; - static auto handle_other_pinvoke_request (std::string_view const& library_name, hash_t library_name_hash, std::string_view const& entrypoint_name, hash_t entrypoint_name_hash) noexcept -> void*; + static auto handle_other_pinvoke_request (std::string_view const& library_name, std::string_view const& entrypoint_name) noexcept -> void*; static void handle_jni_on_load (JavaVM *vm, void *reserved) noexcept; static auto monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name) noexcept -> void*; diff --git a/src/native/clr/include/host/typemap.hh b/src/native/clr/include/host/typemap.hh index eea9564cc98..6a3ee0565e5 100644 --- a/src/native/clr/include/host/typemap.hh +++ b/src/native/clr/include/host/typemap.hh @@ -4,7 +4,6 @@ #include #include "../runtime-base/logger.hh" -#include #include "../xamarin-app.hh" namespace xamarin::android { @@ -21,11 +20,11 @@ namespace xamarin::android { #if defined(RELEASE) static auto compare_mvid (const uint8_t *mvid, TypeMapModule const& module) noexcept -> int; static auto find_module_entry (const uint8_t *mvid, const TypeMapModule *entries, size_t entry_count) noexcept -> const TypeMapModule*; - static auto find_managed_to_java_map_entry (hash_t name_hash, const TypeMapModuleEntry *map, size_t entry_count) noexcept -> const TypeMapModuleEntry*; + static auto find_managed_to_java_map_entry (hash_t name_hash, const char *type_name, size_t type_name_length, const TypeMapModuleEntry *map, size_t entry_count) noexcept -> const TypeMapModuleEntry*; static auto managed_to_java_release (const char *typeName, const uint8_t *mvid) noexcept -> const char*; static auto java_to_managed_release (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_token_id) noexcept -> bool; - static auto find_java_to_managed_entry (hash_t name_hash) noexcept -> const TypeMapJava*; + static auto find_java_to_managed_entry (hash_t name_hash, const char *java_type_name, size_t java_type_name_length) noexcept -> const TypeMapJava*; #else static auto index_to_name (ssize_t index, const char *typeName, const TypeMapEntry *map, const char (&name_map)[], std::string_view const& from_name, std::string_view const& to_name) -> const char*; static auto find_index_by_hash (const char *typeName, const TypeMapEntry *map, const char (&name_map)[], std::string_view const& from_name, std::string_view const& to_name) noexcept -> ssize_t; diff --git a/src/native/clr/include/runtime-base/crc32.hh b/src/native/clr/include/runtime-base/crc32.hh new file mode 100644 index 00000000000..11f3dcf3157 --- /dev/null +++ b/src/native/clr/include/runtime-base/crc32.hh @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include + +namespace xamarin::android { + using hash_t = uint32_t; + static constexpr hash_t CRC32_POLYNOMIAL = 0xedb88320; + + [[gnu::always_inline]] + constexpr auto crc32_hash (const char *value, size_t len) noexcept -> hash_t + { + if (len == 0) [[unlikely]] { + return std::numeric_limits::max (); + } + + hash_t crc = 0xffffffff; + for (size_t i = 0; i < len; i++) { + crc ^= static_cast(value [i]); + for (size_t bit = 0; bit < 8; bit++) { + crc = (crc >> 1) ^ ((crc & 1) != 0 ? CRC32_POLYNOMIAL : 0); + } + } + + return ~crc; + } + + template + consteval auto crc32_hash (const char (&value)[Size]) noexcept -> hash_t + { + return crc32_hash (value, Size - 1); + } + + [[gnu::always_inline]] + constexpr auto crc32_hash (std::string_view const& value) noexcept -> hash_t + { + return crc32_hash (value.data (), value.length ()); + } +} diff --git a/src/native/clr/include/runtime-base/monodroid-dl.hh b/src/native/clr/include/runtime-base/monodroid-dl.hh index cc2973b4515..73d69b95882 100644 --- a/src/native/clr/include/runtime-base/monodroid-dl.hh +++ b/src/native/clr/include/runtime-base/monodroid-dl.hh @@ -8,10 +8,10 @@ #include -#include #include "../xamarin-app.hh" #include "android-system.hh" +#include #include #include #include "startup-aware-lock.hh" @@ -20,59 +20,24 @@ namespace xamarin::android { class MonodroidDl { - enum class CacheKind - { - // Access AOT cache - AOT, - - // Access DSO cache - DSO, - }; - static inline std::mutex dso_handle_write_lock; - template [[gnu::always_inline, gnu::flatten]] - static auto find_dso_cache_entry_common (hash_t hash) noexcept -> DSOCacheEntry* + static auto find_dso_cache_entry (hash_t hash) noexcept -> DSOCacheEntry* { - static_assert (WhichCache == CacheKind::AOT || WhichCache == CacheKind::DSO, "Unknown cache type specified"); - - DSOCacheEntry *arr; - size_t arr_size; - - if constexpr (WhichCache == CacheKind::AOT) { - log_debug (LOG_ASSEMBLY, "Looking for hash {:x} in AOT cache", hash); - arr = aot_dso_cache; - arr_size = application_config.number_of_aot_cache_entries; - } else if constexpr (WhichCache == CacheKind::DSO) { - log_debug (LOG_ASSEMBLY, "Looking for hash {:x} in DSO cache", hash); - arr = dso_cache; - arr_size = application_config.number_of_dso_cache_entries; - } + log_debug (LOG_ASSEMBLY, "Looking for hash {:x} in DSO cache", hash); auto equal = [](DSOCacheEntry const& entry, hash_t key) -> bool { return entry.hash == key; }; auto less_than = [](DSOCacheEntry const& entry, hash_t key) -> bool { return entry.hash < key; }; - ssize_t idx = Search::binary_search (hash, arr, arr_size); + ssize_t idx = Search::binary_search (hash, dso_cache, application_config.number_of_dso_cache_entries); if (idx >= 0) { - return &arr[idx]; + return &dso_cache[idx]; } return nullptr; } - [[gnu::always_inline, gnu::flatten]] - static auto find_only_aot_cache_entry (hash_t hash) noexcept -> DSOCacheEntry* - { - return find_dso_cache_entry_common (hash); - } - - [[gnu::always_inline, gnu::flatten]] - static auto find_only_dso_cache_entry (hash_t hash) noexcept -> DSOCacheEntry* - { - return find_dso_cache_entry_common (hash); - } - public: [[gnu::always_inline]] static auto get_dso_name (const DSOCacheEntry *const dso) -> std::string_view @@ -89,7 +54,7 @@ namespace xamarin::android { auto equal = [](DSOApkEntry const& entry, hash_t key) -> bool { return entry.name_hash == key; }; auto less_than = [](DSOApkEntry const& entry, hash_t key) -> bool { return entry.name_hash < key; }; - ssize_t idx = Search::binary_search ( + ssize_t idx = Search::binary_search ( hash, dso_apk_entries, application_config.number_of_shared_libraries ); @@ -144,7 +109,6 @@ namespace xamarin::android return dso->handle; } - template [[gnu::flatten]] static auto monodroid_dlopen (std::string_view const& name, int flags) noexcept -> void* { if (name.empty ()) [[unlikely]] { @@ -152,39 +116,13 @@ namespace xamarin::android return nullptr; } - hash_t name_hash = xxhash::hash (name.data (), name.size ()); + hash_t name_hash = crc32_hash (name); log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash for name '{}' is {:x}", name, name_hash); - DSOCacheEntry *dso = nullptr; - if constexpr (PREFER_AOT_CACHE) { - // This code isn't currently used by CoreCLR, but it's possible that in the future we will have separate - // .so files for AOT-d assemblies, similar to MonoVM, so let's keep it. - // - // If we're asked to look in the AOT DSO cache, do it first. This is because we're likely called from the - // MonoVM's dlopen fallback handler and it will not be a request to resolved a p/invoke, but most likely to - // find and load an AOT image for a managed assembly. Since there might be naming/hash conflicts in this - // scenario, we look at the AOT cache first. - // - // See: https://github.com/dotnet/android/issues/9081 - dso = find_only_aot_cache_entry (name_hash); - } - - if (dso == nullptr) { - dso = find_only_dso_cache_entry (name_hash); - } - + DSOCacheEntry *dso = find_dso_cache_entry (name_hash); return monodroid_dlopen (dso, name, flags); } - [[gnu::flatten]] - static auto monodroid_dlopen (const char *name, int flags) noexcept -> void* - { - // We're called by MonoVM via a callback, we might need to return an AOT DSO. - // See: https://github.com/dotnet/android/issues/9081 - constexpr bool PREFER_AOT_CACHE = true; - return monodroid_dlopen (name, flags); - } - [[gnu::flatten]] static auto monodroid_dlsym (void *handle, std::string_view const& name) -> void* { diff --git a/src/native/clr/include/xamarin-app.hh b/src/native/clr/include/xamarin-app.hh index 39fbb97f822..b351856ac41 100644 --- a/src/native/clr/include/xamarin-app.hh +++ b/src/native/clr/include/xamarin-app.hh @@ -5,8 +5,7 @@ #include #include - -#include +#include static constexpr uint64_t FORMAT_TAG = 0x00045E6972616D58; // 'Xmari^XY' where XY is the format version static constexpr uint32_t COMPRESSED_DATA_MAGIC = 0x535A4158; // 'XAZS', little-endian @@ -31,7 +30,7 @@ static constexpr uint32_t ASSEMBLY_STORE_ABI = 0x00040000; #endif // Increase whenever an incompatible change is made to the assembly store format -static constexpr uint32_t ASSEMBLY_STORE_FORMAT_VERSION = 3 | ASSEMBLY_STORE_64BIT_FLAG | ASSEMBLY_STORE_ABI; +static constexpr uint32_t ASSEMBLY_STORE_FORMAT_VERSION = 4 | ASSEMBLY_STORE_64BIT_FLAG | ASSEMBLY_STORE_ABI; static constexpr uint32_t MODULE_MAGIC_NAMES = 0x53544158; // 'XATS', little-endian static constexpr uint32_t MODULE_INDEX_MAGIC = 0x49544158; // 'XATI', little-endian @@ -69,7 +68,7 @@ struct TypeMap // MUST match src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.cs struct TypeMapAssembly { - xamarin::android::hash_t mvid_hash; + uint8_t module_uuid[16]; uint64_t name_length; uint64_t name_offset; // into the assembly names blob }; @@ -77,6 +76,8 @@ struct TypeMapAssembly struct TypeMapModuleEntry { xamarin::android::hash_t managed_type_name_hash; + uint32_t managed_type_name_index; + uint32_t managed_type_name_length; uint32_t java_map_index; }; @@ -138,7 +139,7 @@ struct CompressedAssemblyDescriptor // [INDEX_SIZE] uint; index size in bytes // // INDEX (variable size, HEADER.ENTRY_COUNT*2 entries, for assembly names with and without the extension) -// [NAME_HASH] uint on 32-bit platforms, ulong on 64-bit platforms; xxhash of the assembly name +// [NAME_HASH] uint; CRC32 of the assembly name // [DESCRIPTOR_INDEX] uint; index into in-store assembly descriptor array // [IGNORE] byte; if set to anything other than 0, the assembly is to be ignored when loading // @@ -222,7 +223,6 @@ struct ApplicationConfig uint32_t number_of_assemblies_in_apk; uint32_t bundled_assembly_name_width; uint32_t number_of_dso_cache_entries; - uint32_t number_of_aot_cache_entries; uint32_t number_of_shared_libraries; uint32_t android_runtime_jnienv_class_token; uint32_t jnienv_initialize_method_token; @@ -233,30 +233,17 @@ struct ApplicationConfig bool managed_marshal_methods_lookup_enabled; }; -struct RuntimeProperty -{ - const uint32_t key_index; - const uint32_t value_index; - const uint32_t value_size; // including the terminating NUL -}; - -struct RuntimePropertyIndexEntry -{ - xamarin::android::hash_t key_hash; - uint32_t index; -}; - struct DSOApkEntry { - uint64_t name_hash; + xamarin::android::hash_t name_hash; uint32_t offset; // offset into the APK int32_t fd; // apk file descriptor }; struct DSOCacheEntry { - const uint64_t hash; - const uint64_t real_name_hash; + const xamarin::android::hash_t hash; + const xamarin::android::hash_t real_name_hash; const bool ignore; const bool is_jni_library; const uint32_t name_index; @@ -311,7 +298,6 @@ extern "C" { [[gnu::visibility("default")]] extern const uint64_t format_tag; #if defined (DEBUG) - [[gnu::visibility("default")]] extern const bool typemap_use_hashes; [[gnu::visibility("default")]] extern const TypeMap type_map; // MUST match src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.cs [[gnu::visibility("default")]] extern const TypeMapManagedTypeInfo type_map_managed_type_info[]; [[gnu::visibility("default")]] extern const TypeMapAssembly type_map_unique_assemblies[]; @@ -352,14 +338,9 @@ extern "C" { [[gnu::visibility("default")]] extern const uint dso_jni_preloads_idx_stride; [[gnu::visibility("default")]] extern const uint dso_jni_preloads_idx_count; [[gnu::visibility("default")]] extern const uint dso_jni_preloads_idx[]; - [[gnu::visibility("default")]] extern DSOCacheEntry aot_dso_cache[]; [[gnu::visibility("default")]] extern const char dso_names_data[]; [[gnu::visibility("default")]] extern DSOApkEntry dso_apk_entries[]; - [[gnu::visibility("default")]] extern const RuntimeProperty runtime_properties[]; - [[gnu::visibility("default")]] extern const char runtime_properties_data[]; - [[gnu::visibility("default")]] extern const RuntimePropertyIndexEntry runtime_property_index[]; - [[gnu::visibility("default")]] extern const char *init_runtime_property_names[]; [[gnu::visibility("default")]] extern char *init_runtime_property_values[]; } diff --git a/src/native/clr/pinvoke-override/generate-pinvoke-tables.cc b/src/native/clr/pinvoke-override/generate-pinvoke-tables.cc index 4264ab3e8c4..44a7efad40e 100644 --- a/src/native/clr/pinvoke-override/generate-pinvoke-tables.cc +++ b/src/native/clr/pinvoke-override/generate-pinvoke-tables.cc @@ -18,17 +18,19 @@ // #include #include +#include #include #include #include #include #include +#include #include #include #include #include -#include +#include namespace fs = std::filesystem; using namespace xamarin::android; @@ -89,17 +91,16 @@ const std::vector internal_pinvoke_names = { "__android_log_print", }; -template struct PinvokeEntry { std::string name; - Hash hash; + uint32_t hash; bool write_func_pointer; template friend - Os& operator<< (Os& os, PinvokeEntry const& p) + Os& operator<< (Os& os, PinvokeEntry const& p) { - os << std::showbase << std::hex << p.hash << ", \"" << p.name << "\", "; + os << "crc32_hash (\"" << p.name << "\"), \"" << p.name << "\", "; if (p.write_func_pointer) { return os << "reinterpret_cast(&" << p.name << ")"; @@ -121,12 +122,11 @@ void print (std::ostream& os, std::string comment, std::string variable_name, au os << "\t}};" << std::endl << std::endl; } -template -bool add_hash (std::string const& pinvoke, Hash hash, std::vector>& vec, std::unordered_set& used_cache, bool write_func_pointer) +bool add_hash (std::string const& pinvoke, uint32_t hash, std::vector& vec, std::unordered_set& used_cache, bool write_func_pointer) { vec.emplace_back (pinvoke, hash, write_func_pointer); if (used_cache.contains (hash)) { - std::cerr << (sizeof(Hash) == 4 ? "32" : "64") << "-bit hash collision for key '" << pinvoke << "': " << std::hex << std::showbase << hash << std::endl; + std::cerr << "CRC32 hash collision for key '" << pinvoke << "': " << std::hex << std::showbase << hash << std::endl; return true; } @@ -134,45 +134,37 @@ bool add_hash (std::string const& pinvoke, Hash hash, std::vector const& names, std::vector>& pinvokes32, std::vector>& pinvokes64, bool write_func_pointer) +bool generate_hashes (std::string table_name, std::vector const& names, std::vector& pinvokes, bool write_func_pointer) { std::unordered_set used_pinvokes32{}; - std::unordered_set used_pinvokes64{}; - uint32_t hash32; - uint64_t hash64; bool have_collisions = false; std::cout << "There are " << names.size () << " " << table_name << " p/invoke functions" << std::endl; for (std::string const& pinvoke : names) { - have_collisions |= add_hash (pinvoke, xxhash32::hash (pinvoke.c_str (), pinvoke.length ()), pinvokes32, used_pinvokes32, write_func_pointer); - have_collisions |= add_hash (pinvoke, xxhash64::hash (pinvoke.c_str (), pinvoke.length ()), pinvokes64, used_pinvokes64, write_func_pointer); + have_collisions |= add_hash (pinvoke, crc32_hash (pinvoke.c_str (), pinvoke.length ()), pinvokes, used_pinvokes32, write_func_pointer); } std::cout << "p/invoke hash collisions for '" << table_name << "' were " << (have_collisions ? "" : "not ") << "found" << std::endl; - std::ranges::sort (pinvokes32, {}, &PinvokeEntry::hash); - std::ranges::sort (pinvokes64, {}, &PinvokeEntry::hash); + std::ranges::sort (pinvokes, {}, &PinvokeEntry::hash); return have_collisions; } -template -void write_library_name_hash (Hash (*hasher)(const char*, size_t), std::ostream& os, std::string library_name, std::string variable_prefix) +void write_library_name_hash (std::ostream& os, std::string library_name, std::string variable_prefix) { - Hash hash = hasher (library_name.c_str (), library_name.length ()); - os << "constexpr hash_t " << variable_prefix << "_library_hash = " << std::hex << hash << ";" << std::endl; + os << "constexpr hash_t " << variable_prefix << "_library_hash = crc32_hash (\"" << library_name << "\");" << std::endl; } -template -void write_library_name_hashes (Hash (*hasher)(const char*, size_t), std::ostream& output) +void write_library_name_hashes (std::ostream& output) { - write_library_name_hash (hasher, output, "java-interop", "java_interop"); - write_library_name_hash (hasher, output, "xa-internal-api", "xa_internal_api"); - write_library_name_hash (hasher, output, "liblog", "android_liblog"); - write_library_name_hash (hasher, output, "libSystem.Native", "system_native"); - write_library_name_hash (hasher, output, "libSystem.IO.Compression.Native", "system_io_compression_native"); - write_library_name_hash (hasher, output, "libSystem.Security.Cryptography.Native.Android", "system_security_cryptography_native_android"); - write_library_name_hash (hasher, output, "libSystem.Globalization.Native", "system_globalization_native"); + write_library_name_hash (output, "java-interop", "java_interop"); + write_library_name_hash (output, "xa-internal-api", "xa_internal_api"); + write_library_name_hash (output, "liblog", "android_liblog"); + write_library_name_hash (output, "libSystem.Native", "system_native"); + write_library_name_hash (output, "libSystem.IO.Compression.Native", "system_io_compression_native"); + write_library_name_hash (output, "libSystem.Security.Cryptography.Native.Android", "system_security_cryptography_native_android"); + write_library_name_hash (output, "libSystem.Globalization.Native", "system_globalization_native"); } int main (int argc, char **argv) @@ -208,9 +200,8 @@ int main (int argc, char **argv) } bool have_collisions = false; - std::vector> internal_pinvokes32{}; - std::vector> internal_pinvokes64{}; - have_collisions |= generate_hashes ("internal", internal_pinvoke_names, internal_pinvokes32, internal_pinvokes64, true); + std::vector internal_pinvokes{}; + have_collisions |= generate_hashes ("internal", internal_pinvoke_names, internal_pinvokes, true); std::cout << "Generating tables in file: " << output_file_path << std::endl; @@ -219,7 +210,7 @@ int main (int argc, char **argv) output << "//" << std::endl; output << "// Autogenarated file. DO NOT EDIT." << std::endl; output << "//" << std::endl; - output << "// To regenerate run ../../../../build-tools/scripts/generate-pinvoke-tables.sh on Linux or macOS" << std::endl; + output << "// To regenerate, compile and run generate-pinvoke-tables.cc with the output path as the only argument." << std::endl; output << "// A compiler with support for C++20 ranges is required" << std::endl; output << "//" << std::endl << std::endl; @@ -227,18 +218,10 @@ int main (int argc, char **argv) output << "#include " << std::endl << std::endl; output << "namespace {" << std::endl; - output << "#if INTPTR_MAX == INT64_MAX" << std::endl; - print (output, "64-bit internal p/invoke table", "internal_pinvokes", internal_pinvokes64); + print (output, " CRC32 internal p/invoke table", "internal_pinvokes", internal_pinvokes); output << std::endl; - write_library_name_hashes (xxhash64::hash, output); - - output << "#else" << std::endl; - - print (output, "32-bit internal p/invoke table", "internal_pinvokes", internal_pinvokes32); + write_library_name_hashes (output); output << std::endl; - write_library_name_hashes (xxhash32::hash, output); - - output << "#endif" << std::endl << std::endl; output << "constexpr size_t internal_pinvokes_count = " << std::dec << std::noshowbase << internal_pinvoke_names.size () << ";" << std::endl; output << "} // end of anonymous namespace" << std::endl; @@ -247,18 +230,12 @@ int main (int argc, char **argv) } // This serves as a quick compile-time test of the algorithm's correctness. -// The tests are copied from https://github.com/ekpyron/xxhashct/test.cpp -template +template struct constexpr_test { static_assert (value == expected, "Compile-time hash mismatch."); }; -constexpr_test ("", 0), 0x2CC5D05U> constexprTest_1; -constexpr_test ("", 0), 0x36B78AE7U> constexprTest_2; -//constexpr_test ("", 0), 0xEF46DB3751D8E999ULL> constexprTest_3; -//constexpr_test ("", 0), 0xAC75FDA2929B17EFULL> constexprTest_4; -constexpr_test ("test", 4), 0x3E2023CFU> constexprTest32_5; -constexpr_test ("test", 4), 0xA9C14438U> constexprTest32_6; -//constexpr_test ("test", 4), 0x4fdcca5ddb678139ULL> constexprTest64_7; -//constexpr_test ("test", 4), 0x5A183B8150E2F651ULL> constexprTest64_8; +constexpr_test::max ()> constexprTest_1; +constexpr_test constexprTest_2; +constexpr_test constexprTest_3; diff --git a/src/native/clr/pinvoke-override/pinvoke-tables.include b/src/native/clr/pinvoke-override/pinvoke-tables.include index 716ef4ced44..6c28f505ad6 100644 --- a/src/native/clr/pinvoke-override/pinvoke-tables.include +++ b/src/native/clr/pinvoke-override/pinvoke-tables.include @@ -1,7 +1,7 @@ // // Autogenarated file. DO NOT EDIT. // -// To regenerate run ../../../../build-tools/scripts/generate-pinvoke-tables.sh on Linux or macOS +// To regenerate, compile and run generate-pinvoke-tables.cc with the output path as the only argument. // A compiler with support for C++20 ranges is required // @@ -9,89 +9,46 @@ #include namespace { -#if INTPTR_MAX == INT64_MAX - //64-bit internal p/invoke table + // CRC32 internal p/invoke table std::array internal_pinvokes {{ - {0x37307e5fddf709dc, "_monodroid_weak_gref_dec", reinterpret_cast(&_monodroid_weak_gref_dec)}, - {0x3b2467e7eadd4a6a, "_monodroid_lref_log_new", reinterpret_cast(&_monodroid_lref_log_new)}, - {0x423c8f539a2c56d2, "_monodroid_lookup_replacement_type", reinterpret_cast(&_monodroid_lookup_replacement_type)}, - {0x4310c1531ddddc14, "__android_log_print", reinterpret_cast(&__android_log_print)}, - {0x4b1956138764939a, "_monodroid_gref_log_new", reinterpret_cast(&_monodroid_gref_log_new)}, - {0x5f0b4e426eff086b, "_monodroid_detect_cpu_and_architecture", reinterpret_cast(&_monodroid_detect_cpu_and_architecture)}, - {0x9099a4b95e3c3a89, "_monodroid_lref_log_delete", reinterpret_cast(&_monodroid_lref_log_delete)}, - {0x9187e6bc6294cacf, "clr_typemap_managed_to_java", reinterpret_cast(&clr_typemap_managed_to_java)}, - {0x920bf58357fb56f3, "clr_initialize_gc_bridge", reinterpret_cast(&clr_initialize_gc_bridge)}, - {0x9a946dfe9916a942, "clr_typemap_java_to_managed", reinterpret_cast(&clr_typemap_java_to_managed)}, - {0x9d2b3233c41789df, "_monodroid_weak_gref_inc", reinterpret_cast(&_monodroid_weak_gref_inc)}, - {0xa6ec846592d99536, "_monodroid_weak_gref_delete", reinterpret_cast(&_monodroid_weak_gref_delete)}, - {0xa7f58f3ee428cc6b, "_monodroid_gref_log_delete", reinterpret_cast(&_monodroid_gref_log_delete)}, - {0xab094d8d2fc086a1, "_monodroid_gref_dec", reinterpret_cast(&_monodroid_gref_dec)}, - {0xae3df96dda0143bd, "_monodroid_gref_log", reinterpret_cast(&_monodroid_gref_log)}, - {0xb6222d90af401865, "_monodroid_weak_gref_get", reinterpret_cast(&_monodroid_weak_gref_get)}, - {0xb8306f71b963cd3d, "monodroid_log", reinterpret_cast(&monodroid_log)}, - {0xb9bae9c43fb05089, "xamarin_app_init", reinterpret_cast(&xamarin_app_init)}, - {0xc2a21d3f6c8ccc24, "_monodroid_lookup_replacement_method_info", reinterpret_cast(&_monodroid_lookup_replacement_method_info)}, - {0xc5b4690e13898fa3, "monodroid_timing_start", reinterpret_cast(&monodroid_timing_start)}, - {0xcaab0a3ab6057bbd, "_monodroid_gref_inc", reinterpret_cast(&_monodroid_gref_inc)}, - {0xd1e121b94ea63f2e, "_monodroid_gref_get", reinterpret_cast(&_monodroid_gref_get)}, - {0xd5151b00eb33d85e, "monodroid_TypeManager_get_java_class_name", reinterpret_cast(&monodroid_TypeManager_get_java_class_name)}, - {0xe27b9849b7e982cb, "_monodroid_max_gref_get", reinterpret_cast(&_monodroid_max_gref_get)}, - {0xe86307aac9a2631a, "_monodroid_weak_gref_new", reinterpret_cast(&_monodroid_weak_gref_new)}, - {0xf3048baf83034541, "_monodroid_gc_wait_for_bridge_processing", reinterpret_cast(&_monodroid_gc_wait_for_bridge_processing)}, - {0xf41c48df6f9be476, "monodroid_free", reinterpret_cast(&monodroid_free)}, - {0xf5a918ef520db207, "monodroid_timing_stop", reinterpret_cast(&monodroid_timing_stop)}, + {crc32_hash ("monodroid_log"), "monodroid_log", reinterpret_cast(&monodroid_log)}, + {crc32_hash ("_monodroid_gref_log_new"), "_monodroid_gref_log_new", reinterpret_cast(&_monodroid_gref_log_new)}, + {crc32_hash ("_monodroid_lookup_replacement_type"), "_monodroid_lookup_replacement_type", reinterpret_cast(&_monodroid_lookup_replacement_type)}, + {crc32_hash ("_monodroid_weak_gref_inc"), "_monodroid_weak_gref_inc", reinterpret_cast(&_monodroid_weak_gref_inc)}, + {crc32_hash ("_monodroid_gref_log"), "_monodroid_gref_log", reinterpret_cast(&_monodroid_gref_log)}, + {crc32_hash ("monodroid_timing_stop"), "monodroid_timing_stop", reinterpret_cast(&monodroid_timing_stop)}, + {crc32_hash ("_monodroid_gref_inc"), "_monodroid_gref_inc", reinterpret_cast(&_monodroid_gref_inc)}, + {crc32_hash ("_monodroid_weak_gref_get"), "_monodroid_weak_gref_get", reinterpret_cast(&_monodroid_weak_gref_get)}, + {crc32_hash ("_monodroid_gref_get"), "_monodroid_gref_get", reinterpret_cast(&_monodroid_gref_get)}, + {crc32_hash ("_monodroid_gref_log_delete"), "_monodroid_gref_log_delete", reinterpret_cast(&_monodroid_gref_log_delete)}, + {crc32_hash ("_monodroid_weak_gref_delete"), "_monodroid_weak_gref_delete", reinterpret_cast(&_monodroid_weak_gref_delete)}, + {crc32_hash ("_monodroid_lref_log_new"), "_monodroid_lref_log_new", reinterpret_cast(&_monodroid_lref_log_new)}, + {crc32_hash ("__android_log_print"), "__android_log_print", reinterpret_cast(&__android_log_print)}, + {crc32_hash ("xamarin_app_init"), "xamarin_app_init", reinterpret_cast(&xamarin_app_init)}, + {crc32_hash ("_monodroid_lref_log_delete"), "_monodroid_lref_log_delete", reinterpret_cast(&_monodroid_lref_log_delete)}, + {crc32_hash ("monodroid_free"), "monodroid_free", reinterpret_cast(&monodroid_free)}, + {crc32_hash ("_monodroid_detect_cpu_and_architecture"), "_monodroid_detect_cpu_and_architecture", reinterpret_cast(&_monodroid_detect_cpu_and_architecture)}, + {crc32_hash ("_monodroid_lookup_replacement_method_info"), "_monodroid_lookup_replacement_method_info", reinterpret_cast(&_monodroid_lookup_replacement_method_info)}, + {crc32_hash ("monodroid_timing_start"), "monodroid_timing_start", reinterpret_cast(&monodroid_timing_start)}, + {crc32_hash ("_monodroid_weak_gref_dec"), "_monodroid_weak_gref_dec", reinterpret_cast(&_monodroid_weak_gref_dec)}, + {crc32_hash ("monodroid_TypeManager_get_java_class_name"), "monodroid_TypeManager_get_java_class_name", reinterpret_cast(&monodroid_TypeManager_get_java_class_name)}, + {crc32_hash ("clr_typemap_managed_to_java"), "clr_typemap_managed_to_java", reinterpret_cast(&clr_typemap_managed_to_java)}, + {crc32_hash ("clr_initialize_gc_bridge"), "clr_initialize_gc_bridge", reinterpret_cast(&clr_initialize_gc_bridge)}, + {crc32_hash ("_monodroid_weak_gref_new"), "_monodroid_weak_gref_new", reinterpret_cast(&_monodroid_weak_gref_new)}, + {crc32_hash ("_monodroid_gref_dec"), "_monodroid_gref_dec", reinterpret_cast(&_monodroid_gref_dec)}, + {crc32_hash ("_monodroid_gc_wait_for_bridge_processing"), "_monodroid_gc_wait_for_bridge_processing", reinterpret_cast(&_monodroid_gc_wait_for_bridge_processing)}, + {crc32_hash ("_monodroid_max_gref_get"), "_monodroid_max_gref_get", reinterpret_cast(&_monodroid_max_gref_get)}, + {crc32_hash ("clr_typemap_java_to_managed"), "clr_typemap_java_to_managed", reinterpret_cast(&clr_typemap_java_to_managed)}, }}; -constexpr hash_t java_interop_library_hash = 0x54568ec36068e6b6; -constexpr hash_t xa_internal_api_library_hash = 0x43fd1b21148361b2; -constexpr hash_t android_liblog_library_hash = 0x1f2e4bce0544fb0a; -constexpr hash_t system_native_library_hash = 0x4cd7bd0032e920e1; -constexpr hash_t system_io_compression_native_library_hash = 0x9190f4cb761b1d3c; -constexpr hash_t system_security_cryptography_native_android_library_hash = 0x1848c0093f0afd8; -constexpr hash_t system_globalization_native_library_hash = 0x28b5c8fca080abd5; -#else - //32-bit internal p/invoke table - std::array internal_pinvokes {{ - {0xb7a486a, "monodroid_TypeManager_get_java_class_name", reinterpret_cast(&monodroid_TypeManager_get_java_class_name)}, - {0x1bef8dce, "_monodroid_gref_inc", reinterpret_cast(&_monodroid_gref_inc)}, - {0x1f1e0ee9, "_monodroid_gref_dec", reinterpret_cast(&_monodroid_gref_dec)}, - {0x2aea7c33, "_monodroid_max_gref_get", reinterpret_cast(&_monodroid_max_gref_get)}, - {0x3227d81a, "monodroid_timing_start", reinterpret_cast(&monodroid_timing_start)}, - {0x333d4835, "_monodroid_lookup_replacement_method_info", reinterpret_cast(&_monodroid_lookup_replacement_method_info)}, - {0x39e5b5d4, "__android_log_print", reinterpret_cast(&__android_log_print)}, - {0x656e00bd, "clr_typemap_managed_to_java", reinterpret_cast(&clr_typemap_managed_to_java)}, - {0x9070e02c, "_monodroid_lref_log_delete", reinterpret_cast(&_monodroid_lref_log_delete)}, - {0x910452d0, "monodroid_timing_stop", reinterpret_cast(&monodroid_timing_stop)}, - {0x9a734f16, "_monodroid_weak_gref_get", reinterpret_cast(&_monodroid_weak_gref_get)}, - {0x9c5b24a8, "_monodroid_weak_gref_new", reinterpret_cast(&_monodroid_weak_gref_new)}, - {0xa04e5d1c, "monodroid_free", reinterpret_cast(&monodroid_free)}, - {0xa3c1e548, "clr_initialize_gc_bridge", reinterpret_cast(&clr_initialize_gc_bridge)}, - {0xb02468aa, "_monodroid_gref_get", reinterpret_cast(&_monodroid_gref_get)}, - {0xb6431f9a, "clr_typemap_java_to_managed", reinterpret_cast(&clr_typemap_java_to_managed)}, - {0xbe8d7701, "_monodroid_gref_log_new", reinterpret_cast(&_monodroid_gref_log_new)}, - {0xc0d097a7, "_monodroid_lref_log_new", reinterpret_cast(&_monodroid_lref_log_new)}, - {0xc439b5d7, "_monodroid_lookup_replacement_type", reinterpret_cast(&_monodroid_lookup_replacement_type)}, - {0xc5146c54, "_monodroid_gref_log_delete", reinterpret_cast(&_monodroid_gref_log_delete)}, - {0xe215a17c, "_monodroid_weak_gref_delete", reinterpret_cast(&_monodroid_weak_gref_delete)}, - {0xe7e77ca5, "_monodroid_gref_log", reinterpret_cast(&_monodroid_gref_log)}, - {0xe8d7ba9b, "_monodroid_weak_gref_inc", reinterpret_cast(&_monodroid_weak_gref_inc)}, - {0xea2184e3, "_monodroid_gc_wait_for_bridge_processing", reinterpret_cast(&_monodroid_gc_wait_for_bridge_processing)}, - {0xeac7f6e3, "xamarin_app_init", reinterpret_cast(&xamarin_app_init)}, - {0xeea5931c, "_monodroid_weak_gref_dec", reinterpret_cast(&_monodroid_weak_gref_dec)}, - {0xfa1832a6, "_monodroid_detect_cpu_and_architecture", reinterpret_cast(&_monodroid_detect_cpu_and_architecture)}, - {0xfa4e32ca, "monodroid_log", reinterpret_cast(&monodroid_log)}, - }}; - - -constexpr hash_t java_interop_library_hash = 0x6e36e350; -constexpr hash_t xa_internal_api_library_hash = 0x13c9bd62; -constexpr hash_t android_liblog_library_hash = 0xa70c9969; -constexpr hash_t system_native_library_hash = 0x5b9ade60; -constexpr hash_t system_io_compression_native_library_hash = 0xafe3142c; -constexpr hash_t system_security_cryptography_native_android_library_hash = 0x93625cd; -constexpr hash_t system_globalization_native_library_hash = 0xa66f1e5a; -#endif +constexpr hash_t java_interop_library_hash = crc32_hash ("java-interop"); +constexpr hash_t xa_internal_api_library_hash = crc32_hash ("xa-internal-api"); +constexpr hash_t android_liblog_library_hash = crc32_hash ("liblog"); +constexpr hash_t system_native_library_hash = crc32_hash ("libSystem.Native"); +constexpr hash_t system_io_compression_native_library_hash = crc32_hash ("libSystem.IO.Compression.Native"); +constexpr hash_t system_security_cryptography_native_android_library_hash = crc32_hash ("libSystem.Security.Cryptography.Native.Android"); +constexpr hash_t system_globalization_native_library_hash = crc32_hash ("libSystem.Globalization.Native"); constexpr size_t internal_pinvokes_count = 28; } // end of anonymous namespace diff --git a/src/native/clr/pinvoke-override/precompiled.cc b/src/native/clr/pinvoke-override/precompiled.cc index 09d151a85ea..0d19ac52943 100644 --- a/src/native/clr/pinvoke-override/precompiled.cc +++ b/src/native/clr/pinvoke-override/precompiled.cc @@ -15,8 +15,8 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons return nullptr; } - hash_t library_name_hash = xxhash::hash (library_name, strlen (library_name)); - hash_t entrypoint_hash = xxhash::hash (entrypoint_name, strlen (entrypoint_name)); + hash_t library_name_hash = crc32_hash (library_name, strlen (library_name)); + hash_t entrypoint_hash = crc32_hash (entrypoint_name, strlen (entrypoint_name)); if (library_name_hash == java_interop_library_hash || library_name_hash == xa_internal_api_library_hash || library_name_hash == android_liblog_library_hash) { PinvokeEntry *entry = find_pinvoke_address (entrypoint_hash, internal_pinvokes.data (), internal_pinvokes_count); @@ -68,7 +68,7 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons // directories. CoreCLR's default resolver does not replicate that behaviour, so this path is kept. // It also carries no static table, so it is not subject to the drift problem that motivated the // BCL change above. - return handle_other_pinvoke_request (library_name, library_name_hash, entrypoint_name, entrypoint_hash); + return handle_other_pinvoke_request (library_name, entrypoint_name); } const void* Host::clr_pinvoke_override (const char *library_name, const char *entry_point_name) noexcept diff --git a/src/native/clr/shared/CMakeLists.txt b/src/native/clr/shared/CMakeLists.txt index aa6062066a6..2fe4716d5e5 100644 --- a/src/native/clr/shared/CMakeLists.txt +++ b/src/native/clr/shared/CMakeLists.txt @@ -21,7 +21,6 @@ macro(lib_target_options TARGET_NAME) ${TARGET_NAME} PUBLIC "$" - "$" ) target_link_libraries( diff --git a/src/native/clr/xamarin-app-stub/application_dso_stub.cc b/src/native/clr/xamarin-app-stub/application_dso_stub.cc index 14f33be4c29..8dd5dc9c2ea 100644 --- a/src/native/clr/xamarin-app-stub/application_dso_stub.cc +++ b/src/native/clr/xamarin-app-stub/application_dso_stub.cc @@ -2,7 +2,6 @@ #include #include -#include // This file MUST have "valid" values everywhere - the DSO it is compiled into is loaded by the // designer on desktop. @@ -20,7 +19,6 @@ const TypeMap type_map = { .managed_to_java = managed_to_java, }; -const bool typemap_use_hashes = true; const TypeMapManagedTypeInfo type_map_managed_type_info[] = {}; const TypeMapAssembly type_map_unique_assemblies[] = {}; const char type_map_assembly_names[] = {}; @@ -55,14 +53,13 @@ const ApplicationConfig application_config = { .jni_add_native_method_registration_attribute_present = false, .marshal_methods_enabled = false, .ignore_split_configs = false, - .number_of_runtime_properties = 3, + .number_of_runtime_properties = 1, .package_naming_policy = 0, .environment_variable_count = 0, .system_property_count = 0, .number_of_assemblies_in_apk = 2, .bundled_assembly_name_width = 0, .number_of_dso_cache_entries = 2, - .number_of_aot_cache_entries = 2, .number_of_shared_libraries = 2, .android_runtime_jnienv_class_token = 1, .jnienv_initialize_method_token = 2, @@ -105,13 +102,13 @@ AssemblyStoreRuntimeData assembly_store = { .assemblies = nullptr, }; -constexpr char fake_dso_name[] = "libaot-Some.Assembly.dll.so"; -constexpr char fake_dso_name2[] = "libaot-Another.Assembly.dll.so"; +constexpr char fake_dso_name[] = "libSome.Library.so"; +constexpr char fake_dso_name2[] = "libAnother.Library.so"; DSOCacheEntry dso_cache[] = { { - .hash = xamarin::android::xxhash::hash (fake_dso_name, sizeof(fake_dso_name) - 1), - .real_name_hash = xamarin::android::xxhash::hash (fake_dso_name, sizeof(fake_dso_name) - 1), + .hash = xamarin::android::crc32_hash (fake_dso_name), + .real_name_hash = xamarin::android::crc32_hash (fake_dso_name), .ignore = true, .is_jni_library = false, .name_index = 1, @@ -119,8 +116,8 @@ DSOCacheEntry dso_cache[] = { }, { - .hash = xamarin::android::xxhash::hash (fake_dso_name2, sizeof(fake_dso_name2) - 1), - .real_name_hash = xamarin::android::xxhash::hash (fake_dso_name2, sizeof(fake_dso_name2) - 1), + .hash = xamarin::android::crc32_hash (fake_dso_name2), + .real_name_hash = xamarin::android::crc32_hash (fake_dso_name2), .ignore = true, .is_jni_library = false, .name_index = 2, @@ -134,26 +131,6 @@ const uint dso_jni_preloads_idx[1] = { 0 }; -DSOCacheEntry aot_dso_cache[] = { - { - .hash = xamarin::android::xxhash::hash (fake_dso_name, sizeof(fake_dso_name) - 1), - .real_name_hash = xamarin::android::xxhash::hash (fake_dso_name, sizeof(fake_dso_name) - 1), - .ignore = true, - .is_jni_library = true, - .name_index = 3, - .handle = nullptr, - }, - - { - .hash = xamarin::android::xxhash::hash (fake_dso_name2, sizeof(fake_dso_name2) - 1), - .real_name_hash = xamarin::android::xxhash::hash (fake_dso_name2, sizeof(fake_dso_name2) - 1), - .ignore = true, - .is_jni_library = false, - .name_index = 4, - .handle = nullptr, - }, -}; - const char dso_names_data[] = {}; DSOApkEntry dso_apk_entries[2] {}; @@ -244,50 +221,6 @@ const JniRemappingTypeReplacementEntry jni_remapping_type_replacements[] = { }, }; -constexpr char prop_test_string_key[] = "test_string"; -constexpr char prop_test_integer_key[] = "test_integer"; -constexpr char prop_test_boolean_key[] = "test_boolean"; - -const RuntimeProperty runtime_properties[] = { - { - .key_index = 0, - .value_index = 10, - .value_size = 10, - }, - - { - .key_index = 20, - .value_index = 25, - .value_size = 5, - }, - - { - .key_index = 30, - .value_index = 33, - .value_size = 7, - }, -}; - - -const char runtime_properties_data[] = {}; - -const RuntimePropertyIndexEntry runtime_property_index[] = { - { - .key_hash = xamarin::android::xxhash::hash (prop_test_string_key, sizeof(prop_test_string_key) - 1), - .index = 0, - }, - - { - .key_hash = xamarin::android::xxhash::hash (prop_test_integer_key, sizeof(prop_test_integer_key) - 1), - .index = 1, - }, - - { - .key_hash = xamarin::android::xxhash::hash (prop_test_boolean_key, sizeof(prop_test_boolean_key) - 1), - .index = 2, - }, -}; - const char *init_runtime_property_names[] = { "HOST_RUNTIME_CONTRACT", }; diff --git a/src/native/common/include/runtime-base/search.hh b/src/native/common/include/runtime-base/search.hh index 855bac976b0..0bb132eda49 100644 --- a/src/native/common/include/runtime-base/search.hh +++ b/src/native/common/include/runtime-base/search.hh @@ -1,91 +1,73 @@ // Dear Emacs, this is a -*- C++ -*- header #pragma once +#include #include -#include - namespace xamarin::android { class Search final { public: - // Code duplication in the two functions below is lamentable, but avoiding it would require - // making the code much uglier (and harder to read) with meta programming tricks, just not worth it. - template + template [[gnu::always_inline, gnu::flatten]] - static ssize_t binary_search (const TState& state, TKey key, const T *arr, size_t n) noexcept + static size_t lower_bound (const TState& state, TKey key, const T *arr, size_t n) noexcept { - static_assert (equal != nullptr, "equal is a required template parameter"); static_assert (less_than != nullptr, "less_than is a required template parameter"); - ssize_t left = -1z; - ssize_t right = static_cast(n); + size_t left = 0; + size_t right = n; - while (right - left > 1) { - ssize_t middle = (left + right) >> 1u; + while (left < right) { + size_t middle = (left + right) >> 1u; if (less_than (arr[middle], key, state)) { - left = middle; + left = middle + 1; } else { right = middle; } } - return equal (arr[right], key, state) ? right : -1z; + return left; } - template + template [[gnu::always_inline, gnu::flatten]] - static ssize_t binary_search (TKey key, const T *arr, size_t n) noexcept + static size_t lower_bound (TKey key, const T *arr, size_t n) noexcept { - static_assert (equal != nullptr, "equal is a required template parameter"); static_assert (less_than != nullptr, "less_than is a required template parameter"); - ssize_t left = -1z; - ssize_t right = static_cast(n); + size_t left = 0; + size_t right = n; - while (right - left > 1) { - ssize_t middle = (left + right) >> 1u; + while (left < right) { + size_t middle = (left + right) >> 1u; if (less_than (arr[middle], key)) { - left = middle; + left = middle + 1; } else { right = middle; } } - return equal (arr[right], key) ? right : -1z; + return left; } - template + template [[gnu::always_inline, gnu::flatten]] - static ssize_t binary_search (hash_t key, const T *arr, size_t n) noexcept - { - return binary_search (key, arr, n); - } - - [[gnu::always_inline, gnu::flatten]] - static ssize_t binary_search (hash_t key, const hash_t *arr, size_t n) noexcept + static ssize_t binary_search (const TState& state, TKey key, const T *arr, size_t n) noexcept { - auto equal = [](hash_t const& entry, hash_t key) -> bool { return entry == key; }; - auto less_than = [](hash_t const& entry, hash_t key) -> bool { return entry < key; }; + static_assert (equal != nullptr, "equal is a required template parameter"); - return binary_search (key, arr, n); + size_t idx = lower_bound (state, key, arr, n); + return idx < n && equal (arr[idx], key, state) ? static_cast(idx) : -1z; } - [[gnu::always_inline]] - static ptrdiff_t binary_search_branchless (hash_t x, const hash_t *arr, uint32_t len) noexcept + template + [[gnu::always_inline, gnu::flatten]] + static ssize_t binary_search (TKey key, const T *arr, size_t n) noexcept { - const hash_t *base = arr; - while (len > 1) { - uint32_t half = len >> 1; - // __builtin_prefetch(&base[(len - half) / 2]); - // __builtin_prefetch(&base[half + (len - half) / 2]); - base = (base[half] < x ? &base[half] : base); - len -= half; - } + static_assert (equal != nullptr, "equal is a required template parameter"); - //return *(base + (*base < x)); - ptrdiff_t ret = (base + (*base < x)) - arr; - return arr[ret] == x ? ret : -1; + size_t idx = lower_bound (key, arr, n); + return idx < n && equal (arr[idx], key) ? static_cast(idx) : -1z; } }; } diff --git a/src/native/mono/monodroid/embedded-assemblies.cc b/src/native/mono/monodroid/embedded-assemblies.cc index 08099f65324..b553e3d16b3 100644 --- a/src/native/mono/monodroid/embedded-assemblies.cc +++ b/src/native/mono/monodroid/embedded-assemblies.cc @@ -32,7 +32,7 @@ #include "monodroid-state.hh" #include "startup-aware-lock.hh" #include -#include +#include #include using namespace xamarin::android; @@ -399,7 +399,7 @@ EmbeddedAssemblies::find_assembly_store_entry (hash_t hash, const AssemblyStoreI { auto equal = [](AssemblyStoreIndexEntry const& entry, hash_t key) -> bool { return entry.name_hash == key; }; auto less_than = [](AssemblyStoreIndexEntry const& entry, hash_t key) -> bool { return entry.name_hash < key; }; - ssize_t idx = Search::binary_search (hash, entries, entry_count); + ssize_t idx = SearchXxHash::binary_search (hash, entries, entry_count); if (idx >= 0) { return &entries[idx]; } @@ -674,7 +674,7 @@ EmbeddedAssemblies::typemap_java_to_managed (hash_t hash, const MonoString *java // In microbrenchmarks, `binary_search_branchless` is faster than `binary_search` but in "real" application tests, // the simple version appears to yield faster startup... Leaving both for now, for further investigation and // potential optimizations - ssize_t idx = Search::binary_search (hash, map_java_hashes, java_type_count); + ssize_t idx = SearchXxHash::binary_search (hash, map_java_hashes, java_type_count); //ptrdiff_t idx = binary_search_branchless (hash, map_java_hashes, java_type_count); TypeMapJava const* java_entry = idx >= 0 ? &map_java[idx] : nullptr; diff --git a/src/native/mono/monodroid/mono-image-loader.hh b/src/native/mono/monodroid/mono-image-loader.hh index ac33fe53588..011fb4dbe64 100644 --- a/src/native/mono/monodroid/mono-image-loader.hh +++ b/src/native/mono/monodroid/mono-image-loader.hh @@ -12,7 +12,7 @@ #include "platform-compat.hh" #include "xamarin-app.hh" #include -#include +#include #include #include "util.hh" @@ -101,7 +101,7 @@ namespace xamarin::android::internal { force_inline static ssize_t find_index (hash_t hash) noexcept { - ssize_t idx = Search::binary_search (hash, assembly_image_cache_hashes, number_of_cache_index_entries); + ssize_t idx = SearchXxHash::binary_search (hash, assembly_image_cache_hashes, number_of_cache_index_entries); return idx >= 0 ? static_cast(assembly_image_cache_indices[idx]) : -1z; } diff --git a/src/native/mono/runtime-base/monodroid-dl.hh b/src/native/mono/runtime-base/monodroid-dl.hh index 3e2cfe62402..383c1921185 100644 --- a/src/native/mono/runtime-base/monodroid-dl.hh +++ b/src/native/mono/runtime-base/monodroid-dl.hh @@ -11,7 +11,7 @@ #include "android-system.hh" #include "monodroid-state.hh" #include -#include +#include #include "shared-constants.hh" #include "startup-aware-lock.hh" #include "util.hh" @@ -57,7 +57,7 @@ namespace xamarin::android::internal auto equal = [](DSOCacheEntry const& entry, hash_t key) -> bool { return entry.hash == key; }; auto less_than = [](DSOCacheEntry const& entry, hash_t key) -> bool { return entry.hash < key; }; - ssize_t idx = Search::binary_search (hash, arr, arr_size); + ssize_t idx = SearchXxHash::binary_search (hash, arr, arr_size); if (idx >= 0) { log_debug (LOG_ASSEMBLY, "Found hash 0x{:x} entry at index {} of the cache", hash, idx); diff --git a/src/native/mono/runtime-base/search-xxhash.hh b/src/native/mono/runtime-base/search-xxhash.hh new file mode 100644 index 00000000000..8f4adf03c30 --- /dev/null +++ b/src/native/mono/runtime-base/search-xxhash.hh @@ -0,0 +1,47 @@ +// Dear Emacs, this is a -*- C++ -*- header +#pragma once + +#include +#include + +#include +#include + +namespace xamarin::android { + class SearchXxHash final + { + public: + template + [[gnu::always_inline, gnu::flatten]] + static ssize_t binary_search (hash_t key, const T *arr, size_t n) noexcept + { + return Search::binary_search (key, arr, n); + } + + [[gnu::always_inline, gnu::flatten]] + static ssize_t binary_search (hash_t key, const hash_t *arr, size_t n) noexcept + { + auto equal = [](hash_t const& entry, hash_t key) -> bool { return entry == key; }; + auto less_than = [](hash_t const& entry, hash_t key) -> bool { return entry < key; }; + + return Search::binary_search (key, arr, n); + } + + [[gnu::always_inline]] + static ptrdiff_t binary_search_branchless (hash_t x, const hash_t *arr, uint32_t len) noexcept + { + const hash_t *base = arr; + while (len > 1) { + uint32_t half = len >> 1; + // __builtin_prefetch(&base[(len - half) / 2]); + // __builtin_prefetch(&base[half + (len - half) / 2]); + base = (base[half] < x ? &base[half] : base); + len -= half; + } + + //return *(base + (*base < x)); + ptrdiff_t ret = (base + (*base < x)) - arr; + return arr[ret] == x ? ret : -1; + } + }; +} diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.Classes.cs b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.Classes.cs index 3b2c8549ff2..480a15d8fca 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.Classes.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.Classes.cs @@ -30,6 +30,10 @@ public Header (uint magic, uint version, uint entry_count, uint index_entry_coun sealed class IndexEntry { + // We treat `bool` as `byte` here, since that's what gets written to the binary. + public const uint NativeSize32 = 2 * sizeof (uint) + sizeof (byte); + public const uint NativeSize64 = sizeof (ulong) + sizeof (uint) + sizeof (byte); + public readonly ulong name_hash; public readonly uint descriptor_index; public readonly bool ignore; diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs index e10d9a99ab4..f31e44c7a70 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs @@ -13,6 +13,8 @@ partial class StoreReader_V2 : AssemblyStoreReader // Bit 31 is set for 64-bit platforms, cleared for the 32-bit ones const uint ASSEMBLY_STORE_FORMAT_VERSION_64BIT = 0x80000003; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant const uint ASSEMBLY_STORE_FORMAT_VERSION_32BIT = 0x00000003; + const uint ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_64BIT = 0x80000004; // Must match the ASSEMBLY_STORE_FORMAT_VERSION native constant + const uint ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_32BIT = 0x00000004; const uint ASSEMBLY_STORE_FORMAT_VERSION_MASK = 0xF0000000; const uint ASSEMBLY_STORE_ABI_AARCH64 = 0x00010000; @@ -79,6 +81,10 @@ public StoreReader_V2 (Stream store, string path) ASSEMBLY_STORE_FORMAT_VERSION_64BIT | ASSEMBLY_STORE_ABI_X64, ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_ARM, ASSEMBLY_STORE_FORMAT_VERSION_32BIT | ASSEMBLY_STORE_ABI_X86, + ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_64BIT | ASSEMBLY_STORE_ABI_AARCH64, + ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_64BIT | ASSEMBLY_STORE_ABI_X64, + ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_32BIT | ASSEMBLY_STORE_ABI_ARM, + ASSEMBLY_STORE_FORMAT_VERSION_CORECLR_32BIT | ASSEMBLY_STORE_ABI_X86, }; } @@ -150,13 +156,16 @@ protected override void Prepare () StoreStream.Seek ((long)elfOffset + Header.NativeSize, SeekOrigin.Begin); using var reader = CreateReader (); + uint indexEntrySize = GetIndexEntrySize (); var index = new List (); for (uint i = 0; i < header.index_entry_count; i++) { ulong name_hash; - if (Is64Bit) { + if (indexEntrySize == IndexEntry.NativeSize64) { name_hash = reader.ReadUInt64 (); - } else { + } else if (indexEntrySize == IndexEntry.NativeSize32) { name_hash = (ulong)reader.ReadUInt32 (); + } else { + throw new InvalidOperationException ($"Assembly store '{StorePath}' index entry size {indexEntrySize} is not supported."); } uint descriptor_index = reader.ReadUInt32 (); @@ -213,5 +222,18 @@ protected override void Prepare () storeItems.Add (item); } Assemblies = storeItems.AsReadOnly (); + + uint GetIndexEntrySize () + { + if (header.index_entry_count == 0) { + return 0; + } + + if (header.index_size % header.index_entry_count != 0) { + throw new InvalidOperationException ($"Assembly store '{StorePath}' index is corrupted: index size {header.index_size} is not evenly divisible by entry count {header.index_entry_count}."); + } + + return header.index_size / header.index_entry_count; + } } }