aboutsummaryrefslogtreecommitdiff
path: root/src/link.zig
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2020-11-26 11:21:50 +0100
committerJakub Konka <kubkon@jakubkonka.com>2020-11-26 11:50:09 +0100
commit7e8f7da3ec0a841d7d009f9f55dad22235b418d3 (patch)
tree6de09fdabffd4662d4cf6cfec17c1ad751537a0f /src/link.zig
parent64eae8f39240109ce23c21e975c1d29191b4692a (diff)
downloadzig-7e8f7da3ec0a841d7d009f9f55dad22235b418d3.tar.gz
zig-7e8f7da3ec0a841d7d009f9f55dad22235b418d3.zip
stage2 macho: rename inodes to prevent SIGKILL
Diffstat (limited to 'src/link.zig')
-rw-r--r--src/link.zig36
1 files changed, 35 insertions, 1 deletions
diff --git a/src/link.zig b/src/link.zig
index 064c178d66..2b53942ff7 100644
--- a/src/link.zig
+++ b/src/link.zig
@@ -238,7 +238,41 @@ pub const File = struct {
pub fn makeExecutable(base: *File) !void {
switch (base.tag) {
- .coff, .elf, .macho => if (base.file) |f| {
+ .macho => if (base.file) |f| {
+ if (base.intermediary_basename != null) {
+ // The file we have open is not the final file that we want to
+ // make executable, so we don't have to close it.
+ return;
+ }
+ if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) {
+ if (base.options.target.cpu.arch != .aarch64) return; // If we're not targeting aarch64, nothing to do.
+ // XNU starting with Big Sur running on arm64 is caching inodes of running binaries.
+ // Any change to the binary will effectively invalidate the kernel's cache
+ // resulting in a SIGKILL on each subsequent run. Since when doing incremental
+ // linking we're modifying a binary in-place, this will end up with the kernel
+ // killing it on every subsequent run. To circumvent it, we will copy the file
+ // into a new inode, remove the original file, and rename the copy to match
+ // the original file. This is super messy, but there doesn't seem any other
+ // way to please the XNU.
+ const random_bytes_len = 12;
+ comptime const random_sub_path_len = std.base64.Base64Encoder.calcSize(random_bytes_len);
+ const emit = base.options.emit orelse return;
+ var random_bytes: [random_bytes_len]u8 = undefined;
+ try std.crypto.randomBytes(&random_bytes);
+ var random_sub_path: [random_sub_path_len]u8 = undefined;
+ fs.base64_encoder.encode(&random_sub_path, &random_bytes);
+ const tmp_file_name = try mem.join(base.allocator, "_", &[_][]const u8{ emit.sub_path, random_sub_path[0..] });
+ defer base.allocator.free(tmp_file_name);
+ var tmp_file = try emit.directory.handle.createFile(tmp_file_name, .{ .mode = determineMode(base.options) });
+ defer tmp_file.close();
+ const stat = try f.stat();
+ _ = try f.copyRangeAll(0, tmp_file, 0, stat.size);
+ try emit.directory.handle.rename(tmp_file_name, emit.sub_path);
+ }
+ f.close();
+ base.file = null;
+ },
+ .coff, .elf => if (base.file) |f| {
if (base.intermediary_basename != null) {
// The file we have open is not the final file that we want to
// make executable, so we don't have to close it.