Linux中ELF格式文件介紹

Linux中ELF格式文件介紹 

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最終在哪裏被定義.     

參考: 

http://www.dz3w.com/mcu/linux/0070701.html 

http://linux.chinaunix.net/techdoc/beginner/2009/04/23/1108716.shtml 

http://www.dz3w.com/mcu/linux/0070701.html 

http://www.sudu.cn/info/html/edu/20080428/302819.html 


本文涉及的關於elf文件介紹的內容大部分是從網絡上收集整理的,也可以通過"man elf"或者"info elf"來查看其相關文檔信息;後面幾個操作elf文件的工具,也是非常強大和重要的,其更多的信息參見幫助手冊。 


作者:QuietHeart 

Email:[email protected] 

日期:2010年12月20日 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章