authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-08 19:57:15-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-08 20:00:16-07:00
log72f6c6e6345030392a3f8cac79862d58359f1e76
tree535e3696af29fe64f9a97bf746bf3f6f25c5854a
parent21550bb7cd5596e24a623f5ae1374502e879b553

invoke LLD as a child process rather than a library

Closes #3825

8 files changed, 241 insertions(+), 253 deletions(-)

src/link/Coff.zig+43-52
......@@ -907,11 +907,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
907907 // Create an LLD command line and invoke it.
908908 var argv = std.ArrayList([]const u8).init(self.base.allocator);
909909 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" });
915914
916915 try argv.append("-ERRORLIMIT:0");
917916 try argv.append("-NOLOGO");
......@@ -1149,45 +1148,51 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
11491148 }
11501149
11511150 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..]);
11531153 }
11541154
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);
1155 // Sadly, we must run LLD as a child process because it does not behave
1156 // properly as a library. One exception is if we are running in passthrough
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);
11581161 }
11591162
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),
1163 const child = try std.ChildProcess.init(argv.items, arena);
1164 defer child.deinit();
1165
1166 child.stdin_behavior = .Ignore;
1167 child.stdout_behavior = .Ignore;
1168 child.stderr_behavior = .Pipe;
1169
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;
11681177 };
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;
1178
1179 switch (term) {
1180 .Exited => |code| {
1181 if (code != 0) {
1182 // TODO parse this output and surface with the Compilation API rather than
1183 // directly outputting to stderr here.
1184 std.debug.print("{s}", .{stderr});
1185 return error.LLDReportedFailure;
1186 }
1187 },
1188 else => {
1189 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1190 return error.LLDCrashed;
1191 },
11881192 }
1189 if (stderr_context.data.items.len != 0) {
1190 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
1193
1194 if (stderr.len != 0) {
1195 std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
11911196 }
11921197 }
11931198
......@@ -1207,20 +1212,6 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
12071212 }
12081213}
12091214
1210const LLDContext = struct {
1211 data: std.ArrayList(u8),
1212 coff: *Coff,
1213 oom: bool = false,
1214};
1215
1216fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
1217 const lld_context = @intToPtr(*LLDContext, context);
1218 const msg = ptr[0..len];
1219 lld_context.data.appendSlice(msg) catch |err| switch (err) {
1220 error.OutOfMemory => lld_context.oom = true,
1221 };
1222}
1223
12241215pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
12251216 return self.text_section_virtual_address + decl.link.coff.text_offset;
12261217}
src/link/Elf.zig+44-54
......@@ -1360,11 +1360,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13601360 // Create an LLD command line and invoke it.
13611361 var argv = std.ArrayList([]const u8).init(self.base.allocator);
13621362 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" });
13681367 if (is_obj) {
13691368 try argv.append("-r");
13701369 }
......@@ -1628,46 +1627,51 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
16281627 }
16291628
16301629 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..]);
16321632 }
16331633
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);
1634 // Sadly, we must run LLD as a child process because it does not behave
1635 // properly as a library. One exception is if we are running in passthrough
1636 // mode, which means Clang / LLD should inherit stdio and are allowed to
1637 // crash zig directly.
1638 if (comp.clang_passthrough_mode) {
1639 return @import("../main.zig").punt_to_lld(arena, argv.items);
16381640 }
16391641
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),
1642 const child = try std.ChildProcess.init(argv.items, arena);
1643 defer child.deinit();
1644
1645 child.stdin_behavior = .Ignore;
1646 child.stdout_behavior = .Ignore;
1647 child.stderr_behavior = .Pipe;
1648
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;
16481656 };
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});
1657
1658 switch (term) {
1659 .Exited => |code| {
1660 if (code != 0) {
1661 // TODO parse this output and surface with the Compilation API rather than
1662 // directly outputting to stderr here.
1663 std.debug.print("{s}", .{stderr});
1664 return error.LLDReportedFailure;
1665 }
1666 },
1667 else => {
1668 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1669 return error.LLDCrashed;
1670 },
1671 }
1672
1673 if (stderr.len != 0) {
1674 std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
16711675 }
16721676
16731677 if (!self.base.options.disable_lld_caching) {
......@@ -1686,20 +1690,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
16861690 }
16871691}
16881692
1689const LLDContext = struct {
1690 data: std.ArrayList(u8),
1691 elf: *Elf,
1692 oom: bool = false,
1693};
1694
1695fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
1696 const lld_context = @intToPtr(*LLDContext, context);
1697 const msg = ptr[0..len];
1698 lld_context.data.appendSlice(msg) catch |err| switch (err) {
1699 error.OutOfMemory => lld_context.oom = true,
1700 };
1701}
1702
17031693fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
17041694 const target_endian = self.base.options.target.cpu.arch.endian();
17051695 switch (self.ptr_width) {
src/link/MachO.zig+44-53
......@@ -489,12 +489,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
489489 if (self.base.options.system_linker_hack) {
490490 try argv.append("ld");
491491 } 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" });
498496
499497 try argv.append("-error-limit");
500498 try argv.append("0");
......@@ -660,7 +658,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
660658 }
661659
662660 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);
664664 }
665665
666666 // TODO https://github.com/ziglang/zig/issues/6971
......@@ -685,42 +685,47 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
685685 return error.LDReportedFailure;
686686 }
687687 } 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);
688 // Sadly, we must run LLD as a child process because it does not behave
689 // properly as a library. One exception is if we are running in passthrough
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);
691694 }
692695
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),
696 const child = try std.ChildProcess.init(argv.items, arena);
697 defer child.deinit();
698
699 child.stdin_behavior = .Ignore;
700 child.stdout_behavior = .Ignore;
701 child.stderr_behavior = .Pipe;
702
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;
701710 };
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;
711
712 switch (term) {
713 .Exited => |code| {
714 if (code != 0) {
715 // TODO parse this output and surface with the Compilation API rather than
716 // directly outputting to stderr here.
717 std.debug.print("{s}", .{stderr});
718 return error.LLDReportedFailure;
719 }
720 },
721 else => {
722 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
723 return error.LLDCrashed;
724 },
721725 }
722 if (stderr_context.data.items.len != 0) {
723 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
726
727 if (stderr.len != 0) {
728 std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
724729 }
725730
726731 // 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 {
785790 }
786791}
787792
788const LLDContext = struct {
789 data: std.ArrayList(u8),
790 macho: *MachO,
791 oom: bool = false,
792};
793
794fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
795 const lld_context = @intToPtr(*LLDContext, context);
796 const msg = ptr[0..len];
797 lld_context.data.appendSlice(msg) catch |err| switch (err) {
798 error.OutOfMemory => lld_context.oom = true,
799 };
800}
801
802793fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
803794 return switch (arch) {
804795 .aarch64, .aarch64_be, .aarch64_32 => "arm64",
src/link/Wasm.zig+43-52
......@@ -345,11 +345,10 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
345345 // Create an LLD command line and invoke it.
346346 var argv = std.ArrayList([]const u8).init(self.base.allocator);
347347 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" });
353352 if (is_obj) {
354353 try argv.append("-r");
355354 }
......@@ -399,45 +398,51 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
399398 }
400399
401400 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..]);
403403 }
404404
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);
405 // Sadly, we must run LLD as a child process because it does not behave
406 // properly as a library. One exception is if we are running in passthrough
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);
408411 }
409412
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),
413 const child = try std.ChildProcess.init(argv.items, arena);
414 defer child.deinit();
415
416 child.stdin_behavior = .Ignore;
417 child.stdout_behavior = .Ignore;
418 child.stderr_behavior = .Pipe;
419
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;
418427 };
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;
428
429 switch (term) {
430 .Exited => |code| {
431 if (code != 0) {
432 // TODO parse this output and surface with the Compilation API rather than
433 // directly outputting to stderr here.
434 std.debug.print("{s}", .{stderr});
435 return error.LLDReportedFailure;
436 }
437 },
438 else => {
439 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
440 return error.LLDCrashed;
441 },
438442 }
439 if (stderr_context.data.items.len != 0) {
440 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
443
444 if (stderr.len != 0) {
445 std.log.warn("unexpected LLD stderr:\n{s}", .{stderr});
441446 }
442447
443448 if (!self.base.options.disable_lld_caching) {
......@@ -456,20 +461,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
456461 }
457462}
458463
459const LLDContext = struct {
460 data: std.ArrayList(u8),
461 wasm: *Wasm,
462 oom: bool = false,
463};
464
465fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
466 const lld_context = @intToPtr(*LLDContext, context);
467 const msg = ptr[0..len];
468 lld_context.data.appendSlice(msg) catch |err| switch (err) {
469 error.OutOfMemory => lld_context.oom = true,
470 };
471}
472
473464/// Get the current index of a given Decl in the function list
474465/// TODO: we could maintain a hash map to potentially make this
475466fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 {
src/llvm.zig+9-9
......@@ -1,15 +1,15 @@
11//! We do this instead of @cImport because the self-hosted compiler is easier
22//! to bootstrap if it does not depend on translate-c.
33
4pub const Link = ZigLLDLink;
5extern 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;
4extern fn ZigLLDLinkCOFF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
5extern fn ZigLLDLinkELF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
6extern fn ZigLLDLinkMachO(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
7extern fn ZigLLDLinkWasm(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
8
9pub const LinkCOFF = ZigLLDLinkCOFF;
10pub const LinkELF = ZigLLDLinkELF;
11pub const LinkMachO = ZigLLDLinkMachO;
12pub const LinkWasm = ZigLLDLinkWasm;
1313
1414pub const ObjectFormatType = extern enum(c_int) {
1515 Unknown,
src/main.zig+39
......@@ -176,6 +176,12 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
176176 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
177177 {
178178 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);
179185 } else if (mem.eql(u8, cmd, "build")) {
180186 return cmdBuild(gpa, arena, cmd_args);
181187 } else if (mem.eql(u8, cmd, "fmt")) {
......@@ -2819,6 +2825,39 @@ fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory}
28192825 process.exit(@bitCast(u8, @truncate(i8, exit_code)));
28202826}
28212827
2828/// The first argument determines which backend is invoked. The options are:
2829/// * `ld.lld` - ELF
2830/// * `ld64.lld` - Mach-O
2831/// * `lld-link` - COFF
2832/// * `wasm-ld` - WebAssembly
2833/// TODO https://github.com/ziglang/zig/issues/3257
2834pub fn punt_to_lld(arena: *Allocator, args: []const []const u8) error{OutOfMemory} {
2835 if (!build_options.have_llvm)
2836 fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]});
2837 // Convert the args to the format LLD expects.
2838 // We subtract 1 to shave off the zig binary from args[0].
2839 const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null);
2840 for (args[1..]) |arg, i| {
2841 argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
2842 }
2843 const exit_code = rc: {
2844 const llvm = @import("llvm.zig");
2845 const argc = @intCast(c_int, argv.len);
2846 if (mem.eql(u8, args[1], "ld.lld")) {
2847 break :rc llvm.LinkELF(argc, argv.ptr, true);
2848 } else if (mem.eql(u8, args[1], "ld64.lld")) {
2849 break :rc llvm.LinkMachO(argc, argv.ptr, true);
2850 } else if (mem.eql(u8, args[1], "lld-link")) {
2851 break :rc llvm.LinkCOFF(argc, argv.ptr, true);
2852 } else if (mem.eql(u8, args[1], "wasm-ld")) {
2853 break :rc llvm.LinkWasm(argc, argv.ptr, true);
2854 } else {
2855 unreachable;
2856 }
2857 };
2858 process.exit(@bitCast(u8, @truncate(i8, exit_code)));
2859}
2860
28222861const clang_args = @import("clang_options.zig").list;
28232862
28242863pub const ClangArgIterator = struct {
src/zig_llvm.cpp+15-30
......@@ -1056,39 +1056,24 @@ bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size
10561056 return false;
10571057}
10581058
1059int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early) {
1060 std::vector<const char *> args(argv, argv + argc);
1061 return lld::coff::link(args, can_exit_early, llvm::outs(), llvm::errs());
1062}
10591063
1060bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,
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);
1064int 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}
10831068
1084 case ZigLLVM_Wasm:
1085 return lld::wasm::link(array_ref_args, false, diag_stdout, diag_stderr);
1069int 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}
10861073
1087 default:
1088 break;
1089 }
1090 assert(false); // unreachable
1091 abort();
1074int 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());
10921077}
10931078
10941079static 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
507507ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os);
508508ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi);
509509
510ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,
511 void (*append_diagnostic)(void *, const char *, size_t),
512 void *context_stdout, void *context_stderr);
510ZIG_EXTERN_C int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early);
511ZIG_EXTERN_C int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early);
512ZIG_EXTERN_C int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early);
513ZIG_EXTERN_C int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early);
513514
514515ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,
515516 enum ZigLLVM_OSType os_type);