authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-19 21:22:40+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-03-19 21:22:40+01:00
log9a203fa7894cde183dc04bdfea63d9aea18c52da
tree86af1cc4f894294d9cf1d4cd92bbc0391a77ecd4
parent30aeb41a19d3ac15c89190cd546cbe7c05c60ac8
parent6874b2930891363e1fc393d99f59f361eb69eada
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14986 from ziglang/macos-hcs

Upstream macOS hot-code swapping PoC

9 files changed, 574 insertions(+), 231 deletions(-)

build.zig+2
...@@ -152,6 +152,7 @@ pub fn build(b: *std.Build) !void {...@@ -152,6 +152,7 @@ pub fn build(b: *std.Build) !void {
152 if (only_install_lib_files)152 if (only_install_lib_files)
153 return;153 return;
154154
155 const entitlements = b.option([]const u8, "entitlements", "Path to entitlements file for hot-code swapping without sudo on macOS");
155 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");156 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
156 const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);157 const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
157 const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);158 const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
...@@ -173,6 +174,7 @@ pub fn build(b: *std.Build) !void {...@@ -173,6 +174,7 @@ pub fn build(b: *std.Build) !void {
173 exe.pie = pie;174 exe.pie = pie;
174 exe.sanitize_thread = sanitize_thread;175 exe.sanitize_thread = sanitize_thread;
175 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;176 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
177 exe.entitlements = entitlements;
176 exe.install();178 exe.install();
177179
178 const compile_step = b.step("compile", "Build the self-hosted compiler");180 const compile_step = b.step("compile", "Build the self-hosted compiler");
lib/std/c/darwin.zig+309-80
...@@ -203,47 +203,6 @@ pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) kern_retu...@@ -203,47 +203,6 @@ pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) kern_retu
203pub extern "c" fn malloc_size(?*const anyopaque) usize;203pub extern "c" fn malloc_size(?*const anyopaque) usize;
204pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int;204pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int;
205205
206pub const posix_spawnattr_t = *opaque {};
207pub const posix_spawn_file_actions_t = *opaque {};
208pub extern "c" fn posix_spawnattr_init(attr: *posix_spawnattr_t) c_int;
209pub extern "c" fn posix_spawnattr_destroy(attr: *posix_spawnattr_t) c_int;
210pub extern "c" fn posix_spawnattr_setflags(attr: *posix_spawnattr_t, flags: c_short) c_int;
211pub extern "c" fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t, flags: *c_short) c_int;
212pub extern "c" fn posix_spawn_file_actions_init(actions: *posix_spawn_file_actions_t) c_int;
213pub extern "c" fn posix_spawn_file_actions_destroy(actions: *posix_spawn_file_actions_t) c_int;
214pub extern "c" fn posix_spawn_file_actions_addclose(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
215pub extern "c" fn posix_spawn_file_actions_addopen(
216 actions: *posix_spawn_file_actions_t,
217 filedes: fd_t,
218 path: [*:0]const u8,
219 oflag: c_int,
220 mode: mode_t,
221) c_int;
222pub extern "c" fn posix_spawn_file_actions_adddup2(
223 actions: *posix_spawn_file_actions_t,
224 filedes: fd_t,
225 newfiledes: fd_t,
226) c_int;
227pub extern "c" fn posix_spawn_file_actions_addinherit_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
228pub extern "c" fn posix_spawn_file_actions_addchdir_np(actions: *posix_spawn_file_actions_t, path: [*:0]const u8) c_int;
229pub extern "c" fn posix_spawn_file_actions_addfchdir_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
230pub extern "c" fn posix_spawn(
231 pid: *pid_t,
232 path: [*:0]const u8,
233 actions: ?*const posix_spawn_file_actions_t,
234 attr: ?*const posix_spawnattr_t,
235 argv: [*:null]?[*:0]const u8,
236 env: [*:null]?[*:0]const u8,
237) c_int;
238pub extern "c" fn posix_spawnp(
239 pid: *pid_t,
240 path: [*:0]const u8,
241 actions: ?*const posix_spawn_file_actions_t,
242 attr: ?*const posix_spawnattr_t,
243 argv: [*:null]?[*:0]const u8,
244 env: [*:null]?[*:0]const u8,
245) c_int;
246
247pub extern "c" fn kevent64(206pub extern "c" fn kevent64(
248 kq: c_int,207 kq: c_int,
249 changelist: [*]const kevent64_s,208 changelist: [*]const kevent64_s,
...@@ -2176,18 +2135,6 @@ pub const E = enum(u16) {...@@ -2176,18 +2135,6 @@ pub const E = enum(u16) {
2176 _,2135 _,
2177};2136};
21782137
2179pub fn getKernError(err: kern_return_t) KernE {
2180 return @intToEnum(KernE, @truncate(u32, @intCast(usize, err)));
2181}
2182
2183pub fn unexpectedKernError(err: KernE) std.os.UnexpectedError {
2184 if (std.os.unexpected_error_tracing) {
2185 std.debug.print("unexpected errno: {d}\n", .{@enumToInt(err)});
2186 std.debug.dumpCurrentStackTrace(null);
2187 }
2188 return error.Unexpected;
2189}
2190
2191/// Kernel return values2138/// Kernel return values
2192pub const KernE = enum(u32) {2139pub const KernE = enum(u32) {
2193 SUCCESS = 0,2140 SUCCESS = 0,
...@@ -3063,38 +3010,320 @@ pub const CPUFAMILY = enum(u32) {...@@ -3063,38 +3010,320 @@ pub const CPUFAMILY = enum(u32) {
3063 _,3010 _,
3064};3011};
30653012
3066pub const POSIX_SPAWN_RESETIDS = 0x0001;3013pub const PT = struct {
3067pub const POSIX_SPAWN_SETPGROUP = 0x0002;3014 pub const TRACE_ME = 0;
3068pub const POSIX_SPAWN_SETSIGDEF = 0x0004;3015 pub const READ_I = 1;
3069pub const POSIX_SPAWN_SETSIGMASK = 0x0008;3016 pub const READ_D = 2;
3070pub const POSIX_SPAWN_SETEXEC = 0x0040;3017 pub const READ_U = 3;
3071pub const POSIX_SPAWN_START_SUSPENDED = 0x0080;3018 pub const WRITE_I = 4;
3072pub const _POSIX_SPAWN_DISABLE_ASLR = 0x0100;3019 pub const WRITE_D = 5;
3073pub const POSIX_SPAWN_SETSID = 0x0400;3020 pub const WRITE_U = 6;
3074pub const _POSIX_SPAWN_RESLIDE = 0x0800;3021 pub const CONTINUE = 7;
3075pub const POSIX_SPAWN_CLOEXEC_DEFAULT = 0x4000;3022 pub const KILL = 8;
30763023 pub const STEP = 9;
3077pub const PT_TRACE_ME = 0;3024 pub const DETACH = 11;
3078pub const PT_READ_I = 1;3025 pub const SIGEXC = 12;
3079pub const PT_READ_D = 2;3026 pub const THUPDATE = 13;
3080pub const PT_READ_U = 3;3027 pub const ATTACHEXC = 14;
3081pub const PT_WRITE_I = 4;3028 pub const FORCEQUOTA = 30;
3082pub const PT_WRITE_D = 5;3029 pub const DENY_ATTACH = 31;
3083pub const PT_WRITE_U = 6;3030};
3084pub const PT_CONTINUE = 7;
3085pub const PT_KILL = 8;
3086pub const PT_STEP = 9;
3087pub const PT_DETACH = 11;
3088pub const PT_SIGEXC = 12;
3089pub const PT_THUPDATE = 13;
3090pub const PT_ATTACHEXC = 14;
3091pub const PT_FORCEQUOTA = 30;
3092pub const PT_DENY_ATTACH = 31;
30933031
3094pub const caddr_t = ?[*]u8;3032pub const caddr_t = ?[*]u8;
30953033
3096pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int;3034pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int;
30973035
3036pub const POSIX_SPAWN = struct {
3037 pub const RESETIDS = 0x0001;
3038 pub const SETPGROUP = 0x0002;
3039 pub const SETSIGDEF = 0x0004;
3040 pub const SETSIGMASK = 0x0008;
3041 pub const SETEXEC = 0x0040;
3042 pub const START_SUSPENDED = 0x0080;
3043 pub const DISABLE_ASLR = 0x0100;
3044 pub const SETSID = 0x0400;
3045 pub const RESLIDE = 0x0800;
3046 pub const CLOEXEC_DEFAULT = 0x4000;
3047};
3048
3049pub const posix_spawnattr_t = *opaque {};
3050pub const posix_spawn_file_actions_t = *opaque {};
3051pub extern "c" fn posix_spawnattr_init(attr: *posix_spawnattr_t) c_int;
3052pub extern "c" fn posix_spawnattr_destroy(attr: *posix_spawnattr_t) c_int;
3053pub extern "c" fn posix_spawnattr_setflags(attr: *posix_spawnattr_t, flags: c_short) c_int;
3054pub extern "c" fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t, flags: *c_short) c_int;
3055pub extern "c" fn posix_spawn_file_actions_init(actions: *posix_spawn_file_actions_t) c_int;
3056pub extern "c" fn posix_spawn_file_actions_destroy(actions: *posix_spawn_file_actions_t) c_int;
3057pub extern "c" fn posix_spawn_file_actions_addclose(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
3058pub extern "c" fn posix_spawn_file_actions_addopen(
3059 actions: *posix_spawn_file_actions_t,
3060 filedes: fd_t,
3061 path: [*:0]const u8,
3062 oflag: c_int,
3063 mode: mode_t,
3064) c_int;
3065pub extern "c" fn posix_spawn_file_actions_adddup2(
3066 actions: *posix_spawn_file_actions_t,
3067 filedes: fd_t,
3068 newfiledes: fd_t,
3069) c_int;
3070pub extern "c" fn posix_spawn_file_actions_addinherit_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
3071pub extern "c" fn posix_spawn_file_actions_addchdir_np(actions: *posix_spawn_file_actions_t, path: [*:0]const u8) c_int;
3072pub extern "c" fn posix_spawn_file_actions_addfchdir_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
3073pub extern "c" fn posix_spawn(
3074 pid: *pid_t,
3075 path: [*:0]const u8,
3076 actions: ?*const posix_spawn_file_actions_t,
3077 attr: ?*const posix_spawnattr_t,
3078 argv: [*:null]?[*:0]const u8,
3079 env: [*:null]?[*:0]const u8,
3080) c_int;
3081pub extern "c" fn posix_spawnp(
3082 pid: *pid_t,
3083 path: [*:0]const u8,
3084 actions: ?*const posix_spawn_file_actions_t,
3085 attr: ?*const posix_spawnattr_t,
3086 argv: [*:null]?[*:0]const u8,
3087 env: [*:null]?[*:0]const u8,
3088) c_int;
3089
3090pub const PosixSpawn = struct {
3091 const errno = std.os.errno;
3092 const unexpectedErrno = std.os.unexpectedErrno;
3093
3094 pub const Error = error{
3095 SystemResources,
3096 InvalidFileDescriptor,
3097 NameTooLong,
3098 TooBig,
3099 PermissionDenied,
3100 InputOutput,
3101 FileSystem,
3102 FileNotFound,
3103 InvalidExe,
3104 NotDir,
3105 FileBusy,
3106 /// Returned when the child fails to execute either in the pre-exec() initialization step, or
3107 /// when exec(3) is invoked.
3108 ChildExecFailed,
3109 } || std.os.UnexpectedError;
3110
3111 pub const Attr = struct {
3112 attr: posix_spawnattr_t,
3113
3114 pub fn init() Error!Attr {
3115 var attr: posix_spawnattr_t = undefined;
3116 switch (errno(posix_spawnattr_init(&attr))) {
3117 .SUCCESS => return Attr{ .attr = attr },
3118 .NOMEM => return error.SystemResources,
3119 .INVAL => unreachable,
3120 else => |err| return unexpectedErrno(err),
3121 }
3122 }
3123
3124 pub fn deinit(self: *Attr) void {
3125 defer self.* = undefined;
3126 switch (errno(posix_spawnattr_destroy(&self.attr))) {
3127 .SUCCESS => return,
3128 .INVAL => unreachable, // Invalid parameters.
3129 else => unreachable,
3130 }
3131 }
3132
3133 pub fn get(self: Attr) Error!u16 {
3134 var flags: c_short = undefined;
3135 switch (errno(posix_spawnattr_getflags(&self.attr, &flags))) {
3136 .SUCCESS => return @bitCast(u16, flags),
3137 .INVAL => unreachable,
3138 else => |err| return unexpectedErrno(err),
3139 }
3140 }
3141
3142 pub fn set(self: *Attr, flags: u16) Error!void {
3143 switch (errno(posix_spawnattr_setflags(&self.attr, @bitCast(c_short, flags)))) {
3144 .SUCCESS => return,
3145 .INVAL => unreachable,
3146 else => |err| return unexpectedErrno(err),
3147 }
3148 }
3149 };
3150
3151 pub const Actions = struct {
3152 actions: posix_spawn_file_actions_t,
3153
3154 pub fn init() Error!Actions {
3155 var actions: posix_spawn_file_actions_t = undefined;
3156 switch (errno(posix_spawn_file_actions_init(&actions))) {
3157 .SUCCESS => return Actions{ .actions = actions },
3158 .NOMEM => return error.SystemResources,
3159 .INVAL => unreachable,
3160 else => |err| return unexpectedErrno(err),
3161 }
3162 }
3163
3164 pub fn deinit(self: *Actions) void {
3165 defer self.* = undefined;
3166 switch (errno(posix_spawn_file_actions_destroy(&self.actions))) {
3167 .SUCCESS => return,
3168 .INVAL => unreachable, // Invalid parameters.
3169 else => unreachable,
3170 }
3171 }
3172
3173 pub fn open(self: *Actions, fd: fd_t, path: []const u8, flags: u32, mode: mode_t) Error!void {
3174 const posix_path = try std.os.toPosixPath(path);
3175 return self.openZ(fd, &posix_path, flags, mode);
3176 }
3177
3178 pub fn openZ(self: *Actions, fd: fd_t, path: [*:0]const u8, flags: u32, mode: mode_t) Error!void {
3179 switch (errno(posix_spawn_file_actions_addopen(&self.actions, fd, path, @bitCast(c_int, flags), mode))) {
3180 .SUCCESS => return,
3181 .BADF => return error.InvalidFileDescriptor,
3182 .NOMEM => return error.SystemResources,
3183 .NAMETOOLONG => return error.NameTooLong,
3184 .INVAL => unreachable, // the value of file actions is invalid
3185 else => |err| return unexpectedErrno(err),
3186 }
3187 }
3188
3189 pub fn close(self: *Actions, fd: fd_t) Error!void {
3190 switch (errno(posix_spawn_file_actions_addclose(&self.actions, fd))) {
3191 .SUCCESS => return,
3192 .BADF => return error.InvalidFileDescriptor,
3193 .NOMEM => return error.SystemResources,
3194 .INVAL => unreachable, // the value of file actions is invalid
3195 .NAMETOOLONG => unreachable,
3196 else => |err| return unexpectedErrno(err),
3197 }
3198 }
3199
3200 pub fn dup2(self: *Actions, fd: fd_t, newfd: fd_t) Error!void {
3201 switch (errno(posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) {
3202 .SUCCESS => return,
3203 .BADF => return error.InvalidFileDescriptor,
3204 .NOMEM => return error.SystemResources,
3205 .INVAL => unreachable, // the value of file actions is invalid
3206 .NAMETOOLONG => unreachable,
3207 else => |err| return unexpectedErrno(err),
3208 }
3209 }
3210
3211 pub fn inherit(self: *Actions, fd: fd_t) Error!void {
3212 switch (errno(posix_spawn_file_actions_addinherit_np(&self.actions, fd))) {
3213 .SUCCESS => return,
3214 .BADF => return error.InvalidFileDescriptor,
3215 .NOMEM => return error.SystemResources,
3216 .INVAL => unreachable, // the value of file actions is invalid
3217 .NAMETOOLONG => unreachable,
3218 else => |err| return unexpectedErrno(err),
3219 }
3220 }
3221
3222 pub fn chdir(self: *Actions, path: []const u8) Error!void {
3223 const posix_path = try std.os.toPosixPath(path);
3224 return self.chdirZ(&posix_path);
3225 }
3226
3227 pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void {
3228 switch (errno(posix_spawn_file_actions_addchdir_np(&self.actions, path))) {
3229 .SUCCESS => return,
3230 .NOMEM => return error.SystemResources,
3231 .NAMETOOLONG => return error.NameTooLong,
3232 .BADF => unreachable,
3233 .INVAL => unreachable, // the value of file actions is invalid
3234 else => |err| return unexpectedErrno(err),
3235 }
3236 }
3237
3238 pub fn fchdir(self: *Actions, fd: fd_t) Error!void {
3239 switch (errno(posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) {
3240 .SUCCESS => return,
3241 .BADF => return error.InvalidFileDescriptor,
3242 .NOMEM => return error.SystemResources,
3243 .INVAL => unreachable, // the value of file actions is invalid
3244 .NAMETOOLONG => unreachable,
3245 else => |err| return unexpectedErrno(err),
3246 }
3247 }
3248 };
3249
3250 pub fn spawn(
3251 path: []const u8,
3252 actions: ?Actions,
3253 attr: ?Attr,
3254 argv: [*:null]?[*:0]const u8,
3255 envp: [*:null]?[*:0]const u8,
3256 ) Error!pid_t {
3257 const posix_path = try std.os.toPosixPath(path);
3258 return spawnZ(&posix_path, actions, attr, argv, envp);
3259 }
3260
3261 pub fn spawnZ(
3262 path: [*:0]const u8,
3263 actions: ?Actions,
3264 attr: ?Attr,
3265 argv: [*:null]?[*:0]const u8,
3266 envp: [*:null]?[*:0]const u8,
3267 ) Error!pid_t {
3268 var pid: pid_t = undefined;
3269 switch (errno(posix_spawn(
3270 &pid,
3271 path,
3272 if (actions) |a| &a.actions else null,
3273 if (attr) |a| &a.attr else null,
3274 argv,
3275 envp,
3276 ))) {
3277 .SUCCESS => return pid,
3278 .@"2BIG" => return error.TooBig,
3279 .NOMEM => return error.SystemResources,
3280 .BADF => return error.InvalidFileDescriptor,
3281 .ACCES => return error.PermissionDenied,
3282 .IO => return error.InputOutput,
3283 .LOOP => return error.FileSystem,
3284 .NAMETOOLONG => return error.NameTooLong,
3285 .NOENT => return error.FileNotFound,
3286 .NOEXEC => return error.InvalidExe,
3287 .NOTDIR => return error.NotDir,
3288 .TXTBSY => return error.FileBusy,
3289 .BADARCH => return error.InvalidExe,
3290 .BADEXEC => return error.InvalidExe,
3291 .FAULT => unreachable,
3292 .INVAL => unreachable,
3293 else => |err| return unexpectedErrno(err),
3294 }
3295 }
3296
3297 pub fn waitpid(pid: pid_t, flags: u32) Error!std.os.WaitPidResult {
3298 var status: c_int = undefined;
3299 while (true) {
3300 const rc = waitpid(pid, &status, @intCast(c_int, flags));
3301 switch (errno(rc)) {
3302 .SUCCESS => return std.os.WaitPidResult{
3303 .pid = @intCast(pid_t, rc),
3304 .status = @bitCast(u32, status),
3305 },
3306 .INTR => continue,
3307 .CHILD => return error.ChildExecFailed,
3308 .INVAL => unreachable, // Invalid flags.
3309 else => unreachable,
3310 }
3311 }
3312 }
3313};
3314
3315pub fn getKernError(err: kern_return_t) KernE {
3316 return @intToEnum(KernE, @truncate(u32, @intCast(usize, err)));
3317}
3318
3319pub fn unexpectedKernError(err: KernE) std.os.UnexpectedError {
3320 if (std.os.unexpected_error_tracing) {
3321 std.debug.print("unexpected error: {d}\n", .{@enumToInt(err)});
3322 std.debug.dumpCurrentStackTrace(null);
3323 }
3324 return error.Unexpected;
3325}
3326
3098pub const MachError = error{3327pub const MachError = error{
3099 /// Not enough permissions held to perform the requested kernel3328 /// Not enough permissions held to perform the requested kernel
3100 /// call.3329 /// call.
lib/std/macho.zig+4
...@@ -656,6 +656,10 @@ pub const segment_command_64 = extern struct {...@@ -656,6 +656,10 @@ pub const segment_command_64 = extern struct {
656 pub fn segName(seg: *const segment_command_64) []const u8 {656 pub fn segName(seg: *const segment_command_64) []const u8 {
657 return parseName(&seg.segname);657 return parseName(&seg.segname);
658 }658 }
659
660 pub fn isWriteable(seg: segment_command_64) bool {
661 return seg.initprot & PROT.WRITE != 0;
662 }
659};663};
660664
661pub const PROT = struct {665pub const PROT = struct {
lib/std/os.zig+40-13
...@@ -7129,22 +7129,49 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {...@@ -7129,22 +7129,49 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {
71297129
7130pub const PtraceError = error{7130pub const PtraceError = error{
7131 DeviceBusy,7131 DeviceBusy,
7132 InputOutput,
7133 Overflow,
7132 ProcessNotFound,7134 ProcessNotFound,
7133 PermissionDenied,7135 PermissionDenied,
7134} || UnexpectedError;7136} || UnexpectedError;
71357137
7136/// TODO on other OSes7138pub fn ptrace(request: u32, pid: pid_t, addr: usize, signal: usize) PtraceError!void {
7137pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void {7139 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
7138 switch (builtin.os.tag) {7140 @compileError("Unsupported OS");
7139 .macos, .ios, .tvos, .watchos => {},7141
7140 else => @compileError("TODO implement ptrace"),7142 return switch (builtin.os.tag) {
7141 }7143 .linux => switch (errno(linux.ptrace(request, pid, addr, signal, 0))) {
7142 return switch (errno(system.ptrace(request, pid, addr, signal))) {7144 .SUCCESS => {},
7143 .SUCCESS => {},7145 .SRCH => error.ProcessNotFound,
7144 .SRCH => error.ProcessNotFound,7146 .FAULT => unreachable,
7145 .INVAL => unreachable,7147 .INVAL => unreachable,
7146 .PERM => error.PermissionDenied,7148 .IO => return error.InputOutput,
7147 .BUSY => error.DeviceBusy,7149 .PERM => error.PermissionDenied,
7148 else => |err| return unexpectedErrno(err),7150 .BUSY => error.DeviceBusy,
7151 else => |err| return unexpectedErrno(err),
7152 },
7153
7154 .macos, .ios, .tvos, .watchos => switch (errno(darwin.ptrace(
7155 math.cast(i32, request) orelse return error.Overflow,
7156 pid,
7157 @intToPtr(?[*]u8, addr),
7158 math.cast(i32, signal) orelse return error.Overflow,
7159 ))) {
7160 .SUCCESS => {},
7161 .SRCH => error.ProcessNotFound,
7162 .INVAL => unreachable,
7163 .PERM => error.PermissionDenied,
7164 .BUSY => error.DeviceBusy,
7165 else => |err| return unexpectedErrno(err),
7166 },
7167
7168 else => switch (errno(system.ptrace(request, pid, addr, signal))) {
7169 .SUCCESS => {},
7170 .SRCH => error.ProcessNotFound,
7171 .INVAL => unreachable,
7172 .PERM => error.PermissionDenied,
7173 .BUSY => error.DeviceBusy,
7174 else => |err| return unexpectedErrno(err),
7175 },
7149 };7176 };
7150}7177}
src/link.zig+10-10
...@@ -389,11 +389,11 @@ pub const File = struct {...@@ -389,11 +389,11 @@ pub const File = struct {
389 try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{});389 try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{});
390 try emit.directory.handle.rename(tmp_sub_path, emit.sub_path);390 try emit.directory.handle.rename(tmp_sub_path, emit.sub_path);
391 switch (builtin.os.tag) {391 switch (builtin.os.tag) {
392 .linux => {392 .linux => std.os.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| {
393 switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0, 0))) {393 log.warn("ptrace failure: {s}", .{@errorName(err)});
394 .SUCCESS => {},394 },
395 else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}),395 .macos => base.cast(MachO).?.ptraceAttach(pid) catch |err| {
396 }396 log.warn("attaching failed with error: {s}", .{@errorName(err)});
397 },397 },
398 else => return error.HotSwapUnavailableOnHostOperatingSystem,398 else => return error.HotSwapUnavailableOnHostOperatingSystem,
399 }399 }
...@@ -430,11 +430,11 @@ pub const File = struct {...@@ -430,11 +430,11 @@ pub const File = struct {
430430
431 if (base.child_pid) |pid| {431 if (base.child_pid) |pid| {
432 switch (builtin.os.tag) {432 switch (builtin.os.tag) {
433 .linux => {433 .linux => std.os.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0) catch |err| {
434 switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0, 0))) {434 log.warn("ptrace failure: {s}", .{@errorName(err)});
435 .SUCCESS => {},435 },
436 else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}),436 .macos => base.cast(MachO).?.ptraceDetach(pid) catch |err| {
437 }437 log.warn("detaching failed with error: {s}", .{@errorName(err)});
438 },438 },
439 else => return error.HotSwapUnavailableOnHostOperatingSystem,439 else => return error.HotSwapUnavailableOnHostOperatingSystem,
440 }440 }
src/link/MachO.zig+95-7
...@@ -221,6 +221,14 @@ lazy_bindings: BindingTable = .{},...@@ -221,6 +221,14 @@ lazy_bindings: BindingTable = .{},
221/// Table of tracked Decls.221/// Table of tracked Decls.
222decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},222decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
223223
224/// Hot-code swapping state.
225hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
226
227const is_hot_update_compatible = switch (builtin.target.os.tag) {
228 .macos => true,
229 else => false,
230};
231
224const DeclMetadata = struct {232const DeclMetadata = struct {
225 atom: Atom.Index,233 atom: Atom.Index,
226 section: u8,234 section: u8,
...@@ -300,6 +308,10 @@ pub const SymbolWithLoc = struct {...@@ -300,6 +308,10 @@ pub const SymbolWithLoc = struct {
300 }308 }
301};309};
302310
311const HotUpdateState = struct {
312 mach_task: ?std.os.darwin.MachTask = null,
313};
314
303/// When allocating, the ideal_capacity is calculated by315/// When allocating, the ideal_capacity is calculated by
304/// actual_capacity + (actual_capacity / ideal_factor)316/// actual_capacity + (actual_capacity / ideal_factor)
305const ideal_factor = 3;317const ideal_factor = 3;
...@@ -584,7 +596,26 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -584,7 +596,26 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
584 try self.allocateSpecialSymbols();596 try self.allocateSpecialSymbols();
585597
586 for (self.relocs.keys()) |atom_index| {598 for (self.relocs.keys()) |atom_index| {
587 try Atom.resolveRelocations(self, atom_index);599 const relocs = self.relocs.get(atom_index).?;
600 const needs_update = for (relocs.items) |reloc| {
601 if (reloc.dirty) break true;
602 } else false;
603
604 if (!needs_update) continue;
605
606 const atom = self.getAtom(atom_index);
607 const sym = atom.getSymbol(self);
608 const section = self.sections.get(sym.n_sect - 1).header;
609 const file_offset = section.offset + sym.n_value - section.addr;
610
611 var code = std.ArrayList(u8).init(self.base.allocator);
612 defer code.deinit();
613 try code.resize(math.cast(usize, atom.size) orelse return error.Overflow);
614
615 const amt = try self.base.file.?.preadAll(code.items, file_offset);
616 if (amt != code.items.len) return error.InputOutput;
617
618 try self.writeAtom(atom_index, code.items);
588 }619 }
589620
590 if (build_options.enable_logging) {621 if (build_options.enable_logging) {
...@@ -1052,14 +1083,40 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:...@@ -1052,14 +1083,40 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:
1052 }1083 }
1053}1084}
10541085
1055pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []const u8) !void {1086pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {
1056 const atom = self.getAtom(atom_index);1087 const atom = self.getAtom(atom_index);
1057 const sym = atom.getSymbol(self);1088 const sym = atom.getSymbol(self);
1058 const section = self.sections.get(sym.n_sect - 1);1089 const section = self.sections.get(sym.n_sect - 1);
1059 const file_offset = section.header.offset + sym.n_value - section.header.addr;1090 const file_offset = section.header.offset + sym.n_value - section.header.addr;
1060 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });1091 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
1092
1093 if (self.relocs.get(atom_index)) |relocs| {
1094 try Atom.resolveRelocations(self, atom_index, relocs.items, code);
1095 }
1096
1097 if (is_hot_update_compatible) {
1098 if (self.base.child_pid) |pid| blk: {
1099 const task = self.hot_state.mach_task orelse {
1100 log.warn("cannot hot swap: no Mach task acquired for child process with pid {d}", .{pid});
1101 break :blk;
1102 };
1103 self.updateAtomInMemory(task, section.segment_index, sym.n_value, code) catch |err| {
1104 log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)});
1105 };
1106 }
1107 }
1108
1061 try self.base.file.?.pwriteAll(code, file_offset);1109 try self.base.file.?.pwriteAll(code, file_offset);
1062 try Atom.resolveRelocations(self, atom_index);1110}
1111
1112fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void {
1113 const segment = self.segments.items[segment_index];
1114 const cpu_arch = self.base.options.target.cpu.arch;
1115 const nwritten = if (!segment.isWriteable())
1116 try task.writeMemProtected(addr, code, cpu_arch)
1117 else
1118 try task.writeMem(addr, code, cpu_arch);
1119 if (nwritten != code.len) return error.InputOutput;
1063}1120}
10641121
1065fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {1122fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {
...@@ -1068,6 +1125,7 @@ fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {...@@ -1068,6 +1125,7 @@ fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {
1068}1125}
10691126
1070fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {1127fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
1128 log.debug("marking relocs dirty by target: {}", .{target});
1071 // TODO: reverse-lookup might come in handy here1129 // TODO: reverse-lookup might come in handy here
1072 for (self.relocs.values()) |*relocs| {1130 for (self.relocs.values()) |*relocs| {
1073 for (relocs.items) |*reloc| {1131 for (relocs.items) |*reloc| {
...@@ -1078,6 +1136,7 @@ fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {...@@ -1078,6 +1136,7 @@ fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
1078}1136}
10791137
1080fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {1138fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
1139 log.debug("marking relocs dirty by address: {x}", .{addr});
1081 for (self.relocs.values()) |*relocs| {1140 for (self.relocs.values()) |*relocs| {
1082 for (relocs.items) |*reloc| {1141 for (relocs.items) |*reloc| {
1083 const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue;1142 const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue;
...@@ -1702,6 +1761,8 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {...@@ -1702,6 +1761,8 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
1702 if (self.dyld_stub_binder_index != null) return;1761 if (self.dyld_stub_binder_index != null) return;
1703 if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports1762 if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports
17041763
1764 log.debug("resolving dyld_stub_binder", .{});
1765
1705 const gpa = self.base.allocator;1766 const gpa = self.base.allocator;
1706 const sym_index = try self.allocateSymbol();1767 const sym_index = try self.allocateSymbol();
1707 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };1768 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
...@@ -2063,7 +2124,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -2063,7 +2124,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
2063 else2124 else
2064 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);2125 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
20652126
2066 const code = switch (res) {2127 var code = switch (res) {
2067 .ok => code_buffer.items,2128 .ok => code_buffer.items,
2068 .fail => |em| {2129 .fail => |em| {
2069 decl.analysis = .codegen_failure;2130 decl.analysis = .codegen_failure;
...@@ -2115,7 +2176,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -2115,7 +2176,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
2115 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{2176 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
2116 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,2177 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
2117 });2178 });
2118 const code = switch (res) {2179 var code = switch (res) {
2119 .ok => code_buffer.items,2180 .ok => code_buffer.items,
2120 .fail => |em| {2181 .fail => |em| {
2121 decl.analysis = .codegen_failure;2182 decl.analysis = .codegen_failure;
...@@ -2202,7 +2263,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2202,7 +2263,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2202 .parent_atom_index = atom.getSymbolIndex().?,2263 .parent_atom_index = atom.getSymbolIndex().?,
2203 });2264 });
22042265
2205 const code = switch (res) {2266 var code = switch (res) {
2206 .ok => code_buffer.items,2267 .ok => code_buffer.items,
2207 .fail => |em| {2268 .fail => |em| {
2208 decl.analysis = .codegen_failure;2269 decl.analysis = .codegen_failure;
...@@ -2375,7 +2436,7 @@ pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {...@@ -2375,7 +2436,7 @@ pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
2375 return sect_id;2436 return sect_id;
2376}2437}
23772438
2378fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) !u64 {2439fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 {
2379 const gpa = self.base.allocator;2440 const gpa = self.base.allocator;
2380 const mod = self.base.options.module.?;2441 const mod = self.base.options.module.?;
2381 const decl = mod.declPtr(decl_index);2442 const decl = mod.declPtr(decl_index);
...@@ -2788,6 +2849,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2788,6 +2849,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
27882849
2789 if (self.linkedit_segment_cmd_index == null) {2850 if (self.linkedit_segment_cmd_index == null) {
2790 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);2851 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
2852
2791 try self.segments.append(gpa, .{2853 try self.segments.append(gpa, .{
2792 .segname = makeStaticString("__LINKEDIT"),2854 .segname = makeStaticString("__LINKEDIT"),
2793 .maxprot = macho.PROT.READ,2855 .maxprot = macho.PROT.READ,
...@@ -3760,6 +3822,32 @@ pub fn allocatedVirtualSize(self: *MachO, start: u64) u64 {...@@ -3760,6 +3822,32 @@ pub fn allocatedVirtualSize(self: *MachO, start: u64) u64 {
3760 return min_pos - start;3822 return min_pos - start;
3761}3823}
37623824
3825pub fn ptraceAttach(self: *MachO, pid: std.os.pid_t) !void {
3826 if (!is_hot_update_compatible) return;
3827
3828 const mach_task = try std.os.darwin.machTaskForPid(pid);
3829 log.debug("Mach task for pid {d}: {any}", .{ pid, mach_task });
3830 self.hot_state.mach_task = mach_task;
3831
3832 // TODO start exception handler in another thread
3833
3834 // TODO enable ones we register for exceptions
3835 // try std.os.ptrace(std.os.darwin.PT.ATTACHEXC, pid, 0, 0);
3836}
3837
3838pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void {
3839 if (!is_hot_update_compatible) return;
3840
3841 _ = pid;
3842
3843 // TODO stop exception handler
3844
3845 // TODO see comment in ptraceAttach
3846 // try std.os.ptrace(std.os.darwin.PT.DETACH, pid, 0, 0);
3847
3848 self.hot_state.mach_task = null;
3849}
3850
3763pub fn makeStaticString(bytes: []const u8) [16]u8 {3851pub fn makeStaticString(bytes: []const u8) [16]u8 {
3764 var buf = [_]u8{0} ** 16;3852 var buf = [_]u8{0} ** 16;
3765 assert(bytes.len <= buf.len);3853 assert(bytes.len <= buf.len);
src/link/MachO/Atom.zig+4-12
...@@ -183,19 +183,11 @@ pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !...@@ -183,19 +183,11 @@ pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !
183 try gop.value_ptr.append(gpa, binding);183 try gop.value_ptr.append(gpa, binding);
184}184}
185185
186pub fn resolveRelocations(macho_file: *MachO, atom_index: Index) !void {186pub fn resolveRelocations(macho_file: *MachO, atom_index: Index, relocs: []Relocation, code: []u8) !void {
187 const atom = macho_file.getAtom(atom_index);187 log.debug("relocating '{s}'", .{macho_file.getAtom(atom_index).getName(macho_file)});
188 const relocs = macho_file.relocs.get(atom_index) orelse return;188 for (relocs) |*reloc| {
189 const source_sym = atom.getSymbol(macho_file);
190 const source_section = macho_file.sections.get(source_sym.n_sect - 1).header;
191 const file_offset = source_section.offset + source_sym.n_value - source_section.addr;
192
193 log.debug("relocating '{s}'", .{atom.getName(macho_file)});
194
195 for (relocs.items) |*reloc| {
196 if (!reloc.dirty) continue;189 if (!reloc.dirty) continue;
197190 try reloc.resolve(macho_file, atom_index, code);
198 try reloc.resolve(macho_file, atom_index, file_offset);
199 reloc.dirty = false;191 reloc.dirty = false;
200 }192 }
201}193}
src/link/MachO/Relocation.zig+61-85
...@@ -50,7 +50,7 @@ pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index {...@@ -50,7 +50,7 @@ pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index {
50 return macho_file.getAtomIndexForSymbol(self.target);50 return macho_file.getAtomIndexForSymbol(self.target);
51}51}
5252
53pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, base_offset: u64) !void {53pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) !void {
54 const arch = macho_file.base.options.target.cpu.arch;54 const arch = macho_file.base.options.target.cpu.arch;
55 const atom = macho_file.getAtom(atom_index);55 const atom = macho_file.getAtom(atom_index);
56 const source_sym = atom.getSymbol(macho_file);56 const source_sym = atom.getSymbol(macho_file);
...@@ -68,42 +68,28 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, bas...@@ -68,42 +68,28 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, bas
68 });68 });
6969
70 switch (arch) {70 switch (arch) {
71 .aarch64 => return self.resolveAarch64(macho_file, source_addr, target_addr, base_offset),71 .aarch64 => return self.resolveAarch64(source_addr, target_addr, code),
72 .x86_64 => return self.resolveX8664(macho_file, source_addr, target_addr, base_offset),72 .x86_64 => return self.resolveX8664(source_addr, target_addr, code),
73 else => unreachable,73 else => unreachable,
74 }74 }
75}75}
7676
77fn resolveAarch64(77fn resolveAarch64(
78 self: Relocation,78 self: Relocation,
79 macho_file: *MachO,
80 source_addr: u64,79 source_addr: u64,
81 target_addr: i64,80 target_addr: i64,
82 base_offset: u64,81 code: []u8,
83) !void {82) !void {
84 const rel_type = @intToEnum(macho.reloc_type_arm64, self.type);83 const rel_type = @intToEnum(macho.reloc_type_arm64, self.type);
85 if (rel_type == .ARM64_RELOC_UNSIGNED) {84 if (rel_type == .ARM64_RELOC_UNSIGNED) {
86 var buffer: [@sizeOf(u64)]u8 = undefined;85 return switch (self.length) {
87 const code = blk: {86 2 => mem.writeIntLittle(u32, code[self.offset..][0..4], @truncate(u32, @bitCast(u64, target_addr))),
88 switch (self.length) {87 3 => mem.writeIntLittle(u64, code[self.offset..][0..8], @bitCast(u64, target_addr)),
89 2 => {88 else => unreachable,
90 mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr)));
91 break :blk buffer[0..4];
92 },
93 3 => {
94 mem.writeIntLittle(u64, &buffer, @bitCast(u64, target_addr));
95 break :blk &buffer;
96 },
97 else => unreachable,
98 }
99 };89 };
100 return macho_file.base.file.?.pwriteAll(code, base_offset + self.offset);
101 }90 }
10291
103 var buffer: [@sizeOf(u32)]u8 = undefined;92 var buffer = code[self.offset..][0..4];
104 const amt = try macho_file.base.file.?.preadAll(&buffer, base_offset + self.offset);
105 if (amt != buffer.len) return error.InputOutput;
106
107 switch (rel_type) {93 switch (rel_type) {
108 .ARM64_RELOC_BRANCH26 => {94 .ARM64_RELOC_BRANCH26 => {
109 const displacement = math.cast(95 const displacement = math.cast(
...@@ -114,10 +100,10 @@ fn resolveAarch64(...@@ -114,10 +100,10 @@ fn resolveAarch64(
114 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(100 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
115 aarch64.Instruction,101 aarch64.Instruction,
116 aarch64.Instruction.unconditional_branch_immediate,102 aarch64.Instruction.unconditional_branch_immediate,
117 ), &buffer),103 ), buffer),
118 };104 };
119 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));105 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
120 mem.writeIntLittle(u32, &buffer, inst.toU32());106 mem.writeIntLittle(u32, buffer, inst.toU32());
121 },107 },
122 .ARM64_RELOC_PAGE21,108 .ARM64_RELOC_PAGE21,
123 .ARM64_RELOC_GOT_LOAD_PAGE21,109 .ARM64_RELOC_GOT_LOAD_PAGE21,
...@@ -130,31 +116,31 @@ fn resolveAarch64(...@@ -130,31 +116,31 @@ fn resolveAarch64(
130 .pc_relative_address = mem.bytesToValue(meta.TagPayload(116 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
131 aarch64.Instruction,117 aarch64.Instruction,
132 aarch64.Instruction.pc_relative_address,118 aarch64.Instruction.pc_relative_address,
133 ), &buffer),119 ), buffer),
134 };120 };
135 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);121 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
136 inst.pc_relative_address.immlo = @truncate(u2, pages);122 inst.pc_relative_address.immlo = @truncate(u2, pages);
137 mem.writeIntLittle(u32, &buffer, inst.toU32());123 mem.writeIntLittle(u32, buffer, inst.toU32());
138 },124 },
139 .ARM64_RELOC_PAGEOFF12,125 .ARM64_RELOC_PAGEOFF12,
140 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,126 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
141 => {127 => {
142 const narrowed = @truncate(u12, @intCast(u64, target_addr));128 const narrowed = @truncate(u12, @intCast(u64, target_addr));
143 if (isArithmeticOp(&buffer)) {129 if (isArithmeticOp(buffer)) {
144 var inst = aarch64.Instruction{130 var inst = aarch64.Instruction{
145 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(131 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
146 aarch64.Instruction,132 aarch64.Instruction,
147 aarch64.Instruction.add_subtract_immediate,133 aarch64.Instruction.add_subtract_immediate,
148 ), &buffer),134 ), buffer),
149 };135 };
150 inst.add_subtract_immediate.imm12 = narrowed;136 inst.add_subtract_immediate.imm12 = narrowed;
151 mem.writeIntLittle(u32, &buffer, inst.toU32());137 mem.writeIntLittle(u32, buffer, inst.toU32());
152 } else {138 } else {
153 var inst = aarch64.Instruction{139 var inst = aarch64.Instruction{
154 .load_store_register = mem.bytesToValue(meta.TagPayload(140 .load_store_register = mem.bytesToValue(meta.TagPayload(
155 aarch64.Instruction,141 aarch64.Instruction,
156 aarch64.Instruction.load_store_register,142 aarch64.Instruction.load_store_register,
157 ), &buffer),143 ), buffer),
158 };144 };
159 const offset: u12 = blk: {145 const offset: u12 = blk: {
160 if (inst.load_store_register.size == 0) {146 if (inst.load_store_register.size == 0) {
...@@ -170,7 +156,7 @@ fn resolveAarch64(...@@ -170,7 +156,7 @@ fn resolveAarch64(
170 }156 }
171 };157 };
172 inst.load_store_register.offset = offset;158 inst.load_store_register.offset = offset;
173 mem.writeIntLittle(u32, &buffer, inst.toU32());159 mem.writeIntLittle(u32, buffer, inst.toU32());
174 }160 }
175 },161 },
176 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {162 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
...@@ -180,11 +166,11 @@ fn resolveAarch64(...@@ -180,11 +166,11 @@ fn resolveAarch64(
180 size: u2,166 size: u2,
181 };167 };
182 const reg_info: RegInfo = blk: {168 const reg_info: RegInfo = blk: {
183 if (isArithmeticOp(&buffer)) {169 if (isArithmeticOp(buffer)) {
184 const inst = mem.bytesToValue(meta.TagPayload(170 const inst = mem.bytesToValue(meta.TagPayload(
185 aarch64.Instruction,171 aarch64.Instruction,
186 aarch64.Instruction.add_subtract_immediate,172 aarch64.Instruction.add_subtract_immediate,
187 ), &buffer);173 ), buffer);
188 break :blk .{174 break :blk .{
189 .rd = inst.rd,175 .rd = inst.rd,
190 .rn = inst.rn,176 .rn = inst.rn,
...@@ -194,7 +180,7 @@ fn resolveAarch64(...@@ -194,7 +180,7 @@ fn resolveAarch64(
194 const inst = mem.bytesToValue(meta.TagPayload(180 const inst = mem.bytesToValue(meta.TagPayload(
195 aarch64.Instruction,181 aarch64.Instruction,
196 aarch64.Instruction.load_store_register,182 aarch64.Instruction.load_store_register,
197 ), &buffer);183 ), buffer);
198 break :blk .{184 break :blk .{
199 .rd = inst.rt,185 .rd = inst.rt,
200 .rn = inst.rn,186 .rn = inst.rn,
...@@ -214,72 +200,62 @@ fn resolveAarch64(...@@ -214,72 +200,62 @@ fn resolveAarch64(
214 .sf = @truncate(u1, reg_info.size),200 .sf = @truncate(u1, reg_info.size),
215 },201 },
216 };202 };
217 mem.writeIntLittle(u32, &buffer, inst.toU32());203 mem.writeIntLittle(u32, buffer, inst.toU32());
218 },204 },
219 .ARM64_RELOC_POINTER_TO_GOT => {205 .ARM64_RELOC_POINTER_TO_GOT => {
220 const result = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr));206 const result = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr));
221 mem.writeIntLittle(i32, &buffer, result);207 mem.writeIntLittle(i32, buffer, result);
222 },208 },
223 .ARM64_RELOC_SUBTRACTOR => unreachable,209 .ARM64_RELOC_SUBTRACTOR => unreachable,
224 .ARM64_RELOC_ADDEND => unreachable,210 .ARM64_RELOC_ADDEND => unreachable,
225 .ARM64_RELOC_UNSIGNED => unreachable,211 .ARM64_RELOC_UNSIGNED => unreachable,
226 }212 }
227 try macho_file.base.file.?.pwriteAll(&buffer, base_offset + self.offset);
228}213}
229214
230fn resolveX8664(215fn resolveX8664(
231 self: Relocation,216 self: Relocation,
232 macho_file: *MachO,
233 source_addr: u64,217 source_addr: u64,
234 target_addr: i64,218 target_addr: i64,
235 base_offset: u64,219 code: []u8,
236) !void {220) !void {
237 const rel_type = @intToEnum(macho.reloc_type_x86_64, self.type);221 const rel_type = @intToEnum(macho.reloc_type_x86_64, self.type);
238 var buffer: [@sizeOf(u64)]u8 = undefined;222 switch (rel_type) {
239 const code = blk: {223 .X86_64_RELOC_BRANCH,
240 switch (rel_type) {224 .X86_64_RELOC_GOT,
241 .X86_64_RELOC_BRANCH,225 .X86_64_RELOC_GOT_LOAD,
242 .X86_64_RELOC_GOT,226 .X86_64_RELOC_TLV,
243 .X86_64_RELOC_GOT_LOAD,227 => {
244 .X86_64_RELOC_TLV,228 const displacement = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4);
245 => {229 mem.writeIntLittle(u32, code[self.offset..][0..4], @bitCast(u32, displacement));
246 const displacement = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4);230 },
247 mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement));231 .X86_64_RELOC_SIGNED,
248 break :blk buffer[0..4];232 .X86_64_RELOC_SIGNED_1,
249 },233 .X86_64_RELOC_SIGNED_2,
250 .X86_64_RELOC_SIGNED,234 .X86_64_RELOC_SIGNED_4,
251 .X86_64_RELOC_SIGNED_1,235 => {
252 .X86_64_RELOC_SIGNED_2,236 const correction: u3 = switch (rel_type) {
253 .X86_64_RELOC_SIGNED_4,237 .X86_64_RELOC_SIGNED => 0,
254 => {238 .X86_64_RELOC_SIGNED_1 => 1,
255 const correction: u3 = switch (rel_type) {239 .X86_64_RELOC_SIGNED_2 => 2,
256 .X86_64_RELOC_SIGNED => 0,240 .X86_64_RELOC_SIGNED_4 => 4,
257 .X86_64_RELOC_SIGNED_1 => 1,241 else => unreachable,
258 .X86_64_RELOC_SIGNED_2 => 2,242 };
259 .X86_64_RELOC_SIGNED_4 => 4,243 const displacement = @intCast(i32, target_addr - @intCast(i64, source_addr + correction + 4));
260 else => unreachable,244 mem.writeIntLittle(u32, code[self.offset..][0..4], @bitCast(u32, displacement));
261 };245 },
262 const displacement = @intCast(i32, target_addr - @intCast(i64, source_addr + correction + 4));246 .X86_64_RELOC_UNSIGNED => {
263 mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement));247 switch (self.length) {
264 break :blk buffer[0..4];248 2 => {
265 },249 mem.writeIntLittle(u32, code[self.offset..][0..4], @truncate(u32, @bitCast(u64, target_addr)));
266 .X86_64_RELOC_UNSIGNED => {250 },
267 switch (self.length) {251 3 => {
268 2 => {252 mem.writeIntLittle(u64, code[self.offset..][0..8], @bitCast(u64, target_addr));
269 mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr)));253 },
270 break :blk buffer[0..4];254 else => unreachable,
271 },255 }
272 3 => {256 },
273 mem.writeIntLittle(u64, buffer[0..8], @bitCast(u64, target_addr));257 .X86_64_RELOC_SUBTRACTOR => unreachable,
274 break :blk &buffer;258 }
275 },
276 else => unreachable,
277 }
278 },
279 .X86_64_RELOC_SUBTRACTOR => unreachable,
280 }
281 };
282 try macho_file.base.file.?.pwriteAll(code, base_offset + self.offset);
283}259}
284260
285inline fn isArithmeticOp(inst: *const [4]u8) bool {261inline fn isArithmeticOp(inst: *const [4]u8) bool {
src/main.zig+49-24
...@@ -3320,21 +3320,20 @@ fn buildOutputType(...@@ -3320,21 +3320,20 @@ fn buildOutputType(
33203320
3321 try server.listen(.{ .in = ip4_addr });3321 try server.listen(.{ .in = ip4_addr });
33223322
3323 while (true) {3323 const conn = try server.accept();
3324 const conn = try server.accept();3324 defer conn.stream.close();
3325 defer conn.stream.close();3325
33263326 try serve(
3327 try serve(3327 comp,
3328 comp,3328 .{ .handle = conn.stream.handle },
3329 .{ .handle = conn.stream.handle },3329 .{ .handle = conn.stream.handle },
3330 .{ .handle = conn.stream.handle },3330 test_exec_args.items,
3331 test_exec_args.items,3331 self_exe_path,
3332 self_exe_path,3332 arg_mode,
3333 arg_mode,3333 all_args,
3334 all_args,3334 runtime_args_start,
3335 runtime_args_start,3335 );
3336 );3336 return cleanExit();
3337 }
3338 },3337 },
3339 }3338 }
33403339
...@@ -3465,9 +3464,7 @@ fn serve(...@@ -3465,9 +3464,7 @@ fn serve(
3465 const hdr = try server.receiveMessage();3464 const hdr = try server.receiveMessage();
34663465
3467 switch (hdr.tag) {3466 switch (hdr.tag) {
3468 .exit => {3467 .exit => return,
3469 return cleanExit();
3470 },
3471 .update => {3468 .update => {
3472 assert(main_progress_node.recently_updated_child == null);3469 assert(main_progress_node.recently_updated_child == null);
3473 tracy.frameMark();3470 tracy.frameMark();
...@@ -3851,15 +3848,43 @@ fn runOrTestHotSwap(...@@ -3851,15 +3848,43 @@ fn runOrTestHotSwap(
3851 if (runtime_args_start) |i| {3848 if (runtime_args_start) |i| {
3852 try argv.appendSlice(all_args[i..]);3849 try argv.appendSlice(all_args[i..]);
3853 }3850 }
3854 var child = std.ChildProcess.init(argv.items, gpa);
38553851
3856 child.stdin_behavior = .Inherit;3852 switch (builtin.target.os.tag) {
3857 child.stdout_behavior = .Inherit;3853 .macos, .ios, .tvos, .watchos => {
3858 child.stderr_behavior = .Inherit;3854 const PosixSpawn = std.os.darwin.PosixSpawn;
3855
3856 var attr = try PosixSpawn.Attr.init();
3857 defer attr.deinit();
3858
3859 // ASLR is probably a good default for better debugging experience/programming
3860 // with hot-code updates in mind. However, we can also make it work with ASLR on.
3861 const flags: u16 = std.os.darwin.POSIX_SPAWN.SETSIGDEF |
3862 std.os.darwin.POSIX_SPAWN.SETSIGMASK |
3863 std.os.darwin.POSIX_SPAWN.DISABLE_ASLR;
3864 try attr.set(flags);
3865
3866 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
3867 defer arena_allocator.deinit();
3868 const arena = arena_allocator.allocator();
3869
3870 const argv_buf = try arena.allocSentinel(?[*:0]u8, argv.items.len, null);
3871 for (argv.items, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
38593872
3860 try child.spawn();3873 const pid = try PosixSpawn.spawn(argv.items[0], null, attr, argv_buf, std.c.environ);
3874 return pid;
3875 },
3876 else => {
3877 var child = std.ChildProcess.init(argv.items, gpa);
3878
3879 child.stdin_behavior = .Inherit;
3880 child.stdout_behavior = .Inherit;
3881 child.stderr_behavior = .Inherit;
38613882
3862 return child.id;3883 try child.spawn();
3884
3885 return child.id;
3886 },
3887 }
3863}3888}
38643889
3865const AfterUpdateHook = union(enum) {3890const AfterUpdateHook = union(enum) {