Commit 8c4a2dc1df
Changed files (3)
lib
std
src
link
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,
+ _,
+};