图解静态库链接过程

正常链接过程

假设一个工程里有两个源码文件和两个静态库:

  • main.m
    实现了程序入口main,它又调用了未知方法Fun1Fun2
  • object.m
    实现了方法Fun1Fun3,其中Fun1调用了未知方法Fun4
  • libA.a
    包含两个目标文件,第一个实现了Fun2Fun5,第二个实现了Fun6
  • libB.a
    包含一个目标文件,它实现了Fun2Fun4

下图是链接过程:

  1. 解析所有目标文件,生成一个个符号图(符号图用来表示各符号间的引用关系,紫色节点表示未定义符号)
  2. 将所有的符号图合并成一张符号主图(master graph),中间会用已定义符号替换掉同名的未定义符号。同时生成一张全局符号表,用于记录每个符号的状态。
  3. 若生成的符号主图中仍存在未定义的符号,则按顺序扫描静态库,否则跳转第6步直接生成可执行文件。
  4. 静态库其实就是带有符号目录的目标文件集合。若符号目录匹配到了主图所需符号,则从静态库里找到定义该符号的目标文件,并把它所有的符号都合并到主图和全局符号表中,过程中会替换掉已有的同名符号(包括已定义的)。重复执行第3步和第4步,如果所有静态库都扫描完了,仍然存在未定义符号,则报符号找不到的错误symbol not found
  5. 如果有需要(一般Release下才做),会进行死代码剥离的操作,即从主图入口(main)开始遍历,不能访问到的节点就是无效可剥离的符号。
  6. 将主图中的符号都写入可执行文件中去。 每个符号在可执行文件中的地址顺序,与输入文件的先后顺序保持一致。

经典链接错误

  • symbol not found
    报错时机:链接过程中扫描完所有的静态库和动态库后,仍然存在未定义的符号。
    解决方法:工程中添加定义该符号的静态库,同时添加正确的静态库搜索路径。

  • duplicated symbol
    报错时机:

    1. 如果main.oobject.o实现了同名方法,那链接过程中合并符号图时会报错。
    2. 如果object.olibA.a中实现了同名方法,而且libA.a使用了-force_load描述,它会强行加载libA中所有的目标文件,导致符号冲突。
    3. 多个静态库实现了同名方法,全局用了'-all_load'或者各自都用了'-force_load',会导致符号冲突。
-ObjC

这个flag告诉链接器把库中定义的Objective-C类和Category都加载进来。这样编译之后的app会变大(因为加载了其他的objc代码进来)。但是如果静态库中有类和category的话只有加入这个flag才行。

-all_load

这个flag是专门处理-ObjC的一个bug的。用了-ObjC以后,如果类库中只有category没有类的时候这些category还是加载不进来。变通方法就是加入-all_load或者-force-load。-all_load会强制链接器把目标文件都加载进来,即使没有objc代码。

注意:假如你使用了不止一个静态库文件,然后又使用了这个参数,那么你很有可能会遇到ld: duplicate symbol错误,因为不同的库文件里面可能会有相同的目标文件

-force_load

这个flag所做的事情跟-all_load其实是一样的,只是-force_load需要指定要进行全部加载的库文件的路径,这样的话,你就只是完全加载了一个库文件,不影响其余库文件的按需加载 ,-force_load在xcode3.2后可用。

解决方法:

  1. 同工程源码中不能定义多个同名方法,可以通过修改方法前缀、修改命名空间(宏替换也可)、使用static方法等方式
  2. 谨慎使用'-Objc"、"-all_load"、"-force_load',它常用来强制加载OC的category,对于纯C++库往往没多大意义。
  3. 多个静态库冲突时,如果你是第三方库的提供者,应该用使用prelink等方式减少无用符号的导出(参考如何隐藏SDK符号)。如果你只是第三方库使用者(无源码),有一种风险较大的方法就是在其中一个静态库里剔除冲突的目标文件。

参考文章

Linker Design -- lld

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容