编程语言
首页 > 编程语言> > android源码分析之linker初始化

android源码分析之linker初始化

作者:互联网

linker入口函数

在内核程序加载了ELF可执行文件后会判断是否含有动态链接信息。如果需要进行动态链接就会通过ELF可执行文件的PT_INTERP程序段获得需要加载的加载器的路径,然后将应用层的入口函数设置为加载器linker的入口函数。

__linker_init()

extern "C" ElfW(Addr) __linker_init(void* raw_args) {
 
  //获得linker的基地址(在内核中保存在了新进程默认栈中)
  ElfW(Addr) linker_addr = getauxval(AT_BASE);
  if (linker_addr == 0) {
    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文件头
  ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);   
  //linker的e_phoff程序段偏移地址 
  ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);
  
  //初始化linker对应的ELF文件的soinfo结构体,soinfo结构体保存了此ELF的基本信息
  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 = get_elf_exec_load_bias(elf_hdr);
  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_image()会解析ELF文件dynamic程序段的各种类型的segment节区的信息
  if (!tmp_linker_so.prelink_image()) __linker_cannot_link(args.argv[0]);

  //link_image()会对linker进行重定位
  if (!tmp_linker_so.link_image(SymbolLookupList(&tmp_linker_so), &tmp_linker_so, nullptr, nullptr)) __linker_cannot_link(args.argv[0]);

  //__linker_init_post_relocation()会重定位待执行的ELF文件,并且会加载ELF文件依赖的其他库文件并进行重定位
  return __linker_init_post_relocation(args, tmp_linker_so);
}

prelink_image与重定位相关的核心代码如下

  uint32_t needed_count = 0;
  for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
    DEBUG("d = %p, d[0](tag) = %p d[1](val) = %p",
          d, reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
    switch (d->d_tag) {

      case DT_STRTAB:    //对应的是.dynstr节区
        strtab_ = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
        break;

      case DT_STRSZ:    
        strtab_size_ = d->d_un.d_val;
        break;

      case DT_SYMTAB:   //对应的是.symtab节区
        symtab_ = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr);
        break;

      case DT_JMPREL:  //对应的是.rel.plt节区
        #if defined(USE_RELA)
        plt_rela_ = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
        #else
        plt_rel_ = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
        #endif
        break;

      case DT_REL:      //对应的是.rel.dyn节区
        rel_ = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
        break;

      ......

  }

link_image()->soinfo::relocate(),soinfo::relocate()的主要代码如下。

#if defined(USE_RELA)        //如果使用了显式加数(一般64位使用)
  if (rela_ != nullptr) {
    
    if (!plain_relocate<RelocMode::Typical>(relocator, rela_, rela_count_)) {
      return false;
    }
  }
  if (plt_rela_ != nullptr) {
   
    if (!plain_relocate<RelocMode::JumpTable>(relocator, plt_rela_, plt_rela_count_)) {
      return false;
    }
  }
#else                        //如果没有使用显式加数(一般32位不使用)
  if (rel_ != nullptr) {
   //.rel.dyn节区中的重定位信息进行重定位
    if (!plain_relocate<RelocMode::Typical>(relocator, rel_, rel_count_)) {
      return false;
    }
  }
  if (plt_rel_ != nullptr) {
    //.rel.plt节区中的重定位信息进行重定位
    if (!plain_relocate<RelocMode::JumpTable>(relocator, plt_rel_, plt_rel_count_)) {
      return false;
    }
  }
#endif

plain_relocate

plain_relocate()->plain_relocate_impl()->process_relocation()->process_relocation_impl()。

static bool process_relocation_impl(Relocator& relocator, const rel_t& reloc) {
  constexpr bool IsGeneral = Mode == RelocMode::General;

  //relocator.si->load_bias为模块实际的加载基地址
  //rel_target为对应的待重定位数据的实际内存地址(.got表项的地址)
  void* const rel_target = reinterpret_cast<void*>(reloc.r_offset + relocator.si->load_bias);

  //r_type为重定位类型
  const uint32_t r_type = ELFW(R_TYPE)(reloc.r_info);
  //r_sym为对应重定位数据的符号表索引
  const uint32_t r_sym = ELFW(R_SYM)(reloc.r_info);

  //利用r_sym符号表索引从.symtab中获取对应的表项,并利用表项的st_name字段在.dynstr中找到对应的重定位符号字符串
  if (r_sym != 0) {
    sym_name = relocator.get_string(relocator.si_symtab[r_sym].st_name);
  }
  

  #if defined(USE_RELA)      //如果使用了显式加数
    auto get_addend_rel   = [&]() -> ElfW(Addr) { return reloc.r_addend; };
    auto get_addend_norel = [&]() -> ElfW(Addr) { return reloc.r_addend; };
  #else                      //如果没使用显示加数
    auto get_addend_rel   = [&]() -> ElfW(Addr) { return *static_cast<ElfW(Addr)*>(rel_target); };
    auto get_addend_norel = [&]() -> ElfW(Addr) { return 0; };
  #endif


  //symaddr = 对应符号实际在内存中的地址


  //一下解析以没有使用显式加数的为例
  if constexpr (IsGeneral || Mode == RelocMode::JumpTable) {
    //R_GENERIC_JUMP_SLOT是外部函数引用的重定位类型
    if (r_type == R_GENERIC_JUMP_SLOT) {
      count_relocation_if<IsGeneral>(kRelocAbsolute);
      const ElfW(Addr) result = sym_addr + get_addend_norel();  //get_addend_norel()返回0,result = symaddr
      trace_reloc("RELO JMP_SLOT %16p <- %16p %s",
                  rel_target, reinterpret_cast<void*>(result), sym_name);
      *static_cast<ElfW(Addr)*>(rel_target) = result;           //需要重定位的数据修正为symaddr,即其内存中的实际地址
      return true;
    }
  }

  if constexpr (IsGeneral || Mode == RelocMode::Typical) {
    
    if (r_type == R_GENERIC_ABSOLUTE) {
      count_relocation_if<IsGeneral>(kRelocAbsolute);
      const ElfW(Addr) result = sym_addr + get_addend_rel();
      trace_reloc("RELO ABSOLUTE %16p <- %16p %s",
                  rel_target, reinterpret_cast<void*>(result), sym_name);
      *static_cast<ElfW(Addr)*>(rel_target) = result;
      return true;
    } 
    //R_GENERIC_GLOB_DAT为外部符号引用的重定位类型
    else if (r_type == R_GENERIC_GLOB_DAT) {
      
      count_relocation_if<IsGeneral>(kRelocAbsolute);
      const ElfW(Addr) result = sym_addr + get_addend_norel();  //get_addend_norel()返回0,result = symaddr
      trace_reloc("RELO GLOB_DAT %16p <- %16p %s",
                  rel_target, reinterpret_cast<void*>(result), sym_name);
      *static_cast<ElfW(Addr)*>(rel_target) = result;           //需要重定位的数据修正为symaddr,即其内存中的实际地址
      return true;
    }
     //R_GENERIC_RELATIVE为静态或全局变量指针的重定位类型
     else if (r_type == R_GENERIC_RELATIVE) {
      
      count_relocation_if<IsGeneral>(kRelocRelative);                      //get_addend_rel()返回重定位的数据的值 
      const ElfW(Addr) result = relocator.si->load_bias + get_addend_rel();//result = 基地址 + get_addend_rel()返回重定位的数据的值 
      trace_reloc("RELO RELATIVE %16p <- %16p",
                  rel_target, reinterpret_cast<void*>(result));
      *static_cast<ElfW(Addr)*>(rel_target) = result;            //需要重定位的数据修正为:基地址 + get_addend_rel()返回重定位的数据的值,即指针指向的静态或全局变量实际的内存地址
      return true;
    }
  }

__linker_init_post_relocation

static ElfW(Addr) __attribute__((noinline))
__linker_init_post_relocation(KernelArgumentBlock& args, soinfo& tmp_linker_so) {
  __libc_init_main_thread_late();
  
  if (!tmp_linker_so.protect_relro()) __linker_cannot_link(args.argv[0]);

  set_bss_vma_name(&tmp_linker_so);

  __libc_init_globals();

  tmp_linker_so.call_constructors();

  for (const ElfW(Dyn)* d = tmp_linker_so.dynamic; d->d_tag != DT_NULL; ++d) {
    if (d->d_tag == DT_SONAME) {
      tmp_linker_so.set_soname(tmp_linker_so.get_string(d->d_un.d_val));
    }
  }


  ElfW(Addr) start_address = linker_main(args, exe_to_load);
  return start_address;
}

标签:tmp,定位,linker,__,源码,so,rel,android
来源: https://www.cnblogs.com/revercc/p/16299712.html