From 3b5be9fb6e06f6494ca05fb087a2121e220beb3f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 3 May 2024 10:50:39 -0400 Subject: lld: use a response file on `NameTooLong` --- src/link.zig | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) (limited to 'src/link.zig') diff --git a/src/link.zig b/src/link.zig index 8b28a17b9d..ae61e76ebf 100644 --- a/src/link.zig +++ b/src/link.zig @@ -19,6 +19,8 @@ const InternPool = @import("InternPool.zig"); const Type = @import("type.zig").Type; const Value = @import("Value.zig"); const LlvmObject = @import("codegen/llvm.zig").Object; +const lldMain = @import("main.zig").lldMain; +const Package = @import("Package.zig"); /// When adding a new field, remember to update `hashAddSystemLibs`. /// These are *always* dynamically linked. Static libraries will be @@ -982,3 +984,111 @@ pub const File = struct { pub const NvPtx = @import("link/NvPtx.zig"); pub const Dwarf = @import("link/Dwarf.zig"); }; + +pub fn spawnLld( + comp: *Compilation, + arena: Allocator, + argv: []const []const u8, +) !void { + if (comp.verbose_link) { + // Skip over our own name so that the LLD linker name is the first argv item. + Compilation.dump_argv(argv[1..]); + } + + // If possible, we run LLD as a child process because it does not always + // behave properly as a library, unfortunately. + // https://github.com/ziglang/zig/issues/3825 + if (!std.process.can_spawn) { + const exit_code = try lldMain(arena, argv, false); + if (exit_code == 0) return; + if (comp.clang_passthrough_mode) std.process.exit(exit_code); + return error.LLDReportedFailure; + } + + var stderr: []u8 = &.{}; + defer comp.gpa.free(stderr); + + var child = std.process.Child.init(argv, arena); + const term = (if (comp.clang_passthrough_mode) term: { + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + break :term child.spawnAndWait(); + } else term: { + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Ignore; + child.stderr_behavior = .Pipe; + + child.spawn() catch |err| break :term err; + stderr = try child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); + break :term child.wait(); + }) catch |first_err| term: { + const err = switch (first_err) { + error.NameTooLong => err: { + const s = fs.path.sep_str; + const rand_int = std.crypto.random.int(u64); + const rsp_path = "tmp" ++ s ++ Package.Manifest.hex64(rand_int) ++ ".rsp"; + + const rsp_file = try comp.local_cache_directory.handle.createFileZ(rsp_path, .{}); + defer comp.local_cache_directory.handle.deleteFileZ(rsp_path) catch |err| + log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) }); + { + defer rsp_file.close(); + var rsp_buf = std.io.bufferedWriter(rsp_file.writer()); + const rsp_writer = rsp_buf.writer(); + for (argv[2..]) |arg| { + try rsp_writer.writeByte('"'); + for (arg) |c| { + switch (c) { + '\"', '\\' => try rsp_writer.writeByte('\\'), + else => {}, + } + try rsp_writer.writeByte(c); + } + try rsp_writer.writeByte('"'); + try rsp_writer.writeByte('\n'); + } + try rsp_buf.flush(); + } + + var rsp_child = std.process.Child.init(&.{ argv[0], argv[1], try std.fmt.allocPrint( + arena, + "@{s}", + .{try comp.local_cache_directory.join(arena, &.{rsp_path})}, + ) }, arena); + if (comp.clang_passthrough_mode) { + rsp_child.stdin_behavior = .Inherit; + rsp_child.stdout_behavior = .Inherit; + rsp_child.stderr_behavior = .Inherit; + + break :term rsp_child.spawnAndWait() catch |err| break :err err; + } else { + rsp_child.stdin_behavior = .Ignore; + rsp_child.stdout_behavior = .Ignore; + rsp_child.stderr_behavior = .Pipe; + + rsp_child.spawn() catch |err| break :err err; + stderr = try rsp_child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); + break :term rsp_child.wait() catch |err| break :err err; + } + }, + else => first_err, + }; + log.err("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + + switch (term) { + .Exited => |code| if (code != 0) { + comp.lockAndParseLldStderr(argv[1], stderr); + return error.LLDReportedFailure; + }, + else => { + log.err("{s} terminated with stderr:\n{s}", .{ argv[0], stderr }); + return error.LLDCrashed; + }, + } + + if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr}); +} -- cgit v1.2.3