linux中动态链接延迟绑定的实现
2016-05-02 15:55
302 查看
本文为阅读程序员的自我修养第7.4节延迟绑定后,自己写程序并用gdb调试后的发现。
实验代码如下:
a.c:
b.c:
编译命令:
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 <b@plt>
0000000000400570 <b@plt>:
400570: ff 25 92 0a 20 00 jmpq *0x200a92(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x20>
400576: 68 01 00 00 00 pushq $0x1
40057b: e9 d0 ff ff ff jmpq 400550 <_init+0x18>
程序先调用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 <puts@plt-0x10>:
400550: ff 35 9a 0a 20 00 pushq 0x200a9a(%rip) # 600ff0 <_GLOBAL_OFFSET_TABLE_+0x8>
400556: ff 25 9c 0a 20 00 jmpq *0x200a9c(%rip) # 600ff8 <_GLOBAL_OFFSET_TABLE_+0x10>
40055c: 0f 1f 40 00 nopl 0x0(%rax)
在调用函数b@plt的过程中,由于之前没有进行过重定位,所以需要调用连接器进行重定位工作,通过下面代码实现。
40057b: e9 d0 ff ff ff jmpq 400550 <_init+0x18>
地址400550处的代码就是puts@plt-0x10函数,它跳转地址为600ff8处的值(可以发现,这个跳转地址和b@plt的跳转地址都在.got.plt段中)而这个值是在程序加载时由动态链接器填写的。(也就是在加载过程中,动态链接器会将负责符号解析和重定位工作的函数_dl_runtime_resolve()的地址写到这个地方)。这样在需要重定位时,puts@plt(got.plt的第三个函数)将会被调用,需要重定位的函数将会被重定位。之后,dl_runtime_resolve应该会恢复堆栈平衡,并跳转到b@plt处执行,随后通过ret指令返回调用处。
实验代码如下:
a.c:
#include <stdio.h> extern void b(); int main(){ printf("in main\n"); b(); }
b.c:
#include <stdio.h> 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 <b@plt>
0000000000400570 <b@plt>:
400570: ff 25 92 0a 20 00 jmpq *0x200a92(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x20>
400576: 68 01 00 00 00 pushq $0x1
40057b: e9 d0 ff ff ff jmpq 400550 <_init+0x18>
程序先调用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 <puts@plt-0x10>:
400550: ff 35 9a 0a 20 00 pushq 0x200a9a(%rip) # 600ff0 <_GLOBAL_OFFSET_TABLE_+0x8>
400556: ff 25 9c 0a 20 00 jmpq *0x200a9c(%rip) # 600ff8 <_GLOBAL_OFFSET_TABLE_+0x10>
40055c: 0f 1f 40 00 nopl 0x0(%rax)
在调用函数b@plt的过程中,由于之前没有进行过重定位,所以需要调用连接器进行重定位工作,通过下面代码实现。
40057b: e9 d0 ff ff ff jmpq 400550 <_init+0x18>
地址400550处的代码就是puts@plt-0x10函数,它跳转地址为600ff8处的值(可以发现,这个跳转地址和b@plt的跳转地址都在.got.plt段中)而这个值是在程序加载时由动态链接器填写的。(也就是在加载过程中,动态链接器会将负责符号解析和重定位工作的函数_dl_runtime_resolve()的地址写到这个地方)。这样在需要重定位时,puts@plt(got.plt的第三个函数)将会被调用,需要重定位的函数将会被重定位。之后,dl_runtime_resolve应该会恢复堆栈平衡,并跳转到b@plt处执行,随后通过ret指令返回调用处。
相关文章推荐
- linux 权限
- linux基础(4)--文件管理--RHEL6.5
- ssh 登录出现的几种错误以及解决办法
- postgresql9.5 run 文件linux安装后配置成开机服务
- 开源新闻速递(160502):Arch Linux 2016.05.01 发布
- vmware下CentOS的host-only连接
- Linux学习笔记:常用100条命令(三)
- Linux学习笔记:常用100条命令(二)
- CentOS 7 安装virtualBox
- Linux学习笔记:常用100条命令(一)
- centos安装第三方软件库,flash和视频播放器
- CentOS6.5个人目录下中文路径转英文路径
- Linux系统英文切换中文
- list.h linux内核链表分析
- linux基础(3)--可用实验环境配置--RHEL6.5
- Linux PS命令详解
- Linux驱动开发之 三 (那些必须要了解的硬件知识 之 存储器篇)
- Linux netstat命令详解
- Linux开机自启配置
- Linux编辑器vim键盘详解