了解苹果的LD
其实文章关于传统
Section Based Linker那块我还没怎么读懂,有兴趣的欢迎互相探讨。
之前研究Xcode 10兼容libstdc++的时候,稍微把玩了下苹果的LD,借这个机会正好通读了下苹果的LD设计,本文做一下总结。
Atom & FixUp
苹果的LD,核心理念就是基于Atom和FixUp,拿着两个术语是啥意思呢?
Atom就是一块代码(函数)或者数据(全局变量)之类的,每个Atom都有一些属性,比如名称、作用域、内容类型、字节对齐之类的。Fixup可以理解为一个包含种类、便宜、辅助加数以及目标Atom的数据结构。
有点抽象对吧?概要来说,苹果LD通过Atom和FixUP构建一张图,图中的节点都是Atom,连接Atom的则是FixUP。
通过构建这样一张图,苹果就可以在链接期间进行一系列的优化,比如死代码剔除,怎么做呢?比如一段代码也会被抽象成Atom,如果没有FixUP连接的Atom就可以进行剔除
举一个简单的小例子main.c:
#include <stdio.h>
int main()
{
printf("hello world");
return 0;
}
会被抽象出如下行为:

单独编译这个.c文件生成的.o会包含两个atom,一个是main函数,另外一个是C字符串"hello world"
printf 本质上也会一个
atom,但是在这个编译单元内他还没加入图中。
而fixup也存在两个,一个是去调用不知道在哪的函数printf的调用fixup,一个是去加载字符串的fixup。
链接过程
链接过程的第一步就是要处理输入文件,构建一张初始图。
- 如果输入文件是
.o,那么所有的atom都会被加入到初始图当中。 - 如果输入文件是静态库(静态库基本上就是一组
.o文件包含一个目录),初始状态下这里面的atom都默认不会加入到里面,当LD不断初始图中有没被决议的fixup,如果fixup对应的目标atom在这个静态库里面的话,就会把找到的atom的加入到图内。 - 动态库其实在链接期间不会添加任何的
atom,同静态库一样,如果有没被决议的fixup对应的atom在动态库内找到(比如tbd声明的那些),就就提供一个代理,这个代理标记了这个符号来自哪个动态库
- 如果输入文件是
本质上来说,链接期间动态库的作用就是参与标记一下。
考虑完符号决议,还要考虑符号合并之类的,比如根据字符串表的设计,来自不同文件的相同字符串,比如
"haha",不可能保留两份,需要合并。此外还有诸如C中的tentative definitions,C++ Weak Symbol等处理
fixup的时候,也需要分几种类型,见下图:

其他
虽然苹果的LD已经抽象成了Atom-FixUP的架构,但是它的可执行文件Mach-O还是传统的基于section的结构,这限制了Atom-FixUP的能力。