diff --git a/build.zig b/build.zig index b4d8b71f79..ae6073f8be 100644 --- a/build.zig +++ b/build.zig @@ -396,8 +396,8 @@ fn configureStage2(b: *Builder, exe: anytype, ctx: Context, need_cpp_includes: b try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null, need_cpp_includes); exe.linkSystemLibrary("pthread"); // TODO LLD cannot perform this link. + // Set ZIG_SYSTEM_LINKER_HACK env var to use system linker ld instead. // See https://github.com/ziglang/zig/issues/1535 - exe.enableSystemLinkerHack(); } else |err| switch (err) { error.RequiredLibraryNotFound => { // System compiler, not gcc. diff --git a/lib/std/build.zig b/lib/std/build.zig index a1ac3f88f2..5666005fa4 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1237,7 +1237,6 @@ pub const LibExeObjStep = struct { packages: ArrayList(Pkg), build_options_contents: std.ArrayList(u8), build_options_artifact_args: std.ArrayList(BuildOptionArtifactArg), - system_linker_hack: bool = false, object_src: []const u8, @@ -1898,10 +1897,6 @@ pub const LibExeObjStep = struct { self.exec_cmd_args = args; } - pub fn enableSystemLinkerHack(self: *LibExeObjStep) void { - self.system_linker_hack = true; - } - fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void { self.step.dependOn(&other.step); self.link_objects.append(LinkObject{ .OtherStep = other }) catch unreachable; @@ -2283,10 +2278,6 @@ pub const LibExeObjStep = struct { } } - if (self.system_linker_hack) { - try zig_args.append("--system-linker-hack"); - } - if (self.valgrind_support) |valgrind_support| { if (valgrind_support) { try zig_args.append("-fvalgrind"); diff --git a/src/Compilation.zig b/src/Compilation.zig index d4289fad34..36847f0437 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -472,13 +472,22 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { break :blk false; }; - const syslibroot = if (build_options.have_llvm and comptime std.Target.current.isDarwin()) outer: { - const path = if (use_lld and options.is_native_os and options.target.isDarwin()) inner: { - const syslibroot_path = try std.zig.system.getSDKPath(arena); - break :inner syslibroot_path; - } else null; - break :outer path; - } else null; + const DarwinOptions = struct { + syslibroot: ?[]const u8 = null, + system_linker_hack: bool = false, + }; + + const darwin_options: DarwinOptions = if (build_options.have_llvm and comptime std.Target.current.isDarwin()) outer: { + const opts: DarwinOptions = if (use_lld and options.is_native_os and options.target.isDarwin()) inner: { + const syslibroot = try std.zig.system.getSDKPath(arena); + const system_linker_hack = std.os.getenv("ZIG_SYSTEM_LINKER_HACK") != null; + break :inner .{ + .syslibroot = syslibroot, + .system_linker_hack = system_linker_hack, + }; + } else .{}; + break :outer opts; + } else .{}; const link_libc = options.link_libc or target_util.osRequiresLibC(options.target); @@ -775,13 +784,14 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .optimize_mode = options.optimize_mode, .use_lld = use_lld, .use_llvm = use_llvm, + .system_linker_hack = darwin_options.system_linker_hack, .link_libc = link_libc, .link_libcpp = options.link_libcpp, .objects = options.link_objects, .frameworks = options.frameworks, .framework_dirs = options.framework_dirs, .system_libs = system_libs, - .syslibroot = syslibroot, + .syslibroot = darwin_options.syslibroot, .lib_dirs = options.lib_dirs, .rpath_list = options.rpath_list, .strip = strip, diff --git a/src/link.zig b/src/link.zig index 02433ecde7..7b0271f978 100644 --- a/src/link.zig +++ b/src/link.zig @@ -57,6 +57,9 @@ pub const Options = struct { /// other objects. /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary. use_llvm: bool, + /// Darwin-only. If this is true, `use_llvm` is true, and `is_native_os` is true, this link code will + /// use system linker `ld` instead of the LLD. + system_linker_hack: bool, link_libc: bool, link_libcpp: bool, function_sections: bool, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 01830c1561..6fbd1265d5 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -532,11 +532,19 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { // Create an LLD command line and invoke it. var argv = std.ArrayList([]const u8).init(self.base.allocator); defer argv.deinit(); - // Even though we're calling LLD as a library it thinks the first argument is its own exe name. - try argv.append("lld"); - try argv.append("-error-limit"); - try argv.append("0"); + // TODO https://github.com/ziglang/zig/issues/6971 + // Note that there is no need to check if running natively since we do that already + // when setting `system_linker_hack` in Compilation struct. + if (self.base.options.system_linker_hack) { + try argv.append("ld"); + } else { + // Even though we're calling LLD as a library it thinks the first argument is its own exe name. + try argv.append("lld"); + + try argv.append("-error-limit"); + try argv.append("0"); + } try argv.append("-demangle"); @@ -703,42 +711,65 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { Compilation.dump_argv(argv.items); } - const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); - for (argv.items) |arg, i| { - new_argv[i] = try arena.dupeZ(u8, arg); - } + // TODO https://github.com/ziglang/zig/issues/6971 + // Note that there is no need to check if running natively since we do that already + // when setting `system_linker_hack` in Compilation struct. + if (self.base.options.system_linker_hack) { + const result = try std.ChildProcess.exec(.{ .allocator = self.base.allocator, .argv = argv.items }); + defer { + self.base.allocator.free(result.stdout); + self.base.allocator.free(result.stderr); + } + if (result.stdout.len != 0) { + std.log.warn("unexpected LD stdout: {}", .{result.stdout}); + } + if (result.stderr.len != 0) { + std.log.warn("unexpected LD stderr: {}", .{result.stderr}); + } + if (result.term != .Exited or result.term.Exited != 0) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{result.stderr}); + return error.LDReportedFailure; + } + } else { + const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } - var stderr_context: LLDContext = .{ - .macho = self, - .data = std.ArrayList(u8).init(self.base.allocator), - }; - defer stderr_context.data.deinit(); - var stdout_context: LLDContext = .{ - .macho = self, - .data = std.ArrayList(u8).init(self.base.allocator), - }; - defer stdout_context.data.deinit(); - const llvm = @import("../llvm.zig"); - const ok = llvm.Link( - .MachO, - new_argv.ptr, - new_argv.len, - append_diagnostic, - @ptrToInt(&stdout_context), - @ptrToInt(&stderr_context), - ); - if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; - if (stdout_context.data.items.len != 0) { - std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); - } - if (!ok) { - // TODO parse this output and surface with the Compilation API rather than - // directly outputting to stderr here. - std.debug.print("{}", .{stderr_context.data.items}); - return error.LLDReportedFailure; - } - if (stderr_context.data.items.len != 0) { - std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + var stderr_context: LLDContext = .{ + .macho = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stderr_context.data.deinit(); + var stdout_context: LLDContext = .{ + .macho = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stdout_context.data.deinit(); + const llvm = @import("../llvm.zig"); + const ok = llvm.Link( + .MachO, + new_argv.ptr, + new_argv.len, + append_diagnostic, + @ptrToInt(&stdout_context), + @ptrToInt(&stderr_context), + ); + if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; + if (stdout_context.data.items.len != 0) { + std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); + } + if (!ok) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{stderr_context.data.items}); + return error.LLDReportedFailure; + } + if (stderr_context.data.items.len != 0) { + std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + } } }