【靜態連接和動態鏈接庫】
[靜態鏈接]--->靜態庫:和源程序鏈接和並裝載到虛擬內存
[動態鏈接]--->共享庫:
<1>靜態共享庫:針對每個庫,操作系統分配特定虛擬內存地址,模塊裝載到該特定的虛擬地址,若當前共享庫未被裝載,那麼這個地址空間閒置。即最終裝載地址在編譯時未確定,在鏈接時由於知道了庫所以確定了;
<2>動態共享庫:動態共享庫裝載時,由裝載器根據當前虛擬內存中地址空間情況,動態分配虛擬地址,即最終裝載地址在鏈接時依然未確定直到裝載。
[名詞分析]
鏈接:源文件和庫文件建立鏈接;
裝載:鏈接後的文件建立和虛擬內存間的映射;
鏈接重定位:
是指在靜態鏈接時,鏈接之前在源文件中引用的庫文件函數名(如add)地址都記作0x0000,鏈接之後分類合並得到了該函數的相對於鏈接後文件起始地址0的相對地址(如0x1000),從0x0000-->0x1000地址的轉變就是鏈接重定位;鏈接後裝載時僅需同時偏移相應的量即可。
裝載重定位:
動態鏈接由於鏈接時是動態加載庫文件到虛擬內存中的,鏈接後的文件不是一個整體而且無法計算相對地址,所以只能在裝載到虛擬內存時得到該庫當前的內存地址。
【共享對象結構分析】
[.data]數據段:自動存入ELF文件數據段
共享庫中的全局變量數據段(.data段)在裝載時都並入了可執行文件的數據段(.got段),即在每一個進程的可執行文件中都存在其子共享庫全局變量的副本。
共享庫中的局部變量都存放在代碼段(.text段)中,當開始執行後加載到內存中的堆棧裡
[.interp]動態鏈接解釋段:指定動態鏈接器路徑。
[.dynamic]動態鏈接段:存放動態鏈接基本信息
DT_SYMTAB:[.dynsym]動態鏈接符號表地址;
DT_STRTAB:動態連接字符串表地址;
DT_REL:動態鏈接重定位表地址,包括代碼段重定位表(.rel.text)和數據段重定位(.rel.data)。
DT_NEED:當前ETF文件所的共享對象文件
... ...
[.symtab]靜態鏈接符號表:存放靜態鏈接符號表
--------------------------------------------------------------------------------------------------
Q:動態鏈接段中的符號表和靜態鏈接符號表關系?
A:動態鏈接表(.dynsym)只存放動態鏈接符號,而靜態鏈接符號表(.symtab)存放所有符號(包括動態的)。
--------------------------------------------------------------------------------------------------
[.text]代碼段:需要進行地址無關碼轉換!
代碼段與絕對地址有關:
即代碼段對應這某個確定的虛擬地址,那麼它就不能被多個進程共享,因為它不是被操作系統隨機分配的,就是說我們編譯之後就已經知道了它的地址,這就是靜態共享庫了(已淘汰了);
當其他進程想要使用這個絕對地址,可以!因為這個地址早就知道了。
代碼段與絕對地址無關:
即代碼段的絕對地址當裝載時候才能得到絕對地址,裝載之前我們只知道相對地址,裝載過程中操作系統按照內存空間隨機的分配了一個絕對地址(已知了)!
當其他進程想使用這個絕對地址時,把當前虛擬內存中的代碼和數據映射到其他進程的地址空間中。
---------------------------------------------------------------------------------------------------------------
靜態鏈接:鏈接時重定位;
靜態共享庫:不需要重定位;
動態共享庫:裝載時重定位;
動態共享庫:在裝載時分配虛擬內存,各個動態庫首地址一定要進行重定位-shared,但是動態共享庫的代碼段可能會產生和地址有關的代碼:這個時候我們就需要-fPIC來幫助我們把代碼段中的地址有關代碼轉換成地址無關代碼,就多個可以很easy的使用共享對象中的代碼段(只讀)和數據段(讀寫);如果我們沒有做出地址無關碼這一步,那麼共享庫復用的時候從某個進程的虛擬內存映射到另外一個進程中,可以算得共享對象內部的代碼,但這樣就必須預留出空間,和自動分配空間就違背了。---------------------------------------------------------------------------------------------------------------
[代碼段無關碼實現方式]:
<1>內部:保留偏移量來計算當前地址,即與絕對地址無關。
模塊內部函數調用和跳轉;
模塊內部數據訪問。
<2>外部:依靠地址依賴/符號表查找絕對地址,
模塊間數據訪問; ---> 存放到本文件的全局變量地址段(.got段),ATT:不是可執行文件
模塊間函數調用/跳轉;--->保存到本文件的的函數引用地址段 (.got.plt段),ATT:不是可執行文件
Att:.got段中存放本文件全局變量的符號和地址,使用該某個變量只需在段中索引。
Att:.got.plt段中存放三項
<1>.dynamic段地址,使用某個函數名只需在動態鏈接符號表中索引即可找到引用的地址;
<2>存放本模塊鏈接庫的ID,如test.so中存放的就是test。
<3>存放_dll_runtime_resolve()函數地址,用來根據地址和符號完成地址綁定工作。
[簡而言之]
裝載共享對象:保證多個進程所需共享對象的內部代碼段與絕對地址無關,把和絕對地址有關的數據/函數在動態鏈接後存放到各個共享對象的.dynamic段中。
void main(){
CPU控制權轉交動態鏈接器;
操作系統告訴了動態鏈接器有關可執行文件的基本信息(包括入口地址,段的基本信息等);
動態鏈接器通過本身的靜態鏈接自舉,自舉結束後把可執行文件靜態鏈接到內存的某個特定地址;
然後通過查看可執行文件的.dynamic段中的DT_NEED把當前文件所依賴的共享對象文件找出來,
while(每映射一個依賴的動態對象){
查看該動態對象的.dynamic段觀察該對象是否依賴別的對象,如果依賴,返回上一步循環;
把當前動態對象文件中的符號表合並到虛擬空間中的全局符號表中;
把當前依賴文件中的全局變量和靜態局部變量裝載映射到虛擬內存中的靜態區;
把可執行文件和依賴文件的局部變量存放到棧中,棧中作用量會在離開棧時自動銷毀;
把程序代碼映射到虛擬內存程序代碼區;
if(所有依賴對象裝載完畢)
break;
} /*ELF和LInux進程虛擬空間映射完畢*/
CPU控制權轉交可執行文件入口地址;
可執行文件開始執行;
Process Virtual Space --> Physical Space
/*虛擬空間和物理空間映射完畢*/
}