本篇文章接着上一篇ELF文件头和段表继续讲解
本篇文章讲解ELF文件中的不同类型段
int printf(const char* format,...);
int global_init_var = 84;
int global_uinit_var;
void func1(int i)
{
printf("%d\n",i);
}
int main(void)
{
static int static_var = 85;
static int static_var1;
int a = 1;
int b;
func1(static_var + static_var1 + a + b);
return a;
}
ELF文件中用到了很多字符串,比如段名、变量名等。因为字符串的长度往往是不定的, 所以用固定的结构来表示它比较困难。一种很常见的做法是把字符串集中起来存放到一个表,然后使用字符串在表中的偏移来引用字符串
通过这种方法,在ELF中使用字符串只需要标记偏移位置即可。
一般,我们常见的字符串表有两个:.shstrtab
和.strtab
段表字符串表,该表保存段表名称的字符串,比如我们打印出上面程序的.shstrtab表的内容如下,空格我们用换行代替了,比较好观察:
.symtab
.strtab
.shstrtab
.rela.text
.data
.bss
.rodata
.comment
.note.GNU-stack
.rela.eh_frame
有一点需要注意:比如段表.text和.rela.text,字符串表之需要保存.rela.text就够了,因为包含.text,这样可以节省空间。
这个字符串表就是保存程序中所有使用到的字符串了。包括
SampleSection.c
static_var.1731
static_var1.1732
global_init_var
global_uinit_var
func1
printf
main
首先理解一下什么是符号,符号是在链接过程中使用的术语,简单一点说,就是变量和函数的名称,只不过可能经过编译器的处理,是为了使名字唯一。
符号表保存了关于所有符号的信息,符号表的每一项也是一个固定的结构,表示每个符号的信息,下面对于这个结构进行说明
// 这是结构体代码
typedef struct
{
uint32_t st_name;
unsigned char st_info;
unsigned char st_other;
uint16_t st_shndx;
uint64_t st_value;
uint64_t st_size;
} Elf64_Sym;
符号的名称,也是一个偏移值,可以去字符串表中进行匹配
这个字段代表符号类型和绑定字段,他表示两种属性:
先看符号类型,下面列出常用的符号类型
NOTYPE
值为0,未知符号类型
OBJECT
值为1,该符号是个数据对象,比如数组,变量
FUNC
值为2,该符号是个函数
SECTION
值为3,该符号是段表,这种符号的绑定信息必须是LOCAL的
FILE
值为4,该符号表示文件名,这种符号的绑定信息必须是LOCAL的,并且该符号的st_shndx一定是ABS。
COMMON
值为5,该符号是一个Common数据对象
TLS
值为6,该符号是一个线程私有数据对象
NUM
值为6,该符号表示定义类型的数量
LOOS
值为10,该符号表示操作系统特定操作的起始位置
GNU_IFUNC
值为10,该符号表示一个间接代码对象
HIOS
值为12,该符号表示操作系统特定操作的结束位置
LOPROC
值为13,该符号表示处理器特定操作的起始位置
HIPROC
值为15,该符号表示处理器特定操作的结束位置
下面是绑定信息的值的列表
LOCAL
值为0,局部符号,对于目标文件的外部不可见
GLOBAL
值为1,全局符号,外部可见
WEAK
值为2,弱引用符号
NUM
值为3,定义类型的数量
LOOS
值为10,操作系统特定操作的起始位置
GNU_UNIQUE
值为10,独一无二的符号
HIOS
值为12,操作系统特定操作的结束位置
LOPROC
值为13,处理器特定操作的起始位置
HIPROC
值为15,处理器特定操作的结束位置
表示符号所在的段,这个根据符号的类型代表的意义不一样
符号值代表的意思根据st_shndx的不同也不同,上面介绍st_shndx的时候我们基本都涵盖了,值的说明的是:
在可执行文件中,st_value表示符号的虛拟地址。这个虚拟地址对于动态链接器来说十分有用
表示符号的大小,对于包含数据的符号,这个值是数据类型的大小
如果该值为0,表示该符号大小为0或者未知。
该成员暂时没用,为0
重定位表是为了表示段中需要在链接阶段重定位的信息,包括重定位的地址计算方式,在段中的偏移,以及符号信息
注意:如果一个段有需要重定位的信息,则会生成一个针对当前段的重定位表
比如.text的重定位表就叫.rel.text。
重定位表项也是一个结构体,并且结构很简单
typedef struct
{
uint64_t r_offset;
uint64_t r_info;
} Elf64_Rel;
这个字段分两种情况:
该字段表示重定位入口的类型和符号