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