我对共享库的处理和解释有疑问。
假设我使用以下命令从foo.c构建共享对象:
gcc -shared -fPIC -o libfoo.so foo.c
其中foo.c包含:
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int i;
printf("this is a silly test\n");
if(i)
goto ret;
printf("hello world\n");
ret:
return 0;
}
现在,让我们看一下objdump输出,特别是foo的主要输出:
0000000005ec <main>:
5ec: 55 push %rbp
5ed: 48 89 e5 mov %rsp,%rbp
5f0: 48 83 ec 10 sub $0x10,%rsp
5f4: 48 8d 3d 6b 00 00 00 lea 0x6b(%rip),%rdi # 666 <_fini+0xe>
5fb: e8 00 ff ff ff callq 500 <puts@plt>
600: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
604: 75 0e jne 614 <main+0x28>
606: 48 8d 3d 65 00 00 00 lea 0x65(%rip),%rdi # 672 <_fini+0x1a>
60d: e8 ee fe ff ff callq 500 <puts@plt>
612: eb 01 jmp 615 <main+0x29>
614: 90 nop
615: b8 00 00 00 00 mov $0x0,%eax
61a: c9 leaveq
61b: c3 retq
61c: 90 nop
61d: 90 nop
61e: 90 nop
61f: 90 nop
我可以清楚地看到,对put的调用正如预期的那样被重定向到PLT。但是,我不明白的是604和612的说明。它们与IP无关,也与PLT无关。他们使用绝对地址,基于 符号主要。
这个共享库怎么可能在几个进程之间同时使用呢?它可以(并且应该)加载到不同的虚拟地址,但重点是每个进程应该共享存储在RAM中的实现。具有在不同虚拟地址处加载的主要的不同进程如何在604和612处共享指令?
答案 0 :(得分:0)
他们不是绝对的跳跃,他们是PC的相对跳跃。事实上,所有直接跳转和调用指令在x86上都是PC相关的 - 没有绝对的直接跳转(所以如果你想要绝对跳转,它必须是间接的)。
callq指令使用PLT的原因是因为目标符号可能位于不同的共享对象中,所以即使是相对分支也不会工作(可能会加载另一个共享对象)在任何地址,独立于此共享对象)。 PLT本身实际上是共享对象中的一小段代码,每个远程符号都有一个(间接)绝对跳转。当共享对象动态加载时,绝对地址被适当地设置,因此当代码运行时,callq指令将是PLT的pc相对分支(调用),其包含到puts例程的单个间接跳转。 / p>