mirror of
https://github.com/ziglang/zig.git
synced 2024-11-16 09:03:12 +00:00
326 lines
13 KiB
Zig
326 lines
13 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const log = std.log;
|
|
const mem = std.mem;
|
|
|
|
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 = struct {
|
|
port: std.c.mach_port_name_t,
|
|
|
|
pub fn isValid(self: MachTask) bool {
|
|
return self.port != 0;
|
|
}
|
|
|
|
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 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 {};
|