art虚拟机缓存文件创建patchoat进程的分析

    xiaoxiao2021-12-14  25

    标签(空格分隔): 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@framework@boot.art –input-oat-location=/system/framework/boot.oat –output-oat-file=/data/dalvik-cache/x86/system@framework@boot.oat –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@framework@boot.art和system@framework@boot.oat文件,用来最后将重定向后的缓存文件生成到此处,接下来去调用函数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@framework@boot.oat output_image -> /data/dalvik-cache/x86/system@framework@boot.art 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@framework@boot.art和system@framework@boot.oat。函数接着会进入if分支,由这些标志位信息,由于此时system@framework@boot.art和system@framework@boot.oat只是一个空的文件,因此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@framework@boot.oat的文件指针,这个函数就是将/system/framework/x86/boot.oat内容拷贝至/data/dalvik-cache/x86/system@framework@boot.oat。

    Step15: PatchOat::WriteImage (/art/patchoat/patchoat.cc)

    分析函数的入参,output_image为/data/dalvik-cache/x86/system@framework@boot.art的文件指针,这个函数就是将/system/framework/x86/boot.art内容拷贝至/data/dalvik-cache/x86/system@framework@boot.art。

    Step16: patchoat (/art/patchoat/patchoat.cc)

    最终函数返回至patchoat,同时flush缓冲区,将文件写入。至此,/data/dalvik-cache/x86下的缓存文件就已经全部创建成功,同时也是经过重定位了的。这个进程也就完成了它的全部工作,接下来回到主函数main,结束进程。我们整个这个patchoat的工作就已经分析完成,接下来其父进程zygote的waitpid会被唤醒,继续执行,开启java世界,我们后文将继续分析。

    转载请注明原文地址: https://ju.6miu.com/read-970224.html

    最新回复(0)