XNU内核编译简要教程

此处以 macOS 10.13 的内核 xnu-4570.1.46 为例。

下载源码

进入苹果的开源页面
Apple Open Source (https://opensource.apple.com/)

macOS 一列中,点击 10.13 展开列表,点击 10.13 进入源码下载页面。
下载以下源码包:

  • AvailabilityVersions-32
  • dtrace-262
  • libdispatch-913.1.6
  • libplatform-161
  • xnu-4570.1.46

注:源码包后面的版本号可能会有不同。

编译前准备

如果迫不及待地将xnu内核源码解压直接编译,会出现很多错误,主要是缺少头文件、库、工具、脚本。
下面列出了编译时所需要的工具:

  • clang - /usr/bin/clang
  • clang++ - /usr/bin/clang++
  • ctfconvert - dtrace-262
  • ctfdump - dtrace-262
  • ctfmerge - dtrace-262
  • ctf_insert - /usr/bin/ctf_insert
  • dsymutil - /usr/bin/dsymutil
  • ld - /usr/bin/ld
  • lipo - /usr/bin/lipo
  • libtool - /usr/bin/libtool
  • mig - /usr/bin/mig
  • nm - /usr/bin/nm
  • nmedit - /usr/bin/nmedit
  • strip - /usr/bin/strip
  • unifdef - /usr/bin/unifdef

:除了 ctfconvertctfdumpctfmerge 这三个工具是通过 dtrace 源码编译得到的,其它工具可以去苹果开发者下载 Command Line Tools 并安装。

头文件:

  • libplatform-161的所有头文件

库:

  • firehose_buffer_private.h - libdispatch-913.1.6
  • libfirehose_kernel.a - libdispatch-913.1.6

脚本:

  • availability.pl - AvailabilityVersions-32

编译

AvailabilityVersions

1
2
3
4
$ cd AvailabilityVersions-32
$ mkdir -p dst
$ make install SRCROOT=$PWD DSTROOT=$PWD/dst
$ sudo ditto $PWD/dst/usr/local `xcrun -sdk macosx -show-sdk-path`/usr/local

dtrace

只需要编译 dtrace 项目中的 ctfconvertctfdumpctfmerge

1
2
3
4
$ cd dtrace-262
$ mkdir -p obj sym dst
$ xcodebuild install -target ctfconvert -target ctfdump -target ctfmerge ARCHS="x86_64" SRCROOT=$PWD OBJROOT=$PWD/obj SYMROOT=$PWD/sym DSTROOT=$PWD/dst
$ sudo ditto $PWD/dst/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain

XNU头文件

在编译 libdispatch 之前,需要将XNU的头文件复制到SDK文件夹下,因为编译 libdispatch 时需要用到这些头文件。

1
2
3
4
5
$ cd xnu-4570.1.46
$ mkdir -p BUILD.h/obj BUILD.h/sym BUILD.h/dst
$ make installhdrs SDKROOT=macosx ARCH_CONFIGS=X86_64 SRCROOT=$PWD OBJROOT=$PWD/BUILD.h/obj SYMROOT=$PWD/BUILD.h/sym DSTROOT=$PWD/BUILD.h/dst
$ sudo xcodebuild installhdrs -project libsyscall/Libsyscall.xcodeproj -sdk macosx ARCHS="x86_64" SRCROOT=$PWD/libsyscall OBJROOT=$PWD/BUILD.h/obj SYMROOT=$PWD/BUILD.h/sym DSTROOT=$PWD/BUILD.h/dst
$ sudo ditto BUILD.h/dst `xcrun -sdk macosx -show-sdk-path`

xnu-4570.1.46 版本的内核源码因为 缺失thread_self_restrict.h头文件,所以此处必定会编译失败。
需要在 xcodebuild 前先创建一个空的文件来使编译通过。

1
$ touch libsyscall/os/thread_self_restrict.h

libplatform

编译 libdispatch 时同样需要用到 libplatform 的头文件。

1
2
$ cd libplatform-161
$ sudo ditto $PWD/include `xcrun -sdk macosx -show-sdk-path`/usr/local/include

libdispatch

1
2
3
4
$ cd libdispatch-913.1.6
$ mkdir -p obj sym dst
$ sudo xcodebuild install -project libdispatch.xcodeproj -target libfirehose_kernel -sdk macosx ARCHS="x86_64" SRCROOT=$PWD OBJROOT=$PWD/obj SYMROOT=$PWD/sym DSTROOT=$PWD/dst
$ sudo ditto $PWD/dst/usr/local `xcrun -sdk macosx -show-sdk-path`/usr/local

XNU

1
2
3
4
$ cd xnu-4570.1.46

# KERNEL_CONFIGS: RELEASE / DEVELOPMENT / DEBUG
$ make SDKROOT=macosx ARCH_CONFIGS=X86_64 KERNEL_CONFIGS="RELEASE"

编译成功后,即可在BUILD/obj目录下找到内核文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# KERNEL_CONFIGS: RELEASE
$ cd BUILD/obj/RELEASE_X86_64
$ file kernel
kernel: Mach-O 64-bit executable x86_64

# KERNEL_CONFIGS: DEVELOPMENT
$ cd BUILD/obj/DEVELOPMENT_X86_64
$ file kernel.development
kernel: Mach-O 64-bit executable x86_64

# KERNEL_CONFIGS: DEBUG
$ cd BUILD/obj/DEBUG_X86_64
$ file kernel.debug
kernel: Mach-O 64-bit executable x86_64

编译错误问题解决

出现以下编译错误通常是因为使用了对代码规范更严格的高版本SDK去编译了代码不太规范的低版本内核导致的。
例如使用macOS 10.13版本的SDK去编译macOS 10.12的内核就会出现下面的错误。

strict-prototypes

1
2
error:
this function declaration is not a prototype [-Werror,-Wstrict-prototypes]

在XNU源码根目录中的 makedefs 目录下,打开 MakeInc.def 文件。
搜索 -Wstrict-prototypes ,然后修改为 -Wno-strict-prototypes

cast-align

1
2
3
4
error: cast
from 'uint64_t *' (aka 'unsigned long long *') to 'volatile SInt64 *' (aka
'volatile long long *') increases required alignment from 4 to 8
[-Werror,-Wcast-align]

在XNU源码根目录中的 makedefs 目录下,打开 MakeInc.def 文件。
搜索 -Wcast-align ,然后修改为 -Wno-cast-align

ignored-attributes

1
2
error:
'transparent_union' attribute ignored [-Werror,-Wignored-attributes]

在XNU源码根目录中的 makedefs 目录下,打开 MakeInc.def 文件。
CWARNFLAGS_STDCXXWARNFLAGS_STD 的最后,添加 -Wno-ignored-attributes
修改后如下:

1
2
3
4
5
6
7
8
9
10
11
CWARNFLAGS_STD = \
-Weverything -Werror -Wextra -Wno-strict-prototypes \
...
-Wno-zero-length-array \
-Wno-ignored-attributes

CXXWARNFLAGS_STD = \
-Weverything -Werror -Wextra -Wpointer-arith -Wreturn-type \
...
-Wno-zero-length-array \
-Wno-ignored-attributes

format-invalid-specifier

1
2
error:
invalid conversion specifier 's' [-Werror,-Wformat-invalid-specifier]

在XNU源码根目录中的 makedefs 目录下,打开 MakeInc.def 文件。
CWARNFLAGS_STDCXXWARNFLAGS_STD 的最后,添加 -Wno-format-invalid-specifier
修改后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
CWARNFLAGS_STD = \
-Weverything -Werror -Wextra -Wno-strict-prototypes \
...
-Wno-zero-length-array \
-Wno-ignored-attributes \
-Wno-format-invalid-specifier

CXXWARNFLAGS_STD = \
-Weverything -Werror -Wextra -Wpointer-arith -Wreturn-type \
...
-Wno-zero-length-array \
-Wno-ignored-attributes \
-Wno-format-invalid-specifier

替换内核

务必虚拟机 上进行以下操作:

1
2
$sudo cp BUILD/obj/RELEASE_X86_64/kernel /System/Library/Kernels/
$sudo kextcache -invalidate /

如果复制内核时出现如下提示:

1
cp: /System/Library/Kernels/kernel: Operation not permitted

表示没有权限进行操作。只有关闭SIP后操作才会执行成功。

关闭系统完整性保护(SIP, System Integrity Protection)

查看SIP状态

1
2
$ csrutil status
System Integrity Protection status: enabled.

开启/关闭SIP

  1. 重启系统,黑屏后按住Command + R键直至苹果图标出现,进入恢复模式。
  2. 从菜单栏的工具选项中启动终端。
  3. 输入命令。

    1
    2
    3
    4
    5
    # 关闭SIP
    $ csrutil disable

    # 开启SIP
    $ csrutil enable
  4. 重启系统。

参考文献

  1. 《深入解析Mac OS X & iOS操作系统》
    作者:[美]Jonathan Levin
    译者:郑思遥 / 房佩慈
  2. Building the XNU kernel on Mac OS X Sierra (10.12.X)
  3. Building and Debugging Kernels
  4. Configuring System Integrity Protection