MacOS 和 iOS 使用多架构的 MachO 产物,称为 flat binary,本文将探索对于单架构与多架构的各种组合方式。

太长不读结论版:通常会默认以本机系统架构进行融合,如果编译器或链接器无法分析出需要融合的架构,需要指定 -target / -arch 来辅助。

本机系统架构:x86_64

本文大纲如下:
1、多架构编译
2、单架构 MachO 和 多架构库
3、多架构 MachO 和 多架构库
4、多架构 MachO 和 单架构库
5、总结

1、多架构编译

  • 编译多架构
1
2
3
4
5
gcc -target x86_64-apple-macos10.15 -c add.c -o x86

gcc -target arm64-apple-ios9.0 \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk \
-c add.c -o arm64

  • 合并多架构
1
lipo -create -output add.o x86 arm64

  • 打包成静态库
1
ar rcs libadd.a add.o

  • 打包成动态库
1
2
3
4
5
6
7
gcc -target x86_64-apple-macos10.15 -fPIC add.o -shared -o share_x86

gcc -target arm64-apple-ios9.0 -fPIC add.o -shared \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk \
-o share_arm64

lipo -create -output share share_x86 share_arm64

2、单架构 MachO 和 多架构库

  • 静态库
1
2
3
4
5
gcc libadd.a main.c -o main

# 或者使用下面的方式,因为本质上是链接器在做融合
gcc -c main.c -o main.o
ld main.o libadd.a -lSystem -o main

  • 动态库
1
ld main.o share -lSystem -o main

用 MachOView 查看,可以看到多架构动态库 share 只有 share_x86 这个会被链接。

我们合成的多架构动态库 share 被分拆了,只拿了其中的 x86_64 的部分。

注意:这样会有什么问题?

可以看到这里 LC_LOAD_DYLIB 的路径信息 name 是相对路径,即编译器/链接器默认我们 main 执行文件,即放 share 动态库的地方会有 share_x86 这个单架构文件。

那如果找不到的话,是会报运行时错误的,所以多架构合成之后,也要小心不要把单架构文件删掉了。

其原因在于我合成的动态库其架构上的路径的不统一:

不过 iOS 开发不需要担心,Xcode 对于多架构的处理非常好:

其路径下的 name 是完全一样的,而且也没有发生像我这种拆分情况,直接链接的就是多架构的库

是我合成的动态库不标准,不同架构的路径不一样,但 Xcode 合成的路径是同一个。

比如图中的 /usr/lib/libSystem.B.dylib

它就是个多架构的库,并不影响单架构的使用。

那么 Xcode 是如何控制这个路径一样的呢?

1
-install_name @rpath/share

用这个就能控制动态库的搜索路径了。

即使库是多架构,可执行文件是单架构,也不用慌,因为只有对应架构部分被加载到内存当中。

如何验证?可以试试查找多架构库其中的某一个函数,会发现只有对应架构的版本。

1
dis -n "函数符号名"

想想其实很合理,看看文件结构就知道了:

文件当中会有 Fat Header,记录不同架构各自的 Header 信息在哪里,然后再去处理自己所需要的那部分架构。

但如果库中没有所需要的架构,那么肯定就是报错了:

1
dyld: Library not loaded: @rpath/XXX.framework/XXX

题外话:新版 Xcode 可能找不到设置架构的地方,可以用以下操作:

3、多架构 MachO 和 多架构库

1
2
3
4
5
6
7
gcc -target x86_64-apple-macos10.15 -c main.c -o main_x86

gcc -target arm64-apple-ios9.0 \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk \
-c main.c -o main_arm64

lipo -create -output main.o main_x86 main_arm64

多架构与多架构无法直接链接到一起,需要指定架构,分别链接好了,再合成到一起

4、多架构 MachO 和 单架构库

  • x86 库

  • arm64 库

由于默认是本机架构 x86_64,所以没能从中找到对应的符号,指定 arm64 架构看看:

1
2
3
gcc -target arm64-apple-ios9.0 \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk \
main.o arm64 -o main

  • 动态库

可以看到多架构的 MachO 会被分拆,同样试试 arm64:

1
2
3
gcc -target arm64-apple-ios9.0 \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk \
main.o share_arm64 -o main

5、总结

通常会默认以本机系统架构进行融合,如果编译器或链接器无法分析出需要融合的架构,需要指定 -target / -arch 来辅助。