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@[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世界,我們後文將繼續分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章