Commit e1e414e62a
Changed files (6)
lib
std
lib/std/os/darwin/cssm.zig → lib/std/c/darwin/cssm.zig
File renamed without changes
lib/std/c/darwin.zig
@@ -8,6 +8,7 @@ const iovec_const = std.os.iovec_const;
pub const aarch64 = @import("darwin/aarch64.zig");
pub const x86_64 = @import("darwin/x86_64.zig");
+pub const cssm = @import("darwin/cssm.zig");
const arch_bits = switch (native_arch) {
.aarch64 => @import("darwin/aarch64.zig"),
@@ -2179,6 +2180,14 @@ pub fn getKernError(err: kern_return_t) KernE {
return @intToEnum(KernE, @truncate(u32, @intCast(usize, err)));
}
+pub fn unexpectedKernError(err: KernE) std.os.UnexpectedError {
+ if (std.os.unexpected_error_tracing) {
+ std.debug.print("unexpected errno: {d}\n", .{@enumToInt(err)});
+ std.debug.dumpCurrentStackTrace(null);
+ }
+ return error.Unexpected;
+}
+
/// Kernel return values
pub const KernE = enum(u32) {
SUCCESS = 0,
@@ -3085,3 +3094,471 @@ pub const PT_DENY_ATTACH = 31;
pub const caddr_t = ?[*]u8;
pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int;
+
+pub const MachError = error{
+ /// Not enough permissions held to perform the requested kernel
+ /// call.
+ PermissionDenied,
+} || std.os.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.os.pid_t {
+ var pid: std.os.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,
+ @enumToInt(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,
+ @enumToInt(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 => @ptrCast(vm_region_info_t, &info.info.basic),
+ .extended => @ptrCast(vm_region_info_t, &info.info.extended),
+ .top => @ptrCast(vm_region_info_t, &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 => @ptrCast(vm_region_recurse_info_t, &info.info.short),
+ .full => @ptrCast(vm_region_recurse_info_t, &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, @boolToInt(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,
+ @ptrToInt(out_buf.ptr),
+ @intCast(mach_msg_type_number_t, 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..].ptr, @intToPtr([*]const u8, vm_memory), curr_bytes_read);
+ _ = 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,
+ @ptrCast(task_info_t, &vm_info),
+ &info_count,
+ ))) {
+ .SUCCESS => return @intCast(usize, 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,
+ @ptrCast(task_info_t, &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,
+ @ptrToInt(list.buf.ptr),
+ @intCast(vm_size_t, 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 = @ptrCast([*]MachThread, 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,
+ @ptrCast(thread_info_t, &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,
+ @ptrCast(thread_info_t, &info),
+ &count,
+ ))) {
+ .SUCCESS => return info,
+ else => |err| return unexpectedKernError(err),
+ }
+ }
+};
+
+pub fn machTaskForPid(pid: std.os.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() };
+}
lib/std/os/darwin.zig
@@ -1,540 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-const log = std.log;
-const mem = std.mem;
-
-pub const cssm = @import("darwin/cssm.zig");
-
-pub usingnamespace std.c;
-pub usingnamespace mach_task;
-
-const mach_task = if (builtin.target.isDarwin()) struct {
- pub const MachError = error{
- /// Not enough permissions held to perform the requested kernel
- /// call.
- PermissionDenied,
- /// Kernel returned an unhandled and unexpected error code.
- /// This is a catch-all for any yet unobserved kernel response
- /// to some Mach message.
- Unexpected,
- };
-
- 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.os.pid_t {
- var pid: std.os.pid_t = undefined;
- switch (std.c.getKernError(std.c.pid_for_task(self.port, &pid))) {
- .SUCCESS => return pid,
- .FAILURE => return error.PermissionDenied,
- else => |err| {
- log.err("pid_for_task kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- pub fn allocatePort(self: MachTask, right: std.c.MACH_PORT_RIGHT) MachError!MachTask {
- var out_port: std.c.mach_port_name_t = undefined;
- switch (std.c.getKernError(std.c.mach_port_allocate(
- self.port,
- @enumToInt(right),
- &out_port,
- ))) {
- .SUCCESS => return .{ .port = out_port },
- .FAILURE => return error.PermissionDenied,
- else => |err| {
- log.err("mach_task_allocate kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- pub fn deallocatePort(self: MachTask, port: MachTask) void {
- _ = std.c.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 (std.c.getKernError(std.c.mach_port_insert_right(
- self.port,
- port.port,
- port.port,
- @enumToInt(msg),
- ))) {
- .SUCCESS => return,
- .FAILURE => return error.PermissionDenied,
- else => |err| {
- log.err("mach_port_insert_right kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- 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 (std.c.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| {
- log.err("task_get_exception_ports kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- 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 (std.c.getKernError(std.c.task_set_exception_ports(
- self.port,
- mask,
- new_port.port,
- behavior,
- new_flavor,
- ))) {
- .SUCCESS => return,
- .FAILURE => return error.PermissionDenied,
- else => |err| {
- log.err("task_set_exception_ports kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- 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 (std.c.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 => @ptrCast(std.c.vm_region_info_t, &info.info.basic),
- .extended => @ptrCast(std.c.vm_region_info_t, &info.info.extended),
- .top => @ptrCast(std.c.vm_region_info_t, &info.info.top),
- },
- &count,
- &objname,
- ))) {
- .SUCCESS => return info,
- .FAILURE => return error.PermissionDenied,
- else => |err| {
- log.err("mach_vm_region kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- 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 (std.c.getKernError(std.c.mach_vm_region_recurse(
- task.port,
- &info.base_addr,
- &base_len,
- &nesting,
- switch (tag) {
- .short => @ptrCast(std.c.vm_region_recurse_info_t, &info.info.short),
- .full => @ptrCast(std.c.vm_region_recurse_info_t, &info.info.full),
- },
- &count,
- ))) {
- .SUCCESS => return info,
- .FAILURE => return error.PermissionDenied,
- else => |err| {
- log.err("mach_vm_region kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- 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 (std.c.getKernError(std.c.mach_vm_protect(task.port, address, len, @boolToInt(set_max), prot))) {
- .SUCCESS => return,
- .FAILURE => return error.PermissionDenied,
- else => |err| {
- log.err("mach_vm_protect kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- /// 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 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 (std.c.getKernError(std.c.mach_vm_write(
- task.port,
- curr_addr,
- @ptrToInt(out_buf.ptr),
- @intCast(std.c.mach_msg_type_number_t, curr_size),
- ))) {
- .SUCCESS => {},
- .FAILURE => return error.PermissionDenied,
- else => |err| {
- log.err("mach_vm_write kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
-
- switch (arch) {
- .aarch64 => {
- var mattr_value: std.c.vm_machine_attribute_val_t = std.c.MATTR_VAL_CACHE_FLUSH;
- switch (std.c.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| {
- log.err("vm_machine_attribute kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- },
- .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: std.c.mach_msg_type_number_t = 0;
- var vm_memory: std.c.vm_offset_t = undefined;
- switch (std.c.getKernError(std.c.mach_vm_read(task.port, curr_addr, curr_size, &vm_memory, &curr_bytes_read))) {
- .SUCCESS => {},
- .FAILURE => return error.PermissionDenied,
- else => |err| {
- log.err("mach_vm_read kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
-
- @memcpy(out_buf[0..].ptr, @intToPtr([*]const u8, vm_memory), curr_bytes_read);
- _ = 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 (std.c.getKernError(std.c.task_info(
- task.port,
- std.c.TASK_VM_INFO,
- @ptrCast(std.c.task_info_t, &vm_info),
- &info_count,
- ))) {
- .SUCCESS => return @intCast(usize, vm_info.page_size),
- else => {},
- }
- }
- var page_size: std.c.vm_size_t = undefined;
- switch (std.c.getKernError(std.c._host_page_size(std.c.mach_host_self(), &page_size))) {
- .SUCCESS => return page_size,
- else => |err| {
- log.err("_host_page_size kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- 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 (std.c.getKernError(std.c.task_info(
- task.port,
- std.c.MACH_TASK_BASIC_INFO,
- @ptrCast(std.c.task_info_t, &info),
- &count,
- ))) {
- .SUCCESS => return info,
- else => |err| {
- log.err("task_info kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- pub fn @"resume"(task: MachTask) MachError!void {
- switch (std.c.getKernError(std.c.task_resume(task.port))) {
- .SUCCESS => {},
- else => |err| {
- log.err("task_resume kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- pub fn @"suspend"(task: MachTask) MachError!void {
- switch (std.c.getKernError(std.c.task_suspend(task.port))) {
- .SUCCESS => {},
- else => |err| {
- log.err("task_suspend kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- const ThreadList = struct {
- buf: []MachThread,
-
- pub fn deinit(list: ThreadList) void {
- const self_task = machTaskForSelf();
- _ = std.c.vm_deallocate(
- self_task.port,
- @ptrToInt(list.buf.ptr),
- @intCast(std.c.vm_size_t, 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 (std.c.getKernError(std.c.task_threads(task.port, &thread_list, &thread_count))) {
- .SUCCESS => return ThreadList{ .buf = @ptrCast([*]MachThread, thread_list)[0..thread_count] },
- else => |err| {
- log.err("task_threads kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
- };
-
- 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 (std.c.getKernError(std.c.thread_info(
- thread.port,
- std.c.THREAD_BASIC_INFO,
- @ptrCast(std.c.thread_info_t, &info),
- &count,
- ))) {
- .SUCCESS => return info,
- else => |err| {
- log.err("thread_info kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
-
- 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 (std.c.getKernError(std.c.thread_info(
- thread.port,
- std.c.THREAD_IDENTIFIER_INFO,
- @ptrCast(std.c.thread_info_t, &info),
- &count,
- ))) {
- .SUCCESS => return info,
- else => |err| {
- log.err("thread_info kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- }
- };
-
- pub fn machTaskForPid(pid: std.os.pid_t) MachError!MachTask {
- var port: std.c.mach_port_name_t = undefined;
- switch (std.c.getKernError(std.c.task_for_pid(std.c.mach_task_self(), pid, &port))) {
- .SUCCESS => {},
- .FAILURE => return error.PermissionDenied,
- else => |err| {
- log.err("task_for_pid kernel call failed with error code: {s}", .{@tagName(err)});
- return error.Unexpected;
- },
- }
- return MachTask{ .port = port };
- }
-
- pub fn machTaskForSelf() MachTask {
- return .{ .port = std.c.mach_task_self() };
- }
-} else struct {};
lib/std/os/ptrace.zig
@@ -1,28 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-
-const os = @import("../os.zig");
-const system = os.system;
-const errno = system.getErrno;
-const pid_t = system.pid_t;
-const unexpectedErrno = os.unexpectedErrno;
-const UnexpectedError = os.UnexpectedError;
-
-pub usingnamespace ptrace;
-
-const ptrace = if (builtin.target.isDarwin()) struct {
- pub const PtraceError = error{
- ProcessNotFound,
- PermissionDenied,
- } || UnexpectedError;
-
- pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void {
- switch (errno(system.ptrace(request, pid, addr, signal))) {
- .SUCCESS => return,
- .SRCH => return error.ProcessNotFound,
- .INVAL => unreachable,
- .BUSY, .PERM => return error.PermissionDenied,
- else => |err| return unexpectedErrno(err),
- }
- }
-} else struct {};
lib/std/os.zig
@@ -29,7 +29,7 @@ const Allocator = std.mem.Allocator;
const Preopen = std.fs.wasi.Preopen;
const PreopenList = std.fs.wasi.PreopenList;
-pub const darwin = @import("os/darwin.zig");
+pub const darwin = std.c;
pub const dragonfly = std.c;
pub const freebsd = std.c;
pub const haiku = std.c;
@@ -41,7 +41,6 @@ pub const plan9 = @import("os/plan9.zig");
pub const uefi = @import("os/uefi.zig");
pub const wasi = @import("os/wasi.zig");
pub const windows = @import("os/windows.zig");
-pub const ptrace = @import("os/ptrace.zig");
comptime {
assert(@import("std") == std); // std lib tests require --zig-lib-dir
@@ -7127,3 +7126,25 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {
else => |err| return unexpectedErrno(err),
};
}
+
+pub const PtraceError = error{
+ DeviceBusy,
+ ProcessNotFound,
+ PermissionDenied,
+} || UnexpectedError;
+
+/// TODO on other OSes
+pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void {
+ switch (builtin.os.tag) {
+ .macos, .ios, .tvos, .watchos => {},
+ else => @compileError("TODO implement ptrace"),
+ }
+ return switch (errno(system.ptrace(request, pid, addr, signal))) {
+ .SUCCESS => {},
+ .SRCH => error.ProcessNotFound,
+ .INVAL => unreachable,
+ .PERM => error.PermissionDenied,
+ .BUSY => error.DeviceBusy,
+ else => |err| return unexpectedErrno(err),
+ };
+}
CMakeLists.txt
@@ -296,7 +296,6 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig"
"${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig"
"${CMAKE_SOURCE_DIR}/lib/std/os.zig"
- "${CMAKE_SOURCE_DIR}/lib/std/os/darwin.zig"
"${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"
"${CMAKE_SOURCE_DIR}/lib/std/os/linux/errno/generic.zig"
"${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"