authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-11-26 11:21:50+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-11-26 11:50:09+01:00
log7e8f7da3ec0a841d7d009f9f55dad22235b418d3
tree6de09fdabffd4662d4cf6cfec17c1ad751537a0f
parent64eae8f39240109ce23c21e975c1d29191b4692a

stage2 macho: rename inodes to prevent SIGKILL


2 files changed, 38 insertions(+), 1 deletions(-)

src/link.zig+35-1
......@@ -238,7 +238,41 @@ pub const File = struct {
238238
239239 pub fn makeExecutable(base: *File) !void {
240240 switch (base.tag) {
241 .coff, .elf, .macho => if (base.file) |f| {
241 .macho => if (base.file) |f| {
242 if (base.intermediary_basename != null) {
243 // The file we have open is not the final file that we want to
244 // make executable, so we don't have to close it.
245 return;
246 }
247 if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) {
248 if (base.options.target.cpu.arch != .aarch64) return; // If we're not targeting aarch64, nothing to do.
249 // XNU starting with Big Sur running on arm64 is caching inodes of running binaries.
250 // Any change to the binary will effectively invalidate the kernel's cache
251 // resulting in a SIGKILL on each subsequent run. Since when doing incremental
252 // linking we're modifying a binary in-place, this will end up with the kernel
253 // killing it on every subsequent run. To circumvent it, we will copy the file
254 // into a new inode, remove the original file, and rename the copy to match
255 // the original file. This is super messy, but there doesn't seem any other
256 // way to please the XNU.
257 const random_bytes_len = 12;
258 comptime const random_sub_path_len = std.base64.Base64Encoder.calcSize(random_bytes_len);
259 const emit = base.options.emit orelse return;
260 var random_bytes: [random_bytes_len]u8 = undefined;
261 try std.crypto.randomBytes(&random_bytes);
262 var random_sub_path: [random_sub_path_len]u8 = undefined;
263 fs.base64_encoder.encode(&random_sub_path, &random_bytes);
264 const tmp_file_name = try mem.join(base.allocator, "_", &[_][]const u8{ emit.sub_path, random_sub_path[0..] });
265 defer base.allocator.free(tmp_file_name);
266 var tmp_file = try emit.directory.handle.createFile(tmp_file_name, .{ .mode = determineMode(base.options) });
267 defer tmp_file.close();
268 const stat = try f.stat();
269 _ = try f.copyRangeAll(0, tmp_file, 0, stat.size);
270 try emit.directory.handle.rename(tmp_file_name, emit.sub_path);
271 }
272 f.close();
273 base.file = null;
274 },
275 .coff, .elf => if (base.file) |f| {
242276 if (base.intermediary_basename != null) {
243277 // The file we have open is not the final file that we want to
244278 // make executable, so we don't have to close it.
src/main.zig+3
......@@ -1727,6 +1727,9 @@ fn buildOutputType(
17271727 error.SemanticAnalyzeFail => process.exit(1),
17281728 else => |e| return e,
17291729 };
1730 if (output_mode == .Exe) {
1731 try comp.makeBinFileExecutable();
1732 }
17301733
17311734 if (build_options.is_stage1 and comp.stage1_lock != null and watch) {
17321735 warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});