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