《深入理解计算机系统》读书笔记(七)在系统上运行程序

链接  ——  将各种代码和数据片段收集并组合成为一个单一文件的过程,此文件能够被加载到内存中并执行。

链接可发生的阶段:编译时、加载时以及运行时

源文件到可执行目标文件的转化

1、翻译器:预处理(cpp)、编译(ccl)、汇编(as)

2、链接器(ld)

3、加载器:将可执行文件中的代码和数据复制到内存,然后将控制转移到这个程序的开头

静态链接器

输入:一组可重定位目标文件、命令行

输出:完全链接、可以加载和运行的可执行目标文件

链接器的主要功能

1、符号解析:目标文件定义和引用符号,将每个符号引用和一个符号定义关联起来

2、重定位:通过把每个符号定义与一个内存位置关联起来,重定位这些字节,使得其指向这个内存位置

目标文件的主要形式

1、可重定位目标文件:可在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件

2、可执行目标文件:可以直接加载到内存并执行

3、共享目标文件:特殊的可重定位目标文件,可在加载或运行时被动态加载进内存并链接

不同系统的目标文件格式不同

Unix  ——  a.out格式

Windows  ——  可移植可执行格式(Portable Executable,PE)

Mac OS-X  ——  Mach-O格式

现代Linux和Unix  ——  可执行可链接格式(Executable and Linkable Format,ELF)

可重定位目标文件的组成

1、ELF头、节头部表

2、节

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

—— .rodata:只读数据

—— .data:已初始化的全局和静态C变量

—— .bss:未初始化的全局和静态C变量,以及所有被初始化为0的全局和静态变量,不占据任何实际的磁盘空间

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

—— .rel.text:一个.text节中位置的列表

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

—— .debug:一个调试符号表,条目是程序中定义的局部变量和类型定义,只有以-g选项调用编译器驱动程序时,才能得到该表

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

符号和符号表

1、由模块m定义并能被其他模块引用的全局符号

2、由其他模块定义并被模块m引用的全局符号

3、只被模块m定义和引用的局部符号

可重定位目标文件中的三个特殊伪节

ABS —— 不该被重定位的符号

UNDEF —— 未定义的符号,即在本目标模块中引用,但在其他地方定义的符号

COMMON —— 还未被分配位置的未初始化的数据目标

条目的格式

COMMON和.bss的区别

——  COMMON,未初始化的全局变量

—— .bss,未初始化的静态变量以及初始化为0的全局或静态变量

源文件  ——  模块

带static属性的全局变量或函数  ——  模块私有的

不带static属性的全局变量或函数  ——  公共的 

符号解析

解析方法:将每个引用与它输入的可重定位目标文件的符号表中的一个确定的符号定义关联起来。

解析全局符号  ——  将假设该符号是在其他模块定义,并生成一个链接器符号表条目,交由链接器处理,链接器将在所有输入模块中寻找这一符号定义。

关于重载方法的解析

  —— 在C++或java中,编译器会将每个唯一的方法和参数列表组合编码成一个对链接器来说唯一的名字,即“重整”的过程。

多重定义符号的处理规则

1、不允许有多个同名的强符号

2、若强符号与弱符号同名,则选择强符号

3、若多个弱符号同名,则从中任意选择一个

静态链接库

编译系统将所有相关函数编译为独立的目标模块,并打包成一个单独的文件,即为静态库

优点:

1、在链接时链接器只需复制被程序引用的目标模块,减少了可执行文件在磁盘和内存中的大小

2、应用程序员只需包含较少的库文件的名字

链接器工作的基本步骤

1、扫描命令行上的每个输入文件,判断输入文件f是目标文件还是存档文件(静态库)。若f是目标文件,则将f添加到可重定位目标文件集合E中,并修改未解析符号集合U和前面输入文件中已定义符号集合D来反映f中的符号定义和引用。

2、如果f是一个存档文件,则匹配U中未解析符号和存档文件中定义的符号。若某个存档文件成员m定义了一个符号来解析U中的一个引用,就将m添加到E中,并且修改U和D来反映m中的符号定义和引用。重复这个过程直至U和D都不在发生变化

3、如果链接器完成对命令行上输入文件的扫描后,U是非空的,则输出一个错误并终止;否则,合并可重定位E中的目标文件,构建输出的可执行文件。

静态库的使用原则

1、一般将静态库放在扫描文件命令行的结尾

2、若库中的成员不是相互独立的,则需对其进行排序,使得在命令行中满足存档文件成员外部引用的符号S的定义在对S的引用之后

重定位的步骤

1、重定位节 和符号定义,完成后程序中的每条指令和全局变量都有唯一的运行时内存地址

2、重定位节中的符号引用,链接器修改代码节和数据节中对每个符号的引用,使得其指向正确的运行地址

重定位条目类型

1、重定位PC相对引用

2、重定位绝对引用

程序运行的时的内存映像

动态共享链接库

共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并一个在内存中的程序链接起来

静态链接是在创建可执行文件(即编译)时完成,动态链接则是在加载程序时完成。

多个进程如何共享一个共享库?

采用位置无关代码 ——  可以加载而无需重定位的代码(Position-Independent Code,PIC)

原则:无论在内存总的何处加载一个目标模块,数据段与代码段的距离总是保持不变,与代码段和数据段的绝对内存位置是无关的

实现:对于每一个调用共享库函数的目标模块,它都有自己的GOT(全局偏移量表)和PLT(过程链接表)

猜你喜欢

转载自blog.csdn.net/yhl_sophia/article/details/85756928
今日推荐