请教共享库内部的plt调用问题
我写了一个简单的共享库,包含两个函数sum()和_sum(),前者调用后者。
编译后的共享库会有一个自己的plt表,在sum()中调用_sum()的地方会翻译成调用plt项。
我的问题是:这条调用plt项的指令在被映射到不同的进程中时,为什么call的地址不同,这部分代码不是共享的吗?
用objdump共享库的显示如下(注意地址4af处call指令,被映射到不同进程后call的地址会不同,为什么?):
Contents of section .got.plt:
1650 74150000 00000000 00000000 4e030000
1660 5e030000 6e030000 7e030000 8e030000
00000378 <_sum@plt>:
378: jmp *0x18(%ebx)
37e: push $0x18
383: jmp 338 <_init+0x18>
sum:
a = 1;
48a: mov 0xfffffff8(%ebx),%eax
490: movl $0x1,(%eax)
...
4af: call 378 <_sum@plt>
我分别编译了两个程序,一个只链接这个库,另一个先链接别的库再链接这个库。目的是为了让这个库在两个进程中映射的地址不同。
然后我用gdb跟踪这两个程序,程序1中该库被映射到地址0x00111000起始位置,程序2中该库被映射到0x00122000起始位置。
然后我把断点设到sum上,运行到断点,然后在gdb中反汇编sum,看到的结果让我非常惊讶:
程序1中,4af处的指令是 call 0x00111378 <_sum@plt>
程序2中,4af处的指令是 call 0x00122378 <_sum@plt>
我很惊讶,这条指令在两个程序中竟然不一样。我们知道共享库的代码在各进程之间是共享的,而sum就是共享库的代码部分,那内容就应该是相同的。难道地址4af前后不是共享的,在各进程中新分配了各自的物理页?
这一点我没有搞明白,向大家请教。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
当然不能一样.
假设每个地址一样,你能不能为1000000000000000个不同的函数库分配各自不同的地址?显然根据抽屉原理,是不可能的,存在两个函数f1,f2地址相同.
写个程序调用f1,f2,导致矛盾.
另外,了解了解什么叫PIC(Position-independent code)
不管是否PIC,问题是call这条指令时被共享的,也就是说内存中只有一份,可是call的操作数(目的地址)在不同的进程中竟然不同,这是怎么实现的?这条指令如果是编定的,那么它的操作数就应该是固定的一个。那现在显示的是不同的,我想知道call的操作数是怎么实现位置无关的。能指示点儿参考资料吗?
代码被编译为PIC的,但PIC和这个call的地址是什么没有必然直接关系。这个叫符号重定位(relocation)。man elf
LZ应该指明自己测试所用的系统平台.
对于有虚拟内存管理的linux系统来讲:
这是正常的.
每个进程都拥有自己的程序地址空间,可以是不同的.
物理空间上共享库可以只有一份,但可以映射到不同的进程地址空间中去, 也就说相同这不同的<_sum@plt>可以通过映射("映射"这个词可能也不恰当)指向同一个物理上的代码段.
比如代码:
code1:
复制代码
和
code2:
复制代码
#gdb a.out
(gdb)disass f
//此处结果大同小异,所以省略...
//.....
两段代码中的call xxxx<printf@plt> 就可能会不同. 但调用的是同一个printf代码段.
[ 本帖最后由 system888net 于 2009-1-2 16:11 编辑 ]
对于LZ所关心的问题是_sum的地址问题(我理解是这样的,若理解有误的话LZ就可以更正),那么:
xxxx也不会是真正的printf(...), printf的地址(也即printf在动态库中的地址)实际上在stub中第一次运行的时候填入.
同理lZ的程序里call 0x00111378 <_sum@plt>里0x00111378也不是真正的_sum的地址. 真正的_sum地址LZ 可以跟踪进入plt里看到.
[ 本帖最后由 system888net 于 2009-1-2 16:45 编辑 ]
比如静态库,静态库是目标文件(后缀名为o)的集合,目标文件是可重定位的.
我们可以用file命令,对比如一个33.o
# file 33.o
33.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
这里的relocatable就说明了它是可重定位的,所谓可重定位就可以地址可以在使用的时候被重新换过.一般是链接器干这件事,根据链接脚本或者自动来选择不同的库内符号的地址。重新调整之后拼在一起成为一个可执行文件出来供使用。
静态库是这样的.
LZ没有说的太很清楚,我理解LZ是在研究动态链接库中函数的地址.
[ 本帖最后由 system888net 于 2009-1-2 16:56 编辑 ]
看例子:
{
printf("hello\n");
}
请问 mik 是用什么指令反汇编上面的代码的,我今天试了一下objdump、gdb 都弄不出诸如下面的:
======================
08049544 <_GLOBAL_OFFSET_TABLE_>:
8049544: 78 94 js 80494da <_DYNAMIC+0x62>
8049546: 04 08 add $0x8,%al
======================
的格式,请指点下,给出命令/参数,谢谢:)