共享库的链接 -PIC 函数

在前面一篇关于PIC data中我们知道了PIC data的实现,现在我们将进一步去了解PIC中的function call,

延迟binding optimization

当共享库引用一些函数的时候,函数的real地址在加载到内存中之前并不知道,解析这个地址叫做binding,这是动态加载器在加载共享库到进程内存空间时候做的。绑定的过程非常重要,因为加载器必须在特殊的表中寻找函数符号。
解析每个函数都会花费一些时间,不是很多,但是库中的函数数量通常比全局变量多的多,进一步说,这些解析可能没什么意义,因为,通常被调用的函数
只占据一小部分。
对此,为了加速这个过程,a clever lazy binding 的方案被提出,”lazy“,将工作延迟到它确实需要的最后一刻,避免做一些在程序运行时不需要的其结果工作,比如copy-on-write.
这种延迟binding的方案是通过添加另一个间接层来实现的-PLT

The Procedure Linkage Table(PLT)

GOT是.data section的一部分,PLT is part of executable .text,它由a set of entries 组成,每个entry 对应一个共享库调用的外部函数。PLT中存储的不是地址而是一小块可执行代码,不是直接的函数调用,而是call 对应的PLT entry.entry中的代码负责实际的函数调用,sometime this arrangement called a “trampoline”,每个PLT entry又有一个对应的GOT entry,那里面包含了函数的actual offset,但是只有动态加载器解析函数的时候它才会这么做。
在共享库第一次被loaded的时候,函数调用还没有被解析,
在这里插入图片描述
解释:
1 在代码中,一个函数func被调用,编译器翻译it去调用对应的PLT entry,
2 PLT由一个特殊的第一个entry,即PLT[0],后面跟随着一堆相同结构的entry,每个entry对应着一个需要被解析的函数。
3 除了PLT[0],其他的PLT entry由三部分组成,
1)一个jump,跳转到对应的GOT entry中说明的位置
2) 为”resolver“准备参数
3)call PLT[0]中的resolver
4 第一个PLT entry call 一个resolver 例程,它本身位于动态加载器中,用来解析函数的实际地址
5 在函数的实际地址被解析之前,对应的GOT entry中存储的地址只是jmp后一条指令,
一起看看第一次调用会发生什么:
1,PLT[n]被调用,jmp到GOT[n]指向的地址
2,这个地址指向jmp下一条指令,指向为resolver解析参数
3,resolver接着被调用
4,resolver执行函数的实际的地址解析,并且把它放入GOT[n]中

在函数第一次调用之后,
在这里插入图片描述
现在GOT[n]指向对应的函数,直接进入函数,不在需要resolver解析地址,
resolver就是一块在加载器中底层代码,用来做符号解析的,就不细讲了,对目的来说并不重要。

PIC with function calls through PLT and GOT -an example

最后看个实例:

int myglob = 42;

int ml_util_func(int a)
{
    return a + 1;
}

int ml_func(int a, int b)
{
    int c = b + ml_util_func(a);
    myglob += c;
    return b + myglob;
}

将这段代码编译进libmlpic.so,现在把注意力放在ml_func调用ml_util_func上,使用objdump -d libmlpic.so查看共享库的反汇编代码,
在这里插入图片描述
我们可以看到在偏移量0x71c处调用了ml_util_func函数,call 5f0
为什么是5f0呢,找了半天终于找到5f0l
在这里插入图片描述
0x5f0正好就是ml_util_func对应的PLT entry的位置。
接着看
jmpq *200a2a(%rip) 跳去哪里了,
rip=0x5f6,0x5f6+0x200a2a=0x201020,我们看一下.got.plt里面的内容
在这里插入图片描述
巧了,刚刚好就有一个0x00201020,并且里面的内容就是0x05f6

最后跳转到plt[0]处,5d0
在这里插入图片描述
跟我们第一次调用图一模一样。

readelf -r libmlpic.so

在这里插入图片描述
第二行告诉动态加载器应该将符号ml_util_func的地址放入0x201020的位置。

看到第一次调用后,这个GOT entry被修改应该很有趣,我们再次使用GDB观察。

gdb driver
b ml_func
set disassembly-flavor intel
disas ml_func

在这里插入图片描述
再通过反汇编0x7ffff7bd95f0得到对应的GOT的地址
在这里插入图片描述
我们可以看到对应的GOT地址是0x7ffff7dda0a0
我们看看它里面保存了什么

在这里插入图片描述
0x7ffff7bd95f6 就是plt的第二条指令的地址

step结束函数调用,
再次查看GOT中的值
在这里插入图片描述
0x7ffffbd96f5
我们再打印一下ml_util_func函数地址
在这里插入图片描述
结果一样!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值