ELF文件
ELF文件
文章目錄
- ELF文件
- 前言
- 一、.out文件的生成
- 二、ELF格式
- 1.ELF header
- 2.section header
- 3.sections
- 4.Program header table
- 三、實驗驗證
- 四、其他
- 1、符號表和重定位表
- 2、鏈接
- 總結
前言
最近在看書,看到程序的編譯,鏈接時候感覺得整理下,做下筆記,有些知識點并不是該書的重點,因此他也是提及到而已,但是處于好奇,還是想多知道點,因此也查閱了網上相關博主的一些文章,將我所需要的做一個整理,這些知識工作中,很少用到,因此也是會經常忘。
閱讀文獻:
了解過程中,主要是閱讀了《嵌入式c語言自我修養》
網址1:www.luomuxiaoxiao.com
網址2:https://blog.csdn.net/npy_lp/article/details/102604380
一、.out文件的生成
?.out文件是可執行文件,它是目標文件(object file)的一種。目標文件可以分為三種:
?1. 可重定位的目標文件
?2.可執行的目標文件
?3.可被共享的目標文件。
?這幾個我們在嵌入式開發的時候都用到。但是接觸最多的是可執行文件。一般的程序編譯的流程:預處理,編譯,匯編,鏈接。就可以生成一個可執行文件。
二、ELF格式
?不管是.out文件還是其他的目標文件,他們的文件格式都是ELF格式。
在百科里邊的定義是這樣的:
? ELF在計算機科學中,是一種用于二進制文件、可執行文件、目標代碼、共享庫和核心轉儲的標準文件格式。添加鏈接描述
所以,本次這篇筆記主要是結合可執行文件來了解ELF文件格式。
?ELF文件由4部分組成,分別是ELF頭(ELF header)、程序頭表(Program header table)、節(Section)和節頭表(Section header table)。如下圖1和圖2都是我從百度圖片上下載的。
1.ELF header
?下面是ELF 頭的結構體,從header 可以大致分析出這四個組成部分的布局。可使用命令"readelf -h filename"來察看文件頭的內容 可參考的博文
#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 | 最開頭是16個字節的e_ident, 其中包含用以表示ELF文件的字符,以及其他一些與機器無關的信息。開頭的4個字節值固定不變,為0x7f和ELF三個字符。 |
| e_type | 它標識的是該文件的類型,可重定位文件為1,可執行文件為2,共享文件為3 |
| e_machine | 表明運行該程序需要的體系結構 |
| e_version | 表示文件的版本 |
| e_entry | 程序的入口地址 |
| e_phoff | 表示Program header table 在文件中的偏移量 |
| e_shoff | 表示Section header table 在文件中的偏移量 |
| e_flags | 保存了這個ELF文件相關的特定處理器的flag |
| e_ehsize | 表示ELF header大小,32位ELF是52字節,64位是64字節。 |
| e_phentsize | 表示Program header table中每一個條目的大小。 |
| e_phnum | 表示Program header table中有多少個條目。 |
| e_shentsize | 表示Section header table中的每一個條目的大小。 |
| e_shnum | 表示Section header table中有多少個條目。 |
| e_shstrndx | 包含節名稱的字符串是第幾個節 |
2.section header
?section header用來描述每個section的特性,如大小、類型、名稱,section的起始和偏移等等,通過readelf -S filename 可以查看section header 內容。一個可執行文件通常由不同的section組成,這里section不能理解成段這個含義,具體解釋看第四小節。
其結構體如下:
下面我們依次講解結構體各個字段,有些字段目前也不理解,后續慢慢了解吧。
| sh_name | 是一個索引值 |
| sh_type | 描述了section的類型 |
| sh_flags | 包含位標志 |
| sh_addr | 如果section會出現在進程的內存映像中,給出了section第一字節的虛擬地址。 |
| sh_offset | section相對于文件頭的字節偏移。對于不占文件空間的section(比如SHT_NOBITS),它的sh_offset只是給出了section邏輯上的位置。 |
| sh_size | section占多少字節 |
| sh_link | 含有一個section header的index,該值的解釋依賴于section type |
| sh_info | 存放額外的信息,值的解釋依賴于section type。 |
| sh_addralign | 地址對齊,如果一個section有一個doubleword字段,系統在載入section時的內存地址必須是doubleword對齊。也就是說sh_addr必須是sh_addralign的整數倍。只有2的正整數冪是有效的。0和1說明沒有對齊約束。 |
| sh_entsize | 有些section包含固定大小的記錄,比如符號表。這個值給出了每個記錄大小。對于不包含固定大小記錄的section,這個值是0 |
3.sections
?在ELF文件中,數據和代碼分開存放的,這樣可以按照其功能屬性分成一些區域,比如程序、數據、符號表等。
系統預定義了一些節名(以.開頭),這些節有其特定的類型和含義。1. .bss:包含程序運行時未初始化的數據(全局變量和靜態變量)。當程序運行時,這些數據初始化為0。 其類型為SHT_NOBITS,表示不占文件空間。SHF_ALLOC + SHF_WRITE,運行時要占用內存的。 2. .comment 包含版本控制信息(是否包含程序的注釋信息?不包含,注釋在預處理時已經被刪除了)。類型為SHT_PROGBITS。 3. .data和.data1,包含初始化的全局變量和靜態變量。 類型為SHT_PROGBITS,標志為SHF_ALLOC + SHF_WRITE(占用內存,可寫)。 4. .debug,包含了符號調試用的信息,我們要想用gdb等工具調試程序,需要該類型信息,類型為SHT_PROGBITS。 5. .dynamic,類型SHT_DYNAMIC,包含了動態鏈接的信息。標志SHF_ALLOC,是否包含SHF_WRITE和處理器有關。 6. .dynstr,SHT_STRTAB,包含了動態鏈接用的字符串,通常是和符號表中的符號關聯的字符串。標志 SHF_ALLOC 7. .dynsym,類型SHT_DYNSYM,包含動態鏈接符號表, 標志SHF_ALLOC。 8. .fini,類型SHT_PROGBITS,程序正常結束時,要執行該section中的指令。標志SHF_ALLOC + SHF_EXECINSTR(占用內存可執行)。現在ELF還包含.fini_array section。 9. .text: 已編譯程序的二進制代碼 10. .hash,類型SHT_HASH,包含符號hash表,以后細講。標志SHF_ALLOC。 11. .init,SHT_PROGBITS,程序運行時,先執行該節中的代碼。SHF_ALLOC + SHF_EXECINSTR,和.fini對應。現在ELF還包含.init_array section。 12. .interp,SHT_PROGBITS,該節內容是一個字符串,指定了程序解釋器的路徑名。如果文件中有一個可加載的segment包含該節,屬性就包含SHF_ALLOC,否則不包含。 13. .line,SHT_PROGBITS,包含符號調試的行號信息,描述了源程序和機器代碼的對應關系。gdb等調試器需要此信息。 14. .note Note Section, 類型SHT_NOTE,以后單獨講。 15. .plt 過程鏈接表(Procedure Linkage Table),類型SHT_PROGBITS,以后重點講。 16. .relNAME,類型SHT_REL, 包含重定位信息。如果文件有一個可加載的segment包含該section,section屬性將包含SHF_ALLOC,否則不包含。NAME,是應用重定位的節的名字,比如.text的重定位信息存儲在.rel.text中。 17. .relaname 類型SHT_RELA,和.rel相同。SHT_RELA和SHT_REL的區別,會在講重定位的時候說明。 18. .rodata和.rodata1。類型SHT_PROGBITS, 包含只讀數據,組成不可寫的段。標志SHF_ALLOC。 .shstrtab,類型SHT_STRTAB,包含section的名字。有讀者可能會問:section header中不是已經包含名字了嗎,為什么把名字集中存放在這里? sh_name 包含的是.shstrtab 中的索引,真正的字符串存儲在.shstrtab中。那么section names為什么要集中存儲?我想是這樣:如果有相同的字符串,就可以共用一塊存儲空間。如果字符串存在包含關系,也可以共用一塊存儲空間。 19. .strtab SHT_STRTAB,包含字符串,通常是符號表中符號對應的變量名字。如果文件有一個可加載的segment包含該section,屬性將包含SHF_ALLOC。字符串以\0結束, section以\0開始,也以\0結束。一個.strtab可以是空的,它的sh_size將是0。針對空字符串表的非0索引是允許的。 20. symtab,類型SHT_SYMTAB,Symbol Table,符號表。包含了定位、重定位符號定義和引用時需要的信息。符號表是一個數組,Index 0 第一個入口,它的含義是undefined symbol index, STN_UNDEF。如果文件有一個可加載的segment包含該section,屬性將包含SHF_ALLOC。4.Program header table
參考文獻
?segment是section的集合,一個segment可以映射到section。
?左邊是ELF的鏈接視圖,可以理解為是目標代碼文件的內容布局。右邊是ELF的執行視圖,可以理解為可執行文件的內容布局。
?注意目標代碼文件的內容是由section組成的,而可執行文件的內容是由segment組成的。
?要注意區分段(segment)和節(section)的概念,這兩個概念在后面會經常提到。
?我們寫匯編程序時,用.text,.bss,.data這些指示,都指的是section,比如.text,告訴匯編器后面的代碼放入.text section中。
?目標代碼文件中的section和section header table中的條目是一一對應的。section的信息用于鏈接器對代碼重定位。
?而文件載入內存執行時,是以segment組織的,每個segment對應ELF文件中program header table中的一個條目,用來建立可執行文件的進程映像。
?比如我們通常說的,代碼段、數據段是segment,目標代碼中的section會被鏈接器組織到可執行文件的各個segment中。
?.text section的內容會組裝到代碼段中,.data, .bss等節的內容會包含在數據段中。
?可以這么理解。在匯編的時候,也就是.o文件有關于section的描述,但是沒有segment描述。鏈接器把多個可重定位的目標文件中相同的 section 整合成一個segment,成為一個可執行文件。在程序運行的時候,方便加載器的加載。
三、實驗驗證
? 1.以如下一個程序sub.c為例:
#include <stdio.h>int add(int a,int b) {return a+b; } int sub(int a,int b) {return a-b;} int goal =10 ; int gobl; int main() {int val,vbl;static int sUao = 20;val =add(4,5);vbl =add(4,3);printf("test :\n");printf("a= [%d],b=[%d]",val,vbl);return 0; } ~?1.1.將源程序進行預處理、編譯、匯編操作,生成.o文件(可重定位的目標文件)。
gcc -c sub.c?1.2.然后查看ELF header
?可以看出,ELF header 的大小是64字節,每一個section大小64字節,一共有14個,與program 相關的是0。
? 1.3.接著查看section header.還是符合ELF header 描述的。
readelf -S sub.o //S是大寫的? 1.4.查看顯示程序頭(段頭)信息,發現是沒有的,符合ELF header 描述的。
? 2. 將程序編譯成可執行文件,可以看見,有了變化,這里把一些文件鏈接了,如glibc庫。
?依次執行,查看section header,比之前多了好多。
?再看查看顯示程序頭(段頭)信息,后面就會把各個文件的同屬性節整合到一塊。
readelf 命令介紹鏈接
四、其他
1、符號表和重定位表
? 首先,在程序的匯編中,是通過匯編器將前面所生成的匯編文件搞成目標文件。在匯編這一過程中,就生成了符號表和重定位表,這兩個表為后面的鏈接提供了必要的信息。
? 符號表
主要用來保存源程序中各個符號的信息,如符號的地址,類型,占用空間的大小 。
可以通過readelf -s xxx.o 查看。
? 重定位表
如果在當前文件中沒有找到符號的定義,會將這些符號搜集到一塊并保存到一個單獨的符號表中,后面在補充,這個符號表就叫重定位符號表。
可以用 readelf -r xxx.o 查看。.rel.text
2、鏈接
?在鏈接階段,有個分段組裝,意思是將各個目標文件的代碼段放在一塊,作為最終的代碼段,將各個目標文件的數據段放在一塊,作為最終的可執行文件的數據段等等。
與重定向文件一樣,可執行文件也會符號表,他是全局的,收集這各個目標文件的符號表中的符號.
重定位新地址 = 新的段地址 + 段內偏移
總結
文本主要是淺淺了解了目前用的程序的文件格式。以及編譯的大概流程,對理解程序的運行還是由幫助的,如果還想仔細去了解,估計的花時間了。
總結
- 上一篇: python怎么换背景_python –
- 下一篇: Android毕业设计答辩会问什么问题,