美文网首页Android 面试
热修&插件 - Art加载Dex流程

热修&插件 - Art加载Dex流程

作者: Stan_Z | 来源:发表于2021-01-30 10:44 被阅读0次

    代码参考Android 8.0。

    一、Dex加载流程

    dex加载简要时序图

    DexPathList的构造方法中执行makeDexElements,最终产出的数据结构为Element[],而Element作为元素,其数据结构如下:

    Element[]数据结构

    整个流程中,native获取DexFile集的核心函数为:OatFileManager::OpenDexFilesFromOat,该方法核心逻辑如下:

    art/runtime/oat_file_manager.cc
    
    std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
       const char* dex_location,//加载的dex文件路径
       jobject class_loader,//外部传入的classLoader
       jobjectArray dex_elements,//外部传入的Elements[]
       const OatFile** out_oat_file,//OatFile对象
       std::vector<std::string>* error_msgs) {
      ScopedTrace trace(__FUNCTION__);
      CHECK(dex_location != nullptr);
      CHECK(error_msgs != nullptr);
      // Verify we aren't holding the mutator lock, which could starve GC if we
      // have to generate or relocate an oat file.
      Thread* const self = Thread::Current();
      Locks::mutator_lock_->AssertNotHeld(self);
      Runtime* const runtime = Runtime::Current();
      //oatfile处理类
      OatFileAssistant oat_file_assistant(dex_location,
                                         kRuntimeISA,
                                         !runtime->IsAotCompiler());
      // Lock the target oat location to avoid races generating and loading the
      // oat file.
      std::string error_msg;
      if (!oat_file_assistant.Lock(/*out*/&error_msg)) {
       // Don't worry too much if this fails. If it does fail, it's unlikely we
       // can generate an oat file anyway.
       VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
      }
    
      const OatFile* source_oat_file = nullptr;
       //判断是否走dex2oat编译,如下情况满足则会执行编译:
       //1.有dex文件,但还没编过
    //2\. 编译后的odex过期了,比如:系统升级导致oat文件不能匹配boot image,或者oat文件不能匹配compiler filter等。
      if (!oat_file_assistant.IsUpToDate()) {
       // Update the oat file on disk if we can, based on the --compiler-filter
       // option derived from the current runtime options.
       // This may fail, but that's okay. Best effort is all that matters here.
        //执行dex2oat编译
       switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/false, /*out*/ &error_msg)) {
         case OatFileAssistant::kUpdateFailed:
           LOG(WARNING) << error_msg;
           break;
         case OatFileAssistant::kUpdateNotAttempted:
           // Avoid spamming the logs if we decided not to attempt making the oat
           // file up to date.
           VLOG(oat) << error_msg;
           break;
         case OatFileAssistant::kUpdateSucceeded:
           // Nothing to do.
           break;
       }
      }
    
      // Get the oat file on disk.
      //获取当前有效的.odex
      std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
      //odex冲突检测
      if (oat_file != nullptr) {
       // Take the file only if it has no collisions, or we must take it because of preopting.
       bool accept_oat_file =
           !HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg);
       if (!accept_oat_file) {
         // Failed the collision check. Print warning.
         if (Runtime::Current()->IsDexFileFallbackEnabled()) {
           if (!oat_file_assistant.HasOriginalDexFiles()) {
             // We need to fallback but don't have original dex files. We have to
             // fallback to opening the existing oat file. This is potentially
             // unsafe so we warn about it.
             accept_oat_file = true;
             LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. "
                          << "Allow oat file use. This is potentially dangerous.";
           } else {
             // We have to fallback and found original dex files - extract them from an APK.
             // Also warn about this operation because it's potentially wasteful.
             LOG(WARNING) << "Found duplicate classes, falling back to extracting from APK : "
                          << dex_location;
             LOG(WARNING) << "NOTE: This wastes RAM and hurts startup performance.";
           }
         } else {
           // TODO: We should remove this. The fact that we're here implies -Xno-dex-file-fallback
           // was set, which means that we should never fallback. If we don't have original dex
           // files, we should just fail resolution as the flag intended.
           if (!oat_file_assistant.HasOriginalDexFiles()) {
             accept_oat_file = true;
           }
           LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to "
                           " load classes for " << dex_location;
         }
         LOG(WARNING) << error_msg;
       }
       if (accept_oat_file) {
         VLOG(class_linker) << "Registering " << oat_file->GetLocation();
          //冲突检测通过之后把oat_file注册给source_oat_file
         source_oat_file = RegisterOatFile(std::move(oat_file));
         *out_oat_file = source_oat_file;
       }
      }
      std::vector<std::unique_ptr<const DexFile>> dex_files;
      // Load the dex files from the oat file.
      if (source_oat_file != nullptr) {
       bool added_image_space = false;
       if (source_oat_file->IsExecutable()) {
          //kEnableAppImage为true,
         std::unique_ptr<gc::space::ImageSpace> image_space =
             kEnableAppImage ? oat_file_assistant.OpenImageSpace(source_oat_file) : nullptr;
         if (image_space != nullptr) {
           ScopedObjectAccess soa(self);
           StackHandleScope<1> hs(self);
           Handle<mirror::ClassLoader> h_loader(
               hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
           // Can not load app image without class loader.
           if (h_loader != nullptr) {
             std::string temp_error_msg;
             // Add image space has a race condition since other threads could be reading from the
             // spaces array.
    
             {
               ScopedThreadSuspension sts(self, kSuspended);
               gc::ScopedGCCriticalSection gcs(self,
                                               gc::kGcCauseAddRemoveAppImageSpace,
                                               gc::kCollectorTypeAddRemoveAppImageSpace);
               ScopedSuspendAll ssa("Add image space");
               runtime->GetHeap()->AddSpace(image_space.get());
             }
             {
               ScopedTrace trace2(StringPrintf("Adding image space for location %s", dex_location));
                 //.art能直接获取内存镜像,直接通过classLinker加载
                added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(),
                                                                            h_loader,
                                                                            dex_elements,
                                                                            dex_location,
                                                                            /*out*/&dex_files,
                                                                            /*out*/&temp_error_msg);
             }
             if (added_image_space) {
               // Successfully added image space to heap, release the map so that it does not get
               // freed.
               image_space.release();
             } else {
               LOG(INFO) << "Failed to add image file " << temp_error_msg;
               dex_files.clear();
               {
                 ScopedThreadSuspension sts(self, kSuspended);
                 gc::ScopedGCCriticalSection gcs(self,
                                                 gc::kGcCauseAddRemoveAppImageSpace,
                                                 gc::kCollectorTypeAddRemoveAppImageSpace);
                 ScopedSuspendAll ssa("Remove image space");
                 runtime->GetHeap()->RemoveSpace(image_space.get());
               }
               // Non-fatal, don't update error_msg.
             }
           }
         }
       }
    
       if (!added_image_space) {
         DCHECK(dex_files.empty());
          //按OatFile->OatDexFile->DexFile 的顺序获取DexFile, 其实也就是从Oat文件中获取DexFile内容
         dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
       }
    
       if (dex_files.empty()) {
         error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation());
       }
      }
    
      // Fall back to running out of the original dex file if we couldn't load any
      // dex_files from the oat file.
      if (dex_files.empty()) {
       if (oat_file_assistant.HasOriginalDexFiles()) {
         if (Runtime::Current()->IsDexFileFallbackEnabled()) {
           static constexpr bool kVerifyChecksum = true;
            //直接打开原始dex文件
           if (!DexFile::Open(
               dex_location, dex_location, kVerifyChecksum, /*out*/ &error_msg, &dex_files)) {
             LOG(WARNING) << error_msg;
             error_msgs->push_back("Failed to open dex files from " + std::string(dex_location)
                                   + " because: " + error_msg);
           }
         } else {
           error_msgs->push_back("Fallback mode disabled, skipping dex files.");
         }
       } else {
         error_msgs->push_back("No original dex files found for dex location "
             + std::string(dex_location));
       }
      }
      return dex_files;
    }
    

    方法很长,这里通过一个图简单总结下核心流程:


    OatFileManager::OpenDexFilesFromOat核心流程

    上图总结的逻辑已经非常清晰:

    1.如果本地没有优化后的.odex文件,或者有但是失效了,那么先进行dex2oat编译。(Android Q之后,dex2oat编译这一步去掉了)

    2.打开本地优化后的.odex文件,封装为OatFile

    3.通过三种方式获取DexFile集合,优先级依次为:

    • .art获取 ClassLinker加载ImageSpace
    • .odex获取 OatFile->OatDexFile->DexFile
    • .dex获取

    二、DexFile::Open()过程

    LoadDexFiles()最终也会走Dex::Open(),但是它与直接打开原始dex文件的Dex::Open()是两个不同的重载方法。下面来简单看看:

    2.1 LoadDexFiles走的Dex::Open():
    art/runtime/oat_file_assistant.cc
    
    std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
       const OatFile& oat_file, const char* dex_location) {
      std::vector<std::unique_ptr<const DexFile>> dex_files;
      // Load the main dex file.
      std::string error_msg;
      //通过oat_file获取oat_dex_file
      const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
         dex_location, nullptr, &error_msg);
      if (oat_dex_file == nullptr) {
       LOG(WARNING) << error_msg;
       return std::vector<std::unique_ptr<const DexFile>>();
      }
    
      //通过oat_dex_file获取dex_file
      std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
      if (dex_file.get() == nullptr) {
       LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
       return std::vector<std::unique_ptr<const DexFile>>();
      }
    
      dex_files.push_back(std::move(dex_file));
      // Load the rest of the multidex entries
      for (size_t i = 1; ; i++) {
       std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
       oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr);
       if (oat_dex_file == nullptr) {
         // There are no more multidex entries to load.
         break;
       }
    
       dex_file = oat_dex_file->OpenDexFile(&error_msg);
       if (dex_file.get() == nullptr) {
         LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
         return std::vector<std::unique_ptr<const DexFile>>();
       }
       dex_files.push_back(std::move(dex_file));
      }
      return dex_files;
    }
    
    art/runtime/oat_file.cc
    
    std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
      ScopedTrace trace(__PRETTY_FUNCTION__);
      static constexpr bool kVerify = false;
      static constexpr bool kVerifyChecksum = false;
      return DexFile::Open(dex_file_pointer_,
                          FileSize(),
                          dex_file_location_,
                          dex_file_location_checksum_,
                          this,
                          kVerify,
                          kVerifyChecksum,
                          error_msg);
    }
    
    art/runtime/dex_file.cc
    
    std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base,//dex的开始
                                                size_t size,//dex size
                                                const std::string& location,//地址
                                                uint32_t location_checksum,
                                                const OatDexFile* oat_dex_file,//从oat_dex_file找的dex_file
                                                bool verify,
                                                bool verify_checksum,
                                                std::string* error_msg) {
      ScopedTrace trace(std::string("Open dex file from RAM ") + location);
      return OpenCommon(base,
                       size,
                       location,
                       location_checksum,
                       oat_dex_file,
                       verify,
                       verify_checksum,
                       error_msg);
    }
    
    std::unique_ptr<DexFile> DexFile::OpenCommon(const uint8_t* base,
                                                size_t size,
                                                const std::string& location,
                                                uint32_t location_checksum,
                                                const OatDexFile* oat_dex_file,
                                                bool verify,
                                                bool verify_checksum,
                                                std::string* error_msg,
                                                VerifyResult* verify_result) {
      if (verify_result != nullptr) {
       *verify_result = VerifyResult::kVerifyNotAttempted;
      }
      //初始化DexFile对象
      std::unique_ptr<DexFile> dex_file(new DexFile(base,
                                                   size,
                                                   location,
                                                   location_checksum,
                                                   oat_dex_file));
    
      if (dex_file == nullptr) {
       *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
                                 error_msg->c_str());
       return nullptr;
      }
      if (!dex_file->Init(error_msg)) {
       dex_file.reset();
       return nullptr;
      }
      //Verify验证
      if (verify && !DexFileVerifier::Verify(dex_file.get(),
                                            dex_file->Begin(),
                                            dex_file->Size(),
                                            location.c_str(),
                                            verify_checksum,
                                            error_msg)) {
       if (verify_result != nullptr) {
         *verify_result = VerifyResult::kVerifyFailed;
       }
       return nullptr;
      }
      if (verify_result != nullptr) {
       *verify_result = VerifyResult::kVerifySucceeded;
      }
      return dex_file;
    }
    

    因为前面OatFile已经加载进内存,这里直接从OatFile中获取dex内容并进行封装。

    2.2 打开原始Dex文件走的Dex::Open():
    bool DexFile::Open(const char* filename,
                      const std::string& location,
                      bool verify_checksum,
                      std::string* error_msg,
                      std::vector<std::unique_ptr<const DexFile>>* dex_files) {
      ScopedTrace trace(std::string("Open dex file ") + std::string(location));
      DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
      uint32_t magic;
      File fd = OpenAndReadMagic(filename, &magic, error_msg);//打开文件
      if (fd.Fd() == -1) {
       DCHECK(!error_msg->empty());
       return false;
      }
    
      //对zip和dex两种类型分别进行处理,前者可能包含多个dex
      if (IsZipMagic(magic)) {
       return DexFile::OpenZip(fd.Release(), location, verify_checksum, error_msg, dex_files);
      }
      if (IsDexMagic(magic)) {
       std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.Release(),
                                                                 location,
                                                                 /* verify */ true,
                                                                 verify_checksum,
                                                                 error_msg));
       if (dex_file.get() != nullptr) {
         dex_files->push_back(std::move(dex_file));
         return true;
       } else {
         return false;
       }
      }
      *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
      return false;
    }
    

    DexFile::OpenZip和DexFile::OpenFile,都需要将dex文件先加载到内存中,区别是前者可能有多个dex,之后会统一走OpenCommon,这与前面一致,就不赘述了。

    参考:
    https://blog.csdn.net/threepigs/article/details/52789614
    https://blog.csdn.net/doon/article/details/76034383
    https://www.52pojie.cn/forum.php?mod=viewthread&tid=1121372

    相关文章

      网友评论

        本文标题:热修&插件 - Art加载Dex流程

        本文链接:https://www.haomeiwen.com/subject/ajvhtltx.html