android linker自举

本文探讨了Android Linker的自举过程,如何通过其内部功能完成自身的链接。详细介绍了自举实现,包括入口函数及初始化的步骤。同时,文章还涉及linker初始化阶段,如Elf文件格式的理解,装载后堆栈的状态,以及初始化执行中的符号解释和重定位等关键操作。
AI助手已提取文章相关产品:

Android Linker Bootstrap

linker作为动态链接器,但本身也是一个共享库,那么它由谁来链接?

1. linker自举

linker的链接是由它本身完成的,称为自举(bootstrap)。
linker不能依赖其他共享库,其内部的全局和静态变量等的relocation必须由其自身完成。

2. 自举实现

linker的入口函数,即为其自举执行的入口,即_start

ENTRY(_start)
  // Force unwinds to end in this function.
  .cfi_undefined x30

  mov x0, sp
  bl __linker_init

  /* linker init returns the _entry address in the main image */
  br x0
END(_start)

mov x0, sp :以栈指针作为参数,借用x0寄存器传参给__linker_init,以便后续函数获取参数和环境变量等;

bl __linker_init : 跳转到_linker_init函数,开始进行实际的初始化工作;

br x0 : 用于执行完linker初始化后,回到执行进程的实际入口;

3. linker初始化

3.1 Elf format

在开始读代码前,要牢牢记住ELF文件的格式和数据结构:
Elf format

3.2 linker执行前的堆栈

linker实际进行初始化前,也就是在linker elfkernel装载后,进程堆栈内已经存在一些必要的信息供后续linker使用,在此不展开详细讨论,只说明当前进程堆栈的情况(具体可以参照:)

Linker init stack

3.3 初始化执行

__linker_init实际进行linker的初始化,符号解释,重定位等一系列操作:

/*
 * This is the entry point for the linker, called from begin.S. This
 * method is responsible for fixing the linker's own relocations, and
 * then calling __linker_init_post_relocation().
 *
 * Because this method is called before the linker has fixed it's own
 * relocations, any attempt to reference an extern variable, extern
 * function, or other GOT reference will generate a segfault.
 */
extern "C" ElfW(Addr) __linker_init(void* raw_args) {
  // Initialize TLS early so system calls and errno work.
  //初始化主线程一些信息,主要是tcb/tls
  KernelArgumentBlock args(raw_args);   //读取压入栈中的参数和环境变量,堆栈情况参看上面3.2图片
  bionic_tcb temp_tcb __attribute__((uninitialized));
  linker_memclr(&temp_tcb, sizeof(temp_tcb));
  __libc_init_main_thread_early(args, &temp_tcb);

  // When the linker is run by itself (rather than as an interpreter for
  // another program), AT_BASE is 0.
  ElfW(Addr) linker_addr = getauxval(AT_BASE);//通过辅助向量获取linker interpreter的装载地址,对于linker自举来说,linker_addr为0(linker没有interpreter),如果为0,则通过进程执行文件的program header获取基址
  if (linker_addr == 0) {
    // The AT_PHDR and AT_PHNUM aux values describe this linker instance, so use
    // the phdr to find the linker's base address.
    ElfW(Addr) load_bias;
    get_elf_base_from_phdr(
      reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR)), getauxval(AT_PHNUM),
      &linker_addr, &load_bias);//计算linker elf的基址和load bias(对于linker来说,load bias为0)
  }

  ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);//对于linker来说,linker_addr == elf_hdr
  ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);//linker程序头表地址

  // string.h functions must not be used prior to calling the linker's ifunc resolvers.
  const ElfW(Addr) load_bias = get_elf_exec_load_bias(elf_hdr);//对于linker来说,load bias为0
  //与ifunc的重定位相关,现在只有string.h有相关的ifunc实现(获取IFUNC resolver的地址,重定位.rela.iplt),详细可以看:https://github.com/xuwakao/wakao-blogs/blob/master/android-linker/android-ifunc.md
  call_ifunc_resolvers(load_bias);

  soinfo tmp_linker_so(nullptr, nullptr, nullptr, 0, 0);

  tmp_linker_so.base = linker_addr;
  tmp_linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
  tmp_linker_so.load_bias = load_bias;
  tmp_linker_so.dynamic = nullptr;
  tmp_linker_so.phdr = phdr;
  tmp_linker_so.phnum = elf_hdr->e_phnum;
  tmp_linker_so.set_linker_flag();

  // Prelink the linker so we can access linker globals.
  //prelink_image的作用比较简单,就是解释.dynamic section,获取动态链接符号表位置、重定位表位置、so名字等等一些基础全局信息,方便后续使用
  if (!tmp_linker_so.prelink_image()) __linker_cannot_link(args.argv[0]);
  //link_image进行符号解释和重定位(代码太多,就不一一展开了,就是根据符号表,重定位表等等循环解释全部的符号,包括全局变量,函数)
  if (!tmp_linker_so.link_image(SymbolLookupList(&tmp_linker_so), &tmp_linker_so, nullptr, nullptr)) __linker_cannot_link(args.argv[0]);

  return __linker_init_post_relocation(args, tmp_linker_so);
}

整个初始化流程为:

  1. 初始化main thread,主要是TCB/TLS。详细参考:Linker和主线程初始化

  2. 计算一些地址相关,elf_hdrphdr等。

  3. ifunc解释。详细参考:Android IFUNC支持

  4. 执行linker的符号解释,重定位等工作。

  5. linker重定位完成后的,最终完成初始化linker,加载和解释exe等工作,并返回exe main。详细参考:Linker重定位后初始化

您可能感兴趣的与本文相关内容

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值