diff options
| -rw-r--r-- | build/Android.gtest.mk | 19 | ||||
| -rw-r--r-- | compiler/optimizing/inliner.cc | 1 | ||||
| -rw-r--r-- | compiler/optimizing/load_store_elimination.cc | 9 | ||||
| -rw-r--r-- | dex2oat/dex2oat_test.cc | 16 | ||||
| -rw-r--r-- | runtime/dex/dex_file_loader.cc | 6 | ||||
| -rw-r--r-- | runtime/gc/space/image_space.cc | 37 | ||||
| -rw-r--r-- | runtime/runtime.cc | 7 | ||||
| -rw-r--r-- | runtime/well_known_classes.cc | 9 | ||||
| -rw-r--r-- | test/530-checker-lse/expected.txt | 1 | ||||
| -rw-r--r-- | test/530-checker-lse/src/Main.java | 23 |
10 files changed, 122 insertions, 6 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index c8f661561f..894b33b00a 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -78,6 +78,11 @@ ART_TEST_TARGET_GTEST_MainStripped_DEX := $(basename $(ART_TEST_TARGET_GTEST_Mai ART_TEST_HOST_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) ART_TEST_TARGET_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX)) +# Create rules for UncompressedEmpty, a classes.dex that is empty and uncompressed +# for the dex2oat tests. +ART_TEST_HOST_GTEST_EmptyUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))EmptyUncompressed$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) +ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))EmptyUncompressed$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX)) + $(ART_TEST_HOST_GTEST_MainStripped_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) cp $< $@ $(call dexpreopt-remove-classes.dex,$@) @@ -96,6 +101,16 @@ $(ART_TEST_TARGET_GTEST_MainUncompressed_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) $(call uncompress-dexs, $@) $(call align-package, $@) +$(ART_TEST_HOST_GTEST_EmptyUncompressed_DEX): $(ZIPALIGN) + touch $(dir $@)classes.dex + zip -j -qD -X -0 $@ $(dir $@)classes.dex + rm $(dir $@)classes.dex + +$(ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX): $(ZIPALIGN) + touch $(dir $@)classes.dex + zip -j -qD -X -0 $@ $(dir $@)classes.dex + rm $(dir $@)classes.dex + ART_TEST_GTEST_VerifierDeps_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDeps/*.smali)) ART_TEST_GTEST_VerifierDepsMulti_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDepsMulti/*.smali)) ART_TEST_HOST_GTEST_VerifierDeps_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifierDeps,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) @@ -126,7 +141,7 @@ ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods Prof ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex ART_GTEST_dexlayout_test_DEX_DEPS := ManyMethods -ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed +ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed EmptyUncompressed ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi @@ -750,6 +765,8 @@ ART_TEST_HOST_GTEST_MainStripped_DEX := ART_TEST_TARGET_GTEST_MainStripped_DEX := ART_TEST_HOST_GTEST_MainUncompressed_DEX := ART_TEST_TARGET_GTEST_MainUncompressed_DEX := +ART_TEST_HOST_GTEST_EmptyUncompressed_DEX := +ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX := ART_TEST_GTEST_VerifierDeps_SRC := ART_TEST_HOST_GTEST_VerifierDeps_DEX := ART_TEST_TARGET_GTEST_VerifierDeps_DEX := diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index b9ae0ffb12..4fc7262265 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1471,6 +1471,7 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedNotVerified) << "Method " << method->PrettyMethod() << " has soft failures un-handled by the compiler, so it cannot be inlined"; + return false; } if (IsMethodUnverified(compiler_driver_, method)) { diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 8b4eae1780..237ecd3c10 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -882,6 +882,7 @@ class LSEVisitor : public HGraphDelegateVisitor { } if (ref_info->IsSingletonAndRemovable() && !new_instance->NeedsChecks()) { DCHECK(!new_instance->IsFinalizable()); + // new_instance can potentially be eliminated. singleton_new_instances_.push_back(new_instance); } ScopedArenaVector<HInstruction*>& heap_values = @@ -904,7 +905,13 @@ class LSEVisitor : public HGraphDelegateVisitor { return; } if (ref_info->IsSingletonAndRemovable()) { - singleton_new_instances_.push_back(new_array); + if (new_array->GetLength()->IsIntConstant() && + new_array->GetLength()->AsIntConstant()->GetValue() >= 0) { + // new_array can potentially be eliminated. + singleton_new_instances_.push_back(new_array); + } else { + // new_array may throw NegativeArraySizeException. Keep it. + } } ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[new_array->GetBlock()->GetBlockId()]; diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 5614ac6458..17a27f8567 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1543,4 +1543,20 @@ TEST_F(Dex2oatTest, UncompressedTest) { }); } +TEST_F(Dex2oatTest, EmptyUncompressedDexTest) { + std::string out_dir = GetScratchDir(); + const std::string base_oat_name = out_dir + "/base.oat"; + std::string error_msg; + int status = GenerateOdexForTestWithStatus( + { GetTestDexFileName("MainEmptyUncompressed") }, + base_oat_name, + CompilerFilter::Filter::kQuicken, + &error_msg, + { }, + /*use_fd*/ false); + // Expect to fail with code 1 and not SIGSEGV or SIGABRT. + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(WEXITSTATUS(status), 1) << error_msg; +} + } // namespace art diff --git a/runtime/dex/dex_file_loader.cc b/runtime/dex/dex_file_loader.cc index 0f2758e372..2c75c5b5d9 100644 --- a/runtime/dex/dex_file_loader.cc +++ b/runtime/dex/dex_file_loader.cc @@ -319,7 +319,7 @@ std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base, *verify_result = VerifyResult::kVerifyNotAttempted; } std::unique_ptr<DexFile> dex_file; - if (StandardDexFile::IsMagicValid(base)) { + if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) { if (data_size != 0) { CHECK_EQ(base, data_base) << "Unsupported for standard dex"; } @@ -329,7 +329,7 @@ std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base, location_checksum, oat_dex_file, container)); - } else if (CompactDexFile::IsMagicValid(base)) { + } else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) { if (data_base == nullptr) { // TODO: Is there a clean way to support both an explicit data section and reading the one // from the header. @@ -346,6 +346,8 @@ std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base, location_checksum, oat_dex_file, container)); + } else { + *error_msg = "Invalid or truncated dex file"; } if (dex_file == nullptr) { *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(), diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index ca5a3eeb17..2b06e838ce 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -277,6 +277,36 @@ static bool RelocateImage(const char* image_location, return Exec(argv, error_msg); } +static bool VerifyImage(const char* image_location, + const char* dest_filename, + InstructionSet isa, + std::string* error_msg) { + std::string patchoat(Runtime::Current()->GetPatchoatExecutable()); + + std::string input_image_location_arg("--input-image-location="); + input_image_location_arg += image_location; + + std::string output_image_filename_arg("--output-image-file="); + output_image_filename_arg += dest_filename; + + std::string instruction_set_arg("--instruction-set="); + instruction_set_arg += GetInstructionSetString(isa); + + std::vector<std::string> argv; + argv.push_back(patchoat); + + argv.push_back(input_image_location_arg); + argv.push_back(output_image_filename_arg); + + argv.push_back(instruction_set_arg); + + argv.push_back("--verify"); + + std::string command_line(android::base::Join(argv, ' ')); + LOG(INFO) << "VerifyImage: " << command_line; + return Exec(argv, error_msg); +} + static ImageHeader* ReadSpecificImageHeader(const char* filename, std::string* error_msg) { std::unique_ptr<ImageHeader> hdr(new ImageHeader); if (!ReadSpecificImageHeader(filename, hdr.get())) { @@ -1504,7 +1534,12 @@ std::unique_ptr<ImageSpace> ImageSpace::CreateBootImage(const char* image_locati if (is_zygote && dalvik_cache_exists) { DCHECK(!dalvik_cache.empty()); std::string local_error_msg; - if (!CheckSpace(dalvik_cache, &local_error_msg)) { + // All secondary images are verified when the primary image is verified. + bool verified = secondary_image || VerifyImage(image_location, + cache_filename.c_str(), + image_isa, + &local_error_msg); + if (!(verified && CheckSpace(dalvik_cache, &local_error_msg))) { LOG(WARNING) << local_error_msg << " Preemptively pruning the dalvik cache."; PruneDalvikCache(image_isa); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 3afd320f05..b26b09c156 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1178,7 +1178,12 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // (b) Zygote forked a new process that is not exempt (see ZygoteHooks). // TODO(dbrazdil): Turn the NoHiddenApiChecks negative flag into a positive one // to clean up this logic. - do_hidden_api_checks_ = IsAotCompiler() && !runtime_options.Exists(Opt::NoHiddenApiChecks); + if (kIsTargetBuild && IsAotCompiler() && !runtime_options.Exists(Opt::NoHiddenApiChecks)) { + // dex2oat on target without -Xno-hidden-api-checks. + do_hidden_api_checks_ = !IsCompilingBootImage(); + } else { + do_hidden_api_checks_ = false; + } DCHECK(!is_zygote_ || !do_hidden_api_checks_) << "Zygote should not be started with hidden API checks"; diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 5fe10f5c12..f4f5420190 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -278,6 +278,12 @@ uint32_t WellKnownClasses::StringInitToEntryPoint(ArtMethod* string_init) { #undef STRING_INIT_LIST void WellKnownClasses::Init(JNIEnv* env) { + // The following initializers use JNI to get handles on hidden methods/fields. + // Temporarily disable hidden API checks as they would see these calls coming + // from an unattached thread and assume the caller is not in boot class path. + const bool hidden_api_checks_enabled = Runtime::Current()->AreHiddenApiChecksEnabled(); + Runtime::Current()->SetHiddenApiChecksEnabled(false); + dalvik_annotation_optimization_CriticalNative = CacheClass(env, "dalvik/annotation/optimization/CriticalNative"); dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative"); @@ -401,6 +407,9 @@ void WellKnownClasses::Init(JNIEnv* env) { InitStringInit(env); Thread::Current()->InitStringEntryPoints(); + + // Reenable hidden API checks if necessary. + Runtime::Current()->SetHiddenApiChecksEnabled(hidden_api_checks_enabled); } void WellKnownClasses::LateInit(JNIEnv* env) { diff --git a/test/530-checker-lse/expected.txt b/test/530-checker-lse/expected.txt index ddae16aff4..fb67e22d0b 100644 --- a/test/530-checker-lse/expected.txt +++ b/test/530-checker-lse/expected.txt @@ -1 +1,2 @@ java.lang.ArrayIndexOutOfBoundsException: length=3; index=3 +Got NegativeArraySizeException. diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index 98838c5089..ebde3bf845 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -1052,6 +1052,23 @@ public class Main { return array[1] + array[i]; } + /// CHECK-START: int Main.testAllocationEliminationOfArray5(int) load_store_elimination (before) + /// CHECK: NewArray + /// CHECK: ArraySet + /// CHECK: ArrayGet + + /// CHECK-START: int Main.testAllocationEliminationOfArray5(int) load_store_elimination (after) + /// CHECK: NewArray + /// CHECK-NOT: ArraySet + /// CHECK-NOT: ArrayGet + private static int testAllocationEliminationOfArray5(int i) { + // Cannot eliminate array allocation due to unknown i that may + // cause NegativeArraySizeException. + int[] array = new int[i]; + array[1] = 12; + return array[1]; + } + /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (before) /// CHECK: NewInstance /// CHECK: InstanceFieldSet @@ -1205,6 +1222,12 @@ public class Main { assertIntEquals(testAllocationEliminationOfArray2(), 11); assertIntEquals(testAllocationEliminationOfArray3(2), 4); assertIntEquals(testAllocationEliminationOfArray4(2), 6); + assertIntEquals(testAllocationEliminationOfArray5(2), 12); + try { + testAllocationEliminationOfArray5(-2); + } catch (NegativeArraySizeException e) { + System.out.println("Got NegativeArraySizeException."); + } assertIntEquals(testStoreStore().i, 41); assertIntEquals(testStoreStore().j, 43); |