@plt是什么意思?
0x00000000004004b6 <main+30>: callq 0x400398 <printf@plt>
有谁知道?
UPDATE
为什么两个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>
这是一种获取代码修正的方法(根据代码在虚拟内存中的位置来调整地址),而不必为每个进程维护单独的代码副本。 PLT是程序链接表,是使dynamic加载和链接更易于使用的结构之一。
printf@plt
实际上是一个小的存根(最终)调用真正的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映射到一个固定位置的简单情况。 在你的场景中,它是相对于当前的程序计数器而言的,正如你的程序计数器相对查找所certificate的那样:
<printf@plt+0>: jmpq *0x2004c2(%rip) ; 0x600860 <_GOT_+24>
在这里可以find一篇好文章,详细介绍glibc
在运行时是如何加载的。
基本上,创build共享代码的原始方式意味着必须将其加载到每个使用它的进程的虚拟地址空间中的相同内存位置。 无论是它还是它不能被共享,因为修复一个进程的单个共享副本的行为将完全填补另一个进程映射到不同的位置。
通过使用与位置无关的代码以及PLT和全局偏移表(GOT),对函数printf@plt
(在PLT中)的第一次调用是多阶段操作,其中:
- 你可以在PLT中调用
printf@plt
。 - 它会调用GOT版本(通过指针), 最初指向PLT中的一些设置代码。
- 这个设置代码加载相关的共享库,如果尚未完成,则修改 GOT,以便后续直接调用真实
printf
而不是设置代码。
在随后的调用中,由于GOT已被修改,因此简化了多阶段方法:
- 你可以在PLT中调用
printf@plt
。 - 它调用指向真实
printf
的GOT版本(通过指针)。
不确定,但可能你所看到的是有道理的。 第一次运行disas命令时,printf还没有被调用,所以没有解决。 一旦程序在第一次更新GOT时调用printf方法,现在printf被parsing并且GOT指向真实函数。 因此,下一次调用disas命令会显示真正的printf程序集。