http://blog.csdn.net/zhejiang9/article/details/8470891
我又来啦,今天是本系列介绍ELF文件的最后一篇教程.跟随大家一起了解了ELF文件的大致结构.整个结构其实是很明朗的,根据ELF头,程序头,节头.从节头里提取不同的类型,到不同的节表内去获取不同的信息,今天这里主要介绍重定位表.也是常用的节表之一.
有了符号名和动态库的名字操作系统就可以为我们引入函数了,但在我们的程序中是谁会用这些外部函数呢,系统解析出来的函数地址应该给谁呢?这就是重定位表的功劳了!
重定位表的类型有两种SHT_REL和SHT_RELA,我们只谈SHT_REL.
- typedef struct
- {
- Elf32_Addr r_offset; /* Address */
- Elf32_Word r_info; /* Relocation type and symbol index */
- } Elf32_Rel;
重定位表其实很简单,它的r_offset成员给出了需要重定位内容的地址,而它的r_info字段给出了两条信息,一条是与此重定位内容相关的符号,一条是重定位的类型,在elf.h中分别有
- #define ELF32_R_SYM(val) ((val) >> 8)
- #define ELF32_R_TYPE(val) ((val) & 0xff)
- #define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
从成员r_info中获取符号信息和重定位信息,符号信息就是一个符号表的索引,32位下占用这个成员的高24位,剩余的8位就是重定位类型了.符号信息就是一个符号表中的索引.
在i386上从外部引入的动态函数重定位类型是R_386_JMP_SLOT.由这个类型的名称可以看出动态连接在Linux上是处理器密切相关的东西.刚说r_info字段包含了一个符号表中的索引,对于从外部引入的动态函数来说那个符号表就是“动态符号表”,它在节头结构中的类型值为SHT_DYNSYM。r_offset字段是一个地址,加载之初被r_offset指向的内容——也就是一个外部符号的地址——并不正确,操作系统就根据重定位信息引用的符号找到那个地址,然后修改r_offset所指向的内容。这部分我们在第五讲中已经得到证实,发现GOT表内的地址并不是真正的函数地址入口.这就是因为linux的"懒模式"机制.
下面给出代码:
- #include "readRel.h"
- void display_rel(Elf32_Ehdr *ehdr,Elf32_Shdr *shdr)
- {
- Elf32_Rel *dyn = (Elf32_Rel *)((char*)ehdr + shdr->sh_offset);
- int relSize = shdr->sh_size / shdr->sh_entsize;
- char *symName = (char*)(((Elf32_Shdr *)((char*)ehdr + ehdr->e_shoff + shdr->sh_link * sizeof(Elf32_Shdr)))->sh_offset
- + (char*)ehdr);
- printf("rel = 0x%x\n",(char*)dyn);
- printf("relSize = 0x%x\n",(char*)relSize);
- printf("symName = 0x%x\n",(char*)symName);
- printf("Relocation section '.rel.dyn' at offset 0x%x contains %d entries:\n",dyn,relSize);
- int i = 0;
- printf("%-10s%-10s%-10s%s\n","Offset","Info","Type","Sym.Name");
- for(i = 0;i < relSize;i++){
- printf("%-10x",dyn->r_offset);
- printf("%-10x",dyn->r_info);
- printf("%-10d",ELF32_R_TYPE(dyn->r_info));
- printf("%d",ELF32_R_SYM(dyn->r_info)); //注意此处并非字符串的名字,而是指向符号表的索引
- printf("\n");
- dyn++;
- }
- }
- void displayRel(Elf32_Ehdr *ehdr,Elf32_Shdr *shdr)
- {
- int py = ehdr->e_shstrndx * sizeof(Elf32_Shdr);
- Elf32_Shdr *symtab = (Elf32_Shdr *)((char*)shdr + py);
- char *szShdrName = (char*)(symtab->sh_offset + (char*)ehdr);
- int i = 0;
- for(i = 0; i < ehdr->e_shnum; i++){
- if(shdr->sh_type == SHT_REL){
- display_rel(ehdr,shdr);
- }
- shdr++;
- }
- }
前面我们讲了,怎么手动通过GDB拦截printf参数,下一节,我们将通过程序来拦截printf的参数,使之永远都输入我们给定的字符串.
尽情期待...谢谢~~~~
我也是菜鸟.一起努力学习.
再见