我知道在典型的ELF二進制文件中,函數通過過程鏈接表(PLT)進行調用。函數的PLT條目通常包含跳轉到全局偏移表(GOT)條目。該條目將首先引用一些代碼將實際函數地址加載到GOT中,並在第一次調用(延遲綁定)後包含實際函數地址。爲什麼PLT除了GOT之外還存在,而不僅僅是使用GOT?
準確地說,在延遲綁定之前,GOT條目返回到PLT中,指向跳轉到GOT之後的指令。這些指令通常會跳到PLT的頭部,從那裏調用一些綁定例程,然後更新GOT條目。
現在我想知道爲什麼有兩個間接點(調用PLT,然後跳轉到GOT的地址),而不是僅僅保留PLT並直接從GOT調用地址。看起來這樣可以節省跳躍和完整的PLT。您當然仍然需要一些調用綁定例程的代碼,但這可能在PLT之外。
有什麼我失蹤?一個額外的PLT的目的是什麼?
更新: 正如評論所說,我創造了一些(僞)代碼ASCII藝術進一步解釋什麼,我指的是:
情況是這樣的,至於我的理解是,在目前的PLT計劃前懶結合:(PLT的和printf
之間的一些間接性,分別由「...」)。
Program PLT printf
+---------------+ +------------------+ +-----+
| ... | | push [0x603008] |<---+ +-->| ... |
| call j_printf |--+ | jmp [0x603010] |----+--...--+ +-----+
| ... | | | ... | |
+---------------+ +-->| jmp [[email protected]] |-+ |
| push 0xf |<+ |
| jmp 0x400da0 |----+
| ... |
+------------------+
...之後延遲綁定:
Program PLT printf
+---------------+ +------------------+ +-----+
| ... | | push [0x603008] | +-->| ... |
| call j_printf |--+ | jmp [0x603010] | | +-----+
| ... | | | ... | |
+---------------+ +-->| jmp [[email protected]] |--+
| push 0xf |
| jmp 0x400da0 |
| ... |
+------------------+
在我的想象中的替代方案沒有PLT,結合應該是這樣的懶之前的情況:(我不停的代碼中的「延遲綁定表」類似,從PLT的一個。它也可以看不同的,我不在乎)
Program Lazy Binding Table printf
+-------------------+ +------------------+ +-----+
| ... | | push [0x603008] |<-+ +-->| ... |
| call [[email protected]] |--+ | jmp [0x603010] |--+--...--+ +-----+
| ... | | | ... | |
+-------------------+ +-->| push 0xf | |
| jmp 0x400da0 |--+
| ... |
+------------------+
現在後的延遲綁定,一個將不再使用該表:
Program Lazy Binding Table printf
+-------------------+ +------------------+ +-----+
| ... | | push [0x603008] | +-->| ... |
| call [[email protected]] |--+ | jmp [0x603010] | | +-----+
| ... | | | ... | |
+-------------------+ | | push 0xf | |
| | jmp 0x400da0 | |
| | ... | |
| +------------------+ |
+------------------------+
1.「_direct_」表示調用目標是靜態的,不能從內存中讀取?這當然是正確的,但除了呼叫之外,還有一個不必要的跳躍(總共一個呼叫和一個跳轉)。在現代x86上,無條件跳轉可能不是什麼大問題,但是對於所有體系結構而言,這可能並非如此,並且對代碼緩存局部性來說絕對沒有好處。 – F30
2.我的「重新發明的」PLT與原來的相似,因爲它可能包含所有功能的綁定存根。但與我的重要區別在於,並非每個電話都必須從PLT轉到GOT(並返回一次)。相反,它直接進入GOT並回到「重新發明」的PLT進行第一次通話。 – F30
@ F30「直接表示調用目標是靜態的,不能從內存中讀取」 - 不只是指它,這是直接調用的定義。他們肯定有他們的成本,但它(低得多)低於indirects,所以精確是很重要的。 – yugr