前面介绍了section header table,通过这个表可以找到所有section的内容。这些内容根据section type和section name解释。同一个section type可能有多个section,因此section name也是section的识别之一,一些special sections的名字已经预定义好了,根据名字即可确定section的用途。
在ELF中,字符串用来表示符号名和section name,这些字符串存储在字符串表section中。
c语言中的字符串常量存储在.rodata中,并不存储在字符串表中。
通常,有两个字符串表.shstrtab和.strtab,前者存储section name,后者存储符号名。前者的索引存储在ELF header中的e_shstrndx字段中。后者是通过section名字(.strtab)确定。
字符串在字符串表中是以null-terminated存储的。其它地方通过字符串在字符串表中的offset来引用字符串。字符串在字符串表中一个接一个存储。
\0 | . | t | e | x | t | \0 | . | d | a |
t | a | \0 |
如上,".text"在字符串表中的offset为1。在section header table中,.text section条目的sh_name值为1。
第一个字符串的索引为0,为空字符串,根据上下文,0索引表示没有名字或者一个空名字。
空字符串表是允许的,在section header中,sh_size为0,空字符串表的非0索引是非法的。
字符串表的最后一个字节必须是'\0'。
索引子字符串是允许的。如上,如果索引为2,那字符串就是"text"。
字符串没有被其它地方引用也是允许的。
符号表包括但不限于c语言中的符号,文件名等也可以存储在符号表中。符号表中的每个符号条目有固定的大小,用下述结构体表示。
1 2 3 4 5 6 7 8 | 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; |
st_name,符号的名字,存储的是名字在符号字符串表中的索引。
st_value,相关符号的值,这个值不是c语言中变量的值,变量的值是存储在数据段中的。根据上下文,这个值表示绝对值、地址等。
st_size,这个表示符号的大小,对于c语言中的变量,表示的是存储大小,如int变量,这个值就是sizeof(int)。0表示没有大小或者未知大小。
st_info,指定符号的类型和绑定属性。高4位存储绑定属性,低4位存储类型。
st_other,当前未定义,应当为0。
st_shndx,存储section header index,有些index有特殊的含义,可能并不在section header table中。
符号表的第一个条目(index 0)被保留,所有字符段为0。
存储在st_info的高4位,相关类型如下:
Name | Value |
STB_LOCAL | 0 |
STB_GOBAL | 1 |
STB_WEAK | 2 |
STB_LOPROC | 13 |
STB_HIPROC | 15 |
STB_LOCAL,本地符号,在目标文件之外不可见,多个文件可以有同名的本地符号,互不干扰。
STB_GLOBAL,全局符号,对所有要结合的目标文件可见,一个文件定义的全局符号满足其他文件对全局符号未定义的引用。
STB_WEAK,弱符号,类似全局符号,但是他们的定义有低的优先级。
在符号表中,LOCAL符号排在最前面。
在c语言中,static修饰的符号为LOCAL符号。全局变量、函数为GLOBAL符号,
存储在st_info的低4位,相关类型如下:
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相关,主要用于重定向,具有LOCAL绑定。
STT_FILE,通常就是文件名,具有LOCAL绑定,section索引为ABS,在其它LOCAL符号之前。
存储在st_shndx中,如果符号存储在一个section中,则为这个section的索引。如,全局函数存储在.text中,则函数名符号的sh_ndx的值为.text的section index。
有些符号的section index有特殊的section index,表示特殊的含义,如下:
SHN_ABS,符号有绝对值,不受重定向改变。目前还只看到FILE类型有ABS索引。
SHN_COMMON,符号label了一个未分配的公共块,由链接器负责分配,最典型的的是弱定义符号的section index为COMMON,弱定义符号最终由链接器负责选择一个定义。st_value值给出对齐要求。
SHN_UNDEF,未定义符号,由链接器从其它目标文件选择一个定义。从readelf的结果来看,未定义符号的类型为NOTYPE。
根据不同的目标文件类型,有些许不同的解释。
在可重定向文件中,SHN_CONNON索引的符号,给出了对齐要求。
可重定向文件中,如果st_shndx指向了一个存在的section,则给出了符号在section中的偏移。也就是说st_value给出了符号在内存中的位置。
在可执行文件和共享目标文件中,st_value是一个虚拟地址。这样做是为了使这些符号对动态链接器更有用,将section偏移替换为虚拟地址。
重定向是将符号引用和符号定义关联的过程。由于目标文件可能包含外部引用,而共享库可能加载到不同的地址,重定向是必须的。有两种不同类型的重定向,分别用如下结构体表示。
1 2 3 4 5 6 7 8 9 10 | typedef struct elf32_rel { Elf32_Addr r_offset; Elf32_Word r_info; } Elf32_Rel; typedef struct elf32_rela{ Elf32_Addr r_offset; Elf32_Word r_info; Elf32_Sword r_addend; } Elf32_Rela; |
r_offset,给出重定向操作的位置。在可重定向目标文件中,r_offset存储section offset。在可执行文件和共享目标文件中,r_offset存储一个虚拟地址。
r_info,低8位给出重定向类型,由于重定向要修改机器指令,所以重定向的类型是和处理器架构相关的。高24位给出符号表索引,表示重定向的符号。
r_addend,指定一个常量,这个常量加到重定向的计算中。
重定向section引用两个其它section:符号表section和要修改的section。这两个section是通过section header中的sh_info和sh_link成员指定的。