OAT文件是一种Android引入ART虚拟机后的一种私有ELF文件格式,它不仅包含有从DEX文件翻译而来的本地机器指令,还包含有原来的DEX文件内容。Android使用/system/bin/dex2oat(我们也可以编译出debug版本的dex2oatd)来将DEX文件编译成OAT文件,dex2oat的主入口为main函数:
/art/dex2oat/dex2oat.cc
int main(int argc, char** argv) {
return art::dex2oat(argc, argv);
}
dex2oat既可以编译出app的OAT文件,也可以编译出boot.oat和boot.art文件,这个是由传入的”–image=”参数决定。先说下编译出boot.oat和boot.art的流程。在host上生成boot.oat和boot.art的过程中,我们获得的参数是:
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[0]=--runtime-arg
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:1059] dex2oat: option[1]=-Xms64m
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[2]=--runtime-arg
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:1059] dex2oat: option[3]=-Xmx64m
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[4]=--image-classes=frameworks/base/preloaded-classes
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[5]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/com.mstar.android_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[6]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/ngbj_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[7]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/tvm_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[8]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[9]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/conscrypt_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[10]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/okhttp_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[11]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/core-junit_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[12]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/bouncycastle_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[13]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[14]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[15]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[16]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/voip-common_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[17]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/ims-common_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[18]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/mms-common_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[19]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/android.policy_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[20]=--dex-file=out/target/common/obj/JAVA_LIBRARIES/apache-xml_intermediates/javalib.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[21]=--dex-location=/system/framework/com.mstar.android.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[22]=--dex-location=/system/framework/ngbj.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[23]=--dex-location=/system/framework/tvm.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[24]=--dex-location=/system/framework/core-libart.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[25]=--dex-location=/system/framework/conscrypt.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[26]=--dex-location=/system/framework/okhttp.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[27]=--dex-location=/system/framework/core-junit.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[28]=--dex-location=/system/framework/bouncycastle.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[29]=--dex-location=/system/framework/ext.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[30]=--dex-location=/system/framework/framework.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[31]=--dex-location=/system/framework/telephony-common.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[32]=--dex-location=/system/framework/voip-common.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[33]=--dex-location=/system/framework/ims-common.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[34]=--dex-location=/system/framework/mms-common.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[35]=--dex-location=/system/framework/android.policy.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[36]=--dex-location=/system/framework/apache-xml.jar
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[37]=--oat-symbols=out/target/product/ponkan/symbols/system/framework/arm/boot.oat
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[38]=--oat-file=out/target/product/ponkan/dex_bootjars/system/framework/arm/boot.oat
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[39]=--oat-location=/system/framework/arm/boot.oat
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[40]=--image=out/target/product/ponkan/dex_bootjars/system/framework/arm/boot.art
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[41]=--base=0x70000000
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[42]=--instruction-set=arm
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[43]=--instruction-set-features=div
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[44]=--android-root=out/target/product/ponkan/system
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[45]=--include-patch-information
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[46]=--runtime-arg
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:1059] dex2oat: option[47]=-Xnorelocate
dex2oatd I 21068 21068 art/dex2oat/dex2oat.cc:915] dex2oat: option[48]=--no-include-debug-symbols
解释一下这些参数的意义:
–runtime-arg:表示下一个参数是一个运行时参数,这些参数都会用于创建dex2oat进程内部的art虚拟机。
–image-classes:参数值表示要被编到image里面的一些类的路径,这里是Zygote用到的预加载的类,在host的路径是frameworks/base/preloaded-classes,在target的路径是/system/etc/preloaded-classes。
–dex-file:表示要进行编译的dex文件,jar文件或apk文件
–dex-location:与”–dex-file”参数一一对应的jar文件(对应设备上的路径)
–oat-symbols:带symbol的oat文件路径
–oat-file:输出的oat文件名
–oat-location:输出的oat文件的路径(对应设备上的路径)
–image:生成的image的路径
–base:偏移基址
–instruction-set:指令集
–instruction-set-features:指令集参数
–android-root:portable linking所用的库的路径
–include-patch-information:编译时包含patch信息,可以在不重编的情况下重定位
–no-include-debug-symbols:不包含debug用的symbol
上面是在host上编译出boot.oat和boot.art,最终放到设备的/system/framework/arm(arm64)/下。但有时host上并没有编译出这两个文件,那么编译步骤需要在运行时进行。
Zygote中启动的运行时使用”-Ximage:”指定启动用的image名字。当没有指定该参数时,将指定的image设置为”/system/framework/boot.art”。image_file_name不为空,便会调用ImageSpace::Create创建boot.oat和boot.art。
/art/runtime/gc/heap.cc
//image_file_name为image的名字
if (!image_file_name.empty()) {
std::string error_msg;
space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(),
image_instruction_set,
&error_msg);
/art/runtime/gc/space/image_space.cc
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);
ImageSpace* space;
bool relocate = Runtime::Current()->ShouldRelocate();
bool can_compile = Runtime::Current()->IsImageDex2OatEnabled();
//若在/system/framework/arm(arm64)找到boot.art或者在/data/dalvik-cache/arm(arm64)找到system@[email protected],found_image为true
if (found_image) {
const std::string* image_filename;
bool is_system = false;
bool relocated_version_used = false;
//relocate重定位用于首次开机
if (relocate) {
//找不到/data/dalvik-cache/arm(arm64)目录直接返回null
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;
}
//若在/system/framework/arm(arm64)找到boot.art,has_system为true
if (has_system) {
//同时在/data/dalvik-cache/arm(arm64)找到system@[email protected]且checksum匹配
if (has_cache && ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
// We already have a relocated version
//将/data/dalvik-cache/arm(arm64)下的image文件作为启动image
image_filename = &cache_filename;
relocated_version_used = true;
} else {//system目录下有image,而/data目录下没有image或者image不匹配checksum
// 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 {
//relocate system下的image到/data/dalvik-cache/arm(arm64)下
// Try to relocate.
success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &reason);
}
if (success) {
//relocate成功,将/data下的image作为启动image
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.
PruneDexCache(image_isa);
return nullptr;
}
}
} else {//system下没有image,这是data下一定有image
CHECK(has_cache);
// We can just use cache's since it should be fine. This might or might not be relocated.
//依然优先使用/data下的image作为启动image
image_filename = &cache_filename;
}
} else {//非relocate情况
//data和system下都有image
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())) {
//checksum匹配的话优先使用/data下面的image
image_filename = &cache_filename;
relocated_version_used = true;
} else {
//checksum不匹配则使用/system下面的image
image_filename = &system_filename;
is_system = true;
}
} else if (has_system) {//只有/system下面的image,则使用这个image
image_filename = &system_filename;
is_system = true;
} else {//使用/data下面的image
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.
//使用之前定下来的image文件进行imagespace的初始化操作
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());
PruneDexCache(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;
}
}
//以下是/data和/system下面都没image的情况
if (!can_compile) {//需要允许dex2oat
*error_msg = "Not attempting to compile image because -Xnoimage-dex2oat";
return nullptr;
} else if (!dalvik_cache_exists) {//data/dalvik-cache/arm(arm64)目录必须存在
*error_msg = StringPrintf("No place to put generated image.");
return nullptr;
} else if (!ImageCreationAllowed(is_global_cache, error_msg)) {//允许创建image
return nullptr;
} else if (!GenerateImage(cache_filename, image_isa, error_msg)) {
//创建boot.art和boot.oat到/data/dalvik-cache/arm(arm64)下
*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.
PruneDexCache(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.
PruneDexCache(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);
//使用创建的image初始化imagespace
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;
}
}
如果没有使用”-Xbootclasspath:”参数特别指定的话,运行时的bootclasspath就是变量BOOTCLASSPATH的值。可以看到,GenerateImage就是生成dex2oat的各种参数,传给exec命令执行。
18:46:07:223I/art ( 1953): GenerateImage: /system/bin/dex2oat --image=/data/dalvik-cache/arm/system@framework@boot.art --dex-file=/system/framework/com.mstar.android.jar --dex-file=/system/framework/ngbj.jar --dex-file=/system/framework/tvm.jar --dex-file=/system/framework/core-libart.jar --dex-file=/system/framework/conscrypt.jar --dex-file=/system/framework/okhttp.jar --dex-file=/system/framework/core-junit.jar --dex-file=/system/framework/bouncycastle.jar --dex-file=/system/framework/ext.jar --dex-file=/system/framework/framework.jar --dex-file=/system/framework/telephony-common.jar --dex-file=/system/framework/voip-common.jar --dex-file=/system/framework/ims-common.jar --dex-file=/system/framework/mms-common.jar --dex-file=/system/framework/android.policy.jar --dex-file=/system/framework/apache-xml.jar --oat-file=/data/dalvik-cache/arm/system@framework@boot.oat --instruction-set=arm --instruction-set-features=div --base=0x70a72000 --runtime-arg -Xms64m --runtime-arg -Xmx64m --image-classes=/system/etc/preloaded-classes
/art/runtime/gc/space/image_space.cc
static bool GenerateImage(const std::string& image_filename, InstructionSet image_isa,
std::string* error_msg) {
const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
std::vector<std::string> boot_class_path;
Split(boot_class_path_string, ':', boot_class_path);
if (boot_class_path.empty()) {
*error_msg = "Failed to generate image because no boot class path specified";
return false;
}
// We should clean up so we are more likely to have room for the image.
if (Runtime::Current()->IsZygote()) {
LOG(INFO) << "Pruning dalvik-cache since we are generating an image and will need to recompile";
PruneDexCache(image_isa);
}
std::vector<std::string> arg_vector;
std::string dex2oat(Runtime::Current()->GetCompilerExecutable());
arg_vector.push_back(dex2oat);// /system/bin/dex2oat或者/system/bin/dex2oatd(debug版本)为第一个参数
std::string image_option_string("--image=");
image_option_string += image_filename;
arg_vector.push_back(image_option_string);
// /data/dalvik-cache/arm(arm64)/system@[email protected]为第二个参数
//后续的几个参数为"--dex-file=xxx",xxx分别是bootclasspath的以冒号分隔的值
for (size_t i = 0; i < boot_class_path.size(); i++) {
arg_vector.push_back(std::string("--dex-file=") + boot_class_path[i]);
}
// 这个参数是"--oat-file=/data/dalvik-cache/arm(arm64)/system@[email protected]"
std::string oat_file_option_string("--oat-file=");
oat_file_option_string += ImageHeader::GetOatLocationFromImageLocation(image_filename);
arg_vector.push_back(oat_file_option_string);
//一些编译及指令集相关的参数
Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&arg_vector);
CHECK_EQ(image_isa, kRuntimeISA)
<< "We should always be generating an image for the current isa.";
//选择一个合适的基址
int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
ART_BASE_ADDRESS_MAX_DELTA);
LOG(INFO) << "Using an offset of 0x" << std::hex << base_offset << " from default "
<< "art base address of 0x" << std::hex << ART_BASE_ADDRESS;
arg_vector.push_back(StringPrintf("--base=0x%x", ART_BASE_ADDRESS + base_offset));
if (!kIsTargetBuild) {
arg_vector.push_back("--host");
}
const std::vector<std::string>& compiler_options = Runtime::Current()->GetImageCompilerOptions();
for (size_t i = 0; i < compiler_options.size(); ++i) {
arg_vector.push_back(compiler_options[i].c_str());
}
std::string command_line(Join(arg_vector, ' '));
LOG(INFO) << "GenerateImage: " << command_line;
return Exec(arg_vector, error_msg);
}
回到dex2oat的执行过程。简单来说,就是解释参数->创建内部虚拟机->编译生成oat文件->生成image文件。
/art/dex2oat/dex2oat.cc
//这里创建dex2oat内部虚拟机
if (!Dex2Oat::Create(&p_dex2oat,
runtime_options,
*compiler_options,
compiler_kind,
instruction_set,
instruction_set_features,
verification_results.get(),
&method_inliner_map,
thread_count)) {
LOG(ERROR) << "Failed to create dex2oat";
timings.EndTiming();
oat_file->Erase();
return EXIT_FAILURE;
}
...
//编译生成oat文件
std::unique_ptr compiler(dex2oat->CreateOatFile(boot_image_option,
android_root,
is_host,
dex_files,
oat_file.get(),
oat_location,
bitcode_filename,
image,
image_classes,
compiled_classes,
dump_stats,
dump_passes,
timings,
compiler_phases_timings,
swap_fd,
profile_file,
key_value_store.get()));
...
//生成image文件(需要指定"--image="参数)
if (image) {
TimingLogger::ScopedTiming t("dex2oat ImageWriter", &timings);
bool image_creation_success = dex2oat->CreateImageFile(image_filename,
image_base,
oat_unstripped,
oat_location,
*compiler.get());