authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-05-28 09:30:31+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-06-12 13:55:39+01:00
log2fb6f5c1adcd764372ad28ed4014fdaf558da778
treec38d1e129c3ee15c29198aacab1c2d76df857cf5
parent3743c3e39c6bb645db7403fd446953d43ac7c7dc
signaturelock-open Commit is signed but in an unrecognized format.

link: divorce LLD from the self-hosted linkers

Similar to the previous commit, this commit untangles LLD integration from the self-hosted linkers. Despite the big network of functions which were involved, it turns out what was going on here is quite simple. The LLD linking logic is actually very self-contained; it requires a few flags from the `link.File.OpenOptions`, but that's really about it. We don't need any of the mutable state on `Elf`/`Coff`/`Wasm`, for instance. There was some legacy code trying to handle support for using self-hosted codegen with LLD, but that's not a supported use case, so I've just stripped it out. For now, I've just pasted the logic for linking the 3 targets we currently support using LLD for into this new linker implementation, `link.Lld`; however, it's almost certainly possible to combine some of the logic and simplify this file a bit. But to be honest, it's not actually that bad right now. This commit ends up eliminating the distinction between `flush` and `flushZcu` (formerly `flushModule`) in linkers, where the latter previously meant something along the lines of "flush, but if you're going to be linking with LLD, just flush the ZCU object file, don't actually link"?. The distinction here doesn't seem like it was properly defined, and most linkers seem to treat them as essentially identical anyway. Regardless, all calls to `flushZcu` are gone now, so it's deleted -- one `flush` to rule them all! The end result of this commit and the preceding one is that LLVM and LLD fit into the pipeline much more sanely: * If we're using LLVM for the ZCU, that state is on `zcu.llvm_object` * If we're using LLD to link, then the `link.File` is a `link.Lld` * Calls to "ZCU link functions" (e.g. `updateNav`) lower to calls to the LLVM object if it's available, or otherwise to the `link.File` if it's available (neither is available under `-fno-emit-bin`) * After everything is done, linking is finalized by calling `flush` on the `link.File`; for `link.Lld` this invokes LLD, for other linkers it flushes self-hosted linker state There's one messy thing remaining, and that's how self-hosted function codegen in a ZCU works; right now, we process AIR with a call sequence something like this: * `link.doTask` * `Zcu.PerThread.linkerUpdateFunc` * `link.File.updateFunc` * `link.Elf.updateFunc` * `link.Elf.ZigObject.updateFunc` * `codegen.generateFunction` * `arch.x86_64.CodeGen.generate` So, we start in the linker, take a scenic detour through `Zcu`, go back to the linker, into its implementation, and then... right back out, into code which is generic over the linker implementation, and then dispatch on the *backend* instead! Of course, within `arch.x86_64.CodeGen`, there are some more places which switch on the `link` implementation being used. This is all pretty silly... so it shall be my next target.

18 files changed, 2262 insertions(+), 2284 deletions(-)

src/Compilation.zig+3-3
......@@ -1592,9 +1592,9 @@ pub const CreateOptions = struct {
15921592 linker_tsaware: bool = false,
15931593 linker_nxcompat: bool = false,
15941594 linker_dynamicbase: bool = true,
1595 linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null,
1595 linker_compress_debug_sections: ?link.File.Lld.Elf.CompressDebugSections = null,
15961596 linker_module_definition_file: ?[]const u8 = null,
1597 linker_sort_section: ?link.File.Elf.SortSection = null,
1597 linker_sort_section: ?link.File.Lld.Elf.SortSection = null,
15981598 major_subsystem_version: ?u16 = null,
15991599 minor_subsystem_version: ?u16 = null,
16001600 clang_passthrough_mode: bool = false,
......@@ -1616,7 +1616,7 @@ pub const CreateOptions = struct {
16161616 /// building such dependencies themselves, this flag must be set to avoid
16171617 /// infinite recursion.
16181618 skip_linker_dependencies: bool = false,
1619 hash_style: link.File.Elf.HashStyle = .both,
1619 hash_style: link.File.Lld.Elf.HashStyle = .both,
16201620 entry: Entry = .default,
16211621 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .empty,
16221622 stack_size: ?u64 = null,
src/codegen/llvm.zig+9-6
......@@ -1587,12 +1587,15 @@ pub const Object = struct {
15871587 const comp = zcu.comp;
15881588
15891589 // If we're on COFF and linking with LLD, the linker cares about our exports to determine the subsystem in use.
1590 if (comp.bin_file != null and
1591 comp.bin_file.?.tag == .coff and
1592 zcu.comp.config.use_lld and
1593 ip.isFunctionType(ip.getNav(nav_index).typeOf(ip)))
1594 {
1595 const flags = &comp.bin_file.?.cast(.coff).?.lld_export_flags;
1590 coff_export_flags: {
1591 const lf = comp.bin_file orelse break :coff_export_flags;
1592 const lld = lf.cast(.lld) orelse break :coff_export_flags;
1593 const coff = switch (lld.ofmt) {
1594 .elf, .wasm => break :coff_export_flags,
1595 .coff => |*coff| coff,
1596 };
1597 if (!ip.isFunctionType(ip.getNav(nav_index).typeOf(ip))) break :coff_export_flags;
1598 const flags = &coff.lld_export_flags;
15961599 for (export_indices) |export_index| {
15971600 const name = export_index.ptr(zcu).opts.name;
15981601 if (name.eqlSlice("main", ip)) flags.c_main = true;
src/link.zig+42-329
......@@ -19,7 +19,6 @@ const Zcu = @import("Zcu.zig");
1919const InternPool = @import("InternPool.zig");
2020const Type = @import("Type.zig");
2121const Value = @import("Value.zig");
22const lldMain = @import("main.zig").lldMain;
2322const Package = @import("Package.zig");
2423const dev = @import("dev.zig");
2524const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue;
......@@ -388,7 +387,6 @@ pub const File = struct {
388387 /// When linking with LLD, this linker code will output an object file only at
389388 /// this location, and then this path can be placed on the LLD linker line.
390389 zcu_object_sub_path: ?[]const u8 = null,
391 disable_lld_caching: bool,
392390 gc_sections: bool,
393391 print_gc_sections: bool,
394392 build_id: std.zig.BuildId,
......@@ -424,7 +422,7 @@ pub const File = struct {
424422 tsaware: bool,
425423 nxcompat: bool,
426424 dynamicbase: bool,
427 compress_debug_sections: Elf.CompressDebugSections,
425 compress_debug_sections: Lld.Elf.CompressDebugSections,
428426 bind_global_refs_locally: bool,
429427 import_symbols: bool,
430428 import_table: bool,
......@@ -436,8 +434,8 @@ pub const File = struct {
436434 global_base: ?u64,
437435 build_id: std.zig.BuildId,
438436 disable_lld_caching: bool,
439 hash_style: Elf.HashStyle,
440 sort_section: ?Elf.SortSection,
437 hash_style: Lld.Elf.HashStyle,
438 sort_section: ?Lld.Elf.SortSection,
441439 major_subsystem_version: ?u16,
442440 minor_subsystem_version: ?u16,
443441 gc_sections: ?bool,
......@@ -521,12 +519,20 @@ pub const File = struct {
521519 emit: Path,
522520 options: OpenOptions,
523521 ) !*File {
522 if (comp.config.use_lld) {
523 dev.check(.lld_linker);
524 assert(comp.zcu == null or comp.config.use_llvm);
525 // LLD does not support incremental linking.
526 const lld: *Lld = try .createEmpty(arena, comp, emit, options);
527 return &lld.base;
528 }
524529 switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) {
525530 inline else => |tag| {
526531 dev.check(tag.devFeature());
527532 const ptr = try tag.Type().open(arena, comp, emit, options);
528533 return &ptr.base;
529534 },
535 .lld => unreachable, // not known from ofmt
530536 }
531537 }
532538
......@@ -536,12 +542,19 @@ pub const File = struct {
536542 emit: Path,
537543 options: OpenOptions,
538544 ) !*File {
545 if (comp.config.use_lld) {
546 dev.check(.lld_linker);
547 assert(comp.zcu == null or comp.config.use_llvm);
548 const lld: *Lld = try .createEmpty(arena, comp, emit, options);
549 return &lld.base;
550 }
539551 switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) {
540552 inline else => |tag| {
541553 dev.check(tag.devFeature());
542554 const ptr = try tag.Type().createEmpty(arena, comp, emit, options);
543555 return &ptr.base;
544556 },
557 .lld => unreachable, // not known from ofmt
545558 }
546559 }
547560
......@@ -554,6 +567,7 @@ pub const File = struct {
554567 const comp = base.comp;
555568 const gpa = comp.gpa;
556569 switch (base.tag) {
570 .lld => assert(base.file == null),
557571 .coff, .elf, .macho, .plan9, .wasm, .goff, .xcoff => {
558572 if (base.file != null) return;
559573 dev.checkAny(&.{ .coff_linker, .elf_linker, .macho_linker, .plan9_linker, .wasm_linker, .goff_linker, .xcoff_linker });
......@@ -586,13 +600,12 @@ pub const File = struct {
586600 }
587601 }
588602 }
589 const use_lld = build_options.have_llvm and comp.config.use_lld;
590603 const output_mode = comp.config.output_mode;
591604 const link_mode = comp.config.link_mode;
592605 base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{
593606 .truncate = false,
594607 .read = true,
595 .mode = determineMode(use_lld, output_mode, link_mode),
608 .mode = determineMode(output_mode, link_mode),
596609 });
597610 },
598611 .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
......@@ -618,7 +631,6 @@ pub const File = struct {
618631 const comp = base.comp;
619632 const output_mode = comp.config.output_mode;
620633 const link_mode = comp.config.link_mode;
621 const use_lld = build_options.have_llvm and comp.config.use_lld;
622634
623635 switch (output_mode) {
624636 .Obj => return,
......@@ -629,13 +641,9 @@ pub const File = struct {
629641 .Exe => {},
630642 }
631643 switch (base.tag) {
644 .lld => assert(base.file == null),
632645 .elf => if (base.file) |f| {
633646 dev.check(.elf_linker);
634 if (base.zcu_object_sub_path != null and use_lld) {
635 // The file we have open is not the final file that we want to
636 // make executable, so we don't have to close it.
637 return;
638 }
639647 f.close();
640648 base.file = null;
641649
......@@ -650,11 +658,6 @@ pub const File = struct {
650658 },
651659 .coff, .macho, .plan9, .wasm, .goff, .xcoff => if (base.file) |f| {
652660 dev.checkAny(&.{ .coff_linker, .macho_linker, .plan9_linker, .wasm_linker, .goff_linker, .xcoff_linker });
653 if (base.zcu_object_sub_path != null) {
654 // The file we have open is not the final file that we want to
655 // make executable, so we don't have to close it.
656 return;
657 }
658661 f.close();
659662 base.file = null;
660663
......@@ -692,6 +695,7 @@ pub const File = struct {
692695 pub fn getGlobalSymbol(base: *File, name: []const u8, lib_name: ?[]const u8) UpdateNavError!u32 {
693696 log.debug("getGlobalSymbol '{s}' (expected in '{?s}')", .{ name, lib_name });
694697 switch (base.tag) {
698 .lld => unreachable,
695699 .plan9 => unreachable,
696700 .spirv => unreachable,
697701 .c => unreachable,
......@@ -709,6 +713,7 @@ pub const File = struct {
709713 const nav = pt.zcu.intern_pool.getNav(nav_index);
710714 assert(nav.status == .fully_resolved);
711715 switch (base.tag) {
716 .lld => unreachable,
712717 inline else => |tag| {
713718 dev.check(tag.devFeature());
714719 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateNav(pt, nav_index);
......@@ -726,6 +731,7 @@ pub const File = struct {
726731 fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void {
727732 assert(base.comp.zcu.?.llvm_object == null);
728733 switch (base.tag) {
734 .lld => unreachable,
729735 else => {},
730736 inline .elf => |tag| {
731737 dev.check(tag.devFeature());
......@@ -746,6 +752,7 @@ pub const File = struct {
746752 ) UpdateNavError!void {
747753 assert(base.comp.zcu.?.llvm_object == null);
748754 switch (base.tag) {
755 .lld => unreachable,
749756 inline else => |tag| {
750757 dev.check(tag.devFeature());
751758 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, air, liveness);
......@@ -772,6 +779,7 @@ pub const File = struct {
772779 }
773780
774781 switch (base.tag) {
782 .lld => unreachable,
775783 .spirv => {},
776784 .goff, .xcoff => {},
777785 inline else => |tag| {
......@@ -811,8 +819,7 @@ pub const File = struct {
811819 OutOfMemory,
812820 };
813821
814 /// Commit pending changes and write headers. Takes into account final output mode
815 /// and `use_lld`, not only `effectiveOutputMode`.
822 /// Commit pending changes and write headers. Takes into account final output mode.
816823 /// `arena` has the lifetime of the call to `Compilation.update`.
817824 pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {
818825 const comp = base.comp;
......@@ -834,15 +841,7 @@ pub const File = struct {
834841 };
835842 return;
836843 }
837
838844 assert(base.post_prelink);
839
840 const use_lld = build_options.have_llvm and comp.config.use_lld;
841 const output_mode = comp.config.output_mode;
842 const link_mode = comp.config.link_mode;
843 if (use_lld and output_mode == .Lib and link_mode == .static) {
844 return base.linkAsArchive(arena, tid, prog_node);
845 }
846845 switch (base.tag) {
847846 inline else => |tag| {
848847 dev.check(tag.devFeature());
......@@ -851,19 +850,6 @@ pub const File = struct {
851850 }
852851 }
853852
854 /// Commit pending changes and write headers. Works based on `effectiveOutputMode`
855 /// rather than final output mode.
856 /// Never called when LLVM is codegenning the ZCU.
857 fn flushZcu(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {
858 assert(base.comp.zcu.?.llvm_object == null);
859 switch (base.tag) {
860 inline else => |tag| {
861 dev.check(tag.devFeature());
862 return @as(*tag.Type(), @fieldParentPtr("base", base)).flushZcu(arena, tid, prog_node);
863 },
864 }
865 }
866
867853 pub const UpdateExportsError = error{
868854 OutOfMemory,
869855 AnalysisFail,
......@@ -882,6 +868,7 @@ pub const File = struct {
882868 ) UpdateExportsError!void {
883869 assert(base.comp.zcu.?.llvm_object == null);
884870 switch (base.tag) {
871 .lld => unreachable,
885872 inline else => |tag| {
886873 dev.check(tag.devFeature());
887874 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateExports(pt, exported, export_indices);
......@@ -911,6 +898,7 @@ pub const File = struct {
911898 pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) !u64 {
912899 assert(base.comp.zcu.?.llvm_object == null);
913900 switch (base.tag) {
901 .lld => unreachable,
914902 .c => unreachable,
915903 .spirv => unreachable,
916904 .wasm => unreachable,
......@@ -932,6 +920,7 @@ pub const File = struct {
932920 ) !codegen.GenResult {
933921 assert(base.comp.zcu.?.llvm_object == null);
934922 switch (base.tag) {
923 .lld => unreachable,
935924 .c => unreachable,
936925 .spirv => unreachable,
937926 .wasm => unreachable,
......@@ -947,6 +936,7 @@ pub const File = struct {
947936 pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) !u64 {
948937 assert(base.comp.zcu.?.llvm_object == null);
949938 switch (base.tag) {
939 .lld => unreachable,
950940 .c => unreachable,
951941 .spirv => unreachable,
952942 .wasm => unreachable,
......@@ -966,6 +956,8 @@ pub const File = struct {
966956 ) void {
967957 assert(base.comp.zcu.?.llvm_object == null);
968958 switch (base.tag) {
959 .lld => unreachable,
960
969961 .plan9,
970962 .spirv,
971963 .goff,
......@@ -981,6 +973,7 @@ pub const File = struct {
981973
982974 /// Opens a path as an object file and parses it into the linker.
983975 fn openLoadObject(base: *File, path: Path) anyerror!void {
976 if (base.tag == .lld) return;
984977 const diags = &base.comp.link_diags;
985978 const input = try openObjectInput(diags, path);
986979 errdefer input.object.file.close();
......@@ -990,6 +983,7 @@ pub const File = struct {
990983 /// Opens a path as a static library and parses it into the linker.
991984 /// If `query` is non-null, allows GNU ld scripts.
992985 fn openLoadArchive(base: *File, path: Path, opt_query: ?UnresolvedInput.Query) anyerror!void {
986 if (base.tag == .lld) return;
993987 if (opt_query) |query| {
994988 const archive = try openObject(path, query.must_link, query.hidden);
995989 errdefer archive.file.close();
......@@ -1012,6 +1006,7 @@ pub const File = struct {
10121006 /// Opens a path as a shared library and parses it into the linker.
10131007 /// Handles GNU ld scripts.
10141008 fn openLoadDso(base: *File, path: Path, query: UnresolvedInput.Query) anyerror!void {
1009 if (base.tag == .lld) return;
10151010 const dso = try openDso(path, query.needed, query.weak, query.reexport);
10161011 errdefer dso.file.close();
10171012 loadInput(base, .{ .dso = dso }) catch |err| switch (err) {
......@@ -1064,8 +1059,7 @@ pub const File = struct {
10641059 }
10651060
10661061 pub fn loadInput(base: *File, input: Input) anyerror!void {
1067 const use_lld = build_options.have_llvm and base.comp.config.use_lld;
1068 if (use_lld) return;
1062 if (base.tag == .lld) return;
10691063 switch (base.tag) {
10701064 inline .elf, .wasm => |tag| {
10711065 dev.check(tag.devFeature());
......@@ -1079,8 +1073,6 @@ pub const File = struct {
10791073 /// this, `loadInput` will not be called anymore.
10801074 pub fn prelink(base: *File, prog_node: std.Progress.Node) FlushError!void {
10811075 assert(!base.post_prelink);
1082 const use_lld = build_options.have_llvm and base.comp.config.use_lld;
1083 if (use_lld) return;
10841076
10851077 // In this case, an object file is created by the LLVM backend, so
10861078 // there is no prelink phase. The Zig code is linked as a standard
......@@ -1096,170 +1088,6 @@ pub const File = struct {
10961088 }
10971089 }
10981090
1099 fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {
1100 dev.check(.lld_linker);
1101
1102 const tracy = trace(@src());
1103 defer tracy.end();
1104
1105 const comp = base.comp;
1106 const diags = &comp.link_diags;
1107
1108 return linkAsArchiveInner(base, arena, tid, prog_node) catch |err| switch (err) {
1109 error.OutOfMemory => return error.OutOfMemory,
1110 error.LinkFailure => return error.LinkFailure,
1111 else => |e| return diags.fail("failed to link as archive: {s}", .{@errorName(e)}),
1112 };
1113 }
1114
1115 fn linkAsArchiveInner(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {
1116 const comp = base.comp;
1117
1118 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
1119 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
1120 const full_out_path_z = try arena.dupeZ(u8, full_out_path);
1121 const opt_zcu = comp.zcu;
1122
1123 // If there is no Zig code to compile, then we should skip flushing the output file
1124 // because it will not be part of the linker line anyway.
1125 const zcu_obj_path: ?[]const u8 = if (opt_zcu) |zcu| blk: {
1126 if (zcu.llvm_object == null) {
1127 try base.flushZcu(arena, tid, prog_node);
1128 } else {
1129 // `Compilation.flush` has already made LLVM emit this object file for us.
1130 }
1131 const dirname = fs.path.dirname(full_out_path_z) orelse ".";
1132 break :blk try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? });
1133 } else null;
1134
1135 log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"});
1136
1137 const compiler_rt_path: ?Path = if (comp.compiler_rt_strat == .obj)
1138 comp.compiler_rt_obj.?.full_object_path
1139 else
1140 null;
1141
1142 const ubsan_rt_path: ?Path = if (comp.ubsan_rt_strat == .obj)
1143 comp.ubsan_rt_obj.?.full_object_path
1144 else
1145 null;
1146
1147 // This function follows the same pattern as link.Elf.linkWithLLD so if you want some
1148 // insight as to what's going on here you can read that function body which is more
1149 // well-commented.
1150
1151 const id_symlink_basename = "llvm-ar.id";
1152
1153 var man: Cache.Manifest = undefined;
1154 defer if (!base.disable_lld_caching) man.deinit();
1155
1156 const link_inputs = comp.link_inputs;
1157
1158 var digest: [Cache.hex_digest_len]u8 = undefined;
1159
1160 if (!base.disable_lld_caching) {
1161 man = comp.cache_parent.obtain();
1162
1163 // We are about to obtain this lock, so here we give other processes a chance first.
1164 base.releaseLock();
1165
1166 try hashInputs(&man, link_inputs);
1167
1168 for (comp.c_object_table.keys()) |key| {
1169 _ = try man.addFilePath(key.status.success.object_path, null);
1170 }
1171 for (comp.win32_resource_table.keys()) |key| {
1172 _ = try man.addFile(key.status.success.res_path, null);
1173 }
1174 try man.addOptionalFile(zcu_obj_path);
1175 try man.addOptionalFilePath(compiler_rt_path);
1176 try man.addOptionalFilePath(ubsan_rt_path);
1177
1178 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
1179 _ = try man.hit();
1180 digest = man.final();
1181
1182 var prev_digest_buf: [digest.len]u8 = undefined;
1183 const prev_digest: []u8 = Cache.readSmallFile(
1184 directory.handle,
1185 id_symlink_basename,
1186 &prev_digest_buf,
1187 ) catch |err| b: {
1188 log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
1189 break :b prev_digest_buf[0..0];
1190 };
1191 if (mem.eql(u8, prev_digest, &digest)) {
1192 log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
1193 base.lock = man.toOwnedLock();
1194 return;
1195 }
1196
1197 // We are about to change the output file to be different, so we invalidate the build hash now.
1198 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
1199 error.FileNotFound => {},
1200 else => |e| return e,
1201 };
1202 }
1203
1204 var object_files: std.ArrayListUnmanaged([*:0]const u8) = .empty;
1205
1206 try object_files.ensureUnusedCapacity(arena, link_inputs.len);
1207 for (link_inputs) |input| {
1208 object_files.appendAssumeCapacity(try input.path().?.toStringZ(arena));
1209 }
1210
1211 try object_files.ensureUnusedCapacity(arena, comp.c_object_table.count() +
1212 comp.win32_resource_table.count() + 2);
1213
1214 for (comp.c_object_table.keys()) |key| {
1215 object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena));
1216 }
1217 for (comp.win32_resource_table.keys()) |key| {
1218 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));
1219 }
1220 if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
1221 if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
1222 if (ubsan_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
1223
1224 if (comp.verbose_link) {
1225 std.debug.print("ar rcs {s}", .{full_out_path_z});
1226 for (object_files.items) |arg| {
1227 std.debug.print(" {s}", .{arg});
1228 }
1229 std.debug.print("\n", .{});
1230 }
1231
1232 const llvm_bindings = @import("codegen/llvm/bindings.zig");
1233 const llvm = @import("codegen/llvm.zig");
1234 const target = comp.root_mod.resolved_target.result;
1235 llvm.initializeLLVMTarget(target.cpu.arch);
1236 const bad = llvm_bindings.WriteArchive(
1237 full_out_path_z,
1238 object_files.items.ptr,
1239 object_files.items.len,
1240 switch (target.os.tag) {
1241 .aix => .AIXBIG,
1242 .windows => .COFF,
1243 else => if (target.os.tag.isDarwin()) .DARWIN else .GNU,
1244 },
1245 );
1246 if (bad) return error.UnableToWriteArchive;
1247
1248 if (!base.disable_lld_caching) {
1249 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1250 log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)});
1251 };
1252
1253 if (man.have_exclusive_lock) {
1254 man.writeManifest() catch |err| {
1255 log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)});
1256 };
1257 }
1258
1259 base.lock = man.toOwnedLock();
1260 }
1261 }
1262
12631091 pub const Tag = enum {
12641092 coff,
12651093 elf,
......@@ -1270,6 +1098,7 @@ pub const File = struct {
12701098 plan9,
12711099 goff,
12721100 xcoff,
1101 lld,
12731102
12741103 pub fn Type(comptime tag: Tag) type {
12751104 return switch (tag) {
......@@ -1282,10 +1111,11 @@ pub const File = struct {
12821111 .plan9 => Plan9,
12831112 .goff => Goff,
12841113 .xcoff => Xcoff,
1114 .lld => Lld,
12851115 };
12861116 }
12871117
1288 pub fn fromObjectFormat(ofmt: std.Target.ObjectFormat) Tag {
1118 fn fromObjectFormat(ofmt: std.Target.ObjectFormat) Tag {
12891119 return switch (ofmt) {
12901120 .coff => .coff,
12911121 .elf => .elf,
......@@ -1313,15 +1143,7 @@ pub const File = struct {
13131143 ty: InternPool.Index,
13141144 };
13151145
1316 pub fn effectiveOutputMode(
1317 use_lld: bool,
1318 output_mode: std.builtin.OutputMode,
1319 ) std.builtin.OutputMode {
1320 return if (use_lld) .Obj else output_mode;
1321 }
1322
13231146 pub fn determineMode(
1324 use_lld: bool,
13251147 output_mode: std.builtin.OutputMode,
13261148 link_mode: std.builtin.LinkMode,
13271149 ) fs.File.Mode {
......@@ -1330,7 +1152,7 @@ pub const File = struct {
13301152 // more leniently. As another data point, C's fopen seems to open files with the
13311153 // 666 mode.
13321154 const executable_mode = if (builtin.target.os.tag == .windows) 0 else 0o777;
1333 switch (effectiveOutputMode(use_lld, output_mode)) {
1155 switch (output_mode) {
13341156 .Lib => return switch (link_mode) {
13351157 .dynamic => executable_mode,
13361158 .static => fs.File.default_mode,
......@@ -1378,6 +1200,7 @@ pub const File = struct {
13781200 return base.comp.zcu.?.codegenFail(nav_index, format, args);
13791201 }
13801202
1203 pub const Lld = @import("link/Lld.zig");
13811204 pub const C = @import("link/C.zig");
13821205 pub const Coff = @import("link/Coff.zig");
13831206 pub const Plan9 = @import("link/Plan9.zig");
......@@ -1685,116 +1508,6 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
16851508 }
16861509}
16871510
1688pub fn spawnLld(
1689 comp: *Compilation,
1690 arena: Allocator,
1691 argv: []const []const u8,
1692) !void {
1693 if (comp.verbose_link) {
1694 // Skip over our own name so that the LLD linker name is the first argv item.
1695 Compilation.dump_argv(argv[1..]);
1696 }
1697
1698 // If possible, we run LLD as a child process because it does not always
1699 // behave properly as a library, unfortunately.
1700 // https://github.com/ziglang/zig/issues/3825
1701 if (!std.process.can_spawn) {
1702 const exit_code = try lldMain(arena, argv, false);
1703 if (exit_code == 0) return;
1704 if (comp.clang_passthrough_mode) std.process.exit(exit_code);
1705 return error.LinkFailure;
1706 }
1707
1708 var stderr: []u8 = &.{};
1709 defer comp.gpa.free(stderr);
1710
1711 var child = std.process.Child.init(argv, arena);
1712 const term = (if (comp.clang_passthrough_mode) term: {
1713 child.stdin_behavior = .Inherit;
1714 child.stdout_behavior = .Inherit;
1715 child.stderr_behavior = .Inherit;
1716
1717 break :term child.spawnAndWait();
1718 } else term: {
1719 child.stdin_behavior = .Ignore;
1720 child.stdout_behavior = .Ignore;
1721 child.stderr_behavior = .Pipe;
1722
1723 child.spawn() catch |err| break :term err;
1724 stderr = try child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize));
1725 break :term child.wait();
1726 }) catch |first_err| term: {
1727 const err = switch (first_err) {
1728 error.NameTooLong => err: {
1729 const s = fs.path.sep_str;
1730 const rand_int = std.crypto.random.int(u64);
1731 const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp";
1732
1733 const rsp_file = try comp.dirs.local_cache.handle.createFileZ(rsp_path, .{});
1734 defer comp.dirs.local_cache.handle.deleteFileZ(rsp_path) catch |err|
1735 log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) });
1736 {
1737 defer rsp_file.close();
1738 var rsp_buf = std.io.bufferedWriter(rsp_file.writer());
1739 const rsp_writer = rsp_buf.writer();
1740 for (argv[2..]) |arg| {
1741 try rsp_writer.writeByte('"');
1742 for (arg) |c| {
1743 switch (c) {
1744 '\"', '\\' => try rsp_writer.writeByte('\\'),
1745 else => {},
1746 }
1747 try rsp_writer.writeByte(c);
1748 }
1749 try rsp_writer.writeByte('"');
1750 try rsp_writer.writeByte('\n');
1751 }
1752 try rsp_buf.flush();
1753 }
1754
1755 var rsp_child = std.process.Child.init(&.{ argv[0], argv[1], try std.fmt.allocPrint(
1756 arena,
1757 "@{s}",
1758 .{try comp.dirs.local_cache.join(arena, &.{rsp_path})},
1759 ) }, arena);
1760 if (comp.clang_passthrough_mode) {
1761 rsp_child.stdin_behavior = .Inherit;
1762 rsp_child.stdout_behavior = .Inherit;
1763 rsp_child.stderr_behavior = .Inherit;
1764
1765 break :term rsp_child.spawnAndWait() catch |err| break :err err;
1766 } else {
1767 rsp_child.stdin_behavior = .Ignore;
1768 rsp_child.stdout_behavior = .Ignore;
1769 rsp_child.stderr_behavior = .Pipe;
1770
1771 rsp_child.spawn() catch |err| break :err err;
1772 stderr = try rsp_child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize));
1773 break :term rsp_child.wait() catch |err| break :err err;
1774 }
1775 },
1776 else => first_err,
1777 };
1778 log.err("unable to spawn LLD {s}: {s}", .{ argv[0], @errorName(err) });
1779 return error.UnableToSpawnSelf;
1780 };
1781
1782 const diags = &comp.link_diags;
1783 switch (term) {
1784 .Exited => |code| if (code != 0) {
1785 if (comp.clang_passthrough_mode) std.process.exit(code);
1786 diags.lockAndParseLldStderr(argv[1], stderr);
1787 return error.LinkFailure;
1788 },
1789 else => {
1790 if (comp.clang_passthrough_mode) std.process.abort();
1791 return diags.fail("{s} terminated with stderr:\n{s}", .{ argv[0], stderr });
1792 },
1793 }
1794
1795 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1796}
1797
17981511/// Provided by the CLI, processed into `LinkInput` instances at the start of
17991512/// the compilation pipeline.
18001513pub const UnresolvedInput = union(enum) {
src/link/C.zig+1-6
......@@ -145,7 +145,6 @@ pub fn createEmpty(
145145 .stack_size = options.stack_size orelse 16777216,
146146 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
147147 .file = file,
148 .disable_lld_caching = options.disable_lld_caching,
149148 .build_id = options.build_id,
150149 },
151150 };
......@@ -381,10 +380,6 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn
381380 _ = ti_id;
382381}
383382
384pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
385 return self.flushZcu(arena, tid, prog_node);
386}
387
388383fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {
389384 const gpa = self.base.comp.gpa;
390385 var defines = std.ArrayList(u8).init(gpa);
......@@ -400,7 +395,7 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {
400395 return defines;
401396}
402397
403pub fn flushZcu(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
398pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
404399 _ = arena; // Has the same lifetime as the call to Compilation.update.
405400
406401 const tracy = trace(@src());
src/link/Coff.zig+13-617
......@@ -1,23 +1,14 @@
1//! The main driver of the COFF linker.
2//! Currently uses our own implementation for the incremental linker, and falls back to
3//! LLD for traditional linking (linking relocatable object files).
4//! LLD is also the default linker for LLVM.
1//! The main driver of the self-hosted COFF linker.
52
63base: link.File,
74image_base: u64,
8subsystem: ?std.Target.SubSystem,
9tsaware: bool,
10nxcompat: bool,
11dynamicbase: bool,
125/// TODO this and minor_subsystem_version should be combined into one property and left as
136/// default or populated together. They should not be separate fields.
147major_subsystem_version: u16,
158minor_subsystem_version: u16,
16lib_directories: []const Directory,
179entry: link.File.OpenOptions.Entry,
1810entry_addr: ?u32,
1911module_definition_file: ?[]const u8,
20pdb_out_path: ?[]const u8,
2112repro: bool,
2213
2314ptr_width: PtrWidth,
......@@ -84,16 +75,6 @@ base_relocs: BaseRelocationTable = .{},
8475/// Hot-code swapping state.
8576hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
8677
87/// When linking with LLD, these flags are used to determine the subsystem to pass on the LLD command line.
88lld_export_flags: struct {
89 c_main: bool = false,
90 winmain: bool = false,
91 wwinmain: bool = false,
92 winmain_crt_startup: bool = false,
93 wwinmain_crt_startup: bool = false,
94 dllmain_crt_startup: bool = false,
95} = .{},
96
9778const is_hot_update_compatible = switch (builtin.target.os.tag) {
9879 .windows => true,
9980 else => false,
......@@ -233,7 +214,6 @@ pub fn createEmpty(
233214 const output_mode = comp.config.output_mode;
234215 const link_mode = comp.config.link_mode;
235216 const use_llvm = comp.config.use_llvm;
236 const use_lld = build_options.have_llvm and comp.config.use_lld;
237217
238218 const ptr_width: PtrWidth = switch (target.ptrBitWidth()) {
239219 0...32 => .p32,
......@@ -244,12 +224,10 @@ pub fn createEmpty(
244224 else => 0x1000,
245225 };
246226
247 // If using LLD to link, this code should produce an object file so that it
248 // can be passed to LLD.
249227 // If using LLVM to generate the object file for the zig compilation unit,
250228 // we need a place to put the object file so that it can be subsequently
251229 // handled.
252 const zcu_object_sub_path = if (!use_lld and !use_llvm)
230 const zcu_object_sub_path = if (!use_llvm)
253231 null
254232 else
255233 try allocPrint(arena, "{s}.obj", .{emit.sub_path});
......@@ -266,7 +244,6 @@ pub fn createEmpty(
266244 .print_gc_sections = options.print_gc_sections,
267245 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
268246 .file = null,
269 .disable_lld_caching = options.disable_lld_caching,
270247 .build_id = options.build_id,
271248 },
272249 .ptr_width = ptr_width,
......@@ -291,39 +268,21 @@ pub fn createEmpty(
291268 .Obj => 0,
292269 },
293270
294 // Subsystem depends on the set of public symbol names from linked objects.
295 // See LinkerDriver::inferSubsystem from the LLD project for the flow chart.
296 .subsystem = options.subsystem,
297
298271 .entry = options.entry,
299272
300 .tsaware = options.tsaware,
301 .nxcompat = options.nxcompat,
302 .dynamicbase = options.dynamicbase,
303273 .major_subsystem_version = options.major_subsystem_version orelse 6,
304274 .minor_subsystem_version = options.minor_subsystem_version orelse 0,
305 .lib_directories = options.lib_directories,
306275 .entry_addr = math.cast(u32, options.entry_addr orelse 0) orelse
307276 return error.EntryAddressTooBig,
308277 .module_definition_file = options.module_definition_file,
309 .pdb_out_path = options.pdb_out_path,
310278 .repro = options.repro,
311279 };
312280 errdefer coff.base.destroy();
313281
314 if (use_lld and (use_llvm or !comp.config.have_zcu)) {
315 // LLVM emits the object file (if any); LLD links it into the final product.
316 return coff;
317 }
318
319 // What path should this COFF linker code output to?
320 // If using LLD to link, this code should produce an object file so that it
321 // can be passed to LLD.
322 const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;
323 coff.base.file = try emit.root_dir.handle.createFile(sub_path, .{
282 coff.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{
324283 .truncate = true,
325284 .read = true,
326 .mode = link.File.determineMode(use_lld, output_mode, link_mode),
285 .mode = link.File.determineMode(output_mode, link_mode),
327286 });
328287
329288 const gpa = comp.gpa;
......@@ -1327,7 +1286,7 @@ pub fn getOrCreateAtomForLazySymbol(
13271286 }
13281287 state_ptr.* = .pending_flush;
13291288 const atom = atom_ptr.*;
1330 // anyerror needs to be deferred until flushZcu
1289 // anyerror needs to be deferred until flush
13311290 if (lazy_sym.ty != .anyerror_type) try coff.updateLazySymbolAtom(pt, lazy_sym, atom, switch (lazy_sym.kind) {
13321291 .code => coff.text_section_index.?,
13331292 .const_data => coff.rdata_section_index.?,
......@@ -1631,575 +1590,7 @@ fn resolveGlobalSymbol(coff: *Coff, current: SymbolWithLoc) !void {
16311590 gop.value_ptr.* = current;
16321591}
16331592
1634pub fn flush(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
1635 const comp = coff.base.comp;
1636 const use_lld = build_options.have_llvm and comp.config.use_lld;
1637 const diags = &comp.link_diags;
1638 if (use_lld) {
1639 return coff.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) {
1640 error.OutOfMemory => return error.OutOfMemory,
1641 error.LinkFailure => return error.LinkFailure,
1642 else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
1643 };
1644 }
1645 switch (comp.config.output_mode) {
1646 .Exe, .Obj => return coff.flushZcu(arena, tid, prog_node),
1647 .Lib => return diags.fail("writing lib files not yet implemented for COFF", .{}),
1648 }
1649}
1650
1651fn linkWithLLD(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {
1652 dev.check(.lld_linker);
1653
1654 const tracy = trace(@src());
1655 defer tracy.end();
1656
1657 const comp = coff.base.comp;
1658 const gpa = comp.gpa;
1659
1660 const directory = coff.base.emit.root_dir; // Just an alias to make it shorter to type.
1661 const full_out_path = try directory.join(arena, &[_][]const u8{coff.base.emit.sub_path});
1662
1663 // If there is no Zig code to compile, then we should skip flushing the output file because it
1664 // will not be part of the linker line anyway.
1665 const module_obj_path: ?[]const u8 = if (comp.zcu) |zcu| blk: {
1666 if (zcu.llvm_object == null) {
1667 try coff.flushZcu(arena, tid, prog_node);
1668 } else {
1669 // `Compilation.flush` has already made LLVM emit this object file for us.
1670 }
1671
1672 if (fs.path.dirname(full_out_path)) |dirname| {
1673 break :blk try fs.path.join(arena, &.{ dirname, coff.base.zcu_object_sub_path.? });
1674 } else {
1675 break :blk coff.base.zcu_object_sub_path.?;
1676 }
1677 } else null;
1678
1679 const sub_prog_node = prog_node.start("LLD Link", 0);
1680 defer sub_prog_node.end();
1681
1682 const is_lib = comp.config.output_mode == .Lib;
1683 const is_dyn_lib = comp.config.link_mode == .dynamic and is_lib;
1684 const is_exe_or_dyn_lib = is_dyn_lib or comp.config.output_mode == .Exe;
1685 const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib;
1686 const target = comp.root_mod.resolved_target.result;
1687 const optimize_mode = comp.root_mod.optimize_mode;
1688 const entry_name: ?[]const u8 = switch (coff.entry) {
1689 // This logic isn't quite right for disabled or enabled. No point in fixing it
1690 // when the goal is to eliminate dependency on LLD anyway.
1691 // https://github.com/ziglang/zig/issues/17751
1692 .disabled, .default, .enabled => null,
1693 .named => |name| name,
1694 };
1695
1696 // See link/Elf.zig for comments on how this mechanism works.
1697 const id_symlink_basename = "lld.id";
1698
1699 var man: Cache.Manifest = undefined;
1700 defer if (!coff.base.disable_lld_caching) man.deinit();
1701
1702 var digest: [Cache.hex_digest_len]u8 = undefined;
1703
1704 if (!coff.base.disable_lld_caching) {
1705 man = comp.cache_parent.obtain();
1706 coff.base.releaseLock();
1707
1708 comptime assert(Compilation.link_hash_implementation_version == 14);
1709
1710 try link.hashInputs(&man, comp.link_inputs);
1711 for (comp.c_object_table.keys()) |key| {
1712 _ = try man.addFilePath(key.status.success.object_path, null);
1713 }
1714 for (comp.win32_resource_table.keys()) |key| {
1715 _ = try man.addFile(key.status.success.res_path, null);
1716 }
1717 try man.addOptionalFile(module_obj_path);
1718 man.hash.addOptionalBytes(entry_name);
1719 man.hash.add(coff.base.stack_size);
1720 man.hash.add(coff.image_base);
1721 man.hash.add(coff.base.build_id);
1722 {
1723 // TODO remove this, libraries must instead be resolved by the frontend.
1724 for (coff.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path);
1725 }
1726 man.hash.add(comp.skip_linker_dependencies);
1727 if (comp.config.link_libc) {
1728 man.hash.add(comp.libc_installation != null);
1729 if (comp.libc_installation) |libc_installation| {
1730 man.hash.addBytes(libc_installation.crt_dir.?);
1731 if (target.abi == .msvc or target.abi == .itanium) {
1732 man.hash.addBytes(libc_installation.msvc_lib_dir.?);
1733 man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
1734 }
1735 }
1736 }
1737 man.hash.addListOfBytes(comp.windows_libs.keys());
1738 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
1739 man.hash.addOptional(coff.subsystem);
1740 man.hash.add(comp.config.is_test);
1741 man.hash.add(coff.tsaware);
1742 man.hash.add(coff.nxcompat);
1743 man.hash.add(coff.dynamicbase);
1744 man.hash.add(coff.base.allow_shlib_undefined);
1745 // strip does not need to go into the linker hash because it is part of the hash namespace
1746 man.hash.add(coff.major_subsystem_version);
1747 man.hash.add(coff.minor_subsystem_version);
1748 man.hash.add(coff.repro);
1749 man.hash.addOptional(comp.version);
1750 try man.addOptionalFile(coff.module_definition_file);
1751
1752 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
1753 _ = try man.hit();
1754 digest = man.final();
1755 var prev_digest_buf: [digest.len]u8 = undefined;
1756 const prev_digest: []u8 = Cache.readSmallFile(
1757 directory.handle,
1758 id_symlink_basename,
1759 &prev_digest_buf,
1760 ) catch |err| blk: {
1761 log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
1762 // Handle this as a cache miss.
1763 break :blk prev_digest_buf[0..0];
1764 };
1765 if (mem.eql(u8, prev_digest, &digest)) {
1766 log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
1767 // Hot diggity dog! The output binary is already there.
1768 coff.base.lock = man.toOwnedLock();
1769 return;
1770 }
1771 log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
1772
1773 // We are about to change the output file to be different, so we invalidate the build hash now.
1774 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
1775 error.FileNotFound => {},
1776 else => |e| return e,
1777 };
1778 }
1779
1780 if (comp.config.output_mode == .Obj) {
1781 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
1782 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1783 // build-obj. See also the corresponding TODO in linkAsArchive.
1784 const the_object_path = blk: {
1785 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
1786
1787 if (comp.c_object_table.count() != 0)
1788 break :blk comp.c_object_table.keys()[0].status.success.object_path;
1789
1790 if (module_obj_path) |p|
1791 break :blk Path.initCwd(p);
1792
1793 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1794 // regarding eliding redundant object -> object transformations.
1795 return error.NoObjectsToLink;
1796 };
1797 try std.fs.Dir.copyFile(
1798 the_object_path.root_dir.handle,
1799 the_object_path.sub_path,
1800 directory.handle,
1801 coff.base.emit.sub_path,
1802 .{},
1803 );
1804 } else {
1805 // Create an LLD command line and invoke it.
1806 var argv = std.ArrayList([]const u8).init(gpa);
1807 defer argv.deinit();
1808 // We will invoke ourselves as a child process to gain access to LLD.
1809 // This is necessary because LLD does not behave properly as a library -
1810 // it calls exit() and does not reset all global data between invocations.
1811 const linker_command = "lld-link";
1812 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
1813
1814 if (target.isMinGW()) {
1815 try argv.append("-lldmingw");
1816 }
1817
1818 try argv.append("-ERRORLIMIT:0");
1819 try argv.append("-NOLOGO");
1820 if (comp.config.debug_format != .strip) {
1821 try argv.append("-DEBUG");
1822
1823 const out_ext = std.fs.path.extension(full_out_path);
1824 const out_pdb = coff.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{
1825 full_out_path[0 .. full_out_path.len - out_ext.len],
1826 });
1827 const out_pdb_basename = std.fs.path.basename(out_pdb);
1828
1829 try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb}));
1830 try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb_basename}));
1831 }
1832 if (comp.version) |version| {
1833 try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor }));
1834 }
1835
1836 if (target_util.llvmMachineAbi(target)) |mabi| {
1837 try argv.append(try allocPrint(arena, "-MLLVM:-target-abi={s}", .{mabi}));
1838 }
1839
1840 try argv.append(try allocPrint(arena, "-MLLVM:-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}));
1841
1842 if (comp.config.lto != .none) {
1843 switch (optimize_mode) {
1844 .Debug => {},
1845 .ReleaseSmall => try argv.append("-OPT:lldlto=2"),
1846 .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"),
1847 }
1848 }
1849 if (comp.config.output_mode == .Exe) {
1850 try argv.append(try allocPrint(arena, "-STACK:{d}", .{coff.base.stack_size}));
1851 }
1852 try argv.append(try allocPrint(arena, "-BASE:{d}", .{coff.image_base}));
1853
1854 switch (coff.base.build_id) {
1855 .none => try argv.append("-BUILD-ID:NO"),
1856 .fast => try argv.append("-BUILD-ID"),
1857 .uuid, .sha1, .md5, .hexstring => {},
1858 }
1859
1860 if (target.cpu.arch == .x86) {
1861 try argv.append("-MACHINE:X86");
1862 } else if (target.cpu.arch == .x86_64) {
1863 try argv.append("-MACHINE:X64");
1864 } else if (target.cpu.arch == .thumb) {
1865 try argv.append("-MACHINE:ARM");
1866 } else if (target.cpu.arch == .aarch64) {
1867 try argv.append("-MACHINE:ARM64");
1868 }
1869
1870 for (comp.force_undefined_symbols.keys()) |symbol| {
1871 try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol}));
1872 }
1873
1874 if (is_dyn_lib) {
1875 try argv.append("-DLL");
1876 }
1877
1878 if (entry_name) |name| {
1879 try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{name}));
1880 }
1881
1882 if (coff.repro) {
1883 try argv.append("-BREPRO");
1884 }
1885
1886 if (coff.tsaware) {
1887 try argv.append("-tsaware");
1888 }
1889 if (coff.nxcompat) {
1890 try argv.append("-nxcompat");
1891 }
1892 if (!coff.dynamicbase) {
1893 try argv.append("-dynamicbase:NO");
1894 }
1895 if (coff.base.allow_shlib_undefined) {
1896 try argv.append("-FORCE:UNRESOLVED");
1897 }
1898
1899 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
1900
1901 if (comp.implib_emit) |emit| {
1902 const implib_out_path = try emit.root_dir.join(arena, &[_][]const u8{emit.sub_path});
1903 try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));
1904 }
1905
1906 if (comp.config.link_libc) {
1907 if (comp.libc_installation) |libc_installation| {
1908 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
1909
1910 if (target.abi == .msvc or target.abi == .itanium) {
1911 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
1912 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
1913 }
1914 }
1915 }
1916
1917 for (coff.lib_directories) |lib_directory| {
1918 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_directory.path orelse "."}));
1919 }
1920
1921 try argv.ensureUnusedCapacity(comp.link_inputs.len);
1922 for (comp.link_inputs) |link_input| switch (link_input) {
1923 .dso_exact => unreachable, // not applicable to PE/COFF
1924 inline .dso, .res => |x| {
1925 argv.appendAssumeCapacity(try x.path.toString(arena));
1926 },
1927 .object, .archive => |obj| {
1928 if (obj.must_link) {
1929 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)}));
1930 } else {
1931 argv.appendAssumeCapacity(try obj.path.toString(arena));
1932 }
1933 },
1934 };
1935
1936 for (comp.c_object_table.keys()) |key| {
1937 try argv.append(try key.status.success.object_path.toString(arena));
1938 }
1939
1940 for (comp.win32_resource_table.keys()) |key| {
1941 try argv.append(key.status.success.res_path);
1942 }
1943
1944 if (module_obj_path) |p| {
1945 try argv.append(p);
1946 }
1947
1948 if (coff.module_definition_file) |def| {
1949 try argv.append(try allocPrint(arena, "-DEF:{s}", .{def}));
1950 }
1951
1952 const resolved_subsystem: ?std.Target.SubSystem = blk: {
1953 if (coff.subsystem) |explicit| break :blk explicit;
1954 switch (target.os.tag) {
1955 .windows => {
1956 if (comp.zcu != null) {
1957 if (coff.lld_export_flags.dllmain_crt_startup or is_dyn_lib)
1958 break :blk null;
1959 if (coff.lld_export_flags.c_main or comp.config.is_test or
1960 coff.lld_export_flags.winmain_crt_startup or
1961 coff.lld_export_flags.wwinmain_crt_startup)
1962 {
1963 break :blk .Console;
1964 }
1965 if (coff.lld_export_flags.winmain or coff.lld_export_flags.wwinmain)
1966 break :blk .Windows;
1967 }
1968 },
1969 .uefi => break :blk .EfiApplication,
1970 else => {},
1971 }
1972 break :blk null;
1973 };
1974
1975 const Mode = enum { uefi, win32 };
1976 const mode: Mode = mode: {
1977 if (resolved_subsystem) |subsystem| {
1978 const subsystem_suffix = try allocPrint(arena, ",{d}.{d}", .{
1979 coff.major_subsystem_version, coff.minor_subsystem_version,
1980 });
1981
1982 switch (subsystem) {
1983 .Console => {
1984 try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{
1985 subsystem_suffix,
1986 }));
1987 break :mode .win32;
1988 },
1989 .EfiApplication => {
1990 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{
1991 subsystem_suffix,
1992 }));
1993 break :mode .uefi;
1994 },
1995 .EfiBootServiceDriver => {
1996 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{
1997 subsystem_suffix,
1998 }));
1999 break :mode .uefi;
2000 },
2001 .EfiRom => {
2002 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{
2003 subsystem_suffix,
2004 }));
2005 break :mode .uefi;
2006 },
2007 .EfiRuntimeDriver => {
2008 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{
2009 subsystem_suffix,
2010 }));
2011 break :mode .uefi;
2012 },
2013 .Native => {
2014 try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{
2015 subsystem_suffix,
2016 }));
2017 break :mode .win32;
2018 },
2019 .Posix => {
2020 try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{
2021 subsystem_suffix,
2022 }));
2023 break :mode .win32;
2024 },
2025 .Windows => {
2026 try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{
2027 subsystem_suffix,
2028 }));
2029 break :mode .win32;
2030 },
2031 }
2032 } else if (target.os.tag == .uefi) {
2033 break :mode .uefi;
2034 } else {
2035 break :mode .win32;
2036 }
2037 };
2038
2039 switch (mode) {
2040 .uefi => try argv.appendSlice(&[_][]const u8{
2041 "-BASE:0",
2042 "-ENTRY:EfiMain",
2043 "-OPT:REF",
2044 "-SAFESEH:NO",
2045 "-MERGE:.rdata=.data",
2046 "-NODEFAULTLIB",
2047 "-SECTION:.xdata,D",
2048 }),
2049 .win32 => {
2050 if (link_in_crt) {
2051 if (target.abi.isGnu()) {
2052 if (target.cpu.arch == .x86) {
2053 try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
2054 } else {
2055 try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
2056 }
2057
2058 if (is_dyn_lib) {
2059 try argv.append(try comp.crtFileAsString(arena, "dllcrt2.obj"));
2060 if (target.cpu.arch == .x86) {
2061 try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12");
2062 } else {
2063 try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup");
2064 }
2065 } else {
2066 try argv.append(try comp.crtFileAsString(arena, "crt2.obj"));
2067 }
2068
2069 try argv.append(try comp.crtFileAsString(arena, "libmingw32.lib"));
2070 } else {
2071 try argv.append(switch (comp.config.link_mode) {
2072 .static => "libcmt.lib",
2073 .dynamic => "msvcrt.lib",
2074 });
2075
2076 const lib_str = switch (comp.config.link_mode) {
2077 .static => "lib",
2078 .dynamic => "",
2079 };
2080 try argv.append(try allocPrint(arena, "{s}vcruntime.lib", .{lib_str}));
2081 try argv.append(try allocPrint(arena, "{s}ucrt.lib", .{lib_str}));
2082
2083 //Visual C++ 2015 Conformance Changes
2084 //https://msdn.microsoft.com/en-us/library/bb531344.aspx
2085 try argv.append("legacy_stdio_definitions.lib");
2086
2087 // msvcrt depends on kernel32 and ntdll
2088 try argv.append("kernel32.lib");
2089 try argv.append("ntdll.lib");
2090 }
2091 } else {
2092 try argv.append("-NODEFAULTLIB");
2093 if (!is_lib and entry_name == null) {
2094 if (comp.zcu != null) {
2095 if (coff.lld_export_flags.winmain_crt_startup) {
2096 try argv.append("-ENTRY:WinMainCRTStartup");
2097 } else {
2098 try argv.append("-ENTRY:wWinMainCRTStartup");
2099 }
2100 } else {
2101 try argv.append("-ENTRY:wWinMainCRTStartup");
2102 }
2103 }
2104 }
2105 },
2106 }
2107
2108 if (comp.config.link_libc and link_in_crt) {
2109 if (comp.zigc_static_lib) |zigc| {
2110 try argv.append(try zigc.full_object_path.toString(arena));
2111 }
2112 }
2113
2114 // libc++ dep
2115 if (comp.config.link_libcpp) {
2116 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
2117 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
2118 }
2119
2120 // libunwind dep
2121 if (comp.config.link_libunwind) {
2122 try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
2123 }
2124
2125 if (comp.config.any_fuzz) {
2126 try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
2127 }
2128
2129 const ubsan_rt_path: ?Path = blk: {
2130 if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path;
2131 if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path;
2132 break :blk null;
2133 };
2134 if (ubsan_rt_path) |path| {
2135 try argv.append(try path.toString(arena));
2136 }
2137
2138 if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {
2139 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
2140 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
2141 if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena));
2142 if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
2143 }
2144
2145 try argv.ensureUnusedCapacity(comp.windows_libs.count());
2146 for (comp.windows_libs.keys()) |key| {
2147 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
2148 if (comp.crt_files.get(lib_basename)) |crt_file| {
2149 argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena));
2150 continue;
2151 }
2152 if (try findLib(arena, lib_basename, coff.lib_directories)) |full_path| {
2153 argv.appendAssumeCapacity(full_path);
2154 continue;
2155 }
2156 if (target.abi.isGnu()) {
2157 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
2158 if (try findLib(arena, fallback_name, coff.lib_directories)) |full_path| {
2159 argv.appendAssumeCapacity(full_path);
2160 continue;
2161 }
2162 }
2163 if (target.abi == .msvc or target.abi == .itanium) {
2164 argv.appendAssumeCapacity(lib_basename);
2165 continue;
2166 }
2167
2168 log.err("DLL import library for -l{s} not found", .{key});
2169 return error.DllImportLibraryNotFound;
2170 }
2171
2172 try link.spawnLld(comp, arena, argv.items);
2173 }
2174
2175 if (!coff.base.disable_lld_caching) {
2176 // Update the file with the digest. If it fails we can continue; it only
2177 // means that the next invocation will have an unnecessary cache miss.
2178 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
2179 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
2180 };
2181 // Again failure here only means an unnecessary cache miss.
2182 man.writeManifest() catch |err| {
2183 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
2184 };
2185 // We hang on to this lock so that the output file path can be used without
2186 // other processes clobbering it.
2187 coff.base.lock = man.toOwnedLock();
2188 }
2189}
2190
2191fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Directory) !?[]const u8 {
2192 for (lib_directories) |lib_directory| {
2193 lib_directory.handle.access(name, .{}) catch |err| switch (err) {
2194 error.FileNotFound => continue,
2195 else => |e| return e,
2196 };
2197 return try lib_directory.join(arena, &.{name});
2198 }
2199 return null;
2200}
2201
2202pub fn flushZcu(
1593pub fn flush(
22031594 coff: *Coff,
22041595 arena: Allocator,
22051596 tid: Zcu.PerThread.Id,
......@@ -2211,17 +1602,22 @@ pub fn flushZcu(
22111602 const comp = coff.base.comp;
22121603 const diags = &comp.link_diags;
22131604
1605 switch (coff.base.comp.config.output_mode) {
1606 .Exe, .Obj => {},
1607 .Lib => return diags.fail("writing lib files not yet implemented for COFF", .{}),
1608 }
1609
22141610 const sub_prog_node = prog_node.start("COFF Flush", 0);
22151611 defer sub_prog_node.end();
22161612
2217 return flushZcuInner(coff, arena, tid) catch |err| switch (err) {
1613 return flushInner(coff, arena, tid) catch |err| switch (err) {
22181614 error.OutOfMemory => return error.OutOfMemory,
22191615 error.LinkFailure => return error.LinkFailure,
22201616 else => |e| return diags.fail("COFF flush failed: {s}", .{@errorName(e)}),
22211617 };
22221618}
22231619
2224fn flushZcuInner(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id) !void {
1620fn flushInner(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id) !void {
22251621 _ = arena;
22261622
22271623 const comp = coff.base.comp;
src/link/Dwarf.zig+1-1
......@@ -4391,7 +4391,7 @@ fn refAbbrevCode(dwarf: *Dwarf, abbrev_code: AbbrevCode) UpdateError!@typeInfo(A
43914391 return @intFromEnum(abbrev_code);
43924392}
43934393
4394pub fn flushZcu(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
4394pub fn flush(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
43954395 const zcu = pt.zcu;
43964396 const ip = &zcu.intern_pool;
43974397
src/link/Elf.zig+5-776
......@@ -4,7 +4,6 @@ base: link.File,
44zig_object: ?*ZigObject,
55rpath_table: std.StringArrayHashMapUnmanaged(void),
66image_base: u64,
7emit_relocs: bool,
87z_nodelete: bool,
98z_notext: bool,
109z_defs: bool,
......@@ -16,18 +15,7 @@ z_relro: bool,
1615z_common_page_size: ?u64,
1716/// TODO make this non optional and resolve the default in open()
1817z_max_page_size: ?u64,
19hash_style: HashStyle,
20compress_debug_sections: CompressDebugSections,
21symbol_wrap_set: std.StringArrayHashMapUnmanaged(void),
22sort_section: ?SortSection,
2318soname: ?[]const u8,
24bind_global_refs_locally: bool,
25linker_script: ?[]const u8,
26version_script: ?[]const u8,
27allow_undefined_version: bool,
28enable_new_dtags: ?bool,
29print_icf_sections: bool,
30print_map: bool,
3119entry_name: ?[]const u8,
3220
3321ptr_width: PtrWidth,
......@@ -201,9 +189,6 @@ const minimum_atom_size = 64;
201189pub const min_text_capacity = padToIdeal(minimum_atom_size);
202190
203191pub const PtrWidth = enum { p32, p64 };
204pub const HashStyle = enum { sysv, gnu, both };
205pub const CompressDebugSections = enum { none, zlib, zstd };
206pub const SortSection = enum { name, alignment };
207192
208193pub fn createEmpty(
209194 arena: Allocator,
......@@ -214,7 +199,6 @@ pub fn createEmpty(
214199 const target = comp.root_mod.resolved_target.result;
215200 assert(target.ofmt == .elf);
216201
217 const use_lld = build_options.have_llvm and comp.config.use_lld;
218202 const use_llvm = comp.config.use_llvm;
219203 const opt_zcu = comp.zcu;
220204 const output_mode = comp.config.output_mode;
......@@ -265,12 +249,10 @@ pub fn createEmpty(
265249 const is_dyn_lib = output_mode == .Lib and link_mode == .dynamic;
266250 const default_sym_version: elf.Versym = if (is_dyn_lib or comp.config.rdynamic) .GLOBAL else .LOCAL;
267251
268 // If using LLD to link, this code should produce an object file so that it
269 // can be passed to LLD.
270252 // If using LLVM to generate the object file for the zig compilation unit,
271253 // we need a place to put the object file so that it can be subsequently
272254 // handled.
273 const zcu_object_sub_path = if (!use_lld and !use_llvm)
255 const zcu_object_sub_path = if (!use_llvm)
274256 null
275257 else
276258 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
......@@ -292,7 +274,6 @@ pub fn createEmpty(
292274 .stack_size = options.stack_size orelse 16777216,
293275 .allow_shlib_undefined = options.allow_shlib_undefined orelse !is_native_os,
294276 .file = null,
295 .disable_lld_caching = options.disable_lld_caching,
296277 .build_id = options.build_id,
297278 },
298279 .zig_object = null,
......@@ -317,7 +298,6 @@ pub fn createEmpty(
317298 };
318299 },
319300
320 .emit_relocs = options.emit_relocs,
321301 .z_nodelete = options.z_nodelete,
322302 .z_notext = options.z_notext,
323303 .z_defs = options.z_defs,
......@@ -327,27 +307,11 @@ pub fn createEmpty(
327307 .z_relro = options.z_relro,
328308 .z_common_page_size = options.z_common_page_size,
329309 .z_max_page_size = options.z_max_page_size,
330 .hash_style = options.hash_style,
331 .compress_debug_sections = options.compress_debug_sections,
332 .symbol_wrap_set = options.symbol_wrap_set,
333 .sort_section = options.sort_section,
334310 .soname = options.soname,
335 .bind_global_refs_locally = options.bind_global_refs_locally,
336 .linker_script = options.linker_script,
337 .version_script = options.version_script,
338 .allow_undefined_version = options.allow_undefined_version,
339 .enable_new_dtags = options.enable_new_dtags,
340 .print_icf_sections = options.print_icf_sections,
341 .print_map = options.print_map,
342311 .dump_argv_list = .empty,
343312 };
344313 errdefer self.base.destroy();
345314
346 if (use_lld and (use_llvm or !comp.config.have_zcu)) {
347 // LLVM emits the object file (if any); LLD links it into the final product.
348 return self;
349 }
350
351315 // --verbose-link
352316 if (comp.verbose_link) try dumpArgvInit(self, arena);
353317
......@@ -355,13 +319,11 @@ pub fn createEmpty(
355319 const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static);
356320
357321 // What path should this ELF linker code output to?
358 // If using LLD to link, this code should produce an object file so that it
359 // can be passed to LLD.
360 const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;
322 const sub_path = emit.sub_path;
361323 self.base.file = try emit.root_dir.handle.createFile(sub_path, .{
362324 .truncate = true,
363325 .read = true,
364 .mode = link.File.determineMode(use_lld, output_mode, link_mode),
326 .mode = link.File.determineMode(output_mode, link_mode),
365327 });
366328
367329 const gpa = comp.gpa;
......@@ -785,20 +747,6 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
785747}
786748
787749pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
788 const comp = self.base.comp;
789 const use_lld = build_options.have_llvm and comp.config.use_lld;
790 const diags = &comp.link_diags;
791 if (use_lld) {
792 return self.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) {
793 error.OutOfMemory => return error.OutOfMemory,
794 error.LinkFailure => return error.LinkFailure,
795 else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
796 };
797 }
798 try self.flushZcu(arena, tid, prog_node);
799}
800
801pub fn flushZcu(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
802750 const tracy = trace(@src());
803751 defer tracy.end();
804752
......@@ -810,14 +758,14 @@ pub fn flushZcu(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
810758 const sub_prog_node = prog_node.start("ELF Flush", 0);
811759 defer sub_prog_node.end();
812760
813 return flushZcuInner(self, arena, tid) catch |err| switch (err) {
761 return flushInner(self, arena, tid) catch |err| switch (err) {
814762 error.OutOfMemory => return error.OutOfMemory,
815763 error.LinkFailure => return error.LinkFailure,
816764 else => |e| return diags.fail("ELF flush failed: {s}", .{@errorName(e)}),
817765 };
818766}
819767
820fn flushZcuInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
768fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
821769 const comp = self.base.comp;
822770 const gpa = comp.gpa;
823771 const diags = &comp.link_diags;
......@@ -1492,643 +1440,6 @@ pub fn initOutputSection(self: *Elf, args: struct {
14921440 return out_shndx;
14931441}
14941442
1495fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {
1496 dev.check(.lld_linker);
1497
1498 const tracy = trace(@src());
1499 defer tracy.end();
1500
1501 const comp = self.base.comp;
1502 const gpa = comp.gpa;
1503 const diags = &comp.link_diags;
1504
1505 const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
1506 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
1507
1508 // If there is no Zig code to compile, then we should skip flushing the output file because it
1509 // will not be part of the linker line anyway.
1510 const module_obj_path: ?[]const u8 = if (comp.zcu) |zcu| blk: {
1511 if (zcu.llvm_object == null) {
1512 try self.flushZcu(arena, tid, prog_node);
1513 } else {
1514 // `Compilation.flush` has already made LLVM emit this object file for us.
1515 }
1516
1517 if (fs.path.dirname(full_out_path)) |dirname| {
1518 break :blk try fs.path.join(arena, &.{ dirname, self.base.zcu_object_sub_path.? });
1519 } else {
1520 break :blk self.base.zcu_object_sub_path.?;
1521 }
1522 } else null;
1523
1524 const sub_prog_node = prog_node.start("LLD Link", 0);
1525 defer sub_prog_node.end();
1526
1527 const output_mode = comp.config.output_mode;
1528 const is_obj = output_mode == .Obj;
1529 const is_lib = output_mode == .Lib;
1530 const link_mode = comp.config.link_mode;
1531 const is_dyn_lib = link_mode == .dynamic and is_lib;
1532 const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe;
1533 const have_dynamic_linker = link_mode == .dynamic and is_exe_or_dyn_lib;
1534 const target = self.getTarget();
1535 const compiler_rt_path: ?Path = blk: {
1536 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
1537 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
1538 break :blk null;
1539 };
1540 const ubsan_rt_path: ?Path = blk: {
1541 if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path;
1542 if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path;
1543 break :blk null;
1544 };
1545
1546 // Here we want to determine whether we can save time by not invoking LLD when the
1547 // output is unchanged. None of the linker options or the object files that are being
1548 // linked are in the hash that namespaces the directory we are outputting to. Therefore,
1549 // we must hash those now, and the resulting digest will form the "id" of the linking
1550 // job we are about to perform.
1551 // After a successful link, we store the id in the metadata of a symlink named "lld.id" in
1552 // the artifact directory. So, now, we check if this symlink exists, and if it matches
1553 // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
1554 const id_symlink_basename = "lld.id";
1555
1556 var man: std.Build.Cache.Manifest = undefined;
1557 defer if (!self.base.disable_lld_caching) man.deinit();
1558
1559 var digest: [std.Build.Cache.hex_digest_len]u8 = undefined;
1560
1561 if (!self.base.disable_lld_caching) {
1562 man = comp.cache_parent.obtain();
1563
1564 // We are about to obtain this lock, so here we give other processes a chance first.
1565 self.base.releaseLock();
1566
1567 comptime assert(Compilation.link_hash_implementation_version == 14);
1568
1569 try man.addOptionalFile(self.linker_script);
1570 try man.addOptionalFile(self.version_script);
1571 man.hash.add(self.allow_undefined_version);
1572 man.hash.addOptional(self.enable_new_dtags);
1573 try link.hashInputs(&man, comp.link_inputs);
1574 for (comp.c_object_table.keys()) |key| {
1575 _ = try man.addFilePath(key.status.success.object_path, null);
1576 }
1577 try man.addOptionalFile(module_obj_path);
1578 try man.addOptionalFilePath(compiler_rt_path);
1579 try man.addOptionalFilePath(ubsan_rt_path);
1580 try man.addOptionalFilePath(if (comp.tsan_lib) |l| l.full_object_path else null);
1581 try man.addOptionalFilePath(if (comp.fuzzer_lib) |l| l.full_object_path else null);
1582
1583 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
1584 // installation sources because they are always a product of the compiler version + target information.
1585 man.hash.addOptionalBytes(self.entry_name);
1586 man.hash.add(self.image_base);
1587 man.hash.add(self.base.gc_sections);
1588 man.hash.addOptional(self.sort_section);
1589 man.hash.add(comp.link_eh_frame_hdr);
1590 man.hash.add(self.emit_relocs);
1591 man.hash.add(comp.config.rdynamic);
1592 man.hash.addListOfBytes(self.rpath_table.keys());
1593 if (output_mode == .Exe) {
1594 man.hash.add(self.base.stack_size);
1595 }
1596 man.hash.add(self.base.build_id);
1597 man.hash.addListOfBytes(self.symbol_wrap_set.keys());
1598 man.hash.add(comp.skip_linker_dependencies);
1599 man.hash.add(self.z_nodelete);
1600 man.hash.add(self.z_notext);
1601 man.hash.add(self.z_defs);
1602 man.hash.add(self.z_origin);
1603 man.hash.add(self.z_nocopyreloc);
1604 man.hash.add(self.z_now);
1605 man.hash.add(self.z_relro);
1606 man.hash.add(self.z_common_page_size orelse 0);
1607 man.hash.add(self.z_max_page_size orelse 0);
1608 man.hash.add(self.hash_style);
1609 // strip does not need to go into the linker hash because it is part of the hash namespace
1610 if (comp.config.link_libc) {
1611 man.hash.add(comp.libc_installation != null);
1612 if (comp.libc_installation) |libc_installation| {
1613 man.hash.addBytes(libc_installation.crt_dir.?);
1614 }
1615 }
1616 if (have_dynamic_linker) {
1617 man.hash.addOptionalBytes(target.dynamic_linker.get());
1618 }
1619 man.hash.addOptionalBytes(self.soname);
1620 man.hash.addOptional(comp.version);
1621 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
1622 man.hash.add(self.base.allow_shlib_undefined);
1623 man.hash.add(self.bind_global_refs_locally);
1624 man.hash.add(self.compress_debug_sections);
1625 man.hash.add(comp.config.any_sanitize_thread);
1626 man.hash.add(comp.config.any_fuzz);
1627 man.hash.addOptionalBytes(comp.sysroot);
1628
1629 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
1630 _ = try man.hit();
1631 digest = man.final();
1632
1633 var prev_digest_buf: [digest.len]u8 = undefined;
1634 const prev_digest: []u8 = std.Build.Cache.readSmallFile(
1635 directory.handle,
1636 id_symlink_basename,
1637 &prev_digest_buf,
1638 ) catch |err| blk: {
1639 log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
1640 // Handle this as a cache miss.
1641 break :blk prev_digest_buf[0..0];
1642 };
1643 if (mem.eql(u8, prev_digest, &digest)) {
1644 log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
1645 // Hot diggity dog! The output binary is already there.
1646 self.base.lock = man.toOwnedLock();
1647 return;
1648 }
1649 log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
1650
1651 // We are about to change the output file to be different, so we invalidate the build hash now.
1652 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
1653 error.FileNotFound => {},
1654 else => |e| return e,
1655 };
1656 }
1657
1658 // Due to a deficiency in LLD, we need to special-case BPF to a simple file
1659 // copy when generating relocatables. Normally, we would expect `lld -r` to work.
1660 // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails
1661 // before even generating the relocatable.
1662 //
1663 // For m68k, we go through this path because LLD doesn't support it yet, but LLVM can
1664 // produce usable object files.
1665 if (output_mode == .Obj and
1666 (comp.config.lto != .none or
1667 target.cpu.arch.isBpf() or
1668 target.cpu.arch == .lanai or
1669 target.cpu.arch == .m68k or
1670 target.cpu.arch.isSPARC() or
1671 target.cpu.arch == .ve or
1672 target.cpu.arch == .xcore))
1673 {
1674 // In this case we must do a simple file copy
1675 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1676 // build-obj. See also the corresponding TODO in linkAsArchive.
1677 const the_object_path = blk: {
1678 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
1679
1680 if (comp.c_object_table.count() != 0)
1681 break :blk comp.c_object_table.keys()[0].status.success.object_path;
1682
1683 if (module_obj_path) |p|
1684 break :blk Path.initCwd(p);
1685
1686 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1687 // regarding eliding redundant object -> object transformations.
1688 return error.NoObjectsToLink;
1689 };
1690 try std.fs.Dir.copyFile(
1691 the_object_path.root_dir.handle,
1692 the_object_path.sub_path,
1693 directory.handle,
1694 self.base.emit.sub_path,
1695 .{},
1696 );
1697 } else {
1698 // Create an LLD command line and invoke it.
1699 var argv = std.ArrayList([]const u8).init(gpa);
1700 defer argv.deinit();
1701 // We will invoke ourselves as a child process to gain access to LLD.
1702 // This is necessary because LLD does not behave properly as a library -
1703 // it calls exit() and does not reset all global data between invocations.
1704 const linker_command = "ld.lld";
1705 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
1706 if (is_obj) {
1707 try argv.append("-r");
1708 }
1709
1710 try argv.append("--error-limit=0");
1711
1712 if (comp.sysroot) |sysroot| {
1713 try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot}));
1714 }
1715
1716 if (target_util.llvmMachineAbi(target)) |mabi| {
1717 try argv.appendSlice(&.{
1718 "-mllvm",
1719 try std.fmt.allocPrint(arena, "-target-abi={s}", .{mabi}),
1720 });
1721 }
1722
1723 try argv.appendSlice(&.{
1724 "-mllvm",
1725 try std.fmt.allocPrint(arena, "-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}),
1726 });
1727
1728 if (comp.config.lto != .none) {
1729 switch (comp.root_mod.optimize_mode) {
1730 .Debug => {},
1731 .ReleaseSmall => try argv.append("--lto-O2"),
1732 .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"),
1733 }
1734 }
1735 switch (comp.root_mod.optimize_mode) {
1736 .Debug => {},
1737 .ReleaseSmall => try argv.append("-O2"),
1738 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
1739 }
1740
1741 if (self.entry_name) |name| {
1742 try argv.appendSlice(&.{ "--entry", name });
1743 }
1744
1745 for (comp.force_undefined_symbols.keys()) |sym| {
1746 try argv.append("-u");
1747 try argv.append(sym);
1748 }
1749
1750 switch (self.hash_style) {
1751 .gnu => try argv.append("--hash-style=gnu"),
1752 .sysv => try argv.append("--hash-style=sysv"),
1753 .both => {}, // this is the default
1754 }
1755
1756 if (output_mode == .Exe) {
1757 try argv.appendSlice(&.{
1758 "-z",
1759 try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}),
1760 });
1761 }
1762
1763 switch (self.base.build_id) {
1764 .none => try argv.append("--build-id=none"),
1765 .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
1766 @tagName(self.base.build_id),
1767 })),
1768 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{
1769 std.fmt.fmtSliceHexLower(hs.toSlice()),
1770 })),
1771 }
1772
1773 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base}));
1774
1775 if (self.linker_script) |linker_script| {
1776 try argv.append("-T");
1777 try argv.append(linker_script);
1778 }
1779
1780 if (self.sort_section) |how| {
1781 const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)});
1782 try argv.append(arg);
1783 }
1784
1785 if (self.base.gc_sections) {
1786 try argv.append("--gc-sections");
1787 }
1788
1789 if (self.base.print_gc_sections) {
1790 try argv.append("--print-gc-sections");
1791 }
1792
1793 if (self.print_icf_sections) {
1794 try argv.append("--print-icf-sections");
1795 }
1796
1797 if (self.print_map) {
1798 try argv.append("--print-map");
1799 }
1800
1801 if (comp.link_eh_frame_hdr) {
1802 try argv.append("--eh-frame-hdr");
1803 }
1804
1805 if (self.emit_relocs) {
1806 try argv.append("--emit-relocs");
1807 }
1808
1809 if (comp.config.rdynamic) {
1810 try argv.append("--export-dynamic");
1811 }
1812
1813 if (comp.config.debug_format == .strip) {
1814 try argv.append("-s");
1815 }
1816
1817 if (self.z_nodelete) {
1818 try argv.append("-z");
1819 try argv.append("nodelete");
1820 }
1821 if (self.z_notext) {
1822 try argv.append("-z");
1823 try argv.append("notext");
1824 }
1825 if (self.z_defs) {
1826 try argv.append("-z");
1827 try argv.append("defs");
1828 }
1829 if (self.z_origin) {
1830 try argv.append("-z");
1831 try argv.append("origin");
1832 }
1833 if (self.z_nocopyreloc) {
1834 try argv.append("-z");
1835 try argv.append("nocopyreloc");
1836 }
1837 if (self.z_now) {
1838 // LLD defaults to -zlazy
1839 try argv.append("-znow");
1840 }
1841 if (!self.z_relro) {
1842 // LLD defaults to -zrelro
1843 try argv.append("-znorelro");
1844 }
1845 if (self.z_common_page_size) |size| {
1846 try argv.append("-z");
1847 try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size}));
1848 }
1849 if (self.z_max_page_size) |size| {
1850 try argv.append("-z");
1851 try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size}));
1852 }
1853
1854 if (getLDMOption(target)) |ldm| {
1855 try argv.append("-m");
1856 try argv.append(ldm);
1857 }
1858
1859 if (link_mode == .static) {
1860 if (target.cpu.arch.isArm()) {
1861 try argv.append("-Bstatic");
1862 } else {
1863 try argv.append("-static");
1864 }
1865 } else if (switch (target.os.tag) {
1866 else => is_dyn_lib,
1867 .haiku => is_exe_or_dyn_lib,
1868 }) {
1869 try argv.append("-shared");
1870 }
1871
1872 if (comp.config.pie and output_mode == .Exe) {
1873 try argv.append("-pie");
1874 }
1875
1876 if (is_exe_or_dyn_lib and target.os.tag == .netbsd) {
1877 // Add options to produce shared objects with only 2 PT_LOAD segments.
1878 // NetBSD expects 2 PT_LOAD segments in a shared object, otherwise
1879 // ld.elf_so fails loading dynamic libraries with "not found" error.
1880 // See https://github.com/ziglang/zig/issues/9109 .
1881 try argv.append("--no-rosegment");
1882 try argv.append("-znorelro");
1883 }
1884
1885 try argv.append("-o");
1886 try argv.append(full_out_path);
1887
1888 // csu prelude
1889 const csu = try comp.getCrtPaths(arena);
1890 if (csu.crt0) |p| try argv.append(try p.toString(arena));
1891 if (csu.crti) |p| try argv.append(try p.toString(arena));
1892 if (csu.crtbegin) |p| try argv.append(try p.toString(arena));
1893
1894 for (self.rpath_table.keys()) |rpath| {
1895 try argv.appendSlice(&.{ "-rpath", rpath });
1896 }
1897
1898 for (self.symbol_wrap_set.keys()) |symbol_name| {
1899 try argv.appendSlice(&.{ "-wrap", symbol_name });
1900 }
1901
1902 if (comp.config.link_libc) {
1903 if (comp.libc_installation) |libc_installation| {
1904 try argv.append("-L");
1905 try argv.append(libc_installation.crt_dir.?);
1906 }
1907 }
1908
1909 if (have_dynamic_linker and
1910 (comp.config.link_libc or comp.root_mod.resolved_target.is_explicit_dynamic_linker))
1911 {
1912 if (target.dynamic_linker.get()) |dynamic_linker| {
1913 try argv.append("-dynamic-linker");
1914 try argv.append(dynamic_linker);
1915 }
1916 }
1917
1918 if (is_dyn_lib) {
1919 if (self.soname) |soname| {
1920 try argv.append("-soname");
1921 try argv.append(soname);
1922 }
1923 if (self.version_script) |version_script| {
1924 try argv.append("-version-script");
1925 try argv.append(version_script);
1926 }
1927 if (self.allow_undefined_version) {
1928 try argv.append("--undefined-version");
1929 } else {
1930 try argv.append("--no-undefined-version");
1931 }
1932 if (self.enable_new_dtags) |enable_new_dtags| {
1933 if (enable_new_dtags) {
1934 try argv.append("--enable-new-dtags");
1935 } else {
1936 try argv.append("--disable-new-dtags");
1937 }
1938 }
1939 }
1940
1941 // Positional arguments to the linker such as object files.
1942 var whole_archive = false;
1943
1944 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
1945 .res => unreachable, // Windows-only
1946 .dso => continue,
1947 .object, .archive => |obj| {
1948 if (obj.must_link and !whole_archive) {
1949 try argv.append("-whole-archive");
1950 whole_archive = true;
1951 } else if (!obj.must_link and whole_archive) {
1952 try argv.append("-no-whole-archive");
1953 whole_archive = false;
1954 }
1955 try argv.append(try obj.path.toString(arena));
1956 },
1957 .dso_exact => |dso_exact| {
1958 assert(dso_exact.name[0] == ':');
1959 try argv.appendSlice(&.{ "-l", dso_exact.name });
1960 },
1961 };
1962
1963 if (whole_archive) {
1964 try argv.append("-no-whole-archive");
1965 whole_archive = false;
1966 }
1967
1968 for (comp.c_object_table.keys()) |key| {
1969 try argv.append(try key.status.success.object_path.toString(arena));
1970 }
1971
1972 if (module_obj_path) |p| {
1973 try argv.append(p);
1974 }
1975
1976 if (comp.tsan_lib) |lib| {
1977 assert(comp.config.any_sanitize_thread);
1978 try argv.append(try lib.full_object_path.toString(arena));
1979 }
1980
1981 if (comp.fuzzer_lib) |lib| {
1982 assert(comp.config.any_fuzz);
1983 try argv.append(try lib.full_object_path.toString(arena));
1984 }
1985
1986 if (ubsan_rt_path) |p| {
1987 try argv.append(try p.toString(arena));
1988 }
1989
1990 // Shared libraries.
1991 if (is_exe_or_dyn_lib) {
1992 // Worst-case, we need an --as-needed argument for every lib, as well
1993 // as one before and one after.
1994 try argv.ensureUnusedCapacity(2 * self.base.comp.link_inputs.len + 2);
1995 argv.appendAssumeCapacity("--as-needed");
1996 var as_needed = true;
1997
1998 for (self.base.comp.link_inputs) |link_input| switch (link_input) {
1999 .res => unreachable, // Windows-only
2000 .object, .archive, .dso_exact => continue,
2001 .dso => |dso| {
2002 const lib_as_needed = !dso.needed;
2003 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
2004 0b00, 0b11 => {},
2005 0b01 => {
2006 argv.appendAssumeCapacity("--no-as-needed");
2007 as_needed = false;
2008 },
2009 0b10 => {
2010 argv.appendAssumeCapacity("--as-needed");
2011 as_needed = true;
2012 },
2013 }
2014
2015 // By this time, we depend on these libs being dynamically linked
2016 // libraries and not static libraries (the check for that needs to be earlier),
2017 // but they could be full paths to .so files, in which case we
2018 // want to avoid prepending "-l".
2019 argv.appendAssumeCapacity(try dso.path.toString(arena));
2020 },
2021 };
2022
2023 if (!as_needed) {
2024 argv.appendAssumeCapacity("--as-needed");
2025 as_needed = true;
2026 }
2027
2028 // libc++ dep
2029 if (comp.config.link_libcpp) {
2030 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
2031 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
2032 }
2033
2034 // libunwind dep
2035 if (comp.config.link_libunwind) {
2036 try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
2037 }
2038
2039 // libc dep
2040 diags.flags.missing_libc = false;
2041 if (comp.config.link_libc) {
2042 if (comp.libc_installation != null) {
2043 const needs_grouping = link_mode == .static;
2044 if (needs_grouping) try argv.append("--start-group");
2045 try argv.appendSlice(target_util.libcFullLinkFlags(target));
2046 if (needs_grouping) try argv.append("--end-group");
2047 } else if (target.isGnuLibC()) {
2048 for (glibc.libs) |lib| {
2049 if (lib.removed_in) |rem_in| {
2050 if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue;
2051 }
2052
2053 const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
2054 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
2055 });
2056 try argv.append(lib_path);
2057 }
2058 try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a"));
2059 } else if (target.isMuslLibC()) {
2060 try argv.append(try comp.crtFileAsString(arena, switch (link_mode) {
2061 .static => "libc.a",
2062 .dynamic => "libc.so",
2063 }));
2064 } else if (target.isFreeBSDLibC()) {
2065 for (freebsd.libs) |lib| {
2066 const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
2067 comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
2068 });
2069 try argv.append(lib_path);
2070 }
2071 } else if (target.isNetBSDLibC()) {
2072 for (netbsd.libs) |lib| {
2073 const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
2074 comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
2075 });
2076 try argv.append(lib_path);
2077 }
2078 } else {
2079 diags.flags.missing_libc = true;
2080 }
2081
2082 if (comp.zigc_static_lib) |zigc| {
2083 try argv.append(try zigc.full_object_path.toString(arena));
2084 }
2085 }
2086 }
2087
2088 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
2089 // to be after the shared libraries, so they are picked up from the shared
2090 // libraries, not libcompiler_rt.
2091 if (compiler_rt_path) |p| {
2092 try argv.append(try p.toString(arena));
2093 }
2094
2095 // crt postlude
2096 if (csu.crtend) |p| try argv.append(try p.toString(arena));
2097 if (csu.crtn) |p| try argv.append(try p.toString(arena));
2098
2099 if (self.base.allow_shlib_undefined) {
2100 try argv.append("--allow-shlib-undefined");
2101 }
2102
2103 switch (self.compress_debug_sections) {
2104 .none => {},
2105 .zlib => try argv.append("--compress-debug-sections=zlib"),
2106 .zstd => try argv.append("--compress-debug-sections=zstd"),
2107 }
2108
2109 if (self.bind_global_refs_locally) {
2110 try argv.append("-Bsymbolic");
2111 }
2112
2113 try link.spawnLld(comp, arena, argv.items);
2114 }
2115
2116 if (!self.base.disable_lld_caching) {
2117 // Update the file with the digest. If it fails we can continue; it only
2118 // means that the next invocation will have an unnecessary cache miss.
2119 std.Build.Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
2120 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
2121 };
2122 // Again failure here only means an unnecessary cache miss.
2123 man.writeManifest() catch |err| {
2124 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
2125 };
2126 // We hang on to this lock so that the output file path can be used without
2127 // other processes clobbering it.
2128 self.base.lock = man.toOwnedLock();
2129 }
2130}
2131
21321443pub fn writeShdrTable(self: *Elf) !void {
21331444 const gpa = self.base.comp.gpa;
21341445 const target_endian = self.getTarget().cpu.arch.endian();
......@@ -4121,85 +3432,6 @@ fn shdrTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
41213432 };
41223433}
41233434
4124fn getLDMOption(target: std.Target) ?[]const u8 {
4125 // This should only return emulations understood by LLD's parseEmulation().
4126 return switch (target.cpu.arch) {
4127 .aarch64 => switch (target.os.tag) {
4128 .linux => "aarch64linux",
4129 else => "aarch64elf",
4130 },
4131 .aarch64_be => switch (target.os.tag) {
4132 .linux => "aarch64linuxb",
4133 else => "aarch64elfb",
4134 },
4135 .amdgcn => "elf64_amdgpu",
4136 .arm, .thumb => switch (target.os.tag) {
4137 .linux => "armelf_linux_eabi",
4138 else => "armelf",
4139 },
4140 .armeb, .thumbeb => switch (target.os.tag) {
4141 .linux => "armelfb_linux_eabi",
4142 else => "armelfb",
4143 },
4144 .hexagon => "hexagonelf",
4145 .loongarch32 => "elf32loongarch",
4146 .loongarch64 => "elf64loongarch",
4147 .mips => switch (target.os.tag) {
4148 .freebsd => "elf32btsmip_fbsd",
4149 else => "elf32btsmip",
4150 },
4151 .mipsel => switch (target.os.tag) {
4152 .freebsd => "elf32ltsmip_fbsd",
4153 else => "elf32ltsmip",
4154 },
4155 .mips64 => switch (target.os.tag) {
4156 .freebsd => switch (target.abi) {
4157 .gnuabin32, .muslabin32 => "elf32btsmipn32_fbsd",
4158 else => "elf64btsmip_fbsd",
4159 },
4160 else => switch (target.abi) {
4161 .gnuabin32, .muslabin32 => "elf32btsmipn32",
4162 else => "elf64btsmip",
4163 },
4164 },
4165 .mips64el => switch (target.os.tag) {
4166 .freebsd => switch (target.abi) {
4167 .gnuabin32, .muslabin32 => "elf32ltsmipn32_fbsd",
4168 else => "elf64ltsmip_fbsd",
4169 },
4170 else => switch (target.abi) {
4171 .gnuabin32, .muslabin32 => "elf32ltsmipn32",
4172 else => "elf64ltsmip",
4173 },
4174 },
4175 .msp430 => "msp430elf",
4176 .powerpc => switch (target.os.tag) {
4177 .freebsd => "elf32ppc_fbsd",
4178 .linux => "elf32ppclinux",
4179 else => "elf32ppc",
4180 },
4181 .powerpcle => switch (target.os.tag) {
4182 .linux => "elf32lppclinux",
4183 else => "elf32lppc",
4184 },
4185 .powerpc64 => "elf64ppc",
4186 .powerpc64le => "elf64lppc",
4187 .riscv32 => "elf32lriscv",
4188 .riscv64 => "elf64lriscv",
4189 .s390x => "elf64_s390",
4190 .sparc64 => "elf64_sparc",
4191 .x86 => switch (target.os.tag) {
4192 .freebsd => "elf_i386_fbsd",
4193 else => "elf_i386",
4194 },
4195 .x86_64 => switch (target.abi) {
4196 .gnux32, .muslx32 => "elf32_x86_64",
4197 else => "elf_x86_64",
4198 },
4199 else => null,
4200 };
4201}
4202
42033435pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
42043436 return actual_size +| (actual_size / ideal_factor);
42053437}
......@@ -5284,10 +4516,7 @@ const codegen = @import("../codegen.zig");
52844516const dev = @import("../dev.zig");
52854517const eh_frame = @import("Elf/eh_frame.zig");
52864518const gc = @import("Elf/gc.zig");
5287const glibc = @import("../libs/glibc.zig");
52884519const musl = @import("../libs/musl.zig");
5289const freebsd = @import("../libs/freebsd.zig");
5290const netbsd = @import("../libs/netbsd.zig");
52914520const link = @import("../link.zig");
52924521const relocatable = @import("Elf/relocatable.zig");
52934522const relocation = @import("Elf/relocation.zig");
src/link/Elf/ZigObject.zig+4-4
......@@ -310,7 +310,7 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
310310 if (self.dwarf) |*dwarf| {
311311 const pt: Zcu.PerThread = .activate(elf_file.base.comp.zcu.?, tid);
312312 defer pt.deactivate();
313 try dwarf.flushZcu(pt);
313 try dwarf.flush(pt);
314314
315315 const gpa = elf_file.base.comp.gpa;
316316 const cpu_arch = elf_file.getTarget().cpu.arch;
......@@ -481,7 +481,7 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
481481 self.debug_str_section_dirty = false;
482482 }
483483
484 // The point of flushZcu() is to commit changes, so in theory, nothing should
484 // The point of flush() is to commit changes, so in theory, nothing should
485485 // be dirty after this. However, it is possible for some things to remain
486486 // dirty because they fail to be written in the event of compile errors,
487487 // such as debug_line_header_dirty and debug_info_header_dirty.
......@@ -661,7 +661,7 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
661661 if (shdr.sh_type == elf.SHT_NOBITS) continue;
662662 if (atom_ptr.scanRelocsRequiresCode(elf_file)) {
663663 // TODO ideally we don't have to fetch the code here.
664 // Perhaps it would make sense to save the code until flushZcu where we
664 // Perhaps it would make sense to save the code until flush where we
665665 // would free all of generated code?
666666 const code = try self.codeAlloc(elf_file, atom_index);
667667 defer gpa.free(code);
......@@ -1075,7 +1075,7 @@ pub fn getOrCreateMetadataForLazySymbol(
10751075 }
10761076 state_ptr.* = .pending_flush;
10771077 const symbol_index = symbol_index_ptr.*;
1078 // anyerror needs to be deferred until flushZcu
1078 // anyerror needs to be deferred until flush
10791079 if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbol(elf_file, pt, lazy_sym, symbol_index);
10801080 return symbol_index;
10811081}
src/link/Goff.zig-5
......@@ -46,7 +46,6 @@ pub fn createEmpty(
4646 .stack_size = options.stack_size orelse 0,
4747 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
4848 .file = null,
49 .disable_lld_caching = options.disable_lld_caching,
5049 .build_id = options.build_id,
5150 },
5251 };
......@@ -105,10 +104,6 @@ pub fn updateExports(
105104}
106105
107106pub fn flush(self: *Goff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
108 return self.flushZcu(arena, tid, prog_node);
109}
110
111pub fn flushZcu(self: *Goff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
112107 _ = self;
113108 _ = arena;
114109 _ = tid;
src/link/Lld.zig created+2148
......@@ -0,0 +1,2148 @@
1base: link.File,
2disable_caching: bool,
3ofmt: union(enum) {
4 elf: Elf,
5 coff: Coff,
6 wasm: Wasm,
7},
8
9const Coff = struct {
10 image_base: u64,
11 entry: link.File.OpenOptions.Entry,
12 pdb_out_path: ?[]const u8,
13 repro: bool,
14 tsaware: bool,
15 nxcompat: bool,
16 dynamicbase: bool,
17 /// TODO this and minor_subsystem_version should be combined into one property and left as
18 /// default or populated together. They should not be separate fields.
19 major_subsystem_version: u16,
20 minor_subsystem_version: u16,
21 lib_directories: []const Cache.Directory,
22 module_definition_file: ?[]const u8,
23 subsystem: ?std.Target.SubSystem,
24 /// These flags are populated by `codegen.llvm.updateExports` to allow us to guess the subsystem.
25 lld_export_flags: struct {
26 c_main: bool,
27 winmain: bool,
28 wwinmain: bool,
29 winmain_crt_startup: bool,
30 wwinmain_crt_startup: bool,
31 dllmain_crt_startup: bool,
32 },
33 fn init(comp: *Compilation, options: link.File.OpenOptions) !Coff {
34 const target = comp.root_mod.resolved_target.result;
35 const output_mode = comp.config.output_mode;
36 return .{
37 .image_base = options.image_base orelse switch (output_mode) {
38 .Exe => switch (target.cpu.arch) {
39 .aarch64, .x86_64 => 0x140000000,
40 .thumb, .x86 => 0x400000,
41 else => unreachable,
42 },
43 .Lib => switch (target.cpu.arch) {
44 .aarch64, .x86_64 => 0x180000000,
45 .thumb, .x86 => 0x10000000,
46 else => unreachable,
47 },
48 .Obj => 0,
49 },
50 .entry = options.entry,
51 .pdb_out_path = options.pdb_out_path,
52 .repro = options.repro,
53 .tsaware = options.tsaware,
54 .nxcompat = options.nxcompat,
55 .dynamicbase = options.dynamicbase,
56 .major_subsystem_version = options.major_subsystem_version orelse 6,
57 .minor_subsystem_version = options.minor_subsystem_version orelse 0,
58 .lib_directories = options.lib_directories,
59 .module_definition_file = options.module_definition_file,
60 // Subsystem depends on the set of public symbol names from linked objects.
61 // See LinkerDriver::inferSubsystem from the LLD project for the flow chart.
62 .subsystem = options.subsystem,
63 // These flags are initially all `false`; the LLVM backend populates them when it learns about exports.
64 .lld_export_flags = .{
65 .c_main = false,
66 .winmain = false,
67 .wwinmain = false,
68 .winmain_crt_startup = false,
69 .wwinmain_crt_startup = false,
70 .dllmain_crt_startup = false,
71 },
72 };
73 }
74};
75pub const Elf = struct {
76 entry_name: ?[]const u8,
77 hash_style: HashStyle,
78 image_base: u64,
79 linker_script: ?[]const u8,
80 version_script: ?[]const u8,
81 sort_section: ?SortSection,
82 print_icf_sections: bool,
83 print_map: bool,
84 emit_relocs: bool,
85 z_nodelete: bool,
86 z_notext: bool,
87 z_defs: bool,
88 z_origin: bool,
89 z_nocopyreloc: bool,
90 z_now: bool,
91 z_relro: bool,
92 z_common_page_size: ?u64,
93 z_max_page_size: ?u64,
94 rpath_list: []const []const u8,
95 symbol_wrap_set: []const []const u8,
96 soname: ?[]const u8,
97 allow_undefined_version: bool,
98 enable_new_dtags: ?bool,
99 compress_debug_sections: CompressDebugSections,
100 bind_global_refs_locally: bool,
101 pub const HashStyle = enum { sysv, gnu, both };
102 pub const SortSection = enum { name, alignment };
103 pub const CompressDebugSections = enum { none, zlib, zstd };
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 const is_native_os = comp.root_mod.resolved_target.is_native_os;
210
211 const obj_file_ext: []const u8 = switch (target.ofmt) {
212 .coff => "obj",
213 .elf, .wasm => "o",
214 else => unreachable,
215 };
216 const gc_sections: bool = options.gc_sections orelse switch (target.ofmt) {
217 .coff => optimize_mode != .Debug,
218 .elf => optimize_mode != .Debug and output_mode != .Obj,
219 .wasm => output_mode != .Obj,
220 else => unreachable,
221 };
222 const stack_size: u64 = options.stack_size orelse default: {
223 if (target.ofmt == .wasm and target.os.tag == .freestanding)
224 break :default 1 * 1024 * 1024; // 1 MiB
225 break :default 16 * 1024 * 1024; // 16 MiB
226 };
227
228 const lld = try arena.create(Lld);
229 lld.* = .{
230 .base = .{
231 .tag = .lld,
232 .comp = comp,
233 .emit = emit,
234 .zcu_object_sub_path = try allocPrint(arena, "{s}.{s}", .{ emit.sub_path, obj_file_ext }),
235 .gc_sections = gc_sections,
236 .print_gc_sections = options.print_gc_sections,
237 .stack_size = stack_size,
238 .allow_shlib_undefined = options.allow_shlib_undefined orelse !is_native_os,
239 .file = null,
240 .build_id = options.build_id,
241 },
242 .disable_caching = options.disable_lld_caching,
243 .ofmt = switch (target.ofmt) {
244 .coff => .{ .coff = try .init(comp, options) },
245 .elf => .{ .elf = try .init(comp, options) },
246 .wasm => .{ .wasm = try .init(comp, options) },
247 else => unreachable,
248 },
249 };
250 return lld;
251}
252pub fn deinit(lld: *Lld) void {
253 _ = lld;
254}
255pub fn flush(
256 lld: *Lld,
257 arena: Allocator,
258 tid: Zcu.PerThread.Id,
259 prog_node: std.Progress.Node,
260) link.File.FlushError!void {
261 dev.check(.lld_linker);
262 _ = tid;
263
264 const tracy = trace(@src());
265 defer tracy.end();
266
267 const sub_prog_node = prog_node.start("LLD Link", 0);
268 defer sub_prog_node.end();
269
270 const comp = lld.base.comp;
271 const result = if (comp.config.output_mode == .Lib and comp.config.link_mode == .static) r: {
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.LinkFailure => |e| return e,
280 else => |e| return lld.base.comp.link_diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
281 };
282}
283
284fn linkAsArchive(lld: *Lld, arena: Allocator) !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.dupeZ(u8, full_out_path);
290 const opt_zcu = comp.zcu;
291
292 // If there is no Zig code to compile, then we should skip flushing the output file
293 // because it will not be part of the linker line anyway.
294 const zcu_obj_path: ?[]const u8 = if (opt_zcu != null) blk: {
295 const dirname = fs.path.dirname(full_out_path_z) orelse ".";
296 break :blk try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? });
297 } else null;
298
299 log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"});
300
301 const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj)
302 comp.compiler_rt_obj.?.full_object_path
303 else
304 null;
305
306 const ubsan_rt_path: ?Cache.Path = if (comp.ubsan_rt_strat == .obj)
307 comp.ubsan_rt_obj.?.full_object_path
308 else
309 null;
310
311 // This function follows the same pattern as link.Elf.linkWithLLD so if you want some
312 // insight as to what's going on here you can read that function body which is more
313 // well-commented.
314
315 const id_symlink_basename = "llvm-ar.id";
316
317 var man: Cache.Manifest = undefined;
318 defer if (!lld.disable_caching) man.deinit();
319
320 const link_inputs = comp.link_inputs;
321
322 var digest: [Cache.hex_digest_len]u8 = undefined;
323
324 if (!lld.disable_caching) {
325 man = comp.cache_parent.obtain();
326
327 // We are about to obtain this lock, so here we give other processes a chance first.
328 base.releaseLock();
329
330 try link.hashInputs(&man, link_inputs);
331
332 for (comp.c_object_table.keys()) |key| {
333 _ = try man.addFilePath(key.status.success.object_path, null);
334 }
335 for (comp.win32_resource_table.keys()) |key| {
336 _ = try man.addFile(key.status.success.res_path, null);
337 }
338 try man.addOptionalFile(zcu_obj_path);
339 try man.addOptionalFilePath(compiler_rt_path);
340 try man.addOptionalFilePath(ubsan_rt_path);
341
342 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
343 _ = try man.hit();
344 digest = man.final();
345
346 var prev_digest_buf: [digest.len]u8 = undefined;
347 const prev_digest: []u8 = Cache.readSmallFile(
348 directory.handle,
349 id_symlink_basename,
350 &prev_digest_buf,
351 ) catch |err| b: {
352 log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
353 break :b prev_digest_buf[0..0];
354 };
355 if (mem.eql(u8, prev_digest, &digest)) {
356 log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
357 base.lock = man.toOwnedLock();
358 return;
359 }
360
361 // We are about to change the output file to be different, so we invalidate the build hash now.
362 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
363 error.FileNotFound => {},
364 else => |e| return e,
365 };
366 }
367
368 var object_files: std.ArrayListUnmanaged([*:0]const u8) = .empty;
369
370 try object_files.ensureUnusedCapacity(arena, link_inputs.len);
371 for (link_inputs) |input| {
372 object_files.appendAssumeCapacity(try input.path().?.toStringZ(arena));
373 }
374
375 try object_files.ensureUnusedCapacity(arena, comp.c_object_table.count() +
376 comp.win32_resource_table.count() + 2);
377
378 for (comp.c_object_table.keys()) |key| {
379 object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena));
380 }
381 for (comp.win32_resource_table.keys()) |key| {
382 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));
383 }
384 if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
385 if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
386 if (ubsan_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
387
388 if (comp.verbose_link) {
389 std.debug.print("ar rcs {s}", .{full_out_path_z});
390 for (object_files.items) |arg| {
391 std.debug.print(" {s}", .{arg});
392 }
393 std.debug.print("\n", .{});
394 }
395
396 const llvm_bindings = @import("../codegen/llvm/bindings.zig");
397 const llvm = @import("../codegen/llvm.zig");
398 const target = comp.root_mod.resolved_target.result;
399 llvm.initializeLLVMTarget(target.cpu.arch);
400 const bad = llvm_bindings.WriteArchive(
401 full_out_path_z,
402 object_files.items.ptr,
403 object_files.items.len,
404 switch (target.os.tag) {
405 .aix => .AIXBIG,
406 .windows => .COFF,
407 else => if (target.os.tag.isDarwin()) .DARWIN else .GNU,
408 },
409 );
410 if (bad) return error.UnableToWriteArchive;
411
412 if (!lld.disable_caching) {
413 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
414 log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)});
415 };
416
417 if (man.have_exclusive_lock) {
418 man.writeManifest() catch |err| {
419 log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)});
420 };
421 }
422
423 base.lock = man.toOwnedLock();
424 }
425}
426
427fn coffLink(lld: *Lld, arena: Allocator) !void {
428 const comp = lld.base.comp;
429 const gpa = comp.gpa;
430 const base = &lld.base;
431 const coff = &lld.ofmt.coff;
432
433 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
434 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
435
436 // If there is no Zig code to compile, then we should skip flushing the output file because it
437 // will not be part of the linker line anyway.
438 const module_obj_path: ?[]const u8 = if (comp.zcu != null) p: {
439 if (fs.path.dirname(full_out_path)) |dirname| {
440 break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? });
441 } else {
442 break :p base.zcu_object_sub_path.?;
443 }
444 } else null;
445
446 const is_lib = comp.config.output_mode == .Lib;
447 const is_dyn_lib = comp.config.link_mode == .dynamic and is_lib;
448 const is_exe_or_dyn_lib = is_dyn_lib or comp.config.output_mode == .Exe;
449 const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib;
450 const target = comp.root_mod.resolved_target.result;
451 const optimize_mode = comp.root_mod.optimize_mode;
452 const entry_name: ?[]const u8 = switch (coff.entry) {
453 // This logic isn't quite right for disabled or enabled. No point in fixing it
454 // when the goal is to eliminate dependency on LLD anyway.
455 // https://github.com/ziglang/zig/issues/17751
456 .disabled, .default, .enabled => null,
457 .named => |name| name,
458 };
459
460 // See link/Elf.zig for comments on how this mechanism works.
461 const id_symlink_basename = "lld.id";
462
463 var man: Cache.Manifest = undefined;
464 defer if (!lld.disable_caching) man.deinit();
465
466 var digest: [Cache.hex_digest_len]u8 = undefined;
467
468 if (!lld.disable_caching) {
469 man = comp.cache_parent.obtain();
470 base.releaseLock();
471
472 comptime assert(Compilation.link_hash_implementation_version == 14);
473
474 try link.hashInputs(&man, comp.link_inputs);
475 for (comp.c_object_table.keys()) |key| {
476 _ = try man.addFilePath(key.status.success.object_path, null);
477 }
478 for (comp.win32_resource_table.keys()) |key| {
479 _ = try man.addFile(key.status.success.res_path, null);
480 }
481 try man.addOptionalFile(module_obj_path);
482 man.hash.addOptionalBytes(entry_name);
483 man.hash.add(base.stack_size);
484 man.hash.add(coff.image_base);
485 man.hash.add(base.build_id);
486 {
487 // TODO remove this, libraries must instead be resolved by the frontend.
488 for (coff.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path);
489 }
490 man.hash.add(comp.skip_linker_dependencies);
491 if (comp.config.link_libc) {
492 man.hash.add(comp.libc_installation != null);
493 if (comp.libc_installation) |libc_installation| {
494 man.hash.addBytes(libc_installation.crt_dir.?);
495 if (target.abi == .msvc or target.abi == .itanium) {
496 man.hash.addBytes(libc_installation.msvc_lib_dir.?);
497 man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
498 }
499 }
500 }
501 man.hash.addListOfBytes(comp.windows_libs.keys());
502 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
503 man.hash.addOptional(coff.subsystem);
504 man.hash.add(comp.config.is_test);
505 man.hash.add(coff.tsaware);
506 man.hash.add(coff.nxcompat);
507 man.hash.add(coff.dynamicbase);
508 man.hash.add(base.allow_shlib_undefined);
509 // strip does not need to go into the linker hash because it is part of the hash namespace
510 man.hash.add(coff.major_subsystem_version);
511 man.hash.add(coff.minor_subsystem_version);
512 man.hash.add(coff.repro);
513 man.hash.addOptional(comp.version);
514 try man.addOptionalFile(coff.module_definition_file);
515
516 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
517 _ = try man.hit();
518 digest = man.final();
519 var prev_digest_buf: [digest.len]u8 = undefined;
520 const prev_digest: []u8 = Cache.readSmallFile(
521 directory.handle,
522 id_symlink_basename,
523 &prev_digest_buf,
524 ) catch |err| blk: {
525 log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
526 // Handle this as a cache miss.
527 break :blk prev_digest_buf[0..0];
528 };
529 if (mem.eql(u8, prev_digest, &digest)) {
530 log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
531 // Hot diggity dog! The output binary is already there.
532 base.lock = man.toOwnedLock();
533 return;
534 }
535 log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
536
537 // We are about to change the output file to be different, so we invalidate the build hash now.
538 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
539 error.FileNotFound => {},
540 else => |e| return e,
541 };
542 }
543
544 if (comp.config.output_mode == .Obj) {
545 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
546 // here. TODO: think carefully about how we can avoid this redundant operation when doing
547 // build-obj. See also the corresponding TODO in linkAsArchive.
548 const the_object_path = blk: {
549 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
550
551 if (comp.c_object_table.count() != 0)
552 break :blk comp.c_object_table.keys()[0].status.success.object_path;
553
554 if (module_obj_path) |p|
555 break :blk Cache.Path.initCwd(p);
556
557 // TODO I think this is unreachable. Audit this situation when solving the above TODO
558 // regarding eliding redundant object -> object transformations.
559 return error.NoObjectsToLink;
560 };
561 try std.fs.Dir.copyFile(
562 the_object_path.root_dir.handle,
563 the_object_path.sub_path,
564 directory.handle,
565 base.emit.sub_path,
566 .{},
567 );
568 } else {
569 // Create an LLD command line and invoke it.
570 var argv = std.ArrayList([]const u8).init(gpa);
571 defer argv.deinit();
572 // We will invoke ourselves as a child process to gain access to LLD.
573 // This is necessary because LLD does not behave properly as a library -
574 // it calls exit() and does not reset all global data between invocations.
575 const linker_command = "lld-link";
576 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
577
578 if (target.isMinGW()) {
579 try argv.append("-lldmingw");
580 }
581
582 try argv.append("-ERRORLIMIT:0");
583 try argv.append("-NOLOGO");
584 if (comp.config.debug_format != .strip) {
585 try argv.append("-DEBUG");
586
587 const out_ext = std.fs.path.extension(full_out_path);
588 const out_pdb = coff.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{
589 full_out_path[0 .. full_out_path.len - out_ext.len],
590 });
591 const out_pdb_basename = std.fs.path.basename(out_pdb);
592
593 try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb}));
594 try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb_basename}));
595 }
596 if (comp.version) |version| {
597 try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor }));
598 }
599
600 if (target_util.llvmMachineAbi(target)) |mabi| {
601 try argv.append(try allocPrint(arena, "-MLLVM:-target-abi={s}", .{mabi}));
602 }
603
604 try argv.append(try allocPrint(arena, "-MLLVM:-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}));
605
606 if (comp.config.lto != .none) {
607 switch (optimize_mode) {
608 .Debug => {},
609 .ReleaseSmall => try argv.append("-OPT:lldlto=2"),
610 .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"),
611 }
612 }
613 if (comp.config.output_mode == .Exe) {
614 try argv.append(try allocPrint(arena, "-STACK:{d}", .{base.stack_size}));
615 }
616 try argv.append(try allocPrint(arena, "-BASE:{d}", .{coff.image_base}));
617
618 switch (base.build_id) {
619 .none => try argv.append("-BUILD-ID:NO"),
620 .fast => try argv.append("-BUILD-ID"),
621 .uuid, .sha1, .md5, .hexstring => {},
622 }
623
624 if (target.cpu.arch == .x86) {
625 try argv.append("-MACHINE:X86");
626 } else if (target.cpu.arch == .x86_64) {
627 try argv.append("-MACHINE:X64");
628 } else if (target.cpu.arch == .thumb) {
629 try argv.append("-MACHINE:ARM");
630 } else if (target.cpu.arch == .aarch64) {
631 try argv.append("-MACHINE:ARM64");
632 }
633
634 for (comp.force_undefined_symbols.keys()) |symbol| {
635 try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol}));
636 }
637
638 if (is_dyn_lib) {
639 try argv.append("-DLL");
640 }
641
642 if (entry_name) |name| {
643 try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{name}));
644 }
645
646 if (coff.repro) {
647 try argv.append("-BREPRO");
648 }
649
650 if (coff.tsaware) {
651 try argv.append("-tsaware");
652 }
653 if (coff.nxcompat) {
654 try argv.append("-nxcompat");
655 }
656 if (!coff.dynamicbase) {
657 try argv.append("-dynamicbase:NO");
658 }
659 if (base.allow_shlib_undefined) {
660 try argv.append("-FORCE:UNRESOLVED");
661 }
662
663 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
664
665 if (comp.implib_emit) |emit| {
666 const implib_out_path = try emit.root_dir.join(arena, &[_][]const u8{emit.sub_path});
667 try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));
668 }
669
670 if (comp.config.link_libc) {
671 if (comp.libc_installation) |libc_installation| {
672 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
673
674 if (target.abi == .msvc or target.abi == .itanium) {
675 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
676 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
677 }
678 }
679 }
680
681 for (coff.lib_directories) |lib_directory| {
682 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_directory.path orelse "."}));
683 }
684
685 try argv.ensureUnusedCapacity(comp.link_inputs.len);
686 for (comp.link_inputs) |link_input| switch (link_input) {
687 .dso_exact => unreachable, // not applicable to PE/COFF
688 inline .dso, .res => |x| {
689 argv.appendAssumeCapacity(try x.path.toString(arena));
690 },
691 .object, .archive => |obj| {
692 if (obj.must_link) {
693 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Cache.Path, obj.path)}));
694 } else {
695 argv.appendAssumeCapacity(try obj.path.toString(arena));
696 }
697 },
698 };
699
700 for (comp.c_object_table.keys()) |key| {
701 try argv.append(try key.status.success.object_path.toString(arena));
702 }
703
704 for (comp.win32_resource_table.keys()) |key| {
705 try argv.append(key.status.success.res_path);
706 }
707
708 if (module_obj_path) |p| {
709 try argv.append(p);
710 }
711
712 if (coff.module_definition_file) |def| {
713 try argv.append(try allocPrint(arena, "-DEF:{s}", .{def}));
714 }
715
716 const resolved_subsystem: ?std.Target.SubSystem = blk: {
717 if (coff.subsystem) |explicit| break :blk explicit;
718 switch (target.os.tag) {
719 .windows => {
720 if (comp.zcu != null) {
721 if (coff.lld_export_flags.dllmain_crt_startup or is_dyn_lib)
722 break :blk null;
723 if (coff.lld_export_flags.c_main or comp.config.is_test or
724 coff.lld_export_flags.winmain_crt_startup or
725 coff.lld_export_flags.wwinmain_crt_startup)
726 {
727 break :blk .Console;
728 }
729 if (coff.lld_export_flags.winmain or coff.lld_export_flags.wwinmain)
730 break :blk .Windows;
731 }
732 },
733 .uefi => break :blk .EfiApplication,
734 else => {},
735 }
736 break :blk null;
737 };
738
739 const Mode = enum { uefi, win32 };
740 const mode: Mode = mode: {
741 if (resolved_subsystem) |subsystem| {
742 const subsystem_suffix = try allocPrint(arena, ",{d}.{d}", .{
743 coff.major_subsystem_version, coff.minor_subsystem_version,
744 });
745
746 switch (subsystem) {
747 .Console => {
748 try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{
749 subsystem_suffix,
750 }));
751 break :mode .win32;
752 },
753 .EfiApplication => {
754 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{
755 subsystem_suffix,
756 }));
757 break :mode .uefi;
758 },
759 .EfiBootServiceDriver => {
760 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{
761 subsystem_suffix,
762 }));
763 break :mode .uefi;
764 },
765 .EfiRom => {
766 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{
767 subsystem_suffix,
768 }));
769 break :mode .uefi;
770 },
771 .EfiRuntimeDriver => {
772 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{
773 subsystem_suffix,
774 }));
775 break :mode .uefi;
776 },
777 .Native => {
778 try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{
779 subsystem_suffix,
780 }));
781 break :mode .win32;
782 },
783 .Posix => {
784 try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{
785 subsystem_suffix,
786 }));
787 break :mode .win32;
788 },
789 .Windows => {
790 try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{
791 subsystem_suffix,
792 }));
793 break :mode .win32;
794 },
795 }
796 } else if (target.os.tag == .uefi) {
797 break :mode .uefi;
798 } else {
799 break :mode .win32;
800 }
801 };
802
803 switch (mode) {
804 .uefi => try argv.appendSlice(&[_][]const u8{
805 "-BASE:0",
806 "-ENTRY:EfiMain",
807 "-OPT:REF",
808 "-SAFESEH:NO",
809 "-MERGE:.rdata=.data",
810 "-NODEFAULTLIB",
811 "-SECTION:.xdata,D",
812 }),
813 .win32 => {
814 if (link_in_crt) {
815 if (target.abi.isGnu()) {
816 if (target.cpu.arch == .x86) {
817 try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
818 } else {
819 try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
820 }
821
822 if (is_dyn_lib) {
823 try argv.append(try comp.crtFileAsString(arena, "dllcrt2.obj"));
824 if (target.cpu.arch == .x86) {
825 try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12");
826 } else {
827 try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup");
828 }
829 } else {
830 try argv.append(try comp.crtFileAsString(arena, "crt2.obj"));
831 }
832
833 try argv.append(try comp.crtFileAsString(arena, "libmingw32.lib"));
834 } else {
835 try argv.append(switch (comp.config.link_mode) {
836 .static => "libcmt.lib",
837 .dynamic => "msvcrt.lib",
838 });
839
840 const lib_str = switch (comp.config.link_mode) {
841 .static => "lib",
842 .dynamic => "",
843 };
844 try argv.append(try allocPrint(arena, "{s}vcruntime.lib", .{lib_str}));
845 try argv.append(try allocPrint(arena, "{s}ucrt.lib", .{lib_str}));
846
847 //Visual C++ 2015 Conformance Changes
848 //https://msdn.microsoft.com/en-us/library/bb531344.aspx
849 try argv.append("legacy_stdio_definitions.lib");
850
851 // msvcrt depends on kernel32 and ntdll
852 try argv.append("kernel32.lib");
853 try argv.append("ntdll.lib");
854 }
855 } else {
856 try argv.append("-NODEFAULTLIB");
857 if (!is_lib and entry_name == null) {
858 if (comp.zcu != null) {
859 if (coff.lld_export_flags.winmain_crt_startup) {
860 try argv.append("-ENTRY:WinMainCRTStartup");
861 } else {
862 try argv.append("-ENTRY:wWinMainCRTStartup");
863 }
864 } else {
865 try argv.append("-ENTRY:wWinMainCRTStartup");
866 }
867 }
868 }
869 },
870 }
871
872 if (comp.config.link_libc and link_in_crt) {
873 if (comp.zigc_static_lib) |zigc| {
874 try argv.append(try zigc.full_object_path.toString(arena));
875 }
876 }
877
878 // libc++ dep
879 if (comp.config.link_libcpp) {
880 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
881 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
882 }
883
884 // libunwind dep
885 if (comp.config.link_libunwind) {
886 try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
887 }
888
889 if (comp.config.any_fuzz) {
890 try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
891 }
892
893 const ubsan_rt_path: ?Cache.Path = blk: {
894 if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path;
895 if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path;
896 break :blk null;
897 };
898 if (ubsan_rt_path) |path| {
899 try argv.append(try path.toString(arena));
900 }
901
902 if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {
903 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
904 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
905 if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena));
906 if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
907 }
908
909 try argv.ensureUnusedCapacity(comp.windows_libs.count());
910 for (comp.windows_libs.keys()) |key| {
911 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
912 if (comp.crt_files.get(lib_basename)) |crt_file| {
913 argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena));
914 continue;
915 }
916 if (try findLib(arena, lib_basename, coff.lib_directories)) |full_path| {
917 argv.appendAssumeCapacity(full_path);
918 continue;
919 }
920 if (target.abi.isGnu()) {
921 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
922 if (try findLib(arena, fallback_name, coff.lib_directories)) |full_path| {
923 argv.appendAssumeCapacity(full_path);
924 continue;
925 }
926 }
927 if (target.abi == .msvc or target.abi == .itanium) {
928 argv.appendAssumeCapacity(lib_basename);
929 continue;
930 }
931
932 log.err("DLL import library for -l{s} not found", .{key});
933 return error.DllImportLibraryNotFound;
934 }
935
936 try spawnLld(comp, arena, argv.items);
937 }
938
939 if (!lld.disable_caching) {
940 // Update the file with the digest. If it fails we can continue; it only
941 // means that the next invocation will have an unnecessary cache miss.
942 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
943 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
944 };
945 // Again failure here only means an unnecessary cache miss.
946 man.writeManifest() catch |err| {
947 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
948 };
949 // We hang on to this lock so that the output file path can be used without
950 // other processes clobbering it.
951 base.lock = man.toOwnedLock();
952 }
953}
954fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Cache.Directory) !?[]const u8 {
955 for (lib_directories) |lib_directory| {
956 lib_directory.handle.access(name, .{}) catch |err| switch (err) {
957 error.FileNotFound => continue,
958 else => |e| return e,
959 };
960 return try lib_directory.join(arena, &.{name});
961 }
962 return null;
963}
964
965fn elfLink(lld: *Lld, arena: Allocator) !void {
966 const comp = lld.base.comp;
967 const gpa = comp.gpa;
968 const diags = &comp.link_diags;
969 const base = &lld.base;
970 const elf = &lld.ofmt.elf;
971
972 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
973 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
974
975 // If there is no Zig code to compile, then we should skip flushing the output file because it
976 // will not be part of the linker line anyway.
977 const module_obj_path: ?[]const u8 = if (comp.zcu != null) p: {
978 if (fs.path.dirname(full_out_path)) |dirname| {
979 break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? });
980 } else {
981 break :p base.zcu_object_sub_path.?;
982 }
983 } else null;
984
985 const output_mode = comp.config.output_mode;
986 const is_obj = output_mode == .Obj;
987 const is_lib = output_mode == .Lib;
988 const link_mode = comp.config.link_mode;
989 const is_dyn_lib = link_mode == .dynamic and is_lib;
990 const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe;
991 const have_dynamic_linker = link_mode == .dynamic and is_exe_or_dyn_lib;
992 const target = comp.root_mod.resolved_target.result;
993 const compiler_rt_path: ?Cache.Path = blk: {
994 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
995 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
996 break :blk null;
997 };
998 const ubsan_rt_path: ?Cache.Path = blk: {
999 if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path;
1000 if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path;
1001 break :blk null;
1002 };
1003
1004 // Here we want to determine whether we can save time by not invoking LLD when the
1005 // output is unchanged. None of the linker options or the object files that are being
1006 // linked are in the hash that namespaces the directory we are outputting to. Therefore,
1007 // we must hash those now, and the resulting digest will form the "id" of the linking
1008 // job we are about to perform.
1009 // After a successful link, we store the id in the metadata of a symlink named "lld.id" in
1010 // the artifact directory. So, now, we check if this symlink exists, and if it matches
1011 // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
1012 const id_symlink_basename = "lld.id";
1013
1014 var man: std.Build.Cache.Manifest = undefined;
1015 defer if (!lld.disable_caching) man.deinit();
1016
1017 var digest: [std.Build.Cache.hex_digest_len]u8 = undefined;
1018
1019 if (!lld.disable_caching) {
1020 man = comp.cache_parent.obtain();
1021
1022 // We are about to obtain this lock, so here we give other processes a chance first.
1023 base.releaseLock();
1024
1025 comptime assert(Compilation.link_hash_implementation_version == 14);
1026
1027 try man.addOptionalFile(elf.linker_script);
1028 try man.addOptionalFile(elf.version_script);
1029 man.hash.add(elf.allow_undefined_version);
1030 man.hash.addOptional(elf.enable_new_dtags);
1031 try link.hashInputs(&man, comp.link_inputs);
1032 for (comp.c_object_table.keys()) |key| {
1033 _ = try man.addFilePath(key.status.success.object_path, null);
1034 }
1035 try man.addOptionalFile(module_obj_path);
1036 try man.addOptionalFilePath(compiler_rt_path);
1037 try man.addOptionalFilePath(ubsan_rt_path);
1038 try man.addOptionalFilePath(if (comp.tsan_lib) |l| l.full_object_path else null);
1039 try man.addOptionalFilePath(if (comp.fuzzer_lib) |l| l.full_object_path else null);
1040
1041 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
1042 // installation sources because they are always a product of the compiler version + target information.
1043 man.hash.addOptionalBytes(elf.entry_name);
1044 man.hash.add(elf.image_base);
1045 man.hash.add(base.gc_sections);
1046 man.hash.addOptional(elf.sort_section);
1047 man.hash.add(comp.link_eh_frame_hdr);
1048 man.hash.add(elf.emit_relocs);
1049 man.hash.add(comp.config.rdynamic);
1050 man.hash.addListOfBytes(elf.rpath_list);
1051 if (output_mode == .Exe) {
1052 man.hash.add(base.stack_size);
1053 }
1054 man.hash.add(base.build_id);
1055 man.hash.addListOfBytes(elf.symbol_wrap_set);
1056 man.hash.add(comp.skip_linker_dependencies);
1057 man.hash.add(elf.z_nodelete);
1058 man.hash.add(elf.z_notext);
1059 man.hash.add(elf.z_defs);
1060 man.hash.add(elf.z_origin);
1061 man.hash.add(elf.z_nocopyreloc);
1062 man.hash.add(elf.z_now);
1063 man.hash.add(elf.z_relro);
1064 man.hash.add(elf.z_common_page_size orelse 0);
1065 man.hash.add(elf.z_max_page_size orelse 0);
1066 man.hash.add(elf.hash_style);
1067 // strip does not need to go into the linker hash because it is part of the hash namespace
1068 if (comp.config.link_libc) {
1069 man.hash.add(comp.libc_installation != null);
1070 if (comp.libc_installation) |libc_installation| {
1071 man.hash.addBytes(libc_installation.crt_dir.?);
1072 }
1073 }
1074 if (have_dynamic_linker) {
1075 man.hash.addOptionalBytes(target.dynamic_linker.get());
1076 }
1077 man.hash.addOptionalBytes(elf.soname);
1078 man.hash.addOptional(comp.version);
1079 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
1080 man.hash.add(base.allow_shlib_undefined);
1081 man.hash.add(elf.bind_global_refs_locally);
1082 man.hash.add(elf.compress_debug_sections);
1083 man.hash.add(comp.config.any_sanitize_thread);
1084 man.hash.add(comp.config.any_fuzz);
1085 man.hash.addOptionalBytes(comp.sysroot);
1086
1087 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
1088 _ = try man.hit();
1089 digest = man.final();
1090
1091 var prev_digest_buf: [digest.len]u8 = undefined;
1092 const prev_digest: []u8 = std.Build.Cache.readSmallFile(
1093 directory.handle,
1094 id_symlink_basename,
1095 &prev_digest_buf,
1096 ) catch |err| blk: {
1097 log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
1098 // Handle this as a cache miss.
1099 break :blk prev_digest_buf[0..0];
1100 };
1101 if (mem.eql(u8, prev_digest, &digest)) {
1102 log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
1103 // Hot diggity dog! The output binary is already there.
1104 base.lock = man.toOwnedLock();
1105 return;
1106 }
1107 log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
1108
1109 // We are about to change the output file to be different, so we invalidate the build hash now.
1110 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
1111 error.FileNotFound => {},
1112 else => |e| return e,
1113 };
1114 }
1115
1116 // Due to a deficiency in LLD, we need to special-case BPF to a simple file
1117 // copy when generating relocatables. Normally, we would expect `lld -r` to work.
1118 // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails
1119 // before even generating the relocatable.
1120 //
1121 // For m68k, we go through this path because LLD doesn't support it yet, but LLVM can
1122 // produce usable object files.
1123 if (output_mode == .Obj and
1124 (comp.config.lto != .none or
1125 target.cpu.arch.isBpf() or
1126 target.cpu.arch == .lanai or
1127 target.cpu.arch == .m68k or
1128 target.cpu.arch.isSPARC() or
1129 target.cpu.arch == .ve or
1130 target.cpu.arch == .xcore))
1131 {
1132 // In this case we must do a simple file copy
1133 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1134 // build-obj. See also the corresponding TODO in linkAsArchive.
1135 const the_object_path = blk: {
1136 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
1137
1138 if (comp.c_object_table.count() != 0)
1139 break :blk comp.c_object_table.keys()[0].status.success.object_path;
1140
1141 if (module_obj_path) |p|
1142 break :blk Cache.Path.initCwd(p);
1143
1144 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1145 // regarding eliding redundant object -> object transformations.
1146 return error.NoObjectsToLink;
1147 };
1148 try std.fs.Dir.copyFile(
1149 the_object_path.root_dir.handle,
1150 the_object_path.sub_path,
1151 directory.handle,
1152 base.emit.sub_path,
1153 .{},
1154 );
1155 } else {
1156 // Create an LLD command line and invoke it.
1157 var argv = std.ArrayList([]const u8).init(gpa);
1158 defer argv.deinit();
1159 // We will invoke ourselves as a child process to gain access to LLD.
1160 // This is necessary because LLD does not behave properly as a library -
1161 // it calls exit() and does not reset all global data between invocations.
1162 const linker_command = "ld.lld";
1163 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
1164 if (is_obj) {
1165 try argv.append("-r");
1166 }
1167
1168 try argv.append("--error-limit=0");
1169
1170 if (comp.sysroot) |sysroot| {
1171 try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot}));
1172 }
1173
1174 if (target_util.llvmMachineAbi(target)) |mabi| {
1175 try argv.appendSlice(&.{
1176 "-mllvm",
1177 try std.fmt.allocPrint(arena, "-target-abi={s}", .{mabi}),
1178 });
1179 }
1180
1181 try argv.appendSlice(&.{
1182 "-mllvm",
1183 try std.fmt.allocPrint(arena, "-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}),
1184 });
1185
1186 if (comp.config.lto != .none) {
1187 switch (comp.root_mod.optimize_mode) {
1188 .Debug => {},
1189 .ReleaseSmall => try argv.append("--lto-O2"),
1190 .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"),
1191 }
1192 }
1193 switch (comp.root_mod.optimize_mode) {
1194 .Debug => {},
1195 .ReleaseSmall => try argv.append("-O2"),
1196 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
1197 }
1198
1199 if (elf.entry_name) |name| {
1200 try argv.appendSlice(&.{ "--entry", name });
1201 }
1202
1203 for (comp.force_undefined_symbols.keys()) |sym| {
1204 try argv.append("-u");
1205 try argv.append(sym);
1206 }
1207
1208 switch (elf.hash_style) {
1209 .gnu => try argv.append("--hash-style=gnu"),
1210 .sysv => try argv.append("--hash-style=sysv"),
1211 .both => {}, // this is the default
1212 }
1213
1214 if (output_mode == .Exe) {
1215 try argv.appendSlice(&.{
1216 "-z",
1217 try std.fmt.allocPrint(arena, "stack-size={d}", .{base.stack_size}),
1218 });
1219 }
1220
1221 switch (base.build_id) {
1222 .none => try argv.append("--build-id=none"),
1223 .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
1224 @tagName(base.build_id),
1225 })),
1226 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{
1227 std.fmt.fmtSliceHexLower(hs.toSlice()),
1228 })),
1229 }
1230
1231 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{elf.image_base}));
1232
1233 if (elf.linker_script) |linker_script| {
1234 try argv.append("-T");
1235 try argv.append(linker_script);
1236 }
1237
1238 if (elf.sort_section) |how| {
1239 const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)});
1240 try argv.append(arg);
1241 }
1242
1243 if (base.gc_sections) {
1244 try argv.append("--gc-sections");
1245 }
1246
1247 if (base.print_gc_sections) {
1248 try argv.append("--print-gc-sections");
1249 }
1250
1251 if (elf.print_icf_sections) {
1252 try argv.append("--print-icf-sections");
1253 }
1254
1255 if (elf.print_map) {
1256 try argv.append("--print-map");
1257 }
1258
1259 if (comp.link_eh_frame_hdr) {
1260 try argv.append("--eh-frame-hdr");
1261 }
1262
1263 if (elf.emit_relocs) {
1264 try argv.append("--emit-relocs");
1265 }
1266
1267 if (comp.config.rdynamic) {
1268 try argv.append("--export-dynamic");
1269 }
1270
1271 if (comp.config.debug_format == .strip) {
1272 try argv.append("-s");
1273 }
1274
1275 if (elf.z_nodelete) {
1276 try argv.append("-z");
1277 try argv.append("nodelete");
1278 }
1279 if (elf.z_notext) {
1280 try argv.append("-z");
1281 try argv.append("notext");
1282 }
1283 if (elf.z_defs) {
1284 try argv.append("-z");
1285 try argv.append("defs");
1286 }
1287 if (elf.z_origin) {
1288 try argv.append("-z");
1289 try argv.append("origin");
1290 }
1291 if (elf.z_nocopyreloc) {
1292 try argv.append("-z");
1293 try argv.append("nocopyreloc");
1294 }
1295 if (elf.z_now) {
1296 // LLD defaults to -zlazy
1297 try argv.append("-znow");
1298 }
1299 if (!elf.z_relro) {
1300 // LLD defaults to -zrelro
1301 try argv.append("-znorelro");
1302 }
1303 if (elf.z_common_page_size) |size| {
1304 try argv.append("-z");
1305 try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size}));
1306 }
1307 if (elf.z_max_page_size) |size| {
1308 try argv.append("-z");
1309 try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size}));
1310 }
1311
1312 if (getLDMOption(target)) |ldm| {
1313 try argv.append("-m");
1314 try argv.append(ldm);
1315 }
1316
1317 if (link_mode == .static) {
1318 if (target.cpu.arch.isArm()) {
1319 try argv.append("-Bstatic");
1320 } else {
1321 try argv.append("-static");
1322 }
1323 } else if (switch (target.os.tag) {
1324 else => is_dyn_lib,
1325 .haiku => is_exe_or_dyn_lib,
1326 }) {
1327 try argv.append("-shared");
1328 }
1329
1330 if (comp.config.pie and output_mode == .Exe) {
1331 try argv.append("-pie");
1332 }
1333
1334 if (is_exe_or_dyn_lib and target.os.tag == .netbsd) {
1335 // Add options to produce shared objects with only 2 PT_LOAD segments.
1336 // NetBSD expects 2 PT_LOAD segments in a shared object, otherwise
1337 // ld.elf_so fails loading dynamic libraries with "not found" error.
1338 // See https://github.com/ziglang/zig/issues/9109 .
1339 try argv.append("--no-rosegment");
1340 try argv.append("-znorelro");
1341 }
1342
1343 try argv.append("-o");
1344 try argv.append(full_out_path);
1345
1346 // csu prelude
1347 const csu = try comp.getCrtPaths(arena);
1348 if (csu.crt0) |p| try argv.append(try p.toString(arena));
1349 if (csu.crti) |p| try argv.append(try p.toString(arena));
1350 if (csu.crtbegin) |p| try argv.append(try p.toString(arena));
1351
1352 for (elf.rpath_list) |rpath| {
1353 try argv.appendSlice(&.{ "-rpath", rpath });
1354 }
1355
1356 for (elf.symbol_wrap_set) |symbol_name| {
1357 try argv.appendSlice(&.{ "-wrap", symbol_name });
1358 }
1359
1360 if (comp.config.link_libc) {
1361 if (comp.libc_installation) |libc_installation| {
1362 try argv.append("-L");
1363 try argv.append(libc_installation.crt_dir.?);
1364 }
1365 }
1366
1367 if (have_dynamic_linker and
1368 (comp.config.link_libc or comp.root_mod.resolved_target.is_explicit_dynamic_linker))
1369 {
1370 if (target.dynamic_linker.get()) |dynamic_linker| {
1371 try argv.append("-dynamic-linker");
1372 try argv.append(dynamic_linker);
1373 }
1374 }
1375
1376 if (is_dyn_lib) {
1377 if (elf.soname) |soname| {
1378 try argv.append("-soname");
1379 try argv.append(soname);
1380 }
1381 if (elf.version_script) |version_script| {
1382 try argv.append("-version-script");
1383 try argv.append(version_script);
1384 }
1385 if (elf.allow_undefined_version) {
1386 try argv.append("--undefined-version");
1387 } else {
1388 try argv.append("--no-undefined-version");
1389 }
1390 if (elf.enable_new_dtags) |enable_new_dtags| {
1391 if (enable_new_dtags) {
1392 try argv.append("--enable-new-dtags");
1393 } else {
1394 try argv.append("--disable-new-dtags");
1395 }
1396 }
1397 }
1398
1399 // Positional arguments to the linker such as object files.
1400 var whole_archive = false;
1401
1402 for (base.comp.link_inputs) |link_input| switch (link_input) {
1403 .res => unreachable, // Windows-only
1404 .dso => continue,
1405 .object, .archive => |obj| {
1406 if (obj.must_link and !whole_archive) {
1407 try argv.append("-whole-archive");
1408 whole_archive = true;
1409 } else if (!obj.must_link and whole_archive) {
1410 try argv.append("-no-whole-archive");
1411 whole_archive = false;
1412 }
1413 try argv.append(try obj.path.toString(arena));
1414 },
1415 .dso_exact => |dso_exact| {
1416 assert(dso_exact.name[0] == ':');
1417 try argv.appendSlice(&.{ "-l", dso_exact.name });
1418 },
1419 };
1420
1421 if (whole_archive) {
1422 try argv.append("-no-whole-archive");
1423 whole_archive = false;
1424 }
1425
1426 for (comp.c_object_table.keys()) |key| {
1427 try argv.append(try key.status.success.object_path.toString(arena));
1428 }
1429
1430 if (module_obj_path) |p| {
1431 try argv.append(p);
1432 }
1433
1434 if (comp.tsan_lib) |lib| {
1435 assert(comp.config.any_sanitize_thread);
1436 try argv.append(try lib.full_object_path.toString(arena));
1437 }
1438
1439 if (comp.fuzzer_lib) |lib| {
1440 assert(comp.config.any_fuzz);
1441 try argv.append(try lib.full_object_path.toString(arena));
1442 }
1443
1444 if (ubsan_rt_path) |p| {
1445 try argv.append(try p.toString(arena));
1446 }
1447
1448 // Shared libraries.
1449 if (is_exe_or_dyn_lib) {
1450 // Worst-case, we need an --as-needed argument for every lib, as well
1451 // as one before and one after.
1452 try argv.ensureUnusedCapacity(2 * base.comp.link_inputs.len + 2);
1453 argv.appendAssumeCapacity("--as-needed");
1454 var as_needed = true;
1455
1456 for (base.comp.link_inputs) |link_input| switch (link_input) {
1457 .res => unreachable, // Windows-only
1458 .object, .archive, .dso_exact => continue,
1459 .dso => |dso| {
1460 const lib_as_needed = !dso.needed;
1461 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
1462 0b00, 0b11 => {},
1463 0b01 => {
1464 argv.appendAssumeCapacity("--no-as-needed");
1465 as_needed = false;
1466 },
1467 0b10 => {
1468 argv.appendAssumeCapacity("--as-needed");
1469 as_needed = true;
1470 },
1471 }
1472
1473 // By this time, we depend on these libs being dynamically linked
1474 // libraries and not static libraries (the check for that needs to be earlier),
1475 // but they could be full paths to .so files, in which case we
1476 // want to avoid prepending "-l".
1477 argv.appendAssumeCapacity(try dso.path.toString(arena));
1478 },
1479 };
1480
1481 if (!as_needed) {
1482 argv.appendAssumeCapacity("--as-needed");
1483 as_needed = true;
1484 }
1485
1486 // libc++ dep
1487 if (comp.config.link_libcpp) {
1488 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
1489 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
1490 }
1491
1492 // libunwind dep
1493 if (comp.config.link_libunwind) {
1494 try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
1495 }
1496
1497 // libc dep
1498 diags.flags.missing_libc = false;
1499 if (comp.config.link_libc) {
1500 if (comp.libc_installation != null) {
1501 const needs_grouping = link_mode == .static;
1502 if (needs_grouping) try argv.append("--start-group");
1503 try argv.appendSlice(target_util.libcFullLinkFlags(target));
1504 if (needs_grouping) try argv.append("--end-group");
1505 } else if (target.isGnuLibC()) {
1506 for (glibc.libs) |lib| {
1507 if (lib.removed_in) |rem_in| {
1508 if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue;
1509 }
1510
1511 const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
1512 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1513 });
1514 try argv.append(lib_path);
1515 }
1516 try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a"));
1517 } else if (target.isMuslLibC()) {
1518 try argv.append(try comp.crtFileAsString(arena, switch (link_mode) {
1519 .static => "libc.a",
1520 .dynamic => "libc.so",
1521 }));
1522 } else if (target.isFreeBSDLibC()) {
1523 for (freebsd.libs) |lib| {
1524 const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
1525 comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1526 });
1527 try argv.append(lib_path);
1528 }
1529 } else if (target.isNetBSDLibC()) {
1530 for (netbsd.libs) |lib| {
1531 const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
1532 comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1533 });
1534 try argv.append(lib_path);
1535 }
1536 } else {
1537 diags.flags.missing_libc = true;
1538 }
1539
1540 if (comp.zigc_static_lib) |zigc| {
1541 try argv.append(try zigc.full_object_path.toString(arena));
1542 }
1543 }
1544 }
1545
1546 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
1547 // to be after the shared libraries, so they are picked up from the shared
1548 // libraries, not libcompiler_rt.
1549 if (compiler_rt_path) |p| {
1550 try argv.append(try p.toString(arena));
1551 }
1552
1553 // crt postlude
1554 if (csu.crtend) |p| try argv.append(try p.toString(arena));
1555 if (csu.crtn) |p| try argv.append(try p.toString(arena));
1556
1557 if (base.allow_shlib_undefined) {
1558 try argv.append("--allow-shlib-undefined");
1559 }
1560
1561 switch (elf.compress_debug_sections) {
1562 .none => {},
1563 .zlib => try argv.append("--compress-debug-sections=zlib"),
1564 .zstd => try argv.append("--compress-debug-sections=zstd"),
1565 }
1566
1567 if (elf.bind_global_refs_locally) {
1568 try argv.append("-Bsymbolic");
1569 }
1570
1571 try spawnLld(comp, arena, argv.items);
1572 }
1573
1574 if (!lld.disable_caching) {
1575 // Update the file with the digest. If it fails we can continue; it only
1576 // means that the next invocation will have an unnecessary cache miss.
1577 std.Build.Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1578 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
1579 };
1580 // Again failure here only means an unnecessary cache miss.
1581 man.writeManifest() catch |err| {
1582 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
1583 };
1584 // We hang on to this lock so that the output file path can be used without
1585 // other processes clobbering it.
1586 base.lock = man.toOwnedLock();
1587 }
1588}
1589fn getLDMOption(target: std.Target) ?[]const u8 {
1590 // This should only return emulations understood by LLD's parseEmulation().
1591 return switch (target.cpu.arch) {
1592 .aarch64 => switch (target.os.tag) {
1593 .linux => "aarch64linux",
1594 else => "aarch64elf",
1595 },
1596 .aarch64_be => switch (target.os.tag) {
1597 .linux => "aarch64linuxb",
1598 else => "aarch64elfb",
1599 },
1600 .amdgcn => "elf64_amdgpu",
1601 .arm, .thumb => switch (target.os.tag) {
1602 .linux => "armelf_linux_eabi",
1603 else => "armelf",
1604 },
1605 .armeb, .thumbeb => switch (target.os.tag) {
1606 .linux => "armelfb_linux_eabi",
1607 else => "armelfb",
1608 },
1609 .hexagon => "hexagonelf",
1610 .loongarch32 => "elf32loongarch",
1611 .loongarch64 => "elf64loongarch",
1612 .mips => switch (target.os.tag) {
1613 .freebsd => "elf32btsmip_fbsd",
1614 else => "elf32btsmip",
1615 },
1616 .mipsel => switch (target.os.tag) {
1617 .freebsd => "elf32ltsmip_fbsd",
1618 else => "elf32ltsmip",
1619 },
1620 .mips64 => switch (target.os.tag) {
1621 .freebsd => switch (target.abi) {
1622 .gnuabin32, .muslabin32 => "elf32btsmipn32_fbsd",
1623 else => "elf64btsmip_fbsd",
1624 },
1625 else => switch (target.abi) {
1626 .gnuabin32, .muslabin32 => "elf32btsmipn32",
1627 else => "elf64btsmip",
1628 },
1629 },
1630 .mips64el => switch (target.os.tag) {
1631 .freebsd => switch (target.abi) {
1632 .gnuabin32, .muslabin32 => "elf32ltsmipn32_fbsd",
1633 else => "elf64ltsmip_fbsd",
1634 },
1635 else => switch (target.abi) {
1636 .gnuabin32, .muslabin32 => "elf32ltsmipn32",
1637 else => "elf64ltsmip",
1638 },
1639 },
1640 .msp430 => "msp430elf",
1641 .powerpc => switch (target.os.tag) {
1642 .freebsd => "elf32ppc_fbsd",
1643 .linux => "elf32ppclinux",
1644 else => "elf32ppc",
1645 },
1646 .powerpcle => switch (target.os.tag) {
1647 .linux => "elf32lppclinux",
1648 else => "elf32lppc",
1649 },
1650 .powerpc64 => "elf64ppc",
1651 .powerpc64le => "elf64lppc",
1652 .riscv32 => "elf32lriscv",
1653 .riscv64 => "elf64lriscv",
1654 .s390x => "elf64_s390",
1655 .sparc64 => "elf64_sparc",
1656 .x86 => switch (target.os.tag) {
1657 .freebsd => "elf_i386_fbsd",
1658 else => "elf_i386",
1659 },
1660 .x86_64 => switch (target.abi) {
1661 .gnux32, .muslx32 => "elf32_x86_64",
1662 else => "elf_x86_64",
1663 },
1664 else => null,
1665 };
1666}
1667fn wasmLink(lld: *Lld, arena: Allocator) !void {
1668 const comp = lld.base.comp;
1669 const shared_memory = comp.config.shared_memory;
1670 const export_memory = comp.config.export_memory;
1671 const import_memory = comp.config.import_memory;
1672 const target = comp.root_mod.resolved_target.result;
1673 const base = &lld.base;
1674 const wasm = &lld.ofmt.wasm;
1675
1676 const gpa = comp.gpa;
1677
1678 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
1679 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
1680
1681 // If there is no Zig code to compile, then we should skip flushing the output file because it
1682 // will not be part of the linker line anyway.
1683 const module_obj_path: ?[]const u8 = if (comp.zcu != null) p: {
1684 if (fs.path.dirname(full_out_path)) |dirname| {
1685 break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? });
1686 } else {
1687 break :p base.zcu_object_sub_path.?;
1688 }
1689 } else null;
1690
1691 const is_obj = comp.config.output_mode == .Obj;
1692 const compiler_rt_path: ?Cache.Path = blk: {
1693 if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path;
1694 if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path;
1695 break :blk null;
1696 };
1697 const ubsan_rt_path: ?Cache.Path = blk: {
1698 if (comp.ubsan_rt_lib) |lib| break :blk lib.full_object_path;
1699 if (comp.ubsan_rt_obj) |obj| break :blk obj.full_object_path;
1700 break :blk null;
1701 };
1702
1703 const id_symlink_basename = "lld.id";
1704
1705 var man: Cache.Manifest = undefined;
1706 defer if (!lld.disable_caching) man.deinit();
1707
1708 var digest: [Cache.hex_digest_len]u8 = undefined;
1709
1710 if (!lld.disable_caching) {
1711 man = comp.cache_parent.obtain();
1712
1713 // We are about to obtain this lock, so here we give other processes a chance first.
1714 base.releaseLock();
1715
1716 comptime assert(Compilation.link_hash_implementation_version == 14);
1717
1718 try link.hashInputs(&man, comp.link_inputs);
1719 for (comp.c_object_table.keys()) |key| {
1720 _ = try man.addFilePath(key.status.success.object_path, null);
1721 }
1722 try man.addOptionalFile(module_obj_path);
1723 try man.addOptionalFilePath(compiler_rt_path);
1724 try man.addOptionalFilePath(ubsan_rt_path);
1725 man.hash.addOptionalBytes(wasm.entry_name);
1726 man.hash.add(base.stack_size);
1727 man.hash.add(base.build_id);
1728 man.hash.add(import_memory);
1729 man.hash.add(export_memory);
1730 man.hash.add(wasm.import_table);
1731 man.hash.add(wasm.export_table);
1732 man.hash.addOptional(wasm.initial_memory);
1733 man.hash.addOptional(wasm.max_memory);
1734 man.hash.add(shared_memory);
1735 man.hash.addOptional(wasm.global_base);
1736 man.hash.addListOfBytes(wasm.export_symbol_names);
1737 // strip does not need to go into the linker hash because it is part of the hash namespace
1738
1739 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
1740 _ = try man.hit();
1741 digest = man.final();
1742
1743 var prev_digest_buf: [digest.len]u8 = undefined;
1744 const prev_digest: []u8 = Cache.readSmallFile(
1745 directory.handle,
1746 id_symlink_basename,
1747 &prev_digest_buf,
1748 ) catch |err| blk: {
1749 log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
1750 // Handle this as a cache miss.
1751 break :blk prev_digest_buf[0..0];
1752 };
1753 if (mem.eql(u8, prev_digest, &digest)) {
1754 log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
1755 // Hot diggity dog! The output binary is already there.
1756 base.lock = man.toOwnedLock();
1757 return;
1758 }
1759 log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
1760
1761 // We are about to change the output file to be different, so we invalidate the build hash now.
1762 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
1763 error.FileNotFound => {},
1764 else => |e| return e,
1765 };
1766 }
1767
1768 if (is_obj) {
1769 // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy
1770 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1771 // build-obj. See also the corresponding TODO in linkAsArchive.
1772 const the_object_path = blk: {
1773 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
1774
1775 if (comp.c_object_table.count() != 0)
1776 break :blk comp.c_object_table.keys()[0].status.success.object_path;
1777
1778 if (module_obj_path) |p|
1779 break :blk Cache.Path.initCwd(p);
1780
1781 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1782 // regarding eliding redundant object -> object transformations.
1783 return error.NoObjectsToLink;
1784 };
1785 try fs.Dir.copyFile(
1786 the_object_path.root_dir.handle,
1787 the_object_path.sub_path,
1788 directory.handle,
1789 base.emit.sub_path,
1790 .{},
1791 );
1792 } else {
1793 // Create an LLD command line and invoke it.
1794 var argv = std.ArrayList([]const u8).init(gpa);
1795 defer argv.deinit();
1796 // We will invoke ourselves as a child process to gain access to LLD.
1797 // This is necessary because LLD does not behave properly as a library -
1798 // it calls exit() and does not reset all global data between invocations.
1799 const linker_command = "wasm-ld";
1800 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
1801 try argv.append("--error-limit=0");
1802
1803 if (comp.config.lto != .none) {
1804 switch (comp.root_mod.optimize_mode) {
1805 .Debug => {},
1806 .ReleaseSmall => try argv.append("-O2"),
1807 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
1808 }
1809 }
1810
1811 if (import_memory) {
1812 try argv.append("--import-memory");
1813 }
1814
1815 if (export_memory) {
1816 try argv.append("--export-memory");
1817 }
1818
1819 if (wasm.import_table) {
1820 assert(!wasm.export_table);
1821 try argv.append("--import-table");
1822 }
1823
1824 if (wasm.export_table) {
1825 assert(!wasm.import_table);
1826 try argv.append("--export-table");
1827 }
1828
1829 // For wasm-ld we only need to specify '--no-gc-sections' when the user explicitly
1830 // specified it as garbage collection is enabled by default.
1831 if (!base.gc_sections) {
1832 try argv.append("--no-gc-sections");
1833 }
1834
1835 if (comp.config.debug_format == .strip) {
1836 try argv.append("-s");
1837 }
1838
1839 if (wasm.initial_memory) |initial_memory| {
1840 const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory});
1841 try argv.append(arg);
1842 }
1843
1844 if (wasm.max_memory) |max_memory| {
1845 const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory});
1846 try argv.append(arg);
1847 }
1848
1849 if (shared_memory) {
1850 try argv.append("--shared-memory");
1851 }
1852
1853 if (wasm.global_base) |global_base| {
1854 const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base});
1855 try argv.append(arg);
1856 } else {
1857 // We prepend it by default, so when a stack overflow happens the runtime will trap correctly,
1858 // rather than silently overwrite all global declarations. See https://github.com/ziglang/zig/issues/4496
1859 //
1860 // The user can overwrite this behavior by setting the global-base
1861 try argv.append("--stack-first");
1862 }
1863
1864 // Users are allowed to specify which symbols they want to export to the wasm host.
1865 for (wasm.export_symbol_names) |symbol_name| {
1866 const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name});
1867 try argv.append(arg);
1868 }
1869
1870 if (comp.config.rdynamic) {
1871 try argv.append("--export-dynamic");
1872 }
1873
1874 if (wasm.entry_name) |entry_name| {
1875 try argv.appendSlice(&.{ "--entry", entry_name });
1876 } else {
1877 try argv.append("--no-entry");
1878 }
1879
1880 try argv.appendSlice(&.{
1881 "-z",
1882 try std.fmt.allocPrint(arena, "stack-size={d}", .{base.stack_size}),
1883 });
1884
1885 switch (base.build_id) {
1886 .none => try argv.append("--build-id=none"),
1887 .fast, .uuid, .sha1 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
1888 @tagName(base.build_id),
1889 })),
1890 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{
1891 std.fmt.fmtSliceHexLower(hs.toSlice()),
1892 })),
1893 .md5 => {},
1894 }
1895
1896 if (wasm.import_symbols) {
1897 try argv.append("--allow-undefined");
1898 }
1899
1900 if (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic) {
1901 try argv.append("--shared");
1902 }
1903 if (comp.config.pie) {
1904 try argv.append("--pie");
1905 }
1906
1907 try argv.appendSlice(&.{ "-o", full_out_path });
1908
1909 if (target.cpu.arch == .wasm64) {
1910 try argv.append("-mwasm64");
1911 }
1912
1913 const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or
1914 (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic);
1915
1916 if (comp.config.link_libc and is_exe_or_dyn_lib) {
1917 if (target.os.tag == .wasi) {
1918 for (comp.wasi_emulated_libs) |crt_file| {
1919 try argv.append(try comp.crtFileAsString(
1920 arena,
1921 wasi_libc.emulatedLibCRFileLibName(crt_file),
1922 ));
1923 }
1924
1925 try argv.append(try comp.crtFileAsString(
1926 arena,
1927 wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),
1928 ));
1929 try argv.append(try comp.crtFileAsString(arena, "libc.a"));
1930 }
1931
1932 if (comp.zigc_static_lib) |zigc| {
1933 try argv.append(try zigc.full_object_path.toString(arena));
1934 }
1935
1936 if (comp.config.link_libcpp) {
1937 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
1938 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
1939 }
1940 }
1941
1942 // Positional arguments to the linker such as object files.
1943 var whole_archive = false;
1944 for (comp.link_inputs) |link_input| switch (link_input) {
1945 .object, .archive => |obj| {
1946 if (obj.must_link and !whole_archive) {
1947 try argv.append("-whole-archive");
1948 whole_archive = true;
1949 } else if (!obj.must_link and whole_archive) {
1950 try argv.append("-no-whole-archive");
1951 whole_archive = false;
1952 }
1953 try argv.append(try obj.path.toString(arena));
1954 },
1955 .dso => |dso| {
1956 try argv.append(try dso.path.toString(arena));
1957 },
1958 .dso_exact => unreachable,
1959 .res => unreachable,
1960 };
1961 if (whole_archive) {
1962 try argv.append("-no-whole-archive");
1963 whole_archive = false;
1964 }
1965
1966 for (comp.c_object_table.keys()) |key| {
1967 try argv.append(try key.status.success.object_path.toString(arena));
1968 }
1969 if (module_obj_path) |p| {
1970 try argv.append(p);
1971 }
1972
1973 if (compiler_rt_path) |p| {
1974 try argv.append(try p.toString(arena));
1975 }
1976
1977 if (ubsan_rt_path) |p| {
1978 try argv.append(try p.toStringZ(arena));
1979 }
1980
1981 try spawnLld(comp, arena, argv.items);
1982
1983 // Give +x to the .wasm file if it is an executable and the OS is WASI.
1984 // Some systems may be configured to execute such binaries directly. Even if that
1985 // is not the case, it means we will get "exec format error" when trying to run
1986 // it, and then can react to that in the same way as trying to run an ELF file
1987 // from a foreign CPU architecture.
1988 if (fs.has_executable_bit and target.os.tag == .wasi and
1989 comp.config.output_mode == .Exe)
1990 {
1991 // TODO: what's our strategy for reporting linker errors from this function?
1992 // report a nice error here with the file path if it fails instead of
1993 // just returning the error code.
1994 // chmod does not interact with umask, so we use a conservative -rwxr--r-- here.
1995 std.posix.fchmodat(fs.cwd().fd, full_out_path, 0o744, 0) catch |err| switch (err) {
1996 error.OperationNotSupported => unreachable, // Not a symlink.
1997 else => |e| return e,
1998 };
1999 }
2000 }
2001
2002 if (!lld.disable_caching) {
2003 // Update the file with the digest. If it fails we can continue; it only
2004 // means that the next invocation will have an unnecessary cache miss.
2005 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
2006 log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)});
2007 };
2008 // Again failure here only means an unnecessary cache miss.
2009 man.writeManifest() catch |err| {
2010 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
2011 };
2012 // We hang on to this lock so that the output file path can be used without
2013 // other processes clobbering it.
2014 base.lock = man.toOwnedLock();
2015 }
2016}
2017
2018fn spawnLld(
2019 comp: *Compilation,
2020 arena: Allocator,
2021 argv: []const []const u8,
2022) !void {
2023 if (comp.verbose_link) {
2024 // Skip over our own name so that the LLD linker name is the first argv item.
2025 Compilation.dump_argv(argv[1..]);
2026 }
2027
2028 // If possible, we run LLD as a child process because it does not always
2029 // behave properly as a library, unfortunately.
2030 // https://github.com/ziglang/zig/issues/3825
2031 if (!std.process.can_spawn) {
2032 const exit_code = try lldMain(arena, argv, false);
2033 if (exit_code == 0) return;
2034 if (comp.clang_passthrough_mode) std.process.exit(exit_code);
2035 return error.LinkFailure;
2036 }
2037
2038 var stderr: []u8 = &.{};
2039 defer comp.gpa.free(stderr);
2040
2041 var child = std.process.Child.init(argv, arena);
2042 const term = (if (comp.clang_passthrough_mode) term: {
2043 child.stdin_behavior = .Inherit;
2044 child.stdout_behavior = .Inherit;
2045 child.stderr_behavior = .Inherit;
2046
2047 break :term child.spawnAndWait();
2048 } else term: {
2049 child.stdin_behavior = .Ignore;
2050 child.stdout_behavior = .Ignore;
2051 child.stderr_behavior = .Pipe;
2052
2053 child.spawn() catch |err| break :term err;
2054 stderr = try child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize));
2055 break :term child.wait();
2056 }) catch |first_err| term: {
2057 const err = switch (first_err) {
2058 error.NameTooLong => err: {
2059 const s = fs.path.sep_str;
2060 const rand_int = std.crypto.random.int(u64);
2061 const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp";
2062
2063 const rsp_file = try comp.dirs.local_cache.handle.createFileZ(rsp_path, .{});
2064 defer comp.dirs.local_cache.handle.deleteFileZ(rsp_path) catch |err|
2065 log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) });
2066 {
2067 defer rsp_file.close();
2068 var rsp_buf = std.io.bufferedWriter(rsp_file.writer());
2069 const rsp_writer = rsp_buf.writer();
2070 for (argv[2..]) |arg| {
2071 try rsp_writer.writeByte('"');
2072 for (arg) |c| {
2073 switch (c) {
2074 '\"', '\\' => try rsp_writer.writeByte('\\'),
2075 else => {},
2076 }
2077 try rsp_writer.writeByte(c);
2078 }
2079 try rsp_writer.writeByte('"');
2080 try rsp_writer.writeByte('\n');
2081 }
2082 try rsp_buf.flush();
2083 }
2084
2085 var rsp_child = std.process.Child.init(&.{ argv[0], argv[1], try std.fmt.allocPrint(
2086 arena,
2087 "@{s}",
2088 .{try comp.dirs.local_cache.join(arena, &.{rsp_path})},
2089 ) }, arena);
2090 if (comp.clang_passthrough_mode) {
2091 rsp_child.stdin_behavior = .Inherit;
2092 rsp_child.stdout_behavior = .Inherit;
2093 rsp_child.stderr_behavior = .Inherit;
2094
2095 break :term rsp_child.spawnAndWait() catch |err| break :err err;
2096 } else {
2097 rsp_child.stdin_behavior = .Ignore;
2098 rsp_child.stdout_behavior = .Ignore;
2099 rsp_child.stderr_behavior = .Pipe;
2100
2101 rsp_child.spawn() catch |err| break :err err;
2102 stderr = try rsp_child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize));
2103 break :term rsp_child.wait() catch |err| break :err err;
2104 }
2105 },
2106 else => first_err,
2107 };
2108 log.err("unable to spawn LLD {s}: {s}", .{ argv[0], @errorName(err) });
2109 return error.UnableToSpawnSelf;
2110 };
2111
2112 const diags = &comp.link_diags;
2113 switch (term) {
2114 .Exited => |code| if (code != 0) {
2115 if (comp.clang_passthrough_mode) std.process.exit(code);
2116 diags.lockAndParseLldStderr(argv[1], stderr);
2117 return error.LinkFailure;
2118 },
2119 else => {
2120 if (comp.clang_passthrough_mode) std.process.abort();
2121 return diags.fail("{s} terminated with stderr:\n{s}", .{ argv[0], stderr });
2122 },
2123 }
2124
2125 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});
2126}
2127
2128const std = @import("std");
2129const Allocator = std.mem.Allocator;
2130const Cache = std.Build.Cache;
2131const allocPrint = std.fmt.allocPrint;
2132const assert = std.debug.assert;
2133const fs = std.fs;
2134const log = std.log.scoped(.link);
2135const mem = std.mem;
2136
2137const Compilation = @import("../Compilation.zig");
2138const Zcu = @import("../Zcu.zig");
2139const dev = @import("../dev.zig");
2140const freebsd = @import("../libs/freebsd.zig");
2141const glibc = @import("../libs/glibc.zig");
2142const netbsd = @import("../libs/netbsd.zig");
2143const wasi_libc = @import("../libs/wasi_libc.zig");
2144const link = @import("../link.zig");
2145const lldMain = @import("../main.zig").lldMain;
2146const target_util = @import("../target.zig");
2147const trace = @import("../tracy.zig").trace;
2148const Lld = @This();
src/link/MachO.zig+3-13
......@@ -194,7 +194,6 @@ pub fn createEmpty(
194194 .stack_size = options.stack_size orelse 16777216,
195195 .allow_shlib_undefined = allow_shlib_undefined,
196196 .file = null,
197 .disable_lld_caching = options.disable_lld_caching,
198197 .build_id = options.build_id,
199198 },
200199 .rpath_list = options.rpath_list,
......@@ -227,7 +226,7 @@ pub fn createEmpty(
227226 self.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{
228227 .truncate = true,
229228 .read = true,
230 .mode = link.File.determineMode(false, output_mode, link_mode),
229 .mode = link.File.determineMode(output_mode, link_mode),
231230 });
232231
233232 // Append null file
......@@ -341,15 +340,6 @@ pub fn flush(
341340 arena: Allocator,
342341 tid: Zcu.PerThread.Id,
343342 prog_node: std.Progress.Node,
344) link.File.FlushError!void {
345 try self.flushZcu(arena, tid, prog_node);
346}
347
348pub fn flushZcu(
349 self: *MachO,
350 arena: Allocator,
351 tid: Zcu.PerThread.Id,
352 prog_node: std.Progress.Node,
353343) link.File.FlushError!void {
354344 const tracy = trace(@src());
355345 defer tracy.end();
......@@ -373,7 +363,7 @@ pub fn flushZcu(
373363 // --verbose-link
374364 if (comp.verbose_link) try self.dumpArgv(comp);
375365
376 if (self.getZigObject()) |zo| try zo.flushZcu(self, tid);
366 if (self.getZigObject()) |zo| try zo.flush(self, tid);
377367 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
378368 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
379369
......@@ -617,7 +607,7 @@ pub fn flushZcu(
617607 error.LinkFailure => return error.LinkFailure,
618608 else => |e| return diags.fail("failed to calculate and write uuid: {s}", .{@errorName(e)}),
619609 };
620 if (self.getDebugSymbols()) |dsym| dsym.flushZcu(self) catch |err| switch (err) {
610 if (self.getDebugSymbols()) |dsym| dsym.flush(self) catch |err| switch (err) {
621611 error.OutOfMemory => return error.OutOfMemory,
622612 else => |e| return diags.fail("failed to get debug symbols: {s}", .{@errorName(e)}),
623613 };
src/link/MachO/DebugSymbols.zig+1-1
......@@ -178,7 +178,7 @@ fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) !u64
178178 return offset;
179179}
180180
181pub fn flushZcu(self: *DebugSymbols, macho_file: *MachO) !void {
181pub fn flush(self: *DebugSymbols, macho_file: *MachO) !void {
182182 const zo = macho_file.getZigObject().?;
183183 for (self.relocs.items) |*reloc| {
184184 const sym = zo.symbols.items[reloc.target];
src/link/MachO/ZigObject.zig+4-4
......@@ -550,7 +550,7 @@ pub fn getInputSection(self: ZigObject, atom: Atom, macho_file: *MachO) macho.se
550550 return sect;
551551}
552552
553pub fn flushZcu(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.File.FlushError!void {
553pub fn flush(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.File.FlushError!void {
554554 const diags = &macho_file.base.comp.link_diags;
555555
556556 // Handle any lazy symbols that were emitted by incremental compilation.
......@@ -589,7 +589,7 @@ pub fn flushZcu(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) lin
589589 if (self.dwarf) |*dwarf| {
590590 const pt: Zcu.PerThread = .activate(macho_file.base.comp.zcu.?, tid);
591591 defer pt.deactivate();
592 dwarf.flushZcu(pt) catch |err| switch (err) {
592 dwarf.flush(pt) catch |err| switch (err) {
593593 error.OutOfMemory => return error.OutOfMemory,
594594 else => |e| return diags.fail("failed to flush dwarf module: {s}", .{@errorName(e)}),
595595 };
......@@ -599,7 +599,7 @@ pub fn flushZcu(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) lin
599599 self.debug_strtab_dirty = false;
600600 }
601601
602 // The point of flushZcu() is to commit changes, so in theory, nothing should
602 // The point of flush() is to commit changes, so in theory, nothing should
603603 // be dirty after this. However, it is possible for some things to remain
604604 // dirty because they fail to be written in the event of compile errors,
605605 // such as debug_line_header_dirty and debug_info_header_dirty.
......@@ -1537,7 +1537,7 @@ pub fn getOrCreateMetadataForLazySymbol(
15371537 }
15381538 state_ptr.* = .pending_flush;
15391539 const symbol_index = symbol_index_ptr.*;
1540 // anyerror needs to be deferred until flushZcu
1540 // anyerror needs to be deferred until flush
15411541 if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbol(macho_file, pt, lazy_sym, symbol_index);
15421542 return symbol_index;
15431543}
src/link/Plan9.zig+11-29
......@@ -301,7 +301,6 @@ pub fn createEmpty(
301301 .stack_size = options.stack_size orelse 16777216,
302302 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
303303 .file = null,
304 .disable_lld_caching = options.disable_lld_caching,
305304 .build_id = options.build_id,
306305 },
307306 .sixtyfour_bit = sixtyfour_bit,
......@@ -494,7 +493,7 @@ fn updateFinish(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
494493 // write the symbol
495494 // we already have the got index
496495 const sym: aout.Sym = .{
497 .value = undefined, // the value of stuff gets filled in in flushZcu
496 .value = undefined, // the value of stuff gets filled in in flush
498497 .type = atom.type,
499498 .name = try gpa.dupe(u8, nav.name.toSlice(ip)),
500499 };
......@@ -527,25 +526,6 @@ fn allocateGotIndex(self: *Plan9) usize {
527526 }
528527}
529528
530pub fn flush(
531 self: *Plan9,
532 arena: Allocator,
533 tid: Zcu.PerThread.Id,
534 prog_node: std.Progress.Node,
535) link.File.FlushError!void {
536 const comp = self.base.comp;
537 const diags = &comp.link_diags;
538 const use_lld = build_options.have_llvm and comp.config.use_lld;
539 assert(!use_lld);
540
541 switch (link.File.effectiveOutputMode(use_lld, comp.config.output_mode)) {
542 .Exe => {},
543 .Obj => return diags.fail("writing plan9 object files unimplemented", .{}),
544 .Lib => return diags.fail("writing plan9 lib files unimplemented", .{}),
545 }
546 return self.flushZcu(arena, tid, prog_node);
547}
548
549529pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void {
550530 if (delta_line > 0 and delta_line < 65) {
551531 const toappend = @as(u8, @intCast(delta_line));
......@@ -586,7 +566,7 @@ fn atomCount(self: *Plan9) usize {
586566 return data_nav_count + fn_nav_count + lazy_atom_count + extern_atom_count + uav_atom_count;
587567}
588568
589pub fn flushZcu(
569pub fn flush(
590570 self: *Plan9,
591571 arena: Allocator,
592572 /// TODO: stop using this
......@@ -607,10 +587,16 @@ pub fn flushZcu(
607587 const gpa = comp.gpa;
608588 const target = comp.root_mod.resolved_target.result;
609589
590 switch (comp.config.output_mode) {
591 .Exe => {},
592 .Obj => return diags.fail("writing plan9 object files unimplemented", .{}),
593 .Lib => return diags.fail("writing plan9 lib files unimplemented", .{}),
594 }
595
610596 const sub_prog_node = prog_node.start("Flush Module", 0);
611597 defer sub_prog_node.end();
612598
613 log.debug("flushZcu", .{});
599 log.debug("flush", .{});
614600
615601 defer assert(self.hdr.entry != 0x0);
616602
......@@ -1039,7 +1025,7 @@ pub fn getOrCreateAtomForLazySymbol(self: *Plan9, pt: Zcu.PerThread, lazy_sym: F
10391025 const atom = atom_ptr.*;
10401026 _ = try self.getAtomPtr(atom).getOrCreateSymbolTableEntry(self);
10411027 _ = self.getAtomPtr(atom).getOrCreateOffsetTableEntry(self);
1042 // anyerror needs to be deferred until flushZcu
1028 // anyerror needs to be deferred until flush
10431029 if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbolAtom(pt, lazy_sym, atom);
10441030 return atom;
10451031}
......@@ -1182,11 +1168,7 @@ pub fn open(
11821168
11831169 const file = try emit.root_dir.handle.createFile(emit.sub_path, .{
11841170 .read = true,
1185 .mode = link.File.determineMode(
1186 use_lld,
1187 comp.config.output_mode,
1188 comp.config.link_mode,
1189 ),
1171 .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode),
11901172 });
11911173 errdefer file.close();
11921174 self.base.file = file;
src/link/SpirV.zig+3-8
......@@ -17,7 +17,7 @@
1717//! All regular functions.
1818
1919// Because SPIR-V requires re-compilation anyway, and so hot swapping will not work
20// anyway, we simply generate all the code in flushZcu. This keeps
20// anyway, we simply generate all the code in flush. This keeps
2121// things considerably simpler.
2222
2323const SpirV = @This();
......@@ -83,7 +83,6 @@ pub fn createEmpty(
8383 .stack_size = options.stack_size orelse 0,
8484 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
8585 .file = null,
86 .disable_lld_caching = options.disable_lld_caching,
8786 .build_id = options.build_id,
8887 },
8988 .object = codegen.Object.init(gpa, comp.getTarget()),
......@@ -193,18 +192,14 @@ pub fn updateExports(
193192 // TODO: Export regular functions, variables, etc using Linkage attributes.
194193}
195194
196pub fn flush(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
197 return self.flushZcu(arena, tid, prog_node);
198}
199
200pub fn flushZcu(
195pub fn flush(
201196 self: *SpirV,
202197 arena: Allocator,
203198 tid: Zcu.PerThread.Id,
204199 prog_node: std.Progress.Node,
205200) link.File.FlushError!void {
206201 // The goal is to never use this because it's only needed if we need to
207 // write to InternPool, but flushZcu is too late to be writing to the
202 // write to InternPool, but flush is too late to be writing to the
208203 // InternPool.
209204 _ = tid;
210205
src/link/Wasm.zig+5-468
......@@ -40,7 +40,6 @@ const Zcu = @import("../Zcu.zig");
4040const codegen = @import("../codegen.zig");
4141const dev = @import("../dev.zig");
4242const link = @import("../link.zig");
43const lldMain = @import("../main.zig").lldMain;
4443const trace = @import("../tracy.zig").trace;
4544const wasi_libc = @import("../libs/wasi_libc.zig");
4645const Value = @import("../Value.zig");
......@@ -74,8 +73,6 @@ global_base: ?u64,
7473initial_memory: ?u64,
7574/// When defined, sets the maximum memory size of the memory.
7675max_memory: ?u64,
77/// When true, will import the function table from the host environment.
78import_table: bool,
7976/// When true, will export the function table to the host environment.
8077export_table: bool,
8178/// Output name of the file
......@@ -2935,17 +2932,14 @@ pub fn createEmpty(
29352932 const target = comp.root_mod.resolved_target.result;
29362933 assert(target.ofmt == .wasm);
29372934
2938 const use_lld = build_options.have_llvm and comp.config.use_lld;
29392935 const use_llvm = comp.config.use_llvm;
29402936 const output_mode = comp.config.output_mode;
29412937 const wasi_exec_model = comp.config.wasi_exec_model;
29422938
2943 // If using LLD to link, this code should produce an object file so that it
2944 // can be passed to LLD.
29452939 // If using LLVM to generate the object file for the zig compilation unit,
29462940 // we need a place to put the object file so that it can be subsequently
29472941 // handled.
2948 const zcu_object_sub_path = if (!use_lld and !use_llvm)
2942 const zcu_object_sub_path = if (!use_llvm)
29492943 null
29502944 else
29512945 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
......@@ -2970,13 +2964,11 @@ pub fn createEmpty(
29702964 },
29712965 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
29722966 .file = null,
2973 .disable_lld_caching = options.disable_lld_caching,
29742967 .build_id = options.build_id,
29752968 },
29762969 .name = undefined,
29772970 .string_table = .empty,
29782971 .string_bytes = .empty,
2979 .import_table = options.import_table,
29802972 .export_table = options.export_table,
29812973 .import_symbols = options.import_symbols,
29822974 .export_symbol_names = options.export_symbol_names,
......@@ -3004,17 +2996,7 @@ pub fn createEmpty(
30042996 .named => |name| (try wasm.internString(name)).toOptional(),
30052997 };
30062998
3007 if (use_lld and (use_llvm or !comp.config.have_zcu)) {
3008 // LLVM emits the object file (if any); LLD links it into the final product.
3009 return wasm;
3010 }
3011
3012 // What path should this Wasm linker code output to?
3013 // If using LLD to link, this code should produce an object file so that it
3014 // can be passed to LLD.
3015 const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;
3016
3017 wasm.base.file = try emit.root_dir.handle.createFile(sub_path, .{
2999 wasm.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{
30183000 .truncate = true,
30193001 .read = true,
30203002 .mode = if (fs.has_executable_bit)
......@@ -3025,7 +3007,7 @@ pub fn createEmpty(
30253007 else
30263008 0,
30273009 });
3028 wasm.name = sub_path;
3010 wasm.name = emit.sub_path;
30293011
30303012 return wasm;
30313013}
......@@ -3367,21 +3349,6 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void {
33673349 }
33683350}
33693351
3370pub fn flush(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
3371 const comp = wasm.base.comp;
3372 const use_lld = build_options.have_llvm and comp.config.use_lld;
3373 const diags = &comp.link_diags;
3374
3375 if (use_lld) {
3376 return wasm.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) {
3377 error.OutOfMemory => return error.OutOfMemory,
3378 error.LinkFailure => return error.LinkFailure,
3379 else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
3380 };
3381 }
3382 return wasm.flushZcu(arena, tid, prog_node);
3383}
3384
33853352pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!void {
33863353 const tracy = trace(@src());
33873354 defer tracy.end();
......@@ -3773,14 +3740,14 @@ fn markTable(wasm: *Wasm, i: ObjectTableIndex) link.File.FlushError!void {
37733740 try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(i), {});
37743741}
37753742
3776pub fn flushZcu(
3743pub fn flush(
37773744 wasm: *Wasm,
37783745 arena: Allocator,
37793746 tid: Zcu.PerThread.Id,
37803747 prog_node: std.Progress.Node,
37813748) link.File.FlushError!void {
37823749 // The goal is to never use this because it's only needed if we need to
3783 // write to InternPool, but flushZcu is too late to be writing to the
3750 // write to InternPool, but flush is too late to be writing to the
37843751 // InternPool.
37853752 _ = tid;
37863753 const comp = wasm.base.comp;
......@@ -3832,436 +3799,6 @@ pub fn flushZcu(
38323799 };
38333800}
38343801
3835fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {
3836 dev.check(.lld_linker);
3837
3838 const tracy = trace(@src());
3839 defer tracy.end();
3840
3841 const comp = wasm.base.comp;
3842 const diags = &comp.link_diags;
3843 const shared_memory = comp.config.shared_memory;
3844 const export_memory = comp.config.export_memory;
3845 const import_memory = comp.config.import_memory;
3846 const target = comp.root_mod.resolved_target.result;
3847
3848 const gpa = comp.gpa;
3849
3850 const directory = wasm.base.emit.root_dir; // Just an alias to make it shorter to type.
3851 const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path});
3852
3853 // If there is no Zig code to compile, then we should skip flushing the output file because it
3854 // will not be part of the linker line anyway.
3855 const module_obj_path: ?[]const u8 = if (comp.zcu) |zcu| blk: {
3856 if (zcu.llvm_object == null) {
3857 try wasm.flushZcu(arena, tid, prog_node);
3858 } else {
3859 // `Compilation.flush` has already made LLVM emit this object file for us.
3860 }
3861
3862 if (fs.path.dirname(full_out_path)) |dirname| {
3863 break :blk try fs.path.join(arena, &.{ dirname, wasm.base.zcu_object_sub_path.? });
3864 } else {
3865 break :blk wasm.base.zcu_object_sub_path.?;
3866 }
3867 } else null;
3868
3869 const sub_prog_node = prog_node.start("LLD Link", 0);
3870 defer sub_prog_node.end();
3871
3872 const is_obj = comp.config.output_mode == .Obj;
3873 const compiler_rt_path: ?Path = blk: {
3874 if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path;
3875 if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path;
3876 break :blk null;
3877 };
3878 const ubsan_rt_path: ?Path = blk: {
3879 if (comp.ubsan_rt_lib) |lib| break :blk lib.full_object_path;
3880 if (comp.ubsan_rt_obj) |obj| break :blk obj.full_object_path;
3881 break :blk null;
3882 };
3883
3884 const id_symlink_basename = "lld.id";
3885
3886 var man: Cache.Manifest = undefined;
3887 defer if (!wasm.base.disable_lld_caching) man.deinit();
3888
3889 var digest: [Cache.hex_digest_len]u8 = undefined;
3890
3891 if (!wasm.base.disable_lld_caching) {
3892 man = comp.cache_parent.obtain();
3893
3894 // We are about to obtain this lock, so here we give other processes a chance first.
3895 wasm.base.releaseLock();
3896
3897 comptime assert(Compilation.link_hash_implementation_version == 14);
3898
3899 try link.hashInputs(&man, comp.link_inputs);
3900 for (comp.c_object_table.keys()) |key| {
3901 _ = try man.addFilePath(key.status.success.object_path, null);
3902 }
3903 try man.addOptionalFile(module_obj_path);
3904 try man.addOptionalFilePath(compiler_rt_path);
3905 try man.addOptionalFilePath(ubsan_rt_path);
3906 man.hash.addOptionalBytes(wasm.entry_name.slice(wasm));
3907 man.hash.add(wasm.base.stack_size);
3908 man.hash.add(wasm.base.build_id);
3909 man.hash.add(import_memory);
3910 man.hash.add(export_memory);
3911 man.hash.add(wasm.import_table);
3912 man.hash.add(wasm.export_table);
3913 man.hash.addOptional(wasm.initial_memory);
3914 man.hash.addOptional(wasm.max_memory);
3915 man.hash.add(shared_memory);
3916 man.hash.addOptional(wasm.global_base);
3917 man.hash.addListOfBytes(wasm.export_symbol_names);
3918 // strip does not need to go into the linker hash because it is part of the hash namespace
3919
3920 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
3921 _ = try man.hit();
3922 digest = man.final();
3923
3924 var prev_digest_buf: [digest.len]u8 = undefined;
3925 const prev_digest: []u8 = Cache.readSmallFile(
3926 directory.handle,
3927 id_symlink_basename,
3928 &prev_digest_buf,
3929 ) catch |err| blk: {
3930 log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
3931 // Handle this as a cache miss.
3932 break :blk prev_digest_buf[0..0];
3933 };
3934 if (mem.eql(u8, prev_digest, &digest)) {
3935 log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
3936 // Hot diggity dog! The output binary is already there.
3937 wasm.base.lock = man.toOwnedLock();
3938 return;
3939 }
3940 log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
3941
3942 // We are about to change the output file to be different, so we invalidate the build hash now.
3943 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
3944 error.FileNotFound => {},
3945 else => |e| return e,
3946 };
3947 }
3948
3949 if (is_obj) {
3950 // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy
3951 // here. TODO: think carefully about how we can avoid this redundant operation when doing
3952 // build-obj. See also the corresponding TODO in linkAsArchive.
3953 const the_object_path = blk: {
3954 if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
3955
3956 if (comp.c_object_table.count() != 0)
3957 break :blk comp.c_object_table.keys()[0].status.success.object_path;
3958
3959 if (module_obj_path) |p|
3960 break :blk Path.initCwd(p);
3961
3962 // TODO I think this is unreachable. Audit this situation when solving the above TODO
3963 // regarding eliding redundant object -> object transformations.
3964 return error.NoObjectsToLink;
3965 };
3966 try fs.Dir.copyFile(
3967 the_object_path.root_dir.handle,
3968 the_object_path.sub_path,
3969 directory.handle,
3970 wasm.base.emit.sub_path,
3971 .{},
3972 );
3973 } else {
3974 // Create an LLD command line and invoke it.
3975 var argv = std.ArrayList([]const u8).init(gpa);
3976 defer argv.deinit();
3977 // We will invoke ourselves as a child process to gain access to LLD.
3978 // This is necessary because LLD does not behave properly as a library -
3979 // it calls exit() and does not reset all global data between invocations.
3980 const linker_command = "wasm-ld";
3981 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
3982 try argv.append("--error-limit=0");
3983
3984 if (comp.config.lto != .none) {
3985 switch (comp.root_mod.optimize_mode) {
3986 .Debug => {},
3987 .ReleaseSmall => try argv.append("-O2"),
3988 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
3989 }
3990 }
3991
3992 if (import_memory) {
3993 try argv.append("--import-memory");
3994 }
3995
3996 if (export_memory) {
3997 try argv.append("--export-memory");
3998 }
3999
4000 if (wasm.import_table) {
4001 assert(!wasm.export_table);
4002 try argv.append("--import-table");
4003 }
4004
4005 if (wasm.export_table) {
4006 assert(!wasm.import_table);
4007 try argv.append("--export-table");
4008 }
4009
4010 // For wasm-ld we only need to specify '--no-gc-sections' when the user explicitly
4011 // specified it as garbage collection is enabled by default.
4012 if (!wasm.base.gc_sections) {
4013 try argv.append("--no-gc-sections");
4014 }
4015
4016 if (comp.config.debug_format == .strip) {
4017 try argv.append("-s");
4018 }
4019
4020 if (wasm.initial_memory) |initial_memory| {
4021 const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory});
4022 try argv.append(arg);
4023 }
4024
4025 if (wasm.max_memory) |max_memory| {
4026 const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory});
4027 try argv.append(arg);
4028 }
4029
4030 if (shared_memory) {
4031 try argv.append("--shared-memory");
4032 }
4033
4034 if (wasm.global_base) |global_base| {
4035 const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base});
4036 try argv.append(arg);
4037 } else {
4038 // We prepend it by default, so when a stack overflow happens the runtime will trap correctly,
4039 // rather than silently overwrite all global declarations. See https://github.com/ziglang/zig/issues/4496
4040 //
4041 // The user can overwrite this behavior by setting the global-base
4042 try argv.append("--stack-first");
4043 }
4044
4045 // Users are allowed to specify which symbols they want to export to the wasm host.
4046 for (wasm.export_symbol_names) |symbol_name| {
4047 const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name});
4048 try argv.append(arg);
4049 }
4050
4051 if (comp.config.rdynamic) {
4052 try argv.append("--export-dynamic");
4053 }
4054
4055 if (wasm.entry_name.slice(wasm)) |entry_name| {
4056 try argv.appendSlice(&.{ "--entry", entry_name });
4057 } else {
4058 try argv.append("--no-entry");
4059 }
4060
4061 try argv.appendSlice(&.{
4062 "-z",
4063 try std.fmt.allocPrint(arena, "stack-size={d}", .{wasm.base.stack_size}),
4064 });
4065
4066 switch (wasm.base.build_id) {
4067 .none => try argv.append("--build-id=none"),
4068 .fast, .uuid, .sha1 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
4069 @tagName(wasm.base.build_id),
4070 })),
4071 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{
4072 std.fmt.fmtSliceHexLower(hs.toSlice()),
4073 })),
4074 .md5 => {},
4075 }
4076
4077 if (wasm.import_symbols) {
4078 try argv.append("--allow-undefined");
4079 }
4080
4081 if (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic) {
4082 try argv.append("--shared");
4083 }
4084 if (comp.config.pie) {
4085 try argv.append("--pie");
4086 }
4087
4088 try argv.appendSlice(&.{ "-o", full_out_path });
4089
4090 if (target.cpu.arch == .wasm64) {
4091 try argv.append("-mwasm64");
4092 }
4093
4094 const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or
4095 (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic);
4096
4097 if (comp.config.link_libc and is_exe_or_dyn_lib) {
4098 if (target.os.tag == .wasi) {
4099 for (comp.wasi_emulated_libs) |crt_file| {
4100 try argv.append(try comp.crtFileAsString(
4101 arena,
4102 wasi_libc.emulatedLibCRFileLibName(crt_file),
4103 ));
4104 }
4105
4106 try argv.append(try comp.crtFileAsString(
4107 arena,
4108 wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),
4109 ));
4110 try argv.append(try comp.crtFileAsString(arena, "libc.a"));
4111 }
4112
4113 if (comp.zigc_static_lib) |zigc| {
4114 try argv.append(try zigc.full_object_path.toString(arena));
4115 }
4116
4117 if (comp.config.link_libcpp) {
4118 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
4119 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
4120 }
4121 }
4122
4123 // Positional arguments to the linker such as object files.
4124 var whole_archive = false;
4125 for (comp.link_inputs) |link_input| switch (link_input) {
4126 .object, .archive => |obj| {
4127 if (obj.must_link and !whole_archive) {
4128 try argv.append("-whole-archive");
4129 whole_archive = true;
4130 } else if (!obj.must_link and whole_archive) {
4131 try argv.append("-no-whole-archive");
4132 whole_archive = false;
4133 }
4134 try argv.append(try obj.path.toString(arena));
4135 },
4136 .dso => |dso| {
4137 try argv.append(try dso.path.toString(arena));
4138 },
4139 .dso_exact => unreachable,
4140 .res => unreachable,
4141 };
4142 if (whole_archive) {
4143 try argv.append("-no-whole-archive");
4144 whole_archive = false;
4145 }
4146
4147 for (comp.c_object_table.keys()) |key| {
4148 try argv.append(try key.status.success.object_path.toString(arena));
4149 }
4150 if (module_obj_path) |p| {
4151 try argv.append(p);
4152 }
4153
4154 if (compiler_rt_path) |p| {
4155 try argv.append(try p.toString(arena));
4156 }
4157
4158 if (ubsan_rt_path) |p| {
4159 try argv.append(try p.toStringZ(arena));
4160 }
4161
4162 if (comp.verbose_link) {
4163 // Skip over our own name so that the LLD linker name is the first argv item.
4164 Compilation.dump_argv(argv.items[1..]);
4165 }
4166
4167 if (std.process.can_spawn) {
4168 // If possible, we run LLD as a child process because it does not always
4169 // behave properly as a library, unfortunately.
4170 // https://github.com/ziglang/zig/issues/3825
4171 var child = std.process.Child.init(argv.items, arena);
4172 if (comp.clang_passthrough_mode) {
4173 child.stdin_behavior = .Inherit;
4174 child.stdout_behavior = .Inherit;
4175 child.stderr_behavior = .Inherit;
4176
4177 const term = child.spawnAndWait() catch |err| {
4178 log.err("failed to spawn (passthrough mode) LLD {s}: {s}", .{ argv.items[0], @errorName(err) });
4179 return error.UnableToSpawnWasm;
4180 };
4181 switch (term) {
4182 .Exited => |code| {
4183 if (code != 0) {
4184 std.process.exit(code);
4185 }
4186 },
4187 else => std.process.abort(),
4188 }
4189 } else {
4190 child.stdin_behavior = .Ignore;
4191 child.stdout_behavior = .Ignore;
4192 child.stderr_behavior = .Pipe;
4193
4194 try child.spawn();
4195
4196 const stderr = try child.stderr.?.reader().readAllAlloc(arena, std.math.maxInt(usize));
4197
4198 const term = child.wait() catch |err| {
4199 log.err("failed to spawn LLD {s}: {s}", .{ argv.items[0], @errorName(err) });
4200 return error.UnableToSpawnWasm;
4201 };
4202
4203 switch (term) {
4204 .Exited => |code| {
4205 if (code != 0) {
4206 diags.lockAndParseLldStderr(linker_command, stderr);
4207 return error.LinkFailure;
4208 }
4209 },
4210 else => {
4211 return diags.fail("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
4212 },
4213 }
4214
4215 if (stderr.len != 0) {
4216 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
4217 }
4218 }
4219 } else {
4220 const exit_code = try lldMain(arena, argv.items, false);
4221 if (exit_code != 0) {
4222 if (comp.clang_passthrough_mode) {
4223 std.process.exit(exit_code);
4224 } else {
4225 return diags.fail("{s} returned exit code {d}:\n{s}", .{ argv.items[0], exit_code });
4226 }
4227 }
4228 }
4229
4230 // Give +x to the .wasm file if it is an executable and the OS is WASI.
4231 // Some systems may be configured to execute such binaries directly. Even if that
4232 // is not the case, it means we will get "exec format error" when trying to run
4233 // it, and then can react to that in the same way as trying to run an ELF file
4234 // from a foreign CPU architecture.
4235 if (fs.has_executable_bit and target.os.tag == .wasi and
4236 comp.config.output_mode == .Exe)
4237 {
4238 // TODO: what's our strategy for reporting linker errors from this function?
4239 // report a nice error here with the file path if it fails instead of
4240 // just returning the error code.
4241 // chmod does not interact with umask, so we use a conservative -rwxr--r-- here.
4242 std.posix.fchmodat(fs.cwd().fd, full_out_path, 0o744, 0) catch |err| switch (err) {
4243 error.OperationNotSupported => unreachable, // Not a symlink.
4244 else => |e| return e,
4245 };
4246 }
4247 }
4248
4249 if (!wasm.base.disable_lld_caching) {
4250 // Update the file with the digest. If it fails we can continue; it only
4251 // means that the next invocation will have an unnecessary cache miss.
4252 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
4253 log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)});
4254 };
4255 // Again failure here only means an unnecessary cache miss.
4256 man.writeManifest() catch |err| {
4257 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
4258 };
4259 // We hang on to this lock so that the output file path can be used without
4260 // other processes clobbering it.
4261 wasm.base.lock = man.toOwnedLock();
4262 }
4263}
4264
42653802fn defaultEntrySymbolName(
42663803 preloaded_strings: *const PreloadedStrings,
42673804 wasi_exec_model: std.builtin.WasiExecModel,
src/link/Xcoff.zig-5
......@@ -46,7 +46,6 @@ pub fn createEmpty(
4646 .stack_size = options.stack_size orelse 0,
4747 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
4848 .file = null,
49 .disable_lld_caching = options.disable_lld_caching,
5049 .build_id = options.build_id,
5150 },
5251 };
......@@ -105,10 +104,6 @@ pub fn updateExports(
105104}
106105
107106pub fn flush(self: *Xcoff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
108 return self.flushZcu(arena, tid, prog_node);
109}
110
111pub fn flushZcu(self: *Xcoff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
112107 _ = self;
113108 _ = arena;
114109 _ = tid;
src/main.zig+9-9
......@@ -867,9 +867,9 @@ fn buildOutputType(
867867 var linker_allow_undefined_version: bool = false;
868868 var linker_enable_new_dtags: ?bool = null;
869869 var disable_c_depfile = false;
870 var linker_sort_section: ?link.File.Elf.SortSection = null;
870 var linker_sort_section: ?link.File.Lld.Elf.SortSection = null;
871871 var linker_gc_sections: ?bool = null;
872 var linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null;
872 var linker_compress_debug_sections: ?link.File.Lld.Elf.CompressDebugSections = null;
873873 var linker_allow_shlib_undefined: ?bool = null;
874874 var allow_so_scripts: bool = false;
875875 var linker_bind_global_refs_locally: ?bool = null;
......@@ -921,7 +921,7 @@ fn buildOutputType(
921921 var debug_compiler_runtime_libs = false;
922922 var opt_incremental: ?bool = null;
923923 var install_name: ?[]const u8 = null;
924 var hash_style: link.File.Elf.HashStyle = .both;
924 var hash_style: link.File.Lld.Elf.HashStyle = .both;
925925 var entitlements: ?[]const u8 = null;
926926 var pagezero_size: ?u64 = null;
927927 var lib_search_strategy: link.UnresolvedInput.SearchStrategy = .paths_first;
......@@ -1196,11 +1196,11 @@ fn buildOutputType(
11961196 install_name = args_iter.nextOrFatal();
11971197 } else if (mem.startsWith(u8, arg, "--compress-debug-sections=")) {
11981198 const param = arg["--compress-debug-sections=".len..];
1199 linker_compress_debug_sections = std.meta.stringToEnum(link.File.Elf.CompressDebugSections, param) orelse {
1199 linker_compress_debug_sections = std.meta.stringToEnum(link.File.Lld.Elf.CompressDebugSections, param) orelse {
12001200 fatal("expected --compress-debug-sections=[none|zlib|zstd], found '{s}'", .{param});
12011201 };
12021202 } else if (mem.eql(u8, arg, "--compress-debug-sections")) {
1203 linker_compress_debug_sections = link.File.Elf.CompressDebugSections.zlib;
1203 linker_compress_debug_sections = link.File.Lld.Elf.CompressDebugSections.zlib;
12041204 } else if (mem.eql(u8, arg, "-pagezero_size")) {
12051205 const next_arg = args_iter.nextOrFatal();
12061206 pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
......@@ -2368,7 +2368,7 @@ fn buildOutputType(
23682368 if (it.only_arg.len == 0) {
23692369 linker_compress_debug_sections = .zlib;
23702370 } else {
2371 linker_compress_debug_sections = std.meta.stringToEnum(link.File.Elf.CompressDebugSections, it.only_arg) orelse {
2371 linker_compress_debug_sections = std.meta.stringToEnum(link.File.Lld.Elf.CompressDebugSections, it.only_arg) orelse {
23722372 fatal("expected [none|zlib|zstd] after --compress-debug-sections, found '{s}'", .{it.only_arg});
23732373 };
23742374 }
......@@ -2505,7 +2505,7 @@ fn buildOutputType(
25052505 linker_print_map = true;
25062506 } else if (mem.eql(u8, arg, "--sort-section")) {
25072507 const arg1 = linker_args_it.nextOrFatal();
2508 linker_sort_section = std.meta.stringToEnum(link.File.Elf.SortSection, arg1) orelse {
2508 linker_sort_section = std.meta.stringToEnum(link.File.Lld.Elf.SortSection, arg1) orelse {
25092509 fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1});
25102510 };
25112511 } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
......@@ -2551,7 +2551,7 @@ fn buildOutputType(
25512551 try linker_export_symbol_names.append(arena, linker_args_it.nextOrFatal());
25522552 } else if (mem.eql(u8, arg, "--compress-debug-sections")) {
25532553 const arg1 = linker_args_it.nextOrFatal();
2554 linker_compress_debug_sections = std.meta.stringToEnum(link.File.Elf.CompressDebugSections, arg1) orelse {
2554 linker_compress_debug_sections = std.meta.stringToEnum(link.File.Lld.Elf.CompressDebugSections, arg1) orelse {
25552555 fatal("expected [none|zlib|zstd] after --compress-debug-sections, found '{s}'", .{arg1});
25562556 };
25572557 } else if (mem.startsWith(u8, arg, "-z")) {
......@@ -2764,7 +2764,7 @@ fn buildOutputType(
27642764 mem.eql(u8, arg, "--hash-style"))
27652765 {
27662766 const next_arg = linker_args_it.nextOrFatal();
2767 hash_style = std.meta.stringToEnum(link.File.Elf.HashStyle, next_arg) orelse {
2767 hash_style = std.meta.stringToEnum(link.File.Lld.Elf.HashStyle, next_arg) orelse {
27682768 fatal("expected [sysv|gnu|both] after --hash-style, found '{s}'", .{
27692769 next_arg,
27702770 });