歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> Linux中ELF格式文件介紹

Linux中ELF格式文件介紹

日期:2017/3/3 11:32:27   编辑:Linux技術
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文件的工具,也是非常強大和重要的,其更多的信息參見幫助手冊。
轉載網址:http://blog.chinaunix.net/uid-9525959-id-2001831.html點擊打開鏈接
Copyright © Linux教程網 All Rights Reserved