got & plt


got plt類似與Windows PE文件中IAT(Import Address Table)。

要使的代碼地址無關,基本思想就是把與地址相關的部分放到數據段里面。

ELF的做法是在數據段里面建立一個指向這些變量的指針數組,稱為全局偏移表(Global Offset Table,GOT),當代碼需要引用該全局變量時,可以通過GOT中相對應的項間接引用。

GOT本身是放在數據段,所以可以在模塊加載時被修改。延遲綁定,基本思想就是當函數第一次被用到時才進行綁定

一段非常簡單的代碼:

#include <stdio.h>
#include <stdlib.h>

int helloWorld(){
    printf("HelloWorld\n");
    return 0;
}

int main(){
    helloWorld();
    return 0;
}

 

 

第一條指令是通過GOT間接跳轉的指令。如果連接器在初始化階段已經初始化該項,並且將puts()的地址填充入該項,那么這個跳轉指令的結果就是我們所期望的,跳轉到puts(),實現函數正確調用。

但是為了實現延遲綁定,鏈接器在初始化階段並沒有將puts()的地址填入該項,而是將下一條指令的地址填入到put@got.plt項中。

所以第一條指令的效果是跳轉到第二條指令,相當於是沒有任何操作的。第二條指令是將一個數字n壓入堆棧,這個數字是puts()的符號引用在重定位表".rel.plt"中的下標。

接着又是一條push指令將模塊ID壓入到堆棧,然后跳轉到0xf7ff04f0執行,那么這個地址是什么地址?答案是_dl_runtime_resolve,下文會給出解釋。

 

ELF將GOT拆分為兩個表,".got"和".got.plt"。其中".got"用來保存全局變量引用的地址,“.got.plt”用來保存函數引用的地址,對於外部函數的引用全部放在".got.plt"中。

 

通過上面兩幅圖可以看出:R_386_GLOB_DAT是位於.got段的,R_386_JUMP_SLOT位於.got.plt 段。

".got.plt"前三項是有特殊意義的。

第一項保存的是“.dynamic”段的地址;

第二項保存的是本模塊的ID,也就是之前看到的push進的那個值;

第三項保存的是_dl_runtime_resolve()函數的地址,之前跳轉的地址0xf7ff04f0。

之后就是地址,可以看到都是指向.plt段中,在鏈接時,.plt段通常和代碼段等一起合並成一個可讀可執行的Segment。

可以看到puts@plt的地址0x80482f6位於.plt 中。而且puts在got表項中存放的地址0x080482f6就是puts@plt第二條指令的地址。

 

參考資料:

《程序員的自我修養》動態鏈接

《深入理解計算機系統》第七章,鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM