經(jīng)典:第4章-程序的鏈接課件_第1頁
經(jīng)典:第4章-程序的鏈接課件_第2頁
經(jīng)典:第4章-程序的鏈接課件_第3頁
經(jīng)典:第4章-程序的鏈接課件_第4頁
經(jīng)典:第4章-程序的鏈接課件_第5頁
已閱讀5頁,還剩45頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

本學(xué)期考核方法本學(xué)期平時(shí)分占60%,期末考試占40%小測試共2次,每次6分,共20分作業(yè)加考勤基礎(chǔ)分20分,少一次扣4分,作業(yè)不交加考勤不到場超過5次將取消考試資格。實(shí)驗(yàn)課有兩個(gè)實(shí)驗(yàn),每次10分,共20各種加分,回答問題,實(shí)驗(yàn)提前做完等。期末考試形式為閉卷考試

第四章程序的鏈接

目標(biāo)文件格式

符號(hào)解析與重定位

共享庫與動(dòng)態(tài)鏈接可執(zhí)行文件的鏈接生成主要教學(xué)目標(biāo)使學(xué)生了解鏈接器是如何工作的,從而能夠養(yǎng)成良好的程序設(shè)計(jì)習(xí)慣,并增加程序調(diào)試能力。通過了解可執(zhí)行文件的存儲(chǔ)器映像來進(jìn)一步深入理解進(jìn)程的虛擬地址空間的概念。包括以下內(nèi)容鏈接和靜態(tài)鏈接概念三種目標(biāo)文件格式符號(hào)及符號(hào)表、符號(hào)解析使用靜態(tài)庫鏈接重定位信息及重定位過程可執(zhí)行文件的存儲(chǔ)器映像可執(zhí)行文件的加載共享(動(dòng)態(tài))庫鏈接程序的鏈接分以下三個(gè)部分介紹第一講:目標(biāo)文件格式程序的鏈接概述、鏈接的意義與過程ELF目標(biāo)文件、重定位目標(biāo)文件格式、可執(zhí)行目標(biāo)文件格式第二講:符號(hào)解析與重定位符號(hào)和符號(hào)表、符號(hào)解析與靜態(tài)庫的鏈接重定位信息第三講:動(dòng)態(tài)鏈接動(dòng)態(tài)鏈接的特性、程序加載時(shí)的動(dòng)態(tài)鏈接、程序運(yùn)行時(shí)的動(dòng)態(tài)鏈接一個(gè)典型程序的轉(zhuǎn)換處理過程1#include<stdio.h>23intmain()4{5printf("hello,world\n");6}經(jīng)典的“hello.c”C-源程序#include<sp><stdio.3510511099108117100101326011511610010511146h>\n\nint<sp>main()\n{1046210101051101163210997105110404110123\n<sp><sp><sp><sp>printf("hel10323232321121141051101161024034104101108lo,<sp>world\n");\n}10811144321191111141081009211034415910125hello.c的ASCII文本表示計(jì)算機(jī)能夠直接識(shí)別hello.c源程序嗎?不能,需要轉(zhuǎn)換為機(jī)器語言代碼!即:編譯、匯編等功能:輸出“hello,world”一個(gè)C語言程序舉例intbuf[2]={1,2};voidswap();intmain(){swap();return0;}main.cswap.cexternintbuf[];

int*bufp0=&buf[0];staticint*bufp1;voidswap(){inttemp;bufp1=&buf[1];temp=*bufp0;*bufp0=*bufp1;*bufp1=temp;}(1)預(yù)處理(cpp)。在高級語言源程序中插入所有用#include命令指定的文件和用#define聲明指定的宏。(2)編譯(cc1)。將預(yù)處理后的源程序文件編譯生成相應(yīng)的匯編語言程序。(3)匯編(as)。由匯編程序?qū)R編語言源程序文件轉(zhuǎn)換為可重定位目標(biāo)文件。(4)鏈接(ld)。由鏈接器將多個(gè)可重定位目標(biāo)文件及庫例程(如printf.o)鏈接起來,生成可執(zhí)行文件。可執(zhí)行文件的生成使用GCC編譯器編譯并鏈接生成可執(zhí)行程序P:unix>gcc-O2-g-opmain.cswap.cunix>./pLinker(ld)Translators(cpp,cc1,as)main.cmain.oTranslators(cpp,cc1,as)swap.cswap.opSourcefilesSeparatelycompiledrelocatableobjectfilesFullylinkedexecutableobjectfile(containscodeanddataforallfunctionsdefinedinmain.candswap.c)GCC編譯器的靜態(tài)鏈接過程引用符號(hào)的地址需要重定位main()main.oint*bufp0=&buf[0]swap()swap.oSystemcodeint

buf[2]={1,2}System

data可重定位目標(biāo)文件可執(zhí)行目標(biāo)文件.text.data.text.data.text.dataintbuf[2]={1,2}Headersmain()swap()0int

*bufp0=&buf[0]Moresystem

codeSystem

data.text.symtab.debug.dataint*bufp1.bssSystem

codestaticint*bufp1.bss雖然是swap的本地符號(hào),也需在.bss節(jié)重定位使用鏈接的優(yōu)點(diǎn)鏈接帶來的好處1:模塊化(1)一個(gè)程序可以分成很多源程序文件(2)可構(gòu)建公共函數(shù)庫,如數(shù)學(xué)庫,標(biāo)準(zhǔn)C庫等鏈接帶來的好處2:效率高(1)時(shí)間上,可分開編譯只需重新編譯修改的源程序文件,然后重新鏈接(2)空間上,無需包含共享庫所有代碼

源文件中無需包含共享庫函數(shù)的源碼,只要直接調(diào)用即可可執(zhí)行文件和運(yùn)行時(shí)的內(nèi)存中只需包含所調(diào)用函數(shù)的代碼而不需要包含整個(gè)共享庫鏈接操作的步驟Step1.符號(hào)解析(Symbolresolution)程序中有定義和引用的符號(hào)(包括變量和函數(shù)等)voidswap(){…}/*定義符號(hào)swap*/swap();/*引用符號(hào)swap*/int*xp=&x;/*定義符號(hào)xp,引用符號(hào)x*/編譯器將定義的符號(hào)存放在一個(gè)符號(hào)表(symboltable)中.符號(hào)表是一個(gè)結(jié)構(gòu)數(shù)組每個(gè)表項(xiàng)包含符號(hào)名、長度和位置等信息鏈接器將每個(gè)符號(hào)的引用都與一個(gè)確定的符號(hào)定義建立關(guān)聯(lián)Step2.重定位將多個(gè)代碼段與數(shù)據(jù)段分別合并為一個(gè)單獨(dú)的代碼段和數(shù)據(jù)段將.o文件中每個(gè)符號(hào)的相對位置重定位為可執(zhí)行文件中的絕對存儲(chǔ)位置將原來符號(hào)表中的位置信息修改為重定位后的位置信息三類目標(biāo)文件

可重定位目標(biāo)文件(.ofile)其代碼和數(shù)據(jù)可和其他可重定位文件合并為可執(zhí)行文件每個(gè).o文件由對應(yīng)的.c文件生成每個(gè).o文件代碼和數(shù)據(jù)地址都從0開始可執(zhí)行目標(biāo)文件(.afile)包含的代碼和數(shù)據(jù)可以被直接復(fù)制到內(nèi)存并被執(zhí)行代碼和數(shù)據(jù)地址為虛擬地址空間中的地址共享的目標(biāo)文件(.sofile)特殊的可重定位目標(biāo)文件,能在裝入或運(yùn)行時(shí)被裝入到內(nèi)存并自動(dòng)被鏈接Windows中稱其為DynamicLinkLibraries(DLLs)

00000000<add>: 0:55 push%ebp 1:89e5mov%esp,%ebp 3:83ec10sub$0x10,%esp 6:8b450cmov0xc(%ebp),%eax 9:8b5508mov0x8(%ebp),%edx c:8d0402lea(%edx,%eax,1),%eax f:8945fcmov%eax,-0x4(%ebp) 12:8b45fcmov-0x4(%ebp),%eax 15:c9leave 16:c3ret080483d4<add>:80483d4:55push%ebp80483d5:89e5mov%esp,%ebp80483d7:83ec10sub$0x10,%esp80483da:8b450cmov0xc(%ebp),%eax80483dd:8b5508mov0x8(%ebp),%edx80483e0:8d0402lea(%edx,%eax,1),%eax80483e3:8945fcmov%eax,-0x4(%ebp)80483e6:8b45fcmov-0x4(%ebp),%eax80483e9:c9leave80483ea:c3retobjdump-dtest.o

objdump-dtest

/*main.c*/intadd(int,int);intmain(){returnadd(20,13);}/*test.c*/intadd(inti,intj){intx=i+j;returnx;}ExecutableandLinkableFormat(ELF)兩種視圖鏈接視圖:Relocatableobjectfiles執(zhí)行視圖:Executableobjectfiles節(jié)(section)是ELF文件中具有相同特征的最小可處理單位

.text節(jié):代碼.data節(jié):數(shù)據(jù).rodata:只讀數(shù)據(jù).bss:未初始化數(shù)據(jù)由不同的段(segment)組成,描述節(jié)如何映射到存儲(chǔ)段中,可多個(gè)節(jié)映射到同一段,如:可合并.data節(jié)和.bss節(jié),并映射到一個(gè)可讀可寫數(shù)據(jù)段中

鏈接視圖執(zhí)行視圖可重定位目標(biāo)文件格式ELF頭占16字節(jié),包括字長、字節(jié)序(大端/小端)、文件類型(.o,exec,.so)、機(jī)器類型(如IA-32)、節(jié)頭表的偏移、節(jié)頭表的表項(xiàng)大小及表項(xiàng)個(gè)數(shù).text節(jié)編譯后的代碼部分.rodata節(jié)只讀數(shù)據(jù),如printf格式串、switch跳轉(zhuǎn)表等.data節(jié)已初始化的全局變量.bss節(jié)未初始化全局變量,僅是占位符,不占據(jù)任何實(shí)際磁盤空間。目標(biāo)文件格式區(qū)分初始化和非初始化是為了空間效率ELFheader.textsection.rodatasection.bsssection.symtabsection.rel.txtsection.rel.datasection.debugsectionSectionheadertable0.datasection.strtabsection.linesection可重定位目標(biāo)文件格式.symtab節(jié)存放函數(shù)和全局變量(符號(hào)表)信息,它不包括局部變量條目.rel.text節(jié).text節(jié)的重定位信息,用于重新修改代碼段的指令中的地址信息.rel.data節(jié).data節(jié)的重定位信息,用于對被模塊使用或定義的全局變量進(jìn)行重定位的信息.debug節(jié)調(diào)試用符號(hào)表(gcc-g)strtab節(jié)包含symtab和debug節(jié)中符號(hào)及節(jié)名Sectionheadertable(節(jié)頭表)每個(gè)節(jié)的節(jié)名、偏移和大小ELFheader.textsection.rodatasection.bsssection.symtabsection.rel.txtsection.rel.datasection.debugsectionSectionheadertable0.datasection.strtabsection.linesection可執(zhí)行目標(biāo)文件格式與.o文件稍有不同:ELF頭中字段e_entry給出執(zhí)行程序時(shí)第一條指令的地址,而在可重定位文件中,此字段為0多一個(gè).init節(jié),用于定義_init函數(shù),該函數(shù)用來進(jìn)行可執(zhí)行目標(biāo)文件開始執(zhí)行時(shí)的初始化工作少兩.rel節(jié)(無需重定位)多一個(gè)程序頭表,也稱段頭表(segmentheadertable),是一個(gè)結(jié)構(gòu)數(shù)組可執(zhí)行文件的存儲(chǔ)器映像0%esp(棧頂)brk0xC000000000x08048000KernelvirtualmemoryMemory-mappedregionforshared

librariesRun-timeheap(createdby

malloc)Userstack(created

at

runtime)Unused0Read/write

segment(.data,.bss)Read-onlysegment(.init,.text,.rodata)從可執(zhí)行文件裝入程序(段)頭表描述如何映射ELFheaderSegmentheadertable.textsection.datasection.bsssection.symtab.debug.rodatasection.line.initsection.strtab可執(zhí)行文件中的程序頭表typedefstruct{Elf32_Wordp_type;Elf32_Offp_offset;Elf32_Addrp_vaddr;Elf32_Addrp_paddr;Elf32_Wordp_filesz;Elf32_Wordp_memsz;Elf32_Wordp_flags;Elf32_Wordp_align;}Elf32_Phdr;程序頭表能夠描述可執(zhí)行文件中的節(jié)與虛擬空間中的存儲(chǔ)段之間的映射關(guān)系一個(gè)表項(xiàng)說明虛擬地址空間中一個(gè)連續(xù)的片段或一個(gè)特殊的節(jié)

以下是GNUREADELF顯示的某可執(zhí)行目標(biāo)文件的程序頭表信息可執(zhí)行文件中的程序頭表程序頭表中有8個(gè)表項(xiàng),其中有兩個(gè)是可裝入段(type=LOAD)對應(yīng)表項(xiàng)。第一可裝入段對應(yīng)第0x00000~0x004d3字節(jié)(包括ELF頭、程序頭表、.init、.text和.rodata節(jié)),映射到虛擬地址0x8048000開始長度為0x4d4字節(jié)的區(qū)域,按0x1000=212=4K字節(jié)對齊,具有只讀/執(zhí)行權(quán)限(Flg=RE),是只讀代碼段(read-onlycode)。第二可裝入段對應(yīng)第0x000f0c開始長度為0x108字節(jié)的.data節(jié),映射到虛擬地址0x8049f0c開始的長度為0x110字節(jié)的存儲(chǔ)區(qū)域,在0x110=272字節(jié)的存儲(chǔ)區(qū)中,前0x108=264字節(jié)用.data節(jié)內(nèi)容初始化,而后面272-264=8個(gè)字節(jié)對應(yīng).bss節(jié),初始化為0,該段按0x1000=4KB對齊,具有可讀可寫權(quán)限(Flg=RW),因此,它是一個(gè)可讀寫數(shù)據(jù)段(read/writedatasegment)。程序的鏈接分以下三個(gè)部分介紹第一講:目標(biāo)文件格式程序的鏈接概述、鏈接的意義與過程ELF目標(biāo)文件、重定位目標(biāo)文件格式、可執(zhí)行目標(biāo)文件格式第二講:符號(hào)解析與重定位符號(hào)和符號(hào)表、符號(hào)解析與靜態(tài)庫的鏈接重定位信息第三講:動(dòng)態(tài)鏈接動(dòng)態(tài)鏈接的特性、程序加載時(shí)的動(dòng)態(tài)鏈接、程序運(yùn)行時(shí)的動(dòng)態(tài)鏈接符號(hào)和符號(hào)解析

每個(gè)可重定位目標(biāo)模塊m都有一個(gè)符號(hào)表,它包含了在m中定義和引用的所有符號(hào)。有三種鏈接器符號(hào):Globalsymbols(模塊內(nèi)部定義的全局符號(hào))由模塊m定義并能被其他模塊引用的符號(hào)。例如,非staticC函數(shù)和非static的C全局變量(指不帶static的全局變量)

如,main.c中的全局變量名bufExternalsymbols(外部定義的全局符號(hào))由其他模塊定義并被模塊m引用的全局符號(hào)

如,main.c中的函數(shù)名swapLocalsymbols(本模塊的局部符號(hào))僅由模塊m定義和引用的本地符號(hào)。例如,在模塊m中定義的帶static的C函數(shù)和全局變量如,swap.c中的static變量名bufp1鏈接器局部符號(hào)不是指程序中的局部變量(分配在棧中的臨時(shí)性變量),鏈接器不關(guān)心這種局部變量符號(hào)和符號(hào)解析intbuf[2]={1,2};voidswap();intmain(){swap();return0;}main.cexternintbuf[];

int*bufp0=&buf[0];staticint*bufp1;voidswap(){inttemp;bufp1=&buf[1];temp=*bufp0;*bufp0=*bufp1;*bufp1=temp;}swap.cGlobalExternalExternalLocalGlobal局部變量Global目標(biāo)文件中的符號(hào)表符號(hào)表(symtab)中每個(gè)條目的結(jié)構(gòu)如下typedefstruct{intname;/*指向符號(hào)對應(yīng)字符串在strtab節(jié)中的偏移*/ intvalue;/*在對應(yīng)section中的偏移量,可執(zhí)行文件中是虛擬地址*/intsize;/*符號(hào)對應(yīng)目標(biāo)所占字節(jié)數(shù)*/chartype:4,/*符號(hào)對應(yīng)目標(biāo)的類型:數(shù)據(jù)、函數(shù)、源文件、節(jié)*/binding:4;/*符號(hào)對應(yīng)目標(biāo)是全局符號(hào)還是局部符號(hào)*/charreserved;charsection;/*符號(hào)對應(yīng)目標(biāo)所在的section,或其他情況*/}Elf_Symbol;其他情況:ABS表示不該被重定位;UND表示未定義;COM表示未初始化數(shù)據(jù)(.bss),此時(shí),value表示對齊要求,size給出最小大小目標(biāo)文件中的符號(hào)表main.o中的符號(hào)表中最后三個(gè)條目Num: value Size Type Bind Ot Ndx Name8: 0 8 Data Global 0 3 buf9: 0 33 Func Global 0 1 main10: 0 0 Notype Global 0 UND swapswap.o中的符號(hào)表中最后4個(gè)條目Num: value Size Type Bind Ot Ndx Name8: 0 4 Data Global0 3 bufp09: 0 0 NotypeGlobal0 UND buf10: 0 36 Func Global 0 1 swap11: 4 4 Data Local 0 COM bufp1buf是main.o中第3節(jié)(.data)偏移為0的符號(hào),是全局變量,占8B;main是第1節(jié)(.text)偏移為0的符號(hào),是全局函數(shù),占33B;swap是main.o中未定義的符號(hào),不知道類型和大小,全局的(在其他模塊定義)bufp1是未分配地址且未初始化的本地變量(ndx=COM),按4B對齊且占4B符號(hào)解析目的:將每個(gè)模塊中引用的符號(hào)與某個(gè)目標(biāo)模塊中的定義符號(hào)建立關(guān)聯(lián)。每個(gè)定義符號(hào)在代碼段或數(shù)據(jù)段中都被分配了存儲(chǔ)空間,將引用符號(hào)與對應(yīng)定義符號(hào)建立關(guān)聯(lián)后,就可在重定位時(shí)將引用符號(hào)的地址重定位為相關(guān)聯(lián)的定義符號(hào)的地址。本地符號(hào)在本模塊內(nèi)定義并引用,因此,其解析較簡單,只要與本模塊內(nèi)唯一的定義符號(hào)關(guān)聯(lián)即可。全局符號(hào)(外部定義的、內(nèi)部定義的)的解析涉及多個(gè)模塊,故較復(fù)雜

“符號(hào)的定義”其實(shí)質(zhì)是什么?是指符號(hào)被分配了虛擬地址空間。符號(hào)為函數(shù)名即指其代碼所在區(qū);符號(hào)為變量即指其占的靜態(tài)數(shù)據(jù)區(qū)。全局符號(hào)的符號(hào)解析全局符號(hào)的強(qiáng)/弱特性函數(shù)名和已初始化的全局變量名是強(qiáng)符號(hào)未初始化的全局變量名是弱符號(hào)

intfoo=5;p1(){……}intfoo;p2(){……}p1.cp2.cstrongweakstrongstrong以下符號(hào)哪些是強(qiáng)符號(hào)?哪些是弱符號(hào)?全局符號(hào)的符號(hào)解析intbuf[2]={1,2};voidswap();intmain(){swap();return0;}main.cexternintbuf[];

int*bufp0=&buf[0];staticint*bufp1;voidswap(){inttemp;bufp1=&buf[1];temp=*bufp0;*bufp0=*bufp1;*bufp1=temp;}swap.c強(qiáng)符號(hào)此處為引用弱符號(hào)本地符號(hào)強(qiáng)符號(hào)局部變量強(qiáng)符號(hào)以下符號(hào)哪些是強(qiáng)符號(hào)?哪些是弱符號(hào)?鏈接器對符號(hào)的解析規(guī)則多重定義符號(hào)的處理規(guī)則

Rule1:強(qiáng)符號(hào)不能多次定義強(qiáng)符號(hào)只能被定義一次,否則鏈接錯(cuò)誤

Rule2:若一個(gè)符號(hào)被定義為一次強(qiáng)符號(hào)和多次弱符號(hào),則按強(qiáng)定義為準(zhǔn)對弱符號(hào)的引用被解析為其強(qiáng)定義符號(hào)

Rule3:若有多個(gè)弱符號(hào)定義,則任選其中一個(gè)使用命令gcc–fno-common鏈接時(shí),會(huì)告訴鏈接器在遇到多個(gè)弱定義的全局符號(hào)時(shí)輸出一條警告信息。

多重定義符號(hào)的解析舉例intx=10;intp1(void);intmain(){x=p1();returnx;}main.cintx=20;intp1(){returnx;}p1.cmain只有一次強(qiáng)定義p1有一次強(qiáng)定義,一次弱定義x有兩次強(qiáng)定義,所以,鏈接器將輸出一條出錯(cuò)信息

以下程序會(huì)發(fā)生鏈接出錯(cuò)嗎?多重定義符號(hào)的解析舉例p1.cy一次強(qiáng)定義,一次弱定義z兩次弱定義p1一次強(qiáng)定義,一次弱定義main一次強(qiáng)定義#include<stdio.h>inty=100;intz;voidp1(void);intmain(){z=1000;p1();printf(“y=%d,z=%d\n”,y,z);return0;}main.cinty;intz;voidp1(){y=200;z=2000;}問題:打印結(jié)果是什么?y=200,z=2000以下程序會(huì)發(fā)生鏈接出錯(cuò)嗎?該例說明:在兩個(gè)不同模塊定義相同變量名,很可能發(fā)生意想不到的結(jié)果!多重定義符號(hào)的解析舉例p1.c該例說明:兩個(gè)重復(fù)定義的變量具有不同類型時(shí),更容易出現(xiàn)難以理解的結(jié)果!

main.c問題:打印結(jié)果是什么?d=0,x=1072693248

以下程序會(huì)發(fā)生鏈接出錯(cuò)嗎?1#include<stdio.h>2intd=100;3intx=200;4voidp1(void);5intmain()6{7p1();8printf(“d=%d,x=%d\n”,d,x);9return0;10}1doubled;23voidp1()4{5d=1.0;6}p1執(zhí)行后d和x處內(nèi)容是什么?FLD1FSTPl&d1.0:0011111111110…0B=3FF0000000000000H多重定義符號(hào)的解析舉例打印結(jié)果:d=0,x=1072693248Why?

1doubled;23voidp1()4{5d=1.0;6}…….1intd=100;2intx=200;3intmain()4{5p1();6printf(“d=%d,x=%d\n”,d,x);7return0;8}main.cp1.c理解該問題需要知道:機(jī)器級數(shù)據(jù)的表示與存儲(chǔ)鏈接器的符號(hào)解析規(guī)則……double型數(shù)1.0對應(yīng)的機(jī)器數(shù)3FF0000000000000H

低高IA-32是小端方式多重定義全局符號(hào)的問題盡量避免使用全局變量一定需要用的話,就按以下規(guī)則使用盡量使用本地變量(static)全局變量要賦初值外部全局變量要使用extern多重定義全局變量會(huì)造成一些意想不到的錯(cuò)誤,而且是默默發(fā)生的,編譯系統(tǒng)不會(huì)警告,并會(huì)在程序執(zhí)行很久后才能表現(xiàn)出來,且遠(yuǎn)離錯(cuò)誤引發(fā)處。特別是在一個(gè)具有幾百個(gè)模塊的大型軟件中,這類錯(cuò)誤很難修正。大部分程序員并不了解鏈接器如何工作,因而養(yǎng)成良好的編程習(xí)慣是非常重要的。頭文件(.h文件)的作用#include"global.h"intf(){returng+1;}c1.cglobal.h#ifdefINITIALIZEintg=23;staticintinit=1;#elseintg;staticintinit=0;#endif#include<stdio.h>#include"global.h"intmain(){if(!init)g=37;intt=f();printf("Callingfyields%d\n",t);return0;}c2.c預(yù)處理操作#include"global.h"intf(){returng+1;}c1.cglobal.h#ifdefINITIALIZE

intg=23;staticintinit=1;#else

intg;staticintinit=0;#endifintg=23;staticintinit=1;intf(){returng+1;}intg;staticintinit=0;intf(){returng+1;}定義INITIALIZE沒有定義INITIALIZE#include指示被執(zhí)行,插入.h文件的內(nèi)容到源文件中如何劃分模塊?許多函數(shù)無需自己寫,可使用共享庫函數(shù)Math,I/O,memorymanagement,stringmanipulation,etc.避免以下兩種極端做法將所有函數(shù)都放在一個(gè)源文件中修改一個(gè)函數(shù)需要對所有函數(shù)重新編譯時(shí)間和空間兩方面的效率都不高一個(gè)函數(shù)放在一個(gè)源文件中需要程序員顯式地進(jìn)行鏈接效率高,但模塊太多,故太繁瑣靜態(tài)共享庫靜態(tài)庫(.aarchivefiles)將所有相關(guān)的目標(biāo)模塊打包為一個(gè)單獨(dú)的文件,稱為靜態(tài)庫文件,也稱為存檔文件(archive)增強(qiáng)鏈接器功能,使其能通過查找一個(gè)或多個(gè)庫文件中的符號(hào)來解析符號(hào)在構(gòu)建可執(zhí)行文件時(shí)只需指定庫文件名,鏈接器會(huì)自動(dòng)到庫中尋找那些應(yīng)用程序用到的目標(biāo)模塊,并且只把用到的模塊從庫中拷貝出來在gcc命令行中無需明顯指定C標(biāo)準(zhǔn)庫libc.a(默認(rèn)庫)靜態(tài)庫的創(chuàng)建Translatoratoi.catoi.oTranslatorprintf.cprintf.olibc.aArchiver(ar)...Translatorrandom.crandom.ounix>arrslibc.a\atoi.oprintf.o…random.oCstandardlibraryArchiver(歸檔器)允許增量更新,只要重新編譯需修改的源碼并將其.o文件替換到靜態(tài)庫中。自定義一個(gè)靜態(tài)庫文件#include<stdio.h>voidmyfunc1(){printf("%s","Thisismyfunc1!\n");}#include<stdio.h>voidmyfunc2(){printf("%s","Thisismyfunc2\n");}gcc–cmyproc1.cmyproc2.carrcsmylib.amyproc1.omyproc2.omyproc1.cmyproc2.c鏈接器中符號(hào)解析的全過程

voidmyfunc1(viod);intmain(){myfunc1();return0;}main.c調(diào)用關(guān)系:main→myfunc1→printfgcc–cmain.cgcc–static–omyprocmain.o./mylib.a開始E、U、D為空,首先掃描main.o,把它加入E,同時(shí)把myfun1加入U(xiǎn),main加入D。接著掃描到mylib.a,將U中所有符號(hào)(本例中為myfunc1)與mylib.a中所有目標(biāo)模塊(myproc1.o和myproc2.o)依次匹配,發(fā)現(xiàn)在myproc1.o中定義了myfunc1,故myproc1.o加入E,myfunc1從U轉(zhuǎn)移到D。在myproc1.o中發(fā)現(xiàn)還有未解析符號(hào)printf,將其加到U。不斷在mylib.a的各模塊上進(jìn)行迭代以匹配U中的符號(hào),直到U、D都不再變化。此時(shí)U中只有一個(gè)未解析符號(hào)printf,而D中有main和myfunc1。因?yàn)槟Kmyproc2.o沒有被加入E中,因而它被丟棄。E

將被合并以組成可執(zhí)行文件的所有目標(biāo)文件集合U當(dāng)前所有未解析的引用符號(hào)的集合D當(dāng)前所有定義符號(hào)的集合

接著,掃描默認(rèn)的庫文件libc.a,發(fā)現(xiàn)其目標(biāo)模塊printf.o定義了printf,于是printf也從U移到D,并將printf.o加入E,同時(shí)把它定義的所有符號(hào)加入D,而所有未解析符號(hào)加入U(xiǎn)。處理完libc.a時(shí),U一定是空的。

libc.a無需明顯指出!鏈接器中符號(hào)解析的全過程

main.cvoidmyfunc1(viod);intmain(){myfunc1();return0;}

gcc–cmain.cgcc–static–omyprocmain.o./mylib.a問題:若命令為:gcc–static–omyproc./mylib.amain.o,結(jié)果怎樣?main.o中的myfunc1不能被解析,故出現(xiàn)鏈接錯(cuò)誤!鏈接順序問題假設(shè)調(diào)用關(guān)系如下:func.o→libx.a和liby.a中的函數(shù)

libx.a→libz.a中的函數(shù)

libx.a和liby.a之間、liby.a和libz.a相互獨(dú)立則以下幾個(gè)命令行都是可行的:gcc-static–omyfuncfunc.olibx.aliby.alibz.agcc-static–omyfuncfunc.oliby.alibx.alibz.agcc-static–omyfuncfunc.olibx.alibz.aliby.a假設(shè)調(diào)用關(guān)系如下:func.o→libx.a和liby.a中的函數(shù)

libx.a→liby.a同時(shí)liby.a→libx.a則以下命令行可行:gcc-static–omyfuncfunc.olibx.aliby.alibx.a重定位符號(hào)解析完成后,可進(jìn)行重定位工作,分兩步對節(jié)和定義符號(hào)進(jìn)行重定位將集合E的所有目標(biāo)模塊中相同的節(jié)合并成新節(jié),并將運(yùn)行時(shí)的虛擬地址賦給每個(gè)新節(jié)中所有的定義符號(hào)。例如,所有.text節(jié)合并作為可執(zhí)行文件中的.text節(jié),并為每個(gè).text節(jié)確定在新.text節(jié)中的絕對地址,從而為其中定義的函數(shù)確定首地址(含有多個(gè)函數(shù)時(shí)),進(jìn)而確定每條指令的地址。完成這一步后,每條指令和每個(gè)全局變量都可確定地址。對節(jié)中的引用符號(hào)進(jìn)行重定位修改.text節(jié)和.data節(jié)中對每個(gè)符號(hào)的引用(地址)。需要用到在.rel_data和.rel_text節(jié)中保存的重定位信息。重定位信息匯編器遇到對位置未知的目標(biāo)引用時(shí),生成一個(gè)重定位條目數(shù)據(jù)引用的重定位條目在.rel_data節(jié)中指令中引用的重定位條目在.rel_text節(jié)中ELF中重定位條目格式如下:有兩種最基本的重定位類型R_386_PC32:使用32位PC相對地址的引用,重定位指令R_386_32:使用32位絕對地址重定位數(shù)據(jù)typedefstruct{ intoffset;/*需重定位的引用的節(jié)偏移*/ intsymbol:24,

/*需重定位的引用所指向的符號(hào)*/t

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論