标签(空格分隔): 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
在分析之前,先贴上本文的时序图,提前有一个宏观上的了解。
下面结合源码详细分析每一个过程。
这个方法实现很简单,就是继续调用namespace art下的patchoat函数
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去执行下一步。
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对象。
正如前文分析过,这个方法实现很简单,就是看单例对象instance_是否存在,不存在,则创建一个,并调用Init函数。
首先分析函数的入参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创建的代码,下面继续分析
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架构。
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。
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指针。
函数层层返回,最后回带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。下面一个个来分析。
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_的偏移量来实现的。具体涉及的知识点比较复杂,暂且先不关注。
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。
分析函数的入参,output_oat为/data/dalvik-cache/x86/system@[email protected]的文件指针,这个函数就是将/system/framework/x86/boot.oat内容拷贝至/data/dalvik-cache/x86/system@[email protected]。
分析函数的入参,output_image为/data/dalvik-cache/x86/system@[email protected]的文件指针,这个函数就是将/system/framework/x86/boot.art内容拷贝至/data/dalvik-cache/x86/system@[email protected]。
最终函数返回至patchoat,同时flush缓冲区,将文件写入。至此,/data/dalvik-cache/x86下的缓存文件就已经全部创建成功,同时也是经过重定位了的。这个进程也就完成了它的全部工作,接下来回到主函数main,结束进程。我们整个这个patchoat的工作就已经分析完成,接下来其父进程zygote的waitpid会被唤醒,继续执行,开启java世界,我们后文将继续分析。