ELF文件解析(二):ELF header詳解

上一篇講了ELF文件的總體佈局,以及section和segment的概念。按照計劃,今天繼續講 ELF header。

講新的內容之前,先更正一個錯誤:上一篇中講section header table中的條目和文件中的section是一一對應的,其實這麼講是不對的。一個section必定有一個section header來描述它,但一個section header不一定在文件中有對應的section,因爲有的section是不佔用文件字節的。segment也是這個道理。

這篇文章本來應該上週寫的,但上週忙於突擊考試,週末又自我放縱,看了兩天《相聲有新人》,所以今天趕緊補上。

ELF header的定義可以在 /usr/include/elf.h 中找到。Elf32_Ehdr是32位 ELF header的結構體。Elf64_Ehdr是64位ELF header的結構體。

typedef struct
{
  unsigned char    e_ident[EI_NIDENT];    /* Magic number和其它信息 */
  Elf32_Half    e_type;            /* Object file type */
  Elf32_Half    e_machine;        /* Architecture */
  Elf32_Word    e_version;        /* Object file version */
  Elf32_Addr    e_entry;        /* Entry point virtual address */
  Elf32_Off    e_phoff;        /* Program header table file offset */
  Elf32_Off    e_shoff;        /* Section header table file offset */
  Elf32_Word    e_flags;        /* Processor-specific flags */
  Elf32_Half    e_ehsize;        /* ELF header size in bytes */
  Elf32_Half    e_phentsize;        /* Program header table entry size */
  Elf32_Half    e_phnum;        /* Program header table entry count */
  Elf32_Half    e_shentsize;        /* Section header table entry size */
  Elf32_Half    e_shnum;        /* Section header table entry count */
  Elf32_Half    e_shstrndx;        /* Section header string table index */
} Elf32_Ehdr;

typedef struct
{
  unsigned char    e_ident[EI_NIDENT];    /* Magic number and other info */
  Elf64_Half    e_type;            /* Object file type */
  Elf64_Half    e_machine;        /* Architecture */
  Elf64_Word    e_version;        /* Object file version */
  Elf64_Addr    e_entry;        /* Entry point virtual address */
  Elf64_Off    e_phoff;        /* Program header table file offset */
  Elf64_Off    e_shoff;        /* Section header table file offset */
  Elf64_Word    e_flags;        /* Processor-specific flags */
  Elf64_Half    e_ehsize;        /* ELF header size in bytes */
  Elf64_Half    e_phentsize;        /* Program header table entry size */
  Elf64_Half    e_phnum;        /* Program header table entry count */
  Elf64_Half    e_shentsize;        /* Section header table entry size */
  Elf64_Half    e_shnum;        /* Section header table entry count */
  Elf64_Half    e_shstrndx;        /* Section header string table index */
} Elf64_Ehdr;

64位和32位只是個別字段長度不同,比如 Elf64_AddrElf64_Off 都是64位無符號整數。而Elf32_AddrElf32_Off是32位無符號整數。這導致ELF header的所佔的字節數不同。32位的ELF header佔52個字節,64位的ELF header佔64個字節。

ELF header詳解

ELF header字節佈局

  1. e_ident佔16個字節。前四個字節被稱作ELF的Magic Number。後面的字節描述了ELF文件內容如何解碼等信息。等一下詳細講。
  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 (動態庫文件,你用gcc編譯出的二進制往往也屬於這種類型,驚訝嗎?)
     ET_CORE, 4, Core file (core文件,是core dump生成的吧?)
     ET_NUM, 5,表示已經定義了5種文件類型
     ET_LOPROC, 0xff00, Processor-specific
     ET_HIPROC, 0xffff, Processor-specific
    

    ET_LOPROCET_HIPROC 的值,包含特定於處理器的語義。

  3. e_machine,2字節。描述了文件面向的架構,可取值如下(因爲文檔較老,現在有更多取值,參見/usr/include/elf.h中的EM_開頭的宏定義):

     EM_NONE, 0, No machine
     EM_M32, 1, AT&T WE 32100
     EM_SPARC, 2, SPARC
     EM_386, 3, Intel 80386
     EM_68K, 4, Motorola 68000
     EM_88K, 5, Motorola 88000
     EM_860, 7, Intel 80860
     EM_MIPS, 8, MIPS RS3000
     ... ...
    
  4. e_version,2字節,描述了ELF文件的版本號,合法取值如下:

     EV_NONE, 0, Invalid version
     EV_CURRENT, 1, Current version,通常都是這個取值。
     EV_NUM, 2, 表示已經定義了2種版本號
    
  5. e_entry,(32位4字節,64位8字節),執行入口點,如果文件沒有入口點,這個域保持0。
  6. e_phoff, (32位4字節,64位8字節),program header table的offset,如果文件沒有PH,這個值是0。
  7. e_shoff, (32位4字節,64位8字節), section header table 的offset,如果文件沒有SH,這個值是0。
  8. e_flags, 4字節,特定於處理器的標誌,32位和64位Intel架構都沒有定義標誌,因此eflags的值是0。
  9. e_ehsize, 2字節,ELF header的大小,32位ELF是52字節,64位是64字節。
  10. e_phentsize,2字節。program header table中每個入口的大小。
  11. e_phnum, 2字節。如果文件沒有program header table, e_phnum的值爲0。e_phentsize乘以e_phnum就得到了整個program header table的大小。
  12. e_shentsize, 2字節,section header table中entry的大小,即每個section header佔多少字節。
  13. e_shnum, 2字節,section header table中header的數目。如果文件沒有section header table, e_shnum的值爲0。e_shentsize乘以e_shnum,就得到了整個section header table的大小。
  14. e_shstrndx, 2字節。section header string table index. 包含了section header table中section name string table。如果沒有section name string table, e_shstrndx的值是SHN_UNDEF.
注意:program header table一般譯爲程序頭表,section header table 一般譯爲節頭表,因爲這樣的翻譯無助於理解,所以我傾向於不翻。

e_ident

回過頭來,我們仔細看看文件前16個字節,也是e_ident

e_ident各字節含義

如圖,前4個字節是ELF的Magic Number,固定爲7f 45 4c 46
第5個字節指明ELF文件是32位還是64位的。
第6個字節指明瞭數據的編碼方式,即我們通常說的little endian或是big endian。little endian我喜歡稱作小頭在前,低位字節在前,或者直接說低位字節在低位地址,比如0x7f454c46,存儲順序就是46 4c 45 7f。big endian就是大頭在前,高位字節在前,直接說就是高位字節在低位地址,比如0x7f454c46,在文件中的存儲順序是7f 45 4c 46
第7個字節指明瞭ELF header的版本號,目前值都是1。
第8-16個字節,都填充爲0。

readelf讀取ELF header

我們使用readelf -h <elffile>可以讀取文件的ELF header信息。
比如我本地有執行文件hello,我執行reaelf -h hello,結果如下:

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x1050
  Start of program headers:          64 (bytes into file)
  Start of section headers:          14768 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         11
  Size of section headers:           64 (bytes)
  Number of section headers:         29
  Section header string table index: 28

這是我用gcc生成的執行文件,但注意它的Type是DYN (Shared object file),這大概是因爲,這個文件不能直接執行,是依賴於解釋器和c庫才能運行。真正的可執行文件是解釋器,而hello相對於解釋器來說也是個共享庫文件。這是我的推斷,需要後面深入學習後驗證。

今天就講到這了,這周結束前計劃寫本系列第三篇,關於目標文件內section的知識。

2018-10-22 Mon

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