diff options
75 files changed, 1167 insertions, 243 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index be040a98da..3daaf0156e 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -483,18 +483,21 @@ maybe_chroot_command := chroot $(ART_TEST_CHROOT) endif # File witnessing the success of the gtest, the presence of which means the gtest's success. -gtest_witness := $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID +gtest_witness := \ + $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$(gtest_rule)-$$$$PPID + +$$(gtest_rule): GTEST_WITNESS := $$(gtest_witness) .PHONY: $$(gtest_rule) $$(gtest_rule): test-art-target-sync - $(hide) adb shell touch $(gtest_witness) - $(hide) adb shell rm $(gtest_witness) + $(hide) adb shell touch $$(GTEST_WITNESS) + $(hide) adb shell rm $$(GTEST_WITNESS) $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) $(hide) $$(call ART_TEST_SKIP,$$@) && \ (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) $$(PRIVATE_TARGET_EXE) \ - && touch $(gtest_witness)" \ - && (adb pull $(gtest_witness) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ + && touch $$(GTEST_WITNESS)" \ + && (adb pull $$(GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) $(hide) rm -f /tmp/$$@-$$$$PPID @@ -502,20 +505,27 @@ $$(gtest_rule): test-art-target-sync ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule) ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule) +# File witnessing the success of the Valgrind gtest, the presence of which means the gtest's +# success. +valgrind_gtest_witness := \ + $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/valgrind-$$(gtest_rule)-$$$$PPID + +valgrind-$$(gtest_rule): VALGRIND_GTEST_WITNESS := $$(valgrind_gtest_witness) + .PHONY: valgrind-$$(gtest_rule) valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync - $(hide) adb shell touch $(gtest_witness) - $(hide) adb shell rm $(gtest_witness) + $(hide) adb shell touch $$(VALGRIND_GTEST_WITNESS) + $(hide) adb shell rm $$(VALGRIND_GTEST_WITNESS) $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) $(hide) $$(call ART_TEST_SKIP,$$@) && \ (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \ - $$$$ANDROID_ROOT/bin/valgrind \ + $(ART_GTEST_TARGET_ANDROID_ROOT)/bin/valgrind \ --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \ --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \ --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \ - && touch $(gtest_witness)" \ - && (adb pull $(gtest_witness) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ + && touch $$(VALGRIND_GTEST_WITNESS)" \ + && (adb pull $$(VALGRIND_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) $(hide) rm -f /tmp/$$@-$$$$PPID @@ -525,6 +535,7 @@ valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-syn ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) # Clear locally defined variables. + valgrind_gtest_witness := gtest_witness := maybe_chroot_command := maybe_art_test_chroot := diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index a88c5cb213..fd132f4ac4 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -41,23 +41,23 @@ static void XzCompress(const std::vector<uint8_t>* src, std::vector<uint8_t>* ds Lzma2EncProps_Normalize(&lzma2Props); CXzProps props; XzProps_Init(&props); - props.lzma2Props = &lzma2Props; + props.lzma2Props = lzma2Props; // Implement the required interface for communication (written in C so no virtual methods). struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress { - static SRes ReadImpl(void* p, void* buf, size_t* size) { - auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqInStream*>(p)); + static SRes ReadImpl(const ISeqInStream* p, void* buf, size_t* size) { + auto* ctx = static_cast<XzCallbacks*>(const_cast<ISeqInStream*>(p)); *size = std::min(*size, ctx->src_->size() - ctx->src_pos_); memcpy(buf, ctx->src_->data() + ctx->src_pos_, *size); ctx->src_pos_ += *size; return SZ_OK; } - static size_t WriteImpl(void* p, const void* buf, size_t size) { - auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqOutStream*>(p)); + static size_t WriteImpl(const ISeqOutStream* p, const void* buf, size_t size) { + auto* ctx = static_cast<const XzCallbacks*>(p); const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf); ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size); return size; } - static SRes ProgressImpl(void* , UInt64, UInt64) { + static SRes ProgressImpl(const ICompressProgress* , UInt64, UInt64) { return SZ_OK; } size_t src_pos_; @@ -113,4 +113,3 @@ static std::vector<uint8_t> MakeMiniDebugInfoInternal( } // namespace art #endif // ART_COMPILER_DEBUG_ELF_GNU_DEBUGDATA_WRITER_H_ - diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 231017f55e..f57333741c 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -736,6 +736,46 @@ void CodeGenerator::GenerateLoadClassRuntimeCall(HLoadClass* cls) { } } +void CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary( + HLoadMethodHandle* method_handle, + Location runtime_proto_index_location, + Location runtime_return_location) { + DCHECK_EQ(method_handle->InputCount(), 1u); + LocationSummary* locations = + new (method_handle->GetBlock()->GetGraph()->GetAllocator()) LocationSummary( + method_handle, LocationSummary::kCallOnMainOnly); + locations->SetInAt(0, Location::NoLocation()); + locations->AddTemp(runtime_proto_index_location); + locations->SetOut(runtime_return_location); +} + +void CodeGenerator::GenerateLoadMethodHandleRuntimeCall(HLoadMethodHandle* method_handle) { + LocationSummary* locations = method_handle->GetLocations(); + MoveConstant(locations->GetTemp(0), method_handle->GetMethodHandleIndex()); + CheckEntrypointTypes<kQuickResolveMethodHandle, void*, uint32_t>(); + InvokeRuntime(kQuickResolveMethodHandle, method_handle, method_handle->GetDexPc()); +} + +void CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary( + HLoadMethodType* method_type, + Location runtime_proto_index_location, + Location runtime_return_location) { + DCHECK_EQ(method_type->InputCount(), 1u); + LocationSummary* locations = + new (method_type->GetBlock()->GetGraph()->GetAllocator()) LocationSummary( + method_type, LocationSummary::kCallOnMainOnly); + locations->SetInAt(0, Location::NoLocation()); + locations->AddTemp(runtime_proto_index_location); + locations->SetOut(runtime_return_location); +} + +void CodeGenerator::GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type) { + LocationSummary* locations = method_type->GetLocations(); + MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex()); + CheckEntrypointTypes<kQuickResolveMethodType, void*, uint32_t>(); + InvokeRuntime(kQuickResolveMethodType, method_type, method_type->GetDexPc()); +} + static uint32_t GetBootImageOffsetImpl(const void* object, ImageHeader::ImageSections section) { Runtime* runtime = Runtime::Current(); DCHECK(runtime->IsAotCompiler()); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index f0c4ee01cc..bcb25997f4 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -564,6 +564,16 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { Location runtime_return_location); void GenerateLoadClassRuntimeCall(HLoadClass* cls); + static void CreateLoadMethodHandleRuntimeCallLocationSummary(HLoadMethodHandle* method_handle, + Location runtime_handle_index_location, + Location runtime_return_location); + void GenerateLoadMethodHandleRuntimeCall(HLoadMethodHandle* method_handle); + + static void CreateLoadMethodTypeRuntimeCallLocationSummary(HLoadMethodType* method_type, + Location runtime_type_index_location, + Location runtime_return_location); + void GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type); + uint32_t GetBootImageOffset(HLoadClass* load_class); uint32_t GetBootImageOffset(HLoadString* load_string); uint32_t GetBootImageOffset(HInvokeStaticOrDirect* invoke); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index d4cfab82de..6f173e19f5 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -5144,6 +5144,26 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA } } +void LocationsBuilderARM64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARM64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderARM64::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARM64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static MemOperand GetExceptionTlsAddress() { return MemOperand(tr, Thread::ExceptionOffset<kArm64PointerSize>().Int32Value()); } diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 58ce9aa9f0..859e1597c6 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7527,6 +7527,26 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ } } +void LocationsBuilderARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARMVIXL::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 25e2eddbfa..7f3441fdf4 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -8226,6 +8226,26 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF } } +void LocationsBuilderMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderMIPS::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static int32_t GetExceptionTlsOffset() { return Thread::ExceptionOffset<kMipsPointerSize>().Int32Value(); } diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 5b07b55cbb..ee32b96daf 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -6262,6 +6262,26 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S } } +void LocationsBuilderMIPS64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderMIPS64::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static int32_t GetExceptionTlsOffset() { return Thread::ExceptionOffset<kMips64PointerSize>().Int32Value(); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 82d1fda878..9e315381b1 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6539,6 +6539,26 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE } } +void LocationsBuilderX86::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderX86::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void LocationsBuilderX86::VisitClinitCheck(HClinitCheck* check) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 322b0cfc4c..f7397046d7 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5908,6 +5908,26 @@ void LocationsBuilderX86_64::VisitClinitCheck(HClinitCheck* check) { } } +void LocationsBuilderX86_64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + // Custom calling convention: RAX serves as both input and output. + Location location = Location::RegisterLocation(RAX); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86_64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderX86_64::VisitLoadMethodType(HLoadMethodType* load) { + // Custom calling convention: RAX serves as both input and output. + Location location = Location::RegisterLocation(RAX); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86_64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void InstructionCodeGeneratorX86_64::VisitClinitCheck(HClinitCheck* check) { // We assume the class to not be null. SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathX86_64( diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 54d4644580..d65ad40565 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -386,6 +386,18 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << load_class->NeedsAccessCheck() << std::noboolalpha; } + void VisitLoadMethodHandle(HLoadMethodHandle* load_method_handle) OVERRIDE { + StartAttributeStream("load_kind") << "RuntimeCall"; + StartAttributeStream("method_handle_index") << load_method_handle->GetMethodHandleIndex(); + } + + void VisitLoadMethodType(HLoadMethodType* load_method_type) OVERRIDE { + StartAttributeStream("load_kind") << "RuntimeCall"; + const DexFile& dex_file = load_method_type->GetDexFile(); + const DexFile::ProtoId& proto_id = dex_file.GetProtoId(load_method_type->GetProtoIndex()); + StartAttributeStream("method_type") << dex_file.GetProtoSignature(proto_id); + } + void VisitLoadString(HLoadString* load_string) OVERRIDE { StartAttributeStream("load_kind") << load_string->GetLoadKind(); } diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 9647dd5d41..35a39456a2 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1896,6 +1896,20 @@ bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) } } +void HInstructionBuilder::BuildLoadMethodHandle(uint16_t proto_idx, uint32_t dex_pc) { + const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); + HLoadMethodHandle* load_method_handle = + new (allocator_) HLoadMethodHandle(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc); + AppendInstruction(load_method_handle); +} + +void HInstructionBuilder::BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc) { + const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); + HLoadMethodType* load_method_type = + new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc); + AppendInstruction(load_method_type); +} + void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, @@ -2927,6 +2941,20 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, break; } + case Instruction::CONST_METHOD_HANDLE: { + uint16_t method_handle_idx = instruction.VRegB_21c(); + BuildLoadMethodHandle(method_handle_idx, dex_pc); + UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); + break; + } + + case Instruction::CONST_METHOD_TYPE: { + uint16_t proto_idx = instruction.VRegB_21c(); + BuildLoadMethodType(proto_idx, dex_pc); + UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); + break; + } + case Instruction::MOVE_EXCEPTION: { AppendInstruction(new (allocator_) HLoadException(dex_pc)); UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction()); diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index f78829232d..95ffa6b054 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -45,6 +45,7 @@ class VariableSizedHandleScope; namespace mirror { class Class; +class MethodType; } // namespace mirror class HInstructionBuilder : public ValueObject { @@ -239,6 +240,12 @@ class HInstructionBuilder : public ValueObject { bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); + // Builds a `HLoadMethodHandle` loading the given `method_handle_idx`. + void BuildLoadMethodHandle(uint16_t method_handle_idx, uint32_t dex_pc); + + // Builds a `HLoadMethodType` loading the given `proto_idx`. + void BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc); + // Returns the outer-most compiling method's class. ObjPtr<mirror::Class> GetOutermostCompilingClass() const; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 9dcd741388..a7c2d0b125 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -41,6 +41,7 @@ #include "intrinsics_enum.h" #include "locations.h" #include "mirror/class.h" +#include "mirror/method_type.h" #include "offsets.h" #include "utils/intrusive_forward_list.h" @@ -1382,6 +1383,8 @@ class HLoopInformationOutwardIterator : public ValueObject { M(LessThanOrEqual, Condition) \ M(LoadClass, Instruction) \ M(LoadException, Instruction) \ + M(LoadMethodHandle, Instruction) \ + M(LoadMethodType, Instruction) \ M(LoadString, Instruction) \ M(LongConstant, Constant) \ M(Max, Instruction) \ @@ -6498,6 +6501,94 @@ inline void HLoadString::AddSpecialInput(HInstruction* special_input) { special_input->AddUseAt(this, 0); } +class HLoadMethodHandle FINAL : public HInstruction { + public: + HLoadMethodHandle(HCurrentMethod* current_method, + uint16_t method_handle_idx, + const DexFile& dex_file, + uint32_t dex_pc) + : HInstruction(kLoadMethodHandle, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), + special_input_(HUserRecord<HInstruction*>(current_method)), + method_handle_idx_(method_handle_idx), + dex_file_(dex_file) { + } + + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { + return ArrayRef<HUserRecord<HInstruction*>>( + &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); + } + + bool IsClonable() const OVERRIDE { return true; } + + uint16_t GetMethodHandleIndex() const { return method_handle_idx_; } + + const DexFile& GetDexFile() const { return dex_file_; } + + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + + DECLARE_INSTRUCTION(LoadMethodHandle); + + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadMethodHandle); + + private: + // The special input is the HCurrentMethod for kRuntimeCall. + HUserRecord<HInstruction*> special_input_; + + const uint16_t method_handle_idx_; + const DexFile& dex_file_; +}; + +class HLoadMethodType FINAL : public HInstruction { + public: + HLoadMethodType(HCurrentMethod* current_method, + uint16_t proto_idx, + const DexFile& dex_file, + uint32_t dex_pc) + : HInstruction(kLoadMethodType, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), + special_input_(HUserRecord<HInstruction*>(current_method)), + proto_idx_(proto_idx), + dex_file_(dex_file) { + } + + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { + return ArrayRef<HUserRecord<HInstruction*>>( + &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); + } + + bool IsClonable() const OVERRIDE { return true; } + + uint16_t GetProtoIndex() const { return proto_idx_; } + + const DexFile& GetDexFile() const { return dex_file_; } + + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + + DECLARE_INSTRUCTION(LoadMethodType); + + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadMethodType); + + private: + // The special input is the HCurrentMethod for kRuntimeCall. + HUserRecord<HInstruction*> special_input_; + + const uint16_t proto_idx_; + const DexFile& dex_file_; +}; + /** * Performs an initialization check on its Class object input. */ diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index c47c69af67..ecfa790b91 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -59,6 +59,18 @@ ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetClassCla return GetRootHandle(handles_, ClassLinker::kJavaLangClass, &class_class_handle_); } +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodHandleClassHandle() { + return GetRootHandle(handles_, + ClassLinker::kJavaLangInvokeMethodHandleImpl, + &method_handle_class_handle_); +} + +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodTypeClassHandle() { + return GetRootHandle(handles_, + ClassLinker::kJavaLangInvokeMethodType, + &method_type_class_handle_); +} + ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetStringClassHandle() { return GetRootHandle(handles_, ClassLinker::kJavaLangString, &string_class_handle_); } @@ -89,6 +101,8 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitLoadClass(HLoadClass* load_class) OVERRIDE; void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE; void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE; + void VisitLoadMethodHandle(HLoadMethodHandle* instr) OVERRIDE; + void VisitLoadMethodType(HLoadMethodType* instr) OVERRIDE; void VisitLoadString(HLoadString* instr) OVERRIDE; void VisitLoadException(HLoadException* instr) OVERRIDE; void VisitNewArray(HNewArray* instr) OVERRIDE; @@ -668,6 +682,17 @@ void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo()); } +void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodHandle(HLoadMethodHandle* instr) { + instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create( + handle_cache_->GetMethodHandleClassHandle(), + /* is_exact */ true)); +} + +void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodType(HLoadMethodType* instr) { + instr->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(handle_cache_->GetMethodTypeClassHandle(), /* is_exact */ true)); +} + void ReferenceTypePropagation::RTPVisitor::VisitLoadString(HLoadString* instr) { instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact */ true)); diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 400852f4dc..d36d592708 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -75,6 +75,8 @@ class ReferenceTypePropagation : public HOptimization { ReferenceTypeInfo::TypeHandle GetObjectClassHandle(); ReferenceTypeInfo::TypeHandle GetClassClassHandle(); + ReferenceTypeInfo::TypeHandle GetMethodHandleClassHandle(); + ReferenceTypeInfo::TypeHandle GetMethodTypeClassHandle(); ReferenceTypeInfo::TypeHandle GetStringClassHandle(); ReferenceTypeInfo::TypeHandle GetThrowableClassHandle(); @@ -83,6 +85,8 @@ class ReferenceTypePropagation : public HOptimization { ReferenceTypeInfo::TypeHandle object_class_handle_; ReferenceTypeInfo::TypeHandle class_class_handle_; + ReferenceTypeInfo::TypeHandle method_handle_class_handle_; + ReferenceTypeInfo::TypeHandle method_type_class_handle_; ReferenceTypeInfo::TypeHandle string_class_handle_; ReferenceTypeInfo::TypeHandle throwable_class_handle_; }; diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 674dc9a78b..19c405e517 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -153,7 +153,7 @@ const char* const VixlJniHelpersResults[] = { " 21c: f8d9 8034 ldr.w r8, [r9, #52] ; 0x34\n", " 220: 4770 bx lr\n", " 222: 4660 mov r0, ip\n", - " 224: f8d9 c2c4 ldr.w ip, [r9, #708] ; 0x2c4\n", + " 224: f8d9 c2cc ldr.w ip, [r9, #716] ; 0x2cc\n", " 228: 47e0 blx ip\n", nullptr }; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 63518be15f..6b65aca943 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1257,10 +1257,10 @@ class Dex2Oat FINAL { if (stored_class_loader_context_ == nullptr) { Usage("Option --stored-class-loader-context has an incorrect format: %s", stored_context_arg.c_str()); - } else if (!class_loader_context_->VerifyClassLoaderContextMatch( + } else if (class_loader_context_->VerifyClassLoaderContextMatch( stored_context_arg, /*verify_names*/ false, - /*verify_checksums*/ false)) { + /*verify_checksums*/ false) != ClassLoaderContext::VerificationResult::kVerifies) { Usage( "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'", stored_context_arg.c_str(), diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 1699153e7e..df0641cc7f 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -497,7 +497,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(76U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(24U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(162 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index 5285c08cc2..b009774582 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -27,7 +27,6 @@ art_cc_defaults { ], export_include_dirs: ["."], shared_libs: [ - "libartbase", "libbase", ], static_libs: ["libz"], @@ -40,6 +39,7 @@ art_cc_library { "dex2oat-pgo-defaults", ], shared_libs: [ + "libart", "libdexfile", "libprofile", ], @@ -60,6 +60,7 @@ art_cc_library { "art_debug_defaults", ], shared_libs: [ + "libartd", "libdexfiled", "libprofiled", ], @@ -79,9 +80,8 @@ art_cc_binary { name: "dexlayout", defaults: ["dexlayout-defaults"], shared_libs: [ - "libartbase", - "libdexfile", "libprofile", + "libart", "libart-dexlayout", ], } @@ -93,9 +93,8 @@ art_cc_binary { "dexlayout-defaults", ], shared_libs: [ - "libartbased", - "libdexfiled", "libprofiled", + "libartd", "libartd-dexlayout", ], } diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 03dfee319e..62dd1a9554 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -36,8 +36,8 @@ #include "base/logging.h" // For VLOG_IS_ON. #include "base/mem_map.h" #include "base/os.h" -#include "base/unix_file/fd_file.h" #include "base/utils.h" +#include "dex/art_dex_file_loader.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_layout.h" @@ -1930,7 +1930,7 @@ bool DexLayout::ProcessDexFile(const char* file_name, std::string location = "memory mapped file for " + std::string(file_name); // Dex file verifier cannot handle compact dex. bool verify = options_.compact_dex_level_ == CompactDexLevel::kCompactDexLevelNone; - const DexFileLoader dex_file_loader; + const ArtDexFileLoader dex_file_loader; DexContainer::Section* const main_section = (*dex_container)->GetMainSection(); DexContainer::Section* const data_section = (*dex_container)->GetDataSection(); DCHECK_EQ(file_size, main_section->Size()) @@ -1980,32 +1980,10 @@ int DexLayout::ProcessFile(const char* file_name) { // all of which are Zip archives with "classes.dex" inside. const bool verify_checksum = !options_.ignore_bad_checksum_; std::string error_msg; - std::unique_ptr<File> input_file(OS::OpenFileForReading(file_name)); - if (input_file == nullptr) { - LOG(ERROR) << "Could not open file " << file_name << " for reading"; - return -1; - } - std::unique_ptr<MemMap> mmap(MemMap::MapFile(input_file->GetLength(), - PROT_READ, - MAP_PRIVATE, - input_file->Fd(), - /*start*/0, - /*low_4gb*/false, - file_name, - &error_msg)); - if (mmap == nullptr) { - LOG(ERROR) << "MemMap failed for '" << file_name << "' " << error_msg; - return -1; - } - const DexFileLoader dex_file_loader; + const ArtDexFileLoader dex_file_loader; std::vector<std::unique_ptr<const DexFile>> dex_files; - if (!dex_file_loader.OpenAll(mmap->Begin(), - mmap->Size(), - file_name, - /*verify*/true, - verify_checksum, - &error_msg, - &dex_files)) { + if (!dex_file_loader.Open( + file_name, file_name, /* verify */ true, verify_checksum, &error_msg, &dex_files)) { // Display returned error message to user. Note that this error behavior // differs from the error messages shown by the original Dalvik dexdump. LOG(ERROR) << error_msg; diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index 3f92d501f6..185c1420ab 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -34,6 +34,7 @@ #include "base/logging.h" // For InitLogging. #include "base/mem_map.h" #include "profile/profile_compilation_info.h" +#include "runtime.h" namespace art { @@ -65,17 +66,12 @@ static void Usage(void) { LOG(ERROR) << " -x : compact dex generation level, either 'none' or 'fast'"; } -NO_RETURN static void Abort(const char* msg) { - LOG(ERROR) << "Aborted: " << msg; - exit(1); -} - /* * Main driver of the dexlayout utility. */ int DexlayoutDriver(int argc, char** argv) { // Art specific set up. - InitLogging(argv, Abort); + InitLogging(argv, Runtime::Abort); MemMap::Init(); Options options; diff --git a/libdexfile/dex/dex_file_loader.cc b/libdexfile/dex/dex_file_loader.cc index 1e0f5ac6ae..457addf114 100644 --- a/libdexfile/dex/dex_file_loader.cc +++ b/libdexfile/dex/dex_file_loader.cc @@ -191,6 +191,8 @@ std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) { std::string base_location = GetBaseLocation(dex_location); const char* suffix = dex_location + base_location.size(); DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator); + // Warning: Bionic implementation of realpath() allocates > 12KB on the stack. + // Do not run this code on a small stack, e.g. in signal handler. UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr)); if (path != nullptr && path.get() != base_location) { return std::string(path.get()) + suffix; diff --git a/libdexfile/dex/dex_file_loader.h b/libdexfile/dex/dex_file_loader.h index 28cdfc13ce..01532203eb 100644 --- a/libdexfile/dex/dex_file_loader.h +++ b/libdexfile/dex/dex_file_loader.h @@ -71,7 +71,7 @@ class DexFileLoader { // of the dex file. In the second case (oat) it will include the file name // and possibly some multidex annotation to uniquely identify it. // canonical_dex_location: - // the dex_location where it's file name part has been made canonical. + // the dex_location where its file name part has been made canonical. static std::string GetDexCanonicalLocation(const char* dex_location); // For normal dex files, location and base location coincide. If a dex file is part of a multidex diff --git a/libprofile/Android.bp b/libprofile/Android.bp index 5afe73b353..bcb90cb680 100644 --- a/libprofile/Android.bp +++ b/libprofile/Android.bp @@ -40,6 +40,7 @@ cc_defaults { ], }, }, + //generated_sources: ["art_libartbase_operator_srcs"], cflags: ["-DBUILDING_LIBART=1"], shared_libs: [ "libartbase", diff --git a/profman/Android.bp b/profman/Android.bp index c9c92e6685..3c8c72c34a 100644 --- a/profman/Android.bp +++ b/profman/Android.bp @@ -39,7 +39,7 @@ art_cc_binary { name: "profman", defaults: ["profman-defaults"], shared_libs: [ - "libartbase", + "libart", "libprofile", "libdexfile", ], @@ -52,7 +52,7 @@ art_cc_binary { "profman-defaults", ], shared_libs: [ - "libartbased", + "libartd", "libprofiled", "libdexfiled", ], diff --git a/profman/profman.cc b/profman/profman.cc index c16fadd828..cd88d03929 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -18,7 +18,6 @@ #include <stdio.h> #include <stdlib.h> #include <sys/file.h> -#include <sys/mman.h> #include <sys/param.h> #include <unistd.h> @@ -41,6 +40,7 @@ #include "base/utils.h" #include "base/zip_archive.h" #include "boot_image_profile.h" +#include "dex/art_dex_file_loader.h" #include "dex/bytecode_utils.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file.h" @@ -49,6 +49,7 @@ #include "dex/type_reference.h" #include "profile/profile_compilation_info.h" #include "profile_assistant.h" +#include "runtime.h" namespace art { @@ -176,11 +177,6 @@ static constexpr char kMethodFlagStringHot = 'H'; static constexpr char kMethodFlagStringStartup = 'S'; static constexpr char kMethodFlagStringPostStartup = 'P'; -NO_RETURN static void Abort(const char* msg) { - LOG(ERROR) << "Aborted: " << msg; - exit(1); -} - // TODO(calin): This class has grown too much from its initial design. Split the functionality // into smaller, more contained pieces. class ProfMan FINAL { @@ -206,8 +202,8 @@ class ProfMan FINAL { original_argc = argc; original_argv = argv; - MemMap::Init(); - InitLogging(argv, Abort); + Locks::Init(); + InitLogging(argv, Runtime::Abort); // Skip over the command name. argv++; @@ -417,49 +413,36 @@ class ProfMan FINAL { } static constexpr bool kVerifyChecksum = true; for (size_t i = 0; i < dex_locations_.size(); ++i) { - std::unique_ptr<File> apk_file; + std::string error_msg; + const ArtDexFileLoader dex_file_loader; + std::vector<std::unique_ptr<const DexFile>> dex_files_for_location; // We do not need to verify the apk for processing profiles. if (use_apk_fd_list) { - apk_file.reset(new File(apks_fd_[i], false/*checkUsage*/)); + if (dex_file_loader.OpenZip(apks_fd_[i], + dex_locations_[i], + /* verify */ false, + kVerifyChecksum, + &error_msg, + &dex_files_for_location)) { + } else { + LOG(ERROR) << "OpenZip failed for '" << dex_locations_[i] << "' " << error_msg; + return false; + } } else { - apk_file.reset(new File(apk_files_[i], O_RDONLY, false/*checkUsage*/)); - if (apk_file == nullptr) { - LOG(ERROR) << "Open failed for '" << dex_locations_[i] << "' "; + if (dex_file_loader.Open(apk_files_[i].c_str(), + dex_locations_[i], + /* verify */ false, + kVerifyChecksum, + &error_msg, + &dex_files_for_location)) { + } else { + LOG(ERROR) << "Open failed for '" << dex_locations_[i] << "' " << error_msg; return false; } } - std::string error_msg; - std::unique_ptr<MemMap> mmap(MemMap::MapFile(apk_file->GetLength(), - PROT_READ, - MAP_PRIVATE, - apk_file->Fd(), - /*start*/0, - /*low_4gb*/false, - dex_locations_[i].c_str(), - &error_msg)); - if (mmap == nullptr) { - LOG(ERROR) << "MemMap failed for '" << dex_locations_[i] << "' " << error_msg; - return false; - } - const DexFileLoader dex_file_loader; - std::vector<std::unique_ptr<const DexFile>> dex_files_for_location; - if (!dex_file_loader.OpenAll(mmap->Begin(), - mmap->Size(), - dex_locations_[i], - /* verify */ false, - kVerifyChecksum, - &error_msg, - &dex_files_for_location)) { - LOG(ERROR) << "OpenAll failed for '" << dex_locations_[i] << "' " << error_msg; - return false; - } for (std::unique_ptr<const DexFile>& dex_file : dex_files_for_location) { process_fn(std::move(dex_file)); } - // Leak apk_file and mmap for now. - // TODO: close fds, etc. - apk_file.release(); - mmap.release(); } return true; } diff --git a/runtime/Android.bp b/runtime/Android.bp index 116453b1bf..64e6796ba0 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -422,6 +422,7 @@ gensrcs { srcs: [ "arch/instruction_set.h", "base/mutex.h", + "class_loader_context.h", "class_status.h", "debugger.h", "gc_root.h", diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc index 801254fd30..608999b3bf 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -46,9 +46,11 @@ ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant( "cortex-a53", "cortex-a53.a57", "cortex-a53.a72", + "cortex-a55", "cortex-a57", "cortex-a72", "cortex-a73", + "cortex-a75", "exynos-m1", "denver", "kryo" diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index a930cc494e..cd00125de5 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1015,7 +1015,10 @@ ENTRY \name END \name .endm -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY \name @@ -1040,6 +1043,8 @@ END \name ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode // Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc index 42c9a846d0..d0f61c946c 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64.cc @@ -52,6 +52,8 @@ Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant( // Check to see if this is an expected variant. static const char* arm64_known_variants[] = { "cortex-a35", + "cortex-a55", + "cortex-a75", "exynos-m1", "exynos-m2", "exynos-m3", diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc index 7fd39b6b1b..b946f4f637 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc @@ -64,6 +64,26 @@ TEST(Arm64InstructionSetFeaturesTest, Arm64Features) { EXPECT_FALSE(kryo_features->Equals(cortex_a57_features.get())); EXPECT_STREQ("-a53", kryo_features->GetFeatureString().c_str()); EXPECT_EQ(kryo_features->AsBitmap(), 0U); + + std::unique_ptr<const InstructionSetFeatures> cortex_a55_features( + InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a55", &error_msg)); + ASSERT_TRUE(cortex_a55_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a55_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(cortex_a55_features->Equals(cortex_a55_features.get())); + EXPECT_TRUE(cortex_a55_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(cortex_a55_features->Equals(cortex_a57_features.get())); + EXPECT_STREQ("-a53", cortex_a55_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a55_features->AsBitmap(), 0U); + + std::unique_ptr<const InstructionSetFeatures> cortex_a75_features( + InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a75", &error_msg)); + ASSERT_TRUE(cortex_a75_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a75_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(cortex_a75_features->Equals(cortex_a75_features.get())); + EXPECT_TRUE(cortex_a75_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(cortex_a75_features->Equals(cortex_a57_features.get())); + EXPECT_STREQ("-a53", cortex_a75_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a75_features->AsBitmap(), 0U); } } // namespace art diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 9ff5ebede3..ac5b2b8b88 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1533,7 +1533,10 @@ ENTRY \name END \name .endm -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY \name @@ -1577,6 +1580,8 @@ TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode // Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 9418caf98c..5d6e410101 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -203,6 +203,10 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { static_assert(!IsDirectEntrypoint(kQuickInitializeType), "Non-direct C stub marked direct."); qpoints->pResolveString = art_quick_resolve_string; static_assert(!IsDirectEntrypoint(kQuickResolveString), "Non-direct C stub marked direct."); + qpoints->pResolveMethodHandle = art_quick_resolve_method_handle; + static_assert(!IsDirectEntrypoint(kQuickResolveMethodHandle), "Non-direct C stub marked direct."); + qpoints->pResolveMethodType = art_quick_resolve_method_type; + static_assert(!IsDirectEntrypoint(kQuickResolveMethodType), "Non-direct C stub marked direct."); // Field qpoints->pSet8Instance = art_quick_set8_instance; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index d8fe480719..c367ea60c2 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -2027,8 +2027,11 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64 -// Macro for string and type resolution and initialization. -// $a0 is both input and output. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. $a0 is both input and + * output. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY_NO_GP \name @@ -2053,6 +2056,18 @@ END \name .endm /* + * Entry from managed code to resolve a method handle. On entry, A0 holds the method handle + * index. On success the MethodHandle is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode + + /* + * Entry from managed code to resolve a method type. On entry, A0 holds the method type index. + * On success the MethodType is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode + + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast * path check for hit in strings cache has already been performed. diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 8d2a7bd6c1..1f4f174e26 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1930,8 +1930,11 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64 -// Macro for string and type resolution and initialization. -// $a0 is both input and output. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. $a0 is both input and + * output. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY_NO_GP \name @@ -1953,6 +1956,18 @@ END \name .endm /* + * Entry from managed code to resolve a method handle. On entry, A0 holds the method handle + * index. On success the MethodHandle is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode + + /* + * Entry from managed code to resolve a method type. On entry, A0 holds the method type index. + * On success the MethodType is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode + + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast * path check for hit in strings cache has already been performed. diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index df43aef94b..8ab4ce160f 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -923,7 +923,10 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name) END_MACRO -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) DEFINE_FUNCTION VAR(c_name) SETUP_SAVE_EVERYTHING_FRAME ebx, ebx, \runtime_method_offset // save ref containing registers for GC @@ -932,7 +935,7 @@ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset CFI_ADJUST_CFA_OFFSET(8) pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - PUSH eax // pass arg1 + PUSH eax // pass the index of the constant as arg1 call CALLVAR(cxx_name) // cxx_name(arg1, Thread*) addl MACRO_LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) @@ -1278,6 +1281,8 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFr ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 4f941e1c48..eb945ed366 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -951,12 +951,15 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name) END_MACRO -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) DEFINE_FUNCTION VAR(c_name) SETUP_SAVE_EVERYTHING_FRAME \runtime_method_offset // save everything for GC // Outgoing argument set up - movl %eax, %edi // pass string index + movl %eax, %edi // pass the index of the constant as arg0 movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() call CALLVAR(cxx_name) // cxx_name(arg0, Thread*) testl %eax, %eax // If result is null, deliver the OOME. @@ -1298,6 +1301,8 @@ END_FUNCTION art_quick_alloc_object_initialized_region_tlab ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 2f7d6ab98f..70ff40d32c 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -73,7 +73,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 + 162) * __SIZEOF_POINTER__) + (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __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/file_utils.cc b/runtime/base/file_utils.cc index 7921985b15..537216c198 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -261,12 +261,12 @@ std::string ReplaceFileExtension(const std::string& filename, const std::string& } } -bool LocationIsOnSystem(const char* location) { - UniqueCPtr<const char[]> path(realpath(location, nullptr)); - return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str()); +bool LocationIsOnSystem(const char* path) { + UniqueCPtr<const char[]> full_path(realpath(path, nullptr)); + return path != nullptr && android::base::StartsWith(full_path.get(), GetAndroidRoot().c_str()); } -bool LocationIsOnSystemFramework(const char* location) { +bool LocationIsOnSystemFramework(const char* full_path) { std::string error_msg; std::string root_path = GetAndroidRootSafe(&error_msg); if (root_path.empty()) { @@ -275,12 +275,7 @@ bool LocationIsOnSystemFramework(const char* location) { return false; } std::string framework_path = root_path + "/framework/"; - - // Warning: Bionic implementation of realpath() allocates > 12KB on the stack. - // Do not run this code on a small stack, e.g. in signal handler. - UniqueCPtr<const char[]> path(realpath(location, nullptr)); - return path != nullptr && - android::base::StartsWith(path.get(), framework_path.c_str()); + return android::base::StartsWith(full_path, framework_path); } } // namespace art diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 98174142f1..2bd541118b 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -672,9 +672,10 @@ static bool IsAbsoluteLocation(const std::string& location) { return !location.empty() && location[0] == '/'; } -bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec, - bool verify_names, - bool verify_checksums) const { +ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderContextMatch( + const std::string& context_spec, + bool verify_names, + bool verify_checksums) const { if (verify_names || verify_checksums) { DCHECK(dex_files_open_attempted_); DCHECK(dex_files_open_result_); @@ -683,15 +684,21 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex ClassLoaderContext expected_context; if (!expected_context.Parse(context_spec, verify_checksums)) { LOG(WARNING) << "Invalid class loader context: " << context_spec; - return false; + return VerificationResult::kMismatch; } // Special shared library contexts always match. They essentially instruct the runtime // to ignore the class path check because the oat file is known to be loaded in different // contexts. OatFileManager will further verify if the oat file can be loaded based on the // collision check. - if (special_shared_library_ || expected_context.special_shared_library_) { - return true; + if (expected_context.special_shared_library_) { + // Special case where we are the only entry in the class path. + if (class_loader_chain_.size() == 1 && class_loader_chain_[0].classpath.size() == 0) { + return VerificationResult::kVerifies; + } + return VerificationResult::kForcedToSkipChecks; + } else if (special_shared_library_) { + return VerificationResult::kForcedToSkipChecks; } if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) { @@ -699,7 +706,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << expected_context.class_loader_chain_.size() << ", actual=" << class_loader_chain_.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } for (size_t i = 0; i < class_loader_chain_.size(); i++) { @@ -710,14 +717,14 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << GetClassLoaderTypeName(expected_info.type) << ", found=" << GetClassLoaderTypeName(info.type) << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (info.classpath.size() != expected_info.classpath.size()) { LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i << ". expected=" << expected_info.classpath.size() << ", found=" << info.classpath.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (verify_checksums) { @@ -772,7 +779,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.classpath[k] << ", found=" << info.classpath[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } // Compare the checksums. @@ -781,11 +788,11 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.checksums[k] << ", found=" << info.checksums[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } } } - return true; + return VerificationResult::kVerifies; } jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) { diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 1c83007f41..a4268aa09a 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -22,8 +22,10 @@ #include "arch/instruction_set.h" #include "base/dchecked_vector.h" +#include "dex/dex_file.h" #include "handle_scope.h" #include "mirror/class_loader.h" +#include "oat_file.h" #include "scoped_thread_state_change.h" namespace art { @@ -34,6 +36,18 @@ class OatFile; // Utility class which holds the class loader context used during compilation/verification. class ClassLoaderContext { public: + enum class VerificationResult { + kVerifies, + kForcedToSkipChecks, + kMismatch, + }; + + enum ClassLoaderType { + kInvalidClassLoader = 0, + kPathClassLoader = 1, + kDelegateLastClassLoader = 2 + }; + ~ClassLoaderContext(); // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files. @@ -109,7 +123,7 @@ class ClassLoaderContext { // This should be called after OpenDexFiles(). // Names are only verified if verify_names is true. // Checksums are only verified if verify_checksums is true. - bool VerifyClassLoaderContextMatch(const std::string& context_spec, + VerificationResult VerifyClassLoaderContextMatch(const std::string& context_spec, bool verify_names = true, bool verify_checksums = true) const; @@ -141,12 +155,6 @@ class ClassLoaderContext { static std::unique_ptr<ClassLoaderContext> Default(); private: - enum ClassLoaderType { - kInvalidClassLoader = 0, - kPathClassLoader = 1, - kDelegateLastClassLoader = 2 - }; - struct ClassLoaderInfo { // The type of this class loader. ClassLoaderType type; diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 4689ae4c3f..5e3f48c100 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -608,6 +608,17 @@ TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA"); } + +TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextFirstElement) { + std::string context_spec = "PCL[]"; + std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec); + ASSERT_TRUE(context != nullptr); + PretendContextOpenedDexFiles(context.get()); + // Ensure that the special shared library marks as verified for the first thing in the class path. + ASSERT_EQ(context->VerifyClassLoaderContextMatch(OatFile::kSpecialSharedLibrary), + ClassLoaderContext::VerificationResult::kVerifies); +} + TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]"; std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec); @@ -619,28 +630,36 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); VerifyClassLoaderDLC(context.get(), 1, "c.dex"); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), + ClassLoaderContext::VerificationResult::kVerifies); std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_type)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_classpath_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_checksum)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_classpath)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC["; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_spec), + ClassLoaderContext::VerificationResult::kMismatch); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { @@ -652,7 +671,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d); std::string context_with_no_base_dir = context->EncodeContextForOatFile(""); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); std::string dex_location = GetTestDexFileName("ForClassLoaderA"); size_t pos = dex_location.rfind('/'); @@ -661,7 +681,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::string context_with_base_dir = context->EncodeContextForOatFile(parent); ASSERT_NE(context_with_base_dir, context_with_no_base_dir); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) { @@ -669,7 +690,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultide std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile(""))); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")), + ClassLoaderContext::VerificationResult::kVerifies); } } // namespace art diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc index 415e451098..392ce1e7f5 100644 --- a/runtime/dex/art_dex_file_loader.cc +++ b/runtime/dex/art_dex_file_loader.cc @@ -534,7 +534,10 @@ std::unique_ptr<DexFile> ArtDexFileLoader::OpenCommon(const uint8_t* base, // Check if this dex file is located in the framework directory. // If it is, set a flag on the dex file. This is used by hidden API // policy decision logic. - if (dex_file != nullptr && LocationIsOnSystemFramework(location.c_str())) { + // Location can contain multidex suffix, so fetch its canonical version. Note + // that this will call `realpath`. + std::string path = DexFileLoader::GetDexCanonicalLocation(location.c_str()); + if (dex_file != nullptr && LocationIsOnSystemFramework(path.c_str())) { dex_file->SetIsPlatformDexFile(); } diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index aee397b65b..274a6df702 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -49,20 +49,31 @@ class ArtDexFileLoaderTest : public CommonRuntimeTest { CommonRuntimeTest::SetUp(); std::string dex_location = GetTestDexFileName("Main"); + std::string multidex_location = GetTestDexFileName("MultiDex"); data_location_path_ = android_data_ + "/foo.jar"; system_location_path_ = GetAndroidRoot() + "/foo.jar"; system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar"; + data_multi_location_path_ = android_data_ + "/multifoo.jar"; + system_multi_location_path_ = GetAndroidRoot() + "/multifoo.jar"; + system_framework_multi_location_path_ = GetAndroidRoot() + "/framework/multifoo.jar"; Copy(dex_location, data_location_path_); Copy(dex_location, system_location_path_); Copy(dex_location, system_framework_location_path_); + + Copy(multidex_location, data_multi_location_path_); + Copy(multidex_location, system_multi_location_path_); + Copy(multidex_location, system_framework_multi_location_path_); } virtual void TearDown() { remove(data_location_path_.c_str()); remove(system_location_path_.c_str()); remove(system_framework_location_path_.c_str()); + remove(data_multi_location_path_.c_str()); + remove(system_multi_location_path_.c_str()); + remove(system_framework_multi_location_path_.c_str()); CommonRuntimeTest::TearDown(); } @@ -70,6 +81,9 @@ class ArtDexFileLoaderTest : public CommonRuntimeTest { std::string data_location_path_; std::string system_location_path_; std::string system_framework_location_path_; + std::string data_multi_location_path_; + std::string system_multi_location_path_; + std::string system_framework_multi_location_path_; }; // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and @@ -390,6 +404,53 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_TRUE(dex_file->IsPlatformDexFile()); } + + dex_files.clear(); + + // Load multidex file from a non-system directory and check that it is not flagged as framework. + success = loader.Open(data_multi_location_path_.c_str(), + data_multi_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); + for (std::unique_ptr<const DexFile>& dex_file : dex_files) { + ASSERT_FALSE(dex_file->IsPlatformDexFile()); + } + + dex_files.clear(); + + // Load multidex file from a system, non-framework directory and check that it is not flagged + // as framework. + success = loader.Open(system_multi_location_path_.c_str(), + system_multi_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GT(dex_files.size(), 1u); + for (std::unique_ptr<const DexFile>& dex_file : dex_files) { + ASSERT_FALSE(dex_file->IsPlatformDexFile()); + } + + dex_files.clear(); + + // Load multidex file from a system/framework directory and check that it is flagged as a + // framework dex. + success = loader.Open(system_framework_multi_location_path_.c_str(), + system_framework_multi_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GT(dex_files.size(), 1u); + for (std::unique_ptr<const DexFile>& dex_file : dex_files) { + ASSERT_TRUE(dex_file->IsPlatformDexFile()); + } } } // namespace art diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 246c703e93..a58946ae66 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -260,4 +260,26 @@ ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) { return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first; } +ObjPtr<mirror::MethodHandle> ResolveMethodHandleFromCode(ArtMethod* referrer, + uint32_t method_handle_idx) { + Thread::PoisonObjectPointersIfDebug(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + return class_linker->ResolveMethodHandle(Thread::Current(), method_handle_idx, referrer); +} + +ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer, + uint32_t proto_idx) { + Thread::PoisonObjectPointersIfDebug(); + ObjPtr<mirror::MethodType> method_type = + referrer->GetDexCache()->GetResolvedMethodType(proto_idx); + if (UNLIKELY(method_type == nullptr)) { + StackHandleScope<2> hs(Thread::Current()); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader())); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + method_type = class_linker->ResolveMethodType(hs.Self(), proto_idx, dex_cache, class_loader); + } + return method_type; +} + } // namespace art diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index eb32153b16..0a3b5dfc93 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -34,6 +34,8 @@ namespace art { namespace mirror { class Array; class Class; +class MethodHandle; +class MethodType; class Object; class String; } // namespace mirror @@ -151,6 +153,15 @@ inline ObjPtr<mirror::Class> ResolveVerifyAndClinit(dex::TypeIndex type_idx, REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); +ObjPtr<mirror::MethodHandle> ResolveMethodHandleFromCode(ArtMethod* referrer, + uint32_t method_handle_idx) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); + +ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer, uint32_t proto_idx) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); + inline ObjPtr<mirror::String> ResolveStringFromCode(ArtMethod* referrer, dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_) diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index 2d0932a0c4..1804d9e64d 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -37,6 +37,8 @@ extern "C" void art_quick_check_instance_of(art::mirror::Object*, art::mirror::C extern "C" void* art_quick_initialize_static_storage(uint32_t); extern "C" void* art_quick_initialize_type(uint32_t); extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t); +extern "C" void* art_quick_resolve_method_handle(uint32_t); +extern "C" void* art_quick_resolve_method_type(uint32_t); extern "C" void* art_quick_resolve_string(uint32_t); // Field entrypoints. diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 8c90800463..3f66045576 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -37,6 +37,8 @@ static void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qp qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage; qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access; qpoints->pInitializeType = art_quick_initialize_type; + qpoints->pResolveMethodHandle = art_quick_resolve_method_handle; + qpoints->pResolveMethodType = art_quick_resolve_method_type; qpoints->pResolveString = art_quick_resolve_string; // Field diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index cfb427f1ac..cf9ddd8aa8 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -183,6 +183,27 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type return result.Ptr(); } +extern "C" mirror::MethodHandle* artResolveMethodHandleFromCode(uint32_t method_handle_idx, + Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + auto caller_and_outer = + GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); + ArtMethod* caller = caller_and_outer.caller; + ObjPtr<mirror::MethodHandle> result = ResolveMethodHandleFromCode(caller, method_handle_idx); + return result.Ptr(); +} + +extern "C" mirror::MethodType* artResolveMethodTypeFromCode(uint32_t proto_idx, Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, + CalleeSaveType::kSaveEverything); + ArtMethod* caller = caller_and_outer.caller; + ObjPtr<mirror::MethodType> result = ResolveMethodTypeFromCode(caller, proto_idx); + return result.Ptr(); +} + extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 48a56f2fbf..3a8faca11d 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -38,6 +38,8 @@ V(InitializeStaticStorage, void*, uint32_t) \ V(InitializeTypeAndVerifyAccess, void*, uint32_t) \ V(InitializeType, void*, uint32_t) \ + V(ResolveMethodHandle, void*, uint32_t) \ + V(ResolveMethodType, void*, uint32_t) \ V(ResolveString, void*, uint32_t) \ \ V(Set8Instance, int, uint32_t, void*, int8_t) \ diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 1fdf439d3f..1337cd5fb2 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -183,7 +183,9 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeTypeAndVerifyAccess, pInitializeType, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveString, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveMethodHandle, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodHandle, pResolveMethodType, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodType, pResolveString, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveString, pSet8Instance, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Instance, pSet8Static, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Static, pSet16Instance, sizeof(void*)); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 249a8b04fb..d8aa00c45e 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -563,7 +563,7 @@ void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) { } } -void JitCodeCache::FreeCode(const void* code_ptr) { +void JitCodeCache::FreeCodeAndData(const void* code_ptr) { uintptr_t allocation = FromCodeToAllocation(code_ptr); // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. @@ -590,7 +590,7 @@ void JitCodeCache::FreeAllMethodHeaders( MutexLock mu(Thread::Current(), lock_); ScopedCodeCacheWrite scc(this); for (const OatQuickMethodHeader* method_header : method_headers) { - FreeCode(method_header->GetCode()); + FreeCodeAndData(method_header->GetCode()); } } @@ -916,7 +916,7 @@ bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { in_cache = true; if (it->second.GetMethods().empty()) { if (release_memory) { - FreeCode(it->second.GetCode()); + FreeCodeAndData(it->second.GetCode()); } jni_stubs_map_.erase(it); } else { @@ -928,7 +928,7 @@ bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { if (it->second == method) { in_cache = true; if (release_memory) { - FreeCode(it->first); + FreeCodeAndData(it->first); } it = method_code_map_.erase(it); } else { diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index b10f57eff2..958e8e8aa2 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -317,8 +317,8 @@ class JitCodeCache { REQUIRES(lock_) REQUIRES(Locks::mutator_lock_); - // Free in the mspace allocations for `code_ptr`. - void FreeCode(const void* code_ptr) REQUIRES(lock_); + // Free code and data allocations for `code_ptr`. + void FreeCodeAndData(const void* code_ptr) REQUIRES(lock_); // Number of bytes allocated in the code cache. size_t CodeCacheSizeLocked() REQUIRES(lock_); @@ -357,10 +357,10 @@ class JitCodeCache { REQUIRES(lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void FreeCode(uint8_t* code) REQUIRES(lock_); uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_); - void FreeData(uint8_t* data) REQUIRES(lock_); + void FreeCode(uint8_t* code) REQUIRES(lock_); uint8_t* AllocateData(size_t data_size) REQUIRES(lock_); + void FreeData(uint8_t* data) REQUIRES(lock_); bool IsWeakAccessEnabled(Thread* self) const; void WaitUntilInlineCacheAccessible(Thread* self) diff --git a/runtime/oat.h b/runtime/oat.h index 0318606f87..6c683f1541 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Use rMR as temp in Baker RB introspection marking. - static constexpr uint8_t kOatVersion[] = { '1', '4', '1', '\0' }; + // Last oat version changed reason: compiler support const-method-handle + static constexpr uint8_t kOatVersion[] = { '1', '4', '3', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 9c8b6512a7..7d69927ffb 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -1217,7 +1217,9 @@ bool OatFileAssistant::OatFileInfo::ClassLoaderContextIsOkay(ClassLoaderContext* return false; } - bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()); + + const bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()) != + ClassLoaderContext::VerificationResult::kMismatch; if (!result) { VLOG(oat) << "ClassLoaderContext check failed. Context was " << file->GetClassLoaderContext() diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 16e6cf375c..59a1045ba2 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -276,9 +276,19 @@ static void AddNext(/*inout*/DexFileAndClassPair& original, } } -static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded, - std::vector<const DexFile*>& dex_files_unloaded, - std::string* error_msg /*out*/) { +static bool CheckClassCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + std::string* error_msg /*out*/) { + std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles(); + + // Vector that holds the newly opened dex files live, this is done to prevent leaks. + std::vector<std::unique_ptr<const DexFile>> opened_dex_files; + + ScopedTrace st("Collision check"); + // Add dex files from the oat file to check. + std::vector<const DexFile*> dex_files_unloaded; + AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); + // Generate type index information for each dex file. std::vector<TypeIndexInfo> loaded_types; for (const DexFile* dex_file : dex_files_loaded) { @@ -355,9 +365,10 @@ static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded, // against the following top element. If the descriptor is the same, it is now checked whether // the two elements agree on whether their dex file was from an already-loaded oat-file or the // new oat file. Any disagreement indicates a collision. -bool OatFileManager::HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - std::string* error_msg /*out*/) const { +OatFileManager::CheckCollisionResult OatFileManager::CheckCollision( + const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const { DCHECK(oat_file != nullptr); DCHECK(error_msg != nullptr); @@ -367,28 +378,59 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, // Note that this has correctness implications as we cannot guarantee that the class resolution // used during compilation is OK (b/37777332). if (context == nullptr) { - LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; - return false; + LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; + return CheckCollisionResult::kSkippedUnsupportedClassLoader; } - // If the pat file loading context matches the context used during compilation then we accept + // If the oat file loading context matches the context used during compilation then we accept // the oat file without addition checks - if (context->VerifyClassLoaderContextMatch(oat_file->GetClassLoaderContext())) { - return false; + ClassLoaderContext::VerificationResult result = context->VerifyClassLoaderContextMatch( + oat_file->GetClassLoaderContext(), + /*verify_names*/ true, + /*verify_checksums*/ true); + switch (result) { + case ClassLoaderContext::VerificationResult::kForcedToSkipChecks: + return CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary; + case ClassLoaderContext::VerificationResult::kMismatch: + // Mismatched context, do the actual collision check. + break; + case ClassLoaderContext::VerificationResult::kVerifies: + return CheckCollisionResult::kNoCollisions; } // The class loader context does not match. Perform a full duplicate classes check. + return CheckClassCollision(oat_file, context, error_msg) + ? CheckCollisionResult::kPerformedHasCollisions : CheckCollisionResult::kNoCollisions; +} - std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles(); - - // Vector that holds the newly opened dex files live, this is done to prevent leaks. - std::vector<std::unique_ptr<const DexFile>> opened_dex_files; +bool OatFileManager::AcceptOatFile(CheckCollisionResult result) const { + // Take the file only if it has no collisions, or we must take it because of preopting. + // Also accept oat files for shared libraries and unsupported class loaders. + return result != CheckCollisionResult::kPerformedHasCollisions; +} - ScopedTrace st("Collision check"); - // Add dex files from the oat file to check. - std::vector<const DexFile*> dex_files_unloaded; - AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); - return CollisionCheck(dex_files_loaded, dex_files_unloaded, error_msg); +bool OatFileManager::ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + // If we verified the class loader context (skipping due to the special marker doesn't + // count), then also avoid the collision check. + bool load_image = check_collision_result == CheckCollisionResult::kNoCollisions; + // If we skipped the collision check, we need to reverify to be sure its OK to load the + // image. + if (!load_image && + check_collision_result == + CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary) { + // We can load the app image only if there are no collisions. If we know the + // class loader but didn't do the full collision check in HasCollisions(), + // do it now. b/77342775 + load_image = !CheckClassCollision(source_oat_file, context, error_msg); + } + return load_image; + } + return false; } std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( @@ -473,16 +515,17 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( << reinterpret_cast<uintptr_t>(oat_file.get()) << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; + CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions; if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { // Prevent oat files from being loaded if no class_loader or dex_elements are provided. // This can happen when the deprecated DexFile.<init>(String) is called directly, and it // could load oat files without checking the classpath, which would be incorrect. // Take the file only if it has no collisions, or we must take it because of preopting. - bool accept_oat_file = - !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg); + check_collision_result = CheckCollision(oat_file.get(), context.get(), /*out*/ &error_msg); + bool accept_oat_file = AcceptOatFile(check_collision_result); if (!accept_oat_file) { // Failed the collision check. Print warning. - if (Runtime::Current()->IsDexFileFallbackEnabled()) { + if (runtime->IsDexFileFallbackEnabled()) { if (!oat_file_assistant.HasOriginalDexFiles()) { // We need to fallback but don't have original dex files. We have to // fallback to opening the existing oat file. This is potentially @@ -529,10 +572,11 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // We need to throw away the image space if we are debuggable but the oat-file source of the // image is not otherwise we might get classes with inlined methods or other such things. std::unique_ptr<gc::space::ImageSpace> image_space; - if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + if (ShouldLoadAppImage(check_collision_result, + source_oat_file, + context.get(), + &error_msg)) { image_space = oat_file_assistant.OpenImageSpace(source_oat_file); - } else { - image_space = nullptr; } if (image_space != nullptr) { ScopedObjectAccess soa(self); diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 038474e31f..80456e9b75 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -108,23 +108,39 @@ class OatFileManager { void SetOnlyUseSystemOatFiles(); private: + enum class CheckCollisionResult { + kSkippedUnsupportedClassLoader, + kSkippedClassLoaderContextSharedLibrary, + kNoCollisions, + kPerformedHasCollisions, + }; + // Check that the class loader context of the given oat file matches the given context. // This will perform a check that all class loaders in the chain have the same type and // classpath. // If the context is null (which means the initial class loader was null or unsupported) - // this returns false. + // this returns kSkippedUnsupportedClassLoader. // If the context does not validate the method will check for duplicate class definitions of // the given oat file against the oat files (either from the class loaders if possible or all // non-boot oat files otherwise). - // Return true if there are any class definition collisions in the oat_file. - bool HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - /*out*/ std::string* error_msg) const + // Return kPerformedHasCollisions if there are any class definition collisions in the oat_file. + CheckCollisionResult CheckCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const REQUIRES(!Locks::oat_file_manager_lock_); const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const REQUIRES(Locks::oat_file_manager_lock_); + // Return true if we should accept the oat file. + bool AcceptOatFile(CheckCollisionResult result) const; + + // Return true if we should attempt to load the app image. + bool ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg); + std::set<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_); bool have_non_pic_oat_file_; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index bc29aaca6d..3518d2facd 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2287,14 +2287,10 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::CONST_METHOD_HANDLE: work_line_->SetRegisterType<LockOp::kClear>( this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodHandle()); - // TODO: add compiler support for const-method-{handle,type} (b/66890674) - Fail(VERIFY_ERROR_FORCE_INTERPRETER); break; case Instruction::CONST_METHOD_TYPE: work_line_->SetRegisterType<LockOp::kClear>( this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodType()); - // TODO: add compiler support for const-method-{handle,type} (b/66890674) - Fail(VERIFY_ERROR_FORCE_INTERPRETER); break; case Instruction::MONITOR_ENTER: work_line_->PushMonitor(this, inst->VRegA_11x(), work_insn_idx_); diff --git a/test/172-app-image-twice/check b/test/172-app-image-twice/check new file mode 100755 index 0000000000..26a97a48ae --- /dev/null +++ b/test/172-app-image-twice/check @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2018 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. + +# Remove all lines not containing "passed". +grep "^passed" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null diff --git a/test/172-app-image-twice/debug_print_class.cc b/test/172-app-image-twice/debug_print_class.cc new file mode 100644 index 0000000000..6c3de20f2d --- /dev/null +++ b/test/172-app-image-twice/debug_print_class.cc @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 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 "debug_print.h" +#include "dex/dex_file.h" +#include "mirror/class-inl.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" + +namespace art { + +extern "C" JNIEXPORT void JNICALL Java_Main_debugPrintClass(JNIEnv*, jclass, jclass cls) { + ScopedObjectAccess soa(Thread::Current()); + ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls); + LOG(ERROR) << "klass: " << klass.Ptr() << " dex_file: " << klass->GetDexFile().GetLocation() + << "/" << static_cast<const void*>(&klass->GetDexFile()) + << " " << DescribeSpace(klass); +} + +} // namespace art diff --git a/test/172-app-image-twice/expected.txt b/test/172-app-image-twice/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/172-app-image-twice/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/172-app-image-twice/info.txt b/test/172-app-image-twice/info.txt new file mode 100644 index 0000000000..028046e872 --- /dev/null +++ b/test/172-app-image-twice/info.txt @@ -0,0 +1 @@ +Regression test for loading the same app image twice. diff --git a/test/172-app-image-twice/profile b/test/172-app-image-twice/profile new file mode 100644 index 0000000000..70cb2efbb5 --- /dev/null +++ b/test/172-app-image-twice/profile @@ -0,0 +1 @@ +LTestClass; diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run new file mode 100644 index 0000000000..aa2819075f --- /dev/null +++ b/test/172-app-image-twice/run @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Copyright (C) 2018 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. + +# Build an app image with TestClass (specified by profile) and class loader +# context that skips the duplicate class checks. + +# Target and host use a different shell, and we need to special case the +# passing of the class loader context marker. +if [[ "$@" = *" --host "* ]]; then + ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option --class-loader-context=\& +else + ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option '--class-loader-context=\&' +fi diff --git a/test/172-app-image-twice/src/Main.java b/test/172-app-image-twice/src/Main.java new file mode 100644 index 0000000000..a1c151a6bc --- /dev/null +++ b/test/172-app-image-twice/src/Main.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 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.lang.reflect.Method; + +public class Main { + private static String TEST_NAME = "172-app-image-twice"; + + public static void main(String args[]) throws Exception { + System.loadLibrary(args[0]); + + Class<?> tc1 = Class.forName("TestClass"); + + String dexPath = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar"; + Class<?> bdcl = Class.forName("dalvik.system.BaseDexClassLoader"); + Method addDexPathMethod = bdcl.getDeclaredMethod("addDexPath", String.class); + addDexPathMethod.invoke(Main.class.getClassLoader(), dexPath); + + Class<?> tc2 = Class.forName("TestClass"); + + // Add extra logging to simulate libcore logging, this logging should not be compared + // against. + System.out.println("Extra logging"); + + if (tc1 != tc2) { + System.out.println("Class mismatch!"); + debugPrintClass(tc1); + debugPrintClass(tc2); + } else { + System.out.println("passed"); + } + } + + public static native void debugPrintClass(Class<?> cls); +} diff --git a/test/172-app-image-twice/src/TestClass.java b/test/172-app-image-twice/src/TestClass.java new file mode 100644 index 0000000000..5381718f6e --- /dev/null +++ b/test/172-app-image-twice/src/TestClass.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2018 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 TestClass { +} diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt index bc943e368e..bbaaedb0af 100644 --- a/test/979-const-method-handle/expected.txt +++ b/test/979-const-method-handle/expected.txt @@ -1,6 +1,9 @@ (int,Integer,System)String +repeatConstMethodType0((int,Integer,System)String) +repeatConstMethodType1((LocalClass)void) Hello World! And Hello Zog Hello World! And Hello Zorba name is HoverFly 2.718281828459045 +repeatConstMethodHandle() Attempting to set Math.E raised IAE diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java index 663814f232..427ca7a306 100644 --- a/test/979-const-method-handle/src/Main.java +++ b/test/979-const-method-handle/src/Main.java @@ -20,78 +20,146 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; class Main { + /** + * Number of iterations run to attempt to trigger JIT compilation. These tests run on ART and + * the RI so they iterate rather than using the ART only native method ensureJitCompiled(). + */ + private static final int ITERATIONS_FOR_JIT = 12000; + + /** A static field updated by method handle getters and setters. */ private static String name = "default"; private static void unreachable() { throw new Error("Unreachable"); } + private static void assertEquals(Object expected, Object actual) { + if (!expected.equals(actual)) { + throw new AssertionError("Assertion failure: " + expected + " != " + actual); + } + } + + private static class LocalClass { + public LocalClass() {} + + private int field; + } + @ConstantMethodType( - returnType = String.class, - parameterTypes = {int.class, Integer.class, System.class} - ) + returnType = String.class, + parameterTypes = {int.class, Integer.class, System.class}) private static MethodType methodType0() { unreachable(); return null; } + @ConstantMethodType( + returnType = void.class, + parameterTypes = {LocalClass.class}) + private static MethodType methodType1() { + unreachable(); + return null; + } + + private static void repeatConstMethodType0(MethodType expected) { + System.out.print("repeatConstMethodType0("); + System.out.print(expected); + System.out.println(")"); + for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { + MethodType actual = methodType0(); + assertEquals(expected, actual); + } + } + + private static void repeatConstMethodType1(MethodType expected) { + System.out.print("repeatConstMethodType1("); + System.out.print(expected); + System.out.println(")"); + for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { + MethodType actual = methodType1(); + assertEquals(expected, actual); + } + } + static void helloWorld(String who) { System.out.print("Hello World! And Hello "); System.out.println(who); } @ConstantMethodHandle( - kind = ConstantMethodHandle.INVOKE_STATIC, - owner = "Main", - fieldOrMethodName = "helloWorld", - descriptor = "(Ljava/lang/String;)V" - ) + kind = ConstantMethodHandle.INVOKE_STATIC, + owner = "Main", + fieldOrMethodName = "helloWorld", + descriptor = "(Ljava/lang/String;)V") private static MethodHandle printHelloHandle() { unreachable(); return null; } @ConstantMethodHandle( - kind = ConstantMethodHandle.STATIC_PUT, - owner = "Main", - fieldOrMethodName = "name", - descriptor = "Ljava/lang/String;" - ) + kind = ConstantMethodHandle.STATIC_PUT, + owner = "Main", + fieldOrMethodName = "name", + descriptor = "Ljava/lang/String;") private static MethodHandle setNameHandle() { unreachable(); return null; } @ConstantMethodHandle( - kind = ConstantMethodHandle.STATIC_GET, - owner = "java/lang/Math", - fieldOrMethodName = "E", - descriptor = "D" - ) + kind = ConstantMethodHandle.STATIC_GET, + owner = "Main", + fieldOrMethodName = "name", + descriptor = "Ljava/lang/String;") + private static MethodHandle getNameHandle() { + unreachable(); + return null; + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.STATIC_GET, + owner = "java/lang/Math", + fieldOrMethodName = "E", + descriptor = "D") private static MethodHandle getMathE() { unreachable(); return null; } @ConstantMethodHandle( - kind = ConstantMethodHandle.STATIC_PUT, - owner = "java/lang/Math", - fieldOrMethodName = "E", - descriptor = "D" - ) + kind = ConstantMethodHandle.STATIC_PUT, + owner = "java/lang/Math", + fieldOrMethodName = "E", + descriptor = "D") private static MethodHandle putMathE() { unreachable(); return null; } + private static void repeatConstMethodHandle() throws Throwable { + System.out.println("repeatConstMethodHandle()"); + String[] values = {"A", "B", "C"}; + for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { + String value = values[i % values.length]; + setNameHandle().invoke(value); + String actual = (String) getNameHandle().invokeExact(); + assertEquals(value, actual); + assertEquals(value, name); + } + } + public static void main(String[] args) throws Throwable { System.out.println(methodType0()); + repeatConstMethodType0( + MethodType.methodType(String.class, int.class, Integer.class, System.class)); + repeatConstMethodType1(MethodType.methodType(void.class, LocalClass.class)); printHelloHandle().invokeExact("Zog"); printHelloHandle().invokeExact("Zorba"); setNameHandle().invokeExact("HoverFly"); System.out.print("name is "); System.out.println(name); System.out.println(getMathE().invoke()); + repeatConstMethodHandle(); try { putMathE().invokeExact(Math.PI); unreachable(); diff --git a/test/Android.bp b/test/Android.bp index 0c6b449877..76189f62a9 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -423,6 +423,7 @@ cc_defaults { "154-gc-loop/heap_interface.cc", "167-visit-locks/visit_locks.cc", "169-threadgroup-jni/jni_daemon_thread.cc", + "172-app-image-twice/debug_print_class.cc", "1945-proxy-method-arguments/get_args.cc", "203-multi-checkpoint/multi_checkpoint.cc", "305-other-fault-handler/fault_handler.cc", diff --git a/test/knownfailures.json b/test/knownfailures.json index f3137587f6..f473a99a27 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -734,6 +734,7 @@ "164-resolution-trampoline-dex-cache", "167-visit-locks", "168-vmstack-annotated", + "172-app-image-twice", "201-built-in-except-detail-messages", "203-multi-checkpoint", "304-method-tracing", diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc index e2833bf01d..154c60f6ac 100644 --- a/tools/veridex/flow_analysis.cc +++ b/tools/veridex/flow_analysis.cc @@ -112,7 +112,12 @@ void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const VeriClass* cl RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls); } -const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) { +void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls) { + current_registers_[dex_register] = + RegisterValue(RegisterSource::kConstant, value, DexFileReference(nullptr, 0), cls); +} + +const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) const { return current_registers_[dex_register]; } @@ -131,6 +136,49 @@ RegisterValue VeriFlowAnalysis::GetFieldType(uint32_t field_index) { return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls); } +int VeriFlowAnalysis::GetBranchFlags(const Instruction& instruction) const { + switch (instruction.Opcode()) { + #define IF_XX(cond, op) \ + case Instruction::IF_##cond: { \ + RegisterValue lhs = GetRegister(instruction.VRegA()); \ + RegisterValue rhs = GetRegister(instruction.VRegB()); \ + if (lhs.IsConstant() && rhs.IsConstant()) { \ + if (lhs.GetConstant() op rhs.GetConstant()) { \ + return Instruction::kBranch; \ + } else { \ + return Instruction::kContinue; \ + } \ + } \ + break; \ + } \ + case Instruction::IF_##cond##Z: { \ + RegisterValue val = GetRegister(instruction.VRegA()); \ + if (val.IsConstant()) { \ + if (val.GetConstant() op 0) { \ + return Instruction::kBranch; \ + } else { \ + return Instruction::kContinue; \ + } \ + } \ + break; \ + } + + IF_XX(EQ, ==); + IF_XX(NE, !=); + IF_XX(LT, <); + IF_XX(LE, <=); + IF_XX(GT, >); + IF_XX(GE, >=); + + #undef IF_XX + + default: + break; + } + + return Instruction::FlagsOf(instruction.Opcode()); +} + void VeriFlowAnalysis::AnalyzeCode() { std::vector<uint32_t> work_list; work_list.push_back(0); @@ -149,16 +197,17 @@ void VeriFlowAnalysis::AnalyzeCode() { ProcessDexInstruction(inst); SetVisited(dex_pc); - int opcode_flags = Instruction::FlagsOf(inst.Opcode()); - if ((opcode_flags & Instruction::kContinue) != 0) { - if ((opcode_flags & Instruction::kBranch) != 0) { + int branch_flags = GetBranchFlags(inst); + + if ((branch_flags & Instruction::kContinue) != 0) { + if ((branch_flags & Instruction::kBranch) != 0) { uint32_t branch_dex_pc = dex_pc + inst.GetTargetOffset(); if (MergeRegisterValues(branch_dex_pc)) { work_list.push_back(branch_dex_pc); } } dex_pc += inst.SizeInCodeUnits(); - } else if ((opcode_flags & Instruction::kBranch) != 0) { + } else if ((branch_flags & Instruction::kBranch) != 0) { dex_pc += inst.GetTargetOffset(); DCHECK(IsBranchTarget(dex_pc)); } else { @@ -178,12 +227,30 @@ void VeriFlowAnalysis::AnalyzeCode() { void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { switch (instruction.Opcode()) { - case Instruction::CONST_4: - case Instruction::CONST_16: - case Instruction::CONST: + case Instruction::CONST_4: { + int32_t register_index = instruction.VRegA(); + int32_t value = instruction.VRegB_11n(); + UpdateRegister(register_index, value, VeriClass::integer_); + break; + } + case Instruction::CONST_16: { + int32_t register_index = instruction.VRegA(); + int32_t value = instruction.VRegB_21s(); + UpdateRegister(register_index, value, VeriClass::integer_); + break; + } + + case Instruction::CONST: { + int32_t register_index = instruction.VRegA(); + int32_t value = instruction.VRegB_31i(); + UpdateRegister(register_index, value, VeriClass::integer_); + break; + } + case Instruction::CONST_HIGH16: { int32_t register_index = instruction.VRegA(); - UpdateRegister(register_index, VeriClass::integer_); + int32_t value = instruction.VRegB_21h(); + UpdateRegister(register_index, value, VeriClass::integer_); break; } @@ -268,6 +335,8 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::RETURN: { break; } + + // If operations will be handled when looking at the control flow. #define IF_XX(cond) \ case Instruction::IF_##cond: break; \ case Instruction::IF_##cond##Z: break @@ -279,6 +348,8 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { IF_XX(GT); IF_XX(GE); + #undef IF_XX + case Instruction::GOTO: case Instruction::GOTO_16: case Instruction::GOTO_32: { @@ -495,7 +566,13 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: case Instruction::SGET_SHORT: { - UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c())); + uint32_t dest_reg = instruction.VRegA_21c(); + uint16_t field_index = instruction.VRegB_21c(); + if (VeriClass::sdkInt_ != nullptr && resolver_->GetField(field_index) == VeriClass::sdkInt_) { + UpdateRegister(dest_reg, gTargetSdkVersion, VeriClass::integer_); + } else { + UpdateRegister(dest_reg, GetFieldType(instruction.VRegC_22c())); + } break; } diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h index 62c9916a61..fc093600c3 100644 --- a/tools/veridex/flow_analysis.h +++ b/tools/veridex/flow_analysis.h @@ -35,6 +35,7 @@ enum class RegisterSource { kMethod, kClass, kString, + kConstant, kNone }; @@ -44,28 +45,33 @@ enum class RegisterSource { class RegisterValue { public: RegisterValue() : source_(RegisterSource::kNone), - parameter_index_(0), + value_(0), reference_(nullptr, 0), type_(nullptr) {} RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type) - : source_(source), parameter_index_(0), reference_(reference), type_(type) {} + : source_(source), value_(0), reference_(reference), type_(type) {} RegisterValue(RegisterSource source, - uint32_t parameter_index, + uint32_t value, DexFileReference reference, const VeriClass* type) - : source_(source), parameter_index_(parameter_index), reference_(reference), type_(type) {} + : source_(source), value_(value), reference_(reference), type_(type) {} RegisterSource GetSource() const { return source_; } DexFileReference GetDexFileReference() const { return reference_; } const VeriClass* GetType() const { return type_; } uint32_t GetParameterIndex() const { CHECK(IsParameter()); - return parameter_index_; + return value_; + } + uint32_t GetConstant() const { + CHECK(IsConstant()); + return value_; } bool IsParameter() const { return source_ == RegisterSource::kParameter; } bool IsClass() const { return source_ == RegisterSource::kClass; } bool IsString() const { return source_ == RegisterSource::kString; } + bool IsConstant() const { return source_ == RegisterSource::kConstant; } std::string ToString() const { switch (source_) { @@ -91,7 +97,7 @@ class RegisterValue { private: RegisterSource source_; - uint32_t parameter_index_; + uint32_t value_; DexFileReference reference_; const VeriClass* type_; }; @@ -137,12 +143,15 @@ class VeriFlowAnalysis { uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id); void UpdateRegister(uint32_t dex_register, const RegisterValue& value); void UpdateRegister(uint32_t dex_register, const VeriClass* cls); + void UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls); void ProcessDexInstruction(const Instruction& inst); void SetVisited(uint32_t dex_pc); RegisterValue GetFieldType(uint32_t field_index); + int GetBranchFlags(const Instruction& instruction) const; + protected: - const RegisterValue& GetRegister(uint32_t dex_register); + const RegisterValue& GetRegister(uint32_t dex_register) const; RegisterValue GetReturnType(uint32_t method_index); VeridexResolver* resolver_; diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index dc7ea94032..bcd4815a38 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -25,6 +25,7 @@ #include "precise_hidden_api_finder.h" #include "resolver.h" +#include <cstdlib> #include <sstream> namespace art { @@ -62,6 +63,7 @@ VeriMethod VeriClass::getMethod_ = nullptr; VeriMethod VeriClass::getDeclaredMethod_ = nullptr; VeriMethod VeriClass::getClass_ = nullptr; VeriMethod VeriClass::loadClass_ = nullptr; +VeriField VeriClass::sdkInt_ = nullptr; struct VeridexOptions { const char* dex_file = nullptr; @@ -70,6 +72,7 @@ struct VeridexOptions { const char* light_greylist = nullptr; const char* dark_greylist = nullptr; bool precise = true; + int target_sdk_version = 28; /* P */ }; static const char* Substr(const char* str, int index) { @@ -91,6 +94,7 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) { static const char* kDarkGreylistOption = "--dark-greylist="; static const char* kLightGreylistOption = "--light-greylist="; static const char* kImprecise = "--imprecise"; + static const char* kTargetSdkVersion = "--target-sdk-version="; for (int i = 0; i < argc; ++i) { if (StartsWith(argv[i], kDexFileOption)) { @@ -105,6 +109,8 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) { options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption)); } else if (strcmp(argv[i], kImprecise) == 0) { options->precise = false; + } else if (StartsWith(argv[i], kTargetSdkVersion)) { + options->target_sdk_version = atoi(Substr(argv[i], strlen(kTargetSdkVersion))); } } } @@ -124,6 +130,7 @@ class Veridex { static int Run(int argc, char** argv) { VeridexOptions options; ParseArgs(&options, argc, argv); + gTargetSdkVersion = options.target_sdk_version; std::vector<std::string> boot_content; std::vector<std::string> app_content; @@ -200,6 +207,11 @@ class Veridex { VeriClass::loadClass_ = boot_resolvers[0]->LookupDeclaredMethodIn( *VeriClass::class_loader_, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + VeriClass* version = type_map["Landroid/os/Build$VERSION;"]; + if (version != nullptr) { + VeriClass::sdkInt_ = boot_resolvers[0]->LookupFieldIn(*version, "SDK_INT", "I"); + } + std::vector<std::unique_ptr<VeridexResolver>> app_resolvers; Resolve(app_dex_files, resolver_map, type_map, &app_resolvers); diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h index 9c0a158174..31ddbf439e 100644 --- a/tools/veridex/veridex.h +++ b/tools/veridex/veridex.h @@ -24,6 +24,8 @@ namespace art { +static int gTargetSdkVersion = 1000; // Will be initialized after parsing options. + /** * Abstraction for fields defined in dex files. Currently, that's a pointer into their * `encoded_field` description. @@ -86,6 +88,8 @@ class VeriClass { static VeriMethod getClass_; static VeriMethod loadClass_; + static VeriField sdkInt_; + private: Primitive::Type kind_; uint8_t dimensions_; |