一、理论

1.1 名词解释

Mach-O(Mach object):在 Mac OS 和 iOS 上的一种目标文件格式(object file format),如内核扩展、命令行工具、应用程序、frameworks 和 库(共享和静态),具有 Header、Load commands、Raw segment data 基本结构。
Xcode 上列出的类型有:Executable、Dynamic Libary、Bundle、Static Library、Relocatable Object File。

Library:编译后可以用来共享、分发的代码,也是能被程序调用执行特定任务的例程库。特点是隐藏实现、方便分发。
They provide a library of routines that can be called by an application to perform a specific task.

Framework:是一个分层目录(结构),在单个包中封装共享资源,如动态共享库、nib文件、图像文件、本地化字符串、头文件和参考文档。(framework 在不同语境中可有不同含义,也指框架)
A framework is a hierarchical directory that encapsulates shared resources, such as a dynamic shared library, nib files, image files, localized strings, header files, and reference documentation in a single package.

1.2 编译、链接

基本流程:
预处理/编译:处理源代码中以 “#” 开头的预编译指令
编译:对预处理后的文进行词法、语法、静态分析及优化,生成汇编码
汇编:翻译汇编码为机器指令,生成二进制目标文件
链接:把目标文件(一个或多个)和需要的库(静态库/动态库)链接成可执行文件

Xcode 编译器详细流程:
Lexer :读入源文件,并将其转化成字符流
Parser :将字符流转换成AST(抽象语法树)
Semantic Analysis: 对输入的AST进行语法检查
Code Generation: 代码生成,将AST转换成低层次的IR指令
Optimization: 分析IR指令,将其中潜在会拖慢运行速度的指令干掉
AsmPrinter: 通过IR(中间码)生成特定CPU架构的汇编代码
Assemble: 将汇编代码转化成二进制
Linker: 通常程序会引用其他的二进制文件(.a或者framework),但是这些链接在程序中没有正确的地址,只是个占位符。Linker的工作就是给这些占位符正确的地址

编译器架构
[待补图]

编译器前端 Clang,编译器后端 LLVM。
前后端传递的是中间码 IR,bitcode 是一种特殊形式的中间码。

1.3 动态库 vs 静态库

静态库:又称静态归档库(static archive library)、静态共享库(static shared library)、静态链接共享库(static linked shared library)。
静态链接库是目标文件的集合或归档,在静态链接阶段 app 使用到的静态链接库内容会被拷贝至可执行文件中。

动态库:又称动态共享库(dynamic shared library)、动态链接共享库(dynamic linked shared library)。
动态库不会被静态链接到客户端应用程序,更新动态库无需重新编译应用程序,动态链接库在加载应用程序时加载。

使用静态或动态库的共同特性是模块化、方便维护和复用。

静态库的特点:

  • 可重用但不可共享使用
  • 代码编译至可执行目标文件,运行效率更高、文件比较大时加载会相对较慢
  • 静态链接阶段加载,静态链接器检查使用到库中的符号以确保他们存在,如果不存在,链接器将报错

动态库的特点:

  • 减小可执行目标文件体积
  • 可以不重新编译链接目标文件时更新动态库,方便调试
  • 程序运行时动态加载,由 dyld(dynamic linker load)动态加载器完成动态库的加载和链接
1.4 静态链接 vs 动态链接

静态链接:扫描所有的目标文件获取他们的符号,放到一个全局的符号表中,然后解析符号和重定位。
将静态库中的一个或多个目标文件与我们的目标文件一起作为输入输入链接成可执行文件。链接器链接静态库以目标文件为基本单位。

常见问题:
“ld: dumplicate symbols”:多个目标存在相同符号。
“Undefined symbols”:需要重定位的符号,在全局符号表中未找到。

动态链接:主要由 dyld(dynamic linker load)动态加载器完成动态库的加载和链接。
延迟绑定动态链接共享库不明确的符号直到程序执行的时候。动态链接编辑器只解析被程序引用的不明确符号,如果一个符号没有被引用,它不会被绑定到程序上。
静态链接时也会获取动态库其中的符号表,以对目标文件调用的符号做标记,运行时再绑定和重定位。
动态库有不同寻址方式,如相对寻址,通过 got 间接寻址。
got section:Global Offset Table全局偏移表。
symbols:符号,简单理解就是函数名和变量名。

App using static libraries
[待补图]

App using dynamic libraries
[待补图]

1.5 Framework vs library

Framework 对比(单独)静态和动态库具有以下使用优势:

  • Frameworks group related, but separate, resources together. This grouping makes it easier to install, uninstall, and locate those resources.
  • Frameworks can include a wider variety of resource types than libraries. For example, a framework can include any relevant header files and documentation.
  • Multiple versions of a framework can be included in the same bundle. This makes it possible to be backward compatible with older programs.
  • Only one copy of a framework’s read-only resources reside physically in-memory at any given time, regardless of how many processes are using those resources. This sharing of resources reduces the memory footprint of the system and helps improve performance.
1.6 Embedded Binaries vs Link binary with libraries

Embedded Binaries 将 binary 库或 Framework 复制到目标文件,该配置下不允许添加静态库。
Link binary with libraries 将程序连接到内部或外部的 binary 库或 Framework,构建时拷贝链接的资源至目标文件。

1.7 编译配置选项 Other Link Flags

-ObjC:加载静态归档库中所有Objective-C 类和分类。
Loads all members of static archive libraries that implement an Objective-C class or category.
如果多个静态库中存在相同类文件会报错。
-all_load:加载静态归档库中所有成员。
Loads all members of static archive libraries.
-force_load path_to_archive:加载指定静态归档库的所有成员。
注意:-all_load强制加载所有归档的所有成员。此选项允许您定位特定存档。
Loads all members of the specified static archive library.
Note: -all_load forces all members of all archives to be loaded. This option allows you to target a specific archive.

二、实践

2.1 常见 Mach-O 文件

静态库(ar 归档文件)
[待补图]
动态库(动态链接分享库)
[待补图]
可执行文件
[待补图]

file 命令查看文件格式
otool 命令查看 fat_header 信息
lipo 可以增、删、提取 fat-file

2.2 符号冲突问题

基本总结:

  • 未配置 Other LInk Flags 时,程序调用库顺序(符号绑定)和链接顺序有关,优先 Link binary with libraries 前面库的编译、链接
  • 通过配置 -ObjC 可以检查静态库中的类、符号冲突
  • 配置 -ObjC 或 -all_load 后,程序优先调用静态链接库中的类和方法

关于编译、链接和符号绑定,有更多文档、博客可供参考。
以上内容参考大多来自苹果官方文档,部分内容实际针对 Mac OS 系统,最新功能特性可参看 WWDC 课程。

文档参考:
Mach-O Programming TopicsFramework Programming GuideDynamic Library Programming Topics