assembly 为什么需要PLT存根开头的“jmp”?

ua4mk5z4  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(147)

在SystemV ABI中指定PLT用法(并在实践中实现)的方式示意性地类似于以下内容:

# A call from somewhere in code is into a PLT slot
# (In reality not a direct call, in x64 typically an rip-relative one)
0x500:   
          call 0x1000   
...

0x1000:
   .PLT1: jmp [0x2000]  # the slot for f in the binary's GOT
          pushq $index_f
          jmp .PLT0
...
0x2000: 
# initially jumps back to .PLT to call the lazy-binding routine:
   .GOT1: 0x1005
# but after that is called:
          0x3000   # the address of the real implementation of f
...
0x3000:
     f:  ....

我的问题是:
PLT插槽中的第1个jmp是否冗余?这不能用间接调用GOT来代替吗?举例来说:

0x500:   
          call [0x2000]
...

0x1000:
   .PLT1: pushq $index_f
          jmp .PLT0
...
0x2000: 
# initially jumps back to .PLT to call the lazy-binding routine:
   .GOT1: 0x1005
# but after that is called:
          0x3000   # the address of the real implementation of f
...
0x3000:
     f:  ....

这可能有边际性能优势-但我问的原因是最近在链接器/elf社区的争夺拿出额外的字节在一个16字节的PLT插槽,以适应英特尔IBT(搜索失败,并导致额外的.plt.sec间接。12

hmmo2u0o

hmmo2u0o1#

基本问题是,原始调用(在0x500处)是由编译器生成的,此时,编译器不知道这个符号最终是否会出现在这个动态对象中。因此它生成一个简单的调用(直接的,PC相对的),因为这对于动态对象中的本地调用的常见情况是最有效的。
直到链接器运行,我们才知道这是另一个动态对象中的符号,还是这个对象中全局可见的符号(可能被覆盖),或者是一个局部函数调用。对于后一种情况,它将只是使其成为直接调用,但对于前一种情况,它将为符号创建一个PLT条目,并使调用转到PLT条目。
您的建议将保存一个跳转,但需要在编译时知道每个调用是否需要PLT条目,或者需要在链接时根据是否需要PLT在直接和间接调用之间切换。在x86上,直接调用和间接调用的大小不同,因此能够更改将非常棘手。

相关问题