源代码、目标文件、可执行文件
源代码
被编译成机器代码,也就是目标文件
,链接器把多个目标文件进行合并并且和系统库进行连接,得到可执行文件
。
源代码(文本)->汇编语言程序(文本): 预处理、编译
汇编语言程序(文本)->可重定位目标文件(二进制):汇编
可重定位目标文件(二进制)->可执行程序(二进制):链接
可重定位目标文件
使用的是相对物理(内存)地址,重定位的目的就是变相对物理地址到绝对物理地址。
符号表
关于符号表,StackOverflow上的解释:
First, there's the symbol table in your object files. Usually, a C or C++ compiler compiles a single source file into an object file with a .obj or .o extension. This contains a collection of executable code and data that the linker can process into a working application or shared library. The object file has a data structure called a symbol table in it that maps the different items in the object file to names that the linker can understand. If you call a function from your code, the compiler doesn't put the final address of the routine in the object file. Instead, it puts a placeholder value into the code and adds a note that tells the linker to look up the reference in the various symbol tables from all the object files it's processing and stick the final location there.
C或C++的编译器把源文件编译成目标文件(object file)
,目标文件包含了可运行的代码和数据,链接器把它们处理成可执行文件
。目标文件内包含了一个称为符号表
的数据结构,它把目标文件里面不同的对象映射到链接器能理解的名字里。如果你在代码里调用了一个函数,编译器不会把这个函数的最终地址直接放到目标文件里,而是放置一个占位值并告诉链接器在链接时到不同的目标文件的不同符号表里面去找对应的引用,最终把地址放到代码里。
iOS符号表
符号表是内存地址与函数名、文件名、行号的映射表。符号表元素如下所示:
<起始地址> <结束地址> <函数> [<文件名:行号>]
利用符号表我们能对APP发生Crash的程序堆栈进行解析和还原。
iOS平台中,
dSYM
文件是指具有调试信息的目标文件,文件名通常为:xxx.app.dSYM
链接器在链接多个目标文件的过程中,会创建一个符号表,用于记录所有已定义的和所有未定义的符号。链接时如果出现相同符号的情况,就会出现“ld: dumplicate symbols”的错误信息;如果在其他目标文件里没有找到符号,就会提示“Undefined symbols”的错误信息。
链接
前面提到要生成可执行文件就需要链接器对目标文件进行处理,其中就包括链接一些库。
Mach-O
是macOS/iOS上程序以及库的标准格式,也就是可执行文件格式。
链接器的作用,就是完成变量、函数符号和其地址绑定这样的任务。而这里我们所说的符号,就可以理解为变量名和函数名。
链接器还要把项目中的多个 Mach-O 文件合并成一个。
链接分为静态链接和动态链接。静态链接直接把库合并到可执行文件里面,这样会导致包增大。动态链接则是在运行时把地址进行绑定即可。
iOS平台上库文件格式
库文件主要分动态库和静态库两种。
动态库:文件后缀名有.dylib和.framework。 链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。
静态库:文件后缀名有.a和.framework。.framework是一个文件包,包含二进制文件、头文件及相关的资源文件。 链接时完整地拷贝至可执行文件中。
dyld
是苹果的动态链接器。
使用 dyld 加载动态库,有两种方式:有程序启动加载时绑定和符号第一次被用到时绑定。为了减少启动时间,大部分动态库使用的都是符号第一次被用到时再绑定的方式。
简单来说,链接器就是把函数或变量名称和地址链接起来。