ELF文件格式

来源:互联网 发布:linux怎么定时任务 编辑:程序博客网 时间:2024/04/28 04:32
ELF简介

ELF 是由USL(UNIX System Laboratories)作为ABI(Application Binary Interface) 的一部分发布的。ELF 为开发者提供了一整套能应用于多个操作系统的二进制接口定义,在软件移植时大大减少了需要重新编码和编译的地方。ELF 同时支持二进制文件的三种用途,并根据这三种用途分别定义了相应的ELF 文件类型:可重定位文件(relocatable files),可执行文件(executable files),共享目标文件(shared object files) 。

1.  可重定位文件包含了适合用来链接其他目标文件的代码和数据,从而创建出可执行或可共享的目标文件;

2.  可执行文件包含了用于执行的程序,该文件规定了exec如何创建一个程序的进程映像;

3.  共享目标文件包含了用来在两个上下文之间链接的代码和数据。首先,链接器ld将该文件和其他的可重组文件或可共享目标文件进行处理后,创建出新目标文件,其次,动态链接器将该新目标文件与可执行文件或共享对象组合,来共同创建一个进程映像。

在实际使用过程中,以上三类文件其实只用于两种目的:程序连接和程序执行。可重定位文件和共享目标文件用于程序连接,可执行文件用于程序执行。

ELF文件格式

经过汇编器以及链接器创建成的目标文件,其实是在处理器上可直接执行的程序的二进制代表。本章主要叙述了ELF文件格式以及其如何用来构建程序,同时也叙述了目标文件的几个组成部分,集中在程序执行所必须的信息上。

目标文件参与程序的链接(创建一个程序)和程序的执行(运行一个程序)。目标文件格式提供了一个用并行的视角看待文件内容的有效方法,在目标文件的具体活动中,反映出文件不同的用途。

图2-1显示了一个目标文件的组织结构。

链接视图             执行视图

ELF头部

ELF头部

程序头部表(可选)

程序头部表

节区1

段1

 

···

节区n

段2

 

···

···

···

节区头部表

节区头部表(可选)

图2-1 目标文件格式

在两种视图中,ELF头部(ELF Header)都位于文件的开始部分,位置固定,保存了路线图(road map),描述了该文件的组织情况。

在链接视图中,程序头部表(program header table)为可选。从程序的执行来看文件格式,程序头部表告诉系统如何来创建一个进程的内存映象。被用来建立进程映象(执行一个程序)的文件必须要有一个程序头部表,可重定位文件不需要这个头部表。

节区(section)保存着目标文件的信息,从链接视图看,包括指令,数据,符号表和重定位信息等等。其中包含的特殊节区会在“2.3特殊节区”章节中详细叙述。从执行视图看,一个段通常包含几个节区,同样保存着指令,数据,符号表和重定位等信息。

节区头部表(section header table)包含了描述节区的信息。每个节区在这个表中有一个入口,该入口给出了节区的名字,大小等等信息。链接过程中的文件必须有一个节区头部表,而在执行视图中这个节区头部表为可选。

目标文件格式支持8位字节/32位体系结构。不过这种格式是可以扩展的,因此,目标文件以某些机器独立的格式来表达某些控制数据,使得能够以一种的公共的方式来识别和解释其内容。目标文件中的其它数据使用目标处理器的编码结构,而不管文件在何种机器上创建。

目标文件ELF中常用的数据类型如表2-1所示。

表2-1 ELF中常用数据格式

名称

大小

对齐

说明

Elf32_Addr

4

4

无符号程序地址

Elf32_Half

2

2

无符号中等整数

Elf32_Off

4

4

有符号文件偏移

 Elf32_Sword

4

4

有符号大整数

Elf32_Word

4

4

无符号大整数

unsigned char

1

1

无符号小整数

 

所有目标文件格式定义的数据结构是自然大小(natural size)。如果需要,数据结构中明确的包含了确保4字节对齐的填充字段,来使结构大小是4的倍数。数据从文件的开始也有适当的对齐。例如,一个包含了Elf32_Addr成员的结构将会在文件内对齐到4字节的边界上。因为移植性的原因,ELF不使用位字段。

1.2.1 ELF头部

ELF头部位于文件的开始处,用于描述整个文件的组织情况和结构。

用于程序连接的文件中有节区和节区头部表,节区中包含指令、数据、符号表、重定位信息等,节区头部表是节区的描述信息,也是节区信息的集合。

用于程序执行的文件中有段和程序头部表,一个段通常包含一个或多个节区的内容,程序头部(program header) 即段头部( segment header) 是段的叙述信息,程序头部表是程序头部的集合。

ELF头部结构数据组成如表2-2所示。

 

表2-2 ELF头部结构组成

名称

说明

unsigned char

e_ident[EI_NIDENT]

魔数和相关信息

Elf32_Half

e_type

目标文件类型

Elf32_Half

e_machine

硬件体系

Elf32_Word

e_version

目标文件版本

Elf32_Addr

e_entry

程序进入点

Elf32_Off

e_phoff

程序头部偏移量

Elf32_Off

e_shoff

节头部偏移量

Elf32_Word

e_flags

处理器特定标志

Elf32_Half

e_ehsize

ELF头部长度

Elf32_Half

e_phentsize

程序头部中一个条目的长度

Elf32_Half

e_phnum

程序头部条目个数

Elf32_Half

e_shentsize

节头部中一个条目的长度

Elf32_Half

e_shnum

节头部条目个数

Elf32_Half

e_shstrndx

节头部字符表索引

1.  e_ident

这个字段标示该文件为一个目标文件,提供了一个机器无关的数据,解释文件的内容。ELF头部e_ident[]标识索引结构成员如表2-3所示。

表 2-3 e_ident[]标识索引

名称

说明

EI_MAG0

0

文件识别

EI_MAG1

1

文件识别

EI_MAG2

2

文件识别

EI_MAG3

3

文件识别

EI_CLASS

4

文件类

EI_DATA

5

数据编码

EI_VERSION

6

文件版本

EI_PAD

7

补齐字节开始处

EI_NIDENT

16

e_ident[]大小

通过索引访问字节,以下的变量被定义。

•   EI_MAG0 to EI_MAG3:文件的前4个字节保存着一个魔术数(magic number),用来确定该文件是否为ELF的目标文件。ELF文件类型取值如表2-4所示。

表2-4 EI_MAGO-EI_MAGO3数据格式

名称

说明

ELFMAG0

0x7f

e_ident[EI_MAG0]

ELFMAG1

'E'

e_ident[EI_MAG1]

ELFMAG2

'L'

e_ident[EI_MAG2]

ELFMAG3

'F'

e_ident[EI_MAG3]

•   EI_CLASS:接下来的字节是e_ident[EI_CLASS],用来确定文件的类型,值的意思如表2-5所示。

表2-5 EI_CLASS数据格式

名称

说明

ELFCLASSNONE

0

非法类别

ELFCLASS32

1

32位目标

ELFCLASS64

2

64位目标

文件格式被设计成在不同大小机器中可移植的。类型ELFCLASS32支持虚拟地址空间最大可达4GB的机器;类型ELFCLASS64为64位体系的机器保留。如果需要,其他类型将被定义,会有不同的类型和不同大小的数据尺寸。

•   EI_DATA:字节e_ident[EI_DATA]指定了在目标文件中特定处理器数据的编码方式。EI_DATA数据组成如表2-6所示。

表2-6 EI_DATA数据组成

名称

说明

ELFDATANONE

0

无效的数据编码

ELFDATA2LSB

1

高位在前

ELFDATA2MSB

2

地位在前

 ELFDATA2LSB编码如图2-2所示。低字节占用低地址空间。

图片

 

 

 

 

图2-2 ELFDATA2LSB数据编码

ELFDATA2MSB编码如图2-3所示。高字节占用低地址空间。

 

图片

 

 

 

 

图2-3 ELFDATA2MSB数据编码

•   EI_VERSION:字节e_ident[EI_VERSION]表明了ELF头的版本号。现在这个变量的值设为EV_CURRENT,作为ELF头部e_version字段的解释。

•   EI_PAD:该变量标识了在e_ident中开始的未使用的字节。那些字节保留并被设置为0;程序把它们从object 文件中读出但应该忽略。假如当前未被使用的字节有了新的定义,EI_PAD变量将来会被改变。

2.  e_type

该成员确定该目标文件的类型,取值如表2-7所示

表2-7 e_type数据结构

名称

说明

ET_NONE

0

未知目标文件格式

ET_REL

1

可重定位文件

ET_EXEC

2

可执行文件

ET_DYN

3

共享目标文件

ET_CORE

4

Core文件(转储格式)

ET_LOPROC

0xff00

特定处理器文件

ET_HIPROC

0xffff

特定处理器文件

 

虽然CORE的文件内容未被指明,但类型ET_CORE是保留的。值从 ET_LOPROC 到 ET_HIPROC(包括ET_HIPROC)是为特殊的处理器保留的。如有需要,保留的变量将用在新的目标文件类型上。

3.  e_machine

该成员变量指出了运行该程序需要的体系结构。e_machine数据结构如表2-8所示。

表2-8 e_machine数据结构

名称

说明

EM_NONE

0

未指定

EM_M32

1

AT&T WE 32100

EM_SPARC

2

SPARC

EM_386

3

Intel 80386

EM_68K

4

Motorola 68000

EM_88K

5

Motorola 88000

EM_860

7

Intel 80860

EM_MIPS

8

MIPS RS3000

注意:32位Intel体系结构内,ELF头部里的e_machine成员,其值必须为EM_386,e_ident[EI_CLASS]必须为ELFCLASS32,e_ident[EI_

DATA]必须为ELFDATA2LSB。

4.  e_version

这个成员确定目标文件的版本。值1表示原来的文件格式,创建新版本就用>1的数。EV_CURRENT值(表中给出为1)如果需要将指向当前的版本号,目标文件版本信息如表2-9所示。

表2-9 目标文件版本信息

名称

含义

EV_NONE

0

非法版本

EV_CURRENT

1

当前版本

5. e_entry

    该成员是系统第一个传输控制的虚拟地址,在该地址启动进程。假如文件没有如何关联的入口点,该成员就保持为0。

6.  e_phoff

    该成员保持着程序头部表在文件中的偏移量(以字节计数)。假如该文件没有程序头部表的的话,该成员就保持为0。

7. e_shoff

    该成员保持着节区头部表在文件中的偏移量(以字节计数)。假如该文件没有节区头部表的的话,该成员就保持为0。

8.  e_flags

    该成员保存着相关文件的特定处理器标志。flag的名字来自于EF_<machine>_<flag>。

9.  e_ehsize

    该成员保存着ELF头部大小(以字节计数)。

10. e_phentsize

    该成员保存着在文件的程序头部表中一个入口的大小(以字节计数)。所有的入口都是同样的大小。

11. e_phnum

    该成员保存着在程序头部表中入口的个数。因此,e_phentsize和e_phnum的乘积就是表的字节数.假如没有程序头部表,e_phnum变量为0。

12. e_shentsize

    该成员保存着节区头不的大小(以字节计数)。一个节区头部是在节区头部表的一个入口;所有的入口都是同样的大小。

13. e_shnum

    该成员保存着在节区头部表中的入口数目。因此,e_shentsize和e_shnum的乘积就是节区头部表的大小(以字节计数)。假如文件没有section头表,e_shnum值为0。

14. e_shstrndx

    该成员保存着跟字符表相关入口的节区头部表索引。假如文件中没有字符表,该变量值为SHN_UNDEF。

1.2.2 节区

ELF 头部中,e_shoff成员给出从文件头到节区头部表的偏移字节数;e_shnum给出了节区头部表中包含多少个入口;e_shentsize 给出了每个入口的大小。从这些信息中可以确切地定位节区的具体位置、长度。

节区头部表中比较特殊的几个下标如表2-10所示:

表2-10节区头部表格中的特殊下标

名称

取值

说明

SHN_UNDEF

0

标记未定义的,缺失的,不相关的的节区引用

SHN_LORESERVE

0xff00

保留索引的下界

SHN_LOPROC

0xff00

保留给处理器特殊的语义

SHN_HIPROC

0xff1f

保留给处理器特殊的语义

SHN_ABS

0xfff1

包含对应引用量的绝对取值,不会被重定位影响

SHN_COMMON

0xfff2

相对于此节区定义的符号是公共符号

SHN_HIRESERVE

0xffff

保留索引的上界

   SHN_UNDEF:该值表明没有定义,缺少,不相关的或者其他涉及到的无意义的节区。

注意: 虽然索引0保留作为未定义的值,节区头部表包含了一个索引0的入口。因此,假如ELF头部描述一个文件的节区头部表中有6个节区入口,e_shnum的值应该是从0到5。最初的入口的内容会在这个节区中被指定。

   SHN_LORESERVE:该值指定保留的索引范围的最小值。

   SHN_LOPROC  SHN_HIPROC:该值包含了特定处理器语意的保留范围。

   SHN_ABS:该变量是相对于相应参考的绝对地址。

   SHN_COMMON:该节区的标号是一个公共(common)的标号,就象FORTRAN COMMON或者不允许的扩展变量。

   SHN_HIRESERVE:该值指定保留的索引范围的上限。系统保留的索引值是从SHN_LORESERVE到SHN_HIRESERVE;该变量不涉及到节区头部表。因此,节区头部表不为保留的索引值包含入口。

目标文件的节区满足以下条件:

•   每个在目标文件中的节区都有自己的一个节区头部来叙述它。节区头可能存在但节区可以不存在。

   每个节区在文件中都占有一个连续顺序的空间(但可能为空)。

•   文件中的节区不可能重复。文件中没有一个字节既在这个节区中又在另外的一个节区中。

•   目标文件可以有"非活动的"空间。不同的头部和节区可以不覆盖到目标文件中的每个字节。"非活动"数据内容是未指定的。

一个节区头部就是一个Elf32_Shdr结构的数组,数据组成如表2-11所示。

表2-11 节区头部数据组成

名称

说明

Elf32_Word

sh_name

给出节区名称

Elf32_Word

sh_type

为节区的内容和语义进行分类

Elf32_Word

sh_flags

节区支持1位形式的标志,这标志叙述多种属性

Elf32_Addr

sh_addr

给出节区的第一个字节应处的位置

Elf32_Off

sh_offset

给出节区的第一个字节与文件头之间的偏移

Elf32_Word

sh_size

此成员给出节区的长度(字节数)

Elf32_Word

sh_link

此成员给出节区头部表索引链接

Elf32_Word

sh_info

此成员给出附加信息,其解释依赖于节区类型

Elf32_Word

sh_addralign

某些节区带有地址对齐约束

Elf32_Word

sh_entsize

某些节区中包含固定大小的项目,如符号表

1.  sh_name

    该成员指定了这个节区的名字。它的值是字符表的索引。

2.  sh_type

该成员把节区按内容和意义分类。节区的类型和他们的叙述如表2-12所示。

表2-12 节区类型sh_type各字段语义

名称

说明

SHT_NULL

0

此值标志节区头部是非活动的,没有对应的节区。

SHT_PROGBITS

1

包含程序定义的信息,其格式和含义都由程序来解释。

SHT_SYMTAB

2

包含一个符号表

SHT_STRTAB

3

包含字符串表。目标文件可能包含多个字符串表节区。

SHT_RELA

4

包含重定位表项,其中可能会有补齐内容(addend)

SHT_HASH

5

包含符号哈希表。

SHT_DYNAMIC

6

此节区包含动态链接的信息

SHT_NOTE

7

此节区包含以某种方式来标记文件的信息

SHT_NOBITS

8

不占用文件中的空间,其他方面和SHT_PROGBITS 相似。

SHT_REL

9

包含重定位表项,其中没有补齐(addends)

SHT_SHLIB

10

此节区被保留,不过其语义是未规定的

SHT_DYNSYM

11

作为完整的符号表,可能包含对动态链接不必要的符号

SHT_LOPROC

0x70000000

这一段(包括两个边界),

是保留给处理器专用语义的

SHT_HIPROC

0x7fffffff

SHT_LOUSER

0x80000000

此值给出保留给应用程序的索引下界

SHT_HIUSER

0xffffffff

此值给出保留给应用程序的索引上界

3.  sh_flags

节区支持位的标记,用来叙述多个属性。一个节区头部表的sh_flags成员保存着1位标记,用来叙述节区的属性。sh_flags定义的值如表2-13所示,其他的值保留。

表2-13 节区头部的 sh_flags字段取值

名称

说明

SHF_WRITE

0x1

节区包含进程执行过程中将可写的数据

SHF_ALLOC

0x2

此节区在进程执行过程中占用内存

SHF_EXECINSTR

0x4

节区包含可执行的机器指令

SHF_MASKPROC

0xf0000000

包含于此掩码中的四位都用于处理器专用的语义

假如在sh_flags中的一个标记位被设置,该节区相应的属性也被打开。否则,该属性没有被应用。未明的属性就设为0。

4.  sh_addr

    假如该节区将出现在进程的内存映象空间里,该成员给出了一个该节区在内存中的位置。否则,该变量为0。

5.  sh_offset

  该成员变量给出了该节区的字节偏移量(从文件开始计数)。

6.  sh_size

    该成员给你了节区的字节大小。除非这个节区的类型为SHT_NOBITS,否则该节区将在文件中将占有sh_size个字节。SHT_NOBITS类型的节区可能为非0的大小,但是不占文件空间。

7.  sh_link

该成员保存了一个节区头部表的索引连接,它的解释依靠该节区的类型。

8.  sh_info

该成员保存着额外的信息,它的解释依靠该节区的类型。

在节区头部中,两个成员sh_link和sh_info的解释依靠该节区的类型,其意义如表2-14所示。

 

表2-14 sh_link和sh_info的字段解释

sh_type

sh_link

sh_info

SHT_DYNAMIC

此节区中条目所用到的字

符串表的索引

0

 

SHT_HASH

此哈希表所适用的符号表

的节区头部索引

0

SHT_REL

SHT_RELA

相关符号表的节区头部索

重定位所适用的节区的节区头

部索引

SHT_SYMTAB

SHT_DYNSYM

相关联的字符串表的节区

头部索引

最后一个局部符号(绑定 STB_

LOCAL)的符号表索引值加一

  其他

SHN_UNDEF

0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

9.  sh_addralign

    一些节区有地址对齐的约束。例如,假如一个节区保存着双字,系统就必须确定整个节区是否双字对齐。所以sh_addr的值以sh_addralign的值作为模,那么一定为0。

10. sh_entsize

一些节区保存着一张固定大小入口的表,就象符号表。对于这样一个节区来说,该成员给出了每个入口的字节大小。如果该节区没有保存着一张固定大小入口的表,该成员就为0。

值得说明的是索引为0 (SHN_UNDEF)的节区头部也是存在的,尽管此索引标记的是未定义的节区引用。这个入口保存着以下固定的信息,如表2-15所示。

表2-15 SHN_UNDEF(0)节区的内容

名称

说明

sh_name

0

名称

sh_type

SHT_NULL

非活动

sh_flags

0

标志

sh_addr

0

地址

sh_offset

0

文件偏移

sh_size

0

尺寸大小

sh_link

SHN_UNDEF

链接信息

sh_info

0

辅助信息

sh_addralign

0

对齐要求

sh_entsize

0

表项


特殊节区

不同的节区保存着程序和控制信息。特殊节区被系统使用,指示了类型和属性。特殊节区的组成如表2-16所示。

 

表2-16 特殊的节区

名称

类型

属性

  .bss

SHT_NOBITS

SHF_ALLOCSHF_WRITE

  .comment

SHT_PROGBITS

none

  .data

SHT_PROGBITS

SHF_ALLOCSHF_WRITE

  .data1

SHT_PROGBITS

SHF_ALLOCSHF_WRITE

  .debug

SHT_PROGBITS

none

  .dynamic

SHT_DYNAMIC

see below

  .dynstr

SHT_STRTAB

SHF_ALLOC

  .dynsym

SHT_DYNSYM

SHF_ALLOC

  .fini

SHT_PROGBITS

SHF_ALLOCSHF_EXECINSTR

  .got

SHT_PROGBITS

see below

  .hash

SHT_HASH

SHF_ALLOC

  .init

SHT_PROGBITS

SHF_ALLOCSHF_EXECINSTR

  .interp

SHT_PROGBITS

see below

  .line

SHT_PROGBITS

none

  .note

SHT_NOTE

none

  .plt

SHT_PROGBITS

see below

  .rel<name>

SHT_REL

see below

  .rela<name>

SHT_RELA

see below

  .rodata

SHT_PROGBITS

SHF_ALLOC

  .rodata1

SHT_PROGBITS

SHF_ALLOC

  .shstrtab

SHT_STRTAB

none

  .strtab

SHT_STRTAB

see below

  .symtab

SHT_SYMTAB

see below

  .text

SHT_PROGBITS

SHF_ALLOCSHF_EXECINSTR

1  .bss

    该节区保存着未初始化的数据,这些数据存在于程序内存映象中。通过定义,当程序开始运行,系统初始化那些数据为0。该节区不占文件空间,和它的节区类型SHT_NOBITS指示的一致。

2  .comment

     该节区保存着版本控制信息。

3  .data and .data1

    这些节区保存着初始化了的数据,那些数据存在于程序内存映象中。

4  .debug

    该节区保存着调试的信息,内容是未指明的。

5  .dynamic

    该节区保存着动态连接的信息。

6  .dynstr

该节区保存着动态连接时需要的字符串,一般情况下,名字字符串关联着符号表的入口。

7  .dynsym

    该节区保存着动态符号表,如符号表的描述。

8  .fini

    该节区保存着可执行指令,它构成了进程的终止代码。因此,当一个程序正常退出时,系统安排执行这个节区的中的代码。

9  .got

    该节区保存着全局的偏移量表。

10 .hash

    该节区保存着一个标号的哈希表。

11 .init

    该节区保存着可执行指令,它构成了进程的初始化代码。因此,当一个程序开始运行时,在main函数被调用之前(c语言称为main),系统安排执行这个节区中的代码。

12 .interp

    该节区保存了程序的解释程序(interpreter)的路径。

13 .line

    该节区包含编辑字符的行数信息,它叙述源程序与机器代码之间的对应关系。

14 .note

    该节区保存一些信息。

15 .plt

    该节区保存着过程连接表(Procedure Linkage Table)。

16 .rel<name> and .rela<name>

    这些节区保存着重定位的信息。按照惯例,<name>由重定位适用的节区来提供。因此,一个重定位的节区适用的是.text,那么该名字就为.rel.text或者是.rela.text。

17 .rodata and .rodata1

    这些节区保存着只读数据,在进程映象中构造不可写的段。

18 .shstrtab

    该节区保存着节区名称。

19 .strtab

    该节区保存着字符串,一般地,叙述名字的字符串和一个标号的入口相关联。假如文件有一个可装载的段,并且该段包括了符号字符串表,那么节区的SHF_ALLOC属性将被设置;否则不设置。

20 .symtab

    该节区保存着一个符号表,正如在这个节区里符号表的描述。

21 .text

该节区保存着程序的“text”或者说是可执行指令。

前缀是点(.)的节区名是系统保留的,尽管应用程序可以用那些保留的节区名。应用程序可以使用不带前缀的名字以避免和系统的节区冲突。

特殊节区主要包含了三个部分信息:字符串表,符号表和重定位。

1.3.1 字符串表

字符串表节区保存着以NULL终止的一系列字符,一般我们称为字符串。目标文件使用这些字符串来描绘符号和节区名。一个字符串的参考是一个字符串表节区的索引。第一个字节,即索引0,被定义保存着一个NULL字符。同样的,一个字符串表的最后一个字节保存着一个NULL字符,所有的字符串都是以NULL终止。索引0的字符串是没有名字或者说是NULL,它的解释依靠上下文。一个空的字符串是允许的;它的节区头的成员sh_size将为0。对空的字符串表来说,非0的索引是没有用的。

一个节区头的 sh_name 成员保存了一个对应于该节区头字符表部分的索引。表2-17列出了一个有 25 字节的字符串表(这些字符串和不同的索引相关联)。

表2-17 字符串表示例

索引

+0

+1

+2

+3

+4

+5

+6

+7

+8

+9

0

\0

n

a

m

e

.

\0

V

a

r

10

i

a

b

l

e

\0

a

b

l

e

20

\0

\0

x

x

\0

 

 

 

 

 

其中包含的字符串如表2-18所示:

表2-18 字符串表示索引

索引

字符串

0

(空)

1

"name."

7

"Variable"

11

"able"

16

"able"

24

(空字符串)

如上所示,一个字符串表可能涉及该节区中的任意字节。一个字符串可能引用不止一次;引用子串的情况是可能存在的;一个字符串也可能被引用若干次;而不被引用的字符串也是允许存在的。

1.3.2 符号表

一个目标文件的符号表保存了一个程序在定位和重定位时需要的定义和引用的信息。一个符号表入口的格式如图2-4所示。

typedef struct

      Elf32_Word     st_name;

      Elf32_Addr     st_value;

      Elf32_Word     st_size;

      unsigned char   st_info;

      unsigned char   st_other;

      Elf32_Half      st_shndx;

 }  Elf32_Sym;

图2-4 符号表入口格式

符号表入口对于不同的目标文件而言其st_value成员有一些不同的解释。

•   在可重定位文件中,st_value保存了节区索引为SHN_COMMON 符号的强制对齐值。

•   在可重定位文件中,st_value保存了一个符号的节区偏移。也就是说,st_value是从st_shndx定义的节区开头的偏移量。

•   在可执行的和共享的目标文件中,st_value保存了一个虚拟地址。为了使这些文件符号对于动态链接器更为有效,文件层面上的节区偏移让位于内存层面上的虚拟地址(节区编号的目的)。

尽管符号表值对于不同的目标文件有相似的含义,相应的程序还是可以有效地访问数据。

1.3.3 重定位

重定位是连接符号引用和符号定义的过程。比如,当一个程序调用一个函数的时候,相关的调用必须在执行时把控制传送到正确的目标地址。换句话说,重定位文件应当包含有如何修改他们的节区内容的信息,从而允许可执行文件或共享目标文件为一个进程的程序映像保存正确的信息。重定位入口的数据结构如图2-5所示。

 typedef struct

      Elf32_Addr r_offset;

      Elf32_Word r_info;

 } Elf32_Rel;

  typedef struct

      Elf32_Addr r_offset;

      Elf32_Word r_info;

      Elf32_Sword r_addend;

 } Elf32_Rela;

图2-5 重定位入口

•   r_offset:该成员给出了应用重定位行为的地址。对于一个重定位文件而言,该值是从该节区开始处到受到重定位影响的存储单位的字节偏移量。对一个可执行文件或一个共享目标而言,该值是受到重定位影响的存储单位的虚拟地址。

•   r_info:该成员给出了具有受重定位影响因素的符号表索引和重定位应用的类型。比如,一个调用指令的重定位入口应当包含被调用函数的符号索引。如果该索引是 STN_UNDEF(未定义的符号索引),重定位将使用0作为该符号的值。重定位类型是和处理器相关的。当正文(text)引用到一个重定位入口的重定位类型或符号表索引,它表明相应的应ELF32_R_TYPE或 ELF32_R_SYM 于入口的 r_info 成员,如图2-6所示。

 

 

#define ELF32_R_SYM(i)          ((i)>>8)

#define ELF32_R_TYPE(i)         ((unsigned char)(i))

#define ELF32_R_INFO(s, t)      ((s)<<8+(unsigned char)(t))

图2-6 r_info成员

•   r_addend:该成员指定一个常量,用于计算将要存储于重定位域中的值。

      只有 Elf32_Rela 入口包含一个明确的加数。Elf32_Rel 类型的入口在可以修改的地址中存储一个隐含的加数,依赖于处理器结构,因此,特定机器的应用应当使用一种排他性的形式或依赖于上下文的形式。

重定位入口中的成员 r_offset对于不同的目标文件有少许差异:

•   在可重定位文件中,r_offset 表示了一个节区偏移。也就是说,重定位节区自己叙述了如何修改其他在文件中的其他节区;重定位偏移量指明了一个在第二个节区中的存储器单元。

•   在可执行和共享的目标文件中,r_offset 表示一个虚拟地址。为了使得这些文件的重定位入口更为有用(对于动态链接器而言),该节区偏移(文件中)应当让位于一个虚拟地址(内存中的)。

重定位入口叙述了怎样变更指令和数据域(位数在表的两边角下),重定位区域指定一个以任意字节对齐方式占用 4 字节的 32 位域。这些值使用与32位 Intel体系相同的字节顺序。

计算假设正在将一个可重定位文件转换为一个可执行或共享目标文件。从概念上来说,链接器合并一个或多个可重定位文件来组成输出。它首先决定怎样合并、定位输入文件,然后更新符号值,最后进行重定位。对于可执行文件和共享的目标文件而言,重定位过程是相似的并且有相同的结果。

原创粉丝点击