Mach traps: complete slot catalog and operation boundaries
Interface overview · Machine entry paths · IPC · VM
The common table has 128 slots, indexed 0–127. There are 62 potentially named operations: 60 unconditional named slots, slot 47 when __LP64__ || __arm64__, and slot 63 when __LP64__. Thus an LP64 build has 62 named/66 invalid entries; ARM64 without LP64 has 61/67; a build satisfying neither condition has 60/68. These are table counts, not a claim that every named operation performs useful work in every configuration. Four named backing-store interfaces are unconditional ENOTSUP stubs, and other handlers have configuration-dependent bodies. Table, table size and entry layout.
The catalog below preserves every slot, exact argument counts and mungers, kernel argument-field order/types, implementation location, and operation purpose. It is generated from this checkout by build_mach_trap_catalog.py, with curated descriptions checked against selected bodies. The machine-entry companion explains how a slot becomes an ARM64 negative selector or an x86 class/number, and why an invalid slot generally raises a syscall exception before kern_invalid() is invoked.
Complete table: all 128 slots
The ABI column is argument count / 32-bit word count / munger suffix exactly as stored in the table; — means no munger. port marks the table’s direct port-name return flag. Argument declarations below are kernel argument-structure fields, in order; a user_addr_t is a userspace pointer value, not a kernel pointer. Source links on slot numbers identify the exact table entries. Conditional alternatives are preserved rather than evaluated for this host.
Slots 0–31
| Slot | Handler and availability | ABI | Kernel argument fields | Purpose and first implementation boundary |
|---|---|---|---|---|
| 0 | kern_invalid |
0 / 0 / — |
none | Reserved low numbers; not a Mach operation. |
| 1 | kern_invalid |
0 / 0 / — |
none | Reserved low numbers; not a Mach operation. |
| 2 | kern_invalid |
0 / 0 / — |
none | Reserved low numbers; not a Mach operation. |
| 3 | kern_invalid |
0 / 0 / — |
none | Reserved low numbers; not a Mach operation. |
| 4 | kern_invalid |
0 / 0 / — |
none | Reserved low numbers; not a Mach operation. |
| 5 | kern_invalid |
0 / 0 / — |
none | Reserved low numbers; not a Mach operation. |
| 6 | kern_invalid |
0 / 0 / — |
none | Reserved low numbers; not a Mach operation. |
| 7 | kern_invalid |
0 / 0 / — |
none | Reserved low numbers; not a Mach operation. |
| 8 | kern_invalid |
0 / 0 / — |
none | Reserved low numbers; not a Mach operation. |
| 9 | kern_invalid |
0 / 0 / — |
none | Reserved low numbers; not a Mach operation. |
| 10 | _kernelrpc_mach_vm_allocate_trap |
4 / 5 / wwlw |
mach_port_name_t target, user_addr_t addr, mach_vm_size_ut size, int flags (structure) |
Allocate an anonymous region in the current task via mach_vm_allocate_external; address is copied in/out. |
| 11 | _kernelrpc_mach_vm_purgable_control_trap |
4 / 5 / wlww |
mach_port_name_t target, mach_vm_offset_t address, vm_purgable_t control, user_addr_t state (structure) |
Get or change current-task purgeable state via mach_vm_purgable_control; GET accepts the current-task read capability. |
| 12 | _kernelrpc_mach_vm_deallocate_trap |
3 / 5 / wll |
mach_port_name_t target, mach_vm_address_ut address, mach_vm_size_ut size (structure) |
Remove a current-task virtual range via mach_vm_deallocate_external. |
| 13 | task_dyld_process_info_notify_get_trap |
2 / 4 / ll |
mach_vm_address_t names_addr, mach_vm_address_t names_count_addr (structure) |
Copy active dyld notifier send-right names into the current task; capacity/count and failure cleanup are explicit. |
| 14 | _kernelrpc_mach_vm_protect_trap |
5 / 7 / wllww |
mach_port_name_t target, mach_vm_address_ut address, mach_vm_size_ut size, boolean_t set_maximum, vm_prot_ut new_protection (structure) |
Change current-task region protections via mach_vm_protect. |
| 15 | _kernelrpc_mach_vm_map_trap |
6 / 8 / wwllww |
mach_port_name_t target, user_addr_t addr, mach_vm_size_ut size, mach_vm_offset_ut mask, int flags, vm_prot_ut cur_protection (structure) |
Restricted anonymous mapping via mach_vm_map_external with null object, zero object offset, no copy, maximum ALL, default inheritance. |
| 16 | _kernelrpc_mach_port_allocate_trap |
3 / 3 / www |
mach_port_name_t target, mach_port_right_t right, user_addr_t name (structure) |
Allocate a named right in the current IPC space via mach_port_allocate; copy out its name. |
| 17 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 18 | _kernelrpc_mach_port_deallocate_trap |
2 / 2 / ww |
mach_port_name_t target, mach_port_name_t name (structure) |
Drop a user reference through mach_port_deallocate in the current IPC space. |
| 19 | _kernelrpc_mach_port_mod_refs_trap |
4 / 4 / wwww |
mach_port_name_t target, mach_port_name_t name, mach_port_right_t right, mach_port_delta_t delta (structure) |
Adjust selected right user-reference count via mach_port_mod_refs. |
| 20 | _kernelrpc_mach_port_move_member_trap |
3 / 3 / www |
mach_port_name_t target, mach_port_name_t member, mach_port_name_t after (structure) |
Move a receive right between port sets via mach_port_move_member. |
| 21 | _kernelrpc_mach_port_insert_right_trap |
4 / 4 / wwww |
mach_port_name_t target, mach_port_name_t name, mach_port_name_t poly, mach_msg_type_name_t polyPoly (structure) |
Insert a right; same-name MAKE_SEND/COPY_SEND has a fast path, otherwise copy in a right and destroy it if insertion fails. |
| 22 | _kernelrpc_mach_port_insert_member_trap |
3 / 3 / www |
mach_port_name_t target, mach_port_name_t name, mach_port_name_t pset (structure) |
Add receive-right membership to a port set via mach_port_insert_member. |
| 23 | _kernelrpc_mach_port_extract_member_trap |
3 / 3 / www |
mach_port_name_t target, mach_port_name_t name, mach_port_name_t pset (structure) |
Remove receive-right membership from a port set via mach_port_extract_member. |
| 24 | _kernelrpc_mach_port_construct_trap |
4 / 5 / wwlw |
mach_port_name_t target, user_addr_t options, uint64_t context, user_addr_t name (structure) |
Copy in options, construct a port with context, and copy out its name. |
| 25 | _kernelrpc_mach_port_destruct_trap |
4 / 5 / wwwl |
mach_port_name_t target, mach_port_name_t name, mach_port_delta_t srdelta, uint64_t guard (structure) |
Destruct a port with send-reference delta and guard through mach_port_destruct. |
| 26 | mach_reply_port |
0 / 0 / —; port |
no consumed fields (structure) | Allocate a receive right in the current space; return its name directly or MACH_PORT_NULL. |
| 27 | thread_self_trap |
0 / 0 / —; port |
no consumed fields (structure) | Obtain the current thread self send right and install/copy it out with immovable-right support. |
| 28 | task_self_trap |
0 / 0 / —; port |
no consumed fields (structure) | Obtain the current task self send right and install/copy it out with immovable-right support. |
| 29 | host_self_trap |
0 / 0 / —; port |
no consumed fields (structure) | Copy a send right for the current task's host port into its IPC space. |
| 30 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 31 | mach_msg_trap |
7 / 7 / wwwwwww |
user_addr_t msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t rcv_size, mach_port_name_t rcv_name, mach_msg_timeout_t timeout, mach_msg_priority_t priority, user_addr_t rcv_msg (structure) |
Legacy combined message send/receive; sets rcv_msg to zero before the overwrite implementation. |
Slots 32–63
| Slot | Handler and availability | ABI | Kernel argument fields | Purpose and first implementation boundary |
|---|---|---|---|---|
| 32 | mach_msg_overwrite_trap |
8 / 8 / wwwwwwww |
user_addr_t msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t rcv_size, mach_port_name_t rcv_name, mach_msg_timeout_t timeout, mach_msg_priority_t priority, user_addr_t rcv_msg (structure) |
Legacy message send/receive with separate receive buffer; IPC_HAS_LEGACY_MACH_MSG_TRAP body, otherwise KERN_NOT_SUPPORTED. |
| 33 | semaphore_signal_trap |
1 / 1 / w |
mach_port_name_t signal_name (structure) |
Signal one semaphore waiter or increment availability through semaphore_signal_internal_trap. |
| 34 | semaphore_signal_all_trap |
1 / 1 / w |
mach_port_name_t signal_name (structure) |
Wake all eligible semaphore waiters; semaphore object reference is released after the operation. |
| 35 | semaphore_signal_thread_trap |
2 / 2 / ww |
mach_port_name_t signal_name, mach_port_name_t thread_name (structure) |
Signal a semaphore with a selected thread target; resolves both names and drops references. |
| 36 | semaphore_wait_trap |
1 / 1 / w |
mach_port_name_t wait_name (structure) |
Wait on a semaphore; blocking completion may return through a continuation. |
| 37 | semaphore_wait_signal_trap |
2 / 2 / ww |
mach_port_name_t wait_name, mach_port_name_t signal_name (structure) |
Wait on one semaphore while signaling another; coordinated wait/signal internal path. |
| 38 | semaphore_timedwait_trap |
3 / 3 / www |
mach_port_name_t wait_name, unsigned int sec, clock_res_t nsec (structure) |
Semaphore wait with relative seconds/nanoseconds timeout converted to a deadline. |
| 39 | semaphore_timedwait_signal_trap |
4 / 4 / wwww |
mach_port_name_t wait_name, mach_port_name_t signal_name, unsigned int sec, clock_res_t nsec (structure) |
Timed wait plus signal, using the same semaphore wait/signal internal machinery. |
| 40 | _kernelrpc_mach_port_get_attributes_trap |
5 / 5 / wwwww |
mach_port_name_t target, mach_port_name_t name, mach_port_flavor_t flavor, user_addr_t info, user_addr_t count (structure) |
Read current-task port attributes; clamp count to generated reply capacity, zero temporary storage, copy count/data out. |
| 41 | _kernelrpc_mach_port_guard_trap |
4 / 5 / wwlw |
mach_port_name_t target, mach_port_name_t name, uint64_t guard, boolean_t strict (structure) |
Guard a current-space port with a 64-bit context and strictness policy. |
| 42 | _kernelrpc_mach_port_unguard_trap |
3 / 4 / wwl |
mach_port_name_t target, mach_port_name_t name, uint64_t guard (structure) |
Unguard a current-space port after guard validation. |
| 43 | mach_generate_activity_id |
3 / 3 / www |
mach_port_name_t target, int count, user_addr_t activity_id (structure) |
Reserve one or more globally allocated activity IDs; validates count and copies the first ID out. |
| 44 | task_name_for_pid |
3 / 3 / www |
mach_port_name_t target_tport, int pid, user_addr_t t (structure) |
Resolve PID to a task-name capability with BSD process/credential/MAC checks. |
| 45 | task_for_pid |
3 / 3 / www |
mach_port_name_t target_tport, int pid, user_addr_t t (structure) |
Resolve PID to a task-control capability with process, credential, MAC, and task-access policy checks. |
| 46 | pid_for_task |
2 / 2 / ww |
mach_port_name_t t, user_addr_t pid (structure) |
Convert a task port to its PID and copy the PID to userspace. |
| 47 | mach_msg2_trap; __LP64__ || __arm64__, otherwise invalid |
8 / 16 / llllllll |
mach_vm_address_t data, mach_msg_option64_t options, uint64_t msgh_bits_and_send_size, uint64_t msgh_remote_and_local_port, uint64_t msgh_voucher_and_id, uint64_t desc_count_and_rcv_name, uint64_t rcv_size_and_priority, uint64_t timeout (structure) |
Current packed 64-bit message ABI; decode packed header/scalar fields and dispatch send/receive with message policy. |
| 48 | macx_swapon |
4 / 5 / lwww |
uint64_t filename, int flags, int size, int priority (structure) |
Retained ABI stub: returns ENOTSUP; does not create a swap file in this checkout. |
| 49 | macx_swapoff |
2 / 3 / lw |
uint64_t filename, int flags (structure) |
Retained ABI stub: returns ENOTSUP; does not remove a swap file in this checkout. |
| 50 | thread_get_special_reply_port |
0 / 0 / —; port |
no consumed fields (structure) | Unbind any prior user special reply port, allocate a new receive+send right, bind it to the thread, return name. |
| 51 | macx_triggers |
4 / 4 / wwww |
int hi_water, int low_water, int flags, mach_port_t alert_port (structure) |
Only compaction enable/disable flags reach macx_backing_store_compaction; other requests return ENOTSUP. |
| 52 | macx_backing_store_suspend |
1 / 1 / w |
boolean_t suspend (structure) |
Retained ABI stub: returns ENOTSUP. |
| 53 | macx_backing_store_recovery |
1 / 1 / w |
int pid (structure) |
Retained ABI stub: returns ENOTSUP. |
| 54 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 55 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 56 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 57 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 58 | pfz_exit |
0 / 0 / — |
no consumed fields (structure) | No-op success body; pending AST work is picked up on kernel exit from the preemption-free region. |
| 59 | swtch_pri |
0 / 0 / — |
int pri (structure) |
Yield with temporary priority depression; the historical priority argument is unused and table argc is zero. |
| 60 | swtch |
0 / 0 / — |
no consumed fields (structure) | Yield if scheduler policy says useful; boolean result reports whether yielding remains appropriate. |
| 61 | thread_switch |
3 / 3 / www |
mach_port_name_t thread_name, int option, mach_msg_timeout_t option_time (structure) |
Scheduler-directed switch/yield with optional depression/wait and a thread hint; continuation path can discard the stack. |
| 62 | clock_sleep_trap |
5 / 5 / wwwww |
mach_port_name_t clock_name, sleep_type_t sleep_type, int sleep_sec, int sleep_nsec, user_addr_t wakeup_time (structure) |
Clock-port sleep with absolute/relative clock time and wakeup-time copyout. |
| 63 | mach_vm_reclaim_update_kernel_accounting_trap; __LP64__, otherwise invalid |
3 / 5 / wll |
mach_port_name_t target_task, user_addr_t bytes_reclaimed_out, user_addr_t next_deadline_out (structure) |
Current-task deferred-reclamation accounting update; CONFIG_DEFERRED_RECLAIM body or KERN_NOT_SUPPORTED. |
Slots 64–95
| Slot | Handler and availability | ABI | Kernel argument fields | Purpose and first implementation boundary |
|---|---|---|---|---|
| 64 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 65 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 66 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 67 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 68 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 69 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 70 | host_create_mach_voucher_trap |
4 / 4 / wwww |
mach_port_name_t host, mach_voucher_attr_raw_recipe_array_t recipes, int recipes_size, user_addr_t voucher (structure) |
Copy raw recipes, create a voucher, export its send right, and copy its name to userspace. |
| 71 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 72 | mach_voucher_extract_attr_recipe_trap |
4 / 4 / wwww |
mach_port_name_t voucher_name, mach_voucher_attr_key_t key, mach_voucher_attr_raw_recipe_t recipe, user_addr_t recipe_size (structure) |
Resolve a voucher, extract one raw attribute recipe into bounded temporary storage, then copy bytes/count out. |
| 73 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 74 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 75 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 76 | _kernelrpc_mach_port_type_trap |
3 / 3 / wwww |
mach_port_name_t target, mach_port_right_t name, user_addr_t ptype (structure) |
Return current-space right type bits through mach_port_type and copyout. |
| 77 | _kernelrpc_mach_port_request_notification_trap |
7 / 7 / wwwwwww |
mach_port_name_t target, mach_port_name_t name, mach_msg_id_t msgid, mach_port_mscount_t sync, mach_port_name_t notify, mach_msg_type_name_t notifyPoly, user_addr_t previous (structure) |
Register/change a notification using a send-once disposition; copy previous notification right back into the namespace. |
| 78 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 79 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 80 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 81 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 82 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 83 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 84 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 85 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 86 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 87 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 88 | _exclaves_ctl_trap |
8 / 14 / wwllllll |
mach_port_name_t name, uint32_t operation_and_flags, uint64_t identifier, mach_vm_address_t buffer, mach_vm_size_t size, uint64_t param1, uint64_t param2, mach_vm_address_t status (structure) |
Exclaves operation multiplexer: privilege/config checks then resource, IPC, sensor, notification, and boot operations; disabled body returns KERN_NOT_SUPPORTED. |
| 89 | mach_timebase_info_trap |
1 / 1 / w |
user_addr_t info (structure) |
Copy the absolute-time conversion numerator/denominator to userspace. |
| 90 | mach_wait_until_trap |
1 / 2 / l |
uint64_t deadline (structure) |
Wait until an absolute-time deadline through assert_wait_deadline/thread_block; interruption maps to a Mach result. |
| 91 | mk_timer_create_trap |
0 / 0 / —; port |
no consumed fields (structure) | Allocate a timer object and receive-right port; return the name or MACH_PORT_NULL. |
| 92 | mk_timer_destroy_trap |
1 / 1 / w |
mach_port_name_t name (structure) |
Destroy the named timer port with timer-object validation. |
| 93 | mk_timer_arm_trap |
2 / 3 / wl |
mach_port_name_t name, uint64_t expire_time (structure) |
Arm a timer at an absolute deadline through mk_timer_arm_trap_internal. |
| 94 | mk_timer_cancel_trap |
2 / 2 / ww |
mach_port_name_t name, user_addr_t result_time (structure) |
Cancel timer delivery and optionally copy out its previous deadline. |
| 95 | mk_timer_arm_leeway_trap |
4 / 6 / wlll |
mach_port_name_t name, uint64_t mk_timer_flags, uint64_t expire_time, uint64_t mk_leeway (structure) |
Arm a timer with flags and leeway through mk_timer_arm_trap_internal. |
Slots 96–127
| Slot | Handler and availability | ABI | Kernel argument fields | Purpose and first implementation boundary |
|---|---|---|---|---|
| 96 | debug_control_port_for_pid |
3 / 3 / www |
mach_port_name_t target_tport, int pid, user_addr_t t (structure) |
Obtain a task debug-control capability subject to process/credential/entitlement restrictions. |
| 97 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 98 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 99 | kern_invalid |
0 / 0 / — |
none | No operation assigned; dispatch rejects this slot. |
| 100 | iokit_user_client_trap |
8 / 8 / wwwwwwww |
void * userClientRef, uint32_t index, void * p1, void * p2, void * p3, void * p4, void * p5, void * p6 (structure) |
Dispatch an IOKit user-client trap or DriverKit uext trap from a tagged reference; index and six scalars reach the selected target. |
| 101 | kern_invalid |
0 / 0 / — |
none | Reserved for IOKit; currently invalid. |
| 102 | kern_invalid |
0 / 0 / — |
none | Reserved for IOKit; currently invalid. |
| 103 | kern_invalid |
0 / 0 / — |
none | Reserved for IOKit; currently invalid. |
| 104 | kern_invalid |
0 / 0 / — |
none | Reserved for IOKit; currently invalid. |
| 105 | kern_invalid |
0 / 0 / — |
none | Reserved for IOKit; currently invalid. |
| 106 | kern_invalid |
0 / 0 / — |
none | Reserved for IOKit; currently invalid. |
| 107 | kern_invalid |
0 / 0 / — |
none | Reserved for IOKit; currently invalid. |
| 108 | kern_invalid |
0 / 0 / — |
none | Invalid despite the historical thread_set_x86_64_compat userspace stub. |
| 109 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 110 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 111 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 112 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 113 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 114 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 115 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 116 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 117 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 118 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 119 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 120 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 121 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 122 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 123 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 124 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 125 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 126 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
| 127 | kern_invalid |
0 / 0 / — |
none | Explicitly unused table slot. |
ABI metadata and historical discrepancies
The table stores a function pointer, logical argument count, count of 32-bit words, optional 32-bit munger, and a flag for direct port-name results. Six entries set that result flag: 26, 27, 28, 29, 50, and 91. Dispatchers use it when converting policy denial to MACH_PORT_NULL. It does not mean every other handler returns a conventional kern_return_t: swtch returns a boolean, and the retained macx_* bodies use BSD-valued errors through a Mach-table route. Entry definition, filter return handling.
The kernel's argument structures are the decoder contract; library prototypes and assembly macros are additional evidence rather than substitutes. This matters in several visible cases:
| Case | Exact source distinction |
|---|---|
mach_msg_trap, slot 31 |
Shares the overwrite argument structure but consumes seven table arguments; its body explicitly zeros rcv_msg before delegation. |
mach_msg_overwrite_trap, slot 32 |
Kernel structure/table have eight consumed fields; the user prototype and historical stub also describe a ninth rcv_limit argument. The ARM stub comments explicitly say the ninth is unused. |
swtch_pri, slot 59 |
Public prototype/argument structure contain pri, but the table argument count is zero and the body marks the argument structure unused. |
mach_port_type, slot 76 |
Table records 3 / 3 / munge_wwww; the munger name is not a reason to silently rewrite the stored counts to four. |
mk_timer_arm_leeway, slot 95 |
Table records four logical arguments and six 32-bit words with munge_wlll; the argument structure contains one port name plus three 64-bit fields, and the non-LP64 stub requests seven words. The catalog retains this mismatch. |
| VM map stub, slot 15 | Historical stub macro requests nine words, while kernel table stores eight 32-bit words for six logical fields. |
| Compatibility stub, slot 108 | thread_set_x86_64_compat is emitted by the common stub list, but the actual table entry is kern_invalid; its declaration is not evidence of an implemented operation. |
The 32-bit munger bodies make the distinction concrete: munge_wwww widens four words, while munge_wlll reads its last 64-bit value beginning at input word five. Munger implementations, three-long form.
Sources: kernel message structures, stub list, unused ninth-argument comment, swtch body, slot 76, timer structure, timer/compatibility stubs. These are source observations, not fixes or claims that every compatibility path is exercised in a shipped build. Modern ARM64 argument gathering copies 64-bit slots; x86 retains both 64-bit gathering and a 32-bit copy/munge route. See machine entry points.
Current-task fast traps and MIG fallback
The _kernelrpc_*_trap family is not a general in-kernel MIG demultiplexer. Its handlers usually recognize a capability for the current task, then call a VM or IPC-space operation directly. An unsupported destination produces MACH_SEND_INVALID_DEST, allowing the library to choose its message-based _kernelrpc_* implementation. A real operation failure, such as invalid arguments or failed user copy, does not universally trigger the fallback. VM trap bodies, port trap bodies, message interface catalog.
current-task VM allocation fast path
_kernelrpc_mach_vm_allocate_trap(struct _kernelrpc_mach_vm_allocate_trap_args *args)
{
mach_vm_offset_t addr;
task_t task = port_name_to_current_task_noref(args->target);
int rv = MACH_SEND_INVALID_DEST;
if (task) {
if ((rv = mach_copyin(args->addr, (char *)&addr, sizeof(addr)))) {
goto done;
}
rv = mach_vm_allocate_external(task->map, &addr, args->size, args->flags);
if (rv == KERN_SUCCESS) {
rv = mach_copyout(&addr, args->addr, sizeof(addr));
}
}
done:
return rv;
}
The corresponding library branch is equally specific:
VM allocation fallback condition
rv = _kernelrpc_mach_vm_allocate_trap(target, address, size, flags);
if (rv == MACH_SEND_INVALID_DEST) {
rv = _kernelrpc_mach_vm_allocate(target, address, size, flags);
}
Mermaid source
flowchart TD
L["mach_vm_allocate / port wrapper"] --> T["direct trap"]
T --> C{"current-task fast target accepted?"}
C -->|yes| H["copy user inputs → VM / IPC implementation → copy outputs"]
C -->|no| I["MACH_SEND_INVALID_DEST"]
I --> G["library MIG client creates message"]
G --> M["mach_msg transport trap"]
M --> K["destination kobject / MIG routine dispatch"]
H --> R["return operation result"]
| Wrapper family | Local selection / fallback rule |
|---|---|
mach_vm_allocate, deallocate, protect, purgable_control |
Attempt direct trap; only MACH_SEND_INVALID_DEST selects the corresponding _kernelrpc_* message call. VM wrappers, purgeable wrapper. |
mach_vm_map |
Attempt the restricted trap only for a null memory object, maximum protection VM_PROT_ALL, and default inheritance. Other mappings begin with the message fallback result; the full message interface carries object/offset/copy/max/inheritance fields absent from the trap. Mapping wrapper. |
| Port type/allocate/deallocate/mod-refs/membership/right insertion/attributes/guard/construct/destruct/notification | Trap wrappers follow the same invalid-destination fallback pattern; operations without a trap, such as port rename, remain message interfaces. Port wrappers, notification/insertion, construct/guard wrappers. |
| Voucher creation/extraction | Attempt recipe trap, then corresponding MIG routine on invalid destination. Host creation retains architecture-specific historical fallback checks. Host wrapper, recipe extraction. |
| Deferred reclamation accounting | Userspace ring sampling calls the dedicated trap and updates its local accounting/deadline state; this body has no generic MIG fallback. Ring wrapper. |
The fast-path scope has real ownership consequences. mach_port_insert_right uses a same-name MAKE_SEND/COPY_SEND shortcut; otherwise it copies in a right, converts the disposition, invokes insertion, and destroys the acquired right if insertion fails. Notification registration similarly converts/acquires a send-once right and may copy a previous notification right into the caller's space. A copyout error is later than the kernel object transition: allocation and port-construction bodies do not show a universal rollback of a successfully created object when the final scalar copyout fails. Right insertion cleanup, notification ownership, port construction, rights lifecycle.
Message transport, packed ABI, and interruption
Slot 47's eight logical arguments are eight 64-bit fields, with several packing two 32-bit values. The kernel reconstructs the message header from these scalars; descriptor/body data still comes from the user message or vectors. The ordinary nonvector interpretation is:
| Packed argument | Low 32 bits | High 32 bits |
|---|---|---|
msgh_bits_and_send_size |
Header bits | Send byte count |
msgh_remote_and_local_port |
Remote port | Local port |
msgh_voucher_and_id |
Voucher port | Message ID |
desc_count_and_rcv_name |
Descriptor count | Receive port/set name |
rcv_size_and_priority |
Receive size | Priority / relevant send context |
Vector mode changes how size/count fields are interpreted and obtains message/auxiliary vector information first. The kernel applies current-task options and ipc_preflight_msg_option64(), performs send if requested, and only proceeds to receive if send succeeded. A bad receive vector does not necessarily prevent a valid send in a combined operation. Packed decoding and send, message ownership guide.
header reconstruction from packed trap fields
mach_msg_send_uctx_t send_uctx = {
.send_header = {
.msgh_bits = (mach_msg_bits_t) (mb_ss),
.msgh_size = 0,
.msgh_remote_port = (mach_port_name_t)(mr_lp),
.msgh_local_port = (mach_port_name_t)(mr_lp >> 32),
.msgh_voucher_port = (mach_port_name_t)(mv_id),
.msgh_id = (mach_msg_id_t) (mv_id >> 32),
},
.send_dsc_count = (mach_msg_size_t)dc_rn,
};
mach_msg2_internal() removes library-only option bits and retries an interrupted send when allowed. After an interrupted receive, it clears the send action and send size before retrying. Reissuing the original combined operation could otherwise resend a request whose send already completed. Library retry body, receive phase and compensation.
Slots 31 and 32 remain named on all table configurations, but their meaningful legacy body is selected by IPC_HAS_LEGACY_MACH_MSG_TRAP, defined here only for XNU_TARGET_OS_OSX. Otherwise the overwrite handler returns KERN_NOT_SUPPORTED; slot 31 delegates to it. Thus the count of installed function names is not the count of supported transports. Legacy configuration, disabled result.
Message IDs belong to a separate namespace. A request using slot 47 may target a user server, a kernel kobject, or another supported destination; only later IPC/kobject dispatch selects the appropriate service routine. The MIG catalog inventories those messages and generated/external boundaries. The transport trap number does not identify a particular MIG operation.
Semaphores, scheduler calls, and timer ports
The semaphore traps convert port names to retained semaphore objects before using the shared wait/signal machinery. A targeted signal also resolves and later releases a thread reference; a null target means “choose an existing waiter without pre-posting” in that particular operation. Timed waits validate the timespec; {0,0} selects nonblocking behavior rather than an arbitrary already-expired timestamp. Wait handlers supply thread_syscall_return as their continuation, so the blocked operation can complete without returning through its original C frame. Targeted signal, wait entry, timed-wait conversion, wait/turnstile mechanisms.
swtch and swtch_pri ask scheduler policy whether yielding is useful; swtch_pri adds temporary depression and removes it in its continuation. pfz_exit currently just succeeds: its purpose is to enter/exit the kernel so pending AST processing can run, not to invoke a hidden scheduler-specific handler in the trap body. thread_switch has a richer option/hint path. Yield operations, thread switch, scheduler guide.
Timer creation allocates a timer object, initializes a thread call, preallocates a reusable expiration message, and allocates a receive-right port carrying the timer label. The timer retains its own send right. Arming/canceling operate through this port identity; destruction validates the receive right and timer label. Final timer teardown coordinates with an active callback, so “cancel” and “destroy the port/object” are different lifetime operations. Creation, label deallocation, destroy, arm/cancel.
Exclaves slot 88: all 26 secondary operations
operation_and_flags uses the upper byte for the operation and the lower 24 bits for flags; this trap currently rejects nonzero flags. CONFIG_EXCLAVES == 0 leaves the slot present but returns KERN_NOT_SUPPORTED. With the feature enabled, the body applies operation-specific privilege checks, waits for ExclaveCore boot for the ordinary route, and interprets the other seven arguments according to the selected operation. Boot is handled by an earlier privilege-gated branch. Encoding enum/macros, entry privilege/configuration, disabled body.
The following table inventories every non-sentinel enum operation. Field names refer to the slot-88 kernel structure above; the local handler may rename identifier to an offset and param1/param2 to secondary lengths/offsets. These are operation-specific uses, not additional trap numbers.
| Operation | Main arguments and local implementation boundary |
|---|---|
1 ENDPOINT_CALL |
Null name; identifier selects service; buffer/size hold an exact-size IPC buffer. Copy into per-thread IPC storage, check current-domain service availability, call endpoint, copy response out. Body. |
2 NAMED_BUFFER_CREATE |
Identifier is a user string pointer; size and param1 select length/permissions; return resource port through buffer. Body. |
3 NAMED_BUFFER_COPYIN |
Resource port name; buffer/size and identifier/param1/param2 select one/two copy ranges; retained shared-memory resource passed to copyin helper then released. Body. |
4 NAMED_BUFFER_COPYOUT |
Corresponding shared-memory resource-to-user copy and release. Body. |
5 BOOT |
Null name plus identifier/stage; privilege-gated operation_boot() before ordinary resource dispatch. Body. |
6 LAUNCH_CONCLAVE |
Null name; ensure thread IPC buffer, call task_launch_conclave, then handle stop-upcall completion. Body. |
7 LOOKUP_SERVICES |
Buffer/size describe resource lookup data; current body requires exactly one exclaves_resource_user request. Body. |
8 AUDIO_BUFFER_CREATE |
Identifier string and size; map arbitrated audio-memory resource and return a port through buffer. Shared body. |
9 AUDIO_BUFFER_COPYOUT |
Resource port, user buffer/ranges and status address; arbitrated-memory copyout helper. Shared body. |
10 SENSOR_CREATE |
Identifier string; open sensor resource in current domain and export port through buffer. Body. |
11 SENSOR_START |
Sensor port and identifier; call start helper, release resource, copy status through buffer. Body. |
12 SENSOR_STOP |
Same shape with sensor stop helper. Body. |
13 SENSOR_STATUS |
Same shape with status query helper. Body. |
14 NOTIFICATION_RESOURCE_LOOKUP |
Exact resource-user structure in buffer; lookup notification, export port and metadata, undo exported right on output failure. Body. |
15 AOE_SETUP |
Requires joined conclave; setup helper returns message/worker counts through buffer/status addresses. Body. |
16 AOE_MESSAGE_LOOP |
Requires joined conclave; enter exclaves_aoe_message_loop(). Body. |
17 AOE_WORK_LOOP |
Requires joined conclave; enter exclaves_aoe_work_loop(). Body. |
18 SENSOR_MIN_ON_TIME |
Special privilege; null name and exact indicator-deadline structure copied in/out around deadline helper. Body. |
19 ARBITRATED_BUFFER_CREATE |
Same shape as audio creation, selecting the non-audio arbitrated-memory type. Shared body. |
20 ARBITRATED_BUFFER_COPYOUT |
Same arbitrated-resource copyout path as operation 9. Shared body. |
21 DAEMON_NOTIFICATION_REGISTER |
Privileged caller; exact daemon-notification structure, copied send right, named resource lookup, retained resource plus port-list entry under resource mutex. Body. |
22 DAEMON_NOTIFICATION_DEREGISTER |
Match translated send port in the resource list, remove entry, release send right and registration resource reference. Body. |
23 AOE_ENUMERATE_AND_SETUP_SERVICES |
Null name/joined conclave; enumerate/setup and copy service count through buffer. Body. |
24 AOE_GET_ALL_SERVICE_INFOS |
Null name/joined conclave; nonzero integral array of service-info structures, bounded count, helper plus copyout. Body. |
25 AOE_MESSAGE_LOOP_WITH_SERVICE_ID |
Null name/joined conclave; service identifier passed in param1 to message-loop helper. Body. |
26 AOE_WORK_LOOP_WITH_SERVICE_ID |
Same selection for work-loop helper. Body. |
Operation zero, the LAST sentinel, and other unhandled selectors reach invalid-argument handling after the applicable front-end checks. The catalog establishes local argument roles and dispatch, not the secure service's complete implementation. Continue with Exclaves resources and secure execution for resource, thread, scheduler and external-boundary ownership.
IOKit, task access, and retained stubs
Slot 100 is a dispatch surface, not one universal driver operation. iokit_user_client_trap() interprets tagged reference bits, then either looks up a DriverKit uext reference and calls IOUserServerUEXTTrap, or resolves a current-task connection, applies an optional trap filter, and asks the user client for its target/trap at the supplied index. The six scalar payload arguments reach that selected member function. Lookup references are released by the appropriate path. The concrete target's index table remains provider-specific. IOKit trap body, DriverKit dispatch, user-client ownership.
PID/task conversion traps are implemented in BSD's process code even though they live in the Mach trap table. task_for_pid and debug-control access can obtain a task reference, drop a process reference before an access-server upcall, and apply credential/MAC/entitlement policy. A table slot or a PID is not authorization to receive a task-control right. pid_for_task consumes a task-name conversion and returns the PID through a userspace address; its body also handles a corpse task. Task-control entry, debug-control entry, PID conversion, process access policy.
Four historical macx_* handlers—swapon, swapoff, backing-store suspend and recovery—return ENOTSUP immediately. macx_triggers only supports compaction enable/disable flags via macx_backing_store_compaction(), whose disable behavior is further conditional on ARM64 macOS. Their old names/comments must not be used to document nonexistent modern swap-file operations. Stub/compaction bodies.
The deferred-reclamation accounting trap is installed only in LP64 table builds. In both feature configurations it first checks current-task identity and nonnull output addresses, returning MACH_SEND_INVALID_DEST or KERN_INVALID_ARGUMENT on those failures. After those checks, a build without CONFIG_DEFERRED_RECLAIM returns KERN_NOT_SUPPORTED. The enabled path invokes the accounting helper and attempts its two scalar copyouts only if that helper succeeds. It returns the helper's status rather than testing the copyout return values, so success cannot be read as a universal proof that both userspace outputs were written. Accounting trap.
Mach-named operations outside this table
mach_eventlink_signal, mach_eventlink_wait_until, and mach_eventlink_signal_wait_until are BSD slots 496, 497, and 498. The library explicitly explains the choice: these calls need a 64-bit register return containing count/result information, while ordinary Mach traps do not offer that result convention. The wrappers decode the error and, for wait operations, update the caller's count. Creation/association and other object operations can still be message interfaces. BSD declarations, library encoding boundary, BSD catalog, MIG catalog.
Likewise, direct ARM absolute/continuous-time selectors and x86 machine-dependent/diagnostic tables are cataloged in machine entry points, not counted as additional common Mach slots. A port's externalMethod selector, a voucher recipe command, and a MIG message ID are yet other namespaces selected after the top-level entry.
Inspection scope and regeneration
| Area | Work performed and limit |
|---|---|
| Shared table and ABI | All 128 slots, both conditional alternatives, all 62 potential handler definitions, and their kernel argument structures were extracted. Munger spelling and count mismatches are preserved. |
| Current-task fast paths | Read VM/port/voucher trap bodies and library fallback selectors, including rights transfer, bounded buffers and output-copy boundaries. Linked subsystem guides supply full lower-layer algorithms. |
| Message transport | Read legacy feature gating, packed mach_msg2 decoding and send-before-receive boundary, plus library interruption loops. Full message ownership is in the IPC guide. |
| Blocking/time operations | Read semaphore entry/reference/deadline paths, scheduler yield continuations, clock waits and timer creation/arm/cancel/destroy entry bodies. Not every scheduler/interleaving was re-audited. |
| Multiplexers and policy | Read all Exclaves case boundaries and selected resource ownership branches, IOKit trap dispatch, task/PID access entry bodies, accounting and backing-store stubs. Secure service/driver implementations remain separate providers. |
| Validation | python3 -B docs-ai/tools/build_mach_trap_catalog.py --check verifies the generated catalog against the source snapshot; the general documentation checker validates links and exact excerpts. No syscall execution, compatibility test, or kernel build was performed. |
The generator updates only the marked all-slot table, preserving this explanatory material. Its purpose descriptions are curated from implementation bodies and must be reviewed if behavior changes; extraction is not semantic verification or a C preprocessor.