zoukankan      html  css  js  c++  java
  • 安卓linker源码阅读01

    ---恢复内容开始---

    感觉想看源码还是得先了解安卓的makefile,这样知道是怎么编译的

    参考
    Android编译系统参考手册
    http://android.cloudchou.com/build/core/main.php
    深入浅出Android makefile
    http://blog.csdn.net/lizzywu/article/details/12842031

    参考:看雪 linker阅读笔记一

    ioniclinkerarcharmegin.s

    ENTRY(_start)
    mov r0, sp
    bl __linker_init

    /* linker init returns the _entry address in the main image */
    mov pc, r0
    END(_start)

    调用 __linker_init函数,返回值为main函数入口地址

    __linker_init 在ioniclinkerlinker.cpp

    第一行就用了一个类,跟过去
    bioniclibcprivateKernelArgumentBlock.h

    从内核解析出来argc,argv,envp(程序环境变量),auxv(辅助向量,key:value的形式,类似python的字典)

    unsigned long getauxval(unsigned long type, bool* found_match = NULL)
    给key(参数type),查value(参数found_match),注意看注释:Similar to ::getauxval but doesn't require the libc global variables
    去看看man手册,http://www.man7.org/linux/man-pages/man3/getauxval.3.html,一会儿能用到

    ElfW(auxv_t)* v = auxv

    ElfW(auxv_t) 就是下面这个结构,在elf.h

    typedef struct {
    __u32 a_type;
    union {
    __u32 a_val;
    } a_un;
    } Elf32_auxv_t;

    ElfW()宏在link.h里:

    define ElfW(type) Elf32_ ## type

    加起来就是Elf32_auxv_t了

    继续__linker_init

    ElfW(Addr) linker_addr = args.getauxval(AT_BASE);
    ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);
    ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)>(linker_addr);
    ElfW(Phdr)
    phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);

    看man手册,就知道是什么意思了。获取镜像基址,可执行文件的入口点,获取header指针,程序头执指针

    继续__linker_init

    soinfo linker_so("[dynamic linker]", nullptr);

    linker_so.base = linker_addr;
    linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
    linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
    linker_so.dynamic = nullptr;
    linker_so.phdr = phdr;
    linker_so.phnum = elf_hdr->e_phnum;
    linker_so.flags |= FLAG_LINKER;

    ======================================================================================
    /* Returns the size of the extent of all the possibly non-contiguous

    • loadable segments in an ELF program header table. This corresponds
    • to the page-aligned size in bytes that needs to be reserved in the
    • process' address space. If there are no loadable segments, 0 is
    • returned.
    • If out_min_vaddr or out_max_vaddr are not null, they will be
    • set to the minimum and maximum addresses of pages to be reserved,
    • or 0 if there is nothing to load.
      */

    /*
    从ELF program header table中读取,返回不连续的可以被加载的segment
    要在内存中保留的内存范围大小
    */

    size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count,
    ElfW(Addr)* out_min_vaddr,
    ElfW(Addr)* out_max_vaddr) {
    /*
    实际上只用到了2个参数:
    phdr_table
    phdr_count
    */
    ElfW(Addr) min_vaddr = UINTPTR_MAX;
    ElfW(Addr) max_vaddr = 0;

    bool found_pt_load = false;
    //遍历所有的phdr,找到PT_LOAD标志的segment
    for (size_t i = 0; i < phdr_count; ++i) {
    const ElfW(Phdr)* phdr = &phdr_table[i];

    if (phdr->p_type != PT_LOAD) {
      continue;
    }
    found_pt_load = true;
    
    if (phdr->p_vaddr < min_vaddr) {
      min_vaddr = phdr->p_vaddr;
    }
    

    //phdr->p_vaddr 段的第一个字节将被放到内存中的虚拟地址
    if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
    max_vaddr = phdr->p_vaddr + phdr->p_memsz;
    }
    }
    if (!found_pt_load) {
    min_vaddr = 0;
    }

    //PAGE_START PAGE_END 页对齐的宏
    min_vaddr = PAGE_START(min_vaddr);
    max_vaddr = PAGE_END(max_vaddr);

    if (out_min_vaddr != nullptr) {
    *out_min_vaddr = min_vaddr;
    }
    if (out_max_vaddr != nullptr) {
    *out_max_vaddr = max_vaddr;
    }
    return max_vaddr - min_vaddr;
    }

    ===========================================

    /* Compute the load-bias of an existing executable. This shall only

    • be used to compute the load bias of an executable or shared library
    • that was loaded by the kernel itself.
    • Input:
    • elf -> address of ELF header, assumed to be at the start of the file.
    • Return:
    • load bias, i.e. add the value of any p_vaddr in the file to get
    • the corresponding address in memory.
    • bias 偏移,此函数计算load的偏移。

    /
    static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)
    elf) {
    ElfW(Addr) offset = elf->e_phoff;

    //phdr_table 第一个phdr phdr_end 最后一个phdr
    const ElfW(Phdr)* phdr_table = reinterpret_cast<const ElfW(Phdr)>(reinterpret_cast<uintptr_t>(elf) + offset);
    const ElfW(Phdr)
    phdr_end = phdr_table + elf->e_phnum;

    for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_end; phdr++) {
    if (phdr->p_type == PT_LOAD) {
    return reinterpret_cast<ElfW(Addr)>(elf) + phdr->p_offset - phdr->p_vaddr;
    }
    }
    return 0;
    }

    // return reinterpret_cast<ElfW(Addr)>(elf) + phdr->p_offset - phdr->p_vaddr;
    //这句没看懂,elf文件基地址+程序头的文件偏移 - 程序头的内存偏移这是个啥
    //这个就是计算一个文件头和虚拟内存偏移的差值,后面用的时候再加上这个虚拟内存值

    继续__linker_init

    if (!(linker_so.PrelinkImage() && linker_so.LinkImage(nullptr)))

    ===========================================
    /* Return the address and size of the ELF file's .dynamic section in memory,

    • or null if missing.
    • Input:
    • phdr_table -> program header table
    • phdr_count -> number of entries in tables
    • load_bias -> load bias
    • Output:
    • dynamic -> address of table in memory (null on failure).
    • Return:
    • void
      /
      void phdr_table_get_dynamic_section(const ElfW(Phdr)
      phdr_table, size_t phdr_count,
      ElfW(Addr) load_bias, ElfW(Dyn)** dynamic) {
      dynamic = nullptr;
      for (const ElfW(Phdr)
      phdr = phdr_table, *phdr_limit = phdr + phdr_count; phdr < phdr_limit; phdr++) {
      if (phdr->p_type == PT_DYNAMIC) {
      dynamic = reinterpret_cast<ElfW(Dyn)>(load_bias + phdr->p_vaddr);
      return;
      }
      }
      }

    //靠,这里又不懂了,原来返回的是一个PT_LOAD的segment的偏移(文件地址-内存地址),
    //到了这儿,和一个PT_DYNAMIC的segmet加在一起,这是在干啥

    bool soinfo::LinkImage(const android_dlextinfo* extinfo)函数中,重要的是
    2184这几行的代码
    if (plt_rel != nullptr) {
    DEBUG("[ relocating %s plt ]", name);
    if (Relocate(plt_rel, plt_rel_count)) { //这里1
    return false;
    }
    }
    if (rel != nullptr) {
    DEBUG("[ relocating %s ]", name);
    if (Relocate(rel, rel_count)) { //这里2
    return false;
    }

    ifuncs不造是什么

    然后到2229行

    ===========================================

    ===========================================

    ===========================================

    ===========================================

    ---恢复内容结束---

    感觉想看源码还是得先了解安卓的makefile,这样知道是怎么编译的

    参考
    Android编译系统参考手册
    http://android.cloudchou.com/build/core/main.php
    深入浅出Android makefile
    http://blog.csdn.net/lizzywu/article/details/12842031

    看雪:linker阅读笔记一

  • 相关阅读:
    Centos8安装MySQL8(社区版)
    DateTime.Now 在.netcore下的格式问题
    HP Socket FAQ
    docker基本操作
    Win10-Docker和VMware运行环境冲突解决办法
    Centos8安装docker-compose
    .net5 RSA
    密码规则之数字、小写、大写、特殊字符,至少满足3个
    .net5 应用程序启动和停止事件
    MySQL中国省市区数据表
  • 原文地址:https://www.cnblogs.com/Lnju/p/4714109.html
Copyright © 2011-2022 走看看