Elf32 文件重定位研究(二)

前面已经说过,elf目标文件如果要链接成可执行文件,那么需要经过符号解析和重定位,才能变成一个可执行程序。那么对于一个elf目标文件,应用程序能否动态加载他,并调用里面的内部函数去执行,答案是可以的。具体应该如何做,下面来看一下。

应用程序动态加载elf目标文件,先把该文件读到内存中,获取目标文件elf头得信息,得到elf文件sections得起始地址,获取各个section信息。然后根据section中得信息,得到核心代码和核心数据得位置,重新分配一块内存,把核心代码和核心数据加载到该内存,这个时候就要根据加载地址,对该elf文件进行符号解析和重定位了。

elf文件中有许多section,对于符号解析,和重定位相关的比较重要的section是rel开头的可重定位section和符号表sym section。

重定位表,是一个由许多重定位表项组成的数组。
下面是ELF里面重定位项的结构:

struct elf32_rel {
  Elf32_Addr  r_offset;
  Elf32_Word   r_info; //SYMBOL<<8+TYPE&0xff. 
} ;

r_offset是代码中需要进行重定位的地址;
r_info 是符号表中该符号的索引;

符号表,是由许多符号表项组成的数组。
下面是“符号表项”的结构:

struct elf32_sym{
  Elf32_Word st_name; //index into the symbol string table
  Elf32_Addr st_value;
  Elf32_Word st_size; //size of the symbol. 0 for no size or unkown size 
  unsigned char st_info; //BIND<<4+TYPE&0x0f
  unsigned char st_other; //0 for reserve
  Elf32_Half st_shndx; //relevant section table index, some indicates special meanings
};

St_name是符号的名称,该值在字符串表中得的index
St_value是该符号在内存中的地址
st_size是该符号的大小,以字节为单位
st_shndx是符号所在的section的index
符号的名称,是由st_name和符号名表决定。St_name是一个指向符号名表的索引值,通过“符号名表基地址”+st_name就可以得到符号名的地址。
之所以采用这种方法,是为了处理“变长”的符号名。
符号的值(也就是符号在内存中的地址,我们要计算的东西)是由st_value和st_shndx决定的。
在relocatable文件中,st_value是符号相对于某个section起始地址的偏移,这个section是由st_shndex指定的(COMMON类型的section除外,它很少会被用到)。
在executable和shared object文件中,st_value包含一个虚拟地址。这个地址是和ELF文件的预定装载地址联系在一起的。在进行动态链接时,我们需要计算“当前装载地址”与“预定装载地址”之间的差。

1 符号解析

把核心代码加载到内存中以后,该加载地址就是这段代码得运行地址,这时符号表中记录的符号的地址显然是不对的,而且符号表中还有未定义的符号,需要为这些未定义符号填写正确的外部函数地址。所以这边可以遍历符号表,对于全局符号,修改其符号表的值,更正为该符号在内存中的真实地址,未定义符号,为其填写真实的外部函数地址

2 重定位

根据上面的动作,符号表已经修正完毕,但是核心代码段里面的数据还是没有改变的,代码最终执行的时候不会依赖符号表,所以需要遍历重定位section,把需要进行重定位的符号,在核心代码中为其纠正过来。

根据重定位表中的r_info信息在sym section中找到该符号,根据st_value得到该符号在ram中的真实位置,然后根据重定位表中的r_offset 得到该重定位代码在内存中的真实地址,利用st_value修改该重定位代码的寻址地址,完成重定位

 

你可能感兴趣的:(Elf32 文件重定位研究(二))