深入理解ART虚拟机—ImageSpace的加载过程分析

来源:互联网 发布:淘宝网,方太燃气灶配件 编辑:程序博客网 时间:2024/05/22 05:07

上一篇《深入理解ART虚拟机—虚拟机的启动》分析了art虚拟机的启动过程,不过跳过了一个技术点,就是ImageSpace。由之前的分析可知,ClassLinker是用来加载类对象的,有三类类对象,一是ClassRoot,包括java.lang包下的类,这类的类对象是语言自己的,不需要import,ART虚拟机是定义在mirror目录下的。二是boot_class_path,包括jdk和android.jar,这类的类对象是通过指定class path或者系统的Image文件。三是apk自己的dex/oat。ClassLinker的InitFromImage是通过ImageSpace来初始化ClassRoot和boot class path,InitWithoutImage的ClassRoot是直接AllocClass,boot class path是由直接由class path的DexFile生成DexCache加入dex_caches来生成的。第三类类对象是和应用自身相关的,都是走DexCache。

Runtime初始化的时候会创建gc::Heap对象,在Heap的构造函数会创建ImageSpace的实例。

    auto* image_space = space::ImageSpace::Create(image_file_name.c_str(), image_instruction_set, &error_msg);
ImageSpace::Create是ImageSpace的静态构造方法,先找到image文件的路径,之后会生成load镜像文件,具体代码如下:

ImageSpace* ImageSpace::Create(const char* image_location,                               const InstructionSet image_isa,                               std::string* error_msg) {  std::string system_filename;  bool has_system = false;  std::string cache_filename;  bool has_cache = false;  bool dalvik_cache_exists = false;  bool is_global_cache = true;  const bool found_image = FindImageFilename(image_location, image_isa, &system_filename,                                             &has_system, &cache_filename, &dalvik_cache_exists,                                             &has_cache, &is_global_cache);  if (Runtime::Current()->IsZygote()) {    MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());  }  ImageSpace* space;  bool relocate = Runtime::Current()->ShouldRelocate();  bool can_compile = Runtime::Current()->IsImageDex2OatEnabled();  if (found_image) {    const std::string* image_filename;    bool is_system = false;    bool relocated_version_used = false;    if (relocate) {      if (!dalvik_cache_exists) {        *error_msg = StringPrintf("Requiring relocation for image '%s' at '%s' but we do not have "                                  "any dalvik_cache to find/place it in.",                                  image_location, system_filename.c_str());        return nullptr;      }      if (has_system) {        if (has_cache && ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {          // We already have a relocated version          image_filename = &cache_filename;          relocated_version_used = true;        } else {          // We cannot have a relocated version, Relocate the system one and use it.          std::string reason;          bool success;          // Check whether we are allowed to relocate.          if (!can_compile) {            reason = "Image dex2oat disabled by -Xnoimage-dex2oat.";            success = false;          } else if (!ImageCreationAllowed(is_global_cache, &reason)) {            // Whether we can write to the cache.            success = false;          } else {            // Try to relocate.            success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &reason);          }          if (success) {            relocated_version_used = true;            image_filename = &cache_filename;          } else {            *error_msg = StringPrintf("Unable to relocate image '%s' from '%s' to '%s': %s",                                      image_location, system_filename.c_str(),                                      cache_filename.c_str(), reason.c_str());            // We failed to create files, remove any possibly garbage output.            // Since ImageCreationAllowed was true above, we are the zygote            // and therefore the only process expected to generate these for            // the device.            PruneDalvikCache(image_isa);            return nullptr;          }        }      } else {        CHECK(has_cache);        // We can just use cache's since it should be fine. This might or might not be relocated.        image_filename = &cache_filename;      }    } else {      if (has_system && has_cache) {        // Check they have the same cksum. If they do use the cache. Otherwise system.        if (ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {          image_filename = &cache_filename;          relocated_version_used = true;        } else {          image_filename = &system_filename;          is_system = true;        }      } else if (has_system) {        image_filename = &system_filename;        is_system = true;      } else {        CHECK(has_cache);        image_filename = &cache_filename;      }    }    {      // Note that we must not use the file descriptor associated with      // ScopedFlock::GetFile to Init the image file. We want the file      // descriptor (and the associated exclusive lock) to be released when      // we leave Create.      ScopedFlock image_lock;      image_lock.Init(image_filename->c_str(), error_msg);      VLOG(startup) << "Using image file " << image_filename->c_str() << " for image location "                    << image_location;      // If we are in /system we can assume the image is good. We can also      // assume this if we are using a relocated image (i.e. image checksum      // matches) since this is only different by the offset. We need this to      // make sure that host tests continue to work.      space = ImageSpace::Init(image_filename->c_str(), image_location,                               !(is_system || relocated_version_used), error_msg);    }    if (space != nullptr) {      return space;    }    if (relocated_version_used) {      // Something is wrong with the relocated copy (even though checksums match). Cleanup.      // This can happen if the .oat is corrupt, since the above only checks the .art checksums.      // TODO: Check the oat file validity earlier.      *error_msg = StringPrintf("Attempted to use relocated version of %s at %s generated from %s "                                "but image failed to load: %s",                                image_location, cache_filename.c_str(), system_filename.c_str(),                                error_msg->c_str());      PruneDalvikCache(image_isa);      return nullptr;    } else if (is_system) {      // If the /system file exists, it should be up-to-date, don't try to generate it.      *error_msg = StringPrintf("Failed to load /system image '%s': %s",                                image_filename->c_str(), error_msg->c_str());      return nullptr;    } else {      // Otherwise, log a warning and fall through to GenerateImage.      LOG(WARNING) << *error_msg;    }  }  if (!can_compile) {    *error_msg = "Not attempting to compile image because -Xnoimage-dex2oat";    return nullptr;  } else if (!dalvik_cache_exists) {    *error_msg = StringPrintf("No place to put generated image.");    return nullptr;  } else if (!ImageCreationAllowed(is_global_cache, error_msg)) {    return nullptr;  } else if (!GenerateImage(cache_filename, image_isa, error_msg)) {    *error_msg = StringPrintf("Failed to generate image '%s': %s",                              cache_filename.c_str(), error_msg->c_str());    // We failed to create files, remove any possibly garbage output.    // Since ImageCreationAllowed was true above, we are the zygote    // and therefore the only process expected to generate these for    // the device.    PruneDalvikCache(image_isa);    return nullptr;  } else {    // Check whether there is enough space left over after we have generated the image.    if (!CheckSpace(cache_filename, error_msg)) {      // No. Delete the generated image and try to run out of the dex files.      PruneDalvikCache(image_isa);      return nullptr;    }    // Note that we must not use the file descriptor associated with    // ScopedFlock::GetFile to Init the image file. We want the file    // descriptor (and the associated exclusive lock) to be released when    // we leave Create.    ScopedFlock image_lock;    image_lock.Init(cache_filename.c_str(), error_msg);    space = ImageSpace::Init(cache_filename.c_str(), image_location, true, error_msg);    if (space == nullptr) {      *error_msg = StringPrintf("Failed to load generated image '%s': %s",                                cache_filename.c_str(), error_msg->c_str());    }    return space;  }}
FindImageFilename会查找系统和data区是否有image file,系统区镜像的目录是/system/framework/<image_isa>/boot.art,data区的image file的路径是/data/dalvik-cache/<image_isa>/boot.art。/system 下面的是在系统区,是ROM带的,而/data 下的image file是系统运行时生成的,GenerateImage就是生成这个cache image file的函数。image file存在的情况下,就能调用ImageSpace::Init来初始化ImageSpace了。

ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_location,                             bool validate_oat_file, std::string* error_msg) {  CHECK(image_filename != nullptr);  CHECK(image_location != nullptr);  uint64_t start_time = 0;  if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {    start_time = NanoTime();    LOG(INFO) << "ImageSpace::Init entering image_filename=" << image_filename;  }  std::unique_ptr<File> file(OS::OpenFileForReading(image_filename));  if (file.get() == nullptr) {    *error_msg = StringPrintf("Failed to open '%s'", image_filename);    return nullptr;  }  ImageHeader image_header;  bool success = file->ReadFully(&image_header, sizeof(image_header));  if (!success || !image_header.IsValid()) {    *error_msg = StringPrintf("Invalid image header in '%s'", image_filename);    return nullptr;  }  // Check that the file is large enough.  uint64_t image_file_size = static_cast<uint64_t>(file->GetLength());  if (image_header.GetImageSize() > image_file_size) {    *error_msg = StringPrintf("Image file too small for image heap: %" PRIu64 " vs. %zu.",                              image_file_size, image_header.GetImageSize());    return nullptr;  }  if (kIsDebugBuild) {    LOG(INFO) << "Dumping image sections";    for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {      const auto section_idx = static_cast<ImageHeader::ImageSections>(i);      auto& section = image_header.GetImageSection(section_idx);      LOG(INFO) << section_idx << " start="          << reinterpret_cast<void*>(image_header.GetImageBegin() + section.Offset()) << " "          << section;    }  }  const auto& bitmap_section = image_header.GetImageSection(ImageHeader::kSectionImageBitmap);  auto end_of_bitmap = static_cast<size_t>(bitmap_section.End());  if (end_of_bitmap != image_file_size) {    *error_msg = StringPrintf(        "Image file size does not equal end of bitmap: size=%" PRIu64 " vs. %zu.", image_file_size,        end_of_bitmap);    return nullptr;  }  // Note: The image header is part of the image due to mmap page alignment required of offset.  std::unique_ptr<MemMap> map(MemMap::MapFileAtAddress(      image_header.GetImageBegin(), image_header.GetImageSize(),      PROT_READ | PROT_WRITE, MAP_PRIVATE, file->Fd(), 0, false, image_filename, error_msg));  if (map.get() == nullptr) {    DCHECK(!error_msg->empty());    return nullptr;  }  CHECK_EQ(image_header.GetImageBegin(), map->Begin());  DCHECK_EQ(0, memcmp(&image_header, map->Begin(), sizeof(ImageHeader)));  std::unique_ptr<MemMap> image_map(MemMap::MapFileAtAddress(      nullptr, bitmap_section.Size(), PROT_READ, MAP_PRIVATE, file->Fd(),      bitmap_section.Offset(), false, image_filename, error_msg));  if (image_map.get() == nullptr) {    *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());    return nullptr;  }  uint32_t bitmap_index = bitmap_index_.FetchAndAddSequentiallyConsistent(1);  std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_filename,                                       bitmap_index));  std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap(      accounting::ContinuousSpaceBitmap::CreateFromMemMap(          bitmap_name, image_map.release(), reinterpret_cast<uint8_t*>(map->Begin()),          accounting::ContinuousSpaceBitmap::ComputeHeapSize(bitmap_section.Size())));  if (bitmap.get() == nullptr) {    *error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str());    return nullptr;  }  // We only want the mirror object, not the ArtFields and ArtMethods.  uint8_t* const image_end =      map->Begin() + image_header.GetImageSection(ImageHeader::kSectionObjects).End();  std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename, image_location,                                                   map.release(), bitmap.release(), image_end));  // VerifyImageAllocations() will be called later in Runtime::Init()  // as some class roots like ArtMethod::java_lang_reflect_ArtMethod_  // and ArtField::java_lang_reflect_ArtField_, which are used from  // Object::SizeOf() which VerifyImageAllocations() calls, are not  // set yet at this point.  space->oat_file_.reset(space->OpenOatFile(image_filename, error_msg));  if (space->oat_file_.get() == nullptr) {    DCHECK(!error_msg->empty());    return nullptr;  }  space->oat_file_non_owned_ = space->oat_file_.get();  if (validate_oat_file && !space->ValidateOatFile(error_msg)) {    DCHECK(!error_msg->empty());    return nullptr;  }  Runtime* runtime = Runtime::Current();  runtime->SetInstructionSet(space->oat_file_->GetOatHeader().GetInstructionSet());  runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod));  runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod));  runtime->SetImtUnimplementedMethod(      image_header.GetImageMethod(ImageHeader::kImtUnimplementedMethod));  runtime->SetCalleeSaveMethod(      image_header.GetImageMethod(ImageHeader::kCalleeSaveMethod), Runtime::kSaveAll);  runtime->SetCalleeSaveMethod(      image_header.GetImageMethod(ImageHeader::kRefsOnlySaveMethod), Runtime::kRefsOnly);  runtime->SetCalleeSaveMethod(      image_header.GetImageMethod(ImageHeader::kRefsAndArgsSaveMethod), Runtime::kRefsAndArgs);  if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {    LOG(INFO) << "ImageSpace::Init exiting (" << PrettyDuration(NanoTime() - start_time)             << ") " << *space.get();  }  return space.release();}
分析Init函数,先是打开文件读出文件的内容

  std::unique_ptr<File> file(OS::OpenFileForReading(image_filename));  if (file.get() == nullptr) {    *error_msg = StringPrintf("Failed to open '%s'", image_filename);    return nullptr;  }

boot.art的ImageHeader布局如下:


读ImageHeader,并mmap到MemMap。

  ImageHeader image_header;  bool success = file->ReadFully(&image_header, sizeof(image_header));  if (!success || !image_header.IsValid()) {    *error_msg = StringPrintf("Invalid image header in '%s'", image_filename);    return nullptr;  }
  std::unique_ptr<MemMap> map(MemMap::MapFileAtAddress(      image_header.GetImageBegin(), image_header.GetImageSize(),      PROT_READ | PROT_WRITE, MAP_PRIVATE, file->Fd(), 0, false, image_filename, error_msg));  if (map.get() == nullptr) {    DCHECK(!error_msg->empty());    return nullptr;        } 
Header的开头是magic string,接着Image Begin和ImageSize,就是镜像的开始和大小,再后面就是Bitmap的offset和size,bitmap的session被mmap之后会创建bitmap的SpaceBitmap,这个主要是作为ImageSpace的live_bitmap,GC的时候会用。
  std::unique_ptr<MemMap> image_map(MemMap::MapFileAtAddress(      nullptr, bitmap_section.Size(), PROT_READ, MAP_PRIVATE, file->Fd(),      bitmap_section.Offset(), false, image_filename, error_msg));  if (image_map.get() == nullptr) {    *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());    return nullptr;  }  uint32_t bitmap_index = bitmap_index_.FetchAndAddSequentiallyConsistent(1);  std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_filename,                                       bitmap_index));  std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap(      accounting::ContinuousSpaceBitmap::CreateFromMemMap(          bitmap_name, image_map.release(), reinterpret_cast<uint8_t*>(map->Begin()),          accounting::ContinuousSpaceBitmap::ComputeHeapSize(bitmap_section.Size())));  if (bitmap.get() == nullptr) {    *error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str());    return nullptr;  }
最后由image header和live_bitmap初始化ImageSpace的实例

 std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename, image_location,                                                   map.release(), bitmap.release(), image_end));

总结一下,image文件里包含image header和image body,image header的的结构见上图,主要是几个偏移:bitmap offset,image roots,image_methods_ ,oat offset。

除了最后一个oat offset,其他的偏移则是在image file里。image methods指向的是几个公共的ArtMethod,后续分析函数执行的时候再细说,image roots是几类root class,包括DexCache和ClassRoots。oat offset由OatFile open的时候赋值,指向的是OatFile的相关地址,所以当OatFile open了之后,boot.art/boot.oat就联系在一起了。

 OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),                                    image_header.GetOatFileBegin(),                                    !Runtime::Current()->IsAotCompiler(),                                    nullptr, error_msg);


ImageSpace初始化之后,打开boot.oat文件,boot.art和boot.oat是一对的

space->oat_file_.reset(space->OpenOatFile(image_filename, error_msg));  if (space->oat_file_.get() == nullptr) {    DCHECK(!error_msg->empty());    return nullptr;  }  space->oat_file_non_owned_ = space->oat_file_.get();  if (validate_oat_file && !space->ValidateOatFile(error_msg)) {    DCHECK(!error_msg->empty());    return nullptr;  }
OpenOatFile会根据image_filename找到对应的boot.oat文件,并创建OatFile

OatFile* ImageSpace::OpenOatFile(const char* image_path, std::string* error_msg) const {  const ImageHeader& image_header = GetImageHeader();  std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path);  CHECK(image_header.GetOatDataBegin() != nullptr);  OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),                                    image_header.GetOatFileBegin(),                                    !Runtime::Current()->IsAotCompiler(),                                    nullptr, error_msg);  if (oat_file == nullptr) {    *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",                              oat_filename.c_str(), GetName(), error_msg->c_str());    return nullptr;  }  uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();  uint32_t image_oat_checksum = image_header.GetOatChecksum();  if (oat_checksum != image_oat_checksum) {    *error_msg = StringPrintf("Failed to match oat file checksum 0x%x to expected oat checksum 0x%x"                              " in image %s", oat_checksum, image_oat_checksum, GetName());    return nullptr;  }  int32_t image_patch_delta = image_header.GetPatchDelta();  int32_t oat_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();  if (oat_patch_delta != image_patch_delta && !image_header.CompilePic()) {    // We should have already relocated by this point. Bail out.    *error_msg = StringPrintf("Failed to match oat file patch delta %d to expected patch delta %d "                              "in image %s", oat_patch_delta, image_patch_delta, GetName());    return nullptr;  }  return oat_file;}
OatFile::Open打开boot.oat,oat文件是elf文件的扩展,在elf的基础上,多了oat data section,oat exec section,dynamic section

oat data section: 保存oat文件相关信息,包括oat header,oat class信息,对应的dex文件信息、文件内容

oat exec section :保存oat编译的native code

dynamic secion :保存oat data和oat exec的offset和size

oat文件是由PackageManager调用dex2oat命令在apk安装时、系统启动扫描apk时生成的,所以编译后的native code和编译前的dex文件都同时存在,这样art能兼容dalvik的解释器工作模式。正式由于oat是elf格式,所以oat的打开可以由dlopen、dlsym系统函数来加载上面oat的三个section,即OpenDlopen函数。同时支持OpenElfFile的方式,用art自己的elf loader来加载。

OatFile* OatFile::Open(const std::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 things executable.  // TODO: Also try when not executable? The issue here could be re-mapping as writable (as  //       !executable is a sign that 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 libunwind to 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 couple reasons:  //  // On target, dlopen may fail when compiling due to selinux restrictions on installd.  //  // 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 to OpenElfFile.  //  //  // Another independent reason is the absolute placement of boot.oat. dlopen on the host 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 by the  // ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API  // to allow removal when we know the ELF must be borked.  return ret.release();}
OpenDlopen函数:

OatFile* OatFile::OpenDlopen(const std::string& elf_filename,                             const std::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();}bool OatFile::Dlopen(const std::string& elf_filename, uint8_t* requested_base,                     const char* abs_dex_location, std::string* error_msg) {#ifdef __APPLE__  // The dl_iterate_phdr syscall is missing.  There is similar API on OSX,  // but let's fallback to the custom loading code for the time being.  UNUSED(elf_filename);  UNUSED(requested_base);  UNUSED(abs_dex_location);  UNUSED(error_msg);  return false;#else  std::unique_ptr<char> absolute_path(realpath(elf_filename.c_str(), nullptr));  if (absolute_path == nullptr) {    *error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str());    return false;  }#ifdef HAVE_ANDROID_OS  android_dlextinfo extinfo;  extinfo.flags = ANDROID_DLEXT_FORCE_LOAD | ANDROID_DLEXT_FORCE_FIXED_VADDR;  dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo);#else  dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW);#endif  if (dlopen_handle_ == nullptr) {    *error_msg = StringPrintf("Failed to dlopen '%s': %s", elf_filename.c_str(), dlerror());    return false;  }  begin_ = reinterpret_cast<uint8_t*>(dlsym(dlopen_handle_, "oatdata"));  if (begin_ == nullptr) {    *error_msg = StringPrintf("Failed to find oatdata symbol in '%s': %s", elf_filename.c_str(),                              dlerror());    return false;  }  if (requested_base != nullptr && begin_ != requested_base) {    PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);    *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: "                              "oatdata=%p != expected=%p, %s. See process maps in the log.",                              begin_, requested_base, elf_filename.c_str());    return false;  }  end_ = reinterpret_cast<uint8_t*>(dlsym(dlopen_handle_, "oatlastword"));  if (end_ == nullptr) {    *error_msg = StringPrintf("Failed to find oatlastword symbol in '%s': %s", elf_filename.c_str(),                              dlerror());    return false;  }  // Readjust to be non-inclusive upper bound.  end_ += sizeof(uint32_t);  bss_begin_ = reinterpret_cast<uint8_t*>(dlsym(dlopen_handle_, "oatbss"));  if (bss_begin_ == nullptr) {    // No .bss section. Clear dlerror().    bss_end_ = nullptr;    dlerror();  } else {    bss_end_ = reinterpret_cast<uint8_t*>(dlsym(dlopen_handle_, "oatbsslastword"));    if (bss_end_ == nullptr) {      *error_msg = StringPrintf("Failed to find oatbasslastword symbol in '%s'",                                elf_filename.c_str());      return false;    }    // Readjust to be non-inclusive upper bound.    bss_end_ += sizeof(uint32_t);  }  // Ask the linker where it mmaped the file and notify our mmap wrapper of the regions.  struct dl_iterate_context {    static int callback(struct dl_phdr_info *info, size_t /* size */, void *data) {      auto* context = reinterpret_cast<dl_iterate_context*>(data);      // See whether this callback corresponds to the file which we have just loaded.      bool contains_begin = false;      for (int i = 0; i < info->dlpi_phnum; i++) {        if (info->dlpi_phdr[i].p_type == PT_LOAD) {          uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +                                                      info->dlpi_phdr[i].p_vaddr);          size_t memsz = info->dlpi_phdr[i].p_memsz;          if (vaddr <= context->begin_ && context->begin_ < vaddr + memsz) {            contains_begin = true;            break;          }        }      }      // Add dummy mmaps for this file.      if (contains_begin) {        for (int i = 0; i < info->dlpi_phnum; i++) {          if (info->dlpi_phdr[i].p_type == PT_LOAD) {            uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +                                                        info->dlpi_phdr[i].p_vaddr);            size_t memsz = info->dlpi_phdr[i].p_memsz;            MemMap* mmap = MemMap::MapDummy(info->dlpi_name, vaddr, memsz);            context->dlopen_mmaps_->push_back(std::unique_ptr<MemMap>(mmap));          }        }        return 1;  // Stop iteration and return 1 from dl_iterate_phdr.      }      return 0;  // Continue iteration and return 0 from dl_iterate_phdr when finished.    }    const uint8_t* const begin_;    std::vector<std::unique_ptr<MemMap>>* const dlopen_mmaps_;  } context = { begin_, &dlopen_mmaps_ };  if (dl_iterate_phdr(dl_iterate_context::callback, &context) == 0) {    PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);    LOG(ERROR) << "File " << elf_filename << " loaded with dlopen but can not find its mmaps.";  }  return Setup(abs_dex_location, error_msg);#endif  // __APPLE__}
OpenElfFile函数:

OatFile* OatFile::OpenElfFile(File* file,                              const std::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();}bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file_begin,                          bool writable, bool executable,                          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));  if (elf_file_ == nullptr) {    DCHECK(!error_msg->empty());    return false;  }  bool loaded = elf_file_->Load(executable, error_msg);  if (!loaded) {    DCHECK(!error_msg->empty());    return false;  }  begin_ = elf_file_->FindDynamicSymbolAddress("oatdata");  if (begin_ == nullptr) {    *error_msg = StringPrintf("Failed to find oatdata symbol in '%s'", file->GetPath().c_str());    return false;  }  if (requested_base != nullptr && begin_ != requested_base) {    PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);    *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: "                              "oatdata=%p != expected=%p. See process maps in the log.",                              begin_, requested_base);    return false;  }  end_ = elf_file_->FindDynamicSymbolAddress("oatlastword");  if (end_ == nullptr) {    *error_msg = StringPrintf("Failed to find oatlastword symbol in '%s'", file->GetPath().c_str());    return false;  }  // Readjust to be non-inclusive upper bound.  end_ += sizeof(uint32_t);  bss_begin_ = elf_file_->FindDynamicSymbolAddress("oatbss");  if (bss_begin_ == nullptr) {    // No .bss section. Clear dlerror().    bss_end_ = nullptr;    dlerror();  } else {    bss_end_ = elf_file_->FindDynamicSymbolAddress("oatbsslastword");    if (bss_end_ == nullptr) {      *error_msg = StringPrintf("Failed to find oatbasslastword symbol in '%s'",                                file->GetPath().c_str());      return false;    }    // Readjust to be non-inclusive upper bound.    bss_end_ += sizeof(uint32_t);  }  return Setup(abs_dex_location, error_msg);}
两种加载方式一个由系统的dlopen函数,一个由ElfFile这个elf loader,最终的目的都是加载oatdata,oatdata有dex file相关的信息,Setup函数会根据oatdata的dex信息,创建OatDexFile。

bool OatFile::Setup(const char* abs_dex_location, std::string* error_msg) {  if (!GetOatHeader().IsValid()) {    std::string cause = GetOatHeader().GetValidationErrorMessage();    *error_msg = StringPrintf("Invalid oat header for '%s': %s", GetLocation().c_str(),                              cause.c_str());    return false;  }  const uint8_t* oat = Begin();  oat += sizeof(OatHeader);  if (oat > End()) {    *error_msg = StringPrintf("In oat file '%s' found truncated OatHeader", GetLocation().c_str());    return false;  }  oat += GetOatHeader().GetKeyValueStoreSize();  if (oat > End()) {    *error_msg = StringPrintf("In oat file '%s' found truncated variable-size data: "                              "%p + %zd + %ud <= %p", GetLocation().c_str(),                              Begin(), sizeof(OatHeader), GetOatHeader().GetKeyValueStoreSize(),                              End());    return false;  }  uint32_t dex_file_count = GetOatHeader().GetDexFileCount();  oat_dex_files_storage_.reserve(dex_file_count);  for (size_t i = 0; i < dex_file_count; i++) {    uint32_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat);    if (UNLIKELY(dex_file_location_size == 0U)) {      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd with empty location name",                                GetLocation().c_str(), i);      return false;    }    oat += sizeof(dex_file_location_size);    if (UNLIKELY(oat > End())) {      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd truncated after dex file "                                "location size", GetLocation().c_str(), i);      return false;    }    const char* dex_file_location_data = reinterpret_cast<const char*>(oat);    oat += dex_file_location_size;    if (UNLIKELY(oat > End())) {      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd with truncated dex file "                                "location", GetLocation().c_str(), i);      return false;    }    std::string dex_file_location = ResolveRelativeEncodedDexLocation(        abs_dex_location,        std::string(dex_file_location_data, dex_file_location_size));    uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat);    oat += sizeof(dex_file_checksum);    if (UNLIKELY(oat > End())) {      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated after "                                "dex file checksum", GetLocation().c_str(), i,                                dex_file_location.c_str());      return false;    }    uint32_t dex_file_offset = *reinterpret_cast<const uint32_t*>(oat);    if (UNLIKELY(dex_file_offset == 0U)) {      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with zero dex "                                "file offset", GetLocation().c_str(), i, dex_file_location.c_str());      return false;    }    if (UNLIKELY(dex_file_offset > Size())) {      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with dex file "                                "offset %ud > %zd", GetLocation().c_str(), i,                                dex_file_location.c_str(), dex_file_offset, Size());      return false;    }    oat += sizeof(dex_file_offset);    if (UNLIKELY(oat > End())) {      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated "                                "after dex file offsets", GetLocation().c_str(), i,                                dex_file_location.c_str());      return false;    }    const uint8_t* dex_file_pointer = Begin() + dex_file_offset;    if (UNLIKELY(!DexFile::IsMagicValid(dex_file_pointer))) {      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid "                                "dex file magic '%s'", GetLocation().c_str(), i,                                dex_file_location.c_str(), dex_file_pointer);      return false;    }    if (UNLIKELY(!DexFile::IsVersionValid(dex_file_pointer))) {      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid "                                "dex file version '%s'", GetLocation().c_str(), i,                                dex_file_location.c_str(), dex_file_pointer);      return false;    }    const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer);    const uint32_t* methods_offsets_pointer = reinterpret_cast<const uint32_t*>(oat);    oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_);    if (UNLIKELY(oat > End())) {      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with truncated "                                "method offsets", GetLocation().c_str(), i,                                dex_file_location.c_str());      return false;    }    std::string canonical_location = DexFile::GetDexCanonicalLocation(dex_file_location.c_str());    // Create the OatDexFile and add it to the owning container.    OatDexFile* oat_dex_file = new OatDexFile(this,                                              dex_file_location,                                              canonical_location,                                              dex_file_checksum,                                              dex_file_pointer,                                              methods_offsets_pointer);    oat_dex_files_storage_.push_back(oat_dex_file);    // Add the location and canonical location (if different) to the oat_dex_files_ table.    StringPiece key(oat_dex_file->GetDexFileLocation());    oat_dex_files_.Put(key, oat_dex_file);    if (canonical_location != dex_file_location) {      StringPiece canonical_key(oat_dex_file->GetCanonicalDexFileLocation());      oat_dex_files_.Put(canonical_key, oat_dex_file);    }  }  return true;}
OatHeader(oatdata是起始地址)里包含了DexFile文件的个数,每个DexFile文件的size和offset,以及oat class offsets,创建出OatDexFile的实例。而OatDexFile是OatFile的核心,既有OatClass的信息,也有DexFile的信息。

    OatDexFile* oat_dex_file = new OatDexFile(this,                                              dex_file_location,                                              canonical_location,                                              dex_file_checksum,                                              dex_file_pointer,                                              methods_offsets_pointer);    oat_dex_files_storage_.push_back(oat_dex_file);      // Add the location and canonical location (if different) to the oat_dex_files_ table.    StringPiece key(oat_dex_file->GetDexFileLocation());    oat_dex_files_.Put(key, oat_dex_file);    if (canonical_location != dex_file_location) {      StringPiece canonical_key(oat_dex_file->GetCanonicalDexFileLocation());      oat_dex_files_.Put(canonical_key, oat_dex_file);    }
我们用下面的图总结一下oatdata的结构:



OatHeader里有这个oat文件里包含的dex file的个数

  uint32_t dex_file_count = GetOatHeader().GetDexFileCount();
之后对这些dex file做循环,每个dex file都对应有dex_file_location_size,dex_file_data,dex_file_data由dex_file_offset确定,同时dex file的每个class也对应oat_class_offsets_pointer,指向下面的oat_class数据,这些信息一起创建出OatDexFile的实例,每个dex file对应一个OatDexFile。

OatDexFile的每个类定义,都会对应一个OatClass

OatFile::OatClass OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) const {  uint32_t oat_class_offset = GetOatClassOffset(class_def_index);  const uint8_t* oat_class_pointer = oat_file_->Begin() + oat_class_offset;  CHECK_LT(oat_class_pointer, oat_file_->End()) << oat_file_->GetLocation();  const uint8_t* status_pointer = oat_class_pointer;  CHECK_LT(status_pointer, oat_file_->End()) << oat_file_->GetLocation();  mirror::Class::Status status =      static_cast<mirror::Class::Status>(*reinterpret_cast<const int16_t*>(status_pointer));  CHECK_LT(status, mirror::Class::kStatusMax);  const uint8_t* type_pointer = status_pointer + sizeof(uint16_t);  CHECK_LT(type_pointer, oat_file_->End()) << oat_file_->GetLocation();  OatClassType type = static_cast<OatClassType>(*reinterpret_cast<const uint16_t*>(type_pointer));  CHECK_LT(type, kOatClassMax);  const uint8_t* after_type_pointer = type_pointer + sizeof(int16_t);  CHECK_LE(after_type_pointer, oat_file_->End()) << oat_file_->GetLocation();  uint32_t bitmap_size = 0;  const uint8_t* bitmap_pointer = nullptr;  const uint8_t* methods_pointer = nullptr;  if (type != kOatClassNoneCompiled) {    if (type == kOatClassSomeCompiled) {      bitmap_size = static_cast<uint32_t>(*reinterpret_cast<const uint32_t*>(after_type_pointer));      bitmap_pointer = after_type_pointer + sizeof(bitmap_size);      CHECK_LE(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation();      methods_pointer = bitmap_pointer + bitmap_size;    } else {      methods_pointer = after_type_pointer;    }    CHECK_LE(methods_pointer, oat_file_->End()) << oat_file_->GetLocation();  }  return OatFile::OatClass(oat_file_,                           status,                           type,                           bitmap_size,                           reinterpret_cast<const uint32_t*>(bitmap_pointer),                           reinterpret_cast<const OatMethodOffsets*>(methods_pointer));}
Oat Data里OatClass的结构


不同的OatClassType,结构是不一样的,status_pointer是类的Status,type_pointer是类的OatClassType,定义见下面的枚举。不同的OatClassType,OatClass结构也不同,只有kOatClassSomeCompiled的type,才有bitmap_size,bitmap_pointer。methods_pointer是OatMethodOffsets的指针数组,根据method id,可以找到对应的OatMethod数据结构,从而执行OatMethod。

  enum Status {    kStatusRetired = -2,  // Retired, should not be used. Use the newly cloned one instead.    kStatusError = -1,    kStatusNotReady = 0,    kStatusIdx = 1,  // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.    kStatusLoaded = 2,  // DEX idx values resolved.    kStatusResolving = 3,  // Just cloned from temporary class object.    kStatusResolved = 4,  // Part of linking.    kStatusVerifying = 5,  // In the process of being verified.    kStatusRetryVerificationAtRuntime = 6,  // Compile time verification failed, retry at runtime.    kStatusVerifyingAtRuntime = 7,  // Retrying verification at runtime.    kStatusVerified = 8,  // Logically part of linking; done pre-init.    kStatusInitializing = 9,  // Class init in progress.    kStatusInitialized = 10,  // Ready to go.    kStatusMax = 11,  };// OatMethodOffsets are currently 5x32-bits=160-bits long, so if we can// save even one OatMethodOffsets struct, the more complicated encoding// using a bitmap pays for itself since few classes will have 160// methods.enum OatClassType {  kOatClassAllCompiled = 0,   // OatClass is followed by an OatMethodOffsets for each method.  kOatClassSomeCompiled = 1,  // A bitmap of which OatMethodOffsets are present follows the OatClass.  kOatClassNoneCompiled = 2,  // All methods are interpreted so no OatMethodOffsets are necessary.  kOatClassMax = 3,};
至此OatFile初始化完,我们最后总结一下本篇的重点,ClassLinker在初始化的时候,如果存在OAT镜像文件boot.art & boot.oat,那么就会加载镜像文件,初始化ImageSpace。boot.art是镜像文件,只是内存结构,参考ImageHeader的定义,而oat文件内容并不再boot.art里,而在boot.oat里,加载了oat文件之后,整个ImageSpace的初始化过程才结束,初始化之后,后续就可以根据ImageSpace里的OAT信息来执行函数了。下一节我们将介绍《深入理解ART虚拟机—art运行机制分析》。


作者简介:

田力,网易彩票Android端创始人,小米视频创始人,现任roobo技术经理、视频云技术总监

欢迎关注微信公众号 磨剑石,定期推送技术心得以及源码分析等文章,谢谢


0 0