计算机组成 -- 动态链接
静态链接
- 把对应的不同文件内的代码段合并在一起,成为最后的可执行文件,可以做到代码在开发阶段的复用
- 很多程序都需要通过装载器装载到内存里面,里面链接好的同样的功能代码,也都需要再装载一遍,再占一遍内存空间
动态链接
- 动态链接过程中,需要链接的不是存储在硬盘上的目标文件代码,而是加载到内存中的共享库(Shared Libraries)
- 加载到内存中的共享库会被很多程序的指令调用
- Windows的共享库文件是
.dll
(Dynamic-Link Libary)文件;Linux的共享库文件是.so
(Shared Object)文件
地址无关 + 相对地址
- 要在程序运行时共享代码,这些机器码必须是地址无关的,即编译出来的共享库文件的指令代码,是地址无关码
- 地址无关码(PIC):Position-Independent Code,无论加载到哪个物理内存地址,都能够正常执行
- 大部分函数库都能做到地址无关,因为都是接受特定的输入,进行确定的操作,然后给出返回结果
- 对于所有动态链接共享库的程序来讲
- 虽然共享库用的都是同一段物理内存地址
- 但在不同的应用程序里,共享库所在的虚拟内存地址是不同的
- 不应该要求动态链接同一个共享库的不同程序,必须把这个共享库所使用的虚拟内存地址变成一致 – 万恶的侵入性
- 动态共享库编译出来的代码指令如果做到地址无关的?
- 动态共享库内部的变量和函数调用很容易解决,只需要使用相对地址即可
- 各种指令中使用到的内存地址,给出的不是一个绝对的地址空间,而是一个相对于当前指令偏移量的内存地址
- 整个共享库是放在一段连续的虚拟内存地址中
- 无论装载到哪一段虚拟内存地址,不同指令之间的相对地址都是不变的
- 动态共享库内部的变量和函数调用很容易解决,只需要使用相对地址即可
解决方案:PLT + GOT
源代码
lib.h
1 | // lib.h |
lib.c
1 | // lib.c |
show_me_poor.c
1 | // show_me_poor.c |
lib.c -> lib.so
1 | $ gcc lib.c -fPIC -shared -o lib.so |
-fPIC
:Position Independent Code,编译成一个地址无关的代码- gcc编译了可执行文件
show_me_poor
:动态链接了lib.so
PLT – Procedure Link Table
1 | $ objdump -d -M intel -S show_me_poor |
- main函数调用show_me_the_money函数时,对应的代码是
call 4004f0 <show_me_the_money@plt>
@plt
:需要从PLT,也就是程序链接表(Procedure Link Table)查找要调用的函数,对应的地址是4004f0
- 来到4004f0,里面又有一次跳转
jmp 4004e0 <.plt>
,来到GLOBAL_OFFSET_TABLE,即GOT - GLOBAL_OFFSET_TABLE即全局偏移表
GOT – Global Offset Table
- 在共享库的**
data section
,保存了一张全局偏移表(GOT**,Global Offset Table) - 重要前提:共享库的代码部分和数据部分
- 代码部分的物理内存是共享的
- 数据部分是各个动态链接它的应用程序里面各加载一份的
- 所有需要引用当前共享库外部地址的指令,都会查询GOT,来找到当前运行程序的虚拟内存里的对应位置
- 步骤:想要调用共享库的实际指令 -> PLT -> GOT -> 本进程的虚拟内存地址 -> 物理内存地址(共享库)
- GOT里的内容,是在本进程加载一个个共享库的时候写进去的
- GOT里的内容是运行时计算的,并非编译时确定的
- 不同的进程,调用同样的lib.so,各自GOT里面指向最终加载的动态链接库的虚拟内存地址是不同的!!
- 不同的程序调用同样的动态库,各自的内存地址是独立的,调用的又都是同一个动态库
- 但不需要去修改动态库里面代码所使用的地址,各个程序各自维护好自己的GOT,能够找到对应的动态库即可
- 本质:通过各个可执行程序在加载时,生成的各不相同的GOT表,来找到它需要调用到的外部变量和函数的地址
小结
- 静态链接 -> 代码在开发阶段的复用;动态链接 -> 代码在运行阶段的复用
- C语言的标准库在1MB以上
- /usr/bin下有上千个可执行文件,如果每一个都把标准库静态链接进来,会占用几GB甚至几十GB的的磁盘空间
- 服务端要开上千个进程,如果采用静态链接,会占用几GB的内存空间
- 动态链接的主要价值:节省资源(磁盘、内存)!!
参考资料
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.