www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 公眾號精選 > 嵌入式大雜燴
[導讀]ELF文件(ExecutableLinkableFormat)是一種文件存儲格式。Linux下的目標文件和可執(zhí)行文件都按照該格式進行存儲,有必要做個總結。1.鏈接舉例2.ELF文件類型2.1可重定位目標文件(.o文件)2.2可執(zhí)行目標文件(a.out文件)2.3共享對象文件(.s...

ELF文件(Executable Linkable Format)是一種文件存儲格式。Linux下的目標文件和可執(zhí)行文件都按照該格式進行存儲,有必要做個總結。


  • 1. 鏈接舉例

  • 2. ELF文件類型

    • 2.1 可重定位目標文件(.o文件)

    • 2.2 可執(zhí)行目標文件(a.out文件)

    • 2.3 共享對象文件(.so文件)

  • 3. ELF文件作用

  • 4. ELF文件格式

    • 4.1 從編譯和鏈接角度看ELF文件(可重定位目標文件)

    • 4.2 從程序執(zhí)行角度看ELF文件(可執(zhí)行文件)

  • 5.總結


1. 鏈接舉例

??在介紹ELF文件之前,我們先看下,一個.c程序是如何變成可執(zhí)行目標文件的。下面舉個例子。

??該程序由main.c和sum.c兩個模塊組成。sum.c接收數組和數組長度兩個參數,最后將數組求和的結果返回。main.c調用sum函數,并傳遞一個兩元素的int數組array,將計算結果保存在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;
}
??讓我們來看看如果我們使用GCC編譯兩個模塊會發(fā)生什么?

??main.c和sum.c將分別通過翻譯器將源文件處理為可重定位的目標文件main.o和sum.o。翻譯器處理的過程包括了預處理(ccp)、編譯(ccl)、匯編(as) 三個過程。最后,鏈接器(ld) 將可重定位的目標文件main.o和sum.o以及一些必要的系統(tǒng)文件組合起來,創(chuàng)建一個可執(zhí)行目標文件prog。具體過程如下圖所示。

鏈接過程
??由上面的過程,我們可以看出在經過匯編器后會輸出一個.o文件,這個叫做可重定位的目標文件。將main.o和sum.o輸入鏈接器后,鏈接器輸出的prog文件叫做可執(zhí)行目標文件。那這兩個目標文件有什么樣的區(qū)別呢?

2. ELF文件類型

2.1 可重定位目標文件(.o文件)

??包含二進制代碼和數據,其形式可以和其他目標文件進行合并,創(chuàng)建一個可執(zhí)行目標文件。例如lib*.o文件。

2.2 可執(zhí)行目標文件(a.out文件)

??包含二進制代碼和數據,可直接被加載器加載執(zhí)行。例如編譯好的可執(zhí)行文件a.out。

2.3 共享對象文件(.so文件)

??用于和其他共享目標文件或者可重定位文件一起生成ELF目標文件或者和執(zhí)行文件一起創(chuàng)建進程映像,例如lib*.so文件。

3. ELF文件作用

??ELF文件參與程序的連接(建立一個程序)和程序的執(zhí)行(運行一個程序),所以可以從不同的角度來看待ELF格式的文件:

??1.如果用于編譯和鏈接(可重定位文件),則編譯器和鏈接器將把ELF文件看作是節(jié)頭表描述的節(jié)的集合,程序頭表可選。

??2.如果用于加載執(zhí)行(可執(zhí)行文件),則加載器則將把ELF文件看作是程序頭表描述的段的集合,一個段可能包含多個節(jié),節(jié)頭表可選。

4. ELF文件格式

4.1 從編譯和鏈接角度看ELF文件(可重定位目標文件)

從編譯和鏈接角度看ELF文件
ELF頭

??每個ELF文件都必須存在一個ELF_He ader,這里存放了很多重要的信息用來描述整個文件的組織,如: 版本信息,入口信息,偏移信息等。程序執(zhí)行也必須依靠其提供的信息。

段頭表

??段頭表。存放的是所有不同段將在內存中的位置。

.text section

??代碼段。存放已編譯程序的機器代碼,一般是只讀的。

.rodata section

??只讀數據段。此段的數據不可修改,存放常量。比如,printf中的格式化語句。

.data section

??數據段。存放已初始化的全局變量、常量。

.bss section

??bss段。未初始化全局變量,僅是占位符,不占據任何實際磁盤空間。目標文件格式區(qū)分初始化和非初始化是為了空間效率。

從編譯和鏈接角度看ELF文件
.symtab section

??符號表,它存放在程序中定義和引用的函數和全局變量的信息。

.rel.txt section

??.text節(jié)的重定位信息,用于重新修改代碼段的指令中的地址信息。

.rel.data section

??.data節(jié)的重定位信息,用于對被模塊使用或定義的全局變量進行重定位的信息。

.debug section

??調試用的符號表。

.strtab section

??包含 symtab和 debug節(jié)中符號及節(jié)名。

節(jié)頭部表

??每個節(jié)的節(jié)名、偏移和大小。

??以下是32位系統(tǒng)對應的節(jié)頭表數據結構,說明了每個節(jié)的節(jié)名、在文件中的偏移、大小、訪問屬性、對齊方式等。

typedef?struct?{
????Elf32_Word?sh_name;???//節(jié)名字符串在.strtab節(jié)(字符串表)中的偏移
????Elf32_Word?sh_type;???//節(jié)類型:無效/代碼或數據/符號/字符串/...
????Elf32_Word?sh_flags;??//節(jié)標志:該節(jié)在虛擬空間中的訪問屬性
????Elf32_Addr?sh_addr;???//虛擬地址:若可被加載,則對應虛擬地址
????Elf32_Off??sh_offset;?//在文件中的偏移地址,對.bss節(jié)而言則無意義
????Elf32_Word?sh_size;???//節(jié)在文件中所占的長度
????Elf32_Word?sh_link;???//sh_link和sh_info用于與鏈接相關的節(jié)(如?.rel.text節(jié)、.rel.data節(jié)、.symtab節(jié)等)
????Elf32_Word?sh_info;
????Elf32_Word?sh_addralign;?//節(jié)的對齊要求
????Elf32_Word?sh_entsize;???//節(jié)中每個表項的長度,0表示無固定長度表項
}?Elf32_Shdr;
??使用readelf命令命令查看節(jié)頭表內容

[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)
??可重定位目標文件中,每個可裝入節(jié)的起始地址總是0。

??.bss節(jié)應占0x0c大小,但只有裝入內存時才會分配。

4.2 從程序執(zhí)行角度看ELF文件(可執(zhí)行文件)

從程序執(zhí)行角度看ELF文件
??與可重定位目標文件不同:

??1.ELF頭中,字段 e_entry給出執(zhí)行程序時第一條指令的地址,而在可重定位文件中,此字段為0。

??2.多一個init節(jié),用于定義init函數,該函數用來進行可執(zhí)行目標文件開始執(zhí)行時的初始化工作。

??3.少兩個.rel節(jié)(無需重定位)。

??4.多一個程序頭表,也稱段頭表,是一個結構數組。

??使用readelf命令查看ELF頭的內容:

[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)?????????//程序頭表每項32B
??Number?of?program?headers:?????????8??????????????????//程序頭表共8項
??Size?of?section?headers:???????????64?(bytes)
??Number?of?section?headers:?????????13
??Section?header?string?table?index:?10????????????????//.strtab在節(jié)頭表中的索引
??裝入內存時,ELF頭、程序頭表、.init節(jié)、.rodata節(jié)會被裝入只讀代碼段。.data節(jié)和.bss節(jié)會被裝入讀寫數據段。

??段頭表能夠描述可執(zhí)行文件中的節(jié)與虛擬空間中的存儲段之間的映射關系。一個表項32B,說明虛擬地址空間中一個連續(xù)的片段或一個特殊的節(jié)。以下是32位系統(tǒng)對應的段頭表數據結構:

typedef?struct?{
????Elf32_Word?p_type;???//此數組元素描述的段的類型,或者如何解釋此數組元素的信息。
????Elf32_Off?p_offset;??//此成員給出從文件頭到該段第一個字節(jié)的偏移
????Elf32_Addr?p_vaddr;??//此成員給出段的第一個字節(jié)將被放到內存中的虛擬地址
????Elf32_Addr?p_paddr;??//此成員僅用于與物理地址相關的系統(tǒng)中。System V忽略所有應用程序的物理地址信息。
????Elf32_Word?p_filesz;?//此成員給出段在文件映像中所占的字節(jié)數??梢詾?。
????Elf32_Word?p_memsz;??//此成員給出段在內存映像中占用的字節(jié)數。可以為0。
????Elf32_Word?p_flags;??//此成員給出與段相關的標志。
????Elf32_Word?p_align;??//此成員給出段在文件中和內存中如何對齊。
}?Elf32_phdr;
??使用readelf命令查看某可執(zhí)行目標文件的程序頭表。

[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個表項,其中兩個為可裝入段(即Type=LOAD):

??第一可裝入段:第0x0000 0~0x0x8ab的長度為0x8ac字節(jié)的ELF頭、程序頭表、.init、.text和.rodata節(jié),映射到虛擬地址0x400000開始長度為0x8ac字節(jié)的區(qū)域 ,按0x200000=2MB對齊,具有只讀/執(zhí)行權限(Flg=RE),是只讀代碼段。

??第二可裝入段:第0xe10 ~0x104f的長度為0x240字節(jié)的.data節(jié)和磁盤中不占存儲空間的.bss節(jié),映射到虛擬地址0x600e10開始長度為0x248字節(jié)的存儲區(qū)域,在0x248=584B存儲區(qū)中,前0x240= 576B用.data節(jié)內容初始化,后面584-576= 8B對應.bss節(jié),初始化為0 ,按0x200000 =2MB對齊,具有可讀可寫權限(Flg=RW),是可讀寫數據段。

??由此看出.bss節(jié)在文件中不占用磁盤空間,但在存儲器中需要給它分配相應大小的空間。

5.總結

??1.鏈接處理涉及到三種目標文件格式:可重定位目標文件、可執(zhí)行目標文件和共享目標文件。共享庫文件是一種特殊的可重定位目標。

??2.ELF目標文件格式可以從編譯鏈接角度程序執(zhí)行角度兩個角度看,前者是可重定位目標格式,后者是可執(zhí)行目標格式。從編譯鏈接角度看,可重定位目標文件中包含ELF頭、各個節(jié)以及節(jié)頭表??蓤?zhí)行目標文件中包含ELF頭、程序頭表(段頭表)以及各種節(jié)組成的段。

??3.bss段在可執(zhí)行目標文件中不會有它的空間,只有當可執(zhí)行目標文件裝載運行時,才會被分配內存(并且位于data段內存塊之后),并且初始化為0

本文參考《深入理解計算機系統(tǒng)》

??掃描下方二維碼關注我的公眾號【嵌入式與Linux那些事】

??回復【交流群】,掃碼進入技術交流群,一起學習,一起進步!

?有任何問題均可通過下方二維碼聯系我,歡迎各位大佬一起學習交流。


本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯系該專欄作者,如若文章內容侵犯您的權益,請及時聯系本站刪除。
關閉
關閉