static bool RelocateInPlace()

in runtime/gc/space/image_space.cc [946:1147]


static bool RelocateInPlace(ImageHeader& image_header,
                            uint8_t* target_base,
                            accounting::ContinuousSpaceBitmap* bitmap,
                            const OatFile* app_oat_file,
                            std::string* error_msg) {
  DCHECK(error_msg != nullptr);
  if (!image_header.IsPic()) {
    if (image_header.GetImageBegin() == target_base) {
      return true;
    }
    *error_msg = StringPrintf("Cannot relocate non-pic image for oat file %s",
                              (app_oat_file != nullptr) ? app_oat_file->GetLocation().c_str() : "");
    return false;
  }
  // Set up sections.
  uint32_t boot_image_begin = 0;
  uint32_t boot_image_end = 0;
  uint32_t boot_oat_begin = 0;
  uint32_t boot_oat_end = 0;
  const size_t pointer_size = image_header.GetPointerSize();
  gc::Heap* const heap = Runtime::Current()->GetHeap();
  heap->GetBootImagesSize(&boot_image_begin, &boot_image_end, &boot_oat_begin, &boot_oat_end);
  if (boot_image_begin == boot_image_end) {
    *error_msg = "Can not relocate app image without boot image space";
    return false;
  }
  if (boot_oat_begin == boot_oat_end) {
    *error_msg = "Can not relocate app image without boot oat file";
    return false;
  }
  const uint32_t boot_image_size = boot_image_end - boot_image_begin;
  const uint32_t boot_oat_size = boot_oat_end - boot_oat_begin;
  const uint32_t image_header_boot_image_size = image_header.GetBootImageSize();
  const uint32_t image_header_boot_oat_size = image_header.GetBootOatSize();
  if (boot_image_size != image_header_boot_image_size) {
    *error_msg = StringPrintf("Boot image size %" PRIu64 " does not match expected size %"
                                  PRIu64,
                              static_cast<uint64_t>(boot_image_size),
                              static_cast<uint64_t>(image_header_boot_image_size));
    return false;
  }
  if (boot_oat_size != image_header_boot_oat_size) {
    *error_msg = StringPrintf("Boot oat size %" PRIu64 " does not match expected size %"
                                  PRIu64,
                              static_cast<uint64_t>(boot_oat_size),
                              static_cast<uint64_t>(image_header_boot_oat_size));
    return false;
  }
  TimingLogger logger(__FUNCTION__, true, false);
  RelocationRange boot_image(image_header.GetBootImageBegin(),
                             boot_image_begin,
                             boot_image_size);
  RelocationRange boot_oat(image_header.GetBootOatBegin(),
                           boot_oat_begin,
                           boot_oat_size);
  RelocationRange app_image(reinterpret_cast<uintptr_t>(image_header.GetImageBegin()),
                            reinterpret_cast<uintptr_t>(target_base),
                            image_header.GetImageSize());
  // Use the oat data section since this is where the OatFile::Begin is.
  RelocationRange app_oat(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
                          // Not necessarily in low 4GB.
                          reinterpret_cast<uintptr_t>(app_oat_file->Begin()),
                          image_header.GetOatDataEnd() - image_header.GetOatDataBegin());
  VLOG(image) << "App image " << app_image;
  VLOG(image) << "App oat " << app_oat;
  VLOG(image) << "Boot image " << boot_image;
  VLOG(image) << "Boot oat " << boot_oat;
  // True if we need to fixup any heap pointers, otherwise only code pointers.
  const bool fixup_image = boot_image.Delta() != 0 || app_image.Delta() != 0;
  const bool fixup_code = boot_oat.Delta() != 0 || app_oat.Delta() != 0;
  if (!fixup_image && !fixup_code) {
    // Nothing to fix up.
    return true;
  }
  ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
  // Need to update the image to be at the target base.
  const ImageSection& objects_section = image_header.GetImageSection(ImageHeader::kSectionObjects);
  uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
  uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
  FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat);
  if (fixup_image) {
    // Two pass approach, fix up all classes first, then fix up non class-objects.
    // The visited bitmap is used to ensure that pointer arrays are not forwarded twice.
    std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> visited_bitmap(
        gc::accounting::ContinuousSpaceBitmap::Create("Relocate bitmap",
                                                      target_base,
                                                      image_header.GetImageSize()));
    FixupObjectVisitor fixup_object_visitor(visited_bitmap.get(),
                                            pointer_size,
                                            boot_image,
                                            boot_oat,
                                            app_image,
                                            app_oat);
    TimingLogger::ScopedTiming timing("Fixup classes", &logger);
    // Fixup objects may read fields in the boot image, use the mutator lock here for sanity. Though
    // its probably not required.
    ScopedObjectAccess soa(Thread::Current());
    timing.NewTiming("Fixup objects");
    bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor);
    // Fixup image roots.
    CHECK(app_image.InSource(reinterpret_cast<uintptr_t>(
        image_header.GetImageRoots<kWithoutReadBarrier>())));
    image_header.RelocateImageObjects(app_image.Delta());
    CHECK_EQ(image_header.GetImageBegin(), target_base);
    // Fix up dex cache DexFile pointers.
    auto* dex_caches = image_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kDexCaches)->
        AsObjectArray<mirror::DexCache, kVerifyNone, kWithoutReadBarrier>();
    for (int32_t i = 0, count = dex_caches->GetLength(); i < count; ++i) {
      mirror::DexCache* dex_cache = dex_caches->Get<kVerifyNone, kWithoutReadBarrier>(i);
      // Fix up dex cache pointers.
      GcRoot<mirror::String>* strings = dex_cache->GetStrings();
      if (strings != nullptr) {
        GcRoot<mirror::String>* new_strings = fixup_adapter.ForwardObject(strings);
        if (strings != new_strings) {
          dex_cache->SetStrings(new_strings);
        }
        dex_cache->FixupStrings<kWithoutReadBarrier>(new_strings, fixup_adapter);
      }
      GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
      if (types != nullptr) {
        GcRoot<mirror::Class>* new_types = fixup_adapter.ForwardObject(types);
        if (types != new_types) {
          dex_cache->SetResolvedTypes(new_types);
        }
        dex_cache->FixupResolvedTypes<kWithoutReadBarrier>(new_types, fixup_adapter);
      }
      ArtMethod** methods = dex_cache->GetResolvedMethods();
      if (methods != nullptr) {
        ArtMethod** new_methods = fixup_adapter.ForwardObject(methods);
        if (methods != new_methods) {
          dex_cache->SetResolvedMethods(new_methods);
        }
        for (size_t j = 0, num = dex_cache->NumResolvedMethods(); j != num; ++j) {
          ArtMethod* orig = mirror::DexCache::GetElementPtrSize(new_methods, j, pointer_size);
          ArtMethod* copy = fixup_adapter.ForwardObject(orig);
          if (orig != copy) {
            mirror::DexCache::SetElementPtrSize(new_methods, j, copy, pointer_size);
          }
        }
      }
      ArtField** fields = dex_cache->GetResolvedFields();
      if (fields != nullptr) {
        ArtField** new_fields = fixup_adapter.ForwardObject(fields);
        if (fields != new_fields) {
          dex_cache->SetResolvedFields(new_fields);
        }
        for (size_t j = 0, num = dex_cache->NumResolvedFields(); j != num; ++j) {
          ArtField* orig = mirror::DexCache::GetElementPtrSize(new_fields, j, pointer_size);
          ArtField* copy = fixup_adapter.ForwardObject(orig);
          if (orig != copy) {
            mirror::DexCache::SetElementPtrSize(new_fields, j, copy, pointer_size);
          }
        }
      }
    }
  }
  {
    // Only touches objects in the app image, no need for mutator lock.
    TimingLogger::ScopedTiming timing("Fixup methods", &logger);
    FixupArtMethodVisitor method_visitor(fixup_image,
                                         pointer_size,
                                         boot_image,
                                         boot_oat,
                                         app_image,
                                         app_oat);
    image_header.VisitPackedArtMethods(&method_visitor, target_base, pointer_size);
  }
  if (fixup_image) {
    {
      // Only touches objects in the app image, no need for mutator lock.
      TimingLogger::ScopedTiming timing("Fixup fields", &logger);
      FixupArtFieldVisitor field_visitor(boot_image, boot_oat, app_image, app_oat);
      image_header.VisitPackedArtFields(&field_visitor, target_base);
    }
    {
      TimingLogger::ScopedTiming timing("Fixup imt", &logger);
      image_header.VisitPackedImTables(fixup_adapter, target_base, pointer_size);
    }
    {
      TimingLogger::ScopedTiming timing("Fixup conflict tables", &logger);
      image_header.VisitPackedImtConflictTables(fixup_adapter, target_base, pointer_size);
    }
    // In the app image case, the image methods are actually in the boot image.
    image_header.RelocateImageMethods(boot_image.Delta());
    const auto& class_table_section = image_header.GetImageSection(ImageHeader::kSectionClassTable);
    if (class_table_section.Size() > 0u) {
      // Note that we require that ReadFromMemory does not make an internal copy of the elements.
      // This also relies on visit roots not doing any verification which could fail after we update
      // the roots to be the image addresses.
      ScopedObjectAccess soa(Thread::Current());
      WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
      ClassTable temp_table;
      temp_table.ReadFromMemory(target_base + class_table_section.Offset());
      FixupRootVisitor root_visitor(boot_image, boot_oat, app_image, app_oat);
      temp_table.VisitRoots(root_visitor);
    }
  }
  if (VLOG_IS_ON(image)) {
    logger.Dump(LOG(INFO));
  }
  return true;
}