Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 578
[NativeAOT] Use NativeLinker and invoke lld directly for linking#11256
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
97c1414ec129aadb6be18c4969362ff10fbfd6b78ec5415f14ff1c63bde7d34eb9c237df4b013ea77cb2aeaff4534b4a6174261f8c6d3b4a83a71deFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -10,6 +10,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. | ||
| <UsingTask TaskName="Xamarin.Android.Tasks.SetNdkPathForIlc" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" /> | ||
| <UsingTask TaskName="Xamarin.Android.Tasks.GenerateNativeAotLibraryLoadAssemblerSources" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" /> | ||
| <UsingTask TaskName="Xamarin.Android.Tasks.GenerateNativeAotEnvironmentAssemblerSources" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" /> | ||
| <UsingTask TaskName="Xamarin.Android.Tasks.LinkNativeAotSharedLibrary" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" /> | ||
| <!-- Default property values for NativeAOT --> | ||
| <PropertyGroup> | ||
| @@ -275,46 +276,77 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. | ||
| <!-- | ||
| With NativeLib=static, ILC's LinkNative target produces a .a archive via `ar`. | ||
| We need to link the ILC .o output + all LinkerArg items into a .so ourselves. | ||
| This target runs after LinkNative and produces the shared library that Android needs. | ||
| We need to link the ILC .o output + all NativeLibrary/system libs into a .so ourselves. | ||
| This target runs after LinkNative and uses ld.lld directly via LinkNativeAotSharedLibrary task. | ||
| --> | ||
| <Target Name="_AndroidLinkNativeAotSharedLibrary" | ||
| AfterTargets="LinkNative" | ||
| DependsOnTargets="_PrepareNativeAotAndroidAppInputs" | ||
| Inputs="$(NativeObject);@(NativeLibrary)" | ||
| Outputs="$(NativeOutputPath)$(NativeBinaryPrefix)$(TargetName).so"> | ||
| <PropertyGroup> | ||
| <_AndroidNativeAotSharedLibrary>$(NativeOutputPath)$(NativeBinaryPrefix)$(TargetName).so</_AndroidNativeAotSharedLibrary> | ||
| <!-- NDK paths for CRT objects and compiler-rt --> | ||
| <_NdkApiSysrootDir>$(_NdkSysrootDir)$(_NDKApiLevel)/</_NdkApiSysrootDir> | ||
| <_NdkClangResourceDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/lib/clang</_NdkClangResourceDir> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <_AndroidNativeAotLinkerArgs Include=""$(NativeObject)"" /> | ||
| <_AndroidNativeAotLinkerArgs Include="-shared" /> | ||
| <_AndroidNativeAotLinkerArgs Include="-fuse-ld=lld" /> | ||
| <_AndroidNativeAotLinkerArgs Include="-o "$(_AndroidNativeAotSharedLibrary)"" /> | ||
| <!-- These flags are in the ILC targets' LinkNative/LinkerArg but conditioned on NativeLib=Shared | ||
| or LinkerFlavor=lld, so they're missing when NativeLib=static. Add them back. --> | ||
| <_AndroidNativeAotLinkerArgs Include="-Wl,-e,0x0" /> | ||
| <_AndroidNativeAotLinkerArgs Include="-Wl,-z,max-page-size=16384" /> | ||
| <_AndroidNativeAotLinkerArgs Include="-Wl,--version-script="$(ExportsFile)"" Condition="'$(ExportsFile)' != ''" /> | ||
| <_AndroidNativeAotLinkerArgs Include="-Wl,--export-dynamic" Condition="'$(ExportsFile)' != ''" /> | ||
| <_AndroidNativeAotLinkerArgs Include="-Wl,--discard-all" /> | ||
| <_AndroidNativeAotLinkerArgs Include="-Wl,--gc-sections" /> | ||
| <_AndroidNativeAotLinkerArgs Include="-Wl,-T,"$(NativeIntermediateOutputPath)sections.ld"" /> | ||
| <_AndroidNativeAotLinkerArgs Include="@(LinkerArg)" /> | ||
| <!-- CRT start file --> | ||
| <_NativeAotCrtStartFiles Include="$(_NdkApiSysrootDir)crtbegin_so.o"> | ||
| <Abi>@(_PrivateBuildTargetAbi)</Abi> | ||
| </_NativeAotCrtStartFiles> | ||
| <!-- CRT end file --> | ||
| <_NativeAotCrtEndFiles Include="$(_NdkApiSysrootDir)crtend_so.o"> | ||
| <Abi>@(_PrivateBuildTargetAbi)</Abi> | ||
| </_NativeAotCrtEndFiles> | ||
| <!-- Library search paths (matching what clang provides) --> | ||
| <_NativeAotLibSearchPaths Include="$(_NdkApiSysrootDir)" /> | ||
| <_NativeAotLibSearchPaths Include="$(_NdkSysrootDir)" /> | ||
| <!-- ILC runtime .a files are already in @(NativeLibrary) from SetupOSSpecificProps --> | ||
| <!-- Android-added libraries from _AndroidComputeIlcCompileInputs are in @(_NdkLibs) --> | ||
| <_NativeAotLinkLibraries Include="@(NativeLibrary)" /> | ||
| <_NativeAotLinkLibraries Include="@(_NdkLibs)" /> | ||
| <!-- Additional .o files (jni_init, environment) --> | ||
| <_NativeAotAdditionalObjects Include="@(_PrivateJniInitFuncsNativeObjectFile)" /> | ||
| <_NativeAotAdditionalObjects Include="@(_PrivateEnvironmentNativeObjectFile)" /> | ||
| <!-- System libraries (resolved via -L search paths) --> | ||
| <_NativeAotSystemLibraries Include="dl" /> | ||
| <_NativeAotSystemLibraries Include="z" /> | ||
| <_NativeAotSystemLibraries Include="log" /> | ||
| <_NativeAotSystemLibraries Include="m" /> | ||
| <_NativeAotSystemLibraries Include="c" /> | ||
| <!-- Compiler-rt builtins and unwinder (explicit paths from NDK) --> | ||
| <_NativeAotCompilerRtLibs Include="$(_NdkClangResourceDir)/**/libclang_rt.builtins-$(_NdkAbi)-android.a" /> | ||
| <_NativeAotCompilerRtLibs Include="$(_NdkClangResourceDir)/**/$(_NdkAbi)/libunwind.a" /> | ||
| </ItemGroup> | ||
| <!-- Linker script to retain the __modules section (required by NativeAOT runtime) --> | ||
| <WriteLinesToFile File="$(NativeIntermediateOutputPath)sections.ld" Lines="OVERWRITE_SECTIONS { __modules : { KEEP(*(__modules)) } }" Overwrite="true" /> | ||
| <MakeDir Directories="$([System.IO.Path]::GetDirectoryName($(_AndroidNativeAotSharedLibrary)))" /> | ||
| <Exec Command=""$(CppLinker)" @(_AndroidNativeAotLinkerArgs, ' ')" /> | ||
| <!-- Strip debug symbols, matching what ILC's LinkNative does --> | ||
| <Exec Condition="'$(StripSymbols)' == 'true' and '$(NativeDebugSymbols)' == 'true'" | ||
| Command=""$(ObjCopyName)" --only-keep-debug "$(_AndroidNativeAotSharedLibrary)" "$(_AndroidNativeAotSharedLibrary)$(NativeSymbolExt)"" /> | ||
| <Exec Condition="'$(StripSymbols)' == 'true'" | ||
| Command=""$(ObjCopyName)" --strip-debug --strip-unneeded "$(_AndroidNativeAotSharedLibrary)"" /> | ||
| <Exec Condition="'$(StripSymbols)' == 'true' and '$(NativeDebugSymbols)' == 'true'" | ||
| Command=""$(ObjCopyName)" --add-gnu-debuglink="$(_AndroidNativeAotSharedLibrary)$(NativeSymbolExt)" "$(_AndroidNativeAotSharedLibrary)"" /> | ||
| <LinkNativeAotSharedLibrary | ||
| AndroidBinUtilsDirectory="$(_NdkBinDir)" | ||
| IntermediateOutputPath="$(NativeIntermediateOutputPath)" | ||
| RuntimePackLibraryDirectories="@(RuntimePackLibraryDirectories)" | ||
| NativeObject="$(NativeObject)" | ||
| OutputSharedLibrary="$(_AndroidNativeAotSharedLibrary)" | ||
| NativeLibraries="@(_NativeAotLinkLibraries)" | ||
| AdditionalObjectFiles="@(_NativeAotAdditionalObjects)" | ||
| CrtStartFiles="@(_NativeAotCrtStartFiles)" | ||
| CrtEndFiles="@(_NativeAotCrtEndFiles)" | ||
| CompilerRuntimeLibraries="@(_NativeAotCompilerRtLibs)" | ||
| SystemLibraries="@(_NativeAotSystemLibraries)" | ||
| LibrarySearchPaths="@(_NativeAotLibSearchPaths)" | ||
| ExportsFile="$(ExportsFile)" | ||
| LinkerScript="$(NativeIntermediateOutputPath)sections.ld" | ||
| LinkerScriptContent="OVERWRITE_SECTIONS { __modules : { KEEP(*(__modules)) } }" | ||
| ExtraLinkerArgs="$(_AndroidNativeAotExtraLinkerArgs)" | ||
| SupportedAbis="@(_PrivateBuildTargetAbi)" | ||
| DebugBuild="$(AndroidIncludeDebugSymbols)" | ||
sbomer marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| /> | ||
| <ItemGroup> | ||
| <!-- Replace the .a that NativeCompile/ComputeLinkedFilesToPublish would publish with our .so --> | ||
| @@ -324,7 +356,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. | ||
| <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||
| </ResolvedFileToPublish> | ||
| <FileWrites Include="$(_AndroidNativeAotSharedLibrary)" /> | ||
| <FileWrites Include="$(_AndroidNativeAotSharedLibrary)$(NativeSymbolExt)" Condition="'$(StripSymbols)' == 'true' and '$(NativeDebugSymbols)' == 'true'" /> | ||
| <FileWrites Include="$(_AndroidNativeAotSharedLibrary)$(NativeSymbolExt)" Condition=" '$(AndroidIncludeDebugSymbols)' != 'true'" /> | ||
| <FileWrites Include="$(NativeIntermediateOutputPath)sections.ld" /> | ||
| </ItemGroup> | ||
| </Target> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,231 @@ | ||
| #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; | ||
| /// <summary> | ||
| /// Links a NativeAOT shared library (.so) from the ILC-compiled object file and runtime archives. | ||
| /// Uses ld.lld directly instead of clang, matching the approach used by NativeLinker for CoreCLR/Mono. | ||
| /// </summary> | ||
| public class LinkNativeAotSharedLibrary : AndroidTask | ||
| { | ||
| public override string TaskPrefix => "LNAS"; | ||
| [Required] | ||
| public string AndroidBinUtilsDirectory { get; set; } = ""; | ||
| [Required] | ||
| public string IntermediateOutputPath { get; set; } = ""; | ||
| [Required] | ||
| public ITaskItem [] RuntimePackLibraryDirectories { get; set; } = []; | ||
| /// <summary> | ||
| /// The ILC-compiled object file (e.g., TestApp.o) | ||
| /// </summary> | ||
| [Required] | ||
| public ITaskItem NativeObject { get; set; } = null!; // NRT - guarded by [Required] | ||
jonathanpeppers marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| /// <summary> | ||
| /// The output shared library path (e.g., libTestApp.so) | ||
| /// </summary> | ||
| [Required] | ||
| public ITaskItem OutputSharedLibrary { get; set; } = null!; // NRT - guarded by [Required] | ||
| /// <summary> | ||
| /// Runtime and BCL static archives to link (e.g., libSystem.Native.a, libRuntime.WorkstationGC.a) | ||
| /// </summary> | ||
| [Required] | ||
| public ITaskItem [] NativeLibraries { get; set; } = []; | ||
| /// <summary> | ||
| /// Additional object files to link (e.g., jni_init_funcs.o, environment.o, libbootstrapperdll.o) | ||
| /// </summary> | ||
| public ITaskItem []? AdditionalObjectFiles { get; set; } | ||
| /// <summary> | ||
| /// CRT start files (e.g., crtbegin_so.o) — linked first | ||
| /// </summary> | ||
| public ITaskItem []? CrtStartFiles { get; set; } | ||
| /// <summary> | ||
| /// CRT end files (e.g., crtend_so.o) — linked last | ||
| /// </summary> | ||
| public ITaskItem []? CrtEndFiles { get; set; } | ||
| /// <summary> | ||
| /// Compiler-rt and unwinder libraries to link after user libraries (explicit file paths) | ||
| /// </summary> | ||
| public ITaskItem []? CompilerRuntimeLibraries { get; set; } | ||
| /// <summary> | ||
| /// System libraries to link with -l (e.g., "dl", "c", "m", "z", "log") | ||
| /// </summary> | ||
| public ITaskItem []? SystemLibraries { get; set; } | ||
| /// <summary> | ||
| /// Additional library search paths (e.g., NDK sysroot paths) | ||
| /// </summary> | ||
| public ITaskItem []? LibrarySearchPaths { get; set; } | ||
| /// <summary> | ||
| /// Version script for symbol visibility (e.g., TestApp.exports) | ||
| /// </summary> | ||
| public string? ExportsFile { get; set; } | ||
| /// <summary> | ||
| /// Linker script (e.g., sections.ld for __modules retention) | ||
| /// </summary> | ||
| public string? LinkerScript { get; set; } | ||
| /// <summary> | ||
| /// Linker script content to write before linking | ||
| /// </summary> | ||
| public string? LinkerScriptContent { get; set; } | ||
| /// <summary> | ||
| /// Extra arguments to pass directly to the linker, separated by semicolons. | ||
| /// </summary> | ||
| public string? ExtraLinkerArgs { get; set; } | ||
| [Required] | ||
| public string SupportedAbis { get; set; } = ""; | ||
| public bool DebugBuild { get; set; } | ||
| public override bool RunTask () | ||
| { | ||
| foreach (string abi in SupportedAbis.Split (new [] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { | ||
| if (!LinkForAbi (abi)) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| bool LinkForAbi (string abi) | ||
| { | ||
| var linker = new NativeLinker ( | ||
| Log, | ||
| abi, | ||
| Path.GetFileName (OutputSharedLibrary.ItemSpec), | ||
| AndroidBinUtilsDirectory, | ||
| IntermediateOutputPath, | ||
| RuntimePackLibraryDirectories | ||
| ) { | ||
| StripDebugSymbols = !DebugBuild, | ||
| SaveDebugSymbols = !DebugBuild, | ||
| AllowUndefinedSymbols = false, | ||
| // NativeAOT-specific options | ||
| ExportDynamic = true, | ||
| UseEhFrameHdr = true, | ||
| DiscardAll = true, | ||
| AsNeeded = true, | ||
| HashStyleBoth = true, | ||
| LittleEndian = true, | ||
| EntryPoint = "0x0", | ||
| CompressDebugSections = "zlib", | ||
| }; | ||
| if (!ExportsFile.IsNullOrEmpty ()) { | ||
| linker.VersionScript = ExportsFile; | ||
| } | ||
| if (!ExtraLinkerArgs.IsNullOrEmpty ()) { | ||
| linker.ExtraArgs = new List<string> (); | ||
| foreach (var arg in ExtraLinkerArgs.Split (new [] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { | ||
| linker.ExtraArgs.Add (arg.Trim ()); | ||
| } | ||
| } | ||
| // Write linker script if content is provided | ||
| if (!LinkerScriptContent.IsNullOrEmpty () && !LinkerScript.IsNullOrEmpty ()) { | ||
| string? dir = Path.GetDirectoryName (LinkerScript); | ||
| if (!dir.IsNullOrEmpty ()) { | ||
| Directory.CreateDirectory (dir); | ||
| } | ||
| File.WriteAllText (LinkerScript, LinkerScriptContent); | ||
| } | ||
| if (!LinkerScript.IsNullOrEmpty ()) { | ||
| linker.LinkerScript = LinkerScript; | ||
| } | ||
| if (LibrarySearchPaths != null) { | ||
| linker.AdditionalSearchPaths = new List<string> (); | ||
| foreach (var path in LibrarySearchPaths) { | ||
| linker.AdditionalSearchPaths.Add (path.ItemSpec); | ||
| } | ||
| } | ||
| // Build the link items in order: | ||
| // 1. ILC object file | ||
| // 2. Native libraries (.a archives from ILC runtime pack) | ||
| // 3. System libraries (-ldl, -lz, -llog, -lm, -lc) | ||
| // 4. Additional object files (jni_init, environment, etc.) | ||
| // 5. Compiler-rt and unwinder libraries | ||
| var linkItems = new List<ITaskItem> (); | ||
| linkItems.Add (CopyItemWithAbi (NativeObject, abi)); | ||
| foreach (var lib in NativeLibraries) { | ||
| linkItems.Add (CopyItemWithAbi (lib, abi)); | ||
| } | ||
| if (SystemLibraries != null) { | ||
| foreach (var lib in SystemLibraries) { | ||
| linkItems.Add (NativeLinker.MakeLibraryItem (lib.ItemSpec, abi)); | ||
| } | ||
| } | ||
| if (AdditionalObjectFiles != null) { | ||
| foreach (var obj in AdditionalObjectFiles) { | ||
| linkItems.Add (CopyItemWithAbi (obj, abi)); | ||
| } | ||
| } | ||
sbomer marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (CompilerRuntimeLibraries != null) { | ||
| foreach (var lib in CompilerRuntimeLibraries) { | ||
| linkItems.Add (CopyItemWithAbi (lib, abi)); | ||
| } | ||
| } | ||
| // CRT start/end files | ||
| List<ITaskItem>? startFiles = null; | ||
| if (CrtStartFiles != null && CrtStartFiles.Length > 0) { | ||
| startFiles = new List<ITaskItem> (); | ||
| foreach (var crt in CrtStartFiles) { | ||
| startFiles.Add (CopyItemWithAbi (crt, abi)); | ||
| } | ||
| } | ||
| List<ITaskItem>? endFiles = null; | ||
| if (CrtEndFiles != null && CrtEndFiles.Length > 0) { | ||
| endFiles = new List<ITaskItem> (); | ||
| foreach (var crt in CrtEndFiles) { | ||
| endFiles.Add (CopyItemWithAbi (crt, abi)); | ||
| } | ||
| } | ||
| var output = CopyItemWithAbi (OutputSharedLibrary, abi); | ||
| return linker.Link (output, linkItems, startFiles, endFiles); | ||
| } | ||
| /// <summary> | ||
| /// Copy a task item preserving all metadata, then set or override the Abi metadata. | ||
| /// </summary> | ||
| static ITaskItem CopyItemWithAbi (ITaskItem source, string abi) | ||
| { | ||
| var item = new TaskItem (source); | ||
| item.SetMetadata (KnownMetadata.Abi, abi); | ||
| return item; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.