[NativeAOT] Use NativeLinker and invoke lld directly for linking#11256
Open
[NativeAOT] Use NativeLinker and invoke lld directly for linking#11256
Conversation
Set NativeLib=static so ILC produces a .a archive via ar instead of invoking the linker directly. Add _AndroidLinkNativeAotSharedLibrary target that runs after LinkNative and links the ILC .o output into a .so using the NDK clang wrapper. This gives Android full control over the native linker invocation, following the same approach used by macios. Reproduce the flags that LinkNative and SetupOSSpecificProps would have provided for NativeLib=Shared: - -shared, -Wl,-e,0x0, -Wl,-z,max-page-size=16384 (from LinkerArg) - --version-script, --export-dynamic, --discard-all, --gc-sections (from CustomLinkerArg inside LinkNative) - -fuse-ld=lld (from LinkerArg via LinkerFlavor) - sections.ld linker script to retain the __modules section Set IlcExportUnmanagedEntrypoints=true so ILC exports [UnmanagedCallersOnly] methods as native symbols, required for JNI entry points. Clear LinkerFlavor inside _AndroidBeforeIlcCompile to work around an ILC targets bug where _LinkerVersion detection is skipped for NativeLib=Static but the numeric comparison in LinkNative still evaluates. Context: dotnet/runtime#126978 The resulting linker command line is identical to the original. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add Inputs/Outputs to _AndroidLinkNativeAotSharedLibrary so incremental builds can skip relinking when inputs haven't changed. Add FileWrites for the .so and sections.ld so Clean can account for generated files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On Windows, ILC's LinkNative writes linker args to a response file instead of passing them inline — cmd.exe has quoting issues with spaces in paths. Match that behavior in _AndroidLinkNativeAotSharedLibrary. Fixes NativeAOT build failures for projects with spaces or special characters in their names (e.g. CheckProjectWithSpaceInNameWorks tests). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ILC's LinkNative target defaults CppLibCreator to the host 'ar', which does not understand ELF objects when cross-compiling for Android. On macOS this caused Xcode's ranlib to emit spurious 'empty table of contents' warnings for every ABI. Set CppLibCreator to llvm-ar (from the NDK toolchain, already on PATH) so the archiver can correctly process ELF .o files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit 2ff10fb.
ILC's LinkNative target strips symbols via llvm-objcopy after linking, but those steps are skipped when NativeLib=Static. Add the same three objcopy invocations to _AndroidLinkNativeAotSharedLibrary: 1. Extract debug info to .dbg file 2. Strip debug symbols from the .so 3. Add gnu-debuglink back to the .so Without stripping, the .so was ~8MB larger than the original, causing BuildReleaseArm64 apkdiff regression tests to fail. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add opt-in properties to NativeLinker.cs for NativeAOT-specific flags: ExportDynamic, UseEhFrameHdr, DiscardAll, AsNeeded, HashStyleBoth, LittleEndian, VersionScript, LinkerScript, EntryPoint, CompressDebugSections, AdditionalSearchPaths. Move --export-dynamic from standardArgs to an opt-in ExportDynamic property (default true for back-compat with existing consumers). Add LinkNativeAotSharedLibrary task that uses NativeLinker to link the ILC .o output into a .so using ld.lld directly, replacing the clang wrapper invocation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the clang Exec invocation with the LinkNativeAotSharedLibrary task that calls ld.lld directly from the NDK. The task uses NativeLinker.cs which handles response files, ABI-specific flags, and debug symbol stripping. Compute NDK paths for CRT objects (crtbegin_so.o/crtend_so.o), compiler-rt builtins, and libunwind. Pass system libraries (dl, z, log, m, c) and library search paths explicitly. Fix NativeLinker.cs quoting for soname with spaces, and guard against empty runtime pack library directory. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR shifts NativeAOT shared-library linking to use the existing NativeLinker infrastructure (response files, ABI flags, symbol stripping) instead of invoking the NDK clang wrapper, and wires this into the NativeAOT MSBuild targets.
Changes:
- Extend
NativeLinkerwith NativeAOT-focused linker options (export-dynamic, version scripts, linker scripts, debug section compression, extra-Lpaths, etc.). - Add a new MSBuild task (
LinkNativeAotSharedLibrary) that drivesNativeLinkerto produce a.sofrom ILC output + runtime/static archives. - Update
Microsoft.Android.Sdk.NativeAOT.targetsto setNativeLib=static, enable unmanaged entrypoint exports, and run the new link step afterLinkNative.
Show a summary per file
| File | Description |
|---|---|
src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs |
Adds configurable linker options needed for NativeAOT and fixes argument quoting/guards. |
src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotSharedLibrary.cs |
New task orchestrating NativeAOT .so linking via NativeLinker. |
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets |
MSBuild integration: produce .a from ILC then link final .so via the new task, plus NDK CRT/rt/unwind inputs. |
Copilot's findings
Comments suppressed due to low confidence (1)
src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeAotSharedLibrary.cs:140
Directory.CreateDirectory (Path.GetDirectoryName (LinkerScript)!)can throw ifLinkerScriptis just a filename (no directory component), becausePath.GetDirectoryName()would returnnull. Avoid the null-forgiving operator here and handle thenullcase explicitly (or useIntermediateOutputPathas the directory when none is provided).
if (!LinkerScriptContent.IsNullOrEmpty () && !LinkerScript.IsNullOrEmpty ()) {
Directory.CreateDirectory (Path.GetDirectoryName (LinkerScript)!);
File.WriteAllText (LinkerScript, LinkerScriptContent);
}
- Files reviewed: 3/3 changed files
- Comments generated: 4
# Conflicts: # src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets
don't strip and save debug symbols separately. Matches the pattern used by LinkApplicationSharedLibraries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use TaskItem copy constructor instead of creating from ItemSpec alone, so metadata like NativeLinkWholeArchive and NativeDontExportSymbols is preserved. NativeLinker checks these for --whole-archive and --exclude-libs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replace the clang Exec invocation with the LinkNativeAotSharedLibrary task
that calls ld.lld directly from the NDK. The task uses NativeLinker.cs which
handles response files, ABI-specific flags, and debug symbol stripping.
Compute NDK paths for CRT objects (crtbegin_so.o/crtend_so.o), compiler-rt
builtins, and libunwind. Pass system libraries (dl, z, log, m, c) and
library search paths explicitly.
Fix NativeLinker.cs quoting for soname with spaces, and guard against empty
runtime pack library directory.
Builds on #11148.
Contributes to #10697