Commit 8c4a2dc1df

Andrew Kelley <andrew@ziglang.org>
2024-07-19 09:30:06
move non-libc stuff out of std.c
1 parent e8c4e79
Changed files (3)
lib
src
lib/std/c/darwin.zig
@@ -355,31 +355,22 @@ pub const MACH_PORT_RIGHT = enum(mach_port_right_t) {
 pub const MACH_MSG_TYPE = enum(mach_msg_type_name_t) {
     /// Must hold receive right
     MOVE_RECEIVE = 16,
-
     /// Must hold send right(s)
     MOVE_SEND = 17,
-
     /// Must hold sendonce right
     MOVE_SEND_ONCE = 18,
-
     /// Must hold send right(s)
     COPY_SEND = 19,
-
     /// Must hold receive right
     MAKE_SEND = 20,
-
     /// Must hold receive right
     MAKE_SEND_ONCE = 21,
-
     /// NOT VALID
     COPY_RECEIVE = 22,
-
     /// Must hold receive right
     DISPOSE_RECEIVE = 24,
-
     /// Must hold send right(s)
     DISPOSE_SEND = 25,
-
     /// Must hold sendonce right
     DISPOSE_SEND_ONCE = 26,
 };
@@ -469,38 +460,45 @@ pub const vm_behavior_t = i32;
 pub const vm32_object_id_t = u32;
 pub const vm_object_id_t = u64;
 
-pub const VM_INHERIT_SHARE: vm_inherit_t = 0;
-pub const VM_INHERIT_COPY: vm_inherit_t = 1;
-pub const VM_INHERIT_NONE: vm_inherit_t = 2;
-pub const VM_INHERIT_DONATE_COPY: vm_inherit_t = 3;
-pub const VM_INHERIT_DEFAULT = VM_INHERIT_COPY;
-
-pub const VM_BEHAVIOR_DEFAULT: vm_behavior_t = 0;
-pub const VM_BEHAVIOR_RANDOM: vm_behavior_t = 1;
-pub const VM_BEHAVIOR_SEQUENTIAL: vm_behavior_t = 2;
-pub const VM_BEHAVIOR_RSEQNTL: vm_behavior_t = 3;
-
-pub const VM_BEHAVIOR_WILLNEED: vm_behavior_t = 4;
-pub const VM_BEHAVIOR_DONTNEED: vm_behavior_t = 5;
-pub const VM_BEHAVIOR_FREE: vm_behavior_t = 6;
-pub const VM_BEHAVIOR_ZERO_WIRED_PAGES: vm_behavior_t = 7;
-pub const VM_BEHAVIOR_REUSABLE: vm_behavior_t = 8;
-pub const VM_BEHAVIOR_REUSE: vm_behavior_t = 9;
-pub const VM_BEHAVIOR_CAN_REUSE: vm_behavior_t = 10;
-pub const VM_BEHAVIOR_PAGEOUT: vm_behavior_t = 11;
-
-pub const VM_REGION_BASIC_INFO_64 = 9;
-pub const VM_REGION_EXTENDED_INFO = 13;
-pub const VM_REGION_TOP_INFO = 12;
-pub const VM_REGION_SUBMAP_INFO_COUNT_64: mach_msg_type_number_t = @sizeOf(vm_region_submap_info_64) / @sizeOf(natural_t);
-pub const VM_REGION_SUBMAP_SHORT_INFO_COUNT_64: mach_msg_type_number_t = @sizeOf(vm_region_submap_short_info_64) / @sizeOf(natural_t);
-pub const VM_REGION_BASIC_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_basic_info_64) / @sizeOf(c_int);
-pub const VM_REGION_EXTENDED_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_extended_info) / @sizeOf(natural_t);
-pub const VM_REGION_TOP_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_top_info) / @sizeOf(natural_t);
-
-pub fn VM_MAKE_TAG(tag: u8) u32 {
-    return @as(u32, tag) << 24;
-}
+pub const VM = struct {
+    pub const INHERIT = struct {
+        pub const SHARE: vm_inherit_t = 0;
+        pub const COPY: vm_inherit_t = 1;
+        pub const NONE: vm_inherit_t = 2;
+        pub const DONATE_COPY: vm_inherit_t = 3;
+        pub const DEFAULT = COPY;
+    };
+
+    pub const BEHAVIOR = struct {
+        pub const DEFAULT: vm_behavior_t = 0;
+        pub const RANDOM: vm_behavior_t = 1;
+        pub const SEQUENTIAL: vm_behavior_t = 2;
+        pub const RSEQNTL: vm_behavior_t = 3;
+        pub const WILLNEED: vm_behavior_t = 4;
+        pub const DONTNEED: vm_behavior_t = 5;
+        pub const FREE: vm_behavior_t = 6;
+        pub const ZERO_WIRED_PAGES: vm_behavior_t = 7;
+        pub const REUSABLE: vm_behavior_t = 8;
+        pub const REUSE: vm_behavior_t = 9;
+        pub const CAN_REUSE: vm_behavior_t = 10;
+        pub const PAGEOUT: vm_behavior_t = 11;
+    };
+
+    pub const REGION = struct {
+        pub const BASIC_INFO_64 = 9;
+        pub const EXTENDED_INFO = 13;
+        pub const TOP_INFO = 12;
+        pub const SUBMAP_INFO_COUNT_64: mach_msg_type_number_t = @sizeOf(vm_region_submap_info_64) / @sizeOf(natural_t);
+        pub const SUBMAP_SHORT_INFO_COUNT_64: mach_msg_type_number_t = @sizeOf(vm_region_submap_short_info_64) / @sizeOf(natural_t);
+        pub const BASIC_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_basic_info_64) / @sizeOf(c_int);
+        pub const EXTENDED_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_extended_info) / @sizeOf(natural_t);
+        pub const TOP_INFO_COUNT: mach_msg_type_number_t = @sizeOf(vm_region_top_info) / @sizeOf(natural_t);
+    };
+
+    pub fn MAKE_TAG(tag: u8) u32 {
+        return @as(u32, tag) << 24;
+    }
+};
 
 pub const vm_region_basic_info_64 = extern struct {
     protection: vm_prot_t,
@@ -678,29 +676,30 @@ pub const thread_identifier_info = extern struct {
     dispatch_qaddr: u64,
 };
 
-/// Cachability
-pub const MATTR_CACHE = 1;
-/// Migrability
-pub const MATTR_MIGRATE = 2;
-/// Replicability
-pub const MATTR_REPLICATE = 4;
-
-/// (Generic) turn attribute off
-pub const MATTR_VAL_OFF = 0;
-/// (Generic) turn attribute on
-pub const MATTR_VAL_ON = 1;
-/// (Generic) return current value
-pub const MATTR_VAL_GET = 2;
-/// Flush from all caches
-pub const MATTR_VAL_CACHE_FLUSH = 6;
-/// Flush from data caches
-pub const MATTR_VAL_DCACHE_FLUSH = 7;
-/// Flush from instruction caches
-pub const MATTR_VAL_ICACHE_FLUSH = 8;
-/// Sync I+D caches
-pub const MATTR_VAL_CACHE_SYNC = 9;
-/// Get page info (stats)
-pub const MATTR_VAL_GET_INFO = 10;
+pub const MATTR = struct {
+    /// Cachability
+    pub const CACHE = 1;
+    /// Migrability
+    pub const MIGRATE = 2;
+    /// Replicability
+    pub const REPLICATE = 4;
+    /// (Generic) turn attribute off
+    pub const VAL_OFF = 0;
+    /// (Generic) turn attribute on
+    pub const VAL_ON = 1;
+    /// (Generic) return current value
+    pub const VAL_GET = 2;
+    /// Flush from all caches
+    pub const VAL_CACHE_FLUSH = 6;
+    /// Flush from data caches
+    pub const VAL_DCACHE_FLUSH = 7;
+    /// Flush from instruction caches
+    pub const VAL_ICACHE_FLUSH = 8;
+    /// Sync I+D caches
+    pub const VAL_CACHE_SYNC = 9;
+    /// Get page info (stats)
+    pub const VAL_GET_INFO = 10;
+};
 
 pub const TASK_VM_INFO = 22;
 pub const TASK_VM_INFO_COUNT: mach_msg_type_number_t = @sizeOf(task_vm_info_data_t) / @sizeOf(natural_t);
@@ -804,19 +803,14 @@ pub extern "c" fn task_info(
 pub const mach_task_basic_info = extern struct {
     /// Virtual memory size (bytes)
     virtual_size: mach_vm_size_t,
-
     /// Resident memory size (bytes)
     resident_size: mach_vm_size_t,
-
     /// Total user run time for terminated threads
     user_time: time_value_t,
-
     /// Total system run time for terminated threads
     system_time: time_value_t,
-
     /// Default policy for new threads
     policy: policy_t,
-
     /// Suspend count for task
     suspend_count: mach_vm_size_t,
 };
@@ -925,486 +919,6 @@ pub extern "c" fn os_unfair_lock_trylock(o: os_unfair_lock_t) bool;
 pub extern "c" fn os_unfair_lock_assert_owner(o: os_unfair_lock_t) void;
 pub extern "c" fn os_unfair_lock_assert_not_owner(o: os_unfair_lock_t) void;
 
-pub fn getKernError(err: kern_return_t) KernE {
-    return @as(KernE, @enumFromInt(@as(u32, @truncate(@as(usize, @intCast(err))))));
-}
-
-pub fn unexpectedKernError(err: KernE) std.posix.UnexpectedError {
-    if (std.posix.unexpected_error_tracing) {
-        std.debug.print("unexpected error: {d}\n", .{@intFromEnum(err)});
-        std.debug.dumpCurrentStackTrace(null);
-    }
-    return error.Unexpected;
-}
-
-pub const MachError = error{
-    /// Not enough permissions held to perform the requested kernel
-    /// call.
-    PermissionDenied,
-} || std.posix.UnexpectedError;
-
-pub const MachTask = extern struct {
-    port: mach_port_name_t,
-
-    pub fn isValid(self: MachTask) bool {
-        return self.port != TASK_NULL;
-    }
-
-    pub fn pidForTask(self: MachTask) MachError!std.c.pid_t {
-        var pid: std.c.pid_t = undefined;
-        switch (getKernError(pid_for_task(self.port, &pid))) {
-            .SUCCESS => return pid,
-            .FAILURE => return error.PermissionDenied,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    pub fn allocatePort(self: MachTask, right: MACH_PORT_RIGHT) MachError!MachTask {
-        var out_port: mach_port_name_t = undefined;
-        switch (getKernError(mach_port_allocate(
-            self.port,
-            @intFromEnum(right),
-            &out_port,
-        ))) {
-            .SUCCESS => return .{ .port = out_port },
-            .FAILURE => return error.PermissionDenied,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    pub fn deallocatePort(self: MachTask, port: MachTask) void {
-        _ = getKernError(mach_port_deallocate(self.port, port.port));
-    }
-
-    pub fn insertRight(self: MachTask, port: MachTask, msg: MACH_MSG_TYPE) !void {
-        switch (getKernError(mach_port_insert_right(
-            self.port,
-            port.port,
-            port.port,
-            @intFromEnum(msg),
-        ))) {
-            .SUCCESS => return,
-            .FAILURE => return error.PermissionDenied,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    pub const PortInfo = struct {
-        mask: exception_mask_t,
-        masks: [EXC.TYPES_COUNT]exception_mask_t,
-        ports: [EXC.TYPES_COUNT]mach_port_t,
-        behaviors: [EXC.TYPES_COUNT]exception_behavior_t,
-        flavors: [EXC.TYPES_COUNT]thread_state_flavor_t,
-        count: mach_msg_type_number_t,
-    };
-
-    pub fn getExceptionPorts(self: MachTask, mask: exception_mask_t) !PortInfo {
-        var info = PortInfo{
-            .mask = mask,
-            .masks = undefined,
-            .ports = undefined,
-            .behaviors = undefined,
-            .flavors = undefined,
-            .count = 0,
-        };
-        info.count = info.ports.len / @sizeOf(mach_port_t);
-
-        switch (getKernError(task_get_exception_ports(
-            self.port,
-            info.mask,
-            &info.masks,
-            &info.count,
-            &info.ports,
-            &info.behaviors,
-            &info.flavors,
-        ))) {
-            .SUCCESS => return info,
-            .FAILURE => return error.PermissionDenied,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    pub fn setExceptionPorts(
-        self: MachTask,
-        mask: exception_mask_t,
-        new_port: MachTask,
-        behavior: exception_behavior_t,
-        new_flavor: thread_state_flavor_t,
-    ) !void {
-        switch (getKernError(task_set_exception_ports(
-            self.port,
-            mask,
-            new_port.port,
-            behavior,
-            new_flavor,
-        ))) {
-            .SUCCESS => return,
-            .FAILURE => return error.PermissionDenied,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    pub const RegionInfo = struct {
-        pub const Tag = enum {
-            basic,
-            extended,
-            top,
-        };
-
-        base_addr: u64,
-        tag: Tag,
-        info: union {
-            basic: vm_region_basic_info_64,
-            extended: vm_region_extended_info,
-            top: vm_region_top_info,
-        },
-    };
-
-    pub fn getRegionInfo(
-        task: MachTask,
-        address: u64,
-        len: usize,
-        tag: RegionInfo.Tag,
-    ) MachError!RegionInfo {
-        var info: RegionInfo = .{
-            .base_addr = address,
-            .tag = tag,
-            .info = undefined,
-        };
-        switch (tag) {
-            .basic => info.info = .{ .basic = undefined },
-            .extended => info.info = .{ .extended = undefined },
-            .top => info.info = .{ .top = undefined },
-        }
-        var base_len: mach_vm_size_t = if (len == 1) 2 else len;
-        var objname: mach_port_t = undefined;
-        var count: mach_msg_type_number_t = switch (tag) {
-            .basic => VM_REGION_BASIC_INFO_COUNT,
-            .extended => VM_REGION_EXTENDED_INFO_COUNT,
-            .top => VM_REGION_TOP_INFO_COUNT,
-        };
-        switch (getKernError(mach_vm_region(
-            task.port,
-            &info.base_addr,
-            &base_len,
-            switch (tag) {
-                .basic => VM_REGION_BASIC_INFO_64,
-                .extended => VM_REGION_EXTENDED_INFO,
-                .top => VM_REGION_TOP_INFO,
-            },
-            switch (tag) {
-                .basic => @as(vm_region_info_t, @ptrCast(&info.info.basic)),
-                .extended => @as(vm_region_info_t, @ptrCast(&info.info.extended)),
-                .top => @as(vm_region_info_t, @ptrCast(&info.info.top)),
-            },
-            &count,
-            &objname,
-        ))) {
-            .SUCCESS => return info,
-            .FAILURE => return error.PermissionDenied,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    pub const RegionSubmapInfo = struct {
-        pub const Tag = enum {
-            short,
-            full,
-        };
-
-        tag: Tag,
-        base_addr: u64,
-        info: union {
-            short: vm_region_submap_short_info_64,
-            full: vm_region_submap_info_64,
-        },
-    };
-
-    pub fn getRegionSubmapInfo(
-        task: MachTask,
-        address: u64,
-        len: usize,
-        nesting_depth: u32,
-        tag: RegionSubmapInfo.Tag,
-    ) MachError!RegionSubmapInfo {
-        var info: RegionSubmapInfo = .{
-            .base_addr = address,
-            .tag = tag,
-            .info = undefined,
-        };
-        switch (tag) {
-            .short => info.info = .{ .short = undefined },
-            .full => info.info = .{ .full = undefined },
-        }
-        var nesting = nesting_depth;
-        var base_len: mach_vm_size_t = if (len == 1) 2 else len;
-        var count: mach_msg_type_number_t = switch (tag) {
-            .short => VM_REGION_SUBMAP_SHORT_INFO_COUNT_64,
-            .full => VM_REGION_SUBMAP_INFO_COUNT_64,
-        };
-        switch (getKernError(mach_vm_region_recurse(
-            task.port,
-            &info.base_addr,
-            &base_len,
-            &nesting,
-            switch (tag) {
-                .short => @as(vm_region_recurse_info_t, @ptrCast(&info.info.short)),
-                .full => @as(vm_region_recurse_info_t, @ptrCast(&info.info.full)),
-            },
-            &count,
-        ))) {
-            .SUCCESS => return info,
-            .FAILURE => return error.PermissionDenied,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    pub fn getCurrProtection(task: MachTask, address: u64, len: usize) MachError!vm_prot_t {
-        const info = try task.getRegionSubmapInfo(address, len, 0, .short);
-        return info.info.short.protection;
-    }
-
-    pub fn setMaxProtection(task: MachTask, address: u64, len: usize, prot: vm_prot_t) MachError!void {
-        return task.setProtectionImpl(address, len, true, prot);
-    }
-
-    pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: vm_prot_t) MachError!void {
-        return task.setProtectionImpl(address, len, false, prot);
-    }
-
-    fn setProtectionImpl(task: MachTask, address: u64, len: usize, set_max: bool, prot: vm_prot_t) MachError!void {
-        switch (getKernError(mach_vm_protect(task.port, address, len, @intFromBool(set_max), prot))) {
-            .SUCCESS => return,
-            .FAILURE => return error.PermissionDenied,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    /// Will write to VM even if current protection attributes specifically prohibit
-    /// us from doing so, by temporarily setting protection level to a level with VM_PROT_COPY
-    /// variant, and resetting after a successful or unsuccessful write.
-    pub fn writeMemProtected(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
-        const curr_prot = try task.getCurrProtection(address, buf.len);
-        try task.setCurrProtection(
-            address,
-            buf.len,
-            PROT.READ | PROT.WRITE | PROT.COPY,
-        );
-        defer {
-            task.setCurrProtection(address, buf.len, curr_prot) catch {};
-        }
-        return task.writeMem(address, buf, arch);
-    }
-
-    pub fn writeMem(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
-        const count = buf.len;
-        var total_written: usize = 0;
-        var curr_addr = address;
-        const page_size = try getPageSize(task); // TODO we probably can assume value here
-        var out_buf = buf[0..];
-
-        while (total_written < count) {
-            const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_written);
-            switch (getKernError(mach_vm_write(
-                task.port,
-                curr_addr,
-                @intFromPtr(out_buf.ptr),
-                @as(mach_msg_type_number_t, @intCast(curr_size)),
-            ))) {
-                .SUCCESS => {},
-                .FAILURE => return error.PermissionDenied,
-                else => |err| return unexpectedKernError(err),
-            }
-
-            switch (arch) {
-                .aarch64 => {
-                    var mattr_value: vm_machine_attribute_val_t = MATTR_VAL_CACHE_FLUSH;
-                    switch (getKernError(vm_machine_attribute(
-                        task.port,
-                        curr_addr,
-                        curr_size,
-                        MATTR_CACHE,
-                        &mattr_value,
-                    ))) {
-                        .SUCCESS => {},
-                        .FAILURE => return error.PermissionDenied,
-                        else => |err| return unexpectedKernError(err),
-                    }
-                },
-                .x86_64 => {},
-                else => unreachable,
-            }
-
-            out_buf = out_buf[curr_size..];
-            total_written += curr_size;
-            curr_addr += curr_size;
-        }
-
-        return total_written;
-    }
-
-    pub fn readMem(task: MachTask, address: u64, buf: []u8) MachError!usize {
-        const count = buf.len;
-        var total_read: usize = 0;
-        var curr_addr = address;
-        const page_size = try getPageSize(task); // TODO we probably can assume value here
-        var out_buf = buf[0..];
-
-        while (total_read < count) {
-            const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_read);
-            var curr_bytes_read: mach_msg_type_number_t = 0;
-            var vm_memory: vm_offset_t = undefined;
-            switch (getKernError(mach_vm_read(task.port, curr_addr, curr_size, &vm_memory, &curr_bytes_read))) {
-                .SUCCESS => {},
-                .FAILURE => return error.PermissionDenied,
-                else => |err| return unexpectedKernError(err),
-            }
-
-            @memcpy(out_buf[0..curr_bytes_read], @as([*]const u8, @ptrFromInt(vm_memory)));
-            _ = vm_deallocate(mach_task_self(), vm_memory, curr_bytes_read);
-
-            out_buf = out_buf[curr_bytes_read..];
-            curr_addr += curr_bytes_read;
-            total_read += curr_bytes_read;
-        }
-
-        return total_read;
-    }
-
-    fn maxBytesLeftInPage(page_size: usize, address: u64, count: usize) usize {
-        var left = count;
-        if (page_size > 0) {
-            const page_offset = address % page_size;
-            const bytes_left_in_page = page_size - page_offset;
-            if (count > bytes_left_in_page) {
-                left = bytes_left_in_page;
-            }
-        }
-        return left;
-    }
-
-    fn getPageSize(task: MachTask) MachError!usize {
-        if (task.isValid()) {
-            var info_count = TASK_VM_INFO_COUNT;
-            var vm_info: task_vm_info_data_t = undefined;
-            switch (getKernError(task_info(
-                task.port,
-                TASK_VM_INFO,
-                @as(task_info_t, @ptrCast(&vm_info)),
-                &info_count,
-            ))) {
-                .SUCCESS => return @as(usize, @intCast(vm_info.page_size)),
-                else => {},
-            }
-        }
-        var page_size: vm_size_t = undefined;
-        switch (getKernError(_host_page_size(mach_host_self(), &page_size))) {
-            .SUCCESS => return page_size,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    pub fn basicTaskInfo(task: MachTask) MachError!mach_task_basic_info {
-        var info: mach_task_basic_info = undefined;
-        var count = MACH_TASK_BASIC_INFO_COUNT;
-        switch (getKernError(task_info(
-            task.port,
-            MACH_TASK_BASIC_INFO,
-            @as(task_info_t, @ptrCast(&info)),
-            &count,
-        ))) {
-            .SUCCESS => return info,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    pub fn @"resume"(task: MachTask) MachError!void {
-        switch (getKernError(task_resume(task.port))) {
-            .SUCCESS => {},
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    pub fn @"suspend"(task: MachTask) MachError!void {
-        switch (getKernError(task_suspend(task.port))) {
-            .SUCCESS => {},
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    const ThreadList = struct {
-        buf: []MachThread,
-
-        pub fn deinit(list: ThreadList) void {
-            const self_task = machTaskForSelf();
-            _ = vm_deallocate(
-                self_task.port,
-                @intFromPtr(list.buf.ptr),
-                @as(vm_size_t, @intCast(list.buf.len * @sizeOf(mach_port_t))),
-            );
-        }
-    };
-
-    pub fn getThreads(task: MachTask) MachError!ThreadList {
-        var thread_list: mach_port_array_t = undefined;
-        var thread_count: mach_msg_type_number_t = undefined;
-        switch (getKernError(task_threads(task.port, &thread_list, &thread_count))) {
-            .SUCCESS => return ThreadList{ .buf = @as([*]MachThread, @ptrCast(thread_list))[0..thread_count] },
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-};
-
-pub const MachThread = extern struct {
-    port: mach_port_t,
-
-    pub fn isValid(thread: MachThread) bool {
-        return thread.port != THREAD_NULL;
-    }
-
-    pub fn getBasicInfo(thread: MachThread) MachError!thread_basic_info {
-        var info: thread_basic_info = undefined;
-        var count = THREAD_BASIC_INFO_COUNT;
-        switch (getKernError(thread_info(
-            thread.port,
-            THREAD_BASIC_INFO,
-            @as(thread_info_t, @ptrCast(&info)),
-            &count,
-        ))) {
-            .SUCCESS => return info,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-
-    pub fn getIdentifierInfo(thread: MachThread) MachError!thread_identifier_info {
-        var info: thread_identifier_info = undefined;
-        var count = THREAD_IDENTIFIER_INFO_COUNT;
-        switch (getKernError(thread_info(
-            thread.port,
-            THREAD_IDENTIFIER_INFO,
-            @as(thread_info_t, @ptrCast(&info)),
-            &count,
-        ))) {
-            .SUCCESS => return info,
-            else => |err| return unexpectedKernError(err),
-        }
-    }
-};
-
-pub fn machTaskForPid(pid: std.c.pid_t) MachError!MachTask {
-    var port: mach_port_name_t = undefined;
-    switch (getKernError(task_for_pid(mach_task_self(), pid, &port))) {
-        .SUCCESS => {},
-        .FAILURE => return error.PermissionDenied,
-        else => |err| return unexpectedKernError(err),
-    }
-    return MachTask{ .port = port };
-}
-
-pub fn machTaskForSelf() MachTask {
-    return .{ .port = mach_task_self() };
-}
-
 pub const os_signpost_id_t = u64;
 
 pub const OS_SIGNPOST_ID_NULL: os_signpost_id_t = 0;
@@ -1503,168 +1017,6 @@ pub const KEVENT_FLAG_ERROR_EVENTS = 0x002;
 
 pub const SYSPROTO_EVENT = 1;
 pub const SYSPROTO_CONTROL = 2;
-/// Kernel return values
-pub const KernE = enum(u32) {
-    SUCCESS = 0,
-    /// Specified address is not currently valid
-    INVALID_ADDRESS = 1,
-    /// Specified memory is valid, but does not permit the
-    /// required forms of access.
-    PROTECTION_FAILURE = 2,
-    /// The address range specified is already in use, or
-    /// no address range of the size specified could be
-    /// found.
-    NO_SPACE = 3,
-    /// The function requested was not applicable to this
-    /// type of argument, or an argument is invalid
-    INVALID_ARGUMENT = 4,
-    /// The function could not be performed.  A catch-all.
-    FAILURE = 5,
-    /// A system resource could not be allocated to fulfill
-    /// this request.  This failure may not be permanent.
-    RESOURCE_SHORTAGE = 6,
-    /// The task in question does not hold receive rights
-    /// for the port argument.
-    NOT_RECEIVER = 7,
-    /// Bogus access restriction.
-    NO_ACCESS = 8,
-    /// During a page fault, the target address refers to a
-    /// memory object that has been destroyed.  This
-    /// failure is permanent.
-    MEMORY_FAILURE = 9,
-    /// During a page fault, the memory object indicated
-    /// that the data could not be returned.  This failure
-    /// may be temporary; future attempts to access this
-    /// same data may succeed, as defined by the memory
-    /// object.
-    MEMORY_ERROR = 10,
-    /// The receive right is already a member of the portset.
-    ALREADY_IN_SET = 11,
-    /// The receive right is not a member of a port set.
-    NOT_IN_SET = 12,
-    /// The name already denotes a right in the task.
-    NAME_EXISTS = 13,
-    /// The operation was aborted.  Ipc code will
-    /// catch this and reflect it as a message error.
-    ABORTED = 14,
-    /// The name doesn't denote a right in the task.
-    INVALID_NAME = 15,
-    /// Target task isn't an active task.
-    INVALID_TASK = 16,
-    /// The name denotes a right, but not an appropriate right.
-    INVALID_RIGHT = 17,
-    /// A blatant range error.
-    INVALID_VALUE = 18,
-    /// Operation would overflow limit on user-references.
-    UREFS_OVERFLOW = 19,
-    /// The supplied (port) capability is improper.
-    INVALID_CAPABILITY = 20,
-    /// The task already has send or receive rights
-    /// for the port under another name.
-    RIGHT_EXISTS = 21,
-    /// Target host isn't actually a host.
-    INVALID_HOST = 22,
-    /// An attempt was made to supply "precious" data
-    /// for memory that is already present in a
-    /// memory object.
-    MEMORY_PRESENT = 23,
-    /// A page was requested of a memory manager via
-    /// memory_object_data_request for an object using
-    /// a MEMORY_OBJECT_COPY_CALL strategy, with the
-    /// VM_PROT_WANTS_COPY flag being used to specify
-    /// that the page desired is for a copy of the
-    /// object, and the memory manager has detected
-    /// the page was pushed into a copy of the object
-    /// while the kernel was walking the shadow chain
-    /// from the copy to the object. This error code
-    /// is delivered via memory_object_data_error
-    /// and is handled by the kernel (it forces the
-    /// kernel to restart the fault). It will not be
-    /// seen by users.
-    MEMORY_DATA_MOVED = 24,
-    /// A strategic copy was attempted of an object
-    /// upon which a quicker copy is now possible.
-    /// The caller should retry the copy using
-    /// vm_object_copy_quickly. This error code
-    /// is seen only by the kernel.
-    MEMORY_RESTART_COPY = 25,
-    /// An argument applied to assert processor set privilege
-    /// was not a processor set control port.
-    INVALID_PROCESSOR_SET = 26,
-    /// The specified scheduling attributes exceed the thread's
-    /// limits.
-    POLICY_LIMIT = 27,
-    /// The specified scheduling policy is not currently
-    /// enabled for the processor set.
-    INVALID_POLICY = 28,
-    /// The external memory manager failed to initialize the
-    /// memory object.
-    INVALID_OBJECT = 29,
-    /// A thread is attempting to wait for an event for which
-    /// there is already a waiting thread.
-    ALREADY_WAITING = 30,
-    /// An attempt was made to destroy the default processor
-    /// set.
-    DEFAULT_SET = 31,
-    /// An attempt was made to fetch an exception port that is
-    /// protected, or to abort a thread while processing a
-    /// protected exception.
-    EXCEPTION_PROTECTED = 32,
-    /// A ledger was required but not supplied.
-    INVALID_LEDGER = 33,
-    /// The port was not a memory cache control port.
-    INVALID_MEMORY_CONTROL = 34,
-    /// An argument supplied to assert security privilege
-    /// was not a host security port.
-    INVALID_SECURITY = 35,
-    /// thread_depress_abort was called on a thread which
-    /// was not currently depressed.
-    NOT_DEPRESSED = 36,
-    /// Object has been terminated and is no longer available
-    TERMINATED = 37,
-    /// Lock set has been destroyed and is no longer available.
-    LOCK_SET_DESTROYED = 38,
-    /// The thread holding the lock terminated before releasing
-    /// the lock
-    LOCK_UNSTABLE = 39,
-    /// The lock is already owned by another thread
-    LOCK_OWNED = 40,
-    /// The lock is already owned by the calling thread
-    LOCK_OWNED_SELF = 41,
-    /// Semaphore has been destroyed and is no longer available.
-    SEMAPHORE_DESTROYED = 42,
-    /// Return from RPC indicating the target server was
-    /// terminated before it successfully replied
-    RPC_SERVER_TERMINATED = 43,
-    /// Terminate an orphaned activation.
-    RPC_TERMINATE_ORPHAN = 44,
-    /// Allow an orphaned activation to continue executing.
-    RPC_CONTINUE_ORPHAN = 45,
-    /// Empty thread activation (No thread linked to it)
-    NOT_SUPPORTED = 46,
-    /// Remote node down or inaccessible.
-    NODE_DOWN = 47,
-    /// A signalled thread was not actually waiting.
-    NOT_WAITING = 48,
-    /// Some thread-oriented operation (semaphore_wait) timed out
-    OPERATION_TIMED_OUT = 49,
-    /// During a page fault, indicates that the page was rejected
-    /// as a result of a signature check.
-    CODESIGN_ERROR = 50,
-    /// The requested property cannot be changed at this time.
-    POLICY_STATIC = 51,
-    /// The provided buffer is of insufficient size for the requested data.
-    INSUFFICIENT_BUFFER_SIZE = 52,
-    /// Denied by security policy
-    DENIED = 53,
-    /// The KC on which the function is operating is missing
-    MISSING_KC = 54,
-    /// The KC on which the function is operating is invalid
-    INVALID_KC = 55,
-    /// A search or query operation did not return a result
-    NOT_FOUND = 56,
-    _,
-};
 
 pub const mach_msg_return_t = kern_return_t;
 
lib/std/c.zig
@@ -9541,10 +9541,24 @@ pub const CPUFAMILY = darwin.CPUFAMILY;
 pub const DB_RECORDTYPE = darwin.DB_RECORDTYPE;
 pub const EXC = darwin.EXC;
 pub const EXCEPTION = darwin.EXCEPTION;
+pub const MACH_MSG_TYPE = darwin.MACH_MSG_TYPE;
+pub const MACH_PORT_RIGHT = darwin.MACH_PORT_RIGHT;
+pub const MACH_TASK_BASIC_INFO = darwin.MACH_TASK_BASIC_INFO;
+pub const MACH_TASK_BASIC_INFO_COUNT = darwin.MACH_TASK_BASIC_INFO_COUNT;
+pub const MATTR = darwin.MATTR;
 pub const NSVersionOfRunTimeLibrary = darwin.NSVersionOfRunTimeLibrary;
 pub const OPEN_MAX = darwin.OPEN_MAX;
+pub const POSIX_SPAWN = darwin.POSIX_SPAWN;
+pub const TASK_NULL = darwin.TASK_NULL;
+pub const TASK_VM_INFO = darwin.TASK_VM_INFO;
+pub const TASK_VM_INFO_COUNT = darwin.TASK_VM_INFO_COUNT;
+pub const THREAD_BASIC_INFO = darwin.THREAD_BASIC_INFO;
+pub const THREAD_BASIC_INFO_COUNT = darwin.THREAD_BASIC_INFO_COUNT;
+pub const THREAD_IDENTIFIER_INFO_COUNT = darwin.THREAD_IDENTIFIER_INFO_COUNT;
+pub const THREAD_NULL = darwin.THREAD_NULL;
 pub const THREAD_STATE_NONE = darwin.THREAD_STATE_NONE;
 pub const UL = darwin.UL;
+pub const VM = darwin.VM;
 pub const _NSGetExecutablePath = darwin._NSGetExecutablePath;
 pub const __getdirentries64 = darwin.__getdirentries64;
 pub const __ulock_wait = darwin.__ulock_wait;
@@ -9562,16 +9576,21 @@ pub const dispatch_semaphore_signal = darwin.dispatch_semaphore_signal;
 pub const dispatch_semaphore_wait = darwin.dispatch_semaphore_wait;
 pub const dispatch_time = darwin.dispatch_time;
 pub const fcopyfile = darwin.fcopyfile;
+pub const kern_return_t = darwin.kern_return_t;
 pub const kevent64 = darwin.kevent64;
 pub const mach_absolute_time = darwin.mach_absolute_time;
 pub const mach_continuous_time = darwin.mach_continuous_time;
 pub const mach_hdr = darwin.mach_hdr;
 pub const mach_host_self = darwin.mach_host_self;
 pub const mach_msg = darwin.mach_msg;
+pub const mach_msg_type_number_t = darwin.mach_msg_type_number_t;
 pub const mach_port_allocate = darwin.mach_port_allocate;
+pub const mach_port_array_t = darwin.mach_port_array_t;
 pub const mach_port_deallocate = darwin.mach_port_deallocate;
 pub const mach_port_insert_right = darwin.mach_port_insert_right;
+pub const mach_port_name_t = darwin.mach_port_name_t;
 pub const mach_port_t = darwin.mach_port_t;
+pub const mach_task_basic_info = darwin.mach_task_basic_info;
 pub const mach_task_self = darwin.mach_task_self;
 pub const mach_timebase_info = darwin.mach_timebase_info;
 pub const mach_vm_protect = darwin.mach_vm_protect;
@@ -9602,10 +9621,12 @@ pub const posix_spawn_file_actions_addinherit_np = darwin.posix_spawn_file_actio
 pub const posix_spawn_file_actions_addopen = darwin.posix_spawn_file_actions_addopen;
 pub const posix_spawn_file_actions_destroy = darwin.posix_spawn_file_actions_destroy;
 pub const posix_spawn_file_actions_init = darwin.posix_spawn_file_actions_init;
+pub const posix_spawn_file_actions_t = darwin.posix_spawn_file_actions_t;
 pub const posix_spawnattr_destroy = darwin.posix_spawnattr_destroy;
 pub const posix_spawnattr_getflags = darwin.posix_spawnattr_getflags;
 pub const posix_spawnattr_init = darwin.posix_spawnattr_init;
 pub const posix_spawnattr_setflags = darwin.posix_spawnattr_setflags;
+pub const posix_spawnattr_t = darwin.posix_spawnattr_t;
 pub const posix_spawnp = darwin.posix_spawnp;
 pub const pthread_attr_get_qos_class_np = darwin.pthread_attr_get_qos_class_np;
 pub const pthread_attr_set_qos_class_np = darwin.pthread_attr_set_qos_class_np;
@@ -9616,17 +9637,31 @@ pub const sigaddset = darwin.sigaddset;
 pub const task_for_pid = darwin.task_for_pid;
 pub const task_get_exception_ports = darwin.task_get_exception_ports;
 pub const task_info = darwin.task_info;
+pub const task_info_t = darwin.task_info_t;
 pub const task_resume = darwin.task_resume;
 pub const task_set_exception_ports = darwin.task_set_exception_ports;
 pub const task_suspend = darwin.task_suspend;
 pub const task_threads = darwin.task_threads;
+pub const task_vm_info_data_t = darwin.task_vm_info_data_t;
+pub const thread_basic_info = darwin.thread_basic_info;
 pub const thread_get_state = darwin.thread_get_state;
+pub const thread_identifier_info = darwin.thread_identifier_info;
 pub const thread_info = darwin.thread_info;
+pub const thread_info_t = darwin.thread_info_t;
 pub const thread_resume = darwin.thread_resume;
 pub const thread_set_state = darwin.thread_set_state;
 pub const vm_deallocate = darwin.vm_deallocate;
 pub const vm_machine_attribute = darwin.vm_machine_attribute;
+pub const vm_machine_attribute_val_t = darwin.vm_machine_attribute_val_t;
+pub const vm_offset_t = darwin.vm_offset_t;
 pub const vm_prot_t = darwin.vm_prot_t;
+pub const vm_region_basic_info_64 = darwin.vm_region_basic_info_64;
+pub const vm_region_extended_info = darwin.vm_region_extended_info;
+pub const vm_region_info_t = darwin.vm_region_info_t;
+pub const vm_region_recurse_info_t = darwin.vm_region_recurse_info_t;
+pub const vm_region_submap_info_64 = darwin.vm_region_submap_info_64;
+pub const vm_region_submap_short_info_64 = darwin.vm_region_submap_short_info_64;
+pub const vm_region_top_info = darwin.vm_region_top_info;
 
 pub const _ksiginfo = netbsd._ksiginfo;
 pub const _lwp_self = netbsd._lwp_self;
src/link/MachO.zig
@@ -3779,7 +3779,7 @@ pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {
 pub fn ptraceAttach(self: *MachO, pid: std.posix.pid_t) !void {
     if (!is_hot_update_compatible) return;
 
-    const mach_task = try std.c.machTaskForPid(pid);
+    const mach_task = try machTaskForPid(pid);
     log.debug("Mach task for pid {d}: {any}", .{ pid, mach_task });
     self.hot_state.mach_task = mach_task;
 
@@ -4058,7 +4058,7 @@ pub const LiteralPool = struct {
 };
 
 const HotUpdateState = struct {
-    mach_task: ?std.c.MachTask = null,
+    mach_task: ?MachTask = null,
 };
 
 pub const SymtabCtx = struct {
@@ -4562,3 +4562,646 @@ const UnwindInfo = @import("MachO/UnwindInfo.zig");
 const WeakBind = bind.WeakBind;
 const ZigGotSection = synthetic.ZigGotSection;
 const ZigObject = @import("MachO/ZigObject.zig");
+
+pub const MachError = error{
+    /// Not enough permissions held to perform the requested kernel
+    /// call.
+    PermissionDenied,
+} || std.posix.UnexpectedError;
+
+pub const MachTask = extern struct {
+    port: std.c.mach_port_name_t,
+
+    pub fn isValid(self: MachTask) bool {
+        return self.port != std.c.TASK_NULL;
+    }
+
+    pub fn pidForTask(self: MachTask) MachError!std.c.pid_t {
+        var pid: std.c.pid_t = undefined;
+        switch (getKernError(std.c.pid_for_task(self.port, &pid))) {
+            .SUCCESS => return pid,
+            .FAILURE => return error.PermissionDenied,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    pub fn allocatePort(self: MachTask, right: std.c.MACH_PORT_RIGHT) MachError!MachTask {
+        var out_port: std.c.mach_port_name_t = undefined;
+        switch (getKernError(std.c.mach_port_allocate(
+            self.port,
+            @intFromEnum(right),
+            &out_port,
+        ))) {
+            .SUCCESS => return .{ .port = out_port },
+            .FAILURE => return error.PermissionDenied,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    pub fn deallocatePort(self: MachTask, port: MachTask) void {
+        _ = getKernError(std.c.mach_port_deallocate(self.port, port.port));
+    }
+
+    pub fn insertRight(self: MachTask, port: MachTask, msg: std.c.MACH_MSG_TYPE) !void {
+        switch (getKernError(std.c.mach_port_insert_right(
+            self.port,
+            port.port,
+            port.port,
+            @intFromEnum(msg),
+        ))) {
+            .SUCCESS => return,
+            .FAILURE => return error.PermissionDenied,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    pub const PortInfo = struct {
+        mask: std.c.exception_mask_t,
+        masks: [std.c.EXC.TYPES_COUNT]std.c.exception_mask_t,
+        ports: [std.c.EXC.TYPES_COUNT]std.c.mach_port_t,
+        behaviors: [std.c.EXC.TYPES_COUNT]std.c.exception_behavior_t,
+        flavors: [std.c.EXC.TYPES_COUNT]std.c.thread_state_flavor_t,
+        count: std.c.mach_msg_type_number_t,
+    };
+
+    pub fn getExceptionPorts(self: MachTask, mask: std.c.exception_mask_t) !PortInfo {
+        var info: PortInfo = .{
+            .mask = mask,
+            .masks = undefined,
+            .ports = undefined,
+            .behaviors = undefined,
+            .flavors = undefined,
+            .count = 0,
+        };
+        info.count = info.ports.len / @sizeOf(std.c.mach_port_t);
+
+        switch (getKernError(std.c.task_get_exception_ports(
+            self.port,
+            info.mask,
+            &info.masks,
+            &info.count,
+            &info.ports,
+            &info.behaviors,
+            &info.flavors,
+        ))) {
+            .SUCCESS => return info,
+            .FAILURE => return error.PermissionDenied,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    pub fn setExceptionPorts(
+        self: MachTask,
+        mask: std.c.exception_mask_t,
+        new_port: MachTask,
+        behavior: std.c.exception_behavior_t,
+        new_flavor: std.c.thread_state_flavor_t,
+    ) !void {
+        switch (getKernError(std.c.task_set_exception_ports(
+            self.port,
+            mask,
+            new_port.port,
+            behavior,
+            new_flavor,
+        ))) {
+            .SUCCESS => return,
+            .FAILURE => return error.PermissionDenied,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    pub const RegionInfo = struct {
+        pub const Tag = enum {
+            basic,
+            extended,
+            top,
+        };
+
+        base_addr: u64,
+        tag: Tag,
+        info: union {
+            basic: std.c.vm_region_basic_info_64,
+            extended: std.c.vm_region_extended_info,
+            top: std.c.vm_region_top_info,
+        },
+    };
+
+    pub fn getRegionInfo(
+        task: MachTask,
+        address: u64,
+        len: usize,
+        tag: RegionInfo.Tag,
+    ) MachError!RegionInfo {
+        var info: RegionInfo = .{
+            .base_addr = address,
+            .tag = tag,
+            .info = undefined,
+        };
+        switch (tag) {
+            .basic => info.info = .{ .basic = undefined },
+            .extended => info.info = .{ .extended = undefined },
+            .top => info.info = .{ .top = undefined },
+        }
+        var base_len: std.c.mach_vm_size_t = if (len == 1) 2 else len;
+        var objname: std.c.mach_port_t = undefined;
+        var count: std.c.mach_msg_type_number_t = switch (tag) {
+            .basic => std.c.VM.REGION.BASIC_INFO_COUNT,
+            .extended => std.c.VM.REGION.EXTENDED_INFO_COUNT,
+            .top => std.c.VM.REGION.TOP_INFO_COUNT,
+        };
+        switch (getKernError(std.c.mach_vm_region(
+            task.port,
+            &info.base_addr,
+            &base_len,
+            switch (tag) {
+                .basic => std.c.VM.REGION.BASIC_INFO_64,
+                .extended => std.c.VM.REGION.EXTENDED_INFO,
+                .top => std.c.VM.REGION.TOP_INFO,
+            },
+            switch (tag) {
+                .basic => @as(std.c.vm_region_info_t, @ptrCast(&info.info.basic)),
+                .extended => @as(std.c.vm_region_info_t, @ptrCast(&info.info.extended)),
+                .top => @as(std.c.vm_region_info_t, @ptrCast(&info.info.top)),
+            },
+            &count,
+            &objname,
+        ))) {
+            .SUCCESS => return info,
+            .FAILURE => return error.PermissionDenied,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    pub const RegionSubmapInfo = struct {
+        pub const Tag = enum {
+            short,
+            full,
+        };
+
+        tag: Tag,
+        base_addr: u64,
+        info: union {
+            short: std.c.vm_region_submap_short_info_64,
+            full: std.c.vm_region_submap_info_64,
+        },
+    };
+
+    pub fn getRegionSubmapInfo(
+        task: MachTask,
+        address: u64,
+        len: usize,
+        nesting_depth: u32,
+        tag: RegionSubmapInfo.Tag,
+    ) MachError!RegionSubmapInfo {
+        var info: RegionSubmapInfo = .{
+            .base_addr = address,
+            .tag = tag,
+            .info = undefined,
+        };
+        switch (tag) {
+            .short => info.info = .{ .short = undefined },
+            .full => info.info = .{ .full = undefined },
+        }
+        var nesting = nesting_depth;
+        var base_len: std.c.mach_vm_size_t = if (len == 1) 2 else len;
+        var count: std.c.mach_msg_type_number_t = switch (tag) {
+            .short => std.c.VM.REGION.SUBMAP_SHORT_INFO_COUNT_64,
+            .full => std.c.VM.REGION.SUBMAP_INFO_COUNT_64,
+        };
+        switch (getKernError(std.c.mach_vm_region_recurse(
+            task.port,
+            &info.base_addr,
+            &base_len,
+            &nesting,
+            switch (tag) {
+                .short => @as(std.c.vm_region_recurse_info_t, @ptrCast(&info.info.short)),
+                .full => @as(std.c.vm_region_recurse_info_t, @ptrCast(&info.info.full)),
+            },
+            &count,
+        ))) {
+            .SUCCESS => return info,
+            .FAILURE => return error.PermissionDenied,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    pub fn getCurrProtection(task: MachTask, address: u64, len: usize) MachError!std.c.vm_prot_t {
+        const info = try task.getRegionSubmapInfo(address, len, 0, .short);
+        return info.info.short.protection;
+    }
+
+    pub fn setMaxProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void {
+        return task.setProtectionImpl(address, len, true, prot);
+    }
+
+    pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void {
+        return task.setProtectionImpl(address, len, false, prot);
+    }
+
+    fn setProtectionImpl(task: MachTask, address: u64, len: usize, set_max: bool, prot: std.c.vm_prot_t) MachError!void {
+        switch (getKernError(std.c.mach_vm_protect(task.port, address, len, @intFromBool(set_max), prot))) {
+            .SUCCESS => return,
+            .FAILURE => return error.PermissionDenied,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    /// Will write to VM even if current protection attributes specifically prohibit
+    /// us from doing so, by temporarily setting protection level to a level with VM_PROT_COPY
+    /// variant, and resetting after a successful or unsuccessful write.
+    pub fn writeMemProtected(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
+        const curr_prot = try task.getCurrProtection(address, buf.len);
+        try task.setCurrProtection(
+            address,
+            buf.len,
+            std.c.PROT.READ | std.c.PROT.WRITE | std.c.PROT.COPY,
+        );
+        defer {
+            task.setCurrProtection(address, buf.len, curr_prot) catch {};
+        }
+        return task.writeMem(address, buf, arch);
+    }
+
+    pub fn writeMem(task: MachTask, address: u64, buf: []const u8, arch: std.Target.Cpu.Arch) MachError!usize {
+        const count = buf.len;
+        var total_written: usize = 0;
+        var curr_addr = address;
+        const page_size = try MachTask.getPageSize(task); // TODO we probably can assume value here
+        var out_buf = buf[0..];
+
+        while (total_written < count) {
+            const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_written);
+            switch (getKernError(std.c.mach_vm_write(
+                task.port,
+                curr_addr,
+                @intFromPtr(out_buf.ptr),
+                @as(std.c.mach_msg_type_number_t, @intCast(curr_size)),
+            ))) {
+                .SUCCESS => {},
+                .FAILURE => return error.PermissionDenied,
+                else => |err| return unexpectedKernError(err),
+            }
+
+            switch (arch) {
+                .aarch64 => {
+                    var mattr_value: std.c.vm_machine_attribute_val_t = std.c.MATTR.VAL_CACHE_FLUSH;
+                    switch (getKernError(std.c.vm_machine_attribute(
+                        task.port,
+                        curr_addr,
+                        curr_size,
+                        std.c.MATTR.CACHE,
+                        &mattr_value,
+                    ))) {
+                        .SUCCESS => {},
+                        .FAILURE => return error.PermissionDenied,
+                        else => |err| return unexpectedKernError(err),
+                    }
+                },
+                .x86_64 => {},
+                else => unreachable,
+            }
+
+            out_buf = out_buf[curr_size..];
+            total_written += curr_size;
+            curr_addr += curr_size;
+        }
+
+        return total_written;
+    }
+
+    pub fn readMem(task: MachTask, address: u64, buf: []u8) MachError!usize {
+        const count = buf.len;
+        var total_read: usize = 0;
+        var curr_addr = address;
+        const page_size = try MachTask.getPageSize(task); // TODO we probably can assume value here
+        var out_buf = buf[0..];
+
+        while (total_read < count) {
+            const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_read);
+            var curr_bytes_read: std.c.mach_msg_type_number_t = 0;
+            var vm_memory: std.c.vm_offset_t = undefined;
+            switch (getKernError(std.c.mach_vm_read(task.port, curr_addr, curr_size, &vm_memory, &curr_bytes_read))) {
+                .SUCCESS => {},
+                .FAILURE => return error.PermissionDenied,
+                else => |err| return unexpectedKernError(err),
+            }
+
+            @memcpy(out_buf[0..curr_bytes_read], @as([*]const u8, @ptrFromInt(vm_memory)));
+            _ = std.c.vm_deallocate(std.c.mach_task_self(), vm_memory, curr_bytes_read);
+
+            out_buf = out_buf[curr_bytes_read..];
+            curr_addr += curr_bytes_read;
+            total_read += curr_bytes_read;
+        }
+
+        return total_read;
+    }
+
+    fn maxBytesLeftInPage(page_size: usize, address: u64, count: usize) usize {
+        var left = count;
+        if (page_size > 0) {
+            const page_offset = address % page_size;
+            const bytes_left_in_page = page_size - page_offset;
+            if (count > bytes_left_in_page) {
+                left = bytes_left_in_page;
+            }
+        }
+        return left;
+    }
+
+    fn getPageSize(task: MachTask) MachError!usize {
+        if (task.isValid()) {
+            var info_count = std.c.TASK_VM_INFO_COUNT;
+            var vm_info: std.c.task_vm_info_data_t = undefined;
+            switch (getKernError(std.c.task_info(
+                task.port,
+                std.c.TASK_VM_INFO,
+                @as(std.c.task_info_t, @ptrCast(&vm_info)),
+                &info_count,
+            ))) {
+                .SUCCESS => return @as(usize, @intCast(vm_info.page_size)),
+                else => {},
+            }
+        }
+        var page_size: std.c.vm_size_t = undefined;
+        switch (getKernError(std.c._host_page_size(std.c.mach_host_self(), &page_size))) {
+            .SUCCESS => return page_size,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    pub fn basicTaskInfo(task: MachTask) MachError!std.c.mach_task_basic_info {
+        var info: std.c.mach_task_basic_info = undefined;
+        var count = std.c.MACH_TASK_BASIC_INFO_COUNT;
+        switch (getKernError(std.c.task_info(
+            task.port,
+            std.c.MACH_TASK_BASIC_INFO,
+            @as(std.c.task_info_t, @ptrCast(&info)),
+            &count,
+        ))) {
+            .SUCCESS => return info,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    pub fn @"resume"(task: MachTask) MachError!void {
+        switch (getKernError(std.c.task_resume(task.port))) {
+            .SUCCESS => {},
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    pub fn @"suspend"(task: MachTask) MachError!void {
+        switch (getKernError(std.c.task_suspend(task.port))) {
+            .SUCCESS => {},
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    const ThreadList = struct {
+        buf: []MachThread,
+
+        pub fn deinit(list: ThreadList) void {
+            const self_task = machTaskForSelf();
+            _ = std.c.vm_deallocate(
+                self_task.port,
+                @intFromPtr(list.buf.ptr),
+                @as(std.c.vm_size_t, @intCast(list.buf.len * @sizeOf(std.c.mach_port_t))),
+            );
+        }
+    };
+
+    pub fn getThreads(task: MachTask) MachError!ThreadList {
+        var thread_list: std.c.mach_port_array_t = undefined;
+        var thread_count: std.c.mach_msg_type_number_t = undefined;
+        switch (getKernError(std.c.task_threads(task.port, &thread_list, &thread_count))) {
+            .SUCCESS => return ThreadList{ .buf = @as([*]MachThread, @ptrCast(thread_list))[0..thread_count] },
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+};
+
+pub const MachThread = extern struct {
+    port: std.c.mach_port_t,
+
+    pub fn isValid(thread: MachThread) bool {
+        return thread.port != std.c.THREAD_NULL;
+    }
+
+    pub fn getBasicInfo(thread: MachThread) MachError!std.c.thread_basic_info {
+        var info: std.c.thread_basic_info = undefined;
+        var count = std.c.THREAD_BASIC_INFO_COUNT;
+        switch (getKernError(std.c.thread_info(
+            thread.port,
+            std.c.THREAD_BASIC_INFO,
+            @as(std.c.thread_info_t, @ptrCast(&info)),
+            &count,
+        ))) {
+            .SUCCESS => return info,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+
+    pub fn getIdentifierInfo(thread: MachThread) MachError!std.c.thread_identifier_info {
+        var info: std.c.thread_identifier_info = undefined;
+        var count = std.c.THREAD_IDENTIFIER_INFO_COUNT;
+        switch (getKernError(std.c.thread_info(
+            thread.port,
+            std.c.THREAD_IDENTIFIER_INFO,
+            @as(std.c.thread_info_t, @ptrCast(&info)),
+            &count,
+        ))) {
+            .SUCCESS => return info,
+            else => |err| return unexpectedKernError(err),
+        }
+    }
+};
+
+pub fn machTaskForPid(pid: std.c.pid_t) MachError!MachTask {
+    var port: std.c.mach_port_name_t = undefined;
+    switch (getKernError(std.c.task_for_pid(std.c.mach_task_self(), pid, &port))) {
+        .SUCCESS => {},
+        .FAILURE => return error.PermissionDenied,
+        else => |err| return unexpectedKernError(err),
+    }
+    return MachTask{ .port = port };
+}
+
+pub fn machTaskForSelf() MachTask {
+    return .{ .port = std.c.mach_task_self() };
+}
+
+pub fn getKernError(err: std.c.kern_return_t) KernE {
+    return @as(KernE, @enumFromInt(@as(u32, @truncate(@as(usize, @intCast(err))))));
+}
+
+pub fn unexpectedKernError(err: KernE) std.posix.UnexpectedError {
+    if (std.posix.unexpected_error_tracing) {
+        std.debug.print("unexpected error: {d}\n", .{@intFromEnum(err)});
+        std.debug.dumpCurrentStackTrace(null);
+    }
+    return error.Unexpected;
+}
+
+/// Kernel return values
+pub const KernE = enum(u32) {
+    SUCCESS = 0,
+    /// Specified address is not currently valid
+    INVALID_ADDRESS = 1,
+    /// Specified memory is valid, but does not permit the
+    /// required forms of access.
+    PROTECTION_FAILURE = 2,
+    /// The address range specified is already in use, or
+    /// no address range of the size specified could be
+    /// found.
+    NO_SPACE = 3,
+    /// The function requested was not applicable to this
+    /// type of argument, or an argument is invalid
+    INVALID_ARGUMENT = 4,
+    /// The function could not be performed.  A catch-all.
+    FAILURE = 5,
+    /// A system resource could not be allocated to fulfill
+    /// this request.  This failure may not be permanent.
+    RESOURCE_SHORTAGE = 6,
+    /// The task in question does not hold receive rights
+    /// for the port argument.
+    NOT_RECEIVER = 7,
+    /// Bogus access restriction.
+    NO_ACCESS = 8,
+    /// During a page fault, the target address refers to a
+    /// memory object that has been destroyed.  This
+    /// failure is permanent.
+    MEMORY_FAILURE = 9,
+    /// During a page fault, the memory object indicated
+    /// that the data could not be returned.  This failure
+    /// may be temporary; future attempts to access this
+    /// same data may succeed, as defined by the memory
+    /// object.
+    MEMORY_ERROR = 10,
+    /// The receive right is already a member of the portset.
+    ALREADY_IN_SET = 11,
+    /// The receive right is not a member of a port set.
+    NOT_IN_SET = 12,
+    /// The name already denotes a right in the task.
+    NAME_EXISTS = 13,
+    /// The operation was aborted.  Ipc code will
+    /// catch this and reflect it as a message error.
+    ABORTED = 14,
+    /// The name doesn't denote a right in the task.
+    INVALID_NAME = 15,
+    /// Target task isn't an active task.
+    INVALID_TASK = 16,
+    /// The name denotes a right, but not an appropriate right.
+    INVALID_RIGHT = 17,
+    /// A blatant range error.
+    INVALID_VALUE = 18,
+    /// Operation would overflow limit on user-references.
+    UREFS_OVERFLOW = 19,
+    /// The supplied (port) capability is improper.
+    INVALID_CAPABILITY = 20,
+    /// The task already has send or receive rights
+    /// for the port under another name.
+    RIGHT_EXISTS = 21,
+    /// Target host isn't actually a host.
+    INVALID_HOST = 22,
+    /// An attempt was made to supply "precious" data
+    /// for memory that is already present in a
+    /// memory object.
+    MEMORY_PRESENT = 23,
+    /// A page was requested of a memory manager via
+    /// memory_object_data_request for an object using
+    /// a MEMORY_OBJECT_COPY_CALL strategy, with the
+    /// VM_PROT_WANTS_COPY flag being used to specify
+    /// that the page desired is for a copy of the
+    /// object, and the memory manager has detected
+    /// the page was pushed into a copy of the object
+    /// while the kernel was walking the shadow chain
+    /// from the copy to the object. This error code
+    /// is delivered via memory_object_data_error
+    /// and is handled by the kernel (it forces the
+    /// kernel to restart the fault). It will not be
+    /// seen by users.
+    MEMORY_DATA_MOVED = 24,
+    /// A strategic copy was attempted of an object
+    /// upon which a quicker copy is now possible.
+    /// The caller should retry the copy using
+    /// vm_object_copy_quickly. This error code
+    /// is seen only by the kernel.
+    MEMORY_RESTART_COPY = 25,
+    /// An argument applied to assert processor set privilege
+    /// was not a processor set control port.
+    INVALID_PROCESSOR_SET = 26,
+    /// The specified scheduling attributes exceed the thread's
+    /// limits.
+    POLICY_LIMIT = 27,
+    /// The specified scheduling policy is not currently
+    /// enabled for the processor set.
+    INVALID_POLICY = 28,
+    /// The external memory manager failed to initialize the
+    /// memory object.
+    INVALID_OBJECT = 29,
+    /// A thread is attempting to wait for an event for which
+    /// there is already a waiting thread.
+    ALREADY_WAITING = 30,
+    /// An attempt was made to destroy the default processor
+    /// set.
+    DEFAULT_SET = 31,
+    /// An attempt was made to fetch an exception port that is
+    /// protected, or to abort a thread while processing a
+    /// protected exception.
+    EXCEPTION_PROTECTED = 32,
+    /// A ledger was required but not supplied.
+    INVALID_LEDGER = 33,
+    /// The port was not a memory cache control port.
+    INVALID_MEMORY_CONTROL = 34,
+    /// An argument supplied to assert security privilege
+    /// was not a host security port.
+    INVALID_SECURITY = 35,
+    /// thread_depress_abort was called on a thread which
+    /// was not currently depressed.
+    NOT_DEPRESSED = 36,
+    /// Object has been terminated and is no longer available
+    TERMINATED = 37,
+    /// Lock set has been destroyed and is no longer available.
+    LOCK_SET_DESTROYED = 38,
+    /// The thread holding the lock terminated before releasing
+    /// the lock
+    LOCK_UNSTABLE = 39,
+    /// The lock is already owned by another thread
+    LOCK_OWNED = 40,
+    /// The lock is already owned by the calling thread
+    LOCK_OWNED_SELF = 41,
+    /// Semaphore has been destroyed and is no longer available.
+    SEMAPHORE_DESTROYED = 42,
+    /// Return from RPC indicating the target server was
+    /// terminated before it successfully replied
+    RPC_SERVER_TERMINATED = 43,
+    /// Terminate an orphaned activation.
+    RPC_TERMINATE_ORPHAN = 44,
+    /// Allow an orphaned activation to continue executing.
+    RPC_CONTINUE_ORPHAN = 45,
+    /// Empty thread activation (No thread linked to it)
+    NOT_SUPPORTED = 46,
+    /// Remote node down or inaccessible.
+    NODE_DOWN = 47,
+    /// A signalled thread was not actually waiting.
+    NOT_WAITING = 48,
+    /// Some thread-oriented operation (semaphore_wait) timed out
+    OPERATION_TIMED_OUT = 49,
+    /// During a page fault, indicates that the page was rejected
+    /// as a result of a signature check.
+    CODESIGN_ERROR = 50,
+    /// The requested property cannot be changed at this time.
+    POLICY_STATIC = 51,
+    /// The provided buffer is of insufficient size for the requested data.
+    INSUFFICIENT_BUFFER_SIZE = 52,
+    /// Denied by security policy
+    DENIED = 53,
+    /// The KC on which the function is operating is missing
+    MISSING_KC = 54,
+    /// The KC on which the function is operating is invalid
+    INVALID_KC = 55,
+    /// A search or query operation did not return a result
+    NOT_FOUND = 56,
+    _,
+};