Apple 内部:内核中的 Swift
摘要
Apple 已开始通过一项名为 KernelKit 的新工作将 Swift 集成到内核中,嵌入式 Swift 运行时出现在 macOS 和 iOS 中,标志着向内存安全内核扩展迈出了一步。
<p><a href="https://lobste.rs/s/5j33bp/apple_internals_swift_kernel">评论</a></p>
查看缓存全文
缓存时间: 2026/06/22 01:29
# Apple 内部解析:内核中的 Swift
来源:https://blog.calif.io/p/apple-internals-swift-in-the-kernel
WWDC 之后,我看到 Devon Maloney 发布了一张幻灯片(https://x.com/plailect/status/2064138356259213475),称 Apple 已在 27 版本中“开始用 Swift 编写核心操作系统内核的部分内容”。迈出迈向内存安全内核的第一步。这到底意味着什么?!
我自然放下手头工作,开始在 iOS 27 的内核缓存中搜索。可惜一无所获。不过并非全无收获:我在 macOS 27 的 `com.apple.kec.pthread` 中找到了 Embedded Swift 运行时——居然藏在这个地方。然后我翻查了根文件系统,发现 Apple 给整个努力起了个名字:**KernelKit**。
我们来剖析一下。
XNU 示意图,显示 C/C++ 核心(Mach、BSD、IOKit)保持不变,新的 KernelKit 内核扩展以及 Embedded Swift 运行时添加在内核扩展层边缘(https://substackcdn.com/image/fetch/$s_!9HB8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bb311ae-2b1f-4900-b2b6-06a14ecd95cc_840x660.png)
*发展方向:C/C++ 核心(Mach、BSD、IOKit)未被触及。Swift 仅以小型 Embedded 运行时形式出现在扩展层的特定 KernelKit kext 中。*
在 macOS 27 根卷(`26A5353q`)上,紧挨着 `/System/DriverKit` 的是两个 kext:
其中 pthread kext 的 `Info.plist` 有趣的部分如下:
以及 `version.plist`:
所以有一个名为 `kernelkit.macosx27.0.internal` 的内部 SDK,而 `libpthread_kernelkit` 是一个独立的 Xcode 目标,与普通 kext 使用相同的 libpthread 源码构建。`Libm` 也采用了相同处理:`ProjectName: Libm_kernelkit`。
如果这让你想起 DriverKit(https://developer.apple.com/documentation/driverkit),那你的感觉没错:它有自己的 `/System` 目录、自己的 SDK、自己的 Mach-O 平台常量——七年后的同一套打法。
这些二进制文件的 `LC_BUILD_VERSION` 显示:
当前 Mach-O 平台枚举值最高到 24(`visionOSExclaveKit`)。公共头文件、LLVM 或 `loader.h` 都不知道 25 是什么;`ipsw` 只会打印 `Platform(25)`,直到我手动添加了常量。
然后我检查了 iOS 27 内核缓存中的 pthread kext,本以为会和 iOS 26 一样老套地缺失 `LC_BUILD_VERSION`:
但这值得注意,原因有二。`LC_BUILD_VERSION` 是 Mach-O 携带的印记,记录它为哪个操作系统构建,Apple 用数字而非名称来追踪每个系统。去年 iOS 26 构建的这个 kext 根本没有这种印记,所以它出现在这里本身就是新现象。而且数字是 **26**,而不是刚才 macOS 报告的 **25**。Apple 本可以将所有操作系统的内核 Swift 代码归入同一个共享平台,但实际是每个操作系统都有自己的:macOS 是 25,iOS 是 26,其余的见下表。
为了获取实际名称,我提取了 Xcode 27 beta 链接器中的表格。`ld` 维护着一个 96 字节平台描述符的静态数组(来自 `Platform.cpp`);`uint32` ID 位于每个描述符偏移 `+0x20` 处。通过与已知条目 23/24(`visionOS-exclaveCore`/`Kit`)校准,六个新条目如下:
表格结束于 30。这六个都是 27 版本中新增的;iOS 26.6 的 pthread kext(libpthread-539)根本没有 `LC_BUILD_VERSION`。到目前为止,我只在发布的二进制文件中看到过 25 和 26。
`TargetConditionals.h` 中没有 `TARGET_OS_KERNELKIT`,`Contents/Developer/Platforms/` 下也没有 `KernelKit.platform`。但工具链二进制文件中的一些 cstrings 提供了更多线索:
- `ld`:
- `tapi` 和 `swift-frontend`:
六个按操作系统区分的变体。bridgeOS 也有一个,因为显然 Touch Bar 需要比 iOS 更早拥有内核级 Swift。存在 `TARGET_OS_KERNELKIT` 预处理条件。链接器硬编码了 `/System/KernelKit/usr/lib/swift` 作为搜索路径,这暗示了内核级 Swift 标准库在超出当前 pthread 中静态嵌入部分之后将要存放的位置。而 ld 错误字符串 `kernelKit can only be used with -r, -kext and -static` 确认了没有 dylib 或可执行输出,只有 kext 捆绑包和目标文件。
链接器、TBD 存根工具和 Swift 编译器都已经可以针对这个东西进行编译。Apple 只是还没有发布头文件或 SDK。
`/System/KernelKit/` 中的 pthread 二进制文件最终会进入内核缓存。我之所以知道,是因为 UUID 匹配:
相同的 libpthread-553 源码,两个构建。普通的 macOS 平台构建(即部署在 `/System/Library/Extensions` 和 KDK 中的那个)没有 Swift。KernelKit 平台构建位于 `/System/KernelKit`,被预链接到内核缓存中,并静态链接了 Embedded Swift 运行时。
实际内容如下:
`_swift_embedded_*` 系列与开放 Swift 仓库中的 `stdlib/public/core/EmbeddedRuntime.swift`(https://github.com/swiftlang/swift/blob/main/stdlib/public/core/EmbeddedRuntime.swift)匹配。`$e` 符号混淆前缀是文档记录(https://github.com/swiftlang/swift/blob/main/docs/ABI/Mangling.rst)的嵌入式 Swift 前缀(常规 Swift 使用 `$s`;此切换在 #77923(https://github.com/swiftlang/swift/pull/77923)中启用),而 `_$es16_emptyBoxStorageSi_Sitvp` 反混淆后为 `Swift._emptyBoxStorage : (Swift.Int, Swift.Int)`。
没有 `__swift5_*` 反射段,这对于嵌入式 Swift 是正确的;泛型被单态化,且没有运行时元数据。整个运行时大约 2.4 KB 的 `__TEXT_EXEC.__text`。
我在 IDA 中打开了它,以确保这不是 37 个 `ret` 指令伪装成的运行时。`swift_release` 是一个真正的原子引用计数递减,带有 `brk #1` 下溢陷阱和当计数归零时对 `_swift_embedded_invoke_heap_object_destroy` 的调用。`swift_once` 是一个带有自旋等待的 CAS 一次性操作。`swift_dynamicCast` 是 500 字节的元数据链遍历和存在类型处理。`_emptyBoxStorage` 处的数据是 `(0, 0xFFFFFFFFFFFFFFFF)`,即不朽的空盒单例,这证实了这是真正的运行时,而非 37 个伪装起来的存根。
`com.apple.kec.Libm` 在 macOS 内核缓存中已经存在一段时间了(在 26.6 中就有,源码版本 3312)。新的是它使用 KernelKit SDK 重新构建(`Libm_kernelkit`,平台 25,源码版本 3326)并移到了 `/System/KernelKit`。它包含 65 个数学符号:`_cbrt`、`__sincos_stret`、`__ceilf16`、float16 内建函数等等。没有自己的 Swift 符号;它只是被纳入 KernelKit 家族,推测是为了让 Swift 的 `Double`/`Float` 操作有东西可链接。
我在 IDA 中检查了公共 Swift 入口点的交叉引用:`swift_retain`、`swift_release`、`swift_once`、`swift_dynamicCast`、`swift_allocEmptyBox`。唯一的内部调用者是 `swift_dynamicCast` 调用 `swift_release` 来清理自身。十年历史的 pthread C 代码(`_psynch_mutexwait`、`_bsdthread_create`)从未触及 Swift。
然后我扫描了所有 370 个 macOS 内核缓存文件集条目,查找其他任何地方的 `swift_*` 引用,即是否有其他 kext 链接了这个运行时:
只有一条命中——定义了它们的那个 kext:37 个符号中有 23 个是全局导出符号,而内核缓存中没有其他任何组件导入它们。该运行时已被链接、在启动时加载,但处于空闲状态。
iOS 27 则倒退了一步:pthread 和 Libm 是 KernelKit 平台的二进制文件(平台 26),但 Swift 运行时根本没有被链接进来,相同的 libpthread-553 源码,只是使用了不同的构建配置。
目前,Swift 内核运行时仅存在于 macOS 上且未被引用。这在我看来,部署顺序是:先发布 SDK 和运行时,观察一个或两个测试周期没有破坏任何东西,然后开始部署实际的、会链接 `_swift_retain` 等函数的 Swift 内核组件。iOS 现在先有了平台框架,运行时稍后才会加入。
XNU 本身仍然完全是 C/C++。KDK 的 `kernel.release.t6050.dSYM`(`t6050` 是 M5 Pro SoC)有 2,106 个 DWARF 编译单元,`DW_AT_language` 的分布为:1,855 个 `DW_LANG_C11`、249 个 `DW_LANG_C_plus_plus_14`、2 个 `DW_LANG_Mips_Assembler`,以及零个 `DW_LANG_Swift`。在 t6041(M4 Max)和 KASAN 构建上也是如此。编译器每个源文件发出一个编译单元,所以这不可能隐藏在被剥离的符号后面。无论 Swift 如何到来,它都是作为 KernelKit 组件到来,而不是对 Mach 的重写。
另外,如果有 Apple 员工想泄露 `KernelKit.macosx.sdk`,我会以应有的尊重对待它。
附注:忠实的读者们,是的,我们很快就会发布关于 Swift 和 exclaves 的博客。请耐心等待。
#### 关于此帖的讨论
### 想要更多?
相似文章
Apple 使用 Swift:迁移 TrueType Hinting 解释器
Apple 将 TrueType Hinting 解释器从 C 语言迁移到 Swift,实现了内存安全并提升了 13% 的性能。源代码已开源。
将Swift引入Apple II
一位开发者创建了SwiftII,这是一个为Apple II打造的Swift风格迷你开发环境,包含REPL、文件浏览器和文本编辑器,为复古硬件带来了现代编程体验。
Swift Package Index 加入 Apple
Swift Package Index 是一个由社区维护的 Swift 包目录,现正式加入 Apple,这对 Swift 开发者生态系统来说意义重大。
苹果发布全新 Apple Silicon 端侧推理引擎
苹果在 WWDC 上发布了 CoreAI,这是一款适用于 Apple Silicon 的全新端侧推理引擎,将取代 CoreML,并通过优化推理支持多达 200 亿参数的更大模型,重点面向手机和平板设备。
Apple Core AI Framework
Apple 推出 Core AI Framework,一种用于设备端机器学习的新工具。