深入理解ART虚拟机—ImageSpace的加载过程分析
2016-10-14 12:50
489 查看
上一篇《深入理解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的实例。
file存在的情况下,就能调用ImageSpace::Init来初始化ImageSpace了。
boot.art的ImageHeader布局如下:
读ImageHeader,并mmap到MemMap。
总结一下,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就联系在一起了。
ImageSpace初始化之后,打开boot.oat文件,boot.art和boot.oat是一对的
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来加载。
OatHeader里有这个oat文件里包含的dex file的个数
OatDexFile的每个类定义,都会对应一个OatClass
不同的OatClassType,结构是不一样的,status_pointer是类的Status,type_pointer是类的OatClassType,定义见下面的枚举。不同的OatClassType,OatClass结构也不同,只有kOatClassSomeCompiled的type,才有bitmap_size,bitmap_pointer。methods_pointer是OatMethodOffsets的指针数组,根据method id,可以找到对应的OatMethod数据结构,从而执行OatMethod。
作者简介:田力,网易彩票Android端创始人,小米视频创始人,现任roobo技术经理、视频云技术总监欢迎关注微信公众号 磨剑石,定期推送技术心得以及源码分析等文章,谢谢
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技术经理、视频云技术总监欢迎关注微信公众号 磨剑石,定期推送技术心得以及源码分析等文章,谢谢
相关文章推荐
- 深入理解 spring 容器,源码分析加载过程
- Android运行时ART加载类和方法的过程分析
- 深入理解JVM(九)——类加载的过程
- java面试- 深入理解JVM(九)——类加载的过程
- 深入理解SpringCloud之Eureka注册过程分析
- Java学习之类加载全过程_JVM内存分析_反射机制核心原理_常量池理解
- 深入理解Java 类加载全过程
- ART加载OAT文件的过程分析
- 深入理解Java虚拟机 第七章 虚拟机类加载时机与过程
- [深入理解Java虚拟机]第七章 类加载的过程
- Android运行时ART加载OAT文件的过程分析
- 深入理解JVM07--虚拟机类加载机制--类加载过程
- 深入理解 Tomcat(五)源码剖析Tomcat 启动过程----类加载过程
- nib 加载过程分析以及对File’s Owner的理解
- [置顶] 深入理解JVM之四:类加载过程
- 深入理解JVM(九)——类加载的过程
- 深入理解SpringCloud之Eureka注册过程分析
- 深入理解 Tomcat(五)源码剖析Tomcat 启动过程----类加载过程
- 深入理解JVM-虚拟机加载机制-类加载过程
- 深入理解JVM类加载过程