程序運行之ELF文件結構


ELF目標文件格式的最前部是ELF文件頭。包含了整個文件的基本屬性。比如ELF文件版本,目標機器型號,程序入口地址等。然后是ELF的各個段,其中ELF文件中與段有關的重要結構就是段表。段表描述了ELF文件包含的所有段的信息,比如每個段的段名,段的長度,在文件中的偏移,讀寫權限及段的其他屬性。

 

一 文件頭;

通過readelf命令來詳細查看ELF文件的頭信息:

root@zhf-maple:/home/zhf/c_prj# readelf -h main.o

ELF 頭:

  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 

  類別:                              ELF64

  數據:                              2 補碼,小端序 (little endian)

  版本:                              1 (current)

  OS/ABI:                            UNIX - System V

  ABI 版本:                          0

  類型:                              REL (可重定位文件)

  系統架構:                          Advanced Micro Devices X86-64

  版本:                              0x1

  入口點地址:               0x0

  程序頭起點:          0 (bytes into file)

  Start of section headers:          1048 (bytes into file)

  標志:             0x0

  本頭的大小:       64 (字節)

  程序頭大小:       0 (字節)

  Number of program headers:         0

  節頭大小:         64 (字節)

  節頭數量:         13

  字符串表索引節頭: 12

我們將Elf32_Ehdr結構與readelf命令輸出的信息對比可得如下對應關系:

成員

readelf輸出結果

e_ident

Magic,類別,數據,版本,OS/ABI,ABI

e_type

類型

e_machine

系統架構

e_version

版本

e_entry

入口點地址

e_phoff

start of program headers

e_shoff

start of section headers

e_flags

標志

e_ehsize

文件頭的大小

e_phentsize

程序頭大小

e_phnum

number of program headers

e_shentsize

節頭大小

e_shnum

number of program headers

e_shstrndx

字符串表段索引

下面我來逐步分析各個部分:

魔數:

Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 

7f 454c46分別對應ascii碼的Del(刪除)、字母E、字母L、字母F。這四個字節被稱為ELF文件的魔數,操作系統在加載可執行文件時會確認魔數是否正確,如果不正確則拒絕加載。 
第五個字節標識ELF文件是32位(01)還是64位(02)的。 
第六個字節標識該ELF文件字節序是小端(01)還是大端(02)的。 
第七個字節指示ELF文件的版本號,一般是01。 
后九個字節ELF標准未做定義。一般為00.

 

文件類型:

e_type成員標識文件類型,ELF文件有三種類型,如下表所示。

常量標識

類型

ET_REL

1

可重定位文件,一般位.o文件

ET_EXEC

2

可執行文件

ET_DYN

3

共享目標文件,一般位.so文件

 

機器類型:

ELF文件格式被設計成可以在多個平台下使用,但並不表示同一個ELF文件可以在不同的平台下使用,而是表示不同平台下的ELF文件都遵循同一套ELF標准.e_machine成員就表示該ELF文件的平台屬性。

常量標識

系統架構

EM_M32

1

AT&T WE 32100

EM_SPARC

2

SPARC

EM_386

3

Intel 80386

EM_68K

4

Motorola m68k family

EM_88K

5

Motorola m88k family

EM_860

6

Intel 80860

 

 

二 段表:

ELF文件中有各種各樣的段,段表就是保存這些段的基本屬性的結構。它描述了ELF的各個段的信息,比如每個段的段名,段的長度,在文件中的偏移,讀寫權限及段的其他屬性。編譯器,鏈接器和裝載器都是依靠段表來定位和訪問各個段的屬性的。前面使用objdump -h來顯示各個段,但只是顯示了關鍵的幾個段。我們用readelf來輸出段表的內容。段表是存在Elf32_Shdr中,它是一個結構體數組。各個成員的含義如下表:

成員

含義

sh_name

段名,位於一個叫“.shstrtab”的字符串表

sh_type

段的類型,詳細內容看后文

sh_flags

段的標志位,詳細內容見后文

sh_addr

段在被加載后在進程地址空間中的虛擬地址,當段不能被加載時,它為0

sH_offset

段在elf文件中的偏移,如果該段不存在於文件中,則它無意義

sh_szie

段的長度

sh_link

段的鏈接信息,詳細內容見后文

sh_info

段的鏈接信息,詳細內容見后文

sh_addralign

段地址對齊

sh_entsize

項的長度,有的段包含一些固定大小的項,比如符號表,sh_enrsize就是用來指示這些項的大小

 

我們用readelf -S main.o來查看內容。

root@zhf-maple:/home/zhf/c_prj# readelf -S main.o

共有 13 個節頭,從偏移量 0x418 開始:

 

節頭:

  [名稱              類型             地址              偏移量

       大小              全體大小          旗標   鏈接   信息   對齊

  [ 0]                   NULL             0000000000000000  00000000

       0000000000000000  0000000000000000           0     0     0

  [ 1] .text             PROGBITS         0000000000000000  00000040

       000000000000004c  0000000000000000  AX       0     0     1

  [ 2] .rela.text        RELA             0000000000000000  00000320

       0000000000000060  0000000000000018   I      10     1     8

  [ 3] .data             PROGBITS         0000000000000000  0000008c

       0000000000000008  0000000000000000  WA       0     0     4

  [ 4] .bss              NOBITS           0000000000000000  00000094

       0000000000000004  0000000000000000  WA       0     0     4

  [ 5] .rodata           PROGBITS         0000000000000000  00000094

       0000000000000004  0000000000000000   A       0     0     1

  [ 6] .comment          PROGBITS         0000000000000000  00000098

       0000000000000024  0000000000000001  MS       0     0     1

  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000bc

       0000000000000000  0000000000000000           0     0     1

  [ 8] .eh_frame         PROGBITS         0000000000000000  000000c0

       0000000000000058  0000000000000000   A       0     0     8

  [ 9] .rela.eh_frame    RELA             0000000000000000  00000380

       0000000000000030  0000000000000018   I      10     8     8

  [10] .symtab           SYMTAB           0000000000000000  00000118

       0000000000000198  0000000000000018          11    11     8

  [11] .strtab           STRTAB           0000000000000000  000002b0

       000000000000006c  0000000000000000           0     0     1

  [12] .shstrtab         STRTAB           0000000000000000  000003b0

       0000000000000061  0000000000000000           0     0     1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),

  L (link order), O (extra OS processing required), G (group), T (TLS),

  C (compressed), x (unknown), o (OS specific), E (exclude),

  l (large), p (processor specific)

從上面的輸出的第一行可以看到,段表從0x418開始,觀察其他段的偏移量可以發現,段表位於所有段之后,就是文件的末尾,該ELF文件有12個段,每個段的大小也就是sizeof(Elf32_Shdr)的大小,為40字節,所以整個段表的大小就是12*40 = 480個字節,再加上之前的0x418,總共1048+480=1528個字節.

 

段的類型 
上面輸出的第三列就是段的類型,段的類型的相關常量如下表所示:

常量

含義

SHT_NULL

0

無效段

SHT_PROGBITS

1

程序段、代碼段、數據段都是此種類型

SHT_SYMTAB

2

表示該段的類內容為符號表

SHT_STRTAB

3

表示該段的內容是字符串表

SHT_RELA

4

重定位表,該段包含了重定位信息

SHT_HASH

5

符號表的哈希表

SHT_DYNAMIC

6

動態鏈接信息

SHT_NOTE

7

提示性信息

SHT_NOBITS

8

表示該段在文件中沒有內容,比如.bss段

SHT_REL

9

該段包含了重定位信息

SHT_SHLIB

10

該段保留

SHT_DNYSYM

11

動態鏈接的符號表

段的標志位 
以上輸出的flg一列指出了該段在進程虛擬空間中的屬性。

常量

含義

SHF_WRITE

1

表示該段在進程空間中可寫

SHF_ALLOC

2

表示該段需要在進程空間中分配空間

SHF_EXECINSTR

4

表示該段在進程空間中可以被執行

段的鏈接信息

sh_type

sh_link

sh_info

SHT_DYNAMIC

該段所使用的字符串表在段表中的下標

0

SHT_HASH

該段所使用的符號表在段表中的下標

0

SHT_REL,SHT_RELA

該段所使用的符號表在段表中的下標

該重定位表所作用的段在段表中的下標

SHT_SYMTAB、SHT_DNYSYM

操作系統相關

操作系統相關

other

SHN_UNDEF

0

 

可重定位表:

鏈接器在處理目標文件時,須要對目標文件中某些部位進行重定位,即代碼段和數據段中那些對絕對地址的引用的位置。這些重定位的信息都記錄在ELF文件表里面,對於每個須要重定位的代碼段和數據段,都會有一個相應的重定位表,例如 .rel.text 表對應.text段。也就是說,重定位表記錄了須要被重定位的地址都在相應段的哪些地方。比如.rela.text就是針對.text段的重定位表,因為.text段中至少有一個絕對地址的引用,那就是對printf函數的調用。而.data沒有對絕對地址的調用

 

字符串表:

ELF文件中用到了很多字符串。比如段名,變量名等。因為字符串的長度往往是不定的,所以用固定的結構來表示比較困難。常見的做法就是把字符串集中起來存放到一個表。然后使用字符串在表中的偏移來引用字符串。

下圖展示了一個長度為 25 字節的字符串表:

 字符串引用示例:

 

常見的段名是.strtabString Table)或者是.shstrtabSection Header String Table }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM