ELF可执行文件的解析与加载

来源:互联网 发布:淘宝标志在线制作 编辑:程序博客网 时间:2024/05/04 01:01

1.1   前言 
ELF-
可执行链接格式(Executable and Linkable Format)最初是由UNIX系统实验室(USL)作为应用程序二进制接口(ABI)开发和发行。工具接口标准委员会TIS已经将ELF作为运行在Intel32位架构之上的各类型操作系统的可导出对象文件格式标准。ELF标准为开发者提供了一组横跨多运行环境的二进制接口定义来组织软件开发。 

1.2   对象文件
1.2.1 介绍 
本部分描述了ABI对象文件格式,也称之为ELF。有三种主要类型的对象文件: 
1.
可重组(relocatable)文件包含了适合用来链接其他对象文件的代码和数据,从而创建出可执行或可共享的对象文件; 
2.
可执行(executable)文件包含了用于执行的程序,该文件规定了exec如何创建一个程序的进程映像; 
3.
可共享对象(shared object)文件包含了用来在两个上下文之间链接的代码和数据。首先,链接器ld将该文件和其他的可重组文件或可共享对象文件进行处理后,创建出新对象文件,其次,动态链接器将该新对象文件与可执行文件或共享对象组合,来共同创建一个进程映像; 
经过汇编器以及链接器创建成的对象文件,其是在处理器上可直接执行的程序的二进制代表。本部分主要描述文件格式以及其如何用来构建程序。后一部分也描述了对象文件,集中在程序执行所必须的信息上。
1.2.1.1 文件格式 
在程序链接和程序执行过程都涉及到对象文件。出于方便和效率,对象文件格式图从链接和运行两个视角来展示文件的内容。 

ELF header
位于文件的开始处,其用来描述文件的组织结构。Section包含了大量的对象文件信息,从链接的视角来看就是指令、数据、符号表、重组信息等等。SegmentProgram是从程序执行视角来观看的,这将在下部分讲解。 
如果存在Program Header table的话,其将告诉操作系统如何创建进程映像。用来创建进程映像(执行程序)的文件必须包含program header table。可重组(relocatable)文件可以没有该信息。Section header table包含了用来描述文件section的信息。每个section在该表中都有一个对应的表项,每个表项给出了诸如section名称、尺寸等等信息。用于链接的文件必须有section header table,其他的对象文件可有可无。 
这里需要注意的是,虽然图中Program header table紧接着ELF headersection header table紧接着sections,实际的文件中并不一定是这样。而且,sectionssegments也可以不按次序排放,只有ELF header是固定在文件的首部。 
1.2.1.2 数据的表示 
对象文件格式支持8位、32位等架构的大量处理器。然而,为了保证其容易扩展到更多的体系架构,因此对象文件提供了一些机器独立的控制数据,用来按照统一的方式标明和解释对象文件的内容。对象文件中其余的数据都是按照目标处理器硬编码的,当然不用考虑该文件是在哪个文件上创建的。 

对象文件格式中定义的所有数据结构定义都沿守自然尺寸以及对齐原则。必要时,数据结构可以包含填补内容来保证4字节对象的4字节对齐。数据也可以相对于文件起始位置对齐,例如,包含Elf32_Addr成员的数据结构在文件中将会按照4字节对齐。为了保证可移植性,EFL中不使用bit域。 


1.2.2ELF Header 
1.2.2.1 ELFHeader结构 
一些对象文件控制结构可能会增长,因为ELF header包含了这些结构的实际尺寸。如果对象文件格式发生改变,那么程序有可能会碰到控制结构比原来大或者比原来小的情况,这样,程序有可能就会忽略一些额外的信息。至于这些丢失的信息如何处理,则依赖于上下文环境。 

e_ident
这段最先开始的字节标识该文件为对象文件,并且提供了机器独立的数据,其用来解释文件内容。完整的描述见下面的ELF Identification 
e_type
该成员指定了对象文件的类型。虽然核心文件内容没有明确的规定,但是ET_CORE类型则专门为该文件保留。 
0xFF000xFFFF,为特定处理器保留。剩余所有其他的值也是保留的,用于新类型的对象文件。

ET_NONE     0  No 
ET_REL      
1   Relocatable 
ET_EXEC      2    Executable 
ET_DYN       3        Shared 
ET_CORE      4        Core 
ET_LOPROC      0xff00  Processor-specific 
ET_HIPROC       0xffff       Processor-specific 
e_machine 该成员指定了文件适用的计算机架构。除了定义的值之外,其他的值保留,在必要时用于新的计算机架构。 
EM_NONE  
No 
EM_M32    1   AT&T 
EM_SPARC  2  SPARC 
EM_386     3   Intel 
EM_68K     4   Motorola 
EM_88K     5   Motorola 
EM_860    7   Intel 
EM_MIPS   8  MIPS 
e_version 该成员指定了对象文件版本,1表示该文件为最初文件格式。如果扩展了,则使用更高的编号。 EV_NONE 0 Invalid 
EV_CURRENT 1 Current 


e_entry 

该成员标明了系统接管该进程控制的第一条指令的逻辑地址,从而开始了该进程。如果没有关联切入点,则默认为0 
e_phoff
该成员包含了programheader table的文件偏移地址(按字节),如果该文件没有program header table,则该成员为0 
e_shoff
该成员包含了sectionheader table的文件偏移地址(按字节),如果该文件没有section header tables,则该成员为0 
e_flags
该成员包含了文件与处理器相关的标识。 
e_ehsize
该成员包含了ELF header的尺寸(按字节)。 
e_phentsize
包含了program header table中一条表项的字节数,所有表项大小相等。 
e_phnum
包含了program header table中表项的数目。如果该文件没有program header table,则该值为0 
e_shentsize
包含了section header table中一条表项的字节数,所有表项大小相等。 
e_shnum
包含了section header table中表项的数目。如果该文件不含section header table,则该值为0 
e_shstrndx section name string table
(段名称字符串表)独立构成一个section 
e_shstrndx
成员包含了该sectionsection header table中表项的索引号。如果该文件不含section name string table,该成员中填充的值为SHN_UNDEF,见‘‘Sections’’ ‘‘String Table’’

 
1.2.2.2 ELF Header及ELF Identification 
像上面提及到的一样,ELF提供了对象文件框架来支持多处理器、多数据编码、多类型的机器。为了支持这些对象文件家族,文件的初始字节必须指明如何解析本文件,而且该指明过程必须和处理器(查询解析信息就是通过该处理器进行的)无关以及和该文件剩余的内容无关。 
EFL Header
的初始字节也就是e_ident成员。 

每个字节的具体值及其含义如下: 

EI_MAG0
EI_MAG3 
文件的头4个字节包含了特征码,其用来表明该文件是一个ELF对象文件 
ELFMAG       
0   0x7f         e_ident[EI_MAG0] 
ELFMAG        1   ’E’           e_ident[EI_MAG1] 
ELFMAG        2   ’L’           e_ident[EI_MAG2] 
ELFMAG        3   ’F’           e_ident[EI_MAG3] 
EI_CLASS 
e_ident[EI_CLASS]指明了文件的种类或容量。 

ELF
文件格式在设计时就考虑到了在不同总线宽度的机器中通用,而不用考虑到诸如把最大总线宽度的机器码强加到最小机器上去。 
类别ELFCLASS 32支持高达4GB的虚拟地址空间和文件尺寸,其使用上面定义的基本类型。 
类别ELFCLASS6464位架构保留,其也表明了对象文件可能7684改变,不过64位格式还没有定义。 
其他的类别也会在需要时定义,相应的基本类型和尺寸也会发生变化。

ELFCLASSNONE      0   Invalid 
ELFCLASS32            
1            32-bit 
ELFCLASS64            2            64-bit 
EI_DATA 
e_ident[EI_DATA]规定了对象文件中和处理器相关的数据的如何编码。更多的下面会详解。 
ELFDATANONE            0   
Invalid 
ELFDATA2LSB         1  See below 
ELFDATA2MSB        2  See below 
EI_VERSION 
e_ident[EI_DATA]指定了EFL header版本号。当前情况下,必须是EV_CURRENT,后面会详细解释。 
EI_PAD 

该值标明了在e_ident中无用字节的开始,这些字节被设置为0,为保留;读取对象文件的进程应该忽略这些字节。如果未来某些未用字节派上用场了,该值在也许会改变。 

文件的数据解码规定了如何翻译文件中的基本对象。如上所述,类别ELFCLASS32文件使用占用124字节的对象。在编码的前提下,对象的排布如下图所示,序号在每个单元的左上角。 
ELFDATA2LSB
编码规范规定了最末端字节占用最低地址: 


ELFDATA2MSB
编码规范规定了最末端字节占用最高地址: 

1.2.2.3 ELFHeader 机器信息 
对于32Intel体系架构而言,文件标识e_ident中相关成员的值应该如下: 
e_ident[EI_CLASS]= ELFCLASS32 

e_ident[EI_DATA]= ELFDATA2LSB 
同时在ELF header中的e_machine成员必须为EM_286ELF header中的e_flags成员包含了和文件相关的比特标志位。32Intel体系架构不需要任何标志位,因此该值为0 
1.2.3Section header table及section特征 
1.2.3.1section header table概述 
对象文件的section header tables使得可以轻而易举的定位所有的sectionsection header table 是一个Elf32_Shdr结构的数组。sectionheader table index是数组中某个元素的下标。ELF header中的e_shoff成员给出了section header table到文件头的偏移(按字节),e_shnum给出了section header table中包含多少个表项,e_shentsize给出了每个表项的字节数。 
1.2.3.2section header table及特殊的section index 
某些section header table index是保留的,对象文件中的section不应该占用这些特殊index 


SHN_UNDEF
该值标明了一个没有定义的、丢失的、或者没有意义的section引用,例如,一个已经定义了的符号引用了SHN_UNDEF section序号的,则该符号实际被当作为未定义符号。 
注意:虽然索引0被保留为一个未定义的值,但是section header table仍然包含了用于0索引的表项,也就是说,如果e_shnum=6,则索引为0-5 
SHN_LORESERVE
该值指定了保留索引的低端。 
SHN_LOPROC
SHN_HIPROC此范围内的索引用于特定的处理器,也是保留的。 
SHN_ABS
该值指定了相应引用的绝对值。例如,引用了SHN_ABS section序号的已定义符号,其地址将是绝对的,不会被重组所影响。 
SHN_COMMON
引用该section的符号都是常用的符号,例如FORTRAN COMMON或没有分配的C外部变量。 
SHN_HIRESERVE
该值指定了保留索引高端,系统保留索引在SHN_LORESERVESHN_HIRESERVE范围之间(包含SHN_LORESERVESHN_HIRESERVE)。Section head table中不会包含这些保留索引的表项。 

1.2.3.3section的特征 
除了ELF headerprogram header tablesection header table之外,Section包含了对象文件中的所有信息。另外,对象文件的section还必须满足以下几个要求: 
1.
对象文件中的每个sectionsection header table中都必须有一个表项来描述它。即使一个section都没有,section header table也可以存在; 
2.
每个section在文件中都占用一段连续字节的空间(当然也可以为空); 
3.
一个文件中的sections不能重叠,文件中的一个字节不能同时位于一个以上的section中; 
4.
对象文件中可以有不活动的空间,大量的Headers以及sections不一定会覆盖对象文件的每个字节,不活动数据的内容是不确定的。 
1.2.4section header table&section header 
1.2.4.1section header结构 
section header
的结构如下: 

1. sh_name:
该成员指定了section的名称,其值为section“section header string table”(注意section header string table本身构成了一个Section)中某一表项的索引,该索引中为一个以nul终止的字符串; 
2. sh_type:
该成员一句section的内容和语义将section分类。section类型及其描述下面将会有详细描述; 
3. sh_flags: Section
支持使用1bit标志来描述大量的属性,下面将会详细描述; 
4. sh_addr:
如果某个section将会出现在进程的内存映射中,该成员给出的就是该section第一个字节驻留在内存中的地址。否则,该成员将会为0 
5. sh_offsect:
该成员给出了从文件开始处到section中第一个字节的偏移(按字节)。这里需要提到的SHT_NOBITS类型的section,其在文件中不占用任何空间,其sh_offsect则为其在文件中的虚位置; 
6. sh_size:
该成员给出了section的大小尺寸(按字节),如果该section类型不是SHT_NOBITS,则该size即为该section在文件中所占的字节数。如果是,则即使该值不为0,但是其也不会在文件中占用任何空间; 
7. sh_link:
该成员包含了一个sectionheader table的索引链接,其如何解释依赖于该section的类型,下面将会详细描述; 
8. sh_info:
该成员包含了些额外的信息,其如何解释也依赖于section的类型; 
9. sh_addralign:
一些section可能会有地址对齐约束,例如我们要求,如果section包含了一个双字,则系统必须确保整个section按照双字对齐。也就是说,sh_addr取模sh_addralign的值必须为0。目前只有02的整数次幂值可以在此处使用,01意味着无需对齐; 
10. sh_entsize:
有些section包含的是一组固定大小表项的表,例如符号表,对于这样的section,该成员给出了每个表项的尺寸大小。如果section不包括这样的内容,则该值为0 
1.2.4.2section header & section type 
section header
sh_type成员包含了如下的语义: 

1. SHT_NULL:
该值表示该sectionheader是不活动的,其没有对应的section,此时section header中其他的成员都没有意义; 
2. SHT_PROGBITS:
section包含了程序自定义信息,其格式和含义由程序自行决定; 
3. SHT_SYMTAB
SHT_DYNSYM:section包含了符号表。当前每种类型的section在对象文件中只能有一个,该限制在未来有望被放开。SHT_SYMTAB为连接器提供了符号,当然其也包含了用于动态链接的符号。但是由于一个完整的符号表可能会包含了大量的对于动态链接没有用的符号,所以,对象文件或许也应该包含一个SHT_DYNSYM section,其中包含了一组最小化的动态链接符号,用来节省空间; 
4. SHT_STRTAB:
section包含了一个string table。一个对象文件可以有多个string table sections 
5. SHT_RELA:
section包含了重组表项(with explicitaddends); 
6. SHT_HASH:
section包含了符号hash表,所有参与动态链接的对象都必须包含一个符号hash表; 
7. SHT_DYNAMIC:
section包含了动态链接信息; 
8. SHT_NOTE:
section包含了特定的信息,详见Note Section 
9. SHT_NOBITS:
section在文件中不占用空间,但是其类似与SHT_PROGBITS,虽然该section不包含任何字节,但是其sh_offset成员仍然包含了概念上的相对文件起始的偏移地址; 
10. SHT_REL:
section包含了重组表项(withoutexplicit addends); 
11. SHT_SHLIB:
section类型是保留的,还没有被指定语义,和ABI一致的程序不应该包含该section 
12. SHT_LOPROC
SHT_HIPROC:该范围内的值是为特定处理器语义保留的; 
13. SHT_LOUSER:
该值指定了为应用程序编程者保留的索引范围的低端; 
14. SHT_HIUSER:
该值指定了为应用程序编程者保留的索引范围的高端,在SHT_LOUSERSHT_HIUSER之间的类型的section可以被应用程序使用,而不会同当前的或者未来的为系统定义的类型起冲突; 
除上述类型之以外,剩余所有的类型值统统保留, 
1.2.4.3section headersection flag属性 
section header
sh_flags成员包含了1比特标志,用来描述该section的属性,定义的值如下: 

如果sh_flags中其中一个标志bit位被设置,则该属性就为section打开了。 
1. SHF_WRITE:
section包含了在进程执行期间可写的数据; 
2. SHF_ALLOC:
section在进程执行期间会占用内存。一些控制section不会驻留在对象文件的内存映像里,对于这些section,该属性就为OFF 
3. SHF_EXECINSTR:
section包含了可执行的机器指令; 
4. SHF_MASKPROC:
所有包含在该mask中的属性都用于特定处理器的语义; 
1.2.4.4section header  sh_link和sh_info属性 
section header
中的sh_linksh_info成员根据section type的不同,包含了不同的特殊信息。 
Figure1-13: sh_link
sh_info说明 
sh_type sh_link sh_info 

SHT_DYNAMIC 对于当前的sh_type,该属性的值即为该section所使用的string table sectionsection header table中的索引
SHT_HASH
对于当前的sh_type,该属性的值即为该hash section所使用的符号表sectionsection header table中的索引
SHT_REL 

SHT_RELA 对于当前的sh_type,该属性的值即为该section所相关的符号表sectionsection header table中的索引对于当前的sh_type,该属性的值即为该section将要应用重组的sectionsection header table中的索引 
SHT_SYMTAB 

SHT_DYNSYM 对于当前的sh_type,该属性的值即为该section所相关的符号表stringsection header table中的索引比符号表中最后一个Local符号(STB_LOCAL)索引还要大的数值 
other SHN_UNDEF 0 


1.2.4.5section header特殊的section 

几个特殊的sections说明: 
1. .bss
,该section包含了在内存中的程序的未初始化的数据,当程序开始运行时,系统将用0来初始化该区域。该section不占用文件空间,该section type = SHT_NOBITS 
2. .comment
,该section包含了版本控制信息; 
3. .data
.data1,该section包含了在内存中的程序的初始化数据; 
4. .debug
,该section包含了符号调试信息,其中内容没有硬性规定; 
5. .dynamic
,该section包含了动态链接信息,该section属性将包含SHF_ALLOC比特位,而SHF_WRITE比特位是否为1取决于处理器; 
6. .dynstr
,该section包含了用于动态链接的字符串,通常是符号表项名称字符串; 
7. .dynsym
,该section包含了动态链接符号表; 
8. .fini
,该section包含了用于终止进程可执行指令代码; 
9. .got
,该section包含了全局偏移表; 
10. .hash
,该section包含了符号hash表; 
11. .init
,该section包含了用于初始化进程的可执行代码,也就是说,当一个程序开始运行的时候,系统将会执行在该section中的代码,然后才会调用程序的入口点(对于C程序而言就是main); 
12. .interp
,该section包含了程序解释其的路径; 
13. .line
,该section包含了符号调试信息的行号,其用于描述程序源代码和机器码之间的相应关系; 
14. .note
,该section包含了供应商及程序兼容信息等; 
15. .plt
,该section包含了程序链接表; 
16. .relname
.relaname,该section包含了relocation信息,该section的属性包括了SHF_ALLOC比特位,通常,name为将要被重组的section的名称,例如如果要重组.text,那么名称就为.rel.text或者.rela.text 
17. .rodata
.rodata1,该section包含了只读数据,通常进程中的不可写段,例如Program Header 
18. .shstrtab
,该section包含了section名称; 
19. .strtab
,该section包含了符号表项名称字符串,如果文件包含了一个可加载的并且包含了符号字符串表的segment,则sectionSHF_ALLOC比特位属性将被设置; 
20. .symtab
,该section包含了符号表,如果文件包含了一个可加载的并且包含了符号表的segment,则sectionSHF_ALLOC比特位属性将被设置; 
21. .text
,该section包含了程序的可执行指令。 

带有(.)前缀的section是系统保留的,当然应用程序也可以在这些section语义允许的情况下使用这些section。应用程序可是使用前缀来命名其自己使用的section,以避免和系统section冲突。 
1.2.5 特殊的section&Stringtable 
String table section
包含了null终止的字符序列,也就是字符串。对象文件使用这些字符串来代表符号和section名称。对一个字符串的引用其实就是字符串表的索引。第一个字节,也就是0号索引,通常是一个null字符。同样地,字符串表的最后一个字节也是null,确保所有的字符串都是以null结尾。索引为0的字符串为0,即要么是没有名字要么是空名字,这要取决于上下文。空字符串表section也是允许的,此情况下,其section headersh_size成员必须为0 
首先,section header string table sectionsection header中表项的索引通过ELF headere_shstrndx成员找到,然后sectionheadersh_name定位该string table中的某一表项。例如: 

从图中我们可以看出,一个string table index可以指向该section中的任何字节,一个字符串可能会出现多次,当然也允许字符串未被引用; 
1.2.6 特殊的section&SymbolTable 
1.2.6.1 什么是内核符号表
内核并不使用符号名。它是通过变量或函数的地址(指针)来使用变量或函数的,而不是使用size_t BytesRead,内核更喜欢使用(例如)c0343f20来引用这个变量。而另一方面,人们并不喜欢象c0343f20这样的名字。我们跟喜欢使用象 size_t BytesRead这样的表示。通常,这并不会带来什么问题。内核主要是用C语言写成的,所以在我们编程时编译器/连接程序允许我们使用符号名,并且使内核在运行时使用地址表示。这样大家都满意了。然而,存在一种情况,此时我们需要知道一个符号的地址(或者一个地址对应的符号)。这是通过符号表来做到的,与gdb能够从一个地址给出函数名(或者给出一个函数名的地址)的情况很相似。符号表是所有符号及其对应地址的一个列表。例如: 
c03441a0       B        
  dmi_broken 
c03441a4       B             is_sony_vaio_laptop 
c034420c       b             pirq_router_dev 
c0344220       b               ascii_buffer 
c0344224       b             ascii_buf_bytes 
你可以看出名称为dmi_broken的变量位于内核地址c03441a0处。 
1.2.6.2 符号表项 & 表项格式 
对象文件的符号表用来保存用于定位和重组程序的符号定义和引用的信息,其可以独自占用一个section。符号表项0 STN_UNDEF指明了该表中第一项,其用与未定义符号。表项的初始内容将在后面讲到。 

符号表项的内容格式如下: 

1. st_name
该成员包含了对象文件的符号字符串表中一个表项的索引号,其保存了该符号的名称;如果该值为非0,其就是一个用来描述该符号名称的字符表索引,否则,该符号表项没有名字; 
2. st_value
该成员给出了相关符号的值,至于其中的内容是个绝对的值还是地址等等,取决于上下文; 
*
在可重组文件中,st_value包含了符号的对齐约束,该符号必须位于indexSHN_COMMON符号的section中; 
*
在可重组文件中,st_value包含了已定义符号的section偏移。也就是说,st_value为自st_shndx指向的section开始处的偏移; 
*
在可执行和共享对象文件中,st_value包含了逻辑地址,为了使得这些文件的符号更利于动态连接器使用,对应sectionoffset给定的是内存逻辑地址; 

3. st_size
许多符号都有尺寸,例如,一个数据对象的尺寸就是该对象中包含的字节数。如果符号没有大小或者大小未知,则该值为0 
4. st_info
该成员指定了符号的类型和绑定属性,详述见下,其和bind以及type结合计算方法如下: 
#define ELF32_ST_BIND(i) ((i)>>4) 

#define ELF32_ST_TYPE(i) ((i)&0xf) 
#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf))//b=bind,t=type 
5. st_other
该成员未定义; 
6. st_shndx
每个符号表项都和某section相关,该成员保存了此sectionsection head table中的索引。 
1.2.6.3
符号表项&#61664;绑定行为 
符号的绑定决定了链接的可见性以及行为: 

1. STB_LOCAL
本地符号,对于外部对象文件不可见。同样名称的符号也许会存在于多个文件中而不会互相干扰; 
2. STB_GLOBAL
全局符号,一个对象文件定义,所有对象文件共享; 
3. STB_WEAK
类似于全局符号,不过其优先权更低; 
4. STB_LOPROC
STB_HIPROC该范围内的符号为处理器特定的语义; 

在每个符号表中,所有STB_LOCAL类型的符号优先级最高。 
1.2.6.4 符号表项&符号类型 
符号类型提供了实体的分类: 

1. STT_NOTYPE
该符号类型未指定; 
2. STT_OBJECT
该符号和数据对象相关,例如变量,数组等等; 
3. STT_FUNC
该符号和函数或者其他可执行代码相关; 
4. STT_SECTION
该符号和section相关,其绑定行为一般为STB_LOCAL 
5. STT_FILE
通常文件符号的名字给出了该对象文件相关的源文件的名字,其绑定行为一般为STB_LOCAL 
6. STT_LOPROC
STT_HIPROC该范围内的符号为处理器特定的语义; 

1.2.7
特殊的section&#61664;Relocation 
重组就是将符号定义和符号引用连接起来的过程;例如,当程序调用函数时,调用指令必须被传递给执行时正确的目的地址。换句话说,可重组的文件必须包含了这样的信息:该信息描述了如何修改可重组文件的section内容,从而使得可执行或者共享对象文件包含了正确的进程内存映像信息。而Relocation表项就是可重组文件需要包含的信息; 

1. r_offset
该成员给出了应用重组动作的位置,对于一个可重组文件而言,该值就是从该section开始到重组将要影响的存储单元之间的字节偏移。对于可执行文件或共享对象文件而言,该值就是将要被重组影响的存储单元的逻辑地址; 
2. r_info
该成员给出了该需要重组的符号表索引,以及将要重组的类型。类如,一个调用指令的重组表项包含了将要被调用函数的符号表索引。如果其索引为STN_UNDEF,则使用0作为符号值。重组类型是和处理器相关的。其计算方式如下: 

3. r_addend
该成员指定了常量加数用来计算将要被存储到重组域中的值; 

关于重组的详细信息不再叙述,请查阅ELF Format 

1.3程序的加载 
1.3.1 介绍 
本部分介绍了对象文件信息以及创建运行进程的系统行为。这里的一些信息适用于所有的系统,另一些是和处理器相关的。 
可执行和共享对象文件只是静态地代表了程序。要执行这样的程序,操作系统利用该文件来创建动态程序表现,或进程映像。进程印象包含了段,段包含了textdatastack等等。本部分主要讨论以下几个问题: 
1. Program header
该部分补充了前一章讲述的内容,主要描述了程序执行时的对象文件结构,以及用于定位段映像的program header table基本数据结构; 
2. Program loading
给定一个对象文件,操作系统必须将其加载如内存来作为程序运行; 
3. Dynamic linking
详细信息不再叙述,请查阅ELF Format 
1.3.2Program header table 
可执行或共享对象文件的program header table是一个数据结构数组,每一个表项都描述了一个段或者其他系统用来为程序运行做准备的信息。对象文件的段可以包含多个section,下面的段内容将会详细描述。program header table只对可执行和共享对象文件有意义。文件通常通过其ELF header中的e_phentsizee_phnum成员来指定其自身的program header table大小。 
1.3.3Program header table&Program header 

1. p_type
该成员表明了该数组元素所描述的段的类型以及如何解释该数组元素的属性; 
2. p_offset
该成员段的第一字节至文件开始处的偏移; 
3. p_vaddr
该成员给出了段的第一字节在内存中的逻辑地址; 
4. p_paddr
基本无用; 
5. p_filesz
该成员给出了该段在文件中占用的字节数,可以为0 
6. p_memsz
该成员给出了该段在内存映像中占用的字节,可以为0 
7. p_flags
该成员给出了段相关的标志; 
8. p_align
当程序加载后,可加载的进程段的p_vaddrp_offset取模page size(4KB)之后的值必须相等。该成员给出该段应该在内存和文件中的对齐值。 
1.3.4Program header table Program header  Type 
一些表项描述了进程段,其他的一些给出了对进程映像并无作用的辅助信息。段表项可以按照任何顺序出现,除非明确指定。 

1. PT_NULL
该元素不适用,其告诉program header table忽略它; 
2. PT_LOAD
其指定了可加载段,由p_filesz p_memsz来描述。文件中的字节被映射到内存段中。如果段的内存尺寸大于其在文件中的尺寸,那么额外的数据将被填充为0。可加载的段,在program header table中通常按照p_vaddr升序排列; 
3. PT_DYNAMIC
该成员指定了动态链接信息; 
4. PT_INTERP
指定了翻译器的未知; 
5. PT_NOTE
指定了辅助信息的位置和大小; 
6. PT_SHLIB
保留; 
7. PT_PHDR
如果存在,指定了programheader table本身在文件/内存中的位置和大小; 
8. PT_LOPROC
PT_HIPROC保留; 
1.3.5 BaseAddress 
可执行和共享对象文件都有基地址,其是程序对象文件内存映射后的最低的虚拟地址。基地址的用途就是用来在动态链接过程中重组内存映像;详细信息不再叙述,请查阅ELF Format 
1.3.6 初始化和终止函数 
当动态连接器已经建立起进程映像并且执行完毕重组后,每一个共享对象都有机会执行一些初始化代码,这些初始化代码并不是按照特定顺序的,但是所有共享对象的初始化都是发生在可执行文件活动控制权之前。程序可以通过动态结构中的DT_INITDT_FINI动态表项来定义动态section,通常这些代码都驻留在.init.fini section中,详细信息不再叙述,请查阅ELF Format 
1.3.7 程序的加载 
当操作系统创建或者增加一个进程映像的时候,其理论上将会拷贝文件的段到虚拟内存段中。系统读取文件内容的实际依赖于程序的执行环境的行为。一个进程在运行的过程中,如果没有引用逻辑页面,则其也不需要物理页面,通常大部分页面进程都是没有使用的。因此,延缓读取物理页面可以提高系统性能。为了获取该效率,可执行和共享对象文件的段的文件偏移及虚拟地址必须在按页面(4KB)取模后相等。 
虽然上面的例子文件中,对于textdata段而言,文件偏移和逻辑地址取模4KB后都是相等的。但是: 
1. text
的第一个页面包含了ELF headerprogram header table以及其他的信息; 
2. text
的最后一个页面包含了data的开始部分数据的拷贝; 
3. data
的第一个页面包含了text的末尾数据的拷贝; 
4. data
的最后一个页面也许包含了和运行进程不相关的文件信息; 
理论上讲,系统对待每个段的内存权限都是相互独立的。段地址不得不调整来确保地址空间中的每个逻辑页面都有自己的权限;在上面的例子中,包含了text结尾和data开始的区域将要被映射两次:一次就是包含了textdata开始部分,另一个就是text末尾部分和datadata段的末尾还需要对为初始化数据的特殊处理,系统通常将其清零

 

参考资料

Executable and LinkableFormat (ELF) (这专门介绍ELF文件格式的ABI的好文章,网络版在www.skyfree.org/linux/references/ELF_Format.pdf可以得到)

原创粉丝点击