Android在启动一个新的进程的时候,是由execv函数族trap到内核,由kernel去检查和加载可执行文件;kernel做完可执行文件的加载同时会加载/system/bin/linker,然后由linker去加载依赖的动态库,并调用可执行文件的入口函数,完成控制权的转移。
linker本身也是一个ELF格式的动态库文件,它的入口代码位于${bionic}/linker/arch/arm/begin.S文件中
#include <private/bionic_asm.h>
ENTRY(_start)
mov r0, sp
bl __linker_init
/* linker init returns the _entry address in the main image */
bx r0
END(_start)
在_start函数中,栈顶指针寄存器被赋值给r0寄存器作为参数调用__linker_init。__linker_init做完linker的初始化和依赖库的加载后,通过r0返回了可执行文件入口函数,接下来的bx r0 指令就会将控制权移交给可执行文件。
__linker_init() 位于${bionic}/linker/linker_main.cpp文件中:
492 extern "C" ElfW(Addr) __linker_init(void* raw_args) {
493 KernelArgumentBlock args(raw_args);
494
495 // AT_BASE is set to 0 in the case when linker is run by iself
496 // so in order to link the linker it needs to calcuate AT_BASE
497 // using information at hand. The trick below takes advantage
498 // of the fact that the value of linktime_addr before relocations
499 // are run is an offset and this can be used to calculate AT_BASE.
500 static uintptr_t linktime_addr = reinterpret_cast<uintptr_t>(&linktime_addr);
501 ElfW(Addr) linker_addr = reinterpret_cast<uintptr_t>(&linktime_addr) - linktime_addr;
493行的KernelArgumentBlock类在 ${bionic}/libc/private/KernelArgumentBlock.h文件中定义。kernel在加载linker时,已经在堆栈中初始化好了命令行参数、环境变量以及后面的ELF辅助向量(Auxiliary Vector)。raw_args即_start中传入的栈顶指针寄存器,通过args对象只是对上述信息进行封装,提供一系列读写接口而已。堆栈的内存布局如下:
position content size (bytes) comment
------------------------------------------------------------------------
stack pointer -> [ argc = number of args ] 4
[ argv[0] (pointer) ] 4
[ argv[1] (pointer) ] 4
[ argv[..] (pointer) ] 4 * n
[ argv[n - 1] (pointer) ] 4
[ argv[n] (pointer) ] 4 = NULL
[ envp[0] (pointer) ] 4
[ envp[1] (pointer) ] 4
[ envp[..] (pointer) ] 4
[ envp[term] (pointer) ] 4 = NULL
[ auxv[0] (Elf32_auxv_t) ] 8
[ auxv[1] (Elf32_auxv_t) ] 8
[ auxv[..] (Elf32_auxv_t) ] 8
[ auxv[term] (Elf32_auxv_t) ] 8 = AT_NULL vector
[ padding ] 0 - 16
[ argument ASCIIZ strings ] >= 0
[ environment ASCIIZ str. ] >= 0
(0xbffffffc) [ end marker ] 4 = NULL 结束
(0xc0000000) < bottom of stack > 0 (virtual)
500行定义的linker_addr变量就是linker文件在内存中实际映射的基地址,在Android 7之前,linker_addr是通过是直接从ELF辅助向量中读取AT_BASE获得。Android 8之后,通过定义静态变量linktime_addr是来计算linker_addr。这里通过对linker反汇编,来理解这两行代码的trick,是如何计算linker_addr,
先找到__linker_init函数的实现
0xf57ed的指令r2 = r2(0x78c2e) + pc(linker_addr + 0xf582) = linker_addr + 0x881B0; 因为实际在内存运行的时候指令寄存器pc的值是基地址+偏移地址,所以实际当前的pc = linker_addr + 0xf582,armv7三级流水线pc等于取指地址0xf582。
接来下的指令ldr r3, [r2]也就是将linker_addr+0x881B0中的内容赋值给r3,图2中0x881B0地址的值等于0x881B0,所以r3的值等于0x881B0。
然后在地址0xf588的指令,r5 = r2(linker_addr+0x881B0) - r3(0x881B0) = linker_addr
503 #if defined(__clang_analyzer__)
504 // The analyzer assumes that linker_addr will always be null. Make it an
505 // unknown value so we don't have to mark N places with NOLINTs.
506 //
507 // (`+=`, rather than `=`, allows us to sidestep a potential "unused stor