XNU 内核是 Darwin 操作系统的一部分,用于 macOS 和 iOS 操作系统。XNU 是 X is Not Unix 的缩写。 XNU 是一个混合内核,结合了卡内基梅隆大学开发的 Mach 内核与 FreeBSD 的组件,以及一个用于编写驱动程序的 C++ API,称为 IOKit。 XNU 运行在 x86_64 和 ARM64 上,支持单处理器和多处理器配置。
config - 配置用于导出API的体系结构和平台SETUP - 用于配置内核、版本控制和 kextsymbol 管理的基本工具集。EXTERNAL_HEADERS - 从其他项目获取的头文件,以避免构建时的依赖循环。这些头文件应在源代码更新时定期同步。libkern - C++ IOKit 库代码,用于处理驱动程序和 kext。libsa - 用于启动的内核引导代码libsyscall - 用于用户空间程序的系统调用库接口libkdd - 用于解析内核数据(如内核分块数据)的用户库源代码。makedefs - 内核构建的顶级规则和定义。osfmk - 基于 Mach 内核的子系統pexpert - 平台特定代码,如中断处理、原子操作等。security - 强制访问检查策略接口及相关实现。bsd - BSD 子系统代码tools - 用于测试、调试和分析内核的一组实用程序。DEVELOPMENT 内核xnu make 系统可以根据 KERNEL_CONFIGS 和 ARCH_CONFIGS 变量作为参数构建内核。
以下是语法:```text
make SDKROOT= ARCH_CONFIGS= KERNEL_CONFIGS=
其中:
* `<sdkroot>`: 磁盘上 macOS SDK 的路径。(默认为 `/`)
* `<variant>`: 可以是 `debug`、`development`、`release`、`profile`,并配置内核代码中的编译标志和断言。
* `<arch>`: 可以是有效的构建架构。(例如 `X86_64`)
要为运行的操作系统的相同架构构建内核,只需输入```text
make SDKROOT=macosx.internal
另外,也支持通过 ARCH_CONFIGS 配置架构,以及使用 KERNEL_CONFIGS 配置内核。```text
make SDKROOT=macosx.internal ARCH_CONFIGS=X86_64 KERNEL_CONFIGS=DEVELOPMENT
make SDKROOT=macosx.internal ARCH_CONFIGS=X86_64 KERNEL_CONFIGS="RELEASE DEVELOPMENT DEBUG"
> 注意:默认情况下,架构设置为构建机器的架构,默认内核配置设置为用于 `DEVELOPMENT` 进行构建。
这还将创建一个可启动镜像、kernel.[config] 以及一个包含符号的内核二进制文件 kernel.[config].unstripped。
要将内核安装到 DSTROOT,请使用 `install_kernels` 目标:```text
make install_kernels DSTROOT=/tmp/xnu-dst
为了获得更令人满意的内核调试体验,能够访问所有局部变量和参数, 但又无需 DEBUG 内核中所有额外的检查,可以在你的 make 命令中添加类似以下内容:```text CFLAGS_DEVELOPMENTARM64="-O0 -g -DKERNEL_STACK_MULTIPLIER=2" CXXFLAGS_DEVELOPMENTARM64="-O0 -g -DKERNEL_STACK_MULTIPLIER=2"
请记得将 `DEVELOPMENT` 和 `ARM64` 替换为合适的构建和平台。
> 额外标志:您可以在命令行中使用 `EXTRA_CFLAGS` 构建设置向 C 编译器传递额外标志。这些标志会被追加到基础 `CFLAGS` 中,该设置的默认值为空字符串。
>
> 此设置允许您例如有选择地打开由预处理宏保护的调试代码。示例用法...
>
> ```text
> make SDKROOT=macosx.internal PRODUCT_CONFIGS=j314s
> EXTRA_CFLAGS='-DKERNEL_STACK_MULTIPLIER=2'
> ```
* 使用 RELEASE 内核配置构建
```text
make KERNEL_CONFIGS=RELEASE SDKROOT=/path/to/SDK
```
### 构建 FAT 内核二进制文件
在您的环境或运行 make 命令时定义架构。```text
make ARCH_CONFIGS="X86_64" exporthdrs all
XNU 构建系统可以选择性地输出颜色格式的构建输出。要启用此功能,您可以将 XNU_LOGCOLORS 环境变量设置为 y,或者将 LOGCOLORS=y 传递给 make 命令。
XNU 版本通过读取 SDK 或 KDK 中的 System/Library/Extensions/System.kext/Info.plist 文件的 CFBundleVersion 来获得。可以通过在环境中或在 make 命令行中设置 RC_DARWIN_KERNEL_VERSION 变量来自定义。
详见 doc/building/xnu_version.md。
默认情况下,在安装阶段会创建一个 DWARF 调试信息仓库;这是一个名为 kernel.development.<variant>.dSYM 的 "bundle"。 要选择较旧的 STABS 调试信息格式(其中调试信息嵌入在 kernel.development.unstripped 映像中),请设置 BUILD_STABS 环境变量。```sh export BUILD_STABS=1 make
## 构建内核缓存
要测试 xnu 内核,你需要构建一个将 kext 和内核链接在一起的内核缓存,形成一个可启动的镜像。
要构建内核缓存,你可以使用以下机制:
* 使用 `kextd` 自动生成内核缓存。
kextd 守护进程持续监视 `/System/Library/Extensions` 目录的变化。
因此,你可以按照以下步骤设置新内核:
```text
cp BUILD/obj/DEVELOPMENT/X86_64/kernel.development /System/Library/Kernels/
touch /System/Library/Extensions
ps -e | grep kextd
```
* 手动调用 `kextcache` 构建新的内核缓存。
```text
kextcache -q -z -a x86_64 -l -n -c /var/tmp/kernelcache.test -K /var/tmp/kernel.test /System/Library/Extensions
```
## 在目标机器上引导内核缓存
开发内核和 iBoot 支持配置启动参数,以便我们可以安全地引导到测试内核,并且在出现问题的情况下,安全地回退到之前使用的内核缓存。
以下是实现此类设置的步骤:
1. 使用 kextcache 命令创建内核缓存,路径为 `/kernelcache.test`
2. 将现有启动配置复制到备用文件
```sh
cp /Library/Preferences/SystemConfiguration/com.apple.Boot.plist /next_boot.plist
```
3. 更新内核缓存和启动参数以适配你的设置
```sh
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/`
```sh
cp /next_boot.plist /Library/Preferences/SystemConfiguration/boot.plist
```
5. 使用新配置对卷进行 bless 操作。
```text
sudo -n bless --mount / --setBoot --nextonly --options "config=boot"
```
`--nextonly` 标志指定仅使用 `boot.plist` 配置一次启动。
因此,如果内核 panic,你可以轻松地强制重启并恢复到原始内核。
## 创建标签和 cscope
设置好构建环境后,从顶层目录运行:
make tags # 这将构建 ctags 和 etags(在区分大小写的卷上),在大小写不敏感的卷上仅构建 ctags
make TAGS # 这将构建 etags
make cscope # 这将构建 cscope 数据库
## 从 XNU 安装新头文件
XNU 将头文件安装在以下位置 -
a. $(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers
b. $(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders
c. $(DSTROOT)/usr/include/
d. $(DSTROOT)/usr/local/include/
e. $(DSTROOT)/System/DriverKit/usr/include/
f. $(DSTROOT)/System/Library/Frameworks/IOKit.framework/Headers
g. $(DSTROOT)/System/Library/Frameworks/IOKit.framework/PrivateHeaders
h. $(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders
`Kernel.framework` 供内核扩展使用。\
`System.framework`、`/usr/include` 和 `/usr/local/include` 供用户级应用程序使用。\
`IOKit.framework` 供 IOKit 用户空间客户端使用。\
`/System/DriverKit/usr/include` 供用户空间驱动程序使用。\
框架的 `PrivateHeaders` 中的头文件仅适用于 **Apple 内部开发**。
包含头文件的目录应有一个 Makefile,
用于创建应安装在不同位置的文件列表。
如果你是在某个目录中添加第一个头文件,则需要
创建类似于 `xnu/bsd/sys/Makefile` 的 Makefile。
根据你想要安装的位置,将你的头文件添加到正确的文件列表中。
每个文件列表的头文件默认安装位置如下 -
a. `DATAFILES` : 在用户级别提供头文件 -
`$(DSTROOT)/usr/include`
`$(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders`
b. `DRIVERKIT_DATAFILES` : 为 DriverKit 用户空间驱动程序提供头文件 -
`$(DSTROOT)/System/DriverKit/usr/include`
c. `PRIVATE_DATAFILES` : 在用户级别为 Apple 内部提供头文件 -
`$(DSTROOT)/System/Library/Frameworks/System.framework/PrivateHeaders`
d. `EMBEDDED_PRIVATE_DATAFILES` : 在 macOS 上作为 `EXTRA_DATAFILES` 在用户级别提供头文件,
但在嵌入式 OS 上作为 `EXTRA_PRIVATE_DATAFILES` 在用户级别供 Apple 内部使用 -
`$(DSTROOT)/usr/include` (`EXTRA_DATAFILES`)
`$(DSTROOT)/usr/local/include` (`EXTRA_PRIVATE_DATAFILES`)
e. `KERNELFILES` : 在内核级别提供头文件 -
`$(DSTROOT)/System/Library/Frameworks/Kernel.framework/Headers`
`$(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders`
f. `PRIVATE_KERNELFILES` : 在内核扩展层面为 Apple 内部提供头文件 -
`$(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders`
g. `MODULEMAPFILES` : 在用户级别提供模块映射文件 -
`$(DSTROOT)/usr/include`
h. `PRIVATE_MODULEMAPFILES` : 在用户级别为 Apple 内部提供模块映射文件 -
`$(DSTROOT)/usr/local/include`
i. `LIBCXX_DATAFILES` : 为内核中的 libcxx 客户端提供头文件:
`$(DSTROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders/kernel_sdkroot`
j. `EXCLAVEKIT_DATAFILES` : 为 Apple 内部的 ExclaveKit SDK 提供头文件 -
`$(DSTROOT)/System/ExclaveKit/usr/include`
k. `EXCLAVECORE_DATAFILES` : 为 Apple 内部的 ExclaveCore SDK 提供头文件 -
`$(DSTROOT)/System/ExclaveCore/usr/include`
Makefile 将上述文件列表组合成不同的
安装列表,构建系统使用这些列表来安装头文件。有两种
类型的安装列表:机器相关的和机器无关的。
这些列表分别由构建设置中的 `MD` 和 `MI` 表示。如果你的头文件是特定于架构的,则应使用
机器相关的安装列表(例如 `INSTALL_MD_LIST`)。如果你的头文件
应针对所有架构安装,则应使用
机器无关的安装列表(例如 `INSTALL_MI_LIST`)。
如果你需要的安装列表不存在,请通过添加相应的文件列表来创建它。
默认的安装列表、其成员文件列表以及它们的默认位置如下所述 -
a. `INSTALL_MI_LIST`, `INSTALL_MODULEMAP_MI_LIST` : 将头文件和模块映射
文件安装到用户级别所有人都可访问的位置。
位置 -
$(DSTROOT)/usr/include
定义 -
INSTALL_MI_LIST = ${DATAFILES}
INSTALL_MODULEMAP_MI_LIST = ${MODULEMAPFILES}
b. `INSTALL_DRIVERKIT_MI_LIST` : 将头文件安装到
DriverKit 用户空间驱动程序可访问的位置。
位置 -
$(DSTROOT)/System/DriverKit/usr/include
定义 -
INSTALL_DRIVERKIT_MI_LIST = ${DRIVERKIT_DATAFILES}