为什么我在Apple Silicon上编译的本机应用程序有时构建为arm64,有时构建为x86_64



我有一个基本的C程序:

#include <stdio.h>
int main() {
printf("Hello, world!n");
}

当我在Apple Silicon设备上使用cc直接编译时,它会生成一个arm64可执行文件:

% cc hello.c -o hello
% file hello
hello: Mach-O 64-bit executable arm64
% ./hello
Hello, world!

然而,当我通过CMake或Ninja等构建系统构建它时,它会生成x86_64二进制文件:

% ./my-build-system
% file hello
hello: Mach-O 64-bit executable x86_64

我已经验证了构建脚本正在运行的命令与我自己运行的命令相同。如果我复制并粘贴命令并自己运行,那么生成的可执行文件将再次为arm64。

当您的构建命令不包括为哪个体系结构构建的特定标志时,Apple提供的编译器工具(如cc(会根据调用过程的体系结构执行某种内省。这意味着,如果您的构建系统尚未针对arm64进行本机编译,您可能会看到这种行为,因为编译器会认为您想要针对x86_64进行构建!

您可以使用arch工具在x86_64模式下运行cc可执行文件来演示这一点:

% arch -x86_64 cc hello.c -o hello
% file hello
hello: Mach-O 64-bit executable x86_64

作为变通方案,您可以引入一个始终重置为本机体系结构的填充程序编译器。将其保存为force-arm64-cc并使其可执行:

#!/usr/bin/env bash
# Note we are using arm64e because `cc` does not have an arm64 binary!
exec arch -arm64e cc "$@"

然后可以使用此垫片代替cc:

% CC=$PWD/force-arm64-cc ./my-build-system
% file hello
hello: Mach-O 64-bit executable arm64

正确的长期解决方案是在编译时指定目标体系结构:

% arch -x86_64 cc -arch arm64 hello.c -o hello
% file hello
hello: Mach-O 64-bit executable arm64

然而,当您重建二进制文件时,这目前会产生一个伪造的可执行文件,这在编辑-编译-运行周期中很常见:

% ./hello
zsh: killed     ./hello

另请参阅:

  • 为什么使用x86_64构建系统构建的本机arm64应用程序无法进行代码签名,除非我删除以前的可执行文件

最新更新