From 4d524ce5ec8cbb2bc6c428bb5e158505184c71ea Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 22 Jul 2025 21:17:34 +0000 Subject: [PATCH 1/5] Hook up P/Invoke to android crypto init for NativeAOT --- .../Android.Runtime.NativeAOT/JavaInteropRuntime.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs index 5fed268de2f..40db250dda5 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs @@ -14,6 +14,7 @@ static int JNI_OnLoad (IntPtr vm, IntPtr reserved) try { AndroidLog.Print (AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnLoad()"); LogcatTextWriter.Init (); + AndroidCryptoNative_InitLibraryOnLoad(vm, reserved); return (int) JniVersion.v1_6; } catch (Exception e) { @@ -22,6 +23,9 @@ static int JNI_OnLoad (IntPtr vm, IntPtr reserved) } } + [DllImport("*")] + static extern int AndroidCryptoNative_InitLibraryOnLoad (IntPtr vm, IntPtr reserved); + [UnmanagedCallersOnly (EntryPoint="JNI_OnUnload")] static void JNI_OnUnload (IntPtr vm, IntPtr reserved) { From d0935b46b15a3b973eb649248f446abfd59dd4c6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 22 Jul 2025 22:14:34 +0000 Subject: [PATCH 2/5] Update targets based on a functioning android NativeAOT runtime pack --- .../Microsoft.Android.Sdk.NativeAOT.targets | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index c47637cd367..1bfea561797 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -18,7 +18,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. true true - + <_IsPublishing Condition=" '$(_IsPublishing)' == '' ">true @@ -80,18 +80,6 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. true <_ExtraTrimmerArgs>$(_ExtraTrimmerArgs) --notrimwarn - - - - <_OriginalRuntimeIdentifier>$(RuntimeIdentifier) - linux-bionic-arm64 - linux-bionic-x64 - - <_targetOS>linux - - <_linuxLibcFlavor>bionic - - lld @@ -125,13 +113,6 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. - - - - $(_OriginalRuntimeIdentifier) - - - From 2676e47c890f83bca05b4d8ec735d570f93238b2 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 22 Jul 2025 22:53:29 +0000 Subject: [PATCH 3/5] Remove the logcat text writer as the default Console implementation is correct on the android RIDs. --- .../JavaInteropRuntime.cs | 1 - .../Android.Runtime.NativeAOT/Logging.cs | 77 ------------------- 2 files changed, 78 deletions(-) delete mode 100644 src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/Logging.cs diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs index 40db250dda5..676b5f10605 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs @@ -13,7 +13,6 @@ static int JNI_OnLoad (IntPtr vm, IntPtr reserved) { try { AndroidLog.Print (AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnLoad()"); - LogcatTextWriter.Init (); AndroidCryptoNative_InitLibraryOnLoad(vm, reserved); return (int) JniVersion.v1_6; } diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/Logging.cs b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/Logging.cs deleted file mode 100644 index 880e107e72f..00000000000 --- a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/Logging.cs +++ /dev/null @@ -1,77 +0,0 @@ -// NOTE: logging methods below are need temporarily due to: -// 1) linux-bionic BCL doesn't redirect stdout/stderr to logcat -// 2) Android.Util.Log won't work until we initialize the Java.Interop.JreRuntime - -using System.IO; -using System.Runtime.InteropServices; -using System.Text; - -namespace Microsoft.Android.Runtime; - -internal sealed class LogcatTextWriter : TextWriter { - - public static void Init () - { - // This method is a no-op, but it's necessary to ensure the static - // constructor is executed. - } - - static LogcatTextWriter () - { - Console.SetOut (new LogcatTextWriter (AndroidLogLevel.Info)); - Console.SetError (new LogcatTextWriter (AndroidLogLevel.Error)); - } - - AndroidLogLevel Level; - string Tag; - - internal LogcatTextWriter (AndroidLogLevel level, string tag = "NativeAotFromAndroid") - { - Level = level; - Tag = tag; - } - - public override Encoding Encoding => Encoding.UTF8; - public override string NewLine => "\n"; - - public override void WriteLine (string? value) - { - if (value == null) { - AndroidLog.Print (Level, Tag, ""); - return; - } - ReadOnlySpan span = value; - while (!span.IsEmpty) { - if (span.IndexOf ('\n') is int n && n < 0) { - break; - } - var line = span.Slice (0, n); - AndroidLog.Print (Level, Tag, line.ToString ()); - span = span.Slice (n + 1); - } - AndroidLog.Print (Level, Tag, span.ToString ()); - } -} - -static class AndroidLog { - - [DllImport ("log", EntryPoint = "__android_log_print", CallingConvention = CallingConvention.Cdecl)] - private static extern void __android_log_print(AndroidLogLevel level, string? tag, string format, string args, IntPtr ptr); - - internal static void Print(AndroidLogLevel level, string? tag, string message) => - __android_log_print(level, tag, "%s", message, IntPtr.Zero); - -} - -internal enum AndroidLogLevel -{ - Unknown = 0x00, - Default = 0x01, - Verbose = 0x02, - Debug = 0x03, - Info = 0x04, - Warn = 0x05, - Error = 0x06, - Fatal = 0x07, - Silent = 0x08 -} \ No newline at end of file From 05b63eae00a5420c283098df5db22de983b0cb4d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 23 Jul 2025 22:46:25 +0000 Subject: [PATCH 4/5] Make various adjustments to hook up the CoreCLR-targeting native code to be built in a way that NativeAOT can call --- .../Microsoft.Android.Runtime.proj | 6 ++- .../DiagnosticSettings.cs | 37 ------------------- .../JavaInteropRuntime.cs | 9 ++--- .../Android.Runtime.NativeAOT/Logging.cs | 32 ++++++++++++++++ .../Java.Interop/JreRuntime.cs | 7 +--- .../Android.Runtime/AndroidRuntime.cs | 2 +- .../Microsoft.Android.Sdk.NativeAOT.targets | 4 ++ src/native/clr/host/CMakeLists.txt | 25 ++++++++++++- src/native/native.targets | 10 ++++- 9 files changed, 80 insertions(+), 52 deletions(-) create mode 100644 src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/Logging.cs diff --git a/build-tools/create-packs/Microsoft.Android.Runtime.proj b/build-tools/create-packs/Microsoft.Android.Runtime.proj index 53db6c52f87..4cb669cd5f2 100644 --- a/build-tools/create-packs/Microsoft.Android.Runtime.proj +++ b/build-tools/create-packs/Microsoft.Android.Runtime.proj @@ -45,7 +45,7 @@ projects that use the Microsoft.Android.Runtimes framework in .NET 6+. DependsOnTargets="_GetLicense;_GetDefaultPackageVersion" BeforeTargets="GetFilesToPackage" > - <_RuntimeFlavorDirName Condition=" '$(AndroidRuntime)' == 'CoreCLR' ">$(_CLRRuntimeFlavorDirName) + <_RuntimeFlavorDirName Condition=" '$(AndroidRuntime)' == 'CoreCLR' or '$(AndroidRuntime)' == 'NativeAOT' ">$(_CLRRuntimeFlavorDirName) <_RuntimeFlavorDirName Condition=" '$(AndroidRuntime)' == 'Mono' Or '$(AndroidRuntime)' == '' ">$(_MonoRuntimeFlavorDirName) <_ClangArch Condition=" '$(AndroidRID)' == 'android-arm64' ">aarch64 <_ClangArch Condition=" '$(AndroidRID)' == 'android-arm' ">arm @@ -103,6 +103,10 @@ projects that use the Microsoft.Android.Runtimes framework in .NET 6+. <_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Microsoft.Android.Runtime.NativeAOT.dll" /> + + + + diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/DiagnosticSettings.cs b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/DiagnosticSettings.cs index 3731ec693c1..a3a3a411720 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/DiagnosticSettings.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/DiagnosticSettings.cs @@ -14,43 +14,6 @@ struct DiagnosticSettings { public bool LogJniGlobalReferences; private string? GrefPath; - private TextWriter? GrefLrefLog; - - - public TextWriter? GrefLog { - get { - if (!LogJniGlobalReferences) { - return null; - } - return ((LrefPath != null && LrefPath == GrefPath) - ? GrefLrefLog ??= CreateWriter (LrefPath) - : null) - ?? - ((GrefPath != null) - ? CreateWriter (GrefPath) - : null) - ?? - new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:GREF"); - } - } - - public TextWriter? LrefLog { - get { - if (!LogJniLocalReferences) { - return null; - } - return ((LrefPath != null && LrefPath == GrefPath) - ? GrefLrefLog ??= CreateWriter (LrefPath) - : null) - ?? - ((LrefPath != null) - ? CreateWriter (LrefPath) - : null) - ?? - new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:LREF"); - } - } - TextWriter? CreateWriter (string path) { try { diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs index 676b5f10605..e1e1e7a58e4 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs @@ -41,15 +41,12 @@ static void init (IntPtr jnienv, IntPtr klass, IntPtr classLoader) var settings = new DiagnosticSettings (); settings.AddDebugDotnetLog (); - var typeManager = new ManagedTypeManager (); var options = new NativeAotRuntimeOptions { EnvironmentPointer = jnienv, - ClassLoader = new JniObjectReference (classLoader), - TypeManager = typeManager, - ValueManager = new SimpleValueManager (), + ClassLoader = new JniObjectReference (classLoader, JniObjectReferenceType.Global), + TypeManager = new ManagedTypeManager (), + ValueManager = ManagedValueManager.GetOrCreateInstance (), UseMarshalMemberBuilder = false, - JniGlobalReferenceLogWriter = settings.GrefLog, - JniLocalReferenceLogWriter = settings.LrefLog, }; runtime = options.CreateJreVM (); diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/Logging.cs b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/Logging.cs new file mode 100644 index 00000000000..be1911cf04e --- /dev/null +++ b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/Logging.cs @@ -0,0 +1,32 @@ +// NOTE: logging methods below are need temporarily due to: +// 1) linux-bionic BCL doesn't redirect stdout/stderr to logcat +// 2) Android.Util.Log won't work until we initialize the Java.Interop.JreRuntime + +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace Microsoft.Android.Runtime; + +static class AndroidLog { + + [DllImport ("log", EntryPoint = "__android_log_print", CallingConvention = CallingConvention.Cdecl)] + private static extern void __android_log_print(AndroidLogLevel level, string? tag, string format, string args, IntPtr ptr); + + internal static void Print(AndroidLogLevel level, string? tag, string message) => + __android_log_print(level, tag, "%s", message, IntPtr.Zero); + +} + +internal enum AndroidLogLevel +{ + Unknown = 0x00, + Default = 0x01, + Verbose = 0x02, + Debug = 0x03, + Info = 0x04, + Warn = 0x05, + Error = 0x06, + Fatal = 0x07, + Silent = 0x08 +} \ No newline at end of file diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs b/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs index ed4068094f1..49e8b8ff32d 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs @@ -28,9 +28,6 @@ class NativeAotRuntimeOptions : JniRuntime.CreationOptions { public bool IgnoreUnrecognizedOptions {get; set;} - public TextWriter? JniGlobalReferenceLogWriter {get; set;} - public TextWriter? JniLocalReferenceLogWriter {get; set;} - public NativeAotRuntimeOptions () { JniVersion = JniVersion.v1_2; @@ -61,8 +58,8 @@ static NativeAotRuntimeOptions CreateJreVM (NativeAotRuntimeOptions builder) builder.TypeManager ??= new ManagedTypeManager (); #endif // NET - builder.ValueManager ??= new SimpleValueManager (); - builder.ObjectReferenceManager ??= new ManagedObjectReferenceManager (builder.JniGlobalReferenceLogWriter, builder.JniLocalReferenceLogWriter); + builder.ValueManager ??= ManagedValueManager.GetOrCreateInstance(); + builder.ObjectReferenceManager ??= new Android.Runtime.AndroidObjectReferenceManager (); if (builder.InvocationPointer != IntPtr.Zero || builder.EnvironmentPointer != IntPtr.Zero) return builder; diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index 6945d6ad40f..b4f064548d6 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -102,7 +102,7 @@ public AndroidRuntimeOptions (IntPtr jnienv, } } - class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager { + public class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager { public override int GlobalReferenceCount { get {return RuntimeNativeMethods._monodroid_gref_get ();} } diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index 1bfea561797..3ac3850508d 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -110,6 +110,10 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. + + + + diff --git a/src/native/clr/host/CMakeLists.txt b/src/native/clr/host/CMakeLists.txt index 017a66918cf..95ddda4f8e1 100644 --- a/src/native/clr/host/CMakeLists.txt +++ b/src/native/clr/host/CMakeLists.txt @@ -25,6 +25,18 @@ string(TOLOWER ${CMAKE_BUILD_TYPE} XAMARIN_NET_ANDROID_SUFFIX) set(XAMARIN_NET_ANDROID_LIB "net-android${CHECKED_BUILD_INFIX}.${XAMARIN_NET_ANDROID_SUFFIX}") set(XAMARIN_NET_ANDROID_STATIC_LIB "${XAMARIN_NET_ANDROID_LIB}-static") +set(XAMARIN_NET_ANDROID_NATIVEAOT_LIB "net-android-nativeaot${CHECKED_BUILD_INFIX}.${XAMARIN_NET_ANDROID_SUFFIX}") + +set(XAMARIN_NET_ANDROID_NATIVEAOT_SOURCES + bridge-processing.cc + gc-bridge.cc + internal-pinvokes.cc + os-bridge.cc + runtime-util.cc + typemap.cc + xamarin_getifaddrs.cc +) + set(XAMARIN_MONODROID_SOURCES assembly-store.cc bridge-processing.cc @@ -71,6 +83,11 @@ if(BUILD_STATIC_LIBRARY) ) endif() +add_library(${XAMARIN_NET_ANDROID_NATIVEAOT_LIB} + STATIC + ${XAMARIN_NET_ANDROID_NATIVEAOT_SOURCES} +) + macro(lib_target_options TARGET_NAME) target_compile_definitions( ${TARGET_NAME} @@ -154,11 +171,13 @@ macro(lib_target_options TARGET_NAME) xa::pinvoke-override-precompiled xa::lz4 -llog - -lcoreclr ) endmacro () lib_target_options(${XAMARIN_NET_ANDROID_LIB}) +target_link_libraries(${XAMARIN_NET_ANDROID_LIB} + -lcoreclr +) xa_add_compile_definitions(${XAMARIN_NET_ANDROID_LIB}) xa_add_include_directories(${XAMARIN_NET_ANDROID_LIB}) @@ -173,3 +192,7 @@ if(BUILD_STATIC_LIBRARY) xa_add_compile_definitions(${XAMARIN_NET_ANDROID_STATIC_LIB}) xa_add_include_directories(${XAMARIN_NET_ANDROID_STATIC_LIB}) endif() + +lib_target_options(${XAMARIN_NET_ANDROID_NATIVEAOT_LIB}) +xa_add_compile_definitions(${XAMARIN_NET_ANDROID_NATIVEAOT_LIB}) +xa_add_include_directories(${XAMARIN_NET_ANDROID_NATIVEAOT_LIB}) diff --git a/src/native/native.targets b/src/native/native.targets index 0a8d197adc7..75fd7ecac4c 100644 --- a/src/native/native.targets +++ b/src/native/native.targets @@ -306,7 +306,6 @@ AndroidRID="%(AndroidSupportedTargetJitAbi.AndroidRID)" AndroidRuntime="$(CMakeRuntimeFlavor)" RuntimePackName="$(_RuntimePackName)" /> - @@ -318,8 +317,17 @@ AndroidRID="%(AndroidSupportedTargetJitAbi.AndroidRID)" AndroidRuntime="$(CMakeRuntimeFlavor)" RuntimePackName="$(_RuntimePackName)" /> + <_RuntimePackFiles Include="$(NativeRuntimeOutputRootDir)$(_RuntimeRedistDirName)\%(AndroidSupportedTargetJitAbi.AndroidRID)\*.o" + AndroidRID="%(AndroidSupportedTargetJitAbi.AndroidRID)" + AndroidRuntime="$(CMakeRuntimeFlavor)" + RuntimePackName="NativeAOT" /> + <_RuntimePackFiles Include="$(NativeRuntimeOutputRootDir)$(_RuntimeRedistDirName)\%(AndroidSupportedTargetJitAbi.AndroidRID)\*.a" + AndroidRID="%(AndroidSupportedTargetJitAbi.AndroidRID)" + AndroidRuntime="$(CMakeRuntimeFlavor)" + RuntimePackName="NativeAOT" /> + Date: Wed, 13 Aug 2025 13:58:31 +0200 Subject: [PATCH 5/5] WIP --- .../Microsoft.Android.Runtime.proj | 4 +- .../JavaInteropRuntime.cs | 13 ++- .../Android.Runtime/JNIEnvInit.cs | 4 + .../Microsoft.Android.Sdk.NativeAOT.targets | 12 +-- src/native/clr/host/CMakeLists.txt | 8 +- src/native/clr/host/bridge-processing.cc | 27 +++++- src/native/clr/host/gc-bridge.cc | 2 +- src/native/clr/host/host.cc | 33 -------- src/native/clr/host/internal-pinvokes.cc | 83 ++++++++++++++++++- src/native/clr/host/os-bridge.cc | 36 ++++++++ .../clr/include/host/bridge-processing.hh | 6 ++ src/native/clr/include/host/host.hh | 2 - src/native/clr/include/host/os-bridge.hh | 3 + .../include/runtime-base/internal-pinvokes.hh | 2 + 14 files changed, 183 insertions(+), 52 deletions(-) diff --git a/build-tools/create-packs/Microsoft.Android.Runtime.proj b/build-tools/create-packs/Microsoft.Android.Runtime.proj index 4cb669cd5f2..30172483af4 100644 --- a/build-tools/create-packs/Microsoft.Android.Runtime.proj +++ b/build-tools/create-packs/Microsoft.Android.Runtime.proj @@ -105,8 +105,8 @@ projects that use the Microsoft.Android.Runtimes framework in .NET 6+. <_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Microsoft.Android.Runtime.NativeAOT.dll" /> - - + + diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs index e1e1e7a58e4..a6554481986 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs @@ -14,7 +14,8 @@ static int JNI_OnLoad (IntPtr vm, IntPtr reserved) try { AndroidLog.Print (AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnLoad()"); AndroidCryptoNative_InitLibraryOnLoad(vm, reserved); - return (int) JniVersion.v1_6; + clr_initialize_on_onload(vm, reserved); + return (int)JniVersion.v1_6; } catch (Exception e) { AndroidLog.Print (AndroidLogLevel.Error, "JavaInteropRuntime", $"JNI_OnLoad() failed: {e}"); @@ -25,7 +26,13 @@ static int JNI_OnLoad (IntPtr vm, IntPtr reserved) [DllImport("*")] static extern int AndroidCryptoNative_InitLibraryOnLoad (IntPtr vm, IntPtr reserved); - [UnmanagedCallersOnly (EntryPoint="JNI_OnUnload")] + [DllImport("*")] + static extern int clr_initialize_on_onload (IntPtr vm, IntPtr reserved); + + [DllImport("*")] + static extern int clr_initialize_on_runtime_init (); + + [UnmanagedCallersOnly(EntryPoint = "JNI_OnUnload")] static void JNI_OnUnload (IntPtr vm, IntPtr reserved) { AndroidLog.Print(AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnUnload"); @@ -53,6 +60,8 @@ static void init (IntPtr jnienv, IntPtr klass, IntPtr classLoader) // Entry point into Mono.Android.dll JNIEnvInit.InitializeJniRuntime (runtime); + clr_initialize_on_runtime_init (); + transition = new JniTransition (jnienv); var handler = Java.Lang.Thread.DefaultUncaughtExceptionHandler; diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index f0c6947f8ee..9a1881f3e76 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -82,6 +82,10 @@ static Type TypeGetType (string typeName) => internal static void InitializeJniRuntime (JniRuntime runtime) { androidRuntime = runtime; + gref_gc_threshold = RuntimeNativeMethods._monodroid_max_gref_get (); + if (gref_gc_threshold != int.MaxValue) { + gref_gc_threshold = checked((gref_gc_threshold * 9) / 10); + } SetSynchronizationContext (); } diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index 3ac3850508d..03b2cb1cb71 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -55,7 +55,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. @@ -68,6 +68,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. <_NdkSysrootDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/sysroot/usr/lib/$(_NdkSysrootAbi)/ <_NdkBinDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/bin/ clang++ + clang++ llvm-objcopy @@ -105,14 +106,15 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. - + - + - - + + + diff --git a/src/native/clr/host/CMakeLists.txt b/src/native/clr/host/CMakeLists.txt index 95ddda4f8e1..14070916cab 100644 --- a/src/native/clr/host/CMakeLists.txt +++ b/src/native/clr/host/CMakeLists.txt @@ -25,7 +25,7 @@ string(TOLOWER ${CMAKE_BUILD_TYPE} XAMARIN_NET_ANDROID_SUFFIX) set(XAMARIN_NET_ANDROID_LIB "net-android${CHECKED_BUILD_INFIX}.${XAMARIN_NET_ANDROID_SUFFIX}") set(XAMARIN_NET_ANDROID_STATIC_LIB "${XAMARIN_NET_ANDROID_LIB}-static") -set(XAMARIN_NET_ANDROID_NATIVEAOT_LIB "net-android-nativeaot${CHECKED_BUILD_INFIX}.${XAMARIN_NET_ANDROID_SUFFIX}") +set(XAMARIN_NET_ANDROID_NATIVEAOT_LIB "net-android-nativeaot${CHECKED_BUILD_INFIX}-${XAMARIN_NET_ANDROID_SUFFIX}") set(XAMARIN_NET_ANDROID_NATIVEAOT_SOURCES bridge-processing.cc @@ -33,7 +33,6 @@ set(XAMARIN_NET_ANDROID_NATIVEAOT_SOURCES internal-pinvokes.cc os-bridge.cc runtime-util.cc - typemap.cc xamarin_getifaddrs.cc ) @@ -87,6 +86,11 @@ add_library(${XAMARIN_NET_ANDROID_NATIVEAOT_LIB} STATIC ${XAMARIN_NET_ANDROID_NATIVEAOT_SOURCES} ) +target_compile_definitions( + ${XAMARIN_NET_ANDROID_NATIVEAOT_LIB} + PRIVATE + NATIVEAOT +) macro(lib_target_options TARGET_NAME) target_compile_definitions( diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index a72e75d902a..ec25cddc36c 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -14,6 +14,15 @@ void BridgeProcessing::initialize_on_runtime_init (JNIEnv *env, jclass runtimeCl GCUserPeer_class = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_GCUserPeer", true); GCUserPeer_ctor = env->GetMethodID (GCUserPeer_class, "", "()V"); + GCUserPeerable_class = env->FindClass ("net/dot/jni/GCUserPeerable"); + if (GCUserPeerable_class != nullptr) [[likely]] { + GCUserPeerable_class = static_cast(OSBridge::lref_to_gref (env, GCUserPeerable_class)); + GCUserPeerable_jiAddManagedReference = env->GetMethodID (GCUserPeerable_class, "jiAddManagedReference", "(Ljava/lang/Object;)V"); + GCUserPeerable_jiClearManagedReferences = env->GetMethodID (GCUserPeerable_class, "jiClearManagedReferences", "()V"); + } else { + log_error (LOG_DEFAULT, "Failed to find net/dot/jni/GCUserPeerable class."); + } + abort_unless (GCUserPeer_class != nullptr && GCUserPeer_ctor != nullptr, "Failed to load mono.android.GCUserPeer!"); } @@ -126,10 +135,10 @@ void BridgeProcessing::add_circular_references (const StronglyConnectedComponent abort_unless (reference_added, [this, &prev, &next] { jclass prev_java_class = env->GetObjectClass (prev.handle); - const char *prev_class_name = Host::get_java_class_name_for_TypeManager (prev_java_class); + const char *prev_class_name = OSBridge::get_java_class_name_for_TypeManager (prev_java_class); jclass next_java_class = env->GetObjectClass (next.handle); - const char *next_class_name = Host::get_java_class_name_for_TypeManager (next_java_class); + const char *next_class_name = OSBridge::get_java_class_name_for_TypeManager (next_java_class); return detail::_format_message ( "Failed to add reference between objects in a strongly connected component: %s -> %s.", @@ -157,6 +166,11 @@ bool BridgeProcessing::add_reference (jobject from, jobject to) noexcept abort_if_invalid_pointer_argument (from, "from"); abort_if_invalid_pointer_argument (to, "to"); + if (GCUserPeerable_class != nullptr && env->IsInstanceOf (from, GCUserPeerable_class)) { + env->CallVoidMethod (from, GCUserPeerable_jiAddManagedReference, to); + return true; + } + jclass java_class = env->GetObjectClass (from); jmethodID add_method_id = env->GetMethodID (java_class, "monodroidAddReference", "(Ljava/lang/Object;)V"); @@ -197,6 +211,11 @@ void BridgeProcessing::clear_references (jobject handle) noexcept { abort_if_invalid_pointer_argument (handle, "handle"); + if (GCUserPeerable_class != nullptr && env->IsInstanceOf (handle, GCUserPeerable_class)) { + env->CallVoidMethod (handle, GCUserPeerable_jiClearManagedReferences); + return; + } + jclass java_class = env->GetObjectClass (handle); jmethodID clear_method_id = env->GetMethodID (java_class, "monodroidClearReferences", "()V"); @@ -315,7 +334,7 @@ void BridgeProcessing::log_missing_add_references_method ([[maybe_unused]] jclas return; } - char *class_name = Host::get_java_class_name_for_TypeManager (java_class); + char *class_name = OSBridge::get_java_class_name_for_TypeManager (java_class); log_error (LOG_GC, "Missing monodroidAddReferences method for object of class {}", optional_string (class_name)); free (class_name); #endif @@ -331,7 +350,7 @@ void BridgeProcessing::log_missing_clear_references_method ([[maybe_unused]] jcl return; } - char *class_name = Host::get_java_class_name_for_TypeManager (java_class); + char *class_name = OSBridge::get_java_class_name_for_TypeManager (java_class); log_error (LOG_GC, "Missing monodroidClearReferences method for object of class {}", optional_string (class_name)); free (class_name); #endif diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc index 6bf3a387e96..0d16a5900f3 100644 --- a/src/native/clr/host/gc-bridge.cc +++ b/src/native/clr/host/gc-bridge.cc @@ -117,7 +117,7 @@ void GCBridge::log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept jobject handle = ctx->control_block->handle; jclass java_class = env->GetObjectClass (handle); if (java_class != nullptr) { - char *class_name = Host::get_java_class_name_for_TypeManager (java_class); + char *class_name = OSBridge::get_java_class_name_for_TypeManager (java_class); log_info (LOG_GC, "gref {:#x} [{}]", reinterpret_cast (handle), class_name); free (class_name); } else { diff --git a/src/native/clr/host/host.cc b/src/native/clr/host/host.cc index 3fd2e3ba959..e459871873f 100644 --- a/src/native/clr/host/host.cc +++ b/src/native/clr/host/host.cc @@ -531,39 +531,6 @@ void Host::Java_mono_android_Runtime_register (JNIEnv *env, jstring managedType, } } -auto Host::get_java_class_name_for_TypeManager (jclass klass) noexcept -> char* -{ - if (klass == nullptr || Class_getName == nullptr) { - return nullptr; - } - - JNIEnv *env = OSBridge::ensure_jnienv (); - jstring name = reinterpret_cast (env->CallObjectMethod (klass, Class_getName)); - if (name == nullptr) { - log_error (LOG_DEFAULT, "Failed to obtain Java class name for object at {:p}", reinterpret_cast(klass)); - return nullptr; - } - - const char *mutf8 = env->GetStringUTFChars (name, nullptr); - if (mutf8 == nullptr) { - log_error (LOG_DEFAULT, "Failed to convert Java class name to UTF8 (out of memory?)"sv); - env->DeleteLocalRef (name); - return nullptr; - } - char *ret = strdup (mutf8); - - env->ReleaseStringUTFChars (name, mutf8); - env->DeleteLocalRef (name); - - char *dot = strchr (ret, '.'); - while (dot != nullptr) { - *dot = '/'; - dot = strchr (dot + 1, '.'); - } - - return ret; -} - auto Host::Java_JNI_OnLoad (JavaVM *vm, [[maybe_unused]] void *reserved) noexcept -> jint { jvm = vm; diff --git a/src/native/clr/host/internal-pinvokes.cc b/src/native/clr/host/internal-pinvokes.cc index 5260a6bac6a..a0334ffa3e8 100644 --- a/src/native/clr/host/internal-pinvokes.cc +++ b/src/native/clr/host/internal-pinvokes.cc @@ -9,6 +9,8 @@ using namespace xamarin::android; +#define UNREACHABLE Helpers::abort_application (LOG_DEFAULT, "The method is not implemented. This is a stub and should not be called."sv) + int _monodroid_gref_get () noexcept { return OSBridge::get_gc_gref_count (); @@ -31,13 +33,52 @@ void _monodroid_gref_log_delete (jobject handle, char type, const char *threadNa const char* clr_typemap_managed_to_java (const char *typeName, const uint8_t *mvid) noexcept { +#ifdef NATIVEAOT + (void)typeName; // unused parameter + (void)mvid; // unused parameter + UNREACHABLE; + return nullptr; +#else return TypeMapper::managed_to_java (typeName, mvid); +#endif } bool clr_typemap_java_to_managed (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_token_id) noexcept { +#ifdef NATIVEAOT + (void)java_type_name; // unused parameter + (void)assembly_name; // unused parameter + (void)managed_type_token_id; // unused parameter + UNREACHABLE; + return false; +#else return TypeMapper::java_to_managed (java_type_name, assembly_name, managed_type_token_id); +#endif +} + +#ifdef NATIVEAOT +void clr_initialize_on_onload (JavaVM *vm, void *reserved) noexcept +{ + (void)reserved; // unused parameter + + JNIEnv *env = NULL; + vm->GetEnv ((void**)&env, JNI_VERSION_1_6); + + OSBridge::initialize_on_onload (vm, env); + GCBridge::initialize_on_onload (env); + + // AndroidSystem::init_max_gref_count (); +} + +void clr_initialize_on_runtime_init () noexcept +{ + JNIEnv *env = OSBridge::ensure_jnienv (); + jclass runtimeClass = env->FindClass ("mono/android/Runtime"); + + OSBridge::initialize_on_runtime_init (env, runtimeClass); + GCBridge::initialize_on_runtime_init (env, runtimeClass); } +#endif BridgeProcessingFtn clr_initialize_gc_bridge ( BridgeProcessingStartedFtn bridge_processing_started_callback, @@ -81,7 +122,7 @@ void monodroid_log (LogLevel level, LogCategories category, const char *message) char* monodroid_TypeManager_get_java_class_name (jclass klass) noexcept { - return Host::get_java_class_name_for_TypeManager (klass); + return OSBridge::get_java_class_name_for_TypeManager (klass); } void monodroid_free (void *ptr) noexcept @@ -92,17 +133,36 @@ void monodroid_free (void *ptr) noexcept const char* _monodroid_lookup_replacement_type (const char *jniSimpleReference) { +#ifdef NATIVEAOT + (void)jniSimpleReference; // unused parameter + UNREACHABLE; + return nullptr; +#else return JniRemapping::lookup_replacement_type (jniSimpleReference); +#endif } const JniRemappingReplacementMethod* _monodroid_lookup_replacement_method_info (const char *jniSourceType, const char *jniMethodName, const char *jniMethodSignature) { +#ifdef NATIVEAOT + (void)jniSourceType; // unused parameter + (void)jniMethodName; // unused parameter + (void)jniMethodSignature; // unused parameter + UNREACHABLE; + return nullptr; +#else return JniRemapping::lookup_replacement_method_info (jniSourceType, jniMethodName, jniMethodSignature); +#endif } managed_timing_sequence* monodroid_timing_start (const char *message) { +#ifdef NATIVEAOT + (void)message; // unused parameter + UNREACHABLE; + return nullptr; +#else // Technically a reference here is against the idea of shared pointers, but // in this instance it's fine since we know we won't be storing the pointer // and this way things are slightly faster. @@ -117,10 +177,16 @@ managed_timing_sequence* monodroid_timing_start (const char *message) } ret->start = FastTiming::get_time (); return ret; +#endif } void monodroid_timing_stop (managed_timing_sequence *sequence, const char *message) { +#ifdef NATIVEAOT + (void)sequence; // unused parameter + (void)message; // unused parameter + UNREACHABLE; +#else constexpr std::string_view DEFAULT_MESSAGE { "Managed Timing" }; if (sequence == nullptr) { return; @@ -134,6 +200,7 @@ void monodroid_timing_stop (managed_timing_sequence *sequence, const char *messa sequence->end = FastTiming::get_time (); Timing::info (sequence, message == nullptr ? DEFAULT_MESSAGE.data () : message); timing->release_sequence (sequence); +#endif } void _monodroid_weak_gref_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from, int from_writable) @@ -148,7 +215,12 @@ int _monodroid_weak_gref_get () int _monodroid_max_gref_get () { +#ifdef NATIVEAOT + // FIXME + return 10000; +#else return static_cast(AndroidSystem::get_max_gref_count ()); +#endif } void _monodroid_weak_gref_delete (jobject handle, char type, const char *threadName, int threadId, const char *from, int from_writable) @@ -178,13 +250,21 @@ void _monodroid_detect_cpu_and_architecture (uint16_t *built_for_cpu, uint16_t * abort_if_invalid_pointer_argument (running_on_cpu, "running_on_cpu"); abort_if_invalid_pointer_argument (is64bit, "is64bit"); +#ifdef NATIVEAOT + UNREACHABLE; +#else bool _64bit; monodroid_detect_cpu_and_architecture (*built_for_cpu, *running_on_cpu, _64bit); *is64bit = _64bit; +#endif } void* _monodroid_timezone_get_default_id () { +#ifdef NATIVEAOT + UNREACHABLE; + return nullptr; +#else JNIEnv *env = OSBridge::ensure_jnienv (); jmethodID getDefault = env->GetStaticMethodID (Host::get_java_class_TimeZone (), "getDefault", "()Ljava/util/TimeZone;"); jmethodID getID = env->GetMethodID (Host::get_java_class_TimeZone (), "getID", "()Ljava/lang/String;"); @@ -202,4 +282,5 @@ void* _monodroid_timezone_get_default_id () env->DeleteLocalRef (id); env->DeleteLocalRef (d); return def_id; +#endif } diff --git a/src/native/clr/host/os-bridge.cc b/src/native/clr/host/os-bridge.cc index 904ad36c523..d9f639f2ba8 100644 --- a/src/native/clr/host/os-bridge.cc +++ b/src/native/clr/host/os-bridge.cc @@ -28,6 +28,9 @@ void OSBridge::initialize_on_onload (JavaVM *vm, JNIEnv *env) noexcept // weakrefClass != nullptr && weakrefCtor != nullptr && weakrefGet != nullptr, // "Failed to look up required java.lang.ref.WeakReference members" // ); + + jclass grefClass = env->FindClass ("java/lang/Class"); + Class_getName = env->GetMethodID (grefClass, "getName", "()Ljava/lang/String;"); } void OSBridge::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept @@ -419,3 +422,36 @@ void OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type, fflush (Logger::lref_log ()); } + +auto OSBridge::get_java_class_name_for_TypeManager (jclass klass) noexcept -> char* +{ + if (klass == nullptr || Class_getName == nullptr) { + return nullptr; + } + + JNIEnv *env = ensure_jnienv (); + jstring name = reinterpret_cast (env->CallObjectMethod (klass, Class_getName)); + if (name == nullptr) { + log_error (LOG_DEFAULT, "Failed to obtain Java class name for object at {:p}", reinterpret_cast(klass)); + return nullptr; + } + + const char *mutf8 = env->GetStringUTFChars (name, nullptr); + if (mutf8 == nullptr) { + log_error (LOG_DEFAULT, "Failed to convert Java class name to UTF8 (out of memory?)"sv); + env->DeleteLocalRef (name); + return nullptr; + } + char *ret = strdup (mutf8); + + env->ReleaseStringUTFChars (name, mutf8); + env->DeleteLocalRef (name); + + char *dot = strchr (ret, '.'); + while (dot != nullptr) { + *dot = '/'; + dot = strchr (dot + 1, '.'); + } + + return ret; +} diff --git a/src/native/clr/include/host/bridge-processing.hh b/src/native/clr/include/host/bridge-processing.hh index 0fdb56d931e..f33b5e34b57 100644 --- a/src/native/clr/include/host/bridge-processing.hh +++ b/src/native/clr/include/host/bridge-processing.hh @@ -34,6 +34,10 @@ private: static inline jclass GCUserPeer_class = nullptr; static inline jmethodID GCUserPeer_ctor = nullptr; + static inline jclass GCUserPeerable_class = nullptr; + static inline jmethodID GCUserPeerable_jiAddManagedReference = nullptr; + static inline jmethodID GCUserPeerable_jiClearManagedReferences = nullptr; + void prepare_for_java_collection () noexcept; void prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc) noexcept; void take_weak_global_ref (const HandleContext &context) noexcept; @@ -60,4 +64,6 @@ private: void log_gref_delete (jobject handle) noexcept; void log_weak_ref_delete (jobject weak) noexcept; void log_gc_summary () noexcept; + + static char* get_java_class_name (jclass klass) noexcept; }; diff --git a/src/native/clr/include/host/host.hh b/src/native/clr/include/host/host.hh index c1ae90788c2..2233e558b4c 100644 --- a/src/native/clr/include/host/host.hh +++ b/src/native/clr/include/host/host.hh @@ -21,8 +21,6 @@ namespace xamarin::android { jobjectArray assembliesJava, jboolean isEmulator, jboolean haveSplitApks) noexcept; static void Java_mono_android_Runtime_register (JNIEnv *env, jstring managedType, jclass nativeClass, jstring methods) noexcept; - static auto get_java_class_name_for_TypeManager (jclass klass) noexcept -> char*; - static auto get_timing () -> std::shared_ptr { return _timing; diff --git a/src/native/clr/include/host/os-bridge.hh b/src/native/clr/include/host/os-bridge.hh index b9da49b07f9..93d9001659b 100644 --- a/src/native/clr/include/host/os-bridge.hh +++ b/src/native/clr/include/host/os-bridge.hh @@ -50,6 +50,8 @@ namespace xamarin::android { return env; } + static auto get_java_class_name_for_TypeManager (jclass klass) noexcept -> char*; + private: static auto _monodroid_gref_inc () noexcept -> int; static auto _monodroid_gref_dec () noexcept -> int; @@ -60,6 +62,7 @@ namespace xamarin::android { static inline JavaVM *jvm = nullptr; static inline jclass GCUserPeer_class = nullptr; static inline jmethodID GCUserPeer_ctor = nullptr; + static inline jmethodID Class_getName = nullptr; static inline int gc_gref_count = 0; static inline int gc_weak_gref_count = 0; diff --git a/src/native/clr/include/runtime-base/internal-pinvokes.hh b/src/native/clr/include/runtime-base/internal-pinvokes.hh index 0bc3f2d841f..b36aa79d3e4 100644 --- a/src/native/clr/include/runtime-base/internal-pinvokes.hh +++ b/src/native/clr/include/runtime-base/internal-pinvokes.hh @@ -15,6 +15,8 @@ extern "C" { void _monodroid_gref_log_delete (jobject handle, char type, const char *threadName, int threadId, const char *from, int from_writable) noexcept; const char* clr_typemap_managed_to_java (const char *typeName, const uint8_t *mvid) noexcept; bool clr_typemap_java_to_managed (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_token_id) noexcept; + void clr_initialize_on_onload (JavaVM *vm, void *reserved) noexcept; + void clr_initialize_on_runtime_init () noexcept; BridgeProcessingFtn clr_initialize_gc_bridge ( BridgeProcessingStartedFtn bridge_processing_started_callback, BridgeProcessingFinishedFtn mark_cross_references_callback) noexcept;