標籤(空格分隔): javaVM patchoat art android5.1
patchoat進程是由zygote進程第一次啓動時,如果在/data/dalvik-cache/x86/下沒有jvm的一些緩存文件,則會fork出一個子進程,來進行這些文件的創建,同時其父進程zygote會處於等待狀態,直至patchoat進程工作完成,進程patchoat的啓動是通過fork和execv系統調用產生的,其啓動參數爲,我這裏爲x86的架構。
/system/bin/patchoat
–input-image-location=/system/framework/boot.art
–output-image-file=/data/dalvik-cache/x86/system@[email protected]
–input-oat-location=/system/framework/boot.oat
–output-oat-file=/data/dalvik-cache/x86/system@[email protected]
–instruction-set=x86
–base-offset-delta=-344064
在分析之前,先貼上本文的時序圖,提前有一個宏觀上的瞭解。
下面結合源碼詳細分析每一個過程。
Step1: patchoat.main (/art/patchoat/patchoat.cc)
這個方法實現很簡單,就是繼續調用namespace art下的patchoat函數
Step2: patchoat.patchoat (/art/patchoat/patchoat.cc)
static int patchoat(int argc, char **argv) {
InitLogging(argv);
MemMap::Init();
...
for (int i = 0; i < argc; i++) {
...
}
...
bool ret;
if (have_image_files && have_oat_files) {
TimingLogger::ScopedTiming pt("patch image and oat", &timings);
ret = PatchOat::Patch(input_oat.get(), input_image_location, base_delta,
output_oat.get(), output_image.get(), isa, &timings,
output_oat_fd >= 0, // was it opened from FD?
new_oat_out);
// The order here doesn't matter. If the first one is successfully saved and the second one
// erased, ImageSpace will still detect a problem and not use the files.
ret = ret && FinishFile(output_image.get(), ret);
ret = ret && FinishFile(output_oat.get(), ret);
} else if (have_oat_files) {
TimingLogger::ScopedTiming pt("patch oat", &timings);
ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
output_oat_fd >= 0, // was it opened from FD?
new_oat_out);
ret = ret && FinishFile(output_oat.get(), ret);
} else if (have_image_files) {
TimingLogger::ScopedTiming pt("patch image", &timings);
ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
ret = ret && FinishFile(output_image.get(), ret);
} else {
CHECK(false);
ret = true;
}
if (kIsDebugBuild) {
LOG(INFO) << "Exiting with return ... " << ret;
}
cleanup(ret);
return (ret) ? EXIT_SUCCESS : EXIT_FAILURE;
}
在這個方法中,首先for循環中解析execv系統調用的argv參數,have_image_files和have_oat_files均爲true,在我們這種情況下,由於系統第一次運行/data/dalvik-cache/x86目錄下爲空,從命令行解析參數執行效果來看,會在/data/dalvik-cache/x86目錄下創建system@[email protected]和system@[email protected]文件,用來最後將重定向後的緩存文件生成到此處,接下來去調用函數PatchOat::Patch去執行下一步。
Step3: PatchOat::Patch (/art/patchoat/patchoat.cc)
bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t delta,
File* output_oat, File* output_image, InstructionSet isa,
TimingLogger* timings,
bool output_oat_opened_from_fd,
bool new_oat_out) {
...
const char* isa_name = GetInstructionSetString(isa);
std::string image_filename;
if (!LocationToFilename(image_location, isa, &image_filename)) {
LOG(ERROR) << "Unable to find image at location " << image_location;
return false;
}
std::unique_ptr<File> input_image(OS::OpenFileForReading(image_filename.c_str()));
if (input_image.get() == nullptr) {
LOG(ERROR) << "unable to open input image file at " << image_filename
<< " for location " << image_location;
return false;
}
int64_t image_len = input_image->GetLength();
if (image_len < 0) {
LOG(ERROR) << "Error while getting image length";
return false;
}
ImageHeader image_header;
if (sizeof(image_header) != input_image->Read(reinterpret_cast<char*>(&image_header),
sizeof(image_header), 0)) {
LOG(ERROR) << "Unable to read image header from image file " << input_image->GetPath();
}
/*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
// Nothing special to do right now since the image always needs to get patched.
// Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
// Set up the runtime
RuntimeOptions options;
NoopCompilerCallbacks callbacks;
options.push_back(std::make_pair("compilercallbacks", &callbacks));
std::string img = "-Ximage:" + image_location;
options.push_back(std::make_pair(img.c_str(), nullptr));
options.push_back(std::make_pair("imageinstructionset", reinterpret_cast<const void*>(isa_name)));
if (!Runtime::Create(options, false)) {
LOG(ERROR) << "Unable to initialize runtime";
return false;
}
// Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
// give it away now and then switch to a more manageable ScopedObjectAccess.
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
ScopedObjectAccess soa(Thread::Current());
t.NewTiming("Image and oat Patching setup");
// Create the map where we will write the image patches to.
std::string error_msg;
std::unique_ptr<MemMap> image(MemMap::MapFile(image_len, PROT_READ | PROT_WRITE, MAP_PRIVATE,
input_image->Fd(), 0,
input_image->GetPath().c_str(),
&error_msg));
if (image.get() == nullptr) {
LOG(ERROR) << "unable to map image file " << input_image->GetPath() << " : " << error_msg;
return false;
}
gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetImageSpace();
std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
if (elf.get() == nullptr) {
LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
return false;
}
bool skip_patching_oat = false;
MaybePic is_oat_pic = IsOatPic(elf.get());
if (is_oat_pic >= ERROR_FIRST) {
// Error logged by IsOatPic
return false;
} else if (is_oat_pic == PIC) {
// Do not need to do ELF-file patching. Create a symlink and skip the ELF patching.
if (!ReplaceOatFileWithSymlink(input_oat->GetPath(),
output_oat->GetPath(),
output_oat_opened_from_fd,
new_oat_out)) {
// Errors already logged by above call.
return false;
}
// Don't patch the OAT, since we just symlinked it. Image still needs patching.
skip_patching_oat = true;
} else {
CHECK(is_oat_pic == NOT_PIC);
}
PatchOat p(isa, elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
delta, timings);
t.NewTiming("Patching files");
if (!skip_patching_oat && !p.PatchElf()) {
LOG(ERROR) << "Failed to patch oat file " << input_oat->GetPath();
return false;
}
if (!p.PatchImage()) {
LOG(ERROR) << "Failed to patch image file " << input_image->GetPath();
return false;
}
t.NewTiming("Writing files");
if (!skip_patching_oat && !p.WriteElf(output_oat)) {
LOG(ERROR) << "Failed to write oat file " << input_oat->GetPath();
return false;
}
if (!p.WriteImage(output_image)) {
LOG(ERROR) << "Failed to write image file " << input_image->GetPath();
return false;
}
return true;
}
爲了更清楚的分析,函數的入參分別爲
input_oat -> /system/framework/x86/boot.oat
image_location -> /system/framework/boot.art
delta -> -344064
output_oat -> /data/dalvik-cache/x86/system@[email protected]
output_image -> /data/dalvik-cache/x86/system@[email protected]
isa -> 對應x86
timings -> 不關注此參數,爲一個時間的log
output_oat_opened_from_fd -> false
new_oat_out -> true
boot.art與boot.oat與其說是ART虛擬機的兩種執行格式,不如說他倆就是ART虛擬機的一部分,ART離開了這兩個文件,也就無法啓動了。boot.art是一個img文件,而boot.oat文件可以將其理解爲ART虛擬機的啓動類。函數接下來創建input_image一個文件指針,它指向的文件爲/system/framework/x86/boot.art的鏡像文件,而後讀取這個文件的頭信息到ImageHeader類型的image_header中,它是用來讀和驗證art文件的一個對象,這裏只是做了一些驗證的信息,接下來設置RuntimeOptions參數,創建一個Runtime對象。
Step4: Runtime::Create (/art/runtime/runtime.cc)
正如前文分析過,這個方法實現很簡單,就是看單例對象instance_是否存在,不存在,則創建一個,並調用Init函數。
Step5: Runtime::Init (/art/runtime/runtime.cc)
首先分析函數的入參RuntimeOptions options,它裏面的內容爲”[compilercallbacks, ],[-Ximage:/system/framework/boot.art],[imageinstructionset, ]”,在函數中,調用ParsedOptions類進行參數解析,所有關於jvm的設置均在此類中進行解析和設置,再通過這些參數創建一個Heap對象,我們先分析在Heap創建時候做了哪些事情。在Init函數中,因爲此時我們並不是Zygote進程,而是由zygote進程fork出來的,這個進程的作用就是對對boot.oat和boot.art進行重定位,重定位之後新的boot.art和boot.art會存放在/data/dalvik-cache中,因此只需要關注ImageSpace這塊的代碼,這個進程在之後就結束了,,在這個函數中,我們只看Heap創建的代碼,下面繼續分析
Step6:Heap::Heap (/art/runtime/gc/heap.cc)
Heap::Heap(...) {
...
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);
if (image_space != nullptr) {
AddSpace(image_space);
// Oat files referenced by image files immediately follow them in memory, ensure alloc space
// isn't going to get in the middle
byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd();
CHECK_GT(oat_file_end_addr, image_space->End());
requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
} else {
LOG(WARNING) << "Could not create image space with image file '" << image_file_name << "'. "
<< "Attempting to fall back to imageless running. Error was: " << error_msg;
}
}
...
}
構造函數Heap對許多成員變量做了一些初始化,我們這裏並不關注,在zygote進程創建堆時需要重點關注這些,因爲它纔會真正的創建虛擬機和啓動java世界。這個函數中,可以看到繼續調用了ImageSpace::Create創建了一個ImageSpace對象,此時參數image_file_name爲/system/framework/boot.art,image_instruction_set爲x86架構。
Step7: ImageSpace::Create (/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);
if (Runtime::Current()->IsZygote()) {
MarkZygoteStart(image_isa);
}
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根據架構和參數image_location去尋找對應架構下的image文件,然後根據結果去設置一些標誌位的值,具體可以仔細閱讀代碼,此時found_image爲ture,relocate爲false,can_compile爲true,has_system爲ture, has_cache爲true,因爲在patchoat函數中已經在/data/dalvik-cache/x86下創建system@[email protected]和system@[email protected]。函數接着會進入if分支,由這些標誌位信息,由於此時system@[email protected]和system@[email protected]只是一個空的文件,因此ChecksumsMatch會失敗,繼續分析最終函數執行到ImageSpace::Init去初始化這塊ImageSpace,該函數三個入參分別爲/system/framework/x86/boot.art,/system/framework/boot.art,false。
Step8: ImageSpace::Init (/art/runtime/gc/space/image_space.cc)
ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_location,
bool validate_oat_file, std::string* error_msg) {
...
std::unique_ptr<File> file(OS::OpenFileForReading(image_filename));
if (file.get() == NULL) {
*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;
}
// 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() == NULL) {
return nullptr;
}
std::unique_ptr<MemMap> image_map(
MemMap::MapFileAtAddress(nullptr, image_header.GetImageBitmapSize(),
PROT_READ, MAP_PRIVATE,
file->Fd(), image_header.GetBitmapOffset(),
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<byte*>(map->Begin()),
map->Size()));
if (bitmap.get() == nullptr) {
*error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str());
return nullptr;
}
std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename, image_location,
map.release(), bitmap.release()));
// 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;
}
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());
mirror::Object* resolution_method = image_header.GetImageRoot(ImageHeader::kResolutionMethod);
runtime->SetResolutionMethod(down_cast<mirror::ArtMethod*>(resolution_method));
mirror::Object* imt_conflict_method = image_header.GetImageRoot(ImageHeader::kImtConflictMethod);
runtime->SetImtConflictMethod(down_cast<mirror::ArtMethod*>(imt_conflict_method));
mirror::Object* imt_unimplemented_method =
image_header.GetImageRoot(ImageHeader::kImtUnimplementedMethod);
runtime->SetImtUnimplementedMethod(down_cast<mirror::ArtMethod*>(imt_unimplemented_method));
mirror::Object* default_imt = image_header.GetImageRoot(ImageHeader::kDefaultImt);
runtime->SetDefaultImt(down_cast<mirror::ObjectArray<mirror::ArtMethod>*>(default_imt));
mirror::Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod);
runtime->SetCalleeSaveMethod(down_cast<mirror::ArtMethod*>(callee_save_method),
Runtime::kSaveAll);
callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsOnlySaveMethod);
runtime->SetCalleeSaveMethod(down_cast<mirror::ArtMethod*>(callee_save_method),
Runtime::kRefsOnly);
callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsAndArgsSaveMethod);
runtime->SetCalleeSaveMethod(down_cast<mirror::ArtMethod*>(callee_save_method),
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();
}
函數先是調用OpenFileForReading打開這個文件,即是/system/framework/x86/boot.art,file是指向這個文件的文件指針,接下來調用ReadFully將這個文件的頭信息讀入image_header中,具體文件頭結構涉及到art虛擬機文件格式,這裏不需要太關注,以免失去了文章的主線,獲取到這個文件頭信息之後,檢查這個文件是否是有效的art文件,校驗通過之後,接着調用MemMap::MapFileAtAddress去映射到內存,MapFileAtAddress函數中,做了一些頁對齊的設置,而後就是mmap的東西。在這裏,一共映射了兩塊內存區域。
map: 映射整個boot.art文件到內存,起始地址是固定的
image_map: 映射了boot.art文件中的Bitmap的內容到內存,起始地址由系統決定
接下來通過這兩個映射區的指針,創建ImageSpace對象,接下來設置其成員變量oat_file_的值,它是一個std::unique_ptr類型的智能指針,通過成員函數OpenOatFile創建一個OatFile對象。繼續分析Init的最後一部分,接着獲取當前的Runtime實例,設置一些參數,比如架構信息等,最後函數返回一個ImageSpace指針。
Setp9,Step10,Step11
函數層層返回,最後迴帶Patch::Patch函數中繼續執行,input_image爲指向/system/framework/x86/boot.art的文件指針,然後將其映射到內存,返回一個std::unique_ptr類型的智能指針image,接着再通過input_oat(/system/framework/x86/boot.oat)文件創建一個std::unique_ptr類型的智能指針elf,然後再根據這些信息,新建一個PatchOat對象,下面來看PatchOat的構造函數
PatchOat(InstructionSet isa, ElfFile* oat_file, MemMap* image,
gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta,
TimingLogger* timings)
: oat_file_(oat_file), image_(image), bitmap_(bitmap), heap_(heap),
delta_(delta), isa_(isa), timings_(timings) {}
構造函數實現很簡單,就是對其成員變量賦值,ElfFile類型的指針oat_file_指向/system/framework/x86/boot.oat,MemMap類型的image_指針指向/system/framework/x86/boot.art,bitmap_和heap_存放的是ImageSpace中的地址,delta就是上文那個隨機地址變量,爲什麼需要這個呢,Android源碼中指定了一個base地址作爲其加載到內存的默認地址,如果不重定位的話,會導致使用這個ROM的Android 設備image空間起始地址都一樣,這容易被攻擊。所以就需要重定位。一般情況下,/data/dalvik-cache中的boot.art和boot.oat都是經過重定位的。/system/frmework中的是沒有經過重定位的。重定位其實很簡單,就是在一定範圍內產生一個隨機數,然後實際加載地址是base+這個隨機數。接下來會調用4個方法PatchElf、PatchImage、WriteElf、WriteImage。下面一個個來分析。
Step12: PatchOat::PatchElf (/art/patchoat/patchoat.cc)
bool PatchOat::PatchElf() {
TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_);
if (!PatchTextSection()) {
return false;
}
if (!PatchOatHeader()) {
return false;
}
bool need_fixup = false;
t.NewTiming("Fixup Elf Headers");
// Fixup Phdr's
for (unsigned int i = 0; i < oat_file_->GetProgramHeaderNum(); i++) {
Elf32_Phdr* hdr = oat_file_->GetProgramHeader(i);
CHECK(hdr != nullptr);
if (hdr->p_vaddr != 0 && hdr->p_vaddr != hdr->p_offset) {
need_fixup = true;
hdr->p_vaddr += delta_;
}
if (hdr->p_paddr != 0 && hdr->p_paddr != hdr->p_offset) {
need_fixup = true;
hdr->p_paddr += delta_;
}
}
if (!need_fixup) {
// This was never passed through ElfFixup so all headers/symbols just have their offset as
// their addr. Therefore we do not need to update these parts.
return true;
}
t.NewTiming("Fixup Section Headers");
for (unsigned int i = 0; i < oat_file_->GetSectionHeaderNum(); i++) {
Elf32_Shdr* hdr = oat_file_->GetSectionHeader(i);
CHECK(hdr != nullptr);
if (hdr->sh_addr != 0) {
hdr->sh_addr += delta_;
}
}
t.NewTiming("Fixup Dynamics");
for (Elf32_Word i = 0; i < oat_file_->GetDynamicNum(); i++) {
Elf32_Dyn& dyn = oat_file_->GetDynamic(i);
if (IsDynamicSectionPointer(dyn.d_tag, oat_file_->GetHeader().e_machine)) {
dyn.d_un.d_ptr += delta_;
}
}
t.NewTiming("Fixup Elf Symbols");
// Fixup dynsym
Elf32_Shdr* dynsym_sec = oat_file_->FindSectionByName(".dynsym");
CHECK(dynsym_sec != nullptr);
if (!PatchSymbols(dynsym_sec)) {
return false;
}
// Fixup symtab
Elf32_Shdr* symtab_sec = oat_file_->FindSectionByName(".symtab");
if (symtab_sec != nullptr) {
if (!PatchSymbols(symtab_sec)) {
return false;
}
}
return true;
}
這個函數主要就是根據生成的隨機地址delta_,調整oat_file_映射的內存區域的值,oat_file_對應於/system/framework/x86/boot.oat,包括代碼段,ota的頭,符號表等,都是通過在原有基礎上,給一個delta_的偏移量來實現的。具體涉及的知識點比較複雜,暫且先不關注。
Step13: PatchOat::PatchImage (/art/patchoat/patchoat.cc)
bool PatchOat::PatchImage() {
ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
CHECK_GT(image_->Size(), sizeof(ImageHeader));
// These are the roots from the original file.
mirror::Object* img_roots = image_header->GetImageRoots();
image_header->RelocateImage(delta_);
VisitObject(img_roots);
if (!image_header->IsValid()) {
LOG(ERROR) << "reloction renders image header invalid";
return false;
}
{
TimingLogger::ScopedTiming t("Walk Bitmap", timings_);
// Walk the bitmap.
WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
bitmap_->Walk(PatchOat::BitmapCallback, this);
}
return true;
}
函數首先是將image映射的內存區對應於ImageHeader那塊內存地址,賦值給image_header,而後調用函數RelocateImage傳入這個隨機地址變量,對ImageHeader內存塊進行一個偏移量設置,這裏image_對應的即是/system/framework/x86/boot.art。
Step14: PatchOat::WriteElf (/art/patchoat/patchoat.cc)
分析函數的入參,output_oat爲/data/dalvik-cache/x86/system@[email protected]的文件指針,這個函數就是將/system/framework/x86/boot.oat內容拷貝至/data/dalvik-cache/x86/system@[email protected]。
Step15: PatchOat::WriteImage (/art/patchoat/patchoat.cc)
分析函數的入參,output_image爲/data/dalvik-cache/x86/system@[email protected]的文件指針,這個函數就是將/system/framework/x86/boot.art內容拷貝至/data/dalvik-cache/x86/system@[email protected]。
Step16: patchoat (/art/patchoat/patchoat.cc)
最終函數返回至patchoat,同時flush緩衝區,將文件寫入。至此,/data/dalvik-cache/x86下的緩存文件就已經全部創建成功,同時也是經過重定位了的。這個進程也就完成了它的全部工作,接下來回到主函數main,結束進程。我們整個這個patchoat的工作就已經分析完成,接下來其父進程zygote的waitpid會被喚醒,繼續執行,開啓java世界,我們後文將繼續分析。