Fix boot image extension relocation.
Store boot image begin and size to the extension header as
it is needed for relocation; this requires rewriting the
ImageHeader::IsAppImage(). Also mark the class roots as
visited to satisfy a DCHECK() for extension relocation.
And re-enable loading boot image extensions for tests.
Test: Add relocation tests to dex2oat_image_test.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing --relocate --no-relocate
Test: testrunner.py --target --64 --optimizing --relocate
Bug: 119800099
Change-Id: Ie7e883beaa927ca4dc91f0ae660c8de74f7d4ddb
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 567a8da..fc824c1 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -283,10 +283,17 @@
TEST_F(Dex2oatImageTest, TestExtension) {
constexpr size_t kReservationSize = 256 * MB; // This should be enough for the compiled images.
+ // Extend to both directions for maximum relocation difference.
+ static_assert(ART_BASE_ADDRESS_MIN_DELTA < 0);
+ static_assert(ART_BASE_ADDRESS_MAX_DELTA > 0);
+ static_assert(IsAligned<kPageSize>(ART_BASE_ADDRESS_MIN_DELTA));
+ static_assert(IsAligned<kPageSize>(ART_BASE_ADDRESS_MAX_DELTA));
+ constexpr size_t kExtra = ART_BASE_ADDRESS_MAX_DELTA - ART_BASE_ADDRESS_MIN_DELTA;
+ uint32_t min_relocated_address = kBaseAddress + ART_BASE_ADDRESS_MIN_DELTA;
std::string error_msg;
MemMap reservation = MemMap::MapAnonymous("Reservation",
- reinterpret_cast<uint8_t*>(kBaseAddress),
- kReservationSize,
+ reinterpret_cast<uint8_t*>(min_relocated_address),
+ kReservationSize + kExtra,
PROT_NONE,
/*low_4gb=*/ true,
/*reuse=*/ false,
@@ -393,6 +400,7 @@
// Try to load the boot image with different image locations.
std::vector<std::string> boot_class_path = libcore_dex_files;
std::vector<std::unique_ptr<gc::space::ImageSpace>> boot_image_spaces;
+ bool relocate = false;
MemMap extra_reservation;
auto load = [&](const std::string& image_location) {
boot_image_spaces.clear();
@@ -403,7 +411,7 @@
image_location,
kRuntimeISA,
gc::space::ImageSpaceLoadingOrder::kSystemFirst,
- /*relocate=*/ false,
+ relocate,
/*executable=*/ true,
/*is_zygote=*/ false,
/*extra_reservation_size=*/ 0u,
@@ -411,57 +419,61 @@
&extra_reservation);
};
- // Load primary image with full path.
- bool load_ok = load(base_location);
- ASSERT_TRUE(load_ok) << error_msg;
- ASSERT_FALSE(extra_reservation.IsValid());
- ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());
+ for (bool r : { false, true}) {
+ relocate = r;
- // Fail to load primary image with just the name.
- load_ok = load(base_name);
- ASSERT_FALSE(load_ok);
+ // Load primary image with full path.
+ bool load_ok = load(base_location);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_FALSE(extra_reservation.IsValid());
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());
- // Fail to load primary image with a search path.
- load_ok = load("*");
- ASSERT_FALSE(load_ok);
- load_ok = load(scratch_dir + "*");
- ASSERT_FALSE(load_ok);
+ // Fail to load primary image with just the name.
+ load_ok = load(base_name);
+ ASSERT_FALSE(load_ok);
- // Load the primary and first extension with full path.
- load_ok = load(base_location + ':' + mid_location);
- ASSERT_TRUE(load_ok) << error_msg;
- ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
+ // Fail to load primary image with a search path.
+ load_ok = load("*");
+ ASSERT_FALSE(load_ok);
+ load_ok = load(scratch_dir + "*");
+ ASSERT_FALSE(load_ok);
- // Load the primary with full path and fail to load first extension without full path.
- load_ok = load(base_location + ':' + mid_name);
- ASSERT_TRUE(load_ok) << error_msg; // Primary image loaded successfully.
- ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size()); // But only the primary image.
+ // Load the primary and first extension with full path.
+ load_ok = load(base_location + ':' + mid_location);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
- // Load all the libcore images with full paths.
- load_ok = load(base_location + ':' + mid_location + ':' + tail_location);
- ASSERT_TRUE(load_ok) << error_msg;
- ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+ // Load the primary with full path and fail to load first extension without full path.
+ load_ok = load(base_location + ':' + mid_name);
+ ASSERT_TRUE(load_ok) << error_msg; // Primary image loaded successfully.
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size()); // But only the primary image.
- // Load the primary and first extension with full paths, fail to load second extension by name.
- load_ok = load(base_location + ':' + mid_location + ':' + tail_name);
- ASSERT_TRUE(load_ok) << error_msg;
- ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
+ // Load all the libcore images with full paths.
+ load_ok = load(base_location + ':' + mid_location + ':' + tail_location);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
- // Load the primary with full path and fail to load first extension without full path,
- // fail to load second extension because it depends on the first.
- load_ok = load(base_location + ':' + mid_name + ':' + tail_location);
- ASSERT_TRUE(load_ok) << error_msg; // Primary image loaded successfully.
- ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size()); // But only the primary image.
+ // Load the primary and first extension with full paths, fail to load second extension by name.
+ load_ok = load(base_location + ':' + mid_location + ':' + tail_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
- // Load the primary with full path and extensions with a specified search path.
- load_ok = load(base_location + ':' + scratch_dir + '*');
- ASSERT_TRUE(load_ok) << error_msg;
- ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+ // Load the primary with full path and fail to load first extension without full path,
+ // fail to load second extension because it depends on the first.
+ load_ok = load(base_location + ':' + mid_name + ':' + tail_location);
+ ASSERT_TRUE(load_ok) << error_msg; // Primary image loaded successfully.
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size()); // But only the primary image.
- // Load the primary with full path and fail to find extensions in BCP path.
- load_ok = load(base_location + ":*");
- ASSERT_TRUE(load_ok) << error_msg;
- ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());
+ // Load the primary with full path and extensions with a specified search path.
+ load_ok = load(base_location + ':' + scratch_dir + '*');
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary with full path and fail to find extensions in BCP path.
+ load_ok = load(base_location + ":*");
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());
+ }
// Now copy the libcore dex files to the `scratch_dir` and retry loading the boot image
// with BCP in the scratch_dir so that the images can be found based on BCP paths.
@@ -475,52 +487,56 @@
bcp_component = new_location;
}
- // Loading the primary image with just the name now succeeds.
- load_ok = load(base_name);
- ASSERT_TRUE(load_ok) << error_msg;
+ for (bool r : { false, true}) {
+ relocate = r;
- // Loading the primary image with a search path still fails.
- load_ok = load("*");
- ASSERT_FALSE(load_ok);
- load_ok = load(scratch_dir + "*");
- ASSERT_FALSE(load_ok);
+ // Loading the primary image with just the name now succeeds.
+ bool load_ok = load(base_name);
+ ASSERT_TRUE(load_ok) << error_msg;
- // Load the primary and first extension without paths.
- load_ok = load(base_name + ':' + mid_name);
- ASSERT_TRUE(load_ok) << error_msg;
- ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
+ // Loading the primary image with a search path still fails.
+ load_ok = load("*");
+ ASSERT_FALSE(load_ok);
+ load_ok = load(scratch_dir + "*");
+ ASSERT_FALSE(load_ok);
- // Load the primary with full path and the first extension without full path.
- load_ok = load(base_location + ':' + mid_name);
- ASSERT_TRUE(load_ok) << error_msg; // Loaded successfully.
- ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size()); // Including the extension.
+ // Load the primary and first extension without paths.
+ load_ok = load(base_name + ':' + mid_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
- // Load all the libcore images without paths.
- load_ok = load(base_name + ':' + mid_name + ':' + tail_name);
- ASSERT_TRUE(load_ok) << error_msg;
- ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+ // Load the primary with full path and the first extension without full path.
+ load_ok = load(base_location + ':' + mid_name);
+ ASSERT_TRUE(load_ok) << error_msg; // Loaded successfully.
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size()); // Including the extension.
- // Load the primary and first extension with full paths and second extension by name.
- load_ok = load(base_location + ':' + mid_location + ':' + tail_name);
- ASSERT_TRUE(load_ok) << error_msg;
- ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+ // Load all the libcore images without paths.
+ load_ok = load(base_name + ':' + mid_name + ':' + tail_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
- // Load the primary with full path, first extension without path,
- // and second extension with full path.
- load_ok = load(base_location + ':' + mid_name + ':' + tail_location);
- ASSERT_TRUE(load_ok) << error_msg; // Loaded successfully.
- ASSERT_EQ(full_bcp.size(), boot_image_spaces.size()); // Including both extensions.
+ // Load the primary and first extension with full paths and second extension by name.
+ load_ok = load(base_location + ':' + mid_location + ':' + tail_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
- // Load the primary with full path and find both extensions in BCP path.
- load_ok = load(base_location + ":*");
- ASSERT_TRUE(load_ok) << error_msg;
- ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+ // Load the primary with full path, first extension without path,
+ // and second extension with full path.
+ load_ok = load(base_location + ':' + mid_name + ':' + tail_location);
+ ASSERT_TRUE(load_ok) << error_msg; // Loaded successfully.
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size()); // Including both extensions.
- // Fail to load any images with invalid image locations (named component after search paths).
- load_ok = load(base_location + ":*:" + tail_location);
- ASSERT_FALSE(load_ok);
- load_ok = load(base_location + ':' + scratch_dir + "*:" + tail_location);
- ASSERT_FALSE(load_ok);
+ // Load the primary with full path and find both extensions in BCP path.
+ load_ok = load(base_location + ":*");
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Fail to load any images with invalid image locations (named component after search paths).
+ load_ok = load(base_location + ":*:" + tail_location);
+ ASSERT_FALSE(load_ok);
+ load_ok = load(base_location + ':' + scratch_dir + "*:" + tail_location);
+ ASSERT_FALSE(load_ok);
+ }
ClearDirectory(scratch_dir.c_str());
int rmdir_result = rmdir(scratch_dir.c_str());
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 2c0447b..ede5ef7 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -2693,8 +2693,8 @@
PointerToLowMemUInt32(image_info.oat_data_begin_),
PointerToLowMemUInt32(oat_data_end),
PointerToLowMemUInt32(oat_file_end),
- compiler_options_.IsAppImage() ? boot_image_begin_ : 0u,
- compiler_options_.IsAppImage() ? boot_image_size_ : 0u,
+ boot_image_begin_,
+ boot_image_size_,
static_cast<uint32_t>(target_ptr_size_));
}
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index e19f964..51bc6a8 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -2227,6 +2227,7 @@
simple_relocate_visitor);
// Retrieve the Class.class, Method.class and Constructor.class needed in the loops below.
+ ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots;
ObjPtr<mirror::Class> class_class;
ObjPtr<mirror::Class> method_class;
ObjPtr<mirror::Class> constructor_class;
@@ -2241,9 +2242,8 @@
kExtension ? source_size - image_size : image_size);
int32_t class_roots_index = enum_cast<int32_t>(ImageHeader::kClassRoots);
DCHECK_LT(class_roots_index, image_roots->GetLength<kVerifyNone>());
- ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots =
- ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(base_relocate_visitor(
- image_roots->GetWithoutChecks<kVerifyNone>(class_roots_index).Ptr()));
+ class_roots = ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(base_relocate_visitor(
+ image_roots->GetWithoutChecks<kVerifyNone>(class_roots_index).Ptr()));
if (kExtension) {
DCHECK(patched_objects->Test(class_roots.Ptr()));
class_class = GetClassRoot<mirror::Class, kWithoutReadBarrier>(class_roots);
@@ -2375,6 +2375,12 @@
pos += RoundUp(object->SizeOf<kVerifyNone>(), kObjectAlignment);
}
}
+ if (kIsDebugBuild && !kExtension) {
+ // We used just Test() instead of Set() above but we need to use Set()
+ // for class roots to satisfy a DCHECK() for extensions.
+ DCHECK(!patched_objects->Test(class_roots.Ptr()));
+ patched_objects->Set(class_roots.Ptr());
+ }
}
void MaybeRelocateSpaces(const std::vector<std::unique_ptr<ImageSpace>>& spaces,
diff --git a/runtime/image.cc b/runtime/image.cc
index 256b957..08b81c1 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -93,6 +93,13 @@
}
}
+bool ImageHeader::IsAppImage() const {
+ // Unlike boot image and boot image extensions which include address space for
+ // oat files in their reservation size, app images are loaded separately from oat
+ // files and their reservation size is the image size rounded up to full page.
+ return image_reservation_size_ == RoundUp(image_size_, kPageSize);
+}
+
bool ImageHeader::IsValid() const {
if (memcmp(magic_, kImageMagic, sizeof(kImageMagic)) != 0) {
return false;
diff --git a/runtime/image.h b/runtime/image.h
index 13bf112..a2d163a 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -354,11 +354,7 @@
return data_size_;
}
- bool IsAppImage() const {
- // App images currently require a boot image, if the size is non zero then it is an app image
- // header.
- return boot_image_size_ != 0u;
- }
+ bool IsAppImage() const;
// Visit mirror::Objects in the section starting at base.
// TODO: Delete base parameter if it is always equal to GetImageBegin.
diff --git a/test/run-test b/test/run-test
index eeeefbb..72e7562 100755
--- a/test/run-test
+++ b/test/run-test
@@ -672,12 +672,12 @@
elif [ "$runtime" = "art" ]; then
if [ "$target_mode" = "no" ]; then
guess_host_arch_name
- run_args+=(--boot "${ANDROID_HOST_OUT}/framework/core${image_suffix}.art")
+ run_args+=(--boot "${ANDROID_HOST_OUT}/framework/core${image_suffix}.art:*")
run_args+=(--runtime-option "-Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}")
else
guess_target_arch_name
run_args+=(--runtime-option "-Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}")
- run_args+=(--boot "/data/art-test/core${image_suffix}.art")
+ run_args+=(--boot "/data/art-test/core${image_suffix}.art:/data/art-test/*")
fi
if [ "$relocate" = "yes" ]; then
run_args+=(--relocate)