OBJECT FILES
preface
文件格式分析可以借助具体例子学习,用010editor的ELF Template加载任意一个ELF文件:
可以通过010editor分析出的文件格式找到对应的二进制数据。
File Format
ELF header
描述了文件的组织结构。Sections
保存了目标文件Linking View的大量信息:指令、数据、符号表和重定位信息等。Program header table
告诉系统如何创建一个进程映像。Section header table
包含了描述文件sections的信息。每一个section在表中都有一个条目,每一个条目包含了section name、section size等信息。
Data Representation
ELF Header
1 |
|
e_ident
ELF文件的magice_type
标识目标文件的类型。
ET_REL 1 Relocatable file
ET_EXEC 2 Executable file
ET_DYN 3 Shared object file
e_machine
文件运行在哪种体系结构上。e_entry
程序入口点。e_phoff
program header table的文件偏移。e_shoff
section header table的文件偏移。e_flags
处理器相关标识。e_ehsize
ELF header的大小。e_phentsize
文件的program header table一个条目的大小,所有条目一样大小。e_phnum
program header table中条目的个数。e_shentsize
section header table中一个section header的大小,所有section header一样大小。e_shnum
section header table中条目的个数。e_shstrndx
section header table中和section名字字符串表相关的条目的索引。
Sections
Section Header
1 | typedef struct { |
sh_name
指向section header string table section的索引,表示section的名字。sh_type
指明了section的内容和语义。sh_flags
描述section的各项属性。sh_addr
如果section将会出现在一个进程的内存映像中,这个成员给出section的起始地址。sh_offset
section在文件中的偏移地址。sh_size
section的大小。类型为SHT_NOBITS的section的size不为0,但它实际不占文件空间。sh_link
section header table index link。sh_info
额外的信息,含义取决于section的类型。sh_addralign
有些section有地址对齐方面的约束。比如 section 中有个 doubleword 型的数据,系统就必须保证整个 section 是 doubleword 对齐的,即 sh_addr 对 sh_addralign 取模为 0。目前,此成员的取值只允许是 0 和 2 的幂数。取值 0 和 1 代表 section 没有对齐方面的约束。sh_entsize
有些section存放的表项大小固定的表,如符号表。如表项大小不固定,此成员为0。
Section Types, sh_type
SHT_NULL
表示此头部是不活动的,不对应一个section。SHT_PROGBITS
存放着程序定义的信息,格式和含义只由程序决定。SHT_SYMTAB & SHT_DYNSYM
保存着符号表。SHT_STRTAB
保存着字符串表。SHT_RELA
通过显式加数存放重定位条目。SHT_HASH
存放一个符号哈希表。SHT_DYNAMIC
存放动态链接的相关信息。SHT_NOTE
存放文件的注释信息。SHT_NOBITS
不占用文件的空间,其他方面类似于SHT_PROGBITS。SHT_REL
存放没有显式加数的重定位条目。SHT_SHLIB
保留section。SHT_LOPROC through SHT_HIPROC
SHT_LOUSER
留给程序使用的保留索引值的下限。SHT_HIUSER
留给程序实用的保留索引值的上限。
Section Attribute Flags, sh_flags
SHF_WRITE
section包含在进程执行期间可写的数据。SHF_ALLOC
section在进程执行期间占据内存。一些section不在目标文件的内存映像中,这个属性就是off。SHF_EXECINSTR
section包含可执行的机器指令。SHF_MASKPROC
这个掩码中的所有比特位留作处理器相关的语义。
Special Sections
.bss
保存程序内存映像中未初始化的数据。程序开始时,系统把这些数据初始化为0。.comment
保存版本控制信息。.data
and .datal
保存程序内存映像中已经初始化的数据。.debug
保存符号的调试信息。.dynamic
保存动态链接信息。.dynstr
保存动态链接需要的字符串。.dynsym
保存动态链接符号表。.fini
保存进程退出代码所需执行的指令。.got
保存全局偏移表。.hash
保存符号哈希表。.init
保存程序初始化代码所执行的指令。在main之前调用。.interp
保存程序解释器的路径名。.line
保存符号调试用到的行号。.note
按照一定格式保存注释信息。.plt
保存过程链接表。.relname
and .relaname
保存重定位的相关信息。.rodata
and .rodatal
保存进程映像中不可写segment里的只读数据。.shstrtab
保存section的名字。.strtab
保存字符串表,这些字符串通常代表符号表中表项的名字。.symtab
保存一个符号表。.text
保存了程序的执行指令。
名字中包含.
前缀的section是系统保留的。
String Table
字符串表section保存着一些空字节结尾的字符串。
目标文件用symbol string table(.strtab)的字符串表示symbol的名字,用section header string table(.shstrtab这个table是由ELF头部中的e_shstrndx
成员指定)的字符串表示section的名字(section header的sh_name
成员保存的索引)。可以通过索引引用字符串表中的字符串。
Symbol Table
目标文件符号表保存着定位和重定位一个程序的符号定义和引用所需的信息。
符号表表项的数据结构:
1 | typedef struct { |
st_name
这个成员保存了一个指向目标文件symbol string table的索引。st_value
符号的值,取决于上下文,可能是一个绝对值,一个地址等等。st_size
许多符号有相应的大小,例如一个数据对象的大小就是它所包含的字节数。st_info
指明了符号的类型和约束属性。
1 |
st_other
目前为0,没有定义含义。st_shndx
每个符号表项都与一些section有关,这个成员保存着相关section header table的索引。
一个符号的约束决定了链接的可见性和行为。
Symbol Binding, ELF32_ST_BIND
Name Value STB_LOCAL 0 STB_GLOBAL 1 STB_WEAK 2 STB_LOPROC 13 STB_HIPROC 15
STB_LOCAL
Local符号在包含它们的目标文件之外是不可见的。相同名字的Local符号可以在不同的文件中存在,而不会相互冲突。STB_GLOBAL
Global符号对要链接到一起的所有目标文件都是可见的。如果一个文件中定义了一个Global符号,那么另一个文件中无需定义,可直接引用此Global符号。STB_WEAK
Weak符号类似于Global符号,只是它约束的定义优先级较低。STB_LOPROC
至STB_HIPROC
此范围内的取值留作处理器相关的语义。
Symbol Types, ELF32_ST_TYPE
Name Value STT_NOTYPE 0 STT_OBJECT 1 STT_FUNC 2 STT_SECTION 3 STT_FILE 4 STT_LOPROC 13 STT_HIPROC 15
STT_NOTYPE
未指定符号类型。STT_OBJECT
符号对应一个数据对象,比如变量、数组等。STT_FUNC
符号对应一个函数或其它可执行的代码。STT_SECTION
符号对应一个section。这种类型的符号表项位于符号表最前面,用于重定位,通常具有STB_LOCAL约束。STT_FILE
此符号的名字也就是目标文件对应源文件的名字。STT_LOPROC
至STT_HIPROC
此范围间的取值留作处理器相关的语义。
Symbol Values
符号表项在不同的目标文件类型中对st_value成员的解释稍有不同。
1)可重定位文件:如果符号对应section的索引是SHN_COMMON,那么st_value保存的是符号的对齐约束条件。
2)可重定位文件:如果是一个已经定义的符号,st_value保存着该符号在对应section中的偏移,即从st_shndx标识的section开始处的一个偏移量。
3)可执行目标文件和共享目标文件:为了使文件中的符号对动态链接器来说更加有用,st_value保存着一个虚拟地址。section偏移(文件解释)让位给虚拟地址(内存解释),因为在这种情况下,section偏移计数已经不重要了。
除了上面提到的,符号表的取值含义对不同的目标文件来说是类似的,程序也就能够采用高效的方法来访问数据。
Relocation
重定位是将符号引用和符号定义链接到一起的过程。比如,当一个程序调用一个函数,相关的调用指令在执行时必须把控制权传递到正确的目的地址。换句话说,就是可重定位文件必须包含一些信息,用来描述怎样修改它们的section内容,从而使可执行目标文件和共享目标文件掌握正确的信息用于创建进程的程序映像。重定位表项的数据结构如下所示:
1 | typedef struct { |
r_offset
指出重定位操作的位置,对于可重定位文件,它的值是从section的开始处到重定位作用的存储单元的字节偏移量。对于可执行目标文件或共享目标文件,它的值是重定位作用的存储单元的虚拟地址。r_info
指出重定位作用的符号在符号表中的索引和重定位的类型。比如,一个调用指令的重定位表项将保存被调用函数的符号表索引,如果索引是 STN_UNDEF,即未定义的符号表索引,那么重定位使用 0 作为该符号的值。重定位的类型是处理器相关的。当代码访问重定位表项的重定位类型和符号表索引时,需要将ELF32_R_TYPE宏和 ELF32_R_SYM宏分别作用于表项的r_info成员。
1 |
r_addend
指定一个常量加数(addend),用于计算可重定位字段存储的值。