ELF 文件 动态链接 - 地址无关代码(GOT)
Linux 系統(tǒng)中,ELF動態(tài)鏈接文件被稱為 動態(tài)共享對象(DSO,Dynamic Shared Object),簡稱共享對象
文件拓展名為“.so”
?
動態(tài)鏈接下 一個程序可以被分成若干個文件:程序的主要部分 - 可執(zhí)行文件 和 程序所依賴的共享對象(一個或多個.so文件),它們都可稱作為程序的模塊。
動態(tài)鏈接文件(共享對象)的裝載地址為0x00000000;這并非工作時的實際地址,實際地址由裝載器根據(jù)當(dāng)前進(jìn)程地址空間的空閑情況來動態(tài)分配一塊足夠大的虛擬地址空間給共享對象。
?
裝載時重定位
基本思路:在鏈接時對所有的絕對地址的引用不作重定位,而把這一步放在裝載時完成。一旦模塊裝載完成,既目標(biāo)地址確定,那么系統(tǒng)將對程序中的所有絕對地址的引用進(jìn)行重定位。
?
靜態(tài)鏈接時的重定位稱為 鏈接時重定位(Link Time Relocation)
動態(tài)鏈接時的重定位稱為 裝載時重定位(Load Time Relocation) gcc -shared
?
然而光有裝載時重定位也不能解決所有問題,因為對于動態(tài)鏈接文件來講,它時可以分為可修改數(shù)據(jù)部分和不可修改數(shù)據(jù)部分,如果只有裝載重定位,那每個程序必須都有一個共享對象的副本,這樣會很浪費內(nèi)存
這樣就引入來下一個話題
?
地址無關(guān)代碼 (gcc -shared -fPIC)
基本思想: 把指令中需要被修改的部分分離出來,跟數(shù)據(jù)部分放在一起,這樣指令部分就保持不變了,而數(shù)據(jù)部分為每個進(jìn)程都有一個副本,
這就是地址無關(guān)代碼(PIC, Position-independent Code)技術(shù)
?
共享對象模塊內(nèi)的地址引用分為四種情況:
1. 模塊內(nèi)部調(diào)用或跳轉(zhuǎn):
它們之間位置固定,使用相對地址調(diào)用。
2. 模塊內(nèi)部數(shù)據(jù)訪問
同樣使用相對尋址(使用當(dāng)前(指令地址)PC + 偏移量)
3. 模塊與模塊之間的數(shù)據(jù)訪問(重點)
模塊之間的數(shù)據(jù)訪問,其目標(biāo)地址需要等到裝載后才能確定。
這里的基本思想時將這部分與地址相關(guān)的指令的當(dāng)前地址放入到數(shù)據(jù)段里面。
ELF 的做法是在數(shù)據(jù)段中建立了一個 指向這些變量的指針數(shù)組 ,也被稱為 全局偏移表(GOT, Global Offset Table),當(dāng)代碼需要引用該全局變量時,通過GOT間接引用,
即GOT中記錄著該外部函數(shù)真正的地址,裝載器在做動態(tài)鏈接時,會查找每個外部符號的地址,然后填充到GOT的對應(yīng)的項中。
?
GOT 如何做到與指令無關(guān)的呢?(在.so 文件中對應(yīng) .got 段)
1.?模塊在編譯期可以確定模塊內(nèi)部變量相對與當(dāng)前指令的偏移,同樣在編譯期也可以確定GOT相對于當(dāng)前指令的偏移。確定GOT的位置和確定內(nèi)部變量的位置方法上時一樣的,通過
得到PC值然后加上一個偏移量即可得到GOT的位置。當(dāng)然GOT中的每個地址對應(yīng)于哪個符號(變量,函數(shù)都是符號)由編譯期決定。
?
4. 模塊間跳用和跳轉(zhuǎn)
此時與3.中方法類似,只是對應(yīng)的GOT的位置保存的時目標(biāo)函數(shù)的地址,通過GOT實現(xiàn)間接跳轉(zhuǎn)
?
地址無關(guān)小結(jié):
?
| 各種引用方式 | ||
| ? | 指令跳轉(zhuǎn)和調(diào)用 | ?數(shù)據(jù)訪問 |
| 模塊內(nèi)部 | ?相對跳轉(zhuǎn)調(diào)用 | ?相對訪問 |
| 模塊外部 | GOT間接跳轉(zhuǎn)訪問 | GOT間接跳轉(zhuǎn)訪問 |
?
轉(zhuǎn)載于:https://www.cnblogs.com/gradyblog/p/8963785.html
總結(jié)
以上是生活随笔為你收集整理的ELF 文件 动态链接 - 地址无关代码(GOT)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux查看与修改交换内存配置(解决z
- 下一篇: 一个好玩的 屏蔽别人审查元素F12 右键