From 76bceb240d837d859378e01cc5a40b00cb6aacdb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 13 Mar 2022 17:03:04 +0100 Subject: [PATCH] std+macho: revert and fix exposing Mach wrappers in std.os and std.c --- lib/std/c.zig | 4 +- lib/std/macho.zig | 35 ++-- lib/std/os.zig | 7 +- lib/std/os/darwin.zig | 361 +++++++++++++++++++++--------------------- src/link/MachO.zig | 17 +- 5 files changed, 220 insertions(+), 204 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 9153eff994..396a6f6702 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -39,12 +39,10 @@ pub fn versionCheck(glibc_version: std.builtin.Version) type { }; } -pub const darwin = @import("c/darwin.zig"); - pub usingnamespace switch (builtin.os.tag) { .linux => @import("c/linux.zig"), .windows => @import("c/windows.zig"), - .macos, .ios, .tvos, .watchos => darwin, + .macos, .ios, .tvos, .watchos => @import("c/darwin.zig"), .freebsd, .kfreebsd => @import("c/freebsd.zig"), .netbsd => @import("c/netbsd.zig"), .dragonfly => @import("c/dragonfly.zig"), diff --git a/lib/std/macho.zig b/lib/std/macho.zig index 6a5033ead7..2e9ee4dfad 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -2,15 +2,15 @@ const std = @import("std"); const builtin = @import("builtin"); const assert = std.debug.assert; const io = std.io; -const c = std.c.darwin; const mem = std.mem; const meta = std.meta; const testing = std.testing; const Allocator = mem.Allocator; -pub const cpu_type_t = c.integer_t; -pub const cpu_subtype_t = c.integer_t; +pub const cpu_type_t = c_int; +pub const cpu_subtype_t = c_int; +pub const vm_prot_t = c_int; pub const mach_header = extern struct { magic: u32, @@ -605,10 +605,10 @@ pub const segment_command = extern struct { filesize: u32, /// maximum VM protection - maxprot: c.vm_prot_t, + maxprot: vm_prot_t, /// initial VM protection - initprot: c.vm_prot_t, + initprot: vm_prot_t, /// number of sections in segment nsects: u32, @@ -642,10 +642,10 @@ pub const segment_command_64 = extern struct { filesize: u64 = 0, /// maximum VM protection - maxprot: c.vm_prot_t = c.PROT.NONE, + maxprot: vm_prot_t = PROT.NONE, /// initial VM protection - initprot: c.vm_prot_t = c.PROT.NONE, + initprot: vm_prot_t = PROT.NONE, /// number of sections in segment nsects: u32 = 0, @@ -656,6 +656,23 @@ pub const segment_command_64 = extern struct { } }; +pub const PROT = struct { + /// [MC2] no permissions + pub const NONE: vm_prot_t = 0x00; + /// [MC2] pages can be read + pub const READ: vm_prot_t = 0x01; + /// [MC2] pages can be written + pub const WRITE: vm_prot_t = 0x02; + /// [MC2] pages can be executed + 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; +}; + /// A segment is made up of zero or more sections. Non-MH_OBJECT files have /// all of their segments with the proper sections in each, and padded to the /// specified segment alignment when produced by the link editor. The first @@ -2148,8 +2165,8 @@ test "read-write segment command" { .vmaddr = 4294967296, .vmsize = 294912, .filesize = 294912, - .maxprot = c.PROT.READ | c.PROT.WRITE | c.PROT.EXEC, - .initprot = c.PROT.EXEC | c.PROT.READ, + .maxprot = PROT.READ | PROT.WRITE | PROT.EXEC, + .initprot = PROT.EXEC | PROT.READ, .nsects = 1, }, }; diff --git a/lib/std/os.zig b/lib/std/os.zig index aafcd8bea6..ae3128654d 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -29,11 +29,7 @@ const Allocator = std.mem.Allocator; const Preopen = std.fs.wasi.Preopen; const PreopenList = std.fs.wasi.PreopenList; -pub const darwin = struct { - pub usingnamespace std.c; - pub usingnamespace @import("os/darwin.zig"); -}; - +pub const darwin = @import("os/darwin.zig"); pub const dragonfly = std.c; pub const freebsd = std.c; pub const haiku = std.c; @@ -51,6 +47,7 @@ comptime { } test { + _ = darwin; _ = linux; _ = uefi; _ = wasi; diff --git a/lib/std/os/darwin.zig b/lib/std/os/darwin.zig index d21d43b43a..c44caf7343 100644 --- a/lib/std/os/darwin.zig +++ b/lib/std/os/darwin.zig @@ -1,214 +1,219 @@ const std = @import("std"); -const c = std.c.darwin; +const builtin = @import("builtin"); const log = std.log; const mem = std.mem; -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 usingnamespace std.c; +pub usingnamespace mach_task; -pub const MachTask = struct { - port: c.mach_port_name_t, +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 fn isValid(self: MachTask) bool { - return self.port != 0; - } + pub const MachTask = struct { + port: std.c.mach_port_name_t, - pub fn getCurrProtection(task: MachTask, address: u64, len: usize) MachError!c.vm_prot_t { - var base_addr = address; - var base_len: c.mach_vm_size_t = if (len == 1) 2 else len; - var objname: c.mach_port_t = undefined; - var info: c.vm_region_submap_info_64 = undefined; - var count: c.mach_msg_type_number_t = c.VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; - switch (c.getKernError(c.mach_vm_region( - task.port, - &base_addr, - &base_len, - c.VM_REGION_BASIC_INFO_64, - @ptrCast(c.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 isValid(self: MachTask) bool { + return self.port != 0; } - } - pub fn setMaxProtection(task: MachTask, address: u64, len: usize, prot: c.vm_prot_t) MachError!void { - return task.setProtectionImpl(address, len, true, prot); - } - - pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: 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: c.vm_prot_t) MachError!void { - switch (c.getKernError(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, - c.PROT.READ | c.PROT.WRITE | 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 (c.getKernError(c.mach_vm_write( + pub fn getCurrProtection(task: MachTask, address: u64, len: usize) MachError!std.c.vm_prot_t { + var base_addr = address; + var base_len: std.c.mach_vm_size_t = if (len == 1) 2 else len; + var objname: std.c.mach_port_t = undefined; + var info: std.c.vm_region_submap_info_64 = undefined; + var count: std.c.mach_msg_type_number_t = std.c.VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; + switch (std.c.getKernError(std.c.mach_vm_region( task.port, - curr_addr, - @ptrToInt(out_buf.ptr), - @intCast(c.mach_msg_type_number_t, curr_size), + &base_addr, + &base_len, + std.c.VM_REGION_BASIC_INFO_64, + @ptrCast(std.c.vm_region_info_t, &info), + &count, + &objname, ))) { - .SUCCESS => {}, + .SUCCESS => return info.protection, .FAILURE => return error.PermissionDenied, else => |err| { - log.err("mach_vm_write kernel call failed with error code: {s}", .{@tagName(err)}); + log.err("mach_vm_region kernel call failed with error code: {s}", .{@tagName(err)}); return error.Unexpected; }, } - - switch (arch) { - .aarch64 => { - var mattr_value: c.vm_machine_attribute_val_t = c.MATTR_VAL_CACHE_FLUSH; - switch (c.getKernError(c.vm_machine_attribute( - task.port, - curr_addr, - curr_size, - 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 setMaxProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void { + return task.setProtectionImpl(address, len, true, prot); + } - 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..]; + pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void { + return task.setProtectionImpl(address, len, false, prot); + } - while (total_read < count) { - const curr_size = maxBytesLeftInPage(page_size, curr_addr, count - total_read); - var curr_bytes_read: c.mach_msg_type_number_t = 0; - var vm_memory: c.vm_offset_t = undefined; - switch (c.getKernError(c.mach_vm_read(task.port, curr_addr, curr_size, &vm_memory, &curr_bytes_read))) { - .SUCCESS => {}, + 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_read kernel call failed with error code: {s}", .{@tagName(err)}); + log.err("mach_vm_protect 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); - _ = c.vm_deallocate(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; - } + /// 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); + } - 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; + 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; + }, } } - return left; - } + }; - fn getPageSize(task: MachTask) MachError!usize { - if (task.isValid()) { - var info_count = c.TASK_VM_INFO_COUNT; - var vm_info: c.task_vm_info_data_t = undefined; - switch (c.getKernError(c.task_info( - task.port, - c.TASK_VM_INFO, - @ptrCast(c.task_info_t, &vm_info), - &info_count, - ))) { - .SUCCESS => return @intCast(usize, vm_info.page_size), - else => {}, - } - } - var page_size: c.vm_size_t = undefined; - switch (c.getKernError(c._host_page_size(c.mach_host_self(), &page_size))) { - .SUCCESS => return page_size, + 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("_host_page_size kernel call failed with error code: {s}", .{@tagName(err)}); + log.err("task_for_pid kernel call failed with error code: {s}", .{@tagName(err)}); return error.Unexpected; }, } + return MachTask{ .port = port }; } -}; - -pub fn machTaskForPid(pid: c.pid_t) MachError!MachTask { - var port: c.mach_port_name_t = undefined; - switch (c.getKernError(c.task_for_pid(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 }; -} +} else struct {}; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index c2e981a038..1a4bb804b8 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -4,7 +4,6 @@ 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); @@ -4344,8 +4343,8 @@ fn populateMissingMetadata(self: *MachO) !void { .vmaddr = pagezero_vmsize, .vmsize = needed_size, .filesize = needed_size, - .maxprot = darwin.PROT.READ | darwin.PROT.EXEC, - .initprot = darwin.PROT.READ | darwin.PROT.EXEC, + .maxprot = macho.PROT.READ | macho.PROT.EXEC, + .initprot = macho.PROT.READ | macho.PROT.EXEC, }, }, }); @@ -4449,8 +4448,8 @@ fn populateMissingMetadata(self: *MachO) !void { .vmsize = needed_size, .fileoff = fileoff, .filesize = needed_size, - .maxprot = darwin.PROT.READ | darwin.PROT.WRITE, - .initprot = darwin.PROT.READ | darwin.PROT.WRITE, + .maxprot = macho.PROT.READ | macho.PROT.WRITE, + .initprot = macho.PROT.READ | macho.PROT.WRITE, }, }, }); @@ -4498,8 +4497,8 @@ fn populateMissingMetadata(self: *MachO) !void { .vmsize = needed_size, .fileoff = fileoff, .filesize = needed_size, - .maxprot = darwin.PROT.READ | darwin.PROT.WRITE, - .initprot = darwin.PROT.READ | darwin.PROT.WRITE, + .maxprot = macho.PROT.READ | macho.PROT.WRITE, + .initprot = macho.PROT.READ | macho.PROT.WRITE, }, }, }); @@ -4607,8 +4606,8 @@ fn populateMissingMetadata(self: *MachO) !void { .segname = makeStaticString("__LINKEDIT"), .vmaddr = vmaddr, .fileoff = fileoff, - .maxprot = darwin.PROT.READ, - .initprot = darwin.PROT.READ, + .maxprot = macho.PROT.READ, + .initprot = macho.PROT.READ, }, }, });