Return-to-dl-resolve

前置知识:
ELF可执行文件由ELF头部,程序头部和其对应的段,节头部表和其对应的节组成。
ELF文件结构如果想深入了解的话可以看下《程序员的自我修养》的第三章 P70
如果一个可执行文件要进行动态链接,它的程序头部表将包含类型为PT_DYNAMIC的段,它包含.dynamic节结构如下

typedef struct {  
    Elf32_Sword d_tag;  
    union {  
        Elf32_Word d_val;  
        Elf32_Addr d_ptr;  
    } d_un;  
} Elf32_Dyn;

其中tag对应着每个节

Return-to-dl-resolve_第1张图片
image.png

JMPREL对应 .rel.plt 用来保存运行时重定位表
SYMTAB对应着符号表.dymsym 它是一个Elf32_Sym结构的数组
STRTAB对应着字符串表.dymstr 用来存放动态链接时需要用到的字符串

ELF中节中包涵目标文件的所有信息,节的结构如下

typedef struct {  
    Elf32_Word sh_name;      // 节头部字符串表节区的索引  
    Elf32_Word sh_type;      // 节类型  
    Elf32_Word sh_flags;     // 节标志,用于描述属性  
    Elf32_Addr sh_addr;      // 节的内存映像  
    Elf32_Off  sh_offset;    // 节的文件偏移  
    Elf32_Word sh_size;      // 节的长度  
    Elf32_Word sh_link;      // 节头部表索引链接  
    Elf32_Word sh_info;      // 附加信息  
    Elf32_Word sh_addralign; // 节对齐约束  
    Elf32_Word sh_entsize;   // 固定大小的节表项的长度  
} Elf32_Shdr;

.rel.plt节是用于函数重定位 ,.rel.dyn节是用于变量重定位

typedef struct {  
    Elf32_Addr r_offset;    // 对于可执行文件,此值为虚拟地址  
    Elf32_Word r_info;      // 符号表索引  
} Elf32_Rel;  
  

.rel.plt 中的offset 对应着r_offset 是函数在.got.plt表中的位置, Info对应着r_info的高24位,Type对应着r_info的低8位

Return-to-dl-resolve_第2张图片
image.png

GOT表是一个简单数组,存放各种绝对地址。
GOT[0] 存放的是动态链接数组_dynamic[]的地址,GOT[1]存放的是一个link_map类型的指针
GOT[2]存放的是动态链接器的解析函数的入口地址

PLT表是由一小段一小段代码组成的(几条控制跳转的汇编语句)

PLT0:  
push GOT[1];  
jmp GOT[2];  
.......  
PLTn:  
jmp GOT[x+n];  
push n; #relocation offset of symbol ,the second argument of _dl_runtime_resolve  
jmp PLT0; call the rtld

程序第一次调用位于.got.plt表中的函数时 ,会跳转回plt表中
然后将参数压入栈中 ,这个参数是reloc_offset 是函数在got表中的偏移
接着会跳转到PLT0,将link_map压入栈中,在调用_dl_runtime_resolve函数来对函数进行重定位
这个函数原型是_dl_runtime_resolve(link_map,reloc_offset)
调用_dl_runtime_resolve函数后
先根据reloc_offest 找到要解析符号的重定位项结构信息reloc

ELF32_Rel *rel_entry = JMPREL + reloc_offset

然后在根据reloc->r-info 找到符号在符号表中的索引

ELF32_Sym *sym_entry = SYMTAB[ELF32_R_SYM(rel_entry->r_info)]
#define ELF32_R_SYM(info) ((info)>>8) #符号在符号表中的索引,占r_offset的高24位  

其中还会对重定向符号的类型进行检测

assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
#define ELF32_R_TYPE(info) ((unsigned char)(info))重定位类型 占r_offset的低8位  

接着在通过.dynsym项中的st_name找到符号在字符串表.dymstr中的偏移

char *sym_name = STRTAB + sym_entry->st_name

最后通过fixup()函数遍历各个库的符号表 找到函数地址再将地址写入GOT表对应的位置中,当程序第二次调用这个函数时,就会直接调用这个函数

如果想对动态链接更进一步了解的话
可以看《Linux动态链接机制研究及应用》、《程序员的自我修养》

你可能感兴趣的:(Return-to-dl-resolve)