天天看点

链接的一些基础理解

7.1 编译器驱动程序

1. 一般调用预处理,编译器,汇编器,链接器来完成一个程序的驱动

1.首先运行C预处理器cpp,将C源程序main.c翻译成一个ASCII码的中间文件main.i。

2.接下来,C编译器cc1将main.i翻译成一个ASCII汇编语言文件main.s。

3.最后,汇编器as将main.s翻译成一个可重定位目标文件main.o。

4.类似地,生成其他.o文件。最后,运行链接器程序ld,将main.o和其他.o以及一些必要的系统目标文件组合起来,创建一个可执行目标文件。

7.2 静态链接

在以上的这个过程中ld链接器的主要工作:

① 符号解析。目标文件定义和引用符号,符号解析的目的是将每个符号引用和一个符号定义联系起来。

(符号:一个函数,一个全局变量,一个静态变量)

②重定位:把每个符号定义与一个存储器位置联系起来,然后修改对这些符号的引用,是的他们指向这个存储器位置,从而实现重定位。

目标文件是字节块的集合,有的包含代码,有的包含程序数据,其他则是包含引导链接器和加载器的数据结构。

7.3 目标文件

目标文件共有三种形式:

1.可重定位目标文件。包含二进制代码和数据,其可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件。(.o文件)

2.可重定位目标文件:包含二进制代码和数据,在运行的时候可以将和其他可重定位目标文件合并变成可执行文件。(类似a.out)

3.共享目标文件。一种特殊类型的可重定位目标文件,可以在加载或者运行时被动态地加载到存储器并链接。(.so文件也称库文件)

编译器和汇编器生成可重定位目标文件(包括共享目标文件),链接器生成可执行目标文件。一般讨论可执行可链接格式(ELF)

7.4 可重定位目标文件

1.ELF的格式:

链接的一些基础理解

.text:已编译程序的机器代码。

.rodata:制只读数据,比如printf语句中格式串和开关语句的跳转表。

.data:已初始化的全局和静态C变量。局部C变量在运行时被保存在栈中,既不出现在.data节中,也不在.bss节中。

.bss:未初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量。

.symtab:符号表,存放在程序中定义和引用的函数和全局变量的信息。

.rel.text:一个.text节中位置的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。

.rel.data:被模块引用或定义的全局变量的重定位信息。

.debug:一个调试符号表,包含程序中定义的局部变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。

.line:原始C源程序中行号和.text节中机器指令之间的映射。

.strtab:一个字符串表,包含.symtab和.debug节中的符号表,以及节头部中的节名字。

这部分知识点看其他的文章摘下来的。

7.5 符号和符号表

每个可重定位目标模块都有一个符号表,它包含m所定义和引用的符号的信息。在链接器的上下文中,有三种不同的符号:

有m定义且能被其他模块引用的全局符号:全局链接器符号对应于非静态的C函数以及被定义为不带C static属性的全局变量

由其他模块定义并被模块m引用的全局符号:称之为外部符号(external),对应于定义在其他模块中的C函数和变量。

只被模块m定义和引用的本地符号:这些符号在m中随处可见但不能被其他模块引用。

符号表是由汇编器构造的,使用编译器输出到汇编语言.s文件中的符号。

其中.symtab节包含ELF符号表,这张符号表包含一个条目的数组,也可以说是一个结构体数组。

7.6 符号解析

对多重定义的全局符号的解析:

当我们遇到多重定义的全局符号怎么办呢?首先全局符号被分为强或弱,这个信息包含在符号表中,函数和已初始化的全局变量是强符号,未初始化的全局变量是弱符号。根据强弱符号的定义,Unix链接器使用下面的规则来处理多重定义的符号:

规则1:不允许有多个强符号。

规则2:如果有一个强符号和多个弱符号,那么选择强符号。

规则3:如果有多个弱符号,那么从这些弱符号中任意选择一个。

如果链接的程序定义了多个强符号,则会报错。但是规则2和3会导致一些不易察觉的错误:

链接的一些基础理解

函数f将x的值由15213改为15212,这会给main函数的作者带来意外。

静态库:

编译系统提供一种机制,将所有相关的目标模块打包成为一个单独的文件,称为静态库,其文件后缀为.a,里面包含多个.o文件。它可以用做链接器的输人,当链接器构造一个输出的可执行文件时,它只拷贝静态库里被应用程序引用的目标模块。

使用静态库可以减少可执行文件在磁盘和内存中的大小。

链接的一些基础理解

使用ar工具ar rcs .a .o .o 创建静态库

使用gcc -static -o prog(自己起名) .o ./.a 链接静态库,(注意,.a文件必须在要引用它的文件后面,否则会出错)