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

代码参考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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容