Assembly @plt在这里是什么意思?

Assembly @plt在这里是什么意思?,assembly,disassembly,Assembly,Disassembly,这是一种获得代码修复的方法(根据代码在虚拟内存中的位置调整地址,不同进程之间可能不同),而不必为每个进程维护单独的代码副本。PLT是程序链接表,是使动态加载和链接更易于使用的结构之一 printf@plt实际上是一个小存根,它(最终)调用realprintf函数,在过程中进行修改以加快后续调用 realprintf函数可以映射到给定进程(虚拟地址空间)中的任何位置,以及试图调用它的代码 因此,为了允许调用代码(下面左侧)和被调用代码(下面右侧)的正确代码共享,您不希望直接对调用代码应用任何修正,

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

printf@plt
实际上是一个小存根,它(最终)调用real
printf
函数,在过程中进行修改以加快后续调用

real
printf
函数可以映射到给定进程(虚拟地址空间)中的任何位置,以及试图调用它的代码

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

因此,
PLT
是一个较小的进程特定区域,位于运行时可靠计算的地址,进程之间不共享,因此任何给定进程都可以随意更改,而不会产生不利影响


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

(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>
此特定示例显示了PLT映射到固定位置的简单情况。在您的场景中,它位于相对于当前程序计数器的位置,如程序计数器相对查找所示:

Address: 0x1234          0x9000      0x8888
        +-------------+ +---------+ +---------+
        |             | | Private | |         |
ProcA   |             | | PLT/GOT | |         |
        | Shared      | +---------+ | Shared  |
========| application |=============| library |==
        | code        | +---------+ | code    |
        |             | | Private | |         |
ProcB   |             | | PLT/GOT | |         |
        +-------------+ +---------+ +---------+
Address: 0x2020          0x9000      0x6666
:jmpq*0x2004c2(%rip);0x600860
我刚刚使用了固定地址来简化示例

最初的代码共享方式意味着它们必须加载到使用它的每个进程的每个虚拟地址空间中的相同内存位置。要么是这样,要么就是无法共享,因为为一个进程修复单个共享副本的行为将完全阻塞映射到不同位置的其他进程

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

  • 你呼叫
    printf@plt
    在PLT中
  • 它调用get版本(通过指针),该版本最初指向PLT中的一些设置代码
  • 此设置代码加载相关的共享库(如果尚未完成),然后修改get指针,以便后续直接调用real
    printf
    ,而不是PLT设置代码
  • 然后,它在该进程的正确地址调用加载的
    printf
    代码
在后续调用中,由于修改了get指针,因此简化了多阶段方法:

  • 你呼叫
    printf@plt
    在PLT中
  • 它调用get版本(通过指针),该版本现在指向真正的
    printf

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

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

为什么两个
disas printf
会给出不同的结果?存根在我键入
r
之前,另一个在
break
之后,它可以用gdb的任何可执行文件进行复制。这是过程链接表。我会更新更多信息。@Luis,我不确定是否有。我认为安装代码实际上加载了共享库作为其过程的一部分(ASLR意味着它将在某个任意地址加载),因此在该点之后才知道该地址。第一个输出行来自哪里
objdump
我想是吧?
Address: 0x1234          0x9000      0x8888
        +-------------+ +---------+ +---------+
        |             | | Private | |         |
ProcA   |             | | PLT/GOT | |         |
        | Shared      | +---------+ | Shared  |
========| application |=============| library |==
        | code        | +---------+ | code    |
        |             | | Private | |         |
ProcB   |             | | PLT/GOT | |         |
        +-------------+ +---------+ +---------+
Address: 0x2020          0x9000      0x6666
<printf@plt+0>: jmpq  *0x2004c2(%rip)  ; 0x600860 <_GOT_+24>