Darwin XNU

Darwin 内核(镜像)。XNU 内核是 Darwin 操作系统的一部分,用于 macOS 和 iOS 操作系统。XNU 是 X is Not Unix 的缩写。【 The Darwin Kernel (mirror). XNU kernel is part of the Darwin operating system for use in macOS and iOS operating systems. XNU is an acronym for X is Not Unix. 】

  • 所有者: apple/darwin-xnu
  • 平台: iOS, Mac
  • 許可證: Other
  • 分類:
  • 主題:
  • 喜歡:
    0
      比較:

Github星跟蹤圖

什么是XNU?

XNU 内核是用于 OS X 和 iOS 操作系统的 Darwin 操作系统的一部分。 XNU 是 XNU 不是 Unix 的缩写。 XNU 是一个混合内核,将卡内基梅隆大学开发的 Mach 内核与 FreeBSD 和 C++ API 的组件结合起来,用于编写称为 IOKit 的驱动程序。 XNU 可在 I386、X86_64 上运行,适用于单处理器和多处理器配置。

XNU源码树

  • config - 支持架构和平台的导出apis配置
  • SETUP - 用于配置内核,版本控制和kextsymbol管理的基本工具集。
  • EXTERNAL_HEADERS - 来自其他项目的标题,以避免构建时的依赖性循环。更新源时,应定期同步这些标头。
  • libkern - 用于处理驱动程序和kexts的C ++ IOKit库代码。
  • libsa - 用于启动的内核引导代码
  • libsyscall - 用户空间程序的系统调用库接口
  • libkdd - 解析内核数据(如内核分块数据)的用户库源码。
  • makedefs - 内核构建的顶级规则和定义。
  • osfmk - 基于Mach内核的子系统
  • pexpert - 特定于平台的代码,如中断处理,原子等。
  • security - 强制访问检查策略接口和相关实现。
  • bsd - BSD子系统代码
  • tools - 一组用于测试,调试和分析内核的实用程序。

如何构建XNU

构建开发内核

xnu make 系统可以基于 KERNEL_CONFIGS 和 ARCH_CONFIGS 变量作为参数。 这里是语法:

make SDKROOT=<sdkroot> ARCH_CONFIGS=<arch> KERNEL_CONFIGS=<variant>

其中:

  • :磁盘上macos sdk的路径。(默认为 /)
  • :可以是 debug,development,release,profile 并配置编译标志和声明贯穿整个内核代码。
  • :可以是有效的构建版本。 (例如 i386 或 X86_64 )

要构建与运行OS相同的体系结构的内核,只需键入

$ make
$ make SDKROOT=macosx.internal

此外,还支持通过 ARCH_CONFIGS 和 KERNEL_CONFIGS 配置内核配置。

$ make SDKROOT=macosx.internal ARCH_CONFIGS=X86_64 KERNEL_CONFIGS=DEVELOPMENT
$ make SDKROOT=macosx.internal ARCH_CONFIGS=X86_64 KERNEL_CONFIGS="RELEASE DEVELOPMENT DEBUG"

注意:

  • 默认情况下,体系结构设置为构建机器体系结构和默认内核 config已设置为开发。

这也将创建一个可启动映像,内核。[config]和一个内核二进制文件 带符号,内核。[config].unstripped。

  • 使用 RELEASE 内核配置进行构建
    make KERNEL_CONFIGS=RELEASE SDKROOT=/path/to/SDK
        

建立FAT内核二进制文件

在您的环境中或运行make命令时定义体系结构。

$ make ARCH_CONFIGS="X86_64" exporthdrs all

其他makefile选项

  • $ make MAKEJOBS=-j8 #这将在构建期间使用8个进程。默认值是活动CPUS数量的两倍。
  • $ make -j8 #标准命令行选项也被接受
  • $ make -w #trace递归调用。与VERBOSE = YES
  • 结合使用
  • $ make BUILD_LTO=0 #无LLVM链接时间优化版本
  • $ make REMOTEBUILD=user@remotehost #在远程主机上执行构建
  • $ make BUILD_JSON_COMPILATION_DATABASE=1 #构建Clang JSON编译数据库

XNU构建系统可以选择输出颜色格式的构建输出。要启用它,你也可以 将 XNU_LOGCOLORS 环境变量设置为 y ,或者将 LOGCOLORS=y 传递给make命令。

调试信息格式

默认情况下,在安装阶段会创建一个DWARF调试信息库;这是一个名为kernel.development 的“bundle”。.dSYM 要选择较旧的STABS调试信息格式(调试信息嵌入在kernel.development.unstripped映像中),请设置BUILD_STABS环境变量。

$ export BUILD_STABS=1
$ make

构建KernelCaches

为了测试xnu内核,你需要建立一个连接kext和kernel的kernelcache 内核一起成为一个可启动的映像。 构建一个kernelcache哟你可以使用以下机制:

  • 使用 kextd 自动生成kernelcache。 kextd守护进程一直在观察/System/Library/Extensions 目录中的更改。 所以你可以将新内核设置为
    $ cp BUILD/obj/DEVELOPMENT/X86_64/kernel.development /System/Library/Kernels/
    $ touch /System/Library/Extensions
    $ ps -e | grep kextd
        
  • 手动调用 kextcache 来构建新的kernelcache。
    $ kextcache -q -z -a x86_64 -l -n -c /var/tmp/kernelcache.test -K /var/tmp/kernel.test /System/Library/Extensions
        

在目标机器上运行KernelCache

开发内核和iBoot支持配置引导参数,以便我们可以安全地引导到测试内核,并且如果出现问题,安全地回退到先前使用的kernelcache。 以下是获取此类设置的步骤:

  1. 使用kextcache命令创建内核缓存为 /kernelcache.test
  2. 将退出的启动配置复制到备用文件
    $ cp /Library/Preferences/SystemConfiguration/com.apple.Boot.plist /next_boot.plist
        
  3. 更新您的设置的kernelcache和boot-args
    $ plutil -insert "Kernel Cache" -string "kernelcache.test" /next_boot.plist
    $ plutil -replace "Kernel Flags" -string "debug=0x144 -v kernelsuffix=test " /next_boot.plist
        
  4. 将新配置复制到/Library/Preferences/SystemConfiguration/
    $ cp /next_boot.plist /Library/Preferences/SystemConfiguration/boot.plist
        
  5. 用新的配置保佑这个卷。
    $ sudo -n bless  --mount / --setBoot --nextonly --options "config=boot"
        
    - nextonly 标志指定仅为一次引导使用 boot.plist 配置。 因此,如果内核出现恐慌,您可以轻松启动重新启动并恢复到原始内核。

创建标签和cscope

设置您的构建环境,并从顶部目录运行:

  $ make tags #这将在区分大小写的卷上构建ctags和etags,只对大小写不敏感的ctags
$ make TAGS #这将构建etags
$ make cscope #这将构建cscope数据库

编码样式(重新加载文件)

源文件可以用.clang格式的clang格式设置进行重新格式化。 XNU遵循用于源代码格式的WebKit风格的变体。 请参阅 WebKit网站上的格式样式。 有关样式选项的更多选项,请参阅 clang文档

注意:clang格式的二进制文件可能不是基本安装的一部分。它可以从llvm clang源码进行编译,并且可以在$ PATH中访问。

从顶层目录运行:

$ make reindent #使用clang格式重新生成所有源文件。

如何从XNU安装新的头文件

要安装IOKit标头,请参阅 iokit/IOKit/Makefile 中的其他注释。

XNU在以下位置安装头文件 -

a. $(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers
b. $(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders
c. $(DSTROOT)/usr/include/
d. $(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders

内核扩展使用 Kernel.framework。

用户级应用程序使用 System.framework 和/usr/include 。 \ Framework的 PrivateHeaders 中的头文件仅适用于 Apple内部开发

包含头文件的目录应该有一个Makefile 创建应该安装在不同位置的文件列表。 如果您要在目录中添加第一个头文件,您将需要 创建类似于xnu/bsd/sys/Makefile的Makefile。

根据你想要的位置将你的头文件添加到正确的文件列表中 安装它。头文件安装的默认位置 从每个文件列表 -

a. `DATAFILES` : 使用户级别的头文件可用 -
   `$(DSTROOT)/usr/include`
b. `PRIVATE_DATAFILES` : 使用户级别的头文件可供Apple内部使用 -
   `$(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders`
c. `KERNELFILES` : 使内核级别的头文件可用 -
   `$(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers`
   `$(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders`
d. `PRIVATE_KERNELFILES` : 使头文件可供Apple内部使用于内核扩展 -
   `$(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders`

Makefile结合了上面提到的文件列表进入不同的 安装构建系统用来安装头文件的列表。

如果您感兴趣的安装列表不存在,请创建它 通过添加适当的文件列表。默认安装列表,它的 成员文件列表及其默认位置如下所述 -

a. `INSTALL_MI_LIST` : Installs header file to a location that is available to everyone in user level.
    Locations -
       $(DSTROOT)/usr/include
   Definition -
       INSTALL_MI_LIST = ${DATAFILES}
b.  `INSTALL_MI_LCL_LIST` : Installs header file to a location that is available
   for Apple internal in user level.
   Locations -
       $(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders
   Definition -
       INSTALL_MI_LCL_LIST = ${PRIVATE_DATAFILES}
c. `INSTALL_KF_MI_LIST` : Installs header file to location that is available
   to everyone for kernel extensions.
   Locations -
        $(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers
   Definition -
        INSTALL_KF_MI_LIST = ${KERNELFILES}
d. `INSTALL_KF_MI_LCL_LIST` : Installs header file to location that is
   available for Apple internal for kernel extensions.
   Locations -
        $(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders
   Definition -
        INSTALL_KF_MI_LCL_LIST = ${KERNELFILES} ${PRIVATE_KERNELFILES}
e. `EXPORT_MI_LIST` : Exports header file to all of xnu (bsd/, osfmk/, etc.)
   for compilation only. Does not install anything into the SDK.
   Definition -
        EXPORT_MI_LIST = ${KERNELFILES} ${PRIVATE_KERNELFILES}

使用这些步骤,单个头文件可以存在于不同的位置 上文提到的。但是,制作所有代码可能并不理想 在所有位置可用的头文件中。例如,你 希望将函数仅导出到内核级别,而不是用户级别。

您可以使用C语言的预处理器指令(#ifdef,#endif,#ifndef) 控制安装头文件之前生成的文本。内核 如果条件宏是TRUE并且去掉,只包含代码 代码为头文件中的FALSE条件。

一些预定义的宏和它们的描述是 -

a. `PRIVATE` : If defined, enclosed definitions are considered System
Private Interfaces. These are visible within xnu and
exposed in user/kernel headers installed within the AppleInternal
"PrivateHeaders" sections of the System and Kernel frameworks.
b. `KERNEL_PRIVATE` : If defined, enclosed code is available to all of xnu
kernel and Apple internal kernel extensions and omitted from user
headers.
c. `BSD_KERNEL_PRIVATE` : If defined, enclosed code is visible exclusively
within the xnu/bsd module.
d. `MACH_KERNEL_PRIVATE`: If defined, enclosed code is visible exclusively
within the xnu/osfmk module.
e. `XNU_KERNEL_PRIVATE`: If defined, enclosed code is visible exclusively
within xnu.
f. `KERNEL` :  If defined, enclosed code is available within xnu and kernel
   extensions and is not visible in user level header files.  Only the
   header files installed in following paths will have the code -
        $(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers
        $(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders

如何添加新的系统调用

测试内核

XNU内核有多种测试机制。

  • 断言 - 开发和DEBUG内核配置在启用断言的情况下编译。这使开发人员可以轻松 测试不变量和条件。
  • XNU开机自检( XNUPOST ):XNUPOST配置允许使用基本的测试功能组建内核 这是在第一个用户空间进程启动之前运行的。由于XNU是MACH和BSD的混合体,我们有两个位置可以添加测试。
    xnu/osfmk/tests /#用于测试基于mach的内核结构和apis。
    bsd/tests / #用于测试BSD接口。
        
    请按照 osfmk/tests/README.md 的文档进行操作。
  • 用户级别测试: tools/tests/目录包含验证xnu内核的系统调用和其他功能的所有测试。 make目标 xnu_tests 可用于构建所有支持的测试。
    $ make RC_ProjectName = xnu_tests SDKROOT =/path/to/SDK
        
    这些测试是个别程序,可以从终端运行,并通过std posix退出代码(0 ->sucess)和/或 stdout报告测试状态。 请阅读 tools/tests/unit_tests/README.md

内核数据描述符

XNU使用不同的数据格式在其api中传递数据。最标准的方法是使用系统调用参数。但对于复杂的数据,它通常依靠发送由C结构保存的内存。这种打包的数据传输机制很脆弱,并导致接口损坏 在用户空间程序和内核apis之间。 libkdd 目录保存用户空间库,该库可以解析由提供的自定义数据 相同版本的内核。内核分块数据格式在 libkdd/README.md中有详细描述。

调试内核

xnu内核支持使用远程内核调试协议(kdp)进行调试。请参阅技术说明中的文档。 默认情况下,内核被设置为在恐慌时重新启动。为了调试活动的内核,kdp服务器设置为通过以太网侦听UDP连接。对于没有以太网端口的机器,这种行为可以通过使用内核引导参数来改变。以下是一些常见选项。

  • debug=0x144 - 设置调试变量以在恐慌时启动kdp debugserver
  • -v - 在屏幕上打印内核日志。默认情况下,XNU只显示启动画面的灰色屏幕。
  • kdp_match_name=en1 - 覆盖kdp的默认端口选择。支持以太网,Thunderbolt和串口调试。

要调试一个崩溃的内核,请使用llvm调试器(lldb)以及未提取的符号丰富的内核二进制文件。

  sh $lldb kernel.development.unstripped

然后,您可以使用 kdp_remote [ip addr] 或 gdb_remote [hostip:port] 命令连接到崩溃的计算机。 每个内核都与内核特定的调试脚本一起打包,作为构建过程的一部分。出于安全原因,这些特殊命令 当lldb连接到机器时脚本不会自动加载。请将以下设置添加到〜/.lldbinit 中,如果你想永远加载这些宏。

 settings set target.load-script-from-symbol-file true

tools/lldbmacros 目录包含每个命令的源代码。请按照 README.md 进行操作 有关命令及其用法的详细说明。

主要指標

概覽
名稱與所有者apple/darwin-xnu
主編程語言C
編程語言C (語言數: 18)
平台iOS, Mac
許可證Other
所有者活动
創建於2017-06-02 21:33:51
推送於2023-01-13 01:48:01
最后一次提交
發布數123
最新版本名稱xnu-7195.121.3 (發布於 2021-05-27 23:50:47)
第一版名稱xnu-123.5 (發布於 2003-08-08 20:38:03)
用户参与
星數11.1k
關注者數563
派生數1.7k
提交數123
已啟用問題?
問題數0
打開的問題數0
拉請求數0
打開的拉請求數3
關閉的拉請求數29
项目设置
已啟用Wiki?
已存檔?
是復刻?
已鎖定?
是鏡像?
是私有?

What is XNU?

XNU kernel is part of the Darwin operating system for use in macOS and iOS operating systems. XNU is an acronym for X is Not Unix.
XNU is a hybrid kernel combining the Mach kernel developed at Carnegie Mellon University with components from FreeBSD and a C++ API for writing drivers called IOKit.
XNU runs on x86_64 for both single processor and multi-processor configurations.

XNU Source Tree

  • config - configurations for exported apis for supported architecture and platform
  • SETUP - Basic set of tools used for configuring the kernel, versioning and kextsymbol management.
  • EXTERNAL_HEADERS - Headers sourced from other projects to avoid dependency cycles when building. These headers should be regularly synced when source is updated.
  • libkern - C++ IOKit library code for handling of drivers and kexts.
  • libsa - kernel bootstrap code for startup
  • libsyscall - syscall library interface for userspace programs
  • libkdd - source for user library for parsing kernel data like kernel chunked data.
  • makedefs - top level rules and defines for kernel build.
  • osfmk - Mach kernel based subsystems
  • pexpert - Platform specific code like interrupt handling, atomics etc.
  • security - Mandatory Access Check policy interfaces and related implementation.
  • bsd - BSD subsystems code
  • tools - A set of utilities for testing, debugging and profiling kernel.

How to build XNU

Building DEVELOPMENT kernel

The xnu make system can build kernel based on KERNEL_CONFIGS & ARCH_CONFIGS variables as arguments.
Here is the syntax:

make SDKROOT=<sdkroot> ARCH_CONFIGS=<arch> KERNEL_CONFIGS=<variant>

Where:

  • <sdkroot>: path to macOS SDK on disk. (defaults to /)
  • <variant>: can be debug, development, release, profile and configures compilation flags and asserts throughout kernel code.
  • <arch> : can be valid arch to build for. (E.g. X86_64)

To build a kernel for the same architecture as running OS, just type

$ make
$ make SDKROOT=macosx.internal

Additionally, there is support for configuring architectures through ARCH_CONFIGS and kernel configurations with KERNEL_CONFIGS.

$ make SDKROOT=macosx.internal ARCH_CONFIGS=X86_64 KERNEL_CONFIGS=DEVELOPMENT
$ make SDKROOT=macosx.internal ARCH_CONFIGS=X86_64 KERNEL_CONFIGS="RELEASE DEVELOPMENT DEBUG"

Note:

  • By default, architecture is set to the build machine architecture, and the default kernel
    config is set to build for DEVELOPMENT.

This will also create a bootable image, kernel.[config], and a kernel binary
with symbols, kernel.[config].unstripped.

  • To build with RELEASE kernel configuration

    make KERNEL_CONFIGS=RELEASE SDKROOT=/path/to/SDK
    

Building FAT kernel binary

Define architectures in your environment or when running a make command.

$ make ARCH_CONFIGS="X86_64" exporthdrs all

Other makefile options

  • $ make MAKEJOBS=-j8 # this will use 8 processes during the build. The default is 2x the number of active CPUS.
  • $ make -j8 # the standard command-line option is also accepted
  • $ make -w # trace recursive make invocations. Useful in combination with VERBOSE=YES
  • $ make BUILD_LTO=0 # build without LLVM Link Time Optimization
  • $ make REMOTEBUILD=user@remotehost # perform build on remote host
  • $ make BUILD_JSON_COMPILATION_DATABASE=1 # Build Clang JSON Compilation Database

The XNU build system can optionally output color-formatted build output. To enable this, you can either
set the XNU_LOGCOLORS environment variable to y, or you can pass LOGCOLORS=y to the make command.

Debug information formats

By default, a DWARF debug information repository is created during the install phase; this is a "bundle" named kernel.development.<variant>.dSYM
To select the older STABS debug information format (where debug information is embedded in the kernel.development.unstripped image), set the BUILD_STABS environment variable.

$ export BUILD_STABS=1
$ make

Building KernelCaches

To test the xnu kernel, you need to build a kernelcache that links the kexts and
kernel together into a single bootable image.
To build a kernelcache you can use the following mechanisms:

  • Using automatic kernelcache generation with kextd.
    The kextd daemon keeps watching for changing in /System/Library/Extensions directory.
    So you can setup new kernel as

    $ cp BUILD/obj/DEVELOPMENT/X86_64/kernel.development /System/Library/Kernels/
    $ touch /System/Library/Extensions
    $ ps -e, grep kextd
    
  • Manually invoking kextcache to build new kernelcache.

    $ kextcache -q -z -a x86_64 -l -n -c /var/tmp/kernelcache.test -K /var/tmp/kernel.test /System/Library/Extensions
    

Running KernelCache on Target machine

The development kernel and iBoot supports configuring boot arguments so that we can safely boot into test kernel and, if things go wrong, safely fall back to previously used kernelcache.
Following are the steps to get such a setup:

  1. Create kernel cache using the kextcache command as /kernelcache.test

  2. Copy exiting boot configurations to alternate file

    $ cp /Library/Preferences/SystemConfiguration/com.apple.Boot.plist /next_boot.plist
    
  3. Update the kernelcache and boot-args for your setup

    $ plutil -insert "Kernel Cache" -string "kernelcache.test" /next_boot.plist
    $ plutil -replace "Kernel Flags" -string "debug=0x144 -v kernelsuffix=test " /next_boot.plist
    
  4. Copy the new config to /Library/Preferences/SystemConfiguration/

    $ cp /next_boot.plist /Library/Preferences/SystemConfiguration/boot.plist
    
  5. Bless the volume with new configs.

    $ sudo -n bless  --mount / --setBoot --nextonly --options "config=boot"
    

    The --nextonly flag specifies that use the boot.plist configs only for one boot.
    So if the kernel panic's you can easily power reboot and recover back to original kernel.

Creating tags and cscope

Set up your build environment and from the top directory, run:

$ make tags     # this will build ctags and etags on a case-sensitive volume, only ctags on case-insensitive
$ make TAGS     # this will build etags
$ make cscope   # this will build cscope database

Coding styles (Reindenting files)

Source files can be reindented using clang-format setup in .clang-format.
XNU follows a variant of WebKit style for source code formatting.
Please refer to format styles at WebKit website.
Further options about style options is available at clang docs

Note: clang-format binary may not be part of base installation. It can be compiled from llvm clang sources and is reachable in $PATH.

From the top directory, run:

$ make reindent # reindent all source files using clang format.

How to install a new header file from XNU

To install IOKit headers, see additional comments in iokit/IOKit/Makefile.

XNU installs header files at the following locations -

a. $(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers
b. $(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders
c. $(DSTROOT)/usr/include/
d. $(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders

Kernel.framework is used by kernel extensions.
The System.framework and /usr/include are used by user level applications.
The header files in framework's PrivateHeaders are only available for ** Apple Internal Development **.

The directory containing the header file should have a Makefile that
creates the list of files that should be installed at different locations.
If you are adding the first header file in a directory, you will need to
create Makefile similar to xnu/bsd/sys/Makefile.

Add your header file to the correct file list depending on where you want
to install it. The default locations where the header files are installed
from each file list are -

a. `DATAFILES` : To make header file available in user level -
   `$(DSTROOT)/usr/include`

b. `PRIVATE_DATAFILES` : To make header file available to Apple internal in
   user level -
   `$(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders`

c. `KERNELFILES` : To make header file available in kernel level -
   `$(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers`
   `$(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders`

d. `PRIVATE_KERNELFILES` : To make header file available to Apple internal
   for kernel extensions -
   `$(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders`

The Makefile combines the file lists mentioned above into different
install lists which are used by build system to install the header files. There
are two types of install lists: machine-dependent and machine-independent.
These lists are indicated by the presence of MD and MI in the build
setting, respectively. If your header is architecture-specific, then you should
use a machine-dependent install list (e.g. INSTALL_MD_LIST). If your header
should be installed for all architectures, then you should use a
machine-independent install list (e.g. INSTALL_MI_LIST).

If the install list that you are interested does not exist, create it
by adding the appropriate file lists. The default install lists, its
member file lists and their default location are described below -

a. `INSTALL_MI_LIST` : Installs header file to a location that is available to everyone in user level.
    Locations -
       $(DSTROOT)/usr/include
   Definition -
       INSTALL_MI_LIST = ${DATAFILES}

b.  `INSTALL_MI_LCL_LIST` : Installs header file to a location that is available
   for Apple internal in user level.
   Locations -
       $(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders
   Definition -
       INSTALL_MI_LCL_LIST = ${PRIVATE_DATAFILES}

c. `INSTALL_KF_MI_LIST` : Installs header file to location that is available
   to everyone for kernel extensions.
   Locations -
        $(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers
   Definition -
        INSTALL_KF_MI_LIST = ${KERNELFILES}

d. `INSTALL_KF_MI_LCL_LIST` : Installs header file to location that is
   available for Apple internal for kernel extensions.
   Locations -
        $(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders
   Definition -
        INSTALL_KF_MI_LCL_LIST = ${KERNELFILES} ${PRIVATE_KERNELFILES}

e. `EXPORT_MI_LIST` : Exports header file to all of xnu (bsd/, osfmk/, etc.)
   for compilation only. Does not install anything into the SDK.
   Definition -
        EXPORT_MI_LIST = ${KERNELFILES} ${PRIVATE_KERNELFILES}

If you want to install the header file in a sub-directory of the paths
described in (1), specify the directory name using two variables
INSTALL_MI_DIR and EXPORT_MI_DIR as follows -

INSTALL_MI_DIR = dirname
EXPORT_MI_DIR = dirname

A single header file can exist at different locations using the steps
mentioned above. However it might not be desirable to make all the code
in the header file available at all the locations. For example, you
want to export a function only to kernel level but not user level.

You can use C language's pre-processor directive (#ifdef, #endif, #ifndef)
to control the text generated before a header file is installed. The kernel
only includes the code if the conditional macro is TRUE and strips out
code for FALSE conditions from the header file.

Some pre-defined macros and their descriptions are -

a. `PRIVATE` : If defined, enclosed definitions are considered System
Private Interfaces. These are visible within xnu and
exposed in user/kernel headers installed within the AppleInternal
"PrivateHeaders" sections of the System and Kernel frameworks.
b. `KERNEL_PRIVATE` : If defined, enclosed code is available to all of xnu
kernel and Apple internal kernel extensions and omitted from user
headers.
c. `BSD_KERNEL_PRIVATE` : If defined, enclosed code is visible exclusively
within the xnu/bsd module.
d. `MACH_KERNEL_PRIVATE`: If defined, enclosed code is visible exclusively
within the xnu/osfmk module.
e. `XNU_KERNEL_PRIVATE`: If defined, enclosed code is visible exclusively
within xnu.
f. `KERNEL` :  If defined, enclosed code is available within xnu and kernel
   extensions and is not visible in user level header files.  Only the
   header files installed in following paths will have the code -

        $(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers
        $(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders

Conditional compilation

xnu offers the following mechanisms for conditionally compiling code:

a. *CPU Characteristics* If the code you are guarding has specific
characterstics that will vary only based on the CPU architecture being
targeted, use this option. Prefer checking for features of the
architecture (e.g. `__LP64__`, `__LITTLE_ENDIAN__`, etc.).
b. *New Features* If the code you are guarding, when taken together,
implements a feature, you should define a new feature in `config/MASTER`
and use the resulting `CONFIG` preprocessor token (e.g. for a feature
named `config_virtual_memory`, check for `#if CONFIG_VIRTUAL_MEMORY`).
This practice ensures that existing features may be brought to other
platforms by simply changing a feature switch.
c. *Existing Features* You can use existing features if your code is
strongly tied to them (e.g. use `SECURE_KERNEL` if your code implements
new functionality that is exclusively relevant to the trusted kernel and
updates the definition/understanding of what being a trusted kernel means).

It is recommended that you avoid compiling based on the target platform. xnu
does not define the platform macros from TargetConditionals.h
(TARGET_OS_OSX, TARGET_OS_IOS, etc.).

There is a TARGET_OS_EMBEDDED macro, but this should be avoided as it is in
general too broad a definition for most functionality.

How to add a new syscall

Testing the kernel

XNU kernel has multiple mechanisms for testing.

  • Assertions - The DEVELOPMENT and DEBUG kernel configs are compiled with assertions enabled. This allows developers to easily
    test invariants and conditions.

  • XNU Power On Self Tests (XNUPOST): The XNUPOST config allows for building the kernel with basic set of test functions
    that are run before first user space process is launched. Since XNU is hybrid between MACH and BSD, we have two locations where
    tests can be added.

    xnu/osfmk/tests/     # For testing mach based kernel structures and apis.
    bsd/tests/           # For testing BSD interfaces.
    

    Please follow the documentation at osfmk/tests/README.md

  • User level tests: The tools/tests/ directory holds all the tests that verify syscalls and other features of the xnu kernel.
    The make target xnu_tests can be used to build all the tests supported.

    $ make RC_ProjectName=xnu_tests SDKROOT=/path/to/SDK
    

    These tests are individual programs that can be run from Terminal and report tests status by means of std posix exit codes (0 -> success) and/or stdout.
    Please read detailed documentation in tools/tests/unit_tests/README.md

Kernel data descriptors

XNU uses different data formats for passing data in its api. The most standard way is using syscall arguments. But for complex data
it often relies of sending memory saved by C structs. This packaged data transport mechanism is fragile and leads to broken interfaces
between user space programs and kernel apis. libkdd directory holds user space library that can parse custom data provided by the
same version of kernel. The kernel chunked data format is described in detail at libkdd/README.md.

Debugging the kernel

The xnu kernel supports debugging with a remote kernel debugging protocol (kdp). Please refer documentation at [technical note] TN2063
By default the kernel is setup to reboot on a panic. To debug a live kernel, the kdp server is setup to listen for UDP connections
over ethernet. For machines without ethernet port, this behavior can be altered with use of kernel boot-args. Following are some
common options.

  • debug=0x144 - setups debug variables to start kdp debugserver on panic
  • -v - print kernel logs on screen. By default XNU only shows grey screen with boot art.
  • kdp_match_name=en1 - Override default port selection for kdp. Supported for ethernet, thunderbolt and serial debugging.

To debug a panic'ed kernel, use llvm debugger (lldb) along with unstripped symbol rich kernel binary.

sh$ lldb kernel.development.unstripped

And then you can connect to panic'ed machine with kdp_remote [ip addr] or gdb_remote [hostip : port] commands.

Each kernel is packaged with kernel specific debug scripts as part of the build process. For security reasons these special commands
and scripts do not get loaded automatically when lldb is connected to machine. Please add the following setting to your ~/.lldbinit
if you wish to always load these macros.

settings set target.load-script-from-symbol-file true

The tools/lldbmacros directory contains the source for each of these commands. Please follow the README.md
for detailed explanation of commands and their usage.