Linux動態鏈接之三:動態鏈接相關結構

無論是靜態鏈接還是動態鏈接,初始都是操作系統讀取可執行文件的FILE_HEADER,以檢查文件格式、操作權限等屬性,然後根據段表獲取各個”segment”的VMA虛擬裝載位置、文件地址和操作屬性RWXP等,再根據相似屬性原則相連原則完成裝載,而後將控制權交給文件頭結構中e_entry入口地址(ELF程序的入口虛擬地址,可重定位文件不可執行,故而爲0,靜態鏈接的可執行文件便是指向運行庫main初始化函數,動態鏈接的可執行文件則是指向動態鏈接程序的入口地址)。

在Linux下,動態鏈接器ld.so實際上本身也是一個共享對象.so(但它和別的共享對象不同的是,它不能引用其他共享對象),操作系統將ld.so裝載進虛擬進程空間後,將把控制權移交給動態鏈接器的程序入口。ld.so獲取控制權後,首先執行一系列自身的初始化操作,然後將會按照鏈式法則順序加載一系列共享對象.so。當所有.so裝載完畢後,纔將控制權重新交給可執行文件本身。

1.“ .interp”段

現在面對需要動態鏈接的可執行文件,系統該採用的動態鏈接器在哪?哪種動態鏈接器?這是由ELF可執行文件決定的。.interp段(interpreter解釋器)的內容很簡單,保留的就是:該可執行文件所需的動態鏈接器的路徑。

.interp段中的動態鏈接器路徑/lib/ld-linux.so.2.是個軟鏈接,其實指向的應該是/lib/ld-2.6.1.so,當系統Glibc庫升級時,該軟連接就是指向新的動態鏈接器,而不需要修改.interp中的路徑名來配合系統的升級。

也可以通過readlf指令來獲取一個可執行文件所需要的動態鏈接器的路徑

$readlf  -l a.out | grep interpreter
    [Requesting program interpreter: /lib/ld-linux.so.2]

2.“ .dynamic”段

前面的文章說道外部函數地址GOT子表“.got.plt”的第一項便是指向.dynamic段。類似於.interp這樣的段,ELF中還有幾個段也是專門用來動態鏈接的,比如.dynamic段和.dynsym段。

.dynamic段保存了動態鏈接需要的基本信息:依賴哪些共享對象、動態鏈接符號表的位置、動態鏈接重定位表的位置、共享對象初始化代碼的地址。.dynamic段中是如下結構體數組,該結構體元素用來定義動態鏈接所需的各基本信息。

typedef struct{
    Elf32_Sword  d_tag;
    union {
        Elf32_Word  d_val;
        Elf32_Addr  d_ptr;
       } d_un;
      } Elf32_Dyn;


…..

若是想直接查看當前文件依賴於那些共享對象,可用ldd指令

$ldd  program1
    linux-gate.so.1  => (0xffffe000) //該文件屬於內核虛擬共享對象,處在虛擬進程空間的高端,屬於內核區,涉及到內核加載
    ./Lib.so (0xb7f62000)
    libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7e0d000)
    /lib/ld-linux.so.2  (0xb7f66000)

可以看到.dynamic段像以前的FILE_HEADER一樣只是提供具體所需對象的索引數組,下面來繼續查看一些動態鏈接所需的關鍵信息,如動態符號表.dynsym。

3.“ .dynsym”動態符號表

爲了表示動態鏈接的模塊之間的符號導入導出關係,ELF提供了動態符號表.dynsym,與.symtab不同,前者只保存和動態鏈接相關的符號,而模塊內部符號是不保存的(專表專用,加速動態鏈接符號查找過程),故而ELF動態鏈接模塊同時擁有兩個表.symtab和.dynsym, .symtab包括所有符號,包括.dynsym中的符號。

動態符號表也需要一些輔助表,如動態符號字符串表.dynstr (dynamic string table)(因爲符號名稱是非等項的,爲了方便dynsym表的管理,顯然需要字符串這種不穩定分子轉移出來以實現dynsym表項等長),爲了加快符號的查找過程,往往還有輔助的符號哈希表.hash。

4.動態鏈接重定位表

.so共享對象需要重定位的原因在於導入符號的存在,無論是可執行文件還是.so,一旦依賴於其他共享對象,也就是說有導入符號存在,那麼它的代碼或數據中就會有對導入符號的引用。在編譯時這些導入符號的地址未知,靜態鏈接中,這些未知的地址引用在最終鏈接時被修正;而在動態鏈接中,導入符號的地址只有在運行時才確定,所以需要在運行時將這些導入符號的引用修正,即需要重定位。(類似於Windows下的符號導出表和符號導入表)

對於使用PIC技術的可執行文件或共享對象來說,雖然他們的代碼段不需要重定位(因爲地址無關),但是.data段有可能包含了對絕對地址的引用,因爲代碼段中絕對地址相關的部分被分離了出來,變成了GOT,而GOT實際上是數據段的一部分。動態鏈接的文件中,有.rel.dyn和.rel.plt,其中.rel.dyn是對數據引用的修正,修正的位置位於.got以及數據段,而.rel.plt(延遲綁定)是對函數引用的修正,所修正的位置位於.got.plt。

這裏寫圖片描述
靜態鏈接用到的重定位入口
1.R_386_32 絕對地址修正
2.R_386_PC32下一指令相對地址修正
動態鏈接用到的重定位入口類型
1.R_386_Relative:基址重置rebasing,基址+偏移地址
2.R_386_GLOB_DAT:對.got表中的數據符號的重定位標誌,在.got表中填充該數據符號的絕對地址
3.R_386_JUMP_SLOT:對.got.plt的重定位標誌,只需要根據.rel.plt表給出的該函數符號在.got.plt的偏移量找到該函數符號的位置,填寫上該函數加載後的地址

重定位表的意義在於提示動態鏈接器要修正的外部符號信息,可以配合實現PLT等延遲綁定機制,實現動態鏈接的快速進行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章