ilocker:关注 Android 安全(新手) QQ: 2597294287
__linker_init() 在 begin.S 中被调用,并传入两个参数:sp(堆栈指针)、#0。
linker(动态链接器,也称解释器)本身也是一个 shared object,__linker_init() 负责初始化 linker,完成 linker 的重定位等工作。由于在调用 __linker_init() 之前,linker 的重定位还没有完成(GOT还不可用),所以任何对外部变量或函数的引用都会产生 segfault。
由于 linker 的重定位由其自己完成,所以该过程称为 bootstrap(自举)。
__linker_init() 的第一步是分解参数,把由 sp 参数传入的 argc、argv、环境变量、ELF aux vectors 分别装入KernelArgumentBlock 对象的相应成员中,方便访问。
在 auxv 数组中,类型为 AT_BASE 的项存储有 linker 镜像的开始地址。__linker_init() 由此得到 linker_addr,继而得到 elf_hdr(elf header) 和 phdr(program header) 的地址。
__linker_init() 接着填充一个 soinfo 结构变量。
成员 flags 设置上 FLAG_LINKER,后面见此标记就不会打印调试信息,因为那些调试函数在 linker 完成重定位前尚不可用。
phdr_table_get_load_size() 计算整个 linker 镜像的大小,方法是根据 program header ,找到段内存的最大地址和最小地址,在完成页对齐之后相减。
get_elf_exec_load_bias() 得到 linker 在内存中的加载地址,实际上是第一个可加载段(PT_LOAD)在内存中的虚拟地址。
上面描述有误,get_elf_exec_load_bias() 只是计算第一个 LOAD 段在文件中的偏移 p_offset 与其在内存中的偏移 p_vaddr 的一个差值,然后再加上该 elf 文件在内存中的基址。在后面使用时,比如从 LOAD 段里找 .dynamic 时:*dynamic = reinterpret_cast<ElfW(Dyn)*>(load_bias + phdr->p_vaddr);,只需要由 load_bias 加上 .dynamic 的 p_vaddr 就能得到 .dynamic 在内存中的虚拟地址了。
__linker_init() 接着以 linker_so 作为参数,调用 soinfo_link_image()。
在 soinfo_link_image() 中先找到动态链接段(PT_DYNAMIC)的地址,并得到动态链接结构的数量。然后遍历所有动态链接结构,获取诸如“字符串表地址”、“符号表地址”、“重定位表地址”等重要信息,填充到 soinfo 结构的相应成员中。接着加载所有依赖库,但其实在 linker 自举过程中,它并不依赖于任何其他shared object。重定位工作也是在 soinfo_link_image() 中完成,包括 rel 和 plt_rel(与 PLT 关联的重定位项)。
__linker_init() 最后调用 __linker_init_post_relocation(),从名字可以看出,那些需要在重定位之后才能进行的工作都在这个函数中完成。
__linker_init() 最后返回镜像的入口地址,也就是 elf header 中的 e_entry 保存的虚拟地址。
ilocker:关注 Android 安全(新手) QQ: 2597294287