From da7c45beab875e1fa91c55a4591fa2bb019e91a5 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 22 Jul 2025 21:17:34 +0000 Subject: [PATCH 1/2] Hook up P/Invoke to android crypto init for NativeAOT Remove dead code from the NativeAOT runtime logic --- .../DiagnosticSettings.cs | 136 ----------- .../JavaInteropRuntime.cs | 14 +- .../Android.Runtime.NativeAOT/Logging.cs | 50 +--- .../Java.Interop/JreRuntime.cs | 3 - .../ManagedObjectReferenceManager.cs | 229 ------------------ .../Microsoft.Android.Sdk.NativeAOT.targets | 3 +- src/native/native.targets | 8 + 7 files changed, 18 insertions(+), 425 deletions(-) delete mode 100644 src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/DiagnosticSettings.cs delete mode 100644 src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/ManagedObjectReferenceManager.cs diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/DiagnosticSettings.cs b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/DiagnosticSettings.cs deleted file mode 100644 index 3731ec693c1..00000000000 --- a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/DiagnosticSettings.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System.IO; -using System.Text; -using System.Runtime.InteropServices; - -using Java.Interop; - -namespace Microsoft.Android.Runtime; - -struct DiagnosticSettings { - - public bool LogJniLocalReferences; - private string? LrefPath; - - 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 { - return File.CreateText (path); - } - catch (Exception e) { - AndroidLog.Print (AndroidLogLevel.Error, "NativeAot", $"Failed to open log file `{path}`: {e}"); - return null; - } - } - - public void AddDebugDotnetLog () - { - Span value = stackalloc byte [RuntimeNativeMethods.PROP_VALUE_MAX]; - if (!RuntimeNativeMethods.TryGetSystemProperty ("debug.dotnet.log"u8, ref value)) { - return; - } - AddParse (value); - } - - void AddParse (ReadOnlySpan value) - { - while (TryGetNextValue (ref value, out var v)) { - if (v.SequenceEqual ("lref"u8)) { - LogJniLocalReferences = true; - } - else if (v.StartsWith ("lref="u8)) { - LogJniLocalReferences = true; - var path = v.Slice ("lref=".Length); - LrefPath = Encoding.UTF8.GetString (path); - } - else if (v.SequenceEqual ("gref"u8)) { - LogJniGlobalReferences = true; - } - else if (v.StartsWith ("gref="u8)) { - LogJniGlobalReferences = true; - var path = v.Slice ("gref=".Length); - GrefPath = Encoding.UTF8.GetString (path); - } - else if (v.SequenceEqual ("all"u8)) { - LogJniLocalReferences = true; - LogJniGlobalReferences = true; - } - } - - bool TryGetNextValue (ref ReadOnlySpan value, out ReadOnlySpan next) - { - if (value.Length == 0) { - next = default; - return false; - } - int c = value.IndexOf ((byte) ','); - if (c >= 0) { - next = value.Slice (0, c); - value = value.Slice (c + 1); - } - else { - next = value; - value = default; - } - return true; - } - } -} - -static partial class RuntimeNativeMethods { - - [LibraryImport ("c", EntryPoint="__system_property_get")] - static private partial int system_property_get (ReadOnlySpan name, Span value); - - internal const int PROP_VALUE_MAX = 92; - - internal static bool TryGetSystemProperty (ReadOnlySpan name, ref Span value) - { - int len = system_property_get (name, value); - if (len <= 0) { - return false; - } - - value = value.Slice (0, len); - return true; - } -} 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 40c60230a9f..cb621e6ecd4 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs @@ -17,8 +17,8 @@ static int JNI_OnLoad (IntPtr vm, IntPtr reserved) try { AndroidLog.Print (AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnLoad()"); XA_Host_NativeAOT_JNI_OnLoad (vm, reserved); - LogcatTextWriter.Init (); - return (int) JniVersion.v1_6; + AndroidCryptoNative_InitLibraryOnLoad(vm, reserved); + return (int)JniVersion.v1_6; } catch (Exception e) { AndroidLog.Print (AndroidLogLevel.Error, "JavaInteropRuntime", $"JNI_OnLoad() failed: {e}"); @@ -26,7 +26,10 @@ static int JNI_OnLoad (IntPtr vm, IntPtr reserved) } } - [UnmanagedCallersOnly (EntryPoint="JNI_OnUnload")] + [DllImport("*")] + static extern int AndroidCryptoNative_InitLibraryOnLoad (IntPtr vm, IntPtr reserved); + + [UnmanagedCallersOnly(EntryPoint = "JNI_OnUnload")] static void JNI_OnUnload (IntPtr vm, IntPtr reserved) { AndroidLog.Print(AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnUnload"); @@ -42,17 +45,12 @@ static void init (IntPtr jnienv, IntPtr klass, IntPtr classLoader) { JniTransition transition = default; try { - var settings = new DiagnosticSettings (); - settings.AddDebugDotnetLog (); - var options = new NativeAotRuntimeOptions { EnvironmentPointer = jnienv, 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 index 880e107e72f..eace7341569 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/Logging.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/Logging.cs @@ -1,6 +1,5 @@ -// 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 +// NOTE: logging methods below are need temporarily because +// Android.Util.Log won't work until we initialize the Java.Interop.JreRuntime using System.IO; using System.Runtime.InteropServices; @@ -8,51 +7,6 @@ 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)] diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs b/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs index 4e70da25a0e..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; diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/ManagedObjectReferenceManager.cs b/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/ManagedObjectReferenceManager.cs deleted file mode 100644 index b152ff5b797..00000000000 --- a/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/ManagedObjectReferenceManager.cs +++ /dev/null @@ -1,229 +0,0 @@ -// Originally from: https://github.com/dotnet/java-interop/blob/dd3c1d0514addfe379f050627b3e97493e985da6/src/Java.Runtime.Environment/Java.Interop/ManagedObjectReferenceManager.cs -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; - -namespace Java.Interop { - - class ManagedObjectReferenceManager : JniRuntime.JniObjectReferenceManager { - - TextWriter? grefLog; - TextWriter? lrefLog; - - int grefCount; - int wgrefCount; - - - public override int GlobalReferenceCount => grefCount; - public override int WeakGlobalReferenceCount => wgrefCount; - - public override bool LogLocalReferenceMessages => lrefLog != null; - public override bool LogGlobalReferenceMessages => grefLog != null; - - public ManagedObjectReferenceManager (TextWriter? grefLog, TextWriter? lrefLog) - { - if (grefLog != null && lrefLog != null && object.ReferenceEquals (grefLog, lrefLog)) { - this.grefLog = this.lrefLog = TextWriter.Synchronized (grefLog); - return; - } - - var grefPath = Environment.GetEnvironmentVariable ("JAVA_INTEROP_GREF_LOG"); - var lrefPath = Environment.GetEnvironmentVariable ("JAVA_INTEROP_LREF_LOG"); - - bool samePath = !string.IsNullOrEmpty (grefPath) && - !string.IsNullOrEmpty (lrefPath) && - grefPath == lrefPath; - - if (grefLog != null) { - this.grefLog = TextWriter.Synchronized (grefLog); - } - if (lrefLog != null) { - this.lrefLog = TextWriter.Synchronized (lrefLog); - } - - if (this.grefLog == null && !string.IsNullOrEmpty (grefPath)) { - this.grefLog = TextWriter.Synchronized (CreateTextWriter (grefPath)); - } - if (this.lrefLog == null && samePath) { - this.lrefLog = this.grefLog; - } - if (this.lrefLog == null && !string.IsNullOrEmpty (lrefPath)) { - this.lrefLog = TextWriter.Synchronized (CreateTextWriter (lrefPath)); - } - } - - public override void OnSetRuntime (JniRuntime runtime) - { - base.OnSetRuntime (runtime); - } - - static TextWriter CreateTextWriter (string path) - { - return new StreamWriter (path, append: false, encoding: new UTF8Encoding (encoderShouldEmitUTF8Identifier: false)); - } - - public override void WriteLocalReferenceLine (string format, params object[] args) - { - if (lrefLog == null) - return; - lrefLog.WriteLine (format, args); - lrefLog.Flush (); - } - - public override JniObjectReference CreateLocalReference (JniObjectReference reference, ref int localReferenceCount) - { - if (!reference.IsValid) - return reference; - - var r = base.CreateLocalReference (reference, ref localReferenceCount); - - CreatedReference (lrefLog, "+l+ lrefc", localReferenceCount, reference, r, Runtime); - - return r; - } - - public override void DeleteLocalReference (ref JniObjectReference reference, ref int localReferenceCount) - { - if (!reference.IsValid) - return; - - var r = reference; - - base.DeleteLocalReference (ref reference, ref localReferenceCount); - - DeletedReference (lrefLog, "-l- lrefc", localReferenceCount, r, Runtime); - } - - public override void CreatedLocalReference (JniObjectReference reference, ref int localReferenceCount) - { - if (!reference.IsValid) - return; - base.CreatedLocalReference (reference, ref localReferenceCount); - CreatedReference (lrefLog, "+l+ lrefc", localReferenceCount, reference, Runtime); - } - - public override IntPtr ReleaseLocalReference (ref JniObjectReference reference, ref int localReferenceCount) - { - if (!reference.IsValid) - return IntPtr.Zero; - var r = reference; - var p = base.ReleaseLocalReference (ref reference, ref localReferenceCount); - DeletedReference (lrefLog, "-l- lrefc", localReferenceCount, r, Runtime); - return p; - } - - public override void WriteGlobalReferenceLine (string format, params object?[]? args) - { - if (grefLog == null) - return; - grefLog.WriteLine (format, args!); - grefLog.Flush (); - } - - public override JniObjectReference CreateGlobalReference (JniObjectReference reference) - { - if (!reference.IsValid) - return reference; - var n = base.CreateGlobalReference (reference); - int c = Interlocked.Increment (ref grefCount); - CreatedReference (grefLog, "+g+ grefc", c, reference, n, Runtime); - return n; - } - - public override void DeleteGlobalReference (ref JniObjectReference reference) - { - if (!reference.IsValid) - return; - int c = Interlocked.Decrement (ref grefCount); - DeletedReference (grefLog, "-g- grefc", c, reference, Runtime); - base.DeleteGlobalReference (ref reference); - } - - public override JniObjectReference CreateWeakGlobalReference (JniObjectReference reference) - { - if (!reference.IsValid) - return reference; - var n = base.CreateWeakGlobalReference (reference); - - int wc = Interlocked.Increment (ref wgrefCount); - int gc = grefCount; - if (grefLog != null) { - string message = $"+w+ grefc {gc} gwrefc {wc} obj-handle {reference.ToString ()} -> new-handle {n.ToString ()} " + - $"from thread '{Runtime.GetCurrentManagedThreadName ()}'({Environment.CurrentManagedThreadId})" + - Environment.NewLine + - Runtime.GetCurrentManagedThreadStackTrace (skipFrames: 2, fNeedFileInfo: true); - grefLog.WriteLine (message); - grefLog.Flush (); - } - - return n; - } - - public override void DeleteWeakGlobalReference (ref JniObjectReference reference) - { - if (!reference.IsValid) - return; - - int wc = Interlocked.Decrement (ref wgrefCount); - int gc = grefCount; - - if (grefLog != null) { - string message = $"-w- grefc {gc} gwrefc {wc} handle {reference.ToString ()} " + - $"from thread '{Runtime.GetCurrentManagedThreadName ()}'({Environment.CurrentManagedThreadId})" + - Environment.NewLine + - Runtime.GetCurrentManagedThreadStackTrace (skipFrames: 2, fNeedFileInfo: true); - grefLog.WriteLine (message); - grefLog.Flush (); - } - - base.DeleteWeakGlobalReference (ref reference); - } - - protected override void Dispose (bool disposing) - { - } - - static void CreatedReference (TextWriter? writer, string kind, int count, JniObjectReference reference, JniRuntime runtime) - { - if (writer == null) - return; - string message = $"{kind} {count} handle {reference.ToString ()} " + - $"from thread '{runtime.GetCurrentManagedThreadName ()}'({Environment.CurrentManagedThreadId})" + - Environment.NewLine + - runtime.GetCurrentManagedThreadStackTrace (skipFrames: 2, fNeedFileInfo: true); - writer.WriteLine (message); - writer.Flush (); - } - - static void CreatedReference (TextWriter? writer, string kind, int count, JniObjectReference reference, JniObjectReference newReference, JniRuntime runtime) - { - if (writer == null) - return; - string message = $"{kind} {count} obj-handle {reference.ToString ()} -> new-handle {newReference.ToString ()} " + - $"from thread '{runtime.GetCurrentManagedThreadName ()}'({Environment.CurrentManagedThreadId})" + - Environment.NewLine + - runtime.GetCurrentManagedThreadStackTrace (skipFrames: 2, fNeedFileInfo: true); - writer.WriteLine (message); - writer.Flush (); - } - - static void DeletedReference (TextWriter? writer, string kind, int count, JniObjectReference reference, JniRuntime runtime) - { - if (writer == null) - return; - string message = $"{kind} {count} handle {reference.ToString ()} " + - $"from thread '{runtime.GetCurrentManagedThreadName ()}'({Environment.CurrentManagedThreadId})" + - Environment.NewLine + - runtime.GetCurrentManagedThreadStackTrace (skipFrames: 2, fNeedFileInfo: true); - writer.WriteLine (message); - writer.Flush (); - } - } -} \ No newline at end of file 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 0120ff02aba..b3cef07954e 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 @@ -20,7 +20,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. true true - + <_IsPublishing Condition=" '$(_IsPublishing)' == '' ">true @@ -70,6 +70,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 diff --git a/src/native/native.targets b/src/native/native.targets index 9cd05f59b3a..f5d965eb5d2 100644 --- a/src/native/native.targets +++ b/src/native/native.targets @@ -335,6 +335,14 @@ 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" /> From 1191f5c9ee55d53b3fde158b9c3651b34d5d98be Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 12 Sep 2025 21:54:49 +0000 Subject: [PATCH 2/2] Update crypto lib hookup to use the extension point --- .../Android.Runtime.NativeAOT/JavaInteropRuntime.cs | 4 ---- .../targets/Microsoft.Android.Sdk.NativeAOT.targets | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) 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 cb621e6ecd4..ad234e7c8bf 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs @@ -17,7 +17,6 @@ static int JNI_OnLoad (IntPtr vm, IntPtr reserved) try { AndroidLog.Print (AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnLoad()"); XA_Host_NativeAOT_JNI_OnLoad (vm, reserved); - AndroidCryptoNative_InitLibraryOnLoad(vm, reserved); return (int)JniVersion.v1_6; } catch (Exception e) { @@ -26,9 +25,6 @@ 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) { 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 b3cef07954e..6f4bf947655 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 @@ -24,6 +24,10 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. <_IsPublishing Condition=" '$(_IsPublishing)' == '' ">true + + + + true