摘自苹果官方文档[3]:
When your app is approved for the App Store, it is encrypted with DRM and recompressed. The added encryption and DRM affects the ability to compress your binary, and as a result you may see a larger App Store file size for your binary than the binary you uploaded on App Store Connect. The exact final size for your app cannot be determined in advance to the accuracy of a single byte.
对项目工程进行 Archive 后会生成 .xcarchive 文件,该文件中包罗了 App、dsYMS 以及别的信息。如图所示为 .xcarchive 文件中包罗的内容:
想要让 CPU 工作就必须向它提供指令和数据,步伐运行时指令和数据存放在内存中。CPU 通过地址总线来指定内存单元的的地址,地址总线的宽度决定了 CPU 的寻址能力,因此 CPU 对寻址范围有肯定的限制。而不同 CPU 的地址总线宽度不同以及它们所采用的指令模式[6]也不一样,所以不同 CPU 的寻址范围也有差别。
B、BL 指令是 ARM 处置惩罚器中的跳转指令,可以让处置惩罚器跳转到指定的目标地址,从那里继承执行。由于寻址范围是受限的,所以跳转距离不能超出这个限制。ld64 链接器在最终 Output(写可执行文件)时,会对所有的跳转指令进行查抄,若发现跳转距离超出限制就会立刻抛出 ld: b(l) ARM64 branch out of range异常,从而链接失败,就会出现了图上所示的现象。
在苹果开源的 ld64-530 OutputFile.cpp 文件[7] 中总结出来,常见 CPU 详细限制寻址范围如下:
2.2 ld64 链接器所做的事变
按照上面的形貌,随着业务的扩张,代码的膨胀,Mach-O 文件会越来越大,那是不是 Mach-O 文件过大时步伐就无法链接成功了?
当然不是!实际上 ld64 链接器知道会出现跳转距离超出限制的环境,所以它在链接过程中会做 Branch Island[8] 算法,对超限制的跳转指令加以掩护。
// PowerPC can do PC relative branches as far as +/-16MB. (+/-16MB 大概是因为注释比力老)
// If a branch target is >16MB then we insert one or more
// “branch islands” between the branch and its target that
// allows island hopping to the target.
// Branch Island Algorithm
//
// If the __TEXT Segment < 16MB, then no branch islands needed
// Otherwise, every 14MB into the __TEXT Segment a region is
// added which can contain branch islands. Every out-of-range
// B instruction is checked. If it crosses a region, an island
// is added to that region with the same target and the B is
// adjusted to target the island instead.
//
// In theory, if too many islands are added to one region, it
// could grow the __TEXT enough that other previously in-range
// B branches could be pushed out of range. We reduce the
// probability this could happen by placing the ranges every
// 14MB which means the region would have to be 2MB (512,000 islands)
// before any branches could be pushed out of range.
从原理部分我们知道了 Mach-O 的 Data 部分有很多 Segment/Section。实际上 ld64 链接器还给每个 Section 归了类,归类的代码可以在苹果开源的 ld64-530 中的 ld.hpp 文件的第 547 行找到:
每个 Section 都属于其中一种类型。Branch Island 算法会对类型是 typeCode 的 Section 中的跳转指令做查抄,如果跳转的距离超出限制,则会在它们之间插入 “branch islands”,跳转指令会先跳到一个 branch island ,再从这个 branch island 跳到目标地址,以此来包管其跳转距离不凌驾限制。此部分的代码在 branch_island.cpp 文件中可以找到。
__TEXT,__text 的类型是 typeCode,因此,__TEXT,__text 中超出范围跳转指令都会被掩护,在最后 Output 查抄时,就不会出现 branch out of range 的异常。所以,正常构建的 App,即使很大也不会出现链接失败的问题,这都是归功于 Branch Island 算法。
在 Mach-O 文件中,只有 __TEXT,__text的类型是 typeCode(在利用-rename_p 移动 Segment/Section 之后,Section 的类型不会发生改变)。源地址在 __text 中的 跳转指令跳转的环境只有两种:__text -> __text 和 __text -> __stubs。
所以 Branch Island 掩护的 跳转指令的地点 Section ,与目标地址地点的 Section, 只有两种环境: