关于ELF标头中GOT和PLT结构的必要性

时间:2019-05-15 17:16:04

标签: elf got

在某些体系结构(例如x86_64)中,可以使用PC(RIP)相对寻址模式来引用数据(例如mov)和代码(jmp,调用),这确实是技术的原因证明需要这种结构(got,plt)吗?

我的意思是,如果我想将全局数据(例如)移动到寄存器,则可以执行以下指令(标准PIE):

  

mov rax,QWORD PTR [rip + 0x2009db]

     

mov eax,DWORD PTR [rax]

(其中0x2009db是rip和包含符号地址的get右侧条目之间的偏移量)

为什么我们不能做这样的事情:

  

mov rax,rip + 0xYYYYYY

     

mov eax,DWORD PTR [rax]

(0xYYYYYYY是RIP值和符号(例如全局变量)之间的直接增量

我不习惯做ASM,所以我的例子也许是错误的。但是我的想法是:为什么不仅仅基于RIP简单地计算符号的绝对地址,将其放入EAX,然后访问其内容。如果指令集允许对相对寻址做我们想做的任何事情,为什么要使用这样的结构(got,plt)?

同一问题将适用于呼叫/跳转指令。

是因为指令集不允许这样做吗? 是因为偏移值不能覆盖整个地址空间吗?但是..重要吗?由于节的结构保持不变,因此将其映射到进程的虚拟寻址空间中(例如,.dat节,后跟.got或类似内容)。我的意思是,为什么偏移量在直接引用符号地址而不是get中的入口地址时会更大? 其他原因?

谢谢!

1 个答案:

答案 0 :(得分:0)

基本上,这些结构的原因正是具有额外的间接级别。

这样,您可以使用LD_PRELOAD在动态库中插入符号。即使没有它,动态绑定规则也使得可执行文件中定义的符号会覆盖共享库中定义的符号,即使是对该共享库中的调用也是如此(请参见this)。

还要考虑这些要点。

  1. 事先不知道装载被调用函数的实现的共享库的地址(这是设计使然,特别是故意这样做的:这是ld.so的一个功能,称为 ASLR ),因此动态加载程序需要将重定位应用于至少所有在运行时执行的调用站点。
  2. 如果不支持PLT,这将丧失通过不同的进程映像映射到内存的库的共享代码段的优势,因为在不同的进程中,相同的库可能具有不同的地址,从而导致不同的修补的代码。 PLT是不共享的相对较小的数据。参见this post
  3. PLT允许在第一次调用时延迟绑定函数。 PLT插槽最初保存解析器的地址。解析完成后,结果将缓存在PLT stot中。

关于GOT / PLT的重定位机制已在here中介绍。总而言之,互联网上有足够的信息说明PLT和GOT的工作方式(以及原因)。

此外,请查看GCC的-fno-plt选项。这是一种优化,但是请注意,仍然需要GOT,并且没有PLT条目的函数不支持惰性绑定。