基于PowerPC Linux的ELF格式分析17387_第1頁(yè)
基于PowerPC Linux的ELF格式分析17387_第2頁(yè)
基于PowerPC Linux的ELF格式分析17387_第3頁(yè)
基于PowerPC Linux的ELF格式分析17387_第4頁(yè)
基于PowerPC Linux的ELF格式分析17387_第5頁(yè)
已閱讀5頁(yè),還剩21頁(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)介

1、第一部分ELF格式概述ELF(Executable and Linkable Format)是一種對(duì)可執(zhí)行文件、目標(biāo)文件以及庫(kù)文件使用的文件格式,它在Linux下成為標(biāo)準(zhǔn)文件已經(jīng)有很長(zhǎng)的一段時(shí)間,代替了早期的格式。ELF格式的一個(gè)優(yōu)點(diǎn)是同一個(gè)文件格式可以用在Linux Kernel支持的所有體系結(jié)構(gòu)之上。這不僅簡(jiǎn)化了用戶空間工具程序的創(chuàng)建,也簡(jiǎn)化了內(nèi)核自身的程序設(shè)計(jì),比如必須為可執(zhí)行程序生成裝載例程時(shí)。但是文件格式相同并不意味著不同系統(tǒng)上的程序之間存在二進(jìn)制兼容性,例如FreeBSD和Linux都使用ELF作為二進(jìn)制格式,但是FreeBSD上的程序不能運(yùn)行于Linux上,因?yàn)閮烧咴谙到y(tǒng)調(diào)用機(jī)制

2、和系統(tǒng)調(diào)用語(yǔ)義方面仍然有所不同,反之亦然;如果想讓二者之間的程序能夠運(yùn)行,必須要有一個(gè)中間仿真層。同樣PowerPC平臺(tái)編譯的ELF程序,也不能在X86平臺(tái)的機(jī)器上運(yùn)行,反之依然,因?yàn)閮烧叩捏w系結(jié)構(gòu)是完全不同的。但是由于ELF格式的存在,相同體系結(jié)構(gòu)上的ELF程序本身的相關(guān)信息,以及程序的各個(gè)部分在二進(jìn)制文件中的編碼方式都是相同的。Linux不僅將ELF用于用戶空間應(yīng)用程序和庫(kù),還用于工具模塊,另外Linux內(nèi)核本身也是ELF格式。備注:ELF文件是一種開放的格式,其規(guī)范可以自由獲得。ELF文件有三種類型:可重定位文件:也就是通常稱的目標(biāo)文件,后綴為.o;共享文件:也就是通常稱的庫(kù)文件,后綴為

3、.so;可執(zhí)行文件:本文主要討論的文件格式;總的來(lái)說(shuō),可執(zhí)行文件的格式與上述兩種文件的格式之間的區(qū)別主要在于觀察的角度不同:一種稱為連接視圖(Linking View),一種稱為執(zhí)行視圖(Execution View)。示意圖如下:第二部分布局和結(jié)構(gòu)ELF Header:除了用于標(biāo)識(shí)ELF文件的幾個(gè)字節(jié)之外,ELF文件頭還包含了有關(guān)文件類型和大小的信息,以及文件加載后程序執(zhí)行的入口信息等等,總之ELF頭部是一個(gè)關(guān)于本ELF文件的路線圖(road map),從總體上描述文件的結(jié)構(gòu)。程序表頭(Program Head Table):向系統(tǒng)提供了可執(zhí)行文件的數(shù)據(jù)在進(jìn)程虛擬地址空間中組織方式的相關(guān)信息

4、,它還表示了文件可能包含的段的數(shù)目,段的位置以及用途。各個(gè)段SegmentX(X=1,2,)和節(jié)SectionX(X=1,2,):保存了與文件相關(guān)的各種形式的數(shù)據(jù),例如符號(hào)表、實(shí)際的二進(jìn)制代碼、固定值或者程序使用的數(shù)值常數(shù)。節(jié)頭表(Section Header Table):包含了與各段相關(guān)的附加信息。下面我們一個(gè)例子,用readelf工具來(lái)分析ELF文件:例如如下:#include <stdio.h>#include <stdlib.h>int add(int a, int b) printf("Numbers are added togethern&quo

5、t;); return a + b;int main() int a, b; a = 3; b = 4; int ret = add(a,b); printf("Result : %un",ret); exit(0);我們用file命令來(lái)顯示編譯器生成的兩個(gè)文件的信息:一個(gè)是可執(zhí)行文件,另一個(gè)是可重定位文件,示意圖如下:2.1 ELF header文件結(jié)構(gòu)我們用readelf工具來(lái)分析上例中生成ELF文件的ELF header,視圖如下:在test文件的開始處是四個(gè)標(biāo)志字節(jié),0x 7f、0x45、0x4c、0x46。其中的0x45、0x4c、0x46分別代碼“E”“L”“F

6、”的ASCII碼,這使得所有處理ELF文件的工具都可以識(shí)別ELF類型的文件。還有一些與機(jī)器體系結(jié)構(gòu)相關(guān)的信息。比如上圖中Machine類型為PowerPC表明該ELF文件運(yùn)行于PowerPC平臺(tái);ELF32表明這是一個(gè)運(yùn)行在32位平臺(tái)的機(jī)器;文件類型為EXEC表明這是一個(gè)可執(zhí)行程序;Version用于區(qū)分當(dāng)前ELF文件的各個(gè)修訂版本,當(dāng)前ELF文件是基于版本1;另外還包含ELF文件各個(gè)部分的長(zhǎng)度和索引的位置,后面會(huì)相信討論。如果ELF文件是可重定位文件,不同的字段如下圖所示:如圖所示:的文件類型為REL,即它是一個(gè)可重定位的文件,其代碼可以移動(dòng)到任意位置,該文件沒有程序頭Program Hea

7、ders,對(duì)于需要鏈接的對(duì)象而言該表是不需要的,所以其所以長(zhǎng)度均為0。2.2 程序頭表(Program Head Table)分析下面我們來(lái)分析一下ELF可執(zhí)行文件test中Program Head Table,示意圖如下:在Program Headers中列出了8個(gè)段,這些段組成了最終在內(nèi)存中執(zhí)行的程序,并且還提供了各個(gè)段在虛擬地址空間和物理地址空間中的位置、大小、訪問(wèn)授權(quán)和其它方面的信息。從上圖中我們可以看出,示例程序中包含的各個(gè)段的語(yǔ)義如下:PHDR:保存Program Headers TableINTERP:制定程序已經(jīng)從可執(zhí)行文件映射到內(nèi)存之后,必須調(diào)用的解釋器。在這里,解釋器并不意

8、味著二進(jìn)制文件的內(nèi)容必須由另一個(gè)程序解釋(比如Java字節(jié)代碼必須由Java虛擬機(jī)來(lái)解釋),它指的是這樣的程序:通過(guò)鏈接其它庫(kù)來(lái)滿足未解決的引用。LOAD:表示一個(gè)需要從二進(jìn)制文件映射到虛擬地址空間的段,其中保存常量數(shù)據(jù)(如字符串),程序的目標(biāo)代碼等等。DYNAMIC:保存了由動(dòng)態(tài)鏈接區(qū)(即INTERP中指定的解釋器)使用的信息。NOTE:保存了專用信息,與當(dāng)前主題無(wú)關(guān)。GNU_EH_FRAM和GNU_STACK:用來(lái)分析棧幀,與體系結(jié)構(gòu)相關(guān),在PowerPC體系結(jié)構(gòu)中需要分析棧幀實(shí)現(xiàn)回溯。備注:我們需要特別指出段是可以重疊的,比如在上圖中LOAD段從物理地址0x1000 0000到物理地址0

9、x1000 0000+0x007f8的范圍,該范圍包含了PHDR、INTERP、NOTE、GNU_EH_FRAM和GNU_STACK段,在ELF標(biāo)準(zhǔn)中是允許這種行為的。2.3 節(jié)頭標(biāo)(Section Header Table)分析在ELF文件中描述各段的內(nèi)容時(shí),是指定了哪些節(jié)的數(shù)據(jù)映射到段中。在ELF中是有一張節(jié)頭表來(lái)管理文件中的各個(gè)節(jié),readelf同樣可以用于顯示可重定位文件中的各個(gè)節(jié),示意圖如下:這里指定的偏移量0x168是相對(duì)于二進(jìn)制文件的。節(jié)信息不需要復(fù)制到在虛擬地址空間中做為可執(zhí)行文件創(chuàng)建的最終進(jìn)程映象,盡管如此在ELF二進(jìn)制文件中節(jié)信息總是存在的。每個(gè)節(jié)都指定了一個(gè)類型,定義了節(jié)

10、數(shù)據(jù)的語(yǔ)義。包含PROGBITS、SYSTAB、RELA和STRTAB。PROGBITS:程序必須解釋的信息,比如二進(jìn)制代碼,程序的二進(jìn)制代碼被稱之為text,指的是用作機(jī)器代碼的二進(jìn)制信息SYSTAB:符號(hào)表RELA:重定位信息STRTAB:用于存儲(chǔ)與ELF相關(guān)的字符串,但與程序沒有直接的關(guān)系。各個(gè)節(jié)Section都指定了其大小和在二進(jìn)制文件內(nèi)部的偏移量。地址Addr:指定加載到虛擬地址空間的位置,因?yàn)槲覀兊睦又刑幚淼氖且粋€(gè)可鏈接的對(duì)象,目標(biāo)地址是沒有定義的,因而表示為0。標(biāo)志Flg:指出各個(gè)節(jié)Section如何被訪問(wèn)或者處理。我們對(duì)標(biāo)志A比較感興趣,因?yàn)樗刂浦b載文件時(shí)是否將節(jié)的數(shù)據(jù)復(fù)

11、制到虛擬地址空間中。盡管節(jié)的名稱是可以自由選擇的,但是Linux(和其它所有使用ELF的類UNIX系統(tǒng))都提供了一些標(biāo)準(zhǔn)節(jié),其中一些是強(qiáng)制性的,例如總是有一個(gè)名為.text的節(jié)來(lái)保存二進(jìn)制代碼(即與該ELF文件相關(guān)聯(lián)的程序信息),保存了節(jié)的重定位信息。備注:節(jié)名以點(diǎn)開始是由系統(tǒng)自身使用的,如果應(yīng)用程序要想定義自身的節(jié),就不應(yīng)該以點(diǎn)開頭,以避免與系統(tǒng)節(jié)名相沖突??蓤?zhí)行程序包含了一些重定位的信息,示意圖如下:與可重定位文件的11個(gè)節(jié)相比,可執(zhí)行文件有36個(gè)節(jié),并非所有的節(jié)都與我們的討論有關(guān)。上圖中標(biāo)出的節(jié)是有具體意義的:.interp:保存了解釋器的文件名,例如在我們的例子中用的是是;.data:

12、保存了初始化的數(shù)據(jù),這是普通程序數(shù)據(jù)的一部分,可以在程序運(yùn)行時(shí)修改;.rodata:保存了只讀數(shù)據(jù),可以讀但不可以修改,例如編譯器將所有出現(xiàn)在printf中的靜態(tài)字符串封裝到該節(jié);.init和.fini:保存了進(jìn)程初始化和結(jié)束時(shí)所用的代碼,這兩個(gè)節(jié)通常是編譯器自動(dòng)添加的,無(wú)需應(yīng)用程序員關(guān)注;.hash:是一個(gè)散列表,允許在不對(duì)全表元素進(jìn)行線性搜索的情況下,快速訪問(wèn)所有的符號(hào)表項(xiàng)。備注:可執(zhí)行文件ELF各個(gè)Section中的Addr字段保存了有效地址的值,因?yàn)橄鄳?yīng)的代碼必須映射到虛擬地址空間中某些已經(jīng)定義好的位置,在基于PowerPC的Linux中應(yīng)用程序通常使用0x1000 0000以上的內(nèi)存

13、區(qū)間。2.4 符號(hào)表(Symbol Table)符號(hào)表是每個(gè)ELF文件的一個(gè)重要的組成部分,因?yàn)樗4媪顺绦驅(qū)崿F(xiàn)或者使用過(guò)程中所有的(全局)變量和函數(shù),如果程序使用了一個(gè)自身代碼沒有定義的符號(hào),則稱之為未定義符號(hào)(例如例子中的printf函數(shù)是定義在C標(biāo)準(zhǔn)庫(kù)中,自身沒有定義),此類應(yīng)用必須在靜態(tài)鏈接期間用其它的目標(biāo)模塊或者庫(kù)來(lái)解決,或者在加載期間使用來(lái)解決。我們可以使用nm工具生成程序定義或者使用的所有符號(hào)列表,如果下圖所示:左側(cè)一列給出了符號(hào)的值,即符號(hào)定義在目標(biāo)文件中的位置,例子中包含了兩個(gè)不同的符號(hào)類型:如果函數(shù)定義在text段,縮寫為T;而未定義的引用用U標(biāo)明。邏輯上沒有定義的函數(shù)沒有

14、符號(hào)值??稍诳蓤?zhí)行ELF文件test中,還會(huì)有更多的符號(hào)。當(dāng)時(shí)大多數(shù)都是由編譯器自動(dòng)生成的,供運(yùn)行時(shí)系統(tǒng)內(nèi)部使用,以下例子中我們僅標(biāo)出了同時(shí)出現(xiàn)在中的符號(hào):1000058c T add100005dc T main解釋:exit和puts雖然是沒有定義的,但是同時(shí)增加了一些版本信息,標(biāo)明能夠提供函數(shù)的GNU標(biāo)準(zhǔn)庫(kù)的最低版本,在我們的例子中要求庫(kù)的最低版本不能低于,這意味著該程序無(wú)法使用Libc5和Lib4工作,因?yàn)長(zhǎng)ibc4和Libc5是Linux專用的C標(biāo)準(zhǔn)庫(kù),是該庫(kù)的第一個(gè)跨平臺(tái)版本,它替換了就版本,它替換了舊版本。由函數(shù)本身定義的add和main已經(jīng)移到虛擬地址空間中的固定位置(在文件加

15、載時(shí),對(duì)應(yīng)的代碼將會(huì)映射到這些位置)ELF使用以下的三個(gè)節(jié)(Section);來(lái)實(shí)現(xiàn)字符串的管理:.systalb:確定字符串的名稱與其值的索引,但符號(hào)的名稱不是以字符串的形式出現(xiàn)的,而是表示為某個(gè)字符串?dāng)?shù)組的索引;.strtab:保存了字符串?dāng)?shù)組;.hash:保存了一個(gè)hash表用于快速查找符號(hào)。簡(jiǎn)而言之,符號(hào)名在字符串表(string table )中的位置和符號(hào)的值,為了說(shuō)明字符串表示如何管理ELF中的字符串,我們看下面的例子:表的第一個(gè)字節(jié)是NULL(即ASCII碼值為0),后續(xù)的各個(gè)字符串通過(guò)NULL字節(jié)分割,為了引用字符串必須指定一個(gè)位置,即字符串的索引。這將選擇下一個(gè)NULL字節(jié)

16、之前的所有字符(如果用NULL字節(jié)的位置作為索引,那么將會(huì)對(duì)應(yīng)空串)。如果允許索引不僅僅選擇字符串的起始地址,也可以選擇字符串中間的任何位置,就能夠支持字串的用法(但是非常受限)上述的.strtab節(jié)并不是默認(rèn)情況下的唯一的字符串表,.shstrtab用于存放文件中各個(gè)節(jié)的文本名稱(比如.text)第三部分 Linux 內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)內(nèi)核在兩處使用了ELF文件格式:ELF用于處理可執(zhí)行文件和庫(kù),用于實(shí)現(xiàn)模塊。這些地方使用了不同的代碼來(lái)讀取和操作數(shù)據(jù),但這兩種情況下都利用了我們本文所介紹的數(shù)據(jù)結(jié)構(gòu),其基礎(chǔ)是文件,它實(shí)現(xiàn)了ELF標(biāo)準(zhǔn),基本沒有做改動(dòng)!3.1 數(shù)據(jù)類型ELF是一個(gè)與CPU和體系結(jié)構(gòu)

17、無(wú)關(guān)的格式,它不能依賴與特定的字長(zhǎng)和字節(jié)序(大端還是小端),至少對(duì)文件中的那些需要在所有系統(tǒng)上讀取和理解的數(shù)據(jù)元素來(lái)說(shuō)是這樣。出現(xiàn)在.text段中的機(jī)器代碼存儲(chǔ)為宿主系統(tǒng)的表示格式,以避免轉(zhuǎn)換工作。為此Linux內(nèi)核定義了一些數(shù)據(jù)類型,在所有體系結(jié)構(gòu)上具有相同的位寬,如下所示:/32-bit ELF 基本類型typedef _u32 Elf32_Addr;typedef _u16 Elf32_Half;typedef _u32 Elf32_Off;typedef _s32 Elf32_Sword;typedef _u32 Elf32_Word;/ 64-bit ELF 基本類型typedef

18、_u64 Elf64_Addr;typedef _u16 Elf64_Half;typedef _s16 Elf64_SHalf;typedef _u64 Elf64_Off;typedef _s32 Elf64_Sword;typedef _u32 Elf64_Word;typedef _u64 Elf64_Xword;typedef _s64 Elf64_Sxword;因?yàn)轶w系結(jié)構(gòu)相關(guān)的代碼必須總是明確定義整數(shù)類型的符號(hào)和位寬,ELF標(biāo)準(zhǔn)的數(shù)據(jù)類型可以毫不費(fèi)力的通過(guò)typedef實(shí)現(xiàn)3.2 ELF頭部格式實(shí)現(xiàn)對(duì)ELF格式的各種頭部文件,32位和64位需要分別定義其數(shù)據(jù)結(jié)構(gòu):32位的ELF頭

19、在定義如下:#define EI_NIDENT 16typedef struct elf32_hdr unsigned char e_identEI_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

20、e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shstrndx; Elf32_Ehdr;解釋:e_ident:可以容納16個(gè)字節(jié),這些字節(jié)在所有的體系結(jié)構(gòu)上都是由char數(shù)據(jù)類型表示的,前4個(gè)字節(jié)分別為0x7f(即ASCII碼中的DEL)和字母E、L、F,我們?cè)诠?jié)曾介紹過(guò)。其它的字節(jié)位置有其特定的含義:e_ident4:EI_CLASS標(biāo)識(shí)文件的類型,將文件分為32位和64位兩類,即ELFCLASS32和ELFCLASS64e_ident5:EI_DATA指定了格式所使用的字節(jié)序,即ELFDATA2LSB(小端)和ELFDATA2MSB()大端ei

21、_ident6:EI_VERSION指定ELF頭文件的版本(該版本可能獨(dú)立于數(shù)據(jù)段的版本),當(dāng)前,值允許使用EV_CURRENT,這是第一個(gè)版本ei_ident7: EI_OSABI指定采用的OS的ABI版本,當(dāng)前是UNIX System V ABI從ei_ident8EI_PAD起的剩余字節(jié):用NULL來(lái)填充,因?yàn)檫@些位置上ELF不需要。e_type用去區(qū)分各種ELF文件的類型,如下所示:e_machine指定了文件所需的體系結(jié)構(gòu),下表列出了Linux支持的各種選項(xiàng):注意:每種體系結(jié)構(gòu)都需要定義elf_check_arch,并由內(nèi)核的通用代碼使用,來(lái)確保加載的ELF文件可以在相應(yīng)的體系結(jié)構(gòu)上

22、運(yùn)行。e_version:保存了版本信息,用于區(qū)分不同的ELF變體,目前該規(guī)范僅支持版本1,由EV_CURRENT指出來(lái)e_entry:給出了文件在虛擬內(nèi)存中的入口點(diǎn),在程序已經(jīng)加載并映射到內(nèi)存之后,執(zhí)行開始的位置e_phoff:保存了程序頭表(Program Header Table)在二進(jìn)制ELF文件中的偏移e_shoff:保存了節(jié)表頭(Section Header Table) 在二進(jìn)制ELF文件中的偏移e_flags:保存了特定于處理器的標(biāo)志,當(dāng)前Linux內(nèi)核不適用該標(biāo)志e_ehsize:指定了ELF Header的長(zhǎng)度,單位是字節(jié)e_phentsize:指定了Program Hea

23、der中一個(gè)表項(xiàng)的長(zhǎng)度,單位是字節(jié)(所有表項(xiàng)的長(zhǎng)度均相同)e_phnum:指定了Program Header Table中表項(xiàng)的數(shù)目e_shentsize:指定了Section Header Table中一個(gè)表項(xiàng)的長(zhǎng)度,單位是字節(jié)(所有表項(xiàng)的長(zhǎng)度均相同)e_shnum:指定了節(jié)頭表中項(xiàng)的數(shù)目e_shstrndx:保存了包含各節(jié)(Section)名稱的字符串表在Section Header中的索引位置備注:64位下ELF頭的數(shù)據(jù)結(jié)構(gòu)可以同樣定義,唯一的差別在于其中使用了對(duì)應(yīng)的64位數(shù)據(jù)類型,這使得頭文件稍大。但兩者數(shù)據(jù)結(jié)構(gòu)的前16字節(jié)都是相同的,兩種體系結(jié)構(gòu)的類型能夠根據(jù)這些字節(jié)來(lái)識(shí)別不同字長(zhǎng)機(jī)

24、器的ELF文件3.3 Program Header實(shí)現(xiàn)程序頭表由幾個(gè)項(xiàng)實(shí)現(xiàn),其處理方式類似于數(shù)組項(xiàng)(項(xiàng)的數(shù)據(jù)由ELF頭中的e_phnum指定),表項(xiàng)的數(shù)據(jù)類型定義為獨(dú)立的結(jié)構(gòu),在32的機(jī)器上其內(nèi)容如下:typedef struct elf32_phdr Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_flags; Elf32_Word p_align; Elf32_Phdr;解釋

25、:p_type:表示當(dāng)前項(xiàng)描述的段的種類,為其定義了下列常數(shù):PT_NULL:表示沒有使用的段PT_LOAD:表示可裝載段,在程序執(zhí)行前從二進(jìn)制文件映射到內(nèi)存PT_DYNAMIC:表示段包含了用于動(dòng)態(tài)鏈接器的信息PT_INTERP:表示當(dāng)前段指定了用于動(dòng)態(tài)鏈接的解釋程序,比如PT_NOTE:指定一個(gè)段,其中可能含有專有的編譯器信息還有兩個(gè)常數(shù)PT_LOPROC和PT_HIGHPROC用于定義與處理器相關(guān)的用途,內(nèi)核并不使用 p_offset:指出所描述段從ELF文件起始處的偏移量,以字節(jié)為單位p_vaddr:給出段的數(shù)據(jù)映射到虛擬地址空間中的位置(對(duì)應(yīng)PT_LOAD類型的段),只支持

26、物理尋找,不支持虛擬尋址的系統(tǒng)將使用p_paddr保存的信息p_filesz:指定了段在ELF文件中的長(zhǎng)度p_memsz:指定了段在虛擬地址空間中的長(zhǎng)度,與文件中物理段的差值可以通過(guò)階段數(shù)據(jù)或者填充0字節(jié)來(lái)填充p_flags:保存了標(biāo)志信息,定義了段的訪問(wèn)權(quán)限:PF_R讀權(quán)限,PF_W寫權(quán)限,PF_X執(zhí)行權(quán)限p_align:指定了段在內(nèi)存和二進(jìn)制文件中的對(duì)齊方式(即p_vaddr和p_offset地址必須是模p_align,即為p_align的倍數(shù))比如p_align的值為0x1000=4KB,這意味著段必須4KB對(duì)齊。對(duì)64為體系結(jié)構(gòu)定義的類似的數(shù)據(jù)結(jié)構(gòu),與32位相比,唯一的差別在于所使用的

27、數(shù)據(jù)結(jié)構(gòu),各數(shù)據(jù)項(xiàng)的語(yǔ)義都是相同的。其具體內(nèi)容如下:typedef struct elf64_phdr Elf64_Word p_type; Elf64_Word p_flags; Elf64_Off p_offset; /Segment file offset Elf64_Addr p_vaddr; /Segment virtual address Elf64_Addr p_paddr; /Segment physical address Elf64_Xword p_filesz; /Segment size in file Elf64_Xword p_memsz; /Segment siz

28、e in memory Elf64_Xword p_align; /Segment alignment, file & memory Elf64_Phdr;3.4 節(jié)頭表Section Header Table代碼實(shí)現(xiàn)節(jié)頭表通過(guò)數(shù)組實(shí)現(xiàn),每個(gè)數(shù)組項(xiàng)包含一節(jié)的信息,各個(gè)節(jié)構(gòu)成了程序頭表Program Header Table定義的各個(gè)段的內(nèi)容。下來(lái)數(shù)據(jù)結(jié)構(gòu)表示一個(gè)節(jié):typedef struct elf32_shdr Elf32_Word sh_name; Elf32_Word sh_type; Elf32_Word sh_flags; Elf32_Addr sh_addr; Elf32

29、_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;解釋:sh_name:指定了節(jié)的名稱,其值不是字符串本身,而是字符串表的一個(gè)索引。sh_type:指定了節(jié)的類型,有下列的類型可用: SH_NULL表示該節(jié)不使用,其數(shù)據(jù)將會(huì)被忽略 SH_PROGBITS保存程序的相關(guān)信息,其格式是不定義的,與這里的討論無(wú)關(guān) SH_SYMTAB保存一個(gè)符合表,其結(jié)構(gòu)我們?cè)诠?jié)已經(jīng)討論,SH_DYN

30、SYM也保存一個(gè)符號(hào)表,二者的差別我們?cè)谏院笥懻?SH_STRTAB表示一個(gè)包含字符串表的節(jié) SH_RELA和SHT_RELA保存重定位信息,我們也將在后面討論 SH_HASH定義了一個(gè)節(jié)保存HASH表,用于快速的查找符號(hào)表中的項(xiàng) SH_DYNAMIC保存了動(dòng)態(tài)鏈接表的信息,我們也將在后面討論還有類型值SHT_HIPROC、SHT_LOPROC、SHT_HIUSER、SHT_LOUSER,這些專項(xiàng)特定于CPU和應(yīng)用程序的用途,與這里討論的內(nèi)容無(wú)關(guān)sh_flags:表示節(jié)是否可重寫(SHF_WRITE),是否將其分配虛擬內(nèi)存(SHR_ALLOC),是否包含可執(zhí)行的機(jī)器代碼(SHF_EXECINS

31、TR)sh_addr:指定節(jié)映射到虛擬地址空間中的位置sh_offset:指定了節(jié)在內(nèi)存中的位置sh_size:指定了節(jié)的長(zhǎng)度sh_link:引用另一個(gè)節(jié)頭表項(xiàng),可以根據(jù)節(jié)類型進(jìn)行不同的解釋,其性能我們?cè)诤竺鎲为?dú)討論sh_info和sh_link聯(lián)用,其確切語(yǔ)義我們也會(huì)在下文中再討論。sh_addralign:指定了節(jié)數(shù)據(jù)在內(nèi)存中的對(duì)齊方式sh_entsize:指定了節(jié)中各個(gè)數(shù)據(jù)項(xiàng)的長(zhǎng)度,前提是這些數(shù)據(jù)項(xiàng)的長(zhǎng)度均相同。例如字符串表根據(jù)節(jié)類型不同,sh_link和sh_info的用法也不盡相同,具體情況如下:第一:SHT_DYNAMIC類型的節(jié)使用sh_link指向節(jié)數(shù)據(jù)指向的字符串表,這種情況

32、下不使用sh_info,sh_info設(shè)置為0;第二:散列表(SHT_HASH類型的節(jié))使用sh_link指向所散列的符合表,sh_info不使用第三:類型為SHT_RELA和SHT_REL的重定位節(jié),使用sh_link指向相關(guān)的符號(hào)表,sh_info中保存的是節(jié)頭表中的索引,表示對(duì)哪個(gè)節(jié)進(jìn)行重定向;第四:sh_link指定了用作符號(hào)表的字符串表(SHT_SYMTAB和SHT_DYNSYM),而sh_info表示符號(hào)表中緊隨最后一個(gè)局部符號(hào)之后的索引位置(STB_LOCAL類型)而64位系統(tǒng)有一個(gè)單獨(dú)的數(shù)據(jù)結(jié)構(gòu),其內(nèi)容和32系統(tǒng)相同,除了使用64位的數(shù)據(jù)類型。內(nèi)容如下:typedef stru

33、ct elf64_shdr Elf64_Word sh_name; / Section name, index in string tbl Elf64_Word sh_type; / Type of section Elf64_Xword sh_flags; / Miscellaneous section attributes Elf64_Addr sh_addr; / Section virtual addr at execution Elf64_Off sh_offset; / Section file offset Elf64_Xword sh_size; / Size of secti

34、on in bytes Elf64_Word sh_link; / Index of another section Elf64_Word sh_info; / Additional section information Elf64_Xword sh_addralign; / Section alignment Elf64_Xword sh_entsize; / Entry size if section holds table Elf64_Shdr;ELF標(biāo)準(zhǔn)定義了若干固定名稱的節(jié),這些節(jié)用于執(zhí)行大多數(shù)目標(biāo)文件所需要的標(biāo)準(zhǔn)任務(wù),所有名稱都從點(diǎn)開始,以便與用戶定義的節(jié)或者非標(biāo)準(zhǔn)的節(jié)相區(qū)分。最

35、重要的標(biāo)準(zhǔn)節(jié)如下所示:.bss:保存程序未初始化的數(shù)據(jù),在程序開始時(shí)填充0字節(jié).data:保存已經(jīng)初始化的數(shù)據(jù),例如在編譯期間用靜態(tài)數(shù)據(jù)初始化的結(jié)構(gòu),這些數(shù)據(jù)可以在程序運(yùn)行期間更改.rodata:保存了程序使用的只讀數(shù)據(jù),不能更改.dynamic和.dynstr:保存了動(dòng)態(tài)信息,后面討論.interp:保存了程序解釋器的名稱,形式為字符串.shstrtab:包含了一個(gè)字符串表,定義了節(jié)名稱.strtab:保存了一個(gè)字符串表,主要包含了符合表所需要的各個(gè)字符串.symtab:保存了二進(jìn)制文件的符合表.init和.fini:保存了程序初始化和結(jié)束時(shí)執(zhí)行的機(jī)器指令,這兩個(gè)節(jié)的內(nèi)容有編譯器或者輔助工具

36、自動(dòng)創(chuàng)建,主要是為程序建立一個(gè)適當(dāng)?shù)倪\(yùn)行環(huán)境.text:保存了主要的機(jī)器指令3.5 字符串表String Tables字符串表的格式我們?cè)诘慕Y(jié)尾處討論過(guò),因?yàn)槠涓袷椒浅?dòng)態(tài),內(nèi)核不同提供一個(gè)固定的數(shù)據(jù)結(jié)構(gòu),必需手工分析現(xiàn)存數(shù)據(jù)3.6 符號(hào)表Symbol Tables符號(hào)表保存了查找程序符號(hào)、為符號(hào)賦值、重定位符號(hào)所需要的全部信息,如上所述,有一個(gè)專門類型的節(jié)來(lái)保存符號(hào)表,其格式有下列數(shù)據(jù)結(jié)構(gòu)定義:typedef struct elf32_sym Elf32_Word st_name; Elf32_Addr st_value; Elf32_Word st_size; unsigned char

37、st_info; unsigned char st_other; Elf32_Half st_shndx; Elf32_Sym;解釋:符號(hào)表的任務(wù)就是將一個(gè)字符串和一個(gè)值關(guān)聯(lián)起來(lái),例如printf符號(hào)表表示printf函數(shù)在虛擬空間中的地址,該函數(shù)的機(jī)器碼就存在于該地址處。符號(hào)也可能有絕對(duì)值,有程序解釋,例如數(shù)據(jù)常數(shù)。一個(gè)符號(hào)的確切用途有st_info定義,它分為兩個(gè)部分(比特位如何劃分與我們的討論不相關(guān)),其中定義了下列信息:第一:符號(hào)的綁定(binding),這確定了符號(hào)的可見性,允許有下列三種不同的設(shè)置:1:局部符號(hào)(STB_LOCAL):只有在目標(biāo)文件內(nèi)部可見,在于程序的其它部分連接時(shí)

38、是不可見的。如果一個(gè)程序的幾個(gè)目標(biāo)文件都定義同名的此類符號(hào)是沒有問(wèn)題的,這些局部符號(hào)間彼此不會(huì)干擾。2:全局符號(hào)(STB_GLOBAL):在定義的目標(biāo)文件內(nèi)部可見,也可以由構(gòu)成程序的其它目標(biāo)文件引用,每個(gè)全局符號(hào)在一個(gè)程序的內(nèi)部只引用一次,否則鏈接器就會(huì)報(bào)錯(cuò)。執(zhí)行全局符號(hào)的位定義引用,將在重定位期間確定相關(guān)符號(hào)的位置,如果對(duì)全局符號(hào)的未定義引用無(wú)法解決,則拒絕程序執(zhí)行或者靜態(tài)綁定。3:弱符號(hào)(STB_WEAK):在整個(gè)程序中可見,當(dāng)可以由多個(gè)定義。如果程序中一個(gè)全局符號(hào)和一個(gè)局部符號(hào)名稱相同,則全局符號(hào)就優(yōu)先處理;即使一個(gè)弱符號(hào)沒有定義,程序也是可以靜態(tài)鏈接或者動(dòng)態(tài)鏈接,這種情況下若符號(hào)的值為

39、0第二:符號(hào)類型也有若干備選項(xiàng),只有以下三個(gè)與目標(biāo)的主題相關(guān)(對(duì)其它值的描述由ELF標(biāo)準(zhǔn)提供)STT_OBJCT:符號(hào)關(guān)聯(lián)到一二數(shù)據(jù)對(duì)象,比如變量,數(shù)組和指針;STT_FUNC:符號(hào)關(guān)聯(lián)到一個(gè)函數(shù)或者過(guò)程;STT_NOTTYPE:符號(hào)的類型為制定,用于未定義引用;Elf32_Sym結(jié)構(gòu)體除了包含st_name,st_value,st_info,還包含以下成員,其語(yǔ)義如下:st_size:制定對(duì)象的長(zhǎng)度,例如一個(gè)指針的長(zhǎng)度或者struct對(duì)象中包含的字節(jié)數(shù),如果長(zhǎng)度有位置,其值可以設(shè)置為0;標(biāo)準(zhǔn)的ELF標(biāo)準(zhǔn)不支持st_otherst_shndx:保存一個(gè)節(jié)(在節(jié)頭表)中的索引,符號(hào)將綁定到該節(jié),

40、該符號(hào)通常定義在此節(jié)的代碼中,但下列兩個(gè)值具有特殊的語(yǔ)義:SHN_ABS制定符號(hào)的絕對(duì)值,不因重定位而改變SHN_UNDER標(biāo)準(zhǔn)未定義符號(hào),必須通過(guò)外部來(lái)源(比如鏈接目標(biāo)文件或者庫(kù))來(lái)解決。同樣,符號(hào)表也有一個(gè)64位的變體,除了使用的數(shù)據(jù)類型不同,其內(nèi)容與32位的對(duì)應(yīng)結(jié)構(gòu)是相同的,如下所示;typedef struct elf64_sym Elf64_Word st_name; / Symbol name, index in string tbl unsigned char st_info; / Type and binding attributes unsigned char st_othe

41、r; / No defined meaning, 0 Elf64_Half st_shndx; / Associated section index Elf64_Addr st_value; / Value of the symbol Elf64_Xword st_size; / Associated symbol size Elf64_Sym;我們可以使用readelf來(lái)查找程序的符號(hào)表中所有的符號(hào),圖中標(biāo)出的六項(xiàng)在目標(biāo)文件中特別重要,其它的數(shù)據(jù)項(xiàng)由編譯器自動(dòng)生成的,與我們的討論無(wú)關(guān)O(_)O分析:源文件中名稱”test.c”存儲(chǔ)為一個(gè)絕對(duì)值,它是常數(shù)不隨著重定位而改變。該局部符號(hào)使用SET

42、_FILE類型,將一個(gè)目標(biāo)文件關(guān)聯(lián)到對(duì)應(yīng)的源文件、文件中定義了兩個(gè)函數(shù)main和add,存儲(chǔ)為SET_FUNC類型的全家符號(hào),兩個(gè)符號(hào)都執(zhí)行節(jié)1,即ELF文件中的.text節(jié),保存了兩個(gè)函數(shù)的機(jī)器代碼。_nldbl_printf、puts、exit符號(hào)屬于未定義引用,節(jié)索引值為UND,因而在程序鏈接時(shí)它們必須關(guān)聯(lián)到標(biāo)準(zhǔn)庫(kù)中的函數(shù),或者其它庫(kù)中以該名稱定義的函數(shù)。因?yàn)榫幾g器不指定所涉及的符號(hào)的類型,因而這兩個(gè)符號(hào)的類型都是STT_NOTYPE。第四部分重定位項(xiàng)Relocation Entries重定位是將ELF文件中為定義符號(hào)關(guān)聯(lián)到有效值的處理過(guò)程,在我們的例子test.o中,這意味著對(duì)prin

43、tf、exit、puts的未定義引用必須替換為該進(jìn)程的虛擬地址空間中相應(yīng)的機(jī)器代碼所在的空間,在目標(biāo)文件中用到的符號(hào)之處都將被替換。對(duì)用戶空間程序的替換,內(nèi)核并不會(huì)牽涉其中。因?yàn)樗械奶鎿Q操作都是由外面工具完成的。當(dāng)對(duì)于Linux內(nèi)核模塊來(lái)說(shuō),情況有所不同,因?yàn)閮?nèi)核所接受到得模塊裸數(shù)據(jù)與存儲(chǔ)在二進(jìn)制文件中的數(shù)據(jù)完全相同,內(nèi)核負(fù)責(zé)重定位操作。在每個(gè)目標(biāo)文件中都有一個(gè)專門的表,包含了重定位表項(xiàng),標(biāo)識(shí)了需要進(jìn)行重定位的地方。每個(gè)表項(xiàng)都包含了下列信息:第一:一個(gè)偏移量,用來(lái)指定所要修改的項(xiàng)的位置第二:對(duì)符號(hào)的引用(符號(hào)表的索引),提供了所要插入的重定位位置的數(shù)據(jù)為了說(shuō)明如何使用重定位信息,我們來(lái)看一下

44、此前test.c測(cè)試程序,我們用readelf顯示的所有重定位項(xiàng)如下圖所示:在程序運(yùn)行時(shí)或者test.o產(chǎn)生可執(zhí)行文件時(shí),如果某些機(jī)器代碼使用了虛擬地址空間中位置尚不明確的符號(hào)或者函數(shù),則會(huì)使用offset列的信息。main函數(shù)的匯編語(yǔ)言代碼調(diào)用了若干函數(shù),分別位于偏移量0x 7c,0x98,0xa0如下所示:00000050 <main>: 50: 94 21 ff e0 stwu r1,-32(r1)54: 7c 08 02 a6 mflr r0 58: 90 01 00 24 stw r0,36(r1)5c: 93 e1 00 1c stw r31,28(r1) 60: 7c

45、3f 0b 78 mr r31,r1 64: 38 00 00 03 li r0,3 68: 90 1f 00 08 stw r0,8(r31)6c: 38 00 00 04 li r0,4 70: 90 1f 00 0c stw r0,12(r31) 74: 80 7f 00 08 lwz r3,8(r31) 78: 80 9f 00 0c lwz r4,12(r31)7c: 48 00 00 01 bl 7c <main+0x2c> 80: 90 7f 00 10 stw r3,16(r31) 84: 3c 00 00 00 lis r0,0 88: 30 00 00 1c a

46、ddic r0,r0,288c: 7c 03 03 78 mr r3,r0 90: 80 9f 00 10 lwz r4,16(r31)94: 4c c6 31 82 crclr 4*cr1+eq 98: 48 00 00 01 bl 98 <main+0x48>9c: 38 60 00 00 li r3,0a0: 48 00 00 01 bl a0 <main+0x50>備注:我們可以用objdump工具查看,示意圖如下:在printf和add函數(shù)的地址已經(jīng)確定后,必須將它們插入指定的偏移量處,以便可以生成正確運(yùn)行的可執(zhí)行代碼。4.1 Linux內(nèi)核中相關(guān)的數(shù)據(jù)結(jié)構(gòu)由

47、于技術(shù)原因,有兩種類型的重定位信息,有兩種稍有不同的數(shù)據(jù)結(jié)構(gòu)表示:第一種:普通重定位,SHT_REL類型的節(jié)中的重定位表項(xiàng)由以下的數(shù)據(jù)結(jié)構(gòu)表示:typedef struct elf32_rel Elf32_Addr r_offset; Elf32_Word r_info; Elf32_Rel;解釋:r_offset:指定需要重定位的項(xiàng)的位置r_info:不僅提供了符合表中的一個(gè)位置,還包括重定位類型的有關(guān)信息。這是通過(guò)把值劃分為兩個(gè)部分來(lái)達(dá)到的。第二種:需要添加常數(shù)的重定位項(xiàng)只出現(xiàn)在SHT_RELA類型的節(jié)中,這類結(jié)構(gòu)由下列數(shù)據(jù)結(jié)構(gòu)定義:typedef struct elf32_rela El

48、f32_Addr r_offset; Elf32_Word r_info; Elf32_Sword r_addend; Elf32_Rela;解釋:這里除了有第一種重定位類型提供的r_offset和r_info字段之外,還補(bǔ)充了r_addend字段,其中存放一個(gè)稱之為加數(shù)(addend)的值。在計(jì)算重定位值時(shí),將根據(jù)重定位類型,對(duì)該值進(jìn)行不同的處理。請(qǐng)注意:在使用elf32_rel時(shí)也會(huì)出現(xiàn)加數(shù)這個(gè)值,盡管在數(shù)據(jù)結(jié)構(gòu)中沒有明確的保存,但鏈接器根據(jù)該值應(yīng)該在內(nèi)存中出現(xiàn)的位置,將計(jì)算出的重定位長(zhǎng)度作為加數(shù)填入,該值的用途將在下面的例子中說(shuō)明。對(duì)兩種重定位類型,都有功能等效的64位數(shù)據(jù)結(jié)構(gòu):type

49、def struct elf64_rel Elf64_Addr r_offset; /Location at which to apply the action Elf64_Xword r_info; /index and type of relocation Elf64_Rel; typedef struct elf64_rela Elf64_Addr r_offset; /Location at which to apply the action Elf64_Xword r_info; /index and type of relocation Elf64_Sxword r_ad

50、dend; /Constant addend used to compute value Elf64_Rela; 解釋:這兩個(gè)數(shù)據(jù)結(jié)構(gòu)和32位的對(duì)應(yīng)類型非常相似,這里就不在討論 重定位類型ELF標(biāo)準(zhǔn)定義了很多重定位類型,對(duì)于每種支持的體系結(jié)構(gòu),都有一個(gè)獨(dú)立的集合,這些類型大部分用于生成動(dòng)態(tài)庫(kù)或者與裝載位置無(wú)關(guān)的代碼。Linux內(nèi)核只對(duì)模塊的重定位感興趣,因此使用下面的兩種重定位類型:相對(duì)重定位和絕對(duì)重定位。我們通過(guò)一個(gè)例子介紹一下相對(duì)從定位:1000058c <add>:1000058c: 94 21 ff e0 stwu r1,-32(r1)100005d8: 4e 80 00 20 blr100005dc <main>:100005dc: 94 21 ff e0 stwu r1,-32(r1).10000608: 4b ff ff 85 bl 1000058c <add>.10000624: 48 01 03 35 bl 10010958 <_nldbl_printfplt>10000628: 38 60 00 00 li r3,0

溫馨提示

  • 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ù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 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)論