ELF文件格式解析(完)

轉:https://www.52pojie.cn/thread-591986-1-1.html

數據類型
首先在解析之前, 必須對數據類型格式聲明一下

名稱

大小

說明

Elf32_Addr

4

無符號程序地址

Elf32_Half

2

無符號中等整數

Elf32_Off

4

無符號文件偏移

Elf32_SWord

4

有符號大整數

Elf32_Word

4

無符號大整數

unsigned char

1

無符號笑整數



整體結構


概述

  • ELF頭部(ELF_Header): 每個ELF文件都必須存在一個ELF_Header,這裏存放了很多重要的信息用來描述整個文件的組織,如: 版本信息,入口信息,偏移信息等。程序執行也必須依靠其提供的信息。
  • 程序頭部表(Program_Header_Table): 可選的一個表,用於告訴系統如何在內存中創建映像,在圖中也可以看出來,有程序頭部表纔有段,有段就必須有程序頭部表。其中存放各個段的基本信息(包括地址指針)。
  • 節區頭部表(Section_Header_Table): 類似與Program_Header_Table,但與其相對應的是節區(Section)。
  • 節區(Section): 將文件分成一個個節區,每個節區都有其對應的功能,如符號表,哈希表等。
  • 段(Segment): 嗯…就是將文件分成一段一段映射到內存中。段中通常包括一個或多個節區


注:每個節區都應該是前後相連的,且不可有重疊。即在一個地址上的字節只能屬於一個節區


詳解


ELF_Header


以下是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;

  Elf32_Off e_shoff;

  Elf32_Word e_flags;

  Elf32_Half e_ehsize;

  Elf32_Half e_phentsize;

  Elf32_Half e_phnum;

  Elf32_Half e_shentsize;

  Elf32_Half e_shnum;

  Elf32_Half e_shstrndx;

}Elf32_Ehdr;

然後來逐一解釋下各個字段:

  • e_ident 這是一個數組,其每個字節又都有所代表的含義:         
    • EI_MAG0 - EI_MAG3 文件標識就是平時所說的ELF頭,即 7F 45 4C 46(ELF)
    • EI_CLASS 文件類,其實代表的是32位/64位程序

      取值

      代表

      含義

      01

      ELFCLASS32

      32位程序

      02

      ELFCLASS64

      64位程序

    • EI_DATA 數據編碼,一般都是01[td]

      取值

      代表

      含義

      01

      ELFDATA2LSB

      高位在前

      02

      ELFDATA2MSB

      低位在前

    • EI_VERSION 文件版本,固定值01 EV_CURRENT
    • EI_PAD 呃…就是一堆全是00的用來補全大小的數組
    • EI_NIDENT 說是e_ident數組的大小,但我看了好幾個so都是00
  • e_type 標識文件類型

    取值

    代表

    含義

    00

    ET_NONE

    未知文件類型格式

    01

    ET_REL

    可重定位文件

    02

    ET_EXEC

    可執行文件

    03

    ET_DYN

    共享目標文件(SO)

    04

    ...

    ...

  • e_machine 聲明ABI

    取值

    代表

    含義

    01

    ...

    ...

    03

    EM_386

    X86

    04

    ...

    ...

    28h

    EM_ARM

    arm

    29h

    ...

    ...

  • e_version 跟ident[]裏的EI_VERSION一樣,爲01
  • e_entry 可執行程序入口點地址。
  • e_phoff Program Header Offset,程序頭部表索引地址,沒有則爲0。
  • e.shoff Section Header Offset,節區表索引地址,沒有則爲0。
  • e_flags “保存與文件相關的,特定於處理器的標誌。”(不知道有什麼用,看了幾個arm都是00 00 00 05,x86都是0)。

  • e_ehsize ELF_Header Size,嗯..ELF頭部的大小
  • e_phentsize 程序頭部表的單個表項的大小
  • e_phnum 程序頭部表的表項數
  • e_shentsize 節區表的單個表項的大小
  • e_shnum 節區表的表項數
  • e_shstrndx String Table Index,在節區表中有一個存儲各節區名稱的節區(通常是最後一個),這裏表示名稱表在第幾個節區。


Program Header


在ELF_Header中,我們可以得到Program Header的索引地址(e_phoff)段數量(e_phnum)表項大小(e_phentsize)


然後我們來看一下Program Header中表項的結構定義:

typedef struct{

    Elf32_Word p_type;

    Elf32_Off p_offset;

    Elf32_Addr p_vaddr;

    Elf32_Addr p_paddr;

    Elf32_Word p_filesz;

    Elf32_Word p_memsz;

    Elf32_Word p_flage;

    Elf32_Word p_align;

} Elf32_phdr;
  • p_type 聲明此段的作用類型

    取值

    代表

    含義

    00

    PT_NULL

    此數組元素未用。結構中其他成員都是未定義的。

    01

    PT_LOAD

    此數組元素給出一個可加載的段,段的大小由 p_filesz 和 p_memsz 描述。文件中的字節被映射到內存段開始處。如果 p_memsz 大於 p_filesz,“剩餘”的字節要清零。p_filesz 不能大於 p_memsz。可加載的段在程序頭部表格中根據 p_vaddr 成員按升序排列。

    02

    PT_DYNAMIC

    數組元素給出動態鏈接信息。 

    03

    PT_INTERP

    數組元素給出一個 NULL 結尾的字符串的位置和長度,該字符串將被當作解釋器調用。這種段類型僅對與可執行文件有意義(儘管也可能在共享目標文件上發生)。在一個文件中不能出現一次以上。如果存在這種類型的段,它必須在所有可加載段項目的前面。

    04

    PT_NOTE

    此數組元素給出附加信息的位置和大小。

    05

    PT_SHLIB

    此段類型被保留,不過語義未指定。包含這種類型的段的程序與 ABI不符。

    06

    PT_PHDR

    此類型的數組元素如果存在,則給出了程序頭部表自身的大小和位置,既包括在文件中也包括在內存中的信息。此類型的段在文件中不能出現一次以上。並且只有程序頭部表是程序的內存映像的一部分時才起作用。如果存在此類型段,則必須在所有可加載段項目的前面。

    0x70000000

    PT_LOPROC

    此範圍的類型保留給處理器專用語義。

    0x7fffffff

    PT_HIPROC

    此範圍的類型保留給處理器專用語義。

    ...

    ...

    ...


    還有一些編譯器或者處理器標識的段類型,有待補充。

  • p_offset 段相對於文件的索引地址
  • p_vaddr 段在內存中的虛擬地址
  • p_paddr 段的物理地址
  • p_filesz 段在文件中所佔的長度
  • p_memsz 段在內存中所佔的長度
  • p_flage 段相關標誌(read、write、exec)
  • p_align 字節對其,p_vaddr 和 p_offset 對 p_align 取模後應該等於0。

Section Header Table


與Progarm Header類似,我們同樣可以從ELF Header中得到索引地址(e_shoff)節區數量(e_shnum)表項大小(e_shentsize),還可以由名稱節區索引(e_shstrndx)得到各節區的名稱。


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;

}Elf32_Shdr;

  • sh_name 節區名稱,此處是一個在名稱節區的索引。
  • sh_type 節區類型

    名稱

    取值

    說明

    SHT_NULL

    0

    此值標誌節區頭部是非活動的,沒有對應的節區。此節區頭部中的其他成員取值無意義。

    SHT_PROGBITS

    1

    此節區包含程序定義的信息,其格式和含義都由程序來解釋。

    SHT_SYMTAB

    2

    此節區包含一個符號表。目前目標文件對每種類型的節區都只能包含一個,不過這個限制將來可能發生變化。一般,SHT_SYMTAB 節區提供用於鏈接編輯(指 ld 而言)的符號,儘管也可用來實現動態鏈接。

    SHT_STRTAB

    3

    此節區包含字符串表。目標文件可能包含多個字符串表節區。

    SHT_RELA

    4

    此節區包含重定位表項,其中可能會有補齊內容(addend),例如 32 位目標文件中的 Elf32_Rela 類型。目標文件可能擁有多個重定位節區。

    SHT_HASH

    5

    此節區包含符號哈希表。所有參與動態鏈接的目標都必須包含一個符號哈希表。目前,一個目標文件只能包含一個哈希表,不過此限制將來可能會解除。

    SHT_DYNAMIC

    6

    此節區包含動態鏈接的信息。目前一個目標文件中只能包含一個動態節區,將來可能會取消這一限制。

    SHT_NOTE

    7

    此節區包含以某種方式來標記文件的信息。

    SHT_NOBITS

    8

    這種類型的節區不佔用文件中的空間,其他方面和 SHT_PROGBITS 相似。儘管此節區不包含任何字節,成員sh_offset 中還是會包含概念性的文件偏移

    SHT_REL

    9

    此節區包含重定位表項,其中沒有補齊(addends),例如 32 位目標文件中的 Elf32_rel 類型。目標文件中可以擁有多個重定位節區。

    SHT_SHLIB

    10

    此節區被保留,不過其語義是未規定的。包含此類型節區的程序與 ABI 不兼容。

    SHT_DYNSYM

    11

    作爲一個完整的符號表,它可能包含很多對動態鏈接而言不必要的符號。因此,目標文件也可以包含一個 SHT_DYNSYM 節區,其中保存動態鏈接符號的一個最小集合,以節省空間。

    SHT_LOPROC

    0X70000000

    這一段(包括兩個邊界),是保留給處理器專用語義的。

    SHT_HIPROC

    0X7FFFFFFF

    這一段(包括兩個邊界),是保留給處理器專用語義的。

    SHT_LOUSER

    0X80000000

    此值給出保留給應用程序的索引下界。

    SHT_HIUSER

    0X8FFFFFFF

    此值給出保留給應用程序的索引上界。

  • sh_flags 同Program Header的p_flags
  • sh_addr 節區索引地址
  • sh_offset 節區相對於文件的偏移地址
  • sh_size 節區的大小
  • sh_link 此成員給出節區頭部表索引鏈接。
  • sh_info 此成員給出附加信息。

    sh_type

    sh_link

    sh_info

    SHT_DYNAMIC

    此節區中條目所用到的字符串表格的節區頭部索引

    0

    SHT_HASH

    此哈希表所適用的符號表的節區頭部索引

    0

    SHT_REL、SHT_RELA

    相關符號表的節區頭部索引

    重定位所適用的節區的節區頭部索引

    SHT_SYMTAB、SHT_DYNSYM

    相關聯的字符串表的節區頭部索引

    最後一個局部符號(綁定 STB_LOCAL)的符號表索引值加一

    其它

    SHN_UNDEF

    0


9.sh_addralign

 
某些節區帶有地址對齊約束。例如,如果一個節區保存一個doubleword,那麼系統必須保證整個節區能夠按雙字對齊。sh_addr 對sh_addralign 取模,結果必須爲 0。目前僅允許取值爲 0 和 2的冪次數。數值 0 和 1 表示節區沒有對齊約束。


sh_entsize

 
某些節區中包含固定大小的項目,如符號表。對於這類節區,此成員給出每個表項的長度字節數。 如果節區中並不包含固定長度表項的表格,此成員取值爲 0。


一般來說,節區索引爲0,即第一個節區一般都是SHN_UNDEF,其各項值都固定爲0


劃重點:


1. 以“.”開頭的節區名稱是系統保留的。應用程序可以使用沒有前綴的節區名稱,以避免與系統節區衝突。


2. 目標文件中也可以包含多個名字相同的節區。


3. 保留給處理器體系結構的節區名稱一般構成爲:處理器體系結構名稱簡寫 + 節區名稱。


4. 處理器名稱應該與 e_machine 中使用的名稱相同。例如 .FOO.psect 街區是由 FOO 體系結構定義的 psect 節區。

 


部分系統節區作用詳解

 

字符串表

在一個ELF文件中通常擁有一個或以上的字符串表,即類型爲 SHT_STRTAB 的節區,如: ELF Header 中 e_shstrndx 索引的節區名稱表(.shstrtab)、符號名稱表(.dynstr)等。

對於字符串的定義,是以NULL(\0)開頭,以NULL結尾。

以一個.shstrtab表的內容爲例:

00 2E 73 68 73 74 72 74 61 62 00 2E 69 6E 74 65 72 70 00 2E 64 79 6E 73 79 6D 00 ...


從這裏可以得到3個字符串即:

  • 2E 73 68 73 74 72 74 61 62 (.shstrtab);
  • 2E 69 6E 74 65 72 70 (.interp);
  • 2E 64 79 6E 73 79 6D (.dynsym);

假如索引爲0,那麼字符串的內容就是 2E 73 68 73 74 72 74 61 62 (.shstrtab)

符號表

符號: 指函數或者數據對象等。
既然叫做表,那麼也分爲一個一個表項,其表項也有自己的結構定義:

typedef struct {

    Elf32_Word st_name;

    Elf32_Addr st_value;

    Elf32_Word st_size;

    unsigned char st_info;

    unsigned char st_other;

    Elf32_Half st_shndx;

} Elf32_sym;
  • st_name 符號名稱,給出的是一個在符號名稱表(.dynstr)中的索引
  • st_value 一般都是函數地址,或者是一個常量值
  • st_size 從 st_value 地址開始,共佔的長度大小
  • st_info 用於標示此符號的屬性,佔一個字節(2個字),兩個標示位,第一個標示位(低四位)標誌作用域,第二個標示位(高四位)標示符號類型

    取值

    代表

    含義

    0

    STB_LOCAL

    局部符號在包含該符號定義的目標文件以外不可見。相同名稱的局部符號可以存在於多個文件中,互不影響。

    1

    STB_GLOBAL

    全局符號對所有將組合的目標文件都是可見的。一個文件中對某個全局符號的定義將滿足另一個文件對相同全局符號的未定義引用。

    2

    STB_WEAK

    弱符號與全局符號類似,不過他們的定義優先級比較低。

    (以我的理解..就是LOACL是局部變量,GLOBAL 和 WEAK 是全局量, 兩者的差別在於是不是常量?猜的。)

    取值

    代表

    含義

    1

    STT_OBJECT

    符號與某個數據對象相關,比如一個變量、數組等等

    2

    STT_FUNC

    符號與某個函數或者其他可執行代碼相關

    3

    STT_SECTION

    符號與某個節區相關。這種類型的符號表項主要用於重定位,通常具有 STB_LOCAL 綁定。

    4

    STT_FILE

    傳統上,符號的名稱給出了與目標文件相關的源文件的名稱。文件符號具有 STB_LOCAL 綁定,其節區索引是SHN_ABS,並且它優先於文件的其他 STB_LOCAL 符號(如果有的話)

    舉個栗子: 比如此處數值爲0x12,那麼他就對應着 STB_GLOBAL 和 STT_FUNC。從編程的方面來說,就是一個 public 的函數。
  • st_other 固定值爲0。
  • st_shndx

    每個符號表項都以和其他節區間的關係的方式給出定義。此成員給出相關的節區頭部表索引。某些索引具有特殊含義。
    唔..我也不知道有什麼用…



代碼段
代碼段就是存放指令的節區(.text),符號表中的 st_value 指向代碼段中具體的函數地址,以其地址的指令爲函數開頭。

全局偏移表
指.got節區,.got內的值均爲 Elf32_Addr。其爲全局符號提供偏移地址(指向過程鏈接表)。

過程鏈接表
.plt節區,其每個表項都是一段代碼,作用是跳轉至真實的函數地址

哈希表
指.hash節區。哈希表的結構:

其中nchain爲符號表表項數,nchain 和 nbucket 是 chain 和 bucket 的數量。

數據段
.data、.bss、.rodata都屬於數據段。其中,

  • .data 存放已初始化的全局變量、常量。
  • .bss 存放未初始化的全局變量,所以此段數據均爲0,僅作佔位。
  • .rodata 是隻讀數據段,此段的數據不可修改,存放常量。


.init_array .fini_array
程序運行時,執行.init_array中的指令。
程序退出時,執行.fini_array中的指令。


暫時就寫這些吧。寫的不清楚的麻煩自己百度下..

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