静态链接
静态链接三个步骤:
- 布局(将相同节合并,如 text 节,data 节,原本以0地址为参考的地址现在要变到相对全局的位置)
- 符号解析(查其它文件以填写未定义的符号的地址)
- 重定位(将代码中函数调用/变量地址确定下来,由相对地址变绝对地址)
对于静态库中的模块,只链接抽取用到的符号
动态链接
gcc -Og -fPIC a.c -o a.so // PIC 位置无关代码
动态链接步骤:
- 生成可执行文件
- 在 .bss 节分配动态库中的全局变量(包括.data和.bss中的变量),并将 .got 中表项指向那个变量,运行时才将其初始值从动态库复制过来
- 在 .plt 中分配动态库中函数的函数入口表项,表项值在运行时才填写
- 装载程序
- 根据 .interp 节存的动态库路径名找到动态库数据和代码,映射到进程内存空间
- .data .bss 节中变量初始值从动态库复制过来(到 .bss 节对应变量上),装载变量的 got 表项
- 当第一次调用一个动态函数时,跳转到plt表,表项指向got表中对应项的地址,这时这个地址指向的还是之前plt表表项的下一行,这一行做的是把函数对应的标号压栈,然后下一行跳至plt表的统一入口,执行跳转至got表的统一入口,这个入口处做的是根据标号把got表项地址改为重定向的地址。以后再调用函数时,进入plt表项,跳入got表项时就是函数的入口地址了。
- 第一次:plt项->got项->plt项的下一行(取标号)->plt统一入口->got统一入口->修改got表项并进入函数入口
- 以后:plt项->got项->函数入口
补充:
-
.got (全局偏移表)节存有一个间接引用变量的表,每个表项是一个变量或函数入口的地址,这些地址初始是0,当加载程序时才指向进程中分配的变量的地址,当调用函数时才指向进程中函数入口地址。
-
.data .bss 节(初始化与未初始化0变量节)存的是变量初始值,进程在 .bss 节为变量分配空间时,相应 got 表项将指向这些变量
-
.text 代码中使用的变量地址为 .so 文件中用这个变量的指令地址到 .got 中相应表项的相对地址,加载时会重计算