c语言书籍elf文件,扒一扒ELF文件
ELF文件(Executable Linkable Format)是一種文件存儲(chǔ)格式。Linux下的目標(biāo)文件和可執(zhí)行文件都按照該格式進(jìn)行存儲(chǔ),有必要做個(gè)總結(jié)。
1. 鏈接舉例
2. ELF文件類型2.1 可重定位目標(biāo)文件(.o文件)
2.2 可執(zhí)行目標(biāo)文件(a.out文件)
2.3 共享對(duì)象文件(.so文件)
3. ELF文件作用
4. ELF文件格式4.1 從編譯和鏈接角度看ELF文件(可重定位目標(biāo)文件)
4.2 從程序執(zhí)行角度看ELF文件(可執(zhí)行文件)
5.總結(jié)
1. 鏈接舉例
在介紹ELF文件之前,我們先看下,一個(gè).c程序是如何變成可執(zhí)行目標(biāo)文件的。下面舉個(gè)例子。
該程序由main.c和sum.c兩個(gè)模塊組成。sum.c接收數(shù)組和數(shù)組長(zhǎng)度兩個(gè)參數(shù),最后將數(shù)組求和的結(jié)果返回。main.c調(diào)用sum函數(shù),并傳遞一個(gè)兩元素的int數(shù)組array,將計(jì)算結(jié)果保存在val中。//main.c
int?sum(int?*a,?int?n);
int?array[2]?=?{1,?2};
int?main(int?argc,?char**?argv){
int?val?=?sum(array,?2);
return?val;
}//sum.c
int?sum(int?*a,?int?n){
int?i,?s?=?0;
for?(i?=?0;?i?
s?+=?a[i];
}
return?s;
}
讓我們來(lái)看看如果我們使用GCC編譯兩個(gè)模塊會(huì)發(fā)生什么?
main.c和sum.c將分別通過(guò)翻譯器將源文件處理為可重定位的目標(biāo)文件main.o和sum.o。翻譯器處理的過(guò)程包括了預(yù)處理(ccp)、編譯(ccl)、匯編(as)?三個(gè)過(guò)程。最后,鏈接器(ld)?將可重定位的目標(biāo)文件main.o和sum.o以及一些必要的系統(tǒng)文件組合起來(lái),創(chuàng)建一個(gè)可執(zhí)行目標(biāo)文件prog。具體過(guò)程如下圖所示。
鏈接過(guò)程
由上面的過(guò)程,我們可以看出在經(jīng)過(guò)匯編器后會(huì)輸出一個(gè).o文件,這個(gè)叫做可重定位的目標(biāo)文件。將main.o和sum.o輸入鏈接器后,鏈接器輸出的prog文件叫做可執(zhí)行目標(biāo)文件。那這兩個(gè)目標(biāo)文件有什么樣的區(qū)別呢?
2. ELF文件類型
2.1 可重定位目標(biāo)文件(.o文件)
包含二進(jìn)制代碼和數(shù)據(jù),其形式可以和其他目標(biāo)文件進(jìn)行合并,創(chuàng)建一個(gè)可執(zhí)行目標(biāo)文件。例如lib*.o文件。
2.2 可執(zhí)行目標(biāo)文件(a.out文件)
包含二進(jìn)制代碼和數(shù)據(jù),可直接被加載器加載執(zhí)行。例如編譯好的可執(zhí)行文件a.out。
2.3 共享對(duì)象文件(.so文件)
用于和其他共享目標(biāo)文件或者可重定位文件一起生成ELF目標(biāo)文件或者和執(zhí)行文件一起創(chuàng)建進(jìn)程映像,例如lib*.so文件。
3. ELF文件作用
ELF文件參與程序的連接(建立一個(gè)程序)和程序的執(zhí)行(運(yùn)行一個(gè)程序),所以可以從不同的角度來(lái)看待ELF格式的文件:
1.如果用于編譯和鏈接(可重定位文件),則編譯器和鏈接器將把ELF文件看作是節(jié)頭表描述的節(jié)的集合,程序頭表可選。
2.如果用于加載執(zhí)行(可執(zhí)行文件),則加載器則將把ELF文件看作是程序頭表描述的段的集合,一個(gè)段可能包含多個(gè)節(jié),節(jié)頭表可選。
4. ELF文件格式
4.1 從編譯和鏈接角度看ELF文件(可重定位目標(biāo)文件)
從編譯和鏈接角度看ELF文件
ELF頭
每個(gè)ELF文件都必須存在一個(gè)ELF_He ader,這里存放了很多重要的信息用來(lái)描述整個(gè)文件的組織,如: 版本信息,入口信息,偏移信息等。程序執(zhí)行也必須依靠其提供的信息。
段頭表
段頭表。存放的是所有不同段將在內(nèi)存中的位置。
.text section
代碼段。存放已編譯程序的機(jī)器代碼,一般是只讀的。
.rodata?section
只讀數(shù)據(jù)段。此段的數(shù)據(jù)不可修改,存放常量。比如,printf中的格式化語(yǔ)句。
.data?section
數(shù)據(jù)段。存放已初始化的全局變量、常量。
.bss?section
bss段。未初始化全局變量,僅是占位符,不占據(jù)任何實(shí)際磁盤空間。目標(biāo)文件格式區(qū)分初始化和非初始化是為了空間效率。
從編譯和鏈接角度看ELF文件
.symtab?section
符號(hào)表,它存放在程序中定義和引用的函數(shù)和全局變量的信息。
.rel.txt?section
.text節(jié)的重定位信息,用于重新修改代碼段的指令中的地址信息。
.rel.data?section
.data節(jié)的重定位信息,用于對(duì)被模塊使用或定義的全局變量進(jìn)行重定位的信息。
.debug?section
調(diào)試用的符號(hào)表。
.strtab section
包含 symtab和 debug節(jié)中符號(hào)及節(jié)名。
節(jié)頭部表
每個(gè)節(jié)的節(jié)名、偏移和大小。
以下是32位系統(tǒng)對(duì)應(yīng)的節(jié)頭表數(shù)據(jù)結(jié)構(gòu),說(shuō)明了每個(gè)節(jié)的節(jié)名、在文件中的偏移、大小、訪問(wèn)屬性、對(duì)齊方式等。typedef?struct?{
Elf32_Word?sh_name;???//節(jié)名字符串在.strtab節(jié)(字符串表)中的偏移
Elf32_Word?sh_type;???//節(jié)類型:無(wú)效/代碼或數(shù)據(jù)/符號(hào)/字符串/...
Elf32_Word?sh_flags;??//節(jié)標(biāo)志:該節(jié)在虛擬空間中的訪問(wèn)屬性
Elf32_Addr?sh_addr;???//虛擬地址:若可被加載,則對(duì)應(yīng)虛擬地址
Elf32_Off??sh_offset;?//在文件中的偏移地址,對(duì).bss節(jié)而言則無(wú)意義
Elf32_Word?sh_size;???//節(jié)在文件中所占的長(zhǎng)度
Elf32_Word?sh_link;???//sh_link和sh_info用于與鏈接相關(guān)的節(jié)(如?.rel.text節(jié)、.rel.data節(jié)、.symtab節(jié)等)
Elf32_Word?sh_info;
Elf32_Word?sh_addralign;?//節(jié)的對(duì)齊要求
Elf32_Word?sh_entsize;???//節(jié)中每個(gè)表項(xiàng)的長(zhǎng)度,0表示無(wú)固定長(zhǎng)度表項(xiàng)
}?Elf32_Shdr;
使用readelf命令命令查看節(jié)頭表內(nèi)容[ubuntu@localhost?interpositioning]$?readelf?-S?main.o
There?are?13?section?headers,?starting?at?offset?0x3f8:
Section?Headers:
[Nr]?Name??????????????Type?????????????Address???????????Offset
Size??????????????EntSize??????????Flags??Link??Info??Align
[?0]???????????????????NULL?????????????0000000000000000??00000000
0000000000000000??0000000000000000???????????0?????0?????0
[?1]?.text?????????????PROGBITS?????????0000000000000000??00000040
0000000000000071??0000000000000000??AX???????0?????0?????1
[?2]?.rela.text????????RELA?????????????0000000000000000??000002d0
0000000000000090??0000000000000018???I??????11?????1?????8
[?3]?.data?????????????PROGBITS?????????0000000000000000??000000b1
0000000000000049??0000000000000000??WA???????0?????0?????1
[?4]?.bss??????????????NOBITS???????????0000000000000000??000000b1
000000000000000c??0000000000000000??WA???????0?????0?????1
[?5]?.rodata???????????PROGBITS?????????0000000000000000??000000b1
0000000000000019??0000000000000000???A???????0?????0?????1
[?6]?.comment??????????PROGBITS?????????0000000000000000??000000ca
0000000000000035??0000000000000001??MS???????0?????0?????1
[?7]?.note.GNU-stack???PROGBITS?????????0000000000000000??000000ff
0000000000000000??0000000000000000???????????0?????0?????1
[?8]?.eh_frame?????????PROGBITS?????????0000000000000000??00000100
0000000000000058??0000000000000000???A???????0?????0?????8
[?9]?.rela.eh_frame????RELA?????????????0000000000000000??00000360
0000000000000030??0000000000000018???I??????11?????8?????8
[10]?.shstrtab?????????STRTAB???????????0000000000000000??00000390
0000000000000061??0000000000000000???????????0?????0?????1
[11]?.symtab???????????SYMTAB???????????0000000000000000??00000158
0000000000000150??0000000000000018??????????12?????9?????8
[12]?.strtab???????????STRTAB???????????0000000000000000??000002a8
0000000000000023??0000000000000000???????????0?????0?????1
Key?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)
可重定位目標(biāo)文件中,每個(gè)可裝入節(jié)的起始地址總是0。
.bss節(jié)應(yīng)占0x0c大小,但只有裝入內(nèi)存時(shí)才會(huì)分配。
4.2 從程序執(zhí)行角度看ELF文件(可執(zhí)行文件)
從程序執(zhí)行角度看ELF文件
與可重定位目標(biāo)文件不同:
1.ELF頭中,字段 e_entry給出執(zhí)行程序時(shí)第一條指令的地址,而在可重定位文件中,此字段為0。
2.多一個(gè)init節(jié),用于定義init函數(shù),該函數(shù)用來(lái)進(jìn)行可執(zhí)行目標(biāo)文件開(kāi)始執(zhí)行時(shí)的初始化工作。
3.少兩個(gè).rel節(jié)(無(wú)需重定位)。
4.多一個(gè)程序頭表,也稱段頭表,是一個(gè)結(jié)構(gòu)數(shù)組。
使用readelf命令查看ELF頭的內(nèi)容:[ubuntu@localhost?interpositioning]$readelf?-h?main.o
ELF?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:??????????????????????????????REL?(Relocatable?file)
Machine:???????????????????????????Advanced?Micro?Devices?X86-64
Version:???????????????????????????0x1
Entry?point?address:???????????????0x0
Start?of?program?headers:??????????0?(bytes?into?file)
Start?of?section?headers:??????????1064?(bytes?into?file)
Flags:?????????????????????????????0x0
Size?of?this?header:???????????????64?(bytes)
Size?of?program?headers:???????????32?(bytes)?????????//程序頭表每項(xiàng)32B
Number?of?program?headers:?????????8??????????????????//程序頭表共8項(xiàng)
Size?of?section?headers:???????????64?(bytes)
Number?of?section?headers:?????????13
Section?header?string?table?index:?10????????????????//.strtab在節(jié)頭表中的索引
裝入內(nèi)存時(shí),ELF頭、程序頭表、.init節(jié)、.rodata節(jié)會(huì)被裝入只讀代碼段。.data節(jié)和.bss節(jié)會(huì)被裝入讀寫數(shù)據(jù)段。
段頭表能夠描述可執(zhí)行文件中的節(jié)與虛擬空間中的存儲(chǔ)段之間的映射關(guān)系。一個(gè)表項(xiàng)32B,說(shuō)明虛擬地址空間中一個(gè)連續(xù)的片段或一個(gè)特殊的節(jié)。以下是32位系統(tǒng)對(duì)應(yīng)的段頭表數(shù)據(jù)結(jié)構(gòu):typedef?struct?{
Elf32_Word?p_type;???//此數(shù)組元素描述的段的類型,或者如何解釋此數(shù)組元素的信息。
Elf32_Off?p_offset;??//此成員給出從文件頭到該段第一個(gè)字節(jié)的偏移
Elf32_Addr?p_vaddr;??//此成員給出段的第一個(gè)字節(jié)將被放到內(nèi)存中的虛擬地址
Elf32_Addr?p_paddr;??//此成員僅用于與物理地址相關(guān)的系統(tǒng)中。System V忽略所有應(yīng)用程序的物理地址信息。
Elf32_Word?p_filesz;?//此成員給出段在文件映像中所占的字節(jié)數(shù)。可以為0。
Elf32_Word?p_memsz;??//此成員給出段在內(nèi)存映像中占用的字節(jié)數(shù)。可以為0。
Elf32_Word?p_flags;??//此成員給出與段相關(guān)的標(biāo)志。
Elf32_Word?p_align;??//此成員給出段在文件中和內(nèi)存中如何對(duì)齊。
}?Elf32_phdr;
使用readelf命令查看某可執(zhí)行目標(biāo)文件的程序頭表。[ubuntu@localhost?interpositioning]$readelf?-l?main
Elf?file?type?is?EXEC?(Executable?file)
Entry?point?0x400550
There?are?9?program?headers,?starting?at?offset?64
Program?Headers:
Type???????????Offset?????????????VirtAddr???????????PhysAddr
FileSiz????????????MemSiz??????????????Flags??Align
PHDR???????????0x0000000000000040?0x0000000000400040?0x0000000000400040
0x00000000000001f8?0x00000000000001f8??R?E????8
INTERP?????????0x0000000000000238?0x0000000000400238?0x0000000000400238
0x000000000000001c?0x000000000000001c??R??????1
[Requesting?program?interpreter:?/lib64/ld-linux-x86-64.so.2]
LOAD???????????0x0000000000000000?0x0000000000400000?0x0000000000400000
0x00000000000008ac?0x00000000000008ac??R?E????200000
LOAD???????????0x0000000000000e10?0x0000000000600e10?0x0000000000600e10
0x0000000000000240?0x0000000000000248??RW?????200000
DYNAMIC????????0x0000000000000e28?0x0000000000600e28?0x0000000000600e28
0x00000000000001d0?0x00000000000001d0??RW?????8
NOTE???????????0x0000000000000254?0x0000000000400254?0x0000000000400254
0x0000000000000044?0x0000000000000044??R??????4
GNU_EH_FRAME???0x0000000000000780?0x0000000000400780?0x0000000000400780
0x0000000000000034?0x0000000000000034??R??????4
GNU_STACK??????0x0000000000000000?0x0000000000000000?0x0000000000000000
0x0000000000000000?0x0000000000000000??RW?????10
GNU_RELRO??????0x0000000000000e10?0x0000000000600e10?0x0000000000600e10
0x00000000000001f0?0x00000000000001f0??R??????1
程序頭表信息有9個(gè)表項(xiàng),其中兩個(gè)為可裝入段(即Type=LOAD):
第一可裝入段:第0x0000 0~0x0x8ab的長(zhǎng)度為0x8ac字節(jié)的ELF頭、程序頭表、.init、.text和.rodata節(jié),映射到虛擬地址0x400000開(kāi)始長(zhǎng)度為0x8ac字節(jié)的區(qū)域 ,按0x200000=2MB對(duì)齊,具有只讀/執(zhí)行權(quán)限(Flg=RE),是只讀代碼段。
第二可裝入段:第0xe10 ~0x104f的長(zhǎng)度為0x240字節(jié)的.data節(jié)和磁盤中不占存儲(chǔ)空間的.bss節(jié),映射到虛擬地址0x600e10開(kāi)始長(zhǎng)度為0x248字節(jié)的存儲(chǔ)區(qū)域,在0x248=584B存儲(chǔ)區(qū)中,前0x240= 576B用.data節(jié)內(nèi)容初始化,后面584-576= 8B對(duì)應(yīng).bss節(jié),初始化為0 ,按0x200000 =2MB對(duì)齊,具有可讀可寫權(quán)限(Flg=RW),是可讀寫數(shù)據(jù)段。
由此看出.bss節(jié)在文件中不占用磁盤空間,但在存儲(chǔ)器中需要給它分配相應(yīng)大小的空間。
5.總結(jié)
1.鏈接處理涉及到三種目標(biāo)文件格式:可重定位目標(biāo)文件、可執(zhí)行目標(biāo)文件和共享目標(biāo)文件。共享庫(kù)文件是一種特殊的可重定位目標(biāo)。
2.ELF目標(biāo)文件格式可以從編譯鏈接角度和程序執(zhí)行角度兩個(gè)角度看,前者是可重定位目標(biāo)格式,后者是可執(zhí)行目標(biāo)格式。從編譯鏈接角度看,可重定位目標(biāo)文件中包含ELF頭、各個(gè)節(jié)以及節(jié)頭表。可執(zhí)行目標(biāo)文件中包含ELF頭、程序頭表(段頭表)以及各種節(jié)組成的段。
3.bss段在可執(zhí)行目標(biāo)文件中不會(huì)有它的空間,只有當(dāng)可執(zhí)行目標(biāo)文件裝載運(yùn)行時(shí),才會(huì)被分配內(nèi)存(并且位于data段內(nèi)存塊之后),并且初始化為0。
總結(jié)
以上是生活随笔為你收集整理的c语言书籍elf文件,扒一扒ELF文件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 福建省计算机二级c语言题型,计算机二级C
- 下一篇: 树莓派云音乐c语言,基于树莓派的红外遥控