圖文并茂?|?例說(shuō)ELF文件
時(shí)間:2021-09-03 10:10:57
手機(jī)看文章
掃描二維碼
隨時(shí)隨地手機(jī)看文章
[導(dǎo)讀]ELF文件(ExecutableLinkableFormat)是一種文件存儲(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ì)象文件(.s...
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ò)程如下圖所示。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)文件)
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í)行文件)
[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ì)被裝入讀寫(xiě)數(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ù)??梢詾?。
????Elf32_Word?p_memsz;??//此成員給出段在內(nèi)存映像中占用的字節(jié)數(shù)??梢詾?。
????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é)和磁盤(pán)中不占存儲(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ì)齊,具有可讀可寫(xiě)權(quán)限(Flg=RW),是可讀寫(xiě)數(shù)據(jù)段。??由此看出.bss節(jié)在文件中不占用磁盤(pán)空間,但在存儲(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。本文參考《深入理解計(jì)算機(jī)系統(tǒng)》??掃描下方二維碼關(guān)注我的公眾號(hào)【嵌入式與Linux那些事】??回復(fù)【交流群】,掃碼進(jìn)入技術(shù)交流群,一起學(xué)習(xí),一起進(jìn)步!





