问题
I have a simple C application:
#include <stdio.h>
int main() {
printf("Hello, world!\n");
}
When I:
- build this on an Apple Silicon device
- targeting arm64
- using a build system that is compiled for x86_64
- replace an existing binary (such as during a rebuild)
The final binary cannot be executed and is reported as "killed" according to the shell:
% rm hello
# arch simulates the build system which is still compiled for x86_64
% arch -x86_64 cc -arch arm64 hello.c -o hello
% ./hello
Hello, world!
% arch -x86_64 cc -arch arm64 hello.c -o hello
% ./hello
zsh: killed ./hello
If my build system is native arm64 (or arm64e), then there's no problem:
% rm hello
% arch -arm64e cc -arch arm64 hello.c -o hello
% ./hello
Hello, world!
% arch -arm64e cc -arch arm64 hello.c -o hello
% ./hello
Hello, world!
If I copy the binary to a new path and then replace the code signature, then it works:
% cp hello hello2
% codesign -s - -f hello2
hello2: replacing existing signature
% ./hello2
Hello, world!
Original reproduction steps and debugging
When I build this on an Apple Silicon device, targeting arm64, but using a build system that is compiled for x86_64, the final binary cannot be executed and is reported as "killed" according to the shell:
# arch simulates the build system which is still compiled for x86_64
% arch -x86_64 cc -arch arm64 hello.c -o hello
% file hello
hello: Mach-O 64-bit executable arm64
% ./hello
zsh: killed ./hello
Even attempting to debug it fails:
% lldb hello
(lldb) target create "hello"
zsh: killed lldb hello
As a workaround, I can use a shim compiler to "reset" back to an arm64 environment before calling cc
, but that's inelegant and it feels like I'm missing some configuration to avoid jumping through so many hoops.
% cc --version
Apple clang version 12.0.0 (clang-1200.0.32.27)
Target: arm64-apple-darwin20.1.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
This is on macOS Big Sur 11.0.1.
Cross compiling from x86_64
If I cross-compile from an x86_64 machine and scp the binary around, the file executes fine:
% SDKROOT=$(xcrun -sdk macosx11.0 --show-sdk-path) \
MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx11.0 --show-sdk-platform-version) \
cc -arch arm64 hello.c -o hello
% scp hello silicon:/tmp/hello-cross
Switch machines...
% file /tmp/hello-cross
/tmp/hello-cross: Mach-O 64-bit executable arm64
% /tmp/hello-cross
Hello, world!
Tracing child processes
Adding --verbose
to the cc
invocation prints out the child processes (I added newlines to highlight those among the other debugging):
% arch -x86_64 cc --verbose -arch arm64 hello.c -o hello
Apple clang version 12.0.0 (clang-1200.0.32.27)
Target: aarch64-apple-darwin20.1.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple arm64-apple-macosx11.0.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name hello.c -mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=non-leaf -fno-strict-return -masm-verbose -munwind-tables -target-sdk-version=11.0 -target-cpu vortex -target-feature +v8.3a -target-feature +fp-armv8 -target-feature +neon -target-feature +crc -target-feature +crypto -target-feature +fullfp16 -target-feature +ras -target-feature +lse -target-feature +rdm -target-feature +rcpc -target-feature +zcm -target-feature +zcz -target-feature +sha2 -target-feature +aes -target-abi darwinpcs -fallow-half-arguments-and-returns -dwarf-column-info -debugger-tuning=lldb -target-linker-version 609.6 -v -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -I/usr/local/include -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/local/include -internal-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -Wno-reorder-init-list -Wno-implicit-int-float-conversion -Wno-c99-designator -Wno-final-dtor-non-final-class -Wno-extra-semi-stmt -Wno-misleading-indentation -Wno-quoted-include-in-framework-header -Wno-implicit-fallthrough -Wno-enum-enum-conversion -Wno-enum-float-conversion -fdebug-compilation-dir /tmp/example -ferror-limit 19 -fmessage-length 237 -stack-protector 1 -fstack-check -mdarwin-stkchk-strong-link -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fobjc-runtime=macosx-11.0.0 -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -o /var/folders/j2/g4_hr3jd2sz415vrf7b1d6300000gp/T/hello-232f6b.o -x c hello.c
clang -cc1 version 12.0.0 (clang-1200.0.32.27) default target x86_64-apple-darwin20.1.0
ignoring nonexistent directory "/usr/local/include"
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/local/include"
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/Library/Frameworks"
#include "..." search starts here:
#include <...> search starts here:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0/include
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks (framework directory)
End of search list.
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -no_deduplicate -dynamic -arch arm64 -platform_version macos 11.0.0 11.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -o hello -L/usr/local/lib /var/folders/j2/g4_hr3jd2sz415vrf7b1d6300000gp/T/hello-232f6b.o -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0/lib/darwin/libclang_rt.osx.a
The compiler invocation has -triple arm64-apple-macosx11.0.0
and the linker invocation has -arch arm64
, so it seems like appropriate values are being passed.
Comparing failing and successful binaries
As suggested in a comment, I've tried comparing the two binary files. Unfortunately, the suggested Beyond Compare tool also crashes as soon as the failing executable is opened. I suspect that it's for the same underlying reason that LLDB reports being killed.
In the meantime, here's the output of comparing the raw hex data:
--- /tmp/hello-cross.hex 2020-11-14 05:07:04.000000000 -0800
+++ ./hello.hex 2020-11-14 05:06:52.000000000 -0800
@@ -74,10 +74,10 @@
00000490: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000004a0: 0e00 0000 2000 0000 0c00 0000 2f75 7372 .... ......./usr
000004b0: 2f6c 6962 2f64 796c 6400 0000 0000 0000 /lib/dyld.......
-000004c0: 1b00 0000 1800 0000 7b34 532d f414 3f99 ........{4S-..?.
-000004d0: a866 f4d8 29bf f7ce 3200 0000 2000 0000 .f..)...2... ...
+000004c0: 1b00 0000 1800 0000 0ad8 b6d9 c6c4 32f0 ..............2.
+000004d0: 9522 80e2 f036 54f7 3200 0000 2000 0000 ."...6T.2... ...
000004e0: 0100 0000 0000 0b00 0000 0b00 0100 0000 ................
-000004f0: 0300 0000 0006 6102 2a00 0000 1000 0000 ......a.*.......
+000004f0: 0300 0000 0007 6102 2a00 0000 1000 0000 ......a.*.......
00000500: 0000 0000 0000 0000 2800 0080 1800 0000 ........(.......
00000510: 543f 0000 0000 0000 0000 0000 0000 0000 T?..............
00000520: 0c00 0000 3800 0000 1800 0000 0200 0000 ....8...........
@@ -3094,9 +3094,9 @@
0000c150: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000c160: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000c170: 0000 4000 0000 0000 0000 0001 6865 6c6c ..@.........hell
-0000c180: 6f00 e40f cd6f 1abe f672 0c10 0915 8b30 o....o...r.....0
-0000c190: 5c9a 4a69 c9eb efdd a655 11c8 f5dc 3d86 \.Ji.....U....=.
-0000c1a0: b3d5 ad7f acb2 586f c6e9 66c0 04d7 d1d1 ......Xo..f.....
+0000c180: 6f00 29f3 685d 60bf c358 4179 1735 b8b0 o.).h]`..XAy.5..
+0000c190: d018 3ec5 aa3c 0f60 cc8b a4f2 23bf b37d ..>..<.`....#..}
+0000c1a0: 9ee8 ad7f acb2 586f c6e9 66c0 04d7 d1d1 ......Xo..f.....
0000c1b0: 6b02 4f58 05ff 7cb4 7c7a 85da bd8b 4889 k.OX..|.|z....H.
0000c1c0: 2ca7 ad7f acb2 586f c6e9 66c0 04d7 d1d1 ,.....Xo..f.....
0000c1d0: 6b02 4f58 05ff 7cb4 7c7a 85da bd8b 4889 k.OX..|.|z....H.
Running otool -fahlLDtvV
on both files and comparing the outputs yields:
--- /tmp/hello-cross.dump 2020-11-14 05:41:10.000000000 -0800
+++ hello.dump 2020-11-14 05:41:22.000000000 -0800
@@ -226,7 +226,7 @@
Load command 9
cmd LC_UUID
cmdsize 24
- uuid 7B34532D-F414-3F99-A866-F4D829BFF7CE
+ uuid 0AD8B6D9-C6C4-32F0-9522-80E2F03654F7
Load command 10
cmd LC_BUILD_VERSION
cmdsize 32
@@ -235,7 +235,7 @@
minos 11.0
ntools 1
tool ld
- version 609.6
+ version 609.7
Load command 11
cmd LC_SOURCE_VERSION
cmdsize 16
This seems to correspond to the first hunk of the raw hex dump, suggesting that the important difference is in the second hunk.
Code signing
MachOView indicates that address 0xc180
occurs within the "Code Signature" section. However, it appears that the signature is valid enough:
% codesign --verify --verbose hello
hello: valid on disk
hello: satisfies its Designated Requirement
Looking at Console.app, I do see these lines:
CODE SIGNING: cs_invalid_page(0x10439c000): p=32470[hello] final status 0x23020200, denying page sending SIGKILL
CODE SIGNING: process 32470[hello]: rejecting invalid page at address 0x10439c000 from offset 0x0 in file "/private/tmp/example/hello" (cs_mtime:1605366572.426749233 == mtime:1605366572.426749233) (signed:1 validated:1 tainted:1 nx:0 wpmapped:1 dirty:0 depth:0)
来源:https://stackoverflow.com/questions/64830671/why-does-my-native-arm64-application-built-using-an-x86-64-build-system-fail-to