查看原文
其他

检查第三方库是否包含 bitcode 信息

酷酷的哀殿 酷酷的哀殿 2021-07-27

前言

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 。通过它,可以快速地识别文件的类型

file lib/iphoneos/libMockLibrary.a
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
file MockFrameWork.framework/MockFrameWork

输出:

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 命令进行解压缩。

该命令会产出多个 Mach-O object 文件,每个文件对应一个源码文件(.c 或 .m )

tar -xf tmp-armv7.a

在进行后续的校验前,我们先验证产出文件的类型是否为 Mach-O object 类型

file MockLibrary.o
MockLibrary.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 进行验证。

file tmp-armv7.a

我们可以从输出信息看到,动态库已经是 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

segedit tmp-armv7.a -extract "__LLVM" "__bundle" bitcode.xar

验证 xar 文件

xar 是专门针对xml优化的压缩格式

file bitcode.xar
bitcode.xar: xar archive version 1, SHA-1 checksum

通过 xar 命令进行解压缩

xar -xf bitcode.xar

xar 命令会产出多个bitcode文件,任选一个文件进行验证

llvm-bcanalyzer -dump 1
<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 的二进制文件。


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存