前面介绍了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成员指定的。