该方法的主要原理是利用dl_runtime_resolve函数来对动态链接的函数进行重定位。
在linux下,ELF想要调用动态函数库中的函数,为了避免没必要的消耗,而采用了延迟绑定的方法,其核心思想就是函数用到时才对该函数进行绑定(符号查找及重定位),如果没有用到便不会绑定,以便减少资源的消耗。

由于延迟绑定机制,所以在第一次调用puts函数时,0x80496f8内并没有存放着真实的write地址,而是跳转到了下一条指令上。
然后push 0再跳转到PLT表的头部,再push 0x80496f0,最后跳转到dl_runtime_resolve函数上
而push的0,和0x80496f0正是dl_runtime_resolve的两个参数reloc_arg和link_map
reloc_arg=0是要调用的函数也就是puts在重定向链接表(.rel.plt)中的偏移link_map=0x80496f0则是指向了.dynamic节(GOT+4),使之可以访问到.dynamic而dl_runtime_resolve函数内实际上是由_dl_fixup实现的,上面的两个参数也都是传到了这个函数里面
_dl_fixup源代码如下:
_dl_fixup(struct link_map **l*, ElfW(Word) *reloc_arg*)
{
*//* *首先通过参数**reloc_arg**计算重定位入口,这里的**JMPREL**即**.rel.plt**,**reloc_offset**即**reloc_arg*
const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
*//* *然后通过**reloc->r_info**找到**.dynsym**中对应的条目*
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
*//* *这里还会检查**reloc->r_info**的最低位是不是**R_386_JUMP_SLOT=7*
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
*//* *接着通过**strtab+sym->st_name**找到符号表字符串,**result**为**libc**基地址*
result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
*// value**为**libc**基址加上要解析函数的偏移地址,也即实际地址*
value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS (result) + sym->st_value) : 0);
*//* *最后把**value**写入相应的**GOT**表条目中*
return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
}总结一下_dl_fixup 函数的过程
通过link_map参数得出.rel.plt`.dynsym\.dynstr`三个表的地址
relocinfo参数索引(忽略掉最后两位)来找到它在.dynsym(符号表)中的位置,记作symst_name,来确定它在.dynstr(字符串表)中的名字,记作result,而result也就是libc的基地址valuereadelf文章中所接触到的三个表都存在于
PT_DYNAMIC段的.dynamic节中,下面以readelf来说一下这三个表的主要作用

例如:wirte函数
r_offset=0x401cr_info=0x807那么就可以通过r_info去掉最后两位07来确定write在符号表中的位置

包含了动态链接符号表
.dynsym[8]指向的就是write函数,而value的值则是该函数的偏移量

字符串表节区包含了以\x00结尾的字符序列,通常称为字符串

Ret2dlresolve学习(2)(https://cloud.tencent.com/developer/article/1740197)