你的位置:首页 > 操作系统

[操作系统]android linker —— __linker_init()



__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)在内存中的虚拟地址。


__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 保存的虚拟地址。