Linux系統--ELF文件之可執行文件(Executable file)解析

    Linux下ELF文件類型分爲以下幾種:

    1、可重定位文件,例如SimpleSection.o;

    2、可執行文件,例如/bin/bash;

    3、共享目標文件,例如/lib/libc.so。


    在Linux 可重定位文件 ELF結構一文中,我們已經分析了可重定位文件ELF結構。本文分析可執行文件的ELF結構。


    首先附上源代碼:

    SectionMapping.c 

[cpp] view plain copy
  1. #include <stdlib.h>  
  2.   
  3. int main()  
  4. {  
  5.     while(1)  
  6.     {  
  7.         sleep(1000);  
  8.     }  
  9.     return 0;  
  10. }  

    使用命令gcc -static SectionMapping.c -o SectionMapping.elf,靜態鏈接爲可執行文件。


    接着使用命令readelf -S SectionMapping.elf得到Section Table。如下:

[plain] view plain copy
  1. There are 33 section headers, starting at offset 0xc3878:  
  2.   
  3. Section Headers:  
  4.   [Nr] Name              Type             Address           Offset  
  5.        Size              EntSize          Flags  Link  Info  Align  
  6.   [ 0]                   NULL             0000000000000000  00000000  
  7.        0000000000000000  0000000000000000           0     0     0  
  8.   [ 1] .note.ABI-tag     NOTE             0000000000400190  00000190  
  9.        0000000000000020  0000000000000000   A       0     0     4  
  10.   [ 2] .note.gnu.build-i NOTE             00000000004001b0  000001b0  
  11.        0000000000000024  0000000000000000   A       0     0     4  
  12.   [ 3] .rela.plt         RELA             00000000004001d8  000001d8  
  13.        0000000000000120  0000000000000018   A       0     5     8  
  14.   [ 4] .init             PROGBITS         00000000004002f8  000002f8  
  15.        0000000000000018  0000000000000000  AX       0     0     4  
  16.   [ 5] .plt              PROGBITS         0000000000400310  00000310  
  17.        00000000000000c0  0000000000000000  AX       0     0     16  
  18.   [ 6] .text             PROGBITS         00000000004003d0  000003d0  
  19.        0000000000094988  0000000000000000  AX       0     0     16  
  20.   [ 7] __libc_thread_fre PROGBITS         0000000000494d60  00094d60  
  21.        00000000000000a8  0000000000000000  AX       0     0     16  
  22.   [ 8] __libc_freeres_fn PROGBITS         0000000000494e10  00094e10  
  23.        000000000000181c  0000000000000000  AX       0     0     16  
  24.   [ 9] .fini             PROGBITS         000000000049662c  0009662c  
  25.        000000000000000e  0000000000000000  AX       0     0     4  
  26.   [10] .rodata           PROGBITS         0000000000496640  00096640  
  27.        000000000001d344  0000000000000000   A       0     0     32  
  28.   [11] __libc_thread_sub PROGBITS         00000000004b3988  000b3988  
  29.        0000000000000008  0000000000000000   A       0     0     8  
  30.   [12] __libc_subfreeres PROGBITS         00000000004b3990  000b3990  
  31.        0000000000000058  0000000000000000   A       0     0     8  
  32.   [13] __libc_atexit     PROGBITS         00000000004b39e8  000b39e8  
  33.        0000000000000008  0000000000000000   A       0     0     8  
  34.   [14] .eh_frame         PROGBITS         00000000004b39f0  000b39f0  
  35.        000000000000d4c4  0000000000000000   A       0     0     8  
  36.   [15] .gcc_except_table PROGBITS         00000000004c0eb4  000c0eb4  
  37.        0000000000000172  0000000000000000   A       0     0     1  
  38.   [16] .tdata            PROGBITS         00000000006c1ef0  000c1ef0  
  39.        0000000000000020  0000000000000000 WAT       0     0     16  
  40.   [17] .tbss             NOBITS           00000000006c1f10  000c1f10  
  41.        0000000000000038  0000000000000000 WAT       0     0     16  
  42.   [18] .init_array       INIT_ARRAY       00000000006c1f10  000c1f10  
  43.        0000000000000008  0000000000000000  WA       0     0     8  
  44.   [19] .fini_array       FINI_ARRAY       00000000006c1f18  000c1f18  
  45.        0000000000000008  0000000000000000  WA       0     0     8  
  46.   [20] .ctors            PROGBITS         00000000006c1f20  000c1f20  
  47.        0000000000000010  0000000000000000  WA       0     0     8  
  48.   [21] .dtors            PROGBITS         00000000006c1f30  000c1f30  
  49.        0000000000000010  0000000000000000  WA       0     0     8  
  50.   [22] .jcr              PROGBITS         00000000006c1f40  000c1f40  
  51.        0000000000000008  0000000000000000  WA       0     0     8  
  52.   [23] .data.rel.ro      PROGBITS         00000000006c1f50  000c1f50  
  53.        0000000000000080  0000000000000000  WA       0     0     16  
  54.   [24] .got              PROGBITS         00000000006c1fd0  000c1fd0  
  55.        0000000000000010  0000000000000008  WA       0     0     8  
  56.   [25] .got.plt          PROGBITS         00000000006c1fe8  000c1fe8  
  57.        0000000000000078  0000000000000008  WA       0     0     8  
  58.   [26] .data             PROGBITS         00000000006c2060  000c2060  
  59.        0000000000001690  0000000000000000  WA       0     0     32  
  60.   [27] .bss              NOBITS           00000000006c3700  000c36f0  
  61.        0000000000002ba8  0000000000000000  WA       0     0     32  
  62.   [28] __libc_freeres_pt NOBITS           00000000006c62b0  000c36f0  
  63.        0000000000000048  0000000000000000  WA       0     0     16  
  64.   [29] .comment          PROGBITS         0000000000000000  000c36f0  
  65.        000000000000002a  0000000000000001  MS       0     0     1  
  66.   [30] .shstrtab         STRTAB           0000000000000000  000c371a  
  67.        000000000000015b  0000000000000000           0     0     1  
  68.   [31] .symtab           SYMTAB           0000000000000000  000c40b8  
  69.        000000000000c168  0000000000000018          32   870     8  
  70.   [32] .strtab           STRTAB           0000000000000000  000d0220  
  71.        0000000000007a26  0000000000000000           0     0     1  

                                     表 1

    這個可執行文件共有33個Section


    接着我們使用readelf -h SectionMapping.elf,讀取elf可執行文件頭部信息。如下圖:


                                     圖 1


    可以對比,Linux 可重定位文件 ELF結構,這裏多了program header。

    Entry point address:程序的入口地址是0x401058,使用objdump -d SectionMapping.elf | less,可以查看到程序的入口地址是<_start>。如下圖:


                                           圖 2

    Start of program headers:program headers的偏移,由於頭文件大小爲64,所以program headers緊挨着頭文件存放。

    Size of program headers:program headers的大小。爲56個字節。

    Number of section headers:program headers的數量。爲6個。

    在表1中,第一個section在文件中的偏移是0x190,頭文件大小爲64 + program header大小爲56 * program header數量6 = 400 = 0x190。


    然後,我們使用命令readelf -l SectionMapping.elf,我們會得到program header部分。如下圖:


                                 圖  3


    從圖中可見,分爲6個Segment。注意表1中每個段叫Section

    Offset:這個Segment在文件中偏移。

    VirtAddr:這個Segment在虛擬地址的偏移。

    FileSiz:在ELF文件中所佔的長度。

    MemSiz:在進程虛擬空間所佔的長度。

    我們發現第二個Segment,MemSiz > FileSiz,表示在內存中分配的空間大小超過文件實際大小。超過的部分全部初始化爲0,作爲BSS段。因爲數據段和BSS段的唯一區別是,數據段從文件中初始化內容,BSS段內容全部初始化爲0。


    我們主要關心前兩個Segment,第一個是代碼段,虛擬地址從0x00400000到0x004c1026。文件偏移從0x00000000到0x000c1026。

    第二個是數據段,虛擬地址爲從0x006c1ef0到0x006c1ef0+0x4408=0x6c62f8。文件偏移從0x000c1ef0到0x000c1ef0+0x1800=0x000C36f0。

    結合表1和兩個Segment的文件偏移,可以得出:

    第一個Segment從第0個Section到第15個Section。(0x00000000-0x000c1026)

[plain] view plain copy
  1. [Nr] Name              Type             Address           Offset  
  2.      Size              EntSize          Flags  Link  Info  Align  
  3. [ 0]                   NULL             0000000000000000  00000000  
  4.      0000000000000000  0000000000000000           0     0     0  
  5. [ 1] .note.ABI-tag     NOTE             0000000000400190  00000190  
  6.      0000000000000020  0000000000000000   A       0     0     4  
  7. [ 2] .note.gnu.build-i NOTE             00000000004001b0  000001b0  
  8.      0000000000000024  0000000000000000   A       0     0     4  
  9. [ 3] .rela.plt         RELA             00000000004001d8  000001d8  
  10.      0000000000000120  0000000000000018   A       0     5     8  
  11. [ 4] .init             PROGBITS         00000000004002f8  000002f8  
  12.      0000000000000018  0000000000000000  AX       0     0     4  
  13. [ 5] .plt              PROGBITS         0000000000400310  00000310  
  14.      00000000000000c0  0000000000000000  AX       0     0     16  
  15. [ 6] .text             PROGBITS         00000000004003d0  000003d0  
  16.      0000000000094988  0000000000000000  AX       0     0     16  
  17. [ 7] __libc_thread_fre PROGBITS         0000000000494d60  00094d60  
  18.      00000000000000a8  0000000000000000  AX       0     0     16  
  19. [ 8] __libc_freeres_fn PROGBITS         0000000000494e10  00094e10  
  20.      000000000000181c  0000000000000000  AX       0     0     16  
  21. [ 9] .fini             PROGBITS         000000000049662c  0009662c  
  22.      000000000000000e  0000000000000000  AX       0     0     4  
  23. [10] .rodata           PROGBITS         0000000000496640  00096640  
  24.      000000000001d344  0000000000000000   A       0     0     32  
  25. [11] __libc_thread_sub PROGBITS         00000000004b3988  000b3988  
  26.      0000000000000008  0000000000000000   A       0     0     8  
  27. [12] __libc_subfreeres PROGBITS         00000000004b3990  000b3990  
  28.      0000000000000058  0000000000000000   A       0     0     8  
  29. [13] __libc_atexit     PROGBITS         00000000004b39e8  000b39e8  
  30.      0000000000000008  0000000000000000   A       0     0     8  
  31. [14] .eh_frame         PROGBITS         00000000004b39f0  000b39f0  
  32.      000000000000d4c4  0000000000000000   A       0     0     8  
  33. [15] .gcc_except_table PROGBITS         00000000004c0eb4  000c0eb4  
  34.      0000000000000172  0000000000000000   A       0     0     1  

    第二個Segment從第16個Section到26個Section。(0x000c1ef0-0x000C36f0

[plain] view plain copy
  1. [16] .tdata            PROGBITS         00000000006c1ef0  000c1ef0  
  2.      0000000000000020  0000000000000000 WAT       0     0     16  
  3. [17] .tbss             NOBITS           00000000006c1f10  000c1f10  
  4.      0000000000000038  0000000000000000 WAT       0     0     16  
  5. [18] .init_array       INIT_ARRAY       00000000006c1f10  000c1f10  
  6.      0000000000000008  0000000000000000  WA       0     0     8  
  7. [19] .fini_array       FINI_ARRAY       00000000006c1f18  000c1f18  
  8.      0000000000000008  0000000000000000  WA       0     0     8  
  9. [20] .ctors            PROGBITS         00000000006c1f20  000c1f20  
  10.      0000000000000010  0000000000000000  WA       0     0     8  
  11. [21] .dtors            PROGBITS         00000000006c1f30  000c1f30  
  12.      0000000000000010  0000000000000000  WA       0     0     8  
  13. [22] .jcr              PROGBITS         00000000006c1f40  000c1f40  
  14.      0000000000000008  0000000000000000  WA       0     0     8  
  15. [23] .data.rel.ro      PROGBITS         00000000006c1f50  000c1f50  
  16.      0000000000000080  0000000000000000  WA       0     0     16  
  17. [24] .got              PROGBITS         00000000006c1fd0  000c1fd0  
  18.      0000000000000010  0000000000000008  WA       0     0     8  
  19. [25] .got.plt          PROGBITS         00000000006c1fe8  000c1fe8  
  20.      0000000000000078  0000000000000008  WA       0     0     8  
  21. [26] .data             PROGBITS         00000000006c2060  000c2060  
  22.      0000000000001690  0000000000000000  WA       0     0     32  

    以上分析的都是靜態狀態下的程序,下面我們看看動態下的進程的空間是怎麼分配的。

    首先使用命令, ./SectionMapping.elf &,輸出如下:

    

    然後使用命令:cat /proc/2184/maps,輸出如下:


                                    圖 4

    

    靜態時,我們計算出的兩個Segment的虛擬空間的偏移分別爲:

    第一個是代碼段,虛擬地址從0x00400000到0x004c1026。在圖4中,因爲要頁面對齊,所以分配了0x400000到0x4c2000。

    第二個是數據段,虛擬地址爲從0x006c1ef0到0x006c1ef0+0x4408=0x6c62f8。在圖4中,因爲要頁面對齊,所以分配了0x6c1000到0x6c4000。注意,0x6c62f8大於0x6c4000,具體原因以後再分析。

    第三個緊接着是堆。用於動態分配內存。

    第四個是棧。用於存放局部變量。

    整體的結構如下圖:



    程序運行的過程:建立虛擬空間(分配一個頁目錄)-> 建立虛擬空間與可執行文件映射(頁目錄項指向磁盤的程序) -> 跳到程序入口 -> 缺頁異常-> 在內存中尋找空閒頁,將對應的頁換入 -> 建立映射 -> 開始執行。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章