ENABLE_BITCODE 在 xcode 7 中做什么?
What does ENABLE_BITCODE do in xcode 7?
我对嵌入的位码术语有疑问。
什么是嵌入式位码?
什么时候启用,ENABLE_BITCODE
in new Xcode?
Xcode 7 中的 ENABLE_BITCODE
启用后二进制文件会发生什么情况?
Bitcode是指发送到iTunes Connect的代码类型:"LLVM Bitcode"。这允许 Apple 使用某些计算来进一步重新优化应用程序(例如:可能缩小可执行文件的大小)。如果 Apple 需要更改您的可执行文件,那么他们可以在不上传新版本的情况下执行此操作。
这不同于:
切片 这是 Apple 根据设备的分辨率和架构为用户设备优化您的应用程序的过程。切片不需要 Bitcode。 (例如:在 5s 上只包含 @2x 图片)
App Thinning是切片、bitcode、按需资源的组合
Bitcode is an intermediate representation of a compiled program. Apps
you upload to iTunes Connect that contain bitcode will be compiled and
linked on the App Store. Including bitcode will allow Apple to
re-optimize your app binary in the future without the need to submit a
new version of your app to the store.
Bitcode (iOS, watchOS)
Bitcode is an intermediate representation of a compiled program. Apps you upload to iTunes Connect that contain bitcode will be compiled and linked on the App Store. Including bitcode will allow Apple to re-optimize your app binary in the future without the need to submit a new version of your app to the store.
基本上这个概念有点类似于 java,其中字节码在不同的 JVM 上是 运行,在这种情况下,位码放在 iTune 存储上,而不是将中间代码提供给不同的平台(设备)它提供不需要任何虚拟机的编译代码 运行.
因此我们只需要创建一次位码,它就可以用于现有或即将推出的设备。编译并使其与他们拥有的每个平台兼容是 Apple 的头疼。
开发人员无需进行更改并再次提交应用程序即可支持新平台。
以苹果在其中引入x64
芯片时iPhone 5s为例。尽管 x86
应用程序与 x64
架构完全兼容,但要充分利用 x64
平台,开发人员必须更改架构或某些代码。 s/he 完成后,应用程序将提交到应用程序商店进行审核。
如果这个位码概念早些推出,那么我们开发人员无需进行任何更改即可支持 x64
位架构。
什么是嵌入式位码?
根据docs:
Bitcode is an intermediate representation of a compiled program. Apps you upload to iTunes Connect that contain bitcode will be compiled and linked on the App Store. Including bitcode will allow Apple to re-optimize your app binary in the future without the need to submit a new version of your app to the store.
更新: "New Features in Xcode 7"中的这句话让我想了很久 Bitcode 是为了 切片 以减小应用程序大小:
When you archive for submission to the App Store, Xcode will compile your app into an intermediate representation. The App Store will then compile the bitcode down into the 64 or 32 bit executables as necessary.
但这不是真的,Bitcode 和 Slicing 独立工作:Slicing 是关于减少应用程序大小和生成应用程序包变体,Bitcode 是关于某些二进制优化的。我通过检查非位码应用程序的可执行文件中包含的体系结构并发现它们只包含必要的体系结构来验证这一点。
Bitcode 允许其他 App Thinning 组件调用 Slicing 生成具有特定架构的特定可执行文件的应用程序包变体,例如iPhone 5S 变体将仅包含 arm64 可执行文件,iPad Mini armv7 等等。
何时在新 Xcode 中启用 ENABLE_BITCODE?
For iOS apps, bitcode is the default, but optional. If you provide bitcode, all apps and frameworks in the app bundle need to include bitcode. For watchOS and tvOS apps, bitcode is required.
在新 Xcode 中启用 ENABLE_BITCODE 时二进制文件会发生什么?
来自 Xcode 7 个参考:
Activating this setting indicates that the target or project should generate bitcode during compilation for platforms and architectures which support it. For Archive builds, bitcode will be generated in the linked binary for submission to the app store. For other builds, the compiler and linker will check whether the code complies with the requirements for bitcode generation, but will not generate actual bitcode.
这里有几个链接可以帮助加深对 Bitcode 的理解:
由于确切的问题是 "what does enable bitcode do",我想提供一些到目前为止我已经弄清楚的技术细节。在 Apple 发布此编译器的源代码之前,几乎不可能 100% 确定其中的大部分内容
首先,Apple 的位码 看起来 与 LLVM 字节码不同。至少,我无法找出它们之间的任何相似之处。它似乎有一个专有的 header(总是以 "xar!" 开头)并且可能有一些 link-time 参考魔法可以防止数据重复。如果你写出一个硬编码的字符串,这个字符串只会被放入数据中一次,而不是像正常的 LLVM 字节码那样预期的两次。
其次,位码并没有真正像预期的那样作为单独的体系结构在二进制存档中发布。它的交付方式与将 x86 和 ARM 放入一个二进制文件(FAT 存档)的方式不同。相反,他们在架构特定的 MachO 二进制文件中使用一个名为“__LLVM”的特殊部分,它随每个支持的架构一起提供(即重复)。我认为这是他们的编译器系统的一个缺点,将来可能会修复以避免重复。
C代码(用clang -fembed-bitcode hi.c -S -emit-llvm
编译):
#include <stdio.h>
int main() {
printf("hi there!");
return 0;
}
LLVM IR 输出:
; ModuleID = '/var/folders/rd/sv6v2_f50nzbrn4f64gnd4gh0000gq/T/hi-a8c16c.bc'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.10.0"
@.str = private unnamed_addr constant [10 x i8] c"hi there![=11=]", align 1
@llvm.embedded.module = appending constant [1600 x i8] c"\DE\C0[=11=]B[=11=][=11=][=11=][=11=][=11=][=11=][=11=]$[=11=][=11=][=11=][=11=]BC\C0\DE![=11=]C[=11=][=11=][=11=][=11=][=11=]B [=11=][=11=][=11=][=11=][=11=][=11=][=11=]#A\C8I29[=11=]C%EBbEB[=11=]BB28I[=11=]A2D$H[=11=]A!#\C4R[=11=]C!r$\C8b\A8\A0\A8@\C6\F0[=11=][=11=][=11=]Q[=11=][=11=]\C7[=11=][=11=][=11=]Bp$\F8\FF\FF\FF\FF[=11=][=11=]DD\CAaE\E6\A1[=11=]D\E0AE\CAaC\D2aE\CA\A1[=11=]D\CCE\DA!C\C80p`y(pwhsphrhxxtpz(yhr`thE\E4\A1E\CA\DC\E1D\DA\C0C\E4!C\DA\A1C\DA[=11=]E\DE!D\DCE\CAAE\DA\A0C\D8!D\DA\A1[=11=]D\DC\E1D\DC\A1[=11=]D\D8\A1C\C2\C1C[=11=]\C2D\DE\A1[=11=]D\D2\C1D\CCaE\DA\C0C\E0\A1[=11=]D\DA!C\E8D[=11=]svr[=11=]wx6pppyhs6hp\A0t[=11=]\CC!C\D8aE\CA \E6E\C2aC\D6\A1[=11=]D\E0AE\DEE\CAaC\E8\E1D\E4\A1[=11=]D\C4\A1E\CC\C1C\CAAE\DA`E\D2AF\CA\C0\A0ps(zhqz[=11=]\C6\E1D\E4\A1C\E4[=11=] \E8!C\E4\E1C\CAE\DA\C0C\CA!C\E8\A1E\E4\A1C\E6Xyy(9`5|;`5y(6Xyr6Xyr\A8wp0rhs6hp\A0t[=11=]\CC!C\D8aE\CA \EAaE\CA\A1[=11=]D\E6\E1D\CCE\DA\C0C\D8\E1D\C2E[=11=]svr[=11=]6\C8\F0\FF\FF\FF\FF\C1[=11=]E\E50[=11=]F\F3\D0\F0 [=11=]F\E50[=11=]E\E90[=11=]F\E5\D0\E6[=11=][=11=]F\ED[=11=]E\E4[=11=]C8\B0\C3<@\B8\C3;\B49\C8C8\B4C9\B4<\BCC:\B8=<\B4A9\B0C:\B4@[=11=]F\F2P[=11=]F\E5[=11=][=11=]C\EE\F0[=11=]Em`[=11=]E\F2[=11=]E\EDP[=11=]Em[=11=][=11=]F\EF[=11=]E\EE@[=11=]F\E5 [=11=]FmP[=11=]E\EC[=11=]E\ED\D0\EE\F0[=11=]E\EE\D0\ECP[=11=]E\E1`[=11=]E[=11=]\E1[=11=]E\EF\D0\E9\E0[=11=]E\E60[=11=]Fm`[=11=]E\F0\D0\ED[=11=]E\F4[=11=]E9;\CCC9[=11=];\BCCB\B8C8\B8\C3<\B49\C0CB\B4C8\D0:[=11=]\E6[=11=]E\EC0[=11=]F\E5[=11=]\F3@[=11=]F\E10[=11=]E\EB\D0\F0 [=11=]F\EF@[=11=]F\E50[=11=]E\F4\F0[=11=]E\F2\D0\E2P[=11=]F\E6`[=11=]E\E5 [=11=]Fm0[=11=]F\E9\A0[=11=]F\E5[=11=]\E0@\D0C8\C8\C39=\B4\C18\C0C=[=11=]\E3\F0[=11=]E\F2P[=11=]Er[=11=]\F4[=11=]E\F2p[=11=]E\E5@[=11=]Fm`[=11=]E\E5[=11=]E\F4P[=11=]F\F2P[=11=]E\F3[=11=]\AC\C1<\CC\C3<\C3C\B0\C1AC>\C4D\B0\C1A\CC\C3<B\AC\C1<\CCC9\C8B\AC\C1<\CCC9\CC@\D4;\CCC8C9\B49\C0CB\B4C8\D0:[=11=]\E6[=11=]E\EC0[=11=]F\E5[=11=]\F50[=11=]F\E5\D0\F3\F0[=11=]E\E6@[=11=]Fm`[=11=]E\EC\F0[=11=]E\E1@[=11=]F9;\CCC9[=11=][=11=]I[=11=][=11=][=11=][=11=][=11=]`B [=11=][=11=][=11=] [=11=][=11=][=11=]D[=11=][=11=][=11=]2 d\A4\E3\A1LC[=11=]BL0sH*[=11=]\C5C`\AA0F7@3[=11=]4|\C0;\F8;\A06xv(6hpw|8p87[=11=]DeP[=11=]Em\D0[=11=]Ez\F0[=11=]Em[=11=]Ev@z`t\D0\E6p\A0q x\D0\EEzv\A0s z`t\D0\B3r:[=11=]FDH #EBDCI[=11=][=11=]@[=11=][=11=]\C0\A7[=11=][=11=] [=11=][=11=][=11=][=11=][=11=][=11=][=11=]8[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]2E[=11=]CLC&G\C6CA([=11=]AM\D0iD]C[=11=][=11=][=11=]y[=11=][=11=]C[=11=][=11=][=11=]ALF4A&PIC Levela\D80\C2\C05c+ab\B2j\B1+BK{s\B9qqAc[=11=]Bs;k\B9qq\A9qI\D9D\D8\D8\EC\DAC\DA\DE\C8\EA\D8\CAC\CC\D8\C2\CE\E6\A6C66\BB64\B227\BA)A[=11=]y[=11=][=11=]2[=11=][=11=][=11=]3C\C4\E1Cf=C8\C3CByxsq[=11=]C\E6[=11=][=11=]F\ED[=11=]E\F4[=11=]E3[=11=]CBE\C2\C1D\CE\A1Cf0=C8B\CC=\C8C=C=\CCxCtp{yHppzpvxp \CC[=11=]E\EC[=11=]E\E10[=11=]Fn0[=11=]F\E3\F0[=11=]E\F0P[=11=]E3\C4D\DE!C\D8!D\C2aEf0;\BC;\D0C9\B4<\BC<;\CC\F0v`{h7hrh7pp`v(v\F8vxw_qry,\EE\F0[=11=]E\EE\E0[=11=]E\F5\C0[=11=]E\EC[=11=]q [=11=][=11=][=11=][=11=][=11=]&`<\D2L[=11=]C4@\F8\D2[=11=][=11=]a [=11=][=11=][=11=]B[=11=][=11=][=11=]A,[=11=][=11=][=11=][=11=][=11=][=11=]4#[=11=]dC0[=11=]3\CA@[=11=]C\C1[=11=][=11=]#[=11=]CB[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]", section "__LLVM,__bitcode"
@llvm.cmdline = appending constant [67 x i8] c"-triple[=11=]x86_64-apple-macosx10.10.0[=11=]-emit-llvm[=11=]-disable-llvm-optzns[=11=]", section "__LLVM,__cmdline"
; Function Attrs: nounwind ssp uwtable
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1
%2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([10 x i8]* @.str, i32 0, i32 0))
ret i32 0
}
declare i32 @printf(i8*, ...) #1
attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"Apple LLVM version 7.0.0 (clang-700.0.53.3)"}
IR 中的数据数组也会根据 clang 的优化和其他代码生成设置而变化。我完全不知道这是什么格式或任何内容。
编辑:
根据推特上的提示,我决定重新访问并确认。我跟随 this blog post 并使用他的位码提取器工具从 MachO 可执行文件中获取 Apple Archive 二进制文件。在使用 xar 实用程序提取 Apple Archive 之后,我得到了这个(当然是用 llvm-dis 转换为文本)
; ModuleID = '1'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.10.0"
@.str = private unnamed_addr constant [10 x i8] c"hi there![=12=]", align 1
; Function Attrs: nounwind ssp uwtable
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1
%2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0))
ret i32 0
}
declare i32 @printf(i8*, ...) #1
attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"Apple LLVM version 7.0.0 (clang-700.1.76)"}
non-bitcode IR 和 bitcode IR 之间唯一显着的区别是每个体系结构的文件名已被剥离为仅 1、2 等。
我还确认了嵌入在二进制文件中的位码是在优化后生成的。如果您使用 -O3 编译并提取位码,则它与您使用 -O0 编译时不同。
为了获得额外的荣誉,我还确认当您下载 iOS 9 应用程序时,Apple 不会向设备发送位码。它们包括许多我不认识的其他奇怪部分,例如 __LINKEDIT,但它们不包括 __LLVM.__bundle,因此在设备上运行的最终二进制文件中似乎没有包含位码。奇怪的是,Apple 仍然将带有单独 32/64 位代码的胖二进制文件发送到 iOS 8 台设备。
更新
Apple has clarified 切片的发生与启用位码无关。我在实践中也观察到了这一点,其中非位码启用的应用程序将仅作为适合目标设备的架构下载。
原创
Bitcode. Archive your app for submission to the App Store in an
intermediate representation, which is compiled into 64- or 32-bit
executables for the target devices when delivered.
Slicing. Artwork incorporated into the Asset Catalog and tagged for a
platform allows the App Store to deliver only what is needed for
installation.
按照我的理解,如果您支持位码,您应用的下载者将只会获得他们自己的设备所需的编译架构。
位码
Bitcode
(on-disk位码表示,位码文件格式,二进制格式)。
是[Intermediate Representation (IR) in LLVM]. It is bitstream(binary encoding) file format for LLVM IR. It is a result of LLVM IR serialization. It can be optionally embedded into Wrapper or Native Object File(Mach-O
inside Raw segment data[About])的三种表示形式之一。 Just-In-Time 编译器是 suitable。
您可以使用 llvm-dis
将 bitcode
IR 转换为人类可读的 IR
Apple 使用的另一个优势是可以在开发人员不注意的情况下为另一个(新)架构(instruction set architecture (ISA)
)重新编译二进制文件。此外,作为一个小的额外功能,您有可能进行逆向工程,这使 Apple 可以更轻松地分析二进制文件,但另一方面,这是一个可以被恶意分子利用的缺点。它还增加了构建时间
当您构建位码时 .BCSymbolMap
[About] 也会生成用于分析错误堆栈跟踪
请注意,位码不是为模拟器(arch x86_64)生成的。
Xcode 在接下来的场景中使用位码:
标志:
-fembed-bitcode
- 嵌入位码
-fembed-bitcode-marker
- 只需标记它将位于的位置。 __LLVM
段为空,没有任何数据
使用:
Enable Bitcode
(ENABLE_BITCODE
)。是 - 应用程序的默认设置,框架目标
- 使用
-fembed-bitcode-marker
进行常规 build
- 使用
-fembed-bitcode
在 archive(Product -> Archive) 或 (xcodebuild archive) 中嵌入位码
将标志显式添加到 Other C Flags
(OTHER_CFLAGS
)
User-Defined 设置 BITCODE_GENERATION_MODE
marker
- 添加 -fembed-bitcode-marker
bitcode
- 添加 -fembed-bitcode
xcodebuild
上面有适当的选项
//please make sure that this settings is placed before xcodebuild params(.e.g. -workspace, -scheme...)
xcodebuild ENABLE_BITCODE=YES
//or
xcodebuild BITCODE_GENERATION_MODE="bitcode"
//or
xcodebuild OTHER_CFLAGS="-fembed-bitcode"
如果您在应用程序中使用 embed bitcode
但并非所有库都支持它,您会得到
ld: bitcode bundle could not be generated because '<path>' was built without full bitcode. All frameworks and dylibs for bitcode must be generated from Xcode Archive or Install build file '<path>' for architecture <arch>
检查二进制是否包含bitcode
The bitcode must be stored in a section of the object file named __LLVM,__bitcode for MachO and .llvmbc for the other object formats.
Bitcode 注入 __LLVM
段三个部分:__bitcode
、__cmdline
、__asm
。 Apple 版本的 LLVM 使用了一些不同的逻辑,并将 __bitcode
和 __cmdline
移动到 __bundle
部分作为 .xar
存档。
eXtensible ARchive(XAR)
- .xar、.pkg 归档器的文件格式,由 header、table 内容 (toc)、堆组成。 TOC 用于随机访问归档文件。 xar中的每个文件都是独立压缩的
otool -l
并找到 __LLVM __bundle.
您可以在 Mach-O 文件
中检查段名称和节名称
但不保证包含bitcode(如marker)
//<segname> <sectname> e.g. __LLVM __bundle. They are started from __
otool -l "/Users/alex/MyModule.framework/MyModule"
//or universal framework(specify arch)
otool -arch arm64 -l "/Users/alex/MyModule.framework/MyModule"
//or all arch
otool -arch all -l "/Users/alex/MyModule.framework/MyModule"
//-l print the load commands
输出:
Section
sectname __bundle
segname __LLVM
addr 0x00000000000c0000
size 0x00000000003af3ce
offset 770048
...
otool -v -s __LLVM __bundle
otool -v -s __LLVM __bundle <binary_path>
//e.g.
otool -v -s __LLVM __bundle "/Users/alex/MyModule.framework/MyModule"
// -s <segname> <sectname> print contents of section. e.g. -s __LLVM __bundle
// -v print verbosely (symbolically) when possible
otool -s __LLVM __bundle 的输出。是比特流(二进制编码)
Contents of (__LLVM,__bundle) section
00000000000b4000 21726178 01001c00 00000000 c60d0000
00000000000b4010 00000000 be860000 01000000 9decda78
00000000000b4020 b6dc735b f3dfc715 5f7a3429 bdc1ce2f
otool -v -s __LLVM __bundle 的输出。它是 XAR 的 table 内容 (TOC)。 -v
将比特流(二进制编码)转换为 XAR 的 table 内容(TOC)XML 格式
For (__LLVM,__bundle) section: xar table of contents:
<?xml version="1.0" encoding="UTF-8"?>
<xar>
<subdoc subdoc_name="Ld">
<version>1.0</version>
...
- 又生成了一个指标
.bcsymbolmap
[About]
查找并提取位码
Closed source Library developer - XCFramework
App developer - enable bitcode
bitcode是强制性的吗
Official
For iOS apps, bitcode is the default, but optional. For watchOS and tvOS apps, bitcode is required.
二进制大小
Bitcode 会增加二进制文件的大小,如果不是强制性的,您可以使用 bitcode_strip
从二进制文件中手动删除 bitcode
例如
xcrun bitcode_strip -r "/Users/alex/MyModule.framework/MyModule" -o "/Users/alex/MyModule.framework/MyModule"
// -r remove bitcode
// -o output file name
我对嵌入的位码术语有疑问。
什么是嵌入式位码?
什么时候启用,ENABLE_BITCODE
in new Xcode?
Xcode 7 中的 ENABLE_BITCODE
启用后二进制文件会发生什么情况?
Bitcode是指发送到iTunes Connect的代码类型:"LLVM Bitcode"。这允许 Apple 使用某些计算来进一步重新优化应用程序(例如:可能缩小可执行文件的大小)。如果 Apple 需要更改您的可执行文件,那么他们可以在不上传新版本的情况下执行此操作。
这不同于: 切片 这是 Apple 根据设备的分辨率和架构为用户设备优化您的应用程序的过程。切片不需要 Bitcode。 (例如:在 5s 上只包含 @2x 图片)
App Thinning是切片、bitcode、按需资源的组合
Bitcode is an intermediate representation of a compiled program. Apps you upload to iTunes Connect that contain bitcode will be compiled and linked on the App Store. Including bitcode will allow Apple to re-optimize your app binary in the future without the need to submit a new version of your app to the store.
Bitcode (iOS, watchOS)
Bitcode is an intermediate representation of a compiled program. Apps you upload to iTunes Connect that contain bitcode will be compiled and linked on the App Store. Including bitcode will allow Apple to re-optimize your app binary in the future without the need to submit a new version of your app to the store.
基本上这个概念有点类似于 java,其中字节码在不同的 JVM 上是 运行,在这种情况下,位码放在 iTune 存储上,而不是将中间代码提供给不同的平台(设备)它提供不需要任何虚拟机的编译代码 运行.
因此我们只需要创建一次位码,它就可以用于现有或即将推出的设备。编译并使其与他们拥有的每个平台兼容是 Apple 的头疼。
开发人员无需进行更改并再次提交应用程序即可支持新平台。
以苹果在其中引入x64
芯片时iPhone 5s为例。尽管 x86
应用程序与 x64
架构完全兼容,但要充分利用 x64
平台,开发人员必须更改架构或某些代码。 s/he 完成后,应用程序将提交到应用程序商店进行审核。
如果这个位码概念早些推出,那么我们开发人员无需进行任何更改即可支持 x64
位架构。
什么是嵌入式位码?
根据docs:
Bitcode is an intermediate representation of a compiled program. Apps you upload to iTunes Connect that contain bitcode will be compiled and linked on the App Store. Including bitcode will allow Apple to re-optimize your app binary in the future without the need to submit a new version of your app to the store.
更新: "New Features in Xcode 7"中的这句话让我想了很久 Bitcode 是为了 切片 以减小应用程序大小:
When you archive for submission to the App Store, Xcode will compile your app into an intermediate representation. The App Store will then compile the bitcode down into the 64 or 32 bit executables as necessary.
但这不是真的,Bitcode 和 Slicing 独立工作:Slicing 是关于减少应用程序大小和生成应用程序包变体,Bitcode 是关于某些二进制优化的。我通过检查非位码应用程序的可执行文件中包含的体系结构并发现它们只包含必要的体系结构来验证这一点。
Bitcode 允许其他 App Thinning 组件调用 Slicing 生成具有特定架构的特定可执行文件的应用程序包变体,例如iPhone 5S 变体将仅包含 arm64 可执行文件,iPad Mini armv7 等等。
何时在新 Xcode 中启用 ENABLE_BITCODE?
For iOS apps, bitcode is the default, but optional. If you provide bitcode, all apps and frameworks in the app bundle need to include bitcode. For watchOS and tvOS apps, bitcode is required.
在新 Xcode 中启用 ENABLE_BITCODE 时二进制文件会发生什么?
来自 Xcode 7 个参考:
Activating this setting indicates that the target or project should generate bitcode during compilation for platforms and architectures which support it. For Archive builds, bitcode will be generated in the linked binary for submission to the app store. For other builds, the compiler and linker will check whether the code complies with the requirements for bitcode generation, but will not generate actual bitcode.
这里有几个链接可以帮助加深对 Bitcode 的理解:
由于确切的问题是 "what does enable bitcode do",我想提供一些到目前为止我已经弄清楚的技术细节。在 Apple 发布此编译器的源代码之前,几乎不可能 100% 确定其中的大部分内容
首先,Apple 的位码 看起来 与 LLVM 字节码不同。至少,我无法找出它们之间的任何相似之处。它似乎有一个专有的 header(总是以 "xar!" 开头)并且可能有一些 link-time 参考魔法可以防止数据重复。如果你写出一个硬编码的字符串,这个字符串只会被放入数据中一次,而不是像正常的 LLVM 字节码那样预期的两次。
其次,位码并没有真正像预期的那样作为单独的体系结构在二进制存档中发布。它的交付方式与将 x86 和 ARM 放入一个二进制文件(FAT 存档)的方式不同。相反,他们在架构特定的 MachO 二进制文件中使用一个名为“__LLVM”的特殊部分,它随每个支持的架构一起提供(即重复)。我认为这是他们的编译器系统的一个缺点,将来可能会修复以避免重复。
C代码(用clang -fembed-bitcode hi.c -S -emit-llvm
编译):
#include <stdio.h>
int main() {
printf("hi there!");
return 0;
}
LLVM IR 输出:
; ModuleID = '/var/folders/rd/sv6v2_f50nzbrn4f64gnd4gh0000gq/T/hi-a8c16c.bc'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.10.0"
@.str = private unnamed_addr constant [10 x i8] c"hi there![=11=]", align 1
@llvm.embedded.module = appending constant [1600 x i8] c"\DE\C0[=11=]B[=11=][=11=][=11=][=11=][=11=][=11=][=11=]$[=11=][=11=][=11=][=11=]BC\C0\DE![=11=]C[=11=][=11=][=11=][=11=][=11=]B [=11=][=11=][=11=][=11=][=11=][=11=][=11=]#A\C8I29[=11=]C%EBbEB[=11=]BB28I[=11=]A2D$H[=11=]A!#\C4R[=11=]C!r$\C8b\A8\A0\A8@\C6\F0[=11=][=11=][=11=]Q[=11=][=11=]\C7[=11=][=11=][=11=]Bp$\F8\FF\FF\FF\FF[=11=][=11=]DD\CAaE\E6\A1[=11=]D\E0AE\CAaC\D2aE\CA\A1[=11=]D\CCE\DA!C\C80p`y(pwhsphrhxxtpz(yhr`thE\E4\A1E\CA\DC\E1D\DA\C0C\E4!C\DA\A1C\DA[=11=]E\DE!D\DCE\CAAE\DA\A0C\D8!D\DA\A1[=11=]D\DC\E1D\DC\A1[=11=]D\D8\A1C\C2\C1C[=11=]\C2D\DE\A1[=11=]D\D2\C1D\CCaE\DA\C0C\E0\A1[=11=]D\DA!C\E8D[=11=]svr[=11=]wx6pppyhs6hp\A0t[=11=]\CC!C\D8aE\CA \E6E\C2aC\D6\A1[=11=]D\E0AE\DEE\CAaC\E8\E1D\E4\A1[=11=]D\C4\A1E\CC\C1C\CAAE\DA`E\D2AF\CA\C0\A0ps(zhqz[=11=]\C6\E1D\E4\A1C\E4[=11=] \E8!C\E4\E1C\CAE\DA\C0C\CA!C\E8\A1E\E4\A1C\E6Xyy(9`5|;`5y(6Xyr6Xyr\A8wp0rhs6hp\A0t[=11=]\CC!C\D8aE\CA \EAaE\CA\A1[=11=]D\E6\E1D\CCE\DA\C0C\D8\E1D\C2E[=11=]svr[=11=]6\C8\F0\FF\FF\FF\FF\C1[=11=]E\E50[=11=]F\F3\D0\F0 [=11=]F\E50[=11=]E\E90[=11=]F\E5\D0\E6[=11=][=11=]F\ED[=11=]E\E4[=11=]C8\B0\C3<@\B8\C3;\B49\C8C8\B4C9\B4<\BCC:\B8=<\B4A9\B0C:\B4@[=11=]F\F2P[=11=]F\E5[=11=][=11=]C\EE\F0[=11=]Em`[=11=]E\F2[=11=]E\EDP[=11=]Em[=11=][=11=]F\EF[=11=]E\EE@[=11=]F\E5 [=11=]FmP[=11=]E\EC[=11=]E\ED\D0\EE\F0[=11=]E\EE\D0\ECP[=11=]E\E1`[=11=]E[=11=]\E1[=11=]E\EF\D0\E9\E0[=11=]E\E60[=11=]Fm`[=11=]E\F0\D0\ED[=11=]E\F4[=11=]E9;\CCC9[=11=];\BCCB\B8C8\B8\C3<\B49\C0CB\B4C8\D0:[=11=]\E6[=11=]E\EC0[=11=]F\E5[=11=]\F3@[=11=]F\E10[=11=]E\EB\D0\F0 [=11=]F\EF@[=11=]F\E50[=11=]E\F4\F0[=11=]E\F2\D0\E2P[=11=]F\E6`[=11=]E\E5 [=11=]Fm0[=11=]F\E9\A0[=11=]F\E5[=11=]\E0@\D0C8\C8\C39=\B4\C18\C0C=[=11=]\E3\F0[=11=]E\F2P[=11=]Er[=11=]\F4[=11=]E\F2p[=11=]E\E5@[=11=]Fm`[=11=]E\E5[=11=]E\F4P[=11=]F\F2P[=11=]E\F3[=11=]\AC\C1<\CC\C3<\C3C\B0\C1AC>\C4D\B0\C1A\CC\C3<B\AC\C1<\CCC9\C8B\AC\C1<\CCC9\CC@\D4;\CCC8C9\B49\C0CB\B4C8\D0:[=11=]\E6[=11=]E\EC0[=11=]F\E5[=11=]\F50[=11=]F\E5\D0\F3\F0[=11=]E\E6@[=11=]Fm`[=11=]E\EC\F0[=11=]E\E1@[=11=]F9;\CCC9[=11=][=11=]I[=11=][=11=][=11=][=11=][=11=]`B [=11=][=11=][=11=] [=11=][=11=][=11=]D[=11=][=11=][=11=]2 d\A4\E3\A1LC[=11=]BL0sH*[=11=]\C5C`\AA0F7@3[=11=]4|\C0;\F8;\A06xv(6hpw|8p87[=11=]DeP[=11=]Em\D0[=11=]Ez\F0[=11=]Em[=11=]Ev@z`t\D0\E6p\A0q x\D0\EEzv\A0s z`t\D0\B3r:[=11=]FDH #EBDCI[=11=][=11=]@[=11=][=11=]\C0\A7[=11=][=11=] [=11=][=11=][=11=][=11=][=11=][=11=][=11=]8[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]2E[=11=]CLC&G\C6CA([=11=]AM\D0iD]C[=11=][=11=][=11=]y[=11=][=11=]C[=11=][=11=][=11=]ALF4A&PIC Levela\D80\C2\C05c+ab\B2j\B1+BK{s\B9qqAc[=11=]Bs;k\B9qq\A9qI\D9D\D8\D8\EC\DAC\DA\DE\C8\EA\D8\CAC\CC\D8\C2\CE\E6\A6C66\BB64\B227\BA)A[=11=]y[=11=][=11=]2[=11=][=11=][=11=]3C\C4\E1Cf=C8\C3CByxsq[=11=]C\E6[=11=][=11=]F\ED[=11=]E\F4[=11=]E3[=11=]CBE\C2\C1D\CE\A1Cf0=C8B\CC=\C8C=C=\CCxCtp{yHppzpvxp \CC[=11=]E\EC[=11=]E\E10[=11=]Fn0[=11=]F\E3\F0[=11=]E\F0P[=11=]E3\C4D\DE!C\D8!D\C2aEf0;\BC;\D0C9\B4<\BC<;\CC\F0v`{h7hrh7pp`v(v\F8vxw_qry,\EE\F0[=11=]E\EE\E0[=11=]E\F5\C0[=11=]E\EC[=11=]q [=11=][=11=][=11=][=11=][=11=]&`<\D2L[=11=]C4@\F8\D2[=11=][=11=]a [=11=][=11=][=11=]B[=11=][=11=][=11=]A,[=11=][=11=][=11=][=11=][=11=][=11=]4#[=11=]dC0[=11=]3\CA@[=11=]C\C1[=11=][=11=]#[=11=]CB[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]", section "__LLVM,__bitcode"
@llvm.cmdline = appending constant [67 x i8] c"-triple[=11=]x86_64-apple-macosx10.10.0[=11=]-emit-llvm[=11=]-disable-llvm-optzns[=11=]", section "__LLVM,__cmdline"
; Function Attrs: nounwind ssp uwtable
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1
%2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([10 x i8]* @.str, i32 0, i32 0))
ret i32 0
}
declare i32 @printf(i8*, ...) #1
attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"Apple LLVM version 7.0.0 (clang-700.0.53.3)"}
IR 中的数据数组也会根据 clang 的优化和其他代码生成设置而变化。我完全不知道这是什么格式或任何内容。
编辑:
根据推特上的提示,我决定重新访问并确认。我跟随 this blog post 并使用他的位码提取器工具从 MachO 可执行文件中获取 Apple Archive 二进制文件。在使用 xar 实用程序提取 Apple Archive 之后,我得到了这个(当然是用 llvm-dis 转换为文本)
; ModuleID = '1'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.10.0"
@.str = private unnamed_addr constant [10 x i8] c"hi there![=12=]", align 1
; Function Attrs: nounwind ssp uwtable
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1
%2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0))
ret i32 0
}
declare i32 @printf(i8*, ...) #1
attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+sse,+sse2,+sse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"Apple LLVM version 7.0.0 (clang-700.1.76)"}
non-bitcode IR 和 bitcode IR 之间唯一显着的区别是每个体系结构的文件名已被剥离为仅 1、2 等。
我还确认了嵌入在二进制文件中的位码是在优化后生成的。如果您使用 -O3 编译并提取位码,则它与您使用 -O0 编译时不同。
为了获得额外的荣誉,我还确认当您下载 iOS 9 应用程序时,Apple 不会向设备发送位码。它们包括许多我不认识的其他奇怪部分,例如 __LINKEDIT,但它们不包括 __LLVM.__bundle,因此在设备上运行的最终二进制文件中似乎没有包含位码。奇怪的是,Apple 仍然将带有单独 32/64 位代码的胖二进制文件发送到 iOS 8 台设备。
更新
Apple has clarified 切片的发生与启用位码无关。我在实践中也观察到了这一点,其中非位码启用的应用程序将仅作为适合目标设备的架构下载。
原创
Bitcode. Archive your app for submission to the App Store in an intermediate representation, which is compiled into 64- or 32-bit executables for the target devices when delivered.
Slicing. Artwork incorporated into the Asset Catalog and tagged for a platform allows the App Store to deliver only what is needed for installation.
按照我的理解,如果您支持位码,您应用的下载者将只会获得他们自己的设备所需的编译架构。
位码
Bitcode
(on-disk位码表示,位码文件格式,二进制格式)。
是[Intermediate Representation (IR) in LLVM]. It is bitstream(binary encoding) file format for LLVM IR. It is a result of LLVM IR serialization. It can be optionally embedded into Wrapper or Native Object File(Mach-O
inside Raw segment data[About])的三种表示形式之一。 Just-In-Time 编译器是 suitable。
您可以使用 llvm-dis
bitcode
IR 转换为人类可读的 IR
Apple 使用的另一个优势是可以在开发人员不注意的情况下为另一个(新)架构(instruction set architecture (ISA)
)重新编译二进制文件。此外,作为一个小的额外功能,您有可能进行逆向工程,这使 Apple 可以更轻松地分析二进制文件,但另一方面,这是一个可以被恶意分子利用的缺点。它还增加了构建时间
当您构建位码时 .BCSymbolMap
[About] 也会生成用于分析错误堆栈跟踪
请注意,位码不是为模拟器(arch x86_64)生成的。 Xcode 在接下来的场景中使用位码:
标志:
-fembed-bitcode
- 嵌入位码-fembed-bitcode-marker
- 只需标记它将位于的位置。__LLVM
段为空,没有任何数据
使用:
Enable Bitcode
(ENABLE_BITCODE
)。是 - 应用程序的默认设置,框架目标- 使用
-fembed-bitcode-marker
进行常规 build - 使用
-fembed-bitcode
在 archive(Product -> Archive) 或 (xcodebuild archive) 中嵌入位码
- 使用
将标志显式添加到
Other C Flags
(OTHER_CFLAGS
)User-Defined 设置
BITCODE_GENERATION_MODE
marker
- 添加-fembed-bitcode-marker
bitcode
- 添加-fembed-bitcode
xcodebuild
上面有适当的选项
//please make sure that this settings is placed before xcodebuild params(.e.g. -workspace, -scheme...)
xcodebuild ENABLE_BITCODE=YES
//or
xcodebuild BITCODE_GENERATION_MODE="bitcode"
//or
xcodebuild OTHER_CFLAGS="-fembed-bitcode"
如果您在应用程序中使用 embed bitcode
但并非所有库都支持它,您会得到
ld: bitcode bundle could not be generated because '<path>' was built without full bitcode. All frameworks and dylibs for bitcode must be generated from Xcode Archive or Install build file '<path>' for architecture <arch>
检查二进制是否包含bitcode
The bitcode must be stored in a section of the object file named __LLVM,__bitcode for MachO and .llvmbc for the other object formats.
Bitcode 注入 __LLVM
段三个部分:__bitcode
、__cmdline
、__asm
。 Apple 版本的 LLVM 使用了一些不同的逻辑,并将 __bitcode
和 __cmdline
移动到 __bundle
部分作为 .xar
存档。
eXtensible ARchive(XAR)
- .xar、.pkg 归档器的文件格式,由 header、table 内容 (toc)、堆组成。 TOC 用于随机访问归档文件。 xar中的每个文件都是独立压缩的
otool -l
并找到 __LLVM __bundle.
您可以在 Mach-O 文件
中检查段名称和节名称但不保证包含bitcode(如marker)
//<segname> <sectname> e.g. __LLVM __bundle. They are started from __
otool -l "/Users/alex/MyModule.framework/MyModule"
//or universal framework(specify arch)
otool -arch arm64 -l "/Users/alex/MyModule.framework/MyModule"
//or all arch
otool -arch all -l "/Users/alex/MyModule.framework/MyModule"
//-l print the load commands
输出:
Section
sectname __bundle
segname __LLVM
addr 0x00000000000c0000
size 0x00000000003af3ce
offset 770048
...
otool -v -s __LLVM __bundle
otool -v -s __LLVM __bundle <binary_path>
//e.g.
otool -v -s __LLVM __bundle "/Users/alex/MyModule.framework/MyModule"
// -s <segname> <sectname> print contents of section. e.g. -s __LLVM __bundle
// -v print verbosely (symbolically) when possible
otool -s __LLVM __bundle 的输出。是比特流(二进制编码)
Contents of (__LLVM,__bundle) section
00000000000b4000 21726178 01001c00 00000000 c60d0000
00000000000b4010 00000000 be860000 01000000 9decda78
00000000000b4020 b6dc735b f3dfc715 5f7a3429 bdc1ce2f
otool -v -s __LLVM __bundle 的输出。它是 XAR 的 table 内容 (TOC)。 -v
将比特流(二进制编码)转换为 XAR 的 table 内容(TOC)XML 格式
For (__LLVM,__bundle) section: xar table of contents:
<?xml version="1.0" encoding="UTF-8"?>
<xar>
<subdoc subdoc_name="Ld">
<version>1.0</version>
...
- 又生成了一个指标
.bcsymbolmap
[About]
查找并提取位码
Closed source Library developer - XCFramework
App developer - enable bitcode
bitcode是强制性的吗 Official
For iOS apps, bitcode is the default, but optional. For watchOS and tvOS apps, bitcode is required.
二进制大小
Bitcode 会增加二进制文件的大小,如果不是强制性的,您可以使用 bitcode_strip
例如
xcrun bitcode_strip -r "/Users/alex/MyModule.framework/MyModule" -o "/Users/alex/MyModule.framework/MyModule"
// -r remove bitcode
// -o output file name