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


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

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

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

ELF header定義

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_indent中各字節含義

如圖,前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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM