-
Notifications
You must be signed in to change notification settings - Fork 569
Expand file tree
/
Copy pathLinkNativeAotSharedLibrary.cs
More file actions
216 lines (178 loc) · 5.93 KB
/
LinkNativeAotSharedLibrary.cs
File metadata and controls
216 lines (178 loc) · 5.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#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]
/// <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; }
[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;
}
// Write linker script if content is provided
if (!LinkerScriptContent.IsNullOrEmpty () && !LinkerScript.IsNullOrEmpty ()) {
Directory.CreateDirectory (Path.GetDirectoryName (LinkerScript)!);
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));
}
}
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;
}
}