資源描述:
《程序的鏈接和裝入及l(fā)inux下動(dòng)態(tài)鏈接的實(shí)現(xiàn)》由會(huì)員上傳分享,免費(fèi)在線閱讀,更多相關(guān)內(nèi)容在應(yīng)用文檔-天天文庫。
1、程序的鏈接和裝入及Linux下動(dòng)態(tài)鏈接的實(shí)現(xiàn)程序的鏈接和裝入及Linux下動(dòng)態(tài)鏈接的實(shí)現(xiàn)2003年8月10日程序的鏈接和裝入存在著多種方法,而如今最為流行的當(dāng)屬動(dòng)態(tài)鏈接、動(dòng)態(tài)裝入方法。本文首先回顧了鏈接器和裝入器的基本工作原理及這一技術(shù)的發(fā)展歷史,然后通過實(shí)際的例子剖析了Linux系統(tǒng)下動(dòng)態(tài)鏈接的實(shí)現(xiàn)。了解底層關(guān)鍵技術(shù)的實(shí)現(xiàn)細(xì)節(jié)對(duì)系統(tǒng)分析和設(shè)計(jì)人員無疑是必須的,尤其當(dāng)我們?cè)诿鎸?duì)實(shí)時(shí)系統(tǒng),需要對(duì)程序執(zhí)行時(shí)的時(shí)空效率有著精確的度量和把握時(shí),這種知識(shí)更顯重要。一個(gè)程序要想在內(nèi)存中運(yùn)行,除了編譯之外還要經(jīng)過鏈接和裝入這兩個(gè)步驟。從程序員的角度來看,引入這兩個(gè)步驟帶來
2、的好處就是可以直接在程序中使用printf和errno這種有意義的函數(shù)名和變量名,而不用明確指明printf和errno在標(biāo)準(zhǔn)C庫中的地址。當(dāng)然,為了將程序員從早期直接使用地址編程的夢(mèng)魘中解救出來,編譯器和匯編器在這當(dāng)中做出了革命性的貢獻(xiàn)。編譯器和匯編器的出現(xiàn)使得程序員可以在程序中使用更具意義的符號(hào)來為函數(shù)和變量命名,這樣使得程序在正確性和可讀性等方面都得到了極大的提高。但是隨著C語言這種支持分別編譯的程序設(shè)計(jì)語言的流行,一個(gè)完整的程序往往被分割為若干個(gè)獨(dú)立的部分并行開發(fā),而各個(gè)模塊間通過函數(shù)接口或全局變量進(jìn)行通訊。這就帶來了一個(gè)問題,編譯器只能在一個(gè)模塊內(nèi)
3、部完成符號(hào)名到地址的轉(zhuǎn)換工作,不同模塊間的符號(hào)解析由誰來做呢?比如前面所舉的例子,調(diào)用printf的用戶程序和實(shí)現(xiàn)了printf的標(biāo)準(zhǔn)C庫顯然就是兩個(gè)不同的模塊。實(shí)際上,這個(gè)工作是由鏈接器來完成的。為了解決不同模塊間的鏈接問題,鏈接器主要有兩個(gè)工作要做――符號(hào)解析和重定位:符號(hào)解析:當(dāng)一個(gè)模塊使用了在該模塊中沒有定義過的函數(shù)或全局變量時(shí),編譯器生成的符號(hào)表會(huì)標(biāo)記出所有這樣的函數(shù)或全局變量,而鏈接器的責(zé)任就是要到別的模塊中去查找它們的定義,如果沒有找到合適的定義或者找到的合適的定義不唯一,符號(hào)解析都無法正常完成。重定位:編譯器在編譯生成目標(biāo)文件時(shí),通常都使用從
4、零開始的相對(duì)地址。然而,在鏈接過程中,鏈接器將從一個(gè)指定的地址開始,根據(jù)輸入的目標(biāo)文件的順序以段為單位將它們一個(gè)接一個(gè)的拼裝起來。除了目標(biāo)文件的拼裝之外,在重定位的過程中還完成了兩個(gè)任務(wù):一是生成最終的符號(hào)表;二是對(duì)代碼段中的某些位置進(jìn)行修改,所有需要修改的位置都由編譯器生成的重定位表指出。舉個(gè)簡單的例子,上面的概念對(duì)讀者來說就一目了然了。假如我們有一個(gè)程序由兩部分構(gòu)成,m.c中的main函數(shù)調(diào)用f.c中實(shí)現(xiàn)的函數(shù)sum:/*m.c*/inti=1;intj=2;externintsum();voidmain()ints;s=sum(i,j);/*f.c*/
5、intsum(inti,intj)returni+j;在Linux用gcc分別將兩段源程序編譯成目標(biāo)文件:$gcc-cm.c$gcc-cf.c我們通過objdump來看看在編譯過程中生成的符號(hào)表和重定位表:$objdump-xm.o…SYMBOLTABLE:…00000000gO.data00000004i00000004gO.data00000004j00000000gF.text00000021main00000000*UND*00000000sumRELOCATIONRECORDSFOR[.text]:OFFSETTYPEVALUE00000007R_
6、386_32j0000000dR_386_32i00000013R_386_PC32sum首先,我們注意到符號(hào)表里面的sum被標(biāo)記為UND(undefined),也就是在m.o中沒有定義,所以將來要通過ld(Linux下的鏈接器)的符號(hào)解析功能到別的模塊中去查找是否存在函數(shù)sum的定義。另外,在重定位表中有三條記錄,指出了在重定位過程中代碼段中三處需要修改的位置,分別位于7、d和13。下面以一種更加直觀的方式來看一下這三個(gè)位置:$objdump-dxm.oDisassemblyofsection.text:00000000main:0:55push%ebp1
7、:89e5mov%esp,%ebp3:83ec04sub[message]x4,%esp6:a100000000mov0x0,%eax7:R_386_32jb:50push%eaxc:a100000000mov0x0,%eaxd:R_386_32i11:50push%eax12:e8fcffffffcall13main+0x1313:R_386_PC32sum17:83c408add[message]x8,%esp1a:89c0mov%eax,%eax1c:8945fcmov%eax,0xfffffffc(%ebp)1f:c9leave20:c3ret以su
8、m為例,對(duì)函數(shù)sum的調(diào)用是通過call指令實(shí)現(xiàn)的,