diff options
78 files changed, 1201 insertions, 1543 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 5bdfbc74eb..cea38e4ff9 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -120,14 +120,14 @@ ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_optimizing_no-pic_6 ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \ $(HOST_CORE_IMAGE_optimizing_pic_64) \ $(HOST_CORE_IMAGE_optimizing_pic_32) \ - $(HOST_CORE_IMAGE_optimizing_no-pic_64) \ - $(HOST_CORE_IMAGE_optimizing_no-pic_32) \ + $(HOST_CORE_IMAGE_interpreter_pic_64) \ + $(HOST_CORE_IMAGE_interpreter_pic_32) \ $(HOST_OUT_EXECUTABLES)/patchoatd ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_optimizing_pic_64) \ $(TARGET_CORE_IMAGE_optimizing_pic_32) \ - $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \ - $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \ + $(TARGET_CORE_IMAGE_interpreter_pic_64) \ + $(TARGET_CORE_IMAGE_interpreter_pic_32) \ $(TARGET_OUT_EXECUTABLES)/patchoatd ART_GTEST_oat_file_assistant_test_HOST_DEPS := \ diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 5da59f3b3d..faf8b41be1 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1060,13 +1060,13 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (auto& m : c->GetMethods(pointer_size)) { - ResolveExceptionsForMethod(&m, pointer_size); + ResolveExceptionsForMethod(&m); } return true; } private: - void ResolveExceptionsForMethod(ArtMethod* method_handle, PointerSize pointer_size) + void ResolveExceptionsForMethod(ArtMethod* method_handle) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile::CodeItem* code_item = method_handle->GetCodeItem(); if (code_item == nullptr) { @@ -1088,8 +1088,7 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { dex::TypeIndex encoded_catch_handler_handlers_type_idx = dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list)); // Add to set of types to resolve if not already in the dex cache resolved types - if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx, - pointer_size)) { + if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) { exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx, method_handle->GetDexFile()); } diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 86d92ff0b5..c69ed3198b 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -487,7 +487,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(20U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(159 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 05a76e1105..ecabc58c4d 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -431,10 +431,12 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to // kSaveEverything and use a temporary for the .bss entry address in the fast path, // so that we can avoid another calculation here. + UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()); + vixl32::Register temp = temps.Acquire(); CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); - arm_codegen->EmitMovwMovtPlaceholder(labels, ip); - __ Str(OutputRegister(cls_), MemOperand(ip)); + arm_codegen->EmitMovwMovtPlaceholder(labels, temp); + __ Str(OutputRegister(cls_), MemOperand(temp)); } __ B(GetExitLabel()); } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 50aa4425d9..78a4251e3a 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1538,8 +1538,6 @@ bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* } } - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - // Iterate over the list of parameter types and test whether any of the // actual inputs has a more specific reference type than the type declared in // the signature. @@ -1551,9 +1549,9 @@ bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* ++param_idx, ++input_idx) { HInstruction* input = invoke_instruction->InputAt(input_idx); if (input->GetType() == Primitive::kPrimNot) { - mirror::Class* param_cls = resolved_method->GetDexCacheResolvedType( + mirror::Class* param_cls = resolved_method->GetClassFromTypeIndex( param_list->GetTypeItem(param_idx).type_idx_, - pointer_size); + /* resolve */ false); if (IsReferenceTypeRefinement(GetClassRTI(param_cls), /* declared_can_be_null */ true, input)) { @@ -1602,8 +1600,7 @@ void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method, // TODO: we could be more precise by merging the phi inputs but that requires // some functionality from the reference type propagation. DCHECK(return_replacement->IsPhi()); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size); + mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */); return_replacement->SetReferenceTypeInfo(GetClassRTI(cls)); } } diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index a4d59ab587..8854a2b08b 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -840,10 +840,8 @@ void ReferenceTypePropagation::RTPVisitor::VisitInvoke(HInvoke* instr) { } ScopedObjectAccess soa(Thread::Current()); - ClassLinker* cl = Runtime::Current()->GetClassLinker(); - PointerSize pointer_size = cl->GetImagePointerSize(); ArtMethod* method = instr->GetResolvedMethod(); - mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size); + mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(/* resolve */ false); SetClassAsTypeInfo(instr, klass, /* is_exact */ false); } diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 5e552106ab..a3fce02970 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -5610,7 +5610,7 @@ const char* const VixlJniHelpersResults[] = { " 214: ecbd 8a10 vpop {s16-s31}\n", " 218: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n", " 21c: 4660 mov r0, ip\n", - " 21e: f8d9 c2c0 ldr.w ip, [r9, #704] ; 0x2c0\n", + " 21e: f8d9 c2ac ldr.w ip, [r9, #684] ; 0x2ac\n", " 222: 47e0 blx ip\n", nullptr }; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 21b03eb8ba..3fbdb89a74 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1095,6 +1095,8 @@ class Dex2Oat FINAL { compiler_options_->GetNativeDebuggable() ? OatHeader::kTrueValue : OatHeader::kFalseValue); key_value_store_->Put(OatHeader::kCompilerFilter, CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter())); + key_value_store_->Put(OatHeader::kConcurrentCopying, + kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue); } // Parse the arguments from the command line. In case of an unrecognized option or impossible diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 7ae13a574b..a4b414d2ff 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -54,48 +54,6 @@ namespace art { -static bool LocationToFilename(const std::string& location, InstructionSet isa, - std::string* filename) { - bool has_system = false; - bool has_cache = false; - // image_location = /system/framework/boot.art - // system_image_filename = /system/framework/<image_isa>/boot.art - std::string system_filename(GetSystemImageFilename(location.c_str(), isa)); - if (OS::FileExists(system_filename.c_str())) { - has_system = true; - } - - bool have_android_data = false; - bool dalvik_cache_exists = false; - bool is_global_cache = false; - std::string dalvik_cache; - GetDalvikCache(GetInstructionSetString(isa), false, &dalvik_cache, - &have_android_data, &dalvik_cache_exists, &is_global_cache); - - std::string cache_filename; - if (have_android_data && dalvik_cache_exists) { - // Always set output location even if it does not exist, - // so that the caller knows where to create the image. - // - // image_location = /system/framework/boot.art - // *image_filename = /data/dalvik-cache/<image_isa>/boot.art - std::string error_msg; - if (GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(), - &cache_filename, &error_msg)) { - has_cache = true; - } - } - if (has_system) { - *filename = system_filename; - return true; - } else if (has_cache) { - *filename = cache_filename; - return true; - } else { - return false; - } -} - static const OatHeader* GetOatHeader(const ElfFile* elf_file) { uint64_t off = 0; if (!elf_file->GetSectionOffsetAndSize(".rodata", &off, nullptr)) { @@ -106,28 +64,10 @@ static const OatHeader* GetOatHeader(const ElfFile* elf_file) { return oat_header; } -// This function takes an elf file and reads the current patch delta value -// encoded in its oat header value -static bool ReadOatPatchDelta(const ElfFile* elf_file, off_t* delta, std::string* error_msg) { - const OatHeader* oat_header = GetOatHeader(elf_file); - if (oat_header == nullptr) { - *error_msg = "Unable to get oat header from elf file."; - return false; - } - if (!oat_header->IsValid()) { - *error_msg = "Elf file has an invalid oat header"; - return false; - } - *delta = oat_header->GetImagePatchDelta(); - return true; -} - -static File* CreateOrOpen(const char* name, bool* created) { +static File* CreateOrOpen(const char* name) { if (OS::FileExists(name)) { - *created = false; return OS::OpenFileReadWrite(name); } else { - *created = true; std::unique_ptr<File> f(OS::CreateEmptyFile(name)); if (f.get() != nullptr) { if (fchmod(f->Fd(), 0644) != 0) { @@ -206,12 +146,11 @@ bool PatchOat::Patch(const std::string& image_location, Thread::Current()->TransitionFromRunnableToSuspended(kNative); ScopedObjectAccess soa(Thread::Current()); - t.NewTiming("Image and oat Patching setup"); + t.NewTiming("Image Patching setup"); std::vector<gc::space::ImageSpace*> spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); std::map<gc::space::ImageSpace*, std::unique_ptr<File>> space_to_file_map; std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>> space_to_memmap_map; std::map<gc::space::ImageSpace*, PatchOat> space_to_patchoat_map; - std::map<gc::space::ImageSpace*, bool> space_to_skip_patching_map; for (size_t i = 0; i < spaces.size(); ++i) { gc::space::ImageSpace* space = spaces[i]; @@ -255,8 +194,7 @@ bool PatchOat::Patch(const std::string& image_location, space_to_memmap_map.emplace(space, std::move(image)); } - // Do a first pass over the image spaces. Symlink PIC oat and vdex files, and - // prepare PatchOat instances for the rest. + // Symlink PIC oat and vdex files and patch the image spaces in memory. for (size_t i = 0; i < spaces.size(); ++i) { gc::space::ImageSpace* space = spaces[i]; std::string input_image_filename = space->GetImageFilename(); @@ -277,14 +215,17 @@ bool PatchOat::Patch(const std::string& image_location, return false; } - bool skip_patching_oat = false; MaybePic is_oat_pic = IsOatPic(elf.get()); if (is_oat_pic >= ERROR_FIRST) { // Error logged by IsOatPic return false; - } else if (is_oat_pic == PIC) { - // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching. + } else if (is_oat_pic == NOT_PIC) { + LOG(ERROR) << "patchoat cannot be used on non-PIC oat file: " << input_oat_file->GetPath(); + return false; + } else { + CHECK(is_oat_pic == PIC); + // Create a symlink. std::string converted_image_filename = space->GetImageLocation(); std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@'); std::string output_image_filename = output_directory + @@ -296,23 +237,16 @@ bool PatchOat::Patch(const std::string& image_location, ImageHeader::GetOatLocationFromImageLocation(output_image_filename); if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(), - output_oat_filename, - false, - true) || + output_oat_filename) || !SymlinkFile(input_vdex_filename, output_vdex_filename)) { // Errors already logged by above call. return false; } - // Don't patch the OAT, since we just symlinked it. Image still needs patching. - skip_patching_oat = true; - } else { - CHECK(is_oat_pic == NOT_PIC); } PatchOat& p = space_to_patchoat_map.emplace(space, PatchOat( isa, - elf.release(), space_to_memmap_map.find(space)->second.get(), space->GetLiveBitmap(), space->GetMemMap(), @@ -320,36 +254,24 @@ bool PatchOat::Patch(const std::string& image_location, &space_to_memmap_map, timings)).first->second; - t.NewTiming("Patching files"); - if (!skip_patching_oat && !p.PatchElf()) { - LOG(ERROR) << "Failed to patch oat file " << input_oat_file->GetPath(); - return false; - } + t.NewTiming("Patching image"); if (!p.PatchImage(i == 0)) { LOG(ERROR) << "Failed to patch image file " << input_image_filename; return false; } - - space_to_skip_patching_map.emplace(space, skip_patching_oat); } - // Do a second pass over the image spaces. Patch image files, non-PIC oat files - // and symlink their corresponding vdex files. + // Write the patched image spaces. for (size_t i = 0; i < spaces.size(); ++i) { gc::space::ImageSpace* space = spaces[i]; - std::string input_image_filename = space->GetImageFilename(); - std::string input_vdex_filename = - ImageHeader::GetVdexLocationFromImageLocation(input_image_filename); - t.NewTiming("Writing files"); + t.NewTiming("Writing image"); std::string converted_image_filename = space->GetImageLocation(); std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@'); std::string output_image_filename = output_directory + (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") + converted_image_filename; - bool new_oat_out; - std::unique_ptr<File> - output_image_file(CreateOrOpen(output_image_filename.c_str(), &new_oat_out)); + std::unique_ptr<File> output_image_file(CreateOrOpen(output_image_filename.c_str())); if (output_image_file.get() == nullptr) { LOG(ERROR) << "Failed to open output image file at " << output_image_filename; return false; @@ -362,48 +284,10 @@ bool PatchOat::Patch(const std::string& image_location, if (!success) { return false; } - - bool skip_patching_oat = space_to_skip_patching_map.find(space)->second; - if (!skip_patching_oat) { - std::string output_vdex_filename = - ImageHeader::GetVdexLocationFromImageLocation(output_image_filename); - std::string output_oat_filename = - ImageHeader::GetOatLocationFromImageLocation(output_image_filename); - - std::unique_ptr<File> - output_oat_file(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out)); - if (output_oat_file.get() == nullptr) { - LOG(ERROR) << "Failed to open output oat file at " << output_oat_filename; - return false; - } - success = p.WriteElf(output_oat_file.get()); - success = FinishFile(output_oat_file.get(), success); - if (success) { - success = SymlinkFile(input_vdex_filename, output_vdex_filename); - } - if (!success) { - return false; - } - } } return true; } -bool PatchOat::WriteElf(File* out) { - TimingLogger::ScopedTiming t("Writing Elf File", timings_); - - CHECK(oat_file_.get() != nullptr); - CHECK(out != nullptr); - size_t expect = oat_file_->Size(); - if (out->WriteFully(reinterpret_cast<char*>(oat_file_->Begin()), expect) && - out->SetLength(expect) == 0) { - return true; - } else { - LOG(ERROR) << "Writing to oat file " << out->GetPath() << " failed."; - return false; - } -} - bool PatchOat::WriteImage(File* out) { TimingLogger::ScopedTiming t("Writing image File", timings_); std::string error_msg; @@ -466,22 +350,7 @@ PatchOat::MaybePic PatchOat::IsOatPic(const ElfFile* oat_in) { } bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename, - const std::string& output_oat_filename, - bool output_oat_opened_from_fd, - bool new_oat_out) { - // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD. - if (output_oat_opened_from_fd) { - // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC? - LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC"; - return false; - } - - // Image was PIC. Create symlink where the oat is supposed to go. - if (!new_oat_out) { - LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite"; - return false; - } - + const std::string& output_oat_filename) { // Delete the original file, since we won't need it. unlink(output_oat_filename.c_str()); @@ -799,133 +668,6 @@ void PatchOat::FixupMethod(ArtMethod* object, ArtMethod* copy) { object->GetDataPtrSize(pointer_size)), pointer_size); } -bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings, - bool output_oat_opened_from_fd, bool new_oat_out) { - CHECK(input_oat != nullptr); - CHECK(output_oat != nullptr); - CHECK_GE(input_oat->Fd(), 0); - CHECK_GE(output_oat->Fd(), 0); - TimingLogger::ScopedTiming t("Setup Oat File Patching", timings); - - std::string error_msg; - std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat, - PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg)); - if (elf.get() == nullptr) { - LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg; - return false; - } - - MaybePic is_oat_pic = IsOatPic(elf.get()); - if (is_oat_pic >= ERROR_FIRST) { - // Error logged by IsOatPic - return false; - } else if (is_oat_pic == PIC) { - // Do not need to do ELF-file patching. Create a symlink and skip the rest. - // Any errors will be logged by the function call. - return ReplaceOatFileWithSymlink(input_oat->GetPath(), - output_oat->GetPath(), - output_oat_opened_from_fd, - new_oat_out); - } else { - CHECK(is_oat_pic == NOT_PIC); - } - - PatchOat p(elf.release(), delta, timings); - t.NewTiming("Patch Oat file"); - if (!p.PatchElf()) { - return false; - } - - t.NewTiming("Writing oat file"); - if (!p.WriteElf(output_oat)) { - return false; - } - return true; -} - -template <typename ElfFileImpl> -bool PatchOat::PatchOatHeader(ElfFileImpl* oat_file) { - auto rodata_sec = oat_file->FindSectionByName(".rodata"); - if (rodata_sec == nullptr) { - return false; - } - OatHeader* oat_header = reinterpret_cast<OatHeader*>(oat_file->Begin() + rodata_sec->sh_offset); - if (!oat_header->IsValid()) { - LOG(ERROR) << "Elf file " << oat_file->GetFilePath() << " has an invalid oat header"; - return false; - } - oat_header->RelocateOat(delta_); - return true; -} - -bool PatchOat::PatchElf() { - if (oat_file_->Is64Bit()) { - return PatchElf<ElfFileImpl64>(oat_file_->GetImpl64()); - } else { - return PatchElf<ElfFileImpl32>(oat_file_->GetImpl32()); - } -} - -template <typename ElfFileImpl> -bool PatchOat::PatchElf(ElfFileImpl* oat_file) { - TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_); - - // Fix up absolute references to locations within the boot image. - if (!oat_file->ApplyOatPatchesTo(".text", delta_)) { - return false; - } - - // Update the OatHeader fields referencing the boot image. - if (!PatchOatHeader<ElfFileImpl>(oat_file)) { - return false; - } - - bool need_boot_oat_fixup = true; - for (unsigned int i = 0; i < oat_file->GetProgramHeaderNum(); ++i) { - auto hdr = oat_file->GetProgramHeader(i); - if (hdr->p_type == PT_LOAD && hdr->p_vaddr == 0u) { - need_boot_oat_fixup = false; - break; - } - } - if (!need_boot_oat_fixup) { - // This is an app oat file that can be loaded at an arbitrary address in memory. - // Boot image references were patched above and there's nothing else to do. - return true; - } - - // This is a boot oat file that's loaded at a particular address and we need - // to patch all absolute addresses, starting with ELF program headers. - - t.NewTiming("Fixup Elf Headers"); - // Fixup Phdr's - oat_file->FixupProgramHeaders(delta_); - - t.NewTiming("Fixup Section Headers"); - // Fixup Shdr's - oat_file->FixupSectionHeaders(delta_); - - t.NewTiming("Fixup Dynamics"); - oat_file->FixupDynamic(delta_); - - t.NewTiming("Fixup Elf Symbols"); - // Fixup dynsym - if (!oat_file->FixupSymbols(delta_, true)) { - return false; - } - // Fixup symtab - if (!oat_file->FixupSymbols(delta_, false)) { - return false; - } - - t.NewTiming("Fixup Debug Sections"); - if (!oat_file->FixupDebugSections(delta_)) { - return false; - } - - return true; -} - static int orig_argc; static char** orig_argv; @@ -960,32 +702,10 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError("Usage: patchoat [options]..."); UsageError(""); UsageError(" --instruction-set=<isa>: Specifies the instruction set the patched code is"); - UsageError(" compiled for. Required if you use --input-oat-location"); - UsageError(""); - UsageError(" --input-oat-file=<file.oat>: Specifies the exact filename of the oat file to be"); - UsageError(" patched."); - UsageError(""); - UsageError(" --input-oat-fd=<file-descriptor>: Specifies the file-descriptor of the oat file"); - UsageError(" to be patched."); - UsageError(""); - UsageError(" --input-vdex-fd=<file-descriptor>: Specifies the file-descriptor of the vdex file"); - UsageError(" associated with the oat file."); - UsageError(""); - UsageError(" --input-oat-location=<file.oat>: Specifies the 'location' to read the patched"); - UsageError(" oat file from. If used one must also supply the --instruction-set"); + UsageError(" compiled for (required)."); UsageError(""); UsageError(" --input-image-location=<file.art>: Specifies the 'location' of the image file to"); - UsageError(" be patched. If --instruction-set is not given it will use the instruction set"); - UsageError(" extracted from the --input-oat-file."); - UsageError(""); - UsageError(" --output-oat-file=<file.oat>: Specifies the exact file to write the patched oat"); - UsageError(" file to."); - UsageError(""); - UsageError(" --output-oat-fd=<file-descriptor>: Specifies the file-descriptor to write the"); - UsageError(" patched oat file to."); - UsageError(""); - UsageError(" --output-vdex-fd=<file-descriptor>: Specifies the file-descriptor to copy the"); - UsageError(" the vdex file associated with the patch oat file to."); + UsageError(" be patched."); UsageError(""); UsageError(" --output-image-file=<file.art>: Specifies the exact file to write the patched"); UsageError(" image file to."); @@ -993,15 +713,6 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --base-offset-delta=<delta>: Specify the amount to change the old base-offset by."); UsageError(" This value may be negative."); UsageError(""); - UsageError(" --patched-image-location=<file.art>: Relocate the oat file to be the same as the"); - UsageError(" image at the given location. If used one must also specify the"); - UsageError(" --instruction-set flag. It will search for this image in the same way that"); - UsageError(" is done when loading one."); - UsageError(""); - UsageError(" --lock-output: Obtain a flock on output oat file before starting."); - UsageError(""); - UsageError(" --no-lock-output: Do not attempt to obtain a flock on output oat file."); - UsageError(""); UsageError(" --dump-timings: dump out patch timing information"); UsageError(""); UsageError(" --no-dump-timings: do not dump out patch timing information"); @@ -1010,34 +721,6 @@ NO_RETURN static void Usage(const char *fmt, ...) { exit(EXIT_FAILURE); } -static bool ReadBaseDelta(const char* name, off_t* delta, std::string* error_msg) { - CHECK(name != nullptr); - CHECK(delta != nullptr); - std::unique_ptr<File> file; - if (OS::FileExists(name)) { - file.reset(OS::OpenFileForReading(name)); - if (file.get() == nullptr) { - *error_msg = "Failed to open file %s for reading"; - return false; - } - } else { - *error_msg = "File %s does not exist"; - return false; - } - CHECK(file.get() != nullptr); - ImageHeader hdr; - if (sizeof(hdr) != file->Read(reinterpret_cast<char*>(&hdr), sizeof(hdr), 0)) { - *error_msg = "Failed to read file %s"; - return false; - } - if (!hdr.IsValid()) { - *error_msg = "%s does not contain a valid image header."; - return false; - } - *delta = hdr.GetPatchDelta(); - return true; -} - static int patchoat_image(TimingLogger& timings, InstructionSet isa, const std::string& input_image_location, @@ -1076,293 +759,6 @@ static int patchoat_image(TimingLogger& timings, return ret ? EXIT_SUCCESS : EXIT_FAILURE; } -static int patchoat_oat(TimingLogger& timings, - InstructionSet isa, - const std::string& patched_image_location, - off_t base_delta, - bool base_delta_set, - int input_oat_fd, - int input_vdex_fd, - const std::string& input_oat_location, - std::string input_oat_filename, - bool have_input_oat, - int output_oat_fd, - int output_vdex_fd, - std::string output_oat_filename, - bool have_output_oat, - bool lock_output, - bool debug) { - { - // Only 1 of these may be set. - uint32_t cnt = 0; - cnt += (base_delta_set) ? 1 : 0; - cnt += (!patched_image_location.empty()) ? 1 : 0; - if (cnt > 1) { - Usage("Only one of --base-offset-delta or --patched-image-location may be used."); - } else if (cnt == 0) { - Usage("Must specify --base-offset-delta or --patched-image-location."); - } - } - - if (!have_input_oat || !have_output_oat) { - Usage("Both input and output oat must be supplied to patch an app odex."); - } - - if (!input_oat_location.empty()) { - if (!LocationToFilename(input_oat_location, isa, &input_oat_filename)) { - Usage("Unable to find filename for input oat location %s", input_oat_location.c_str()); - } - if (debug) { - LOG(INFO) << "Using input-oat-file " << input_oat_filename; - } - } - - if ((input_oat_fd == -1) != (input_vdex_fd == -1)) { - Usage("Either both input oat and vdex have to be passed as file descriptors or none of them"); - } else if ((output_oat_fd == -1) != (output_vdex_fd == -1)) { - Usage("Either both output oat and vdex have to be passed as file descriptors or none of them"); - } - - bool match_delta = false; - if (!patched_image_location.empty()) { - std::string system_filename; - bool has_system = false; - std::string cache_filename; - bool has_cache = false; - bool has_android_data_unused = false; - bool is_global_cache = false; - if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa, - &system_filename, &has_system, &cache_filename, - &has_android_data_unused, &has_cache, - &is_global_cache)) { - Usage("Unable to determine image file for location %s", patched_image_location.c_str()); - } - std::string patched_image_filename; - if (has_cache) { - patched_image_filename = cache_filename; - } else if (has_system) { - LOG(WARNING) << "Only image file found was in /system for image location " - << patched_image_location; - patched_image_filename = system_filename; - } else { - Usage("Unable to determine image file for location %s", patched_image_location.c_str()); - } - if (debug) { - LOG(INFO) << "Using patched-image-file " << patched_image_filename; - } - - base_delta_set = true; - match_delta = true; - std::string error_msg; - if (!ReadBaseDelta(patched_image_filename.c_str(), &base_delta, &error_msg)) { - Usage(error_msg.c_str(), patched_image_filename.c_str()); - } - } - - if (!IsAligned<kPageSize>(base_delta)) { - Usage("Base offset/delta must be alligned to a pagesize (0x%08x) boundary.", kPageSize); - } - - // We can symlink VDEX only if we have both input and output specified as filenames. - // Store that piece of information before we possibly create bogus filenames for - // files passed as file descriptors. - bool symlink_vdex = !input_oat_filename.empty() && !output_oat_filename.empty(); - - // Infer names of VDEX files. - std::string input_vdex_filename; - std::string output_vdex_filename; - if (!input_oat_filename.empty()) { - input_vdex_filename = ReplaceFileExtension(input_oat_filename, "vdex"); - } - if (!output_oat_filename.empty()) { - output_vdex_filename = ReplaceFileExtension(output_oat_filename, "vdex"); - } - - // Do we need to cleanup output files if we fail? - bool new_oat_out = false; - bool new_vdex_out = false; - - std::unique_ptr<File> input_oat; - std::unique_ptr<File> output_oat; - - if (input_oat_fd != -1) { - if (input_oat_filename.empty()) { - input_oat_filename = "input-oat-file"; - } - input_oat.reset(new File(input_oat_fd, input_oat_filename, false)); - if (input_oat_fd == output_oat_fd) { - input_oat.get()->DisableAutoClose(); - } - if (input_oat == nullptr) { - // Unlikely, but ensure exhaustive logging in non-0 exit code case - LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd; - return EXIT_FAILURE; - } - } else { - CHECK(!input_oat_filename.empty()); - input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str())); - if (input_oat == nullptr) { - int err = errno; - LOG(ERROR) << "Failed to open input oat file " << input_oat_filename - << ": " << strerror(err) << "(" << err << ")"; - return EXIT_FAILURE; - } - } - - std::string error_msg; - std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat.get(), PROT_READ, MAP_PRIVATE, &error_msg)); - if (elf.get() == nullptr) { - LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg; - return EXIT_FAILURE; - } - if (!elf->HasSection(".text.oat_patches")) { - LOG(ERROR) << "missing oat patch section in input oat file " << input_oat->GetPath(); - return EXIT_FAILURE; - } - - if (output_oat_fd != -1) { - if (output_oat_filename.empty()) { - output_oat_filename = "output-oat-file"; - } - output_oat.reset(new File(output_oat_fd, output_oat_filename, true)); - if (output_oat == nullptr) { - // Unlikely, but ensure exhaustive logging in non-0 exit code case - LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd; - } - } else { - CHECK(!output_oat_filename.empty()); - output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out)); - if (output_oat == nullptr) { - int err = errno; - LOG(ERROR) << "Failed to open output oat file " << output_oat_filename - << ": " << strerror(err) << "(" << err << ")"; - } - } - - // Open VDEX files if we are not symlinking them. - std::unique_ptr<File> input_vdex; - std::unique_ptr<File> output_vdex; - if (symlink_vdex) { - new_vdex_out = !OS::FileExists(output_vdex_filename.c_str()); - } else { - if (input_vdex_fd != -1) { - input_vdex.reset(new File(input_vdex_fd, input_vdex_filename, true)); - if (input_vdex == nullptr) { - // Unlikely, but ensure exhaustive logging in non-0 exit code case - LOG(ERROR) << "Failed to open input vdex file by its FD" << input_vdex_fd; - } - } else { - input_vdex.reset(OS::OpenFileForReading(input_vdex_filename.c_str())); - if (input_vdex == nullptr) { - PLOG(ERROR) << "Failed to open input vdex file " << input_vdex_filename; - return EXIT_FAILURE; - } - } - if (output_vdex_fd != -1) { - output_vdex.reset(new File(output_vdex_fd, output_vdex_filename, true)); - if (output_vdex == nullptr) { - // Unlikely, but ensure exhaustive logging in non-0 exit code case - LOG(ERROR) << "Failed to open output vdex file by its FD" << output_vdex_fd; - } - } else { - output_vdex.reset(CreateOrOpen(output_vdex_filename.c_str(), &new_vdex_out)); - if (output_vdex == nullptr) { - PLOG(ERROR) << "Failed to open output vdex file " << output_vdex_filename; - return EXIT_FAILURE; - } - } - } - - // TODO: get rid of this. - auto cleanup = [&output_oat_filename, &output_vdex_filename, &new_oat_out, &new_vdex_out] - (bool success) { - if (!success) { - if (new_oat_out) { - CHECK(!output_oat_filename.empty()); - unlink(output_oat_filename.c_str()); - } - if (new_vdex_out) { - CHECK(!output_vdex_filename.empty()); - unlink(output_vdex_filename.c_str()); - } - } - - if (kIsDebugBuild) { - LOG(INFO) << "Cleaning up.. success? " << success; - } - }; - - if (output_oat.get() == nullptr) { - cleanup(false); - return EXIT_FAILURE; - } - - if (match_delta) { - // Figure out what the current delta is so we can match it to the desired delta. - off_t current_delta = 0; - if (!ReadOatPatchDelta(elf.get(), ¤t_delta, &error_msg)) { - LOG(ERROR) << "Unable to get current delta: " << error_msg; - cleanup(false); - return EXIT_FAILURE; - } - // Before this line base_delta is the desired final delta. We need it to be the actual amount to - // change everything by. We subtract the current delta from it to make it this. - base_delta -= current_delta; - if (!IsAligned<kPageSize>(base_delta)) { - LOG(ERROR) << "Given image file was relocated by an illegal delta"; - cleanup(false); - return false; - } - } - - if (debug) { - LOG(INFO) << "moving offset by " << base_delta - << " (0x" << std::hex << base_delta << ") bytes or " - << std::dec << (base_delta/kPageSize) << " pages."; - } - - ScopedFlock output_oat_lock; - if (lock_output) { - if (!output_oat_lock.Init(output_oat.get(), &error_msg)) { - LOG(ERROR) << "Unable to lock output oat " << output_oat->GetPath() << ": " << error_msg; - cleanup(false); - return EXIT_FAILURE; - } - } - - TimingLogger::ScopedTiming pt("patch oat", &timings); - bool ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings, - output_oat_fd >= 0, // was it opened from FD? - new_oat_out); - ret = FinishFile(output_oat.get(), ret); - - if (ret) { - if (symlink_vdex) { - ret = SymlinkFile(input_vdex_filename, output_vdex_filename); - } else { - ret = unix_file::CopyFile(*input_vdex.get(), output_vdex.get()); - } - } - - if (kIsDebugBuild) { - LOG(INFO) << "Exiting with return ... " << ret; - } - cleanup(ret); - return ret ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int ParseFd(const StringPiece& option, const char* cmdline_arg) { - int fd; - const char* fd_str = option.substr(strlen(cmdline_arg)).data(); - if (!ParseInt(fd_str, &fd)) { - Usage("Failed to parse %d argument '%s' as an integer", cmdline_arg, fd_str); - } - if (fd < 0) { - Usage("%s pass a negative value %d", cmdline_arg, fd); - } - return fd; -} - static int patchoat(int argc, char **argv) { InitLogging(argv, Runtime::Aborter); MemMap::Init(); @@ -1384,23 +780,11 @@ static int patchoat(int argc, char **argv) { // cmd line args bool isa_set = false; InstructionSet isa = kNone; - std::string input_oat_filename; - std::string input_oat_location; - int input_oat_fd = -1; - int input_vdex_fd = -1; - bool have_input_oat = false; std::string input_image_location; - std::string output_oat_filename; - int output_oat_fd = -1; - int output_vdex_fd = -1; - bool have_output_oat = false; std::string output_image_filename; off_t base_delta = 0; bool base_delta_set = false; - std::string patched_image_filename; - std::string patched_image_location; bool dump_timings = kIsDebugBuild; - bool lock_output = true; for (int i = 0; i < argc; ++i) { const StringPiece option(argv[i]); @@ -1415,42 +799,8 @@ static int patchoat(int argc, char **argv) { if (isa == kNone) { Usage("Unknown or invalid instruction set %s", isa_str); } - } else if (option.starts_with("--input-oat-location=")) { - if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); - } - have_input_oat = true; - input_oat_location = option.substr(strlen("--input-oat-location=")).data(); - } else if (option.starts_with("--input-oat-file=")) { - if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); - } - have_input_oat = true; - input_oat_filename = option.substr(strlen("--input-oat-file=")).data(); - } else if (option.starts_with("--input-oat-fd=")) { - if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); - } - have_input_oat = true; - input_oat_fd = ParseFd(option, "--input-oat-fd="); - } else if (option.starts_with("--input-vdex-fd=")) { - input_vdex_fd = ParseFd(option, "--input-vdex-fd="); } else if (option.starts_with("--input-image-location=")) { input_image_location = option.substr(strlen("--input-image-location=")).data(); - } else if (option.starts_with("--output-oat-file=")) { - if (have_output_oat) { - Usage("Only one of --output-oat-file, and --output-oat-fd may be used."); - } - have_output_oat = true; - output_oat_filename = option.substr(strlen("--output-oat-file=")).data(); - } else if (option.starts_with("--output-oat-fd=")) { - if (have_output_oat) { - Usage("Only one of --output-oat-file, --output-oat-fd may be used."); - } - have_output_oat = true; - output_oat_fd = ParseFd(option, "--output-oat-fd="); - } else if (option.starts_with("--output-vdex-fd=")) { - output_vdex_fd = ParseFd(option, "--output-vdex-fd="); } else if (option.starts_with("--output-image-file=")) { output_image_filename = option.substr(strlen("--output-image-file=")).data(); } else if (option.starts_with("--base-offset-delta=")) { @@ -1459,12 +809,6 @@ static int patchoat(int argc, char **argv) { if (!ParseInt(base_delta_str, &base_delta)) { Usage("Failed to parse --base-offset-delta argument '%s' as an off_t", base_delta_str); } - } else if (option.starts_with("--patched-image-location=")) { - patched_image_location = option.substr(strlen("--patched-image-location=")).data(); - } else if (option == "--lock-output") { - lock_output = true; - } else if (option == "--no-lock-output") { - lock_output = false; } else if (option == "--dump-timings") { dump_timings = true; } else if (option == "--no-dump-timings") { @@ -1479,33 +823,13 @@ static int patchoat(int argc, char **argv) { Usage("Instruction set must be set."); } - int ret; - if (!input_image_location.empty()) { - ret = patchoat_image(timings, - isa, - input_image_location, - output_image_filename, - base_delta, - base_delta_set, - debug); - } else { - ret = patchoat_oat(timings, - isa, - patched_image_location, - base_delta, - base_delta_set, - input_oat_fd, - input_vdex_fd, - input_oat_location, - input_oat_filename, - have_input_oat, - output_oat_fd, - output_vdex_fd, - output_oat_filename, - have_output_oat, - lock_output, - debug); - } + int ret = patchoat_image(timings, + isa, + input_image_location, + output_image_filename, + base_delta, + base_delta_set, + debug); timings.EndTiming(); if (dump_timings) { diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h index a51963127a..e15a6bc695 100644 --- a/patchoat/patchoat.h +++ b/patchoat/patchoat.h @@ -44,17 +44,7 @@ class Class; class PatchOat { public: - // Patch only the oat file - static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings, - bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ? - bool new_oat_out); // Output oat was a new file created by us? - - // Patch only the image (art file) - static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa, - TimingLogger* timings); - - // Patch both the image and the oat file - static bool Patch(const std::string& art_location, + static bool Patch(const std::string& image_location, off_t delta, const std::string& output_directory, InstructionSet isa, @@ -64,18 +54,11 @@ class PatchOat { PatchOat(PatchOat&&) = default; private: - // Takes ownership only of the ElfFile. All other pointers are only borrowed. - PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings) - : oat_file_(oat_file), image_(nullptr), bitmap_(nullptr), heap_(nullptr), delta_(delta), - isa_(kNone), space_map_(nullptr), timings_(timings) {} - PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, - MemMap* heap, off_t delta, TimingLogger* timings) - : image_(image), bitmap_(bitmap), heap_(heap), - delta_(delta), isa_(isa), space_map_(nullptr), timings_(timings) {} - PatchOat(InstructionSet isa, ElfFile* oat_file, MemMap* image, + // All pointers are only borrowed. + PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta, std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* map, TimingLogger* timings) - : oat_file_(oat_file), image_(image), bitmap_(bitmap), heap_(heap), + : image_(image), bitmap_(bitmap), heap_(heap), delta_(delta), isa_(isa), space_map_(map), timings_(timings) {} // Was the .art image at image_path made with --compile-pic ? @@ -94,9 +77,7 @@ class PatchOat { // Attempt to replace the file with a symlink // Returns false if it fails static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename, - const std::string& output_oat_filename, - bool output_oat_opened_from_fd, - bool new_oat_out); // Output oat was newly created? + const std::string& output_oat_filename); static void BitmapCallback(mirror::Object* obj, void* arg) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -108,13 +89,6 @@ class PatchOat { void FixupMethod(ArtMethod* object, ArtMethod* copy) REQUIRES_SHARED(Locks::mutator_lock_); - // Patches oat in place, modifying the oat_file given to the constructor. - bool PatchElf(); - template <typename ElfFileImpl> - bool PatchElf(ElfFileImpl* oat_file); - template <typename ElfFileImpl> - bool PatchOatHeader(ElfFileImpl* oat_file); - bool PatchImage(bool primary_image) REQUIRES_SHARED(Locks::mutator_lock_); void PatchArtFields(const ImageHeader* image_header) REQUIRES_SHARED(Locks::mutator_lock_); void PatchArtMethods(const ImageHeader* image_header) REQUIRES_SHARED(Locks::mutator_lock_); @@ -128,7 +102,6 @@ class PatchOat { void PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots) REQUIRES_SHARED(Locks::mutator_lock_); - bool WriteElf(File* out); bool WriteImage(File* out); template <typename T> @@ -175,19 +148,6 @@ class PatchOat { return reinterpret_cast<T*>(ret); } - template <typename T> - T RelocatedAddressOfIntPointer(T obj) const { - if (obj == 0) { - return obj; - } - T ret = obj + delta_; - // Trim off high bits in case negative relocation with 64 bit patchoat. - if (Is32BitISA()) { - ret = static_cast<T>(static_cast<uint32_t>(ret)); - } - return ret; - } - bool Is32BitISA() const { return InstructionSetPointerSize(isa_) == PointerSize::k32; } @@ -213,8 +173,6 @@ class PatchOat { mirror::Object* const copy_; }; - // The elf file we are patching. - std::unique_ptr<ElfFile> oat_file_; // A mmap of the image we are patching. This is modified. const MemMap* const image_; // The bitmap over the image within the heap we are patching. This is not modified. diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 102c313a6a..db1cad670d 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -856,27 +856,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * r0 = array, r1 = index, r2 = value - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - tst r0, r0 - bne art_quick_aput_obj_with_bound_check - b art_quick_throw_null_pointer_exception -END art_quick_aput_obj_with_null_and_bound_check - - .hidden art_quick_aput_obj_with_bound_check -ENTRY art_quick_aput_obj_with_bound_check - ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] - cmp r3, r1 - bhi art_quick_aput_obj - mov r0, r1 - mov r1, r3 - b art_quick_throw_array_bounds -END art_quick_aput_obj_with_bound_check - #ifdef USE_READ_BARRIER .extern artReadBarrierSlow #endif diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 3b3783cad4..ed24a0723f 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1404,33 +1404,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * x0 = array, x1 = index, x2 = value - * - * Currently all values should fit into w0/w1/w2, and w1 always will as indices are 32b. We - * assume, though, that the upper 32b are zeroed out. At least for x1/w1 we can do better by - * using index-zero-extension in load/stores. - * - * Temporaries: x3, x4 - * TODO: x4 OK? ip seems wrong here. - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - tst x0, x0 - bne art_quick_aput_obj_with_bound_check - b art_quick_throw_null_pointer_exception -END art_quick_aput_obj_with_null_and_bound_check - -ENTRY art_quick_aput_obj_with_bound_check - ldr w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET] - cmp w3, w1 - bhi art_quick_aput_obj - mov x0, x1 - mov x1, x3 - b art_quick_throw_array_bounds -END art_quick_aput_obj_with_bound_check - #ifdef USE_READ_BARRIER .extern artReadBarrierSlow #endif @@ -1675,8 +1648,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTL // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) implemented in asm // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 5c569232ac..36f9ea78e1 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -142,16 +142,8 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { static_assert(!IsDirectEntrypoint(kQuickGetObjStatic), "Non-direct C stub marked direct."); // Array - qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check; - static_assert(!IsDirectEntrypoint(kQuickAputObjectWithNullAndBoundCheck), - "Non-direct C stub marked direct."); - qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check; - static_assert(!IsDirectEntrypoint(kQuickAputObjectWithBoundCheck), - "Non-direct C stub marked direct."); qpoints->pAputObject = art_quick_aput_obj; static_assert(!IsDirectEntrypoint(kQuickAputObject), "Non-direct C stub marked direct."); - qpoints->pHandleFillArrayData = art_quick_handle_fill_data; - static_assert(!IsDirectEntrypoint(kQuickHandleFillArrayData), "Non-direct C stub marked direct."); // JNI qpoints->pJniMethodStart = JniMethodStart; @@ -262,6 +254,7 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { art_quick_invoke_virtual_trampoline_with_access_check; static_assert(!IsDirectEntrypoint(kQuickInvokeVirtualTrampolineWithAccessCheck), "Non-direct C stub marked direct."); + qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic; // Thread qpoints->pTestSuspend = art_quick_test_suspend; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 3acc0a9d5b..76218fb542 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1389,28 +1389,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * a0 = array, a1 = index, a2 = value - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - bnez $a0, .Lart_quick_aput_obj_with_bound_check_gp_set - nop - b art_quick_throw_null_pointer_exception - nop -END art_quick_aput_obj_with_null_and_bound_check - -ENTRY art_quick_aput_obj_with_bound_check - lw $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0) - sltu $t1, $a1, $t0 - bnez $t1, .Lart_quick_aput_obj_gp_set - nop - move $a0, $a1 - b art_quick_throw_array_bounds - move $a1, $t0 -END art_quick_aput_obj_with_bound_check - #ifdef USE_READ_BARRIER .extern artReadBarrierSlow #endif @@ -2285,15 +2263,12 @@ END art_quick_string_compareto ENTRY art_quick_invoke_polymorphic SETUP_SAVE_REFS_AND_ARGS_FRAME move $a2, rSELF # Make $a2 an alias for the current Thread. - move $a3, $sp # Make $a3 a pointer to the saved frame context. - addiu $sp, $sp, -24 # Reserve space for JValue result and 4 words for callee. - .cfi_adjust_cfa_offset 24 + addiu $a3, $sp, ARG_SLOT_SIZE # Make $a3 a pointer to the saved frame context. sw $zero, 20($sp) # Initialize JValue result. sw $zero, 16($sp) - addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result la $t9, artInvokePolymorphic jalr $t9 # (result, receiver, Thread*, context) - nop + addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result .macro MATCH_RETURN_TYPE c, handler li $t0, \c beq $v0, $t0, \handler @@ -2307,18 +2282,17 @@ ENTRY art_quick_invoke_polymorphic MATCH_RETURN_TYPE 'D', .Lstore_double_result MATCH_RETURN_TYPE 'F', .Lstore_float_result MATCH_RETURN_TYPE 'S', .Lstore_int_result + MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result .purgem MATCH_RETURN_TYPE nop b .Lcleanup_and_return nop .Lstore_boolean_result: - lbu $v0, 16($sp) # Move byte from JValue result to return value register. b .Lcleanup_and_return - nop + lbu $v0, 16($sp) # Move byte from JValue result to return value register. .Lstore_char_result: - lhu $v0, 16($sp) # Move char from JValue result to return value register. b .Lcleanup_and_return - nop + lhu $v0, 16($sp) # Move char from JValue result to return value register. .Lstore_double_result: .Lstore_float_result: LDu $f0, $f1, 16, $sp, $t0 # Move double/float from JValue result to return value register. @@ -2331,8 +2305,6 @@ ENTRY art_quick_invoke_polymorphic lw $v0, 16($sp) # Move lower bits from JValue result to return value register. // Fall-through to clean up and return. .Lcleanup_and_return: - addiu $sp, $sp, 24 # Remove space for JValue result and the 4 words for the callee. - .cfi_adjust_cfa_offset -24 lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_ RESTORE_SAVE_REFS_AND_ARGS_FRAME bnez $t7, 1f # Success if no exception is pending. diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index ae786fe626..b53fd100fa 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1360,29 +1360,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * a0 = array, a1 = index, a2 = value - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - bne $a0, $zero, .Lart_quick_aput_obj_with_bound_check_gp_set - nop - b art_quick_throw_null_pointer_exception - .cpreturn # Restore gp from t8 in branch delay slot. -END art_quick_aput_obj_with_null_and_bound_check - -ENTRY art_quick_aput_obj_with_bound_check - lwu $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0) - sltu $t1, $a1, $t0 - bne $t1, $zero, .Lart_quick_aput_obj_gp_set - nop - move $a0, $a1 - move $a1, $t0 - b art_quick_throw_array_bounds - .cpreturn # Restore gp from t8 in branch delay slot. -END art_quick_aput_obj_with_bound_check - ENTRY art_quick_aput_obj beq $a2, $zero, .Ldo_aput_null nop @@ -2132,9 +2109,8 @@ ENTRY art_quick_invoke_polymorphic daddiu $sp, $sp, -8 # Reserve space for JValue result. .cfi_adjust_cfa_offset 8 sd $zero, 0($sp) # Initialize JValue result. - move $a0, $sp # Make $a0 a pointer to the JValue result jal artInvokePolymorphic # (result, receiver, Thread*, context) - nop + move $a0, $sp # Make $a0 a pointer to the JValue result .macro MATCH_RETURN_TYPE c, handler li $t0, \c beq $v0, $t0, \handler @@ -2148,27 +2124,24 @@ ENTRY art_quick_invoke_polymorphic MATCH_RETURN_TYPE 'D', .Lstore_double_result MATCH_RETURN_TYPE 'F', .Lstore_float_result MATCH_RETURN_TYPE 'S', .Lstore_long_result + MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result .purgem MATCH_RETURN_TYPE nop b .Lcleanup_and_return nop .Lstore_boolean_result: - lbu $v0, 0($sp) # Move byte from JValue result to return value register. b .Lcleanup_and_return - nop + lbu $v0, 0($sp) # Move byte from JValue result to return value register. .Lstore_char_result: - lhu $v0, 0($sp) # Move char from JValue result to return value register. b .Lcleanup_and_return - nop + lhu $v0, 0($sp) # Move char from JValue result to return value register. .Lstore_double_result: .Lstore_float_result: - l.d $f0, 0($sp) # Move double/float from JValue result to return value register. b .Lcleanup_and_return - nop + l.d $f0, 0($sp) # Move double/float from JValue result to return value register. .Lstore_ref_result: - lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register. b .Lcleanup_and_return - nop + lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register. .Lstore_long_result: ld $v0, 0($sp) # Move long from JValue result to return value register. // Fall-through to clean up and return. diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S index abd9046174..3959cbee05 100644 --- a/runtime/arch/quick_alloc_entrypoints.S +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -29,10 +29,6 @@ THREE_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCod // Called by managed code to allocate an array when the caller doesn't know whether it has access // to the created type. THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate a string from bytes FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate a string from chars @@ -71,10 +67,6 @@ GENERATE_ALLOC_ENTRYPOINTS _region_tlab_instrumented, RegionTLABInstrumented THREE_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \ THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check ## c_suffix, artAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(c_suffix, cxx_suffix) \ - THREE_ARG_DOWNCALL art_quick_check_and_alloc_array ## c_suffix, artCheckAndAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \ - THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check ## c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \ FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \ @@ -95,8 +87,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTL GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) @@ -110,8 +100,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) @@ -129,8 +117,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc, DlMalloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc) @@ -141,8 +127,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc_instrumented GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented) @@ -154,8 +138,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc) @@ -166,8 +148,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc_instrumented GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosAllocInstrumented) @@ -178,8 +158,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer, BumpPoi GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer, BumpPointer) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer) @@ -190,8 +168,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer_instrume GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented) @@ -202,8 +178,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab_instrumented, TL GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInstrumented) @@ -214,8 +188,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region) @@ -226,8 +198,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_instrumented, GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented) @@ -238,8 +208,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab_instrumen GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab_instrumented, RegionTLABInstrumented) diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index ee65fa8ab0..393dfe6d19 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -908,139 +908,6 @@ TEST_F(StubTest, CheckCast) { #endif } - -TEST_F(StubTest, APutObj) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ - (defined(__x86_64__) && !defined(__APPLE__)) - Thread* self = Thread::Current(); - - // Do not check non-checked ones, we'd need handlers and stuff... - const uintptr_t art_quick_aput_obj_with_null_and_bound_check = - StubTest::GetEntrypoint(self, kQuickAputObjectWithNullAndBoundCheck); - - // Create an object - ScopedObjectAccess soa(self); - // garbage is created during ClassLinker::Init - - StackHandleScope<5> hs(soa.Self()); - Handle<mirror::Class> c( - hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); - Handle<mirror::Class> ca( - hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"))); - - // Build a string array of size 1 - Handle<mirror::ObjectArray<mirror::Object>> array( - hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), 10))); - - // Build a string -> should be assignable - Handle<mirror::String> str_obj( - hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!"))); - - // Build a generic object -> should fail assigning - Handle<mirror::Object> obj_obj(hs.NewHandle(c->AllocObject(soa.Self()))); - - // Play with it... - - // 1) Success cases - // 1.1) Assign str_obj to array[0..3] - - EXPECT_FALSE(self->IsExceptionPending()); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(0)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(1)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(2)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(3)); - - // 1.2) Assign null to array[0..3] - - Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(0)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(1)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(2)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(3)); - - // TODO: Check _which_ exception is thrown. Then make 3) check that it's the right check order. - - // 2) Failure cases (str into str[]) - // 2.1) Array = null - // TODO: Throwing NPE needs actual DEX code - -// Invoke3(reinterpret_cast<size_t>(nullptr), 0U, reinterpret_cast<size_t>(str_obj.Get()), -// reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self); -// -// EXPECT_TRUE(self->IsExceptionPending()); -// self->ClearException(); - - // 2.2) Index < 0 - - Invoke3(reinterpret_cast<size_t>(array.Get()), static_cast<size_t>(-1), - reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_TRUE(self->IsExceptionPending()); - self->ClearException(); - - // 2.3) Index > 0 - - Invoke3(reinterpret_cast<size_t>(array.Get()), 10U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_TRUE(self->IsExceptionPending()); - self->ClearException(); - - // 3) Failure cases (obj into str[]) - - Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(obj_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_TRUE(self->IsExceptionPending()); - self->ClearException(); - - // Tests done. -#else - LOG(INFO) << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA; - // Force-print to std::cout so it's also outside the logcat. - std::cout << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA << std::endl; -#endif -} - TEST_F(StubTest, AllocObject) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ (defined(__x86_64__) && !defined(__APPLE__)) diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 1d979d852e..c4202596f2 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1352,26 +1352,6 @@ MACRO4(READ_BARRIER, obj_reg, offset, dest_reg, pop_eax) #endif // USE_READ_BARRIER END_MACRO - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * eax = array, ecx = index, edx = value - */ -DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check - testl %eax, %eax - jnz SYMBOL(art_quick_aput_obj_with_bound_check) - jmp SYMBOL(art_quick_throw_null_pointer_exception) -END_FUNCTION art_quick_aput_obj_with_null_and_bound_check - -DEFINE_FUNCTION art_quick_aput_obj_with_bound_check - movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ebx - cmpl %ebx, %ecx - jb SYMBOL(art_quick_aput_obj) - mov %ecx, %eax - mov %ebx, %ecx - jmp SYMBOL(art_quick_throw_array_bounds) -END_FUNCTION art_quick_aput_obj_with_bound_check - DEFINE_FUNCTION art_quick_aput_obj test %edx, %edx // store of null jz .Ldo_aput_null diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 28034c9bae..550a5e8452 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -989,8 +989,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTL // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) @@ -1000,8 +998,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) @@ -1466,7 +1462,7 @@ END_MACRO * 64b PUSH/POP and 32b argument. * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path. * - * As with art_quick_aput_obj* functions, the 64b versions are in comments. + * As with art_quick_aput_obj function, the 64b versions are in comments. */ MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64) #ifdef USE_READ_BARRIER @@ -1503,46 +1499,6 @@ MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64) #endif // USE_READ_BARRIER END_MACRO - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * - * Currently all the parameters should fit into the 32b portions of the registers. Index always - * will. So we optimize for a tighter encoding. The 64b versions are in comments. - * - * rdi(edi) = array, rsi(esi) = index, rdx(edx) = value - */ -DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check -#if defined(__APPLE__) - int3 - int3 -#else - testl %edi, %edi -// testq %rdi, %rdi - jnz art_quick_aput_obj_with_bound_check - jmp art_quick_throw_null_pointer_exception -#endif // __APPLE__ -END_FUNCTION art_quick_aput_obj_with_null_and_bound_check - - -DEFINE_FUNCTION art_quick_aput_obj_with_bound_check -#if defined(__APPLE__) - int3 - int3 -#else - movl MIRROR_ARRAY_LENGTH_OFFSET(%edi), %ecx -// movl MIRROR_ARRAY_LENGTH_OFFSET(%rdi), %ecx // This zero-extends, so value(%rcx)=value(%ecx) - cmpl %ecx, %esi - jb art_quick_aput_obj - mov %esi, %edi -// mov %rsi, %rdi - mov %ecx, %esi -// mov %rcx, %rsi - jmp art_quick_throw_array_bounds -#endif // __APPLE__ -END_FUNCTION art_quick_aput_obj_with_bound_check - - DEFINE_FUNCTION art_quick_aput_obj testl %edx, %edx // store of null // test %rdx, %rdx diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 9c207400a2..0fd891c9ec 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -180,20 +180,6 @@ inline GcRoot<mirror::Class>* ArtMethod::GetDexCacheResolvedTypes(PointerSize po pointer_size); } -template <bool kWithCheck> -inline mirror::Class* ArtMethod::GetDexCacheResolvedType(dex::TypeIndex type_index, - PointerSize pointer_size) { - if (kWithCheck) { - mirror::DexCache* dex_cache = GetInterfaceMethodIfProxy(pointer_size)->GetDexCache(); - if (UNLIKELY(type_index.index_ >= dex_cache->NumResolvedTypes())) { - ThrowArrayIndexOutOfBoundsException(type_index.index_, dex_cache->NumResolvedTypes()); - return nullptr; - } - } - mirror::Class* klass = GetDexCacheResolvedTypes(pointer_size)[type_index.index_].Read(); - return (klass != nullptr && !klass->IsErroneous()) ? klass : nullptr; -} - inline bool ArtMethod::HasDexCacheResolvedTypes(PointerSize pointer_size) { return GetDexCacheResolvedTypes(pointer_size) != nullptr; } @@ -207,15 +193,15 @@ inline bool ArtMethod::HasSameDexCacheResolvedTypes(ArtMethod* other, PointerSiz return GetDexCacheResolvedTypes(pointer_size) == other->GetDexCacheResolvedTypes(pointer_size); } -inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, - bool resolve, - PointerSize pointer_size) { - mirror::Class* type = GetDexCacheResolvedType(type_idx, pointer_size); - if (type == nullptr && resolve) { - type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); +inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) { + ObjPtr<mirror::DexCache> dex_cache = GetDexCache(); + ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); + if (UNLIKELY(type == nullptr) && resolve) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + type = class_linker->ResolveType(type_idx, this); CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); } - return type; + return type.Ptr(); } inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) { @@ -333,9 +319,9 @@ inline const DexFile::CodeItem* ArtMethod::GetCodeItem() { return GetDexFile()->GetCodeItem(GetCodeItemOffset()); } -inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) { +inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx) { DCHECK(!IsProxyMethod()); - return GetDexCacheResolvedType(type_idx, pointer_size) != nullptr; + return GetClassFromTypeIndex(type_idx, /* resolve */ false) != nullptr; } inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) { @@ -435,18 +421,13 @@ inline void ArtMethod::SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_c SetNativePointer(DexCacheResolvedTypesOffset(pointer_size), new_dex_cache_types, pointer_size); } -inline mirror::Class* ArtMethod::GetReturnType(bool resolve, PointerSize pointer_size) { +inline mirror::Class* ArtMethod::GetReturnType(bool resolve) { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex()); const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); dex::TypeIndex return_type_idx = proto_id.return_type_idx_; - mirror::Class* type = GetDexCacheResolvedType(return_type_idx, pointer_size); - if (type == nullptr && resolve) { - type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this); - CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); - } - return type; + return GetClassFromTypeIndex(return_type_idx, resolve); } inline bool ArtMethod::HasSingleImplementation() { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index dfc7837aea..d7d39afa8f 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -236,7 +236,6 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, // Default to handler not found. uint32_t found_dex_pc = DexFile::kDexNoIndex; // Iterate over the catch handlers associated with dex_pc. - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) { dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex(); // Catch all case @@ -245,9 +244,7 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, break; } // Does this catch exception type apply? - mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, - true /* resolve */, - pointer_size); + mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, true /* resolve */); if (UNLIKELY(iter_exception_type == nullptr)) { // Now have a NoClassDefFoundError as exception. Ignore in case the exception class was // removed by a pro-guard like tool. diff --git a/runtime/art_method.h b/runtime/art_method.h index b80ed9cf04..d292630de9 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -351,9 +351,6 @@ class ArtMethod FINAL { bool HasSameDexCacheResolvedMethods(ArtMethod** other_cache, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - template <bool kWithCheck = true> - mirror::Class* GetDexCacheResolvedType(dex::TypeIndex type_idx, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); void SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); @@ -364,9 +361,7 @@ class ArtMethod FINAL { REQUIRES_SHARED(Locks::mutator_lock_); // Get the Class* from the type index into this method's dex cache. - mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, - bool resolve, - PointerSize pointer_size) + mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if this method has the same name and signature of the other method. @@ -558,8 +553,7 @@ class ArtMethod FINAL { const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_); - bool IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); + bool IsResolvedTypeIdx(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); int32_t GetLineNumFromDexPC(uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_); @@ -580,8 +574,7 @@ class ArtMethod FINAL { // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large // number of bugs at call sites. - mirror::Class* GetReturnType(bool resolve, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Class* GetReturnType(bool resolve) REQUIRES_SHARED(Locks::mutator_lock_); mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 4b15a22411..18a53c916e 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -104,7 +104,7 @@ ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, // Offset of field Thread::tlsPtr_.mterp_current_ibase. #define THREAD_CURRENT_IBASE_OFFSET \ - (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__) + (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 159) * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 9116097604..e05a85a116 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -46,6 +46,7 @@ Mutex* Locks::deoptimization_lock_ = nullptr; ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr; Mutex* Locks::instrument_entrypoints_lock_ = nullptr; Mutex* Locks::intern_table_lock_ = nullptr; +Mutex* Locks::jni_function_table_lock_ = nullptr; Mutex* Locks::jni_libraries_lock_ = nullptr; Mutex* Locks::logging_lock_ = nullptr; Mutex* Locks::mem_maps_lock_ = nullptr; @@ -957,6 +958,7 @@ void Locks::Init() { DCHECK(verifier_deps_lock_ != nullptr); DCHECK(host_dlopen_handles_lock_ != nullptr); DCHECK(intern_table_lock_ != nullptr); + DCHECK(jni_function_table_lock_ != nullptr); DCHECK(jni_libraries_lock_ != nullptr); DCHECK(logging_lock_ != nullptr); DCHECK(mutator_lock_ != nullptr); @@ -1098,6 +1100,10 @@ void Locks::Init() { DCHECK(jni_weak_globals_lock_ == nullptr); jni_weak_globals_lock_ = new Mutex("JNI weak global reference table lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kJniFunctionTableLock); + DCHECK(jni_function_table_lock_ == nullptr); + jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kAbortLock); DCHECK(abort_lock_ == nullptr); abort_lock_ = new Mutex("abort lock", current_lock_level, true); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 2adeb8cc97..21dd437711 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -68,6 +68,7 @@ enum LockLevel { kRosAllocBulkFreeLock, kMarkSweepMarkStackLock, kTransactionLogLock, + kJniFunctionTableLock, kJniWeakGlobalsLock, kJniGlobalsLock, kReferenceQueueSoftReferencesLock, @@ -698,8 +699,11 @@ class Locks { // Guard accesses to the JNI Weak Global Reference table. static Mutex* jni_weak_globals_lock_ ACQUIRED_AFTER(jni_globals_lock_); + // Guard accesses to the JNI function table override. + static Mutex* jni_function_table_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_); + // Have an exclusive aborting thread. - static Mutex* abort_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_); + static Mutex* abort_lock_ ACQUIRED_AFTER(jni_function_table_lock_); // Allow mutual exclusion when manipulating Thread::suspend_count_. // TODO: Does the trade-off of a per-thread lock make sense? diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 5fc5f1a2f5..2e17dd85e6 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -25,7 +25,6 @@ #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" #include "mirror/iftable.h" -#include "mirror/throwable.h" #include "mirror/object_array.h" #include "handle_scope-inl.h" #include "scoped_thread_state_change-inl.h" @@ -90,25 +89,16 @@ inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMetho if (kIsDebugBuild) { Thread::Current()->AssertNoPendingException(); } - ObjPtr<mirror::Class> resolved_type = - referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_); + ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { StackHandleScope<2> hs(Thread::Current()); - // There could be an out of bounds exception from GetDexCacheResolvedType, don't call - // ResolveType for this case. - if (LIKELY(!hs.Self()->IsExceptionPending())) { - ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile& dex_file = *dex_cache->GetDexFile(); - resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); - // Note: We cannot check here to see whether we added the type to the cache. The type - // might be an erroneous class, which results in it being hidden from us. - } else { - // Make sure its an array out of bounds exception. - DCHECK(hs.Self()->GetException()->GetClass()->DescriptorEquals( - "Ljava/lang/ArrayIndexOutOfBoundsException;")); - } + ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); + const DexFile& dex_file = *dex_cache->GetDexFile(); + resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); + // Note: We cannot check here to see whether we added the type to the cache. The type + // might be an erroneous class, which results in it being hidden from us. } return resolved_type.Ptr(); } @@ -256,8 +246,8 @@ ArtMethod* ClassLinker::FindMethodForProxy(ObjPtr<mirror::Class> proxy_class, // Locate the dex cache of the original interface/Object for (const DexCacheData& data : dex_caches_) { if (!self->IsJWeakCleared(data.weak_root) && - proxy_method->HasSameDexCacheResolvedTypes(data.resolved_types, - image_pointer_size_)) { + proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods, + image_pointer_size_)) { ObjPtr<mirror::DexCache> dex_cache = ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); if (dex_cache != nullptr) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 448b4600cc..07c6eda03f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3303,7 +3303,7 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, DexCacheData data; data.weak_root = dex_cache_jweak; data.dex_file = dex_cache->GetDexFile(); - data.resolved_types = dex_cache->GetResolvedTypes(); + data.resolved_methods = dex_cache->GetResolvedMethods(); dex_caches_.push_back(data); } @@ -4373,8 +4373,7 @@ void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) cons CHECK_STREQ(np->GetName(), prototype->GetName()); CHECK_STREQ(np->GetShorty(), prototype->GetShorty()); // More complex sanity - via dex cache - CHECK_EQ(np->GetReturnType(true /* resolve */, image_pointer_size_), - prototype->GetReturnType(true /* resolve */, image_pointer_size_)); + CHECK_EQ(np->GetReturnType(true /* resolve */), prototype->GetReturnType(true /* resolve */)); } bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_init_statics, @@ -4836,7 +4835,6 @@ static void ThrowSignatureMismatch(Handle<mirror::Class> klass, } static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, - PointerSize pointer_size, Handle<mirror::Class> klass, Handle<mirror::Class> super_klass, ArtMethod* method1, @@ -4844,14 +4842,12 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, REQUIRES_SHARED(Locks::mutator_lock_) { { StackHandleScope<1> hs(self); - Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */, - pointer_size))); + Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */))); if (UNLIKELY(return_type.Get() == nullptr)) { ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1); return false; } - ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */, - pointer_size); + ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */); if (UNLIKELY(other_return_type == nullptr)) { ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2); return false; @@ -4896,7 +4892,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, StackHandleScope<1> hs(self); dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_; Handle<mirror::Class> param_type(hs.NewHandle( - method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */, pointer_size))); + method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */))); if (UNLIKELY(param_type.Get() == nullptr)) { ThrowSignatureCheckResolveArgException(klass, super_klass, method1, method1, i, param_type_idx); @@ -4904,7 +4900,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, } dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_; ObjPtr<mirror::Class> other_param_type = - method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */, pointer_size); + method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */); if (UNLIKELY(other_param_type == nullptr)) { ThrowSignatureCheckResolveArgException(klass, super_klass, method1, method2, i, other_param_type_idx); @@ -4940,9 +4936,11 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) { auto* m = klass->GetVTableEntry(i, image_pointer_size_); auto* super_m = klass->GetSuperClass()->GetVTableEntry(i, image_pointer_size_); if (m != super_m) { - if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_, - klass, super_klass, - m, super_m))) { + if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, + klass, + super_klass, + m, + super_m))) { self->AssertPendingException(); return false; } @@ -4958,9 +4956,11 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) { j, image_pointer_size_); auto* super_m = super_klass->GetVirtualMethod(j, image_pointer_size_); if (m != super_m) { - if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_, - klass, super_klass, - m, super_m))) { + if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, + klass, + super_klass, + m, + super_m))) { self->AssertPendingException(); return false; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index cad674e472..9b98671cb4 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -649,6 +649,10 @@ class ClassLinker { ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_); + void AppendToBootClassPath(Thread* self, const DexFile& dex_file) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_); + struct DexCacheData { // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may // not work properly. @@ -657,7 +661,7 @@ class ClassLinker { // jweak decode that triggers read barriers (and mark them alive unnecessarily and mess with // class unloading.) const DexFile* dex_file; - GcRoot<mirror::Class>* resolved_types; + ArtMethod** resolved_methods; }; private: @@ -744,9 +748,6 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - void AppendToBootClassPath(Thread* self, const DexFile& dex_file) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Locks::dex_lock_); void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index df4413d52c..006476bafc 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -3985,9 +3985,7 @@ JDWP::JdwpError Dbg::PrepareInvokeMethod(uint32_t request_id, JDWP::ObjectId thr if (shorty[i + 1] == 'L') { // Did we really get an argument of an appropriate reference type? mirror::Class* parameter_type = - m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, - true /* resolve */, - kRuntimePointerSize); + m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, true /* resolve */); mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error); if (error != JDWP::ERR_NONE) { return JDWP::ERR_INVALID_OBJECT; diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index b0c4597d73..7ae9f03c83 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -160,7 +160,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { // image at GetImageLocation(). This is used for testing mismatched // image checksums in the oat_file_assistant_tests. std::string GetImageLocation2() const { - return GetImageDirectory() + "/core-npic.art"; + return GetImageDirectory() + "/core-interpreter.art"; } std::string GetDexSrc1() const { diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 9504e8bd29..16a447b0a6 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -608,7 +608,7 @@ mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, return nullptr; } Handle<mirror::Class> method_return(hs.NewHandle( - annotation_method->GetReturnType(true /* resolve */, pointer_size))); + annotation_method->GetReturnType(true /* resolve */))); DexFile::AnnotationValue annotation_value; if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return, @@ -948,9 +948,7 @@ mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { DexFile::AnnotationValue annotation_value; StackHandleScope<2> hs(Thread::Current()); Handle<mirror::Class> h_klass(hs.NewHandle(klass)); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - Handle<mirror::Class> return_type(hs.NewHandle( - method->GetReturnType(true /* resolve */, pointer_size))); + Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */))); if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type, DexFile::kAllObjects)) { return nullptr; diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 7d6f866617..87046bc09d 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -241,10 +241,9 @@ inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, *slow_path = true; return nullptr; // Failure } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - PointerSize pointer_size = class_linker->GetImagePointerSize(); - mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size); + mirror::Class* klass = method->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); klass = class_linker->ResolveType(type_idx, method); *slow_path = true; if (klass == nullptr) { // Error diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index b17e1a8ab1..25fd727968 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -38,98 +38,13 @@ namespace art { -static inline mirror::Class* CheckFilledNewArrayAlloc(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* referrer, - Thread* self, - bool access_check) - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) { - if (UNLIKELY(component_count < 0)) { - ThrowNegativeArraySizeException(component_count); - return nullptr; // Failure - } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - PointerSize pointer_size = class_linker->GetImagePointerSize(); - mirror::Class* klass = referrer->GetDexCacheResolvedType<false>(type_idx, pointer_size); - if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve - klass = class_linker->ResolveType(type_idx, referrer); - if (klass == nullptr) { // Error - DCHECK(self->IsExceptionPending()); - return nullptr; // Failure - } - } - if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) { - if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) { - ThrowRuntimeException("Bad filled array request for type %s", - klass->PrettyDescriptor().c_str()); - } else { - self->ThrowNewExceptionF( - "Ljava/lang/InternalError;", - "Found type %s; filled-new-array not implemented for anything but 'int'", - klass->PrettyDescriptor().c_str()); - } - return nullptr; // Failure - } - if (access_check) { - mirror::Class* referrer_klass = referrer->GetDeclaringClass(); - if (UNLIKELY(!referrer_klass->CanAccess(klass))) { - ThrowIllegalAccessErrorClass(referrer_klass, klass); - return nullptr; // Failure - } - } - DCHECK(klass->IsArrayClass()) << klass->PrettyClass(); - return klass; -} - -// Helper function to allocate array for FILLED_NEW_ARRAY. -mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* referrer, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) { - mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self, - access_check); - if (UNLIKELY(klass == nullptr)) { - return nullptr; - } - // Always go slow path for now, filled new array is not common. - gc::Heap* heap = Runtime::Current()->GetHeap(); - // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then - // the heap switched the allocator type while we were suspended. - return mirror::Array::Alloc<false>(self, klass, component_count, - klass->GetComponentSizeShift(), - heap->GetCurrentAllocator()); -} - -// Helper function to allocate array for FILLED_NEW_ARRAY. -mirror::Array* CheckAndAllocArrayFromCodeInstrumented( - dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* referrer, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) { - mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self, - access_check); - if (UNLIKELY(klass == nullptr)) { - return nullptr; - } - gc::Heap* heap = Runtime::Current()->GetHeap(); - // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then - // the heap switched the allocator type while we were suspended. - return mirror::Array::Alloc<true>(self, klass, component_count, - klass->GetComponentSizeShift(), - heap->GetCurrentAllocator()); -} - void CheckReferenceResult(Handle<mirror::Object> o, Thread* self) { if (o.Get() == nullptr) { return; } // Make sure that the result is an instance of the type this method was expected to return. ArtMethod* method = self->GetCurrentMethod(nullptr); - mirror::Class* return_type = method->GetReturnType(true /* resolve */, kRuntimePointerSize); + mirror::Class* return_type = method->GetReturnType(true /* resolve */); if (!o->InstanceOf(return_type)) { Runtime::Current()->GetJavaVM()->JniAbortF(nullptr, @@ -192,8 +107,7 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons ArtMethod* interface_method = soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod(); // This can cause thread suspension. - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */, pointer_size); + mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */); ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result); JValue result_unboxed; if (!UnboxPrimitiveForResult(result_ref.Ptr(), result_type, &result_unboxed)) { diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index d4cf83c8de..b427c0717f 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -102,24 +102,6 @@ ALWAYS_INLINE inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* kl REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* method, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Roles::uninterruptible_); - -mirror::Array* CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* method, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Roles::uninterruptible_); - // Type of find field operation for fast and slow case. enum FindFieldType { InstanceObjectRead, diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 2d06508069..23a99a06c6 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -109,46 +109,6 @@ extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( self, \ allocator_type); \ } \ -extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \ - uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \ - REQUIRES_SHARED(Locks::mutator_lock_) { \ - ScopedQuickEntrypointChecks sqec(self); \ - if (!(instrumented_bool)) { \ - return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - false, \ - allocator_type); \ - } else { \ - return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - false, \ - allocator_type); \ - } \ -} \ -extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \ - uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \ - REQUIRES_SHARED(Locks::mutator_lock_) { \ - ScopedQuickEntrypointChecks sqec(self); \ - if (!(instrumented_bool)) { \ - return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - true, \ - allocator_type); \ - } else { \ - return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - true, \ - allocator_type); \ - } \ -} \ extern "C" mirror::String* artAllocStringFromBytesFromCode##suffix##suffix2( \ mirror::ByteArray* byte_array, int32_t high, int32_t offset, int32_t byte_count, \ Thread* self) \ @@ -219,8 +179,6 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix##_instrumented; \ qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix##_instrumented; \ qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix##_instrumented; \ - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix##_instrumented; \ - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented; \ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix##_instrumented; \ qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix##_instrumented; \ qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix##_instrumented; \ @@ -231,8 +189,6 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix; \ qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix; \ qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix; \ - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix; \ - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix; \ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix; \ qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix; \ qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix; \ diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 8ce61c1021..6481b97ae1 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -66,10 +66,7 @@ void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) qpoints->pGetObjStatic = art_quick_get_obj_static; // Array - qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check; - qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check; qpoints->pAputObject = art_quick_aput_obj; - qpoints->pHandleFillArrayData = art_quick_handle_fill_data; // JNI qpoints->pJniMethodStart = JniMethodStart; diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 4d5d6de41d..0e75e94d52 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -26,8 +26,6 @@ V(AllocObjectResolved, void*, mirror::Class*) \ V(AllocObjectInitialized, void*, mirror::Class*) \ V(AllocObjectWithChecks, void*, mirror::Class*) \ - V(CheckAndAllocArray, void*, uint32_t, int32_t, ArtMethod*) \ - V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \ V(AllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t) \ V(AllocStringFromChars, void*, int32_t, int32_t, void*) \ V(AllocStringFromString, void*, void*) \ @@ -65,10 +63,7 @@ V(GetObjInstance, void*, uint32_t, void*) \ V(GetObjStatic, void*, uint32_t) \ \ - V(AputObjectWithNullAndBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \ - V(AputObjectWithBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \ V(AputObject, void, mirror::Array*, int32_t, mirror::Object*) \ - V(HandleFillArrayData, void, void*, void*) \ \ V(JniMethodStart, uint32_t, Thread*) \ V(JniMethodFastStart, uint32_t, Thread*) \ diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 6301f93b2f..b9caf0f555 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -161,12 +161,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithChecks, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pCheckAndAllocArray, + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pAllocStringFromBytes, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArray, pCheckAndAllocArrayWithAccessCheck, - sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArrayWithAccessCheck, - pAllocStringFromBytes, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromBytes, pAllocStringFromChars, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromChars, pAllocStringFromString, @@ -205,13 +201,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Instance, pGet64Static, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Static, pGetObjInstance, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjInstance, pGetObjStatic, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObjectWithNullAndBoundCheck, - sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithNullAndBoundCheck, - pAputObjectWithBoundCheck, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithBoundCheck, pAputObject, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pHandleFillArrayData, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHandleFillArrayData, pJniMethodStart, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObject, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pJniMethodStart, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStart, pJniMethodFastStart, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodFastStart, pJniMethodStartSynchronized, diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 7b86339663..6044053b4f 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -846,7 +846,7 @@ void ConcurrentCopying::IssueEmptyCheckpoint() { // Some threads in 'runnable_thread_ids' are probably stuck. Try to dump their stacks. // Avoid using ThreadList::Dump() initially because it is likely to get stuck as well. { - ReaderMutexLock mu0(self, *Locks::mutator_lock_); + ScopedObjectAccess soa(self); MutexLock mu1(self, *Locks::thread_list_lock_); for (Thread* thread : thread_list->GetList()) { uint32_t tid = thread->GetThreadId(); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index ca26207093..76777d938b 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -751,16 +751,14 @@ static inline bool DoCallCommon(ArtMethod* called_method, case 'L': { ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg); if (do_assignability_check && o != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); const dex::TypeIndex type_idx = params->GetTypeItem(shorty_pos).type_idx_; - ObjPtr<mirror::Class> arg_type = method->GetDexCacheResolvedType(type_idx, - pointer_size); + ObjPtr<mirror::Class> arg_type = method->GetDexCache()->GetResolvedType(type_idx); if (arg_type == nullptr) { StackHandleScope<1> hs(self); // Preserve o since it is used below and GetClassFromTypeIndex may cause thread // suspension. HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&o); - arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */, pointer_size); + arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */); if (arg_type == nullptr) { CHECK(self->IsExceptionPending()); return false; diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index d7dfcd4408..a77a3fc2b3 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -285,9 +285,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, const size_t ref_idx = inst->VRegA_11x(inst_data); ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx); if (do_assignability_check && obj_result != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */, - pointer_size); + ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */); // Re-load since it might have moved. obj_result = shadow_frame.GetVRegReference(ref_idx); if (return_type == nullptr) { diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc index 5a3fafa726..0148a1c3b0 100644 --- a/runtime/jni_env_ext.cc +++ b/runtime/jni_env_ext.cc @@ -29,6 +29,7 @@ #include "mirror/object-inl.h" #include "nth_caller_visitor.h" #include "thread-inl.h" +#include "thread_list.h" namespace art { @@ -37,6 +38,8 @@ using android::base::StringPrintf; static constexpr size_t kMonitorsInitial = 32; // Arbitrary. static constexpr size_t kMonitorsMax = 4096; // Arbitrary sanity check. +const JNINativeInterface* JNIEnvExt::table_override_ = nullptr; + // Checking "locals" requires the mutator lock, but at creation time we're really only interested // in validity, which isn't changing. To avoid grabbing the mutator lock, factored out and tagged // with NO_THREAD_SAFETY_ANALYSIS. @@ -78,10 +81,10 @@ JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in, std::string* error_msg) runtime_deleted(false), critical(0), monitors("monitors", kMonitorsInitial, kMonitorsMax) { - functions = unchecked_functions = GetJniNativeInterface(); - if (vm->IsCheckJniEnabled()) { - SetCheckJniEnabled(true); - } + MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_); + check_jni = vm->IsCheckJniEnabled(); + functions = GetFunctionTable(check_jni); + unchecked_functions = GetJniNativeInterface(); } void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() { @@ -107,7 +110,12 @@ void JNIEnvExt::DeleteLocalRef(jobject obj) { void JNIEnvExt::SetCheckJniEnabled(bool enabled) { check_jni = enabled; - functions = enabled ? GetCheckJniNativeInterface() : GetJniNativeInterface(); + MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_); + functions = GetFunctionTable(enabled); + // Check whether this is a no-op because of override. + if (enabled && JNIEnvExt::table_override_ != nullptr) { + LOG(WARNING) << "Enabling CheckJNI after a JNIEnv function table override is not functional."; + } } void JNIEnvExt::DumpReferenceTables(std::ostream& os) { @@ -269,4 +277,33 @@ void JNIEnvExt::CheckNoHeldMonitors() { } } +static void ThreadResetFunctionTable(Thread* thread, void* arg ATTRIBUTE_UNUSED) + REQUIRES(Locks::jni_function_table_lock_) { + JNIEnvExt* env = thread->GetJniEnv(); + bool check_jni = env->check_jni; + env->functions = JNIEnvExt::GetFunctionTable(check_jni); +} + +void JNIEnvExt::SetTableOverride(const JNINativeInterface* table_override) { + MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + MutexLock mu2(Thread::Current(), *Locks::jni_function_table_lock_); + + JNIEnvExt::table_override_ = table_override; + + // See if we have a runtime. Note: we cannot run other code (like JavaVMExt's CheckJNI install + // code), as we'd have to recursively lock the mutex. + Runtime* runtime = Runtime::Current(); + if (runtime != nullptr) { + runtime->GetThreadList()->ForEach(ThreadResetFunctionTable, nullptr); + } +} + +const JNINativeInterface* JNIEnvExt::GetFunctionTable(bool check_jni) { + const JNINativeInterface* override = JNIEnvExt::table_override_; + if (override != nullptr) { + return override; + } + return check_jni ? GetCheckJniNativeInterface() : GetJniNativeInterface(); +} + } // namespace art diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h index 5cca0aef9b..4004c457b5 100644 --- a/runtime/jni_env_ext.h +++ b/runtime/jni_env_ext.h @@ -43,7 +43,7 @@ struct JNIEnvExt : public JNIEnv { void DumpReferenceTables(std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_); - void SetCheckJniEnabled(bool enabled); + void SetCheckJniEnabled(bool enabled) REQUIRES(!Locks::jni_function_table_lock_); void PushFrame(int capacity) REQUIRES_SHARED(Locks::mutator_lock_); void PopFrame() REQUIRES_SHARED(Locks::mutator_lock_); @@ -104,10 +104,27 @@ struct JNIEnvExt : public JNIEnv { // Set the functions to the runtime shutdown functions. void SetFunctionsToRuntimeShutdownFunctions(); + // Set the function table override. This will install the override (or original table, if null) + // to all threads. + // Note: JNI function table overrides are sensitive to the order of operations wrt/ CheckJNI. + // After overriding the JNI function table, CheckJNI toggling is ignored. + static void SetTableOverride(const JNINativeInterface* table_override) + REQUIRES(!Locks::thread_list_lock_, !Locks::jni_function_table_lock_); + + // Return either the regular, or the CheckJNI function table. Will return table_override_ instead + // if it is not null. + static const JNINativeInterface* GetFunctionTable(bool check_jni) + REQUIRES(Locks::jni_function_table_lock_); + private: + // Override of function tables. This applies to both default as well as instrumented (CheckJNI) + // function tables. + static const JNINativeInterface* table_override_ GUARDED_BY(Locks::jni_function_table_lock_); + // The constructor should not be called directly. It may leave the object in an erroneous state, // and the result needs to be checked. - JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg); + JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg) + REQUIRES(!Locks::jni_function_table_lock_); // All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI // to ensure that only monitors locked in this native frame are being unlocked, and that at diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 4da5e23502..08d1eeb95d 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -2346,4 +2346,39 @@ TEST_F(JniInternalTest, JNIEnvExtOffsets) { EXPECT_EQ(segment_state_now, segment_state_computed); } +static size_t gGlobalRefCount = 0; +static const JNINativeInterface* gOriginalEnv = nullptr; + +static jobject CountNewGlobalRef(JNIEnv* env, jobject o) { + ++gGlobalRefCount; + return gOriginalEnv->NewGlobalRef(env, o); +} + +// Test the table override. +TEST_F(JniInternalTest, JNIEnvExtTableOverride) { + JNINativeInterface env_override; + memcpy(&env_override, env_->functions, sizeof(JNINativeInterface)); + + gOriginalEnv = env_->functions; + env_override.NewGlobalRef = CountNewGlobalRef; + gGlobalRefCount = 0; + + jclass local = env_->FindClass("java/lang/Object"); + ASSERT_TRUE(local != nullptr); + + // Set the table, add a global ref, see whether the counter increases. + JNIEnvExt::SetTableOverride(&env_override); + + jobject global = env_->NewGlobalRef(local); + EXPECT_EQ(1u, gGlobalRefCount); + env_->DeleteGlobalRef(global); + + // Reset + JNIEnvExt::SetTableOverride(nullptr); + + jobject global2 = env_->NewGlobalRef(local); + EXPECT_EQ(1u, gGlobalRefCount); + env_->DeleteGlobalRef(global2); +} + } // namespace art diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index a6f56aee42..6a4ec9dca7 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -306,23 +306,6 @@ TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) { } -TEST_F(ObjectTest, CheckAndAllocArrayFromCode) { - // pretend we are trying to call 'new char[3]' from String.toCharArray - ScopedObjectAccess soa(Thread::Current()); - Class* java_util_Arrays = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/Arrays;"); - ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V", kRuntimePointerSize); - const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId("[I"); - ASSERT_TRUE(type_id != nullptr); - dex::TypeIndex type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id); - Object* array = CheckAndAllocArrayFromCodeInstrumented( - type_idx, 3, sort, Thread::Current(), false, - Runtime::Current()->GetHeap()->GetCurrentAllocator()); - EXPECT_TRUE(array->IsArrayInstance()); - EXPECT_EQ(3, array->AsArray()->GetLength()); - EXPECT_TRUE(array->GetClass()->IsArrayClass()); - EXPECT_TRUE(array->GetClass()->GetComponentType()->IsPrimitive()); -} - TEST_F(ObjectTest, CreateMultiArray) { ScopedObjectAccess soa(Thread::Current()); diff --git a/runtime/oat.cc b/runtime/oat.cc index 1a07cdc7f3..d14b399a9a 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -473,6 +473,10 @@ bool OatHeader::IsDebuggable() const { return IsKeyEnabled(OatHeader::kDebuggableKey); } +bool OatHeader::IsConcurrentCopying() const { + return IsKeyEnabled(OatHeader::kConcurrentCopying); +} + bool OatHeader::IsNativeDebuggable() const { return IsKeyEnabled(OatHeader::kNativeDebuggableKey); } diff --git a/runtime/oat.h b/runtime/oat.h index ab03252b9b..f9396ae2ca 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '9', '8', '\0' }; // art::Thread fields reorder + static constexpr uint8_t kOatVersion[] = { '0', '9', '9', '\0' }; // Remove old entrypoints static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; @@ -43,6 +43,7 @@ class PACKED(4) OatHeader { static constexpr const char* kCompilerFilter = "compiler-filter"; static constexpr const char* kClassPathKey = "classpath"; static constexpr const char* kBootClassPathKey = "bootclasspath"; + static constexpr const char* kConcurrentCopying = "concurrent-copying"; static constexpr const char kTrueValue[] = "true"; static constexpr const char kFalseValue[] = "false"; @@ -112,6 +113,7 @@ class PACKED(4) OatHeader { bool IsDebuggable() const; bool IsNativeDebuggable() const; CompilerFilter::Filter GetCompilerFilter() const; + bool IsConcurrentCopying() const; private: bool KeyHasValue(const char* key, const char* value, size_t value_size) const; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index f12a5e7742..8554fa2693 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -304,6 +304,13 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() { } OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) { + // Verify the ART_USE_READ_BARRIER state. + const bool is_cc = file.GetOatHeader().IsConcurrentCopying(); + constexpr bool kRuntimeIsCC = kUseReadBarrier; + if (is_cc != kRuntimeIsCC) { + return kOatCannotOpen; + } + // Verify the dex checksum. // Note: GetOatDexFile will return null if the dex checksum doesn't match // what we provide, which verifies the primary dex checksum for us. @@ -903,4 +910,3 @@ std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() { return std::unique_ptr<OatFile>(); } } // namespace art - diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index af027f6ba6..d5c652035a 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -23,10 +23,12 @@ cc_defaults { "ti_class.cc", "ti_field.cc", "ti_heap.cc", + "ti_jni.cc", "ti_method.cc", "ti_monitor.cc", "ti_object.cc", "ti_properties.cc", + "ti_search.cc", "ti_stack.cc", "ti_redefine.cc", "ti_thread.cc", diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index d9aea01ef8..90467db8f6 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -51,11 +51,13 @@ #include "ti_class.h" #include "ti_field.h" #include "ti_heap.h" +#include "ti_jni.h" #include "ti_method.h" #include "ti_monitor.h" #include "ti_object.h" #include "ti_properties.h" #include "ti_redefine.h" +#include "ti_search.h" #include "ti_stack.h" #include "ti_thread.h" #include "ti_threadgroup.h" @@ -801,11 +803,11 @@ class JvmtiFunctions { } static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table) { - return ERR(NOT_IMPLEMENTED); + return JNIUtil::SetJNIFunctionTable(env, function_table); } static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) { - return ERR(NOT_IMPLEMENTED); + return JNIUtil::GetJNIFunctionTable(env, function_table); } // TODO: This will require locking, so that an agent can't remove callbacks when we're dispatching @@ -856,7 +858,8 @@ class JvmtiFunctions { } } - return gEventHandler.SetEvent(ArtJvmTiEnv::AsArtJvmTiEnv(env), art_thread, event_type, mode); + ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); + return gEventHandler.SetEvent(art_env, art_thread, GetArtJvmtiEvent(art_env, event_type), mode); } static jvmtiError GenerateEvents(jvmtiEnv* env, jvmtiEvent event_type) { @@ -902,11 +905,15 @@ class JvmtiFunctions { ENSURE_NON_NULL(capabilities_ptr); ArtJvmTiEnv* art_env = static_cast<ArtJvmTiEnv*>(env); jvmtiError ret = OK; + jvmtiCapabilities changed; #define ADD_CAPABILITY(e) \ do { \ if (capabilities_ptr->e == 1) { \ if (kPotentialCapabilities.e == 1) { \ - art_env->capabilities.e = 1;\ + if (art_env->capabilities.e != 1) { \ + art_env->capabilities.e = 1; \ + changed.e = 1; \ + }\ } else { \ ret = ERR(NOT_AVAILABLE); \ } \ @@ -955,6 +962,9 @@ class JvmtiFunctions { ADD_CAPABILITY(can_generate_resource_exhaustion_heap_events); ADD_CAPABILITY(can_generate_resource_exhaustion_threads_events); #undef ADD_CAPABILITY + gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env), + changed, + /*added*/true); return ret; } @@ -963,10 +973,14 @@ class JvmtiFunctions { ENSURE_VALID_ENV(env); ENSURE_NON_NULL(capabilities_ptr); ArtJvmTiEnv* art_env = reinterpret_cast<ArtJvmTiEnv*>(env); + jvmtiCapabilities changed; #define DEL_CAPABILITY(e) \ do { \ if (capabilities_ptr->e == 1) { \ - art_env->capabilities.e = 0;\ + if (art_env->capabilities.e == 1) { \ + art_env->capabilities.e = 0;\ + changed.e = 1; \ + } \ } \ } while (false) @@ -1012,6 +1026,9 @@ class JvmtiFunctions { DEL_CAPABILITY(can_generate_resource_exhaustion_heap_events); DEL_CAPABILITY(can_generate_resource_exhaustion_threads_events); #undef DEL_CAPABILITY + gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env), + changed, + /*added*/false); return OK; } @@ -1052,11 +1069,11 @@ class JvmtiFunctions { } static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment) { - return ERR(NOT_IMPLEMENTED); + return SearchUtil::AddToBootstrapClassLoaderSearch(env, segment); } static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment) { - return ERR(NOT_IMPLEMENTED); + return SearchUtil::AddToSystemClassLoaderSearch(env, segment); } static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) { @@ -1231,7 +1248,12 @@ class JvmtiFunctions { } static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) { - return ERR(NOT_IMPLEMENTED); + // Report BCI as jlocation format. We report dex bytecode indices. + if (format_ptr == nullptr) { + return ERR(NULL_POINTER); + } + *format_ptr = jvmtiJlocationFormat::JVMTI_JLOCATION_JVMBCI; + return ERR(NONE); } // TODO Remove this once events are working. diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h index d0272010d4..1e07bc6b7b 100644 --- a/runtime/openjdkjvmti/events-inl.h +++ b/runtime/openjdkjvmti/events-inl.h @@ -17,14 +17,28 @@ #ifndef ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_ #define ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_ +#include <array> + #include "events.h" #include "art_jvmti.h" namespace openjdkjvmti { +static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) { + if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) { + if (env->capabilities.can_retransform_classes) { + return ArtJvmtiEvent::kClassFileLoadHookRetransformable; + } else { + return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable; + } + } else { + return static_cast<ArtJvmtiEvent>(e); + } +} + template <typename FnType> -ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, jvmtiEvent event) { +ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, ArtJvmtiEvent event) { if (env->event_callbacks == nullptr) { return nullptr; } @@ -33,84 +47,80 @@ ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, jvmtiEvent eve // function. switch (event) { - case JVMTI_EVENT_VM_INIT: + case ArtJvmtiEvent::kVmInit: return reinterpret_cast<FnType*>(env->event_callbacks->VMInit); - case JVMTI_EVENT_VM_DEATH: + case ArtJvmtiEvent::kVmDeath: return reinterpret_cast<FnType*>(env->event_callbacks->VMDeath); - case JVMTI_EVENT_THREAD_START: + case ArtJvmtiEvent::kThreadStart: return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart); - case JVMTI_EVENT_THREAD_END: + case ArtJvmtiEvent::kThreadEnd: return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd); - case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK: + case ArtJvmtiEvent::kClassFileLoadHookRetransformable: + case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable: return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook); - case JVMTI_EVENT_CLASS_LOAD: + case ArtJvmtiEvent::kClassLoad: return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad); - case JVMTI_EVENT_CLASS_PREPARE: + case ArtJvmtiEvent::kClassPrepare: return reinterpret_cast<FnType*>(env->event_callbacks->ClassPrepare); - case JVMTI_EVENT_VM_START: + case ArtJvmtiEvent::kVmStart: return reinterpret_cast<FnType*>(env->event_callbacks->VMStart); - case JVMTI_EVENT_EXCEPTION: + case ArtJvmtiEvent::kException: return reinterpret_cast<FnType*>(env->event_callbacks->Exception); - case JVMTI_EVENT_EXCEPTION_CATCH: + case ArtJvmtiEvent::kExceptionCatch: return reinterpret_cast<FnType*>(env->event_callbacks->ExceptionCatch); - case JVMTI_EVENT_SINGLE_STEP: + case ArtJvmtiEvent::kSingleStep: return reinterpret_cast<FnType*>(env->event_callbacks->SingleStep); - case JVMTI_EVENT_FRAME_POP: + case ArtJvmtiEvent::kFramePop: return reinterpret_cast<FnType*>(env->event_callbacks->FramePop); - case JVMTI_EVENT_BREAKPOINT: + case ArtJvmtiEvent::kBreakpoint: return reinterpret_cast<FnType*>(env->event_callbacks->Breakpoint); - case JVMTI_EVENT_FIELD_ACCESS: + case ArtJvmtiEvent::kFieldAccess: return reinterpret_cast<FnType*>(env->event_callbacks->FieldAccess); - case JVMTI_EVENT_FIELD_MODIFICATION: + case ArtJvmtiEvent::kFieldModification: return reinterpret_cast<FnType*>(env->event_callbacks->FieldModification); - case JVMTI_EVENT_METHOD_ENTRY: + case ArtJvmtiEvent::kMethodEntry: return reinterpret_cast<FnType*>(env->event_callbacks->MethodEntry); - case JVMTI_EVENT_METHOD_EXIT: + case ArtJvmtiEvent::kMethodExit: return reinterpret_cast<FnType*>(env->event_callbacks->MethodExit); - case JVMTI_EVENT_NATIVE_METHOD_BIND: + case ArtJvmtiEvent::kNativeMethodBind: return reinterpret_cast<FnType*>(env->event_callbacks->NativeMethodBind); - case JVMTI_EVENT_COMPILED_METHOD_LOAD: + case ArtJvmtiEvent::kCompiledMethodLoad: return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodLoad); - case JVMTI_EVENT_COMPILED_METHOD_UNLOAD: + case ArtJvmtiEvent::kCompiledMethodUnload: return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodUnload); - case JVMTI_EVENT_DYNAMIC_CODE_GENERATED: + case ArtJvmtiEvent::kDynamicCodeGenerated: return reinterpret_cast<FnType*>(env->event_callbacks->DynamicCodeGenerated); - case JVMTI_EVENT_DATA_DUMP_REQUEST: + case ArtJvmtiEvent::kDataDumpRequest: return reinterpret_cast<FnType*>(env->event_callbacks->DataDumpRequest); - case JVMTI_EVENT_MONITOR_WAIT: + case ArtJvmtiEvent::kMonitorWait: return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWait); - case JVMTI_EVENT_MONITOR_WAITED: + case ArtJvmtiEvent::kMonitorWaited: return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWaited); - case JVMTI_EVENT_MONITOR_CONTENDED_ENTER: + case ArtJvmtiEvent::kMonitorContendedEnter: return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEnter); - case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED: + case ArtJvmtiEvent::kMonitorContendedEntered: return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEntered); - case JVMTI_EVENT_RESOURCE_EXHAUSTED: + case ArtJvmtiEvent::kResourceExhausted: return reinterpret_cast<FnType*>(env->event_callbacks->ResourceExhausted); - case JVMTI_EVENT_GARBAGE_COLLECTION_START: + case ArtJvmtiEvent::kGarbageCollectionStart: return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionStart); - case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH: + case ArtJvmtiEvent::kGarbageCollectionFinish: return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionFinish); - case JVMTI_EVENT_OBJECT_FREE: + case ArtJvmtiEvent::kObjectFree: return reinterpret_cast<FnType*>(env->event_callbacks->ObjectFree); - case JVMTI_EVENT_VM_OBJECT_ALLOC: + case ArtJvmtiEvent::kVmObjectAlloc: return reinterpret_cast<FnType*>(env->event_callbacks->VMObjectAlloc); } return nullptr; } template <typename ...Args> -inline void EventHandler::DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args) { +inline void EventHandler::DispatchEvent(art::Thread* thread, + ArtJvmtiEvent event, + Args... args) const { using FnType = void(jvmtiEnv*, Args...); for (ArtJvmTiEnv* env : envs) { - bool dispatch = env->event_masks.global_event_mask.Test(event); - - if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) { - EventMask* mask = env->event_masks.GetEventMaskOrNull(thread); - dispatch = mask != nullptr && mask->Test(event); - } - - if (dispatch) { + if (ShouldDispatch(event, env, thread)) { FnType* callback = GetCallback<FnType>(env, event); if (callback != nullptr) { (*callback)(env, args...); @@ -119,6 +129,52 @@ inline void EventHandler::DispatchEvent(art::Thread* thread, jvmtiEvent event, A } } +inline bool EventHandler::ShouldDispatch(ArtJvmtiEvent event, + ArtJvmTiEnv* env, + art::Thread* thread) { + bool dispatch = env->event_masks.global_event_mask.Test(event); + + if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) { + EventMask* mask = env->event_masks.GetEventMaskOrNull(thread); + dispatch = mask != nullptr && mask->Test(event); + } + return dispatch; +} + +inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { + bool union_value = false; + for (const ArtJvmTiEnv* stored_env : envs) { + union_value |= stored_env->event_masks.global_event_mask.Test(event); + union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event); + if (union_value) { + break; + } + } + global_mask.Set(event, union_value); +} + +inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env, + const jvmtiCapabilities& caps, + bool added) { + ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable + : ArtJvmtiEvent::kClassFileLoadHookRetransformable; + return caps.can_retransform_classes == 1 && + IsEventEnabledAnywhere(event) && + env->event_masks.IsEnabledAnywhere(event); +} + +inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env, + const jvmtiCapabilities& caps, + bool added) { + if (UNLIKELY(NeedsEventUpdate(env, caps, added))) { + env->event_masks.HandleChangedCapabilities(caps, added); + if (caps.can_retransform_classes == 1) { + RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable); + RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable); + } + } +} + } // namespace openjdkjvmti #endif // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_ diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc index 12692a168d..f38aa869d9 100644 --- a/runtime/openjdkjvmti/events.cc +++ b/runtime/openjdkjvmti/events.cc @@ -47,6 +47,10 @@ namespace openjdkjvmti { +bool EventMasks::IsEnabledAnywhere(ArtJvmtiEvent event) { + return global_event_mask.Test(event) || unioned_thread_event_mask.Test(event); +} + EventMask& EventMasks::GetEventMask(art::Thread* thread) { if (thread == nullptr) { return global_event_mask; @@ -83,7 +87,7 @@ EventMask* EventMasks::GetEventMaskOrNull(art::Thread* thread) { } -void EventMasks::EnableEvent(art::Thread* thread, jvmtiEvent event) { +void EventMasks::EnableEvent(art::Thread* thread, ArtJvmtiEvent event) { DCHECK(EventMask::EventIsInRange(event)); GetEventMask(thread).Set(event); if (thread != nullptr) { @@ -91,7 +95,7 @@ void EventMasks::EnableEvent(art::Thread* thread, jvmtiEvent event) { } } -void EventMasks::DisableEvent(art::Thread* thread, jvmtiEvent event) { +void EventMasks::DisableEvent(art::Thread* thread, ArtJvmtiEvent event) { DCHECK(EventMask::EventIsInRange(event)); GetEventMask(thread).Set(event, false); if (thread != nullptr) { @@ -107,20 +111,49 @@ void EventMasks::DisableEvent(art::Thread* thread, jvmtiEvent event) { } } +void EventMasks::HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added) { + if (UNLIKELY(caps.can_retransform_classes == 1)) { + // If we are giving this env the retransform classes cap we need to switch all events of + // NonTransformable to Transformable and vice versa. + ArtJvmtiEvent to_remove = caps_added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable + : ArtJvmtiEvent::kClassFileLoadHookRetransformable; + ArtJvmtiEvent to_add = caps_added ? ArtJvmtiEvent::kClassFileLoadHookRetransformable + : ArtJvmtiEvent::kClassFileLoadHookNonRetransformable; + if (global_event_mask.Test(to_remove)) { + CHECK(!global_event_mask.Test(to_add)); + global_event_mask.Set(to_remove, false); + global_event_mask.Set(to_add, true); + } + + if (unioned_thread_event_mask.Test(to_remove)) { + CHECK(!unioned_thread_event_mask.Test(to_add)); + unioned_thread_event_mask.Set(to_remove, false); + unioned_thread_event_mask.Set(to_add, true); + } + for (auto thread_mask : thread_event_masks) { + if (thread_mask.second.Test(to_remove)) { + CHECK(!thread_mask.second.Test(to_add)); + thread_mask.second.Set(to_remove, false); + thread_mask.second.Set(to_add, true); + } + } + } +} + void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) { envs.push_back(env); } -static bool IsThreadControllable(jvmtiEvent event) { +static bool IsThreadControllable(ArtJvmtiEvent event) { switch (event) { - case JVMTI_EVENT_VM_INIT: - case JVMTI_EVENT_VM_START: - case JVMTI_EVENT_VM_DEATH: - case JVMTI_EVENT_THREAD_START: - case JVMTI_EVENT_COMPILED_METHOD_LOAD: - case JVMTI_EVENT_COMPILED_METHOD_UNLOAD: - case JVMTI_EVENT_DYNAMIC_CODE_GENERATED: - case JVMTI_EVENT_DATA_DUMP_REQUEST: + case ArtJvmtiEvent::kVmInit: + case ArtJvmtiEvent::kVmStart: + case ArtJvmtiEvent::kVmDeath: + case ArtJvmtiEvent::kThreadStart: + case ArtJvmtiEvent::kCompiledMethodLoad: + case ArtJvmtiEvent::kCompiledMethodUnload: + case ArtJvmtiEvent::kDynamicCodeGenerated: + case ArtJvmtiEvent::kDataDumpRequest: return false; default: @@ -136,7 +169,7 @@ class JvmtiAllocationListener : public art::gc::AllocationListener { OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { DCHECK_EQ(self, art::Thread::Current()); - if (handler_->IsEventEnabledAnywhere(JVMTI_EVENT_VM_OBJECT_ALLOC)) { + if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kVmObjectAlloc)) { art::StackHandleScope<1> hs(self); auto h = hs.NewHandleWrapper(obj); // jvmtiEventVMObjectAlloc parameters: @@ -162,7 +195,7 @@ class JvmtiAllocationListener : public art::gc::AllocationListener { jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass())); handler_->DispatchEvent(self, - JVMTI_EVENT_VM_OBJECT_ALLOC, + ArtJvmtiEvent::kVmObjectAlloc, jni_env, thread.get(), object.get(), @@ -196,11 +229,11 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener { finish_enabled_(false) {} void StartPause() OVERRIDE { - handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_START); + handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionStart); } void EndPause() OVERRIDE { - handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH); + handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionFinish); } bool IsEnabled() { @@ -221,10 +254,10 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener { bool finish_enabled_; }; -static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, jvmtiEvent event, bool enable) { +static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, ArtJvmtiEvent event, bool enable) { bool old_state = listener->IsEnabled(); - if (event == JVMTI_EVENT_GARBAGE_COLLECTION_START) { + if (event == ArtJvmtiEvent::kGarbageCollectionStart) { listener->SetStartEnabled(enable); } else { listener->SetFinishEnabled(enable); @@ -242,14 +275,14 @@ static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, jvmtiEvent even } // Handle special work for the given event type, if necessary. -void EventHandler::HandleEventType(jvmtiEvent event, bool enable) { +void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { switch (event) { - case JVMTI_EVENT_VM_OBJECT_ALLOC: + case ArtJvmtiEvent::kVmObjectAlloc: SetupObjectAllocationTracking(alloc_listener_.get(), enable); return; - case JVMTI_EVENT_GARBAGE_COLLECTION_START: - case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH: + case ArtJvmtiEvent::kGarbageCollectionStart: + case ArtJvmtiEvent::kGarbageCollectionFinish: SetupGcPauseTracking(gc_pause_listener_.get(), event, enable); return; @@ -260,7 +293,7 @@ void EventHandler::HandleEventType(jvmtiEvent event, bool enable) { jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env, art::Thread* thread, - jvmtiEvent event, + ArtJvmtiEvent event, jvmtiEventMode mode) { if (thread != nullptr) { art::ThreadState state = thread->GetState(); @@ -293,17 +326,7 @@ jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env, DCHECK_EQ(mode, JVMTI_DISABLE); env->event_masks.DisableEvent(thread, event); - - // Gotta recompute the global mask. - bool union_value = false; - for (const ArtJvmTiEnv* stored_env : envs) { - union_value |= stored_env->event_masks.global_event_mask.Test(event); - union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event); - if (union_value) { - break; - } - } - global_mask.Set(event, union_value); + RecalculateGlobalEventMask(event); } bool new_state = global_mask.Test(event); diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h index 07d6bfd4c2..7990141562 100644 --- a/runtime/openjdkjvmti/events.h +++ b/runtime/openjdkjvmti/events.h @@ -30,22 +30,76 @@ struct ArtJvmTiEnv; class JvmtiAllocationListener; class JvmtiGcPauseListener; +// an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between +// retransformation capable and incapable loading +enum class ArtJvmtiEvent { + kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL, + kVmInit = JVMTI_EVENT_VM_INIT, + kVmDeath = JVMTI_EVENT_VM_DEATH, + kThreadStart = JVMTI_EVENT_THREAD_START, + kThreadEnd = JVMTI_EVENT_THREAD_END, + kClassFileLoadHookNonRetransformable = JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, + kClassLoad = JVMTI_EVENT_CLASS_LOAD, + kClassPrepare = JVMTI_EVENT_CLASS_PREPARE, + kVmStart = JVMTI_EVENT_VM_START, + kException = JVMTI_EVENT_EXCEPTION, + kExceptionCatch = JVMTI_EVENT_EXCEPTION_CATCH, + kSingleStep = JVMTI_EVENT_SINGLE_STEP, + kFramePop = JVMTI_EVENT_FRAME_POP, + kBreakpoint = JVMTI_EVENT_BREAKPOINT, + kFieldAccess = JVMTI_EVENT_FIELD_ACCESS, + kFieldModification = JVMTI_EVENT_FIELD_MODIFICATION, + kMethodEntry = JVMTI_EVENT_METHOD_ENTRY, + kMethodExit = JVMTI_EVENT_METHOD_EXIT, + kNativeMethodBind = JVMTI_EVENT_NATIVE_METHOD_BIND, + kCompiledMethodLoad = JVMTI_EVENT_COMPILED_METHOD_LOAD, + kCompiledMethodUnload = JVMTI_EVENT_COMPILED_METHOD_UNLOAD, + kDynamicCodeGenerated = JVMTI_EVENT_DYNAMIC_CODE_GENERATED, + kDataDumpRequest = JVMTI_EVENT_DATA_DUMP_REQUEST, + kMonitorWait = JVMTI_EVENT_MONITOR_WAIT, + kMonitorWaited = JVMTI_EVENT_MONITOR_WAITED, + kMonitorContendedEnter = JVMTI_EVENT_MONITOR_CONTENDED_ENTER, + kMonitorContendedEntered = JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, + kResourceExhausted = JVMTI_EVENT_RESOURCE_EXHAUSTED, + kGarbageCollectionStart = JVMTI_EVENT_GARBAGE_COLLECTION_START, + kGarbageCollectionFinish = JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, + kObjectFree = JVMTI_EVENT_OBJECT_FREE, + kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC, + kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1, + kMaxEventTypeVal = kClassFileLoadHookRetransformable, +}; + +// Convert a jvmtiEvent into a ArtJvmtiEvent +ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e); + +static inline jvmtiEvent GetJvmtiEvent(ArtJvmtiEvent e) { + if (UNLIKELY(e == ArtJvmtiEvent::kClassFileLoadHookRetransformable)) { + return JVMTI_EVENT_CLASS_FILE_LOAD_HOOK; + } else { + return static_cast<jvmtiEvent>(e); + } +} + struct EventMask { - static constexpr size_t kEventsSize = JVMTI_MAX_EVENT_TYPE_VAL - JVMTI_MIN_EVENT_TYPE_VAL + 1; + static constexpr size_t kEventsSize = + static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal) - + static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal) + 1; std::bitset<kEventsSize> bit_set; - static bool EventIsInRange(jvmtiEvent event) { - return event >= JVMTI_MIN_EVENT_TYPE_VAL && event <= JVMTI_MAX_EVENT_TYPE_VAL; + static bool EventIsInRange(ArtJvmtiEvent event) { + return event >= ArtJvmtiEvent::kMinEventTypeVal && event <= ArtJvmtiEvent::kMaxEventTypeVal; } - void Set(jvmtiEvent event, bool value = true) { + void Set(ArtJvmtiEvent event, bool value = true) { DCHECK(EventIsInRange(event)); - bit_set.set(event - JVMTI_MIN_EVENT_TYPE_VAL, value); + bit_set.set(static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal), + value); } - bool Test(jvmtiEvent event) const { + bool Test(ArtJvmtiEvent event) const { DCHECK(EventIsInRange(event)); - return bit_set.test(event - JVMTI_MIN_EVENT_TYPE_VAL); + return bit_set.test( + static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal)); } }; @@ -68,8 +122,13 @@ struct EventMasks { EventMask& GetEventMask(art::Thread* thread); EventMask* GetEventMaskOrNull(art::Thread* thread); - void EnableEvent(art::Thread* thread, jvmtiEvent event); - void DisableEvent(art::Thread* thread, jvmtiEvent event); + void EnableEvent(art::Thread* thread, ArtJvmtiEvent event); + void DisableEvent(art::Thread* thread, ArtJvmtiEvent event); + bool IsEnabledAnywhere(ArtJvmtiEvent event); + // Make any changes to event masks needed for the given capability changes. If caps_added is true + // then caps is all the newly set capabilities of the jvmtiEnv. If it is false then caps is the + // set of all capabilities that were removed from the jvmtiEnv. + void HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added); }; // Helper class for event handling. @@ -82,20 +141,44 @@ class EventHandler { // enabled, yet. void RegisterArtJvmTiEnv(ArtJvmTiEnv* env); - bool IsEventEnabledAnywhere(jvmtiEvent event) { + bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const { if (!EventMask::EventIsInRange(event)) { return false; } return global_mask.Test(event); } - jvmtiError SetEvent(ArtJvmTiEnv* env, art::Thread* thread, jvmtiEvent event, jvmtiEventMode mode); + jvmtiError SetEvent(ArtJvmTiEnv* env, + art::Thread* thread, + ArtJvmtiEvent event, + jvmtiEventMode mode); template <typename ...Args> - ALWAYS_INLINE inline void DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args); + ALWAYS_INLINE + inline void DispatchEvent(art::Thread* thread, ArtJvmtiEvent event, Args... args) const; + + // Tell the event handler capabilities were added/lost so it can adjust the sent events.If + // caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false + // then caps is the set of all capabilities that were removed from the jvmtiEnv. + ALWAYS_INLINE + inline void HandleChangedCapabilities(ArtJvmTiEnv* env, + const jvmtiCapabilities& caps, + bool added); private: - void HandleEventType(jvmtiEvent event, bool enable); + ALWAYS_INLINE + static inline bool ShouldDispatch(ArtJvmtiEvent event, ArtJvmTiEnv* env, art::Thread* thread); + + ALWAYS_INLINE + inline bool NeedsEventUpdate(ArtJvmTiEnv* env, + const jvmtiCapabilities& caps, + bool added); + + // Recalculates the event mask for the given event. + ALWAYS_INLINE + inline void RecalculateGlobalEventMask(ArtJvmtiEvent event); + + void HandleEventType(ArtJvmtiEvent event, bool enable); // List of all JvmTiEnv objects that have been created, in their creation order. std::vector<ArtJvmTiEnv*> envs; diff --git a/runtime/openjdkjvmti/jvmti.h b/runtime/openjdkjvmti/jvmti.h index ee708cb193..de07c163fc 100644 --- a/runtime/openjdkjvmti/jvmti.h +++ b/runtime/openjdkjvmti/jvmti.h @@ -74,7 +74,7 @@ typedef jobject jthreadGroup; typedef jlong jlocation; struct _jrawMonitorID; typedef struct _jrawMonitorID *jrawMonitorID; -typedef struct JNINativeInterface_ jniNativeInterface; +typedef struct JNINativeInterface jniNativeInterface; /* Constants */ diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc index b983e79658..94cb46a428 100644 --- a/runtime/openjdkjvmti/object_tagging.cc +++ b/runtime/openjdkjvmti/object_tagging.cc @@ -177,7 +177,7 @@ bool ObjectTagTable::SetLocked(art::Thread* self, art::mirror::Object* obj, jlon } void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) { - if (event_handler_->IsEventEnabledAnywhere(JVMTI_EVENT_OBJECT_FREE)) { + if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree)) { SweepImpl<true>(visitor); } else { SweepImpl<false>(visitor); @@ -207,7 +207,7 @@ void ObjectTagTable::SweepImpl(art::IsMarkedVisitor* visitor) { } void ObjectTagTable::HandleNullSweep(jlong tag) { - event_handler_->DispatchEvent(nullptr, JVMTI_EVENT_OBJECT_FREE, tag); + event_handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kObjectFree, tag); } template <typename T, ObjectTagTable::TableUpdateNullTarget kTargetNull> diff --git a/runtime/openjdkjvmti/ti_jni.cc b/runtime/openjdkjvmti/ti_jni.cc new file mode 100644 index 0000000000..88f0395ba5 --- /dev/null +++ b/runtime/openjdkjvmti/ti_jni.cc @@ -0,0 +1,91 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_jni.h" + +#include "jni.h" + +#include "art_jvmti.h" +#include "base/mutex.h" +#include "java_vm_ext.h" +#include "jni_env_ext.h" +#include "runtime.h" +#include "thread-inl.h" + +namespace openjdkjvmti { + +jvmtiError JNIUtil::SetJNIFunctionTable(jvmtiEnv* env ATTRIBUTE_UNUSED, + const jniNativeInterface* function_table) { + // While we supporting setting null (which will reset the table), the spec says no. + if (function_table == nullptr) { + return ERR(NULL_POINTER); + } + + art::JNIEnvExt::SetTableOverride(function_table); + return ERR(NONE); +} + +jvmtiError JNIUtil::GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) { + if (function_table == nullptr) { + return ERR(NULL_POINTER); + } + + // We use the generic JNIEnvExt::GetFunctionTable instead of querying a specific JNIEnv, as + // this has to work in the start phase. + + // Figure out which table is current. Conservatively assume check-jni is off. + bool check_jni = false; + art::Runtime* runtime = art::Runtime::Current(); + if (runtime != nullptr && runtime->GetJavaVM() != nullptr) { + check_jni = runtime->GetJavaVM()->IsCheckJniEnabled(); + } + + // Get that table. + const JNINativeInterface* current_table; + { + art::MutexLock mu(art::Thread::Current(), *art::Locks::jni_function_table_lock_); + current_table = art::JNIEnvExt::GetFunctionTable(check_jni); + } + + // Allocate memory and copy the table. + unsigned char* data; + jvmtiError data_result = env->Allocate(sizeof(JNINativeInterface), &data); + if (data_result != ERR(NONE)) { + return data_result; + } + memcpy(data, current_table, sizeof(JNINativeInterface)); + + *function_table = reinterpret_cast<JNINativeInterface*>(data); + + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_jni.h b/runtime/openjdkjvmti/ti_jni.h new file mode 100644 index 0000000000..906aab0667 --- /dev/null +++ b/runtime/openjdkjvmti/ti_jni.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +// Note: Currently, JNI function table changes are sensitive to the order of operations wrt/ +// CheckJNI. If an agent sets the function table, and a program than late-enables CheckJNI, +// CheckJNI will not be working (as the agent will forward to the non-CheckJNI table). +// +// This behavior results from our usage of the function table to avoid a check of the +// CheckJNI flag. A future implementation may install on loading of this plugin an +// intermediate function table that explicitly checks the flag, so that switching CheckJNI +// is transparently handled. + +class JNIUtil { + public: + static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table); + + static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_ diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc new file mode 100644 index 0000000000..913d2b6a74 --- /dev/null +++ b/runtime/openjdkjvmti/ti_search.cc @@ -0,0 +1,122 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_search.h" + +#include "jni.h" + +#include "art_jvmti.h" +#include "base/macros.h" +#include "class_linker.h" +#include "dex_file.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "ScopedLocalRef.h" + +namespace openjdkjvmti { + +jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_UNUSED, + const char* segment) { + art::Runtime* current = art::Runtime::Current(); + if (current == nullptr) { + return ERR(WRONG_PHASE); + } + if (current->GetClassLinker() == nullptr) { + // TODO: Support boot classpath change in OnLoad. + return ERR(WRONG_PHASE); + } + if (segment == nullptr) { + return ERR(NULL_POINTER); + } + + std::string error_msg; + std::vector<std::unique_ptr<const art::DexFile>> dex_files; + if (!art::DexFile::Open(segment, segment, true, &error_msg, &dex_files)) { + LOG(WARNING) << "Could not open " << segment << " for boot classpath extension: " << error_msg; + return ERR(ILLEGAL_ARGUMENT); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) { + current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), *dex_file.release()); + } + + return ERR(NONE); +} + +jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED, + const char* segment) { + if (segment == nullptr) { + return ERR(NULL_POINTER); + } + + art::Runtime* current = art::Runtime::Current(); + if (current == nullptr) { + return ERR(WRONG_PHASE); + } + jobject sys_class_loader = current->GetSystemClassLoader(); + if (sys_class_loader == nullptr) { + // TODO: Support classpath change in OnLoad. + return ERR(WRONG_PHASE); + } + + // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside, + // exceptions are swallowed. + + art::Thread* self = art::Thread::Current(); + JNIEnv* env = self->GetJniEnv(); + if (!env->IsInstanceOf(sys_class_loader, + art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) { + return ERR(INTERNAL); + } + + jmethodID add_dex_path_id = env->GetMethodID( + art::WellKnownClasses::dalvik_system_BaseDexClassLoader, + "addDexPath", + "(Ljava/lang/String;)V"); + if (add_dex_path_id == nullptr) { + return ERR(INTERNAL); + } + + ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment)); + if (dex_path.get() == nullptr) { + return ERR(INTERNAL); + } + env->CallVoidMethod(sys_class_loader, add_dex_path_id, dex_path.get()); + + if (env->ExceptionCheck()) { + env->ExceptionClear(); + return ERR(ILLEGAL_ARGUMENT); + } + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_search.h b/runtime/openjdkjvmti/ti_search.h new file mode 100644 index 0000000000..6a52e80405 --- /dev/null +++ b/runtime/openjdkjvmti/ti_search.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_ + +#include "jvmti.h" + +namespace openjdkjvmti { + +class SearchUtil { + public: + static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment); + + static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_ diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 4d2450135e..75176f938e 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -227,11 +227,11 @@ class ArgArray { for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) { ObjPtr<mirror::Object> arg(args->Get(args_offset)); if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + // Note: The method's parameter's type must have been previously resolved. ObjPtr<mirror::Class> dst_class( m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_, - true /* resolve */, - pointer_size)); + false /* resolve */)); + DCHECK(dst_class != nullptr) << m->PrettyMethod() << " arg #" << i; if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) { ThrowIllegalArgumentException( StringPrintf("method %s argument %zd has type %s, got %s", @@ -363,12 +363,9 @@ static void CheckMethodArguments(JavaVMExt* vm, ArtMethod* m, uint32_t* args) } // TODO: If args contain object references, it may cause problems. Thread* const self = Thread::Current(); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (uint32_t i = 0; i < num_params; i++) { dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_; - ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, - true /* resolve*/, - pointer_size)); + ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, true /* resolve */)); if (param_type == nullptr) { CHECK(self->IsExceptionPending()); LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: " diff --git a/runtime/thread.cc b/runtime/thread.cc index 40b6d73d94..016a379437 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2632,8 +2632,6 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pAllocObjectResolved) QUICK_ENTRY_POINT_INFO(pAllocObjectInitialized) QUICK_ENTRY_POINT_INFO(pAllocObjectWithChecks) - QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray) - QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck) QUICK_ENTRY_POINT_INFO(pAllocStringFromBytes) QUICK_ENTRY_POINT_INFO(pAllocStringFromChars) QUICK_ENTRY_POINT_INFO(pAllocStringFromString) @@ -2667,10 +2665,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pGet64Static) QUICK_ENTRY_POINT_INFO(pGetObjInstance) QUICK_ENTRY_POINT_INFO(pGetObjStatic) - QUICK_ENTRY_POINT_INFO(pAputObjectWithNullAndBoundCheck) - QUICK_ENTRY_POINT_INFO(pAputObjectWithBoundCheck) QUICK_ENTRY_POINT_INFO(pAputObject) - QUICK_ENTRY_POINT_INFO(pHandleFillArrayData) QUICK_ENTRY_POINT_INFO(pJniMethodStart) QUICK_ENTRY_POINT_INFO(pJniMethodStartSynchronized) QUICK_ENTRY_POINT_INFO(pJniMethodEnd) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 25a179bd32..b915457557 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2901,9 +2901,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { ArtMethod* called_method = VerifyInvocationArgs(inst, type, is_range); const RegType* return_type = nullptr; if (called_method != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_, - pointer_size); + mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { return_type = &FromClass(called_method->GetReturnTypeDescriptor(), return_type_class, @@ -2946,9 +2944,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { is_constructor = called_method->IsConstructor(); return_type_descriptor = called_method->GetReturnTypeDescriptor(); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_, - pointer_size); + mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { return_type = &FromClass(return_type_descriptor, return_type_class, @@ -5133,9 +5129,7 @@ InstructionFlags* MethodVerifier::CurrentInsnFlags() { const RegType& MethodVerifier::GetMethodReturnType() { if (return_type_ == nullptr) { if (mirror_method_ != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_, - pointer_size); + mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(), return_type_class, diff --git a/test/928-jni-table/build b/test/928-jni-table/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/928-jni-table/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 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. + +./default-build "$@" --experimental agents diff --git a/test/928-jni-table/expected.txt b/test/928-jni-table/expected.txt new file mode 100644 index 0000000000..a965a70ed4 --- /dev/null +++ b/test/928-jni-table/expected.txt @@ -0,0 +1 @@ +Done diff --git a/test/928-jni-table/info.txt b/test/928-jni-table/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/928-jni-table/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/928-jni-table/jni_table.cc b/test/928-jni-table/jni_table.cc new file mode 100644 index 0000000000..5123d3a43f --- /dev/null +++ b/test/928-jni-table/jni_table.cc @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 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 <stdio.h> + +#include "jni.h" +#include "openjdkjvmti/jvmti.h" + +#include "base/logging.h" +#include "base/macros.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test927JNITable { + +// This test is equivalent to the jni_internal_test JNIEnvExtTableOverride. + +static size_t gGlobalRefCount = 0; +static JNINativeInterface* gOriginalEnv = nullptr; + +static jobject CountNewGlobalRef(JNIEnv* env, jobject o) { + ++gGlobalRefCount; + return gOriginalEnv->NewGlobalRef(env, o); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_doJNITableTest( + JNIEnv* env, jclass klass) { + // Get the current table, as the delegate. + jvmtiError getorig_result = jvmti_env->GetJNIFunctionTable(&gOriginalEnv); + if (JvmtiErrorToException(env, getorig_result)) { + return; + } + + // Get the current table, as the override we'll install. + JNINativeInterface* env_override; + jvmtiError getoverride_result = jvmti_env->GetJNIFunctionTable(&env_override); + if (JvmtiErrorToException(env, getoverride_result)) { + return; + } + + env_override->NewGlobalRef = CountNewGlobalRef; + gGlobalRefCount = 0; + + // Install the override. + jvmtiError setoverride_result = jvmti_env->SetJNIFunctionTable(env_override); + if (JvmtiErrorToException(env, setoverride_result)) { + return; + } + + jobject global = env->NewGlobalRef(klass); + CHECK_EQ(1u, gGlobalRefCount); + env->DeleteGlobalRef(global); + + // Install the "original." There is no real reset. + jvmtiError setoverride2_result = jvmti_env->SetJNIFunctionTable(gOriginalEnv); + if (JvmtiErrorToException(env, setoverride2_result)) { + return; + } + + jobject global2 = env->NewGlobalRef(klass); + CHECK_EQ(1u, gGlobalRefCount); + env->DeleteGlobalRef(global2); + + // Try to install null. Should return NULL_POINTER error. + jvmtiError setoverride3_result = jvmti_env->SetJNIFunctionTable(nullptr); + if (setoverride3_result != JVMTI_ERROR_NULL_POINTER) { + LOG(FATAL) << "Didn't receive NULL_POINTER"; + } + + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(env_override)); +} + +} // namespace Test927JNITable +} // namespace art diff --git a/test/928-jni-table/run b/test/928-jni-table/run new file mode 100755 index 0000000000..4379349cb2 --- /dev/null +++ b/test/928-jni-table/run @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright 2016 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. + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/928-jni-table/src/Main.java b/test/928-jni-table/src/Main.java new file mode 100644 index 0000000000..b0baea1f9d --- /dev/null +++ b/test/928-jni-table/src/Main.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 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. + */ + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doJNITableTest(); + + System.out.println("Done"); + } + + public static native void doJNITableTest(); +} diff --git a/test/929-search/build b/test/929-search/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/929-search/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 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. + +./default-build "$@" --experimental agents diff --git a/test/929-search/expected.txt b/test/929-search/expected.txt new file mode 100644 index 0000000000..a965a70ed4 --- /dev/null +++ b/test/929-search/expected.txt @@ -0,0 +1 @@ +Done diff --git a/test/929-search/info.txt b/test/929-search/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/929-search/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/929-search/run b/test/929-search/run new file mode 100755 index 0000000000..0a8d0672f6 --- /dev/null +++ b/test/929-search/run @@ -0,0 +1,23 @@ +#!/bin/bash +# +# Copyright 2016 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. + +# This test checks whether dex files can be injected into parent classloaders. App images preload +# classes, which will make the injection moot. Turn off app images to avoid the issue. + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti \ + --no-app-image diff --git a/test/929-search/search.cc b/test/929-search/search.cc new file mode 100644 index 0000000000..d1c698491e --- /dev/null +++ b/test/929-search/search.cc @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 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 <inttypes.h> + +#include "android-base/stringprintf.h" +#include "base/logging.h" +#include "base/macros.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" +#include "ScopedUtfChars.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test929Search { + +extern "C" JNIEXPORT void JNICALL Java_Main_addToBootClassLoader( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) { + ScopedUtfChars utf(env, segment); + if (utf.c_str() == nullptr) { + return; + } + jvmtiError result = jvmti_env->AddToBootstrapClassLoaderSearch(utf.c_str()); + JvmtiErrorToException(env, result); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_addToSystemClassLoader( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) { + ScopedUtfChars utf(env, segment); + if (utf.c_str() == nullptr) { + return; + } + jvmtiError result = jvmti_env->AddToSystemClassLoaderSearch(utf.c_str()); + JvmtiErrorToException(env, result); +} + +} // namespace Test929Search +} // namespace art diff --git a/test/929-search/src-ex/A.java b/test/929-search/src-ex/A.java new file mode 100644 index 0000000000..64acb2fcfe --- /dev/null +++ b/test/929-search/src-ex/A.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 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. + */ + +public class A { +}
\ No newline at end of file diff --git a/test/929-search/src/B.java b/test/929-search/src/B.java new file mode 100644 index 0000000000..f1458c3bca --- /dev/null +++ b/test/929-search/src/B.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 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. + */ + +public class B { +}
\ No newline at end of file diff --git a/test/929-search/src/Main.java b/test/929-search/src/Main.java new file mode 100644 index 0000000000..d253e6fdf6 --- /dev/null +++ b/test/929-search/src/Main.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 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. + */ + +import java.util.Arrays; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doTest(); + } + + private static void doTest() throws Exception { + doTest(true, DEX1, "B"); + doTest(false, DEX2, "A"); + System.out.println("Done"); + } + + private static void doTest(boolean boot, String segment, String className) throws Exception { + ClassLoader expectedClassLoader; + if (boot) { + expectedClassLoader = Object.class.getClassLoader(); + addToBootClassLoader(segment); + } else { + expectedClassLoader = ClassLoader.getSystemClassLoader(); + addToSystemClassLoader(segment); + } + + Class<?> c = Class.forName(className); + if (c.getClassLoader() != expectedClassLoader) { + throw new RuntimeException(className + "(" + boot + "/" + segment + "): " + + c.getClassLoader() + " vs " + expectedClassLoader); + } + } + + private static native void addToBootClassLoader(String s); + private static native void addToSystemClassLoader(String s); + + private static final String DEX1 = System.getenv("DEX_LOCATION") + "/929-search.jar"; + private static final String DEX2 = System.getenv("DEX_LOCATION") + "/929-search-ex.jar"; +} diff --git a/test/Android.bp b/test/Android.bp index c551b9de45..965d07aa43 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -267,6 +267,8 @@ art_cc_defaults { "924-threads/threads.cc", "925-threadgroups/threadgroups.cc", "927-timers/timers.cc", + "928-jni-table/jni_table.cc", + "929-search/search.cc", ], shared_libs: [ "libbase", diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 11d069a395..e604c93c72 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -280,6 +280,7 @@ TEST_ART_BROKEN_TARGET_TESTS := \ # These 9** tests are not supported in current form due to linker # restrictions. See b/31681198 TEST_ART_BROKEN_TARGET_TESTS += \ + 901-hello-ti-agent \ 902-hello-transformation \ 903-hello-tagging \ 904-object-allocation \ @@ -306,6 +307,8 @@ TEST_ART_BROKEN_TARGET_TESTS += \ 925-threadgroups \ 926-multi-obsolescence \ 927-timers \ + 928-jni-table \ + 929-search \ ifneq (,$(filter target,$(TARGET_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ @@ -466,10 +469,12 @@ TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \ 629-vdex-speed # This test fails without an image. -# 964 often times out due to the large number of classes it tries to compile. +# 018, 961, 964 often time out. b/34369284 TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \ 137-cfi \ 138-duplicate-classes-check \ + 018-stack-overflow \ + 961-default-iface-resolution-gen \ 964-default-iface-init ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES))) |