1base: link.File,
2ofmt: union(enum) {
3 elf: Elf,
4 coff: Coff,
5 wasm: Wasm,
6},
7
8const Coff = struct {
9 image_base: u64,
10 entry: link.File.OpenOptions.Entry,
11 pdb_out_path: ?[]const u8,
12 repro: bool,
13 tsaware: bool,
14 nxcompat: bool,
15 dynamicbase: bool,
16 /// TODO this and minor_subsystem_version should be combined into one property and left as
17 /// default or populated together. They should not be separate fields.
18 major_subsystem_version: u16,
19 minor_subsystem_version: u16,
20 lib_directories: []const Cache.Directory,
21 module_definition_file: ?[]const u8,
22 subsystem: ?std.zig.Subsystem,
23 /// These flags are populated by `codegen.llvm.updateExports` to allow us to guess the subsystem.
24 lld_export_flags: struct {
25 c_main: bool,
26 winmain: bool,
27 wwinmain: bool,
28 winmain_crt_startup: bool,
29 wwinmain_crt_startup: bool,
30 dllmain_crt_startup: bool,
31 },
32 fn init(comp: *Compilation, options: link.File.OpenOptions) !Coff {
33 const target = &comp.root_mod.resolved_target.result;
34 const output_mode = comp.config.output_mode;
35 return .{
36 .image_base = options.image_base orelse switch (output_mode) {
37 .Exe => switch (target.cpu.arch) {
38 .aarch64, .x86_64 => 0x140000000,
39 .thumb, .x86 => 0x400000,
40 else => return error.UnsupportedCoffArchitecture,
41 },
42 .Lib => switch (target.cpu.arch) {
43 .aarch64, .x86_64 => 0x180000000,
44 .thumb, .x86 => 0x10000000,
45 else => return error.UnsupportedCoffArchitecture,
46 },
47 .Obj => 0,
48 },
49 .entry = options.entry,
50 .pdb_out_path = options.pdb_out_path,
51 .repro = options.repro,
52 .tsaware = options.tsaware,
53 .nxcompat = options.nxcompat,
54 .dynamicbase = options.dynamicbase,
55 .major_subsystem_version = options.major_subsystem_version orelse 6,
56 .minor_subsystem_version = options.minor_subsystem_version orelse 0,
57 .lib_directories = options.lib_directories,
58 .module_definition_file = options.module_definition_file,
59 // Subsystem depends on the set of public symbol names from linked objects.
60 // See LinkerDriver::inferSubsystem from the LLD project for the flow chart.
61 .subsystem = options.subsystem,
62 // These flags are initially all `false`; the LLVM backend populates them when it learns about exports.
63 .lld_export_flags = .{
64 .c_main = false,
65 .winmain = false,
66 .wwinmain = false,
67 .winmain_crt_startup = false,
68 .wwinmain_crt_startup = false,
69 .dllmain_crt_startup = false,
70 },
71 };
72 }
73};
74pub const Elf = struct {
75 entry_name: ?[]const u8,
76 hash_style: HashStyle,
77 image_base: u64,
78 linker_script: ?Cache.Path,
79 version_script: ?Cache.Path,
80 sort_section: ?SortSection,
81 print_icf_sections: bool,
82 print_map: bool,
83 nmagic: bool,
84 fatal_warnings: bool,
85 emit_relocs: bool,
86 z_nodelete: bool,
87 z_notext: bool,
88 z_defs: bool,
89 z_origin: bool,
90 z_nocopyreloc: bool,
91 z_now: bool,
92 z_relro: bool,
93 z_common_page_size: ?u64,
94 z_max_page_size: ?u64,
95 rpath_list: []const []const u8,
96 symbol_wrap_set: []const []const u8,
97 soname: ?[]const u8,
98 allow_undefined_version: bool,
99 enable_new_dtags: ?bool,
100 compress_debug_sections: std.zig.CompressDebugSections,
101 bind_global_refs_locally: bool,
102 pub const HashStyle = enum { sysv, gnu, both };
103 pub const SortSection = enum { name, alignment };
104
105 fn init(comp: *Compilation, options: link.File.OpenOptions) !Elf {
106 const PtrWidth = enum { p32, p64 };
107 const target = &comp.root_mod.resolved_target.result;
108 const output_mode = comp.config.output_mode;
109 const is_dyn_lib = output_mode == .Lib and comp.config.link_mode == .dynamic;
110 const ptr_width: PtrWidth = switch (target.ptrBitWidth()) {
111 0...32 => .p32,
112 33...64 => .p64,
113 else => return error.UnsupportedElfArchitecture,
114 };
115 const default_entry_name: []const u8 = switch (target.cpu.arch) {
116 .mips, .mipsel, .mips64, .mips64el => "__start",
117 else => "_start",
118 };
119 return .{
120 .entry_name = switch (options.entry) {
121 .disabled => null,
122 .default => if (output_mode != .Exe) null else default_entry_name,
123 .enabled => default_entry_name,
124 .named => |name| name,
125 },
126 .hash_style = options.hash_style,
127 .image_base = b: {
128 if (is_dyn_lib) break :b 0;
129 if (output_mode == .Exe and (comp.config.pie or target.os.tag == .haiku)) break :b 0;
130 break :b options.image_base orelse switch (ptr_width) {
131 .p32 => 0x10000,
132 .p64 => 0x1000000,
133 };
134 },
135 .linker_script = options.linker_script,
136 .version_script = options.version_script,
137 .sort_section = options.sort_section,
138 .print_icf_sections = options.print_icf_sections,
139 .print_map = options.print_map,
140 .nmagic = options.nmagic,
141 .fatal_warnings = options.fatal_warnings,
142 .emit_relocs = options.emit_relocs,
143 .z_nodelete = options.z_nodelete,
144 .z_notext = options.z_notext,
145 .z_defs = options.z_defs,
146 .z_origin = options.z_origin,
147 .z_nocopyreloc = options.z_nocopyreloc,
148 .z_now = options.z_now,
149 .z_relro = options.z_relro,
150 .z_common_page_size = options.z_common_page_size,
151 .z_max_page_size = options.z_max_page_size,
152 .rpath_list = options.rpath_list,
153 .symbol_wrap_set = options.symbol_wrap_set.keys(),
154 .soname = options.soname,
155 .allow_undefined_version = options.allow_undefined_version,
156 .enable_new_dtags = options.enable_new_dtags,
157 .compress_debug_sections = options.compress_debug_sections,
158 .bind_global_refs_locally = options.bind_global_refs_locally,
159 };
160 }
161};
162const Wasm = struct {
163 /// Symbol name of the entry function to export
164 entry_name: ?[]const u8,
165 /// When true, will import the function table from the host environment.
166 import_table: bool,
167 /// When true, will export the function table to the host environment.
168 export_table: bool,
169 /// When true, remove maximum size from function table, allowing table to grow.
170 growable_table: bool,
171 /// When defined, sets the initial memory size of the memory.
172 initial_memory: ?u64,
173 /// When defined, sets the maximum memory size of the memory.
174 max_memory: ?u64,
175 /// When defined, sets the start of the data section.
176 global_base: ?u64,
177 /// Set of *global* symbol names to export to the host environment.
178 export_symbol_names: []const []const u8,
179 /// When true, will allow undefined symbols
180 import_symbols: bool,
181 fn init(comp: *Compilation, options: link.File.OpenOptions) !Wasm {
182 const default_entry_name: []const u8 = switch (comp.config.wasi_exec_model) {
183 .reactor => "_initialize",
184 .command => "_start",
185 };
186 return .{
187 .entry_name = switch (options.entry) {
188 .disabled => null,
189 .default => if (comp.config.output_mode != .Exe) null else default_entry_name,
190 .enabled => default_entry_name,
191 .named => |name| name,
192 },
193 .import_table = options.import_table,
194 .export_table = options.export_table,
195 .growable_table = options.growable_table,
196 .initial_memory = options.initial_memory,
197 .max_memory = options.max_memory,
198 .global_base = options.global_base,
199 .export_symbol_names = options.export_symbol_names,
200 .import_symbols = options.import_symbols,
201 };
202 }
203};
204
205pub fn createEmpty(
206 arena: Allocator,
207 comp: *Compilation,
208 emit: Cache.Path,
209 options: link.File.OpenOptions,
210) !*Lld {
211 const target = &comp.root_mod.resolved_target.result;
212 const output_mode = comp.config.output_mode;
213 const optimize_mode = comp.root_mod.optimize_mode;
214
215 const gc_sections: bool = options.gc_sections orelse switch (target.ofmt) {
216 .coff => optimize_mode != .debug,
217 .elf => optimize_mode != .debug and output_mode != .Obj,
218 .wasm => output_mode != .Obj,
219 else => unreachable,
220 };
221 const stack_size: u64 = options.stack_size orelse default: {
222 if (target.ofmt == .wasm and target.os.tag == .freestanding)
223 break :default 1 * 1024 * 1024; // 1 MiB
224 break :default 16 * 1024 * 1024; // 16 MiB
225 };
226
227 const lld = try arena.create(Lld);
228 lld.* = .{
229 .base = .{
230 .tag = .lld,
231 .comp = comp,
232 .emit = emit,
233 .gc_sections = gc_sections,
234 .print_gc_sections = options.print_gc_sections,
235 .stack_size = stack_size,
236 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
237 .file = null,
238 .build_id = options.build_id,
239 },
240 .ofmt = switch (target.ofmt) {
241 .coff => .{ .coff = try .init(comp, options) },
242 .elf => .{ .elf = try .init(comp, options) },
243 .wasm => .{ .wasm = try .init(comp, options) },
244 else => unreachable,
245 },
246 };
247 return lld;
248}
249pub fn deinit(lld: *Lld) void {
250 _ = lld;
251}
252pub fn flush(
253 lld: *Lld,
254 arena: Allocator,
255 tid: Zcu.PerThread.Id,
256 prog_node: std.Progress.Node,
257) link.Error!void {
258 dev.check(.lld_linker);
259 _ = tid;
260
261 const tracy = trace(@src());
262 defer tracy.end();
263
264 const sub_prog_node = prog_node.start("LLD Link", 0);
265 defer sub_prog_node.end();
266
267 const comp = lld.base.comp;
268 const result = if (comp.config.output_mode == .Lib and comp.config.link_mode == .static) r: {
269 if (!@import("build_options").have_llvm or !comp.config.use_lib_llvm) {
270 return lld.base.comp.link_diags.fail("using lld without libllvm not implemented", .{});
271 }
272 break :r linkAsArchive(lld, arena);
273 } else switch (lld.ofmt) {
274 .coff => coffLink(lld, arena),
275 .elf => elfLink(lld, arena),
276 .wasm => wasmLink(lld, arena),
277 };
278 result catch |err| switch (err) {
279 error.OutOfMemory, error.AlreadyReported, error.Canceled => |e| return e,
280 else => |e| return lld.base.comp.link_diags.fail("failed to link with LLD: {t}", .{e}),
281 };
282}
283
284fn linkAsArchive(lld: *Lld, arena: Allocator) link.Error!void {
285 const base = &lld.base;
286 const comp = base.comp;
287 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
288 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
289 const full_out_path_z = try arena.dupeSentinel(u8, full_out_path, 0);
290 const opt_zcu = comp.zcu;
291
292 const zcu_obj_path: ?Cache.Path = if (opt_zcu) |zcu| p: {
293 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
294 } else null;
295
296 log.debug("zcu_obj_path={?f}", .{zcu_obj_path});
297
298 const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj)
299 comp.compiler_rt_obj.?.full_object_path
300 else
301 null;
302
303 const ubsan_rt_path: ?Cache.Path = if (comp.ubsan_rt_strat == .obj)
304 comp.ubsan_rt_obj.?.full_object_path
305 else
306 null;
307
308 // This function follows the same pattern as link.Elf.linkWithLLD so if you want some
309 // insight as to what's going on here you can read that function body which is more
310 // well-commented.
311
312 var object_files: std.ArrayList([*:0]const u8) = .empty;
313
314 try object_files.ensureUnusedCapacity(arena, comp.link_inputs.len);
315 for (comp.link_inputs) |input| switch (input) {
316 .dso, .dso_exact, .archive => {}, // static archives should not contain shared libraries or other static archives
317 .res, .object => {
318 const path = try input.path().?.toStringZ(arena);
319 object_files.appendAssumeCapacity(path);
320 },
321 };
322
323 try object_files.ensureUnusedCapacity(arena, comp.c_objects.items.len +
324 comp.win32_resources.items.len + 2);
325
326 for (comp.c_objects.items) |c_object| {
327 object_files.appendAssumeCapacity(try c_object.status.success.object_path.toStringZ(arena));
328 }
329 for (comp.win32_resources.items) |win32_resource| {
330 object_files.appendAssumeCapacity(try arena.dupeSentinel(u8, win32_resource.status.success.res_path, 0));
331 }
332 if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
333 if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
334 if (ubsan_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
335
336 if (comp.verbose_link) {
337 std.debug.print("ar rcs {s}", .{full_out_path_z});
338 for (object_files.items) |arg| {
339 std.debug.print(" {s}", .{arg});
340 }
341 std.debug.print("\n", .{});
342 }
343
344 const llvm_bindings = @import("../codegen/llvm/bindings.zig");
345 const llvm = @import("../codegen/llvm.zig");
346 const target = &comp.root_mod.resolved_target.result;
347 llvm.initializeLLVMTarget(comp.io, target.cpu.arch);
348 var err_file_index: usize = undefined;
349 var err_msg: [*:0]u8 = undefined;
350 if (llvm_bindings.WriteArchive(
351 full_out_path_z,
352 object_files.items.ptr,
353 object_files.items.len,
354 switch (target.os.tag) {
355 .windows => .COFF,
356 else => if (target.os.tag.isDarwin()) .DARWIN else .GNU,
357 },
358 &err_file_index,
359 &err_msg,
360 )) {
361 defer std.c.free(err_msg);
362 if (err_file_index < object_files.items.len) {
363 return comp.link_diags.fail("LLD failed to open input file '{s}': {s}", .{
364 object_files.items[err_file_index],
365 err_msg,
366 });
367 } else {
368 return comp.link_diags.fail("LLD failed to write archive: {s}", .{err_msg});
369 }
370 }
371}
372
373fn addCommonArgs(argv: *std.array_list.Managed([]const u8), coff: bool) !void {
374 if (builtin.os.tag == .netbsd) {
375 // NetBSD 10.1's `malloc` appears to have some nasty bugs that occur
376 // when doing parallel linking in LLD, manifesting as input and/or
377 // output section memory randomly being unmapped. So just don't do
378 // parallel linking for now.
379 try argv.append(if (coff) "-threads:1" else "--threads=1");
380 }
381}
382
383fn coffLink(lld: *Lld, arena: Allocator) !void {
384 const comp = lld.base.comp;
385 const gpa = comp.gpa;
386 const io = comp.io;
387 const base = &lld.base;
388 const coff = &lld.ofmt.coff;
389
390 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
391 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
392
393 const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: {
394 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
395 } else null;
396
397 const is_lib = comp.config.output_mode == .Lib;
398 const is_dyn_lib = comp.config.link_mode == .dynamic and is_lib;
399 const is_exe_or_dyn_lib = is_dyn_lib or comp.config.output_mode == .Exe;
400 const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib;
401 const target = &comp.root_mod.resolved_target.result;
402 const optimize_mode = comp.root_mod.optimize_mode;
403 const entry_name: ?[]const u8 = switch (coff.entry) {
404 // This logic isn't quite right for default or enabled. No point in fixing it
405 // when the goal is to eliminate dependency on LLD anyway.
406 // https://github.com/ziglang/zig/issues/17751
407 .disabled, .default, .enabled => null,
408 .named => |name| name,
409 };
410
411 if (comp.config.output_mode == .Obj) {
412 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
413 // here. TODO: think carefully about how we can avoid this redundant operation when doing
414 // build-obj. See also the corresponding TODO in linkAsArchive.
415 const the_object_path = blk: {
416 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
417
418 if (comp.c_objects.items.len != 0)
419 break :blk comp.c_objects.items[0].status.success.object_path;
420
421 if (zcu_obj_path) |p|
422 break :blk p;
423
424 // TODO I think this is unreachable. Audit this situation when solving the above TODO
425 // regarding eliding redundant object -> object transformations.
426 return error.NoObjectsToLink;
427 };
428 try Io.Dir.copyFile(
429 the_object_path.root_dir.handle,
430 the_object_path.sub_path,
431 directory.handle,
432 base.emit.sub_path,
433 io,
434 .{},
435 );
436 } else {
437 // Create an LLD command line and invoke it.
438 var argv = std.array_list.Managed([]const u8).init(gpa);
439 defer argv.deinit();
440 // We will invoke ourselves as a child process to gain access to LLD.
441 // This is necessary because LLD does not behave properly as a library -
442 // it calls exit() and does not reset all global data between invocations.
443 const linker_command = "lld-link";
444 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
445 try addCommonArgs(&argv, true);
446
447 if (target.isMinGW()) {
448 try argv.append("-lldmingw");
449 }
450
451 try argv.append("-ERRORLIMIT:0");
452 try argv.append("-NOLOGO");
453 if (comp.config.debug_format != .strip) {
454 try argv.append("-DEBUG");
455
456 const out_ext = std.fs.path.extension(full_out_path);
457 const out_pdb = coff.pdb_out_path orelse try arena.print("{s}.pdb", .{
458 full_out_path[0 .. full_out_path.len - out_ext.len],
459 });
460 const out_pdb_basename = std.fs.path.basename(out_pdb);
461
462 try argv.append(try arena.print("-PDB:{s}", .{out_pdb}));
463 try argv.append(try arena.print("-PDBALTPATH:{s}", .{out_pdb_basename}));
464 }
465 if (comp.version) |version| {
466 try argv.append(try arena.print("-VERSION:{d}.{d}", .{ version.major, version.minor }));
467 }
468
469 if (target_util.llvmMachineAbi(target)) |mabi| {
470 try argv.append(try arena.print("-MLLVM:-target-abi={s}", .{mabi}));
471 }
472
473 try argv.append(try arena.print("-MLLVM:-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}));
474
475 if (comp.config.lto != .none) {
476 switch (optimize_mode) {
477 .debug => {},
478 .small => try argv.append("-OPT:lldlto=2"),
479 .fast, .safe => try argv.append("-OPT:lldlto=3"),
480 }
481 }
482 if (comp.config.output_mode == .Exe) {
483 try argv.append(try arena.print("-STACK:{d}", .{base.stack_size}));
484 }
485 try argv.append(try arena.print("-BASE:{d}", .{coff.image_base}));
486
487 switch (base.build_id) {
488 .none => try argv.append("-BUILD-ID:NO"),
489 .fast => try argv.append("-BUILD-ID"),
490 .uuid, .sha1, .md5, .hexstring => {},
491 }
492
493 if (target.cpu.arch == .x86) {
494 try argv.append("-MACHINE:X86");
495 } else if (target.cpu.arch == .x86_64) {
496 try argv.append("-MACHINE:X64");
497 } else if (target.cpu.arch == .thumb) {
498 try argv.append("-MACHINE:ARM");
499 } else if (target.cpu.arch == .aarch64) {
500 try argv.append("-MACHINE:ARM64");
501 }
502
503 for (comp.force_undefined_symbols.keys()) |symbol| {
504 try argv.append(try arena.print("-INCLUDE:{s}", .{symbol}));
505 }
506
507 if (is_dyn_lib) {
508 try argv.append("-DLL");
509 }
510
511 if (entry_name) |name| {
512 try argv.append(try arena.print("-ENTRY:{s}", .{name}));
513 } else if (coff.entry == .disabled) {
514 try argv.append("-NOENTRY");
515 }
516
517 if (coff.repro) {
518 try argv.append("-BREPRO");
519 }
520
521 if (coff.tsaware) {
522 try argv.append("-tsaware");
523 }
524 if (coff.nxcompat) {
525 try argv.append("-nxcompat");
526 }
527 if (!coff.dynamicbase) {
528 try argv.append("-dynamicbase:NO");
529 }
530 if (base.allow_shlib_undefined) {
531 try argv.append("-FORCE:UNRESOLVED");
532 }
533
534 try argv.append(try arena.print("-OUT:{s}", .{full_out_path}));
535
536 if (comp.emit_implib) |raw_emit_path| {
537 const path = try comp.resolveEmitPathFlush(arena, .artifact, raw_emit_path);
538 try argv.append(try arena.print("-IMPLIB:{f}", .{path}));
539 }
540
541 if (comp.config.link_libc) {
542 if (comp.libc_installation) |libc_installation| {
543 try argv.append(try arena.print("-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
544
545 if (target.abi == .msvc or target.abi == .itanium) {
546 try argv.append(try arena.print("-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
547 try argv.append(try arena.print("-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
548 }
549 }
550 }
551
552 for (coff.lib_directories) |lib_directory| {
553 try argv.append(try arena.print("-LIBPATH:{s}", .{lib_directory.path orelse "."}));
554 }
555
556 try argv.ensureUnusedCapacity(comp.link_inputs.len);
557 for (comp.link_inputs) |link_input| switch (link_input) {
558 .dso_exact => unreachable, // not applicable to PE/COFF
559 inline .dso, .res => |x| {
560 argv.appendAssumeCapacity(try x.path.toString(arena));
561 },
562 .object, .archive => |obj| {
563 if (obj.must_link) {
564 argv.appendAssumeCapacity(try arena.print("-WHOLEARCHIVE:{f}", .{@as(Cache.Path, obj.path)}));
565 } else {
566 argv.appendAssumeCapacity(try obj.path.toString(arena));
567 }
568 },
569 };
570
571 for (comp.c_objects.items) |c_object| {
572 try argv.append(try c_object.status.success.object_path.toString(arena));
573 }
574
575 for (comp.win32_resources.items) |win32_resource| {
576 try argv.append(win32_resource.status.success.res_path);
577 }
578
579 if (zcu_obj_path) |p| {
580 try argv.append(try p.toString(arena));
581 }
582
583 if (coff.module_definition_file) |def| {
584 try argv.append(try arena.print("-DEF:{s}", .{def}));
585 }
586
587 const resolved_subsystem: ?std.zig.Subsystem = blk: {
588 if (coff.subsystem) |explicit| break :blk explicit;
589 switch (target.os.tag) {
590 .windows => {
591 if (comp.zcu != null) {
592 if (coff.lld_export_flags.dllmain_crt_startup or is_dyn_lib)
593 break :blk null;
594 if (coff.lld_export_flags.c_main or comp.config.is_test or
595 coff.lld_export_flags.winmain_crt_startup or
596 coff.lld_export_flags.wwinmain_crt_startup)
597 {
598 break :blk .console;
599 }
600 if (coff.lld_export_flags.winmain or coff.lld_export_flags.wwinmain)
601 break :blk .windows;
602 }
603 },
604 .uefi => break :blk .efi_application,
605 else => {},
606 }
607 break :blk null;
608 };
609
610 const Mode = enum { uefi, win32 };
611 const mode: Mode = mode: {
612 if (resolved_subsystem) |subsystem| {
613 try argv.append(try arena.print("-SUBSYSTEM:{s},{d}.{d}", .{
614 @tagName(subsystem),
615 coff.major_subsystem_version,
616 coff.minor_subsystem_version,
617 }));
618 break :mode switch (subsystem) {
619 .console,
620 .windows,
621 .posix,
622 .native,
623 => .win32,
624 .efi_application,
625 .efi_boot_service_driver,
626 .efi_rom,
627 .efi_runtime_driver,
628 => .uefi,
629 };
630 } else if (target.os.tag == .uefi) {
631 break :mode .uefi;
632 } else {
633 break :mode .win32;
634 }
635 };
636
637 switch (mode) {
638 .uefi => try argv.appendSlice(&[_][]const u8{
639 "-BASE:0",
640 "-ENTRY:EfiMain",
641 "-OPT:REF",
642 "-SAFESEH:NO",
643 "-MERGE:.rdata=.data",
644 "-NODEFAULTLIB",
645 "-SECTION:.xdata,D",
646 }),
647 .win32 => {
648 if (link_in_crt) {
649 if (target.abi.isGnu()) {
650 if (target.cpu.arch == .x86) {
651 try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
652 } else {
653 try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
654 }
655
656 try argv.append(try comp.crtFileAsString(arena, if (is_dyn_lib) "dllcrt2.obj" else "crt2.obj"));
657 try argv.append(try comp.crtFileAsString(arena, "libmingw32.lib"));
658 } else {
659 try argv.append(switch (comp.config.link_mode) {
660 .static => "libcmt.lib",
661 .dynamic => "msvcrt.lib",
662 });
663
664 const lib_str = switch (comp.config.link_mode) {
665 .static => "lib",
666 .dynamic => "",
667 };
668 try argv.append(try arena.print("{s}vcruntime.lib", .{lib_str}));
669 try argv.append(try arena.print("{s}ucrt.lib", .{lib_str}));
670
671 //Visual C++ 2015 Conformance Changes
672 //https://msdn.microsoft.com/en-us/library/bb531344.aspx
673 try argv.append("legacy_stdio_definitions.lib");
674
675 // msvcrt depends on kernel32 and ntdll
676 try argv.append("kernel32.lib");
677 try argv.append("ntdll.lib");
678 }
679 } else {
680 try argv.append("-NODEFAULTLIB");
681 if (!is_lib and entry_name == null) {
682 if (comp.zcu != null) {
683 if (coff.lld_export_flags.winmain_crt_startup) {
684 try argv.append("-ENTRY:WinMainCRTStartup");
685 } else {
686 try argv.append("-ENTRY:wWinMainCRTStartup");
687 }
688 } else {
689 try argv.append("-ENTRY:wWinMainCRTStartup");
690 }
691 }
692 }
693 },
694 }
695
696 if (comp.config.link_libc and link_in_crt) {
697 if (comp.zigc_static_lib) |zigc| {
698 try argv.append(try zigc.full_object_path.toString(arena));
699 }
700 }
701
702 // libc++ dep
703 if (comp.config.link_libcpp) {
704 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
705 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
706 }
707
708 // libunwind dep
709 if (comp.config.link_libunwind) {
710 try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
711 }
712
713 if (comp.config.any_fuzz) {
714 try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
715 }
716
717 const ubsan_rt_path: ?Cache.Path = blk: {
718 if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path;
719 if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path;
720 break :blk null;
721 };
722 if (ubsan_rt_path) |path| {
723 try argv.append(try path.toString(arena));
724 }
725
726 if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {
727 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
728 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
729 if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena));
730 if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
731 }
732
733 try argv.ensureUnusedCapacity(comp.windows_libs.count());
734 for (comp.windows_libs.keys()) |key| {
735 const lib_basename = try arena.print("{s}.lib", .{key});
736 if (comp.crt_files.get(lib_basename)) |crt_file| {
737 argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena));
738 continue;
739 }
740 if (try findLib(arena, io, lib_basename, coff.lib_directories)) |full_path| {
741 argv.appendAssumeCapacity(full_path);
742 continue;
743 }
744 if (target.abi.isGnu()) {
745 const fallback_name = try arena.print("lib{s}.dll.a", .{key});
746 if (try findLib(arena, io, fallback_name, coff.lib_directories)) |full_path| {
747 argv.appendAssumeCapacity(full_path);
748 continue;
749 }
750 }
751 if (target.abi == .msvc or target.abi == .itanium) {
752 argv.appendAssumeCapacity(lib_basename);
753 continue;
754 }
755
756 log.err("DLL import library for -l{s} not found", .{key});
757 return error.DllImportLibraryNotFound;
758 }
759
760 try spawnLld(comp, arena, argv.items);
761 }
762}
763fn findLib(arena: Allocator, io: Io, name: []const u8, lib_directories: []const Cache.Directory) !?[]const u8 {
764 for (lib_directories) |lib_directory| {
765 lib_directory.handle.access(io, name, .{}) catch |err| switch (err) {
766 error.FileNotFound => continue,
767 else => |e| return e,
768 };
769 return try lib_directory.join(arena, &.{name});
770 }
771 return null;
772}
773
774fn elfLink(lld: *Lld, arena: Allocator) !void {
775 const comp = lld.base.comp;
776 const gpa = comp.gpa;
777 const io = comp.io;
778 const diags = &comp.link_diags;
779 const base = &lld.base;
780 const elf = &lld.ofmt.elf;
781
782 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
783 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
784
785 const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: {
786 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
787 } else null;
788
789 const output_mode = comp.config.output_mode;
790 const is_obj = output_mode == .Obj;
791 const is_lib = output_mode == .Lib;
792 const link_mode = comp.config.link_mode;
793 const is_dyn_lib = link_mode == .dynamic and is_lib;
794 const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe;
795 const target = &comp.root_mod.resolved_target.result;
796 const compiler_rt_path: ?Cache.Path = blk: {
797 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
798 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
799 break :blk null;
800 };
801 const ubsan_rt_path: ?Cache.Path = blk: {
802 if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path;
803 if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path;
804 break :blk null;
805 };
806
807 // Due to a deficiency in LLD, we need to special-case BPF to a simple file
808 // copy when generating relocatables. Normally, we would expect `lld -r` to work.
809 // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails
810 // before even generating the relocatable.
811 //
812 // For m68k, we go through this path because LLD doesn't support it yet, but LLVM can
813 // produce usable object files.
814 if (output_mode == .Obj and
815 (comp.config.lto != .none or
816 target.cpu.arch.isBpf() or
817 target.cpu.arch == .lanai or
818 target.cpu.arch == .m68k or
819 target.cpu.arch.isSPARC() or
820 target.cpu.arch == .ve or
821 target.cpu.arch == .xcore or
822 target.cpu.arch == .xtensa))
823 {
824 // In this case we must do a simple file copy
825 // here. TODO: think carefully about how we can avoid this redundant operation when doing
826 // build-obj. See also the corresponding TODO in linkAsArchive.
827 const the_object_path = blk: {
828 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
829
830 if (comp.c_objects.items.len != 0)
831 break :blk comp.c_objects.items[0].status.success.object_path;
832
833 if (zcu_obj_path) |p|
834 break :blk p;
835
836 // TODO I think this is unreachable. Audit this situation when solving the above TODO
837 // regarding eliding redundant object -> object transformations.
838 return error.NoObjectsToLink;
839 };
840 try Io.Dir.copyFile(
841 the_object_path.root_dir.handle,
842 the_object_path.sub_path,
843 directory.handle,
844 base.emit.sub_path,
845 io,
846 .{},
847 );
848 } else {
849 // Create an LLD command line and invoke it.
850 var argv = std.array_list.Managed([]const u8).init(gpa);
851 defer argv.deinit();
852 // We will invoke ourselves as a child process to gain access to LLD.
853 // This is necessary because LLD does not behave properly as a library -
854 // it calls exit() and does not reset all global data between invocations.
855 const linker_command = "ld.lld";
856 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
857 try addCommonArgs(&argv, false);
858
859 if (is_obj) {
860 try argv.append("-r");
861 }
862
863 try argv.append("--error-limit=0");
864
865 if (comp.sysroot) |sysroot| {
866 try argv.append(try arena.print("--sysroot={s}", .{sysroot}));
867 }
868
869 if (target_util.llvmMachineAbi(target)) |mabi| {
870 try argv.appendSlice(&.{
871 "-mllvm",
872 try arena.print("-target-abi={s}", .{mabi}),
873 });
874 }
875
876 try argv.appendSlice(&.{
877 "-mllvm",
878 try arena.print("-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}),
879 });
880
881 switch (target.cpu.arch) {
882 .armeb, .thumbeb => if (is_exe_or_dyn_lib and target.cpu.has(.arm, .has_v6)) try argv.append("--be8"),
883 else => {},
884 }
885
886 if (comp.config.lto != .none) {
887 switch (comp.root_mod.optimize_mode) {
888 .debug => {},
889 .small => try argv.append("--lto-O2"),
890 .fast, .safe => try argv.append("--lto-O3"),
891 }
892 }
893 switch (comp.root_mod.optimize_mode) {
894 .debug => {},
895 .small => try argv.append("-O2"),
896 .fast, .safe => try argv.append("-O3"),
897 }
898
899 if (elf.entry_name) |name| {
900 try argv.appendSlice(&.{ "--entry", name });
901 }
902
903 for (comp.force_undefined_symbols.keys()) |sym| {
904 try argv.append("-u");
905 try argv.append(sym);
906 }
907
908 switch (elf.hash_style) {
909 .gnu => try argv.append("--hash-style=gnu"),
910 .sysv => try argv.append("--hash-style=sysv"),
911 .both => {}, // this is the default
912 }
913
914 if (output_mode == .Exe) {
915 try argv.appendSlice(&.{
916 "-z",
917 try arena.print("stack-size={d}", .{base.stack_size}),
918 });
919 }
920
921 switch (base.build_id) {
922 .none => try argv.append("--build-id=none"),
923 .fast, .uuid, .sha1, .md5 => try argv.append(try arena.print("--build-id={s}", .{
924 @tagName(base.build_id),
925 })),
926 .hexstring => |hs| try argv.append(try arena.print("--build-id=0x{x}", .{hs.toSlice()})),
927 }
928
929 try argv.append(try arena.print("--image-base={d}", .{elf.image_base}));
930
931 if (elf.linker_script) |linker_script| {
932 try argv.append("-T");
933 try argv.append(try linker_script.toString(arena));
934 }
935
936 if (elf.sort_section) |how| {
937 const arg = try arena.print("--sort-section={s}", .{@tagName(how)});
938 try argv.append(arg);
939 }
940
941 if (base.gc_sections) {
942 try argv.append("--gc-sections");
943 }
944
945 if (base.print_gc_sections) {
946 try argv.append("--print-gc-sections");
947 }
948
949 if (elf.print_icf_sections) {
950 try argv.append("--print-icf-sections");
951 }
952
953 if (elf.print_map) {
954 try argv.append("--print-map");
955 }
956
957 if (elf.nmagic) {
958 try argv.append("--nmagic");
959 }
960
961 if (elf.fatal_warnings) {
962 try argv.append("--fatal-warnings");
963 }
964
965 if (comp.link_eh_frame_hdr) {
966 try argv.append("--eh-frame-hdr");
967 }
968
969 if (elf.emit_relocs) {
970 try argv.append("--emit-relocs");
971 }
972
973 if (comp.config.rdynamic) {
974 try argv.append("--export-dynamic");
975 }
976
977 if (comp.config.debug_format == .strip) {
978 try argv.append("-s");
979 }
980
981 if (elf.z_nodelete) {
982 try argv.append("-z");
983 try argv.append("nodelete");
984 }
985 if (elf.z_notext) {
986 try argv.append("-z");
987 try argv.append("notext");
988 }
989 if (elf.z_defs) {
990 try argv.append("-z");
991 try argv.append("defs");
992 }
993 if (elf.z_origin) {
994 try argv.append("-z");
995 try argv.append("origin");
996 }
997 if (elf.z_nocopyreloc) {
998 try argv.append("-z");
999 try argv.append("nocopyreloc");
1000 }
1001 if (elf.z_now) {
1002 // LLD defaults to -zlazy
1003 try argv.append("-znow");
1004 }
1005 if (!elf.z_relro) {
1006 // LLD defaults to -zrelro
1007 try argv.append("-znorelro");
1008 }
1009 if (elf.z_common_page_size) |size| {
1010 try argv.append("-z");
1011 try argv.append(try arena.print("common-page-size={d}", .{size}));
1012 }
1013 if (elf.z_max_page_size) |size| {
1014 try argv.append("-z");
1015 try argv.append(try arena.print("max-page-size={d}", .{size}));
1016 }
1017
1018 if (getLDMOption(target)) |ldm| {
1019 try argv.append("-m");
1020 try argv.append(ldm);
1021 }
1022
1023 if (link_mode == .static) {
1024 if (target.cpu.arch.isArm()) {
1025 try argv.append("-Bstatic");
1026 } else {
1027 try argv.append("-static");
1028 }
1029 } else if (switch (target.os.tag) {
1030 else => is_dyn_lib,
1031 .haiku => is_exe_or_dyn_lib,
1032 }) {
1033 try argv.append("-shared");
1034 }
1035
1036 if (comp.config.pie and output_mode == .Exe) {
1037 try argv.append("-pie");
1038 }
1039
1040 if (is_exe_or_dyn_lib and target.os.tag == .netbsd) {
1041 // Add options to produce shared objects with only 2 PT.LOAD segments.
1042 // NetBSD expects 2 PT.LOAD segments in a shared object, otherwise
1043 // ld.elf_so fails loading dynamic libraries with "not found" error.
1044 // See https://github.com/ziglang/zig/issues/9109 .
1045 try argv.append("--no-rosegment");
1046 try argv.append("-znorelro");
1047 }
1048
1049 try argv.append("-o");
1050 try argv.append(full_out_path);
1051
1052 // csu prelude
1053 const csu = try comp.getCrtPaths(arena);
1054 if (csu.crt0) |p| try argv.append(try p.toString(arena));
1055 if (csu.crti) |p| try argv.append(try p.toString(arena));
1056 if (csu.crtbegin) |p| try argv.append(try p.toString(arena));
1057
1058 for (elf.rpath_list) |rpath| {
1059 try argv.appendSlice(&.{ "-rpath", rpath });
1060 }
1061
1062 for (elf.symbol_wrap_set) |symbol_name| {
1063 try argv.appendSlice(&.{ "-wrap", symbol_name });
1064 }
1065
1066 if (comp.config.link_libc) {
1067 if (comp.libc_installation) |libc_installation| {
1068 try argv.append("-L");
1069 try argv.append(libc_installation.crt_dir.?);
1070 }
1071 }
1072
1073 if (output_mode == .Exe and link_mode == .dynamic) {
1074 if (target.dynamic_linker.get()) |dynamic_linker| {
1075 try argv.append("--dynamic-linker");
1076 try argv.append(dynamic_linker);
1077 } else {
1078 try argv.append("--no-dynamic-linker");
1079 }
1080 }
1081
1082 if (is_dyn_lib) {
1083 if (elf.soname) |soname| {
1084 try argv.append("-soname");
1085 try argv.append(soname);
1086 }
1087 if (elf.version_script) |version_script| {
1088 try argv.append("-version-script");
1089 try argv.append(try version_script.toString(arena));
1090 }
1091 if (elf.allow_undefined_version) {
1092 try argv.append("--undefined-version");
1093 } else {
1094 try argv.append("--no-undefined-version");
1095 }
1096 if (elf.enable_new_dtags) |enable_new_dtags| {
1097 if (enable_new_dtags) {
1098 try argv.append("--enable-new-dtags");
1099 } else {
1100 try argv.append("--disable-new-dtags");
1101 }
1102 }
1103 }
1104
1105 // Positional arguments to the linker such as object files.
1106 var whole_archive = false;
1107
1108 for (base.comp.link_inputs) |link_input| switch (link_input) {
1109 .res => unreachable, // Windows-only
1110 .dso => continue,
1111 .object, .archive => |obj| {
1112 if (obj.must_link and !whole_archive) {
1113 try argv.append("--whole-archive");
1114 whole_archive = true;
1115 } else if (!obj.must_link and whole_archive) {
1116 try argv.append("--no-whole-archive");
1117 whole_archive = false;
1118 }
1119 try argv.append(try obj.path.toString(arena));
1120 },
1121 .dso_exact => |dso_exact| {
1122 assert(dso_exact.name[0] == ':');
1123 try argv.appendSlice(&.{ "-l", dso_exact.name });
1124 },
1125 };
1126
1127 if (whole_archive) {
1128 try argv.append("--no-whole-archive");
1129 whole_archive = false;
1130 }
1131
1132 for (comp.c_objects.items) |c_object| {
1133 try argv.append(try c_object.status.success.object_path.toString(arena));
1134 }
1135
1136 if (zcu_obj_path) |p| {
1137 try argv.append(try p.toString(arena));
1138 }
1139
1140 if (comp.tsan_lib) |lib| {
1141 assert(comp.config.any_sanitize_thread);
1142 try argv.appendSlice(&.{
1143 "--whole-archive",
1144 try lib.full_object_path.toString(arena),
1145 "--no-whole-archive",
1146 });
1147 }
1148
1149 if (comp.fuzzer_lib) |lib| {
1150 assert(comp.config.any_fuzz);
1151 try argv.append(try lib.full_object_path.toString(arena));
1152 }
1153
1154 if (ubsan_rt_path) |p| {
1155 try argv.append(try p.toString(arena));
1156 }
1157
1158 // Shared libraries.
1159 if (is_exe_or_dyn_lib) {
1160 // Worst-case, we need an --as-needed argument for every lib, as well
1161 // as one before and one after.
1162 try argv.ensureUnusedCapacity(2 * base.comp.link_inputs.len + 2);
1163 argv.appendAssumeCapacity("--as-needed");
1164 var as_needed = true;
1165
1166 for (base.comp.link_inputs) |link_input| switch (link_input) {
1167 .res => unreachable, // Windows-only
1168 .object, .archive, .dso_exact => continue,
1169 .dso => |dso| {
1170 const lib_as_needed = !dso.needed;
1171 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
1172 0b00, 0b11 => {},
1173 0b01 => {
1174 argv.appendAssumeCapacity("--no-as-needed");
1175 as_needed = false;
1176 },
1177 0b10 => {
1178 argv.appendAssumeCapacity("--as-needed");
1179 as_needed = true;
1180 },
1181 }
1182
1183 // By this time, we depend on these libs being dynamically linked
1184 // libraries and not static libraries (the check for that needs to be earlier),
1185 // but they could be full paths to .so files, in which case we
1186 // want to avoid prepending "-l".
1187 argv.appendAssumeCapacity(try dso.path.toString(arena));
1188 },
1189 };
1190
1191 if (!as_needed) {
1192 argv.appendAssumeCapacity("--as-needed");
1193 as_needed = true;
1194 }
1195
1196 // libc++ dep
1197 if (comp.config.link_libcpp) {
1198 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
1199 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
1200 }
1201
1202 // libunwind dep
1203 if (comp.config.link_libunwind) {
1204 try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
1205 }
1206
1207 // libc dep
1208 diags.flags.missing_libc = false;
1209 if (comp.config.link_libc) {
1210 if (comp.libc_installation != null) {
1211 const needs_grouping = link_mode == .static;
1212 if (needs_grouping) try argv.append("--start-group");
1213 try argv.appendSlice(target_util.libcFullLinkFlags(target));
1214 if (needs_grouping) try argv.append("--end-group");
1215 } else if (target.isGnuLibC()) {
1216 for (glibc.libs) |lib| {
1217 if (lib.removed_in) |rem_in| {
1218 if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue;
1219 }
1220
1221 const lib_path = try arena.print("{f}{c}lib{s}.so.{d}", .{
1222 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1223 });
1224 try argv.append(lib_path);
1225 }
1226 try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a"));
1227 } else if (target.isMuslLibC()) {
1228 try argv.append(try comp.crtFileAsString(arena, switch (link_mode) {
1229 .static => "libc.a",
1230 .dynamic => "libc.so",
1231 }));
1232 } else if (target.isFreeBSDLibC()) {
1233 for (freebsd.libs) |lib| {
1234 if (lib.added_in) |add_in| {
1235 if (target.os.version_range.semver.min.order(add_in) == .lt) continue;
1236 }
1237
1238 const lib_path = try arena.print("{f}{c}lib{s}.so.{d}", .{
1239 comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.getSoVersion(&target.os),
1240 });
1241 try argv.append(lib_path);
1242 }
1243 } else if (target.isNetBSDLibC()) {
1244 for (netbsd.libs) |lib| {
1245 const lib_path = try arena.print("{f}{c}lib{s}.so.{d}", .{
1246 comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1247 });
1248 try argv.append(lib_path);
1249 }
1250 } else if (target.isOpenBSDLibC()) {
1251 for (openbsd.libs) |lib| {
1252 const lib_path = try arena.print("{f}{c}lib{s}.so", .{
1253 comp.openbsd_so_files.?.dir_path, fs.path.sep, lib.name,
1254 });
1255 try argv.append(lib_path);
1256 }
1257 } else {
1258 diags.flags.missing_libc = true;
1259 }
1260
1261 if (comp.zigc_static_lib) |zigc| {
1262 try argv.append(try zigc.full_object_path.toString(arena));
1263 }
1264 }
1265 }
1266
1267 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
1268 // to be after the shared libraries, so they are picked up from the shared
1269 // libraries, not libcompiler_rt.
1270 if (compiler_rt_path) |p| {
1271 try argv.append(try p.toString(arena));
1272 }
1273
1274 // crt postlude
1275 if (csu.crtend) |p| try argv.append(try p.toString(arena));
1276 if (csu.crtn) |p| try argv.append(try p.toString(arena));
1277
1278 if (base.allow_shlib_undefined) {
1279 try argv.append("--allow-shlib-undefined");
1280 }
1281
1282 switch (elf.compress_debug_sections) {
1283 .none => {},
1284 .zlib => try argv.append("--compress-debug-sections=zlib"),
1285 .zstd => try argv.append("--compress-debug-sections=zstd"),
1286 }
1287
1288 if (elf.bind_global_refs_locally) {
1289 try argv.append("-Bsymbolic");
1290 }
1291
1292 try spawnLld(comp, arena, argv.items);
1293 }
1294}
1295fn getLDMOption(target: *const std.Target) ?[]const u8 {
1296 // This should only return emulations understood by LLD's parseEmulation().
1297 return switch (target.cpu.arch) {
1298 .aarch64 => switch (target.os.tag) {
1299 .linux => "aarch64linux",
1300 else => "aarch64elf",
1301 },
1302 .aarch64_be => switch (target.os.tag) {
1303 .linux => "aarch64linuxb",
1304 else => "aarch64elfb",
1305 },
1306 .amdgcn => "elf64_amdgpu",
1307 .arm, .thumb => switch (target.os.tag) {
1308 .linux => "armelf_linux_eabi",
1309 else => "armelf",
1310 },
1311 .armeb, .thumbeb => switch (target.os.tag) {
1312 .linux => "armelfb_linux_eabi",
1313 else => "armelfb",
1314 },
1315 .hexagon => "hexagonelf",
1316 .loongarch32 => "elf32loongarch",
1317 .loongarch64 => "elf64loongarch",
1318 .mips => switch (target.os.tag) {
1319 .freebsd => "elf32btsmip_fbsd",
1320 else => "elf32btsmip",
1321 },
1322 .mipsel => switch (target.os.tag) {
1323 .freebsd => "elf32ltsmip_fbsd",
1324 else => "elf32ltsmip",
1325 },
1326 .mips64 => switch (target.os.tag) {
1327 .freebsd => switch (target.abi) {
1328 .gnuabin32, .muslabin32, .abin32 => "elf32btsmipn32_fbsd",
1329 else => "elf64btsmip_fbsd",
1330 },
1331 else => switch (target.abi) {
1332 .gnuabin32, .muslabin32, .abin32 => "elf32btsmipn32",
1333 else => "elf64btsmip",
1334 },
1335 },
1336 .mips64el => switch (target.os.tag) {
1337 .freebsd => switch (target.abi) {
1338 .gnuabin32, .muslabin32, .abin32 => "elf32ltsmipn32_fbsd",
1339 else => "elf64ltsmip_fbsd",
1340 },
1341 else => switch (target.abi) {
1342 .gnuabin32, .muslabin32, .abin32 => "elf32ltsmipn32",
1343 else => "elf64ltsmip",
1344 },
1345 },
1346 .msp430 => "msp430elf",
1347 .powerpc => switch (target.os.tag) {
1348 .freebsd => "elf32ppc_fbsd",
1349 .linux => "elf32ppclinux",
1350 else => "elf32ppc",
1351 },
1352 .powerpcle => switch (target.os.tag) {
1353 .linux => "elf32lppclinux",
1354 else => "elf32lppc",
1355 },
1356 .powerpc64 => "elf64ppc",
1357 .powerpc64le => "elf64lppc",
1358 .riscv32 => "elf32lriscv",
1359 .riscv32be => "elf32briscv",
1360 .riscv64 => "elf64lriscv",
1361 .riscv64be => "elf64briscv",
1362 .s390x => "elf64_s390",
1363 .sparc64 => "elf64_sparc",
1364 .x86 => switch (target.os.tag) {
1365 .freebsd => "elf_i386_fbsd",
1366 else => "elf_i386",
1367 },
1368 .x86_64 => switch (target.abi) {
1369 .gnux32, .muslx32, .x32 => "elf32_x86_64",
1370 else => "elf_x86_64",
1371 },
1372 else => null,
1373 };
1374}
1375fn wasmLink(lld: *Lld, arena: Allocator) !void {
1376 const comp = lld.base.comp;
1377 const diags = &comp.link_diags;
1378 const shared_memory = comp.config.shared_memory;
1379 const export_memory = comp.config.export_memory;
1380 const import_memory = comp.config.import_memory;
1381 const target = &comp.root_mod.resolved_target.result;
1382 const base = &lld.base;
1383 const wasm = &lld.ofmt.wasm;
1384
1385 const gpa = comp.gpa;
1386 const io = comp.io;
1387
1388 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
1389 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
1390
1391 const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: {
1392 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
1393 } else null;
1394
1395 const is_obj = comp.config.output_mode == .Obj;
1396 const compiler_rt_path: ?Cache.Path = blk: {
1397 if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path;
1398 if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path;
1399 break :blk null;
1400 };
1401 const ubsan_rt_path: ?Cache.Path = blk: {
1402 if (comp.ubsan_rt_lib) |lib| break :blk lib.full_object_path;
1403 if (comp.ubsan_rt_obj) |obj| break :blk obj.full_object_path;
1404 break :blk null;
1405 };
1406
1407 if (is_obj) {
1408 // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy
1409 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1410 // build-obj. See also the corresponding TODO in linkAsArchive.
1411 const the_object_path = blk: {
1412 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
1413
1414 if (comp.c_objects.items.len != 0)
1415 break :blk comp.c_objects.items[0].status.success.object_path;
1416
1417 if (zcu_obj_path) |p|
1418 break :blk p;
1419
1420 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1421 // regarding eliding redundant object -> object transformations.
1422 return error.NoObjectsToLink;
1423 };
1424 try Io.Dir.copyFile(
1425 the_object_path.root_dir.handle,
1426 the_object_path.sub_path,
1427 directory.handle,
1428 base.emit.sub_path,
1429 io,
1430 .{},
1431 );
1432 } else {
1433 // Create an LLD command line and invoke it.
1434 var argv = std.array_list.Managed([]const u8).init(gpa);
1435 defer argv.deinit();
1436 // We will invoke ourselves as a child process to gain access to LLD.
1437 // This is necessary because LLD does not behave properly as a library -
1438 // it calls exit() and does not reset all global data between invocations.
1439 const linker_command = "wasm-ld";
1440 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
1441 try addCommonArgs(&argv, false);
1442
1443 try argv.append("--error-limit=0");
1444
1445 if (comp.config.lto != .none) {
1446 switch (comp.root_mod.optimize_mode) {
1447 .debug => {},
1448 .small => try argv.append("-O2"),
1449 .fast, .safe => try argv.append("-O3"),
1450 }
1451 }
1452
1453 if (import_memory) {
1454 try argv.append("--import-memory");
1455 }
1456
1457 if (export_memory) {
1458 try argv.append("--export-memory");
1459 }
1460
1461 if (wasm.import_table) {
1462 assert(!wasm.export_table);
1463 try argv.append("--import-table");
1464 }
1465
1466 if (wasm.export_table) {
1467 assert(!wasm.import_table);
1468 try argv.append("--export-table");
1469 }
1470
1471 if (wasm.growable_table) {
1472 try argv.append("--growable-table");
1473 }
1474
1475 // For wasm-ld we only need to specify '--no-gc-sections' when the user explicitly
1476 // specified it as garbage collection is enabled by default.
1477 if (!base.gc_sections) {
1478 try argv.append("--no-gc-sections");
1479 }
1480
1481 if (comp.config.debug_format == .strip) {
1482 try argv.append("-s");
1483 }
1484
1485 if (wasm.initial_memory) |initial_memory| {
1486 const arg = try arena.print("--initial-memory={d}", .{initial_memory});
1487 try argv.append(arg);
1488 }
1489
1490 if (wasm.max_memory) |max_memory| {
1491 const arg = try arena.print("--max-memory={d}", .{max_memory});
1492 try argv.append(arg);
1493 }
1494
1495 if (shared_memory) {
1496 try argv.append("--shared-memory");
1497 }
1498
1499 if (wasm.global_base) |global_base| {
1500 const arg = try arena.print("--global-base={d}", .{global_base});
1501 try argv.append(arg);
1502 } else {
1503 // We prepend it by default, so when a stack overflow happens the runtime will trap correctly,
1504 // rather than silently overwrite all global declarations. See https://github.com/ziglang/zig/issues/4496
1505 //
1506 // The user can overwrite this behavior by setting the global-base
1507 try argv.append("--stack-first");
1508 }
1509
1510 // Users are allowed to specify which symbols they want to export to the wasm host.
1511 for (wasm.export_symbol_names) |symbol_name| {
1512 const arg = try arena.print("--export={s}", .{symbol_name});
1513 try argv.append(arg);
1514 }
1515
1516 if (comp.config.rdynamic) {
1517 try argv.append("--export-dynamic");
1518 }
1519
1520 if (wasm.entry_name) |entry_name| {
1521 try argv.appendSlice(&.{ "--entry", entry_name });
1522 } else {
1523 try argv.append("--no-entry");
1524 }
1525
1526 try argv.appendSlice(&.{
1527 "-z",
1528 try arena.print("stack-size={d}", .{base.stack_size}),
1529 });
1530
1531 switch (base.build_id) {
1532 .none => try argv.append("--build-id=none"),
1533 .fast, .uuid, .sha1 => try argv.append(try arena.print("--build-id={s}", .{
1534 @tagName(base.build_id),
1535 })),
1536 .hexstring => |hs| try argv.append(try arena.print("--build-id=0x{x}", .{hs.toSlice()})),
1537 .md5 => {},
1538 }
1539
1540 if (wasm.import_symbols) {
1541 try argv.append("--allow-undefined");
1542 }
1543
1544 if (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic) {
1545 try argv.append("--shared");
1546 }
1547 if (comp.config.pie) {
1548 try argv.append("--pie");
1549 }
1550
1551 try argv.appendSlice(&.{ "-o", full_out_path });
1552
1553 if (target.cpu.arch == .wasm64) {
1554 try argv.append("-mwasm64");
1555 }
1556
1557 const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or
1558 (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic);
1559
1560 if (comp.config.link_libc and is_exe_or_dyn_lib) {
1561 if (target.os.tag == .wasi) {
1562 try argv.append(try comp.crtFileAsString(
1563 arena,
1564 wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),
1565 ));
1566 try argv.append(try comp.crtFileAsString(arena, "libc.a"));
1567 }
1568
1569 if (comp.zigc_static_lib) |zigc| {
1570 try argv.append(try zigc.full_object_path.toString(arena));
1571 }
1572
1573 if (comp.config.link_libcpp) {
1574 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
1575 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
1576 }
1577 }
1578
1579 // Positional arguments to the linker such as object files.
1580 var whole_archive = false;
1581 for (comp.link_inputs) |link_input| switch (link_input) {
1582 .object, .archive => |obj| {
1583 if (obj.must_link and !whole_archive) {
1584 try argv.append("--whole-archive");
1585 whole_archive = true;
1586 } else if (!obj.must_link and whole_archive) {
1587 try argv.append("--no-whole-archive");
1588 whole_archive = false;
1589 }
1590 try argv.append(try obj.path.toString(arena));
1591 },
1592 .dso => |dso| {
1593 try argv.append(try dso.path.toString(arena));
1594 },
1595 .dso_exact => unreachable,
1596 .res => unreachable,
1597 };
1598 if (whole_archive) {
1599 try argv.append("--no-whole-archive");
1600 whole_archive = false;
1601 }
1602
1603 for (comp.c_objects.items) |c_object| {
1604 try argv.append(try c_object.status.success.object_path.toString(arena));
1605 }
1606 if (zcu_obj_path) |p| {
1607 try argv.append(try p.toString(arena));
1608 }
1609
1610 if (compiler_rt_path) |p| {
1611 try argv.append(try p.toString(arena));
1612 }
1613
1614 if (ubsan_rt_path) |p| {
1615 try argv.append(try p.toStringZ(arena));
1616 }
1617
1618 try spawnLld(comp, arena, argv.items);
1619
1620 // Give +x to the .wasm file if it is an executable and the OS is WASI.
1621 // Some systems may be configured to execute such binaries directly. Even if that
1622 // is not the case, it means we will get "exec format error" when trying to run
1623 // it, and then can react to that in the same way as trying to run an ELF file
1624 // from a foreign CPU architecture.
1625 if (Io.File.Permissions.has_executable_bit and target.os.tag == .wasi and
1626 comp.config.output_mode == .Exe)
1627 {
1628 // chmod does not interact with umask, so we use a conservative -rwxr--r-- here.
1629 Io.Dir.cwd().setFilePermissions(io, full_out_path, .fromMode(0o744), .{}) catch |err|
1630 return diags.fail("{s}: failed to enable executable permissions: {t}", .{ full_out_path, err });
1631 }
1632 }
1633}
1634
1635fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !void {
1636 const io = comp.io;
1637 const gpa = comp.gpa;
1638
1639 if (comp.verbose_link) {
1640 // Skip over our own name so that the LLD linker name is the first argv item.
1641 try Compilation.dumpArgv(io, argv[1..]);
1642 }
1643
1644 // If possible, we run LLD as a child process because it does not always
1645 // behave properly as a library, unfortunately.
1646 // https://github.com/ziglang/zig/issues/3825
1647 if (!std.process.can_spawn) {
1648 const exit_code = try lldMain(arena, argv, false);
1649 if (exit_code == 0) return;
1650 if (comp.clang_passthrough_mode) std.process.exit(exit_code);
1651 return error.AlreadyReported;
1652 }
1653
1654 var stderr: []u8 = &.{};
1655 defer gpa.free(stderr);
1656
1657 // TODO rework this awkward logic to call child.kill() in the failure case
1658 const term = (if (comp.clang_passthrough_mode) term: {
1659 var child = std.process.spawn(io, .{
1660 .argv = argv,
1661 .stdin = .inherit,
1662 .stdout = .inherit,
1663 .stderr = .inherit,
1664 }) catch |err| break :term err;
1665
1666 break :term child.wait(io);
1667 } else term: {
1668 var child = std.process.spawn(io, .{
1669 .argv = argv,
1670 .stdin = .ignore,
1671 .stdout = .ignore,
1672 .stderr = .pipe,
1673 }) catch |err| break :term err;
1674
1675 var stderr_reader = child.stderr.?.readerStreaming(io, &.{});
1676 stderr = stderr_reader.interface.allocRemaining(gpa, .unlimited) catch |err| switch (err) {
1677 error.StreamTooLong => unreachable, // unlimited
1678 error.OutOfMemory => |e| return e,
1679 error.ReadFailed => return stderr_reader.err.?,
1680 };
1681 break :term child.wait(io);
1682 }) catch |first_err| term: {
1683 const err = switch (first_err) {
1684 error.NameTooLong => err: {
1685 const s = fs.path.sep_str;
1686 const rand_int = r: {
1687 var x: u64 = undefined;
1688 io.random(@ptrCast(&x));
1689 break :r x;
1690 };
1691 const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp";
1692
1693 const rsp_file = try comp.dirs.local_cache.handle.createFile(io, rsp_path, .{});
1694 defer comp.dirs.local_cache.handle.deleteFile(io, rsp_path) catch |err|
1695 log.warn("failed to delete response file {s}: {t}", .{ rsp_path, err });
1696 {
1697 defer rsp_file.close(io);
1698 var rsp_file_buffer: [1024]u8 = undefined;
1699 var rsp_file_writer = rsp_file.writer(io, &rsp_file_buffer);
1700 const rsp_writer = &rsp_file_writer.interface;
1701 for (argv[2..]) |arg| {
1702 try rsp_writer.writeByte('"');
1703 for (arg) |c| {
1704 switch (c) {
1705 '\"', '\\' => try rsp_writer.writeByte('\\'),
1706 else => {},
1707 }
1708 try rsp_writer.writeByte(c);
1709 }
1710 try rsp_writer.writeByte('"');
1711 try rsp_writer.writeByte('\n');
1712 }
1713 try rsp_writer.flush();
1714 }
1715
1716 var rsp_child = std.process.spawn(io, .{
1717 .argv = &.{
1718 argv[0],
1719 argv[1],
1720 try arena.print("@{s}", .{
1721 try comp.dirs.local_cache.join(arena, &.{rsp_path}),
1722 }),
1723 },
1724 .stdin = if (comp.clang_passthrough_mode) .inherit else .ignore,
1725 .stdout = if (comp.clang_passthrough_mode) .inherit else .ignore,
1726 .stderr = if (comp.clang_passthrough_mode) .inherit else .pipe,
1727 }) catch |err| break :err err;
1728 if (comp.clang_passthrough_mode) {
1729 break :term rsp_child.wait(io) catch |err| break :err err;
1730 } else {
1731 var stderr_reader = rsp_child.stderr.?.readerStreaming(io, &.{});
1732 stderr = stderr_reader.interface.allocRemaining(gpa, .unlimited) catch |err| switch (err) {
1733 error.StreamTooLong => unreachable, // unlimited
1734 error.OutOfMemory => |e| return e,
1735 error.ReadFailed => return stderr_reader.err.?,
1736 };
1737 break :term rsp_child.wait(io) catch |err| break :err err;
1738 }
1739 },
1740 else => first_err,
1741 };
1742 log.err("unable to spawn LLD {s}: {t}", .{ argv[0], err });
1743 return error.UnableToSpawnSelf;
1744 };
1745
1746 const diags = &comp.link_diags;
1747 switch (term) {
1748 .exited => |code| if (code != 0) {
1749 if (comp.clang_passthrough_mode) std.process.exit(code);
1750 diags.lockAndParseLldStderr(argv[1], stderr);
1751 return error.AlreadyReported;
1752 },
1753 .signal => |sig| {
1754 if (comp.clang_passthrough_mode) std.process.abort();
1755 return diags.fail("{s} terminated with signal {t} and stderr:\n{s}", .{ argv[0], sig, stderr });
1756 },
1757 .stopped => |sig| {
1758 if (comp.clang_passthrough_mode) std.process.abort();
1759 return diags.fail("{s} stopped with signal {t} and stderr:\n{s}", .{ argv[0], sig, stderr });
1760 },
1761 .unknown => |code| {
1762 if (comp.clang_passthrough_mode) std.process.abort();
1763 return diags.fail("{s} terminated for unknown reason with code {d} and stderr:\n{s}", .{ argv[0], code, stderr });
1764 },
1765 }
1766
1767 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1768}
1769
1770const builtin = @import("builtin");
1771const std = @import("std");
1772const Io = std.Io;
1773const Allocator = std.mem.Allocator;
1774const Cache = std.Build.Cache;
1775const assert = std.debug.assert;
1776const fs = std.fs;
1777const log = std.log.scoped(.link);
1778const mem = std.mem;
1779
1780const Compilation = @import("../Compilation.zig");
1781const Zcu = @import("../Zcu.zig");
1782const dev = @import("../dev.zig");
1783const freebsd = @import("../libs/freebsd.zig");
1784const glibc = @import("../libs/glibc.zig");
1785const netbsd = @import("../libs/netbsd.zig");
1786const openbsd = @import("../libs/openbsd.zig");
1787const wasi_libc = @import("../libs/wasi_libc.zig");
1788const link = @import("../link.zig");
1789const lldMain = @import("../main.zig").lldMain;
1790const target_util = @import("../target.zig");
1791const trace = @import("../tracy.zig").trace;
1792const Lld = @This();