搭载 Apple 芯片的 Mac 上的 Rosetta 2
搭载 Apple 芯片的 Mac 可通过叫做 Rosetta 2 的转译机制来运行针对 x86_64 指令集编译的代码。提供的转译类型有两种:即时和提前。
即时转译
在即时 (JIT) 转译流程中,x86_64 Mach 对象早在映像执行路径中就会被识别。遇到这些映像时,内核会将控制转交给特殊的 Rosetta 转译存根,而不是动态链接编辑器 dyld(1)
。然后转译存根会在映像执行期间转译 x86_64 页。此转译完全在该过程中进行。内核仍会根据页出错所在二进制文件中所附的代码签名来验证每个 x86_64 页的代码哈希值。如果某个哈希值不匹配,内核会实施适用于该过程的修复策略。
提前转译
在提前 (AOT) 转译流程中,x86_64 二进制文件会在系统认定对该代码响应能力最有益的时间从储存空间中被读取出来。转译后的成品会作为特殊类型的 Mach 对象文件写入储存空间。该文件类似于可执行映像,但会被标记以指示这是另一个映像的转译结果。
在此模型中,AOT 成品会根据原始 x86_64 可执行映像派生出其全部身份信息。为了实施此绑定,有权限的用户空间实体会使用安全隔区管理的设备特定密钥给转译成品签名。此密钥仅对该有权限的用户空间实体可见,该实体使用受限授权进行识别。为该转译成品创建的代码目录包括原始 x86_64 可执行映像的代码目录哈希值。转译成品本身的签名称为补充签名。
AOT 流程的开始与 JIT 流程类似,内核将控制转交给 Rosetta 运行环境,而不是动态链接编辑器 dyld(1)
。但 Rosetta 运行环境随后会向 Rosetta 系统服务发送进程间通信 (IPC) 查询,询问当前可执行映像是否有可用的 AOT 转译。如果找到,Rosetta 服务会向该转译提供一个句柄,以将其映射到过程中并执行。执行期间,内核为转译成品计算代码目录哈希值,该哈希值经过植根于设备特定签名密钥的签名认证。此过程不涉及原始 x86_64 映像的代码目录哈希值。
转译成品会储存在数据保险箱中,运行时除 Rosetta 服务外任何实体都无法访问。Rosetta 服务通过向单个转译成品分发只读文件描述符来管理其缓存的访问权限;这限制了对 AOT 成品缓存的访问。此服务的进程间通信和从属足迹有意保持在非常窄的范围,以限制其攻击面。
如果原始 x86_64 映像的代码目录哈希值与编码到 AOT 转译成品签名中的哈希值不匹配,此结果会被视为等同于无效代码签名,此时会执行适当的实施操作。
如果远程过程向内核查询经 AOT 转译的可执行内容的授权或其他代码身份属性,内核会向其返回原始 x86_64 映像的身份属性。
静态信任缓存内容
macOS 11 或更高版本附带 Mach “胖”二进制文件,其中包含 x86_64 和 arm64 电脑代码的分段。在搭载 Apple 芯片的 Mac 上,用户可能会决定通过 Rosetta 流程执行系统二进制文件的 x86_64 分段,例如要载入没有原生 arm64 变体的插件时。为了支持这一方法,macOS 附带的静态信任缓存一般会为每个 Mach 对象文件包含三个代码目录哈希值:
arm64 分段的代码目录哈希值
x86_64 分段的代码目录哈希值
x86_64 分段 AOT 转译的代码目录哈希值
Rosetta AOT 转译过程是确定的,针对任何给定输入都会重现相同的输出,无论什么时间或在什么设备上执行转译。
macOS 构建期间,每个 Mach 对象文件都通过与被构建 macOS 版本关联的 Rosetta AOT 转译流程运行,生成的代码目录哈希值会记录到信任缓存中。为提高效率,实际转译结果不会附带在操作系统中,而是在用户请求时按需重新整合。
在搭载 Apple 芯片的 Mac 上执行 x86_64 映像时,如果该映像的代码目录哈希值位于静态信任缓存中,则生成的 AOT 成品的代码目录哈希值也应该在静态信任缓存中。此类结果不会由设备特定密钥签名,因为签名权限植根于 Apple 安全启动链中。
未签名 x86_64 代码
搭载 Apple 芯片的 Mac 不允许原生 arm64 代码执行,除非其附带有效的签名。此签名可以像 ad hoc 代码签名(请参阅 codesign(1)
)一样简单,该类代码签名中不包含来自非对称密钥对中私钥部分的任何实际身份(而只是二进制文件的一个未认证测量值)。
为提高二进制文件兼容性,转译后的 x86_64 代码被允许在没有任何签名信息的情况下通过 Rosetta 执行。设备特定安全隔区签名过程中不会将特定身份信息传递到此代码中,代码执行的限制条件与原生未签名代码在基于 Intel 的 Mac 上执行时完全相同。