From e1e414e62a86cc460ef215ea8050c953b68b6080 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 15 Mar 2023 10:12:48 +0100 Subject: [PATCH] std: move os/darwin.zig and related to c/darwin.zig Move to c/darwin.zig as they really are libSystem/libc imports/wrappers. As an added bonus, get rid of the nasty `usingnamespace`s which are now unneeded. Finally, add `os.ptrace` but currently only implemented on darwin. --- CMakeLists.txt | 1 - lib/std/c/darwin.zig | 477 ++++++++++++++++++++++++++ lib/std/{os => c}/darwin/cssm.zig | 0 lib/std/os.zig | 25 +- lib/std/os/darwin.zig | 540 ------------------------------ lib/std/os/ptrace.zig | 28 -- 6 files changed, 500 insertions(+), 571 deletions(-) rename lib/std/{os => c}/darwin/cssm.zig (100%) delete mode 100644 lib/std/os/darwin.zig delete mode 100644 lib/std/os/ptrace.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index b0867c220b..3f5cf7cd6e 100644 --- a/CMakeLists.txt +++ b/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" diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index 9c5ac1e93a..75267cc171 100644 --- a/lib/std/c/darwin.zig +++ b/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() }; +} diff --git a/lib/std/os/darwin/cssm.zig b/lib/std/c/darwin/cssm.zig similarity index 100% rename from lib/std/os/darwin/cssm.zig rename to lib/std/c/darwin/cssm.zig diff --git a/lib/std/os.zig b/lib/std/os.zig index 722028b3f8..77995da034 100644 --- a/lib/std/os.zig +++ b/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), + }; +} diff --git a/lib/std/os/darwin.zig b/lib/std/os/darwin.zig deleted file mode 100644 index 164a0e06c2..0000000000 --- a/lib/std/os/darwin.zig +++ /dev/null @@ -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 {}; diff --git a/lib/std/os/ptrace.zig b/lib/std/os/ptrace.zig deleted file mode 100644 index afe0b51e2e..0000000000 --- a/lib/std/os/ptrace.zig +++ /dev/null @@ -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 {};