Mach Kernel Abstractions and XNU Internals
Search for the truth is the noblest occupation of man; its publication is a duty. - Anne Louise Germaine de Staël; De l'Allemagne
Preamble: XNU and the Mach Kernel
An operating system is a set of system software that acts as a bridge between the application software and the underlying hardware. Among the various software packaged with an operating system is the kernel — a program responsible for communicating with the hardware and managing the system in its entirety. The purpose of this post is to explore the XNU kernel source and understand the core abstractions of the Mach kernel that underpin the macOS kernel architecture. We'll walk through Mach's taxonomy — tasks, threads, IPC ports, processor sets — and examine the XNU-specific implementation details, including pointer authentication and key Mach routines like task_info and mach_task_self.
The Mach Kernel
Mach is a microkernel initially released in 1985. Since then, it has been incorporated into many operating systems such as GNU Hurd and Apple's XNU (see [XNU_GIT]). XNU is the kernel used by the Darwin operating system, which in turn underlies macOS and iOS. One of the major differences between the Mach kernel and Linux is that various kernel subsystems can be modularized. The distinction between a monolithic kernel and a microkernel is largely theoretical (see [MICRO_MONO_KERN]), and most production-grade kernels have converged on properties of both architectures. Even so, subtle differences remain between Mach and monolithic kernels such as Linux.
The aim of the Mach kernel was to allow operating systems to be built on top of Mach. As with microkernel architecture, Mach provides only a small but fundamental subset of functionality: memory management, CPU scheduling, and inter-process communication. It is the BSD layer which is responsible for providing interfaces common in operating systems. Figure 1 shows the OS X architecture, an excerpt from Wikipedia: Architecture of macOS.
Mach Kernel Abstractions in XNU
The section Mach Kernel Abstractions of [DEV_APPLE_MACH] briefly lays out the various abstractions offered by the Mach kernel. Each of those abstractions can be located in the XNU source. A quick browse of the XNU source tree shows that there are various files and directories. Explaining all of the source is beyond the scope of this blog (see [MOSX_INTERNALS] and [OS_INTERNALS]), but we will try and locate the Mach-specific components of our interest. The [osfmk/] directory contains the components of the Mach kernel. Also, [bsd/] contains the BSD kernel components (see [BSD_ON_MACH]). The list below gives the respective structure definitions of the various abstractions introduced by Mach:
-
Mach Tasks [osfmk/kern/task.h]. A container holding the various resources required for a "process". Essential resources include: virtual address space (
vm_map_t __ptrauth(...) map;) (see [XNU_PTRAUTH_SIGNED_PTR]), list of Mach threads (queue_head_t threads;), IPC space (struct ipc_space *itk_space;), and also BSD info (struct proc_ro *bsd_info_ro;). Separately from the space itself, a task holds a number of individual ports--its own task ports (struct ipc_port *itk_task_ports[TASK_SELF_PORT_COUNT];), a send right to the host port (struct ipc_port *itk_host;), and a send right to its bootstrap port (struct ipc_port *itk_bootstrap;), among others. The mentioned attributes alone do not constitute a Mach task, and many more exist. -
Machine Independent Mach Threads [osfmk/kern/thread.h]. A kernel thread. It contains attributes such as Mach task (
struct task *t_task;), read-only thread zone (struct thread_ro *t_tro;), Asynchronous System Trap (AST) pending for the thread (ast_t volatile _Atomic ast;) (see [AST_ENUM_OPT_DECL]), current kernel stack (vm_offset_t kernel_stack;), machine-dependent thread state (struct machine_thread machine;), and other attributes. The relation between a Mach thread and its BSD user thread is explored in Mach Threads and BSD User Threads. -
Machine Dependent Mach Threads [osfmk/arm/thread.h] and [osfmk/i386/thread.h]. As seen in the comment in the source, this structure (for "historical reasons") is also referred to as the Process Control Block (PCB) in the i386 structure definition. I'll discuss the ARM variant here. As seen in the machine independent structure above, it embeds the machine-dependent thread structure, containing attributes such as user context (
arm_context_t *contextData;) (see [MD_THREAD_CTX]), current per-CPU data (struct cpu_data *CpuDatap;), and plenty of reserved fields. -
BSD process [bsd/sys/proc_internal.h]. Earlier on Mach Tasks, we saw that a reference to BSD info is embedded within the structure (
bsd_info_ro). I should make the reader aware that it's the read-only data associated with a task and/or process, (see [bsd/sys/proc_ro.h]). The comment above the structure definition states that the lifetime of the read-only process structure is 1:1 with that of aproc_t(which is type definition of this structure) or atask_t(again, type definition of Mach task structure). It also states that theproc_tandtask_tstructures point to the samestruct proc_rostructure, except for corpses. This structure contains attributes such as parent process ID (pid_t p_ppid;), process group ID (pid_t p_pgrpid;), various UNIX identifiers including user ID, group ID, real user ID, real group ID, session ID, list of processes in process group (LIST_ENTRY(proc) p_pglist;) (see [LIST_ENTRY]), open file structures (struct filedesc p_fd;), actions for signals (struct sigacts p_sigacts;) (see [PROC_SIGACTS]), and other attributes. -
User Threads [bsd/sys/user.h]. It is the BSD per-thread user structure. It contains various attributes, some being related to system calls (
uint64_t uu_args[8];andint uu_rval[2];), continuation attributes for some system calls, signals pending for the thread (sigset_t uu_siglist;), signal mask for the thread (sigset_t uu_sigmask;), list of user threads in a [BSD] process (TAILQ_ENTRY(uthread) uu_list;) (see [TAILQ_ENTRY]), exit reason (struct os_reason *uu_exit_reason;), and other attributes. -
IPC Ports [osfmk/ipc/ipc_port.h].
-
Processor Set [osfmk/kern/processor.h]. Mach divides the available processors on a system into one or more processor sets. It is a logical grouping of processors. See Processor Sets in XNU for how XNU actually uses them.
-
Processor [osfmk/kern/processor.h]. A processor object is a logical representation of a physical processor. It may be a CPU, a core, or a hyperthread (see Simultaneous Multithreading and Hyper-Threading). Its attributes are largely about what is running right now and where the processor sits in the topology: current dispatch state (
processor_state_t state;), the thread currently on the processor (struct thread *active_thread;), the processor's own idle thread (struct thread *idle_thread;), the set it is assigned to (processor_set_t processor_set;), and a group of cached properties of the current thread such as its priority (int current_pri;) and urgency (thread_urgency_t current_urgency;). Note that whether the processor is a hyperthread (bool is_SMT;) is only present when the kernel is compiled withCONFIG_SCHED_SMT.
Mach Threads and BSD User Threads
On Figure 7-10 of [MOSX_INTERNALS], the definition of struct thread is shown. At the end, there is one field declared as:
struct thread {
...
#ifdef MACH_BSD
void *uthread; // per-thread user structure
#endif
};
However, the structure definition has changed since then. Until commit [e6231be], the uthread field was present in struct thread. However, starting from commit [e777678], there no longer exists a distinct uthread field inside the structure.
A Mach thread has a 1:1 relation with the user thread (though Mach threads with no associated user thread can exist). This means that a user thread is associated with only one Mach thread. Looking at the thread_create_internal function in [osfmk/kern/thread.c], we see the following:
static kern_return_t
thread_create_internal (
task_t parent_task,
integer_t priority,
thread_continue_t continuation,
void *parameter,
thread_create_internal_options_t options,
thread_t *out_thread)
{
thread_t new_thread;
struct thread_ro tro_tpl = { };
...
/*
* Allocate a thread and initialize static fields
*/
new_thread = zalloc_flags(thread_zone, Z_WAITOK | Z_NOFAIL);
...
#ifdef MACH_BSD
uthread_init(parent_task, get_bsdthread_info(new_thread),
&tro_tpl, (options & (TH_OPTION_WORKQ | TH_OPTION_AIO_WORKQ)) != 0);
if (!task_is_a_corpse(parent_task)) {
/*
* uthread_init will set tro_cred (with a +1)
* and tro_proc for live tasks.
*/
assert(tro_tpl.tro_cred && tro_tpl.tro_proc);
}
#endif
...
}
and (although not exactly required) the function uthread_init (in [bsd/kern/kern_fork.c]) has the signature as:
void uthread_init (task_t task, uthread_t uth, thread_ro_t tro_tpl, int workq_thread);
Before I explain how the user thread structure is accessed from the Mach thread, let's see the type definition of thread_t and uthread_t. On [osfmk/mach/mach_types.h], we see the two type definitions:
...
#if KERNEL
/*
* If we are in the kernel, then pick up the kernel definitions for
* the basic mach types.
*/
...
typedef struct thread *thread_t, *thread_act_t, *thread_inspect_t, *thread_read_t;
...
#endif /* KERNEL */
...
/*
* If we are not in the kernel, then these will all be represented by
* ports at user-space.
*/
...
typedef mach_port_t thread_t;
...
and since we're looking at the kernel implementation, it will indeed use the struct thread definition for thread_t.
For uthread_t, it is type defined where struct uthread is defined: [bsd/sys/user.h].
typedef struct uthread * uthread_t;
We'll explore the attributes of struct uthread later. For now, let's inspect the get_bsdthread_info function which apparently takes thread_t as its argument and returns a pointer to struct uthread. It is defined in [osfmk/kern/bsd_kern.c] as:
struct uthread *
get_bsdthread_info(thread_t th)
{
return (struct uthread *)((uintptr_t)th + sizeof(struct thread));
}
Notice that there is an offset of sizeof(struct thread) applied to the pointer to struct thread (which is essentially what th is above). This would mean that a Mach thread structure is immediately followed by the corresponding user thread structure, in terms of addressing. Consequently, this function returns an address which lies just outside of struct thread and does an explicit cast to struct uthread *.
The reader should also be aware that a previous commit of this file shows a different variation of the get_bsdthread_info function. Instead of doing pointer arithmetic to fetch the address of the uthread structure, it would simply return the uthread field that was present in struct thread itself.
Processor Sets in XNU
Section 7.2.2 of [MOSX_INTERNALS] describes the purpose of Processor Set as:
The original motivation behind processor sets was to group processors to allocate them to specific system activities--a coarse-grained allocation. Moreover, in early versions of Mac OS X, processor sets had associated scheduling policies and attributes, which provided a uniform control of the scheduling aspects of the threads in the set.
When the kernel starts up, a default processor set (pset0 on XNU) is initialized. Except for the default processor set, a processor set may contain zero processors. The default processor set must contain at least one processor. Furthermore, a processor must belong to at most one processor set at a time.
Under Processor Management of Chapter 9 (Volume 2) of [OS_INTERNALS], the following remark could be found regarding processor set:
Recall, that Mach allows for multiple processor sets (and processor nodes), which allows to quickly and easily scale up to Non-Uniform Memory Access (NUMA) and other distributed processing architectures. Apple, however, has neutered this in XNU, relying only on the single
pset0- returned by themach_hostsubsystem'sprocessor_set_default()Mach Interface Generator (MIG) routine. There are, at the time of writing, hints that this may finally change - at least in iOS, wherein the A-series processor's cores are asymmetric ("P"erformance cores and "E"fficiency cores), and could thus greatly benefit from a multi-set configuration.
Asymmetric and heterogeneous computing (both covered in Symmetric vs Asymmetric Multiprocessing) use the notion of P and E cores described above; terminology commonly found in Intel implementations. Nevertheless, we notice that there is only one processor set object on the system; pset0.
Figure 7-2 of [MOSX_INTERNALS] has the following definition of structure processor_set:
// osfmk/kern/processor.h
struct processor_set {
queue_head_t idle_queue; // queue of idle processors
int idle_count; // how many idle processors?
queue_head_t active_queue; // queue of active processors
queue_head_t processors; // queue of all processors
...
};
extern struct processor_set default_set;
In the current implementation, there is no explicit attribute on the processor set object that references the processor object.
Supplementary Notes
[MICRO_MONO_KERN] Despite their differences, many authors agree that the theoretical architectures often misrepresent the actual implementation. The labels describe a spectrum rather than two disjoint categories.
Linux is the textbook monolithic kernel, yet parts of it have moved in the microkernel direction: FUSE lets a filesystem--ordinarily core kernel territory--run as an ordinary userspace process, and frameworks like VFIO and DPDK do the same for device drivers and network stacks. Relocating a subsystem out of the privileged address space is the property microkernels are actually defined by. Linux separately accepts user-supplied programs into the kernel: classic BPF has let userspace install filter bytecode, checked by an in-kernel verifier, since Linux adopted it for socket filtering in 1997, and eBPF later generalized this well beyond packet filtering. This extends the kernel rather than decomposing it: such programs run in kernel space at kernel privilege, sandboxed by static verification rather than by address-space separation. Still, it addresses the same complaint that motivated microkernel research: that a monolithic kernel cannot be extended without rebuilding it. For a deeper look at the classic BPF packet filter and its instruction set, see Berkeley Packet Filter (BPF): Packet Capture and Filtering in C.
XNU converges from the other direction. Mach is a microkernel, but XNU is not a microkernel-based system in the strict sense: Mach, BSD, and the I/O Kit are compiled into a single kernel address space and call one another directly rather than through Mach IPC. The kernel task--one task with many threads--carries scheduling, thread reaping, callout management, paging, and UNIX exception handling. Note that scheduling, paging, and IPC are precisely the responsibilities a microkernel keeps; what makes XNU monolithic is not that list, but that the BSD and I/O Kit layers sit beside Mach in the same address space with no protection boundary between them.
[BSD_ON_MACH] Although one might assume that the Mach kernel is a BSD-like kernel, this is not entirely correct. The BSD kernel found in the OS X implementation is based upon FreeBSD. The Kernel Architecture Overview chapter illustrates the OS X architecture. We can observe that the BSD layer is built on top of the Mach kernel. In fact, a BSD process is one which has a corresponding Mach task assigned to it, but not all Mach tasks are required to have a BSD process.
[MD_THREAD_CTX] For the ARM variant, the type arm_context_t is defined as typedef struct arm_context arm_context_t;, and this structure in turn contains another structure member, struct arm_saved_state ss;, along with a member for ARM NEON state (which we won't look into). struct arm_saved_state contains a union member, union { struct arm_saved_state32; struct arm_saved_state64; } uss;. Looking into either structure declared within the union, we observe that it contains the array of general purpose registers along with "special" registers such as the stack pointer and frame pointer.
[PROC_SIGACTS] For details on the sigacts structure and how Mach exceptions are translated to UNIX signals via ux_exception.
[LIST_ENTRY] The macro used, LIST_ENTRY, is defined under [bsd/sys/queue.h] as:
#define LIST_ENTRY(type) \
__MISMATCH_TAGS_PUSH \
__NULLABILITY_COMPLETENESS_PUSH \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
} \
__NULLABILITY_COMPLETENESS_POP \
__MISMATCH_TAGS_POP
Although not needed, __MISMATCH_TAGS_PUSH and __MISMATCH_TAGS_POP expand to pragma operators for C++ (and when using clang(1) compiler), else to nothing. These macros too are defined in [bsd/sys/queue.h] with definition as:
#if defined(__clang__) && defined(__cplusplus)
#define __MISMATCH_TAGS_PUSH \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wmismatched-tags\"")
#define __MISMATCH_TAGS_POP \
_Pragma("clang diagnostic pop")
#else
#define __MISMATCH_TAGS_PUSH
#define __MISMATCH_TAGS_POP
#endif
Likewise, __NULLABILITY_COMPLETENESS_PUSH and __NULLABILITY_COMPLETENESS_POP only expand to pragma when compiling with clang. It is also defined in [bsd/sys/queue.h]. The definition is as follows:
#if defined(__clang__)
#define __NULLABILITY_COMPLETENESS_PUSH \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wnullability-completeness\"")
#define __NULLABILITY_COMPLETENESS_POP \
_Pragma("clang diagnostic pop")
#else
#define __NULLABILITY_COMPLETENESS_PUSH
#define __NULLABILITY_COMPLETENESS_POP
#endif
[TAILQ_ENTRY] The macro TAILQ_ENTRY is defined in [bsd/sys/queue.h] as:
#define TRACEBUF struct qm_trace trace;
#define TAILQ_ENTRY(type) \
__MISMATCH_TAGS_PUSH \
__NULLABILITY_COMPLETENESS_PUSH \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
TRACEBUF \
} \
__NULLABILITY_COMPLETENESS_POP \
__MISMATCH_TAGS_POP
Except for the presence of another macro: TRACEBUF, it is similar to one described in [LIST_ENTRY].
XNU Implementation Details
The implementation patterns below make extensive use of C preprocessor techniques such as conditional compilation with __has_attribute and token pasting. For a focused walkthrough of how these techniques work in Apple's API_AVAILABLE macro, see How the API_AVAILABLE Macro Works on macOS.
This section documents some of the implementation found in the XNU source tree.
1. [XNU_PTRAUTH_SIGNED_PTR] In the source, we see the field declared as:
struct task {
...
vm_map_t XNU_PTRAUTH_SIGNED_PTR("task.map") map;
...
}
The arm64e specification introduced the notion of Pointer Authentication Code. Various articles are available on the web which explain this topic. If you want to explore it, Pointer Authentication documentation from clang explains this topic clearly. XNU_PTRAUTH_SIGNED_PTR is a macro which is defined (in [libkern/os/base.h]) as:
#if KERNEL
#if __has_feature(ptrauth_calls)
#define XNU_PTRAUTH_SIGNED_FUNCTION_PTR(type) \
__ptrauth(ptrauth_key_function_pointer, 1, ptrauth_string_discriminator(type))
#else
#define XNU_PTRAUTH_SIGNED_FUNCTION_PTR(type)
#endif
#define XNU_PTRAUTH_SIGNED_PTR OS_PTRAUTH_SIGNED_PTR
#define XNU_PTRAUTH_SIGNED_PTR_AUTH_NULL OS_PTRAUTH_SIGNED_PTR_AUTH_NULL
#endif // KERNEL
The macro OS_PTRAUTH_SIGNED_PTR is also defined under the same file, [libkern/os/base.h], as:
#if KERNEL
#if __has_feature(ptrauth_calls)
#include <ptrauth.h>
#define OS_PTRAUTH_SIGNED_PTR(type) __ptrauth(ptrauth_key_process_independent_data, 1, ptrauth_string_discriminator(type))
#define OS_PTRAUTH_SIGNED_PTR_AUTH_NULL(type) __ptrauth(ptrauth_key_process_independent_data, 1, ptrauth_string_discriminator(type), "authenticates-null-values")
#define OS_PTRAUTH_DISCRIMINATOR(str) ptrauth_string_discriminator(str)
#define __ptrauth_only
#else // __has_feature(ptrauth_calls)
#define OS_PTRAUTH_SIGNED_PTR(type)
#define OS_PTRAUTH_SIGNED_PTR_AUTH_NULL(type)
#define OS_PTRAUTH_DISCRIMINATOR(str) 0
#define __ptrauth_only __unused
#endif // __has_feature(ptrauth_calls)
#endif // KERNEL
2. [AST_ENUM_OPT_DECL] In the source, we see the field declared as: os_atomic(ast_t) ast;. os_atomic is a parameterized macro that expands conditionally. In [libkern/os/atomic.h], there are two (conditional) macro definitions for os_atomic. From what I can tell, if C++ is used, it is defined as: #define os_atomic(type) std::atomic<type> volatile, and otherwise defined as: #define os_atomic(type) type volatile _Atomic.
ast_t is an enumeration type, defined (in [osfmk/kern/ast.h]) as:
__options_decl(ast_t, uint32_t, {
AST_PREEMPT = 0x01,
AST_QUANTUM = 0x02,
AST_URGENT = 0x04,
AST_HANDOFF = 0x08,
AST_YIELD = 0x10,
AST_APC = 0x20, /* migration APC hook */
AST_LEDGER = 0x40,
AST_BSD = 0x80,
AST_KPERF = 0x100, /* kernel profiling */
AST_MACF = 0x200, /* MACF user ret pending */
AST_RESET_PCS = 0x400, /* restartable ranges */
AST_ARCADE = 0x800, /* arcade subsciption support */
AST_MACH_EXCEPTION = 0x1000,
AST_TELEMETRY_USER = 0x2000, /* telemetry sample requested on interrupt from userspace */
AST_TELEMETRY_KERNEL = 0x4000, /* telemetry sample requested on interrupt from kernel */
AST_TELEMETRY_PMI = 0x8000, /* telemetry sample requested on PMI */
AST_SFI = 0x10000, /* Evaluate if SFI wait is needed before return to userspace */
AST_DTRACE = 0x20000,
AST_TELEMETRY_IO = 0x40000, /* telemetry sample requested for I/O */
AST_KEVENT = 0x80000,
AST_REBALANCE = 0x100000, /* thread context switched due to rebalancing */
// was AST_UNQUIESCE 0x200000
AST_PROC_RESOURCE = 0x400000, /* port space and/or file descriptor table has reached its limits */
AST_DEBUG_ASSERT = 0x800000, /* check debug assertion */
AST_TELEMETRY_MACF = 0x1000000, /* telemetry sample requested by MAC framework */
AST_SYNTHESIZE_MACH = 0x2000000,
});
where __options_decl is a parameterized macro (defined in [bsd/sys/cdefs.h]) which expands as:
#if __has_attribute(enum_extensibility)
#define __enum_open __attribute__((__enum_extensibility__(open)))
#define __enum_closed __attribute__((__enum_extensibility__(closed)))
#else
#define __enum_open
#define __enum_closed
#endif // __has_attribute(enum_extensibility)
#if __has_attribute(flag_enum)
#define __enum_options __attribute__((__flag_enum__))
#else
#define __enum_options
#endif
#define __options_decl(_name, _type, ...) \
typedef enum : _type __VA_ARGS__ __enum_open __enum_options _name
The internal type used for an enumeration type is an integer. It is implementation-defined whether the integer is signed or unsigned. The width of the integer is usually that of the int data type.
If we want to override the default implementation of the enumeration type (like shown above), the syntax would be:
enum : unsigned int {
FOO,
BAR,
BAZ
};
This is not defined in the C standard, but most compilers for the C language support this extension for an enumeration type.
4. See TAILQ_ENTRY
Mach Kernel Routines
The description of most of these routines can be found in MIT: Mach IPC Interface
-
mach_task_self: Return a send right to the caller's task_self port.#include <mach/mach_traps.h>
/*
* param: none
*
* return: send right to the task's kernel port.
*/
mach_port_t
mach_task_self (void);cautionUnder
mach/mach_init.hfile, we see the declaration ofmach_task_selfas:extern mach_port_t mach_task_self_;
#define mach_task_self() mach_task_self_
#define current_task() mach_task_self() -
task_info: Return per-task information according to specified flavor.#include <mach/task.h>
/*
* params:
* task - The port of the task for which the information is to be returned.
* flavor - The type of information to be returned. See below.
* task_info - Information about the specified task.
* task_info_count - On input, the maximum size of the buffer; on output, the size returned (in natural-sized units).
*/
kern_return_t
task_info ( task_t task,
task_flavor_t flavor,
task_info_t task_info,
mach_msg_type_number_t task_info_count);The table below describes the various arguments to flavor, task_info, and task_info_count that are currently implemented in XNU (specifically [osfmk/kern/task.c]).
flavor task_info (address of) task_info_count Remark TASK_BASIC_INFO_32 struct task_basic_info_32 (task_basic_info_32_data_t) TASK_BASIC_INFO_32_COUNT Not preferred; use MACH_TASK_BASIC_INFO TASK_BASIC2_INFO_32 struct task_basic_info_32 (task_basic_info_32_data_t) TASK_BASIC_INFO_32_COUNT Not preferred; BASIC2 was used to get the maximum resident size instead of current resident size TASK_BASIC_INFO_64 struct task_basic_info_32 (task_basic_info_32_data_t) TASK_BASIC_INFO_32_COUNT Not preferred; only available on arm64 (non-arm64 described below) TASK_BASIC_INFO_64 struct task_basic_info_64 (task_basic_info_64_data_t) TASK_BASIC_INFO_64_COUNT Not preferred; used on non-arm64 variant TASK_BASIC_INFO_64_2 struct task_basic_info_64_2 (task_basic_info_64_2_data_t) TASK_BASIC_INFO_64_2_COUNT Not preferred; only available on arm64 MACH_TASK_BASIC_INFO struct mach_task_basic_info (mach_task_basic_info_data_t) MACH_TASK_BASIC_INFO_COUNT - TASK_THREAD_TIMES_INFO struct task_thread_times_info (task_thread_times_info_data_t) TASK_THREAD_TIMES_INFO_COUNT - TASK_ABSOLUTETIME_INFO struct task_absolutetime_info (task_absolutetime_info_data_t) TASK_ABSOLUTETIME_INFO_COUNT - TASK_DYLD_INFO struct task_dyld_info (task_dyld_info_data_t) TASK_DYLD_INFO_COUNT After the addition of all_image_info_formatfield,task_info_countcould be either TASK_DYLD_INFO_COUNT or the backward compatible TASK_LEGACY_DYLD_INFO_COUNTTASK_EXTMOD_INFO struct task_extmod_info (task_extmod_info_data_t) TASK_EXTMOD_INFO_COUNT - TASK_KERNELMEMORY_INFO struct task_kernelmemory_info (task_kernelmemory_info_data_t) TASK_KERNELMEMORY_INFO_COUNT - TASK_SCHED_FIFO_INFO struct policy_fifo_base (policy_fifo_base_data_t) POLICY_FIFO_BASE_COUNT Obsolete; see below TASK_SCHED_RR_INFO struct policy_rr_base (policy_rr_base_data_t) POLICY_RR_BASE_COUNT Obsolete; see below TASK_SCHED_TIMESHARE_INFO struct policy_timeshare_base (policy_timeshare_base_data_t) POLICY_TIMESHARE_BASE_COUNT Obsolete; see below TASK_SECURITY_TOKEN security_token_t (defined in mach/message.h)TASK_SECURITY_TOKEN_COUNT - TASK_AUDIT_TOKEN audit_token_t (defined in mach/message.h)TASK_AUDIT_TOKEN_COUNT - TASK_SCHED_INFO - - Obsolete TASK_EVENTS_INFO struct task_events_info (task_events_info_data_t) TASK_EVENTS_INFO_COUNT - TASK_AFFINITY_TAG_INFO struct task_affinity_tag_info (task_affinity_tag_info_data_t) TASK_AFFINITY_TAG_INFO_COUNT On task_info implementation of XNU, it appears to internally call task_affinity_infowhich is defined in [osfmk/kern/affinity.c]TASK_POWER_INFO struct task_power_info (task_power_info_data_t) TASK_POWER_INFO_COUNT On task_info implementation of XNU, it appears to internally call task_power_info_lockedwhich is defined in [osfmk/kern/task.c]TASK_POWER_INFO_V2 struct task_power_info_v2 (task_power_info_v2_data_t) TASK_POWER_INFO_V2_COUNT (or TASK_POWER_INFO_V2_COUNT_OLD) On task_info implementation of XNU, it only checks for TASK_POWER_INFO_V2_COUNT_OLD, and also calls task_power_info_lockedTASK_VM_INFO struct task_vm_info (task_vm_info_data_t) TASK_VM_INFO_COUNT The value of task_info_countcould also beTASK_VM_INFO_REV[0-7]_COUNT, whereTASK_INFO_REV7_COUNTis identical toTASK_VM_INFO_COUNT. The structure definition provides additional attributes for each new revision. Some additional checks is performed on arm64 architecture on Iphones (some apps passtask_info_countas count of bytes instead of count ofnatural_t) ...TASK_VM_INFO_PURGEABLE struct task_vm_info (task_vm_info_data_t) TASK_VM_INFO_COUNT ... and if the task is a kernel task, the internalfield oftask_vm_info_data_tis adjusted by considering the "memory held in VM compressor". Furthermore, whenTASK_VM_INFO_PURGEABLEis used,purgeable_volatile_[pmap|resident|virtual]attributes are appropriately returned. A series of checks for revision is performed and appropriate attributes are filledTASK_WAIT_STATE_INFO struct task_wait_state_info (task_wait_state_info_data_t) TASK_WAIT_STATE_INFO_COUNT Marked as deprecated with further note as, "Currently allowing some results until all users stop calling it. The results may not be accurate." TASK_VM_INFO_PURGEABLE_ACCOUNT struct pvm_account_info (pvm_account_info_data_t) PVM_ACCOUNT_INFO_COUNT Only visible in development builds ( DEVELOPMENTorDEBUGsymbol must be present while compiling the kernel). ReturnsKERN_NOT_SUPPORTEDwhen used on production buildsTASK_FLAGS_INFO struct task_flags_info (task_flags_info_data_t) TASK_FLAGS_INFO_COUNT - TASK_DEBUG_INFO_INTERNAL struct task_debug_info_internal (task_debug_info_internal_data_t) TASK_DEBUG_INFO_INTERNAL_COUNT Only visible in development builds ( DEVELOPMENTorDEBUGsymbol must be present while compiling the kernel). ReturnsKERN_NOT_SUPPORTEDwhen used on production buildsTASK_SUSPEND_STATS_INFO struct task_suspend_stats_s (task_suspend_stats_data_t) TASK_SUSPEND_STATS_INFO_COUNT Requires CONFIG_TASK_SUSPEND_STATSsymbol to be present along with either of debug symbol (DEVELOPMENTorDEBUG) when compiling the kernel to support this flavor. ReturnsKERN_NOT_SUPPORTEDwhen used on production buildsTASK_SUSPEND_SOURCES_INFO struct task_suspend_source_s (task_suspend_source_data_t) TASK_SUSPEND_SOURCES_INFO_COUNT Requires CONFIG_TASK_SUSPEND_STATSsymbol to be present along with either of debug symbol (DEVELOPMENTorDEBUG) when compiling the kernel to support this flavor. ReturnsKERN_NOT_SUPPORTEDwhen used on production buildsTASK_SECURITY_CONFIG_INFO struct task_security_config_info TASK_SECURITY_CONFIG_INFO_COUNT - TASK_IPC_SPACE_POLICY_INFO struct task_ipc_space_policy_info TASK_IPC_SPACE_POLICY_INFO_COUNT - warningRegarding
TASK_SCHED_FIFO_INFO,TASK_SCHED_RR_INFO, andTASK_SCHED_TIMESHARE_INFO, until commit [c1dac77], XNU provided appropriatetask_infohandling capability. However, starting commit [8149afc], support forTASK_SCHED_FIFO_INFOwas removed. Although capability forTASK_SCHED_RR_INFOandTASK_SCHED_TIMESHARE_INFOstill exist, commit [14e3d83] marked them as obsolete.
References
[DEV_APPLE_MACH] https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/Mach/Mach.html
[MOSX_INTERNALS] Singh, A (2007). Mac OS X Internals: A Systems Approach.
[OS_INTERNALS] Levin, J (2016, 2017, 2019) MacOS and iOS Internals Trilogy. https://www.newosxbook.com/index.php
[XNU_GIT] https://github.com/apple-oss-distributions/xnu
Source references in this post are against XNU commit f6217f8 (xnu-12377.1.9).
