| author | |
| committer | |
| log | 9a203fa7894cde183dc04bdfea63d9aea18c52da |
| tree | 86af1cc4f894294d9cf1d4cd92bbc0391a77ecd4 |
| parent | 30aeb41a19d3ac15c89190cd546cbe7c05c60ac8 |
| parent | 6874b2930891363e1fc393d99f59f361eb69eada |
| signature |
Upstream macOS hot-code swapping PoC9 files changed, 574 insertions(+), 231 deletions(-)
build.zig+2| ... | ... | @@ -152,6 +152,7 @@ pub fn build(b: *std.Build) !void { |
| 152 | 152 | if (only_install_lib_files) |
| 153 | 153 | return; |
| 154 | 154 | |
| 155 | const entitlements = b.option([]const u8, "entitlements", "Path to entitlements file for hot-code swapping without sudo on macOS"); | |
| 155 | 156 | const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); |
| 156 | 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 | 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 | 174 | exe.pie = pie; |
| 174 | 175 | exe.sanitize_thread = sanitize_thread; |
| 175 | 176 | exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false; |
| 177 | exe.entitlements = entitlements; | |
| 176 | 178 | exe.install(); |
| 177 | 179 | |
| 178 | 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 | 203 | pub extern "c" fn malloc_size(?*const anyopaque) usize; |
| 204 | 204 | pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int; |
| 205 | 205 | |
| 206 | pub const posix_spawnattr_t = *opaque {}; | |
| 207 | pub const posix_spawn_file_actions_t = *opaque {}; | |
| 208 | pub extern "c" fn posix_spawnattr_init(attr: *posix_spawnattr_t) c_int; | |
| 209 | pub extern "c" fn posix_spawnattr_destroy(attr: *posix_spawnattr_t) c_int; | |
| 210 | pub extern "c" fn posix_spawnattr_setflags(attr: *posix_spawnattr_t, flags: c_short) c_int; | |
| 211 | pub extern "c" fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t, flags: *c_short) c_int; | |
| 212 | pub extern "c" fn posix_spawn_file_actions_init(actions: *posix_spawn_file_actions_t) c_int; | |
| 213 | pub extern "c" fn posix_spawn_file_actions_destroy(actions: *posix_spawn_file_actions_t) c_int; | |
| 214 | pub extern "c" fn posix_spawn_file_actions_addclose(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int; | |
| 215 | pub 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; | |
| 222 | pub 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; | |
| 227 | pub extern "c" fn posix_spawn_file_actions_addinherit_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int; | |
| 228 | pub extern "c" fn posix_spawn_file_actions_addchdir_np(actions: *posix_spawn_file_actions_t, path: [*:0]const u8) c_int; | |
| 229 | pub extern "c" fn posix_spawn_file_actions_addfchdir_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int; | |
| 230 | pub 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; | |
| 238 | pub 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 | ||
| 247 | 206 | pub extern "c" fn kevent64( |
| 248 | 207 | kq: c_int, |
| 249 | 208 | changelist: [*]const kevent64_s, |
| ... | ... | @@ -2176,18 +2135,6 @@ pub const E = enum(u16) { |
| 2176 | 2135 | _, |
| 2177 | 2136 | }; |
| 2178 | 2137 | |
| 2179 | pub fn getKernError(err: kern_return_t) KernE { | |
| 2180 | return @intToEnum(KernE, @truncate(u32, @intCast(usize, err))); | |
| 2181 | } | |
| 2182 | ||
| 2183 | pub 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 | 2138 | /// Kernel return values |
| 2192 | 2139 | pub const KernE = enum(u32) { |
| 2193 | 2140 | SUCCESS = 0, |
| ... | ... | @@ -3063,38 +3010,320 @@ pub const CPUFAMILY = enum(u32) { |
| 3063 | 3010 | _, |
| 3064 | 3011 | }; |
| 3065 | 3012 | |
| 3066 | pub const POSIX_SPAWN_RESETIDS = 0x0001; | |
| 3067 | pub const POSIX_SPAWN_SETPGROUP = 0x0002; | |
| 3068 | pub const POSIX_SPAWN_SETSIGDEF = 0x0004; | |
| 3069 | pub const POSIX_SPAWN_SETSIGMASK = 0x0008; | |
| 3070 | pub const POSIX_SPAWN_SETEXEC = 0x0040; | |
| 3071 | pub const POSIX_SPAWN_START_SUSPENDED = 0x0080; | |
| 3072 | pub const _POSIX_SPAWN_DISABLE_ASLR = 0x0100; | |
| 3073 | pub const POSIX_SPAWN_SETSID = 0x0400; | |
| 3074 | pub const _POSIX_SPAWN_RESLIDE = 0x0800; | |
| 3075 | pub const POSIX_SPAWN_CLOEXEC_DEFAULT = 0x4000; | |
| 3076 | ||
| 3077 | pub const PT_TRACE_ME = 0; | |
| 3078 | pub const PT_READ_I = 1; | |
| 3079 | pub const PT_READ_D = 2; | |
| 3080 | pub const PT_READ_U = 3; | |
| 3081 | pub const PT_WRITE_I = 4; | |
| 3082 | pub const PT_WRITE_D = 5; | |
| 3083 | pub const PT_WRITE_U = 6; | |
| 3084 | pub const PT_CONTINUE = 7; | |
| 3085 | pub const PT_KILL = 8; | |
| 3086 | pub const PT_STEP = 9; | |
| 3087 | pub const PT_DETACH = 11; | |
| 3088 | pub const PT_SIGEXC = 12; | |
| 3089 | pub const PT_THUPDATE = 13; | |
| 3090 | pub const PT_ATTACHEXC = 14; | |
| 3091 | pub const PT_FORCEQUOTA = 30; | |
| 3092 | pub const PT_DENY_ATTACH = 31; | |
| 3013 | pub const PT = struct { | |
| 3014 | pub const TRACE_ME = 0; | |
| 3015 | pub const READ_I = 1; | |
| 3016 | pub const READ_D = 2; | |
| 3017 | pub const READ_U = 3; | |
| 3018 | pub const WRITE_I = 4; | |
| 3019 | pub const WRITE_D = 5; | |
| 3020 | pub const WRITE_U = 6; | |
| 3021 | pub const CONTINUE = 7; | |
| 3022 | pub const KILL = 8; | |
| 3023 | pub const STEP = 9; | |
| 3024 | pub const DETACH = 11; | |
| 3025 | pub const SIGEXC = 12; | |
| 3026 | pub const THUPDATE = 13; | |
| 3027 | pub const ATTACHEXC = 14; | |
| 3028 | pub const FORCEQUOTA = 30; | |
| 3029 | pub const DENY_ATTACH = 31; | |
| 3030 | }; | |
| 3093 | 3031 | |
| 3094 | 3032 | pub const caddr_t = ?[*]u8; |
| 3095 | 3033 | |
| 3096 | 3034 | pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int; |
| 3097 | 3035 | |
| 3036 | pub 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 | ||
| 3049 | pub const posix_spawnattr_t = *opaque {}; | |
| 3050 | pub const posix_spawn_file_actions_t = *opaque {}; | |
| 3051 | pub extern "c" fn posix_spawnattr_init(attr: *posix_spawnattr_t) c_int; | |
| 3052 | pub extern "c" fn posix_spawnattr_destroy(attr: *posix_spawnattr_t) c_int; | |
| 3053 | pub extern "c" fn posix_spawnattr_setflags(attr: *posix_spawnattr_t, flags: c_short) c_int; | |
| 3054 | pub extern "c" fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t, flags: *c_short) c_int; | |
| 3055 | pub extern "c" fn posix_spawn_file_actions_init(actions: *posix_spawn_file_actions_t) c_int; | |
| 3056 | pub extern "c" fn posix_spawn_file_actions_destroy(actions: *posix_spawn_file_actions_t) c_int; | |
| 3057 | pub extern "c" fn posix_spawn_file_actions_addclose(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int; | |
| 3058 | pub 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; | |
| 3065 | pub 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; | |
| 3070 | pub extern "c" fn posix_spawn_file_actions_addinherit_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int; | |
| 3071 | pub extern "c" fn posix_spawn_file_actions_addchdir_np(actions: *posix_spawn_file_actions_t, path: [*:0]const u8) c_int; | |
| 3072 | pub extern "c" fn posix_spawn_file_actions_addfchdir_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int; | |
| 3073 | pub 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; | |
| 3081 | pub 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 | ||
| 3090 | pub 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 | ||
| 3315 | pub fn getKernError(err: kern_return_t) KernE { | |
| 3316 | return @intToEnum(KernE, @truncate(u32, @intCast(usize, err))); | |
| 3317 | } | |
| 3318 | ||
| 3319 | pub 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 | ||
| 3098 | 3327 | pub const MachError = error{ |
| 3099 | 3328 | /// Not enough permissions held to perform the requested kernel |
| 3100 | 3329 | /// call. |
lib/std/macho.zig+4| ... | ... | @@ -656,6 +656,10 @@ pub const segment_command_64 = extern struct { |
| 656 | 656 | pub fn segName(seg: *const segment_command_64) []const u8 { |
| 657 | 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 | }; |
| 660 | 664 | |
| 661 | 665 | pub const PROT = struct { |
lib/std/os.zig+40-13| ... | ... | @@ -7129,22 +7129,49 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec { |
| 7129 | 7129 | |
| 7130 | 7130 | pub const PtraceError = error{ |
| 7131 | 7131 | DeviceBusy, |
| 7132 | InputOutput, | |
| 7133 | Overflow, | |
| 7132 | 7134 | ProcessNotFound, |
| 7133 | 7135 | PermissionDenied, |
| 7134 | 7136 | } || UnexpectedError; |
| 7135 | 7137 | |
| 7136 | /// TODO on other OSes | |
| 7137 | pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void { | |
| 7138 | switch (builtin.os.tag) { | |
| 7139 | .macos, .ios, .tvos, .watchos => {}, | |
| 7140 | else => @compileError("TODO implement ptrace"), | |
| 7141 | } | |
| 7142 | return switch (errno(system.ptrace(request, pid, addr, signal))) { | |
| 7143 | .SUCCESS => {}, | |
| 7144 | .SRCH => error.ProcessNotFound, | |
| 7145 | .INVAL => unreachable, | |
| 7146 | .PERM => error.PermissionDenied, | |
| 7147 | .BUSY => error.DeviceBusy, | |
| 7148 | else => |err| return unexpectedErrno(err), | |
| 7138 | pub fn ptrace(request: u32, pid: pid_t, addr: usize, signal: usize) PtraceError!void { | |
| 7139 | if (builtin.os.tag == .windows or builtin.os.tag == .wasi) | |
| 7140 | @compileError("Unsupported OS"); | |
| 7141 | ||
| 7142 | return switch (builtin.os.tag) { | |
| 7143 | .linux => switch (errno(linux.ptrace(request, pid, addr, signal, 0))) { | |
| 7144 | .SUCCESS => {}, | |
| 7145 | .SRCH => error.ProcessNotFound, | |
| 7146 | .FAULT => unreachable, | |
| 7147 | .INVAL => unreachable, | |
| 7148 | .IO => return error.InputOutput, | |
| 7149 | .PERM => error.PermissionDenied, | |
| 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 | 389 | try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{}); |
| 390 | 390 | try emit.directory.handle.rename(tmp_sub_path, emit.sub_path); |
| 391 | 391 | switch (builtin.os.tag) { |
| 392 | .linux => { | |
| 393 | switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0, 0))) { | |
| 394 | .SUCCESS => {}, | |
| 395 | else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), | |
| 396 | } | |
| 392 | .linux => std.os.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| { | |
| 393 | log.warn("ptrace failure: {s}", .{@errorName(err)}); | |
| 394 | }, | |
| 395 | .macos => base.cast(MachO).?.ptraceAttach(pid) catch |err| { | |
| 396 | log.warn("attaching failed with error: {s}", .{@errorName(err)}); | |
| 397 | 397 | }, |
| 398 | 398 | else => return error.HotSwapUnavailableOnHostOperatingSystem, |
| 399 | 399 | } |
| ... | ... | @@ -430,11 +430,11 @@ pub const File = struct { |
| 430 | 430 | |
| 431 | 431 | if (base.child_pid) |pid| { |
| 432 | 432 | switch (builtin.os.tag) { |
| 433 | .linux => { | |
| 434 | switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0, 0))) { | |
| 435 | .SUCCESS => {}, | |
| 436 | else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}), | |
| 437 | } | |
| 433 | .linux => std.os.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0) catch |err| { | |
| 434 | log.warn("ptrace failure: {s}", .{@errorName(err)}); | |
| 435 | }, | |
| 436 | .macos => base.cast(MachO).?.ptraceDetach(pid) catch |err| { | |
| 437 | log.warn("detaching failed with error: {s}", .{@errorName(err)}); | |
| 438 | 438 | }, |
| 439 | 439 | else => return error.HotSwapUnavailableOnHostOperatingSystem, |
| 440 | 440 | } |
src/link/MachO.zig+95-7| ... | ... | @@ -221,6 +221,14 @@ lazy_bindings: BindingTable = .{}, |
| 221 | 221 | /// Table of tracked Decls. |
| 222 | 222 | decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, |
| 223 | 223 | |
| 224 | /// Hot-code swapping state. | |
| 225 | hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, | |
| 226 | ||
| 227 | const is_hot_update_compatible = switch (builtin.target.os.tag) { | |
| 228 | .macos => true, | |
| 229 | else => false, | |
| 230 | }; | |
| 231 | ||
| 224 | 232 | const DeclMetadata = struct { |
| 225 | 233 | atom: Atom.Index, |
| 226 | 234 | section: u8, |
| ... | ... | @@ -300,6 +308,10 @@ pub const SymbolWithLoc = struct { |
| 300 | 308 | } |
| 301 | 309 | }; |
| 302 | 310 | |
| 311 | const HotUpdateState = struct { | |
| 312 | mach_task: ?std.os.darwin.MachTask = null, | |
| 313 | }; | |
| 314 | ||
| 303 | 315 | /// When allocating, the ideal_capacity is calculated by |
| 304 | 316 | /// actual_capacity + (actual_capacity / ideal_factor) |
| 305 | 317 | const ideal_factor = 3; |
| ... | ... | @@ -584,7 +596,26 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 584 | 596 | try self.allocateSpecialSymbols(); |
| 585 | 597 | |
| 586 | 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 | } |
| 589 | 620 | |
| 590 | 621 | if (build_options.enable_logging) { |
| ... | ... | @@ -1052,14 +1083,40 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: |
| 1052 | 1083 | } |
| 1053 | 1084 | } |
| 1054 | 1085 | |
| 1055 | pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []const u8) !void { | |
| 1086 | pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void { | |
| 1056 | 1087 | const atom = self.getAtom(atom_index); |
| 1057 | 1088 | const sym = atom.getSymbol(self); |
| 1058 | 1089 | const section = self.sections.get(sym.n_sect - 1); |
| 1059 | 1090 | const file_offset = section.header.offset + sym.n_value - section.header.addr; |
| 1060 | 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 | 1109 | try self.base.file.?.pwriteAll(code, file_offset); |
| 1062 | try Atom.resolveRelocations(self, atom_index); | |
| 1110 | } | |
| 1111 | ||
| 1112 | fn 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 | } |
| 1064 | 1121 | |
| 1065 | 1122 | fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void { |
| ... | ... | @@ -1068,6 +1125,7 @@ fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void { |
| 1068 | 1125 | } |
| 1069 | 1126 | |
| 1070 | 1127 | fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void { |
| 1128 | log.debug("marking relocs dirty by target: {}", .{target}); | |
| 1071 | 1129 | // TODO: reverse-lookup might come in handy here |
| 1072 | 1130 | for (self.relocs.values()) |*relocs| { |
| 1073 | 1131 | for (relocs.items) |*reloc| { |
| ... | ... | @@ -1078,6 +1136,7 @@ fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void { |
| 1078 | 1136 | } |
| 1079 | 1137 | |
| 1080 | 1138 | fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { |
| 1139 | log.debug("marking relocs dirty by address: {x}", .{addr}); | |
| 1081 | 1140 | for (self.relocs.values()) |*relocs| { |
| 1082 | 1141 | for (relocs.items) |*reloc| { |
| 1083 | 1142 | const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue; |
| ... | ... | @@ -1702,6 +1761,8 @@ pub fn resolveDyldStubBinder(self: *MachO) !void { |
| 1702 | 1761 | if (self.dyld_stub_binder_index != null) return; |
| 1703 | 1762 | if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports |
| 1704 | 1763 | |
| 1764 | log.debug("resolving dyld_stub_binder", .{}); | |
| 1765 | ||
| 1705 | 1766 | const gpa = self.base.allocator; |
| 1706 | 1767 | const sym_index = try self.allocateSymbol(); |
| 1707 | 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 | 2124 | else |
| 2064 | 2125 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); |
| 2065 | 2126 | |
| 2066 | const code = switch (res) { | |
| 2127 | var code = switch (res) { | |
| 2067 | 2128 | .ok => code_buffer.items, |
| 2068 | 2129 | .fail => |em| { |
| 2069 | 2130 | decl.analysis = .codegen_failure; |
| ... | ... | @@ -2115,7 +2176,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu |
| 2115 | 2176 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{ |
| 2116 | 2177 | .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?, |
| 2117 | 2178 | }); |
| 2118 | const code = switch (res) { | |
| 2179 | var code = switch (res) { | |
| 2119 | 2180 | .ok => code_buffer.items, |
| 2120 | 2181 | .fail => |em| { |
| 2121 | 2182 | decl.analysis = .codegen_failure; |
| ... | ... | @@ -2202,7 +2263,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) |
| 2202 | 2263 | .parent_atom_index = atom.getSymbolIndex().?, |
| 2203 | 2264 | }); |
| 2204 | 2265 | |
| 2205 | const code = switch (res) { | |
| 2266 | var code = switch (res) { | |
| 2206 | 2267 | .ok => code_buffer.items, |
| 2207 | 2268 | .fail => |em| { |
| 2208 | 2269 | decl.analysis = .codegen_failure; |
| ... | ... | @@ -2375,7 +2436,7 @@ pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 { |
| 2375 | 2436 | return sect_id; |
| 2376 | 2437 | } |
| 2377 | 2438 | |
| 2378 | fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) !u64 { | |
| 2439 | fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 { | |
| 2379 | 2440 | const gpa = self.base.allocator; |
| 2380 | 2441 | const mod = self.base.options.module.?; |
| 2381 | 2442 | const decl = mod.declPtr(decl_index); |
| ... | ... | @@ -2788,6 +2849,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2788 | 2849 | |
| 2789 | 2850 | if (self.linkedit_segment_cmd_index == null) { |
| 2790 | 2851 | self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len); |
| 2852 | ||
| 2791 | 2853 | try self.segments.append(gpa, .{ |
| 2792 | 2854 | .segname = makeStaticString("__LINKEDIT"), |
| 2793 | 2855 | .maxprot = macho.PROT.READ, |
| ... | ... | @@ -3760,6 +3822,32 @@ pub fn allocatedVirtualSize(self: *MachO, start: u64) u64 { |
| 3760 | 3822 | return min_pos - start; |
| 3761 | 3823 | } |
| 3762 | 3824 | |
| 3825 | pub 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 | ||
| 3838 | pub 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 | ||
| 3763 | 3851 | pub fn makeStaticString(bytes: []const u8) [16]u8 { |
| 3764 | 3852 | var buf = [_]u8{0} ** 16; |
| 3765 | 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 | 183 | try gop.value_ptr.append(gpa, binding); |
| 184 | 184 | } |
| 185 | 185 | |
| 186 | pub fn resolveRelocations(macho_file: *MachO, atom_index: Index) !void { | |
| 187 | const atom = macho_file.getAtom(atom_index); | |
| 188 | const relocs = macho_file.relocs.get(atom_index) orelse return; | |
| 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| { | |
| 186 | pub fn resolveRelocations(macho_file: *MachO, atom_index: Index, relocs: []Relocation, code: []u8) !void { | |
| 187 | log.debug("relocating '{s}'", .{macho_file.getAtom(atom_index).getName(macho_file)}); | |
| 188 | for (relocs) |*reloc| { | |
| 196 | 189 | if (!reloc.dirty) continue; |
| 197 | ||
| 198 | try reloc.resolve(macho_file, atom_index, file_offset); | |
| 190 | try reloc.resolve(macho_file, atom_index, code); | |
| 199 | 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 | 50 | return macho_file.getAtomIndexForSymbol(self.target); |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, base_offset: u64) !void { | |
| 53 | pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) !void { | |
| 54 | 54 | const arch = macho_file.base.options.target.cpu.arch; |
| 55 | 55 | const atom = macho_file.getAtom(atom_index); |
| 56 | 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 | 68 | }); |
| 69 | 69 | |
| 70 | 70 | switch (arch) { |
| 71 | .aarch64 => return self.resolveAarch64(macho_file, source_addr, target_addr, base_offset), | |
| 72 | .x86_64 => return self.resolveX8664(macho_file, source_addr, target_addr, base_offset), | |
| 71 | .aarch64 => return self.resolveAarch64(source_addr, target_addr, code), | |
| 72 | .x86_64 => return self.resolveX8664(source_addr, target_addr, code), | |
| 73 | 73 | else => unreachable, |
| 74 | 74 | } |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | 77 | fn resolveAarch64( |
| 78 | 78 | self: Relocation, |
| 79 | macho_file: *MachO, | |
| 80 | 79 | source_addr: u64, |
| 81 | 80 | target_addr: i64, |
| 82 | base_offset: u64, | |
| 81 | code: []u8, | |
| 83 | 82 | ) !void { |
| 84 | 83 | const rel_type = @intToEnum(macho.reloc_type_arm64, self.type); |
| 85 | 84 | if (rel_type == .ARM64_RELOC_UNSIGNED) { |
| 86 | var buffer: [@sizeOf(u64)]u8 = undefined; | |
| 87 | const code = blk: { | |
| 88 | switch (self.length) { | |
| 89 | 2 => { | |
| 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 | } | |
| 85 | return switch (self.length) { | |
| 86 | 2 => mem.writeIntLittle(u32, code[self.offset..][0..4], @truncate(u32, @bitCast(u64, target_addr))), | |
| 87 | 3 => mem.writeIntLittle(u64, code[self.offset..][0..8], @bitCast(u64, target_addr)), | |
| 88 | else => unreachable, | |
| 99 | 89 | }; |
| 100 | return macho_file.base.file.?.pwriteAll(code, base_offset + self.offset); | |
| 101 | 90 | } |
| 102 | 91 | |
| 103 | var buffer: [@sizeOf(u32)]u8 = undefined; | |
| 104 | const amt = try macho_file.base.file.?.preadAll(&buffer, base_offset + self.offset); | |
| 105 | if (amt != buffer.len) return error.InputOutput; | |
| 106 | ||
| 92 | var buffer = code[self.offset..][0..4]; | |
| 107 | 93 | switch (rel_type) { |
| 108 | 94 | .ARM64_RELOC_BRANCH26 => { |
| 109 | 95 | const displacement = math.cast( |
| ... | ... | @@ -114,10 +100,10 @@ fn resolveAarch64( |
| 114 | 100 | .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload( |
| 115 | 101 | aarch64.Instruction, |
| 116 | 102 | aarch64.Instruction.unconditional_branch_immediate, |
| 117 | ), &buffer), | |
| 103 | ), buffer), | |
| 118 | 104 | }; |
| 119 | 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 | 108 | .ARM64_RELOC_PAGE21, |
| 123 | 109 | .ARM64_RELOC_GOT_LOAD_PAGE21, |
| ... | ... | @@ -130,31 +116,31 @@ fn resolveAarch64( |
| 130 | 116 | .pc_relative_address = mem.bytesToValue(meta.TagPayload( |
| 131 | 117 | aarch64.Instruction, |
| 132 | 118 | aarch64.Instruction.pc_relative_address, |
| 133 | ), &buffer), | |
| 119 | ), buffer), | |
| 134 | 120 | }; |
| 135 | 121 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); |
| 136 | 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 | 125 | .ARM64_RELOC_PAGEOFF12, |
| 140 | 126 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, |
| 141 | 127 | => { |
| 142 | 128 | const narrowed = @truncate(u12, @intCast(u64, target_addr)); |
| 143 | if (isArithmeticOp(&buffer)) { | |
| 129 | if (isArithmeticOp(buffer)) { | |
| 144 | 130 | var inst = aarch64.Instruction{ |
| 145 | 131 | .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( |
| 146 | 132 | aarch64.Instruction, |
| 147 | 133 | aarch64.Instruction.add_subtract_immediate, |
| 148 | ), &buffer), | |
| 134 | ), buffer), | |
| 149 | 135 | }; |
| 150 | 136 | inst.add_subtract_immediate.imm12 = narrowed; |
| 151 | mem.writeIntLittle(u32, &buffer, inst.toU32()); | |
| 137 | mem.writeIntLittle(u32, buffer, inst.toU32()); | |
| 152 | 138 | } else { |
| 153 | 139 | var inst = aarch64.Instruction{ |
| 154 | 140 | .load_store_register = mem.bytesToValue(meta.TagPayload( |
| 155 | 141 | aarch64.Instruction, |
| 156 | 142 | aarch64.Instruction.load_store_register, |
| 157 | ), &buffer), | |
| 143 | ), buffer), | |
| 158 | 144 | }; |
| 159 | 145 | const offset: u12 = blk: { |
| 160 | 146 | if (inst.load_store_register.size == 0) { |
| ... | ... | @@ -170,7 +156,7 @@ fn resolveAarch64( |
| 170 | 156 | } |
| 171 | 157 | }; |
| 172 | 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 | 162 | .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => { |
| ... | ... | @@ -180,11 +166,11 @@ fn resolveAarch64( |
| 180 | 166 | size: u2, |
| 181 | 167 | }; |
| 182 | 168 | const reg_info: RegInfo = blk: { |
| 183 | if (isArithmeticOp(&buffer)) { | |
| 169 | if (isArithmeticOp(buffer)) { | |
| 184 | 170 | const inst = mem.bytesToValue(meta.TagPayload( |
| 185 | 171 | aarch64.Instruction, |
| 186 | 172 | aarch64.Instruction.add_subtract_immediate, |
| 187 | ), &buffer); | |
| 173 | ), buffer); | |
| 188 | 174 | break :blk .{ |
| 189 | 175 | .rd = inst.rd, |
| 190 | 176 | .rn = inst.rn, |
| ... | ... | @@ -194,7 +180,7 @@ fn resolveAarch64( |
| 194 | 180 | const inst = mem.bytesToValue(meta.TagPayload( |
| 195 | 181 | aarch64.Instruction, |
| 196 | 182 | aarch64.Instruction.load_store_register, |
| 197 | ), &buffer); | |
| 183 | ), buffer); | |
| 198 | 184 | break :blk .{ |
| 199 | 185 | .rd = inst.rt, |
| 200 | 186 | .rn = inst.rn, |
| ... | ... | @@ -214,72 +200,62 @@ fn resolveAarch64( |
| 214 | 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 | 205 | .ARM64_RELOC_POINTER_TO_GOT => { |
| 220 | 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 | 209 | .ARM64_RELOC_SUBTRACTOR => unreachable, |
| 224 | 210 | .ARM64_RELOC_ADDEND => unreachable, |
| 225 | 211 | .ARM64_RELOC_UNSIGNED => unreachable, |
| 226 | 212 | } |
| 227 | try macho_file.base.file.?.pwriteAll(&buffer, base_offset + self.offset); | |
| 228 | 213 | } |
| 229 | 214 | |
| 230 | 215 | fn resolveX8664( |
| 231 | 216 | self: Relocation, |
| 232 | macho_file: *MachO, | |
| 233 | 217 | source_addr: u64, |
| 234 | 218 | target_addr: i64, |
| 235 | base_offset: u64, | |
| 219 | code: []u8, | |
| 236 | 220 | ) !void { |
| 237 | 221 | const rel_type = @intToEnum(macho.reloc_type_x86_64, self.type); |
| 238 | var buffer: [@sizeOf(u64)]u8 = undefined; | |
| 239 | const code = blk: { | |
| 240 | switch (rel_type) { | |
| 241 | .X86_64_RELOC_BRANCH, | |
| 242 | .X86_64_RELOC_GOT, | |
| 243 | .X86_64_RELOC_GOT_LOAD, | |
| 244 | .X86_64_RELOC_TLV, | |
| 245 | => { | |
| 246 | const displacement = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4); | |
| 247 | mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement)); | |
| 248 | break :blk buffer[0..4]; | |
| 249 | }, | |
| 250 | .X86_64_RELOC_SIGNED, | |
| 251 | .X86_64_RELOC_SIGNED_1, | |
| 252 | .X86_64_RELOC_SIGNED_2, | |
| 253 | .X86_64_RELOC_SIGNED_4, | |
| 254 | => { | |
| 255 | const correction: u3 = switch (rel_type) { | |
| 256 | .X86_64_RELOC_SIGNED => 0, | |
| 257 | .X86_64_RELOC_SIGNED_1 => 1, | |
| 258 | .X86_64_RELOC_SIGNED_2 => 2, | |
| 259 | .X86_64_RELOC_SIGNED_4 => 4, | |
| 260 | else => unreachable, | |
| 261 | }; | |
| 262 | const displacement = @intCast(i32, target_addr - @intCast(i64, source_addr + correction + 4)); | |
| 263 | mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement)); | |
| 264 | break :blk buffer[0..4]; | |
| 265 | }, | |
| 266 | .X86_64_RELOC_UNSIGNED => { | |
| 267 | switch (self.length) { | |
| 268 | 2 => { | |
| 269 | mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr))); | |
| 270 | break :blk buffer[0..4]; | |
| 271 | }, | |
| 272 | 3 => { | |
| 273 | mem.writeIntLittle(u64, buffer[0..8], @bitCast(u64, target_addr)); | |
| 274 | break :blk &buffer; | |
| 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); | |
| 222 | switch (rel_type) { | |
| 223 | .X86_64_RELOC_BRANCH, | |
| 224 | .X86_64_RELOC_GOT, | |
| 225 | .X86_64_RELOC_GOT_LOAD, | |
| 226 | .X86_64_RELOC_TLV, | |
| 227 | => { | |
| 228 | const displacement = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4); | |
| 229 | mem.writeIntLittle(u32, code[self.offset..][0..4], @bitCast(u32, displacement)); | |
| 230 | }, | |
| 231 | .X86_64_RELOC_SIGNED, | |
| 232 | .X86_64_RELOC_SIGNED_1, | |
| 233 | .X86_64_RELOC_SIGNED_2, | |
| 234 | .X86_64_RELOC_SIGNED_4, | |
| 235 | => { | |
| 236 | const correction: u3 = switch (rel_type) { | |
| 237 | .X86_64_RELOC_SIGNED => 0, | |
| 238 | .X86_64_RELOC_SIGNED_1 => 1, | |
| 239 | .X86_64_RELOC_SIGNED_2 => 2, | |
| 240 | .X86_64_RELOC_SIGNED_4 => 4, | |
| 241 | else => unreachable, | |
| 242 | }; | |
| 243 | const displacement = @intCast(i32, target_addr - @intCast(i64, source_addr + correction + 4)); | |
| 244 | mem.writeIntLittle(u32, code[self.offset..][0..4], @bitCast(u32, displacement)); | |
| 245 | }, | |
| 246 | .X86_64_RELOC_UNSIGNED => { | |
| 247 | switch (self.length) { | |
| 248 | 2 => { | |
| 249 | mem.writeIntLittle(u32, code[self.offset..][0..4], @truncate(u32, @bitCast(u64, target_addr))); | |
| 250 | }, | |
| 251 | 3 => { | |
| 252 | mem.writeIntLittle(u64, code[self.offset..][0..8], @bitCast(u64, target_addr)); | |
| 253 | }, | |
| 254 | else => unreachable, | |
| 255 | } | |
| 256 | }, | |
| 257 | .X86_64_RELOC_SUBTRACTOR => unreachable, | |
| 258 | } | |
| 283 | 259 | } |
| 284 | 260 | |
| 285 | 261 | inline fn isArithmeticOp(inst: *const [4]u8) bool { |
src/main.zig+49-24| ... | ... | @@ -3320,21 +3320,20 @@ fn buildOutputType( |
| 3320 | 3320 | |
| 3321 | 3321 | try server.listen(.{ .in = ip4_addr }); |
| 3322 | 3322 | |
| 3323 | while (true) { | |
| 3324 | const conn = try server.accept(); | |
| 3325 | defer conn.stream.close(); | |
| 3326 | ||
| 3327 | try serve( | |
| 3328 | comp, | |
| 3329 | .{ .handle = conn.stream.handle }, | |
| 3330 | .{ .handle = conn.stream.handle }, | |
| 3331 | test_exec_args.items, | |
| 3332 | self_exe_path, | |
| 3333 | arg_mode, | |
| 3334 | all_args, | |
| 3335 | runtime_args_start, | |
| 3336 | ); | |
| 3337 | } | |
| 3323 | const conn = try server.accept(); | |
| 3324 | defer conn.stream.close(); | |
| 3325 | ||
| 3326 | try serve( | |
| 3327 | comp, | |
| 3328 | .{ .handle = conn.stream.handle }, | |
| 3329 | .{ .handle = conn.stream.handle }, | |
| 3330 | test_exec_args.items, | |
| 3331 | self_exe_path, | |
| 3332 | arg_mode, | |
| 3333 | all_args, | |
| 3334 | runtime_args_start, | |
| 3335 | ); | |
| 3336 | return cleanExit(); | |
| 3338 | 3337 | }, |
| 3339 | 3338 | } |
| 3340 | 3339 | |
| ... | ... | @@ -3465,9 +3464,7 @@ fn serve( |
| 3465 | 3464 | const hdr = try server.receiveMessage(); |
| 3466 | 3465 | |
| 3467 | 3466 | switch (hdr.tag) { |
| 3468 | .exit => { | |
| 3469 | return cleanExit(); | |
| 3470 | }, | |
| 3467 | .exit => return, | |
| 3471 | 3468 | .update => { |
| 3472 | 3469 | assert(main_progress_node.recently_updated_child == null); |
| 3473 | 3470 | tracy.frameMark(); |
| ... | ... | @@ -3851,15 +3848,43 @@ fn runOrTestHotSwap( |
| 3851 | 3848 | if (runtime_args_start) |i| { |
| 3852 | 3849 | try argv.appendSlice(all_args[i..]); |
| 3853 | 3850 | } |
| 3854 | var child = std.ChildProcess.init(argv.items, gpa); | |
| 3855 | 3851 | |
| 3856 | child.stdin_behavior = .Inherit; | |
| 3857 | child.stdout_behavior = .Inherit; | |
| 3858 | child.stderr_behavior = .Inherit; | |
| 3852 | switch (builtin.target.os.tag) { | |
| 3853 | .macos, .ios, .tvos, .watchos => { | |
| 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; | |
| 3859 | 3872 | |
| 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; | |
| 3861 | 3882 | |
| 3862 | return child.id; | |
| 3883 | try child.spawn(); | |
| 3884 | ||
| 3885 | return child.id; | |
| 3886 | }, | |
| 3887 | } | |
| 3863 | 3888 | } |
| 3864 | 3889 | |
| 3865 | 3890 | const AfterUpdateHook = union(enum) { |