日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

c语言书籍elf文件,扒一扒ELF文件

發布時間:2025/3/20 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言书籍elf文件,扒一扒ELF文件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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

1. 鏈接舉例

2. ELF文件類型2.1 可重定位目標文件(.o文件)

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

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

3. ELF文件作用

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

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

5.總結

1. 鏈接舉例

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

該程序由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編譯兩個模塊會發生什么?

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

鏈接過程

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

2. ELF文件類型

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

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

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

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

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

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

3. ELF文件作用

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

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

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

4. ELF文件格式

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

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

ELF頭

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

段頭表

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

.text section

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

.rodata?section

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

.data?section

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

.bss?section

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

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

.symtab?section

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

.rel.txt?section

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

.rel.data?section

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

.debug?section

調試用的符號表。

.strtab section

包含 symtab和 debug節中符號及節名。

節頭部表

每個節的節名、偏移和大小。

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

Elf32_Word?sh_name;???//節名字符串在.strtab節(字符串表)中的偏移

Elf32_Word?sh_type;???//節類型:無效/代碼或數據/符號/字符串/...

Elf32_Word?sh_flags;??//節標志:該節在虛擬空間中的訪問屬性

Elf32_Addr?sh_addr;???//虛擬地址:若可被加載,則對應虛擬地址

Elf32_Off??sh_offset;?//在文件中的偏移地址,對.bss節而言則無意義

Elf32_Word?sh_size;???//節在文件中所占的長度

Elf32_Word?sh_link;???//sh_link和sh_info用于與鏈接相關的節(如?.rel.text節、.rel.data節、.symtab節等)

Elf32_Word?sh_info;

Elf32_Word?sh_addralign;?//節的對齊要求

Elf32_Word?sh_entsize;???//節中每個表項的長度,0表示無固定長度表項

}?Elf32_Shdr;

使用readelf命令命令查看節頭表內容[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)

可重定位目標文件中,每個可裝入節的起始地址總是0。

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

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

從程序執行角度看ELF文件

與可重定位目標文件不同:

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

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

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

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在節頭表中的索引

裝入內存時,ELF頭、程序頭表、.init節、.rodata節會被裝入只讀代碼段。.data節和.bss節會被裝入讀寫數據段。

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

Elf32_Word?p_type;???//此數組元素描述的段的類型,或者如何解釋此數組元素的信息。

Elf32_Off?p_offset;??//此成員給出從文件頭到該段第一個字節的偏移

Elf32_Addr?p_vaddr;??//此成員給出段的第一個字節將被放到內存中的虛擬地址

Elf32_Addr?p_paddr;??//此成員僅用于與物理地址相關的系統中。System V忽略所有應用程序的物理地址信息。

Elf32_Word?p_filesz;?//此成員給出段在文件映像中所占的字節數。可以為0。

Elf32_Word?p_memsz;??//此成員給出段在內存映像中占用的字節數。可以為0。

Elf32_Word?p_flags;??//此成員給出與段相關的標志。

Elf32_Word?p_align;??//此成員給出段在文件中和內存中如何對齊。

}?Elf32_phdr;

使用readelf命令查看某可執行目標文件的程序頭表。[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字節的ELF頭、程序頭表、.init、.text和.rodata節,映射到虛擬地址0x400000開始長度為0x8ac字節的區域 ,按0x200000=2MB對齊,具有只讀/執行權限(Flg=RE),是只讀代碼段。

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

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

5.總結

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

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

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

總結

以上是生活随笔為你收集整理的c语言书籍elf文件,扒一扒ELF文件的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。