2011-03-29 244 views
49
0x00000000004004b6 <main+30>: callq 0x400398 <[email protected]> 

任何人都知道吗?@plt是什么意思?

UPDATE

为什么是两个disas printf给我不同的结果?

(gdb) disas printf 
Dump of assembler code for function [email protected]: 
0x0000000000400398 <[email protected]+0>: jmpq *0x2004c2(%rip)  # 0x600860 <_GLOBAL_OFFSET_TABLE_+24> 
0x000000000040039e <[email protected]+6>: pushq $0x0 
0x00000000004003a3 <[email protected]+11>: jmpq 0x400388 

(gdb) disas printf 
Dump of assembler code for function printf: 
0x00000037aa44d360 <printf+0>: sub $0xd8,%rsp 
0x00000037aa44d367 <printf+7>: mov %rdx,0x30(%rsp) 
0x00000037aa44d36c <printf+12>: movzbl %al,%edx 
0x00000037aa44d36f <printf+15>: mov %rsi,0x28(%rsp) 
0x00000037aa44d374 <printf+20>: lea 0x0(,%rdx,4),%rax 
0x00000037aa44d37c <printf+28>: lea 0x3f(%rip),%rdx  # 0x37aa44d3c2 <printf+98> 
+0

第一条输出线从哪里来? 'objdump'我想象? – 2015-05-31 20:37:37

回答

83

这是一种方式来获得代码调整,而无需维护代码的单独副本的每个处理(调整基于在码坐在虚拟存储器地址)。 PLT是过程链接表,这是使动态加载和链接更易于使用的结构之一。

[email protected]实际上是一个小的存根(最终)称为真正的printf函数。

这个实际功能可以映射到任意在给定进程(虚拟地址空间)中的位置,以及调用它的代码。

因此,为了允许正确的代码共享调用代码(左下方),您不希望直接对其应用任何修正,因为这将限制它可以位于其他进程中的位置。

PLT处于可靠地计算-在运行时地址不是进程之间共享,所以任何给定的过程,但是可以更改其它要更小的过程特异性区域。

换句话说,检查以下图,它示出了两个代码,并在两个过程映射到不同的虚拟地址的库代码:

  Mapped to: 0x1234  0x9000  0x8888 
     +-----------------+ +----------+ +----------+ 
     |     | | Private | |   | 
ProcA |     | | PLT/GOT | |   | 
     |     | | area | |   | 
     | Shared   | +----------+ | Shared | 
========| application |==============| library |== 
     | code   | +----------+ | code | 
     |     | | Private | |   | 
ProcB |     | | PLT/GOT | |   | 
     |     | | area | |   | 
     +-----------------+ +----------+ +----------+ 
     Mapped to: 0x2020  0x9000  0x6666 

这个特殊的例子示出了一个简单的情况下PLT映射到固定的位置。

<[email protected]+0>: jmpq *0x2004c2(%rip) ; 0x600860 <_GOT_+24> 

一篇好文章可以发现here,详细glibc是如何在运行时加载:在你的情况下,就证明你的程序计数器相关的查找它的位置相对于当前程序计数器。

基本上,创建共享代码的原始方式意味着它必须加载到使用它的每个进程的虚拟地址空间中的相同内存位置。无论是它还是它都不能共享,因为修复一个进程的共享副本的行为将完全填满另一个进程,并将其映射到不同的位置。

通过使用与位置无关的代码,与PLT和全局偏移表(GOT),所述第一的函数的调用[email protected](在PLT)沿是一个多阶段的操作,其中:

  • 您在PLT中致电[email protected]
  • 它调用GOT版本(通过指针),其中最初指向PLT中的某些设置代码。
  • 这个设置代码加载相关的共享库,如果还没有完成,则修改 GOT,以便后续调用直接到实际printf而不是设置代码。

在随后的调用,因为GOT已被修改,多阶段的方法被简化:

  • 调用的PLT [email protected]
  • 它调用指向实数的GOT版本(通过指针)printf
+0

为什么两个'disas printf'给我不同的结果? – gdb 2011-03-29 07:49:17

+1

存根是在我输入'r'之前,另一个是在'break'之后 – gdb 2011-03-29 08:02:22

+0

它可以用gdb与任何可执行文件重现。 – gdb 2011-03-29 08:32:13

4

不确定,但可能你看到的是有道理的。第一次运行disas命令时,printf还没有被调用,所以没有解决。一旦程序在第一次更新GOT时调用printf方法,现在printf被解析并且GOT指向实际函数。因此,下一次调用disas命令会显示真正的printf程序集。