一.几个重要的基本概念函数
连接:连接是将各类代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件能够被加载到存储器中并执行。工具
编译器驱动程序:编译的过程能够分为如下几个步骤:1.语言预处理器 2.编译器 3.汇编器 4.连接器编码
静态连接:以一组可重定位目标文件和命令行参数做为输入,生成一个彻底连接的能够加载和运行的可执行目标文件做为输出。输入的可定位目标文件,包含二进制代码和数据,其形式能够在编译时与其余可重定位目标文件合并起来,建立一个可执行目标文件。spa
目标文件:1.可重定位目标文件 2.可执行目标文件 3.共享目标文件命令行
可重定位目标文件:一个典型的ELF可重定位目标文件格式,翻译
ELFdebug |
.text进程 |
.rodataci |
.data字符串 |
.bbs |
.symtab |
.rel.text |
.debug |
.line |
.strtab |
节头部表 |
符号和符号表:
每一个可重定位目标模块m都有一个模块表,它表示m所定义和引用的符号的信息,在连接的上下文中,有三种不一样的符号:
1.由m定义并能被其余模块引用的全局符号2.由其余模块定义并能被模块m引用的全局符号 3.只能被模块m定义和引用的本地符号
二.重要的几个过程
静态库连接:
在符号解析的阶段,连接器从左到右按照它们在编译器驱动程序命令行上出现的相同顺序来扫描可重定位目标文件和存档文件。(驱动程序自动将命令行中全部的.c文件翻译成.o文件),在此次扫描中,连接器维持一个可重定位目标文件的集合E(这个集合中的文件会被合并起来造成可执行文件),一个未解析的符号(即引用了可是还没有定义的符号)集合U,以及一个在前面输入文件中已定义的符号集D,初始时,E、U和D都是空的。
重定位:
一旦连接器完成了符号解析这一步,它就把代码中的每个符号引用和一个肯定的符号定义(跟它的一个输入目标模块中的一个符号表条目)联系起来,在这时,连接器就知道它的输入目标模块中的代码节和数据节的确切大小,如今就能够开始重定位了,在这个步骤中,将合并输入模块,并为每一个符号分配运行时地址。重定位包括两个步骤:1.重定位节和符号定义 2.重定义节中的符号引用
动态连接共享库:共享库是以两种不一样方式来“共享”的,首先在任何给定的文件系统中,对于一个库只有一个.so文件,全部引用该库的可执行目标文件共享这个.so文件中的代码和数据,而不是像静态库的内容那样被拷贝和嵌入到引用它们的可执行文件中,其次,在存储器中,一个共享的.text节的一个副本能够被不一样正在运行的进程共享。
从应用程序中加载和连接共享库:应用程序还有可能在它运行时要求动态连接器加载和连接任意共享库,而无需在编译时连接到那些库到应用中。
处理目标文件的一些工具:
AR:建立静态库,插入、删除、列出和提交成员
STRINGS:列出一个目标文件中全部可打印的字符串,
STRIP:从目标文件中删除符号表信息
NM:列出一个目标文件的符号表中定义的符号
SIZE:列出目标文件中的节的名字和大小
READELF:显出一个目标文件的完整结构,包括ELF头中的编码全部信息,包含SIZE和NM的功能
OBJDUMP:全部二进制工具之母,可以显示一个目标文件中全部的信息,它最大的做用是反汇编.text中的二进制指令。
三.小结
连接能够在编译时由静态编译器来完成,也能够在加载时和运行时由动态连接器来完成。连接器处理成为目标文件的二进制文件,它有三种不一样的形式:可重定位的、可执行的和共享的。可重定位的目标文件由静态连接器合并成为一个可执行的目标文件,它能够加载到存储器中并执行。共享目标文件(共享库)是在运行时由动态连接器连接和加载的,或者隐含地在调用程序被加载和开始执行时,或者根据须要在程序调用dlopen库的函数时。连接器的两个主要任务是符号解析和重定位,符号解析将目标文件中的每一个全局符号都绑定到一个惟一的定义,而重定位肯定每一个符号的最终存储地址,并修改对那些目标的引用。