《深刻理解计算机系统》第七章学习笔记

第七章  连接数据结构

姓名:王玮怡  学号:20135116函数

1、关于连接工具

一、含义spa

  连接是将各类代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或被拷贝)到存储器并执行。连接是由连接器程序自动执行的。操作系统

二、执行时间命令行

  • 编译时
  • 加载时
  • 运行时

 

2、编译器驱动程序翻译

  驱动编译器:表明用户在须要时调用语言预处理器、编译器、汇编器和连接器。debug

 

3、静态连接3d

一、静态连接器指针

  Unix的静态连接器(static linker)ld,以一组可重位目标文件和命令行参数做为输入,生成一个彻底连接的能够加载和运行的可执行目标文件做为输出。输入的可重定位目标文件由各类不一样的代码和数据节(section)组成。指令在一个节中,初始化的全局变量在另外一个节中,而未初始化的变量又在另一个节中。

二、连接器的两个任务

  • 符号解析:将每一个符号引用恰好与一个符号定义联系起来
  • 重定位:连接器将每个符号定义与一个存储器位置链接起来,而后修改这些符号引用,使得他们指向这个存储器位置,从而重定位

 

4、目标文件

目标文件的三种形式

  • 可重定位目标文件(编译器和汇编器可生成)
  • 可执行目标文件(连接器可生成)
  • 共享目标文件(编译器和汇编器可生成)

 

5、可重定位目标文件

 

  • .text:已编译程序的机器代码。
  • .rodata:只读数据,好比printf语句中的格式串和开关语句的跳转表。
  • .data:已初始化的全局C变量。
  • .bss:未初始化的全局C变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。
  • .symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。
  • .rel.text:一个.text节中位置的列表,当连接器把这个目标文件和其余文件结合时,须要修改这些位置。
  • .rel.data:被模块引用或定义的任何全局变量的重定位信息。
  • .debug:一个调试符号表,其条目是程序中定义的局部变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。只有以-g选项调用编译驱动程序时才会获得这张表。
  • .line:原始C源程序中的行号和.text节中机器指令之间的映射。
  • .strtab:一个字符串表,其内容包括:.symtab和.debug节中的符号表,以及节头部中的节名字。字符串表就是以null结尾的字符串序列。

 

6、符号和符号表

一、连接器的上下文中,有三种不一样的符号:

  • 由m定义并能被其余模块引用的全局符号。全局连接器符号对应于非静态的C函数以及被定义为不带C static属性的全局变量。
  • 由其余模块定义并被模块m引用的全局符号。这些符号称为外部符号(external),对应于定义在其余模块中的C函数和变量。
  • 只被模块m定义和引用的本地符号。有的本地连接器符号对应于带static属性的C函数和全局变量。

二、符号表

 

 

7、符号解析

一、连接器如何解析多重定义的全局符号

(1)强符号:函数和已初始化的全局变量

(2)弱符号:未初始化的全局变量

(3)处理规则:

  • 不容许有多个强符号。
  • 若是有一个强符号和多个弱符号,那么选择强符号。
  • 若是有多个弱符号,那么从这些弱符号中任意选择一个。

二、与静态库连接

  全部的编译系统都提供一种机制,将全部相关的目标模块打包成为一个单独的文件,称为静态库,能够用作连接器的输入。其中,在Linux下是存档文件,Windows下是lib。

  在符号解析的阶段,连接器从左到右按照它们在编译器驱动程序命令行上出现的相同顺序来扫描可重定位目标文件和存档文件。(驱动程序自动将命令行中全部的.c文件翻译成.o文件),在此次扫描中,连接器维持一个可重定位目标文件的集合E(这个集合中的文件会被合并起来造成可执行文件),一个未解析的符号(即引用了可是还没有定义的符号)集合U,以及一个在前面输入文件中已定义的符号集D,初始时,E、U和D都是空的。

 

7、重定位

一、重定位的两个步骤

(1)重定位节和符号定义

  • 连接器将全部相同类型的节合并为同一类型的新的聚合节,将运行时存储器地址赋给新的聚合节,赋给输入模块定义的每一个节,以及赋给输入模块定义的每一个符号。
  • 完成这一步后,程序中的每一个指令和全局变量都有惟一的运行时存储器地址了。

 

(2)重定义节中的符号引用

  • 连接器修改代码节和数据节中对每一个符号的引用,使得它们指向正确的运行时地址。
  • 连接器依赖于称为重定位条目的可重定位目标模块中的数据结构。

二、重定位条目

(1)不管什么时候汇编器遇到对最终位置位置的目标引用,它就会生成一个重定位条目,告诉连接器在将目标文件合并成可执行文件时如何修改这个引用。

(2)代码的重定位条目放在.rel.text中。

(3)已初始化的数据的重定位条目放在.rel.data中。

(4)ELF定义了11种不一样的重定位类型。两种最基本的重定位类型:

  • *R_386_PC32 重定位一个使用32位PC相对地址的引用。
  • *R_386_32 重定位一个使用32位绝对地址的引用。

  • offset:须要被修改的引用的节偏移
  • symbol:标识被修改的引用应该指向的符号
  • type:告知连接器如何修改新的引用

三、重定位符号引用

(1)相对引用

(2)绝对引用

 

8、可执行目标文件

 

 

9、加载可执行目标文件

  加载器将可执行目标文件中的执行代码和数据从磁盘拷贝到存储器中,而后经过跳转到程序的第一条指令或入口点来运行该程序。这个将程序拷贝到存储器并运行的过程叫作加载。

  • 在32位Linux系统中,代码段老是从地址0x08048000处开始。
  • 数据段是在接下来的下一个4KB对齐的地址处。
  • 运行时堆在读/写段以后接下来的第一个4KB对齐的地址处,并经过调用malloc库往上增加。
  • 有一个段是为共享库保留的。
  • 用户栈老是最大的合法用户地址开始,向下增加的(向低存储器地址方向增加)。从栈的上部开始的段是为操做系统驻留存储器的部分(也就是内核)的代码和数据保留的。

 

10、动态连接共享库

一、静态库的缺点:

  • 静态库在更新时,使用该库的程序须要与更新的库进行从新连接。
  • 因为使用静态库的程序在连接时都会拷贝静态库里被应用程序引用的目标模块,像printf和scanf这样的函数的代码在运行时都会被复制到每一个运行进程的文本段中,这形成了冗余,浪费了稀缺的存储器资源。

二、共享库

  • 共享库是一个目标模块,在运行时,能够加载到任意的存储器地址,并和一个在存储器中的程序连接起来。这个过程称为动态连接,是由一个叫作动态连接器的程序来执行的。
  • 共享库也称为共享目标,在Unix系统中一般用.so后缀来表示。微软的操做系统大量地利用了共享库,它们称为DLL(动态连接库)。
  • 共享库是以两种不一样的方式来“共享”的(在Windows中分别称为“隐式连接”和“显示连接”)。
  •       首先,在任何给定的文件系统中,对于一个库只有一个.so文件。全部引用该库的可执行目标文件共享这个.so文件中的代码和数据,而不是像静态库的内容那样被拷贝和嵌入引用它们的可执行的文件中。其次,在存储器中,一个共享库的.text节 一个副本能够被不一样的正在运行的进程共享。

 

11、从应用程序中加载和连接共享库

#include<dlfcn.h>

void *dlopen(const char *filename,int flag); //返回:若成功则为指向句柄的指针,若出错则为NULL

void *dlsym(void *handle,char *symbol); //返回:若成功则为指向符号的指针,若出错则为NULL

int dlclose(void *handle); //返回:若成功则为0,若出错则为-1

const char *dlerror(void);

 

12、与位置无关的代码(PIC)

  • 编译库代码,使得不须要连接器修改库代码就能够在任何地址加载和执行这些代码。
  • 用户对GCC使用-fPIC选项指示GNU生成PIC代码

 

十3、处理目标文件的工具

 

 

十4、总结

  连接能够在编译时由静态编译器来完成,也能够在加载时和运行时由动态连接器来完成。连接器处理成为目标文件的二进制文件,它有三种不一样的形式:可重定位的、可执行的和共享的。可重定位的目标文件由静态连接器合并成为一个可执行的目标文件,它能够加载到存储器中并执行。共享目标文件(共享库)是在运行时由动态连接器连接和加载的,或者隐含地在调用程序被加载和开始执行时,或者根据须要在程序调用dlopen库的函数时。

相关文章
相关标签/搜索