1 簡介數組
可執行連接格式(Executable and Linking Format)最初是由UNIX 系統實驗室(UNIX System Laboratories,USL)開發併發布的,做爲應用程序二進制接口(Application Binary Interface,ABI )的一部分。工具接口標準(Tool Interface Standards,TIS)委員會將還在發展的ELF 標準選做爲一種可移植的目標文件格式,能夠在32 位Intel 體系結構上的不少操做系統中使用[1, 2] 。數據結構
ELF 標準的目的是爲軟件開發人員提供一組二進制接口定義,這些接口能夠延伸到多種操做環境,從而減小從新編碼、從新編譯程序的須要。接口的內容包括目標模塊格式、可執行文件格式以及調試記錄信息與格式等。併發
TIS 給出的Portable Formats Specification 1.1 版本中主要針對三種不一樣類型的目標文件做了規定,並規定了程序加載與動態連接相關過程細節,給出了標準ANSI C 和libc例程必須提供的符號[1] 。在該組織隨後發佈的 Executable and Linking Format(ELF) Specification 1.2 版本中則分以下三個部分,主要的不一樣點是對與操做系統相關的部分進行了從新組織:編輯器
2 相關標準ide
2.1 System V ABI函數
System V Application Binary Interface(ABI)爲已編譯的應用程序定義了系統接口,同時也爲安裝腳本支持提供了一個最小環境。其目的是爲應用程序提供一個標準的二進制接口,使得這些程序可以運行在符合 X/Open Commen Application Environment Specification, Issue 4.2 和System V Interf ace Defin ition, Fourth Edition 的操做系統上。二進制要包含與不一樣處理器體系結構相關的信息,因此 ABI並非只有一個規範,而是一個規範體系。System V ABI 由兩個部分組成:一個通用的部分,描述System V 在全部硬件平臺上都一致的接口;一個處理器相關的部分,描述特定於某個處理器體系結構的具體實現。工具
2.2 LSB編碼
因爲咱們所關心的主要是 Linux 平臺上目標文件的格式,因此 Linux 標準 LSB(Linux Standard Base)也是重要的參考資料。LSB 的目標是加強不一樣的Linux 發佈版本之間的兼容性,與ABI 相似,也由兩個部分組成: 操作系統
gLSB (Generic LSB )適用於全部體系結構debug
archLSB (Architecture Specific LSB )特定某種體系結構的LSB
目前,LSB 由SourceForge 開放源碼項目社區提供支持。
3 ELF 文件格式
3.1 簡介
目標文件有三種類型:
可重定位文件(Relocatable File ) 包含適合於與其餘目標文件連接來建立可執行文件或者共享目標文件的代碼和數據。
可執行文件(Executable File) 包含適合於執行的一個程序,此文件規定了 exec() 如何建立一個程序的進程映像。
共享目標文件 (Shared Object File) 包含可在兩種上下文中連接的代碼和數據。首先連接編輯器能夠將它和其它可重定位文件和共享目標文件一塊兒處理,生成另一個目標文件。其次,動態連接器(Dynamic Linker)可能將它與某個可執行文件以及其它共享目標一塊兒組合,建立進程映像。
目標文件所有是程序的二進制表示,目的是直接在某種處理器上直接執行。
3.1.1 目標文件中的數據表示
目標文件格式支持 8 位字節/32 位體系結構。不過這種格式是能夠擴展的,目標文件所以以某些機器獨立的格式表達某些控制數據,使得可以以一種公共的方式來識別和解釋其內容。目標文件中的其它數據使用目標處理器的編碼結構,而無論文件在何種機器上建立。
表 1 ELF 中經常使用數據格式
名稱 大小 對齊 目的
Elf32_Addr 4 4 無符號程序地址
Elf32_Half 2 2 無符號中等整數
Elf32_Off 4 4 無符號文件偏移
Elf32_SWord 4 4 有符號大整數
Elf32_Word 4 4 無符號大整數
unsigned char 1 1 無符號小整數
目標文件中的全部數據結構都聽從「 天然」大小和對齊規則。若是必要,數據結構能夠包含顯式的補齊,例如爲了確保4 字節對象按4 字節邊界對齊。數據對齊一樣適用於文件內部。
3.2 目標文件格式
目標文件既要參與程序連接又要參與程序執行。出於方便性和效率考慮,目標文件格式提供了兩種並行視圖,分別反映了這些活動的不一樣需求。
圖 1 目標文件格式
連接視圖 執行視圖
ELF 頭部 ELF 頭部
程序頭部表(可選) 程序頭部表
節區 1
段 1
...
節區 n
段 2
...
... ...
節區頭部表 節區頭部表(可選)
文件開始處是一個 ELF 頭部(ELF Header ),用來描述整個文件的組織。節區部分包含連接視圖的大量信息:指令、數據、符號表、重定位信息等等。
程序頭部表 (Program Header Table ),若是存在的話,告訴系統如何建立進程映像。用來構造進程映像的目標文件必須具備程序頭部表,可重定位文件不須要這個表。
節區頭部表(Section Heade Table )包含了描述文件節區的信息,每一個節區在表中都有一項,每一項給出諸如節區名稱、節區大小這類信息。用於連接的目標文件必須包含節區頭部表,其餘目標文件能夠有,也能夠沒有這個表。
注意:儘管圖中顯示的各個組成部分是有順序的,實際上除了 ELF 頭部表之外,其餘節區和段都沒有規定的順序
3.3 ELF Header 部分
文件的最開始幾個字節給出如何解釋文件的提示信息。這些信息獨立於處理器,也獨立於文件中的其他內容。ELF Header 部分能夠用下圖中的數據結構表示:
圖 2 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 數組給出了ELF 的一些標識信息,這個數組中不一樣下標的含義如表 2 所示:
表 2 e_ident[] 標識索引
名稱 取值 目的
EI_MAG0 0 文件標識
EI_MAG1 1 文件標識
EI_MAG2 2 文件標識
EI_MAG3 3 文件標識
EI_CLASS 4 文件類
EI_DATA 5 數據編碼
EI_VERSION 6 文件版本
EI_PAD 7 補齊字節開始處
EI_NIDENT 16 e_ident[]大小
這些索引訪問包含如下數值的字節:
表 3 e_ident[]的內容說明
索引 說明
魔數(Magic Number),標誌此文件是一個 ELF 目標文件。
名稱 取值 位置
EI_MAG0 到 EI_MAG0 0x7f e_ident[EI_MAG0]
EI_MAG3 EI_MAG1 'E' e_ident[EI_MAG1]
EI_MAG2 'L' e_ident[EI_MAG2]
EI_MAG3 'F' e_ident[EI_MAG3]
標識文件的類別,或者說,容量。
名稱 取值 位置
ELFCLASSNONE 0 非法類別
EI_CLASS ELFCLASS32 1 32 位目標
ELFCLASS64 2 64 位目標
ELFCLASS32 支持虛存範圍 4 GB。ELFCLASS64 是爲 64 位預
留的,不過文件中的其餘內容都沒有針對 64 位定義。
EI_DATA 字節 e_ident[EI_DATA] 給出處理器特定數據的數據編碼方式。
名稱 取值 位置
ELFDATANONE 0 非法數據編碼
ELFDATA2LSB 1 高位在前
ELFDATA2MSB 2 低位在前
EI_VERSION ELF 頭部的版本號碼,不前此值必須是 EV_CURRENT。
EI_PAD 標記 e_ident 中未使用字節的開始。初始化爲 0 。
某些目標文件控制結構能夠增加,由於 ELF 頭部可以給出它們的實際大小。若是目標文件格式確實發生改變,可能會發生控制結構比預期的大或者小的狀況。在這種狀況下,忽略這些信息是容許的。至於對 「缺失」信息的處理方式則要依賴於上下文,若是定義了擴展的話,將會對這些作出規定。
注意:在 32 位 Intel 體系結構上要求:
一、標誌
位置 取值
e_ident[EI_CLASS] ELFCLASS32
e_ident[EI_DATA] ELFDATA2LSB
二、處理器標識(e_machine)成員必須是 EM_386。
ELF Header 中各個字段的說明如表 4 :
表 4 ELF Header 中各個字段的含義
成員 說明
e_ident 目標文件標識
目標文件類型:
名稱 取值 含義
ET_NONE 0 未知目標文件格式
ET_REL 1 可重定位文件
ET_EXEC 2 可執行文件
e_type ET_DYN 3 共享目標文件
ET_CORE 4 Core 文件(轉儲格式)
ET_LOPROC 0xff00 特定處理器文件
ET_HIPROC 0xffff 特定處理器文件
ET_LOPROC 和 ET_HIPROC 之間的取值用來標識與處理器相關的文件
格式。
給出文件的目標體系結構類型
名稱 取值 含義
EM_NONE 0 未指定
EM_M32 1 AT&T WE 32100
e_machine
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
其它值都是保留的。特定處理器的 ELF 名稱會使用機器名來進行區分。
目標文件版本
名稱 取值 含義
e_version
EV_NONE 0 非法版本
EV_CURRENT 1 當前版本
e_entry 程序入口的虛擬地址。若是目標文件沒有程序入口,能夠爲 0 。
程序頭部表格(Program Header Table)的偏移量(按字節計算)。若是文
e_phoff
件沒有程序頭部表格,能夠爲 0 。
節區頭部表格 (Section Header Table )的偏移量 (按字節計算)。若是文件
e_shoff
沒有節區頭部表格,能夠爲 0 。
保存與文件相關的,特定於處理器的標誌。標誌名稱採用 EF_machine_flag
e_flags
的格式。
e_ehsize ELF 頭部的大小(以字節計算)。
e_phentsize 程序頭部表格的表項大小(按字節計算)。
e_phnum 程序頭部表格的表項數目。能夠爲 0 。
e_shentsize 節區頭部表格的表項大小(按字節計算)。
e_shnum 節區頭部表格的表項數目。能夠爲 0 。
節區頭部表格中與節區名稱字符串表相關的表項的索引。若是文件沒有節
e_shstrndx
區名稱字符串表,此參數能夠爲 SHN_UNDEF 。
3.4 節區(Sections)
節區中包含目標文件中的全部信息,除了:ELF 頭部、程序頭部表格、節區頭部表格。節區知足如下條件:
(1). 目標文件中的每一個節區都有對應的節區頭部描述它,反過來,有節區頭部不意味着有節區。
(2). 每一個節區佔用文件中一個連續字節區域(這個區域可能長度爲 0 )。
(3). 文件中的節區不能重疊,不容許一個字節存在於兩個節區中的狀況發生。
(4). 目標文件中可能包含非活動空間(INACTIVE SPACE )。這些區域不屬於任何頭部和節區,其內容未指定。
3.4.1 節區頭部表格
ELF 頭部中,e_shoff 成員給出從文件頭到節區頭部表格的偏移字節數;e_shnum給出表格中條目數目;e_shentsize給出每一個項目的字節數。從這些信息中能夠確切地定位節區的具體位置、長度。
節區頭部表格中比較特殊的幾個下標以下:
表 5 節區頭部表格中的特殊下標
名稱 取值 說明
SHN_UNDEF 0 標記未定義的、缺失的、不相關的,或者沒有含義的
節區引用
SHN_LORESERVE OXFF00 保留索引的下界
SHN_LOPROC 0XFF00
保留給處理器特殊的語義
SHN_HIPROC 0XFF1F
包含對應引用量的絕對取值。這些值不會被重定位所
SHN_ABS OXFFF1
影響
相對於此節區定義的符號是公共符號。如 FORTRAN
SHN_COMMON OXFFF2
中 COMMON 或者未分配的 C 外部變量。
SHN_HIRESERVE 0XFFFF 保留索引的上界
介於 SHN_LORESERVE 和 SHN_HIRESERVE 之間的表項不會出如今節區頭部表中。
3.4.2 節區頭部
每一個節區頭部能夠用以下數據結構描述:
圖 3 節區頭部數據結構
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;
對其中各個字段的解釋以下:
表 6 節區頭部字段說明
成員 說明
給出節區名稱。是節區頭部字符串表節區(Section Header String sh_name Table Section )的索引。名字是一個 NULL 結尾的字符串。
sh_type 爲節區的內容和語義進行分類。參見節區類型。
sh_flags 節區支持 1 位形式的標誌,這些標誌描述了多種屬性。若是節區將出如今進程的內存映像中,此成員給出節區的第一個字 sh_addr 節應處的位置。不然,此字段爲 0 。
此成員的取值給出節區的第一個字節與文件頭之間的偏移。不過,sh_offset SHT_NOBITS 類型的節區不佔用文件的空間,所以其 sh_offset 成員給出的是其概念性的偏移。
此成員給出節區的長度(字節數)。除非節區的類型是 sh_size
SHT_NOBITS ,不然節區佔用文件中的 sh_size 字節。類型爲SHT_NOBITS 的節區長度可能非零,不過卻不佔用文件中的空間。
sh_link 此成員給出節區頭部表索引連接。其具體的解釋依賴於節區類型。
sh_info 此成員給出附加信息,其解釋依賴於節區類型。
某些節區帶有地址對齊約束。例如,若是一個節區保存一個doubleword,那麼系統必須保證整個節區可以按雙字對齊。sh_addr
sh_addralign 對 sh_addralign 取模,結果必須爲 0 。目前僅容許取值爲 0 和 2 的冪次數。數值 0 和 1 表示節區沒有對齊約束。
某些節區中包含固定大小的項目,如符號表。對於這類節區,此成 sh_entsize 員給出每一個表項的長度字節數。 若是節區中並不包含固定長度表項的表格,此成員取值爲 0 。
索引爲零 (SHN_UNDEF )的節區頭部也是存在的,儘管此索引標記的是未定義的節區引用。這個節區的內容固定以下:
表 7 SHN_UNDEF(0)節區的內容
字段名稱 取值 說明
sh_name 0 無名稱
sh_type SHT_NULL 非活動
sh_flags 0 無標誌
sh_addr 0 無地址
sh_offset 0 無文件偏移
sh_size 0 無尺寸大小
sh_link SHN_UNDEF 無連接信息
sh_info 0 無輔助信息
sh_addralign 0 無對齊要求
sh_entsize 0 無表項
3.4.2.1 節區類型—sh_type 字段
節區類型定義如表 8 :
表 8 節區類型定義
名稱 取值 說明
此值標誌節區頭部是非活動的,沒有對應的節區。此節區頭部
SHT_NULL 0
中的其餘成員取值無心義。
SHT_PROGBITS 1 此節區包含程序定義的信息,其格式和含義都由程序來解釋。
此節區包含一個符號表。目前目標文件對每種類型的節區都只
能包含一個,不過這個限制未來可能發生變化。
SHT_SYMTAB 2
通常,SHT_SYMTAB 節區提供用於連接編輯(指 ld 而言)
的符號,儘管也可用來實現動態連接。
SHT_STRTAB 3 此節區包含字符串表。目標文件可能包含多個字符串表節區。
此節區包含重定位表項,其中可能會有補齊內容 (addend ),例
SHT_RELA 4 如 32 位目標文件中的 Elf32_Rela 類型。目標文件可能擁有多
個重定位節區。
此節區包含符號哈希表。全部參與動態連接的目標都必須包含
SHT_HASH 5 一個符號哈希表。目前,一個目標文件只能包含一個哈希表,
不過此限制未來可能會解除。
此節區包含動態連接的信息。目前一個目標文件中只能包含一
SHT_DYNAMIC 6
個動態節區,未來可能會取消這一限制。
SHT_NOTE 7 此節區包含以某種方式來標記文件的信息。
這種類型 的節區不 佔用文件中的空間,其餘方面和
SHT_NOBITS 8 SHT_PROGBITS 類似。儘管此節區不包含任何字節,成員
sh_offset 中仍是會包含概念性的文件偏移
此節區包含重定位表項,其中沒有補齊 (addends),例如 32 位
SHT_REL 9 目標文件中的 Elf32_rel 類型。目標文件中能夠擁有多個重定
位節區。
此節區被保留,不過其語義是未規定的。包含此類型節區的程
SHT_SHLIB 10
序與 ABI 不兼容。
做爲一個完整的符號表,它可能包含不少對動態連接而言沒必要
SHT_DYNSYM 11 要的符號。所以,目標文件也能夠包含一個 SHT_DYNSYM 節
區,其中保存動態連接符號的一個最小集合,以節省空間。
SHT_LOPROC 0X70000000
這一段(包括兩個邊界),是保留給處理器專用語義的。
SHT_HIPROC OX7FFFFFFF
SHT_LOUSER 0X80000000 此值給出保留給應用程序的索引下界。
SHT_HIUSER 0X8FFFFFFF 此值給出保留給應用程序的索引上界。
其它的節區類型是保留的。
3.4.2.2 sh_flags 字段
sh_flags 字段定義了一個節區中包含的內容是否能夠修改、是否能夠執行等信息。若是一個標誌位被設置,則該位取值爲 1。未定義的各位都設置爲0。
表 9 節區頭部的 sh_flags 字段取值
名稱 取值
SHF_WRITE 0x1
SHF_ALLOC 0x2
SHF_EXECINSTR 0x4
SHF_MASKPROC 0xF0000000
其中已經定義了的各位含義以下:
SHF_WRITE: 節區包含進程執行過程當中將可寫的數據。
SHF_ALLOC: 此節區在進程執行過程當中佔用內存。某些控制節區並不出現於目標
文件的內存映像中,對於那些節區,此位應設置爲 0。
SHF_EXECINSTR: 節區包含可執行的機器指令。
SHF_MASKPROC: 全部包含於此掩碼中的四位都用於處理器專用的語義。
3.4.2.3 sh_link 和 sh_info 字段
根據節區類型的不一樣,sh_link 和 sh_info 的具體含義也有所不一樣:
表 10 sh_link 和 sh_info 字段解釋
sh_type sh_link sh_info
此節區中條目所用到的字符串表格
SHT_DYNAMIC 0
的節區頭部索引
此哈希表所適用的符號表的節區頭
SHT_HASH 0
部索引
SHT_REL 重定位所適用的節區的
相關符號表的節區頭部索引
SHT_RELA 節區頭部索引
最後一個局部符號(綁
SHT_SYMTAB
相關聯的字符串表的節區頭部索引 定 STB_LOCAL )的符
SHT_DYNSYM
號表索引值加一
其它 SHN_UNDEF 0
3.4.3 特殊節區
不少節區中包含了程序和控制信息。下面的表中給出了系統使用的節區,以及它們的類型和屬性。
表 11 常見特殊節區
名稱 類型 屬性 含義
包含將出如今程序的內存映像中的爲初始
SHF_ALLOC + 化數據。根據定義,當程序開始執行,系統
.bss SHT_NOBITS
SHF_WRITE 將把這些數據初始化爲 0 。此節區不佔用文
件空間。
.comment SHT_PROGBITS (無) 包含版本控制信息。
SHF_ALLOC +
.data SHT_PROGBITS
SHF_WRITE 這些節區包含初始化了的數據,將出如今程
SHF_ALLOC + 序的內存映像中。
.data1 SHT_PROGBITS
SHF_WRITE
.debug SHT_PROGBITS (無) 此節區包含用於符號調試的信息。
此節區包含動態連接信息。節區的屬性將包
.dynamic SHT_DYNAMIC 含 SHF_ALLOC 位。是否 SHF_WRITE 位
被設置取決於處理器。
此節區包含用於動態連接的字符串,大多數
.dynstr SHT_STRTAB SHF_ALLOC 狀況下這些字符串表明了與符號表項相關
的名稱。
.dynsym SHT_DYNSYM SHF_ALLOC 此節區包含了動態連接符號表。
此節區包含了可執行的指令,是進程終止代
SHF_ALLOC +
.fini SHT_PROGBITS 碼的一部分。程序正常退出時,系統將安排
SHF_EXECINSTR
執行這裏的代碼。
.got SHT_PROGBITS 此節區包含全局偏移表。
.hash SHT_HASH SHF_ALLOC 此節區包含了一個符號哈希表。
此節區包含了可執行指令,是進程初始化代
SHF_ALLOC + 碼的一部分。當程序開始執行時,系統要在
.init SHT_PROGBITS
SHF_EXECINSTR 開始調用主程序入口以前(一般指 C 語言
的 main 函數)執行這些代碼。
此節區包含程序解釋器的路徑名。若是程序
包含一個可加載的段,段中包含此節區,那
.interp SHT_PROGBITS
麼節區的屬性將包含 SHF_ALLOC 位,否
則該位爲 0 。
此節區包含符號調試的行號信息,其中描述
.line SHT_PROGBITS (無) 了源程序與機器指令之間的對應關係。其內
容是未定義的。