mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
Merge pull request #14986 from ziglang/macos-hcs
Upstream macOS hot-code swapping PoC
This commit is contained in:
commit
9a203fa789
@ -152,6 +152,7 @@ pub fn build(b: *std.Build) !void {
|
||||
if (only_install_lib_files)
|
||||
return;
|
||||
|
||||
const entitlements = b.option([]const u8, "entitlements", "Path to entitlements file for hot-code swapping without sudo on macOS");
|
||||
const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
|
||||
const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
|
||||
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 {
|
||||
exe.pie = pie;
|
||||
exe.sanitize_thread = sanitize_thread;
|
||||
exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
|
||||
exe.entitlements = entitlements;
|
||||
exe.install();
|
||||
|
||||
const compile_step = b.step("compile", "Build the self-hosted compiler");
|
||||
|
||||
@ -203,47 +203,6 @@ pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) kern_retu
|
||||
pub extern "c" fn malloc_size(?*const anyopaque) usize;
|
||||
pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int;
|
||||
|
||||
pub const posix_spawnattr_t = *opaque {};
|
||||
pub const posix_spawn_file_actions_t = *opaque {};
|
||||
pub extern "c" fn posix_spawnattr_init(attr: *posix_spawnattr_t) c_int;
|
||||
pub extern "c" fn posix_spawnattr_destroy(attr: *posix_spawnattr_t) c_int;
|
||||
pub extern "c" fn posix_spawnattr_setflags(attr: *posix_spawnattr_t, flags: c_short) c_int;
|
||||
pub extern "c" fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t, flags: *c_short) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_init(actions: *posix_spawn_file_actions_t) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_destroy(actions: *posix_spawn_file_actions_t) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_addclose(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_addopen(
|
||||
actions: *posix_spawn_file_actions_t,
|
||||
filedes: fd_t,
|
||||
path: [*:0]const u8,
|
||||
oflag: c_int,
|
||||
mode: mode_t,
|
||||
) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_adddup2(
|
||||
actions: *posix_spawn_file_actions_t,
|
||||
filedes: fd_t,
|
||||
newfiledes: fd_t,
|
||||
) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_addinherit_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_addchdir_np(actions: *posix_spawn_file_actions_t, path: [*:0]const u8) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_addfchdir_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
|
||||
pub extern "c" fn posix_spawn(
|
||||
pid: *pid_t,
|
||||
path: [*:0]const u8,
|
||||
actions: ?*const posix_spawn_file_actions_t,
|
||||
attr: ?*const posix_spawnattr_t,
|
||||
argv: [*:null]?[*:0]const u8,
|
||||
env: [*:null]?[*:0]const u8,
|
||||
) c_int;
|
||||
pub extern "c" fn posix_spawnp(
|
||||
pid: *pid_t,
|
||||
path: [*:0]const u8,
|
||||
actions: ?*const posix_spawn_file_actions_t,
|
||||
attr: ?*const posix_spawnattr_t,
|
||||
argv: [*:null]?[*:0]const u8,
|
||||
env: [*:null]?[*:0]const u8,
|
||||
) c_int;
|
||||
|
||||
pub extern "c" fn kevent64(
|
||||
kq: c_int,
|
||||
changelist: [*]const kevent64_s,
|
||||
@ -2176,18 +2135,6 @@ pub const E = enum(u16) {
|
||||
_,
|
||||
};
|
||||
|
||||
pub fn getKernError(err: kern_return_t) KernE {
|
||||
return @intToEnum(KernE, @truncate(u32, @intCast(usize, err)));
|
||||
}
|
||||
|
||||
pub fn unexpectedKernError(err: KernE) std.os.UnexpectedError {
|
||||
if (std.os.unexpected_error_tracing) {
|
||||
std.debug.print("unexpected errno: {d}\n", .{@enumToInt(err)});
|
||||
std.debug.dumpCurrentStackTrace(null);
|
||||
}
|
||||
return error.Unexpected;
|
||||
}
|
||||
|
||||
/// Kernel return values
|
||||
pub const KernE = enum(u32) {
|
||||
SUCCESS = 0,
|
||||
@ -3063,38 +3010,320 @@ pub const CPUFAMILY = enum(u32) {
|
||||
_,
|
||||
};
|
||||
|
||||
pub const POSIX_SPAWN_RESETIDS = 0x0001;
|
||||
pub const POSIX_SPAWN_SETPGROUP = 0x0002;
|
||||
pub const POSIX_SPAWN_SETSIGDEF = 0x0004;
|
||||
pub const POSIX_SPAWN_SETSIGMASK = 0x0008;
|
||||
pub const POSIX_SPAWN_SETEXEC = 0x0040;
|
||||
pub const POSIX_SPAWN_START_SUSPENDED = 0x0080;
|
||||
pub const _POSIX_SPAWN_DISABLE_ASLR = 0x0100;
|
||||
pub const POSIX_SPAWN_SETSID = 0x0400;
|
||||
pub const _POSIX_SPAWN_RESLIDE = 0x0800;
|
||||
pub const POSIX_SPAWN_CLOEXEC_DEFAULT = 0x4000;
|
||||
|
||||
pub const PT_TRACE_ME = 0;
|
||||
pub const PT_READ_I = 1;
|
||||
pub const PT_READ_D = 2;
|
||||
pub const PT_READ_U = 3;
|
||||
pub const PT_WRITE_I = 4;
|
||||
pub const PT_WRITE_D = 5;
|
||||
pub const PT_WRITE_U = 6;
|
||||
pub const PT_CONTINUE = 7;
|
||||
pub const PT_KILL = 8;
|
||||
pub const PT_STEP = 9;
|
||||
pub const PT_DETACH = 11;
|
||||
pub const PT_SIGEXC = 12;
|
||||
pub const PT_THUPDATE = 13;
|
||||
pub const PT_ATTACHEXC = 14;
|
||||
pub const PT_FORCEQUOTA = 30;
|
||||
pub const PT_DENY_ATTACH = 31;
|
||||
pub const PT = struct {
|
||||
pub const TRACE_ME = 0;
|
||||
pub const READ_I = 1;
|
||||
pub const READ_D = 2;
|
||||
pub const READ_U = 3;
|
||||
pub const WRITE_I = 4;
|
||||
pub const WRITE_D = 5;
|
||||
pub const WRITE_U = 6;
|
||||
pub const CONTINUE = 7;
|
||||
pub const KILL = 8;
|
||||
pub const STEP = 9;
|
||||
pub const DETACH = 11;
|
||||
pub const SIGEXC = 12;
|
||||
pub const THUPDATE = 13;
|
||||
pub const ATTACHEXC = 14;
|
||||
pub const FORCEQUOTA = 30;
|
||||
pub const DENY_ATTACH = 31;
|
||||
};
|
||||
|
||||
pub const caddr_t = ?[*]u8;
|
||||
|
||||
pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int;
|
||||
|
||||
pub const POSIX_SPAWN = struct {
|
||||
pub const RESETIDS = 0x0001;
|
||||
pub const SETPGROUP = 0x0002;
|
||||
pub const SETSIGDEF = 0x0004;
|
||||
pub const SETSIGMASK = 0x0008;
|
||||
pub const SETEXEC = 0x0040;
|
||||
pub const START_SUSPENDED = 0x0080;
|
||||
pub const DISABLE_ASLR = 0x0100;
|
||||
pub const SETSID = 0x0400;
|
||||
pub const RESLIDE = 0x0800;
|
||||
pub const CLOEXEC_DEFAULT = 0x4000;
|
||||
};
|
||||
|
||||
pub const posix_spawnattr_t = *opaque {};
|
||||
pub const posix_spawn_file_actions_t = *opaque {};
|
||||
pub extern "c" fn posix_spawnattr_init(attr: *posix_spawnattr_t) c_int;
|
||||
pub extern "c" fn posix_spawnattr_destroy(attr: *posix_spawnattr_t) c_int;
|
||||
pub extern "c" fn posix_spawnattr_setflags(attr: *posix_spawnattr_t, flags: c_short) c_int;
|
||||
pub extern "c" fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t, flags: *c_short) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_init(actions: *posix_spawn_file_actions_t) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_destroy(actions: *posix_spawn_file_actions_t) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_addclose(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_addopen(
|
||||
actions: *posix_spawn_file_actions_t,
|
||||
filedes: fd_t,
|
||||
path: [*:0]const u8,
|
||||
oflag: c_int,
|
||||
mode: mode_t,
|
||||
) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_adddup2(
|
||||
actions: *posix_spawn_file_actions_t,
|
||||
filedes: fd_t,
|
||||
newfiledes: fd_t,
|
||||
) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_addinherit_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_addchdir_np(actions: *posix_spawn_file_actions_t, path: [*:0]const u8) c_int;
|
||||
pub extern "c" fn posix_spawn_file_actions_addfchdir_np(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int;
|
||||
pub extern "c" fn posix_spawn(
|
||||
pid: *pid_t,
|
||||
path: [*:0]const u8,
|
||||
actions: ?*const posix_spawn_file_actions_t,
|
||||
attr: ?*const posix_spawnattr_t,
|
||||
argv: [*:null]?[*:0]const u8,
|
||||
env: [*:null]?[*:0]const u8,
|
||||
) c_int;
|
||||
pub extern "c" fn posix_spawnp(
|
||||
pid: *pid_t,
|
||||
path: [*:0]const u8,
|
||||
actions: ?*const posix_spawn_file_actions_t,
|
||||
attr: ?*const posix_spawnattr_t,
|
||||
argv: [*:null]?[*:0]const u8,
|
||||
env: [*:null]?[*:0]const u8,
|
||||
) c_int;
|
||||
|
||||
pub const PosixSpawn = struct {
|
||||
const errno = std.os.errno;
|
||||
const unexpectedErrno = std.os.unexpectedErrno;
|
||||
|
||||
pub const Error = error{
|
||||
SystemResources,
|
||||
InvalidFileDescriptor,
|
||||
NameTooLong,
|
||||
TooBig,
|
||||
PermissionDenied,
|
||||
InputOutput,
|
||||
FileSystem,
|
||||
FileNotFound,
|
||||
InvalidExe,
|
||||
NotDir,
|
||||
FileBusy,
|
||||
/// Returned when the child fails to execute either in the pre-exec() initialization step, or
|
||||
/// when exec(3) is invoked.
|
||||
ChildExecFailed,
|
||||
} || std.os.UnexpectedError;
|
||||
|
||||
pub const Attr = struct {
|
||||
attr: posix_spawnattr_t,
|
||||
|
||||
pub fn init() Error!Attr {
|
||||
var attr: posix_spawnattr_t = undefined;
|
||||
switch (errno(posix_spawnattr_init(&attr))) {
|
||||
.SUCCESS => return Attr{ .attr = attr },
|
||||
.NOMEM => return error.SystemResources,
|
||||
.INVAL => unreachable,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Attr) void {
|
||||
defer self.* = undefined;
|
||||
switch (errno(posix_spawnattr_destroy(&self.attr))) {
|
||||
.SUCCESS => return,
|
||||
.INVAL => unreachable, // Invalid parameters.
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(self: Attr) Error!u16 {
|
||||
var flags: c_short = undefined;
|
||||
switch (errno(posix_spawnattr_getflags(&self.attr, &flags))) {
|
||||
.SUCCESS => return @bitCast(u16, flags),
|
||||
.INVAL => unreachable,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(self: *Attr, flags: u16) Error!void {
|
||||
switch (errno(posix_spawnattr_setflags(&self.attr, @bitCast(c_short, flags)))) {
|
||||
.SUCCESS => return,
|
||||
.INVAL => unreachable,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Actions = struct {
|
||||
actions: posix_spawn_file_actions_t,
|
||||
|
||||
pub fn init() Error!Actions {
|
||||
var actions: posix_spawn_file_actions_t = undefined;
|
||||
switch (errno(posix_spawn_file_actions_init(&actions))) {
|
||||
.SUCCESS => return Actions{ .actions = actions },
|
||||
.NOMEM => return error.SystemResources,
|
||||
.INVAL => unreachable,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Actions) void {
|
||||
defer self.* = undefined;
|
||||
switch (errno(posix_spawn_file_actions_destroy(&self.actions))) {
|
||||
.SUCCESS => return,
|
||||
.INVAL => unreachable, // Invalid parameters.
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(self: *Actions, fd: fd_t, path: []const u8, flags: u32, mode: mode_t) Error!void {
|
||||
const posix_path = try std.os.toPosixPath(path);
|
||||
return self.openZ(fd, &posix_path, flags, mode);
|
||||
}
|
||||
|
||||
pub fn openZ(self: *Actions, fd: fd_t, path: [*:0]const u8, flags: u32, mode: mode_t) Error!void {
|
||||
switch (errno(posix_spawn_file_actions_addopen(&self.actions, fd, path, @bitCast(c_int, flags), mode))) {
|
||||
.SUCCESS => return,
|
||||
.BADF => return error.InvalidFileDescriptor,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.INVAL => unreachable, // the value of file actions is invalid
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(self: *Actions, fd: fd_t) Error!void {
|
||||
switch (errno(posix_spawn_file_actions_addclose(&self.actions, fd))) {
|
||||
.SUCCESS => return,
|
||||
.BADF => return error.InvalidFileDescriptor,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.INVAL => unreachable, // the value of file actions is invalid
|
||||
.NAMETOOLONG => unreachable,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dup2(self: *Actions, fd: fd_t, newfd: fd_t) Error!void {
|
||||
switch (errno(posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) {
|
||||
.SUCCESS => return,
|
||||
.BADF => return error.InvalidFileDescriptor,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.INVAL => unreachable, // the value of file actions is invalid
|
||||
.NAMETOOLONG => unreachable,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inherit(self: *Actions, fd: fd_t) Error!void {
|
||||
switch (errno(posix_spawn_file_actions_addinherit_np(&self.actions, fd))) {
|
||||
.SUCCESS => return,
|
||||
.BADF => return error.InvalidFileDescriptor,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.INVAL => unreachable, // the value of file actions is invalid
|
||||
.NAMETOOLONG => unreachable,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chdir(self: *Actions, path: []const u8) Error!void {
|
||||
const posix_path = try std.os.toPosixPath(path);
|
||||
return self.chdirZ(&posix_path);
|
||||
}
|
||||
|
||||
pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void {
|
||||
switch (errno(posix_spawn_file_actions_addchdir_np(&self.actions, path))) {
|
||||
.SUCCESS => return,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.BADF => unreachable,
|
||||
.INVAL => unreachable, // the value of file actions is invalid
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fchdir(self: *Actions, fd: fd_t) Error!void {
|
||||
switch (errno(posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) {
|
||||
.SUCCESS => return,
|
||||
.BADF => return error.InvalidFileDescriptor,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.INVAL => unreachable, // the value of file actions is invalid
|
||||
.NAMETOOLONG => unreachable,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn spawn(
|
||||
path: []const u8,
|
||||
actions: ?Actions,
|
||||
attr: ?Attr,
|
||||
argv: [*:null]?[*:0]const u8,
|
||||
envp: [*:null]?[*:0]const u8,
|
||||
) Error!pid_t {
|
||||
const posix_path = try std.os.toPosixPath(path);
|
||||
return spawnZ(&posix_path, actions, attr, argv, envp);
|
||||
}
|
||||
|
||||
pub fn spawnZ(
|
||||
path: [*:0]const u8,
|
||||
actions: ?Actions,
|
||||
attr: ?Attr,
|
||||
argv: [*:null]?[*:0]const u8,
|
||||
envp: [*:null]?[*:0]const u8,
|
||||
) Error!pid_t {
|
||||
var pid: pid_t = undefined;
|
||||
switch (errno(posix_spawn(
|
||||
&pid,
|
||||
path,
|
||||
if (actions) |a| &a.actions else null,
|
||||
if (attr) |a| &a.attr else null,
|
||||
argv,
|
||||
envp,
|
||||
))) {
|
||||
.SUCCESS => return pid,
|
||||
.@"2BIG" => return error.TooBig,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.BADF => return error.InvalidFileDescriptor,
|
||||
.ACCES => return error.PermissionDenied,
|
||||
.IO => return error.InputOutput,
|
||||
.LOOP => return error.FileSystem,
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.NOENT => return error.FileNotFound,
|
||||
.NOEXEC => return error.InvalidExe,
|
||||
.NOTDIR => return error.NotDir,
|
||||
.TXTBSY => return error.FileBusy,
|
||||
.BADARCH => return error.InvalidExe,
|
||||
.BADEXEC => return error.InvalidExe,
|
||||
.FAULT => unreachable,
|
||||
.INVAL => unreachable,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: pid_t, flags: u32) Error!std.os.WaitPidResult {
|
||||
var status: c_int = undefined;
|
||||
while (true) {
|
||||
const rc = waitpid(pid, &status, @intCast(c_int, flags));
|
||||
switch (errno(rc)) {
|
||||
.SUCCESS => return std.os.WaitPidResult{
|
||||
.pid = @intCast(pid_t, rc),
|
||||
.status = @bitCast(u32, status),
|
||||
},
|
||||
.INTR => continue,
|
||||
.CHILD => return error.ChildExecFailed,
|
||||
.INVAL => unreachable, // Invalid flags.
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn getKernError(err: kern_return_t) KernE {
|
||||
return @intToEnum(KernE, @truncate(u32, @intCast(usize, err)));
|
||||
}
|
||||
|
||||
pub fn unexpectedKernError(err: KernE) std.os.UnexpectedError {
|
||||
if (std.os.unexpected_error_tracing) {
|
||||
std.debug.print("unexpected error: {d}\n", .{@enumToInt(err)});
|
||||
std.debug.dumpCurrentStackTrace(null);
|
||||
}
|
||||
return error.Unexpected;
|
||||
}
|
||||
|
||||
pub const MachError = error{
|
||||
/// Not enough permissions held to perform the requested kernel
|
||||
/// call.
|
||||
|
||||
@ -656,6 +656,10 @@ pub const segment_command_64 = extern struct {
|
||||
pub fn segName(seg: *const segment_command_64) []const u8 {
|
||||
return parseName(&seg.segname);
|
||||
}
|
||||
|
||||
pub fn isWriteable(seg: segment_command_64) bool {
|
||||
return seg.initprot & PROT.WRITE != 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const PROT = struct {
|
||||
|
||||
@ -7129,22 +7129,49 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {
|
||||
|
||||
pub const PtraceError = error{
|
||||
DeviceBusy,
|
||||
InputOutput,
|
||||
Overflow,
|
||||
ProcessNotFound,
|
||||
PermissionDenied,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// TODO on other OSes
|
||||
pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void {
|
||||
switch (builtin.os.tag) {
|
||||
.macos, .ios, .tvos, .watchos => {},
|
||||
else => @compileError("TODO implement ptrace"),
|
||||
}
|
||||
return switch (errno(system.ptrace(request, pid, addr, signal))) {
|
||||
.SUCCESS => {},
|
||||
.SRCH => error.ProcessNotFound,
|
||||
.INVAL => unreachable,
|
||||
.PERM => error.PermissionDenied,
|
||||
.BUSY => error.DeviceBusy,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
pub fn ptrace(request: u32, pid: pid_t, addr: usize, signal: usize) PtraceError!void {
|
||||
if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
|
||||
@compileError("Unsupported OS");
|
||||
|
||||
return switch (builtin.os.tag) {
|
||||
.linux => switch (errno(linux.ptrace(request, pid, addr, signal, 0))) {
|
||||
.SUCCESS => {},
|
||||
.SRCH => error.ProcessNotFound,
|
||||
.FAULT => unreachable,
|
||||
.INVAL => unreachable,
|
||||
.IO => return error.InputOutput,
|
||||
.PERM => error.PermissionDenied,
|
||||
.BUSY => error.DeviceBusy,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
},
|
||||
|
||||
.macos, .ios, .tvos, .watchos => switch (errno(darwin.ptrace(
|
||||
math.cast(i32, request) orelse return error.Overflow,
|
||||
pid,
|
||||
@intToPtr(?[*]u8, addr),
|
||||
math.cast(i32, signal) orelse return error.Overflow,
|
||||
))) {
|
||||
.SUCCESS => {},
|
||||
.SRCH => error.ProcessNotFound,
|
||||
.INVAL => unreachable,
|
||||
.PERM => error.PermissionDenied,
|
||||
.BUSY => error.DeviceBusy,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
},
|
||||
|
||||
else => switch (errno(system.ptrace(request, pid, addr, signal))) {
|
||||
.SUCCESS => {},
|
||||
.SRCH => error.ProcessNotFound,
|
||||
.INVAL => unreachable,
|
||||
.PERM => error.PermissionDenied,
|
||||
.BUSY => error.DeviceBusy,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
20
src/link.zig
20
src/link.zig
@ -389,11 +389,11 @@ pub const File = struct {
|
||||
try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{});
|
||||
try emit.directory.handle.rename(tmp_sub_path, emit.sub_path);
|
||||
switch (builtin.os.tag) {
|
||||
.linux => {
|
||||
switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0, 0))) {
|
||||
.SUCCESS => {},
|
||||
else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}),
|
||||
}
|
||||
.linux => std.os.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| {
|
||||
log.warn("ptrace failure: {s}", .{@errorName(err)});
|
||||
},
|
||||
.macos => base.cast(MachO).?.ptraceAttach(pid) catch |err| {
|
||||
log.warn("attaching failed with error: {s}", .{@errorName(err)});
|
||||
},
|
||||
else => return error.HotSwapUnavailableOnHostOperatingSystem,
|
||||
}
|
||||
@ -430,11 +430,11 @@ pub const File = struct {
|
||||
|
||||
if (base.child_pid) |pid| {
|
||||
switch (builtin.os.tag) {
|
||||
.linux => {
|
||||
switch (std.os.errno(std.os.linux.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0, 0))) {
|
||||
.SUCCESS => {},
|
||||
else => |errno| log.warn("ptrace failure: {s}", .{@tagName(errno)}),
|
||||
}
|
||||
.linux => std.os.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0) catch |err| {
|
||||
log.warn("ptrace failure: {s}", .{@errorName(err)});
|
||||
},
|
||||
.macos => base.cast(MachO).?.ptraceDetach(pid) catch |err| {
|
||||
log.warn("detaching failed with error: {s}", .{@errorName(err)});
|
||||
},
|
||||
else => return error.HotSwapUnavailableOnHostOperatingSystem,
|
||||
}
|
||||
|
||||
@ -221,6 +221,14 @@ lazy_bindings: BindingTable = .{},
|
||||
/// Table of tracked Decls.
|
||||
decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
|
||||
|
||||
/// Hot-code swapping state.
|
||||
hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
|
||||
|
||||
const is_hot_update_compatible = switch (builtin.target.os.tag) {
|
||||
.macos => true,
|
||||
else => false,
|
||||
};
|
||||
|
||||
const DeclMetadata = struct {
|
||||
atom: Atom.Index,
|
||||
section: u8,
|
||||
@ -300,6 +308,10 @@ pub const SymbolWithLoc = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const HotUpdateState = struct {
|
||||
mach_task: ?std.os.darwin.MachTask = null,
|
||||
};
|
||||
|
||||
/// When allocating, the ideal_capacity is calculated by
|
||||
/// actual_capacity + (actual_capacity / ideal_factor)
|
||||
const ideal_factor = 3;
|
||||
@ -584,7 +596,26 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
try self.allocateSpecialSymbols();
|
||||
|
||||
for (self.relocs.keys()) |atom_index| {
|
||||
try Atom.resolveRelocations(self, atom_index);
|
||||
const relocs = self.relocs.get(atom_index).?;
|
||||
const needs_update = for (relocs.items) |reloc| {
|
||||
if (reloc.dirty) break true;
|
||||
} else false;
|
||||
|
||||
if (!needs_update) continue;
|
||||
|
||||
const atom = self.getAtom(atom_index);
|
||||
const sym = atom.getSymbol(self);
|
||||
const section = self.sections.get(sym.n_sect - 1).header;
|
||||
const file_offset = section.offset + sym.n_value - section.addr;
|
||||
|
||||
var code = std.ArrayList(u8).init(self.base.allocator);
|
||||
defer code.deinit();
|
||||
try code.resize(math.cast(usize, atom.size) orelse return error.Overflow);
|
||||
|
||||
const amt = try self.base.file.?.preadAll(code.items, file_offset);
|
||||
if (amt != code.items.len) return error.InputOutput;
|
||||
|
||||
try self.writeAtom(atom_index, code.items);
|
||||
}
|
||||
|
||||
if (build_options.enable_logging) {
|
||||
@ -1052,14 +1083,40 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []const u8) !void {
|
||||
pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {
|
||||
const atom = self.getAtom(atom_index);
|
||||
const sym = atom.getSymbol(self);
|
||||
const section = self.sections.get(sym.n_sect - 1);
|
||||
const file_offset = section.header.offset + sym.n_value - section.header.addr;
|
||||
log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
|
||||
|
||||
if (self.relocs.get(atom_index)) |relocs| {
|
||||
try Atom.resolveRelocations(self, atom_index, relocs.items, code);
|
||||
}
|
||||
|
||||
if (is_hot_update_compatible) {
|
||||
if (self.base.child_pid) |pid| blk: {
|
||||
const task = self.hot_state.mach_task orelse {
|
||||
log.warn("cannot hot swap: no Mach task acquired for child process with pid {d}", .{pid});
|
||||
break :blk;
|
||||
};
|
||||
self.updateAtomInMemory(task, section.segment_index, sym.n_value, code) catch |err| {
|
||||
log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
try self.base.file.?.pwriteAll(code, file_offset);
|
||||
try Atom.resolveRelocations(self, atom_index);
|
||||
}
|
||||
|
||||
fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void {
|
||||
const segment = self.segments.items[segment_index];
|
||||
const cpu_arch = self.base.options.target.cpu.arch;
|
||||
const nwritten = if (!segment.isWriteable())
|
||||
try task.writeMemProtected(addr, code, cpu_arch)
|
||||
else
|
||||
try task.writeMem(addr, code, cpu_arch);
|
||||
if (nwritten != code.len) return error.InputOutput;
|
||||
}
|
||||
|
||||
fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {
|
||||
@ -1068,6 +1125,7 @@ fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {
|
||||
}
|
||||
|
||||
fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
|
||||
log.debug("marking relocs dirty by target: {}", .{target});
|
||||
// TODO: reverse-lookup might come in handy here
|
||||
for (self.relocs.values()) |*relocs| {
|
||||
for (relocs.items) |*reloc| {
|
||||
@ -1078,6 +1136,7 @@ fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
|
||||
}
|
||||
|
||||
fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
|
||||
log.debug("marking relocs dirty by address: {x}", .{addr});
|
||||
for (self.relocs.values()) |*relocs| {
|
||||
for (relocs.items) |*reloc| {
|
||||
const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue;
|
||||
@ -1702,6 +1761,8 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
|
||||
if (self.dyld_stub_binder_index != null) return;
|
||||
if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports
|
||||
|
||||
log.debug("resolving dyld_stub_binder", .{});
|
||||
|
||||
const gpa = self.base.allocator;
|
||||
const sym_index = try self.allocateSymbol();
|
||||
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
|
||||
else
|
||||
try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
|
||||
|
||||
const code = switch (res) {
|
||||
var code = switch (res) {
|
||||
.ok => code_buffer.items,
|
||||
.fail => |em| {
|
||||
decl.analysis = .codegen_failure;
|
||||
@ -2115,7 +2176,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
|
||||
const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
|
||||
.parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
|
||||
});
|
||||
const code = switch (res) {
|
||||
var code = switch (res) {
|
||||
.ok => code_buffer.items,
|
||||
.fail => |em| {
|
||||
decl.analysis = .codegen_failure;
|
||||
@ -2202,7 +2263,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
|
||||
.parent_atom_index = atom.getSymbolIndex().?,
|
||||
});
|
||||
|
||||
const code = switch (res) {
|
||||
var code = switch (res) {
|
||||
.ok => code_buffer.items,
|
||||
.fail => |em| {
|
||||
decl.analysis = .codegen_failure;
|
||||
@ -2375,7 +2436,7 @@ pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
|
||||
return sect_id;
|
||||
}
|
||||
|
||||
fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) !u64 {
|
||||
fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 {
|
||||
const gpa = self.base.allocator;
|
||||
const mod = self.base.options.module.?;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
@ -2788,6 +2849,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
|
||||
|
||||
if (self.linkedit_segment_cmd_index == null) {
|
||||
self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
|
||||
|
||||
try self.segments.append(gpa, .{
|
||||
.segname = makeStaticString("__LINKEDIT"),
|
||||
.maxprot = macho.PROT.READ,
|
||||
@ -3760,6 +3822,32 @@ pub fn allocatedVirtualSize(self: *MachO, start: u64) u64 {
|
||||
return min_pos - start;
|
||||
}
|
||||
|
||||
pub fn ptraceAttach(self: *MachO, pid: std.os.pid_t) !void {
|
||||
if (!is_hot_update_compatible) return;
|
||||
|
||||
const mach_task = try std.os.darwin.machTaskForPid(pid);
|
||||
log.debug("Mach task for pid {d}: {any}", .{ pid, mach_task });
|
||||
self.hot_state.mach_task = mach_task;
|
||||
|
||||
// TODO start exception handler in another thread
|
||||
|
||||
// TODO enable ones we register for exceptions
|
||||
// try std.os.ptrace(std.os.darwin.PT.ATTACHEXC, pid, 0, 0);
|
||||
}
|
||||
|
||||
pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void {
|
||||
if (!is_hot_update_compatible) return;
|
||||
|
||||
_ = pid;
|
||||
|
||||
// TODO stop exception handler
|
||||
|
||||
// TODO see comment in ptraceAttach
|
||||
// try std.os.ptrace(std.os.darwin.PT.DETACH, pid, 0, 0);
|
||||
|
||||
self.hot_state.mach_task = null;
|
||||
}
|
||||
|
||||
pub fn makeStaticString(bytes: []const u8) [16]u8 {
|
||||
var buf = [_]u8{0} ** 16;
|
||||
assert(bytes.len <= buf.len);
|
||||
|
||||
@ -183,19 +183,11 @@ pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !
|
||||
try gop.value_ptr.append(gpa, binding);
|
||||
}
|
||||
|
||||
pub fn resolveRelocations(macho_file: *MachO, atom_index: Index) !void {
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
const relocs = macho_file.relocs.get(atom_index) orelse return;
|
||||
const source_sym = atom.getSymbol(macho_file);
|
||||
const source_section = macho_file.sections.get(source_sym.n_sect - 1).header;
|
||||
const file_offset = source_section.offset + source_sym.n_value - source_section.addr;
|
||||
|
||||
log.debug("relocating '{s}'", .{atom.getName(macho_file)});
|
||||
|
||||
for (relocs.items) |*reloc| {
|
||||
pub fn resolveRelocations(macho_file: *MachO, atom_index: Index, relocs: []Relocation, code: []u8) !void {
|
||||
log.debug("relocating '{s}'", .{macho_file.getAtom(atom_index).getName(macho_file)});
|
||||
for (relocs) |*reloc| {
|
||||
if (!reloc.dirty) continue;
|
||||
|
||||
try reloc.resolve(macho_file, atom_index, file_offset);
|
||||
try reloc.resolve(macho_file, atom_index, code);
|
||||
reloc.dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index {
|
||||
return macho_file.getAtomIndexForSymbol(self.target);
|
||||
}
|
||||
|
||||
pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, base_offset: u64) !void {
|
||||
pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) !void {
|
||||
const arch = macho_file.base.options.target.cpu.arch;
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
const source_sym = atom.getSymbol(macho_file);
|
||||
@ -68,42 +68,28 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, bas
|
||||
});
|
||||
|
||||
switch (arch) {
|
||||
.aarch64 => return self.resolveAarch64(macho_file, source_addr, target_addr, base_offset),
|
||||
.x86_64 => return self.resolveX8664(macho_file, source_addr, target_addr, base_offset),
|
||||
.aarch64 => return self.resolveAarch64(source_addr, target_addr, code),
|
||||
.x86_64 => return self.resolveX8664(source_addr, target_addr, code),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolveAarch64(
|
||||
self: Relocation,
|
||||
macho_file: *MachO,
|
||||
source_addr: u64,
|
||||
target_addr: i64,
|
||||
base_offset: u64,
|
||||
code: []u8,
|
||||
) !void {
|
||||
const rel_type = @intToEnum(macho.reloc_type_arm64, self.type);
|
||||
if (rel_type == .ARM64_RELOC_UNSIGNED) {
|
||||
var buffer: [@sizeOf(u64)]u8 = undefined;
|
||||
const code = blk: {
|
||||
switch (self.length) {
|
||||
2 => {
|
||||
mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr)));
|
||||
break :blk buffer[0..4];
|
||||
},
|
||||
3 => {
|
||||
mem.writeIntLittle(u64, &buffer, @bitCast(u64, target_addr));
|
||||
break :blk &buffer;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
return switch (self.length) {
|
||||
2 => mem.writeIntLittle(u32, code[self.offset..][0..4], @truncate(u32, @bitCast(u64, target_addr))),
|
||||
3 => mem.writeIntLittle(u64, code[self.offset..][0..8], @bitCast(u64, target_addr)),
|
||||
else => unreachable,
|
||||
};
|
||||
return macho_file.base.file.?.pwriteAll(code, base_offset + self.offset);
|
||||
}
|
||||
|
||||
var buffer: [@sizeOf(u32)]u8 = undefined;
|
||||
const amt = try macho_file.base.file.?.preadAll(&buffer, base_offset + self.offset);
|
||||
if (amt != buffer.len) return error.InputOutput;
|
||||
|
||||
var buffer = code[self.offset..][0..4];
|
||||
switch (rel_type) {
|
||||
.ARM64_RELOC_BRANCH26 => {
|
||||
const displacement = math.cast(
|
||||
@ -114,10 +100,10 @@ fn resolveAarch64(
|
||||
.unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
|
||||
aarch64.Instruction,
|
||||
aarch64.Instruction.unconditional_branch_immediate,
|
||||
), &buffer),
|
||||
), buffer),
|
||||
};
|
||||
inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
|
||||
mem.writeIntLittle(u32, &buffer, inst.toU32());
|
||||
mem.writeIntLittle(u32, buffer, inst.toU32());
|
||||
},
|
||||
.ARM64_RELOC_PAGE21,
|
||||
.ARM64_RELOC_GOT_LOAD_PAGE21,
|
||||
@ -130,31 +116,31 @@ fn resolveAarch64(
|
||||
.pc_relative_address = mem.bytesToValue(meta.TagPayload(
|
||||
aarch64.Instruction,
|
||||
aarch64.Instruction.pc_relative_address,
|
||||
), &buffer),
|
||||
), buffer),
|
||||
};
|
||||
inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
|
||||
inst.pc_relative_address.immlo = @truncate(u2, pages);
|
||||
mem.writeIntLittle(u32, &buffer, inst.toU32());
|
||||
mem.writeIntLittle(u32, buffer, inst.toU32());
|
||||
},
|
||||
.ARM64_RELOC_PAGEOFF12,
|
||||
.ARM64_RELOC_GOT_LOAD_PAGEOFF12,
|
||||
=> {
|
||||
const narrowed = @truncate(u12, @intCast(u64, target_addr));
|
||||
if (isArithmeticOp(&buffer)) {
|
||||
if (isArithmeticOp(buffer)) {
|
||||
var inst = aarch64.Instruction{
|
||||
.add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
|
||||
aarch64.Instruction,
|
||||
aarch64.Instruction.add_subtract_immediate,
|
||||
), &buffer),
|
||||
), buffer),
|
||||
};
|
||||
inst.add_subtract_immediate.imm12 = narrowed;
|
||||
mem.writeIntLittle(u32, &buffer, inst.toU32());
|
||||
mem.writeIntLittle(u32, buffer, inst.toU32());
|
||||
} else {
|
||||
var inst = aarch64.Instruction{
|
||||
.load_store_register = mem.bytesToValue(meta.TagPayload(
|
||||
aarch64.Instruction,
|
||||
aarch64.Instruction.load_store_register,
|
||||
), &buffer),
|
||||
), buffer),
|
||||
};
|
||||
const offset: u12 = blk: {
|
||||
if (inst.load_store_register.size == 0) {
|
||||
@ -170,7 +156,7 @@ fn resolveAarch64(
|
||||
}
|
||||
};
|
||||
inst.load_store_register.offset = offset;
|
||||
mem.writeIntLittle(u32, &buffer, inst.toU32());
|
||||
mem.writeIntLittle(u32, buffer, inst.toU32());
|
||||
}
|
||||
},
|
||||
.ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
|
||||
@ -180,11 +166,11 @@ fn resolveAarch64(
|
||||
size: u2,
|
||||
};
|
||||
const reg_info: RegInfo = blk: {
|
||||
if (isArithmeticOp(&buffer)) {
|
||||
if (isArithmeticOp(buffer)) {
|
||||
const inst = mem.bytesToValue(meta.TagPayload(
|
||||
aarch64.Instruction,
|
||||
aarch64.Instruction.add_subtract_immediate,
|
||||
), &buffer);
|
||||
), buffer);
|
||||
break :blk .{
|
||||
.rd = inst.rd,
|
||||
.rn = inst.rn,
|
||||
@ -194,7 +180,7 @@ fn resolveAarch64(
|
||||
const inst = mem.bytesToValue(meta.TagPayload(
|
||||
aarch64.Instruction,
|
||||
aarch64.Instruction.load_store_register,
|
||||
), &buffer);
|
||||
), buffer);
|
||||
break :blk .{
|
||||
.rd = inst.rt,
|
||||
.rn = inst.rn,
|
||||
@ -214,72 +200,62 @@ fn resolveAarch64(
|
||||
.sf = @truncate(u1, reg_info.size),
|
||||
},
|
||||
};
|
||||
mem.writeIntLittle(u32, &buffer, inst.toU32());
|
||||
mem.writeIntLittle(u32, buffer, inst.toU32());
|
||||
},
|
||||
.ARM64_RELOC_POINTER_TO_GOT => {
|
||||
const result = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr));
|
||||
mem.writeIntLittle(i32, &buffer, result);
|
||||
mem.writeIntLittle(i32, buffer, result);
|
||||
},
|
||||
.ARM64_RELOC_SUBTRACTOR => unreachable,
|
||||
.ARM64_RELOC_ADDEND => unreachable,
|
||||
.ARM64_RELOC_UNSIGNED => unreachable,
|
||||
}
|
||||
try macho_file.base.file.?.pwriteAll(&buffer, base_offset + self.offset);
|
||||
}
|
||||
|
||||
fn resolveX8664(
|
||||
self: Relocation,
|
||||
macho_file: *MachO,
|
||||
source_addr: u64,
|
||||
target_addr: i64,
|
||||
base_offset: u64,
|
||||
code: []u8,
|
||||
) !void {
|
||||
const rel_type = @intToEnum(macho.reloc_type_x86_64, self.type);
|
||||
var buffer: [@sizeOf(u64)]u8 = undefined;
|
||||
const code = blk: {
|
||||
switch (rel_type) {
|
||||
.X86_64_RELOC_BRANCH,
|
||||
.X86_64_RELOC_GOT,
|
||||
.X86_64_RELOC_GOT_LOAD,
|
||||
.X86_64_RELOC_TLV,
|
||||
=> {
|
||||
const displacement = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4);
|
||||
mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement));
|
||||
break :blk buffer[0..4];
|
||||
},
|
||||
.X86_64_RELOC_SIGNED,
|
||||
.X86_64_RELOC_SIGNED_1,
|
||||
.X86_64_RELOC_SIGNED_2,
|
||||
.X86_64_RELOC_SIGNED_4,
|
||||
=> {
|
||||
const correction: u3 = switch (rel_type) {
|
||||
.X86_64_RELOC_SIGNED => 0,
|
||||
.X86_64_RELOC_SIGNED_1 => 1,
|
||||
.X86_64_RELOC_SIGNED_2 => 2,
|
||||
.X86_64_RELOC_SIGNED_4 => 4,
|
||||
else => unreachable,
|
||||
};
|
||||
const displacement = @intCast(i32, target_addr - @intCast(i64, source_addr + correction + 4));
|
||||
mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement));
|
||||
break :blk buffer[0..4];
|
||||
},
|
||||
.X86_64_RELOC_UNSIGNED => {
|
||||
switch (self.length) {
|
||||
2 => {
|
||||
mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr)));
|
||||
break :blk buffer[0..4];
|
||||
},
|
||||
3 => {
|
||||
mem.writeIntLittle(u64, buffer[0..8], @bitCast(u64, target_addr));
|
||||
break :blk &buffer;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.X86_64_RELOC_SUBTRACTOR => unreachable,
|
||||
}
|
||||
};
|
||||
try macho_file.base.file.?.pwriteAll(code, base_offset + self.offset);
|
||||
switch (rel_type) {
|
||||
.X86_64_RELOC_BRANCH,
|
||||
.X86_64_RELOC_GOT,
|
||||
.X86_64_RELOC_GOT_LOAD,
|
||||
.X86_64_RELOC_TLV,
|
||||
=> {
|
||||
const displacement = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4);
|
||||
mem.writeIntLittle(u32, code[self.offset..][0..4], @bitCast(u32, displacement));
|
||||
},
|
||||
.X86_64_RELOC_SIGNED,
|
||||
.X86_64_RELOC_SIGNED_1,
|
||||
.X86_64_RELOC_SIGNED_2,
|
||||
.X86_64_RELOC_SIGNED_4,
|
||||
=> {
|
||||
const correction: u3 = switch (rel_type) {
|
||||
.X86_64_RELOC_SIGNED => 0,
|
||||
.X86_64_RELOC_SIGNED_1 => 1,
|
||||
.X86_64_RELOC_SIGNED_2 => 2,
|
||||
.X86_64_RELOC_SIGNED_4 => 4,
|
||||
else => unreachable,
|
||||
};
|
||||
const displacement = @intCast(i32, target_addr - @intCast(i64, source_addr + correction + 4));
|
||||
mem.writeIntLittle(u32, code[self.offset..][0..4], @bitCast(u32, displacement));
|
||||
},
|
||||
.X86_64_RELOC_UNSIGNED => {
|
||||
switch (self.length) {
|
||||
2 => {
|
||||
mem.writeIntLittle(u32, code[self.offset..][0..4], @truncate(u32, @bitCast(u64, target_addr)));
|
||||
},
|
||||
3 => {
|
||||
mem.writeIntLittle(u64, code[self.offset..][0..8], @bitCast(u64, target_addr));
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.X86_64_RELOC_SUBTRACTOR => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
inline fn isArithmeticOp(inst: *const [4]u8) bool {
|
||||
|
||||
71
src/main.zig
71
src/main.zig
@ -3320,21 +3320,20 @@ fn buildOutputType(
|
||||
|
||||
try server.listen(.{ .in = ip4_addr });
|
||||
|
||||
while (true) {
|
||||
const conn = try server.accept();
|
||||
defer conn.stream.close();
|
||||
const conn = try server.accept();
|
||||
defer conn.stream.close();
|
||||
|
||||
try serve(
|
||||
comp,
|
||||
.{ .handle = conn.stream.handle },
|
||||
.{ .handle = conn.stream.handle },
|
||||
test_exec_args.items,
|
||||
self_exe_path,
|
||||
arg_mode,
|
||||
all_args,
|
||||
runtime_args_start,
|
||||
);
|
||||
}
|
||||
try serve(
|
||||
comp,
|
||||
.{ .handle = conn.stream.handle },
|
||||
.{ .handle = conn.stream.handle },
|
||||
test_exec_args.items,
|
||||
self_exe_path,
|
||||
arg_mode,
|
||||
all_args,
|
||||
runtime_args_start,
|
||||
);
|
||||
return cleanExit();
|
||||
},
|
||||
}
|
||||
|
||||
@ -3465,9 +3464,7 @@ fn serve(
|
||||
const hdr = try server.receiveMessage();
|
||||
|
||||
switch (hdr.tag) {
|
||||
.exit => {
|
||||
return cleanExit();
|
||||
},
|
||||
.exit => return,
|
||||
.update => {
|
||||
assert(main_progress_node.recently_updated_child == null);
|
||||
tracy.frameMark();
|
||||
@ -3851,15 +3848,43 @@ fn runOrTestHotSwap(
|
||||
if (runtime_args_start) |i| {
|
||||
try argv.appendSlice(all_args[i..]);
|
||||
}
|
||||
var child = std.ChildProcess.init(argv.items, gpa);
|
||||
|
||||
child.stdin_behavior = .Inherit;
|
||||
child.stdout_behavior = .Inherit;
|
||||
child.stderr_behavior = .Inherit;
|
||||
switch (builtin.target.os.tag) {
|
||||
.macos, .ios, .tvos, .watchos => {
|
||||
const PosixSpawn = std.os.darwin.PosixSpawn;
|
||||
|
||||
try child.spawn();
|
||||
var attr = try PosixSpawn.Attr.init();
|
||||
defer attr.deinit();
|
||||
|
||||
return child.id;
|
||||
// ASLR is probably a good default for better debugging experience/programming
|
||||
// with hot-code updates in mind. However, we can also make it work with ASLR on.
|
||||
const flags: u16 = std.os.darwin.POSIX_SPAWN.SETSIGDEF |
|
||||
std.os.darwin.POSIX_SPAWN.SETSIGMASK |
|
||||
std.os.darwin.POSIX_SPAWN.DISABLE_ASLR;
|
||||
try attr.set(flags);
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
const argv_buf = try arena.allocSentinel(?[*:0]u8, argv.items.len, null);
|
||||
for (argv.items, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
|
||||
|
||||
const pid = try PosixSpawn.spawn(argv.items[0], null, attr, argv_buf, std.c.environ);
|
||||
return pid;
|
||||
},
|
||||
else => {
|
||||
var child = std.ChildProcess.init(argv.items, gpa);
|
||||
|
||||
child.stdin_behavior = .Inherit;
|
||||
child.stdout_behavior = .Inherit;
|
||||
child.stderr_behavior = .Inherit;
|
||||
|
||||
try child.spawn();
|
||||
|
||||
return child.id;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const AfterUpdateHook = union(enum) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user