計(jì)算機(jī)系統(tǒng)導(dǎo)論 課件 第7、8章 程序的鏈接、程序的加載和執(zhí)行_第1頁(yè)
計(jì)算機(jī)系統(tǒng)導(dǎo)論 課件 第7、8章 程序的鏈接、程序的加載和執(zhí)行_第2頁(yè)
計(jì)算機(jī)系統(tǒng)導(dǎo)論 課件 第7、8章 程序的鏈接、程序的加載和執(zhí)行_第3頁(yè)
計(jì)算機(jī)系統(tǒng)導(dǎo)論 課件 第7、8章 程序的鏈接、程序的加載和執(zhí)行_第4頁(yè)
計(jì)算機(jī)系統(tǒng)導(dǎo)論 課件 第7、8章 程序的鏈接、程序的加載和執(zhí)行_第5頁(yè)
已閱讀5頁(yè),還剩144頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第七章程序的鏈接

目標(biāo)文件格式

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

共享庫(kù)與動(dòng)態(tài)鏈接可執(zhí)行文件的鏈接生成主要教學(xué)目標(biāo)使學(xué)生了解鏈接器是如何工作的,從而能夠養(yǎng)成良好的程序設(shè)計(jì)習(xí)慣,并增強(qiáng)程序調(diào)試能力。通過(guò)了解可執(zhí)行文件的存儲(chǔ)器映像來(lái)進(jìn)一步深入理解進(jìn)程的虛擬地址空間的概念。包括以下內(nèi)容鏈接和靜態(tài)鏈接概念三種目標(biāo)文件格式符號(hào)及符號(hào)表、符號(hào)解析使用靜態(tài)庫(kù)鏈接重定位信息及重定位過(guò)程可執(zhí)行文件的存儲(chǔ)器映像可執(zhí)行文件的加載共享(動(dòng)態(tài))庫(kù)鏈接程序的鏈接分以下三個(gè)部分介紹第一講:目標(biāo)文件格式程序的鏈接概述、鏈接的意義與過(guò)程ELF目標(biāo)文件、重定位目標(biāo)文件格式、可執(zhí)行目標(biāo)文件格式第二講:符號(hào)解析與重定位符號(hào)和符號(hào)表、符號(hào)解析與靜態(tài)庫(kù)的鏈接重定位信息、重定位過(guò)程可執(zhí)行文件的加載第三講:動(dòng)態(tài)鏈接動(dòng)態(tài)鏈接的特性、程序加載時(shí)的動(dòng)態(tài)鏈接、程序運(yùn)行時(shí)的動(dòng)態(tài)鏈接、動(dòng)態(tài)鏈接舉例一個(gè)典型程序的轉(zhuǎn)換處理過(guò)程#include<stdio.h>intmain(){printf("hello,world\n");}經(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文本表示功能:輸出“hello,world”預(yù)處理(cpp)編譯(cc1)匯編(as)鏈接(ld)printf.o計(jì)算機(jī)不能直接執(zhí)行hello.c!hello.c源程序(文本)hello.i源程序(文本)hello.s匯編語(yǔ)言程序(文本)hello.o可重定位目標(biāo)程序(二進(jìn)制)hello可執(zhí)行目標(biāo)程序(二進(jìn)制)原始的鏈接概念早在高級(jí)編程語(yǔ)言出現(xiàn)之前就已存在最早程序員用機(jī)器語(yǔ)言編寫程序,并記錄在紙帶或卡片上鏈接器的由來(lái)穿孔表示0,未穿孔為10:010101101:001001012:……3:……4:……5:011001116:……假設(shè):0010-jmp若在第5條指令前加入指令,則程序員需重新計(jì)算jmp指令的目標(biāo)地址(重定位),然后重新打孔。太原始了,無(wú)法忍受,咋辦?用符號(hào)表示而不用0/1表示!用符號(hào)表示跳轉(zhuǎn)位置和變量位置,是否簡(jiǎn)化了問(wèn)題?匯編語(yǔ)言出現(xiàn)用助記符表示操作碼用符號(hào)表示位置用助記符表示寄存器…..更高級(jí)編程語(yǔ)言出現(xiàn)程序越來(lái)越復(fù)雜,需多人開(kāi)發(fā)不同的程序模塊子程序(函數(shù))起始地址和變量起始地址是符號(hào)定義(definition)調(diào)用子程序(函數(shù)或過(guò)程)和使用變量即是符號(hào)的引用(reference)一個(gè)模塊定義的符號(hào)可以被另一個(gè)模塊引用最終須鏈接(即合并),合并時(shí)須在符號(hào)引用處填入定義處的地址如上例,先確定L0的地址,再在jmp指令中填入L0的地址鏈接器的由來(lái)0:010101101:001001012:……3:……4:……5:011001116:……addBjmpL0

……

…………L0:subC……使用鏈接的好處鏈接帶來(lái)的好處1:模塊化(1)一個(gè)程序可以分成很多源程序文件(2)可構(gòu)建公共函數(shù)庫(kù),如數(shù)學(xué)庫(kù),標(biāo)準(zhǔn)I/O庫(kù)等鏈接帶來(lái)的好處2:效率高(1)時(shí)間上,可分開(kāi)編譯只需重新編譯被修改的源程序文件,然后重新鏈接(2)空間上,無(wú)需包含共享庫(kù)所有代碼

源文件中無(wú)需包含共享庫(kù)函數(shù)的源碼,只要直接調(diào)用即可可執(zhí)行文件和運(yùn)行時(shí)的內(nèi)存中只需包含所調(diào)用函數(shù)的代碼而不需要包含整個(gè)共享庫(kù)一個(gè)C語(yǔ)言程序舉例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;}你能說(shuō)出哪些是符號(hào)定義?哪些是符號(hào)的引用?局部變量temp分配在棧中,不會(huì)在過(guò)程外被引用,因此不是符號(hào)定義可執(zhí)行文件的生成使用GCC編譯器編譯并鏈接生成可執(zhí)行程序P:$gcc-O2-g-opmain.cswap.c$./p鏈接(ld)程序轉(zhuǎn)換(cpp,cc1,as)main.cmain.o程序轉(zhuǎn)換(cpp,cc1,as)swap.cswap.op源程序文件分別轉(zhuǎn)換(預(yù)處理、編譯、匯編)為可重定位目標(biāo)文件完全可執(zhí)行的目標(biāo)文件GCC編譯器的靜態(tài)鏈接過(guò)程-O2:2級(jí)優(yōu)化-g:生成調(diào)試信息-o:目標(biāo)文件名鏈接過(guò)程的本質(zhì)main()main.oint*bufp0=&buf[0]swap()swap.o系統(tǒng)代碼int

buf[2]={1,2}系統(tǒng)數(shù)據(jù)可重定位目標(biāo)文件可執(zhí)行目標(biāo)文件.text.data.text.data.text.dataintbuf[2]={1,2}Headersmain()swap()0int

*bufp0=&buf[0]更多系統(tǒng)代碼系統(tǒng)數(shù)據(jù).text.symtab.debug.dataint*bufp1.bss系統(tǒng)代碼staticint*bufp1.bss鏈接本質(zhì):合并相同的“節(jié)”目標(biāo)文件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;}可執(zhí)行文件的存儲(chǔ)器映像0%esp(棧頂)brk0xC000000000x08048000內(nèi)核虛存區(qū)共享庫(kù)區(qū)域堆(heap)(由malloc動(dòng)態(tài)生成)用戶棧(Userstack)動(dòng)態(tài)生成未使用0讀寫數(shù)據(jù)段(.data,.bss)只讀代碼段(.init,.text,.rodata)從可執(zhí)行文件裝入程序(段)頭表描述如何映射ELF頭程序(段)頭表.text節(jié).data節(jié).bss節(jié).symtab節(jié).debug節(jié).rodata節(jié).line節(jié).init節(jié).strtab節(jié)1GB鏈接操作的步驟1)確定標(biāo)號(hào)引用關(guān)系(符號(hào)解析)2)合并相關(guān).o文件3)確定每個(gè)標(biāo)號(hào)的地址4)在指令中填入新地址代碼數(shù)據(jù)P0:addBjmpL0

……

call

P1……L0:subC……P1:addA

……

…………

subB……B:

10C:20A:

30P1:addA

……

…………

subB……A:

30P1.oP0:addBjmpL0

……

call

P1……L0:subC……B:

10C:20P0.o重定位合并后的地址是指文件中的地址還是虛擬地址空間中的地址?可執(zhí)行文件的存儲(chǔ)器映像0%esp(棧頂)brk0xC000000000x08048000內(nèi)核虛存區(qū)共享庫(kù)區(qū)域堆(heap)(由malloc動(dòng)態(tài)生成)用戶棧(Userstack)動(dòng)態(tài)生成未使用0讀寫數(shù)據(jù)段(.data,.bss)只讀代碼段(.init,.text,.rodata)從可執(zhí)行文件裝入程序(段)頭表描述如何映射ELF頭程序(段)頭表.text節(jié).data節(jié).bss節(jié).symtab節(jié).debug節(jié).rodata節(jié).line節(jié).init節(jié).strtab節(jié)1GB鏈接操作的步驟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)名、長(zhǎng)度和位置等信息鏈接器將每個(gè)符號(hào)的引用都與一個(gè)確定的符號(hào)定義建立關(guān)聯(lián)Step2.重定位將多個(gè)代碼段與數(shù)據(jù)段分別合并為一個(gè)單獨(dú)的代碼段和數(shù)據(jù)段計(jì)算每個(gè)定義的符號(hào)在虛擬地址空間中的絕對(duì)地址將可執(zhí)行文件中符號(hào)引用處的地址修改為重定位后的地址信息addBjmpL0

……

…………L0:subC……三類目標(biāo)文件

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

目標(biāo)文件的格式目標(biāo)代碼(ObjectCode)指編譯器和匯編器處理源代碼后所生成的機(jī)器語(yǔ)言目標(biāo)代碼目標(biāo)文件(ObjectFile)指包含目標(biāo)代碼的文件最早的目標(biāo)文件格式是自有格式,非標(biāo)準(zhǔn)的標(biāo)準(zhǔn)的幾種目標(biāo)文件格式DOS操作系統(tǒng)(最簡(jiǎn)單):COM格式,文件中僅包含代碼和數(shù)據(jù),且被加載到固定位置SystemVUNIX早期版本:COFF格式,文件中不僅包含代碼和數(shù)據(jù),還包含重定位信息、調(diào)試信息、符號(hào)表等其他信息,由一組嚴(yán)格定義的數(shù)據(jù)結(jié)構(gòu)序列組成Windows:PE格式(COFF的變種),稱為可移植可執(zhí)行(PortableExecutable,簡(jiǎn)稱PE)Linux等類UNIX:ELF格式(COFF的變種),稱為可執(zhí)行可鏈接(ExecutableandLinkableFormat,簡(jiǎn)稱ELF)ExecutableandLinkableFormat(ELF)兩種視圖鏈接視圖(被鏈接):Relocatableobjectfiles執(zhí)行視圖(被執(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)文件可被鏈接(合并)生成可執(zhí)行文件或共享目標(biāo)文件靜態(tài)鏈接庫(kù)文件由若干個(gè)可重定位目標(biāo)文件組成包含代碼、數(shù)據(jù)(已初始化.data和未初始化.bss)包含重定位信息(指出哪些符號(hào)引用處需要重定位)文件擴(kuò)展名為.o(相當(dāng)于Windows中的.obj文件)intx=100;inty;voidprn(intn){

printf(“%d\n”,n);}voidmain(){

staticinta=1;staticintb;inti=200,j;prn(x+a+i);}ELF的鏈接視圖.text節(jié).data節(jié).bss節(jié)為了進(jìn)行鏈接,還需要其他許多信息,如符號(hào)表、重定位信息等許多其他的節(jié)(Section)可重定位目標(biāo)文件格式ELF頭定義了ELF魔數(shù)、版本、小端/大端、操作系統(tǒng)平臺(tái)、目標(biāo)文件的類型、機(jī)器結(jié)構(gòu)類型、節(jié)頭表的起始位置和長(zhǎng)度等.text節(jié)編譯后的代碼部分.rodata節(jié)只讀數(shù)據(jù),如printf格式串、switch跳轉(zhuǎn)表等.data節(jié)已初始化的全局變量.bss節(jié)未初始化全局變量,僅是占位符,不占據(jù)任何實(shí)際磁盤空間。區(qū)分初始化和非初始化是為了空間效率ELF頭.text節(jié).rodata節(jié).bss節(jié).symtab節(jié).rel.txt節(jié).rel.data節(jié).debug節(jié)Sectionheadertable(節(jié)頭表)0.data節(jié).strtab節(jié).line節(jié)SKIPvoidmain(){ intx; printf(“x=%d\n”,x);}字符串“x=%d\n”屬于只讀數(shù)據(jù)BACKswitch-case語(yǔ)句舉例intsw_test(inta,intb,intc){intresult;switch(a){case15:c=b&0x0f;case10:result=c+50;break;case12:case17:result=b+50;break;case14:result=bbreak;default:result=a;}returnresult;}

跳轉(zhuǎn)表在目標(biāo)文件的只讀節(jié)中,按4字節(jié)邊界對(duì)齊。R[eax]=a-10=iif(a-10)>7轉(zhuǎn)L5轉(zhuǎn).L8+4*i

處的地址1011121314151617a=BACKELF頭(ELFHeader)ELF頭位于ELF文件開(kāi)始,包含文件結(jié)構(gòu)說(shuō)明信息。分32位系統(tǒng)對(duì)應(yīng)結(jié)構(gòu)和64位系統(tǒng)對(duì)應(yīng)結(jié)構(gòu)(32位版本、64位版本)以下是32位系統(tǒng)對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)#defineEI_NIDENT16typedefstruct{unsignedchar e_ident[EI_NIDENT];Elf32_Half e_type;Elf32_Half e_machine;Elf32_Word e_version;Elf32_Addr e_entry;Elf32_Off e_phoff;Elf32_Off e_shoff;Elf32_Word e_flags;Elf32_Half e_ehsize;Elf32_Half e_phentsize;Elf32_Half e_phnum;Elf32_Half e_shentsize;Elf32_Half e_shnum;Elf32_Half e_shstrndx;}Elf32_Ehdr;定義了ELF魔數(shù)、版本、小端/大端、操作系統(tǒng)平臺(tái)、目標(biāo)文件的類型、機(jī)器結(jié)構(gòu)類型、程序執(zhí)行的入口地址、程序頭表(段頭表)的起始位置和長(zhǎng)度、節(jié)頭表的起始位置和長(zhǎng)度等魔數(shù):文件開(kāi)頭幾個(gè)字節(jié)通常用來(lái)確定文件的類型或格式a.out的魔數(shù):01H07HPE格式魔數(shù):4DH5AH加載或讀取文件時(shí),可用魔數(shù)確認(rèn)文件類型是否正確ELF頭信息舉例$readelf-hmain.o

ELFHeader:Magic:7f454c46010101000000000000000000Class:ELF32Data:2'scomplement,littleendianVersion:1(current)OS/ABI:UNIX-SystemVABIVersion:0Type:REL(Relocatablefile)Machine:Intel80386Version:0x1Entrypointaddress:0x0Startofprogramheaders:0(bytesintofile)Startofsectionheaders:516(bytesintofile)Flags:0x0Sizeofthisheader:52(bytes)Sizeofprogramheaders:0(bytes)Numberofprogramheaders:0Sizeofsectionheaders:40(bytes)Numberofsectionheaders:15Sectionheaderstringtableindex:12ELF頭.text節(jié).rodata節(jié).bss節(jié).symtab節(jié).rel.txt節(jié).rel.data節(jié).debug節(jié)Sectionheader(節(jié)頭表)0.data節(jié).strtab節(jié).line節(jié)可重定位目標(biāo)文件的ELF頭沒(méi)有程序頭表15x40B.strtab在節(jié)頭表中的索引ELF文件的魔數(shù)ABI:應(yīng)用程序二進(jìn)制接口節(jié)頭表(SectionHeaderTable)除ELF頭之外,節(jié)頭表是ELF可重定位目標(biāo)文件中最重要的部分內(nèi)容描述每個(gè)節(jié)的節(jié)名、在文件中的偏移、大小、訪問(wèn)屬性、對(duì)齊方式等以下是32位系統(tǒng)對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)(每個(gè)表項(xiàng)占40B)typedefstruct{Elf32_Word sh_name;Elf32_Word sh_type;Elf32_Word sh_flags;Elf32_Addr sh_addr;Elf32_Off sh_offset;Elf32_Word sh_size;Elf32_Word sh_link;Elf32_Word sh_info;Elf32_Word sh_addralign;Elf32_Word sh_entsize;

}Elf32_Shdr;節(jié)名字符串在.strtab中的偏移節(jié)類型:無(wú)效/代碼或數(shù)據(jù)/符號(hào)/字符串/…節(jié)標(biāo)志:該節(jié)在虛擬空間中的訪問(wèn)屬性虛擬地址:若可被加載,則對(duì)應(yīng)虛擬地址在文件中的偏移地址,對(duì).bss節(jié)而言則無(wú)意義節(jié)在文件中所占的長(zhǎng)度sh_link和sh_info用于與鏈接相關(guān)的節(jié)(如.rel.text節(jié)、.rel.data節(jié)、.symtab節(jié)等)節(jié)的對(duì)齊要求節(jié)中每個(gè)表項(xiàng)的長(zhǎng)度,0表示無(wú)固定長(zhǎng)度表項(xiàng)節(jié)頭表信息舉例$readelf-Stest.o

Thereare11sectionheaders,startingatoffset0x120:

SectionHeaders:

[Nr]Name

Type

Addr

Off

Size

ESFlgLkInfAl

[0]

NULL

0000000000000000000000

0

0

0

[1].text

PROGBITS

0000000000003400005b00

AX

0

0

4

[2].rel.text

REL

0000000000049800002808

9

1

4

[3].data

PROGBITS

0000000000009000000c00

WA

0

0

4

[4].bss

NOBITS

0000000000009c00000c00

WA

0

0

4

[5].rodata

PROGBITS

0000000000009c00000400

A

0

0

1

[6].comment

PROGBITS

000000000000a000002e00

0

0

1

[7].note.GNU-stack

PROGBITS

000000000000ce00000000

0

0

1

[8].shstrtab

STRTAB

000000000000ce00005100

0

0

1

[9].symtab

SYMTAB

000000000002d800012010

1013

4

[10].strtab

STRTAB

000000000003f800009e00

0

0

1KeytoFlags:

W(write),A(alloc),X(execute),M(merge),S(strings)

I(info),L(linkorder),G(group),x(unknown)

O(extraOSprocessingrequired)o(OSspecific),p(processorspecific)ELF頭.text節(jié).rodata節(jié).bss節(jié).symtab節(jié).rel.txt節(jié).rel.data節(jié).debug節(jié)Sectionheader(節(jié)頭表)0.data節(jié).strtab節(jié).line節(jié)可重定位目標(biāo)文件中,每個(gè)可裝入節(jié)的起始地址總是0節(jié)頭表信息舉例$readelf-Stest.o

Thereare11sectionheaders,startingatoffset0x120:

SectionHeaders:

[Nr]Name

Off

Size

ESFlgLkInfAl

[0]

00000000000000

0

0

0

[1].text

00003400005b00

AX

0

0

4

[2].rel.text

00049800002808

9

1

4

[3].data

00009000000c00

WA

0

0

4

[4].bss

00009c00000c00

WA

0

0

4

[5].rodata

00009c00000400

A

0

0

1

[6].comment

0000a000002e00

0

0

1

[7].note.GNU-stack

0000ce00000000

0

0

1

[8].shstrtab

0000ce00005100

0

0

1

[9].symtab

0002d800012010

1013

4

[10].strtab

0003f800009e00

0

0

1KeytoFlags:

W(write),A(alloc),X(execute),M(merge),S(strings)

I(info),L(linkorder),G(group),x(unknown)

………..有4個(gè)節(jié)分配(A)空間.text:可執(zhí)行.data和.bss:可讀寫.rodata:只讀ELF頭e_shoff=0x120.text.data.rodata.comment.shstrtab節(jié)頭表.symtab.strtab.rel.text00000000003400009000009c0000a00000ce.bss0001200002d80003f800049800011f5b0c040c2e511b81209e2800008f000496可重定位目標(biāo)文件test.o的結(jié)構(gòu)shstrtab:節(jié)名表strtab:符號(hào)名表symtab:符號(hào)表執(zhí)行視圖—可執(zhí)行目標(biāo)文件包含代碼、數(shù)據(jù)(已初始化.data和未初始化.bss)定義的所有變量和函數(shù)已有確定地址(虛擬地址空間中的地址)符號(hào)引用處已被重定位,以指向所引用的定義符號(hào)沒(méi)有文件擴(kuò)展名或默認(rèn)為a.out(相當(dāng)于Windows中的.exe文件)可被CPU直接執(zhí)行,指令地址和指令給出的操作數(shù)地址都是虛擬地址為了能執(zhí)行,還需將具相同訪問(wèn)屬性的節(jié)合并成段(Segment),并說(shuō)明每個(gè)段的屬性,如:在可執(zhí)行文件中的位移、大小、在虛擬空間中的位置、對(duì)齊方式、訪問(wèn)屬性等intx=100;inty;voidprn(intn){

printf(“%d\n”,n);}voidmain(){

staticinta=1;staticintb;inti=200,j;prn(x+a+i);}ELF的執(zhí)行視圖.text節(jié).data節(jié).bss節(jié)程序頭表用來(lái)說(shuō)明段信息,也稱段頭表可執(zhí)行目標(biāo)文件格式ELF頭.text節(jié).rodata節(jié).bss節(jié).symtab節(jié)程序頭表.init節(jié).debug節(jié)Sectionheadertable(節(jié)頭表).data節(jié).strtab節(jié).line節(jié)只讀(代碼)段讀寫(數(shù)據(jù))段無(wú)需裝入到存儲(chǔ)空間的信息與可重定位文件稍有不同:ELF頭中字段e_entry給出執(zhí)行程序時(shí)第一條指令的地址,而在可重定位文件中,此字段為0多一個(gè)程序頭表,也稱段頭表(segmentheadertable),是一個(gè)結(jié)構(gòu)數(shù)組多一個(gè).init節(jié),用于定義_init函數(shù),該函數(shù)用來(lái)進(jìn)行可執(zhí)行目標(biāo)文件開(kāi)始執(zhí)行時(shí)的初始化工作少兩個(gè).rel節(jié)(無(wú)需重定位)ELF頭信息舉例$readelf-hmain

ELFHeader:Magic:7f454c46010101000000000000000000Class:ELF32Data:2'scomplement,littleendianVersion:1(current)OS/ABI:UNIX-SystemVABIVersion:0Type:EXEC(Executablefile)Machine:Intel80386Version:0x1Entrypointaddress:x8048580Startofprogramheaders:52(bytesintofile)Startofsectionheaders:3232(bytesintofile)Flags:0x0Sizeofthisheader:52(bytes)Sizeofprogramheaders:32(bytes)Numberofprogramheaders:8Sizeofsectionheaders:40(bytes)Numberofsectionheaders:29Sectionheaderstringtableindex:26

可執(zhí)行目標(biāo)文件的ELF頭ELF頭.text節(jié).rodata節(jié).bss節(jié).symtab節(jié)程序頭表.init節(jié).debug節(jié)Sectionheadertable(節(jié)頭表).data節(jié).strtab節(jié).line節(jié)29x40B8x32Bi386SystemVABI規(guī)定的存儲(chǔ)器映像0%esp(棧頂)brk0xC000000000x08048000內(nèi)核虛存區(qū)共享庫(kù)區(qū)域堆(heap)(由malloc動(dòng)態(tài)生成)用戶棧(Userstack)動(dòng)態(tài)生成未使用0讀寫數(shù)據(jù)段(.data,.bss)只讀代碼段(.init,.text,.rodata)從可執(zhí)行文件裝入程序頭表描述如何映射ELF頭程序(段)頭表.text節(jié).data節(jié).bss節(jié).symtab節(jié).debug節(jié).rodata節(jié).line節(jié).init節(jié).strtab節(jié)可執(zhí)行文件與虛擬地址空間間的存儲(chǔ)器映像(memorymapping)由ABI規(guī)范定義可執(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)(32B)說(shuō)明虛擬地址空間中一個(gè)連續(xù)的段或一個(gè)特殊的節(jié)

以下是某可執(zhí)行目標(biāo)文件程序頭表信息有8個(gè)表項(xiàng),其中兩個(gè)為可裝入段(即Type=LOAD)$readelf–lmain可執(zhí)行文件中的程序頭表第一可裝入段:第0x00000~0x004d3字節(jié)(包括ELF頭、程序頭表、.init、.text和.rodata節(jié)),映射到虛擬地址0x8048000開(kāi)始長(zhǎng)度為0x4d4字節(jié)的區(qū)域,按0x1000=212=4KB對(duì)齊,具有只讀/執(zhí)行權(quán)限(Flg=RE),是只讀代碼段。第二可裝入段:第0x000f0c開(kāi)始長(zhǎng)度為0x108字節(jié)的.data節(jié),映射到虛擬地址0x8049f0c開(kāi)始長(zhǎng)度為0x110字節(jié)的存儲(chǔ)區(qū)域,在0x110=272B存儲(chǔ)區(qū)中,前0x108=264B用.data節(jié)內(nèi)容初始化,后面272-264=8B對(duì)應(yīng).bss節(jié),初始化為0,按0x1000=4KB對(duì)齊,具有可讀可寫權(quán)限(Flg=RW),是可讀寫數(shù)據(jù)段。SKIP可執(zhí)行文件的存儲(chǔ)器映像00000%esp(棧頂)brk0xC000000000x08048000內(nèi)核虛存區(qū)共享庫(kù)區(qū)域堆(heap)(由malloc動(dòng)態(tài)生成)用戶棧(Userstack)動(dòng)態(tài)生成未使用0讀寫數(shù)據(jù)段(.data,.bss)只讀代碼段(.init,.text,.rodata)從可執(zhí)行文件裝入程序(段)頭表描述如何映射ELF頭程序(段)頭表.text節(jié).data節(jié).bss節(jié).symtab節(jié).debug節(jié).rodata節(jié).line節(jié).init節(jié).strtab節(jié)BACK1GB004d300f0c010140101c0x08049000節(jié)頭表要求思考的問(wèn)題你會(huì)實(shí)現(xiàn)自己的objdump嗎?readelf(-h/-S/-l)呢?00000%esp(棧頂)brk0xC000000000x08048000內(nèi)核虛存區(qū)共享庫(kù)區(qū)域堆(heap)(由malloc動(dòng)態(tài)生成)用戶棧(Userstack)動(dòng)態(tài)生成未使用0讀寫數(shù)據(jù)段(.data,.bss)只讀代碼段(.init,.text,.rodata)從可執(zhí)行文件裝入程序(段)頭表描述如何映射ELF頭程序(段)頭表.text節(jié).data節(jié).bss節(jié).symtab節(jié).debug節(jié).rodata節(jié).line節(jié).init節(jié).strtab節(jié)1GB004d300f0c010140101c0x08049000節(jié)頭表程序的鏈接分以下三個(gè)部分介紹第一講:目標(biāo)文件格式程序的鏈接概述、鏈接的意義與過(guò)程ELF目標(biāo)文件、重定位目標(biāo)文件格式、可執(zhí)行目標(biāo)文件格式第二講:符號(hào)解析與重定位符號(hào)和符號(hào)表、符號(hào)解析與靜態(tài)庫(kù)的鏈接重定位信息、重定位過(guò)程可執(zhí)行文件的加載第三講:動(dòng)態(tài)鏈接動(dòng)態(tài)鏈接的特性、程序加載時(shí)的動(dòng)態(tài)鏈接、程序運(yùn)行時(shí)的動(dòng)態(tài)鏈接、動(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)。例如,非static函數(shù)和非static的全局變量(指不帶static的全局變量)

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

如,main.c中的函數(shù)名swapLocalsymbols(本模塊的局部符號(hào))僅由模塊m定義和引用的本地符號(hào)。例如,在模塊m中定義的帶static的函數(shù)和全局變量如,swap.c中的static變量名bufp1

鏈接器局部符號(hào)不是指程序中的局部變量(分配在棧中的臨時(shí)性變量),鏈接器不關(guān)心這種局部變量符號(hào)和符號(hào)解析intbuf[2]={1,2};externvoidswap();intmain(){swap();return0;}main.cexternintbuf[];

int*bufp0=&buf[0];staticint*bufp1;voidswap(){inttemp;bufp1=&buf[1];temp=*bufp0;*bufp0=*bufp1;*bufp1=temp;}swap.c你能說(shuō)出哪些是全局符號(hào)?哪些是外部符號(hào)?哪些是局部符號(hào)?目標(biāo)文件中的符號(hào)表符號(hào)表(symtab)中每個(gè)條目的結(jié)構(gòu)如下:typedefstruct{intname;/*符號(hào)對(duì)應(yīng)字符串在strtab節(jié)中的偏移量*/ intvalue;/*在對(duì)應(yīng)節(jié)中的偏移量,可執(zhí)行文件中是虛擬地址*/intsize;/*符號(hào)對(duì)應(yīng)目標(biāo)所占字節(jié)數(shù)*/chartype:4,/*符號(hào)對(duì)應(yīng)目標(biāo)的類型:數(shù)據(jù)、函數(shù)、源文件、節(jié)*/binding:4;/*符號(hào)類別:全局符號(hào)、局部符號(hào)、弱符號(hào)*/charreserved;charsection;/*符號(hào)對(duì)應(yīng)目標(biāo)所在的節(jié),或其他情況*/}Elf_Symbol;其他情況:ABS表示不該被重定位;UND表示未定義;COM表示未初始化數(shù)據(jù)(.bss),此時(shí),value表示對(duì)齊要求,size給出最小大小.symtab節(jié)記錄符號(hào)表信息,是一個(gè)結(jié)構(gòu)數(shù)組函數(shù)名在text節(jié)中變量名在data節(jié)或bss節(jié)中函數(shù)大小或變量長(zhǎng)度目標(biāo)文件中的符號(hào)表main.o中的符號(hào)表中最后三個(gè)條目(共10個(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è)條目(共11個(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對(duì)齊且占4B符號(hào)解析(SymbolResolution)目的:將每個(gè)模塊中引用的符號(hào)與某個(gè)目標(biāo)模塊中的定義符號(hào)建立關(guān)聯(lián)。每個(gè)定義符號(hào)在代碼段或數(shù)據(jù)段中都被分配了存儲(chǔ)空間,將引用符號(hào)與定義符號(hào)建立關(guān)聯(lián)后,就可在重定位時(shí)將引用符號(hào)的地址重定位為相關(guān)聯(lián)的定義符號(hào)的地址。局部(本地)符號(hào)在本模塊定義并引用,其解析較簡(jiǎn)單,只要與本模塊內(nèi)唯一的定義符號(hào)關(guān)聯(lián)即可。全局符號(hào)(外部定義的、內(nèi)部定義的)的解析涉及多個(gè)模塊,故較復(fù)雜

“符號(hào)的定義”其實(shí)質(zhì)是什么?指被分配了存儲(chǔ)空間。為函數(shù)名時(shí),指代碼所在區(qū);為變量名時(shí),指所占的靜態(tài)數(shù)據(jù)區(qū)。addBjmpL0

……L0:sub23……B:……確定L0的地址,再在jmp指令中填入L0的地址所有定義符號(hào)的值就是其目標(biāo)所在的首地址符號(hào)解析也稱符號(hào)綁定全局符號(hào)的符號(hào)解析全局符號(hào)的強(qiáng)/弱特性函數(shù)名和已初始化的全局變量名是強(qiáng)符號(hào)未初始化的全局變量名是弱符號(hào)

intvar=5;p1(){……}intvar;p2(){……}p1.cp2.c以下符號(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此處為引用本地局部符號(hào)局部變量以下符號(hào)哪些是強(qiáng)符號(hào)?哪些是弱符號(hào)?鏈接器對(duì)符號(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)對(duì)弱符號(hào)的引用被解析為其強(qiáng)定義符號(hào)

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

符號(hào)解析時(shí)只能有一個(gè)確定的定義(即每個(gè)符號(hào)僅占一處存儲(chǔ)空間)多重定義符號(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;}問(wèn)題:打印結(jié)果是什么?y=200,z=2000以下程序會(huì)發(fā)生鏈接出錯(cuò)嗎?該例說(shuō)明:在兩個(gè)不同模塊定義相同變量名,很可能發(fā)生意想不到的結(jié)果!多重定義符號(hào)的解析舉例p1.c該例說(shuō)明:兩個(gè)重復(fù)定義的變量具有不同類型時(shí),更容易出現(xiàn)難以理解的結(jié)果!

main.c問(wèn)題:打印結(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.cdouble型數(shù)1.0對(duì)應(yīng)的機(jī)器數(shù)3FF0000000000000H

低高IA-32是小端方式230-1-(220-1)=230-220=1024*1024*1023=1072693248多重定義全局符號(hào)的問(wèn)題盡量避免使用全局變量一定需要用的話,就按以下規(guī)則使用盡量使用本地變量(static)全局變量要賦初值外部全局變量要使用extern多重定義全局變量會(huì)造成一些意想不到的錯(cuò)誤,而且是默默發(fā)生的,編譯系統(tǒng)不會(huì)警告,并會(huì)在程序執(zhí)行很久后才能表現(xiàn)出來(lái),且遠(yuǎn)離錯(cuò)誤引發(fā)處。特別是在一個(gè)具有幾百個(gè)模塊的大型軟件中,這類錯(cuò)誤很難修正。大部分程序員并不了解鏈接器如何工作,因而養(yǎng)成良好的編程習(xí)慣是非常重要的。如何劃分模塊?許多函數(shù)無(wú)需自己寫,可使用共享庫(kù)函數(shù)如數(shù)學(xué)庫(kù),輸入/輸出庫(kù),存儲(chǔ)管理庫(kù),字符串處理等避免以下兩種極端做法將所有函數(shù)都放在一個(gè)源文件中修改一個(gè)函數(shù)需要對(duì)所有函數(shù)重新編譯時(shí)間和空間兩方面的效率都不高一個(gè)源文件中僅包含一個(gè)函數(shù)需要程序員顯式地進(jìn)行鏈接效率高,但模塊太多,故太繁瑣靜態(tài)共享庫(kù)靜態(tài)庫(kù)(.aarchivefiles)將所有相關(guān)的目標(biāo)模塊(.o)打包為一個(gè)單獨(dú)的庫(kù)文件(.a),稱為靜態(tài)庫(kù)文件,也稱存檔文件(archive)增強(qiáng)了鏈接器功能,使其能通過(guò)查找一個(gè)或多個(gè)庫(kù)文件中的符號(hào)來(lái)解析符號(hào)在構(gòu)建可執(zhí)行文件時(shí)只需指定庫(kù)文件名,鏈接器會(huì)自動(dòng)到庫(kù)中尋找那些應(yīng)用程序用到的目標(biāo)模塊,并且只把用到的模塊從庫(kù)中拷貝出來(lái)在gcc命令行中無(wú)需明顯指定C標(biāo)準(zhǔn)庫(kù)libc.a(默認(rèn)庫(kù))靜態(tài)庫(kù)的創(chuàng)建轉(zhuǎn)換(cpp,cc1,as)atoi.catoi.o轉(zhuǎn)換(cpp,cc1,as)printf.cprintf.olibc.aArchiver(ar)...random.crandom.o$arrcslibc.a\atoi.oprintf.o…random.oC標(biāo)準(zhǔn)靜態(tài)庫(kù)Archiver(歸檔器)允許增量更新,只要重新編譯需修改的源碼并將其.o文件替換到靜態(tài)庫(kù)中。轉(zhuǎn)換(cpp,cc1,as)在gcc命令行中無(wú)需明顯指定C標(biāo)準(zhǔn)庫(kù)libc.a(默認(rèn)庫(kù))常用靜態(tài)庫(kù)libc.a(C標(biāo)準(zhǔn)庫(kù))1392個(gè)目標(biāo)文件(大約8MB)包含I/O、存儲(chǔ)分配、信號(hào)處理、字符串處理、時(shí)間和日期、隨機(jī)數(shù)生成、定點(diǎn)整數(shù)算術(shù)運(yùn)算libm.a(theCmathlibrary)401個(gè)目標(biāo)文件(大約1MB)浮點(diǎn)數(shù)算術(shù)運(yùn)算(如sin,cos,tan,log,exp,sqrt,…)%ar-t/usr/lib/libc.a|sort…fork.o…fprintf.ofpu_control.ofputc.ofreopen.ofscanf.ofseek.ofstab.o…%ar-t/usr/lib/libm.a|sort…e_acos.oe_acosf.oe_acosh.oe_acoshf.oe_acoshl.oe_acosl.oe_asin.oe_asinf.oe_asinl.o…自定義一個(gè)靜態(tài)庫(kù)文件#include<stdio.h>voidmyfunc1(){printf("Thisismyfunc1!\n");}#include<stdio.h>voidmyfunc2(){printf("Thisismyfunc2\n");}$gcc–cmyproc1.cmyproc2.c$arrcsmylib.amyproc1.omyproc2.omyproc1.cmyproc2.c舉例:將myproc1.o和myproc2.o打包生成mylib.avoidmyfunc1(viod);intmain(){myfunc1();return0;}main.c調(diào)用關(guān)系:main→myfunc1→printf$gcc–cmain.c$gcc–static–omyprocmain.o./mylib.alibc.a無(wú)需明顯指出!問(wèn)題:如何進(jìn)行符號(hào)解析?鏈接器中符號(hào)解析的全過(guò)程

voidmyfunc1(viod);intmain(){myfunc1();return0;}main.c調(diào)用關(guān)系:main→myfunc1→printf$gcc–cmain.c$gcc–static–omyprocmain.o./mylib.a開(kāi)始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沒(méi)有被加入E中,因而它被丟棄。E

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

接著,掃描默認(rèn)的庫(kù)文件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一定是空的,D中符號(hào)唯一。

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

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

$gcc–static–omyprocmain.o./mylib.a解析結(jié)果:E中有main.o、myproc1.o、printf.o及其調(diào)用的模塊D中有main、myproc1、printf及其引用的符號(hào)main→myfunc1→printf轉(zhuǎn)換(cpp,cc1,as)main.cmain.omylib.aprintf.o及其調(diào)用模塊myproc靜態(tài)鏈接器(ld)Libc.amyproc1.o完全鏈接的可執(zhí)行目標(biāo)文件自定義靜態(tài)庫(kù)標(biāo)準(zhǔn)靜態(tài)庫(kù)注意:E中無(wú)myproc2.o$arrcsmylib.amyproc1.omyproc2.o鏈接器中符號(hào)解析的全過(guò)程

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

$gcc–static–omyprocmain.o./mylib.a若命令為:$

gcc–static–omyproc./mylib.amain.o,結(jié)果怎樣?首先,掃描mylib,因是靜態(tài)庫(kù),應(yīng)根據(jù)其中是否存在U中未解析符號(hào)對(duì)應(yīng)的定義符號(hào)來(lái)確定哪個(gè).o被加入E。因?yàn)閁中一開(kāi)始為空,所以mylib中的myproc1.o和myproc2.o都被丟棄。然后,掃描main.o,將myfunc1加入U(xiǎn),直到最后它都不能被解析。因此,出現(xiàn)鏈接錯(cuò)誤!解析結(jié)果:E中有main.o、myproc1.o、printf.o及其調(diào)用的模塊D中有main、myproc1、printf及其引用符號(hào)main→myfunc1→printfWhy?被鏈接模塊應(yīng)按調(diào)用順序指定!它只能用mylib.a中符號(hào)來(lái)解析,而mylib中兩個(gè).o模塊都已被丟棄!使用靜態(tài)庫(kù)鏈接器對(duì)外部引用的解析算法要點(diǎn)如下:按照命令行給出的順序掃描.o和.a文件掃描期間將當(dāng)前未解析的引用記錄到一個(gè)列表U中每遇到一個(gè)新的.o或.a中的模塊,都試圖用其來(lái)解析U中的符號(hào)如果掃描到最后,U中還有未被解析的符號(hào),則發(fā)生錯(cuò)誤問(wèn)題和對(duì)策能否正確解析與命令行給出的順序有關(guān)好的做法:將靜態(tài)庫(kù)放在命令行的最后$gcc-L.libtest.o-lmine$gcc-L.-lminelibtest.o

libtest.o:Infunction`main':libtest.o(.text+0x4):undefinedreferenceto`libfun'

說(shuō)明在libtest.o中的main調(diào)用了libfun這個(gè)在庫(kù)libmine中的函數(shù),所以,在命令行中,應(yīng)該將libtest.o放在前面,像第一行中那樣!libmine.a是靜態(tài)庫(kù)假設(shè)調(diào)用關(guān)系:libtest.o→libfun.o(在libmine.a中)

(main)→(libfun)掃描libtest.o,將libfun送U,掃描到libmine.a時(shí),用其定義的libfun來(lái)解析-lxxx=libxxx.a鏈接順序問(wèn)題假設(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–omyfunc

func.o

libx.a

liby.a

libz.agcc-static–omyfunc

func.o

liby.a

libx.a

libz.agcc-static–omyfunc

func.o

libx.a

libz.a

liby.a假設(shè)調(diào)用關(guān)系如下:

func.o

→libx.a

和liby.a

中的函數(shù)

libx.a

liby.a

同時(shí)liby.a

libx.a

則以下命令行可行:gcc-static–omyfunc

func.o

libx.a

liby.a

libx.agcc-static–omyfunc

func.o

liby.a

libx.a

liby.a鏈接操作的步驟代碼數(shù)據(jù)P0:addBjmpL0

……

call

P1……L0:subC……P1:addA

……

…………

subB……B:

10C:20A:

30P1:addA

……

…………

subB……A:

30P0:addBjmpL0

……

call

P1……L0:subC……B:

10C:20%espbrk0xC000000000x080480000內(nèi)核虛存區(qū)共享庫(kù)區(qū)域堆(heap)動(dòng)態(tài)生成)用戶棧動(dòng)態(tài)生成未使用讀寫數(shù)據(jù)段(.data,.bss)只讀代碼段(.text,.rodata等)從可執(zhí)行文件裝入1GB符號(hào)綁定同節(jié)合并確定地址修改引用重定位符號(hào)解析完成后,可進(jìn)行重定位工作,分三步合并相同的節(jié)將集合E的所有目標(biāo)模塊中相同的節(jié)合并成新節(jié)

例如,所有.text節(jié)合并作為可執(zhí)行文件中的.text節(jié)對(duì)定義符號(hào)進(jìn)行重定位(確定地址)確定新節(jié)中所有定義符號(hào)在虛擬地址空間中的地址例如,為函數(shù)確定首地址,進(jìn)而確定每條指令的地址,為變量確定首地址完成這一步后,每條指令和每個(gè)全局變量都可確定地址對(duì)引用符號(hào)進(jìn)行重定位(確定地址)修改.text節(jié)和.data節(jié)中對(duì)每個(gè)符號(hào)的引用(地址)

需要用到在.rel_data和.rel_text節(jié)中保存的重定位信息重定位信息匯編器遇到引用時(shí),生成一個(gè)重定位條目數(shù)據(jù)引用的重定位條目在.rel_data節(jié)中指令中引用的重定位條目在.rel_text節(jié)中ELF中重定位條目格式如下:IA-32有兩種最基本的重定位類型R_386_32:絕對(duì)地址R_386_PC32:

溫馨提示

  • 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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論