要想了解底层,链接是一个不得不过的一关,我总结了下学习的心得,首先要了解链接器到底是如何工作的,链接器分为两类,一个是静态链接,一个是动态链接,先来讲解静态链接,静态链接要干两件事:
- 符号解析 目标文件定义和引用符号 符号解析的目的是将每个符号引用和一个符号定义联系起来
- 重定位 编译器和汇编器生成从地址零开始的代码和数据节。链接器通过把每个符号定义与一个存储器位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器位置,从而重定位这些节。 静态链接的输入文件是一系列的目标文件,输出是可执行的目标文件。这些目标文件都可以统称为ELF文件:executable and linkable format(可执行和可链接格式)。 从而引入了一个重要的知识点:ELF文件格式。 首先是可重定位目标文件的ELF文件:从上到下依次是:
可重定位目标文件的ELF文件 |
---|
ELF头部 |
.text |
.rodata |
.data |
.bss |
.symtab |
rel.text |
rel.data |
.debug |
.line |
.strtab |
节头部表 |
注意上面的除了节头部表,其余都可以叫做节,这就是一个很核心的点,节是和链接相关的,每一个节经过链接会变成段,段对应的是执行相关的,而且段对应的是可执行目标文件的ELF文件,现在就来看看经过静态链接生成的可执行目标文件的ELF文件格式,这里面都是以段作为术语的:
可执行目标文件的ELF文件 |
---|
ELF头部 |
段头表 |
.init |
.text |
.rodata |
.data |
.bss |
.symtab |
.debug |
.line |
.strtab |
节头表 |
随即这些段就会被加载到存储器地址中,也就是大名鼎鼎的存储器结构,包含栈堆,内核等等的结构。 这就是一个静态链接器如何将一个可重定位文建变成可执行目标文件从而运行到平台上。 动态链接有是怎么回事呢? 在了解之前,我们要知道静态库,刚才我们所说的都仅仅是将一系列的可重定位目标文件变成一个可执行目标文件,实际上,所有的编译系统都提供了一种机制,将所有的目标模块打包成一个单独的文件,称为静态库,他也可以作为静态链接器的输入,当链接器构造一个输出文件时,它只需要拷贝静态库里被应用程序引用的目标模块。 像一些glibc提供的函数,我们可能随时随处都会用上,如果我们每次把函数的代码复制到每个进程的文本段中,这无疑是一种浪费资源,所以出来了共享库的概念,在运行时,可以加载到任意的存储器地址,并在存储器中和一个程序链接起来,这个过程就叫做动态链接。共享库也叫共享目标,以.so为结尾,像linux系统提供了dlopen这样函数去加载和链接共享库。 其实.symtab这个存放符号的地方也是值得研究的,有机会再记录。