日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

計(jì)算機(jī)原理系列之二 ——

 xiaofenglib 2020-09-16

下面我們介紹一種文件格式:ELF格式,全名為可執(zhí)行和可鏈接格式(Executable and Linkable Format)。維基百科中這樣描述:

在計(jì)算機(jī)科學(xué)中,ELF文件是一種用于可執(zhí)行文件、目標(biāo)文件、共享庫和核心轉(zhuǎn)儲(chǔ)(core dump)的標(biāo)準(zhǔn)文件格式。其中核心轉(zhuǎn)儲(chǔ)是指: 操作系統(tǒng)在進(jìn)程收到某些信號(hào)而終止時(shí),將此時(shí)進(jìn)程地址空間的內(nèi)容以及有關(guān)進(jìn)程狀態(tài)的其他信息寫出的一個(gè)磁盤文件。這種信息往往用于調(diào)試。

一、ELF文件類型

通俗點(diǎn)說由匯編器和鏈接器生成的文件都屬于ELF文件。通常我們接觸的ELF文件主要有以下三類:

  • 可重定位文件relocatable file) 它保存了一些可以和其他目標(biāo)文件鏈接并生成可執(zhí)行文件或者共享庫的二進(jìn)制代碼和數(shù)據(jù)。

  • 可執(zhí)行文件excutable file)它保存了適合直接加載到內(nèi)存中執(zhí)行的二進(jìn)制程序。

  • 共享庫文件shared object file)一種特殊的可重定位目標(biāo)文件,可以在加載或者運(yùn)行時(shí)被動(dòng)態(tài)的加載進(jìn)內(nèi)存并鏈接。

總之,ELF文件是一種文件格式。但凡是一種格式,總要有一些規(guī)則,下面我們來介紹ELF文件的格式規(guī)則。

二、ELF文件結(jié)構(gòu)

一個(gè)典型的ELF文件包括ELF Header、SectionsSection Header TableProgram Header Table。其位置分布如下圖所示:
01-elf
圖1 ELF文件構(gòu)成

1. ELF Header

每個(gè)ELF文件都存在一個(gè)ELF Header用來描述其結(jié)構(gòu)和組成。ELF Header其實(shí)對(duì)應(yīng)的是一個(gè)結(jié)構(gòu)體,該結(jié)構(gòu)體定義如下:

#define EI_NIDENT 16 typedef struct { unsigned char e_ident[EI_NIDENT]; uint16_t e_type; uint16_t e_machine; uint32_t e_version; ElfN_Addr e_entry; ElfN_Off e_phoff; ElfN_Off e_shoff; uint32_t e_flags; uint16_t e_ehsize; uint16_t e_phentsize; uint16_t e_phnum; uint16_t e_shentsize; uint16_t e_shnum; uint16_t e_shstrndx; } ElfN_Ehdr;

其中

ElfN_Addr       Unsigned program address, uintN_tElfN_Off        Unsigned file offset, uintN_t

上述結(jié)構(gòu)體的各成員意義如下:

  • e_ident:包含一個(gè)magic number、ABI信息,該文件使用的平臺(tái)、大小端規(guī)則

  • e_type:文件類型, 表示該文件屬于可執(zhí)行文件、可重定位文件、core dump文件或者共享庫

  • e_machine:機(jī)器類型

  • e_version:通常都是1

  • e_entry: 表示程序執(zhí)行的入口地址

  • e_phoff: 表示Program Header的入口偏移量(以字節(jié)為單位)

  • e_shoff: 表示Section Header的入口偏移量(以字節(jié)為單位)

  • e_flags: 保存了這個(gè)ELF文件相關(guān)的特定處理器的flag

  • e_ehsize: 表示ELF Header大?。ㄒ宰止?jié)為單位)

  • e_phentsize: 表示Program Header大?。ㄒ宰止?jié)為單位)

  • e_phnum: 表示Program Header的數(shù)量 (十進(jìn)制數(shù)字)

  • e_shentsize: 表示Section Header大?。ㄒ宰止?jié)為單位)

  • e_shnum: 表示Section Header的數(shù)量 (十進(jìn)制數(shù)字)

  • e_shstrndx: 表示字符串表的索引,字符串表用來保存ELF文件中的字符串,比如段名、變量名。 然后通過字符串在表中的偏移訪問字符串。

例如,machine的值為0x3e, 十進(jìn)制為62,其代表的意義可以從文件elf-em.h中找到,如下:

#define EM_X86_64 62 /* AMD x86-64 */

表示,這個(gè)ELF文件可以運(yùn)行在x86_64的機(jī)器上。

2. Section

在ELF文件中,數(shù)據(jù)和代碼分開存放的,這樣可以按照其功能屬性分成一些區(qū)域,比如程序、數(shù)據(jù)、符號(hào)表等。這些分離存放的區(qū)域在ELF文件中反映成section。ELF文件中典型的section如下:

  • .text: 已編譯程序的二進(jìn)制代碼

  • .rodata: 只讀數(shù)據(jù)段,比如常量

  • .data: 已初始化的全局變量和靜態(tài)變量

  • .bss: 未初始化的全局變量和靜態(tài)變量,所有被初始化成0的全局變量和靜態(tài)變量

  • .sysmtab: 符號(hào)表,它存放了程序中定義和引用的函數(shù)和全局變量的信息

  • .debug: 調(diào)試符號(hào)表,它需要以'-g'選項(xiàng)編譯才能得到,里面保存了程序中定義的局部變量和類型定義,程序中定義和引用的全局變量,以及原始的C文件

  • .line: 原始的C文件行號(hào)和.text節(jié)中機(jī)器指令之間的映射

  • .strtab: 字符串表,內(nèi)容包括.symtab.debug節(jié)中的符號(hào)表

特殊的,
1)對(duì)于可重定位的文件,由于在編譯時(shí),并不能確定它引用的外部函數(shù)和變量的地址信息,因此,編譯器在生成目標(biāo)文件時(shí),增加了兩個(gè)section:

  • .rel.text 保存了程序中引用的外部函數(shù)的重定位信息,這些信息用于在鏈接時(shí)重定位其對(duì)應(yīng)的符號(hào)。

  • .rel.data 保存了被模塊引用或定義的所有全局變量的重定位信息,這些信息用于在鏈接時(shí)重定位其對(duì)應(yīng)的全局變量。

2)對(duì)于可執(zhí)行文件,由于它已經(jīng)全部完成了重定位工作,可以直接加載到內(nèi)存中執(zhí)行,所以它不存在.rel.text.rel.data這兩個(gè)section。但是,它增加了一個(gè)section:

  • .init: 這個(gè)section里面保存了程序運(yùn)行前的初始化代碼

上述描述的各個(gè)文件中包含的這些section是必須存在的,當(dāng)然除了這些section,每種文件還有一些其他的section用來存放編譯器或者鏈接器所需要的輔助信息,詳情請(qǐng)參考參考閱讀1,在這里就不過多的討論了。

3. Section Header Table

上述各個(gè)section的大小和位置等具體信息的存放是由Section Header Table來描述的。Section Header Table是一個(gè)結(jié)構(gòu)體數(shù)組,對(duì)應(yīng)的結(jié)構(gòu)體定義如下:

typedef struct {    uint32_t   sh_name;    uint32_t   sh_type;    uint64_t   sh_flags;    Elf64_Addr sh_addr;    Elf64_Off  sh_offset;    uint64_t   sh_size;    uint32_t   sh_link;    uint32_t   sh_info;    uint64_t   sh_addralign;    uint64_t   sh_entsize;} Elf64_Shdr;

其中各成員的意義如下:

  • sh_name:表示該section的名字相對(duì)于.shstrtab section的地址偏移量。

  • sh_type:表示該section中存放的內(nèi)容類型,比如符號(hào)表,可重定位段等。

  • sh_flags: 表示該section的一些屬性,比如是否可寫,可執(zhí)行等。

  • sh_addr: 表示該section在程序運(yùn)行時(shí)的內(nèi)存地址

  • sh_offset: 表示該section相對(duì)于ELF文件起始地址的偏移量

  • sh_size: 表示該section的大小

  • sh_link: 配合sh_info保存section的額外信息

  • sh_info:保存該section相關(guān)的一些額外信息

  • sh_addralign:表示該section需要的地址對(duì)齊信息

  • sh_entsize:有些section里保存的是一些固定長度的條目,比如符號(hào)表。對(duì)于這些section來講,sh_entsize里保存的就是條目的長度。

4. Program Header Table

前面講過了,section基本是按照目標(biāo)文件內(nèi)容的功能來劃分的一些區(qū)域,而根據(jù)其內(nèi)容在內(nèi)存中是否可讀寫等屬性,又可以將不同的section劃分成不同的segment。其中每個(gè)segment可以由一個(gè)或多個(gè)section組成。其關(guān)系如圖1所示。

在可執(zhí)行文件中,ELF header下面緊接著就是Program Header Table。它描述了各個(gè)segment在ELF文件中的位置以及在程序執(zhí)行過程中系統(tǒng)需要準(zhǔn)備的其他信息。它也是用一個(gè)結(jié)構(gòu)體數(shù)組來表示的。具體代碼如下:

typedef uint64_t Elf64_Addr;typedef uint64_t Elf64_Off;typedef uint32_t Elf64_Word;typedef uint64_t Elf64_Xword;typedef struct { Elf64_Word p_type; // 4 Elf64_Word p_flags; // 4 Elf64_Off p_offset; // 8 Elf64_Addr p_vaddr; // 8 Elf64_Addr p_paddr; // 8 Elf64_Xword p_filesz; // 8 Elf64_Xword p_memsz; // 8 Elf64_Xword p_align; // 8} Elf64_Phdr;

各個(gè)字段的具體含義如下:

  • p_type:描述了當(dāng)前segment是何種類型的或者如何解釋當(dāng)前segment,比如是動(dòng)態(tài)鏈接相關(guān)的或者可加載類型的等

  • p_flags:保存了該segment的flag

  • p_offset:表示從ELF文件到該segment第一個(gè)字節(jié)的偏移量

  • p_vaddr:表示該segment的第一個(gè)字節(jié)在內(nèi)存中的虛擬地址

  • p_paddr:對(duì)于使用物理地址的系統(tǒng)來講,這個(gè)成員表示該segment的物理地址

  • p_filesz:表示該segment的大小,以字節(jié)表示

  • p_memsz:表示該segment在內(nèi)存中的大小,以字節(jié)表示

  • p_align:表示該segment在文件中或者內(nèi)存中需要以多少字節(jié)對(duì)齊

三、實(shí)踐

為了進(jìn)一步加深對(duì)ELF文件整體結(jié)構(gòu)的理解,我們?nèi)∫粋€(gè)64bit的linux上的可執(zhí)行文件hello來驗(yàn)證。
首先,使用READELF工具查看其ELF Header信息:

$ readelf -h helloELF 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:                              EXEC (Executable file)  Machine:                           Advanced Micro Devices X86-64  Version:                           0x1  Entry point address:               0x400430  Start of program headers:          64 (bytes into file)  Start of section headers:          6616 (bytes into file)  Flags:                             0x0  Size of this header:               64 (bytes)  Size of program headers:           56 (bytes)  Number of program headers:         9  Size of section headers:           64 (bytes)  Number of section headers:         31  Section header string table index: 28

從ELF Header中我們得到如下信息:

  • ELF Header大?。?code>Size of this header)為64 byte

  • Program Header Table的起始地址(Start of program headers)是64 byte,其內(nèi)部共有9個(gè)(Number of program headers)Program Header,每個(gè)大小為56 byte(Size of program headers

  • Section Header Table的起始地址(Start of section headers)是6616 byte,其內(nèi)部共有31個(gè)(Number of section headers)Section Header,每個(gè)大小為64 byte(Size of this header

其次,使用READELF命令讀取section header table信息:

$ readelf -S helloThere are 31 section headers, starting at offset 0x19d8:Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .interp PROGBITS 0000000000400238 00000238 000000000000001c 0000000000000000 A 0 0 1 [ 2] .note.ABI-tag NOTE 0000000000400254 00000254 0000000000000020 0000000000000000 A 0 0 4 [ 3] .note.gnu.build-i NOTE 0000000000400274 00000274 0000000000000024 0000000000000000 A 0 0 4 ... snip ... [27] .comment PROGBITS 0000000000000000 00001038 0000000000000035 0000000000000001 MS 0 0 1 [28] .shstrtab STRTAB 0000000000000000 000018cc 000000000000010c 0000000000000000 0 0 1 [29] .symtab SYMTAB 0000000000000000 00001070 0000000000000648 0000000000000018 30 47 8 [30] .strtab STRTAB 0000000000000000 000016b8 0000000000000214 0000000000000000 0 0 1Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), l (large) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)

根據(jù)上述section信息,我們得到:

  • section的起始地址是0x238(第一個(gè)section .interp的起始地址)

  • section的結(jié)束地址是.shstrtab的結(jié)束地址即:0x18cc + 0x10c = 0x19d8

根據(jù)我們上面的結(jié)論,我們可以得到該ELF文件的組成圖如下:

namestart_addrsizeend_addr
ELF Header00x40
Program Header Table0x400x1F8
Section0x238
0x19D8
Section Header Table0x19D80x7C0

我們可以計(jì)算出各部分的end_addr上述空白部分,得到:

cs02-table

顯然,各個(gè)section正好和下一個(gè)section首尾相接。然后,通過ls -l hello命令查看該二進(jìn)制文件的大小,得到該文件大小為8600 byte,而Section Header Table的end_addr 0x2198換算成十進(jìn)制大小正好是8600 byte。從而證明我們上述的分析是沒問題的。

四、總結(jié)

  1. ELF文件包括可重定位文件、可執(zhí)行文件、共享庫和Core dump文件。其基本結(jié)構(gòu)包括ELF Header,Program Header Table,Sections和Section Header Table。其中不同的sections組成了不同的segment。

  2. ELF Header描述了該ELF文件中Program Header Table的位置、大小,Section Header Table的位置、大小和程序執(zhí)行的入口地址等信息。

  3. Program Header Table描述了各個(gè)segment的類型、虛擬地址、相對(duì)文件的偏移地址等信息。

  4. Section Header Table描述了各個(gè)section的名字、類型、相對(duì)文件的偏移地址等信息。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多