計(jì)算機(jī)那些事(3)——ELF文件結(jié)構(gòu)

原文鏈接

前文結(jié)尾說到編譯器編譯源代碼后生成的文件叫做目標(biāo)文件,而目標(biāo)文件經(jīng)過編譯器鏈接之后得到的就是可執(zhí)行文件。那么目標(biāo)文件到底是什么?它和可執(zhí)行文件又有什么區(qū)別?鏈接到底又做了什么呢?接下來,我們將探索一下目標(biāo)文件的本質(zhì)。

目標(biāo)文件的格式

目前,PC平臺(tái)流行的 可執(zhí)行文件格式(Executable) 主要包含如下兩種,它們都是 COFF(Common File Format) 格式的變種。

  • Windows下的 PE(Portable Executable)
  • Linux下的 ELF(Executable Linkable Format)

目標(biāo)文件就是源代碼經(jīng)過編譯后但未進(jìn)行連接的那些中間文件(Windows的.obj和Linux的.o),它與可執(zhí)行文件的格式非常相似,所以一般跟可執(zhí)行文件格式一起采用同一種格式存儲(chǔ)。在Windows下采用PE-COFF文件格式;Linux下采用ELF文件格式。

事實(shí)上,除了可執(zhí)行文件外,動(dòng)態(tài)鏈接庫(DDL,Dynamic Linking Library)、靜態(tài)鏈接庫(Static Linking Library) 均采用可執(zhí)行文件格式存儲(chǔ)。它們在Window下均按照PE-COFF格式存儲(chǔ);Linux下均按照ELF格式存儲(chǔ)。只是文件名后綴不同而已。

  • 動(dòng)態(tài)鏈接庫:Windows的.dll、Linux的.so
  • 靜態(tài)鏈接庫:Windows的.lib、Linux的.a

下面,我們將以ELF文件為例進(jìn)行介紹。

ELF文件結(jié)構(gòu)

image

注意:段(Segment)與節(jié)(Section)的區(qū)別。很多地方對兩者有所混淆。段是程序執(zhí)行的必要組成,當(dāng)多個(gè)目標(biāo)文件鏈接成一個(gè)可執(zhí)行文件時(shí),會(huì)將相同權(quán)限的節(jié)合并到一個(gè)段中。相比而言,節(jié)的粒度更小。

如圖所示,為ELF文件的基本結(jié)構(gòu),其主要由四部分組成:

  • ELF Header
  • ELF Program Header Table (或稱Program Headers、程序頭)
  • ELF Section Header Table (或稱Section Headers、節(jié)頭表)
  • ELF Sections

從圖中,我們就能看出它們各自的數(shù)據(jù)結(jié)構(gòu)以及相互之間的索引關(guān)系。下面我們依次進(jìn)行介紹。


ELF Header

我們可以使用readelf工具來查看ELF Header。

$ readelf -h hello.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:          672 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         13
  Section header string table index: 10
成員 含義
e_ident Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little end
Version: 1(current)
OS/ABI: UNIX - System V
ABI Version: 0
e_type Type: REL (Relocatable file)
ELF文件類型
e_machine Machine: Advanced Micro Devices X86-64
ELF文件的CPI平臺(tái)屬性
e_version Version: 0x1
ELF版本號(hào)。一般為常數(shù)1
e_entry Entry point address: 0x0
入口地址,規(guī)定ELF程序的入口虛擬地址,操作系統(tǒng)在加載完該程序后從這個(gè)地址開始執(zhí)行進(jìn)程的指令??芍囟ㄎ恢噶钜话銢]有入口地址,則該值為0
e_phoff Start of program headers: 0(bytes into file)
e_shoff Start of section headers: 672 (bytes into file)
Section Header Table 在文件中的偏移
e_word Flags: 0x0
ELF標(biāo)志位,用來標(biāo)識(shí)一些ELF文件平臺(tái)相關(guān)的屬性。
e_ehsize Size of this header: 64 (bytes)
ELF Header本身的大小
e_phentsize Size of program headers: 0 (bytes)
e_phnum Number of program headers: 0
e_shentsize Size of section headers: 64 (bytes)
單個(gè)Section Header大小
e_shnum Number of section headers: 13
Section Header的數(shù)量
e_shstrndx Section header string table index: 10
Section Header字符串表在Section Header Table中的索引

ELF文件結(jié)構(gòu)示意圖中定義的Elf_Ehdr的各個(gè)成員的含義與readelf具有對應(yīng)關(guān)系。如下表所示:

成員 含義
e_ident Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little end
Version: 1(current)
OS/ABI: UNIX - System V
ABI Version: 0
e_type Type: REL (Relocatable file)
ELF文件類型
e_machine Machine: Advanced Micro Devices X86-64
ELF文件的CPI平臺(tái)屬性
e_version Version: 0x1
ELF版本號(hào)。一般為常數(shù)1
e_entry Entry point address: 0x0
入口地址,規(guī)定ELF程序的入口虛擬地址,操作系統(tǒng)在加載完該程序后從這個(gè)地址開始執(zhí)行進(jìn)程的指令??芍囟ㄎ恢噶钜话銢]有入口地址,則該值為0
e_phoff Start of program headers: 0(bytes into file)
e_shoff Start of section headers: 672 (bytes into file)
Section Header Table 在文件中的偏移
e_word Flags: 0x0
ELF標(biāo)志位,用來標(biāo)識(shí)一些ELF文件平臺(tái)相關(guān)的屬性。
e_ehsize Size of this header: 64 (bytes)
ELF Header本身的大小
e_phentsize Size of program headers: 0 (bytes)
e_phnum Number of program headers: 0
e_shentsize Size of section headers: 64 (bytes)
單個(gè)Section Header大小
e_shnum Number of section headers: 13
Section Header的數(shù)量
e_shstrndx Section header string table index: 10
Section Header字符串表在Section Header Table中的索引

ELF魔數(shù)

每種可執(zhí)行文件的格式的開頭幾個(gè)字節(jié)都是很特殊的,特別是開頭4個(gè)字節(jié),通常被稱為魔數(shù)(Magic Number)。通過對魔數(shù)的判斷可以確定文件的格式和類型。如:ELF的可執(zhí)行文件格式的頭4個(gè)字節(jié)為0x7F、e、l、f;Java的可執(zhí)行文件格式的頭4個(gè)字節(jié)為ca、f、e;如果被執(zhí)行的是Shell腳本或perl、python等解釋型語言的腳本,那么它的第一行往往是#!/bin/sh#!/usr/bin/perl#!/usr/bin/python,此時(shí)前兩個(gè)字節(jié)#!就構(gòu)成了魔數(shù),系統(tǒng)一旦判斷到這兩個(gè)字節(jié),就對后面的字符串進(jìn)行解析,以確定具體的解釋程序路徑。

ELF文件類型

ELF文件主要有三種類型,可以通過ELF Header中的e_type成員進(jìn)行區(qū)分。

  • 可重定位文件(Relocatable File)ETL_REL。一般為.o文件??梢员绘溄映煽蓤?zhí)行文件或共享目標(biāo)文件。靜態(tài)鏈接庫屬于可重定位文件。
  • 可執(zhí)行文件(Executable File)ET_EXEC。可以直接執(zhí)行的程序。
  • 共享目標(biāo)文件(Shared Object File)ET_DYN。一般為.so文件。有兩種情況可以使用。
    • 鏈接器將其與其他可重定位文件、共享目標(biāo)文件鏈接成新的目標(biāo)文件;
    • 動(dòng)態(tài)鏈接器將其與其他共享目標(biāo)文件、結(jié)合一個(gè)可執(zhí)行文件,創(chuàng)建進(jìn)程映像。
image

ELF Section Header Table

ELF 節(jié)頭表是一個(gè)節(jié)頭數(shù)組。每一個(gè)節(jié)頭都描述了其所對應(yīng)的節(jié)的信息,如節(jié)名、節(jié)大小、在文件中的偏移、讀寫權(quán)限等。編譯器、鏈接器、裝載器都是通過節(jié)頭表來定位和訪問各個(gè)節(jié)的屬性的。

我們可以使用readelf工具來查看節(jié)頭表。

$ readelf -S hello.o

There are 13 section headers, starting at offset 0x2a0:

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
       0000000000000015  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  000001f0
       0000000000000030  0000000000000018   I      11     1     8
  [ 3] .data             PROGBITS         0000000000000000  00000055
       0000000000000000  0000000000000000  WA       0     0     1
  [ 4] .bss              NOBITS           0000000000000000  00000055
       0000000000000000  0000000000000000  WA       0     0     1
  [ 5] .rodata           PROGBITS         0000000000000000  00000055
       000000000000000d  0000000000000000   A       0     0     1
  [ 6] .comment          PROGBITS         0000000000000000  00000062
       0000000000000035  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  00000097
       0000000000000000  0000000000000000           0     0     1
  [ 8] .eh_frame         PROGBITS         0000000000000000  00000098
       0000000000000038  0000000000000000   A       0     0     8
  [ 9] .rela.eh_frame    RELA             0000000000000000  00000220
       0000000000000018  0000000000000018   I      11     8     8
  [10] .shstrtab         STRTAB           0000000000000000  00000238
       0000000000000061  0000000000000000           0     0     1
  [11] .symtab           SYMTAB           0000000000000000  000000d0
       0000000000000108  0000000000000018          12     9     8
  [12] .strtab           STRTAB           0000000000000000  000001d8
       0000000000000013  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)

ELF文件結(jié)構(gòu)示意圖中定義的Elf_Shdr的各個(gè)成員的含義與readelf具有對應(yīng)關(guān)系。如下表所示:

成員 含義
sh_name 節(jié)名
節(jié)名是一個(gè)字符串,保存在一個(gè)名為.shstrtab的字符串表(可通過Section Header索引到)。sh_name的值實(shí)際上是其節(jié)名字符串在.shstrtab中的偏移值
sh_type 節(jié)類型
sh_flags 節(jié)標(biāo)志位
sh_addr 節(jié)地址:節(jié)的虛擬地址
如果該節(jié)可以被加載,則sh_addr為該節(jié)被加載后在進(jìn)程地址空間中的虛擬地址;否則sh_addr為0
sh_offset 節(jié)偏移
如果該節(jié)存在于文件中,則表示該節(jié)在文件中的偏移;否則無意義,如sh_offset對于BSS 節(jié)來說是沒有意義的
sh_size 節(jié)大小
sh_link、sh_info 節(jié)鏈接信息
sh_addralign 節(jié)地址對齊方式
sh_entsize 節(jié)項(xiàng)大小
有些節(jié)包含了一些固定大小的項(xiàng),如符號(hào)表,其包含的每個(gè)符號(hào)所在的大小都一樣的,對于這種節(jié),sh_entsize表示每個(gè)項(xiàng)的大小。如果為0,則表示該節(jié)不包含固定大小的項(xiàng)。

節(jié)類型(sh_type)

節(jié)名是一個(gè)字符串,只是在鏈接和編譯過程中有意義,但它并不能真正地表示節(jié)的類型。對于編譯器和鏈接器來說,主要決定節(jié)的屬性是節(jié)的類型(sh_type)和節(jié)的標(biāo)志位(sh_flags)。

節(jié)的類型相關(guān)常量以SHT_開頭,上述readelf -S命令執(zhí)行的結(jié)果省略了該前綴。常見的節(jié)類型如下表所示:

常量 含義
SHT_NULL 0 無效節(jié)
SHT_PROGBITS 1 程序節(jié)。代碼節(jié)、數(shù)據(jù)節(jié)都是這種類型。
SHT_SYMTAB 2 符號(hào)表
SHT_STRTAB 3 字符串表
SHT_RELA 4 重定位表。該節(jié)包含了重定位信息。
SHT_HASH 5 符號(hào)表的哈希表
SHT_DYNAMIC 6 動(dòng)態(tài)鏈接信息
SHT_NOTE 7 提示性信息
SHT_NOBITS 8 表示該節(jié)在文件中沒有內(nèi)容。如.bss節(jié)
SHT_REL 9 該節(jié)包含了重定位信息
SHT_SHLIB 10 保留
SHT_DNYSYM 11 動(dòng)態(tài)鏈接的符號(hào)表

節(jié)標(biāo)志位(sh_flag)

節(jié)標(biāo)志位表示該節(jié)在進(jìn)程虛擬地址空間中的屬性。如是否可寫、是否可執(zhí)行等。相關(guān)常量以SHF_開頭。常見的節(jié)標(biāo)志位如下表所示:

常量 含義
SHF_WRITE 1 表示該節(jié)在進(jìn)程空間中可寫
SHF_ALLOC 2 表示該節(jié)在進(jìn)程空間中需要分配空間。有些包含指示或控制信息的節(jié)不需要在進(jìn)程空間中分配空間,就不會(huì)有這個(gè)標(biāo)志。
SHF_EXECINSTR 4 表示該節(jié)在進(jìn)程空間中可以被執(zhí)行

節(jié)鏈接信息(sh_link、sh_info)

如果節(jié)的類型是與鏈接相關(guān)的(無論是動(dòng)態(tài)鏈接還是靜態(tài)鏈接),如重定位表、符號(hào)表、等,則sh_linksh_info兩個(gè)成員所包含的意義如下所示。其他類型的節(jié),這兩個(gè)成員沒有意義。

sh_type sh_link sh_info
SHT_DYNAMIC 該節(jié)所使用的字符串表在節(jié)頭表中的下標(biāo) 0
SHT_HASH 該節(jié)所使用的符號(hào)表在節(jié)頭表中的下標(biāo) 0
SHT_REL 該節(jié)所使用的相應(yīng)符號(hào)表在節(jié)頭表中的下標(biāo) 該重定位表所作用的節(jié)在節(jié)頭表中的下標(biāo)
SHT_RELA 該節(jié)所使用的相應(yīng)符號(hào)表在節(jié)頭表中的下標(biāo) 該重定位表所作用的節(jié)在節(jié)頭表中的下標(biāo)
SHT_SYMTAB 操作系統(tǒng)相關(guān) 操作系統(tǒng)相關(guān)
SHT_DYNSYM 操作系統(tǒng)相關(guān) 操作系統(tǒng)相關(guān)
other SHN_UNDEF 0

ELF Sections

節(jié)的分類

上述ELF Section Header Table部分已經(jīng)簡單介紹了節(jié)類型。接下來我們來介紹詳細(xì)一些比較重要的節(jié)。

.text節(jié)

.text節(jié)是保存了程序代碼指令的代碼節(jié)。一段可執(zhí)行程序,如果存在Phdr,則.text節(jié)就會(huì)存在于text段中。由于.text節(jié)保存了程序代碼,所以節(jié)類型為SHT_PROGBITS。

.rodata節(jié)

rodata節(jié)保存了只讀的數(shù)據(jù),如一行C語言代碼中的字符串。由于.rodata節(jié)是只讀的,所以只能存在于一個(gè)可執(zhí)行文件的只讀段中。因此,只能在text段(不是data段)中找到.rodata節(jié)。由于.rodata節(jié)是只讀的,所以節(jié)類型為SHT_PROGBITS。

.plt節(jié)(過程鏈接表)

.plt節(jié)也稱為過程鏈接表(Procedure Linkage Table),其包含了動(dòng)態(tài)鏈接器調(diào)用從共享庫導(dǎo)入的函數(shù)所必需的相關(guān)代碼。由于.plt節(jié)保存了代碼,所以節(jié)類型為SHT_PROGBITS。

.data節(jié)

.data節(jié)存在于data段中,其保存了初始化的全局變量等數(shù)據(jù)。由于.data節(jié)保存了程序的變量數(shù)據(jù),所以節(jié)類型為SHT_PROGBITS。

.bss節(jié)

.bss節(jié)存在于data段中,占用空間不超過4字節(jié),僅表示這個(gè)節(jié)本省的空間。.bss節(jié)保存了未進(jìn)行初始化的全局?jǐn)?shù)據(jù)。程序加載時(shí)數(shù)據(jù)被初始化為0,在程序執(zhí)行期間可以進(jìn)行賦值。由于.bss節(jié)未保存實(shí)際的數(shù)據(jù),所以節(jié)類型為SHT_NOBITS

.got.plt節(jié)(全局偏移表-過程鏈接表)

.got節(jié)保存了全局偏移表。.got節(jié)和.plt節(jié)一起提供了對導(dǎo)入的共享庫函數(shù)的訪問入口,由動(dòng)態(tài)鏈接器在運(yùn)行時(shí)進(jìn)行修改。由于.got.plt節(jié)與程序執(zhí)行有關(guān),所以節(jié)類型為SHT_PROGBITS。

.dynsym節(jié)(動(dòng)態(tài)鏈接符號(hào)表)

.dynsym節(jié)保存在text段中。其保存了從共享庫導(dǎo)入的動(dòng)態(tài)符號(hào)表。節(jié)類型為SHT_DYNSYM。

.dynstr節(jié)(動(dòng)態(tài)鏈接字符串表)

.dynstr保存了動(dòng)態(tài)鏈接字符串表,表中存放了一系列字符串,這些字符串代表了符號(hào)名稱,以空字符作為終止符。

.rel.*節(jié)(重定位表)

重定位表保存了重定位相關(guān)的信息,這些信息描述了如何在鏈接或運(yùn)行時(shí),對ELF目標(biāo)文件的某部分或者進(jìn)程鏡像進(jìn)行補(bǔ)充或修改。由于重定位表保存了重定位相關(guān)的數(shù)據(jù),所以節(jié)類型為SHT_REL

.hash節(jié)

.hash節(jié)也稱為.gnu.hash,其保存了一個(gè)用于查找符號(hào)的散列表。

.symtab節(jié)(符號(hào)表)

.symtab節(jié)是一個(gè)ElfN_Sym的數(shù)組,保存了符號(hào)信息。節(jié)類型為SHT_SYMTAB。

.strtab節(jié)(字符串表)

.strtab節(jié)保存的是符號(hào)字符串表,表中的內(nèi)容會(huì)被.symtabElfN_Sym結(jié)構(gòu)中的st_name引用。節(jié)類型為SHT_STRTAB。

.ctors節(jié)和.dtors節(jié)

.ctors構(gòu)造器)節(jié)和.dtors析構(gòu)器)節(jié)分別保存了指向構(gòu)造函數(shù)和析構(gòu)函數(shù)的函數(shù)指針,構(gòu)造函數(shù)是在main函數(shù)執(zhí)行之前需要執(zhí)行的代碼;析構(gòu)函數(shù)是在main函數(shù)之后需要執(zhí)行的代碼。

符號(hào)表

節(jié)的分類中我們介紹了.dynsym節(jié)和.symtab節(jié),兩者都是符號(hào)表。那么它們到底有什么區(qū)別呢?存在什么關(guān)系呢?

符號(hào)是對某些類型的數(shù)據(jù)或代碼(如全局變量或函數(shù))的符號(hào)引用,函數(shù)名或變量名就是符號(hào)名。例如,printf()函數(shù)會(huì)在動(dòng)態(tài)鏈接符號(hào)表.dynsym中存有一個(gè)指向該函數(shù)的符號(hào)項(xiàng)(以Elf_Sym數(shù)據(jù)結(jié)構(gòu)表示)。在大多數(shù)共享庫和動(dòng)態(tài)鏈接可執(zhí)行文件中,存在兩個(gè)符號(hào)表。即.dynsym.symtab

.dynsym保存了引用來自外部文件符號(hào)的全局符號(hào)。如printf庫函數(shù)。.dynsym保存的符號(hào)是.symtab所保存符合的子集,.symtab中還保存了可執(zhí)行文件的本地符號(hào)。如全局變量,代碼中定義的本地函數(shù)等。

既然.dynsym.symtab的子集,那為何要同時(shí)存在兩個(gè)符號(hào)表呢?

通過readelf -S命令可以查看可執(zhí)行文件的輸出,一部分節(jié)標(biāo)志位(sh_flags)被標(biāo)記為了A(ALLOC)、WA(WRITE/ALLOC)、AX(ALLOC/EXEC)。其中,.dynsym被標(biāo)記為ALLOC,而.symtab則沒有標(biāo)記。

ALLOC表示有該標(biāo)記的節(jié)會(huì)在運(yùn)行時(shí)分配并裝載進(jìn)入內(nèi)存,而.symtab不是在運(yùn)行時(shí)必需的,因此不會(huì)被裝載到內(nèi)存中。.dynsym保存的符號(hào)只能在運(yùn)行時(shí)被解析,因此是運(yùn)行時(shí)動(dòng)態(tài)鏈接器所需的唯一符號(hào)。.dynsym對于動(dòng)態(tài)鏈接可執(zhí)行文件的執(zhí)行是必需的,而.symtab只是用來進(jìn)行調(diào)試和鏈接的。

image

上圖所示為通過符號(hào)表索引字符串表的示意圖。符號(hào)表中的每一項(xiàng)都是一個(gè)Elf_Sym結(jié)構(gòu),對應(yīng)可以在字符串表中索引得到一個(gè)字符串。該數(shù)據(jù)結(jié)構(gòu)中成員的含義如下表所示:

成員 含義
st_name 符號(hào)名。該值為該符號(hào)名在字符串表中的偏移地址。
st_value 符號(hào)對應(yīng)的值。存放符號(hào)的值(可能是地址或位置偏移量)。
st_size 符號(hào)的大小。
st_other 0
st_shndx 符號(hào)所在的節(jié)
st_info 符號(hào)類型及綁定屬性

使用readelf工具我們也能夠看到符號(hào)表的相關(guān)信息。

$ readelf -s hello.o

Symbol table '.symtab' contains 11 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6
     9: 0000000000000000    21 FUNC    GLOBAL DEFAULT    1 main
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts

字符串表

類似于符號(hào)表,在大多數(shù)共享庫和動(dòng)態(tài)鏈接可執(zhí)行文件中,也存在兩個(gè)字符串表。即.dynstr.strtab,分別對應(yīng)于.dynsymsymtab。此外,還有一個(gè).shstrtab的節(jié)頭字符串表,用于保存節(jié)頭表中用到的字符串,可通過sh_name進(jìn)行索引。

ELF文件中所有字符表的結(jié)構(gòu)基本一致,如上圖所示。

重定位表

重定位就是將符號(hào)定義和符號(hào)引用進(jìn)行連接的過程??芍囟ㄎ晃募枰枋鋈绾涡薷墓?jié)內(nèi)容的相關(guān)信息,從而使可執(zhí)行文件和共享目標(biāo)文件能夠保存進(jìn)程的程序鏡像所需要的正確信息。

重定位表是進(jìn)行重定位的重要依據(jù)。我們可以使用objdump工具查看目標(biāo)文件的重定位表:

$ objdump -r hello.o


hello.o:     file format elf64-x86-64

RELOCATION RECORDS FOR [.text]:
OFFSET           TYPE              VALUE
0000000000000005 R_X86_64_32       .rodata
000000000000000a R_X86_64_PC32     puts-0x0000000000000004


RELOCATION RECORDS FOR [.eh_frame]:
OFFSET           TYPE              VALUE
0000000000000020 R_X86_64_PC32     .text

重定位表是一個(gè)Elf_Rel類型的數(shù)組結(jié)構(gòu),每一項(xiàng)對應(yīng)一個(gè)需要進(jìn)行重定位的項(xiàng)。
其成員含義如下表所示:

成員 含義
r_offset 重定位入口的偏移。
對于可重定位文件來說,這個(gè)值是該重定位入口所要修正的位置的第一個(gè)字節(jié)相對于節(jié)起始的偏移
對于可執(zhí)行文件或共享對象文件來說,這個(gè)值是該重定位入口所要修正的位置的第一個(gè)字節(jié)的虛擬地址
r_info 重定位入口的類型和符號(hào)
因?yàn)椴煌幚砥鞯闹噶钕到y(tǒng)不一樣,所以重定位所要修正的指令地址格式也不一樣。每種處理器都有自己的一套重定位入口的類型。
對于可執(zhí)行文件和共享目標(biāo)文件來說,它們的重定位入口是動(dòng)態(tài)鏈接類型的。

重定位是目標(biāo)文件鏈接成為可執(zhí)行文件的關(guān)鍵。我們將在后面的進(jìn)行介紹。

參考

  1. Executable and Linkable Format (ELF)
  2. 《Linux 二進(jìn)制分析》
  3. 《程序員的自我修養(yǎng)——鏈接、裝載與庫》
  4. Executable and Linkable Format
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容