天天看點

ELF檔案格式入門

什麼是ELF

ELF(Executable and Linking Format),即“可執行可連接配接格式”,最初由 UNIX系統實驗室做為應用程式二進制接口(ABI)的一部分而制定和釋出。簡單說就是一種檔案格式。

ELF檔案類型

(1)可重定位檔案, 一般就是源檔案編譯生成的".o"檔案,這些檔案用于與其它目标檔案進行連結生成可執行檔案或動态連結庫。

(2)共享目标檔案, 即動态連結庫檔案, 就是".so"檔案。

(3)可執行檔案, 經過連結的,可以執行的程式檔案。

ELF檔案格式

ELF檔案格式入門

因為ELF格式的檔案有些是用來連接配接的,有些是可以執行的。是以上面中從“連接配接”和“運作”兩個視角來看ELF檔案的格式。

ELF檔案主要由四部分組成:

(1)ELF檔案頭

(2)程式頭表

(3)節(Section)或段

(4)節頭表或段頭表

1、ELF檔案頭

描述檔案的主要特性:類型,CPU架構,入口位址,現有部分的大小和偏移等。

檔案頭資料結構如下:

#define EI_NIDENT  16

typedef struct elf64_hdr {
  unsigned char  e_ident[EI_NIDENT]; //魔術字,用來确認檔案是否是一個ELF檔案
  Elf64_Half e_type;  //檔案類型,比如可重定位檔案,可執行檔案等
  Elf64_Half e_machine; //處理器架構
  Elf64_Word e_version; //ELF檔案格式的版本
  Elf64_Addr e_entry;   //程式入口的虛拟位址
  Elf64_Off e_phoff;    //程式頭表開始處在檔案中的偏移量
  Elf64_Off e_shoff;   //節頭表開始處在檔案中的偏移量
  Elf64_Word e_flags;  //處理器特定的标志位
  Elf64_Half e_ehsize; //ELF檔案頭的大小
  Elf64_Half e_phentsize; //在程式頭表中每一個表項的大小
  Elf64_Half e_phnum; //程式頭表中總共有多少個表項
  Elf64_Half e_shentsize; //節頭表中每一個表項的大小
  Elf64_Half e_shnum;  //節頭表中每一個表項的大小
  Elf64_Half e_shstrndx; //節頭表中與節名字表相對應的表項的索引
} Elf64_Ehdr;      

2、程式頭表

程式頭表就是一個數組,裡面的每個元素都是一個程式頭。每一個程式頭描述了一個“段(segment)”。一個“段(segment)”包含一個或者多個“節(section)”。

程式頭的資料結構如下:

typedef struct elf64_phdr {
  Elf64_Word p_type;   //段的類型
  Elf64_Word p_flags;  //段屬性,處理器特定的标志位
  Elf64_Off p_offset;  //段内容在檔案中的位置
  Elf64_Addr p_vaddr;  //段内容的開始位置在程序空間中的虛拟位址
  Elf64_Addr p_paddr;  //段内容的開始位置在程序空間中的實體位址
  Elf64_Xword p_filesz;//段内容在檔案中的大小
  Elf64_Xword p_memsz; //段内容在記憶體中的大小
  Elf64_Xword p_align; //段對齊
} Elf64_Phdr;      

*注意: 程式頭隻對可執行檔案或共享目标檔案有意義,對于其它類型的目标檔案,該資訊可以忽略。

3、節頭表

節頭表也是一個數組。節頭表的每一個表項描述了節的資訊,通過每一個表項可以定位到對應的節。

節頭的資料結構如下:

typedef struct elf64_shdr {
  Elf64_Word sh_name; //節的名稱
  Elf64_Word sh_type; //節的類型
  Elf64_Xword sh_flags;//節的屬性
  Elf64_Addr sh_addr; //執行時節的虛拟位址
  Elf64_Off sh_offset;//節在檔案中的位置
  Elf64_Xword sh_size;//節的大小
  Elf64_Word sh_link; //索引值,指向節頭表中本節所對應的位置
  Elf64_Word sh_info; //節的附加資訊
  Elf64_Xword sh_addralign;//節對齊
  Elf64_Xword sh_entsize;  //有一些節的内容是一張表,
                         //其中每一個表項的大小是固定的,比如符号表。
                         //對于這種表來說,本成員指定其每一個表項的大小。
} Elf64_Shdr;      

4.節(Section)

節裡面就是代碼和資料。比如".text", “.data” , ".rodata"等。如下圖:

ELF檔案格式入門
  • .text : 就是存放代碼的節
  • .rodata: 存放隻讀資料的節
  • .data: 存放讀寫資料的節

在 ELF檔案中有一些特定的節是預定義好的,其内容是指令代碼或者控制資訊。比如:“.text”, “.bss”, ".data"等都是預定義的節。

應用程式也可以構造自己的段,但最好不要取與上述系統已定義的節相同的名字,也不要取以點号開頭的名字,以避免潛在的沖突。

更多定義可參考下面文檔:

​​https://paper.seebug.org/papers/Archive/refs/elf/Understanding_ELF.pdf​​

執行個體分析

我們自己寫一個應用程式,然後通過readelf指令來分析elf檔案。

(1)讀取可執行檔案的ELF檔案頭

readelf -h timer      
ELF檔案格式入門

這些解析完的參數就是對應上面的ELF檔案頭的結構體。

上面的檔案類型就是可執行檔案。

(2)讀取.o檔案的ELF檔案頭

readelf -h timer.o      
ELF檔案格式入門

上面的檔案類型為可重定位檔案。

(3)讀取可執行檔案的程式頭

readelf -l timer      
ELF檔案格式入門

從上面可以看到總共有9個程式頭,也就有9個段。這9個段和節之間的映射關系如下:

ELF檔案格式入門

總結

看完這些是不是覺得沒什麼用,好像開發中也很少用到。但如果有一天你的項目需要你寫連結腳本時,可能會從中找到一些有用的東西!

繼續閱讀