Commit 76bceb240d

Jakub Konka <kubkon@jakubkonka.com>
2022-03-13 17:03:04
std+macho: revert and fix exposing Mach wrappers in std.os and std.c
1 parent 4a2100e
Changed files (5)
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 const MachTask = struct {
-    port: c.mach_port_name_t,
-
-    pub fn isValid(self: MachTask) bool {
-        return self.port != 0;
-    }
-
-    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 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 {};
+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;
         }
-        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;
+        pub fn setMaxProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void {
+            return task.setProtectionImpl(address, len, true, prot);
         }
 
-        return total_written;
-    }
+        pub fn setCurrProtection(task: MachTask, address: u64, len: usize, prot: std.c.vm_prot_t) MachError!void {
+            return task.setProtectionImpl(address, len, false, 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..];
-
-        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;
                 },
             }
+        }
+
+        /// 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);
+        }
 
-            @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);
+        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;
+            }
 
-            out_buf = out_buf[curr_bytes_read..];
-            curr_addr += curr_bytes_read;
-            total_read += curr_bytes_read;
+            return total_written;
         }
 
-        return total_read;
-    }
+        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;
+            }
 
-    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 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;
         }
-        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 => {},
+        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;
+                },
             }
         }
-        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 {};
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"),
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,
         },
     };
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;
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,
                 },
             },
         });