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;
}