@plt在这里是什么意思?


76
0x00000000004004b6 <main+30>:   callq  0x400398 <printf@plt>

有谁知道?

更新

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

(gdb) disas printf
Dump of assembler code for function printf@plt:
0x0000000000400398 <printf@plt+0>:  jmpq   *0x2004c2(%rip)        # 0x600860 <_GLOBAL_OFFSET_TABLE_+24>
0x000000000040039e <printf@plt+6>:  pushq  $0x0
0x00000000004003a3 <printf@plt+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>

第一条输出线从何而来?objdump我想?
Ciro Santilli郝海东冠状病六四事件法轮功

Answers:


121

这是获取代码修复程序的一种方法(根据代码在虚拟内存中的位置来调整地址,在不同的进程中可能不同),而不必为每个进程维护单独的代码副本。PLT是过程链接表,它是使动态加载和链接更易于使用的结构之一。

printf@plt实际上是一个小存根,它(最终)调用了实printf函数,在途中对事物进行了修改以使后续调用更快。

真正 printf功能可以被映射到任何为可被试图调用它的代码在给定的处理(虚拟地址空间)的位置。

因此,为了允许正确共享调用代码(下面左侧)和被调用代码(下面右侧),您不希望直接对调用代码应用任何修正,因为这将限制其在代码中的放置位置。其他过程。

因此,在运行时可靠地计算出的地址处PLT是一个较小的特定过程的区域,该区域之间没有共享,因此任何给定的过程都可以随意更改,但可以随意更改,而不会产生不利影响。


检查下图,该图显示了您的代码和库代码在两个不同的进程中映射到不同的虚拟地址,ProcA并且ProcB

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

此特定示例显示了一个简单的情况,其中PLT映射到固定位置。在您的方案中,它相对于当前程序计数器位于,如您的程序计数器相对查找所示:

<printf@plt+0>: jmpq  *0x2004c2(%rip)  ; 0x600860 <_GOT_+24>

我只是使用固定地址来简化示例。

共享代码的原始方式意味着必须将它们加载到使用它的每个进程的每个虚拟地址空间中的相同内存位置。要么不能共享,要么为一个进程修复单个共享副本的行为将完全填满其他进程,而其他进程将其映射到其他位置。

通过使用与位置无关的代码以及PLT和全局偏移表(GOT),对函数的第一次调用printf@plt(在PLT中)是一个多阶段操作,其中发生以下操作:

  • 您呼叫printf@pltPLT。
  • 它调用GOT版本(通过指针),该版本最初指向PLT中的某些设置代码。
  • 如果尚未完成,此设置代码将加载相关的共享库,然后修改GOT指针,以便随后的调用直接调用实数printf而不是PLT设置代码。
  • 然后,它将printf在此过程的正确地址处调用已加载的代码。

在后续调用中,由于已修改了GOT指针,因此简化了多阶段方法:

  • 您呼叫printf@pltPLT。
  • 它调用GOT版本(通过指针),该版本现在指向real printf

这里可以找到很好的文章,详细介绍了如何glibc在运行时加载。


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

2
存根在我键入之前r,另一个在之后break
gdb

可以使用gdb的任何可执行文件进行复制。
gdb

3
这是过程链接表。我将提供更多信息。
paxdiablo 2011年

1
@路易斯,我不确定是否有。我认为设置代码实际上是在其过程中加载共享库的(ASLR表示它将在任意地址加载),因此直到那一点之后,该地址才真正被知道。
paxdiablo

6

不确定,但是您所看到的可能有意义。第一次运行disas命令时,尚未调用printf,因此无法解决。一旦您的程序第一次调用printf方法,就更新了GOT,现在解析了printf,并且GOT指向实函数。因此,对disas命令的下一次调用显示了实际的printf程序集。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.