检查第三方库是否包含 bitcode 信息
前言
bitcode 是一种从源码到汇编中间的中间码。
iOS 9 发布后,开发者可以提交包含 bitcode 的程序到APPStore。当 llvm 更新或者新款手机使用了新的cpu命令集架构时,苹果可以使用最新的llvm版本重新将bitcode编译为安装包,确保能够充分利用新的llvm的最新优化或者适配最新的cpu命令集架构,避免了开发者手动提交新安装包的繁琐过程。
规模较大的APP都会包含众多的内部库和外部库。比如,一个浏览器包含了图片识别功能、语音识别功能。开发者通常会将浏览器当做“宿主”,图片识别、语音识别当做(闭源或开源)第三方库的方式合入。
当需要将第三方库以非源码的形式引入,则需要校验第三方库是否包含bitcode。
库
在iOS的环境中,第三方库通常可以编译为两种格式:
静态库
动态库
对两种库不熟悉的同学,可以通过搜索引擎检索一下,本文不再做过多的说明。
architecture
根据iPhone手机处理器型号的不同,源码可以被编译为不同的架构,常见的架构包括 arm-v7、arm64架构。
胖文件
同一份源码可以编译为多个架构的文件。我们提交给Apple审核的程序通常包含多个架构的文件。包含多个架构的文件通常被称之为胖文件(又被称作 universal binary)
file
file
是 Apple 提供的一个内置程序,存储路径为 /usr/bin/file
。通过它,可以快速地识别文件的类型
lib/iphoneos/libMockLibrary.a: Mach-O universal binary with 2 architectures: [arm_v7:current ar archive random library] [arm64:current ar archive random library]
lib/iphoneos/libMockLibrary.a (for architecture armv7): current ar archive random library
lib/iphoneos/libMockLibrary.a (for architecture arm64): current ar archive random library
输出:
MockFrameWork.framework/MockFrameWork: Mach-O universal binary with 2 architectures: [arm_v7:Mach-O dynamically linked shared library arm_v7] [arm64:Mach-O 64-bit dynamically linked shared library arm64]MockFrameWork.framework/MockFrameWork (for architecture armv7): Mach-O dynamically linked shared library arm_v7
MockFrameWork.framework/MockFrameWork (for architecture arm64): Mach-O 64-bit dynamically linked shared library arm64
lipo
lipo 是Apple提供的针对“胖文件”进行查看、合并、切割、替换等操作的工具。
针对多个静态库组成的胖文件,我们可以通过以下命令转储一份单架构的文件
lipo ../libMockLibrary.a -thin armv7 -output tmp-armv7.a验证静态库是否包含 bitcode
首先,我们需要先验证文件类型是否是为静态库(archive 文件)
file ./tmp-armv7.a./tmp-armv7.a: current ar archive random library
.a 文件通常由多个 Mach-O 文件通过特殊算法压缩的,所以,我们可以通过 tar
命令进行解压缩。
tar -xf tmp-armv7.a该命令会产出多个 Mach-O object 文件,每个文件对应一个源码文件(.c 或 .m )
在进行后续的校验前,我们先验证产出文件的类型是否为 Mach-O object 类型
file MockLibrary.oMockLibrary.o: Mach-O object arm_v7
每个 Mach-O 包含很多份不同用途的 data。bitcode 信息就存储在 __LLVM __bitcode
segedit
segedit 可以转储或者替换 Mach-O object 文件中sections的一部分
通过 -extract 参数转储bitcode部分
segedit MockLibrary.o -extract "__LLVM" "__bitcode" bitcode.bc验证bitcode格式是否正确
llvm-bcanalyzer -dump bitcode.bc下面是部分的输出内容,通过开头可以看到,转储文件确实是 bitcode 文件。
<BITCODE_WRAPPER_HEADER Magic=0x0b17c0de Version=0x00000000 Offset=0x00000014 Size=0x00001d40 CPUType=0x0000000c/><IDENTIFICATION_BLOCK_ID NumWords=7 BlockCodeSize=5>
<STRING abbrevid=4 op0=65 op1=80 op2=80 op3=76 op4=69 op5=95 op6=49 op7=95 op8=49 op9=48 op10=48 op11=48 op12=46 op13=49 op14=49 op15=46 op16=52 op17=53 op18=46 op19=53 op20=95 op21=48/> record string = 'APPLE_1_1000.11.45.5_0'
<EPOCH abbrevid=5 op0=0/>
</IDENTIFICATION_BLOCK_ID>
<MODULE_BLOCK NumWords=1336 BlockCodeSize=3>
<VERSION op0=2/>
<BLOCKINFO_BLOCK/>
…………
验证动态库是否包含 bitcode
编译器对静态库和动态库的处理流程有些不同,所以,动态库需要通过另外的方式进行校验。
首先,我们还是先验证输入文件是否为动态库。
和之前一样,我们使用 file
进行验证。
我们可以从输出信息看到,动态库已经是 Mach-O 格式的文件。(静态库是:current ar archive random library)
tmp-armv7.a: Mach-O dynamically linked shared library arm_v7动态库的 __LLVM __bundle
包含被压缩为 xar 格式的文件 bitcode 数据。通过 segedit
导出 __LLVM __bundle
验证 xar 文件
file bitcode.xarxar 是专门针对xml优化的压缩格式
bitcode.xar: xar archive version 1, SHA-1 checksum
通过 xar
命令进行解压缩
xar
命令会产出多个bitcode文件,任选一个文件进行验证
<BITCODE_WRAPPER_HEADER Magic=0x0b17c0de Version=0x00000000 Offset=0x00000014 Size=0x00001934 CPUType=0x0000000c/>
<IDENTIFICATION_BLOCK_ID NumWords=7 BlockCodeSize=5>
<STRING abbrevid=4 op0=65 op1=80 op2=80 op3=76 op4=69 op5=95 op6=49 op7=95 op8=49 op9=48 op10=48 op11=48 op12=46 op13=49 op14=49 op15=46 op16=52 op17=53 op18=46 op19=53 op20=95 op21=48/> record string = 'APPLE_1_1000.11.45.5_0'
<EPOCH abbrevid=5 op0=0/>
</IDENTIFICATION_BLOCK_ID>
<MODULE_BLOCK NumWords=1120 BlockCodeSize=3>
<VERSION op0=2/>
<BLOCKINFO_BLOCK/>
<PARAMATTR_GROUP_BLOCK_ID NumWords=302 BlockCodeSize=3>
…………
…………
One more thing
很多人会说,根本不需要这么麻烦的检测,只需要通过以下命令就可以检测是否包含 bitcode。
otool -arch arm64 -l 库 |grep bitcode | wc -l
otool -arch arm64 -l 库 |grep LLVM | wc -l
但是,根据苹果的官方文档,Xcode 有一个特殊的优化,我们正常开发时,不需要上传 bitcode 信息,所以,__LLVM
和 __bitcode
虽然存在,但是它们的责任是“站位”, data部分的实际长度是1。只有当我们使用 Archive
提交审核包时或者通过一些特殊的操作,才会产生真正包括 bitcode
的二进制文件。