Commit 9b4cae4750

Justus Klausecker <justus@klausecker.de>
2025-09-13 14:12:56
std.posix.ptrace: support more platforms more correctly
1 parent bfda12e
lib/std/c/darwin.zig
@@ -4,6 +4,7 @@ const native_arch = builtin.target.cpu.arch;
 const assert = std.debug.assert;
 const AF = std.c.AF;
 const PROT = std.c.PROT;
+const caddr_t = std.c.caddr_t;
 const fd_t = std.c.fd_t;
 const iovec_const = std.posix.iovec_const;
 const mode_t = std.c.mode_t;
@@ -1318,8 +1319,6 @@ pub const PT = enum(c_int) {
     _,
 };
 
-pub const caddr_t = ?[*]u8;
-
 pub extern "c" fn ptrace(request: PT, pid: pid_t, addr: caddr_t, data: c_int) c_int;
 
 pub const POSIX_SPAWN = packed struct(c_short) {
lib/std/c/dragonfly.zig
@@ -1,6 +1,7 @@
 const std = @import("../std.zig");
 
 const SIG = std.c.SIG;
+const caddr_t = std.c.caddr_t;
 const gid_t = std.c.gid_t;
 const iovec = std.c.iovec;
 const pid_t = std.c.pid_t;
@@ -8,6 +9,7 @@ const socklen_t = std.c.socklen_t;
 const uid_t = std.c.uid_t;
 
 pub extern "c" fn lwp_gettid() c_int;
+pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int;
 pub extern "c" fn umtx_sleep(ptr: *const volatile c_int, value: c_int, timeout: c_int) c_int;
 pub extern "c" fn umtx_wakeup(ptr: *const volatile c_int, count: c_int) c_int;
 
lib/std/c/freebsd.zig
@@ -5,6 +5,7 @@ const assert = std.debug.assert;
 const PATH_MAX = std.c.PATH_MAX;
 const blkcnt_t = std.c.blkcnt_t;
 const blksize_t = std.c.blksize_t;
+const caddr_t = std.c.caddr_t;
 const dev_t = std.c.dev_t;
 const fd_t = std.c.fd_t;
 const gid_t = std.c.gid_t;
@@ -25,6 +26,8 @@ comptime {
     assert(builtin.os.tag == .freebsd); // Prevent access of std.c symbols on wrong OS.
 }
 
+pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int;
+
 pub extern "c" fn kinfo_getfile(pid: pid_t, cntp: *c_int) ?[*]kinfo_file;
 pub extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*off_t, fd_out: fd_t, off_out: ?*off_t, len: usize, flags: u32) usize;
 
lib/std/c/netbsd.zig
@@ -5,6 +5,8 @@ const pthread_t = std.c.pthread_t;
 const sigval_t = std.c.sigval_t;
 const uid_t = std.c.uid_t;
 
+pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: ?*anyopaque, data: c_int) c_int;
+
 pub const lwpid_t = i32;
 
 pub extern "c" fn _lwp_self() lwpid_t;
lib/std/c/openbsd.zig
@@ -2,6 +2,7 @@ const std = @import("../std.zig");
 const assert = std.debug.assert;
 const maxInt = std.math.maxInt;
 const builtin = @import("builtin");
+const caddr_t = std.c.caddr_t;
 const iovec = std.posix.iovec;
 const iovec_const = std.posix.iovec_const;
 const passwd = std.c.passwd;
@@ -13,6 +14,8 @@ comptime {
     assert(builtin.os.tag == .openbsd); // Prevent access of std.c symbols on wrong OS.
 }
 
+pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) c_int;
+
 pub const pthread_spinlock_t = extern struct {
     inner: ?*anyopaque = null,
 };
lib/std/c.zig
@@ -10856,6 +10856,18 @@ pub const pthread_threadid_np = switch (native_os) {
     else => {},
 };
 
+pub const caddr_t = ?[*]u8;
+
+pub const ptrace = switch (native_os) {
+    .linux, .serenity => private.ptrace,
+    .macos, .ios, .tvos, .watchos, .visionos => darwin.ptrace,
+    .dragonfly => dragonfly.ptrace,
+    .freebsd => freebsd.ptrace,
+    .netbsd => netbsd.ptrace,
+    .openbsd => openbsd.ptrace,
+    else => {},
+};
+
 pub extern "c" fn sem_init(sem: *sem_t, pshared: c_int, value: c_uint) c_int;
 pub extern "c" fn sem_destroy(sem: *sem_t) c_int;
 pub extern "c" fn sem_open(name: [*:0]const u8, flag: c_int, mode: mode_t, value: c_uint) *sem_t;
@@ -11305,7 +11317,6 @@ pub const pthread_attr_get_qos_class_np = darwin.pthread_attr_get_qos_class_np;
 pub const pthread_attr_set_qos_class_np = darwin.pthread_attr_set_qos_class_np;
 pub const pthread_get_qos_class_np = darwin.pthread_get_qos_class_np;
 pub const pthread_set_qos_class_self_np = darwin.pthread_set_qos_class_self_np;
-pub const ptrace = darwin.ptrace;
 pub const qos_class_t = darwin.qos_class_t;
 pub const task_flavor_t = darwin.task_flavor_t;
 pub const task_for_pid = darwin.task_for_pid;
@@ -11341,7 +11352,6 @@ pub const vm_region_submap_info_64 = darwin.vm_region_submap_info_64;
 pub const vm_region_submap_short_info_64 = darwin.vm_region_submap_short_info_64;
 pub const vm_region_top_info = darwin.vm_region_top_info;
 
-pub const caddr_t = darwin.caddr_t;
 pub const exception_behavior_array_t = darwin.exception_behavior_array_t;
 pub const exception_behavior_t = darwin.exception_behavior_t;
 pub const exception_data_t = darwin.exception_data_t;
@@ -11466,6 +11476,9 @@ const private = struct {
     extern "c" fn mlockall(flags: MCL) c_int;
     extern "c" fn munlockall() c_int;
 
+    // linux and https://github.com/SerenityOS/serenity/blob/502caef9a40bccc7459f9835f2174a601106299a/Userland/Libraries/LibC/sys/ptrace.cpp
+    extern "c" fn ptrace(request: c_int, pid: pid_t, addr: ?*anyopaque, data: ?*anyopaque) c_long;
+
     /// macos modernized symbols.
     /// x86_64 links to $INODE64 suffix for 64-bit support.
     /// Note these are not necessary on aarch64.
lib/std/posix.zig
@@ -7377,18 +7377,33 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!system.itimerspec {
 }
 
 pub const PtraceError = error{
+    DeadLock,
     DeviceBusy,
     InputOutput,
+    NameTooLong,
+    OperationNotSupported,
+    OutOfMemory,
     ProcessNotFound,
     PermissionDenied,
 } || UnexpectedError;
 
-pub fn ptrace(request: u32, pid: pid_t, addr: usize, signal: usize) PtraceError!void {
-    if (native_os == .windows or native_os == .wasi)
-        @compileError("Unsupported OS");
-
+pub fn ptrace(request: u32, pid: pid_t, addr: usize, data: usize) PtraceError!void {
     return switch (native_os) {
-        .linux => switch (errno(linux.ptrace(request, pid, addr, signal, 0))) {
+        .windows,
+        .wasi,
+        .emscripten,
+        .haiku,
+        .solaris,
+        .illumos,
+        .plan9,
+        => @compileError("ptrace unsupported by target OS"),
+
+        .linux => switch (errno(if (builtin.link_libc) std.c.ptrace(
+            @intCast(request),
+            pid,
+            @ptrFromInt(addr),
+            @ptrFromInt(data),
+        ) else linux.ptrace(request, pid, addr, data, 0))) {
             .SUCCESS => {},
             .SRCH => error.ProcessNotFound,
             .FAULT => unreachable,
@@ -7400,27 +7415,80 @@ pub fn ptrace(request: u32, pid: pid_t, addr: usize, signal: usize) PtraceError!
         },
 
         .macos, .ios, .tvos, .watchos, .visionos => switch (errno(std.c.ptrace(
+            @enumFromInt(request),
+            pid,
+            @ptrFromInt(addr),
+            @intCast(data),
+        ))) {
+            .SUCCESS => {},
+            .SRCH => error.ProcessNotFound,
+            .INVAL => unreachable,
+            .PERM => error.PermissionDenied,
+            .BUSY => error.DeviceBusy,
+            else => |err| return unexpectedErrno(err),
+        },
+
+        .dragonfly => switch (errno(std.c.ptrace(
+            @intCast(request),
+            pid,
+            @ptrFromInt(addr),
+            @intCast(data),
+        ))) {
+            .SUCCESS => {},
+            .SRCH => error.ProcessNotFound,
+            .INVAL => unreachable,
+            .PERM => error.PermissionDenied,
+            .BUSY => error.DeviceBusy,
+            else => |err| return unexpectedErrno(err),
+        },
+
+        .freebsd => switch (errno(std.c.ptrace(
+            @intCast(request),
+            pid,
+            @ptrFromInt(addr),
+            @intCast(data),
+        ))) {
+            .SUCCESS => {},
+            .SRCH => error.ProcessNotFound,
+            .INVAL => unreachable,
+            .PERM => error.PermissionDenied,
+            .BUSY => error.DeviceBusy,
+            .NOENT, .NOMEM => error.OutOfMemory,
+            .NAMETOOLONG => error.NameTooLong,
+            else => |err| return unexpectedErrno(err),
+        },
+
+        .netbsd => switch (errno(std.c.ptrace(
             @intCast(request),
             pid,
             @ptrFromInt(addr),
-            @intCast(signal),
+            @intCast(data),
         ))) {
             .SUCCESS => {},
             .SRCH => error.ProcessNotFound,
             .INVAL => unreachable,
             .PERM => error.PermissionDenied,
             .BUSY => error.DeviceBusy,
+            .DEADLK => error.DeadLock,
             else => |err| return unexpectedErrno(err),
         },
 
-        else => switch (errno(system.ptrace(request, pid, addr, signal))) {
+        .openbsd => switch (errno(std.c.ptrace(
+            @intCast(request),
+            pid,
+            @ptrFromInt(addr),
+            @intCast(data),
+        ))) {
             .SUCCESS => {},
             .SRCH => error.ProcessNotFound,
             .INVAL => unreachable,
             .PERM => error.PermissionDenied,
             .BUSY => error.DeviceBusy,
+            .NOTSUP => error.OperationNotSupported,
             else => |err| return unexpectedErrno(err),
         },
+
+        else => @compileError("std.posix.ptrace unimplemented for target OS"),
     };
 }