ELF(Executable and Linkable Format)即可執行連接文件格式,是一種比較復雜的文件格式,但其應用廣泛。與linux下的其他可執行文件(a.out,cof)相比,它對節的定義和gnu工具鏈對它的支持使它十分靈活,它保存的足夠了系統相關信息使它能支持不同平臺上的交叉編譯和交叉鏈接,可移植性很強.同時它在執行中支持動態鏈接共享庫。?
通過本文,可以大致了解Linux系統中ELF格式文件的分類,組成,作用,以及其中包含的內容。另外后面介紹了幾種常用的對elf文件進行操作的工具,并且對其使用進行簡單舉例,便于對elf文件有一個比較直觀的理解。?
主要內容:?
[描述]?
1 ELF文件簡介?
2 ELF文件格式?
3 ELF的特性?
[舉例]?
1 readelf工具?
2 objcopy工具?
3 objdump工具?
4 nm工具?
5 ldd工具?
[其它]?
[描述]?
1 ELF文件簡介?
ELF(Executable and Linkable Format)即可執行連接文件格式,是Linux,SVR4和Solaris2.0默認的目標文件格式,目前標準接口委員會TIS已將ELF標準化為一種可移植的目標文件格式,運行于32-bit Intel體系微機上,可與多種操作系統兼容。分析elf文件有助于理解一些重要的系統概念,例如程序的編譯和鏈接,程序的加載和運行等。?
(1)ELF文件類型:?
種類型的ELF文件:?
a)可重定位文件:用戶和其他目標文件一起創建可執行文件或者共享目標文件,例如lib*.a文件。?
b)可執行文件:用于生成進程映像,載入內存執行,例如編譯好的可執行文件a.out。?
c)共享目標文件:用于和其他共享目標文件或者可重定位文件一起生成elf目標文件或者和執行文件一起創建進程映像,例如lib*.so文件。?
(2)ELF文件作用:?
ELF文件參與程序的連接(建立一個程序)和程序的執行(運行一個程序),所以可以從不同的角度來看待elf格式的文件:?
a)如果用于編譯和鏈接(可重定位文件),則編譯器和鏈接器將把elf文件看作是節頭表描述的節的集合,程序頭表可選。?
b)如果用于加載執行(可執行文件),則加載器則將把elf文件看作是程序頭表描述的段的集合,一個段可能包含多個節,節頭表可選。?
c)如果是共享文件,則兩者都含有。?
(3)ELF文件總體組成:?
elf文件頭描述elf文件的總體信息。包括:?
系統相關,類型相關,加載相關,鏈接相關。?
系統相關表示:elf文件標識的魔術數,以及硬件和平臺等相關信息,增加了elf文件的移植性,使交叉編譯成為可能。?
類型相關就是前面說的那個類型。?
加載相關:包括程序頭表相關信息。?
鏈接相關:節頭表相關信息。?
下面對其進行了詳細的介紹。?
2 ELF文件格式?
2.1 ELF文件的類型?
ELF文件主要有三種類型:?
(1)可重定位文件:包含了代碼和數據.可與其它ELF文件建立一個可執行或共享的文件:?
(2)可執行文件:是可以直接執行的程序:?
(3)共享目標文件:包括代碼和數據,可以在兩個地方鏈接。第一,連接器可以把它和其它可重定位文件和共享文件一起處理以建立另一個ELF文件;第二,動態鏈接器把它和一個可執行文件和其它共享文件結合在一起建立一個進程映像。?
2.2 ELF文件的組織?
ELF文件參與程序的連接(建立一個程序)和程序的執行(運行一個程序),編譯器和鏈接器將其視為節頭表(section header table)描述的一些節(section)的集合,而加載器則將其視為程序頭表(program header table)描述的段(segment)的集合,通常一個段可以包含多個節。可重定位文件都包含一個節頭表,可執行文件都包含一個程序頭表。共享文件兩者都包含有。為此,ELF文件格式同時提供了兩種看待文件內容的方式,反映了不同行為的不同要求。?
從鏈接的角度看,ELF文件從開始到結束,可以看成是如下組成的:?
a)ELF文件頭?
b)程序頭表(可選)?
c)第1節,第2節,...,第n節,...?
d)節頭表?
從執行的角度看,ELF文件從開始到結束,可以看成是如下組成的:?
a)ELF文件頭?
b)程序頭表?
c)第1段,第2段,...,?
d)節頭表(可選)?
2.3 文件頭(Elf header)?
Elf頭在程序的開始部位,作為引路表描述整個ELF的文件結構,其信息大致分為四部分:一是系統相關信息,二是目標文件類型,三是加載相關信息,四是鏈接相關信息。?
其中系統相關信息包括elf文件魔數(標識elf文件),平臺位數,數據編碼方式,elf頭部版本,硬件平臺e_machine,目標文件版本 e_version,處理器特定標志e_ftags:這些信息的引入極大增強了elf文件的可移植性,使交叉編譯成為可能。目標文件類型用e_type的值表示,可重定位文件為1,可執行文件為2,共享文件為3;加載相關信息有:程序進入點e_entry.程序頭表偏移量e_phoff,elf頭部長度 e_ehsize,程序頭表中一個條目的長度e_phentsize,程序頭表條目數目e_phnum;鏈接相關信息有:節頭表偏移量e_shoff,節頭表中一個條目的長度e_shentsize,節頭表條目個數e_shnum ,節頭表字符索引e shstmdx。可使用命令"readelf -h filename"來察看文件頭的內容。?
文件頭的數據結構如下:?
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;//節頭表偏移量?
Elf32_Word e_flags;/處理器特定標志?
Elf32_Half e_ehsize;//elf頭部長度?
Elf32_Half e_phentsize;//程序頭表中一個條目的長度?
Elf32_Half e_phnum;//程序頭表條目數目?
Elf32_Half e_shentsize;//節頭表中一個條目的長度?
Elf32_Half e_shnum;//節頭表條目個數?
Elf32_Half e_shstrmdx;//節頭表字符索引?
}Elf32_Ehdr;?
2.4 程序頭表(program header table)?
程序頭表告訴系統如何建立一個進程映像.它是從加載執行的角度來看待elf文件.從它的角度看.elf文件被分成許多段,elf文件中的代碼、鏈接信息和注釋都以段的形式存放。每個段都在程序頭表中有一個表項描述,包含以下屬性:段的類型,段的駐留位置相對于文件開始處的偏移,段在內存中的首字節地址,段的物理地址,段在文件映像中的字節數.段在內存映像中的字節數,段在內存和文件中的對齊標記。可用"readelf -l filename"察看程序頭表中的內容。程序頭表的結構如下:?
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;?
2.5節頭表(section header table)?
節頭表描述程序節,為編譯器和鏈接器服務。它把elf文件分成了許多節.每個節保存著用于不同目的的數據.這些數據可能被前面的程序頭重復使用,完成一次任務所需的信息往往被分散到不同的節里。由于節中數據的用途不同,節被分成不同的類型,每種類型的節都有自己組織數據的方式。每一個節在節頭表中都有一個表項描述該節的屬性,節的屬性包括小節名在字符表中的索引,類型,屬性,運行時的虛擬地址,文件偏移,以字節為單位的大小,小節的對齊等信息,可使用"readelf -S filename"來察看節頭表的內容。節頭表的結構如下:?
typedef struct{?
Elf32_Word sh_name;//小節名在字符表中的索引?
E1t32_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; //一些sections保存著一張固定大小入口的表。就像符號表?
}Elf32_Shdr;?
3 ELF的特性?
3.1平臺相關?
在ELF 文件頭中包含了足夠的平臺相關信息,如數據編碼方式,平臺位數,硬件平臺e_machine等,這些平臺相關信息可在編譯由編譯器決定。例如,與平臺位數的相關的數據結構的定義在elf.h的頭文件中.在編譯預處理時確定:?
#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系統加載ELF可執行文件時,必須首先做一些簡單的一致性檢查.其代碼如下:?
if(memcmp(elf_ex.e_ident,ELFMAG,SELFMAG)!=0)?
goto out; //檢查文件頭開始四個字符是否為ELF魔數'\0177ELF?
if(elf_ex.e_type!=ET_EXEC&&elf_ex.e_type!=ET_DYN)?
goto out;//檢查文件類型是否為可執行文件或共享目標文件?
if(!elf_check_arch(&elf_ex))?
goto out;//檢查硬件平臺是否一致?
其中的elf_check_arch(x)在不同的硬件平臺上有不同的定義,其由系統的硬件平臺決定。這樣,在硬件平臺相同的系統上,ELF可以不作修改的執行。因此,它可以支持不同平臺上的交叉編譯(cross_compilation)和交叉鏈接(cross_linking)。?
3.2 PIC?
ELF可以生成一種特殊的代碼——與位置無關的代碼(position-independent code,PIC)。用戶對gcc使用-fPIC指示GNU編譯系統生成PIC代碼。它是實現共享庫或共享可執行代碼的基礎.這種代碼的特殊性在于它可以加載到內存地址空間的任何地址執行.這也是加載器可以很方便的在進程中動態鏈接共享庫。?
PIC的實現運用了一個事實,就是代碼段中任何指令和數據段中的任何變量之間的距離都是一個與代碼段和數據段的絕對存儲器位置無關的常量。因此,編譯器在數據段開始的地方創建了一個表.叫做全局偏移量表(global offset table.GOT)。GOT包含每個被這個目標模塊引用的全局數據目標的表目。編譯器還為GOT中每個表目生成一個重定位記錄。在加載時,動態鏈接器會重定位GOT中的每個表目,使得它包含正確的絕對地址。PIC代碼在代碼中實現通過GOT間接的引用每個全局變量,這樣,代碼中本來簡單的數據引用就變得復雜,必須加入得到GOT適當表目內容的指令。對只讀數據的引用也根據同樣的道理,所以,加上 IC編譯成的代碼比一般的代碼開銷大。?
如果一個elf可執行文件需要調用定義在共享庫中的任何函數,那么它就有自己的GOT和PLT(procedure linkage table,過程鏈接表).這兩個節之間的交互可以實現延遲綁定(lazy binging),這種方法將過程地址的綁定推遲到第一次調用該函數。為了實現延遲綁定,GOT的頭三條表目是特殊的:GOT[0]包含.dynamic 段的地址,.dynamic段包含了動態鏈接器用來綁定過程地址的信息,比如符號的位置和重定位信息;GOT[1]包含動態鏈接器的標識;GOT[2]包含動態鏈接器的延遲綁定代碼的入口點。GOT的其他表目為本模塊要引用的一個全局變量或函數的地址。PLT是一個以16字節(32位平臺中)表目的數組形式出現的代碼序列。其中PLT[0]是一個特殊的表目,它跳轉到動態鏈接器中執行;每個定義在共享庫中并被本模塊調用的函數在PLT中都有一個表目,從 PLT[1]開始.模塊對函數的調用會轉到相應PLT表目中執行,這些表目由三條指令構成。第一條指令是跳轉到相應的GOT存儲的地址值中.第二條指令把函數相應的ID壓入棧中,第三條指令跳轉到PLT[O]中調用動態鏈接器解析函數地址,并把函數真正地址存入相應的GOT表目中。被調用函數GOT相應表目中存儲的最初地址為相應PLT表目中第二條指令的地址值,函數第一次被調用后.GOT表目中的值就為函數的真正地址。因此,第一次調用函數時開銷比較大.但是其后的每次調用都只會花費一條指令和一個間接的存儲器引用。?
3.3 強大的工具支持?
由于gnu有大量的工具支持elf文件格式.隨著gnu工具的功能的擴展.程序員對ELF文件的運用也越來越靈活。例如,在C++中全局的構造函數和析構函數必須非常小心的處理碰到的語言規范問題。構造函數必須在main函數之前被調用。析構函數必須在main函數返回之后被調用。ELF文件格式中,定義了兩個特殊的節 (section),.init和.fini,.init保存著可執行指令,它構成了進程的初始化代碼。當一個程序開始運行時,在main函數被調用之前 (c語言稱為main),系統安排執行這個section的中的代碼。.fini保存著可執行指令,它構成了進程的終止代碼。當一個程序正常退出時.系統安排執行這個section的中的代碼。C++編譯器利用這個特性.構造正確的.init和.fini sections.并結合.ctors(該section保存著程序的全局的構造函數的指針數組)和.dtors(該section保存著程序的全局的析構函數的指針數組)兩個section,完成全局的構造函數和析構函數的處理。?
GCC還有許多擴展的特性.有些對ELF 特別的有用。其中一個就是_attribute_ 。使用_attribute_可以使一個函數放到_CTOR_LIST_或者_DTOR_LIST_里。 _attribute_((constructor))促使函數在進入main之前會被自動調用。_attribute_((destructor))促使函數在main返回或者exit調用之后被自動調用。這種函數必須是不能帶參數的而且必須是static void類型的函數。在ELF下,這個特性在一般的可執行文件和共享庫中都能很好的工作。另外一個GCC的特性是 attribute_(section("sectionname")),使用這個,能把一個函數或者是數據結構放到任何的section中。?
[舉例]?
這里,通過使用一些用于操作ELF文件的工具的例子,來對其有一個直觀的了解。?
1 readelf工具?
readelf用來顯示ELF格式目標文件的信息.可通過參數選項來控制顯示哪些特定信息。?
*讀取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編譯生成的一個簡單的可執行程序。?
*查看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文件的節信息:?
$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可以把一種目標文件中的內容復制到另一種類型的目標文件中.?
通過objcopy的各種選項,可以對目標文件進行各種類型的操作。例如去掉可執行文件的調試信息(效果等同于strip)等等,具體需要做什么操作,要求我們對elf文件中每個部分的內容有所理解。?
這里只給出一個例子:?
*使用objcopy把.comment段和.note段的信息去掉:?
$ objcopy -R .comment -R .note hello hello.min?
這里,hello是一個可執行文件,通過"readelf -l hello"或者"readelf -S hello"命令可以知道文件中包含一個.note段和.comment段,通過這個命令,將這兩個段從文件中刪除,不會改變原來的文件,而是將刪除了這些信息的文件存放在hello.min中。實際通過這個方法,可以減少可執行文件的大小,且不影響可執行文件的功能。?
項的含義是:?
-R .note -R .comment 表示移掉 .note 與 .comment 段?
-O binary xyb xyb.bin 表示由xyb生成二進制文件xyb.bin?
3 objdump工具
objdump是用查看目標文件或者可執行的目標文件的構成的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?
......其余內容省略......?
這里,main是一個可執行文件。?
*輸出目標文件的符號表:?
# 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?
......其余信息省略......?
*以某種分類信息的形式把目標文件的數據組織(被分為幾大塊)輸出??:?
# 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?
...省略...?
這里可知,分別顯示出各個段相關的信息。?
*輸出指定段的信息:?
# 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列出的符號有很多,常見的有三種,一種是在庫中被調用,但并沒有在庫中定義(表明需要其他庫支持),用U表示;一種是庫中定義的函數,用T表示,這是最常見的;另外一種是所謂的“弱態”符號,它們雖然在庫中被定義,但是可能被其他庫中的同名符號覆蓋,用W表示。?
*假設開發者希望知道hello庫中是否定義了 printf():?
$nm libhello.so |grep printf?
U printf?
U表示符號printf被引用,但是并沒有在函數內定義,由此可以推斷,要正常使用hello庫,必須有其它庫支持。?
5 ldd工具?
ldd命令可以用來查看一個可執行文件或者庫依賴哪些其他的文件。?
*使用ldd命令查看hello依賴于哪些庫:?
$ldd hello?
libc.so.6=>/lib/libc.so.6(0x400la000)?
/lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)?
這里,結合nm,從上面的結果可以繼續查看printf最終在哪里被定義.?
?
評論