程序员的自我修养——链接,装载与库(六)动态链接

  • 静态链接使得不同的程序开发者和部门能够相对地开发和测试自己的程序模块,从某种意义上来讲大大促进了程序的开发效率,原来限制程序的规模也随之扩大,但是静态链接也存在浪费内存和磁盘空间,模块更新困难的问题。而且一旦程序中有任何模块的更新,整个程序就要重新链。
  • 动态链接的基本思想:把程序按照块分成各个相对独立的部分,在程序运行时才将它们链接在一其形成一个完整的程序,而不是像静态链接一样把所有的程序模块都链接成一个单独的可执行文件。
  • 静态链接是编译时进行链接。
  • 动态链接:程序运行时进行链接,链接时利用程序局部性原理进行链接。
  • 动态链接中多个进程在内存中共享一个目标文件可以节省内存,可以减少物理页面的换入换出和增加CPU的命中率。
  • 动态链接中升级程序库和共享某个模块时,只要将旧的模块覆盖掉即可。
  • 动态链接的方式使得开发过程中各个模块更加独立,耦合度更小。
  • 动态链接时程序在运行时可以动态的选择加载各种程序模块,这个优点可以用来制作程序的插件。
  • 动态链接可以加强程序的兼容性
  • 动态链接涉及运行时的链接,需要有操作系统的支持。
  • Linux下,ELF动态链接文件被称为共享对象(以.so为扩展名),windows下,动态链接文件被称为动态的链接库(以.dll为扩展名)。
  • 程序被加载时,系统的动态链接库会将程序所需要的所有的动态链接库或者共享对象装载到进程的地址空间,并且将程序中所有未决议的符号绑定到相应的动态链接库中,并进行地址的重定位。
  • 程序与动态链接文件的链接由动态链接器来完成,程序在运行时动态链接器将动态链接文件加载到内存中。
  • 静态链接时,整个程序最终只有一个模板即可执行文件,它是一个不可分割的整体。动态链接时,一个程序被分成了若干个文件,由程序的主要部分(可执行文件)和程序依赖的动态链接文件。
  • 静态链接中,当要引用某个目标文件中的函数时,静态链接器必须对该函数进行地址的重定位。动态链接中,当要引用某个动态链接文件中的函数时,动态链接器会将该符号(函数或者变量)的引用标记为一个动态链接的符号。
  • 静态链接中,只有一个可执行文件向虚拟地址空间进行映射。动态链接中,除了可执行文件需要映射外,还要对若干个动态链接文件进行映射。
  • 动态链接器与动态链接文件一样被映射到虚拟地址空间,程序开始运行前会把控制权交给动态链接器。
  • 动态链接文件在编译时的地址是不确定的。
  • 静态共享库就是将各种模块统一的交给操作系统来管理,操作系统在某个特定的地址划分出一些地址块,为那些已知的模块预留足够的内存空间。
  • 动态链接文件在编译时不能假设自己在进程虚拟地址空间中的位置。但是可执行文件可以确定自己的进程虚拟地址空间中的起始位置,因为可执行文件是被第一个加载进内存的。
  • 装载时重定位:在链接时,对所有的绝对地址的引用不作重定位,而把这一步推迟到装载时再完成,一旦模块装载地址确定,即目标地址确定,那么系统就对程序中所有的绝对地址引用进行重定位。
  • 静态链接在链接时进行重定位,动态链接在装载时重定位。
  • 装载是时重定位缺点是指令部分不能在进程中共享。
  • 地址无关代码:把指令中需要修改的部分分离出来,与数据部分放在一起,这样指令部分就可以保持不变,而数据部分可以在每个进程中拥有一个副本。
  • 动态链接中动态链接文件中的地址引用按照是否跨越模板分成两类:模块内部引用和模块外部引用。、
  • 动态链接比静态链接的主要原因是:第一:动态链接对全局对象和静态对象的访问需要进行复杂的GOT定位,然后间接寻址。 第二:程序运行时动态链接器进行链接工作,动态链接器会寻找并装载所需要的共享对象,然后进行符号查找和地址的重定位。
  • 延迟绑定的基本思想是:函数被第一次使用时进行符号解析和地址的重定位。也就是说,当程序开始执行时,当函数被需要的时候由动态链接器对函数进行符号解析和地址的重定位。
  • 静态链接中,操作系统会读取可执行文件的头部,然后将可执行文件中的各个段映射到虚拟内存空间的相应的虚拟内存区域上。
  • 动态链接中,可执行文件被映射到虚拟地址空间后,因为还需要对动态链接的文件进行映射,使用操作系统会启动动态链接器(动态链接器实际也是动态链接文件),将动态链接器映射到虚拟内存空间,操作系统将控制权交给动态链接器的入口地址,动态链接器得到控制权便会执行自身的初始化操作,然后对可执行文件进行链接工作,当链接完成后,动态链接器将控制权交给可执行文件的入口地址处,程序开始正式执行。
  • 动态链接器的位置是由ELF的可执行文件决定的。
  • 动态链接中符号的导入与导出关系,ELF可执行文件专门有一个动态符号表(保存动态链接所需要的符号信息)。
  • 动态链接符号表的结构与静态链接的符号表几乎一模一样,导入函数是对其他目标文件中函数的引用,导出函数是在目标问件中定义的函数。
  • 动态链接中由于导入函数的存在,因此动态链接文件需要进行地址的重定位(运行期进行地址的重定位)。
  • 静态链接中目标文件的重定位是在链接时执行的,动态链接时动态链接文件的重定位是在装载时进行重定位的。
  • 进程初始化时,堆栈里面保存了关于进程执行环境,命令行参数和动态链接器所需要的辅助信息数组。
  • 动态链接的基本实现分为三部分:第一:启动动态链接器 第二:装载所有需要的动态链接文件 第三:动态文件的重定位和符号解析,初始化。
  • 动态链接文件的往往是自举的。动态链接器不需要任何动态链接文件,它本身的重定位工作由自身完成。
  • 动态链接文件的入口地址就是自举代码的入口地址。
  • 动态链接器完成自举后,便会将可执行文件和它自己本身的符号合并到全局符号表中,然后链接器开始寻找可执行文件所依赖的动态链接文件。当一个新的动态链接文件被链接进来时,该动态链接文件的符号会被合并到全局符号表中。最终全局符号表涵盖了动态链接所需要的全部符号。
  • 全局符号介入:一个动态链接文件的符号被另一个动态链接文件的符号所覆盖。
  • 动态链接器的自举和装载所有所需的动态链接文件完成后,链接器遍历可执行文件和每个动态链接文件的重定位表,将它们的GOT/PLP中的每个需要的位置进行修正。
  • 动态链接器不仅是个动态链接文件,而且还是可执行程序。
  • 动态链接器进行静态链接。
发布了78 篇原创文章 · 获赞 11 · 访问量 5069

猜你喜欢

转载自blog.csdn.net/qq_43145594/article/details/104279175