android linker 执行流程

本文详细介绍了Android平台上的Linker执行流程,从_kernel_argument_block_开始,经过_prelink_image_函数解析PT_DYNAMIC段,到调用printf函数并进行重定位的过程。在汇编指令层面,讲解了如何修改Sym.Value以指向外部函数地址。最终,通过_get_libdl_info_和__linker_init_post_relocation_完成符号重定位,确保构造函数在main函数执行前完成初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通过前一篇的博客,我们知道,在linux kernel加载完可执行程序后,在需要解释器的情况下,返回用户空间时会先跳到解释器的函数中执行,对于android平台而已,就是先跑到_start()函数,然后再跳转到__linker_init()。

bionic/linker/linker.cpp

extern "C" ElfW(Addr) __linker_init(void* raw_args) {
4431    KernelArgumentBlock args(raw_args);
4432  
4433    ElfW(Addr) linker_addr = args.getauxval(AT_BASE);   //interp_load_addr 解释器 加载到内存的地址
4434    ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);   //可执行程序的入口地址
4435    ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);  //linker文件的起始位置  Elf64_Ehdr
4436    ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);   // linker中的程序头表  Elf64_Phdr
4437  
4438    soinfo linker_so(nullptr, nullptr, nullptr, 0, 0);    //soinfo 一个很重要的结构体
...
4452  
4453    linker_so.base = linker_addr;
4454    linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);  //加载的PT_LOAD段的大小
4455    linker_so.load_bias = get_elf_exec_load_bias(elf_hdr); //类似于一个加载的偏移
4456    linker_so.dynamic = nullptr;
4457    linker_so.phdr = phdr;   //
4458    linker_so.phnum = elf_hdr->e_phnum;  //
4459    linker_so.set_linker_flag();
4460  
4461    // Prelink the linker so we can access linker globals.
4462    if (!linker_so.prelink_image()) __linker_cannot_link(args);  //解析(遍历)PT_DYNAMIC 段(.dynamic节)里面的内容
...
4470    if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args); //根据.rela.dyn .rela.plt进行重定位
...
4481    // Initialize the main thread (including TLS, so system calls really work).
4482    __libc_init_main_thread(args);
4483  
4484    // We didn't protect the linker's RELRO pages in link_image because we
4485    // couldn't make system calls on x86 at that point, but we can now...
4486    if (!linker_so.protect_relro()) __linker_cannot_link(args); //将PT_GNU_RELRO段指向的内存地址通过mprotoct函数设置为PROT_READ
4487  
4488    // Initialize the linker's static libc's globals
4489    __libc_init_globals(args);
4490  
4491    // Initialize the linker's own global variables
4492    linker_so.call_constructors();
4493  
4494    // Initialize static variables. Note that in order to
4495    // get correct libdl_info we need to call constructors
4496    // before get_libdl_info().
4497    solist = get_libdl_info();
4498    sonext = get_libdl_info();
4499    g_default_namespace.add_soinfo(get_libdl_info());
4500  
4501    // We have successfully fixed our own relocations. It's safe to run
4502    // the main part of the linker now.
4503    args.abort_message_ptr = &g_abort_message;
4504    ElfW(Addr) start_address = __linker_init_post_relocation(args, linker_addr);
4505  
4506    INFO("[ Jumping to _start (%p)... ]", reinterpret_cast<void*>(start_address));
4507  
4508    // Return the address that the calling assembly stub should jump to.
4509    return start_address;
4510  }

KernelArgumentBlock 这个类是用来解析从kernel 拷贝过来的参数

bionic/libc/private/KernelArgumentBlock.h

/*   根据前一篇博客的分析,kernel拷贝参数到用户空间的分布如下
 *                  |   0               |
 *                  |   0               |
 *                  |   elf_info[valn]  |
 *                  |   elf_info[id0]   |
 *                  |   ...             |
 *                  |   elf_info[val0]  |
 *                  |   elf_info[id0]   |
 *                  |    0              |
 *                  |   envn            |
 *                  |   ...             |
 *                  |   env0            |
 *                  |   0               |
 *                  |   argn            |
 *                  |   ....            |
 *                  |   arg0            |
 *    raw_args ->   |   argc            |
 */
class KernelArgumentBlock {
34   public:
35    KernelArgumentBlock(void* raw_args) {
36      uintptr_t* args = reinterpret_cast<uintptr_t*>(raw_args);
37      argc = static_cast<int>(*args);  //传递的参数个数,不包括环境变量参数,也就是上面的argc
38      argv = reinterpret_cast<char**>(args + 1); //指向arg0 的位置
39      envp = argv + argc + 1;  //加上argc,再加1,就指到env0 的位置
40  
41      // Skip over all environment variable definitions to find the aux vector.
42      // The end of the environment block is marked by a NULL pointer.
43      char** p = envp;
44      while (*p != NULL) { //等于NULL,就指到envn 后面 0的位置
45        ++p;
46      }
47      ++p; // Skip the NULL itself.//再加1就指到elf_info[id0]位置
48  
49      auxv = reinterpret_cast<ElfW(auxv_t)*>(p); 
50    }
51  
52    // Similar to ::getauxval but doesn't require the libc global variables to be set up,
53    // so it's safe to call this really early on.
54    unsigned long getauxval(unsigned long type) {
55      for (ElfW(auxv_t)* v = auxv; v->a_type != AT_NULL; ++v) {
56        if (v->a_type == type) {
57          return v->a_un.a_val;  //返回a_type 对应的value
58        }
59      }
60      return 0;
61    }
62  
63    int argc;
64    char** argv;
65    char** envp;
66    ElfW(auxv_t)* auxv;
67  
68    abort_msg_t** abort_message_ptr;
69  
70   private:
71    DISALLOW_COPY_AND_ASSIGN(KernelArgumentBlock);
72  };


解析完参数后,接下来创建一个soinfo结构体linker_so,这是一个局部变量,调用它的prelink_image()函数

bionic/linker/linker.cpp

3495  bool soinfo::prelink_image() {
3496    /* Extract dynamic section */
3497    ElfW(Word) dynamic_flags = 0;
3498    phdr_table_get_dynamic_section(phdr, phnum, load_bias, &dynamic, &dynamic_flags); //获得linker加载到内存后,.dynamic这个节在内存中的位置
...
/*  ElfW(Dyn)对应的数据结构如下
 *  linux-4.10/include/uapi/linux/elf.h
 *  typedef struct {
 *      Elf64_Sxword d_tag;		/* entry tag value */
 *      union {
 *          Elf64_Xword d_val; //保存值
 *          Elf64_Addr d_ptr;  //保存地址
 *      } d_un;
 *   } Elf64_Dyn;
*/
3529    for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {  //解析(遍历)PT_DYNAMIC 段(.dynamic节)里面的内容
3530      DEBUG("d = %p, d[0](tag) = %p d[1](val) = %p",
3531            d, reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
3532      switch (d->d_tag) {
3533        case DT_SONAME:
3534          // this is parsed after we have strtab initialized (see below).
3535          break;
...
3566        case DT_STRTAB:  // str table 的位置
3567          strtab_ = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
3568          break;
3569  
3570        case DT_STRSZ: //str table 每一项的大小
3571          strtab_size_ = d->d_un.d_val;
3572          break;
3573  
3574        case DT_SYMTAB:  // sym table 的位置
3575          symtab_ = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr);
3576          break;
3577  
/*
 *  typedef struct elf64_sym {
 *    Elf64_Word st_name;  	  //4 by
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值