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

标签(空格分隔): 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

在分析之前,先贴上本文的时序图,提前有一个宏观上的了解。
<img src="http://o767chiwj.bkt.clouddn.com/patchoat.png"/>
下面结合源码详细分析每一个过程。

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<OatFile>类型的智能指针,通过成员函数OpenOatFile创建一个OatFile对象。继续分析Init的最后一部分,接着获取当前的Runtime实例,设置一些参数,比如架构信息等,最后函数返回一个ImageSpace指针。

Setp9,Step10,Step11###

函数层层返回,最后回带Patch::Patch函数中继续执行,input_image为指向/system/framework/x86/boot.art的文件指针,然后将其映射到内存,返回一个std::unique_ptr<MemMap>类型的智能指针image,接着再通过input_oat(/system/framework/x86/boot.oat)文件创建一个std::unique_ptr<ElfFile>类型的智能指针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世界,我们后文将继续分析。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容