| author | |
| committer | |
| log | 72f6c6e6345030392a3f8cac79862d58359f1e76 |
| tree | 535e3696af29fe64f9a97bf746bf3f6f25c5854a |
| parent | 21550bb7cd5596e24a623f5ae1374502e879b553 |
Closes #38258 files changed, 241 insertions(+), 253 deletions(-)
src/link/Coff.zig+43-52| ... | @@ -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 it | 910 | // 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's | 911 | // This is necessary because LLD does not behave properly as a library - |
| 912 | // correctly printed when `verbose_link` is true. This is needed for some | 912 | // 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"); | ||
| 915 | 914 | ||
| 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,51 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -1149,45 +1148,51 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 1149 | } | 1148 | } |
| 1150 | 1149 | ||
| 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 | } |
| 1154 | 1154 | ||
| 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. One exception is if we are running in passthrough |
| 1157 | new_argv[i] = try arena.dupeZ(u8, arg); | 1157 | // mode, which means Clang / LLD should inherit stdio and are allowed to |
| 1158 | // crash zig directly. | ||
| 1159 | if (comp.clang_passthrough_mode) { | ||
| 1160 | return @import("../main.zig").punt_to_lld(arena, argv.items); | ||
| 1158 | } | 1161 | } |
| 1159 | 1162 | ||
| 1160 | var stderr_context: LLDContext = .{ | 1163 | const child = try std.ChildProcess.init(argv.items, arena); |
| 1161 | .coff = self, | 1164 | defer child.deinit(); |
| 1162 | .data = std.ArrayList(u8).init(self.base.allocator), | 1165 | |
| 1163 | }; | 1166 | child.stdin_behavior = .Ignore; |
| 1164 | defer stderr_context.data.deinit(); | 1167 | child.stdout_behavior = .Ignore; |
| 1165 | var stdout_context: LLDContext = .{ | 1168 | child.stderr_behavior = .Pipe; |
| 1166 | .coff = self, | 1169 | |
| 1167 | .data = std.ArrayList(u8).init(self.base.allocator), | 1170 | try child.spawn(); |
| 1171 | |||
| 1172 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); | ||
| 1173 | |||
| 1174 | const term = child.wait() catch |err| { | ||
| 1175 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 1176 | return error.UnableToSpawnSelf; | ||
| 1168 | }; | 1177 | }; |
| 1169 | defer stdout_context.data.deinit(); | 1178 | |
| 1170 | const llvm = @import("../llvm.zig"); | 1179 | switch (term) { |
| 1171 | const ok = llvm.Link( | 1180 | .Exited => |code| { |
| 1172 | .COFF, | 1181 | if (code != 0) { |
| 1173 | new_argv.ptr, | 1182 | // TODO parse this output and surface with the Compilation API rather than |
| 1174 | new_argv.len, | 1183 | // directly outputting to stderr here. |
| 1175 | append_diagnostic, | 1184 | std.debug.print("{s}", .{stderr}); |
| 1176 | @ptrToInt(&stdout_context), | 1185 | return error.LLDReportedFailure; |
| 1177 | @ptrToInt(&stderr_context), | 1186 | } |
| 1178 | ); | 1187 | }, |
| 1179 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | 1188 | else => { |
| 1180 | if (stdout_context.data.items.len != 0) { | 1189 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); |
| 1181 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | 1190 | return error.LLDCrashed; |
| 1182 | } | 1191 | }, |
| 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 | } | 1192 | } |
| 1189 | if (stderr_context.data.items.len != 0) { | 1193 | |
| 1190 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | 1194 | if (stderr.len != 0) { |
| 1195 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | ||
| 1191 | } | 1196 | } |
| 1192 | } | 1197 | } |
| 1193 | 1198 | ||
| ... | @@ -1207,20 +1212,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -1207,20 +1212,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 1207 | } | 1212 | } |
| 1208 | } | 1213 | } |
| 1209 | 1214 | ||
| 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 | pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { | 1215 | pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { |
| 1225 | return self.text_section_virtual_address + decl.link.coff.text_offset; | 1216 | return self.text_section_virtual_address + decl.link.coff.text_offset; |
| 1226 | } | 1217 | } |
src/link/Elf.zig+44-54| ... | @@ -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 it | 1363 | // 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's | 1364 | // This is necessary because LLD does not behave properly as a library - |
| 1365 | // correctly printed when `verbose_link` is true. This is needed for some | 1365 | // 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,51 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1628,46 +1627,51 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1628 | } | 1627 | } |
| 1629 | 1628 | ||
| 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 | } |
| 1633 | 1633 | ||
| 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. One exception is if we are running in passthrough |
| 1636 | for (argv.items) |arg, i| { | 1636 | // mode, which means Clang / LLD should inherit stdio and are allowed to |
| 1637 | new_argv[i] = try arena.dupeZ(u8, arg); | 1637 | // crash zig directly. |
| 1638 | if (comp.clang_passthrough_mode) { | ||
| 1639 | return @import("../main.zig").punt_to_lld(arena, argv.items); | ||
| 1638 | } | 1640 | } |
| 1639 | 1641 | ||
| 1640 | var stderr_context: LLDContext = .{ | 1642 | const child = try std.ChildProcess.init(argv.items, arena); |
| 1641 | .elf = self, | 1643 | defer child.deinit(); |
| 1642 | .data = std.ArrayList(u8).init(self.base.allocator), | 1644 | |
| 1643 | }; | 1645 | child.stdin_behavior = .Ignore; |
| 1644 | defer stderr_context.data.deinit(); | 1646 | child.stdout_behavior = .Ignore; |
| 1645 | var stdout_context: LLDContext = .{ | 1647 | child.stderr_behavior = .Pipe; |
| 1646 | .elf = self, | 1648 | |
| 1647 | .data = std.ArrayList(u8).init(self.base.allocator), | 1649 | try child.spawn(); |
| 1650 | |||
| 1651 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); | ||
| 1652 | |||
| 1653 | const term = child.wait() catch |err| { | ||
| 1654 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 1655 | return error.UnableToSpawnSelf; | ||
| 1648 | }; | 1656 | }; |
| 1649 | defer stdout_context.data.deinit(); | 1657 | |
| 1650 | const llvm = @import("../llvm.zig"); | 1658 | switch (term) { |
| 1651 | const ok = llvm.Link( | 1659 | .Exited => |code| { |
| 1652 | .ELF, | 1660 | if (code != 0) { |
| 1653 | new_argv.ptr, | 1661 | // TODO parse this output and surface with the Compilation API rather than |
| 1654 | new_argv.len, | 1662 | // directly outputting to stderr here. |
| 1655 | append_diagnostic, | 1663 | std.debug.print("{s}", .{stderr}); |
| 1656 | @ptrToInt(&stdout_context), | 1664 | return error.LLDReportedFailure; |
| 1657 | @ptrToInt(&stderr_context), | 1665 | } |
| 1658 | ); | 1666 | }, |
| 1659 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | 1667 | else => { |
| 1660 | if (stdout_context.data.items.len != 0) { | 1668 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); |
| 1661 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | 1669 | return error.LLDCrashed; |
| 1662 | } | 1670 | }, |
| 1663 | if (!ok) { | 1671 | } |
| 1664 | // TODO parse this output and surface with the Compilation API rather than | 1672 | |
| 1665 | // directly outputting to stderr here. | 1673 | if (stderr.len != 0) { |
| 1666 | std.debug.print("{}", .{stderr_context.data.items}); | 1674 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); |
| 1667 | return error.LLDReportedFailure; | ||
| 1668 | } | ||
| 1669 | if (stderr_context.data.items.len != 0) { | ||
| 1670 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | ||
| 1671 | } | 1675 | } |
| 1672 | 1676 | ||
| 1673 | if (!self.base.options.disable_lld_caching) { | 1677 | if (!self.base.options.disable_lld_caching) { |
| ... | @@ -1686,20 +1690,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1686,20 +1690,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1686 | } | 1690 | } |
| 1687 | } | 1691 | } |
| 1688 | 1692 | ||
| 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 | fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { | 1693 | fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { |
| 1704 | const target_endian = self.base.options.target.cpu.arch.endian(); | 1694 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 1705 | switch (self.ptr_width) { | 1695 | switch (self.ptr_width) { |
src/link/MachO.zig+44-53| ... | @@ -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, set | 492 | // 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 that | 493 | // This is necessary because LLD does not behave properly as a library - |
| 494 | // it's correctly printed when `verbose_link` is true. This is | 494 | // 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 C | 495 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld64.lld" }); |
| 496 | // compiler. | ||
| 497 | try argv.append("ld64"); | ||
| 498 | 496 | ||
| 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 | } |
| 661 | 659 | ||
| 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 | } |
| 665 | 665 | ||
| 666 | // TODO https://github.com/ziglang/zig/issues/6971 | 666 | // TODO https://github.com/ziglang/zig/issues/6971 |
| ... | @@ -685,42 +685,47 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -685,42 +685,47 @@ 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. One exception is if we are running in passthrough |
| 690 | new_argv[i] = try arena.dupeZ(u8, arg); | 690 | // mode, which means Clang / LLD should inherit stdio and are allowed to |
| 691 | // crash zig directly. | ||
| 692 | if (comp.clang_passthrough_mode) { | ||
| 693 | return @import("../main.zig").punt_to_lld(arena, argv.items); | ||
| 691 | } | 694 | } |
| 692 | 695 | ||
| 693 | var stderr_context: LLDContext = .{ | 696 | const child = try std.ChildProcess.init(argv.items, arena); |
| 694 | .macho = self, | 697 | defer child.deinit(); |
| 695 | .data = std.ArrayList(u8).init(self.base.allocator), | 698 | |
| 696 | }; | 699 | child.stdin_behavior = .Ignore; |
| 697 | defer stderr_context.data.deinit(); | 700 | child.stdout_behavior = .Ignore; |
| 698 | var stdout_context: LLDContext = .{ | 701 | child.stderr_behavior = .Pipe; |
| 699 | .macho = self, | 702 | |
| 700 | .data = std.ArrayList(u8).init(self.base.allocator), | 703 | try child.spawn(); |
| 704 | |||
| 705 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); | ||
| 706 | |||
| 707 | const term = child.wait() catch |err| { | ||
| 708 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 709 | return error.UnableToSpawnSelf; | ||
| 701 | }; | 710 | }; |
| 702 | defer stdout_context.data.deinit(); | 711 | |
| 703 | const llvm = @import("../llvm.zig"); | 712 | switch (term) { |
| 704 | const ok = llvm.Link( | 713 | .Exited => |code| { |
| 705 | .MachO, | 714 | if (code != 0) { |
| 706 | new_argv.ptr, | 715 | // TODO parse this output and surface with the Compilation API rather than |
| 707 | new_argv.len, | 716 | // directly outputting to stderr here. |
| 708 | append_diagnostic, | 717 | std.debug.print("{s}", .{stderr}); |
| 709 | @ptrToInt(&stdout_context), | 718 | return error.LLDReportedFailure; |
| 710 | @ptrToInt(&stderr_context), | 719 | } |
| 711 | ); | 720 | }, |
| 712 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | 721 | else => { |
| 713 | if (stdout_context.data.items.len != 0) { | 722 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); |
| 714 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | 723 | return error.LLDCrashed; |
| 715 | } | 724 | }, |
| 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 | } | 725 | } |
| 722 | if (stderr_context.data.items.len != 0) { | 726 | |
| 723 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | 727 | if (stderr.len != 0) { |
| 728 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | ||
| 724 | } | 729 | } |
| 725 | 730 | ||
| 726 | // At this stage, LLD has done its job. It is time to patch the resultant | 731 | // At this stage, LLD has done its job. It is time to patch the resultant |
| ... | @@ -785,20 +790,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -785,20 +790,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 785 | } | 790 | } |
| 786 | } | 791 | } |
| 787 | 792 | ||
| 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 | fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { | 793 | fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 803 | return switch (arch) { | 794 | return switch (arch) { |
| 804 | .aarch64, .aarch64_be, .aarch64_32 => "arm64", | 795 | .aarch64, .aarch64_be, .aarch64_32 => "arm64", |
src/link/Wasm.zig+43-52| ... | @@ -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 it | 348 | // 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's | 349 | // This is necessary because LLD does not behave properly as a library - |
| 350 | // correctly printed when `verbose_link` is true. This is needed for some | 350 | // 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,51 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | ... | @@ -399,45 +398,51 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 399 | } | 398 | } |
| 400 | 399 | ||
| 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 | } |
| 404 | 404 | ||
| 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. One exception is if we are running in passthrough |
| 407 | new_argv[i] = try arena.dupeZ(u8, arg); | 407 | // mode, which means Clang / LLD should inherit stdio and are allowed to |
| 408 | // crash zig directly. | ||
| 409 | if (comp.clang_passthrough_mode) { | ||
| 410 | return @import("../main.zig").punt_to_lld(arena, argv.items); | ||
| 408 | } | 411 | } |
| 409 | 412 | ||
| 410 | var stderr_context: LLDContext = .{ | 413 | const child = try std.ChildProcess.init(argv.items, arena); |
| 411 | .wasm = self, | 414 | defer child.deinit(); |
| 412 | .data = std.ArrayList(u8).init(self.base.allocator), | 415 | |
| 413 | }; | 416 | child.stdin_behavior = .Ignore; |
| 414 | defer stderr_context.data.deinit(); | 417 | child.stdout_behavior = .Ignore; |
| 415 | var stdout_context: LLDContext = .{ | 418 | child.stderr_behavior = .Pipe; |
| 416 | .wasm = self, | 419 | |
| 417 | .data = std.ArrayList(u8).init(self.base.allocator), | 420 | try child.spawn(); |
| 421 | |||
| 422 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); | ||
| 423 | |||
| 424 | const term = child.wait() catch |err| { | ||
| 425 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 426 | return error.UnableToSpawnSelf; | ||
| 418 | }; | 427 | }; |
| 419 | defer stdout_context.data.deinit(); | 428 | |
| 420 | const llvm = @import("../llvm.zig"); | 429 | switch (term) { |
| 421 | const ok = llvm.Link( | 430 | .Exited => |code| { |
| 422 | .Wasm, | 431 | if (code != 0) { |
| 423 | new_argv.ptr, | 432 | // TODO parse this output and surface with the Compilation API rather than |
| 424 | new_argv.len, | 433 | // directly outputting to stderr here. |
| 425 | append_diagnostic, | 434 | std.debug.print("{s}", .{stderr}); |
| 426 | @ptrToInt(&stdout_context), | 435 | return error.LLDReportedFailure; |
| 427 | @ptrToInt(&stderr_context), | 436 | } |
| 428 | ); | 437 | }, |
| 429 | if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; | 438 | else => { |
| 430 | if (stdout_context.data.items.len != 0) { | 439 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); |
| 431 | std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); | 440 | return error.LLDCrashed; |
| 432 | } | 441 | }, |
| 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 | } | 442 | } |
| 439 | if (stderr_context.data.items.len != 0) { | 443 | |
| 440 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | 444 | if (stderr.len != 0) { |
| 445 | std.log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | ||
| 441 | } | 446 | } |
| 442 | 447 | ||
| 443 | if (!self.base.options.disable_lld_caching) { | 448 | if (!self.base.options.disable_lld_caching) { |
| ... | @@ -456,20 +461,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | ... | @@ -456,20 +461,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 456 | } | 461 | } |
| 457 | } | 462 | } |
| 458 | 463 | ||
| 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 | /// Get the current index of a given Decl in the function list | 464 | /// Get the current index of a given Decl in the function list |
| 474 | /// TODO: we could maintain a hash map to potentially make this | 465 | /// TODO: we could maintain a hash map to potentially make this |
| 475 | fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 { | 466 | fn 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 easier | 1 | //! 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. |
| 3 | 3 | ||
| 4 | pub const Link = ZigLLDLink; | 4 | extern fn ZigLLDLinkCOFF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; |
| 5 | extern fn ZigLLDLink( | 5 | extern fn ZigLLDLinkELF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; |
| 6 | oformat: ObjectFormatType, | 6 | extern fn ZigLLDLinkMachO(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; |
| 7 | args: [*:null]const ?[*:0]const u8, | 7 | extern 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, | 9 | pub const LinkCOFF = ZigLLDLinkCOFF; |
| 10 | context_stdout: usize, | 10 | pub const LinkELF = ZigLLDLinkELF; |
| 11 | context_stderr: usize, | 11 | pub const LinkMachO = ZigLLDLinkMachO; |
| 12 | ) bool; | 12 | pub const LinkWasm = ZigLLDLinkWasm; |
| 13 | 13 | ||
| 14 | pub const ObjectFormatType = extern enum(c_int) { | 14 | pub 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 | } |
| 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 | const clang_args = @import("clang_options.zig").list; | 2861 | const clang_args = @import("clang_options.zig").list; |
| 2823 | 2862 | ||
| 2824 | pub const ClangArgIterator = struct { | 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,39 +1056,24 @@ bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size |
| 1056 | return false; | 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, | 1064 | int 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); | ||
| 1083 | 1068 | ||
| 1084 | case ZigLLVM_Wasm: | 1069 | int 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 | } | ||
| 1086 | 1073 | ||
| 1087 | default: | 1074 | int 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 | } |
| 1093 | 1078 | ||
| 1094 | static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) { | 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,9 +507,10 @@ ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor |
| 507 | ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os); | 507 | ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os); |
| 508 | ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi); | 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, | 510 | ZIG_EXTERN_C int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early); |
| 511 | void (*append_diagnostic)(void *, const char *, size_t), | 511 | ZIG_EXTERN_C int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early); |
| 512 | void *context_stdout, void *context_stderr); | 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 | ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count, | 515 | ZIG_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); |