aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2022-12-14 17:42:02 +0100
committerGitHub <noreply@github.com>2022-12-14 17:42:02 +0100
commitb27b17e2533505955622ea9950aaacb87f87c4fb (patch)
treed0c06156cf9272dbce9c5f156197dd16c105de34 /lib/std
parent0b4461d97b5d315e71302f96076c54bbdffb7717 (diff)
parentec2697b7ea355281330df0bab91c4a09f6466950 (diff)
downloadzig-b27b17e2533505955622ea9950aaacb87f87c4fb.tar.gz
zig-b27b17e2533505955622ea9950aaacb87f87c4fb.zip
Merge pull request #13935 from ziglang/mach-tasks
darwin: expose more Mach primitives, expose wrapped ptrace, and allow starting suspended for posix_spawn
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/c/darwin.zig397
-rw-r--r--lib/std/c/darwin/aarch64.zig20
-rw-r--r--lib/std/c/darwin/x86_64.zig27
-rw-r--r--lib/std/child_process.zig6
-rw-r--r--lib/std/os.zig1
-rw-r--r--lib/std/os/darwin.zig217
-rw-r--r--lib/std/os/ptrace.zig28
7 files changed, 690 insertions, 6 deletions
diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig
index 5c0338af80..b68f04379f 100644
--- a/lib/std/c/darwin.zig
+++ b/lib/std/c/darwin.zig
@@ -15,6 +15,131 @@ const arch_bits = switch (native_arch) {
else => struct {},
};
+pub const EXC_TYPES_COUNT = arch_bits.EXC_TYPES_COUNT;
+pub const THREAD_STATE_NONE = arch_bits.THREAD_STATE_NONE;
+
+pub const EXC = enum(exception_type_t) {
+ NULL = 0,
+ /// Could not access memory
+ BAD_ACCESS = 1,
+ /// Instruction failed
+ BAD_INSTRUCTION = 2,
+ /// Arithmetic exception
+ ARITHMETIC = 3,
+ /// Emulation instruction
+ EMULATION = 4,
+ /// Software generated exception
+ SOFTWARE = 5,
+ /// Trace, breakpoint, etc.
+ BREAKPOINT = 6,
+ /// System calls.
+ SYSCALL = 7,
+ /// Mach system calls.
+ MACH_SYSCALL = 8,
+ /// RPC alert
+ RPC_ALERT = 9,
+ /// Abnormal process exit
+ CRASH = 10,
+ /// Hit resource consumption limit
+ RESOURCE = 11,
+ /// Violated guarded resource protections
+ GUARD = 12,
+ /// Abnormal process exited to corpse state
+ CORPSE_NOTIFY = 13,
+};
+
+pub const EXC_SOFT_SIGNAL = 0x10003;
+
+pub const EXC_MASK_BAD_ACCESS = 1 << @enumToInt(EXC.BAD_ACCESS);
+pub const EXC_MASK_BAD_INSTRUCTION = 1 << @enumToInt(EXC.BAD_INSTRUCTION);
+pub const EXC_MASK_ARITHMETIC = 1 << @enumToInt(EXC.ARITHMETIC);
+pub const EXC_MASK_EMULATION = 1 << @enumToInt(EXC.EMULATION);
+pub const EXC_MASK_SOFTWARE = 1 << @enumToInt(EXC.SOFTWARE);
+pub const EXC_MASK_BREAKPOINT = 1 << @enumToInt(EXC.BREAKPOINT);
+pub const EXC_MASK_SYSCALL = 1 << @enumToInt(EXC.SYSCALL);
+pub const EXC_MASK_MACH_SYSCALL = 1 << @enumToInt(EXC.MACH_SYSCALL);
+pub const EXC_MASK_RPC_ALERT = 1 << @enumToInt(EXC.RPC_ALERT);
+pub const EXC_MASK_CRASH = 1 << @enumToInt(EXC.CRASH);
+pub const EXC_MASK_RESOURCE = 1 << @enumToInt(EXC.RESOURCE);
+pub const EXC_MASK_GUARD = 1 << @enumToInt(EXC.GUARD);
+pub const EXC_MASK_CORPSE_NOTIFY = 1 << @enumToInt(EXC.CORPSE_NOTIFY);
+pub const EXC_MASK_MACHINE = arch_bits.EXC_MASK_MACHINE;
+
+pub const EXC_MASK_ALL = EXC_MASK_BAD_ACCESS |
+ EXC_MASK_BAD_INSTRUCTION |
+ EXC_MASK_ARITHMETIC |
+ EXC_MASK_EMULATION |
+ EXC_MASK_SOFTWARE |
+ EXC_MASK_BREAKPOINT |
+ EXC_MASK_SYSCALL |
+ EXC_MASK_MACH_SYSCALL |
+ EXC_MASK_RPC_ALERT |
+ EXC_MASK_RESOURCE |
+ EXC_MASK_GUARD |
+ EXC_MASK_MACHINE;
+
+/// Send a catch_exception_raise message including the identity.
+pub const EXCEPTION_DEFAULT = 1;
+/// Send a catch_exception_raise_state message including the
+/// thread state.
+pub const EXCEPTION_STATE = 2;
+/// Send a catch_exception_raise_state_identity message including
+/// the thread identity and state.
+pub const EXCEPTION_STATE_IDENTITY = 3;
+/// Send a catch_exception_raise_identity_protected message including protected task
+/// and thread identity.
+pub const EXCEPTION_IDENTITY_PROTECTED = 4;
+/// Prefer sending a catch_exception_raice_backtrace message, if applicable.
+pub const MACH_EXCEPTION_BACKTRACE_PREFERRED = 0x20000000;
+/// include additional exception specific errors, not used yet.
+pub const MACH_EXCEPTION_ERRORS = 0x40000000;
+/// Send 64-bit code and subcode in the exception header */
+pub const MACH_EXCEPTION_CODES = 0x80000000;
+
+pub const MACH_EXCEPTION_MASK = MACH_EXCEPTION_CODES |
+ MACH_EXCEPTION_ERRORS |
+ MACH_EXCEPTION_BACKTRACE_PREFERRED;
+
+pub const TASK_NULL: task_t = 0;
+pub const THREAD_NULL: thread_t = 0;
+pub const MACH_PORT_NULL: mach_port_t = 0;
+pub const MACH_MSG_TIMEOUT_NONE: mach_msg_timeout_t = 0;
+
+pub const MACH_MSG_OPTION_NONE = 0x00000000;
+
+pub const MACH_SEND_MSG = 0x00000001;
+pub const MACH_RCV_MSG = 0x00000002;
+
+pub const MACH_RCV_LARGE = 0x00000004;
+pub const MACH_RCV_LARGE_IDENTITY = 0x00000008;
+
+pub const MACH_SEND_TIMEOUT = 0x00000010;
+pub const MACH_SEND_OVERRIDE = 0x00000020;
+pub const MACH_SEND_INTERRUPT = 0x00000040;
+pub const MACH_SEND_NOTIFY = 0x00000080;
+pub const MACH_SEND_ALWAYS = 0x00010000;
+pub const MACH_SEND_FILTER_NONFATAL = 0x00010000;
+pub const MACH_SEND_TRAILER = 0x00020000;
+pub const MACH_SEND_NOIMPORTANCE = 0x00040000;
+pub const MACH_SEND_NODENAP = MACH_SEND_NOIMPORTANCE;
+pub const MACH_SEND_IMPORTANCE = 0x00080000;
+pub const MACH_SEND_SYNC_OVERRIDE = 0x00100000;
+pub const MACH_SEND_PROPAGATE_QOS = 0x00200000;
+pub const MACH_SEND_SYNC_USE_THRPRI = MACH_SEND_PROPAGATE_QOS;
+pub const MACH_SEND_KERNEL = 0x00400000;
+pub const MACH_SEND_SYNC_BOOTSTRAP_CHECKIN = 0x00800000;
+
+pub const MACH_RCV_TIMEOUT = 0x00000100;
+pub const MACH_RCV_NOTIFY = 0x00000000;
+pub const MACH_RCV_INTERRUPT = 0x00000400;
+pub const MACH_RCV_VOUCHER = 0x00000800;
+pub const MACH_RCV_OVERWRITE = 0x00000000;
+pub const MACH_RCV_GUARDED_DESC = 0x00001000;
+pub const MACH_RCV_SYNC_WAIT = 0x00004000;
+pub const MACH_RCV_SYNC_PEEK = 0x00008000;
+
+pub const MACH_MSG_STRICT_REPLY = 0x00000200;
+
pub const ucontext_t = extern struct {
onstack: c_int,
sigmask: sigset_t,
@@ -155,6 +280,11 @@ pub extern "c" fn @"close$NOCANCEL"(fd: fd_t) c_int;
pub extern "c" fn mach_host_self() mach_port_t;
pub extern "c" fn clock_get_time(clock_serv: clock_serv_t, cur_time: *mach_timespec_t) kern_return_t;
+pub const exception_type_t = c_int;
+pub const exception_data_type_t = integer_t;
+pub const exception_data_t = ?*mach_exception_data_type_t;
+pub const mach_exception_data_type_t = i64;
+pub const mach_exception_data_t = ?*mach_exception_data_type_t;
pub const vm_map_t = mach_port_t;
pub const vm_map_read_t = mach_port_t;
pub const vm_region_flavor_t = c_int;
@@ -163,19 +293,122 @@ pub const vm_region_recurse_info_t = *c_int;
pub const mach_vm_address_t = usize;
pub const vm_offset_t = usize;
pub const mach_vm_size_t = u64;
+pub const mach_msg_bits_t = c_uint;
+pub const mach_msg_id_t = integer_t;
pub const mach_msg_type_number_t = natural_t;
+pub const mach_msg_type_name_t = c_uint;
+pub const mach_msg_option_t = integer_t;
+pub const mach_msg_size_t = natural_t;
+pub const mach_msg_timeout_t = natural_t;
+pub const mach_port_right_t = natural_t;
+pub const task_t = mach_port_t;
+pub const thread_port_t = task_t;
+pub const thread_t = thread_port_t;
+pub const exception_mask_t = c_uint;
+pub const exception_mask_array_t = [*]exception_mask_t;
+pub const exception_handler_t = mach_port_t;
+pub const exception_handler_array_t = [*]exception_handler_t;
+pub const exception_port_t = exception_handler_t;
+pub const exception_port_array_t = exception_handler_array_t;
+pub const exception_flavor_array_t = [*]thread_state_flavor_t;
+pub const exception_behavior_t = c_uint;
+pub const exception_behavior_array_t = [*]exception_behavior_t;
+pub const thread_state_flavor_t = c_int;
+pub const ipc_space_t = mach_port_t;
+pub const ipc_space_port_t = ipc_space_t;
+
+pub const MACH_PORT_RIGHT = enum(mach_port_right_t) {
+ SEND = 0,
+ RECEIVE = 1,
+ SEND_ONCE = 2,
+ PORT_SET = 3,
+ DEAD_NAME = 4,
+ /// Obsolete right
+ LABELH = 5,
+ /// Right not implemented
+ NUMBER = 6,
+};
+
+pub const MACH_MSG_TYPE = enum(mach_msg_type_name_t) {
+ /// Must hold receive right
+ MOVE_RECEIVE = 16,
+
+ /// Must hold send right(s)
+ MOVE_SEND = 17,
+
+ /// Must hold sendonce right
+ MOVE_SEND_ONCE = 18,
+
+ /// Must hold send right(s)
+ COPY_SEND = 19,
+
+ /// Must hold receive right
+ MAKE_SEND = 20,
+
+ /// Must hold receive right
+ MAKE_SEND_ONCE = 21,
+
+ /// NOT VALID
+ COPY_RECEIVE = 22,
+
+ /// Must hold receive right
+ DISPOSE_RECEIVE = 24,
+
+ /// Must hold send right(s)
+ DISPOSE_SEND = 25,
+
+ /// Must hold sendonce right
+ DISPOSE_SEND_ONCE = 26,
+};
extern "c" var mach_task_self_: mach_port_t;
pub fn mach_task_self() callconv(.C) mach_port_t {
return mach_task_self_;
}
+pub extern "c" fn mach_msg(
+ msg: ?*mach_msg_header_t,
+ option: mach_msg_option_t,
+ send_size: mach_msg_size_t,
+ rcv_size: mach_msg_size_t,
+ rcv_name: mach_port_name_t,
+ timeout: mach_msg_timeout_t,
+ notify: mach_port_name_t,
+) kern_return_t;
+
+pub const mach_msg_header_t = extern struct {
+ msgh_bits: mach_msg_bits_t,
+ msgh_size: mach_msg_size_t,
+ msgh_remote_port: mach_port_t,
+ msgh_local_port: mach_port_t,
+ msgh_voucher_port: mach_port_name_t,
+ msgh_id: mach_msg_id_t,
+};
+
+pub extern "c" fn task_get_exception_ports(
+ task: task_t,
+ exception_mask: exception_mask_t,
+ masks: exception_mask_array_t,
+ masks_cnt: *mach_msg_type_number_t,
+ old_handlers: exception_handler_array_t,
+ old_behaviors: exception_behavior_array_t,
+ old_flavors: exception_flavor_array_t,
+) kern_return_t;
+pub extern "c" fn task_set_exception_ports(
+ task: task_t,
+ exception_mask: exception_mask_t,
+ new_port: mach_port_t,
+ behavior: exception_behavior_t,
+ new_flavor: thread_state_flavor_t,
+) kern_return_t;
+
pub const task_read_t = mach_port_t;
pub extern "c" fn task_resume(target_task: task_read_t) kern_return_t;
pub extern "c" fn task_suspend(target_task: task_read_t) kern_return_t;
pub extern "c" fn task_for_pid(target_tport: mach_port_name_t, pid: pid_t, t: *mach_port_name_t) kern_return_t;
+pub extern "c" fn pid_for_task(target_tport: mach_port_name_t, pid: *pid_t) kern_return_t;
pub extern "c" fn mach_vm_read(
target_task: vm_map_read_t,
address: mach_vm_address_t,
@@ -343,7 +576,7 @@ pub const vm_region_submap_short_info_64 = extern struct {
pub const thread_act_t = mach_port_t;
pub const thread_state_t = *natural_t;
-pub const mach_port_array_t = *mach_port_t;
+pub const mach_port_array_t = [*]mach_port_t;
pub extern "c" fn task_threads(
target_task: mach_port_t,
@@ -373,6 +606,9 @@ pub extern "c" fn thread_resume(thread: thread_act_t) kern_return_t;
pub const THREAD_BASIC_INFO = 3;
pub const THREAD_BASIC_INFO_COUNT: mach_msg_type_number_t = @sizeOf(thread_basic_info) / @sizeOf(natural_t);
+pub const THREAD_IDENTIFIER_INFO = 4;
+pub const THREAD_IDENTIFIER_INFO_COUNT: mach_msg_type_number_t = @sizeOf(thread_identifier_info) / @sizeOf(natural_t);
+
pub const thread_flavor_t = natural_t;
pub const thread_info_t = *integer_t;
pub const time_value_t = time_value;
@@ -404,6 +640,17 @@ pub const thread_basic_info = extern struct {
sleep_time: integer_t,
};
+pub const thread_identifier_info = extern struct {
+ /// System-wide unique 64-bit thread id
+ thread_id: u64,
+
+ /// Handle to be used by libproc
+ thread_handle: u64,
+
+ /// libdispatch queue address
+ dispatch_qaddr: u64,
+};
+
/// Cachability
pub const MATTR_CACHE = 1;
/// Migrability
@@ -506,7 +753,18 @@ pub extern "c" fn mach_vm_protect(
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;
+pub extern "c" fn mach_port_allocate(
+ task: ipc_space_t,
+ right: mach_port_right_t,
+ name: *mach_port_name_t,
+) kern_return_t;
+pub extern "c" fn mach_port_deallocate(task: ipc_space_t, name: mach_port_name_t) kern_return_t;
+pub extern "c" fn mach_port_insert_right(
+ task: ipc_space_t,
+ name: mach_port_name_t,
+ poly: mach_port_t,
+ poly_poly: mach_msg_type_name_t,
+) kern_return_t;
pub extern "c" fn task_info(
target_task: task_name_t,
@@ -514,6 +772,30 @@ pub extern "c" fn task_info(
task_info_out: task_info_t,
task_info_outCnt: *mach_msg_type_number_t,
) kern_return_t;
+
+pub const mach_task_basic_info = extern struct {
+ /// Virtual memory size (bytes)
+ virtual_size: mach_vm_size_t,
+
+ /// Resident memory size (bytes)
+ resident_size: mach_vm_size_t,
+
+ /// Total user run time for terminated threads
+ user_time: time_value_t,
+
+ /// Total system run time for terminated threads
+ system_time: time_value_t,
+
+ /// Default policy for new threads
+ policy: policy_t,
+
+ /// Suspend count for task
+ suspend_count: mach_vm_size_t,
+};
+
+pub const MACH_TASK_BASIC_INFO = 20;
+pub const MACH_TASK_BASIC_INFO_COUNT: mach_msg_type_number_t = @sizeOf(mach_task_basic_info) / @sizeOf(natural_t);
+
pub extern "c" fn _host_page_size(task: mach_port_t, size: *vm_size_t) kern_return_t;
pub extern "c" fn vm_deallocate(target_task: vm_map_t, address: vm_address_t, size: vm_size_t) kern_return_t;
pub extern "c" fn vm_machine_attribute(
@@ -1885,11 +2167,11 @@ pub const E = enum(u16) {
};
pub fn getKernError(err: kern_return_t) KernE {
- return @intToEnum(KernE, @truncate(u8, @intCast(usize, err)));
+ return @intToEnum(KernE, @truncate(u32, @intCast(usize, err)));
}
/// Kernel return values
-pub const KernE = enum(u8) {
+pub const KernE = enum(u32) {
SUCCESS = 0,
/// Specified address is not currently valid
@@ -2108,6 +2390,104 @@ pub const KernE = enum(u8) {
_,
};
+pub const mach_msg_return_t = kern_return_t;
+
+pub fn getMachMsgError(err: mach_msg_return_t) MachMsgE {
+ return @intToEnum(MachMsgE, @truncate(u32, @intCast(usize, err)));
+}
+
+/// All special error code bits defined below.
+pub const MACH_MSG_MASK: u32 = 0x3e00;
+/// No room in IPC name space for another capability name.
+pub const MACH_MSG_IPC_SPACE: u32 = 0x2000;
+/// No room in VM address space for out-of-line memory.
+pub const MACH_MSG_VM_SPACE: u32 = 0x1000;
+/// Kernel resource shortage handling out-of-line memory.
+pub const MACH_MSG_IPC_KERNEL: u32 = 0x800;
+/// Kernel resource shortage handling an IPC capability.
+pub const MACH_MSG_VM_KERNEL: u32 = 0x400;
+
+/// Mach msg return values
+pub const MachMsgE = enum(u32) {
+ SUCCESS = 0x00000000,
+
+ /// Thread is waiting to send. (Internal use only.)
+ SEND_IN_PROGRESS = 0x10000001,
+ /// Bogus in-line data.
+ SEND_INVALID_DATA = 0x10000002,
+ /// Bogus destination port.
+ SEND_INVALID_DEST = 0x10000003,
+ /// Message not sent before timeout expired.
+ SEND_TIMED_OUT = 0x10000004,
+ /// Bogus voucher port.
+ SEND_INVALID_VOUCHER = 0x10000005,
+ /// Software interrupt.
+ SEND_INTERRUPTED = 0x10000007,
+ /// Data doesn't contain a complete message.
+ SEND_MSG_TOO_SMALL = 0x10000008,
+ /// Bogus reply port.
+ SEND_INVALID_REPLY = 0x10000009,
+ /// Bogus port rights in the message body.
+ SEND_INVALID_RIGHT = 0x1000000a,
+ /// Bogus notify port argument.
+ SEND_INVALID_NOTIFY = 0x1000000b,
+ /// Invalid out-of-line memory pointer.
+ SEND_INVALID_MEMORY = 0x1000000c,
+ /// No message buffer is available.
+ SEND_NO_BUFFER = 0x1000000d,
+ /// Send is too large for port
+ SEND_TOO_LARGE = 0x1000000e,
+ /// Invalid msg-type specification.
+ SEND_INVALID_TYPE = 0x1000000f,
+ /// A field in the header had a bad value.
+ SEND_INVALID_HEADER = 0x10000010,
+ /// The trailer to be sent does not match kernel format.
+ SEND_INVALID_TRAILER = 0x10000011,
+ /// The sending thread context did not match the context on the dest port
+ SEND_INVALID_CONTEXT = 0x10000012,
+ /// compatibility: no longer a returned error
+ SEND_INVALID_RT_OOL_SIZE = 0x10000015,
+ /// The destination port doesn't accept ports in body
+ SEND_NO_GRANT_DEST = 0x10000016,
+ /// Message send was rejected by message filter
+ SEND_MSG_FILTERED = 0x10000017,
+
+ /// Thread is waiting for receive. (Internal use only.)
+ RCV_IN_PROGRESS = 0x10004001,
+ /// Bogus name for receive port/port-set.
+ RCV_INVALID_NAME = 0x10004002,
+ /// Didn't get a message within the timeout value.
+ RCV_TIMED_OUT = 0x10004003,
+ /// Message buffer is not large enough for inline data.
+ RCV_TOO_LARGE = 0x10004004,
+ /// Software interrupt.
+ RCV_INTERRUPTED = 0x10004005,
+ /// compatibility: no longer a returned error
+ RCV_PORT_CHANGED = 0x10004006,
+ /// Bogus notify port argument.
+ RCV_INVALID_NOTIFY = 0x10004007,
+ /// Bogus message buffer for inline data.
+ RCV_INVALID_DATA = 0x10004008,
+ /// Port/set was sent away/died during receive.
+ RCV_PORT_DIED = 0x10004009,
+ /// compatibility: no longer a returned error
+ RCV_IN_SET = 0x1000400a,
+ /// Error receiving message header. See special bits.
+ RCV_HEADER_ERROR = 0x1000400b,
+ /// Error receiving message body. See special bits.
+ RCV_BODY_ERROR = 0x1000400c,
+ /// Invalid msg-type specification in scatter list.
+ RCV_INVALID_TYPE = 0x1000400d,
+ /// Out-of-line overwrite region is not large enough
+ RCV_SCATTER_SMALL = 0x1000400e,
+ /// trailer type or number of trailer elements not supported
+ RCV_INVALID_TRAILER = 0x1000400f,
+ /// Waiting for receive with timeout. (Internal use only.)
+ RCV_IN_PROGRESS_TIMED = 0x10004011,
+ /// invalid reply port used in a STRICT_REPLY message
+ RCV_INVALID_REPLY = 0x10004012,
+};
+
pub const SIGSTKSZ = 131072;
pub const MINSIGSTKSZ = 32768;
@@ -2677,11 +3057,20 @@ pub const _POSIX_SPAWN_RESLIDE = 0x0800;
pub const POSIX_SPAWN_CLOEXEC_DEFAULT = 0x4000;
pub const PT_TRACE_ME = 0;
+pub const PT_READ_I = 1;
+pub const PT_READ_D = 2;
+pub const PT_READ_U = 3;
+pub const PT_WRITE_I = 4;
+pub const PT_WRITE_D = 5;
+pub const PT_WRITE_U = 6;
pub const PT_CONTINUE = 7;
pub const PT_KILL = 8;
pub const PT_STEP = 9;
pub const PT_DETACH = 11;
+pub const PT_SIGEXC = 12;
+pub const PT_THUPDATE = 13;
pub const PT_ATTACHEXC = 14;
+pub const PT_FORCEQUOTA = 30;
pub const PT_DENY_ATTACH = 31;
pub const caddr_t = ?[*]u8;
diff --git a/lib/std/c/darwin/aarch64.zig b/lib/std/c/darwin/aarch64.zig
index 70153b5dfb..48b03363a1 100644
--- a/lib/std/c/darwin/aarch64.zig
+++ b/lib/std/c/darwin/aarch64.zig
@@ -16,3 +16,23 @@ pub const thread_state = extern struct {
cpsr: u32, // Current program status register
__pad: u32,
};
+
+pub const EXC_TYPES_COUNT = 14;
+pub const EXC_MASK_MACHINE = 0;
+
+pub const ARM_THREAD_STATE = 1;
+pub const ARM_UNIFIED_THREAD_STATE = ARM_THREAD_STATE;
+pub const ARM_VFP_STATE = 2;
+pub const ARM_EXCEPTION_STATE = 3;
+pub const ARM_DEBUG_STATE = 4;
+pub const THREAD_STATE_NONE = 5;
+pub const ARM_THREAD_STATE64 = 6;
+pub const ARM_EXCEPTION_STATE64 = 7;
+pub const ARM_THREAD_STATE_LAST = 8;
+pub const ARM_THREAD_STATE32 = 9;
+pub const ARM_DEBUG_STATE32 = 14;
+pub const ARM_DEBUG_STATE64 = 15;
+pub const ARM_NEON_STATE = 16;
+pub const ARM_NEON_STATE64 = 17;
+pub const ARM_CPMU_STATE64 = 18;
+pub const ARM_PAGEIN_STATE = 27;
diff --git a/lib/std/c/darwin/x86_64.zig b/lib/std/c/darwin/x86_64.zig
index 3d38d34d36..c7671bc23a 100644
--- a/lib/std/c/darwin/x86_64.zig
+++ b/lib/std/c/darwin/x86_64.zig
@@ -33,3 +33,30 @@ pub const thread_state = extern struct {
pub const THREAD_STATE = 4;
pub const THREAD_STATE_COUNT: c.mach_msg_type_number_t = @sizeOf(thread_state) / @sizeOf(c_int);
+
+pub const EXC_TYPES_COUNT = 14;
+pub const EXC_MASK_MACHINE = 0;
+
+pub const x86_THREAD_STATE32 = 1;
+pub const x86_FLOAT_STATE32 = 2;
+pub const x86_EXCEPTION_STATE32 = 3;
+pub const x86_THREAD_STATE64 = 4;
+pub const x86_FLOAT_STATE64 = 5;
+pub const x86_EXCEPTION_STATE64 = 6;
+pub const x86_THREAD_STATE = 7;
+pub const x86_FLOAT_STATE = 8;
+pub const x86_EXCEPTION_STATE = 9;
+pub const x86_DEBUG_STATE32 = 10;
+pub const x86_DEBUG_STATE64 = 11;
+pub const x86_DEBUG_STATE = 12;
+pub const THREAD_STATE_NONE = 13;
+pub const x86_AVX_STATE32 = 16;
+pub const x86_AVX_STATE64 = (x86_AVX_STATE32 + 1);
+pub const x86_AVX_STATE = (x86_AVX_STATE32 + 2);
+pub const x86_AVX512_STATE32 = 19;
+pub const x86_AVX512_STATE64 = (x86_AVX512_STATE32 + 1);
+pub const x86_AVX512_STATE = (x86_AVX512_STATE32 + 2);
+pub const x86_PAGEIN_STATE = 22;
+pub const x86_THREAD_FULL_STATE64 = 23;
+pub const x86_INSTRUCTION_STATE = 24;
+pub const x86_LAST_BRANCH_STATE = 25;
diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig
index 7d7bea323b..b706f0aecb 100644
--- a/lib/std/child_process.zig
+++ b/lib/std/child_process.zig
@@ -60,6 +60,9 @@ pub const ChildProcess = struct {
/// Darwin-only. Disable ASLR for the child process.
disable_aslr: bool = false,
+ /// Darwin-only. Start child process in suspended state as if SIGSTOP was sent.
+ start_suspended: bool = false,
+
pub const Arg0Expand = os.Arg0Expand;
pub const SpawnError = error{
@@ -578,6 +581,9 @@ pub const ChildProcess = struct {
if (self.disable_aslr) {
flags |= os.darwin._POSIX_SPAWN_DISABLE_ASLR;
}
+ if (self.start_suspended) {
+ flags |= os.darwin.POSIX_SPAWN_START_SUSPENDED;
+ }
try attr.set(flags);
var actions = try os.posix_spawn.Actions.init();
diff --git a/lib/std/os.zig b/lib/std/os.zig
index 4abaccb68d..3d0c7a6351 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -42,6 +42,7 @@ pub const uefi = @import("os/uefi.zig");
pub const wasi = @import("os/wasi.zig");
pub const windows = @import("os/windows.zig");
pub const posix_spawn = @import("os/posix_spawn.zig");
+pub const ptrace = @import("os/ptrace.zig");
comptime {
assert(@import("std") == std); // std lib tests require --zig-lib-dir
diff --git a/lib/std/os/darwin.zig b/lib/std/os/darwin.zig
index 96d43e4f8e..b3fb681d5a 100644
--- a/lib/std/os/darwin.zig
+++ b/lib/std/os/darwin.zig
@@ -17,11 +17,120 @@ const mach_task = if (builtin.target.isDarwin()) struct {
Unexpected,
};
- pub const MachTask = struct {
+ pub const MachTask = extern struct {
port: std.c.mach_port_name_t,
pub fn isValid(self: MachTask) bool {
- return self.port != 0;
+ return self.port != std.c.TASK_NULL;
+ }
+
+ pub fn pidForTask(self: MachTask) MachError!std.os.pid_t {
+ var pid: std.os.pid_t = undefined;
+ switch (std.c.getKernError(std.c.pid_for_task(self.port, &pid))) {
+ .SUCCESS => return pid,
+ .FAILURE => return error.PermissionDenied,
+ else => |err| {
+ log.err("pid_for_task kernel call failed with error code: {s}", .{@tagName(err)});
+ return error.Unexpected;
+ },
+ }
+ }
+
+ pub fn allocatePort(self: MachTask, right: std.c.MACH_PORT_RIGHT) MachError!MachTask {
+ var out_port: std.c.mach_port_name_t = undefined;
+ switch (std.c.getKernError(std.c.mach_port_allocate(
+ self.port,
+ @enumToInt(right),
+ &out_port,
+ ))) {
+ .SUCCESS => return .{ .port = out_port },
+ .FAILURE => return error.PermissionDenied,
+ else => |err| {
+ log.err("mach_task_allocate kernel call failed with error code: {s}", .{@tagName(err)});
+ return error.Unexpected;
+ },
+ }
+ }
+
+ pub fn deallocatePort(self: MachTask, port: MachTask) void {
+ _ = std.c.getKernError(std.c.mach_port_deallocate(self.port, port.port));
+ }
+
+ pub fn insertRight(self: MachTask, port: MachTask, msg: std.c.MACH_MSG_TYPE) !void {
+ switch (std.c.getKernError(std.c.mach_port_insert_right(
+ self.port,
+ port.port,
+ port.port,
+ @enumToInt(msg),
+ ))) {
+ .SUCCESS => return,
+ .FAILURE => return error.PermissionDenied,
+ else => |err| {
+ log.err("mach_port_insert_right kernel call failed with error code: {s}", .{@tagName(err)});
+ return error.Unexpected;
+ },
+ }
+ }
+
+ pub const PortInfo = struct {
+ mask: std.c.exception_mask_t,
+ masks: [std.c.EXC_TYPES_COUNT]std.c.exception_mask_t,
+ ports: [std.c.EXC_TYPES_COUNT]std.c.mach_port_t,
+ behaviors: [std.c.EXC_TYPES_COUNT]std.c.exception_behavior_t,
+ flavors: [std.c.EXC_TYPES_COUNT]std.c.thread_state_flavor_t,
+ count: std.c.mach_msg_type_number_t,
+ };
+
+ pub fn getExceptionPorts(self: MachTask, mask: std.c.exception_mask_t) !PortInfo {
+ var info = PortInfo{
+ .mask = mask,
+ .masks = undefined,
+ .ports = undefined,
+ .behaviors = undefined,
+ .flavors = undefined,
+ .count = 0,
+ };
+ info.count = info.ports.len / @sizeOf(std.c.mach_port_t);
+
+ switch (std.c.getKernError(std.c.task_get_exception_ports(
+ self.port,
+ info.mask,
+ &info.masks,
+ &info.count,
+ &info.ports,
+ &info.behaviors,
+ &info.flavors,
+ ))) {
+ .SUCCESS => return info,
+ .FAILURE => return error.PermissionDenied,
+ else => |err| {
+ log.err("task_get_exception_ports kernel call failed with error code: {s}", .{@tagName(err)});
+ return error.Unexpected;
+ },
+ }
+ }
+
+ pub fn setExceptionPorts(
+ self: MachTask,
+ mask: std.c.exception_mask_t,
+ new_port: MachTask,
+ behavior: std.c.exception_behavior_t,
+ new_flavor: std.c.thread_state_flavor_t,
+ ) !void {
+ switch (std.c.getKernError(std.c.task_set_exception_ports(
+ self.port,
+ mask,
+ new_port.port,
+ behavior,
+ new_flavor,
+ ))) {
+ .SUCCESS => return,
+ .FAILURE => return error.PermissionDenied,
+ else => |err| {
+ log.err("task_set_exception_ports kernel call failed with error code: {s}", .{@tagName(err)});
+ return error.Unexpected;
+ },
+ }
}
pub const RegionInfo = struct {
@@ -304,6 +413,110 @@ const mach_task = if (builtin.target.isDarwin()) struct {
},
}
}
+
+ pub fn basicTaskInfo(task: MachTask) MachError!std.c.mach_task_basic_info {
+ var info: std.c.mach_task_basic_info = undefined;
+ var count = std.c.MACH_TASK_BASIC_INFO_COUNT;
+ switch (std.c.getKernError(std.c.task_info(
+ task.port,
+ std.c.MACH_TASK_BASIC_INFO,
+ @ptrCast(std.c.task_info_t, &info),
+ &count,
+ ))) {
+ .SUCCESS => return info,
+ else => |err| {
+ log.err("task_info kernel call failed with error code: {s}", .{@tagName(err)});
+ return error.Unexpected;
+ },
+ }
+ }
+
+ pub fn @"resume"(task: MachTask) MachError!void {
+ switch (std.c.getKernError(std.c.task_resume(task.port))) {
+ .SUCCESS => {},
+ else => |err| {
+ log.err("task_resume kernel call failed with error code: {s}", .{@tagName(err)});
+ return error.Unexpected;
+ },
+ }
+ }
+
+ pub fn @"suspend"(task: MachTask) MachError!void {
+ switch (std.c.getKernError(std.c.task_suspend(task.port))) {
+ .SUCCESS => {},
+ else => |err| {
+ log.err("task_suspend kernel call failed with error code: {s}", .{@tagName(err)});
+ return error.Unexpected;
+ },
+ }
+ }
+
+ const ThreadList = struct {
+ buf: []MachThread,
+
+ pub fn deinit(list: ThreadList) void {
+ const self_task = machTaskForSelf();
+ _ = std.c.vm_deallocate(
+ self_task.port,
+ @ptrToInt(list.buf.ptr),
+ @intCast(std.c.vm_size_t, list.buf.len * @sizeOf(std.c.mach_port_t)),
+ );
+ }
+ };
+
+ pub fn getThreads(task: MachTask) MachError!ThreadList {
+ var thread_list: std.c.mach_port_array_t = undefined;
+ var thread_count: std.c.mach_msg_type_number_t = undefined;
+ switch (std.c.getKernError(std.c.task_threads(task.port, &thread_list, &thread_count))) {
+ .SUCCESS => return ThreadList{ .buf = @ptrCast([*]MachThread, thread_list)[0..thread_count] },
+ else => |err| {
+ log.err("task_threads kernel call failed with error code: {s}", .{@tagName(err)});
+ return error.Unexpected;
+ },
+ }
+ }
+ };
+
+ pub const MachThread = extern struct {
+ port: std.c.mach_port_t,
+
+ pub fn isValid(thread: MachThread) bool {
+ return thread.port != std.c.THREAD_NULL;
+ }
+
+ pub fn getBasicInfo(thread: MachThread) MachError!std.c.thread_basic_info {
+ var info: std.c.thread_basic_info = undefined;
+ var count = std.c.THREAD_BASIC_INFO_COUNT;
+ switch (std.c.getKernError(std.c.thread_info(
+ thread.port,
+ std.c.THREAD_BASIC_INFO,
+ @ptrCast(std.c.thread_info_t, &info),
+ &count,
+ ))) {
+ .SUCCESS => return info,
+ else => |err| {
+ log.err("thread_info kernel call failed with error code: {s}", .{@tagName(err)});
+ return error.Unexpected;
+ },
+ }
+ }
+
+ pub fn getIdentifierInfo(thread: MachThread) MachError!std.c.thread_identifier_info {
+ var info: std.c.thread_identifier_info = undefined;
+ var count = std.c.THREAD_IDENTIFIER_INFO_COUNT;
+ switch (std.c.getKernError(std.c.thread_info(
+ thread.port,
+ std.c.THREAD_IDENTIFIER_INFO,
+ @ptrCast(std.c.thread_info_t, &info),
+ &count,
+ ))) {
+ .SUCCESS => return info,
+ else => |err| {
+ log.err("thread_info kernel call failed with error code: {s}", .{@tagName(err)});
+ return error.Unexpected;
+ },
+ }
+ }
};
pub fn machTaskForPid(pid: std.os.pid_t) MachError!MachTask {
diff --git a/lib/std/os/ptrace.zig b/lib/std/os/ptrace.zig
new file mode 100644
index 0000000000..afe0b51e2e
--- /dev/null
+++ b/lib/std/os/ptrace.zig
@@ -0,0 +1,28 @@
+const std = @import("std");
+const builtin = @import("builtin");
+
+const os = @import("../os.zig");
+const system = os.system;
+const errno = system.getErrno;
+const pid_t = system.pid_t;
+const unexpectedErrno = os.unexpectedErrno;
+const UnexpectedError = os.UnexpectedError;
+
+pub usingnamespace ptrace;
+
+const ptrace = if (builtin.target.isDarwin()) struct {
+ pub const PtraceError = error{
+ ProcessNotFound,
+ PermissionDenied,
+ } || UnexpectedError;
+
+ pub fn ptrace(request: i32, pid: pid_t, addr: ?[*]u8, signal: i32) PtraceError!void {
+ switch (errno(system.ptrace(request, pid, addr, signal))) {
+ .SUCCESS => return,
+ .SRCH => return error.ProcessNotFound,
+ .INVAL => unreachable,
+ .BUSY, .PERM => return error.PermissionDenied,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+} else struct {};