| ... | ... | @@ -238,7 +238,41 @@ pub const File = struct { |
| 238 | 238 | |
| 239 | 239 | pub fn makeExecutable(base: *File) !void { |
| 240 | 240 | 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| { |
| 242 | 276 | if (base.intermediary_basename != null) { |
| 243 | 277 | // The file we have open is not the final file that we want to |
| 244 | 278 | // make executable, so we don't have to close it. |