From a1c7ecc45ba7631fc2dd6db4e3a58a9a51f206b9 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Fri, 16 Jan 2026 21:11:57 -0800 Subject: [PATCH 1/5] Use android-native-tools for NativeAOT linking instead of Android NDK This eliminates the NDK dependency for NativeAOT builds by using our custom LLVM build (android-native-tools) that ships with the Android workload. Changes: - Add LinkNativeAotLibrary task that invokes ld.lld from android-native-tools - Set NativeCompilationDuringPublish=false and NativeLib=Static so ILC produces only object files, then we link them ourselves - Add -z nostart-stop-gc linker flag for __start/__stop symbols - Include sysroot libraries (libc++_static.a, libunwind.a, etc.) in NativeAOT runtime pack - Fix duplicate assembly error by removing items before re-adding them to ResolvedFileToPublish - Keep legacy NDK path behind AndroidNativeAotUseNdk=true flag --- .../Microsoft.Android.Runtime.proj | 14 + ...oft.Android.Sdk.AssemblyResolution.targets | 7 +- .../Microsoft.Android.Sdk.NativeAOT.targets | 213 +++++++++++++--- .../Tasks/LinkNativeAotLibrary.cs | 239 ++++++++++++++++++ .../ProcessRuntimePackLibraryDirectories.cs | 3 +- .../Utilities/NativeLinker.cs | 7 + 6 files changed, 443 insertions(+), 40 deletions(-) create mode 100644 src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotLibrary.cs diff --git a/build-tools/create-packs/Microsoft.Android.Runtime.proj b/build-tools/create-packs/Microsoft.Android.Runtime.proj index 93452ea06b6..0289d0aa47a 100644 --- a/build-tools/create-packs/Microsoft.Android.Runtime.proj +++ b/build-tools/create-packs/Microsoft.Android.Runtime.proj @@ -105,6 +105,20 @@ projects that use the Microsoft.Android.Runtimes framework in .NET 6+. + + + + + + + + + + + + + + <_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Microsoft.Android.Runtime.NativeAOT.dll" /> diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets index b1717d2b0fe..dd9d74553f0 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets @@ -32,8 +32,11 @@ _ResolveAssemblies MSBuild target. - - <_RunAotMaybe Condition=" '$(_AndroidUseMarshalMethods)' != 'True' ">_AndroidAot + + <_RunAotMaybe Condition=" '$(_AndroidUseMarshalMethods)' != 'True' and '$(_AndroidRuntime)' != 'NativeAOT' ">_AndroidAot @@ -10,6 +17,8 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. + + @@ -22,6 +31,28 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. true <_IsPublishing Condition=" '$(_IsPublishing)' == '' ">true + + + false + + + true + + + false + + + <_RunAotMaybe Condition=" '$(_AndroidUseMarshalMethods)' != 'True' ">_AndroidNativeAotCompileAndLink @@ -43,40 +74,38 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. /> - + _AndroidBeforeIlcCompile; + Compile; SetupOSSpecificProps; PrepareForILLink; ILLink; ComputeIlcCompileInputs; _AndroidComputeIlcCompileInputs; - $(IlcCompileDependsOn) - - <_NdkAbi Condition=" '$(RuntimeIdentifier)' == 'android-arm64' ">aarch64 - <_NdkAbi Condition=" '$(RuntimeIdentifier)' == 'android-x64' ">x86_64 - <_NdkSysrootAbi>$(_NdkAbi)-linux-android - <_NdkPrebuiltAbi Condition=" $([MSBuild]::IsOSPlatform('windows')) ">windows-x86_64 - <_NdkPrebuiltAbi Condition=" $([MSBuild]::IsOSPlatform('osx')) ">darwin-x86_64 - <_NdkPrebuiltAbi Condition=" $([MSBuild]::IsOSPlatform('linux')) ">linux-x86_64 - <_NdkSysrootDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/sysroot/usr/lib/$(_NdkSysrootAbi)/ - <_NdkBinDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/bin/ - clang++ - llvm-objcopy + + Static false - + true + <_ExtraTrimmerArgs>$(_ExtraTrimmerArgs) --notrimwarn - - @@ -96,39 +123,35 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. $(_OriginalSuppressTrimAnalysisWarnings) - - - - - + <_AndroidILLinkAssemblies Include="@(ManagedAssemblyToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" Condition="Exists('$(IntermediateLinkDir)%(Filename)%(Extension)')" /> + + <_AndroidILLinkAssemblies Remove="$(IntermediateLinkDir)$(TargetName)$(TargetExt)" /> + - + + + + - <_NdkLibs Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" /> - - - <_NdkLibs Include="$(_NdkSysrootDir)libc++_static.a" /> - <_NdkLibs Include="$(_NdkSysrootDir)libc++abi.a" /> - - - - @@ -192,19 +215,135 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. DebugBuild="$(AndroidIncludeDebugSymbols)" WorkingDirectory="$(_NativeAssemblySourceDir)" AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)" /> + + + + + <_AndroidNativeAotLinkTarget Condition=" '$(AndroidNativeAotUseNdk)' != 'true' ">_LinkNativeAotLibrary + <_AndroidNativeAotLinkTarget Condition=" '$(AndroidNativeAotUseNdk)' == 'true' ">_GenerateNativeAotAndroidAppLibrary + + + + + + + - - + + <_NativeAotObjectFiles Include="@(_PrivateJniInitFuncsNativeObjectFile)" /> + <_NativeAotObjectFiles Include="@(_PrivateEnvironmentNativeObjectFile)" /> + + + <_NativeAotArchives Include="@(NativeLibrary)" /> + + + <_NativeAotArchives Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" /> + + + + + + + + <_NativeAotOutputLibrary>$(NativeOutputPath)lib$(TargetName).so + + + + + + + + + + + + + + + + lib$(TargetName).so + lib$(TargetName).so + PreserveNewest + - + + DependsOnTargets="_GenerateNativeAotAndroidAppAssemblerSources" + Condition=" '$(AndroidNativeAotUseNdk)' == 'true' "> + + + <_NdkAbi Condition=" '$(RuntimeIdentifier)' == 'android-arm64' ">aarch64 + <_NdkAbi Condition=" '$(RuntimeIdentifier)' == 'android-x64' ">x86_64 + <_NdkSysrootAbi>$(_NdkAbi)-linux-android + <_NdkPrebuiltAbi Condition=" $([MSBuild]::IsOSPlatform('windows')) ">windows-x86_64 + <_NdkPrebuiltAbi Condition=" $([MSBuild]::IsOSPlatform('osx')) ">darwin-x86_64 + <_NdkPrebuiltAbi Condition=" $([MSBuild]::IsOSPlatform('linux')) ">linux-x86_64 + <_NdkSysrootDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/sysroot/usr/lib/$(_NdkSysrootAbi)/ + <_NdkBinDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/bin/ + clang++ + llvm-objcopy + + + + + + + + + + + <_NdkLibs Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" /> + + + <_NdkLibs Include="$(_NdkSysrootDir)libc++_static.a" /> + <_NdkLibs Include="$(_NdkSysrootDir)libc++abi.a" /> + + + + + diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotLibrary.cs b/src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotLibrary.cs new file mode 100644 index 00000000000..bf524fa2946 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotLibrary.cs @@ -0,0 +1,239 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.IO; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +using Microsoft.Android.Build.Tasks; + +namespace Xamarin.Android.Tasks; + +/// +/// Links NativeAOT-compiled object files into a shared library (.so) for Android. +/// Uses android-native-tools (our custom LLVM build) instead of the Android NDK. +/// +public class LinkNativeAotLibrary : AndroidTask +{ + public override string TaskPrefix => "LNA"; + + [Required] + public string AndroidBinUtilsDirectory { get; set; } = ""; + + [Required] + public string IntermediateOutputPath { get; set; } = ""; + + /// + /// The main object file produced by ILC ($(NativeObject)). + /// + [Required] + public string NativeObject { get; set; } = ""; + + /// + /// Additional object files (e.g., generated assembler sources). + /// + public ITaskItem[] NativeObjectFiles { get; set; } = []; + + /// + /// Static archives from ILC SDK and runtime packs. + /// + [Required] + public ITaskItem[] NativeArchives { get; set; } = []; + + [Required] + public string OutputLibrary { get; set; } = ""; + + [Required] + public string RuntimeIdentifier { get; set; } = ""; + + [Required] + public ITaskItem[] RuntimePackLibraryDirectories { get; set; } = []; + + public bool StripDebugSymbols { get; set; } = true; + public bool SaveDebugSymbols { get; set; } = true; + + public override bool RunTask () + { + string abi = GetAbiFromRuntimeIdentifier (RuntimeIdentifier); + string clangArch = GetClangArchFromRuntimeIdentifier (RuntimeIdentifier); + + // Compute soname - Android requires a proper soname or it will refuse to load the library + string soname = Path.GetFileNameWithoutExtension (OutputLibrary); + if (soname.StartsWith ("lib", StringComparison.OrdinalIgnoreCase)) { + soname = soname.Substring (3); + } + + // Find the sysroot directory from runtime pack library directories + string? sysrootDir = FindSysrootDirectory (); + if (sysrootDir == null) { + Log.LogError ("Could not find sysroot directory containing C++ runtime libraries in runtime pack"); + return false; + } + + var linker = new NativeLinker (Log, abi, soname, AndroidBinUtilsDirectory, IntermediateOutputPath, RuntimePackLibraryDirectories) { + StripDebugSymbols = StripDebugSymbols, + SaveDebugSymbols = SaveDebugSymbols, + AllowUndefinedSymbols = false, + UseNdkLibraries = false, + TargetsCLR = false, // NativeAOT doesn't need --eh-frame-hdr like CoreCLR does + UseSymbolic = true, + IsNativeAOT = true, // Enable NativeAOT-specific linker flags + }; + + List linkItems = OrganizeCommandLineItems (abi, sysrootDir, clangArch); + List linkStartFiles = GetCrtStartFiles (abi, sysrootDir); + List linkEndFiles = GetCrtEndFiles (abi, sysrootDir); + + bool success = linker.Link ( + CreateItemWithAbi (OutputLibrary, abi), + linkItems, + linkStartFiles, + linkEndFiles, + exportDynamicSymbols: null + ); + + if (!success) { + Log.LogError ($"Failed to link NativeAOT library: {OutputLibrary}"); + } + + return success; + } + + string GetAbiFromRuntimeIdentifier (string rid) + { + return rid switch { + "android-arm64" => "arm64-v8a", + "android-x64" => "x86_64", + _ => throw new NotSupportedException ($"Unsupported RuntimeIdentifier for NativeAOT: {rid}") + }; + } + + string GetClangArchFromRuntimeIdentifier (string rid) + { + return rid switch { + "android-arm64" => "aarch64", + "android-x64" => "x86_64", + _ => throw new NotSupportedException ($"Unsupported RuntimeIdentifier for NativeAOT: {rid}") + }; + } + + /// + /// Finds the sysroot directory containing C++ runtime libraries. + /// + string? FindSysrootDirectory () + { + foreach (var dir in RuntimePackLibraryDirectories) { + string libcppPath = Path.Combine (dir.ItemSpec, "libc++_static.a"); + if (File.Exists (libcppPath)) { + return dir.ItemSpec; + } + } + return null; + } + + /// + /// Get CRT start files (crtbegin_so.o). + /// + List GetCrtStartFiles (string abi, string sysrootDir) + { + var items = new List (); + string crtbegin = Path.Combine (sysrootDir, "crtbegin_so.o"); + if (File.Exists (crtbegin)) { + items.Add (CreateItemWithAbi (crtbegin, abi)); + } else { + Log.LogError ($"Required CRT file 'crtbegin_so.o' not found in {sysrootDir}. The NativeAOT runtime pack may be incomplete."); + } + return items; + } + + /// + /// Get CRT end files (crtend_so.o). + /// + List GetCrtEndFiles (string abi, string sysrootDir) + { + var items = new List (); + string crtend = Path.Combine (sysrootDir, "crtend_so.o"); + if (File.Exists (crtend)) { + items.Add (CreateItemWithAbi (crtend, abi)); + } else { + Log.LogError ($"Required CRT file 'crtend_so.o' not found in {sysrootDir}. The NativeAOT runtime pack may be incomplete."); + } + return items; + } + + /// + /// Organizes link items in the correct order for the native linker. + /// Order matters for static linking! + /// + List OrganizeCommandLineItems (string abi, string sysrootDir, string clangArch) + { + var items = new List (); + + // First: ILC's main object file + items.Add (CreateItemWithAbi (NativeObject, abi)); + + // Then: additional object files (generated assembler sources) + foreach (ITaskItem objFile in NativeObjectFiles) { + items.Add (CreateItemWithAbi (objFile.ItemSpec, abi)); + } + + // Then: static archives from ILC SDK and runtime packs + foreach (ITaskItem archive in NativeArchives) { + var item = CreateItemWithAbi (archive.ItemSpec, abi); + // Check if this archive should be included with --whole-archive + string? wholeArchive = archive.GetMetadata (KnownMetadata.NativeLinkWholeArchive); + if (!wholeArchive.IsNullOrEmpty () && Boolean.Parse (wholeArchive)) { + item.SetMetadata (KnownMetadata.NativeLinkWholeArchive, "true"); + } + items.Add (item); + } + + // C++ standard library (required by NativeAOT runtime for std::nothrow, operator new/delete, etc.) + string libcppStatic = Path.Combine (sysrootDir, "libc++_static.a"); + if (File.Exists (libcppStatic)) { + items.Add (CreateItemWithAbi (libcppStatic, abi)); + } else { + Log.LogError ($"Required library 'libc++_static.a' not found in {sysrootDir}. The NativeAOT runtime pack may be incomplete."); + } + + string libcppabi = Path.Combine (sysrootDir, "libc++abi.a"); + if (File.Exists (libcppabi)) { + items.Add (CreateItemWithAbi (libcppabi, abi)); + } else { + Log.LogError ($"Required library 'libc++abi.a' not found in {sysrootDir}. The NativeAOT runtime pack may be incomplete."); + } + + // Unwinding support + string libunwind = Path.Combine (sysrootDir, "libunwind.a"); + if (File.Exists (libunwind)) { + items.Add (CreateItemWithAbi (libunwind, abi)); + } else { + Log.LogError ($"Required library 'libunwind.a' not found in {sysrootDir}. The NativeAOT runtime pack may be incomplete."); + } + + // Compiler runtime builtins (required for atomic intrinsics and TLS emulation) + string libclangBuiltins = Path.Combine (sysrootDir, $"libclang_rt.builtins-{clangArch}-android.a"); + if (File.Exists (libclangBuiltins)) { + items.Add (CreateItemWithAbi (libclangBuiltins, abi)); + } else { + Log.LogError ($"Required library 'libclang_rt.builtins-{clangArch}-android.a' not found in {sysrootDir}. The NativeAOT runtime pack may be incomplete."); + } + + // Add required system libraries (linked dynamically) + items.Add (NativeLinker.MakeLibraryItem ("log", abi)); // Android logging + items.Add (NativeLinker.MakeLibraryItem ("z", abi)); // zlib compression + items.Add (NativeLinker.MakeLibraryItem ("m", abi)); // math library + items.Add (NativeLinker.MakeLibraryItem ("dl", abi)); // dynamic linking + items.Add (NativeLinker.MakeLibraryItem ("c", abi)); // C library (must be last) + + return items; + } + + ITaskItem CreateItemWithAbi (string path, string abi) + { + var item = new TaskItem (path); + item.SetMetadata (KnownMetadata.Abi, abi); + return item; + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessRuntimePackLibraryDirectories.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessRuntimePackLibraryDirectories.cs index 142a0dccde6..c4d10e2f3f9 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessRuntimePackLibraryDirectories.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessRuntimePackLibraryDirectories.cs @@ -95,6 +95,7 @@ bool IsInSupportedRuntimePack (ITaskItem item) } return NuGetPackageId.StartsWith ("Microsoft.Android.Runtime.CoreCLR.", StringComparison.OrdinalIgnoreCase) || - NuGetPackageId.StartsWith ("Microsoft.Android.Runtime.Mono.", StringComparison.OrdinalIgnoreCase); + NuGetPackageId.StartsWith ("Microsoft.Android.Runtime.Mono.", StringComparison.OrdinalIgnoreCase) || + NuGetPackageId.StartsWith ("Microsoft.Android.Runtime.NativeAOT.", StringComparison.OrdinalIgnoreCase); } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs index ac53cfebd11..44df0f0cf33 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs @@ -49,6 +49,7 @@ class NativeLinker public bool UseNdkLibraries { get; set; } = false; public bool TargetsCLR { get; set; } public bool UseSymbolic { get; set; } + public bool IsNativeAOT { get; set; } public string? NdkRootPath { get; set; } public string? NdkApiLevel { get; set; } public int ZipAlignmentPages { get; set; } = AndroidZipAlign.DefaultZipAlignment64Bit; @@ -163,6 +164,12 @@ public bool Link (ITaskItem outputLibraryPath, List linkItems, List Date: Tue, 20 Jan 2026 15:55:56 -0800 Subject: [PATCH 2/5] Add --eh-frame-hdr linker flag for NativeAOT builds NativeAOT's DWARF-based stack unwinder (LLVM libunwind) requires the .eh_frame_hdr section to locate Frame Description Entries during GC stack walking. Without this section, the runtime crashes with SIGSEGV when attempting to read unwind information from invalid memory. --- src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotLibrary.cs | 2 +- src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotLibrary.cs b/src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotLibrary.cs index bf524fa2946..3fa91120660 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotLibrary.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotLibrary.cs @@ -76,7 +76,7 @@ public override bool RunTask () SaveDebugSymbols = SaveDebugSymbols, AllowUndefinedSymbols = false, UseNdkLibraries = false, - TargetsCLR = false, // NativeAOT doesn't need --eh-frame-hdr like CoreCLR does + TargetsCLR = false, // NativeAOT uses its own runtime, not CoreCLR UseSymbolic = true, IsNativeAOT = true, // Enable NativeAOT-specific linker flags }; diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs index 44df0f0cf33..5b5f5d89262 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs @@ -156,8 +156,8 @@ public bool Link (ITaskItem outputLibraryPath, List linkItems, List Date: Wed, 21 Jan 2026 13:34:55 -0800 Subject: [PATCH 3/5] Fix NativeAOT linking for projects with spaces in names Quote the soname argument to handle project names containing spaces. Without this fix, a project named 'Test Me' would cause the linker to fail with 'cannot open Me: No such file or directory' because the unquoted soname would be split into separate arguments. --- src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs index 5b5f5d89262..8b5c04d767a 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs @@ -66,7 +66,7 @@ public NativeLinker (TaskLoggingHelper log, string abi, string soname, string bi ld = Path.Combine (binutilsDir, MonoAndroidHelper.GetExecutablePath (binutilsDir, "ld")); objcopy = Path.Combine (binutilsDir, MonoAndroidHelper.GetExecutablePath (binutilsDir, "llvm-objcopy")); - extraArgs.Add ($"-soname {soname}"); + extraArgs.Add ($"-soname {MonoAndroidHelper.QuoteFileNameArgument (soname)}"); string? elfArch = null; uint maxPageSize; From d2c6f45c55590e1e810cb897dfef3a00fbac27b1 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 2 Feb 2026 11:48:48 -0800 Subject: [PATCH 4/5] Remove sysroot libraries from NativeAOT publish output The sysroot libraries (libc.so, libdl.so, liblog.so, libm.so, libz.so) are needed by ld.lld for symbol resolution during linking, but should not be packaged in the APK - the real implementations are provided by the Android system at runtime. For Mono/CoreCLR, these are removed in _ResolveAssemblies via ProcessRuntimePackLibraryDirectories, but NativeAOT was excluded from that logic. Remove the exclusion so all runtimes use the same removal logic in the outer build. The inner build still runs ProcessRuntimePackLibraryDirectories to get _RuntimePackLibraryDirectory for linking, but doesn't need to handle the removal - that's done by the outer build after it receives the ResolvedFileToPublish items. --- .../targets/Microsoft.Android.Sdk.AssemblyResolution.targets | 1 - .../targets/Microsoft.Android.Sdk.NativeAOT.targets | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets index dd9d74553f0..00179fd0b38 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets @@ -116,7 +116,6 @@ _ResolveAssemblies MSBuild target. are never taken into consideration in any context. --> 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 a1c08760901..be672824e4f 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 @@ -272,11 +272,12 @@ This eliminates the NDK dependency for NativeAOT builds. We need to run ProcessRuntimePackLibraryDirectories here because this code runs in an inner build and `_RuntimePackLibraryDirectory` items aren't carried over from the outer build. This discovers sysroot library directories (containing libc.so, liblog.so, etc.) from the runtime pack. + Note: Removal of sysroot libraries from ResolvedFileToPublish happens in the outer build's + _ResolveAssemblies target, not here. --> - From 2a742d0ca879808f2b7bf7489713df5e8c0852d2 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 2 Feb 2026 16:19:35 -0800 Subject: [PATCH 5/5] Copy CRT object files to NativeAOT runtime pack The _CopyToPackDirs target was only copying *.a files for NativeAOT, but crtbegin_so.o and crtend_so.o are required for linking. Add *.o glob pattern to NativeAOT section, matching CoreCLR behavior. --- src/native/native.targets | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/native/native.targets b/src/native/native.targets index 9cd05f59b3a..c2d73b1fda2 100644 --- a/src/native/native.targets +++ b/src/native/native.targets @@ -338,6 +338,10 @@ + <_RuntimePackFiles Include="$(NativeRuntimeOutputRootDir)$(_RuntimeRedistDirName)\%(AndroidSupportedTargetJitAbi.AndroidRID)\*.o" + AndroidRID="%(AndroidSupportedTargetJitAbi.AndroidRID)" + AndroidRuntime="$(CMakeRuntimeFlavor)" + RuntimePackName="$(_RuntimePackName)" /> <_RuntimePackFiles Include="$(NativeRuntimeOutputRootDir)$(_RuntimeRedistDirName)\%(AndroidSupportedTargetJitAbi.AndroidRID)\*.a" AndroidRID="%(AndroidSupportedTargetJitAbi.AndroidRID)" AndroidRuntime="$(CMakeRuntimeFlavor)"