共享对象代码Unix中的绝对跳转

时间:2015-04-06 21:20:50

标签: c unix linker dynamic-linking

我对共享库的处理和解释有疑问。

假设我使用以下命令从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处共享指令?

1 个答案:

答案 0 :(得分:0)

他们不是绝对的跳跃,他们是PC的相对跳跃。事实上,所有直接跳转和调用指令在x86上都是PC相关的 - 没有绝对的直接跳转(所以如果你想要绝对跳转,它必须是间接的)。

callq指令使用PLT的原因是因为目标符号可能位于不同的共享对象中,所以即使是相对分支也不会工作(可能会加载另一个共享对象)在任何地址,独立于此共享对象)。 PLT本身实际上是共享对象中的一小段代码,每个远程符号都有一个(间接)绝对跳转。当共享对象动态加载时,绝对地址被适当地设置,因此当代码运行时,callq指令将是PLT的pc相对分支(调用),其包含到puts例程的单个间接跳转。 / p>