mirror of
https://github.com/ziglang/zig.git
synced 2026-02-03 21:23:36 +00:00
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.
This commit is contained in:
parent
bd242ce1ce
commit
e1e414e62a
@ -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"
|
||||
|
||||
@ -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() };
|
||||
}
|
||||
|
||||
@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
@ -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 {};
|
||||
@ -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 {};
|
||||
Loading…
x
Reference in New Issue
Block a user