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

當(dāng)前位置:首頁 > 公眾號精選 > 嵌入式大雜燴
[導(dǎo)讀]ELF文件(Executable Linkable Format)是一種文件存儲格式。Linux下的目標(biāo)文件

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

  • 1. 鏈接舉例

  • 2. ELF文件類型

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

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

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

  • 3. ELF文件作用

  • 4. ELF文件格式

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

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

  • 5.總結(jié)

1. 鏈接舉例

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

??該程序由main.c和sum.c兩個模塊組成。sum.c接收數(shù)組和數(shù)組長度兩個參數(shù),最后將數(shù)組求和的結(jié)果返回。main.c調(diào)用sum函數(shù),并傳遞一個兩元素的int數(shù)組array,將計算結(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 < n; i ) {
        s  = a[i];
    }
    return s;
}
??讓我們來看看如果我們使用GCC編譯兩個模塊會發(fā)生什么?

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

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

2. ELF文件類型

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

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

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

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

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

??用于和其他共享目標(biāo)文件或者可重定位文件一起生成ELF目標(biāo)文件或者和執(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文件(可重定位目標(biāo)文件)

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

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

段頭表

??段頭表。存放的是所有不同段將在內(nèi)存中的位置。

.text section

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

.rodata section

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

.data section

??數(shù)據(jù)段。存放已初始化的全局變量、常量。

.bss section

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

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

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

.rel.txt section

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

.rel.data section

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

.debug section

??調(diào)試用的符號表。

.strtab section

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

節(jié)頭部表

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

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


typedef struct {
    Elf32_Word sh_name;   //節(jié)名字符串在.strtab節(jié)(字符串表)中的偏移
    Elf32_Word sh_type;   //節(jié)類型:無效/代碼或數(shù)據(jù)/符號/字符串/...
    Elf32_Word sh_flags;  //節(jié)標(biāo)志:該節(jié)在虛擬空間中的訪問屬性
    Elf32_Addr sh_addr;   //虛擬地址:若可被加載,則對應(yīng)虛擬地址
    Elf32_Off  sh_offset; //在文件中的偏移地址,對.bss節(jié)而言則無意義
    Elf32_Word sh_size;   //節(jié)在文件中所占的長度
    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é)的對齊要求
    Elf32_Word sh_entsize;   //節(jié)中每個表項的長度,0表示無固定長度表項
} 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)文件中,每個可裝入節(jié)的起始地址總是0。

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

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

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

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

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

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

??4.多一個程序頭表,也稱段頭表,是一個結(jié)構(gòu)數(shù)組。

??使用readelf命令查看ELF頭的內(nèi)容:


[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é)頭表中的索引
??裝入內(nèi)存時,ELF頭、程序頭表、.init節(jié)、.rodata節(jié)會被裝入只讀代碼段。.data節(jié)和.bss節(jié)會被裝入讀寫數(shù)據(jù)段。

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


typedef struct {
    Elf32_Word p_type;   //此數(shù)組元素描述的段的類型,或者如何解釋此數(shù)組元素的信息。
    Elf32_Off p_offset;  //此成員給出從文件頭到該段第一個字節(jié)的偏移
    Elf32_Addr p_vaddr;  //此成員給出段的第一個字節(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)存中如何對齊。
} 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個表項,其中兩個為可裝入段(即Type=LOAD):

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

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

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

5.總結(jié)

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

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

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

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


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