linux+动态绑定,linux中动态链接延迟绑定的实现

本文为阅读程序员的自我修养第7.4节延迟绑定后,自己写程序并用gdb调试后的发现。

实验代码如下:

a.c:

#include extern void b();

int main(){

printf("in main\n");

b();

}

b.c:

#include void b(){

printf("b\n");

}

编译命令:

gcc -fPIC -g -DDBUG -o lib.so b.c

gcc -g -o a_test a.c ./lib.so

使用objdump反汇编lib.so:

objdump -D lib.so >lib.dump

objdump -D a_test >a.dump

其中a.dump中调用函数b的过程如下:

400696:    b8 00 00 00 00           mov    $0x0,%eax

40069b:    e8 d0 fe ff ff           callq  400570

0000000000400570 :

400570:    ff 25 92 0a 20 00        jmpq   *0x200a92(%rip)        # 601008 400576:    68 01 00 00 00           pushq  $0x1

40057b:    e9 d0 ff ff ff           jmpq   400550

程序先调用b@plt。后者先jmpq到一个地址处,这个地址为601008处的值.此地址附近的数据如下(这些数据被objdump反汇编了,其实这些根本不是机器指令,而仅仅是数据):

600fff:    00 66 05                 add    %ah,0x5(%rsi)

601002:    40 00 00                 add    %al,(%rax)

601005:    00 00                    add    %al,(%rax)

601007:    00 76 05                 add    %dh,0x5(%rsi)

60100a:    40 00 00                 add    %al,(%rax)

60100d:    00 00                    add    %al,(%rax)

60100f:    00 86 05 40 00 00        add    %al,0x4005(%rsi)

601015:    00 00                    add    %al,(%rax)

601017:    00 96 05 40 00 00        add    %dl,0x4005(%rsi)

60101d:    00 00                    add    %al,(%rax)

可以发现601008处的地址是0x400576(小端),所以jmp指令将跳到这个地址处(jmp指令后面有个*,和指针含义类似)。这个地址就是jmpq指令的下一个指令地址。

这样就实现了延迟绑定的目的:第一次调用函数时,由连接器负责重定位,第二次调用函数时直接调用,不必经过连接器。

这个过程可以通过gdb调试进行验证。在函数被调用前,通过x命令查看got处的内存值,在函数被调用后再次查看got中的内存值便可发现函数地址已被重置。

连接器负责重定位是如何进行的呢?代码如下:

0000000000400550 :

400550:    ff 35 9a 0a 20 00        pushq  0x200a9a(%rip)        # 600ff0

400556:    ff 25 9c 0a 20 00        jmpq   *0x200a9c(%rip)        # 600ff8

40055c:    0f 1f 40 00              nopl   0x0(%rax) 在调用函数b@plt的过程中,由于之前没有进行过重定位,所以需要调用连接器进行重定位工作,通过下面代码实现。

40057b:    e9 d0 ff ff ff           jmpq   400550

地址400550处的代码就是puts@plt-0x10函数,它跳转地址为600ff8处的值(可以发现,这个跳转地址和b@plt的跳转地址都在.got.plt段中)而这个值是在程序加载时由动态链接器填写的。(也就是在加载过程中,动态链接器会将负责符号解析和重定位工作的函数_dl_runtime_resolve()的地址写到这个地方)。这样在需要重定位时,puts@plt(got.plt的第三个函数)将会被调用,需要重定位的函数将会被重定位。之后,dl_runtime_resolve应该会恢复堆栈平衡,并跳转到b@plt处执行,随后通过ret指令返回调用处。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值