ELF文件中包含了動態連接器的全路徑,內核定位"正確"的動態連接器在內存中的地址是"正確"運行可執行文件的保證,參考資料 13討論了如何通過查找動態連接器在內存中的地址以達到顛覆(Subversiver)動態連接機制的方法。
最后我們討論ELF文件的動態連接機制。每一個外部定義的符號在全局偏移表(Global Offset Table GOT)中有相應的條目,如果符號是函數則在過程連接表(Procedure Linkage Table PLT)中也有相應的條目,且一個PLT條目對應一個GOT條目。對外部定義函數解析可能是整個ELF文件規范中最復雜的,下面是函數符號解析過程的一個描述。
1:代碼中調用外部函數func,語句形式為call 0xaabbccdd,地址0xaabbccdd實際上就是符號func在PLT表中對應的條目地址(假設地址為標號.PLT2)。
2:PLT表的形式如下
.PLT0: pushl 4(%ebx) /* GOT表的地址保存在寄存器ebx中 */ |
3:查看標號.PLT2的語句,實際上是跳轉到符號func在GOT表中對應的條目。
4:在符號沒有重定位前,GOT表中此符號對應的地址為標號.PLT2的下一條語句,即是pushl $offset,其中$offset是符號func的重定位偏移量。注意到這是一個二次跳轉。
5:在符號func的重定位偏移量壓棧后,控制跳到PLT表的第一條目,把GOT[1]的內容壓棧,并跳轉到GOT[2]對應的地址。
6:GOT[2]對應的實際上是動態符號解析函數的代碼,在對符號func的地址解析后,會把func在內存中的地址設置到GOT表中此符號對應的條目中。
7:當第二次調用此符號時,GOT表中對應的條目已經包含了此符號的地址,就可直接調用而不需要利用PLT表進行跳轉。
動態連接是比較復雜的,但為了獲得靈活性的代價通常就是復雜性。其最終目的是把GOT表中條目的值修改為符號的真實地址,這也可解釋節.got包含在可讀可寫段中。
動態連接是一個非常重要的進步,這意味著庫文件可以被升級、移動到其他目錄等等而不需要重新編譯程序(當然,這不意味庫可以任意修改,如函數入參的個數、數據類型應保持兼容性)。從很大程度上說,動態連接機制是ELF格式代替a.out格式的決定性原因。如果說面對對象的編程本質是面對接口(interface)的編程,那么動態連接機制則是這種思想的地一個非常典型的應用,具體的講,動態連接機制與設計模式中的橋接(BRIDGE)方法比較類似,而它的LAZY特性則與代理(PROXY)方法非常相似。動態連接操作的細節描述請參閱參考資料 8,9,10,11。通過閱讀命令readelf、objdump 的源代碼以及參考資料 14中所提及的相關軟件源代碼,可以對ELF文件的格式有更徹底的了解。
總結
不同時期的可執行文件格式深刻的反映了技術進步的過程,技術進步通常是針對解決存在的問題和適應新的環境。早期的UNIX系統使用a.out格式,隨著操作系統和硬件系統的進步,a.out格式的局限性越來越明顯。新的可執行文件格式COFF在UNIX System VR3中出現,COFF格式相對a.out格式最大變化是多了一個節頭表(section head table),能夠在包含基礎的文本段、數據段、BSS段之外包含更多的段,但是COFF對動態連接和C++程序的支持仍然比較困難。為了解決上述問題, UNIX系統實驗室(UNIX SYSTEM Laboratories USL) 開發出ELF文件格式,它被作為應用程序二進制接口(Application binary Interface ABI)的一部分,其目的是替代傳統的a.out格式。例如,ELF文件格式中引入初始化段.init和結束段.fini(分別對應構造函數和析構函數)則主要是為了支持C++程序。1994年6月ELF格式出現在LINUX系統上,現在ELF格式作為UNIX/LINUX最主要的可執行文件格式。當然我們完全有理由相信,在將來還會有新的可執行文件格式出現。
上述三種可執行文件格式都很好的體現了設計思想中分層的概念,由一個總的頭部刻畫了文件的基本要素,再由若干子頭部/條目刻畫了文件的若干細節。比較一下可執行文件格式和以太數據包中以太頭、IP頭、TCP頭的設計,我想我們能很好的感受分層這一重要的設計思想。參考資料 21從全局的角度討論了各種文件的格式,并提出一個比較夸張的結論:Everything Is Byte!
最后的題外話:大多數資料中對a.out格式的評價較低,常見的詞語有黑暗年代(dark ages)、丑陋(ugly)等等,當然,從現代的觀點來看,的確是比較簡單,但是如果沒有曾經的簡單何來今天的精巧?正如我們今天可以評價石器時代的技術是ugly,那么將來的人們也可以嘲諷今天的技術是非常ugly。我想我們也許應該用更平和的心態來對曾經的技術有一個公正的評價。