1. 程式人生 > 實用技巧 >ELF 檔案 學習

ELF 檔案 學習

ElF 是什麼?

  1. 可用於連結的可從定位檔案,與其他目標檔案生成可執行檔案或者共享檔案
  2. 可執行檔案
  3. 目標共享檔案

通俗的說 在c++或者 C 編譯型語言 經過編譯階段生成可從定位的二進位制檔案可以說是一種ELF檔案,第二種就是在Linux下可執行的檔案.elf(一個或者多個.o 檔案經過連結過程生成的可執行檔案)同時要具有可執行許可權,就能在Linux下執行。第三種就是.so庫 。

對於ELF檔案提供兩種檢視:

  • 連結檢視
  • 執行檢視

按照我的理解 兩種檢視兩種規範,上面所說的3種ELF檔案形式,那麼系統如何區分?在於這個檔案的具體作用,例如ELF檔案是個用於連結的可從定位檔案,那麼就遵循連結檢視,如果是可執行檔案就要遵循執行檢視,也就是說 基於不同型別的ELF檔案所產生的規則。

具體檢視:(圖片來源網路)

連結檢視以節為單位,執行檢視以段為單位

  • ELF 頭部: 描述的是整個檔案的組織

ELF header/格式程式碼如下:#define EI_NIDENT 16

  

typedef struct{

  unsigned char e_ident[EI_NIDENT];

  Elf32_Half e_type;     //它標識的是該檔案的型別。
  Elf32_Half e_machine;  //表明執行該程式需要的體系結構。
  Elf32_Word e_version;  // 表示檔案的版本。
  Elf32_Addr e_entry;    //程式的入口地址。
  Elf32_Off e_phoff; //表示Program header table 在檔案中的偏移量 如果沒有程式頭表為0   Elf32_Off e_shoff; //表示Section header table 在檔案中的偏移量 如果沒有節頭表為0   Elf32_Word e_flags; // 對IA32而言,此項為0。   Elf32_Half e_ehsize; //表示ELF header大小   Elf32_Half e_phentsize; //表示Program header table中每一個條目的大小。   Elf32_Half e_phnum; //表示Program header table中有多少個條目。
  Elf32_Half e_shentsize; // 表示Section header table中的每一個條目的大小   Elf32_Half e_shnum; //表示Section header table中有多少個條目。   Elf32_Half e_shstrndx; //包含節名稱的字串是第幾個節(從零開始計數   }Elf32_Ehdr; 64位基本同理

  1. e_ident :16位元組 主要是描述ELF檔案的解碼資訊(指明是32位還是64位)
  2. e_type :2位元組 描述ELF檔案的型別
    ET_NONE, 0, No file type
    ET_REL, 1, Relocatable file(可重定位檔案,通常是檔名以.o結尾,目標檔案) ET_EXEC, 2, Executable file (可執行檔案)
     ET_DYN, 3, Shared object file (動態庫檔案,)
     ET_CORE, 4, Core file (core檔案) ET_NUM, 5,表示已經定義了5種檔案型別 ET_LOPROC, 0xff00, Processor-specific ET_HIPROC, 0xffff, Processor-specific

    其他欄位看上面程式碼中描述

回到上面 那麼節和段有什麼區別呢? c++程式碼在連結階段的第一個階段 主要是段合併和符號表輸出,那麼要想生成一個可執行檔案,段是程式執行的必要條件,則節的粒度相對就小

再看看節區頭部表(Elf_Shdr):描述了其所對應的節的資訊,如節名、節大小、在檔案中的偏移、讀寫許可權等。編譯器、連結器、裝載器都是通過節頭表來定位和訪問各個節的屬性的。

sh_name    節名 :節名是一個字串,儲存在一個名為.shstrtab的字串表(可通過Section Header索引到)。sh_name的值實際上是其節名字串在.shstrtab中的偏移值

sh_type    節型別
sh_flags    節標誌位
sh_addr    節地址:節的虛擬地址  如果該節可以被載入,則sh_addr為該節被載入後在程序地址空間中的虛擬地址;否則sh_addr為0
sh_offset    節偏移  如果該節存在於檔案中,則表示該節在檔案中的偏移;否則無意義,如sh_offset對於BSS 節來說是沒有意義的
sh_size    節大小
sh_link、sh_info    節連結資訊
sh_addralign    節地址對齊方式
sh_entsize    節項大小  有些節包含了一些固定大小的項,如符號表,其包含的每個符號所在的大小都一樣的,對於這種節,sh_entsize表示每個項的大小。如果為0,則表示該節不包含固定大小的項。
 

節的分類

  1. .text 儲存了程式程式碼指令的程式碼節。一段可執行程式
  2. .rodata節 儲存只讀資料
  3. .plt過程連結表,包含了動態連結器從共享庫匯入函式所必需的程式碼
  4. .data 存在於data段中,儲存變數資訊
  5. .bss 儲存未初始化的全域性變數,初始化為0的
  6. .got 儲存了全域性偏移表(和.plt一同表示動態庫的入口地址),由動態連結器修改
  7. .dynsym節(動態連結符號表)儲存在text中,儲存了從動態庫匯入的符號表
  8. .dynstr節(動態連結字串表)
  9. .rel 重定位表
  10. .symtab 符號表是一個Elfn_sym中的一個數組,儲存了符號資訊
  11. .strtab 儲存符號字串表,表中內容被Elfn_sym結構體中的st_name 引用
  12. .ctors(構造器) .dtors(析構器)儲存了指向建構函式和解構函式的指標

符號表 :符號表是對某些資料或程式碼的符號的引用 .dynsym 儲存了外部檔案符號的全域性符號 例如printf庫函式 ,.symtab還儲存了可執行檔案的本地符號,如全域性變數或者函式等

.dynsym 和 .symtab 的區別是 .dynsym是.symtab 的子集 .dynsym 被標記為ALLOC 表示執行時分配並載入記憶體

符號表中的每一項都是一個Elf_sym結構體

st_name    符號名。該值為該符號名在字串表中的偏移地址。
st_value    符號對應的值。存放符號的值(可能是地址或位置偏移量)。
st_size    符號的大小。
st_other    0
st_shndx    符號所在的節
st_info    符號型別及繫結屬性

還有一個重要的表: 重定位表Elf_Rel

r_offset    重定位入口的偏移。
對於可重定位檔案來說,這個值是該重定位入口所要修正的位置的第一個位元組相對於節起始的偏移
對於可執行檔案或共享物件檔案來說,這個值是該重定位入口所要修正的位置的第一個位元組的虛擬地址



r_info    重定位入口的型別和符號
因為不同處理器的指令系統不一樣,所以重定位所要修正的指令地址格式也不一樣。每種處理器都有自己的一套重定位入口的型別。
對於可執行檔案和共享目標檔案來說,它們的重定位入口是動態連結型別的。

Elf 檔案中 基本重要的結構就這些,如有不對 請指教