ELF檔案格式與相關命令
一、相關概念介紹
可執行檔案(executable)格式:
windows平臺下是PE(protable executable) linux平臺下是ELF(executable linkable format)
目標檔案:
原始碼(.c .cpp)編譯之後生成的檔案(.o或.obj),從檔案結構上講,它是已經編譯後的可執行檔案格式,只是沒有經過連結的過程,其中可能有些符號或有些地址還沒有被調整。它本身是按照可執行檔案格式儲存的,跟真正的可執行檔案在結構上稍有不同,因此目標檔案和可執行檔案看成是一種檔案型別。動態連結庫(.so .dll)和靜態連結庫(.a .lib)檔案都是按照可執行檔案格式儲存。
二、ELF檔案格式分析:
ELF檔案格式的種類:
可重定位檔案(relocatable) .o .a 可執行檔案(executable) 共享目標檔案(shared object) .so 核心轉儲檔案 core dump file 檔名 該命令可以用來檢視檔案格式的具體型別。
ELF檔案格式提供了兩種不同的視角,在彙編器和連結器看來,ELF檔案是由section header table描述的一系列section的集合,而執行一個ELF檔案時,在載入器
本文的討論基於一個執行linux的x86系統環境,使用標準ELF檔案格式。討論集中在32位程式碼,在x86-64系統上用gcc -m32產生32位程式碼。
若編譯時發生/usr/include/features.h:364:25: fatal error: sys/cdefs.h: 沒有那個檔案或目錄,則使用命令sudo apt-get install libc6-dev-i386解決。ELF可重定位檔案詳解:
相關術語的說明:
ELF可執行檔案大體按照圖中的形式組織,不同的是在ELF可重定位檔案中的內容稱為節(section),對應的有section header table。在ELF可執行檔案中的內容稱為段(segment),對應的有program header table。可重定位檔案中的節(section)在連結時對應節進行合併生成可執行檔案中的段(segment)。.text 存放程式原始碼編譯後的機器指令 .data 存放全域性變數和區域性靜態變數 .bss 為未初始化的全域性變數和區域性靜態變數預留位置,目標檔案中,未初始化變數不需要佔據任何實際的磁碟空間 .rel.text 存放呼叫外部函式或者引用全域性變數等相關的重定位資訊 .rel.data 一個全域性變數被初始化為其他檔案中的全域性變數地址或者外部定義函式的地址。 .symtab 存放函式和全域性變數的資訊,重定位時根據.rel.text和.rel.data來修正相關地址 .debug -g選項之後才有, 除錯符號表 .line -g選項之後才有,記錄原始碼行號和.text中機器指令的對映關係 .strtab 以null結尾的字串序列 ELF頭(elf header)------readelf -h filename 包含系統相關、型別相關、載入相關、連結相關的資訊 節頭部表(section header table)------readelf -S filename 描述程式節,為彙編器和連結器服務。它把elf檔案分成了許多
三、例項解析ELF檔案:
原始碼:
main.c int add(int a,int b); static int si;//.bss extern int buf[]; int *copy = &buf[0];//.rel.data int main() { int a = 3; int b = 5; int c = add(a,b);//.rel.text char *s = "hello c";//.rodata static int si;//.bss return 0; } add.c int buf[2]; int add(int a,int b) { return (a+b); } makefile(為了簡化討論,makefile檔案中沒有新增-g選項) all:main main:main.o add.o gcc -o main main.o add.o -m32 main.o:main.c gcc -c main.c -m32 add.o:add.c gcc -c add.c -m32 clean: rm -rf *.o main
ELF頭的描述
readelf -h main.o
readelf -h main
從ELF頭中type的值可判斷出檔案的具體型別,同時也可以從ELF頭的入口地址判斷出檔案的具體型別(elf可重定位檔案僅僅只是編譯後的檔案,沒有經過連結過程,不能執行,因此不能給出正確的入口地址,只能給出一個非法的地址0x0,標識該檔案是可重定位檔案)
ELF檔案頭包含的資訊有:
系統相關資訊:elf檔案魔數(標識elf檔案)、平臺位數、資料編碼方式、elf頭部版本、硬體平臺e_machine、目標檔案版本e_version、處理器特定標誌e_ftags
目標檔案型別:e_type標識目標檔案型別,1為可重定位檔案,2為可執行檔案,3為共享檔案
載入相關資訊:e_entry為程式進入點,e_phoff為程式表頭偏移量,e_ehsize為elf頭部長度,e_phentsize為程式表頭中一個條目的長度,e_phnum為程式頭表條目個數
連結相關資訊:e_shoff為節頭表偏移量,e_shentsize為節頭表中一個條目的長度,e_shnum為節頭表條目個數,e_shstmdx為節頭表字符索引ELF檔案頭結構被定義在”/usr/include/elf.h“,資料結構中的EI_NIDENT在elf.h中被定義為16,魔數(magic)同樣也是16位元組的序列。最開始的4個位元組是所有ELF檔案必須相同的標識碼0x7f對應ASCII字元中的DEL控制符,0x45 0x4c 0x46分別對應ELF三個字元。第五個位元組標識ELF檔案類(0x01表示32位,0x02表示64位),第六個位元組標識位元組序(0x0表示無效0x01表示小端0x02表示大端),第七個位元組表示ELF檔案版本(通常是1),其餘九個位元組填充為0。資料結構中e_ident陣列包含了Magic到ABI Version的資訊,其餘成員與圖中一一對應。
ELF頭的資料結構:
ELF可重定位檔案section header table的描述
readelf -S main.o
section header table為彙編器和連結器服務,elf檔案被分成了許多節,每個節儲存著用於不同目的的資料。每一個節在節頭部表中都有一個表項描述該節的屬性,節的屬性包括小節名在字元表中的索引,型別,屬性,執行時的虛擬地址,檔案偏移,以位元組為單位的大小,小節的對齊等資訊
ELF的section header table的資料結構:
typedef struct{ Elf32_Word sh_name; /*小節名在字元表中的索引*/ Elf32_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; /*一些section儲存著一張固定大小入口的表*/ }Elf32_Shdr;
ELF可執行檔案program header table的描述
program header table告訴系統如何建立一個程序映像,它是從載入執行的角度來看待elf檔案。elf檔案被分成許多段(segment),elf檔案中的程式碼、連結資訊、註釋都是以段(segment)的形式存放。每個段都在program header table中有一個表項描述,包含以下屬性:段的型別,段的駐留位置相對於檔案開始處的偏移,段在記憶體中的首位元組地址,段在檔案映像中的位元組數,段在記憶體映像中的位元組數,段在記憶體和檔案中的對齊標記。
readelf -l main
對 一個ELF可執行程式而言,一個基本的段是標記p_type為INTERP的段,它表明了執行此程式所需要的程式直譯器(/lib/ld- linux.so.2),實際上也就是動態聯結器(dynamiclinker)。最重要的段是標記p_type為LOAD的段,它表明了為執行程 序而需要載入到記憶體的資料。檢視上面實際輸入,可以看見有兩個可LOAD段,第一個為可讀可執行的程式碼段.text(FLg為RE),第二個為可讀可寫的資料段.data(Flg為RW)。
ELF的program header table的資料結構:
readelf -S main
本圖中列出了main被連結前所有的節(section),上圖中展示了section到segment的對映關係,以下詳細介紹main被連結前所有的節(section)以及其他一些節(section)
.comment 編譯器版本資訊 .data 初始化了的資料 .debug 除錯資訊 .dynamic 動態連結資訊 .dynstr 動態連結時需要的字串 .dynsym 動態符號表 .hash 符號雜湊表 .shstrtab section string table節名錶 .strtab string table字串表,用於儲存ELF檔案中用到的各種字串 .symtab symbol table符號表 .note 額外的編譯器資訊 .init 程序的初始化程式碼,在main函式之前執行(c語言中) .fini 程序的終止程式碼 .interp 程式的解釋程式(interpreter)的路徑,若該section中有一個可裝載的段(segment),那麼 該section的屬性的SH_ALLOC位將被設定,否則,該位不會被設定。 .plt 儲存著過程連結表(procedure linkage table) .got 儲存著全域性偏移量表(global offset table)
.eh_frame的相關資訊參見:http://book.2cto.com/201309/33387.html簡單來說,在ELF檔案的彙編檔案(.s)中,存在一些偽指令,這些偽指令輔助編譯過程建立棧幀資訊。它們被儲存在目標檔案的段“.eh_frame”中。
其他分析ELF檔案的工具: