2015-05-11 15:14:39 2068瀏覽
在前面一文中,我們介紹了Android運(yùn)行時(shí)ART,它的核心是OAT文件。OAT文件是一種Android私有ELF文件格式,它不僅包含有從DEX文件翻譯而來(lái)的本地機(jī)器指令,還包含有原來(lái)的DEX文件內(nèi)容。這使得我們無(wú)需重新編譯原有的APK就可以讓它正常地在ART里面運(yùn)行,也就是我們不需要改變?cè)瓉?lái)的APK編程接口。本文我們通過(guò)OAT文件的加載過(guò)程分析OAT文件的結(jié)構(gòu),為后面分析ART的工作原理打基礎(chǔ)。
OAT文件的結(jié)構(gòu)如圖1所示:
由于OAT文件本質(zhì)上是一個(gè)ELF文件,因此在最外層它具有一般ELF文件的結(jié)構(gòu),例如它有標(biāo)準(zhǔn)的ELF文件頭以及通過(guò)段(Section)來(lái)描述文件內(nèi)容。
作為Android私有的一種ELF文件,OAT文件包含有兩個(gè)特殊的段oatdata和oatexec,前者包含有用來(lái)生成本地機(jī)器指令的dex文件內(nèi)容,后者包含有生成的本地機(jī)器指令,它們之間的關(guān)系通過(guò)儲(chǔ)存在oatdata段前面的oat頭部描述。此外,在OAT文件的dynamic段,導(dǎo)出了三個(gè)符號(hào)oatdata、oatexec和oatlastword,它們的值就是用來(lái)界定oatdata段和oatexec段的起止位置的。其中,[oatdata, oatexec - 4]描述的是oatdata段的起止位置,而[oatexec, oatlastword]描述的是oatlastword的起止位置。要完全理解OAT的文件格式,除了要理解本文即將要分析的OAT加載過(guò)程之外,還需要掌握接下來(lái)文章分析的類和方法查找過(guò)程。
在分析OAT文件的加載過(guò)程之前,我們需要簡(jiǎn)單介紹一下OAT是如何產(chǎn)生的。如前面Android ART運(yùn)行時(shí)無(wú)縫替換Dalvik虛擬機(jī)的過(guò)程分析一文所示,APK在安裝的過(guò)程中,會(huì)通過(guò)dex2oat工具生成一個(gè)OAT文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, const char* output_file_name, const char* dexopt_flags) { static const char* DEX2OAT_BIN = "/system/bin/dex2oat"; static const int MAX_INT_LEN = 12; // '-'+10dig+'' -OR- 0x+8dig char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN]; char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX]; char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN]; char oat_location_arg[strlen("--oat-name=") + PKG_PATH_MAX]; sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd); sprintf(zip_location_arg, "--zip-location=%s", input_file_name); sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd); sprintf(oat_location_arg, "--oat-location=%s", output_file_name); ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name); execl(DEX2OAT_BIN, DEX2OAT_BIN, zip_fd_arg, zip_location_arg, oat_fd_arg, oat_location_arg, (char*) NULL); ALOGE("execl(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno)); } |
這個(gè)函數(shù)定義在文件frameworks/native/cmds/installd/commands.c中。
其中,參數(shù)zip_fd和oat_fd都是打開(kāi)文件描述符,指向的分別是正在安裝的APK文件和要生成的OAT文件。OAT文件的生成過(guò)程主要就是涉及到將包含在APK里面的classes.dex文件的DEX字節(jié)碼翻譯成本地機(jī)器指令。這相當(dāng)于是編寫一個(gè)輸入文件為DEX、輸出文件為OAT的編譯器。這個(gè)編譯器是基于LLVM開(kāi)發(fā)的。編譯器的工作原理比較高大上,所幸的是它不會(huì)影響到我們接下來(lái)的分析,因此我們就略過(guò)DEX字節(jié)碼翻譯成本地機(jī)器指令的過(guò)程,假設(shè)它很愉快地完成了。
APK安裝過(guò)程中生成的OAT文件的輸入只有一個(gè)DEX文件,也就是來(lái)自于打包在要安裝的APK文件里面的classex.dex文件。實(shí)際上,一個(gè)OAT文件是可以由若干個(gè)DEX生成的。這意味著在生成的OAT文件的oatdata段中,包含有多個(gè)DEX文件。那么,在什么情況下,會(huì)生成包含多個(gè)DEX文件的OAT文件呢?
從前面Android ART運(yùn)行時(shí)無(wú)縫替換Dalvik虛擬機(jī)的過(guò)程分析一文可以知道,當(dāng)我們選擇了ART運(yùn)行時(shí)時(shí),Zygote進(jìn)程在啟動(dòng)的過(guò)程中,會(huì)調(diào)用libart.so里面的函數(shù)JNI_CreateJavaVM來(lái)創(chuàng)建一個(gè)ART虛擬機(jī)。函數(shù)JNI_CreateJavaVM的實(shí)現(xiàn)如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args); if (IsBadJniVersion(args->version)) { LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version; return JNI_EVERSION; } Runtime::Options options; for (int i = 0; i < args->nOptions; ++i) { JavaVMOption* option = &args->options[i]; options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo)); } bool ignore_unrecognized = args->ignoreUnrecognized; if (!Runtime::Create(options, ignore_unrecognized)) { return JNI_ERR; } Runtime* runtime = Runtime::Current(); bool started = runtime->Start(); if (!started) { delete Thread::Current()->GetJniEnv(); delete runtime->GetJavaVM(); LOG(WARNING) << "CreateJavaVM failed"; return JNI_ERR; } *p_env = Thread::Current()->GetJniEnv(); *p_vm = runtime->GetJavaVM(); return JNI_OK; } |
這個(gè)函數(shù)定義在文件art/runtime/jni_internal.cc中。
參數(shù)vm_args用作ART虛擬機(jī)的啟動(dòng)參數(shù),它被轉(zhuǎn)換為一個(gè)JavaVMInitArgs對(duì)象后,再按照Key-Value的組織形式保存一個(gè)Options向量中,并且作該向量作為參數(shù)傳遞給Runtime類的靜態(tài)成員函數(shù)Create。
Runtime類的靜態(tài)成員函數(shù)Create負(fù)責(zé)在進(jìn)程中創(chuàng)建一個(gè)ART虛擬機(jī)。創(chuàng)建成功后,就調(diào)用Runtime類的另外一個(gè)靜態(tài)成員函數(shù)Start啟動(dòng)該ART虛擬機(jī)。注意,這個(gè)創(chuàng)建ART虛擬的動(dòng)作只會(huì)在Zygote進(jìn)程中執(zhí)行,SystemServer系統(tǒng)進(jìn)程以及Android應(yīng)用程序進(jìn)程的ART虛擬機(jī)都是直接從Zygote進(jìn)程fork出來(lái)共享的。這與Dalvik虛擬機(jī)的創(chuàng)建方式是完全一樣的。
接下來(lái)我們就重點(diǎn)分析Runtime類的靜態(tài)成員函數(shù)Create,它的實(shí)現(xiàn)如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
bool Runtime::Create(const Options& options, bool ignore_unrecognized) { // TODO: acquire a static mutex on Runtime to avoid racing. if (Runtime::instance_ != NULL) { return false; } InitLogging(NULL); // Calls Locks::Init() as a side effect. instance_ = new Runtime; if (!instance_->Init(options, ignore_unrecognized)) { delete instance_; instance_ = NULL; return false; } return true; } |
這個(gè)函數(shù)定義在文件art/runtime/runtime.cc中。
instance_是Runtime類的靜態(tài)成員變量,它指向進(jìn)程中的一個(gè)Runtime單例。這個(gè)Runtime單例描述的就是當(dāng)前進(jìn)程的ART虛擬機(jī)實(shí)例。
函數(shù)首先判斷當(dāng)前進(jìn)程是否已經(jīng)創(chuàng)建有一個(gè)ART虛擬機(jī)實(shí)例了。如果有的話,函數(shù)就立即返回。否則的話,就創(chuàng)建一個(gè)ART虛擬機(jī)實(shí)例,并且保存在Runtime類的靜態(tài)成員變量instance_中,最后調(diào)用Runtime類的成員函數(shù)Init對(duì)該新創(chuàng)建的ART虛擬機(jī)進(jìn)行初始化。
Runtime類的成員函數(shù)Init的實(shí)現(xiàn)如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { ...... UniquePtr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized)); ...... heap_ = new gc::Heap(options->heap_initial_size_, options->heap_growth_limit_, options->heap_min_free_, options->heap_max_free_, options->heap_target_utilization_, options->heap_maximum_size_, options->image_, options->is_concurrent_gc_enabled_, options->parallel_gc_threads_, options->conc_gc_threads_, options->low_memory_mode_, options->long_pause_log_threshold_, options->long_gc_log_threshold_, options->ignore_max_footprint_); ...... java_vm_ = new JavaVMExt(this, options.get()); ...... Thread* self = Thread::Attach("main", false, NULL, false); ...... if (GetHeap()->GetContinuousSpaces()[0]->IsImageSpace()) { class_linker_ = ClassLinker::CreateFromImage(intern_table_); } else { ...... class_linker_ = ClassLinker::CreateFromCompiler(*options->boot_class_path_, intern_table_); } ...... return true; } |
這個(gè)函數(shù)定義在文件art/runtime/runtime.cc中。
Runtime類的成員函數(shù)Init首先調(diào)用ParsedOptions類的靜態(tài)成員函數(shù)Create對(duì)ART虛擬機(jī)的啟動(dòng)參數(shù)raw_options進(jìn)行解析。解析后得到的參數(shù)保存在一個(gè)ParsedOptions對(duì)象中,接下來(lái)就根據(jù)這些參數(shù)一個(gè)ART虛擬機(jī)堆。ART虛擬機(jī)堆使用一個(gè)Heap對(duì)象來(lái)描述。
創(chuàng)建好ART虛擬機(jī)堆后,Runtime類的成員函數(shù)Init接著又創(chuàng)建了一個(gè)JavaVMExt實(shí)例。這個(gè)JavaVMExt實(shí)例最終是要返回給調(diào)用者的,使得調(diào)用者可以通過(guò)該JavaVMExt實(shí)例來(lái)和ART虛擬機(jī)交互。再接下來(lái),Runtime類的成員函數(shù)Init通過(guò)Thread類的成員函數(shù)Attach將當(dāng)前線程作為ART虛擬機(jī)的主線程,使得當(dāng)前線程可以調(diào)用ART虛擬機(jī)提供的JNI接口。
Runtime類的成員函數(shù)GetHeap返回的便是當(dāng)前ART虛擬機(jī)的堆,也就是前面創(chuàng)建的ART虛擬機(jī)堆。通過(guò)調(diào)用Heap類的成員函數(shù)GetContinuousSpaces可以獲得堆里面的連續(xù)空間列表。如果這個(gè)列表的第一個(gè)連續(xù)空間是一個(gè)Image空間,那么就調(diào)用ClassLinker類的靜態(tài)成員函數(shù)CreateFromImage來(lái)創(chuàng)建一個(gè)ClassLinker對(duì)象。否則的話,上述ClassLinker對(duì)象就要通過(guò)ClassLinker類的另外一個(gè)靜態(tài)成員函數(shù)CreateFromCompiler來(lái)創(chuàng)建。創(chuàng)建出來(lái)的ClassLinker對(duì)象是后面ART虛擬機(jī)加載加載Java類時(shí)要用到的。
后面我們分析ART虛擬機(jī)的垃圾收集機(jī)制時(shí)會(huì)看到,ART虛擬機(jī)的堆包含有三個(gè)連續(xù)空間和一個(gè)不連續(xù)空間。三個(gè)連續(xù)空間分別用來(lái)分配不同的對(duì)象。當(dāng)?shù)谝粋€(gè)連續(xù)空間不是Image空間時(shí),就表明當(dāng)前進(jìn)程不是Zygote進(jìn)程,而是安裝應(yīng)用程序時(shí)啟動(dòng)的一個(gè)dex2oat進(jìn)程。安裝應(yīng)用程序時(shí)啟動(dòng)的dex2oat進(jìn)程也會(huì)在內(nèi)部創(chuàng)建一個(gè)ART虛擬機(jī),不過(guò)這個(gè)ART虛擬機(jī)是用來(lái)將DEX字節(jié)碼編譯成本地機(jī)器指令的,而Zygote進(jìn)程創(chuàng)建的ART虛擬機(jī)是用來(lái)運(yùn)行應(yīng)用程序的。
接下來(lái)我們主要分析ParsedOptions類的靜態(tài)成員函數(shù)Create和ART虛擬機(jī)堆Heap的構(gòu)造函數(shù),以便可以了解ART虛擬機(jī)的啟動(dòng)參數(shù)解析過(guò)程和ART虛擬機(jī)的堆創(chuàng)建過(guò)程。
ParsedOptions類的靜態(tài)成員函數(shù)Create的實(shí)現(xiàn)如下所示:
Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, bool ignore_unrecognized) { UniquePtr<ParsedOptions> parsed(new ParsedOptions()); const char* boot_class_path_string = getenv("BOOTCLASSPATH"); if (boot_class_path_string != NULL) { parsed->boot_class_path_string_ = boot_class_path_string; } ...... parsed->is_compiler_ = false; ...... for (size_t i = 0; i < options.size(); ++i) { const std::string option(options[i].first); ...... if (StartsWith(option, "-Xbootclasspath:")) { parsed->boot_class_path_string_ = option.substr(strlen("-Xbootclasspath:")).data(); } else if (option == "bootclasspath") { parsed->boot_class_path_ = reinterpret_cast<const std::vector<const DexFile*>*>(options[i].second); } else if (StartsWith(option, "-Ximage:")) { parsed->image_ = option.substr(strlen("-Ximage:")).data(); } else if (......) { ...... } else if (option == "compiler") { parsed->is_compiler_ = true; } else { ......