/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "image_test.h" #include "base/pointer_size.h" #include "oat/image.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" namespace art { namespace linker { TEST_F(ImageTest, TestImageLayout) { std::vector image_sizes; std::vector image_sizes_extra; // Compile multi-image with ImageLayoutA being the last image. { CompilationHelper helper; Compile(ImageHeader::kStorageModeUncompressed, /*max_image_block_size=*/std::numeric_limits::max(), helper, "ImageLayoutA", {"LMyClass;"}); image_sizes = helper.GetImageObjectSectionSizes(); } TearDown(); runtime_.reset(); SetUp(); // Compile multi-image with ImageLayoutB being the last image. { CompilationHelper helper; Compile(ImageHeader::kStorageModeUncompressed, /*max_image_block_size=*/std::numeric_limits::max(), helper, "ImageLayoutB", {"LMyClass;"}); image_sizes_extra = helper.GetImageObjectSectionSizes(); } // Make sure that the new stuff in the clinit in ImageLayoutB is in the last image and not in the // first two images. ASSERT_EQ(image_sizes.size(), image_sizes.size()); // Sizes of the object sections should be the same for all but the last image. for (size_t i = 0; i < image_sizes.size() - 1; ++i) { EXPECT_EQ(image_sizes[i], image_sizes_extra[i]); } // Last image should be larger since it has a hash map and a string. EXPECT_LT(image_sizes.back(), image_sizes_extra.back()); } TEST_F(ImageTest, ImageHeaderIsValid) { uint32_t image_begin = ART_BASE_ADDRESS; uint32_t image_size_ = kElfSegmentAlignment; uint32_t image_roots = ART_BASE_ADDRESS + (1 * KB); uint32_t oat_checksum = 0; uint32_t oat_file_begin = ART_BASE_ADDRESS + (kElfSegmentAlignment); uint32_t oat_data_begin = ART_BASE_ADDRESS + (2 * kElfSegmentAlignment); uint32_t oat_data_end = ART_BASE_ADDRESS + (2 * kElfSegmentAlignment + 1 * KB); uint32_t oat_file_end = ART_BASE_ADDRESS + (2 * kElfSegmentAlignment + 2 * KB); ImageSection sections[ImageHeader::kSectionCount]; uint32_t image_reservation_size = RoundUp(oat_file_end - image_begin, kElfSegmentAlignment); ImageHeader image_header(image_reservation_size, /*component_count=*/ 1u, image_begin, image_size_, sections, image_roots, oat_checksum, oat_file_begin, oat_data_begin, oat_data_end, oat_file_end, /*boot_image_begin=*/ 0u, /*boot_image_size=*/ 0u, /*boot_image_component_count=*/ 0u, /*boot_image_checksum=*/ 0u, kRuntimePointerSize); ASSERT_TRUE(image_header.IsValid()); // Please note that for the following condition to be true, the above values should be chosen in // a way that image_reservation_size != RoundUp(image_size_, kElfSegmentAlignment). ASSERT_TRUE(!image_header.IsAppImage()); char* magic = const_cast(image_header.GetMagic()); strcpy(magic, ""); // bad magic ASSERT_FALSE(image_header.IsValid()); strcpy(magic, "art\n000"); // bad version ASSERT_FALSE(image_header.IsValid()); } // Test that pointer to quick code is the same in // a default method of an interface and in a copied method // of a class which implements the interface. This should be true // only if the copied method and the origin method are located in the // same oat file. TEST_F(ImageTest, TestDefaultMethods) { // TODO(b/376621099): investigate LUCI failures (timeouts?) and re-enable this test. // This is probably not related to riscv64 arch, but a combination of riscv64 and running // on VM, but we don't use TEST_DISABLED_ON_VM to keep running it on other VM builders. TEST_DISABLED_FOR_RISCV64(); // Use this test to compile managed code to catch crashes when compiling the boot class path. // This test already needs to compile some managed methods and by compiling with "speed" we // avoid the need to create a specialized profile for the "speed-profile" compilation. // (Using "speed" shall compile most methods. We could compile more with "everything".) SetCompilerFilter(CompilerFilter::kSpeed); CompilationHelper helper; Compile(ImageHeader::kStorageModeUncompressed, /*max_image_block_size=*/std::numeric_limits::max(), helper, "DefaultMethods", {"LIface;", "LImpl;", "LIterableBase;"}); PointerSize pointer_size = class_linker_->GetImagePointerSize(); Thread* self = Thread::Current(); ScopedObjectAccess soa(self); // Test the pointer to quick code is the same in origin method // and in the copied method from the same oat file. ObjPtr iface_klass = class_linker_->LookupClass(self, "LIface;", /*class_loader=*/ nullptr); ASSERT_NE(nullptr, iface_klass); ArtMethod* origin = iface_klass->FindInterfaceMethod("defaultMethod", "()V", pointer_size); ASSERT_NE(nullptr, origin); ASSERT_OBJ_PTR_EQ(origin->GetDeclaringClass(), iface_klass); const void* code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); // The origin method should have a pointer to quick code ASSERT_NE(nullptr, code); ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); ObjPtr impl_klass = class_linker_->LookupClass(self, "LImpl;", /*class_loader=*/ nullptr); ASSERT_NE(nullptr, impl_klass); ArtMethod* copied = FindCopiedMethod(origin, impl_klass); ASSERT_NE(nullptr, copied); // the copied method should have pointer to the same quick code as the origin method ASSERT_EQ(code, copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)); // Test the origin method has pointer to quick code // but the copied method has pointer to interpreter // because these methods are in different oat files. ObjPtr iterable_klass = class_linker_->LookupClass(self, "Ljava/lang/Iterable;", /*class_loader=*/ nullptr); ASSERT_NE(nullptr, iterable_klass); origin = iterable_klass->FindClassMethod( "forEach", "(Ljava/util/function/Consumer;)V", pointer_size); ASSERT_NE(nullptr, origin); ASSERT_FALSE(origin->IsDirect()); ASSERT_OBJ_PTR_EQ(origin->GetDeclaringClass(), iterable_klass); code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); // the origin method should have a pointer to quick code ASSERT_NE(nullptr, code); ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); ObjPtr iterablebase_klass = class_linker_->LookupClass(self, "LIterableBase;", /*class_loader=*/ nullptr); ASSERT_NE(nullptr, iterablebase_klass); copied = FindCopiedMethod(origin, iterablebase_klass); ASSERT_NE(nullptr, copied); code = copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); // the copied method should have a pointer to interpreter ASSERT_TRUE(class_linker_->IsQuickToInterpreterBridge(code)); } // Regression test for dex2oat crash for soft verification failure during // class initialization check from the transactional interpreter while // running the class initializer for another class. TEST_F(ImageTest, TestSoftVerificationFailureDuringClassInitialization) { CompilationHelper helper; Compile(ImageHeader::kStorageModeUncompressed, /*max_image_block_size=*/std::numeric_limits::max(), helper, "VerifySoftFailDuringClinit", /*image_classes=*/ {"LClassToInitialize;"}, /*image_classes_failing_aot_clinit=*/ {"LClassToInitialize;"}); } TEST_F(ImageTest, TestImageClassWithArrayClassWithUnresolvedComponent) { CompilationHelper helper; Compile(ImageHeader::kStorageModeUncompressed, /*max_image_block_size=*/std::numeric_limits::max(), helper, "ArrayClassWithUnresolvedComponent", /*image_classes=*/ {"LClassWithStatic;", "LClassWithStaticConst;", "[LClassWithMissingInterface;", "[[LClassWithMissingInterface;", "[LClassWithMissingSuper", "[[LClassWithMissingSuper"}, /*image_classes_failing_aot_clinit=*/ { "LClassWithStatic;", "LClassWithStaticConst;"}, /*image_classes_failing_resolution=*/ { "[LClassWithMissingInterface;", "[[LClassWithMissingInterface;", "[LClassWithMissingSuper", "[[LClassWithMissingSuper"}); } TEST_F(ImageTest, TestSuperWithAccessChecks) { CompilationHelper helper; Compile(ImageHeader::kStorageModeUncompressed, /*max_image_block_size=*/std::numeric_limits::max(), helper, "SuperWithAccessChecks", /*image_classes=*/ {"LSubClass;", "LImplementsClass;"}, /*image_classes_failing_aot_clinit=*/ {"LSubClass;", "LImplementsClass;"}); } // Regression test for b/297453985, where we used to generate a bogus image // checksum. TEST_F(ImageTest, ImageChecksum) { uint32_t image_begin = ART_BASE_ADDRESS; uint32_t image_roots = ART_BASE_ADDRESS + (1 * KB); ImageSection sections[ImageHeader::kSectionCount]; // We require bitmap section to be at least kElfSegmentAlignment. sections[ImageHeader::kSectionImageBitmap] = ImageSection(0, kElfSegmentAlignment); ImageHeader image_header(/*image_reservation_size=*/ kElfSegmentAlignment, /*component_count=*/ 1u, image_begin, /*image_size=*/ sizeof(ImageHeader), sections, image_roots, /*oat_checksum=*/ 0u, /*oat_file_begin=*/ 0u, /*oat_data_begin=*/ 0u, /*oat_data_end=*/ 0u, /*oat_file_end=*/ 0u, /*boot_image_begin=*/ 0u, /*boot_image_size=*/ 0u, /*boot_image_component_count=*/ 0u, /*boot_image_checksum=*/ 0u, kRuntimePointerSize); ASSERT_TRUE(image_header.IsValid()); std::string error_msg; ImageFileGuard image_file; ScratchFile location; image_file.reset(OS::CreateEmptyFile(location.GetFilename().c_str())); const uint8_t* data = reinterpret_cast(&image_header); std::unique_ptr bitmap(new uint8_t[kElfSegmentAlignment]); memset(bitmap.get(), 0, kElfSegmentAlignment); ASSERT_EQ(image_header.GetImageChecksum(), 0u); ASSERT_TRUE(image_header.WriteData( image_file, data, bitmap.get(), ImageHeader::kStorageModeUncompressed, /*max_image_block_size=*/std::numeric_limits::max(), /*update_checksum=*/ true, &error_msg)) << error_msg; uint32_t first_checksum = image_header.GetImageChecksum(); // Reset the image checksum, `WriteData` updated it. image_header.SetImageChecksum(0u); // Change the header to ensure the checksum will be different. image_header.SetOatChecksum(0xFFFF); ASSERT_TRUE(image_header.WriteData( image_file, data, bitmap.get(), ImageHeader::kStorageModeUncompressed, /*max_image_block_size=*/std::numeric_limits::max(), /*update_checksum=*/ true, &error_msg)) << error_msg; ASSERT_NE(first_checksum, image_header.GetImageChecksum()); } } // namespace linker } // namespace art