




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
本文格式為Word版,下載可任意編輯——C語(yǔ)言中的鏈接編寫(xiě)假設(shè)你有一個(gè)c++做的動(dòng)態(tài)鏈接庫(kù).so文件,而你只有一些相關(guān)類的聲明,那么你如何用C調(diào)用呢?C語(yǔ)言中的鏈接編寫(xiě)再怎么寫(xiě)呢?接待大家閱讀!更多相關(guān)信息請(qǐng)關(guān)注相關(guān)欄目!
鏈接
鏈接就是將不同片面的代碼和數(shù)據(jù)收集和組合成為一個(gè)單一文件的過(guò)程,這個(gè)文件可被加載或拷貝到存儲(chǔ)器執(zhí)行.
鏈接可以執(zhí)行與編譯時(shí)源代碼被翻譯成機(jī)器代碼時(shí),也可以執(zhí)行與加載時(shí)在程序被加載器加載到存儲(chǔ)器并執(zhí)行時(shí),甚至執(zhí)行與運(yùn)行時(shí),由應(yīng)用程序來(lái)執(zhí)行.在現(xiàn)代系統(tǒng)中,鏈接是由鏈接器自動(dòng)執(zhí)行的.
鏈接器分為:靜態(tài)鏈接器和動(dòng)態(tài)鏈接器兩種.
靜態(tài)鏈接器
靜態(tài)鏈接器以一組可重定位目標(biāo)文件和命令行參數(shù)作為輸入,生成一個(gè)完全鏈接的可以加載和運(yùn)行的可執(zhí)行目標(biāo)文件作為輸出.
靜態(tài)鏈接器主要完成兩個(gè)任務(wù):
1符號(hào)解析:目標(biāo)文件定義和引用符號(hào).符號(hào)解析的目的在于將每個(gè)符號(hào)引用和一個(gè)符號(hào)定義聯(lián)系起來(lái).
2重定位:編譯器和匯編器生成從地址零開(kāi)頭的代碼和數(shù)據(jù)節(jié).鏈接器通過(guò)把每個(gè)符號(hào)定義和一個(gè)存儲(chǔ)器位置聯(lián)系起來(lái),然后修改全體對(duì)這些符號(hào)的引用,使得他們執(zhí)行這個(gè)存儲(chǔ)位置,從而重定位這些節(jié).
目標(biāo)文件:
目標(biāo)文件有三種形式:
1可重定位的目標(biāo)文件:
包含二進(jìn)制代碼和數(shù)據(jù),其形式可以再編譯時(shí)與其他可定位目標(biāo)文件合并起來(lái),創(chuàng)造一個(gè)可執(zhí)行目標(biāo)文件.
2可執(zhí)行目標(biāo)文件:
包含二進(jìn)制代碼和數(shù)據(jù),其形式可以被直接拷貝到存儲(chǔ)器并執(zhí)行.
3共享目標(biāo)文件:
一種特殊的可重定位目標(biāo)文件,可以再加載或運(yùn)行時(shí),被動(dòng)態(tài)地夾在到存儲(chǔ)器并執(zhí)行.
編譯器和匯編器生成可重定位目標(biāo)文件包括共享目標(biāo)文件,鏈接器生成可執(zhí)行目標(biāo)文件.
可重定位目標(biāo)文件:
EF頭L以一個(gè)16字節(jié)的序列開(kāi)頭,這個(gè)序列描述了字的大小和生成該文件的系統(tǒng)字節(jié)依次.ELF頭剩下的片面包含扶助鏈接器解析和解釋目標(biāo)文件的信息.其中包括ELF頭的大小,目標(biāo)文件的類型譬如,可重定位,可執(zhí)行,共享目標(biāo)文件,機(jī)器類型,節(jié)頭部表的文件偏移,以及節(jié)頭部表中的表目大小和數(shù)量.不同節(jié)的位置和大小是節(jié)頭部表描述的,其中目標(biāo)文件中的每個(gè)節(jié)都有一個(gè)固定大小的表目.ELF格式的可重定位目標(biāo)文件布局如下圖:
.text:已編譯程序的機(jī)器代碼
.rodata:只讀數(shù)據(jù)
.data:已初始化的全局C變量
.bss:未初始化的全局C變量.在目標(biāo)文件中這個(gè)節(jié)不占實(shí)際空間,僅是一個(gè)占位符.
.sysmtab:一個(gè)符號(hào)表,存放在程序中被定義和引用的函數(shù)和全局變量的信息.
.rel.text:當(dāng)鏈接器把這個(gè)目標(biāo)文件和其他文件結(jié)合時(shí),.text節(jié)中的大量位置都需要修改.一般而言,任何調(diào)用外部函數(shù)或者引用全局變量的指令都要修改.另一個(gè)方面,調(diào)用本地函數(shù)的指令那么不需要修改.
.rel.data:被模塊定義或引用的任何全局變量的信息.
.debug:一個(gè)調(diào)試符號(hào)表
.line:原始C源程序中的行號(hào)和.text節(jié)中機(jī)器指令之間的映射.
.strtab:一個(gè)字符串表,其中內(nèi)容包括.symtab和.debug節(jié)中的符號(hào)表,以及節(jié)頭部中的節(jié)名字.
符號(hào)和符號(hào)表
每個(gè)可重定位目標(biāo)模塊m都有一個(gè)符號(hào)表,它包含m所定義和引用的符號(hào)的信息.在鏈接器上下文中,有三種不同的符號(hào):
1由m定義并能被其他模塊引用的全局符號(hào).全局鏈接器符號(hào)對(duì)應(yīng)于非靜態(tài)的C函數(shù)以及被定義為不帶C的static屬性的全局變量.
2由其他模塊定義并被模塊m引用的全局符號(hào).這些符號(hào)成為外部符號(hào),對(duì)應(yīng)于定義在其他模塊中的C函數(shù)和變量.
3只被模塊m定義和引用的本地符號(hào).有的本地符號(hào)鏈接器符號(hào)對(duì)應(yīng)于帶static屬性的C函數(shù)和全局變量.這些符號(hào)在模塊m中的任何地方都可見(jiàn),但是不能被其他模塊引用.目標(biāo)文件中對(duì)應(yīng)于模塊m的節(jié)和相應(yīng)的源文件的名字也能獲得本地符號(hào).
符號(hào)表式有匯編器構(gòu)造的,使用編譯器輸出到匯編語(yǔ)言.s文件中的符號(hào).sysmab節(jié)中包含ELF符號(hào)表.這張符號(hào)表包含一個(gè)關(guān)于表目的數(shù)組.表目的格式如下:
typedefstructintname;//stringtableoffsetintvalue;//sectionoffset,orVMaddressintsize;//objectsizeinbyteschartype:4,//data,func,section,orsrcfilebinding:4;//localorglobalcharreserved;//unusedcharsection;//sectionheaderindex,ABS,UNDEF,orCOMMONElf_Symbol;
符號(hào)解析
鏈接器解析符號(hào)引用的方法是將每個(gè)引用和它輸入的可重定位目標(biāo)文件按的符號(hào)表中的一個(gè)確定的符號(hào)定義聯(lián)系起來(lái).
對(duì)于那些和引用定義在一致模塊的本地符號(hào)的引用,符號(hào)解析式分外簡(jiǎn)樸領(lǐng)略的.編譯器只允許每個(gè)模塊中的每個(gè)本地符號(hào)只有一個(gè)定義.編譯器還確保靜態(tài)本地變量,它們會(huì)有本地鏈接器符號(hào),擁有唯一的名字.
對(duì)于全局符號(hào)的引用解析,當(dāng)編譯器遇到一個(gè)不是在當(dāng)前模塊中定義的符號(hào)變量或函數(shù)名時(shí),它會(huì)假設(shè)該符號(hào)式在其他某個(gè)模塊中定義的,生成一個(gè)鏈接器符號(hào)表表目,并把它交給鏈接器處理.假設(shè)鏈接器在它的任何輸入模塊中都找不到這個(gè)被引用的符號(hào),它就輸出一條錯(cuò)誤信息并終止.
在編譯時(shí),編譯器輸出的每個(gè)全局符號(hào)給匯編器,或者是強(qiáng),或者是弱,而匯編器把這個(gè)信息隱含地編碼在可重定位目標(biāo)文件的符號(hào)表中.函數(shù)和以初始化的全局變量是強(qiáng)符號(hào),未初始化的全局變量是弱符號(hào).
根據(jù)符號(hào)的強(qiáng)弱,有如下規(guī)矩:
1不允許有多個(gè)強(qiáng)符號(hào)
2假設(shè)有一個(gè)強(qiáng)符號(hào)和多個(gè)弱符號(hào),那么選擇強(qiáng)符號(hào)
3假設(shè)有多個(gè)弱符號(hào),那么任選一個(gè)弱符號(hào)
與靜態(tài)庫(kù)鏈接
全體編譯系統(tǒng)都供給一種機(jī)制,將全體相關(guān)的目標(biāo)模塊打包為一個(gè)單獨(dú)的文件,稱為靜態(tài)庫(kù),它可以用做鏈接器的輸入.當(dāng)鏈接器構(gòu)造一個(gè)輸出的可執(zhí)行文件時(shí),它只拷貝靜態(tài)庫(kù)里被應(yīng)用程序引用的目標(biāo)模塊.
在unix系統(tǒng)中,靜態(tài)庫(kù)以一種稱為存檔的特殊文件格式存放在磁盤(pán)中.存檔文件是一組連接起來(lái)的可重定位目標(biāo)文件的集合,有一個(gè)頭部描述每個(gè)成員目標(biāo)文件的大小和位置.
鏈接器如何使用靜態(tài)庫(kù)來(lái)解析引用
在符號(hào)解析階段,鏈接器從左到右按照它們?cè)诰幾g驅(qū)動(dòng)程序命令行上展現(xiàn)的一致依次來(lái)掃描可重定位目標(biāo)文件和存檔文件.在這次掃描中,鏈接器位置一個(gè)可重定位目標(biāo)文件集合E,這個(gè)集合中的文件會(huì)被合并起來(lái)形成可執(zhí)行文件,和一個(gè)未解析的符號(hào)集合U,以及一個(gè)在前面輸入文件中已定義的符號(hào)結(jié)合D.初始時(shí),E,U,D都是空的.
1對(duì)于命令行上的每個(gè)輸入文件f,鏈接器會(huì)判斷f是一個(gè)目標(biāo)文件還是一個(gè)存檔文件.假設(shè)是一個(gè)目標(biāo)文件,那么鏈接器把f添加到E,修改U和D來(lái)反映f中的符號(hào)定義和引用,并持續(xù)下一個(gè)輸入文件.
2假設(shè)f是一個(gè)存檔文件,那么鏈接器就嘗試匹配U中未解析的符號(hào)由存檔文件成員定義的符號(hào).假設(shè)某個(gè)存檔文件成員m,定義了一個(gè)符號(hào)來(lái)解析U中的一個(gè)引用,那么就將m加到E中,并且鏈接器修改U和D來(lái)反映m中的符號(hào)定義和引用.對(duì)存檔文件中的全體成員目標(biāo)文件都反復(fù)舉行這個(gè)過(guò)程,知道U和D都不再發(fā)生變化.在此時(shí),任何不包含在E中的成員目標(biāo)文件都會(huì)被丟棄,而鏈接器將持續(xù)到下一個(gè)輸入文件.
3假設(shè)當(dāng)鏈接器完成對(duì)輸入命令行的掃描后,U是非空的,那么鏈接器就會(huì)輸出一個(gè)錯(cuò)誤并終止.否那么,它會(huì)合并重定位E中的目標(biāo)文件,從而構(gòu)建輸出的可執(zhí)行文件.
這種方式,導(dǎo)致了在輸入命令時(shí)要考慮到,靜態(tài)庫(kù)和目標(biāo)文件的位置,庫(kù)文件放在目標(biāo)文件的后面,假設(shè)庫(kù)文件之間有引用關(guān)系,那么被引用的庫(kù)放在后面.
重定位
當(dāng)鏈接器完成了符號(hào)解析這一步時(shí),它就把代碼中的每個(gè)符號(hào)引用和確定的一個(gè)符號(hào)定義也就是,它的一個(gè)輸入目標(biāo)模塊中的一個(gè)符號(hào)表表目聯(lián)系起來(lái).此時(shí),鏈接器就知道它的輸入目標(biāo)模塊中的代碼節(jié)和數(shù)據(jù)解確實(shí)切大小.然后就開(kāi)頭重定位步驟.重定位由兩步組成:
1重定位節(jié)和符號(hào)定義:
在這一步中,鏈接器將全體一致類型的節(jié)合并為一個(gè)新的聚合節(jié).然后,鏈接器將運(yùn)行時(shí)存儲(chǔ)器地址賦值給新的聚合節(jié),賦給輸入模塊定義的每個(gè)節(jié),以及賦給輸入模塊定義的每個(gè)符號(hào).當(dāng)這一步完成時(shí),程序中的每個(gè)指令和全局變量都一個(gè)唯一的運(yùn)行時(shí)存儲(chǔ)器地址.
2重定位節(jié)中的符號(hào)引用:
在這一步中,鏈接器修改代碼節(jié)和數(shù)據(jù)節(jié)中對(duì)每個(gè)符號(hào)的引用,使得它們指向正確的運(yùn)行時(shí)地址.為了執(zhí)行這一步,鏈接器憑借于稱為重定位表目的可重定位目標(biāo)模塊中的數(shù)據(jù)布局.
重定位表目:
當(dāng)匯編器生成一個(gè)目標(biāo)模塊時(shí),它并不知道數(shù)據(jù)和代碼最終將存放在存儲(chǔ)器中的什么位置.它也不知道這個(gè)模塊引用的任何外部定義的函數(shù)或者全局變量的位置.所以,無(wú)論何時(shí)匯編器遇到對(duì)最終位置未知的目標(biāo)引用,它就會(huì)生成一個(gè)重定位表目,報(bào)告鏈接器在將目標(biāo)文件合并為可執(zhí)行文件時(shí),如何修改這個(gè)引用.代碼的重定位表目放在.rel.text中.已初始化數(shù)據(jù)的重定位表目放在rel.data中.
ELF重定位表目的格式如下:
typedefstruct
intoffset;//offsetofthereferencetorelocate
intsymbol:24,//symbolthereferencepointto
type:8;//relocationtype
Elf32_Rel;
ELF定義了11中不同的重定位類型,其中最根本的兩種重定位類型是:R_386_PC32重定位一個(gè)使用32PC相關(guān)的地址引用和R_386_32重定位一個(gè)使用32位十足地址的引用.
動(dòng)態(tài)鏈接器
共享庫(kù)是一個(gè)目標(biāo)模塊,在運(yùn)行時(shí),可以加載到任意的存儲(chǔ)器地址,并在存儲(chǔ)器中和一個(gè)程序鏈接起來(lái).這個(gè)過(guò)程稱為動(dòng)態(tài)鏈接,是由動(dòng)態(tài)鏈接器完成的.
共享庫(kù)的共享在兩個(gè)方面有所不同.首先,在任何給定的文件系統(tǒng)中,對(duì)于一個(gè)庫(kù)只有一個(gè).so文件.全體引用該庫(kù)德可執(zhí)行目標(biāo)文件共享這個(gè).so文件中的代碼和數(shù)據(jù),而不是像靜態(tài)庫(kù)德內(nèi)容那樣被拷貝和嵌入到引用它們的可執(zhí)行的文件中.其次,在存儲(chǔ)器中,一個(gè)共享庫(kù)的.text節(jié)只有一個(gè)副本可以被不同的正在運(yùn)行的進(jìn)程共享.
多目標(biāo)文件的鏈接
stack.c
#include#defineSTACKSIZE1000
typedefstructstack
intdata[STACKSIZE];
inttop;
stack;
stacks;
intcount=0;
voidpushStackintd
s.data[s.top++]=d;
count++;intpopStack
returns.data[--s.top];
intisEmpty
returns.top==0;
link.c
#includeinta,b;
intmain
a=b=1;
pushStacka;
pushStackb;
pushStacka;
while!isEmpty
printf%dn,popStack;
return0;
編譯方式:
gcc-Wallstack.clink.c-omain
提示出錯(cuò)信息如下:
但是代碼是可以執(zhí)行的
定義和聲明
static和extern修飾函數(shù)
上述編譯展現(xiàn)錯(cuò)誤的理由是:編譯器在處理函數(shù)調(diào)用代碼時(shí)沒(méi)有找到函數(shù)原型,只好根據(jù)函數(shù)調(diào)用代碼做隱式聲明,把這三個(gè)函數(shù)聲明為:
intpushStackint;intpopStackvoid;intisEmptyvoid;
編譯器往往不知道去哪里找函數(shù)定義,像上面的例子,我讓編譯器編譯main.c,而這幾個(gè)函數(shù)定義卻在stack.c里,編譯器無(wú)法知道,因此可以用extern聲明。修改link.c如下:
#includeinta,b;externvoidpushStackintd;externintpopStackvoid;externintisEmptyvoid;intmaina=b=1;pushStacka;pushStackb;pushStacka;while!isEmptyprintf%dn,popStack;return0;
這樣編譯器就不會(huì)報(bào)警了。這里extern關(guān)鍵字表示這個(gè)標(biāo)識(shí)符具有ExternalLinkage.pushStack這個(gè)標(biāo)識(shí)符具有ExternalLinkage指的是:假設(shè)link.c和stack.c鏈接在一起,假設(shè)pushStack在link.c和stack.c中都聲明在stack.c中的聲明同時(shí)也是定義,那么這些聲明指的是同一個(gè)函數(shù),鏈接后是同一個(gè)GLOBAL符號(hào),代表同一個(gè)地址。函數(shù)聲明中的extern可以省略不寫(xiě),不屑extern的函數(shù)聲明也表示這個(gè)函數(shù)具有ExternalLinkage。
假設(shè)用static關(guān)鍵字修飾一個(gè)函數(shù)聲明,那么表示該標(biāo)識(shí)符具有InternalLinkage,例如有以下兩個(gè)程序文件:
/*foo.c*/staticvoidfoovoid/*main.c*/voidfoovoid;intmainvoidfoo;return0;
編譯鏈接在一起會(huì)出錯(cuò),理由是:
雖然在foo.c中定義了函數(shù)foo,但是這個(gè)函數(shù)是static屬性,只具有internalLinkage。假設(shè)把foo.c編譯成目標(biāo)文件,函數(shù)名foo在其中是一個(gè)LOCAL的符號(hào),不參與鏈接過(guò)程,所以在鏈接時(shí),main.c中用到一個(gè)ExternalLinkage的foo函數(shù),鏈接器卻找不到它的定義在哪,無(wú)法確定它的地址,也就無(wú)法做符號(hào)解析,只好報(bào)錯(cuò)。
只要被屢屢聲明的變量或函數(shù),務(wù)必有且只有一個(gè)聲明是定義,假設(shè)有多個(gè)定義,或者一個(gè)定義都沒(méi)有,鏈接器就無(wú)法完成鏈接
static和extern修飾變量
假設(shè)我想在link.c中訪問(wèn)stack.c中定義的int變量count,那么可以用extern聲明
#include
inta,b;
externvoidpushStackintd;
externintpopStackvoid;
externintisEmptyvoid;
externintcount;
intmain
a=b=1;
pushStacka;
pushStackb;
pushStacka;
printf%dn,count;
while!isEmpty
printf%dn,popStack;
return0;
變量count具有externallinkage,它的存儲(chǔ)空間是在stack.c中調(diào)配的,所以link.c中的變量聲明externintcount;不是變量定義,由于它不調(diào)配存儲(chǔ)空間。
假設(shè)不想在stack.c外讓外界訪問(wèn)到count,那么可以用static關(guān)鍵字將count聲明為InternalLinkage
識(shí)別
變量生命和函數(shù)聲明有一點(diǎn)不同,函數(shù)聲明的extern可寫(xiě)可不寫(xiě),而變量聲明假設(shè)不寫(xiě)extern,意思就完全變了。假設(shè)上面的例子不寫(xiě)extern就表示在main函數(shù)中定義一個(gè)全局變量count。
用static關(guān)鍵字聲明具有InternalLinkage的函數(shù)和關(guān)鍵字是處于養(yǎng)護(hù)內(nèi)部狀態(tài)的目的,也是一種封裝Encapsulation的思想。一個(gè)模塊中,有些函數(shù)是供給給外界使用的,也稱為導(dǎo)出Export給外界使用,這些函數(shù)用extern聲明為ExternalLinkage的。
頭文件
為了防止每次函數(shù)extern聲明,例如又有一個(gè)foo.c也使用pushStack等函數(shù),又需要在foo.c中寫(xiě)多個(gè)extern聲明,為了制止這種重復(fù)麻煩的操作,可以自己定義一個(gè)stack.h頭文件:
#ifndefSTACK_H
#defineSTACK_H
#defineSTACKSIZE1000
typedefstructstack
intdata[STACKSIZE];
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024-2025學(xué)年江蘇省連云港市贛榆區(qū)高一上冊(cè)10月月考數(shù)學(xué)檢測(cè)試題(含解析)
- 學(xué)校維修工程合同書(shū)范文
- 出售鏟車(chē)吊車(chē)合同范例
- 豐臺(tái)工人食堂承包合同范例
- 第十單元實(shí)驗(yàn)活動(dòng)6 酸、堿的化學(xué)性質(zhì)教學(xué)設(shè)計(jì)-2023-2024學(xué)年九年級(jí)化學(xué)人教版下冊(cè)
- 倉(cāng)儲(chǔ)物流服務(wù)合同范例
- 吊裝工聘用合同范例
- 住建房出讓合同范例
- 勞動(dòng)合同范本嘉興
- 寫(xiě)二手車(chē)交易合同范例
- 保潔管理服務(wù)定位
- 三年級(jí)數(shù)學(xué)下冊(cè)蘇教版《解決問(wèn)題的策略-從問(wèn)題想起》課件(區(qū)級(jí)公開(kāi)課)
- ad-hoc第二章-ad-hoc網(wǎng)絡(luò)中的MAC協(xié)議
- 建筑工程施工質(zhì)量控制PPT課件
- 心性修煉與教育智慧
- 二手房買(mǎi)賣(mài)合同正式版空白
- 西方企業(yè)組織變革理論綜述
- 結(jié)構(gòu)力學(xué)中必須掌握的彎矩圖
- 國(guó)家二字碼大全--253個(gè)國(guó)家
- (完整版)螺旋鉆孔灌注樁施工工藝
- 公務(wù)接待制度公務(wù)接待審批單公務(wù)接待清單
評(píng)論
0/150
提交評(píng)論