扒一扒ELF文件

      網友投稿 879 2025-03-31

      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; }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      //sum.c int sum(int *a, int n) { int i, s = 0; for (i = 0; i < n; i++) { s += a[i]; } return s; }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      讓我們來看看如果我們使用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_He

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

      段頭表

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

      .text section

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

      .rodata section

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

      .data section

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

      .bss section

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

      .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;

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      使用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)

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      扒一扒ELF文件

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

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

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

      4.2 從程序執行角度看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在節頭表中的索引

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      裝入內存時,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;

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      使用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

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      程序頭表信息有9個表項,其中兩個為可裝入段(Type=LOAD):

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

      第二可裝入段(第17,18行):第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。

      本文參考

      《深入理解計算機系統》

      HTML 數據結構

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:思源項目管理
      下一篇:懸掛縮進在哪里啊(懸掛縮進什么樣子)
      相關文章
      亚洲妇女熟BBW| 激情五月亚洲色图| 亚洲人成网站999久久久综合| 亚洲视频一区二区在线观看| 亚洲成人午夜在线| 亚洲精品乱码久久久久久蜜桃图片 | 亚洲欧美中文日韩视频| 亚洲人成人网毛片在线播放| 亚洲伦理中文字幕| 亚洲人成综合网站7777香蕉| 亚洲三级在线观看| 亚洲中文无码永久免| 亚洲综合av一区二区三区| 亚洲中文字幕久久精品无码A| 亚洲一本之道高清乱码| 亚洲一区二区三区高清视频| 亚洲国产成人精品激情| 亚洲综合在线一区二区三区| 亚洲码和欧洲码一码二码三码 | 国产精品亚洲精品日韩已满| 国产亚洲视频在线播放| 亚洲精品成人片在线观看精品字幕| 亚洲国产精品va在线播放| 亚洲av无码av制服另类专区| 亚洲精品天天影视综合网| 中文字幕亚洲综合久久| 亚洲人成综合在线播放| 亚洲国产系列一区二区三区 | 亚洲国产精品久久久天堂| 亚洲尹人九九大色香蕉网站| 亚洲精品456在线播放| 亚洲五月丁香综合视频| 亚洲暴爽av人人爽日日碰| 亚洲av区一区二区三| 国产成人亚洲综合| 亚洲国产AV无码一区二区三区 | 久久久久亚洲av无码专区喷水| 亚洲神级电影国语版| 亚洲 暴爽 AV人人爽日日碰| 亚洲av成人一区二区三区在线播放| 亚洲国产成人精品91久久久|