深入理解ART虚拟机—ImageSpace的加载过程分析
Posted threepigs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解ART虚拟机—ImageSpace的加载过程分析相关的知识,希望对你有一定的参考价值。
上一篇《深入理解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 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技术经理、视频云技术总监
欢迎关注微信公众号 磨剑石,定期推送技术心得以及源码分析等文章,谢谢
以上是关于深入理解ART虚拟机—ImageSpace的加载过程分析的主要内容,如果未能解决你的问题,请参考以下文章