日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ELF文件格式详解-请查收

發(fā)布時間:2023/12/31 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ELF文件格式详解-请查收 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上一篇文章中主要介紹了ELF文件的基本定義和目標文件的分類,這篇文章中主要介紹下ELF文件格式

ELF文件主要提供了兩個視圖:鏈接視圖及執(zhí)行視圖,分別針對程序運行過程的鏈接過程和執(zhí)行過程。如下圖所示:

單從相貌上來看,二者長得很像,除了肚子。可以看出鏈接視圖采用的節(jié)(section)作為基本單位,而執(zhí)行視圖采用(segment)為基本單位。那么問題來了,這二者有什么區(qū)別?這是個設問句,區(qū)別如下:

Section與segment

在匯編語言中,代碼段、數(shù)據(jù)段就是所謂的段(segment)。在可執(zhí)行文件載入內(nèi)存時,是以segment組織的,每個segment對應ELF文件中的program header中的一個條目,用來建立可執(zhí)行文件映像。

在寫匯編程序時,用.bss .data .txt或者data segment等這些段前指示,都是section,比如.text,告訴匯編器后面的代碼放入.text section中。目標代碼文件中的section和section header table中的條目是一一對應的。Section的信息用于鏈接器對代碼的重定位。

可能還是不太懂,可以看下匯編程序的執(zhí)行過程:

具體過程如下:

寫一個匯編程序或通過編譯器會產(chǎn)生文本文件hello.s。匯編器讀取hello.s時會將其轉(zhuǎn)換為目標文件hello.o,目標文件有若干個section組成,我們在匯編程序中聲明的.section會成為目標文件中的section,此外匯編器還會自動添加一些section(比如符號表)。意思就是不同目標文件中相同的section會被合并成生成文件的一個section。

然后鏈接器把目標文件中的section合并成幾個segment,生成可執(zhí)行文件Hello.o。最后又加載器(loader)根據(jù)可執(zhí)行文件中的segment信息加載運行這個程序。

大概是這樣子的,吧?如果有不太對的地方歡迎指正。

然后針對兩個視圖中的每一部分進行詳細解釋,在此之前,我們熟悉一個命令readelf,其常用的命令格式如下

readelf -h [filename] //查看文件頭 readelf -S [filename] //查看文件節(jié)頭表 readelf -l [filename] //查看程序頭表 readelf -s [filename] //查看符號表 readelf -a [filename] //查看重定位表、符號表等信息 readelf -r [filename] //只查看重定位表信息/*注意這里的filename是elf文件的文件名,常見elf文件后綴有.o、.out等*/

1.ELF文件頭解析

采用readelf -h xxx.o查看頭部信息
我這里創(chuàng)建了一個簡單的say hello的hello.o文件,頭部信息如下
我們看一下文件頭在elf.h文件中的結(jié)構(gòu)體定義,會發(fā)現(xiàn)其有14個成員

typedef struct{unsigned char e_ident[EI_NIDENT]; /*Magic number and other info */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;

類似于程序頭表、節(jié)頭表,elf文件頭同時定義了32位及64位結(jié)構(gòu)體,二者的區(qū)別只有地址位數(shù)的不同,其他成員的數(shù)據(jù)長度及類型都是相同的:

如下所示:

/* Type of addresses. */ typedef uint32_t Elf32_Addr; /* 32位 */ typedef uint64_t Elf64_Addr; /* 64位 *//* Type of file offsets. */ typedef uint32_t Elf32_Off; /* 32位 */ typedef uint64_t Elf64_Off; /* 64位 */ 名稱大小對齊代表
Elf32_Addr44uint32_t
Elf64_Addr88uint64_t
Elf32_Half/Elf64_Half22uint16_t
Elf32_Off44uint32_t
Elf64_Off88uint64_t
Elf32_Sword/Elf64_Sword44int32_t
Elf32_Word/Elf64_Word44uint32_t

其14個成員如下圖所示:

2.ELF節(jié)頭表

采用readelf -S xxx.o查看節(jié)頭表信息

查看源文件(以Elf32_Shdr為例)

/* Section header. */ typedef struct { Elf32_Word sh_name; /* Section name (string tbl index) */ Elf32_Word sh_type; /* Section type */ Elf32_Word sh_flags; /* Section flags */ Elf32_Addr sh_addr; /*Section virtual addr at execution */ Elf32_Off sh_offset; /* Section file offset */ Elf32_Word sh_size; /* Section size in bytes */ Elf32_Word sh_link; /* Link to another section */ Elf32_Word sh_info; /* Additional section information */ Elf32_Word sh_addralign; /* Section alignment */ Elf32_Word sh_entsize; /* Entry size if section holds table */ } Elf32_Shdr;

在節(jié)頭表中,包含10個成員,如圖所示:

成員說明如下:

成員名稱說明
sh_name給出節(jié)的名稱,本身不是字符串,而是數(shù)值,代表目標文件字符串表中的一個索引值。
sh_type指出節(jié)類型
sh_flags標志位
sh_addr如果節(jié)將出現(xiàn)在進程的內(nèi)存映像中,此成員指定該節(jié)數(shù)據(jù)在內(nèi)存中的起始地址,否則,如該節(jié)數(shù)據(jù)不需要映射到內(nèi)存中時,此字段為0
sh_offset該節(jié)數(shù)據(jù)在目標文件中的偏移量。需要注意的是:該節(jié)類型是SHT_NOBITS(如.bss節(jié)),說明該節(jié)在目標文件中并不占用空間大小,此時,該成員的數(shù)值只是概念上的偏移
sh_size節(jié)的大小(字節(jié)數(shù))。當該節(jié)類型不是SHT_NOBITS時,成員數(shù)值就應該是該節(jié)在目標文件的實際大小。
sh_link節(jié)頭部表的索引鏈接
sh_info附加信息,其會因為節(jié)類型的不同而不同。特別的,類型為SHT_REL或SHT_RELA的節(jié),此時成員代表的是一個節(jié)頭表索引值,指代需要做重定位操作的節(jié)
sh_addralign地址對齊的約束條件,數(shù)值0、1沒有對其約束
sh_entsize如果節(jié)的內(nèi)容是固定大小的表,例如符號表,此時成員sh_entsize給出的是每個表項的大小。如果該成員的數(shù)值為0,說明該節(jié)的數(shù)據(jù)內(nèi)容并不是一張表

節(jié)類型(sh_type)說明:

類型說明
SHT_NULL說明節(jié)頭表表項無效,目標中沒有它對應的節(jié)
SHT_PROGBITS表明該節(jié)有程序定義的信息,這些信息的格式與意義完全由程序所定義
SHT_SYMTAB/SHT_DYNSYM說明節(jié)的數(shù)據(jù)內(nèi)容為符號表,SHT_SYMTAB:為靜態(tài)鏈接器提供符號信息,SHT_DYNSYM:為動態(tài)連接器提供符號信息
SHT_STRTAB說明該節(jié)是字符串表
SHT_RELA說明該節(jié)的內(nèi)容是重定位表,表項帶Addend
SHT_HASH說明該節(jié)包含符號哈希表
SHT_DYNAMIC說明該節(jié)內(nèi)容是用來提供給動態(tài)鏈接器使用的
SHT_NOTE包含系統(tǒng)構(gòu)建人員需要使用的特殊信息
SHT_NOBITS在文件中不占空間,其他屬性類似于SHT_PROGBITS
SHT_REL說明該節(jié)內(nèi)容是一個重定位表,表項不帶Addend
SHT_INIT_ARRAY說明該節(jié)存儲的是一個函數(shù)指針數(shù)組
andso on…

節(jié)類型中的SHT_RELA和SHT_REL的區(qū)別其實不太大,知道他們都是重定位表,應該就可以了。
原文中是這么說的,英語好的同學可以琢磨一下>

SHT_RELA
The section holds relocation entries with explicit addends, such as type Elf32_Relafor the 32-bit class of object files. An object file may have multiple relocation sections.
SHT_REL
The section holds relocation entries without explicit addends, such as type Elf32_Rel for the 32-bit class of object files. An object file may have multiple relocation sections. See ‘‘Relocation’’ below for details.

標志位說明如下:

特殊節(jié)如下所示:

如果節(jié)sh_link的值為1,則其可鏈接到本程序的相關(guān)節(jié)

若sh_info的值為1,則其可鏈接到符號表內(nèi)的索引+1

查看符號表:readelf -s hello.o


學習的時候要笑!接下來笑著>_<看程序頭表

3.程序頭表

采用readelf -l xxx.o查看文件的程序頭表

查看定義

/* Program segment header. */typedef struct {Elf32_Word p_type; /* Segment type */ Elf32_Off p_offset; /* Segment file offset */ Elf32_Addr p_vaddr; /* Segment virtual address */Elf32_Addr p_paddr; /* Segment physical address */Elf32_Word p_filesz; /* Segment size in file */Elf32_Word p_memsz; /* Segment size in memory */Elf32_Word p_flags; /* Segment flags */Elf32_Word p_align; /* Segment alignment */ } Elf32_Phdr;

有8個成員

P_type類型描述:

4.符號表

符號:對數(shù)據(jù)、函數(shù)或者代碼(如全局變量或者函數(shù))的引用

符號表:簡單理解就是通過索引對數(shù)據(jù)、函數(shù)、變量的調(diào)用

例如:在使用printf()函數(shù)時,就會調(diào)用動態(tài)符號表.dynsym,因為在符號表.dynsym中存在一個指向該函數(shù)的符號條目。

在大多數(shù)共享庫和可執(zhí)行文件中,存在兩個符號表:.symtab、.dynsym。

.dynsym保存了引用來自外部文件的全局符號,如printf這樣的庫函數(shù)。可以說.dynsym是.symtab的一個子集,因為.symtab中還存有可執(zhí)行文件的本地符號,如全局變量或者代碼定義的本地函數(shù)等,因此.symtab保存了所有的符號,而.dynsym只保存了動態(tài)/全局符號。

但存在一個問題,為什么同時定義包含相同部分的兩個符號表呢?在采用readelf -S 查看可執(zhí)行文件的輸出時,我們發(fā)現(xiàn):.dynsym被標記了ALLOC,即運行時會被內(nèi)存調(diào)用,而.symtab并沒有。可以看出,在程序運行的時候,.symtab并不是必需的。因此不會被裝載到內(nèi)存中。而.dynsym保存的符號只能在運行時被解析,因此是運行時動態(tài)鏈接器所需要的唯一符號。可以說:.dynsym符號表對于動態(tài)鏈接可執(zhí)行文件的執(zhí)行來說是必需的,而.symtab符號表只是用來調(diào)試和鏈接的,有時候為了節(jié)省空間,會將.symtab符號表刪掉

符號表查看,readelf -s hello.o,如下圖所示:

符號類型說明:

符號綁定說明:

5.重定位表

靜態(tài)鏈接的過程分為兩步:第一步是空間與地址的分配,第二步是符號解析與重定位;第二步是鏈接過程的核心。在重定位過程中,重定位表和符號表起著至關(guān)重要的作用,重定位表確定了需要被重定位的地址,符號表決定了其需要被替換成什么值。

可采用readelf -a test.o或readelf -r test.o查看文件重定位表,不同的是,后者只顯示重定位表,如圖所示:

靜態(tài)鏈接中,目標文件中包含有用于重定位的表:代碼段重定位表.rel.text;數(shù)據(jù)段重定位表.rel.data

動態(tài)鏈接中,目標文件的重定位表:.rel.dyn對數(shù)據(jù)引用的修正,修正的位置位于.got和數(shù)據(jù)段;.rel.plt對函數(shù)引用的修正,修正位置位于.got.plt。

如上圖所示,我們可以觀察到幾種重定位入口類型

R_X86_64_RELATIVE, R_X86_64_GLOB_DAT,
R_X86_64_JUMP_SLO

R_X86_64_GLOB_DAT(.rel.dyn中針對.got)和R_X86_64_JUMP_SLO(.rel.plt中針對.got.plt)表示被修正的位置只需要直接將符號的地址填入。

而在重定位表中的Offset表明了當前符號在.got或.got.plt中的偏移,可以根據(jù)該值在GOT表中尋找對應的位置。

總結(jié)

以上是生活随笔為你收集整理的ELF文件格式详解-请查收的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。