提问人:Ofek Shilon 提问时间:8/27/2023 最后编辑:Peter CordesOfek Shilon 更新时间:8/28/2023 访问量:81
为什么需要PLT存根开头的“jmp”?
Why is the `jmp` at the start of the PLT stub needed?
问:
在 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 个不是多余的吗?这不能通过间接调用 GOT 来代替吗?例如:jmp
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: ....
这可能会带来边际性能优势 - 但我问的原因是链接器/精灵社区最近在 16 字节的 PLT 插槽中提出额外的字节以容纳英特尔 IBT(搜索失败,并导致额外的间接。1、2.plt.sec
)
答:
2赞
Chris Dodd
8/28/2023
#1
基本问题是编译器正在生成原始调用(在 0x500 处),此时,编译器不知道此符号最终是否会出现在此动态对象中。因此,它会生成一个简单的调用(直接的、相对于 PC)的调用,因为对于动态对象中本地调用的常见情况来说,这是最有效的。
直到链接器运行,我们才知道这是另一个 dynmic 对象中的符号,还是此对象中的全局可见符号(可能被覆盖)或本地函数调用。对于后一种情况,它只会使其成为直接调用,但对于前一种情况,它将为符号创建一个 PLT 条目,并使调用转到 PLT 条目。
您的建议将节省跳转,但需要在编译时知道每次调用是否需要 PLT 条目,或者需要在链接时根据是否需要 PLT 在直接调用和间接调用之间切换。在 x86 上,直接调用和间接调用的大小不同,因此能够更改将非常棘手。
评论
0赞
Ofek Shilon
8/28/2023
默认情况下,即使对于同一库中的函数,也会通过 PLT 生成来自共享库的调用。具有“默认”可见性的符号是可插接的,并且插补只能在运行时发生。
1赞
Peter Cordes
8/28/2023
gcc -fno-plt
将有同样的问题(对于在链接器输入中找到的符号的不必要的间接)。它通过“放松”以使用虚拟地址大小前缀定向呼叫来解决,该前缀对其执行方式没有影响。(但是,指令需要在机器代码中占用相同的空间而不插入 .对于“轻松”的呼叫,有一种特殊的重新定位类型。(示例:无法从汇编 (yasm) 代码在 64 位 Linux 上调用 C 标准库函数 - NASM 使用非宽松 :/)call [rip+rel32]
a32 call rel32
nop
1赞
Peter Cordes
8/28/2023
@OfekShilon:但是大多数时候你不希望这样,甚至在使用 或 with 编译时(即使默认情况下 GCC 也会直接调用其他函数),GCC 也不知道未定义的符号是在另一个对象中找到还是仅在共享对象中找到。GCC 通过让链接器重写调用以在需要时通过 PLT 来处理此问题(传统),或者让链接器放松到 ( without , or visibility=hidden)。或者看看我之前的评论:放松.-fPIE
-fno-pie -no-pie
.o
.so
-fno-pie
call foo@plt
call foo
-fPIE
-fno-plt
call [rip+rel32]
评论
push
jmp
call
call+jmp
call
push+jmp
call
f
ret
call
jmp
f
ret