authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-09 02:26:13-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-12-09 02:26:13-05:00
log391d81a3802e48f59d804746eedc396d60ad3820
treee4e634ad79891ae089b7f02f417bf661824f6679
parent21550bb7cd5596e24a623f5ae1374502e879b553
parent7dd4afb224f4ca747b8eb462c28337ce9a63d38c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7355 from ziglang/lld-child-process

invoke LLD as a child process rather than a library

11 files changed, 332 insertions(+), 267 deletions(-)

lib/std/testing.zig+21
...@@ -247,6 +247,7 @@ test "expectWithinEpsilon" {...@@ -247,6 +247,7 @@ test "expectWithinEpsilon" {
247/// This function is intended to be used only in tests. When the two slices are not247/// This function is intended to be used only in tests. When the two slices are not
248/// equal, prints diagnostics to stderr to show exactly how they are not equal,248/// equal, prints diagnostics to stderr to show exactly how they are not equal,
249/// then aborts.249/// then aborts.
250/// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead.
250pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) void {251pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) void {
251 // TODO better printing of the difference252 // TODO better printing of the difference
252 // If the arrays are small enough we could print the whole thing253 // If the arrays are small enough we could print the whole thing
...@@ -368,6 +369,26 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) void {...@@ -368,6 +369,26 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) void {
368 }369 }
369}370}
370371
372pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) void {
373 if (std.mem.endsWith(u8, actual, expected_ends_with))
374 return;
375
376 const shortened_actual = if (actual.len >= expected_ends_with.len)
377 actual[0..expected_ends_with.len]
378 else
379 actual;
380
381 print("\n====== expected to end with: =========\n", .{});
382 printWithVisibleNewlines(expected_ends_with);
383 print("\n====== instead ended with: ===========\n", .{});
384 printWithVisibleNewlines(shortened_actual);
385 print("\n========= full output: ==============\n", .{});
386 printWithVisibleNewlines(actual);
387 print("\n======================================\n", .{});
388
389 @panic("test failure");
390}
391
371fn printIndicatorLine(source: []const u8, indicator_index: usize) void {392fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
372 const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin|393 const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin|
373 line_begin + 1394 line_begin + 1
src/Compilation.zig+1-1
...@@ -1804,7 +1804,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *...@@ -1804,7 +1804,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
1804 if (comp.clang_preprocessor_mode == .stdout)1804 if (comp.clang_preprocessor_mode == .stdout)
1805 std.process.exit(0);1805 std.process.exit(0);
1806 },1806 },
1807 else => std.process.exit(1),1807 else => std.process.abort(),
1808 }1808 }
1809 } else {1809 } else {
1810 child.stdin_behavior = .Ignore;1810 child.stdin_behavior = .Ignore;
src/link/Coff.zig+60-55
...@@ -907,11 +907,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -907,11 +907,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
907 // Create an LLD command line and invoke it.907 // Create an LLD command line and invoke it.
908 var argv = std.ArrayList([]const u8).init(self.base.allocator);908 var argv = std.ArrayList([]const u8).init(self.base.allocator);
909 defer argv.deinit();909 defer argv.deinit();
910 // The first argument is ignored as LLD is called as a library, set it910 // We will invoke ourselves as a child process to gain access to LLD.
911 // anyway to the correct LLD driver name for this target so that it's911 // This is necessary because LLD does not behave properly as a library -
912 // correctly printed when `verbose_link` is true. This is needed for some912 // it calls exit() and does not reset all global data between invocations.
913 // tools such as CMake when Zig is used as C compiler.913 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" });
914 try argv.append("lld-link");
915914
916 try argv.append("-ERRORLIMIT:0");915 try argv.append("-ERRORLIMIT:0");
917 try argv.append("-NOLOGO");916 try argv.append("-NOLOGO");
...@@ -1149,45 +1148,65 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -1149,45 +1148,65 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
1149 }1148 }
11501149
1151 if (self.base.options.verbose_link) {1150 if (self.base.options.verbose_link) {
1152 Compilation.dump_argv(argv.items);1151 // Skip over our own name so that the LLD linker name is the first argv item.
1152 Compilation.dump_argv(argv.items[1..]);
1153 }1153 }
11541154
1155 const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);1155 // Sadly, we must run LLD as a child process because it does not behave
1156 for (argv.items) |arg, i| {1156 // properly as a library.
1157 new_argv[i] = try arena.dupeZ(u8, arg);1157 const child = try std.ChildProcess.init(argv.items, arena);
1158 }1158 defer child.deinit();
1159
1160 if (comp.clang_passthrough_mode) {
1161 child.stdin_behavior = .Inherit;
1162 child.stdout_behavior = .Inherit;
1163 child.stderr_behavior = .Inherit;
1164
1165 const term = child.spawnAndWait() catch |err| {
1166 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1167 return error.UnableToSpawnSelf;
1168 };
1169 switch (term) {
1170 .Exited => |code| {
1171 if (code != 0) {
1172 // TODO https://github.com/ziglang/zig/issues/6342
1173 std.process.exit(1);
1174 }
1175 },
1176 else => std.process.abort(),
1177 }
1178 } else {
1179 child.stdin_behavior = .Ignore;
1180 child.stdout_behavior = .Ignore;
1181 child.stderr_behavior = .Pipe;
1182
1183 try child.spawn();
1184
1185 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
1186
1187 const term = child.wait() catch |err| {
1188 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1189 return error.UnableToSpawnSelf;
1190 };
1191
1192 switch (term) {
1193 .Exited => |code| {
1194 if (code != 0) {
1195 // TODO parse this output and surface with the Compilation API rather than
1196 // directly outputting to stderr here.
1197 std.debug.print("{s}", .{stderr});
1198 return error.LLDReportedFailure;
1199 }
1200 },
1201 else => {
1202 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1203 return error.LLDCrashed;
1204 },
1205 }
11591206
1160 var stderr_context: LLDContext = .{1207 if (stderr.len != 0) {
1161 .coff = self,1208 std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1162 .data = std.ArrayList(u8).init(self.base.allocator),1209 }
1163 };
1164 defer stderr_context.data.deinit();
1165 var stdout_context: LLDContext = .{
1166 .coff = self,
1167 .data = std.ArrayList(u8).init(self.base.allocator),
1168 };
1169 defer stdout_context.data.deinit();
1170 const llvm = @import("../llvm.zig");
1171 const ok = llvm.Link(
1172 .COFF,
1173 new_argv.ptr,
1174 new_argv.len,
1175 append_diagnostic,
1176 @ptrToInt(&stdout_context),
1177 @ptrToInt(&stderr_context),
1178 );
1179 if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
1180 if (stdout_context.data.items.len != 0) {
1181 std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
1182 }
1183 if (!ok) {
1184 // TODO parse this output and surface with the Compilation API rather than
1185 // directly outputting to stderr here.
1186 std.debug.print("{}", .{stderr_context.data.items});
1187 return error.LLDReportedFailure;
1188 }
1189 if (stderr_context.data.items.len != 0) {
1190 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
1191 }1210 }
1192 }1211 }
11931212
...@@ -1207,20 +1226,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -1207,20 +1226,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
1207 }1226 }
1208}1227}
12091228
1210const LLDContext = struct {
1211 data: std.ArrayList(u8),
1212 coff: *Coff,
1213 oom: bool = false,
1214};
1215
1216fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
1217 const lld_context = @intToPtr(*LLDContext, context);
1218 const msg = ptr[0..len];
1219 lld_context.data.appendSlice(msg) catch |err| switch (err) {
1220 error.OutOfMemory => lld_context.oom = true,
1221 };
1222}
1223
1224pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {1229pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
1225 return self.text_section_virtual_address + decl.link.coff.text_offset;1230 return self.text_section_virtual_address + decl.link.coff.text_offset;
1226}1231}
src/link/Elf.zig+60-56
...@@ -1360,11 +1360,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1360,11 +1360,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1360 // Create an LLD command line and invoke it.1360 // Create an LLD command line and invoke it.
1361 var argv = std.ArrayList([]const u8).init(self.base.allocator);1361 var argv = std.ArrayList([]const u8).init(self.base.allocator);
1362 defer argv.deinit();1362 defer argv.deinit();
1363 // The first argument is ignored as LLD is called as a library, set it1363 // We will invoke ourselves as a child process to gain access to LLD.
1364 // anyway to the correct LLD driver name for this target so that it's1364 // This is necessary because LLD does not behave properly as a library -
1365 // correctly printed when `verbose_link` is true. This is needed for some1365 // it calls exit() and does not reset all global data between invocations.
1366 // tools such as CMake when Zig is used as C compiler.1366 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld.lld" });
1367 try argv.append("ld.lld");
1368 if (is_obj) {1367 if (is_obj) {
1369 try argv.append("-r");1368 try argv.append("-r");
1370 }1369 }
...@@ -1628,46 +1627,65 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1628,46 +1627,65 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1628 }1627 }
16291628
1630 if (self.base.options.verbose_link) {1629 if (self.base.options.verbose_link) {
1631 Compilation.dump_argv(argv.items);1630 // Skip over our own name so that the LLD linker name is the first argv item.
1631 Compilation.dump_argv(argv.items[1..]);
1632 }1632 }
16331633
1634 // Oh, snapplesauce! We need null terminated argv.1634 // Sadly, we must run LLD as a child process because it does not behave
1635 const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);1635 // properly as a library.
1636 for (argv.items) |arg, i| {1636 const child = try std.ChildProcess.init(argv.items, arena);
1637 new_argv[i] = try arena.dupeZ(u8, arg);1637 defer child.deinit();
1638 }
16391638
1640 var stderr_context: LLDContext = .{1639 if (comp.clang_passthrough_mode) {
1641 .elf = self,1640 child.stdin_behavior = .Inherit;
1642 .data = std.ArrayList(u8).init(self.base.allocator),1641 child.stdout_behavior = .Inherit;
1643 };1642 child.stderr_behavior = .Inherit;
1644 defer stderr_context.data.deinit();1643
1645 var stdout_context: LLDContext = .{1644 const term = child.spawnAndWait() catch |err| {
1646 .elf = self,1645 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1647 .data = std.ArrayList(u8).init(self.base.allocator),1646 return error.UnableToSpawnSelf;
1648 };1647 };
1649 defer stdout_context.data.deinit();1648 switch (term) {
1650 const llvm = @import("../llvm.zig");1649 .Exited => |code| {
1651 const ok = llvm.Link(1650 if (code != 0) {
1652 .ELF,1651 // TODO https://github.com/ziglang/zig/issues/6342
1653 new_argv.ptr,1652 std.process.exit(1);
1654 new_argv.len,1653 }
1655 append_diagnostic,1654 },
1656 @ptrToInt(&stdout_context),1655 else => std.process.abort(),
1657 @ptrToInt(&stderr_context),1656 }
1658 );1657 } else {
1659 if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;1658 child.stdin_behavior = .Ignore;
1660 if (stdout_context.data.items.len != 0) {1659 child.stdout_behavior = .Ignore;
1661 std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});1660 child.stderr_behavior = .Pipe;
1662 }1661
1663 if (!ok) {1662 try child.spawn();
1664 // TODO parse this output and surface with the Compilation API rather than1663
1665 // directly outputting to stderr here.1664 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
1666 std.debug.print("{}", .{stderr_context.data.items});1665
1667 return error.LLDReportedFailure;1666 const term = child.wait() catch |err| {
1668 }1667 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1669 if (stderr_context.data.items.len != 0) {1668 return error.UnableToSpawnSelf;
1670 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});1669 };
1670
1671 switch (term) {
1672 .Exited => |code| {
1673 if (code != 0) {
1674 // TODO parse this output and surface with the Compilation API rather than
1675 // directly outputting to stderr here.
1676 std.debug.print("{s}", .{stderr});
1677 return error.LLDReportedFailure;
1678 }
1679 },
1680 else => {
1681 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1682 return error.LLDCrashed;
1683 },
1684 }
1685
1686 if (stderr.len != 0) {
1687 std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1688 }
1671 }1689 }
16721690
1673 if (!self.base.options.disable_lld_caching) {1691 if (!self.base.options.disable_lld_caching) {
...@@ -1686,20 +1704,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1686,20 +1704,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1686 }1704 }
1687}1705}
16881706
1689const LLDContext = struct {
1690 data: std.ArrayList(u8),
1691 elf: *Elf,
1692 oom: bool = false,
1693};
1694
1695fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
1696 const lld_context = @intToPtr(*LLDContext, context);
1697 const msg = ptr[0..len];
1698 lld_context.data.appendSlice(msg) catch |err| switch (err) {
1699 error.OutOfMemory => lld_context.oom = true,
1700 };
1701}
1702
1703fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {1707fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
1704 const target_endian = self.base.options.target.cpu.arch.endian();1708 const target_endian = self.base.options.target.cpu.arch.endian();
1705 switch (self.ptr_width) {1709 switch (self.ptr_width) {
src/link/MachO.zig+61-56
...@@ -489,12 +489,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -489,12 +489,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
489 if (self.base.options.system_linker_hack) {489 if (self.base.options.system_linker_hack) {
490 try argv.append("ld");490 try argv.append("ld");
491 } else {491 } else {
492 // The first argument is ignored as LLD is called as a library, set492 // We will invoke ourselves as a child process to gain access to LLD.
493 // it anyway to the correct LLD driver name for this target so that493 // This is necessary because LLD does not behave properly as a library -
494 // it's correctly printed when `verbose_link` is true. This is494 // it calls exit() and does not reset all global data between invocations.
495 // needed for some tools such as CMake when Zig is used as C495 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld64.lld" });
496 // compiler.
497 try argv.append("ld64");
498496
499 try argv.append("-error-limit");497 try argv.append("-error-limit");
500 try argv.append("0");498 try argv.append("0");
...@@ -660,7 +658,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -660,7 +658,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
660 }658 }
661659
662 if (self.base.options.verbose_link) {660 if (self.base.options.verbose_link) {
663 Compilation.dump_argv(argv.items);661 // Potentially skip over our own name so that the LLD linker name is the first argv item.
662 const adjusted_argv = if (self.base.options.system_linker_hack) argv.items else argv.items[1..];
663 Compilation.dump_argv(adjusted_argv);
664 }664 }
665665
666 // TODO https://github.com/ziglang/zig/issues/6971666 // TODO https://github.com/ziglang/zig/issues/6971
...@@ -685,42 +685,61 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -685,42 +685,61 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
685 return error.LDReportedFailure;685 return error.LDReportedFailure;
686 }686 }
687 } else {687 } else {
688 const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);688 // Sadly, we must run LLD as a child process because it does not behave
689 for (argv.items) |arg, i| {689 // properly as a library.
690 new_argv[i] = try arena.dupeZ(u8, arg);690 const child = try std.ChildProcess.init(argv.items, arena);
691 }691 defer child.deinit();
692
693 if (comp.clang_passthrough_mode) {
694 child.stdin_behavior = .Inherit;
695 child.stdout_behavior = .Inherit;
696 child.stderr_behavior = .Inherit;
697
698 const term = child.spawnAndWait() catch |err| {
699 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
700 return error.UnableToSpawnSelf;
701 };
702 switch (term) {
703 .Exited => |code| {
704 if (code != 0) {
705 // TODO https://github.com/ziglang/zig/issues/6342
706 std.process.exit(1);
707 }
708 },
709 else => std.process.abort(),
710 }
711 } else {
712 child.stdin_behavior = .Ignore;
713 child.stdout_behavior = .Ignore;
714 child.stderr_behavior = .Pipe;
715
716 try child.spawn();
717
718 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
719
720 const term = child.wait() catch |err| {
721 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
722 return error.UnableToSpawnSelf;
723 };
724
725 switch (term) {
726 .Exited => |code| {
727 if (code != 0) {
728 // TODO parse this output and surface with the Compilation API rather than
729 // directly outputting to stderr here.
730 std.debug.print("{s}", .{stderr});
731 return error.LLDReportedFailure;
732 }
733 },
734 else => {
735 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
736 return error.LLDCrashed;
737 },
738 }
692739
693 var stderr_context: LLDContext = .{740 if (stderr.len != 0) {
694 .macho = self,741 std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
695 .data = std.ArrayList(u8).init(self.base.allocator),742 }
696 };
697 defer stderr_context.data.deinit();
698 var stdout_context: LLDContext = .{
699 .macho = self,
700 .data = std.ArrayList(u8).init(self.base.allocator),
701 };
702 defer stdout_context.data.deinit();
703 const llvm = @import("../llvm.zig");
704 const ok = llvm.Link(
705 .MachO,
706 new_argv.ptr,
707 new_argv.len,
708 append_diagnostic,
709 @ptrToInt(&stdout_context),
710 @ptrToInt(&stderr_context),
711 );
712 if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
713 if (stdout_context.data.items.len != 0) {
714 std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
715 }
716 if (!ok) {
717 // TODO parse this output and surface with the Compilation API rather than
718 // directly outputting to stderr here.
719 std.log.err("{}", .{stderr_context.data.items});
720 return error.LLDReportedFailure;
721 }
722 if (stderr_context.data.items.len != 0) {
723 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
724 }743 }
725744
726 // At this stage, LLD has done its job. It is time to patch the resultant745 // At this stage, LLD has done its job. It is time to patch the resultant
...@@ -785,20 +804,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -785,20 +804,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
785 }804 }
786}805}
787806
788const LLDContext = struct {
789 data: std.ArrayList(u8),
790 macho: *MachO,
791 oom: bool = false,
792};
793
794fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
795 const lld_context = @intToPtr(*LLDContext, context);
796 const msg = ptr[0..len];
797 lld_context.data.appendSlice(msg) catch |err| switch (err) {
798 error.OutOfMemory => lld_context.oom = true,
799 };
800}
801
802fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {807fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
803 return switch (arch) {808 return switch (arch) {
804 .aarch64, .aarch64_be, .aarch64_32 => "arm64",809 .aarch64, .aarch64_be, .aarch64_32 => "arm64",
src/link/Wasm.zig+60-55
...@@ -345,11 +345,10 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -345,11 +345,10 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
345 // Create an LLD command line and invoke it.345 // Create an LLD command line and invoke it.
346 var argv = std.ArrayList([]const u8).init(self.base.allocator);346 var argv = std.ArrayList([]const u8).init(self.base.allocator);
347 defer argv.deinit();347 defer argv.deinit();
348 // The first argument is ignored as LLD is called as a library, set it348 // We will invoke ourselves as a child process to gain access to LLD.
349 // anyway to the correct LLD driver name for this target so that it's349 // This is necessary because LLD does not behave properly as a library -
350 // correctly printed when `verbose_link` is true. This is needed for some350 // it calls exit() and does not reset all global data between invocations.
351 // tools such as CMake when Zig is used as C compiler.351 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "wasm-ld" });
352 try argv.append("ld-wasm");
353 if (is_obj) {352 if (is_obj) {
354 try argv.append("-r");353 try argv.append("-r");
355 }354 }
...@@ -399,45 +398,65 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -399,45 +398,65 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
399 }398 }
400399
401 if (self.base.options.verbose_link) {400 if (self.base.options.verbose_link) {
402 Compilation.dump_argv(argv.items);401 // Skip over our own name so that the LLD linker name is the first argv item.
402 Compilation.dump_argv(argv.items[1..]);
403 }403 }
404404
405 const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);405 // Sadly, we must run LLD as a child process because it does not behave
406 for (argv.items) |arg, i| {406 // properly as a library.
407 new_argv[i] = try arena.dupeZ(u8, arg);407 const child = try std.ChildProcess.init(argv.items, arena);
408 }408 defer child.deinit();
409409
410 var stderr_context: LLDContext = .{410 if (comp.clang_passthrough_mode) {
411 .wasm = self,411 child.stdin_behavior = .Inherit;
412 .data = std.ArrayList(u8).init(self.base.allocator),412 child.stdout_behavior = .Inherit;
413 };413 child.stderr_behavior = .Inherit;
414 defer stderr_context.data.deinit();414
415 var stdout_context: LLDContext = .{415 const term = child.spawnAndWait() catch |err| {
416 .wasm = self,416 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
417 .data = std.ArrayList(u8).init(self.base.allocator),417 return error.UnableToSpawnSelf;
418 };418 };
419 defer stdout_context.data.deinit();419 switch (term) {
420 const llvm = @import("../llvm.zig");420 .Exited => |code| {
421 const ok = llvm.Link(421 if (code != 0) {
422 .Wasm,422 // TODO https://github.com/ziglang/zig/issues/6342
423 new_argv.ptr,423 std.process.exit(1);
424 new_argv.len,424 }
425 append_diagnostic,425 },
426 @ptrToInt(&stdout_context),426 else => std.process.abort(),
427 @ptrToInt(&stderr_context),427 }
428 );428 } else {
429 if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;429 child.stdin_behavior = .Ignore;
430 if (stdout_context.data.items.len != 0) {430 child.stdout_behavior = .Ignore;
431 std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});431 child.stderr_behavior = .Pipe;
432 }432
433 if (!ok) {433 try child.spawn();
434 // TODO parse this output and surface with the Compilation API rather than434
435 // directly outputting to stderr here.435 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
436 std.debug.print("{}", .{stderr_context.data.items});436
437 return error.LLDReportedFailure;437 const term = child.wait() catch |err| {
438 }438 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
439 if (stderr_context.data.items.len != 0) {439 return error.UnableToSpawnSelf;
440 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});440 };
441
442 switch (term) {
443 .Exited => |code| {
444 if (code != 0) {
445 // TODO parse this output and surface with the Compilation API rather than
446 // directly outputting to stderr here.
447 std.debug.print("{s}", .{stderr});
448 return error.LLDReportedFailure;
449 }
450 },
451 else => {
452 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
453 return error.LLDCrashed;
454 },
455 }
456
457 if (stderr.len != 0) {
458 std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
459 }
441 }460 }
442461
443 if (!self.base.options.disable_lld_caching) {462 if (!self.base.options.disable_lld_caching) {
...@@ -456,20 +475,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -456,20 +475,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
456 }475 }
457}476}
458477
459const LLDContext = struct {
460 data: std.ArrayList(u8),
461 wasm: *Wasm,
462 oom: bool = false,
463};
464
465fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
466 const lld_context = @intToPtr(*LLDContext, context);
467 const msg = ptr[0..len];
468 lld_context.data.appendSlice(msg) catch |err| switch (err) {
469 error.OutOfMemory => lld_context.oom = true,
470 };
471}
472
473/// Get the current index of a given Decl in the function list478/// Get the current index of a given Decl in the function list
474/// TODO: we could maintain a hash map to potentially make this479/// TODO: we could maintain a hash map to potentially make this
475fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 {480fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 {
src/llvm.zig+9-9
...@@ -1,15 +1,15 @@...@@ -1,15 +1,15 @@
1//! We do this instead of @cImport because the self-hosted compiler is easier1//! We do this instead of @cImport because the self-hosted compiler is easier
2//! to bootstrap if it does not depend on translate-c.2//! to bootstrap if it does not depend on translate-c.
33
4pub const Link = ZigLLDLink;4extern fn ZigLLDLinkCOFF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
5extern fn ZigLLDLink(5extern fn ZigLLDLinkELF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
6 oformat: ObjectFormatType,6extern fn ZigLLDLinkMachO(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
7 args: [*:null]const ?[*:0]const u8,7extern fn ZigLLDLinkWasm(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
8 arg_count: usize,8
9 append_diagnostic: fn (context: usize, ptr: [*]const u8, len: usize) callconv(.C) void,9pub const LinkCOFF = ZigLLDLinkCOFF;
10 context_stdout: usize,10pub const LinkELF = ZigLLDLinkELF;
11 context_stderr: usize,11pub const LinkMachO = ZigLLDLinkMachO;
12) bool;12pub const LinkWasm = ZigLLDLinkWasm;
1313
14pub const ObjectFormatType = extern enum(c_int) {14pub const ObjectFormatType = extern enum(c_int) {
15 Unknown,15 Unknown,
src/main.zig+39
...@@ -176,6 +176,12 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -176,6 +176,12 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
176 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))176 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
177 {177 {
178 return punt_to_clang(arena, args);178 return punt_to_clang(arena, args);
179 } else if (mem.eql(u8, cmd, "ld.lld") or
180 mem.eql(u8, cmd, "ld64.lld") or
181 mem.eql(u8, cmd, "lld-link") or
182 mem.eql(u8, cmd, "wasm-ld"))
183 {
184 return punt_to_lld(arena, args);
179 } else if (mem.eql(u8, cmd, "build")) {185 } else if (mem.eql(u8, cmd, "build")) {
180 return cmdBuild(gpa, arena, cmd_args);186 return cmdBuild(gpa, arena, cmd_args);
181 } else if (mem.eql(u8, cmd, "fmt")) {187 } else if (mem.eql(u8, cmd, "fmt")) {
...@@ -2819,6 +2825,39 @@ fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory}...@@ -2819,6 +2825,39 @@ fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory}
2819 process.exit(@bitCast(u8, @truncate(i8, exit_code)));2825 process.exit(@bitCast(u8, @truncate(i8, exit_code)));
2820}2826}
28212827
2828/// The first argument determines which backend is invoked. The options are:
2829/// * `ld.lld` - ELF
2830/// * `ld64.lld` - Mach-O
2831/// * `lld-link` - COFF
2832/// * `wasm-ld` - WebAssembly
2833/// TODO https://github.com/ziglang/zig/issues/3257
2834pub fn punt_to_lld(arena: *Allocator, args: []const []const u8) error{OutOfMemory} {
2835 if (!build_options.have_llvm)
2836 fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]});
2837 // Convert the args to the format LLD expects.
2838 // We subtract 1 to shave off the zig binary from args[0].
2839 const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null);
2840 for (args[1..]) |arg, i| {
2841 argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
2842 }
2843 const exit_code = rc: {
2844 const llvm = @import("llvm.zig");
2845 const argc = @intCast(c_int, argv.len);
2846 if (mem.eql(u8, args[1], "ld.lld")) {
2847 break :rc llvm.LinkELF(argc, argv.ptr, true);
2848 } else if (mem.eql(u8, args[1], "ld64.lld")) {
2849 break :rc llvm.LinkMachO(argc, argv.ptr, true);
2850 } else if (mem.eql(u8, args[1], "lld-link")) {
2851 break :rc llvm.LinkCOFF(argc, argv.ptr, true);
2852 } else if (mem.eql(u8, args[1], "wasm-ld")) {
2853 break :rc llvm.LinkWasm(argc, argv.ptr, true);
2854 } else {
2855 unreachable;
2856 }
2857 };
2858 process.exit(@bitCast(u8, @truncate(i8, exit_code)));
2859}
2860
2822const clang_args = @import("clang_options.zig").list;2861const clang_args = @import("clang_options.zig").list;
28232862
2824pub const ClangArgIterator = struct {2863pub const ClangArgIterator = struct {
src/zig_llvm.cpp+15-30
...@@ -1056,39 +1056,24 @@ bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size...@@ -1056,39 +1056,24 @@ bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size
1056 return false;1056 return false;
1057}1057}
10581058
1059int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early) {
1060 std::vector<const char *> args(argv, argv + argc);
1061 return lld::coff::link(args, can_exit_early, llvm::outs(), llvm::errs());
1062}
10591063
1060bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,1064int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early) {
1061 void (*append_diagnostic)(void *, const char *, size_t),1065 std::vector<const char *> args(argv, argv + argc);
1062 void *context_stdout, void *context_stderr)1066 return lld::elf::link(args, can_exit_early, llvm::outs(), llvm::errs());
1063{1067}
1064 ArrayRef<const char *> array_ref_args(args, arg_count);
1065
1066 MyOStream diag_stdout(append_diagnostic, context_stdout);
1067 MyOStream diag_stderr(append_diagnostic, context_stderr);
1068
1069 switch (oformat) {
1070 case ZigLLVM_UnknownObjectFormat:
1071 case ZigLLVM_XCOFF:
1072 assert(false); // unreachable
1073 break;
1074
1075 case ZigLLVM_COFF:
1076 return lld::coff::link(array_ref_args, false, diag_stdout, diag_stderr);
1077
1078 case ZigLLVM_ELF:
1079 return lld::elf::link(array_ref_args, false, diag_stdout, diag_stderr);
1080
1081 case ZigLLVM_MachO:
1082 return lld::mach_o::link(array_ref_args, false, diag_stdout, diag_stderr);
10831068
1084 case ZigLLVM_Wasm:1069int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early) {
1085 return lld::wasm::link(array_ref_args, false, diag_stdout, diag_stderr);1070 std::vector<const char *> args(argv, argv + argc);
1071 return lld::mach_o::link(args, can_exit_early, llvm::outs(), llvm::errs());
1072}
10861073
1087 default:1074int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early) {
1088 break;1075 std::vector<const char *> args(argv, argv + argc);
1089 }1076 return lld::wasm::link(args, can_exit_early, llvm::outs(), llvm::errs());
1090 assert(false); // unreachable
1091 abort();
1092}1077}
10931078
1094static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) {1079static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) {
src/zig_llvm.h+4-3
...@@ -507,9 +507,10 @@ ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor...@@ -507,9 +507,10 @@ ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor
507ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os);507ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os);
508ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi);508ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi);
509509
510ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,510ZIG_EXTERN_C int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early);
511 void (*append_diagnostic)(void *, const char *, size_t),511ZIG_EXTERN_C int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early);
512 void *context_stdout, void *context_stderr);512ZIG_EXTERN_C int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early);
513ZIG_EXTERN_C int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early);
513514
514ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,515ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,
515 enum ZigLLVM_OSType os_type);516 enum ZigLLVM_OSType os_type);
test/cli.zig+2-2
...@@ -92,13 +92,13 @@ fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess...@@ -92,13 +92,13 @@ fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess
92fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {92fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
93 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" });93 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" });
94 const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" });94 const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" });
95 testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 1 tests passed.\n"));95 testing.expectStringEndsWith(test_result.stderr, "All 1 tests passed.\n");
96}96}
9797
98fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {98fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
99 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });99 _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
100 const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" });100 const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" });
101 testing.expect(std.mem.eql(u8, run_result.stderr, "info: All your codebase are belong to us.\n"));101 testing.expectEqualStrings("info: All your codebase are belong to us.\n", run_result.stderr);
102}102}
103103
104fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {104fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {