mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
macos: add Mach task abstraction
`std.os.darwin.MachTask` wraps `mach_port_t` and can be used to issue kernel calls tied to the wrapped Mach kernel port/task.
This commit is contained in:
parent
2036af94e9
commit
633c4a2a60
@ -460,6 +460,7 @@ 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"
|
||||
|
||||
@ -201,9 +201,9 @@ pub const vm_object_id_t = u64;
|
||||
|
||||
pub const vm_region_submap_info_64 = extern struct {
|
||||
// present across protection
|
||||
protection: std.macho.vm_prot_t,
|
||||
protection: vm_prot_t,
|
||||
// max avail through vm_prot
|
||||
max_protection: std.macho.vm_prot_t,
|
||||
max_protection: vm_prot_t,
|
||||
// behavior of map/obj on fork
|
||||
inheritance: vm_inherit_t,
|
||||
// offset into object/map
|
||||
@ -391,6 +391,7 @@ pub const task_vm_info = extern struct {
|
||||
};
|
||||
pub const task_vm_info_data_t = task_vm_info;
|
||||
|
||||
pub const vm_prot_t = c_int;
|
||||
pub const boolean_t = c_int;
|
||||
|
||||
pub extern "c" fn mach_vm_protect(
|
||||
@ -398,7 +399,7 @@ pub extern "c" fn mach_vm_protect(
|
||||
address: mach_vm_address_t,
|
||||
size: mach_vm_size_t,
|
||||
set_maximum: boolean_t,
|
||||
new_protection: std.macho.vm_prot_t,
|
||||
new_protection: vm_prot_t,
|
||||
) kern_return_t;
|
||||
|
||||
pub extern "c" fn mach_port_deallocate(target_tport: mach_port_name_t, task: mach_port_name_t) kern_return_t;
|
||||
@ -920,13 +921,19 @@ pub const STDERR_FILENO = 2;
|
||||
|
||||
pub const PROT = struct {
|
||||
/// [MC2] no permissions
|
||||
pub const NONE = 0x00;
|
||||
pub const NONE: vm_prot_t = 0x00;
|
||||
/// [MC2] pages can be read
|
||||
pub const READ = 0x01;
|
||||
pub const READ: vm_prot_t = 0x01;
|
||||
/// [MC2] pages can be written
|
||||
pub const WRITE = 0x02;
|
||||
pub const WRITE: vm_prot_t = 0x02;
|
||||
/// [MC2] pages can be executed
|
||||
pub const EXEC = 0x04;
|
||||
pub const EXEC: vm_prot_t = 0x04;
|
||||
/// When a caller finds that they cannot obtain write permission on a
|
||||
/// mapped entry, the following flag can be used. The entry will be
|
||||
/// made "needs copy" effectively copying the object (using COW),
|
||||
/// and write permission will be added to the maximum protections for
|
||||
/// the associated entry.
|
||||
pub const COPY: vm_prot_t = 0x10;
|
||||
};
|
||||
|
||||
pub const MAP = struct {
|
||||
@ -1771,6 +1778,10 @@ pub const E = enum(u16) {
|
||||
_,
|
||||
};
|
||||
|
||||
pub fn getKernError(err: kern_return_t) KernE {
|
||||
return @intToEnum(KernE, err);
|
||||
}
|
||||
|
||||
/// Kernel return values
|
||||
pub const KernE = enum(u8) {
|
||||
SUCCESS = 0,
|
||||
|
||||
@ -2,12 +2,16 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const io = std.io;
|
||||
const os = std.os.darwin;
|
||||
const mem = std.mem;
|
||||
const meta = std.meta;
|
||||
const testing = std.testing;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
pub const cpu_type_t = os.integer_t;
|
||||
pub const cpu_subtype_t = os.integer_t;
|
||||
|
||||
pub const mach_header = extern struct {
|
||||
magic: u32,
|
||||
cputype: cpu_type_t,
|
||||
@ -601,10 +605,10 @@ pub const segment_command = extern struct {
|
||||
filesize: u32,
|
||||
|
||||
/// maximum VM protection
|
||||
maxprot: vm_prot_t,
|
||||
maxprot: os.vm_prot_t,
|
||||
|
||||
/// initial VM protection
|
||||
initprot: vm_prot_t,
|
||||
initprot: os.vm_prot_t,
|
||||
|
||||
/// number of sections in segment
|
||||
nsects: u32,
|
||||
@ -638,10 +642,10 @@ pub const segment_command_64 = extern struct {
|
||||
filesize: u64 = 0,
|
||||
|
||||
/// maximum VM protection
|
||||
maxprot: vm_prot_t = VM_PROT_NONE,
|
||||
maxprot: os.vm_prot_t = os.PROT.NONE,
|
||||
|
||||
/// initial VM protection
|
||||
initprot: vm_prot_t = VM_PROT_NONE,
|
||||
initprot: os.vm_prot_t = os.PROT.NONE,
|
||||
|
||||
/// number of sections in segment
|
||||
nsects: u32 = 0,
|
||||
@ -1438,11 +1442,6 @@ pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15;
|
||||
/// 32-bit offsets to initializers
|
||||
pub const S_INIT_FUNC_OFFSETS = 0x16;
|
||||
|
||||
pub const cpu_type_t = integer_t;
|
||||
pub const cpu_subtype_t = integer_t;
|
||||
pub const integer_t = c_int;
|
||||
pub const vm_prot_t = c_int;
|
||||
|
||||
/// CPU type targeting 64-bit Intel-based Macs
|
||||
pub const CPU_TYPE_X86_64: cpu_type_t = 0x01000007;
|
||||
|
||||
@ -1455,26 +1454,6 @@ pub const CPU_SUBTYPE_X86_64_ALL: cpu_subtype_t = 0x3;
|
||||
/// All ARM-based Macs
|
||||
pub const CPU_SUBTYPE_ARM_ALL: cpu_subtype_t = 0x0;
|
||||
|
||||
// Protection values defined as bits within the vm_prot_t type
|
||||
/// No VM protection
|
||||
pub const VM_PROT_NONE: vm_prot_t = 0x0;
|
||||
|
||||
/// VM read permission
|
||||
pub const VM_PROT_READ: vm_prot_t = 0x1;
|
||||
|
||||
/// VM write permission
|
||||
pub const VM_PROT_WRITE: vm_prot_t = 0x2;
|
||||
|
||||
/// VM execute permission
|
||||
pub const VM_PROT_EXECUTE: vm_prot_t = 0x4;
|
||||
|
||||
/// When a caller finds that they cannot obtain write permission on a
|
||||
/// mapped entry, the following flag can be used. The entry will be
|
||||
/// made "needs copy" effectively copying the object (using COW),
|
||||
/// and write permission will be added to the maximum protections for
|
||||
/// the associated entry.
|
||||
pub const VM_PROT_COPY: vm_prot_t = 0x10;
|
||||
|
||||
// The following are used to encode rebasing information
|
||||
pub const REBASE_TYPE_POINTER: u8 = 1;
|
||||
pub const REBASE_TYPE_TEXT_ABSOLUTE32: u8 = 2;
|
||||
@ -2169,8 +2148,8 @@ test "read-write segment command" {
|
||||
.vmaddr = 4294967296,
|
||||
.vmsize = 294912,
|
||||
.filesize = 294912,
|
||||
.maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE,
|
||||
.initprot = VM_PROT_EXECUTE | VM_PROT_READ,
|
||||
.maxprot = os.PROT.READ | os.PROT.WRITE | os.PROT.EXEC,
|
||||
.initprot = os.PROT.EXEC | os.PROT.READ,
|
||||
.nsects = 1,
|
||||
},
|
||||
};
|
||||
|
||||
@ -29,7 +29,7 @@ const Allocator = std.mem.Allocator;
|
||||
const Preopen = std.fs.wasi.Preopen;
|
||||
const PreopenList = std.fs.wasi.PreopenList;
|
||||
|
||||
pub const darwin = std.c;
|
||||
pub const darwin = @import("os/darwin.zig");
|
||||
pub const dragonfly = std.c;
|
||||
pub const freebsd = std.c;
|
||||
pub const haiku = std.c;
|
||||
|
||||
216
lib/std/os/darwin.zig
Normal file
216
lib/std/os/darwin.zig
Normal file
@ -0,0 +1,216 @@
|
||||
const std = @import("std");
|
||||
const log = std.log;
|
||||
const mem = std.mem;
|
||||
const os = @This();
|
||||
|
||||
pub usingnamespace @import("../c.zig");
|
||||
|
||||
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: os.mach_port_name_t,
|
||||
|
||||
pub fn isValid(self: MachTask) bool {
|
||||
return self.port != 0;
|
||||
}
|
||||
|
||||
pub fn getCurrProtection(task: MachTask, address: u64, len: usize) MachError!os.vm_prot_t {
|
||||
var base_addr = address;
|
||||
var base_len: os.mach_vm_size_t = if (len == 1) 2 else len;
|
||||
var objname: os.mach_port_t = undefined;
|
||||
var info: os.vm_region_submap_info_64 = undefined;
|
||||
var count: os.mach_msg_type_number_t = os.VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
|
||||
switch (os.getKernError(os.mach_vm_region(
|
||||
task.port,
|
||||
&base_addr,
|
||||
&base_len,
|
||||
os.VM_REGION_BASIC_INFO_64,
|
||||
@ptrCast(os.vm_region_info_t, &info),
|
||||
&count,
|
||||
&objname,
|
||||
))) {
|
||||
.SUCCESS => return info.protection,
|
||||
.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 setMaxProtection(task: MachTask, address: u64, len: usize, prot: os.vm_prot_t) MachError!void {
|
||||
return task.setProtectionImpl(address, len, true, prot);
|
||||
}
|
||||
|
||||
pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: os.vm_prot_t) MachError!void {
|
||||
return task.setProtectionImpl(address, len, false, prot);
|
||||
}
|
||||
|
||||
fn setProtectionImpl(task: MachTask, address: u64, len: usize, set_max: bool, prot: os.vm_prot_t) MachError!void {
|
||||
switch (os.getKernError(os.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,
|
||||
os.PROT.READ | os.PROT.WRITE | os.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 (os.getKernError(os.mach_vm_write(
|
||||
task.port,
|
||||
curr_addr,
|
||||
@ptrToInt(out_buf.ptr),
|
||||
@intCast(os.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: os.vm_machine_attribute_val_t = os.MATTR_VAL_CACHE_FLUSH;
|
||||
switch (os.getKernError(os.vm_machine_attribute(
|
||||
task.port,
|
||||
curr_addr,
|
||||
curr_size,
|
||||
os.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: os.mach_msg_type_number_t = 0;
|
||||
var vm_memory: os.vm_offset_t = undefined;
|
||||
switch (os.getKernError(os.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);
|
||||
_ = os.vm_deallocate(os.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 = os.TASK_VM_INFO_COUNT;
|
||||
var vm_info: os.task_vm_info_data_t = undefined;
|
||||
switch (os.getKernError(os.task_info(
|
||||
task.port,
|
||||
os.TASK_VM_INFO,
|
||||
@ptrCast(os.task_info_t, &vm_info),
|
||||
&info_count,
|
||||
))) {
|
||||
.SUCCESS => return @intCast(usize, vm_info.page_size),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
var page_size: os.vm_size_t = undefined;
|
||||
switch (os.getKernError(os._host_page_size(os.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: os.pid_t) MachError!MachTask {
|
||||
var port: os.mach_port_name_t = undefined;
|
||||
switch (os.getKernError(os.task_for_pid(os.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 };
|
||||
}
|
||||
@ -4,6 +4,7 @@ const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const darwin = std.os.darwin;
|
||||
const fmt = std.fmt;
|
||||
const fs = std.fs;
|
||||
const log = std.log.scoped(.link);
|
||||
@ -4382,8 +4383,8 @@ fn populateMissingMetadata(self: *MachO) !void {
|
||||
.vmaddr = pagezero_vmsize,
|
||||
.vmsize = needed_size,
|
||||
.filesize = needed_size,
|
||||
.maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
|
||||
.initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
|
||||
.maxprot = darwin.PROT.READ | darwin.PROT.EXEC,
|
||||
.initprot = darwin.PROT.READ | darwin.PROT.EXEC,
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -4487,8 +4488,8 @@ fn populateMissingMetadata(self: *MachO) !void {
|
||||
.vmsize = needed_size,
|
||||
.fileoff = fileoff,
|
||||
.filesize = needed_size,
|
||||
.maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
|
||||
.initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
|
||||
.maxprot = darwin.PROT.READ | darwin.PROT.WRITE,
|
||||
.initprot = darwin.PROT.READ | darwin.PROT.WRITE,
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -4536,8 +4537,8 @@ fn populateMissingMetadata(self: *MachO) !void {
|
||||
.vmsize = needed_size,
|
||||
.fileoff = fileoff,
|
||||
.filesize = needed_size,
|
||||
.maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
|
||||
.initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
|
||||
.maxprot = darwin.PROT.READ | darwin.PROT.WRITE,
|
||||
.initprot = darwin.PROT.READ | darwin.PROT.WRITE,
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -4645,8 +4646,8 @@ fn populateMissingMetadata(self: *MachO) !void {
|
||||
.segname = makeStaticString("__LINKEDIT"),
|
||||
.vmaddr = vmaddr,
|
||||
.fileoff = fileoff,
|
||||
.maxprot = macho.VM_PROT_READ,
|
||||
.initprot = macho.VM_PROT_READ,
|
||||
.maxprot = darwin.PROT.READ,
|
||||
.initprot = darwin.PROT.READ,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user