请教共享库内部的plt调用问题

发布于 2022-09-23 13:56:07 字数 1223 浏览 12 评论 0

我写了一个简单的共享库,包含两个函数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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

爱给你人给你 2022-09-30 13:56:07

当然不能一样.
假设每个地址一样,你能不能为1000000000000000个不同的函数库分配各自不同的地址?显然根据抽屉原理,是不可能的,存在两个函数f1,f2地址相同.
写个程序调用f1,f2,导致矛盾.
另外,了解了解什么叫PIC(Position-independent code)

纵山崖 2022-09-30 13:56:07

不管是否PIC,问题是call这条指令时被共享的,也就是说内存中只有一份,可是call的操作数(目的地址)在不同的进程中竟然不同,这是怎么实现的?这条指令如果是编定的,那么它的操作数就应该是固定的一个。那现在显示的是不同的,我想知道call的操作数是怎么实现位置无关的。能指示点儿参考资料吗?

烟花易冷人易散 2022-09-30 13:56:07

代码被编译为PIC的,但PIC和这个call的地址是什么没有必然直接关系。这个叫符号重定位(relocation)。man elf

指尖上的星空 2022-09-30 13:56:07

原帖由 detian 于 2009-1-2 09:52 发表
我写了一个简单的共享库,包含两个函数sum()和_sum(),前者调用后者。
编译后的共享库会有一个自己的plt表,在sum()中调用_sum()的地方会翻译成调用plt项。
我的问题是:这条调用plt项的指令在被映射到不同的 ...

LZ应该指明自己测试所用的系统平台.

对于有虚拟内存管理的linux系统来讲:
这是正常的.
每个进程都拥有自己的程序地址空间,可以是不同的.
物理空间上共享库可以只有一份,但可以映射到不同的进程地址空间中去, 也就说相同这不同的<_sum@plt>可以通过映射("映射"这个词可能也不恰当)指向同一个物理上的代码段.

比如代码:
code1:

  1. int f()
  2. {
  3.     printf("\r\nhello,world...");
  4.     return(1);
  5. }
  6. int main()
  7. {
  8.    f();
  9.    return(0);
  10. }

复制代码

code2:

  1. int f()
  2. {
  3.     printf("\r\nhello,world...");
  4.     return(1);
  5. }
  6. int main()
  7. {
  8.    int handle;
  9.    handle=open(...);
  10.    f();
  11.    close(handle);
  12.    return(0);
  13. }

复制代码

#gdb a.out
(gdb)disass f
//此处结果大同小异,所以省略...
//.....

两段代码中的call  xxxx<printf@plt> 就可能会不同. 但调用的是同一个printf代码段.

[ 本帖最后由 system888net 于 2009-1-2 16:11 编辑 ]

ゃ懵逼小萝莉 2022-09-30 13:56:07

对于LZ所关心的问题是_sum的地址问题(我理解是这样的,若理解有误的话LZ就可以更正),那么:
xxxx也不会是真正的printf(...), printf的地址(也即printf在动态库中的地址)实际上在stub中第一次运行的时候填入.
同理lZ的程序里call 0x00111378 <_sum@plt>里0x00111378也不是真正的_sum的地址. 真正的_sum地址LZ 可以跟踪进入plt里看到.

[ 本帖最后由 system888net 于 2009-1-2 16:45 编辑 ]

三五鸿雁 2022-09-30 13:56:07

比如静态库,静态库是目标文件(后缀名为o)的集合,目标文件是可重定位的.
我们可以用file命令,对比如一个33.o
# file 33.o
33.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

这里的relocatable就说明了它是可重定位的,所谓可重定位就可以地址可以在使用的时候被重新换过.一般是链接器干这件事,根据链接脚本或者自动来选择不同的库内符号的地址。重新调整之后拼在一起成为一个可执行文件出来供使用。

桜花祭 2022-09-30 13:56:07

原帖由 cjaizss 于 2009-1-2 16:47 发表
比如静态库,静态库是目标文件(后缀名为o)的集合,目标文件是可重定位的.
我们可以用file命令,对比如一个33.o
# file 33.o
33.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
...

静态库是这样的.

LZ没有说的太很清楚,我理解LZ是在研究动态链接库中函数的地址.

[ 本帖最后由 system888net 于 2009-1-2 16:56 编辑 ]

一杆小烟枪 2022-09-30 13:56:07

看例子:

int main()
{
        printf("hello\n");
}

如果没结果 2022-09-30 13:56:07

请问  mik 是用什么指令反汇编上面的代码的,我今天试了一下objdump、gdb 都弄不出诸如下面的:
======================
08049544 <_GLOBAL_OFFSET_TABLE_>:
8049544:        78 94                        js     80494da <_DYNAMIC+0x62>
8049546:        04 08                        add    $0x8,%al
======================

的格式,请指点下,给出命令/参数,谢谢:)

原帖由 mik 于 2009-1-2 21:51 发表
看例子:
int main()
{
        printf("hello\n");
}

a.out:
08048354 :
8048354:        8d 4c 24 04                  lea    0x4(%esp),%ecx
8048358:        83 e4 f0                     and    $0xfffffff0,%esp
80 ...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文