authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-09 02:26:13-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-09 00:32:30-07:00
log85c1db9222f6aa9abbc50309dfb7ce56b3cb6cf0
tree5e58184b91fe86ce5fc060b5bfda6b9be99b709b
parent40e37d4324dd4e74c2a3285a73942b6ae50d003e

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

invoke LLD as a child process rather than a library

11 files changed, 332 insertions(+), 254 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
...@@ -1756,7 +1756,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *...@@ -1756,7 +1756,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
1756 if (comp.clang_preprocessor_mode == .stdout)1756 if (comp.clang_preprocessor_mode == .stdout)
1757 std.process.exit(0);1757 std.process.exit(0);
1758 },1758 },
1759 else => std.process.exit(1),1759 else => std.process.abort(),
1760 }1760 }
1761 } else {1761 } else {
1762 child.stdin_behavior = .Ignore;1762 child.stdin_behavior = .Ignore;
src/link/Coff.zig+60-52
...@@ -907,8 +907,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -907,8 +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 // Even though we're calling LLD as a library it thinks the first argument is its own exe name.910 // We will invoke ourselves as a child process to gain access to LLD.
911 try argv.append("lld");911 // This is necessary because LLD does not behave properly as a library -
912 // it calls exit() and does not reset all global data between invocations.
913 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" });
912914
913 try argv.append("-ERRORLIMIT:0");915 try argv.append("-ERRORLIMIT:0");
914 try argv.append("-NOLOGO");916 try argv.append("-NOLOGO");
...@@ -1146,45 +1148,65 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -1146,45 +1148,65 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
1146 }1148 }
11471149
1148 if (self.base.options.verbose_link) {1150 if (self.base.options.verbose_link) {
1149 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..]);
1150 }1153 }
11511154
1152 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
1153 for (argv.items) |arg, i| {1156 // properly as a library.
1154 new_argv[i] = try arena.dupeZ(u8, arg);1157 const child = try std.ChildProcess.init(argv.items, arena);
1155 }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 }
11561206
1157 var stderr_context: LLDContext = .{1207 if (stderr.len != 0) {
1158 .coff = self,1208 std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1159 .data = std.ArrayList(u8).init(self.base.allocator),1209 }
1160 };
1161 defer stderr_context.data.deinit();
1162 var stdout_context: LLDContext = .{
1163 .coff = self,
1164 .data = std.ArrayList(u8).init(self.base.allocator),
1165 };
1166 defer stdout_context.data.deinit();
1167 const llvm = @import("../llvm.zig");
1168 const ok = llvm.Link(
1169 .COFF,
1170 new_argv.ptr,
1171 new_argv.len,
1172 append_diagnostic,
1173 @ptrToInt(&stdout_context),
1174 @ptrToInt(&stderr_context),
1175 );
1176 if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
1177 if (stdout_context.data.items.len != 0) {
1178 std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
1179 }
1180 if (!ok) {
1181 // TODO parse this output and surface with the Compilation API rather than
1182 // directly outputting to stderr here.
1183 std.debug.print("{}", .{stderr_context.data.items});
1184 return error.LLDReportedFailure;
1185 }
1186 if (stderr_context.data.items.len != 0) {
1187 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
1188 }1210 }
1189 }1211 }
11901212
...@@ -1204,20 +1226,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -1204,20 +1226,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
1204 }1226 }
1205}1227}
12061228
1207const LLDContext = struct {
1208 data: std.ArrayList(u8),
1209 coff: *Coff,
1210 oom: bool = false,
1211};
1212
1213fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
1214 const lld_context = @intToPtr(*LLDContext, context);
1215 const msg = ptr[0..len];
1216 lld_context.data.appendSlice(msg) catch |err| switch (err) {
1217 error.OutOfMemory => lld_context.oom = true,
1218 };
1219}
1220
1221pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {1229pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
1222 return self.text_section_virtual_address + decl.link.coff.text_offset;1230 return self.text_section_virtual_address + decl.link.coff.text_offset;
1223}1231}
src/link/Elf.zig+60-53
...@@ -1360,8 +1360,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1360,8 +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 // Even though we're calling LLD as a library it thinks the first argument is its own exe name.1363 // We will invoke ourselves as a child process to gain access to LLD.
1364 try argv.append("lld");1364 // This is necessary because LLD does not behave properly as a library -
1365 // it calls exit() and does not reset all global data between invocations.
1366 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld.lld" });
1365 if (is_obj) {1367 if (is_obj) {
1366 try argv.append("-r");1368 try argv.append("-r");
1367 }1369 }
...@@ -1621,46 +1623,65 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1621,46 +1623,65 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1621 }1623 }
16221624
1623 if (self.base.options.verbose_link) {1625 if (self.base.options.verbose_link) {
1624 Compilation.dump_argv(argv.items);1626 // Skip over our own name so that the LLD linker name is the first argv item.
1627 Compilation.dump_argv(argv.items[1..]);
1625 }1628 }
16261629
1627 // Oh, snapplesauce! We need null terminated argv.1630 // Sadly, we must run LLD as a child process because it does not behave
1628 const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);1631 // properly as a library.
1629 for (argv.items) |arg, i| {1632 const child = try std.ChildProcess.init(argv.items, arena);
1630 new_argv[i] = try arena.dupeZ(u8, arg);1633 defer child.deinit();
1631 }
16321634
1633 var stderr_context: LLDContext = .{1635 if (comp.clang_passthrough_mode) {
1634 .elf = self,1636 child.stdin_behavior = .Inherit;
1635 .data = std.ArrayList(u8).init(self.base.allocator),1637 child.stdout_behavior = .Inherit;
1636 };1638 child.stderr_behavior = .Inherit;
1637 defer stderr_context.data.deinit();1639
1638 var stdout_context: LLDContext = .{1640 const term = child.spawnAndWait() catch |err| {
1639 .elf = self,1641 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1640 .data = std.ArrayList(u8).init(self.base.allocator),1642 return error.UnableToSpawnSelf;
1641 };1643 };
1642 defer stdout_context.data.deinit();1644 switch (term) {
1643 const llvm = @import("../llvm.zig");1645 .Exited => |code| {
1644 const ok = llvm.Link(1646 if (code != 0) {
1645 .ELF,1647 // TODO https://github.com/ziglang/zig/issues/6342
1646 new_argv.ptr,1648 std.process.exit(1);
1647 new_argv.len,1649 }
1648 append_diagnostic,1650 },
1649 @ptrToInt(&stdout_context),1651 else => std.process.abort(),
1650 @ptrToInt(&stderr_context),1652 }
1651 );1653 } else {
1652 if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;1654 child.stdin_behavior = .Ignore;
1653 if (stdout_context.data.items.len != 0) {1655 child.stdout_behavior = .Ignore;
1654 std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});1656 child.stderr_behavior = .Pipe;
1655 }1657
1656 if (!ok) {1658 try child.spawn();
1657 // TODO parse this output and surface with the Compilation API rather than1659
1658 // directly outputting to stderr here.1660 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
1659 std.debug.print("{}", .{stderr_context.data.items});1661
1660 return error.LLDReportedFailure;1662 const term = child.wait() catch |err| {
1661 }1663 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1662 if (stderr_context.data.items.len != 0) {1664 return error.UnableToSpawnSelf;
1663 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});1665 };
1666
1667 switch (term) {
1668 .Exited => |code| {
1669 if (code != 0) {
1670 // TODO parse this output and surface with the Compilation API rather than
1671 // directly outputting to stderr here.
1672 std.debug.print("{s}", .{stderr});
1673 return error.LLDReportedFailure;
1674 }
1675 },
1676 else => {
1677 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1678 return error.LLDCrashed;
1679 },
1680 }
1681
1682 if (stderr.len != 0) {
1683 std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1684 }
1664 }1685 }
16651686
1666 if (!self.base.options.disable_lld_caching) {1687 if (!self.base.options.disable_lld_caching) {
...@@ -1679,20 +1700,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1679,20 +1700,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1679 }1700 }
1680}1701}
16811702
1682const LLDContext = struct {
1683 data: std.ArrayList(u8),
1684 elf: *Elf,
1685 oom: bool = false,
1686};
1687
1688fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
1689 const lld_context = @intToPtr(*LLDContext, context);
1690 const msg = ptr[0..len];
1691 lld_context.data.appendSlice(msg) catch |err| switch (err) {
1692 error.OutOfMemory => lld_context.oom = true,
1693 };
1694}
1695
1696fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {1703fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
1697 const target_endian = self.base.options.target.cpu.arch.endian();1704 const target_endian = self.base.options.target.cpu.arch.endian();
1698 switch (self.ptr_width) {1705 switch (self.ptr_width) {
src/link/MachO.zig+61-52
...@@ -544,8 +544,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -544,8 +544,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
544 if (self.base.options.system_linker_hack) {544 if (self.base.options.system_linker_hack) {
545 try argv.append("ld");545 try argv.append("ld");
546 } else {546 } else {
547 // Even though we're calling LLD as a library it thinks the first argument is its own exe name.547 // We will invoke ourselves as a child process to gain access to LLD.
548 try argv.append("lld");548 // This is necessary because LLD does not behave properly as a library -
549 // it calls exit() and does not reset all global data between invocations.
550 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld64.lld" });
549551
550 try argv.append("-error-limit");552 try argv.append("-error-limit");
551 try argv.append("0");553 try argv.append("0");
...@@ -711,7 +713,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -711,7 +713,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
711 }713 }
712714
713 if (self.base.options.verbose_link) {715 if (self.base.options.verbose_link) {
714 Compilation.dump_argv(argv.items);716 // Potentially skip over our own name so that the LLD linker name is the first argv item.
717 const adjusted_argv = if (self.base.options.system_linker_hack) argv.items else argv.items[1..];
718 Compilation.dump_argv(adjusted_argv);
715 }719 }
716720
717 // TODO https://github.com/ziglang/zig/issues/6971721 // TODO https://github.com/ziglang/zig/issues/6971
...@@ -736,42 +740,61 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -736,42 +740,61 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
736 return error.LDReportedFailure;740 return error.LDReportedFailure;
737 }741 }
738 } else {742 } else {
739 const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);743 // Sadly, we must run LLD as a child process because it does not behave
740 for (argv.items) |arg, i| {744 // properly as a library.
741 new_argv[i] = try arena.dupeZ(u8, arg);745 const child = try std.ChildProcess.init(argv.items, arena);
742 }746 defer child.deinit();
747
748 if (comp.clang_passthrough_mode) {
749 child.stdin_behavior = .Inherit;
750 child.stdout_behavior = .Inherit;
751 child.stderr_behavior = .Inherit;
752
753 const term = child.spawnAndWait() catch |err| {
754 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
755 return error.UnableToSpawnSelf;
756 };
757 switch (term) {
758 .Exited => |code| {
759 if (code != 0) {
760 // TODO https://github.com/ziglang/zig/issues/6342
761 std.process.exit(1);
762 }
763 },
764 else => std.process.abort(),
765 }
766 } else {
767 child.stdin_behavior = .Ignore;
768 child.stdout_behavior = .Ignore;
769 child.stderr_behavior = .Pipe;
770
771 try child.spawn();
772
773 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
774
775 const term = child.wait() catch |err| {
776 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
777 return error.UnableToSpawnSelf;
778 };
779
780 switch (term) {
781 .Exited => |code| {
782 if (code != 0) {
783 // TODO parse this output and surface with the Compilation API rather than
784 // directly outputting to stderr here.
785 std.debug.print("{s}", .{stderr});
786 return error.LLDReportedFailure;
787 }
788 },
789 else => {
790 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
791 return error.LLDCrashed;
792 },
793 }
743794
744 var stderr_context: LLDContext = .{795 if (stderr.len != 0) {
745 .macho = self,796 std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
746 .data = std.ArrayList(u8).init(self.base.allocator),797 }
747 };
748 defer stderr_context.data.deinit();
749 var stdout_context: LLDContext = .{
750 .macho = self,
751 .data = std.ArrayList(u8).init(self.base.allocator),
752 };
753 defer stdout_context.data.deinit();
754 const llvm = @import("../llvm.zig");
755 const ok = llvm.Link(
756 .MachO,
757 new_argv.ptr,
758 new_argv.len,
759 append_diagnostic,
760 @ptrToInt(&stdout_context),
761 @ptrToInt(&stderr_context),
762 );
763 if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
764 if (stdout_context.data.items.len != 0) {
765 std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
766 }
767 if (!ok) {
768 // TODO parse this output and surface with the Compilation API rather than
769 // directly outputting to stderr here.
770 std.debug.print("{}", .{stderr_context.data.items});
771 return error.LLDReportedFailure;
772 }
773 if (stderr_context.data.items.len != 0) {
774 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
775 }798 }
776 }799 }
777 }800 }
...@@ -792,20 +815,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -792,20 +815,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
792 }815 }
793}816}
794817
795const LLDContext = struct {
796 data: std.ArrayList(u8),
797 macho: *MachO,
798 oom: bool = false,
799};
800
801fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
802 const lld_context = @intToPtr(*LLDContext, context);
803 const msg = ptr[0..len];
804 lld_context.data.appendSlice(msg) catch |err| switch (err) {
805 error.OutOfMemory => lld_context.oom = true,
806 };
807}
808
809fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {818fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
810 return switch (arch) {819 return switch (arch) {
811 .aarch64, .aarch64_be, .aarch64_32 => "arm64",820 .aarch64, .aarch64_be, .aarch64_32 => "arm64",
src/link/Wasm.zig+60-52
...@@ -345,8 +345,10 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -345,8 +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 // Even though we're calling LLD as a library it thinks the first argument is its own exe name.348 // We will invoke ourselves as a child process to gain access to LLD.
349 try argv.append("lld");349 // This is necessary because LLD does not behave properly as a library -
350 // it calls exit() and does not reset all global data between invocations.
351 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "wasm-ld" });
350 if (is_obj) {352 if (is_obj) {
351 try argv.append("-r");353 try argv.append("-r");
352 }354 }
...@@ -396,45 +398,65 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -396,45 +398,65 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
396 }398 }
397399
398 if (self.base.options.verbose_link) {400 if (self.base.options.verbose_link) {
399 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..]);
400 }403 }
401404
402 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
403 for (argv.items) |arg, i| {406 // properly as a library.
404 new_argv[i] = try arena.dupeZ(u8, arg);407 const child = try std.ChildProcess.init(argv.items, arena);
405 }408 defer child.deinit();
406409
407 var stderr_context: LLDContext = .{410 if (comp.clang_passthrough_mode) {
408 .wasm = self,411 child.stdin_behavior = .Inherit;
409 .data = std.ArrayList(u8).init(self.base.allocator),412 child.stdout_behavior = .Inherit;
410 };413 child.stderr_behavior = .Inherit;
411 defer stderr_context.data.deinit();414
412 var stdout_context: LLDContext = .{415 const term = child.spawnAndWait() catch |err| {
413 .wasm = self,416 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
414 .data = std.ArrayList(u8).init(self.base.allocator),417 return error.UnableToSpawnSelf;
415 };418 };
416 defer stdout_context.data.deinit();419 switch (term) {
417 const llvm = @import("../llvm.zig");420 .Exited => |code| {
418 const ok = llvm.Link(421 if (code != 0) {
419 .Wasm,422 // TODO https://github.com/ziglang/zig/issues/6342
420 new_argv.ptr,423 std.process.exit(1);
421 new_argv.len,424 }
422 append_diagnostic,425 },
423 @ptrToInt(&stdout_context),426 else => std.process.abort(),
424 @ptrToInt(&stderr_context),427 }
425 );428 } else {
426 if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;429 child.stdin_behavior = .Ignore;
427 if (stdout_context.data.items.len != 0) {430 child.stdout_behavior = .Ignore;
428 std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});431 child.stderr_behavior = .Pipe;
429 }432
430 if (!ok) {433 try child.spawn();
431 // TODO parse this output and surface with the Compilation API rather than434
432 // directly outputting to stderr here.435 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
433 std.debug.print("{}", .{stderr_context.data.items});436
434 return error.LLDReportedFailure;437 const term = child.wait() catch |err| {
435 }438 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
436 if (stderr_context.data.items.len != 0) {439 return error.UnableToSpawnSelf;
437 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 }
438 }460 }
439461
440 if (!self.base.options.disable_lld_caching) {462 if (!self.base.options.disable_lld_caching) {
...@@ -453,20 +475,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -453,20 +475,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
453 }475 }
454}476}
455477
456const LLDContext = struct {
457 data: std.ArrayList(u8),
458 wasm: *Wasm,
459 oom: bool = false,
460};
461
462fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
463 const lld_context = @intToPtr(*LLDContext, context);
464 const msg = ptr[0..len];
465 lld_context.data.appendSlice(msg) catch |err| switch (err) {
466 error.OutOfMemory => lld_context.oom = true,
467 };
468}
469
470/// Get the current index of a given Decl in the function list478/// Get the current index of a given Decl in the function list
471/// TODO: we could maintain a hash map to potentially make this479/// TODO: we could maintain a hash map to potentially make this
472fn 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")) {
...@@ -2786,6 +2792,39 @@ fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory}...@@ -2786,6 +2792,39 @@ fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory}
2786 process.exit(@bitCast(u8, @truncate(i8, exit_code)));2792 process.exit(@bitCast(u8, @truncate(i8, exit_code)));
2787}2793}
27882794
2795/// The first argument determines which backend is invoked. The options are:
2796/// * `ld.lld` - ELF
2797/// * `ld64.lld` - Mach-O
2798/// * `lld-link` - COFF
2799/// * `wasm-ld` - WebAssembly
2800/// TODO https://github.com/ziglang/zig/issues/3257
2801pub fn punt_to_lld(arena: *Allocator, args: []const []const u8) error{OutOfMemory} {
2802 if (!build_options.have_llvm)
2803 fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]});
2804 // Convert the args to the format LLD expects.
2805 // We subtract 1 to shave off the zig binary from args[0].
2806 const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null);
2807 for (args[1..]) |arg, i| {
2808 argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
2809 }
2810 const exit_code = rc: {
2811 const llvm = @import("llvm.zig");
2812 const argc = @intCast(c_int, argv.len);
2813 if (mem.eql(u8, args[1], "ld.lld")) {
2814 break :rc llvm.LinkELF(argc, argv.ptr, true);
2815 } else if (mem.eql(u8, args[1], "ld64.lld")) {
2816 break :rc llvm.LinkMachO(argc, argv.ptr, true);
2817 } else if (mem.eql(u8, args[1], "lld-link")) {
2818 break :rc llvm.LinkCOFF(argc, argv.ptr, true);
2819 } else if (mem.eql(u8, args[1], "wasm-ld")) {
2820 break :rc llvm.LinkWasm(argc, argv.ptr, true);
2821 } else {
2822 unreachable;
2823 }
2824 };
2825 process.exit(@bitCast(u8, @truncate(i8, exit_code)));
2826}
2827
2789const clang_args = @import("clang_options.zig").list;2828const clang_args = @import("clang_options.zig").list;
27902829
2791pub const ClangArgIterator = struct {2830pub const ClangArgIterator = struct {
src/zig_llvm.cpp+15-30
...@@ -1048,39 +1048,24 @@ bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size...@@ -1048,39 +1048,24 @@ bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size
1048 return false;1048 return false;
1049}1049}
10501050
1051int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early) {
1052 std::vector<const char *> args(argv, argv + argc);
1053 return lld::coff::link(args, can_exit_early, llvm::outs(), llvm::errs());
1054}
10511055
1052bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,1056int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early) {
1053 void (*append_diagnostic)(void *, const char *, size_t),1057 std::vector<const char *> args(argv, argv + argc);
1054 void *context_stdout, void *context_stderr)1058 return lld::elf::link(args, can_exit_early, llvm::outs(), llvm::errs());
1055{1059}
1056 ArrayRef<const char *> array_ref_args(args, arg_count);
1057
1058 MyOStream diag_stdout(append_diagnostic, context_stdout);
1059 MyOStream diag_stderr(append_diagnostic, context_stderr);
1060
1061 switch (oformat) {
1062 case ZigLLVM_UnknownObjectFormat:
1063 case ZigLLVM_XCOFF:
1064 assert(false); // unreachable
1065 break;
1066
1067 case ZigLLVM_COFF:
1068 return lld::coff::link(array_ref_args, false, diag_stdout, diag_stderr);
1069
1070 case ZigLLVM_ELF:
1071 return lld::elf::link(array_ref_args, false, diag_stdout, diag_stderr);
1072
1073 case ZigLLVM_MachO:
1074 return lld::mach_o::link(array_ref_args, false, diag_stdout, diag_stderr);
10751060
1076 case ZigLLVM_Wasm:1061int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early) {
1077 return lld::wasm::link(array_ref_args, false, diag_stdout, diag_stderr);1062 std::vector<const char *> args(argv, argv + argc);
1063 return lld::mach_o::link(args, can_exit_early, llvm::outs(), llvm::errs());
1064}
10781065
1079 default:1066int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early) {
1080 break;1067 std::vector<const char *> args(argv, argv + argc);
1081 }1068 return lld::wasm::link(args, can_exit_early, llvm::outs(), llvm::errs());
1082 assert(false); // unreachable
1083 abort();
1084}1069}
10851070
1086static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) {1071static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) {
src/zig_llvm.h+4-3
...@@ -505,9 +505,10 @@ ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor...@@ -505,9 +505,10 @@ ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor
505ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os);505ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os);
506ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi);506ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi);
507507
508ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,508ZIG_EXTERN_C int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early);
509 void (*append_diagnostic)(void *, const char *, size_t),509ZIG_EXTERN_C int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early);
510 void *context_stdout, void *context_stderr);510ZIG_EXTERN_C int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early);
511ZIG_EXTERN_C int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early);
511512
512ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,513ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,
513 enum ZigLLVM_OSType os_type);514 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 {