目录
7.5.x 这几小节看的有点懵,只能多看几遍,慢慢理解。定几个小目标:
- 搞清楚.dynamic, .dynsym, .rel.dyn, .rel.plt 这几个段都保存了哪些信息?
- 这些段能在什么阶段起到什么作用?
.interp 段
保存了动态链接器的路径
.dynamic 段
保存了各种地址。该段中,数组元素的类型有如下几种(部分):
.dynsym段 (动态符号表)
这个表与静态链接里的符号表十分类似,静态链接中的符号表可以参考这篇文章:
《程序员的自我修养——链接、装载与库》读书笔记—— 3.5 链接的接口:符号
.rel.dyn 和 .rel.plt (动态链接重定位表)
重定位表的作用:
共享对象的重定位,与静态链接的目标文件重定位十分相似。不同的是静态链接的重定位发生在静态链接时,共享对象的重定位发生在装载时。其中,
.rel.dyn 对数据引用的修正,修正的位置位于.got以及数据段
.rel.plt 是对函数引用的修正,修正的位置位于.got.plt
接下来,编写两个.so,其中一个引用另外一个中的全局变量。分别分析三种重定位入口类型:_JUMP_SLOT, _GLOB_DATA, _RELATIVE
源码参考文末附录。
.rel.dyn 和 R_X86_64_GLOB_DAT
从下边的重定位表.rela.dyn 中可以看出,g_var_a 的Offset为 200fd8,
从反汇编中可以看出,对g_var_a的访问确实是去*0x200fd8 (注意此处有*号)。
当然,实际的寻址是通过%rip(pc指针)+一个偏移来实现的。汇编代码中的这个0x20096e 应该就是根据重定位表中的0x200fd8来计算出来的。这就是重定位表所发挥的作用。
下图可以看出0x200fd8所在段正是.got段。印证了书中“.rel.dyn 修正的位置位于.got以及数据段”这个说法。
gdb 停在断点上,然后反汇编,可以看到%rax = 0x7ffff7dd1fd8,也就是g_var_a 的地址保存在 0x7ffff7dd1fd8 这块内存上。
查看0x7ffff7dd1fd8 的内容,以及maps
通过map 可以看到,0x00007ffff77df024 这个地址确实位于liba 的权限为rw 的segment上。
.rel.plt 和 R_X86_64_JUMP_SLO
libb.so 的重定位表中,g_func_a 位于 .rel.plt 段,其类型为书中所述的R_X86_64_JUMP_SLO。offset 为0x201018,如下图所示
代码中对g_func_a 的访问,会直接跳转到g_func_a@plt,g_func_a@plt中会以offset 0x201018地址中保存的地址作为g_func_a 的地址。代码中的0x200ab2 应该就是根据重定位表中的0x201018 计算得来。
同样可以看到,0x201018 位于.got.plt段中,也印证了书中“.rel.plt 修正的位置位于.got.plt”的说法
后面如何访问真正的g_func_a,在《程序员的自我修养——链接、装载与库》读书笔记—— 7.4 延迟绑定 中已经推过一遍了,这里就不重复了。
R_X86_64_RELATIVE
书中提到了 _RELATIVE 类型的重定位入口,我没有复现出来,暂时搁置吧。
附录
/*a.c*/
int g_var_a;
void g_func_a(void)
{
g_var_a = 0x123;
}
/*b.c*/
extern int g_var_a;
static int *g_ptr_var_a = &g_var_a;
void g_func_a(void);
void g_func_b()
{
g_func_a();
g_var_a = 0x456;
g_ptr_var_a = 0;
}
/*ab.c*/
extern void g_func_b();
int main(int argc, char *argv[])
{
g_func_b();
return 0;
}
# Makefile
ab: a.c b.c ab.c
gcc a.c -fPIC -shared -g -o liba.so
gcc b.c -fPIC -shared -g -o libb.so -L. -la
gcc ab.c -no-pie -g -o ab -L. -la -lb
objdump -ds libb.so > libb.so.S
参考
https://www.cnblogs.com/cocoajin/p/10904646.html