ELF(Executable and Linkable Format)即可執(zhí)行連接文件格式,是一種比較復(fù)雜的文件格式,但其應(yīng)用廣泛。與linux下的其他可執(zhí)行文件(a.out,cof)相比,它對節(jié)的定義和gnu工具鏈對它的支持使它十分靈活,它保存的足夠了系統(tǒng)相關(guān)信息使它能支持不同平臺上的交叉編譯和交叉鏈接,可移植性很強.同時它在執(zhí)行中支持動態(tài)鏈接共享庫。?
通過本文,可以大致了解Linux系統(tǒng)中ELF格式文件的分類,組成,作用,以及其中包含的內(nèi)容。另外后面介紹了幾種常用的對elf文件進行操作的工具,并且對其使用進行簡單舉例,便于對elf文件有一個比較直觀的理解。?
主要內(nèi)容:?
[描述]?
1 ELF文件簡介?
2 ELF文件格式?
3 ELF的特性?
[舉例]?
1 readelf工具?
2 objcopy工具?
3 objdump工具?
4 nm工具?
5 ldd工具?
[其它]?
[描述]?
1 ELF文件簡介?
ELF(Executable and Linkable Format)即可執(zhí)行連接文件格式,是Linux,SVR4和Solaris2.0默認的目標文件格式,目前標準接口委員會TIS已將ELF標準化為一種可移植的目標文件格式,運行于32-bit Intel體系微機上,可與多種操作系統(tǒng)兼容。分析elf文件有助于理解一些重要的系統(tǒng)概念,例如程序的編譯和鏈接,程序的加載和運行等。?
(1)ELF文件類型:?
種類型的ELF文件:?
a)可重定位文件:用戶和其他目標文件一起創(chuàng)建可執(zhí)行文件或者共享目標文件,例如lib*.a文件。?
b)可執(zhí)行文件:用于生成進程映像,載入內(nèi)存執(zhí)行,例如編譯好的可執(zhí)行文件a.out。?
c)共享目標文件:用于和其他共享目標文件或者可重定位文件一起生成elf目標文件或者和執(zhí)行文件一起創(chuàng)建進程映像,例如lib*.so文件。?
(2)ELF文件作用:?
ELF文件參與程序的連接(建立一個程序)和程序的執(zhí)行(運行一個程序),所以可以從不同的角度來看待elf格式的文件:?
a)如果用于編譯和鏈接(可重定位文件),則編譯器和鏈接器將把elf文件看作是節(jié)頭表描述的節(jié)的集合,程序頭表可選。?
b)如果用于加載執(zhí)行(可執(zhí)行文件),則加載器則將把elf文件看作是程序頭表描述的段的集合,一個段可能包含多個節(jié),節(jié)頭表可選。?
c)如果是共享文件,則兩者都含有。?
(3)ELF文件總體組成:?
elf文件頭描述elf文件的總體信息。包括:?
系統(tǒng)相關(guān),類型相關(guān),加載相關(guān),鏈接相關(guān)。?
系統(tǒng)相關(guān)表示:elf文件標識的魔術(shù)數(shù),以及硬件和平臺等相關(guān)信息,增加了elf文件的移植性,使交叉編譯成為可能。?
類型相關(guān)就是前面說的那個類型。?
加載相關(guān):包括程序頭表相關(guān)信息。?
鏈接相關(guān):節(jié)頭表相關(guān)信息。?
下面對其進行了詳細的介紹。?
2 ELF文件格式?
2.1 ELF文件的類型?
ELF文件主要有三種類型:?
(1)可重定位文件:包含了代碼和數(shù)據(jù).可與其它ELF文件建立一個可執(zhí)行或共享的文件:?
(2)可執(zhí)行文件:是可以直接執(zhí)行的程序:?
(3)共享目標文件:包括代碼和數(shù)據(jù),可以在兩個地方鏈接。第一,連接器可以把它和其它可重定位文件和共享文件一起處理以建立另一個ELF文件;第二,動態(tài)鏈接器把它和一個可執(zhí)行文件和其它共享文件結(jié)合在一起建立一個進程映像。?
2.2 ELF文件的組織?
ELF文件參與程序的連接(建立一個程序)和程序的執(zhí)行(運行一個程序),編譯器和鏈接器將其視為節(jié)頭表(section header table)描述的一些節(jié)(section)的集合,而加載器則將其視為程序頭表(program header table)描述的段(segment)的集合,通常一個段可以包含多個節(jié)??芍囟ㄎ晃募及粋€節(jié)頭表,可執(zhí)行文件都包含一個程序頭表。共享文件兩者都包含有。為此,ELF文件格式同時提供了兩種看待文件內(nèi)容的方式,反映了不同行為的不同要求。?
從鏈接的角度看,ELF文件從開始到結(jié)束,可以看成是如下組成的:?
a)ELF文件頭?
b)程序頭表(可選)?
c)第1節(jié),第2節(jié),...,第n節(jié),...?
d)節(jié)頭表?
從執(zhí)行的角度看,ELF文件從開始到結(jié)束,可以看成是如下組成的:?
a)ELF文件頭?
b)程序頭表?
c)第1段,第2段,...,?
d)節(jié)頭表(可選)?
2.3 文件頭(Elf header)?
Elf頭在程序的開始部位,作為引路表描述整個ELF的文件結(jié)構(gòu),其信息大致分為四部分:一是系統(tǒng)相關(guān)信息,二是目標文件類型,三是加載相關(guān)信息,四是鏈接相關(guān)信息。?
其中系統(tǒng)相關(guān)信息包括elf文件魔數(shù)(標識elf文件),平臺位數(shù),數(shù)據(jù)編碼方式,elf頭部版本,硬件平臺e_machine,目標文件版本 e_version,處理器特定標志e_ftags:這些信息的引入極大增強了elf文件的可移植性,使交叉編譯成為可能。目標文件類型用e_type的值表示,可重定位文件為1,可執(zhí)行文件為2,共享文件為3;加載相關(guān)信息有:程序進入點e_entry.程序頭表偏移量e_phoff,elf頭部長度 e_ehsize,程序頭表中一個條目的長度e_phentsize,程序頭表條目數(shù)目e_phnum;鏈接相關(guān)信息有:節(jié)頭表偏移量e_shoff,節(jié)頭表中一個條目的長度e_shentsize,節(jié)頭表條目個數(shù)e_shnum ,節(jié)頭表字符索引e shstmdx??墒褂妹?readelf -h filename"來察看文件頭的內(nèi)容。?
文件頭的數(shù)據(jù)結(jié)構(gòu)如下:?
typedef struct elf32_hdr{?
unsigned char e_ident[EI_NIDENT];?
Elf32_Half e_type;//目標文件類型?
Elf32_Half e_machine;//硬件平臺?
Elf32_Word e_version;//elf頭部版本?
Elf32_Addr e_entry;//程序進入點?
Elf32_Off e_phoff;//程序頭表偏移量?
Elf32_Off e_shoff;//節(jié)頭表偏移量?
Elf32_Word e_flags;/處理器特定標志?
Elf32_Half e_ehsize;//elf頭部長度?
Elf32_Half e_phentsize;//程序頭表中一個條目的長度?
Elf32_Half e_phnum;//程序頭表條目數(shù)目?
Elf32_Half e_shentsize;//節(jié)頭表中一個條目的長度?
Elf32_Half e_shnum;//節(jié)頭表條目個數(shù)?
Elf32_Half e_shstrmdx;//節(jié)頭表字符索引?
}Elf32_Ehdr;?
2.4 程序頭表(program header table)?
程序頭表告訴系統(tǒng)如何建立一個進程映像.它是從加載執(zhí)行的角度來看待elf文件.從它的角度看.elf文件被分成許多段,elf文件中的代碼、鏈接信息和注釋都以段的形式存放。每個段都在程序頭表中有一個表項描述,包含以下屬性:段的類型,段的駐留位置相對于文件開始處的偏移,段在內(nèi)存中的首字節(jié)地址,段的物理地址,段在文件映像中的字節(jié)數(shù).段在內(nèi)存映像中的字節(jié)數(shù),段在內(nèi)存和文件中的對齊標記??捎?readelf -l filename"察看程序頭表中的內(nèi)容。程序頭表的結(jié)構(gòu)如下:?
typedef struct elf32_phdr{?
Elf32_Word p_type; //段的類型?
Elf32_Off p_offset; //段的位置相對于文件開始處的偏移?
Elf32_Addr p_vaddr; //段在內(nèi)存中的首字節(jié)地址?
Elf32_Addr p_paddr;//段的物理地址?
Elf32_Word p_filesz;//段在文件映像中的字節(jié)數(shù)?
Elf32_Word p_memsz;//段在內(nèi)存映像中的字節(jié)數(shù)?
Elf32_Word p_flags;//段的標記?
Elf32_Word p_align;,/段在內(nèi)存中的對齊標記?
)Elf32_Phdr;?
2.5節(jié)頭表(section header table)?
節(jié)頭表描述程序節(jié),為編譯器和鏈接器服務(wù)。它把elf文件分成了許多節(jié).每個節(jié)保存著用于不同目的的數(shù)據(jù).這些數(shù)據(jù)可能被前面的程序頭重復(fù)使用,完成一次任務(wù)所需的信息往往被分散到不同的節(jié)里。由于節(jié)中數(shù)據(jù)的用途不同,節(jié)被分成不同的類型,每種類型的節(jié)都有自己組織數(shù)據(jù)的方式。每一個節(jié)在節(jié)頭表中都有一個表項描述該節(jié)的屬性,節(jié)的屬性包括小節(jié)名在字符表中的索引,類型,屬性,運行時的虛擬地址,文件偏移,以字節(jié)為單位的大小,小節(jié)的對齊等信息,可使用"readelf -S filename"來察看節(jié)頭表的內(nèi)容。節(jié)頭表的結(jié)構(gòu)如下:?
typedef struct{?
Elf32_Word sh_name;//小節(jié)名在字符表中的索引?
E1t32_Word sh_type;//小節(jié)的類型?
Elf32_Word sh_flags;//小節(jié)屬性?
Elf32_Addr sh_addr; //小節(jié)在運行時的虛擬地址?
Elf32_Off sh_offset;//小節(jié)的文件偏移?
Elf32_Word sh_size;//小節(jié)的大?。宰止?jié)為單位?
Elf32_Word sh_link;//鏈接的另外一小節(jié)的索引?
Elf32 Word sh_info;//附加的小節(jié)信息?
Elf32 Word sh_addralign;//小節(jié)的對齊?
Elf32 Word sh_entsize; //一些sections保存著一張固定大小入口的表。就像符號表?
}Elf32_Shdr;?
3 ELF的特性?
3.1平臺相關(guān)?
在ELF 文件頭中包含了足夠的平臺相關(guān)信息,如數(shù)據(jù)編碼方式,平臺位數(shù),硬件平臺e_machine等,這些平臺相關(guān)信息可在編譯由編譯器決定。例如,與平臺位數(shù)的相關(guān)的數(shù)據(jù)結(jié)構(gòu)的定義在elf.h的頭文件中.在編譯預(yù)處理時確定:?
#if ELF CLASS==ELFCLASS32?
extern Elf32_Dyn_DYNAMIC[];?
#define elfhdr elf32_hdr;?
#define elf_phdr elf32_phdr;?
#define elf_note elf32_note;?
#else?
extern Elf64_Dyn_DYNAMIC[];?
#define elfhdr elf64_hdr;?
#define elf_phdr elf64_phdr;?
#define elf_note elf64_note;?
#endif?
linux系統(tǒng)加載ELF可執(zhí)行文件時,必須首先做一些簡單的一致性檢查.其代碼如下:?
if(memcmp(elf_ex.e_ident,ELFMAG,SELFMAG)!=0)?
goto out; //檢查文件頭開始四個字符是否為ELF魔數(shù)'\0177ELF?
if(elf_ex.e_type!=ET_EXEC&&elf_ex.e_type!=ET_DYN)?
goto out;//檢查文件類型是否為可執(zhí)行文件或共享目標文件?
if(!elf_check_arch(&elf_ex))?
goto out;//檢查硬件平臺是否一致?
其中的elf_check_arch(x)在不同的硬件平臺上有不同的定義,其由系統(tǒng)的硬件平臺決定。這樣,在硬件平臺相同的系統(tǒng)上,ELF可以不作修改的執(zhí)行。因此,它可以支持不同平臺上的交叉編譯(cross_compilation)和交叉鏈接(cross_linking)。?
3.2 PIC?
ELF可以生成一種特殊的代碼——與位置無關(guān)的代碼(position-independent code,PIC)。用戶對gcc使用-fPIC指示GNU編譯系統(tǒng)生成PIC代碼。它是實現(xiàn)共享庫或共享可執(zhí)行代碼的基礎(chǔ).這種代碼的特殊性在于它可以加載到內(nèi)存地址空間的任何地址執(zhí)行.這也是加載器可以很方便的在進程中動態(tài)鏈接共享庫。?
PIC的實現(xiàn)運用了一個事實,就是代碼段中任何指令和數(shù)據(jù)段中的任何變量之間的距離都是一個與代碼段和數(shù)據(jù)段的絕對存儲器位置無關(guān)的常量。因此,編譯器在數(shù)據(jù)段開始的地方創(chuàng)建了一個表.叫做全局偏移量表(global offset table.GOT)。GOT包含每個被這個目標模塊引用的全局數(shù)據(jù)目標的表目。編譯器還為GOT中每個表目生成一個重定位記錄。在加載時,動態(tài)鏈接器會重定位GOT中的每個表目,使得它包含正確的絕對地址。PIC代碼在代碼中實現(xiàn)通過GOT間接的引用每個全局變量,這樣,代碼中本來簡單的數(shù)據(jù)引用就變得復(fù)雜,必須加入得到GOT適當表目內(nèi)容的指令。對只讀數(shù)據(jù)的引用也根據(jù)同樣的道理,所以,加上 IC編譯成的代碼比一般的代碼開銷大。?
如果一個elf可執(zhí)行文件需要調(diào)用定義在共享庫中的任何函數(shù),那么它就有自己的GOT和PLT(procedure linkage table,過程鏈接表).這兩個節(jié)之間的交互可以實現(xiàn)延遲綁定(lazy binging),這種方法將過程地址的綁定推遲到第一次調(diào)用該函數(shù)。為了實現(xiàn)延遲綁定,GOT的頭三條表目是特殊的:GOT[0]包含.dynamic 段的地址,.dynamic段包含了動態(tài)鏈接器用來綁定過程地址的信息,比如符號的位置和重定位信息;GOT[1]包含動態(tài)鏈接器的標識;GOT[2]包含動態(tài)鏈接器的延遲綁定代碼的入口點。GOT的其他表目為本模塊要引用的一個全局變量或函數(shù)的地址。PLT是一個以16字節(jié)(32位平臺中)表目的數(shù)組形式出現(xiàn)的代碼序列。其中PLT[0]是一個特殊的表目,它跳轉(zhuǎn)到動態(tài)鏈接器中執(zhí)行;每個定義在共享庫中并被本模塊調(diào)用的函數(shù)在PLT中都有一個表目,從 PLT[1]開始.模塊對函數(shù)的調(diào)用會轉(zhuǎn)到相應(yīng)PLT表目中執(zhí)行,這些表目由三條指令構(gòu)成。第一條指令是跳轉(zhuǎn)到相應(yīng)的GOT存儲的地址值中.第二條指令把函數(shù)相應(yīng)的ID壓入棧中,第三條指令跳轉(zhuǎn)到PLT[O]中調(diào)用動態(tài)鏈接器解析函數(shù)地址,并把函數(shù)真正地址存入相應(yīng)的GOT表目中。被調(diào)用函數(shù)GOT相應(yīng)表目中存儲的最初地址為相應(yīng)PLT表目中第二條指令的地址值,函數(shù)第一次被調(diào)用后.GOT表目中的值就為函數(shù)的真正地址。因此,第一次調(diào)用函數(shù)時開銷比較大.但是其后的每次調(diào)用都只會花費一條指令和一個間接的存儲器引用。?
3.3 強大的工具支持?
由于gnu有大量的工具支持elf文件格式.隨著gnu工具的功能的擴展.程序員對ELF文件的運用也越來越靈活。例如,在C++中全局的構(gòu)造函數(shù)和析構(gòu)函數(shù)必須非常小心的處理碰到的語言規(guī)范問題。構(gòu)造函數(shù)必須在main函數(shù)之前被調(diào)用。析構(gòu)函數(shù)必須在main函數(shù)返回之后被調(diào)用。ELF文件格式中,定義了兩個特殊的節(jié) (section),.init和.fini,.init保存著可執(zhí)行指令,它構(gòu)成了進程的初始化代碼。當一個程序開始運行時,在main函數(shù)被調(diào)用之前 (c語言稱為main),系統(tǒng)安排執(zhí)行這個section的中的代碼。.fini保存著可執(zhí)行指令,它構(gòu)成了進程的終止代碼。當一個程序正常退出時.系統(tǒng)安排執(zhí)行這個section的中的代碼。C++編譯器利用這個特性.構(gòu)造正確的.init和.fini sections.并結(jié)合.ctors(該section保存著程序的全局的構(gòu)造函數(shù)的指針數(shù)組)和.dtors(該section保存著程序的全局的析構(gòu)函數(shù)的指針數(shù)組)兩個section,完成全局的構(gòu)造函數(shù)和析構(gòu)函數(shù)的處理。?
GCC還有許多擴展的特性.有些對ELF 特別的有用。其中一個就是_attribute_ 。使用_attribute_可以使一個函數(shù)放到_CTOR_LIST_或者_DTOR_LIST_里。 _attribute_((constructor))促使函數(shù)在進入main之前會被自動調(diào)用。_attribute_((destructor))促使函數(shù)在main返回或者exit調(diào)用之后被自動調(diào)用。這種函數(shù)必須是不能帶參數(shù)的而且必須是static void類型的函數(shù)。在ELF下,這個特性在一般的可執(zhí)行文件和共享庫中都能很好的工作。另外一個GCC的特性是 attribute_(section("sectionname")),使用這個,能把一個函數(shù)或者是數(shù)據(jù)結(jié)構(gòu)放到任何的section中。?
[舉例]?
這里,通過使用一些用于操作ELF文件的工具的例子,來對其有一個直觀的了解。?
1 readelf工具?
readelf用來顯示ELF格式目標文件的信息.可通過參數(shù)選項來控制顯示哪些特定信息。?
*讀取elf文件頭信息:?
$ readelf -h fbtest?
輸入之后,輸出如下:?
ELF Header:?
Magic:?? 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00?
Class:???????????????????????????? ELF32?
Data:????????????????????????????? 2's complement, little endian?
Version:?????????????????????????? 1 (current)?
OS/ABI:??????????????????????????? UNIX - System V?
ABI Version:?????????????????????? 0?
Type:????????????????????????????? EXEC (Executable file)?
Machine:?????????????????????????? Intel 80386?
Version:?????????????????????????? 0x1?
Entry point address:?????????????? 0x80484d0?
Start of program headers:????????? 52 (bytes into file)?
Start of section headers:????????? 5924 (bytes into file)?
Flags:???????????????????????????? 0x0?
Size of this header:?????????????? 52 (bytes)?
Size of program headers:?????????? 32 (bytes)?
Number of program headers:???????? 8?
Size of section headers:?????????? 40 (bytes)?
Number of section headers:???????? 36?
Section header string table index: 33?
這里,fbtest是在本地使用gcc編譯生成的一個簡單的可執(zhí)行程序。?
*查看elf文件程序頭表信息:?
$readelf -l fbtest?
輸入之后,輸出如下:?
Program Headers:?
Type?????????? Offset?? VirtAddr?? PhysAddr?? FileSiz MemSiz? Flg Align?
PHDR?????????? 0x000034 0x08048034 0x08048034 0x00100 0x00100 R E 0x4?
INTERP???????? 0x000134 0x08048134 0x08048134 0x00013 0x00013 R?? 0x1?
[Requesting program interpreter: /lib/ld-linux.so.2]?
LOAD?????????? 0x000000 0x08048000 0x08048000 0x00df4 0x00df4 R E 0x1000?
LOAD?????????? 0x000f0c 0x08049f0c 0x08049f0c 0x00128 0x00178 RW? 0x1000?
DYNAMIC??????? 0x000f20 0x08049f20 0x08049f20 0x000d0 0x000d0 RW? 0x4?
NOTE?????????? 0x000148 0x08048148 0x08048148 0x00020 0x00020 R?? 0x4?
GNU_STACK????? 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW? 0x4?
GNU_RELRO????? 0x000f0c 0x08049f0c 0x08049f0c 0x000f4 0x000f4 R?? 0x1?
Section to Segment mapping:?
Segment Sections...?
00?
01???? .interp?
02???? .interp .note.ABI-tag .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame?
03???? .ctors .dtors .jcr .dynamic .got .got.plt .data .bss?
04???? .dynamic?
05???? .note.ABI-tag?
06?
07???? .ctors .dtors .jcr .dynamic .got?
*查看elf文件的節(jié)信息:?
$readelf -S libmy.so?
輸入之后,輸出如下:?
There are 33 section headers, starting at offset 0xfd0:?
Section Headers:?
[Nr] Name????????????? Type??????????? Addr???? Off??? Size?? ES Flg Lk Inf Al?
[ 0]?????????????????? NULL??????????? 00000000 000000 000000 00????? 0?? 0? 0?
[ 1] .hash???????????? HASH??????????? 000000d4 0000d4 0000a0 04?? A? 3?? 0? 4?
[ 2] .gnu.hash???????? GNU_HASH??????? 00000174 000174 000040 04?? A? 3?? 0? 4?
[ 3] .dynsym?????????? DYNSYM????????? 000001b4 0001b4 000150 10?? A? 4?? 1? 4?
[ 4] .dynstr?????????? STRTAB????????? 00000304 000304 00018a 00?? A? 0?? 0? 1?
[ 5] .gnu.version????? VERSYM????????? 0000048e 00048e 00002a 02?? A? 3?? 0? 2?
[ 6] .gnu.version_r??? VERNEED???????? 000004b8 0004b8 000020 00?? A? 4?? 1? 4?
[ 7] .rel.dyn????????? REL???????????? 000004d8 0004d8 0000e8 08?? A? 3?? 0? 4?
[ 8] .rel.plt????????? REL???????????? 000005c0 0005c0 000010 08?? A? 3? 10? 4?
[ 9] .init???????????? PROGBITS??????? 000005d0 0005d0 000030 00? AX? 0?? 0? 4?
[10] .plt????????????? PROGBITS??????? 00000600 000600 000030 04? AX? 0?? 0? 4?
[11] .text???????????? PROGBITS??????? 00000630 000630 0002a4 00? AX? 0?? 0 16?
[12] .fini???????????? PROGBITS??????? 000008d4 0008d4 00001c 00? AX? 0?? 0? 4?
[13] .rodata?????????? PROGBITS??????? 000008f0 0008f0 000006 00?? A? 0?? 0? 1?
[14] .eh_frame_hdr???? PROGBITS??????? 000008f8 0008f8 000034 00?? A? 0?? 0? 4?
[15] .eh_frame???????? PROGBITS??????? 0000092c 00092c 0000d8 00?? A? 0?? 0? 4?
[16] .ctors??????????? PROGBITS??????? 00001a04 000a04 00000c 00? WA? 0?? 0? 4?
[17] .dtors??????????? PROGBITS??????? 00001a10 000a10 000008 00? WA? 0?? 0? 4?
[18] .jcr????????????? PROGBITS??????? 00001a18 000a18 000004 00? WA? 0?? 0? 4?
[19] .dynamic????????? DYNAMIC???????? 00001a1c 000a1c 0000d0 08? WA? 4?? 0? 4?
[20] .got????????????? PROGBITS??????? 00001aec 000aec 00000c 04? WA? 0?? 0? 4?
[21] .got.plt????????? PROGBITS??????? 00001af8 000af8 000014 04? WA? 0?? 0? 4?
[22] .data???????????? PROGBITS??????? 00001b0c 000b0c 000008 00? WA? 0?? 0? 4?
[23] .bss????????????? NOBITS????????? 00001b14 000b14 000008 00? WA? 0?? 0? 4?
[24] .comment????????? PROGBITS??????? 00000000 000b14 0000d2 00????? 0?? 0? 1?
[25] .debug_aranges??? PROGBITS??????? 00000000 000be8 000050 00????? 0?? 0? 8?
[26] .debug_info?????? PROGBITS??????? 00000000 000c38 00011a 00????? 0?? 0? 1?
[27] .debug_abbrev???? PROGBITS??????? 00000000 000d52 000024 00????? 0?? 0? 1?
[28] .debug_line?????? PROGBITS??????? 00000000 000d76 000102 00????? 0?? 0? 1?
[29] .debug_ranges???? PROGBITS??????? 00000000 000e78 000040 00????? 0?? 0? 8?
[30] .shstrtab???????? STRTAB????????? 00000000 000eb8 000116 00????? 0?? 0? 1?
[31] .symtab?????????? SYMTAB????????? 00000000 0014f8 0004c0 10???? 32? 56? 4?
[32] .strtab?????????? STRTAB????????? 00000000 0019b8 00031c 00????? 0?? 0? 1?
Key to Flags:?
W (write), A (alloc), X (execute), M (merge), S (strings)?
I (info), L (link order), G (group), x (unknown)?
O (extra OS processing required) o (OS specific), p (processor specific)?
這里的libmy.so是自行生成的一個共享庫。?
2 objcopy工具?
objcopy可以把一種目標文件中的內(nèi)容復(fù)制到另一種類型的目標文件中.?
通過objcopy的各種選項,可以對目標文件進行各種類型的操作。例如去掉可執(zhí)行文件的調(diào)試信息(效果等同于strip)等等,具體需要做什么操作,要求我們對elf文件中每個部分的內(nèi)容有所理解。?
這里只給出一個例子:?
*使用objcopy把.comment段和.note段的信息去掉:?
$ objcopy -R .comment -R .note hello hello.min?
這里,hello是一個可執(zhí)行文件,通過"readelf -l hello"或者"readelf -S hello"命令可以知道文件中包含一個.note段和.comment段,通過這個命令,將這兩個段從文件中刪除,不會改變原來的文件,而是將刪除了這些信息的文件存放在hello.min中。實際通過這個方法,可以減少可執(zhí)行文件的大小,且不影響可執(zhí)行文件的功能。?
項的含義是:?
-R .note -R .comment 表示移掉 .note 與 .comment 段?
-O binary xyb xyb.bin 表示由xyb生成二進制文件xyb.bin?
3 objdump工具
objdump是用查看目標文件或者可執(zhí)行的目標文件的構(gòu)成的GCC工具。?
以下給出幾個常用的例子:?
*輸出目標文件的所有段概括:?
# objdump -h main?
輸入之后,輸出信息大致如下:?
main:???? file format elf32-i386?
Sections:?
Idx Name????????? Size????? VMA?????? LMA?????? File off? Algn?
0 .interp?????? 00000013? 08048134? 08048134? 00000134? 2**0?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
1 .note.ABI-tag 00000020? 08048148? 08048148? 00000148? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
2 .gnu.hash???? 00000030? 08048168? 08048168? 00000168? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
3 .dynsym?????? 000000d0? 08048198? 08048198? 00000198? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
4 .dynstr?????? 00000183? 08048268? 08048268? 00000268? 2**0?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
5 .gnu.version? 0000001a? 080483ec? 080483ec? 000003ec? 2**1?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
6 .gnu.version_r 00000060? 08048408? 08048408? 00000408? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
7 .rel.dyn????? 00000010? 08048468? 08048468? 00000468? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
8 .rel.plt????? 00000048? 08048478? 08048478? 00000478? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
9 .init???????? 00000017? 080484c0? 080484c0? 000004c0? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, CODE?
10 .plt????????? 000000a0? 080484d8? 080484d8? 000004d8? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, CODE?
11 .text???????? 00000238? 08048580? 08048580? 00000580? 2**4?
CONTENTS, ALLOC, LOAD, READONLY, CODE?
12 .fini???????? 0000001c? 080487b8? 080487b8? 000007b8? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, CODE?
13 .rodata?????? 00000013? 080487d4? 080487d4? 000007d4? 2**2?
......其余內(nèi)容省略......?
這里,main是一個可執(zhí)行文件。?
*輸出目標文件的符號表:?
# objdump -t main?
輸入之后,輸出類似如下:?
main:???? file format elf32-i386?
SYMBOL TABLE:?
08048134 l??? d? .interp??????? 00000000????????????? .interp?
08048148 l??? d? .note.ABI-tag? 00000000????????????? .note.ABI-tag?
08048168 l??? d? .gnu.hash????? 00000000????????????? .gnu.hash?
08048198 l??? d? .dynsym??????? 00000000????????????? .dynsym?
08048268 l??? d? .dynstr??????? 00000000????????????? .dynstr?
080483ec l??? d? .gnu.version?? 00000000????????????? .gnu.version?
08048408 l??? d? .gnu.version_r 00000000????????????? .gnu.version_r?
08048468 l??? d? .rel.dyn?????? 00000000????????????? .rel.dyn?
08048478 l??? d? .rel.plt?????? 00000000????????????? .rel.plt?
080484c0 l??? d? .init? 00000000????????????? .init?
080484d8 l??? d? .plt?? 00000000????????????? .plt?
08048580 l??? d? .text? 00000000????????????? .text?
080487b8 l??? d? .fini? 00000000????????????? .fini?
080487d4 l??? d? .rodata??????? 00000000????????????? .rodata?
080487e8 l??? d? .eh_frame_hdr? 00000000????????????? .eh_frame_hdr?
08048824 l??? d? .eh_frame????? 00000000????????????? .eh_frame?
08049914 l??? d? .ctors 00000000????????????? .ctors?
08049920 l??? d? .dtors 00000000????????????? .dtors?
08049928 l??? d? .jcr?? 00000000????????????? .jcr?
0804992c l??? d? .dynamic?????? 00000000????????????? .dynamic?
08049a0c l??? d? .got?? 00000000????????????? .got?
08049a10 l??? d? .got.plt?????? 00000000????????????? .got.plt?
08049a40 l??? d? .data? 00000000????????????? .data?
08049a48 l??? d? .bss?? 00000000????????????? .bss?
00000000 l??? d? .comment?????? 00000000????????????? .comment?
080485a4 l???? F .text? 00000000????????????? call_gmon_start?
00000000 l??? df *ABS*? 00000000????????????? crtstuff.c?
08049914 l???? O .ctors 00000000????????????? __CTOR_LIST__?
08049920 l???? O .dtors 00000000????????????? __DTOR_LIST__?
08049928 l???? O .jcr?? 00000000????????????? __JCR_LIST__?
08049ad4 l???? O .bss?? 00000004????????????? dtor_idx.5793?
08049ad8 l???? O .bss?? 00000001????????????? completed.5791?
080485d0 l???? F .text? 00000000????????????? __do_global_dtors_aux?
......其余信息省略......?
*以某種分類信息的形式把目標文件的數(shù)據(jù)組織(被分為幾大塊)輸出??:?
# objdump -x main?
輸入之后,輸出信息類似如下:?
main:???? file format elf32-i386?
main?
architecture: i386, flags 0x00000112:?
EXEC_P, HAS_SYMS, D_PAGED?
start address 0x08048580?
Program Header:?
PHDR off??? 0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2?
filesz 0x00000100 memsz 0x00000100 flags r-x?
INTERP off??? 0x00000134 vaddr 0x08048134 paddr 0x08048134 align 2**0?
filesz 0x00000013 memsz 0x00000013 flags r--?
LOAD off??? 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12?
filesz 0x00000914 memsz 0x00000914 flags r-x?
LOAD off??? 0x00000914 vaddr 0x08049914 paddr 0x08049914 align 2**12?
filesz 0x00000130 memsz 0x000001cc flags rw-?
DYNAMIC off??? 0x0000092c vaddr 0x0804992c paddr 0x0804992c align 2**2?
filesz 0x000000e0 memsz 0x000000e0 flags rw-?
NOTE off??? 0x00000148 vaddr 0x08048148 paddr 0x08048148 align 2**2?
filesz 0x00000020 memsz 0x00000020 flags r--?
EH_FRAME off??? 0x000007e8 vaddr 0x080487e8 paddr 0x080487e8 align 2**2?
filesz 0x0000003c memsz 0x0000003c flags r--?
STACK off??? 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2?
...省略...?
Dynamic Section:?
NEEDED????? libstdc++.so.6?
NEEDED????? libm.so.6?
NEEDED????? libgcc_s.so.1?
NEEDED????? libc.so.6?
INIT??????? 0x80484c0?
FINI??????? 0x80487b8?
GNU_HASH??? 0x8048168?
...省略...?
Version References:?
required from libstdc++.so.6:?
0x056bafd3 0x00 05 CXXABI_1.3?
0x08922974 0x00 03 GLIBCXX_3.4?
required from libc.so.6:?
0x0d696910 0x00 04 GLIBC_2.0?
0x09691f73 0x00 02 GLIBC_2.1.3?
Sections:?
Idx Name????????? Size????? VMA?????? LMA?????? File off? Algn?
0 .interp?????? 00000013? 08048134? 08048134? 00000134? 2**0?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
1 .note.ABI-tag 00000020? 08048148? 08048148? 00000148? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
2 .gnu.hash???? 00000030? 08048168? 08048168? 00000168? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
3 .dynsym?????? 000000d0? 08048198? 08048198? 00000198? 2**2?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
4 .dynstr?????? 00000183? 08048268? 08048268? 00000268? 2**0?
CONTENTS, ALLOC, LOAD, READONLY, DATA?
5 .gnu.version? 0000001a? 080483ec? 080483ec? 000003ec? 2**1?
...省略...?
SYMBOL TABLE:?
08048134 l??? d? .interp??????? 00000000????????????? .interp?
08048148 l??? d? .note.ABI-tag? 00000000????????????? .note.ABI-tag?
08048168 l??? d? .gnu.hash????? 00000000????????????? .gnu.hash?
08048198 l??? d? .dynsym??????? 00000000????????????? .dynsym?
08048268 l??? d? .dynstr??????? 00000000????????????? .dynstr?
080483ec l??? d? .gnu.version?? 00000000????????????? .gnu.version?
08048408 l??? d? .gnu.version_r 00000000????????????? .gnu.version_r?
...省略...?
這里可知,分別顯示出各個段相關(guān)的信息。?
*輸出指定段的信息:?
# objdump? -j .text -S? main?
輸入之后,輸出類似如下:?
main:???? file format elf32-i386?
Disassembly of section .text:?
08048580 <_start>:?
8048580:?????? 31 ed?????????????????? xor??? %ebp,%ebp?
8048582:?????? 5e????????????????????? pop??? %esi?
8048583:?????? 89 e1?????????????????? mov??? %esp,%ecx?
8048585:?????? 83 e4 f0??????????????? and??? $0xfffffff0,%esp?
8048588:?????? 50????????????????????? push?? %eax?
8048589:?????? 54????????????????????? push?? %esp?
...省略...?
這里,反匯編會用到類似的命令。?
4 nm工具
這個命令可以用來查看庫中的符號。?
nm列出的符號有很多,常見的有三種,一種是在庫中被調(diào)用,但并沒有在庫中定義(表明需要其他庫支持),用U表示;一種是庫中定義的函數(shù),用T表示,這是最常見的;另外一種是所謂的“弱態(tài)”符號,它們雖然在庫中被定義,但是可能被其他庫中的同名符號覆蓋,用W表示。?
*假設(shè)開發(fā)者希望知道hello庫中是否定義了 printf():?
$nm libhello.so |grep printf?
U printf?
U表示符號printf被引用,但是并沒有在函數(shù)內(nèi)定義,由此可以推斷,要正常使用hello庫,必須有其它庫支持。?
5 ldd工具?
ldd命令可以用來查看一個可執(zhí)行文件或者庫依賴哪些其他的文件。?
*使用ldd命令查看hello依賴于哪些庫:?
$ldd hello?
libc.so.6=>/lib/libc.so.6(0x400la000)?
/lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)?
這里,結(jié)合nm,從上面的結(jié)果可以繼續(xù)查看printf最終在哪里被定義.?
?
評論
查看更多