ELF 動態鏈接 - so 的 重定位表


動態鏈接下,無論時可執行文件還是共享對象,一旦對其他共享對象有依賴,也就是所有導入的符號時,那么代碼或數據中就會有對於導入符號的引用。而在編譯時期這些導入符號的確切地址時未知的。只有在運行期才能確定真正確切的地址

 

靜態編譯下,這些未知的地址會被編譯器一一修正。

 

對於動態鏈接來說,共享文件有兩種編譯方式(gcc -shared 和 gcc -fPIC -shared)

如果不使用PIC模式編譯,那么裝載時肯定是要重定位的,而且時每個進程都有一個副本(相對比較占用內存)

如果使用PIC模式編譯,將會在編譯期生成地址無關代碼(PIC Position-Independent Code),則代碼段可以實現多程序共享,而僅數據段部分會在每個程序中有一個副本(節省內存)

 

對於這兩種模式來說都是要重定位的,當相對PIC模式編譯的模塊僅需要對數據段進行重定位(因為代碼段中的絕對地址引用部分被分離到了GOT中,而GOT是數據段的一部分;數據段中也可能包含絕對地址的引用,正好重定位數據段)

 

靜態鏈接中,目標文件里面包含有用於重定位的表:代碼段重定位表“.rel.text”;數據段重定位表“.rel.data”。

動態鏈接中,目標文件的重定位表:“.rel.dyn”對數據引用的修正,修正的位置位於“.got”和數據段;“.rel.plt”對函數引用的修正,修正位置位於“.got.plt”。

 

可以通過readelf 查看一個動態鏈接文件的重定位表

1 readelf -r XXX.so

這里可以看到幾種重定位入口類型:

R_386_RELATIVE  R_386_GLOB_DATR_386_JUMP_SLOT

R_386_GLOB_DAT(.rel.dyn中針對.got)R_386_JUMP_SLOT(.rel.plt中針對.got.plt)表示被修正的位置只需要直接將符號的地址填入。

而在重定位表中的列Offset 表明了當前符號在“.got" 或“.got.plt”中的偏移,可以根據該值在兩個GOT表中尋找對應的位置,填入(連接器在全局符號表中查找)真實的外部符號地址。

 

R_386_RELATIVE 是基址重置。有些共享對象的數據段是無法做到地址無關的,比如:

static int a;
static int *p = &a;

由於共享對象編譯時,基址是從0開始的,所以a的地址(在未重定位時)是相對與起始地址0的偏移(假設為B),則此時p的值為B;

而當共享對象裝載到指定進程中的地址C時,則變量a的地址將編程B+C,即p的值需要加上裝載的地址B。

R_386_RELATIVE 類型就是專門用來重定位指針變量p這種類型的。

 


免責聲明!

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



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