版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、第一部分ELF格式概述ELF(Executable and Linkable Format)是一種對可執(zhí)行文件、目標文件以及庫文件使用的文件格式,它在Linux下成為標準文件已經有很長的一段時間,代替了早期的格式。ELF格式的一個優(yōu)點是同一個文件格式可以用在Linux Kernel支持的所有體系結構之上。這不僅簡化了用戶空間工具程序的創(chuàng)建,也簡化了內核自身的程序設計,比如必須為可執(zhí)行程序生成裝載例程時。但是文件格式相同并不意味著不同系統(tǒng)上的程序之間存在二進制兼容性,例如FreeBSD和Linux都使用ELF作為二進制格式,但是FreeBSD上的程序不能運行于Linux上,因為兩者在系統(tǒng)調用機制
2、和系統(tǒng)調用語義方面仍然有所不同,反之亦然;如果想讓二者之間的程序能夠運行,必須要有一個中間仿真層。同樣PowerPC平臺編譯的ELF程序,也不能在X86平臺的機器上運行,反之依然,因為兩者的體系結構是完全不同的。但是由于ELF格式的存在,相同體系結構上的ELF程序本身的相關信息,以及程序的各個部分在二進制文件中的編碼方式都是相同的。Linux不僅將ELF用于用戶空間應用程序和庫,還用于工具模塊,另外Linux內核本身也是ELF格式。備注:ELF文件是一種開放的格式,其規(guī)范可以自由獲得。ELF文件有三種類型:可重定位文件:也就是通常稱的目標文件,后綴為.o;共享文件:也就是通常稱的庫文件,后綴為
3、.so;可執(zhí)行文件:本文主要討論的文件格式;總的來說,可執(zhí)行文件的格式與上述兩種文件的格式之間的區(qū)別主要在于觀察的角度不同:一種稱為連接視圖(Linking View),一種稱為執(zhí)行視圖(Execution View)。示意圖如下:第二部分布局和結構ELF Header:除了用于標識ELF文件的幾個字節(jié)之外,ELF文件頭還包含了有關文件類型和大小的信息,以及文件加載后程序執(zhí)行的入口信息等等,總之ELF頭部是一個關于本ELF文件的路線圖(road map),從總體上描述文件的結構。程序表頭(Program Head Table):向系統(tǒng)提供了可執(zhí)行文件的數據在進程虛擬地址空間中組織方式的相關信息
4、,它還表示了文件可能包含的段的數目,段的位置以及用途。各個段SegmentX(X=1,2,)和節(jié)SectionX(X=1,2,):保存了與文件相關的各種形式的數據,例如符號表、實際的二進制代碼、固定值或者程序使用的數值常數。節(jié)頭表(Section Header Table):包含了與各段相關的附加信息。下面我們一個例子,用readelf工具來分析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命令來顯示編譯器生成的兩個文件的信息:一個是可執(zhí)行文件,另一個是可重定位文件,示意圖如下:2.1 ELF header文件結構我們用readelf工具來分析上例中生成ELF文件的ELF header,視圖如下:在test文件的開始處是四個標志字節(jié),0x 7f、0x45、0x4c、0x46。其中的0x45、0x4c、0x46分別代碼“E”“L”“F
6、”的ASCII碼,這使得所有處理ELF文件的工具都可以識別ELF類型的文件。還有一些與機器體系結構相關的信息。比如上圖中Machine類型為PowerPC表明該ELF文件運行于PowerPC平臺;ELF32表明這是一個運行在32位平臺的機器;文件類型為EXEC表明這是一個可執(zhí)行程序;Version用于區(qū)分當前ELF文件的各個修訂版本,當前ELF文件是基于版本1;另外還包含ELF文件各個部分的長度和索引的位置,后面會相信討論。如果ELF文件是可重定位文件,不同的字段如下圖所示:如圖所示:的文件類型為REL,即它是一個可重定位的文件,其代碼可以移動到任意位置,該文件沒有程序頭Program Hea
7、ders,對于需要鏈接的對象而言該表是不需要的,所以其所以長度均為0。2.2 程序頭表(Program Head Table)分析下面我們來分析一下ELF可執(zhí)行文件test中Program Head Table,示意圖如下:在Program Headers中列出了8個段,這些段組成了最終在內存中執(zhí)行的程序,并且還提供了各個段在虛擬地址空間和物理地址空間中的位置、大小、訪問授權和其它方面的信息。從上圖中我們可以看出,示例程序中包含的各個段的語義如下:PHDR:保存Program Headers TableINTERP:制定程序已經從可執(zhí)行文件映射到內存之后,必須調用的解釋器。在這里,解釋器并不意
8、味著二進制文件的內容必須由另一個程序解釋(比如Java字節(jié)代碼必須由Java虛擬機來解釋),它指的是這樣的程序:通過鏈接其它庫來滿足未解決的引用。LOAD:表示一個需要從二進制文件映射到虛擬地址空間的段,其中保存常量數據(如字符串),程序的目標代碼等等。DYNAMIC:保存了由動態(tài)鏈接區(qū)(即INTERP中指定的解釋器)使用的信息。NOTE:保存了專用信息,與當前主題無關。GNU_EH_FRAM和GNU_STACK:用來分析棧幀,與體系結構相關,在PowerPC體系結構中需要分析棧幀實現(xiàn)回溯。備注:我們需要特別指出段是可以重疊的,比如在上圖中LOAD段從物理地址0x1000 0000到物理地址0
9、x1000 0000+0x007f8的范圍,該范圍包含了PHDR、INTERP、NOTE、GNU_EH_FRAM和GNU_STACK段,在ELF標準中是允許這種行為的。2.3 節(jié)頭標(Section Header Table)分析在ELF文件中描述各段的內容時,是指定了哪些節(jié)的數據映射到段中。在ELF中是有一張節(jié)頭表來管理文件中的各個節(jié),readelf同樣可以用于顯示可重定位文件中的各個節(jié),示意圖如下:這里指定的偏移量0x168是相對于二進制文件的。節(jié)信息不需要復制到在虛擬地址空間中做為可執(zhí)行文件創(chuàng)建的最終進程映象,盡管如此在ELF二進制文件中節(jié)信息總是存在的。每個節(jié)都指定了一個類型,定義了節(jié)
10、數據的語義。包含PROGBITS、SYSTAB、RELA和STRTAB。PROGBITS:程序必須解釋的信息,比如二進制代碼,程序的二進制代碼被稱之為text,指的是用作機器代碼的二進制信息SYSTAB:符號表RELA:重定位信息STRTAB:用于存儲與ELF相關的字符串,但與程序沒有直接的關系。各個節(jié)Section都指定了其大小和在二進制文件內部的偏移量。地址Addr:指定加載到虛擬地址空間的位置,因為我們的例子中處理的是一個可鏈接的對象,目標地址是沒有定義的,因而表示為0。標志Flg:指出各個節(jié)Section如何被訪問或者處理。我們對標志A比較感興趣,因為它控制著裝載文件時是否將節(jié)的數據復
11、制到虛擬地址空間中。盡管節(jié)的名稱是可以自由選擇的,但是Linux(和其它所有使用ELF的類UNIX系統(tǒng))都提供了一些標準節(jié),其中一些是強制性的,例如總是有一個名為.text的節(jié)來保存二進制代碼(即與該ELF文件相關聯(lián)的程序信息),保存了節(jié)的重定位信息。備注:節(jié)名以點開始是由系統(tǒng)自身使用的,如果應用程序要想定義自身的節(jié),就不應該以點開頭,以避免與系統(tǒng)節(jié)名相沖突。可執(zhí)行程序包含了一些重定位的信息,示意圖如下:與可重定位文件的11個節(jié)相比,可執(zhí)行文件有36個節(jié),并非所有的節(jié)都與我們的討論有關。上圖中標出的節(jié)是有具體意義的:.interp:保存了解釋器的文件名,例如在我們的例子中用的是是;.data:
12、保存了初始化的數據,這是普通程序數據的一部分,可以在程序運行時修改;.rodata:保存了只讀數據,可以讀但不可以修改,例如編譯器將所有出現(xiàn)在printf中的靜態(tài)字符串封裝到該節(jié);.init和.fini:保存了進程初始化和結束時所用的代碼,這兩個節(jié)通常是編譯器自動添加的,無需應用程序員關注;.hash:是一個散列表,允許在不對全表元素進行線性搜索的情況下,快速訪問所有的符號表項。備注:可執(zhí)行文件ELF各個Section中的Addr字段保存了有效地址的值,因為相應的代碼必須映射到虛擬地址空間中某些已經定義好的位置,在基于PowerPC的Linux中應用程序通常使用0x1000 0000以上的內存
13、區(qū)間。2.4 符號表(Symbol Table)符號表是每個ELF文件的一個重要的組成部分,因為它保存了程序實現(xiàn)或者使用過程中所有的(全局)變量和函數,如果程序使用了一個自身代碼沒有定義的符號,則稱之為未定義符號(例如例子中的printf函數是定義在C標準庫中,自身沒有定義),此類應用必須在靜態(tài)鏈接期間用其它的目標模塊或者庫來解決,或者在加載期間使用來解決。我們可以使用nm工具生成程序定義或者使用的所有符號列表,如果下圖所示:左側一列給出了符號的值,即符號定義在目標文件中的位置,例子中包含了兩個不同的符號類型:如果函數定義在text段,縮寫為T;而未定義的引用用U標明。邏輯上沒有定義的函數沒有
14、符號值??稍诳蓤?zhí)行ELF文件test中,還會有更多的符號。當時大多數都是由編譯器自動生成的,供運行時系統(tǒng)內部使用,以下例子中我們僅標出了同時出現(xiàn)在中的符號:1000058c T add100005dc T main解釋:exit和puts雖然是沒有定義的,但是同時增加了一些版本信息,標明能夠提供函數的GNU標準庫的最低版本,在我們的例子中要求庫的最低版本不能低于,這意味著該程序無法使用Libc5和Lib4工作,因為Libc4和Libc5是Linux專用的C標準庫,是該庫的第一個跨平臺版本,它替換了就版本,它替換了舊版本。由函數本身定義的add和main已經移到虛擬地址空間中的固定位置(在文件加
15、載時,對應的代碼將會映射到這些位置)ELF使用以下的三個節(jié)(Section);來實現(xiàn)字符串的管理:.systalb:確定字符串的名稱與其值的索引,但符號的名稱不是以字符串的形式出現(xiàn)的,而是表示為某個字符串數組的索引;.strtab:保存了字符串數組;.hash:保存了一個hash表用于快速查找符號。簡而言之,符號名在字符串表(string table )中的位置和符號的值,為了說明字符串表示如何管理ELF中的字符串,我們看下面的例子:表的第一個字節(jié)是NULL(即ASCII碼值為0),后續(xù)的各個字符串通過NULL字節(jié)分割,為了引用字符串必須指定一個位置,即字符串的索引。這將選擇下一個NULL字節(jié)
16、之前的所有字符(如果用NULL字節(jié)的位置作為索引,那么將會對應空串)。如果允許索引不僅僅選擇字符串的起始地址,也可以選擇字符串中間的任何位置,就能夠支持字串的用法(但是非常受限)上述的.strtab節(jié)并不是默認情況下的唯一的字符串表,.shstrtab用于存放文件中各個節(jié)的文本名稱(比如.text)第三部分 Linux 內核中的數據結構內核在兩處使用了ELF文件格式:ELF用于處理可執(zhí)行文件和庫,用于實現(xiàn)模塊。這些地方使用了不同的代碼來讀取和操作數據,但這兩種情況下都利用了我們本文所介紹的數據結構,其基礎是文件,它實現(xiàn)了ELF標準,基本沒有做改動!3.1 數據類型ELF是一個與CPU和體系結構
17、無關的格式,它不能依賴與特定的字長和字節(jié)序(大端還是小端),至少對文件中的那些需要在所有系統(tǒng)上讀取和理解的數據元素來說是這樣。出現(xiàn)在.text段中的機器代碼存儲為宿主系統(tǒng)的表示格式,以避免轉換工作。為此Linux內核定義了一些數據類型,在所有體系結構上具有相同的位寬,如下所示:/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;因為體系結構相關的代碼必須總是明確定義整數類型的符號和位寬,ELF標準的數據類型可以毫不費力的通過typedef實現(xiàn)3.2 ELF頭部格式實現(xiàn)對ELF格式的各種頭部文件,32位和64位需要分別定義其數據結構: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個字節(jié),這些字節(jié)在所有的體系結構上都是由char數據類型表示的,前4個字節(jié)分別為0x7f(即ASCII碼中的DEL)和字母E、L、F,我們在節(jié)曾介紹過。其它的字節(jié)位置有其特定的含義:e_ident4:EI_CLASS標識文件的類型,將文件分為32位和64位兩類,即ELFCLASS32和ELFCLASS64e_ident5:EI_DATA指定了格式所使用的字節(jié)序,即ELFDATA2LSB(小端)和ELFDATA2MSB()大端ei
21、_ident6:EI_VERSION指定ELF頭文件的版本(該版本可能獨立于數據段的版本),當前,值允許使用EV_CURRENT,這是第一個版本ei_ident7: EI_OSABI指定采用的OS的ABI版本,當前是UNIX System V ABI從ei_ident8EI_PAD起的剩余字節(jié):用NULL來填充,因為這些位置上ELF不需要。e_type用去區(qū)分各種ELF文件的類型,如下所示:e_machine指定了文件所需的體系結構,下表列出了Linux支持的各種選項:注意:每種體系結構都需要定義elf_check_arch,并由內核的通用代碼使用,來確保加載的ELF文件可以在相應的體系結構上
22、運行。e_version:保存了版本信息,用于區(qū)分不同的ELF變體,目前該規(guī)范僅支持版本1,由EV_CURRENT指出來e_entry:給出了文件在虛擬內存中的入口點,在程序已經加載并映射到內存之后,執(zhí)行開始的位置e_phoff:保存了程序頭表(Program Header Table)在二進制ELF文件中的偏移e_shoff:保存了節(jié)表頭(Section Header Table) 在二進制ELF文件中的偏移e_flags:保存了特定于處理器的標志,當前Linux內核不適用該標志e_ehsize:指定了ELF Header的長度,單位是字節(jié)e_phentsize:指定了Program Hea
23、der中一個表項的長度,單位是字節(jié)(所有表項的長度均相同)e_phnum:指定了Program Header Table中表項的數目e_shentsize:指定了Section Header Table中一個表項的長度,單位是字節(jié)(所有表項的長度均相同)e_shnum:指定了節(jié)頭表中項的數目e_shstrndx:保存了包含各節(jié)(Section)名稱的字符串表在Section Header中的索引位置備注:64位下ELF頭的數據結構可以同樣定義,唯一的差別在于其中使用了對應的64位數據類型,這使得頭文件稍大。但兩者數據結構的前16字節(jié)都是相同的,兩種體系結構的類型能夠根據這些字節(jié)來識別不同字長機
24、器的ELF文件3.3 Program Header實現(xiàn)程序頭表由幾個項實現(xiàn),其處理方式類似于數組項(項的數據由ELF頭中的e_phnum指定),表項的數據類型定義為獨立的結構,在32的機器上其內容如下: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:表示當前項描述的段的種類,為其定義了下列常數:PT_NULL:表示沒有使用的段PT_LOAD:表示可裝載段,在程序執(zhí)行前從二進制文件映射到內存PT_DYNAMIC:表示段包含了用于動態(tài)鏈接器的信息PT_INTERP:表示當前段指定了用于動態(tài)鏈接的解釋程序,比如PT_NOTE:指定一個段,其中可能含有專有的編譯器信息還有兩個常數PT_LOPROC和PT_HIGHPROC用于定義與處理器相關的用途,內核并不使用 p_offset:指出所描述段從ELF文件起始處的偏移量,以字節(jié)為單位p_vaddr:給出段的數據映射到虛擬地址空間中的位置(對應PT_LOAD類型的段),只支持
26、物理尋找,不支持虛擬尋址的系統(tǒng)將使用p_paddr保存的信息p_filesz:指定了段在ELF文件中的長度p_memsz:指定了段在虛擬地址空間中的長度,與文件中物理段的差值可以通過階段數據或者填充0字節(jié)來填充p_flags:保存了標志信息,定義了段的訪問權限:PF_R讀權限,PF_W寫權限,PF_X執(zhí)行權限p_align:指定了段在內存和二進制文件中的對齊方式(即p_vaddr和p_offset地址必須是模p_align,即為p_align的倍數)比如p_align的值為0x1000=4KB,這意味著段必須4KB對齊。對64為體系結構定義的類似的數據結構,與32位相比,唯一的差別在于所使用的
27、數據結構,各數據項的語義都是相同的。其具體內容如下: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代碼實現(xiàn)節(jié)頭表通過數組實現(xiàn),每個數組項包含一節(jié)的信息,各個節(jié)構成了程序頭表Program Header Table定義的各個段的內容。下來數據結構表示一個節(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é)的名稱,其值不是字符串本身,而是字符串表的一個索引。sh_type:指定了節(jié)的類型,有下列的類型可用: SH_NULL表示該節(jié)不使用,其數據將會被忽略 SH_PROGBITS保存程序的相關信息,其格式是不定義的,與這里的討論無關 SH_SYMTAB保存一個符合表,其結構我們在節(jié)已經討論,SH_DYN
30、SYM也保存一個符號表,二者的差別我們在稍后討論 SH_STRTAB表示一個包含字符串表的節(jié) SH_RELA和SHT_RELA保存重定位信息,我們也將在后面討論 SH_HASH定義了一個節(jié)保存HASH表,用于快速的查找符號表中的項 SH_DYNAMIC保存了動態(tài)鏈接表的信息,我們也將在后面討論還有類型值SHT_HIPROC、SHT_LOPROC、SHT_HIUSER、SHT_LOUSER,這些專項特定于CPU和應用程序的用途,與這里討論的內容無關sh_flags:表示節(jié)是否可重寫(SHF_WRITE),是否將其分配虛擬內存(SHR_ALLOC),是否包含可執(zhí)行的機器代碼(SHF_EXECINS
31、TR)sh_addr:指定節(jié)映射到虛擬地址空間中的位置sh_offset:指定了節(jié)在內存中的位置sh_size:指定了節(jié)的長度sh_link:引用另一個節(jié)頭表項,可以根據節(jié)類型進行不同的解釋,其性能我們在后面單獨討論sh_info和sh_link聯(lián)用,其確切語義我們也會在下文中再討論。sh_addralign:指定了節(jié)數據在內存中的對齊方式sh_entsize:指定了節(jié)中各個數據項的長度,前提是這些數據項的長度均相同。例如字符串表根據節(jié)類型不同,sh_link和sh_info的用法也不盡相同,具體情況如下:第一:SHT_DYNAMIC類型的節(jié)使用sh_link指向節(jié)數據指向的字符串表,這種情況
32、下不使用sh_info,sh_info設置為0;第二:散列表(SHT_HASH類型的節(jié))使用sh_link指向所散列的符合表,sh_info不使用第三:類型為SHT_RELA和SHT_REL的重定位節(jié),使用sh_link指向相關的符號表,sh_info中保存的是節(jié)頭表中的索引,表示對哪個節(jié)進行重定向;第四:sh_link指定了用作符號表的字符串表(SHT_SYMTAB和SHT_DYNSYM),而sh_info表示符號表中緊隨最后一個局部符號之后的索引位置(STB_LOCAL類型)而64位系統(tǒng)有一個單獨的數據結構,其內容和32系統(tǒng)相同,除了使用64位的數據類型。內容如下: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標準定義了若干固定名稱的節(jié),這些節(jié)用于執(zhí)行大多數目標文件所需要的標準任務,所有名稱都從點開始,以便與用戶定義的節(jié)或者非標準的節(jié)相區(qū)分。最
35、重要的標準節(jié)如下所示:.bss:保存程序未初始化的數據,在程序開始時填充0字節(jié).data:保存已經初始化的數據,例如在編譯期間用靜態(tài)數據初始化的結構,這些數據可以在程序運行期間更改.rodata:保存了程序使用的只讀數據,不能更改.dynamic和.dynstr:保存了動態(tài)信息,后面討論.interp:保存了程序解釋器的名稱,形式為字符串.shstrtab:包含了一個字符串表,定義了節(jié)名稱.strtab:保存了一個字符串表,主要包含了符合表所需要的各個字符串.symtab:保存了二進制文件的符合表.init和.fini:保存了程序初始化和結束時執(zhí)行的機器指令,這兩個節(jié)的內容有編譯器或者輔助工具
36、自動創(chuàng)建,主要是為程序建立一個適當的運行環(huán)境.text:保存了主要的機器指令3.5 字符串表String Tables字符串表的格式我們在的結尾處討論過,因為其格式非常動態(tài),內核不同提供一個固定的數據結構,必需手工分析現(xiàn)存數據3.6 符號表Symbol Tables符號表保存了查找程序符號、為符號賦值、重定位符號所需要的全部信息,如上所述,有一個專門類型的節(jié)來保存符號表,其格式有下列數據結構定義: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;解釋:符號表的任務就是將一個字符串和一個值關聯(lián)起來,例如printf符號表表示printf函數在虛擬空間中的地址,該函數的機器碼就存在于該地址處。符號也可能有絕對值,有程序解釋,例如數據常數。一個符號的確切用途有st_info定義,它分為兩個部分(比特位如何劃分與我們的討論不相關),其中定義了下列信息:第一:符號的綁定(binding),這確定了符號的可見性,允許有下列三種不同的設置:1:局部符號(STB_LOCAL):只有在目標文件內部可見,在于程序的其它部分連接時
38、是不可見的。如果一個程序的幾個目標文件都定義同名的此類符號是沒有問題的,這些局部符號間彼此不會干擾。2:全局符號(STB_GLOBAL):在定義的目標文件內部可見,也可以由構成程序的其它目標文件引用,每個全局符號在一個程序的內部只引用一次,否則鏈接器就會報錯。執(zhí)行全局符號的位定義引用,將在重定位期間確定相關符號的位置,如果對全局符號的未定義引用無法解決,則拒絕程序執(zhí)行或者靜態(tài)綁定。3:弱符號(STB_WEAK):在整個程序中可見,當可以由多個定義。如果程序中一個全局符號和一個局部符號名稱相同,則全局符號就優(yōu)先處理;即使一個弱符號沒有定義,程序也是可以靜態(tài)鏈接或者動態(tài)鏈接,這種情況下若符號的值為
39、0第二:符號類型也有若干備選項,只有以下三個與目標的主題相關(對其它值的描述由ELF標準提供)STT_OBJCT:符號關聯(lián)到一二數據對象,比如變量,數組和指針;STT_FUNC:符號關聯(lián)到一個函數或者過程;STT_NOTTYPE:符號的類型為制定,用于未定義引用;Elf32_Sym結構體除了包含st_name,st_value,st_info,還包含以下成員,其語義如下:st_size:制定對象的長度,例如一個指針的長度或者struct對象中包含的字節(jié)數,如果長度有位置,其值可以設置為0;標準的ELF標準不支持st_otherst_shndx:保存一個節(jié)(在節(jié)頭表)中的索引,符號將綁定到該節(jié),
40、該符號通常定義在此節(jié)的代碼中,但下列兩個值具有特殊的語義:SHN_ABS制定符號的絕對值,不因重定位而改變SHN_UNDER標準未定義符號,必須通過外部來源(比如鏈接目標文件或者庫)來解決。同樣,符號表也有一個64位的變體,除了使用的數據類型不同,其內容與32位的對應結構是相同的,如下所示;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來查找程序的符號表中所有的符號,圖中標出的六項在目標文件中特別重要,其它的數據項由編譯器自動生成的,與我們的討論無關O(_)O分析:源文件中名稱”test.c”存儲為一個絕對值,它是常數不隨著重定位而改變。該局部符號使用SET
42、_FILE類型,將一個目標文件關聯(lián)到對應的源文件、文件中定義了兩個函數main和add,存儲為SET_FUNC類型的全家符號,兩個符號都執(zhí)行節(jié)1,即ELF文件中的.text節(jié),保存了兩個函數的機器代碼。_nldbl_printf、puts、exit符號屬于未定義引用,節(jié)索引值為UND,因而在程序鏈接時它們必須關聯(lián)到標準庫中的函數,或者其它庫中以該名稱定義的函數。因為編譯器不指定所涉及的符號的類型,因而這兩個符號的類型都是STT_NOTYPE。第四部分重定位項Relocation Entries重定位是將ELF文件中為定義符號關聯(lián)到有效值的處理過程,在我們的例子test.o中,這意味著對prin
43、tf、exit、puts的未定義引用必須替換為該進程的虛擬地址空間中相應的機器代碼所在的空間,在目標文件中用到的符號之處都將被替換。對用戶空間程序的替換,內核并不會牽涉其中。因為所有的替換操作都是由外面工具完成的。當對于Linux內核模塊來說,情況有所不同,因為內核所接受到得模塊裸數據與存儲在二進制文件中的數據完全相同,內核負責重定位操作。在每個目標文件中都有一個專門的表,包含了重定位表項,標識了需要進行重定位的地方。每個表項都包含了下列信息:第一:一個偏移量,用來指定所要修改的項的位置第二:對符號的引用(符號表的索引),提供了所要插入的重定位位置的數據為了說明如何使用重定位信息,我們來看一下
44、此前test.c測試程序,我們用readelf顯示的所有重定位項如下圖所示:在程序運行時或者test.o產生可執(zhí)行文件時,如果某些機器代碼使用了虛擬地址空間中位置尚不明確的符號或者函數,則會使用offset列的信息。main函數的匯編語言代碼調用了若干函數,分別位于偏移量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函數的地址已經確定后,必須將它們插入指定的偏移量處,以便可以生成正確運行的可執(zhí)行代碼。4.1 Linux內核中相關的數據結構由
47、于技術原因,有兩種類型的重定位信息,有兩種稍有不同的數據結構表示:第一種:普通重定位,SHT_REL類型的節(jié)中的重定位表項由以下的數據結構表示:typedef struct elf32_rel Elf32_Addr r_offset; Elf32_Word r_info; Elf32_Rel;解釋:r_offset:指定需要重定位的項的位置r_info:不僅提供了符合表中的一個位置,還包括重定位類型的有關信息。這是通過把值劃分為兩個部分來達到的。第二種:需要添加常數的重定位項只出現(xiàn)在SHT_RELA類型的節(jié)中,這類結構由下列數據結構定義:typedef struct elf32_rela El
48、f32_Addr r_offset; Elf32_Word r_info; Elf32_Sword r_addend; Elf32_Rela;解釋:這里除了有第一種重定位類型提供的r_offset和r_info字段之外,還補充了r_addend字段,其中存放一個稱之為加數(addend)的值。在計算重定位值時,將根據重定位類型,對該值進行不同的處理。請注意:在使用elf32_rel時也會出現(xiàn)加數這個值,盡管在數據結構中沒有明確的保存,但鏈接器根據該值應該在內存中出現(xiàn)的位置,將計算出的重定位長度作為加數填入,該值的用途將在下面的例子中說明。對兩種重定位類型,都有功能等效的64位數據結構: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; 解釋:這兩個數據結構和32位的對應類型非常相似,這里就不在討論 重定位類型ELF標準定義了很多重定位類型,對于每種支持的體系結構,都有一個獨立的集合,這些類型大部分用于生成動態(tài)庫或者與裝載位置無關的代碼。Linux內核只對模塊的重定位感興趣,因此使用下面的兩種重定位類型:相對重定位和絕對重定位。我們通過一個例子介紹一下相對從定位: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. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2022年大學數學專業(yè)大學物理下冊月考試卷D卷-附解析
- 2022年大學航空航天專業(yè)大學物理下冊期中考試試題D卷-附解析
- 2022年大學電氣信息專業(yè)大學物理下冊模擬考試試題D卷-含答案
- 2022年大學藥學專業(yè)大學物理下冊期末考試試題D卷-附答案
- 年度磷酸氧鈦鉀晶體(KTP)戰(zhàn)略市場規(guī)劃報告
- 年度室內清潔健康電器競爭策略分析報告
- 大型項目施工組織設計方案
- 城市建設圍擋管理方案
- 跨學科閱讀活動實施方案
- 銷售團隊管理人員360度反饋評估方案
- 消防安全培訓內容
- 2024-2030年辣椒種植行業(yè)市場深度分析及發(fā)展策略研究報告
- 變電站綠化維護施工方案
- 校園展美 課件 2024-2025學年人美版(2024)初中美術七年級上冊
- 2024版《糖尿病健康宣教》課件
- ktv保安管理制度及崗位職責(共5篇)
- 腦出血試題完整版本
- 義務教育信息科技課程標準(2022年版)考試題庫及答案
- 建筑施工安全生產責任書
- 新員工三級安全教育考試試題參考答案
- 公司年會策劃及執(zhí)行服務合同
評論
0/150
提交評論