ART加载OAT文件的分析

本文对老罗博客http://blog.csdn.net/luoshengyang/article/details/39307813 进行学习理解,针对android6.0系统源码,连个人理解带复制粘贴,总结的ART虚拟机对OAT文件的加载解析流程。









声明:由于CSDN官方bug,文章编码全部整合为乱码,无法显示代码,所以重新整合,代码用普通字体排版。

 










ART运行时提供了一个OatFile类,通过调用它的静态成员函数Open可以在本进程中加载OAT文件,它的实现如下所示(art/runtime/oat_file.cc):


OatFile* OatFile::Open(conststd::string& filename,

                       const std::string&location,

                       uint8_t* requested_base,

                       uint8_t* oat_file_begin,

                       bool executable,

                       const char*abs_dex_location,

                       std::string* error_msg){

 CHECK(!filename.empty()) << location;

 CheckLocation(location);

 std::unique_ptr<OatFile> ret;

 

  //Use dlopen only when flagged to do so, and when it's OK to load thingsexecutable.

  //TODO: Also try when not executable? The issue here could be re-mapping aswritable (as

 //       !executable is a signthat we may want to patch), which may not be allowed for

 //       various reasons.

  if(kUseDlopen && (kIsTargetBuild || kUseDlopenOnHost) &&executable) {

   // Try to use dlopen. This may fail for various reasons, outlined below.We try dlopen, as

   // this will register the oat file with the linker and allows libunwindto find our info.

   ret.reset(OpenDlopen(filename, location, requested_base,abs_dex_location, error_msg));

   if (ret.get() != nullptr) {

     return ret.release();

    }

   if (kPrintDlOpenErrorMessage) {

     LOG(ERROR) << "Failed to dlopen: " << *error_msg;

    }

  }

 

  //If we aren't trying to execute, we just use our own ElfFile loader for a couplereasons:

  //

  //On target, dlopen may fail when compiling due to selinux restrictions oninstalld.

  //

  //We use our own ELF loader for Quick to deal with legacy apps that

  //open a generated dex file by name, remove the file, then open

  //another generated dex file with the same name. http://b/10614658

  //

  //On host, dlopen is expected to fail when cross compiling, so fall back toOpenElfFile.

  //

  //

  //Another independent reason is the absolute placement of boot.oat. dlopen on thehost usually

  //does honor the virtual address encoded in the ELF file only for ET_EXEC files,not ET_DYN.

 std::unique_ptr<File>file(OS::OpenFileForReading(filename.c_str()));

  if(file == nullptr) {

   *error_msg = StringPrintf("Failed to open oat filename for reading:%s", strerror(errno));

   return nullptr;

  }

  ret.reset(OpenElfFile(file.get(),location, requested_base, oat_file_begin, false, executable,

                        abs_dex_location,error_msg));

 

  //It would be nice to unlink here. But we might have opened the file created bythe

  //ScopedLock, which we better not delete to avoid races. TODO: Investigate how tofix the API

  //to allow removal when we know the ELF must be borked.

 return ret.release();

}


参数filename和location实际上是一样的,指向要加载的OAT文件。参数requested_base是一个可选参数,用来描述要加载的OAT文件里面的oatdata段要加载在的位置。参数executable表示要加载的OAT是不是应用程序的主执行文件。


上述代码简言之就是根据ART_USE_PORTABLE_COMPILER宏选择用OpenDlopen还是OpenElfFile方法加载OAT文件。接下来分析这两个函数。


首先分析OpenDlopen函数(art/runtime/oat_file.cc):


OatFile* OatFile::OpenDlopen(conststd::string& elf_filename,

                             conststd::string& location,

                             uint8_t*requested_base,

                             const char*abs_dex_location,

                             std::string* error_msg) {

 std::unique_ptr<OatFile> oat_file(new OatFile(location, true));

 bool success = oat_file->Dlopen(elf_filename, requested_base,abs_dex_location, error_msg);

  if(!success) {

   return nullptr;

  }

 return oat_file.release();

}


跳转到Dlopen函数(一下代码为极度精简版,源代码请自行查阅art/runtime/oat_file.cc):


bool OatFile::Dlopen(const std::string&elf_filename, byte* requested_base) { 

 char* absolute_path = realpath(elf_filename.c_str(), NULL); 

 ...... 

 dlopen_handle_ = dlopen(absolute_path, RTLD_NOW); 

 ...... 

 begin_ = reinterpret_cast<byte*>(dlsym(dlopen_handle_,"oatdata")); 

 ...... 

  if(requested_base != NULL && begin_ != requested_base) { 

   ...... 

   return false; 

 } 

 end_ = reinterpret_cast<byte*>(dlsym(dlopen_handle_,"oatlastword")); 

 ...... 

  //Readjust to be non-inclusive upper bound. 

 end_ += sizeof(uint32_t); 

 return Setup(); 


Dlopen首先通过动态链接器提供的dlopen函数将参数elf_filename指定的OAT文件加载到内存中来,接着同样是通过动态链接器提供的dlsym函数从加载的OAT文件获得oatdata和oatlastword的地址,分别保存在当前正在处理的OatFile对象的成员变量begin_和end_中。oatdata的地址即为OAT文件里面的oatdata段加载到内存中的开始地址,而oatlastword的地址即为OAT文件里面的oatexec加载到内存中的结束地址。符号oatlastword本身也是属于oatexec段的,它自己占用了一个地址,也就是sizeof(uint32_t)个字节,于是将前面得到的end_值加上sizeof(uint32_t),得到的才是oatexec段的结束地址。


      实际上,上面得到的begin_值指向的是加载内存中的oatdata段的头部,即OAT头。这个OAT头描述了OAT文件所包含的DEX文件的信息,以及定义在这些DEX文件里面的类方法所对应的本地机器指令在内存的位置。另外,上面得到的end_是用来在解析OAT头时验证数据的正确性的。此外,如果参数requested_base的值不等于0,那么就要求oatdata段必须要加载到requested_base指定的位置去,也就是上面得到的begin_值与requested_base值相等,否则的话就会出错返回。

      最后,OatFile类的成员函数Dlopen通过调用另外一个成员函数Setup来解析已经加载内存中的oatdata段,以获得ART运行时所需要的更多信息。我们分析完成OatFile类的静态成员函数OpenElfFile之后,再来看OatFile类的成员函数Setup的实现。

 

接着分析OpenElfFile函数的实现,就是另外一种加载OAT文件的函数(art/runtime/oat_file.cc):


OatFile* OatFile::OpenElfFile(File* file,

                              conststd::string& location,

                              uint8_t*requested_base,

                              uint8_t*oat_file_begin,

                              bool writable,

                              bool executable,

                              const char*abs_dex_location,

                              std::string*error_msg) {

 std::unique_ptr<OatFile> oat_file(new OatFile(location,executable));

 bool success = oat_file->ElfFileOpen(file, requested_base,oat_file_begin, writable, executable,

                                      abs_dex_location, error_msg);

  if(!success) {

   CHECK(!error_msg->empty());

   return nullptr;

  }

 return oat_file.release();

}



跳转到ElfFileOpen函数:


bool OatFile::ElfFileOpen(File* file,uint8_t* requested_base, uint8_t* oat_file_begin,

                          bool writable, boolexecutable,

                          const char*abs_dex_location,

                          std::string*error_msg) {

  //TODO: rename requested_base to oat_data_begin

 elf_file_.reset(ElfFile::Open(file, writable,/*program_header_only*/true, error_msg,

                               oat_file_begin));

       ......

 boolloaded = elf_file_->Load(executable, error_msg);

       ......

 begin_ =elf_file_->FindDynamicSymbolAddress("oatdata");

       ......

 end_= elf_file_->FindDynamicSymbolAddress("oatlastword");

       ......

  //Readjust to be non-inclusive upper bound.

 end_ += sizeof(uint32_t);

       ......

 returnSetup(abs_dex_location, error_msg);}

 

bool OatFile::ElfFileOpen(File* file,uint8_t* requested_base, uint8_t* oat_file_begin,

                          bool writable, boolexecutable,

                          const char*abs_dex_location,

              

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值