| author | |
| committer | |
| log | 391d81a3802e48f59d804746eedc396d60ad3820 |
| tree | e4e634ad79891ae089b7f02f417bf661824f6679 |
| parent | 21550bb7cd5596e24a623f5ae1374502e879b553 |
| parent | 7dd4afb224f4ca747b8eb462c28337ce9a63d38c |
| signature |
invoke LLD as a child process rather than a library11 files changed, 332 insertions(+), 267 deletions(-)
lib/std/testing.zig+21| ... | ... | @@ -247,6 +247,7 @@ test "expectWithinEpsilon" { |
| 247 | 247 | /// This function is intended to be used only in tests. When the two slices are not |
| 248 | 248 | /// equal, prints diagnostics to stderr to show exactly how they are not equal, |
| 249 | 249 | /// then aborts. |
| 250 | /// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead. | |
| 250 | 251 | pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) void { |
| 251 | 252 | // TODO better printing of the difference |
| 252 | 253 | // 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 | 369 | } |
| 369 | 370 | } |
| 370 | 371 | |
| 372 | pub 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 | ||
| 371 | 392 | fn printIndicatorLine(source: []const u8, indicator_index: usize) void { |
| 372 | 393 | const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin| |
| 373 | 394 | line_begin + 1 |
src/Compilation.zig+1-1| ... | ... | @@ -1804,7 +1804,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * |
| 1804 | 1804 | if (comp.clang_preprocessor_mode == .stdout) |
| 1805 | 1805 | std.process.exit(0); |
| 1806 | 1806 | }, |
| 1807 | else => std.process.exit(1), | |
| 1807 | else => std.process.abort(), | |
| 1808 | 1808 | } |
| 1809 | 1809 | } else { |
| 1810 | 1810 | child.stdin_behavior = .Ignore; |
src/link/Coff.zig+60-55| ... | ... | @@ -907,11 +907,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 907 | 907 | // Create an LLD command line and invoke it. |
| 908 | 908 | var argv = std.ArrayList([]const u8).init(self.base.allocator); |
| 909 | 909 | defer argv.deinit(); |
| 910 | // The first argument is ignored as LLD is called as a library, set it | |
| 911 | // anyway to the correct LLD driver name for this target so that it's | |
| 912 | // correctly printed when `verbose_link` is true. This is needed for some | |
| 913 | // tools such as CMake when Zig is used as C compiler. | |
| 914 | try argv.append("lld-link"); | |
| 910 | // We will invoke ourselves as a child process to gain access to 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" }); | |
| 915 | 914 | |
| 916 | 915 | try argv.append("-ERRORLIMIT:0"); |
| 917 | 916 | try argv.append("-NOLOGO"); |
| ... | ... | @@ -1149,45 +1148,65 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 1149 | 1148 | } |
| 1150 | 1149 | |
| 1151 | 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 | } |
| 1154 | 1154 | |
| 1155 | const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); | |
| 1156 | for (argv.items) |arg, i| { | |
| 1157 | new_argv[i] = try arena.dupeZ(u8, arg); | |
| 1158 | } | |
| 1155 | // Sadly, we must run LLD as a child process because it does not behave | |
| 1156 | // properly as a library. | |
| 1157 | const child = try std.ChildProcess.init(argv.items, arena); | |
| 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 | } | |
| 1159 | 1206 | |
| 1160 | var stderr_context: LLDContext = .{ | |
| 1161 | .coff = self, | |
| 1162 | .data = std.ArrayList(u8).init(self.base.allocator), | |
| 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}); | |
| 1207 | if (stderr.len != 0) { | |
| 1208 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | |
| 1209 | } | |
| 1191 | 1210 | } |
| 1192 | 1211 | } |
| 1193 | 1212 | |
| ... | ... | @@ -1207,20 +1226,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 1207 | 1226 | } |
| 1208 | 1227 | } |
| 1209 | 1228 | |
| 1210 | const LLDContext = struct { | |
| 1211 | data: std.ArrayList(u8), | |
| 1212 | coff: *Coff, | |
| 1213 | oom: bool = false, | |
| 1214 | }; | |
| 1215 | ||
| 1216 | fn 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 | ||
| 1224 | 1229 | pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { |
| 1225 | 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 | 1360 | // Create an LLD command line and invoke it. |
| 1361 | 1361 | var argv = std.ArrayList([]const u8).init(self.base.allocator); |
| 1362 | 1362 | defer argv.deinit(); |
| 1363 | // The first argument is ignored as LLD is called as a library, set it | |
| 1364 | // anyway to the correct LLD driver name for this target so that it's | |
| 1365 | // correctly printed when `verbose_link` is true. This is needed for some | |
| 1366 | // tools such as CMake when Zig is used as C compiler. | |
| 1367 | try argv.append("ld.lld"); | |
| 1363 | // We will invoke ourselves as a child process to gain access to 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" }); | |
| 1368 | 1367 | if (is_obj) { |
| 1369 | 1368 | try argv.append("-r"); |
| 1370 | 1369 | } |
| ... | ... | @@ -1628,46 +1627,65 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1628 | 1627 | } |
| 1629 | 1628 | |
| 1630 | 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 | } |
| 1633 | 1633 | |
| 1634 | // Oh, snapplesauce! We need null terminated argv. | |
| 1635 | const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); | |
| 1636 | for (argv.items) |arg, i| { | |
| 1637 | new_argv[i] = try arena.dupeZ(u8, arg); | |
| 1638 | } | |
| 1634 | // Sadly, we must run LLD as a child process because it does not behave | |
| 1635 | // properly as a library. | |
| 1636 | const child = try std.ChildProcess.init(argv.items, arena); | |
| 1637 | defer child.deinit(); | |
| 1639 | 1638 | |
| 1640 | var stderr_context: LLDContext = .{ | |
| 1641 | .elf = self, | |
| 1642 | .data = std.ArrayList(u8).init(self.base.allocator), | |
| 1643 | }; | |
| 1644 | defer stderr_context.data.deinit(); | |
| 1645 | var stdout_context: LLDContext = .{ | |
| 1646 | .elf = self, | |
| 1647 | .data = std.ArrayList(u8).init(self.base.allocator), | |
| 1648 | }; | |
| 1649 | defer stdout_context.data.deinit(); | |
| 1650 | const llvm = @import("../llvm.zig"); | |
| 1651 | const ok = llvm.Link( | |
| 1652 | .ELF, | |
| 1653 | new_argv.ptr, | |
| 1654 | new_argv.len, | |
| 1655 | append_diagnostic, | |
| 1656 | @ptrToInt(&stdout_context), | |
| 1657 | @ptrToInt(&stderr_context), | |
| 1658 | ); | |
| 1659 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | |
| 1660 | if (stdout_context.data.items.len != 0) { | |
| 1661 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | |
| 1662 | } | |
| 1663 | if (!ok) { | |
| 1664 | // TODO parse this output and surface with the Compilation API rather than | |
| 1665 | // directly outputting to stderr here. | |
| 1666 | std.debug.print("{}", .{stderr_context.data.items}); | |
| 1667 | return error.LLDReportedFailure; | |
| 1668 | } | |
| 1669 | if (stderr_context.data.items.len != 0) { | |
| 1670 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | |
| 1639 | if (comp.clang_passthrough_mode) { | |
| 1640 | child.stdin_behavior = .Inherit; | |
| 1641 | child.stdout_behavior = .Inherit; | |
| 1642 | child.stderr_behavior = .Inherit; | |
| 1643 | ||
| 1644 | const term = child.spawnAndWait() catch |err| { | |
| 1645 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | |
| 1646 | return error.UnableToSpawnSelf; | |
| 1647 | }; | |
| 1648 | switch (term) { | |
| 1649 | .Exited => |code| { | |
| 1650 | if (code != 0) { | |
| 1651 | // TODO https://github.com/ziglang/zig/issues/6342 | |
| 1652 | std.process.exit(1); | |
| 1653 | } | |
| 1654 | }, | |
| 1655 | else => std.process.abort(), | |
| 1656 | } | |
| 1657 | } else { | |
| 1658 | child.stdin_behavior = .Ignore; | |
| 1659 | child.stdout_behavior = .Ignore; | |
| 1660 | child.stderr_behavior = .Pipe; | |
| 1661 | ||
| 1662 | try child.spawn(); | |
| 1663 | ||
| 1664 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); | |
| 1665 | ||
| 1666 | const term = child.wait() catch |err| { | |
| 1667 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | |
| 1668 | return error.UnableToSpawnSelf; | |
| 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 | } |
| 1672 | 1690 | |
| 1673 | 1691 | if (!self.base.options.disable_lld_caching) { |
| ... | ... | @@ -1686,20 +1704,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1686 | 1704 | } |
| 1687 | 1705 | } |
| 1688 | 1706 | |
| 1689 | const LLDContext = struct { | |
| 1690 | data: std.ArrayList(u8), | |
| 1691 | elf: *Elf, | |
| 1692 | oom: bool = false, | |
| 1693 | }; | |
| 1694 | ||
| 1695 | fn 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 | ||
| 1703 | 1707 | fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { |
| 1704 | 1708 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 1705 | 1709 | switch (self.ptr_width) { |
src/link/MachO.zig+61-56| ... | ... | @@ -489,12 +489,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 489 | 489 | if (self.base.options.system_linker_hack) { |
| 490 | 490 | try argv.append("ld"); |
| 491 | 491 | } else { |
| 492 | // The first argument is ignored as LLD is called as a library, set | |
| 493 | // it anyway to the correct LLD driver name for this target so that | |
| 494 | // it's correctly printed when `verbose_link` is true. This is | |
| 495 | // needed for some tools such as CMake when Zig is used as C | |
| 496 | // compiler. | |
| 497 | try argv.append("ld64"); | |
| 492 | // We will invoke ourselves as a child process to gain access to LLD. | |
| 493 | // This is necessary because LLD does not behave properly as a library - | |
| 494 | // it calls exit() and does not reset all global data between invocations. | |
| 495 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld64.lld" }); | |
| 498 | 496 | |
| 499 | 497 | try argv.append("-error-limit"); |
| 500 | 498 | try argv.append("0"); |
| ... | ... | @@ -660,7 +658,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 660 | 658 | } |
| 661 | 659 | |
| 662 | 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 | } |
| 665 | 665 | |
| 666 | 666 | // TODO https://github.com/ziglang/zig/issues/6971 |
| ... | ... | @@ -685,42 +685,61 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 685 | 685 | return error.LDReportedFailure; |
| 686 | 686 | } |
| 687 | 687 | } else { |
| 688 | const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); | |
| 689 | for (argv.items) |arg, i| { | |
| 690 | new_argv[i] = try arena.dupeZ(u8, arg); | |
| 691 | } | |
| 688 | // Sadly, we must run LLD as a child process because it does not behave | |
| 689 | // properly as a library. | |
| 690 | const child = try std.ChildProcess.init(argv.items, arena); | |
| 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 | } | |
| 692 | 739 | |
| 693 | var stderr_context: LLDContext = .{ | |
| 694 | .macho = self, | |
| 695 | .data = std.ArrayList(u8).init(self.base.allocator), | |
| 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}); | |
| 740 | if (stderr.len != 0) { | |
| 741 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | |
| 742 | } | |
| 724 | 743 | } |
| 725 | 744 | |
| 726 | 745 | // 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 | 804 | } |
| 786 | 805 | } |
| 787 | 806 | |
| 788 | const LLDContext = struct { | |
| 789 | data: std.ArrayList(u8), | |
| 790 | macho: *MachO, | |
| 791 | oom: bool = false, | |
| 792 | }; | |
| 793 | ||
| 794 | fn 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 | ||
| 802 | 807 | fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 803 | 808 | return switch (arch) { |
| 804 | 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 | 345 | // Create an LLD command line and invoke it. |
| 346 | 346 | var argv = std.ArrayList([]const u8).init(self.base.allocator); |
| 347 | 347 | defer argv.deinit(); |
| 348 | // The first argument is ignored as LLD is called as a library, set it | |
| 349 | // anyway to the correct LLD driver name for this target so that it's | |
| 350 | // correctly printed when `verbose_link` is true. This is needed for some | |
| 351 | // tools such as CMake when Zig is used as C compiler. | |
| 352 | try argv.append("ld-wasm"); | |
| 348 | // We will invoke ourselves as a child process to gain access to 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" }); | |
| 353 | 352 | if (is_obj) { |
| 354 | 353 | try argv.append("-r"); |
| 355 | 354 | } |
| ... | ... | @@ -399,45 +398,65 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 399 | 398 | } |
| 400 | 399 | |
| 401 | 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 | } |
| 404 | 404 | |
| 405 | const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); | |
| 406 | for (argv.items) |arg, i| { | |
| 407 | new_argv[i] = try arena.dupeZ(u8, arg); | |
| 408 | } | |
| 405 | // Sadly, we must run LLD as a child process because it does not behave | |
| 406 | // properly as a library. | |
| 407 | const child = try std.ChildProcess.init(argv.items, arena); | |
| 408 | defer child.deinit(); | |
| 409 | 409 | |
| 410 | var stderr_context: LLDContext = .{ | |
| 411 | .wasm = self, | |
| 412 | .data = std.ArrayList(u8).init(self.base.allocator), | |
| 413 | }; | |
| 414 | defer stderr_context.data.deinit(); | |
| 415 | var stdout_context: LLDContext = .{ | |
| 416 | .wasm = self, | |
| 417 | .data = std.ArrayList(u8).init(self.base.allocator), | |
| 418 | }; | |
| 419 | defer stdout_context.data.deinit(); | |
| 420 | const llvm = @import("../llvm.zig"); | |
| 421 | const ok = llvm.Link( | |
| 422 | .Wasm, | |
| 423 | new_argv.ptr, | |
| 424 | new_argv.len, | |
| 425 | append_diagnostic, | |
| 426 | @ptrToInt(&stdout_context), | |
| 427 | @ptrToInt(&stderr_context), | |
| 428 | ); | |
| 429 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | |
| 430 | if (stdout_context.data.items.len != 0) { | |
| 431 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | |
| 432 | } | |
| 433 | if (!ok) { | |
| 434 | // TODO parse this output and surface with the Compilation API rather than | |
| 435 | // directly outputting to stderr here. | |
| 436 | std.debug.print("{}", .{stderr_context.data.items}); | |
| 437 | return error.LLDReportedFailure; | |
| 438 | } | |
| 439 | if (stderr_context.data.items.len != 0) { | |
| 440 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | |
| 410 | if (comp.clang_passthrough_mode) { | |
| 411 | child.stdin_behavior = .Inherit; | |
| 412 | child.stdout_behavior = .Inherit; | |
| 413 | child.stderr_behavior = .Inherit; | |
| 414 | ||
| 415 | const term = child.spawnAndWait() catch |err| { | |
| 416 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | |
| 417 | return error.UnableToSpawnSelf; | |
| 418 | }; | |
| 419 | switch (term) { | |
| 420 | .Exited => |code| { | |
| 421 | if (code != 0) { | |
| 422 | // TODO https://github.com/ziglang/zig/issues/6342 | |
| 423 | std.process.exit(1); | |
| 424 | } | |
| 425 | }, | |
| 426 | else => std.process.abort(), | |
| 427 | } | |
| 428 | } else { | |
| 429 | child.stdin_behavior = .Ignore; | |
| 430 | child.stdout_behavior = .Ignore; | |
| 431 | child.stderr_behavior = .Pipe; | |
| 432 | ||
| 433 | try child.spawn(); | |
| 434 | ||
| 435 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); | |
| 436 | ||
| 437 | const term = child.wait() catch |err| { | |
| 438 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | |
| 439 | return error.UnableToSpawnSelf; | |
| 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 | } |
| 442 | 461 | |
| 443 | 462 | if (!self.base.options.disable_lld_caching) { |
| ... | ... | @@ -456,20 +475,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 456 | 475 | } |
| 457 | 476 | } |
| 458 | 477 | |
| 459 | const LLDContext = struct { | |
| 460 | data: std.ArrayList(u8), | |
| 461 | wasm: *Wasm, | |
| 462 | oom: bool = false, | |
| 463 | }; | |
| 464 | ||
| 465 | fn 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 | 478 | /// Get the current index of a given Decl in the function list |
| 474 | 479 | /// TODO: we could maintain a hash map to potentially make this |
| 475 | 480 | fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 { |
src/llvm.zig+9-9| ... | ... | @@ -1,15 +1,15 @@ |
| 1 | 1 | //! We do this instead of @cImport because the self-hosted compiler is easier |
| 2 | 2 | //! to bootstrap if it does not depend on translate-c. |
| 3 | 3 | |
| 4 | pub const Link = ZigLLDLink; | |
| 5 | extern fn ZigLLDLink( | |
| 6 | oformat: ObjectFormatType, | |
| 7 | args: [*:null]const ?[*:0]const u8, | |
| 8 | arg_count: usize, | |
| 9 | append_diagnostic: fn (context: usize, ptr: [*]const u8, len: usize) callconv(.C) void, | |
| 10 | context_stdout: usize, | |
| 11 | context_stderr: usize, | |
| 12 | ) bool; | |
| 4 | extern fn ZigLLDLinkCOFF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; | |
| 5 | extern fn ZigLLDLinkELF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; | |
| 6 | extern fn ZigLLDLinkMachO(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; | |
| 7 | extern fn ZigLLDLinkWasm(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; | |
| 8 | ||
| 9 | pub const LinkCOFF = ZigLLDLinkCOFF; | |
| 10 | pub const LinkELF = ZigLLDLinkELF; | |
| 11 | pub const LinkMachO = ZigLLDLinkMachO; | |
| 12 | pub const LinkWasm = ZigLLDLinkWasm; | |
| 13 | 13 | |
| 14 | 14 | pub const ObjectFormatType = extern enum(c_int) { |
| 15 | 15 | Unknown, |
src/main.zig+39| ... | ... | @@ -176,6 +176,12 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v |
| 176 | 176 | mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) |
| 177 | 177 | { |
| 178 | 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 | 185 | } else if (mem.eql(u8, cmd, "build")) { |
| 180 | 186 | return cmdBuild(gpa, arena, cmd_args); |
| 181 | 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 | 2825 | process.exit(@bitCast(u8, @truncate(i8, exit_code))); |
| 2820 | 2826 | } |
| 2821 | 2827 | |
| 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 | |
| 2834 | pub 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 | ||
| 2822 | 2861 | const clang_args = @import("clang_options.zig").list; |
| 2823 | 2862 | |
| 2824 | 2863 | pub 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 | 1056 | return false; |
| 1057 | 1057 | } |
| 1058 | 1058 | |
| 1059 | int 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 | } | |
| 1059 | 1063 | |
| 1060 | bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, | |
| 1061 | void (*append_diagnostic)(void *, const char *, size_t), | |
| 1062 | void *context_stdout, void *context_stderr) | |
| 1063 | { | |
| 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); | |
| 1064 | int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early) { | |
| 1065 | std::vector<const char *> args(argv, argv + argc); | |
| 1066 | return lld::elf::link(args, can_exit_early, llvm::outs(), llvm::errs()); | |
| 1067 | } | |
| 1083 | 1068 | |
| 1084 | case ZigLLVM_Wasm: | |
| 1085 | return lld::wasm::link(array_ref_args, false, diag_stdout, diag_stderr); | |
| 1069 | int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early) { | |
| 1070 | std::vector<const char *> args(argv, argv + argc); | |
| 1071 | return lld::mach_o::link(args, can_exit_early, llvm::outs(), llvm::errs()); | |
| 1072 | } | |
| 1086 | 1073 | |
| 1087 | default: | |
| 1088 | break; | |
| 1089 | } | |
| 1090 | assert(false); // unreachable | |
| 1091 | abort(); | |
| 1074 | int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early) { | |
| 1075 | std::vector<const char *> args(argv, argv + argc); | |
| 1076 | return lld::wasm::link(args, can_exit_early, llvm::outs(), llvm::errs()); | |
| 1092 | 1077 | } |
| 1093 | 1078 | |
| 1094 | 1079 | static 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 | 507 | ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os); |
| 508 | 508 | ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi); |
| 509 | 509 | |
| 510 | ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, | |
| 511 | void (*append_diagnostic)(void *, const char *, size_t), | |
| 512 | void *context_stdout, void *context_stderr); | |
| 510 | ZIG_EXTERN_C int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early); | |
| 511 | ZIG_EXTERN_C int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early); | |
| 512 | ZIG_EXTERN_C int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early); | |
| 513 | ZIG_EXTERN_C int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early); | |
| 513 | 514 | |
| 514 | 515 | ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count, |
| 515 | 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 | 92 | fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { |
| 93 | 93 | _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" }); |
| 94 | 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 | } |
| 97 | 97 | |
| 98 | 98 | fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { |
| 99 | 99 | _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); |
| 100 | 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 | } |
| 103 | 103 | |
| 104 | 104 | fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { |