diff options
80 files changed, 1398 insertions, 1053 deletions
diff --git a/build/codegen.go b/build/codegen.go index 8526bf192b..d0db78e571 100644 --- a/build/codegen.go +++ b/build/codegen.go @@ -107,8 +107,9 @@ func codegen(ctx android.LoadHookContext, c *codegenProperties, library bool) { } type CodegenCommonArchProperties struct { - Srcs []string - Cflags []string + Srcs []string + Cflags []string + Cppflags []string } type CodegenLibraryArchProperties struct { diff --git a/compiler/Android.bp b/compiler/Android.bp index 30a65b280a..0d92b05593 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -183,7 +183,6 @@ art_cc_defaults { shared_libs: [ "libbase", "libcutils", // for atrace. - "liblzma", ], include_dirs: ["art/disassembler"], header_libs: [ @@ -199,7 +198,6 @@ cc_defaults { static_libs: [ "libbase", "libcutils", - "liblzma", ], } @@ -233,12 +231,12 @@ art_cc_library { // VIXL assembly support for ARM targets. static: { whole_static_libs: [ - "libvixl-arm", + "libvixl", ], }, shared: { shared_libs: [ - "libvixl-arm", + "libvixl", ], }, }, @@ -246,12 +244,12 @@ art_cc_library { // VIXL assembly support for ARM64 targets. static: { whole_static_libs: [ - "libvixl-arm64", + "libvixl", ], }, shared: { shared_libs: [ - "libvixl-arm64", + "libvixl", ], }, }, @@ -295,12 +293,12 @@ art_cc_library { // VIXL assembly support for ARM targets. static: { whole_static_libs: [ - "libvixld-arm", + "libvixld", ], }, shared: { shared_libs: [ - "libvixld-arm", + "libvixld", ], }, }, @@ -308,12 +306,12 @@ art_cc_library { // VIXL assembly support for ARM64 targets. static: { whole_static_libs: [ - "libvixld-arm64", + "libvixld", ], }, shared: { shared_libs: [ - "libvixld-arm64", + "libvixld", ], }, }, @@ -454,8 +452,7 @@ art_cc_test { "libprofiled", "libartd-compiler", "libartd-simulator-container", - "libvixld-arm", - "libvixld-arm64", + "libvixld", "libbacktrace", "libnativeloader", @@ -512,7 +509,6 @@ art_cc_test { }, shared_libs: [ "libartd-compiler", - "libvixld-arm", - "libvixld-arm64", + "libvixld", ], } diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 599f4aa189..66421e2562 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -184,7 +184,7 @@ void CommonCompilerTest::OverrideInstructionSetFeatures(InstructionSet instructi void CommonCompilerTest::CreateCompilerDriver() { ApplyInstructionSet(); - compiler_options_->boot_image_ = true; + compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImage; compiler_options_->compile_pic_ = false; // Non-PIC boot image is a test configuration. compiler_options_->SetCompilerFilter(GetCompilerFilter()); compiler_options_->image_classes_.swap(*GetImageClasses()); @@ -345,7 +345,7 @@ void CommonCompilerTest::SetDexFilesForOatFile(const std::vector<const DexFile*> } void CommonCompilerTest::ClearBootImageOption() { - compiler_options_->boot_image_ = false; + compiler_options_->image_type_ = CompilerOptions::ImageType::kNone; } } // namespace art diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index 4f83d605a3..f61e6c4848 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -41,7 +41,7 @@ class DexToDexDecompilerTest : public CommonCompilerTest { void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) { TimingLogger timings("CompilerDriverTest::CompileAll", false, false); TimingLogger::ScopedTiming t(__FUNCTION__, &timings); - compiler_options_->boot_image_ = false; + compiler_options_->image_type_ = CompilerOptions::ImageType::kNone; compiler_options_->SetCompilerFilter(CompilerFilter::kQuicken); // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate // the results for all the dex files, not just the results for the current dex file. diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index be8e10e41e..3610f1831b 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -44,9 +44,8 @@ CompilerOptions::CompilerOptions() no_inline_from_(), dex_files_for_oat_file_(), image_classes_(), - boot_image_(false), - core_image_(false), - app_image_(false), + image_type_(ImageType::kNone), + compiling_with_core_image_(false), baseline_(false), debuggable_(false), generate_debug_info_(kDefaultGenerateDebugInfo), diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 77f84820e5..12fa251606 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -58,6 +58,12 @@ class CompilerOptions final { static const size_t kDefaultInlineMaxCodeUnits = 32; static constexpr size_t kUnsetInlineMaxCodeUnits = -1; + enum class ImageType : uint8_t { + kNone, // JIT or AOT app compilation producing only an oat file but no image. + kBootImage, // Creating boot image. + kAppImage, // Creating app image. + }; + CompilerOptions(); ~CompilerOptions(); @@ -191,27 +197,23 @@ class CompilerOptions final { // Are we compiling a boot image? bool IsBootImage() const { - return boot_image_; + return image_type_ == ImageType::kBootImage; } bool IsBaseline() const { return baseline_; } - // Are we compiling a core image (small boot image only used for ART testing)? - bool IsCoreImage() const { - // Ensure that `core_image_` => `boot_image_`. - DCHECK(!core_image_ || boot_image_); - return core_image_; - } - // Are we compiling an app image? bool IsAppImage() const { - return app_image_; + return image_type_ == ImageType::kAppImage; } - void DisableAppImage() { - app_image_ = false; + // Returns whether we are compiling against a "core" image, which + // is an indicative we are running tests. The compiler will use that + // information for checking invariants. + bool CompilingWithCoreImage() const { + return compiling_with_core_image_; } // Should the code be compiled as position independent? @@ -356,9 +358,8 @@ class CompilerOptions final { // Must not be empty for real boot image, only for tests pretending to compile boot image. HashSet<std::string> image_classes_; - bool boot_image_; - bool core_image_; - bool app_image_; + ImageType image_type_; + bool compiling_with_core_image_; bool baseline_; bool debuggable_; bool generate_debug_info_; diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index f22f61fa21..bb35065921 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -90,10 +90,11 @@ JitCompiler::JitCompiler() { // Special case max code units for inlining, whose default is "unset" (implictly // meaning no limit). Do this before parsing the actual passed options. compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); + Runtime* runtime = Runtime::Current(); { std::string error_msg; - if (!compiler_options_->ParseCompilerOptions(Runtime::Current()->GetCompilerOptions(), - true /* ignore_unrecognized */, + if (!compiler_options_->ParseCompilerOptions(runtime->GetCompilerOptions(), + /*ignore_unrecognized=*/ true, &error_msg)) { LOG(FATAL) << error_msg; UNREACHABLE(); @@ -103,7 +104,7 @@ JitCompiler::JitCompiler() { compiler_options_->SetNonPic(); // Set debuggability based on the runtime value. - compiler_options_->SetDebuggable(Runtime::Current()->IsJavaDebuggable()); + compiler_options_->SetDebuggable(runtime->IsJavaDebuggable()); const InstructionSet instruction_set = compiler_options_->GetInstructionSet(); if (kRuntimeISA == InstructionSet::kArm) { @@ -112,7 +113,7 @@ JitCompiler::JitCompiler() { DCHECK_EQ(instruction_set, kRuntimeISA); } std::unique_ptr<const InstructionSetFeatures> instruction_set_features; - for (const StringPiece option : Runtime::Current()->GetCompilerOptions()) { + for (const StringPiece option : runtime->GetCompilerOptions()) { VLOG(compiler) << "JIT compiler option " << option; std::string error_msg; if (option.starts_with("--instruction-set-variant=")) { @@ -144,6 +145,8 @@ JitCompiler::JitCompiler() { instruction_set_features = InstructionSetFeatures::FromCppDefines(); } compiler_options_->instruction_set_features_ = std::move(instruction_set_features); + compiler_options_->compiling_with_core_image_ = + CompilerDriver::IsCoreImageFilename(runtime->GetImageLocation()); compiler_driver_.reset(new CompilerDriver( compiler_options_.get(), diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc index e79a96bc2a..5a18c1f72b 100644 --- a/compiler/optimizing/code_generator_vector_arm64.cc +++ b/compiler/optimizing/code_generator_vector_arm64.cc @@ -216,7 +216,7 @@ void InstructionCodeGeneratorARM64::VisitVecReduce(HVecReduce* instruction) { switch (instruction->GetPackedType()) { case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); - switch (instruction->GetKind()) { + switch (instruction->GetReductionKind()) { case HVecReduce::kSum: __ Addv(dst.S(), src.V4S()); break; @@ -230,7 +230,7 @@ void InstructionCodeGeneratorARM64::VisitVecReduce(HVecReduce* instruction) { break; case DataType::Type::kInt64: DCHECK_EQ(2u, instruction->GetVectorLength()); - switch (instruction->GetKind()) { + switch (instruction->GetReductionKind()) { case HVecReduce::kSum: __ Addp(dst.D(), src.V2D()); break; diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc index 62b6c4ea01..b092961a56 100644 --- a/compiler/optimizing/code_generator_vector_arm_vixl.cc +++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc @@ -138,7 +138,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecReduce(HVecReduce* instruction) { switch (instruction->GetPackedType()) { case DataType::Type::kInt32: DCHECK_EQ(2u, instruction->GetVectorLength()); - switch (instruction->GetKind()) { + switch (instruction->GetReductionKind()) { case HVecReduce::kSum: __ Vpadd(DataTypeValue::I32, dst, src, src); break; diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc index 24f4fb2d7b..09e96cc1f4 100644 --- a/compiler/optimizing/code_generator_vector_mips.cc +++ b/compiler/optimizing/code_generator_vector_mips.cc @@ -187,7 +187,7 @@ void InstructionCodeGeneratorMIPS::VisitVecReduce(HVecReduce* instruction) { switch (instruction->GetPackedType()) { case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); - switch (instruction->GetKind()) { + switch (instruction->GetReductionKind()) { case HVecReduce::kSum: __ Hadd_sD(tmp, src, src); __ IlvlD(dst, tmp, tmp); @@ -209,7 +209,7 @@ void InstructionCodeGeneratorMIPS::VisitVecReduce(HVecReduce* instruction) { break; case DataType::Type::kInt64: DCHECK_EQ(2u, instruction->GetVectorLength()); - switch (instruction->GetKind()) { + switch (instruction->GetReductionKind()) { case HVecReduce::kSum: __ IlvlD(dst, src, src); __ AddvD(dst, dst, src); diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc index 972c49ebb1..b6873b1a67 100644 --- a/compiler/optimizing/code_generator_vector_mips64.cc +++ b/compiler/optimizing/code_generator_vector_mips64.cc @@ -185,7 +185,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecReduce(HVecReduce* instruction) { switch (instruction->GetPackedType()) { case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); - switch (instruction->GetKind()) { + switch (instruction->GetReductionKind()) { case HVecReduce::kSum: __ Hadd_sD(tmp, src, src); __ IlvlD(dst, tmp, tmp); @@ -207,7 +207,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecReduce(HVecReduce* instruction) { break; case DataType::Type::kInt64: DCHECK_EQ(2u, instruction->GetVectorLength()); - switch (instruction->GetKind()) { + switch (instruction->GetReductionKind()) { case HVecReduce::kSum: __ IlvlD(dst, src, src); __ AddvD(dst, dst, src); diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc index c52ecc77c5..0ee00356b9 100644 --- a/compiler/optimizing/code_generator_vector_x86.cc +++ b/compiler/optimizing/code_generator_vector_x86.cc @@ -205,8 +205,8 @@ void LocationsBuilderX86::VisitVecReduce(HVecReduce* instruction) { CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction); // Long reduction or min/max require a temporary. if (instruction->GetPackedType() == DataType::Type::kInt64 || - instruction->GetKind() == HVecReduce::kMin || - instruction->GetKind() == HVecReduce::kMax) { + instruction->GetReductionKind() == HVecReduce::kMin || + instruction->GetReductionKind() == HVecReduce::kMax) { instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister()); } } @@ -218,38 +218,23 @@ void InstructionCodeGeneratorX86::VisitVecReduce(HVecReduce* instruction) { switch (instruction->GetPackedType()) { case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); - switch (instruction->GetKind()) { + switch (instruction->GetReductionKind()) { case HVecReduce::kSum: __ movaps(dst, src); __ phaddd(dst, dst); __ phaddd(dst, dst); break; - case HVecReduce::kMin: { - XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); - __ movaps(tmp, src); - __ movaps(dst, src); - __ psrldq(tmp, Immediate(8)); - __ pminsd(dst, tmp); - __ psrldq(tmp, Immediate(4)); - __ pminsd(dst, tmp); - break; - } - case HVecReduce::kMax: { - XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); - __ movaps(tmp, src); - __ movaps(dst, src); - __ psrldq(tmp, Immediate(8)); - __ pmaxsd(dst, tmp); - __ psrldq(tmp, Immediate(4)); - __ pmaxsd(dst, tmp); - break; - } + case HVecReduce::kMin: + case HVecReduce::kMax: + // Historical note: We've had a broken implementation here. b/117863065 + // Do not draw on the old code if we ever want to bring MIN/MAX reduction back. + LOG(FATAL) << "Unsupported reduction type."; } break; case DataType::Type::kInt64: { DCHECK_EQ(2u, instruction->GetVectorLength()); XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); - switch (instruction->GetKind()) { + switch (instruction->GetReductionKind()) { case HVecReduce::kSum: __ movaps(tmp, src); __ movaps(dst, src); diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc index 87d0106c3e..9c2882766c 100644 --- a/compiler/optimizing/code_generator_vector_x86_64.cc +++ b/compiler/optimizing/code_generator_vector_x86_64.cc @@ -188,8 +188,8 @@ void LocationsBuilderX86_64::VisitVecReduce(HVecReduce* instruction) { CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction); // Long reduction or min/max require a temporary. if (instruction->GetPackedType() == DataType::Type::kInt64 || - instruction->GetKind() == HVecReduce::kMin || - instruction->GetKind() == HVecReduce::kMax) { + instruction->GetReductionKind() == HVecReduce::kMin || + instruction->GetReductionKind() == HVecReduce::kMax) { instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister()); } } @@ -201,38 +201,23 @@ void InstructionCodeGeneratorX86_64::VisitVecReduce(HVecReduce* instruction) { switch (instruction->GetPackedType()) { case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); - switch (instruction->GetKind()) { + switch (instruction->GetReductionKind()) { case HVecReduce::kSum: __ movaps(dst, src); __ phaddd(dst, dst); __ phaddd(dst, dst); break; - case HVecReduce::kMin: { - XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); - __ movaps(tmp, src); - __ movaps(dst, src); - __ psrldq(tmp, Immediate(8)); - __ pminsd(dst, tmp); - __ psrldq(tmp, Immediate(4)); - __ pminsd(dst, tmp); - break; - } - case HVecReduce::kMax: { - XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); - __ movaps(tmp, src); - __ movaps(dst, src); - __ psrldq(tmp, Immediate(8)); - __ pmaxsd(dst, tmp); - __ psrldq(tmp, Immediate(4)); - __ pmaxsd(dst, tmp); - break; - } + case HVecReduce::kMin: + case HVecReduce::kMax: + // Historical note: We've had a broken implementation here. b/117863065 + // Do not draw on the old code if we ever want to bring MIN/MAX reduction back. + LOG(FATAL) << "Unsupported reduction type."; } break; case DataType::Type::kInt64: { DCHECK_EQ(2u, instruction->GetVectorLength()); XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); - switch (instruction->GetKind()) { + switch (instruction->GetReductionKind()) { case HVecReduce::kSum: __ movaps(tmp, src); __ movaps(dst, src); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index c1daf95727..45d91675fd 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -39,7 +39,6 @@ #include "mirror/object_array-alloc-inl.h" #include "mirror/object_array-inl.h" #include "nodes.h" -#include "optimizing_compiler.h" #include "reference_type_propagation.h" #include "register_allocator_linear_scan.h" #include "scoped_thread_state_change-inl.h" @@ -151,13 +150,13 @@ bool HInliner::Run() { // If we're compiling with a core image (which is only used for // test purposes), honor inlining directives in method names: - // - if a method's name contains the substring "$inline$", ensure - // that this method is actually inlined; // - if a method's name contains the substring "$noinline$", do not - // inline that method. + // inline that method; + // - if a method's name contains the substring "$inline$", ensure + // that this method is actually inlined. // We limit the latter to AOT compilation, as the JIT may or may not inline // depending on the state of classes at runtime. - const bool honor_noinline_directives = IsCompilingWithCoreImage(); + const bool honor_noinline_directives = codegen_->GetCompilerOptions().CompilingWithCoreImage(); const bool honor_inline_directives = honor_noinline_directives && Runtime::Current()->IsAotCompiler(); @@ -1419,10 +1418,6 @@ size_t HInliner::CountRecursiveCallsOf(ArtMethod* method) const { static inline bool MayInline(const CompilerOptions& compiler_options, const DexFile& inlined_from, const DexFile& inlined_into) { - if (kIsTargetBuild) { - return true; - } - // We're not allowed to inline across dex files if we're the no-inline-from dex file. if (!IsSameDexFile(inlined_from, inlined_into) && ContainsElement(compiler_options.GetNoInlineFromDexFile(), &inlined_from)) { @@ -1737,6 +1732,21 @@ static inline Handle<T> NewHandleIfDifferent(T* object, return (object != hint.Get()) ? handles->NewHandle(object) : hint; } +static bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!Runtime::Current()->IsAotCompiler()) { + // JIT can always encode methods in stack maps. + return true; + } + if (IsSameDexFile(caller_dex_file, *callee->GetDexFile())) { + return true; + } + // TODO(ngeoffray): Support more AOT cases for inlining: + // - methods in multidex + // - methods in boot image for on-device non-PIC compilation. + return false; +} + bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, ArtMethod* resolved_method, ReferenceTypeInfo receiver_type, diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 0c463a280e..4d45a9991c 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -446,7 +446,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMathRint(HInvoke* invoke) { void IntrinsicCodeGeneratorARMVIXL::VisitMathRint(HInvoke* invoke) { DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions()); ArmVIXLAssembler* assembler = GetAssembler(); - __ Vrintn(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0)); + __ Vrintn(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0)); } void IntrinsicLocationsBuilderARMVIXL::VisitMathRoundFloat(HInvoke* invoke) { @@ -481,7 +481,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitMathRoundFloat(HInvoke* invoke) { // Handle input < 0 cases. // If input is negative but not a tie, previous result (round to nearest) is valid. // If input is a negative tie, change rounding direction to positive infinity, out_reg += 1. - __ Vrinta(F32, F32, temp1, in_reg); + __ Vrinta(F32, temp1, in_reg); __ Vmov(temp2, 0.5); __ Vsub(F32, temp1, in_reg, temp1); __ Vcmp(F32, temp1, temp2); @@ -2945,7 +2945,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMathCeil(HInvoke* invoke) { void IntrinsicCodeGeneratorARMVIXL::VisitMathCeil(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions()); - __ Vrintp(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0)); + __ Vrintp(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0)); } void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) { @@ -2957,7 +2957,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) { void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions()); - __ Vrintm(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0)); + __ Vrintm(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0)); } void IntrinsicLocationsBuilderARMVIXL::VisitIntegerValueOf(HInvoke* invoke) { diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 597e399dd1..cd4f45e830 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -384,21 +384,21 @@ class HVecReduce final : public HVecUnaryOperation { HInstruction* input, DataType::Type packed_type, size_t vector_length, - ReductionKind kind, + ReductionKind reduction_kind, uint32_t dex_pc) : HVecUnaryOperation(kVecReduce, allocator, input, packed_type, vector_length, dex_pc), - kind_(kind) { + reduction_kind_(reduction_kind) { DCHECK(HasConsistentPackedTypes(input, packed_type)); } - ReductionKind GetKind() const { return kind_; } + ReductionKind GetReductionKind() const { return reduction_kind_; } bool CanBeMoved() const override { return true; } bool InstructionDataEquals(const HInstruction* other) const override { DCHECK(other->IsVecReduce()); const HVecReduce* o = other->AsVecReduce(); - return HVecOperation::InstructionDataEquals(o) && GetKind() == o->GetKind(); + return HVecOperation::InstructionDataEquals(o) && GetReductionKind() == o->GetReductionKind(); } DECLARE_INSTRUCTION(VecReduce); @@ -407,7 +407,7 @@ class HVecReduce final : public HVecUnaryOperation { DEFAULT_COPY_CONSTRUCTOR(VecReduce); private: - const ReductionKind kind_; + const ReductionKind reduction_kind_; }; // Converts every component in the vector, diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc index af13449646..b0a665d704 100644 --- a/compiler/optimizing/nodes_vector_test.cc +++ b/compiler/optimizing/nodes_vector_test.cc @@ -401,9 +401,9 @@ TEST_F(NodesVectorTest, VectorKindMattersOnReduce) { EXPECT_TRUE(v2->CanBeMoved()); EXPECT_TRUE(v3->CanBeMoved()); - EXPECT_EQ(HVecReduce::kSum, v1->GetKind()); - EXPECT_EQ(HVecReduce::kMin, v2->GetKind()); - EXPECT_EQ(HVecReduce::kMax, v3->GetKind()); + EXPECT_EQ(HVecReduce::kSum, v1->GetReductionKind()); + EXPECT_EQ(HVecReduce::kMin, v2->GetReductionKind()); + EXPECT_EQ(HVecReduce::kMax, v3->GetReductionKind()); EXPECT_TRUE(v1->Equals(v1)); EXPECT_TRUE(v2->Equals(v2)); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index c9c1194e5a..fe6abd4999 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1136,7 +1136,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, } if (kIsDebugBuild && - IsCompilingWithCoreImage() && + compiler_driver->GetCompilerOptions().CompilingWithCoreImage() && IsInstructionSetSupported(compiler_driver->GetCompilerOptions().GetInstructionSet())) { // For testing purposes, we put a special marker on method names // that should be compiled with this compiler (when the @@ -1234,30 +1234,11 @@ Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { return new OptimizingCompiler(driver); } -bool IsCompilingWithCoreImage() { - const std::string& image = Runtime::Current()->GetImageLocation(); - return CompilerDriver::IsCoreImageFilename(image); -} - bool EncodeArtMethodInInlineInfo(ArtMethod* method ATTRIBUTE_UNUSED) { // Note: the runtime is null only for unit testing. return Runtime::Current() == nullptr || !Runtime::Current()->IsAotCompiler(); } -bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee) { - if (!Runtime::Current()->IsAotCompiler()) { - // JIT can always encode methods in stack maps. - return true; - } - if (IsSameDexFile(caller_dex_file, *callee->GetDexFile())) { - return true; - } - // TODO(ngeoffray): Support more AOT cases for inlining: - // - methods in multidex - // - methods in boot image for on-device non-PIC compilation. - return false; -} - bool OptimizingCompiler::JitCompile(Thread* self, jit::JitCodeCache* code_cache, ArtMethod* method, diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h index 6ee9c70fdb..f5279e83eb 100644 --- a/compiler/optimizing/optimizing_compiler.h +++ b/compiler/optimizing/optimizing_compiler.h @@ -29,14 +29,7 @@ class DexFile; Compiler* CreateOptimizingCompiler(CompilerDriver* driver); -// Returns whether we are compiling against a "core" image, which -// is an indicative we are running tests. The compiler will use that -// information for checking invariants. -bool IsCompilingWithCoreImage(); - bool EncodeArtMethodInInlineInfo(ArtMethod* method); -bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee) - REQUIRES_SHARED(Locks::mutator_lock_); } // namespace art diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index e1b23cc315..eb44dd7f1d 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -79,7 +79,7 @@ class VerifierDepsTest : public CommonCompilerTest { } void SetupCompilerDriver() { - compiler_options_->boot_image_ = false; + compiler_options_->image_type_ = CompilerOptions::ImageType::kNone; compiler_driver_->InitializeThreadPools(); } diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index fd5f3cd9e4..8ce96a4c16 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -71,11 +71,8 @@ art_cc_defaults { generated_sources: ["art_dex2oat_operator_srcs"], shared_libs: [ "libbase", - "liblz4", - "liblzma", ], include_dirs: [ - "external/lz4/lib", "external/zlib", ], export_include_dirs: ["."], @@ -98,8 +95,6 @@ cc_defaults { }, static_libs: [ "libbase", - "liblz4", - "liblzma", ], } @@ -247,7 +242,6 @@ art_cc_binary { "libdexfile", "libartbase", "libbase", - "liblz4", "libsigchain", ], static_libs: [ @@ -285,7 +279,6 @@ art_cc_binary { "libdexfiled", "libartbased", "libbase", - "liblz4", "libsigchain", ], static_libs: [ @@ -315,7 +308,6 @@ cc_defaults { ], static_libs: [ "libbase", - "liblz4", "libsigchain_dummy", ], } @@ -411,11 +403,11 @@ art_cc_test { "external/zlib", ], shared_libs: [ - "libprofiled", + "libartbased", "libartd-compiler", "libartd-dexlayout", "libbase", - "liblz4", + "libprofiled", "libsigchain", "libziparchive", ], diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 77928d3299..12a8354007 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -729,16 +729,15 @@ class Dex2Oat final { void ProcessOptions(ParserOptions* parser_options) { compiler_options_->compile_pic_ = true; // All AOT compilation is PIC. - compiler_options_->boot_image_ = !image_filenames_.empty(); - compiler_options_->app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty(); - - if (IsBootImage() && image_filenames_.size() == 1) { - const std::string& boot_image_filename = image_filenames_[0]; - compiler_options_->core_image_ = CompilerDriver::IsCoreImageFilename(boot_image_filename); + DCHECK(compiler_options_->image_type_ == CompilerOptions::ImageType::kNone); + if (!image_filenames_.empty()) { + compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImage; } - - if (IsAppImage() && IsBootImage()) { - Usage("Can't have both --image and (--app-image-fd or --app-image-file)"); + if (app_image_fd_ != -1 || !app_image_file_name_.empty()) { + if (compiler_options_->IsBootImage()) { + Usage("Can't have both --image and (--app-image-fd or --app-image-file)"); + } + compiler_options_->image_type_ = CompilerOptions::ImageType::kAppImage; } if (oat_filenames_.empty() && oat_fd_ == -1) { @@ -951,6 +950,9 @@ class Dex2Oat final { } } compiler_options_->passes_to_run_ = passes_to_run_.get(); + compiler_options_->compiling_with_core_image_ = + !boot_image_filename_.empty() && + CompilerDriver::IsCoreImageFilename(boot_image_filename_); } static bool SupportsDeterministicCompilation() { @@ -1597,7 +1599,7 @@ class Dex2Oat final { // If we need to downgrade the compiler-filter for size reasons. if (!IsBootImage() && IsVeryLarge(dex_files)) { // Disable app image to make sure dex2oat unloading is enabled. - compiler_options_->DisableAppImage(); + compiler_options_->image_type_ = CompilerOptions::ImageType::kNone; // If we need to downgrade the compiler-filter for size reasons, do that early before we read // it below for creating verification callbacks. @@ -1959,7 +1961,6 @@ class Dex2Oat final { image_writer_.reset(new linker::ImageWriter(*compiler_options_, image_base_, - IsAppImage(), image_storage_mode_, oat_filenames_, dex_file_oat_index_map_, diff --git a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc index 3d7277aab3..b93e091ae6 100644 --- a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc +++ b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc @@ -827,26 +827,38 @@ void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t r } } -#define TEST_BAKER_FIELD_WIDE(offset, ref_reg) \ - TEST_F(Thumb2RelativePatcherTest, \ - BakerOffsetWide##offset##_##ref_reg) { \ - TestBakerFieldWide(offset, ref_reg); \ +TEST_F(Thumb2RelativePatcherTest, BakerOffsetWide) { + struct TestCase { + uint32_t offset; + uint32_t ref_reg; + }; + static const TestCase test_cases[] = { + { 0u, 0u }, + { 8u, 3u }, + { 28u, 7u }, + { 0xffcu, 11u }, + }; + for (const TestCase& test_case : test_cases) { + Reset(); + TestBakerFieldWide(test_case.offset, test_case.ref_reg); } +} -TEST_BAKER_FIELD_WIDE(/* offset */ 0, /* ref_reg */ 0) -TEST_BAKER_FIELD_WIDE(/* offset */ 8, /* ref_reg */ 3) -TEST_BAKER_FIELD_WIDE(/* offset */ 28, /* ref_reg */ 7) -TEST_BAKER_FIELD_WIDE(/* offset */ 0xffc, /* ref_reg */ 11) - -#define TEST_BAKER_FIELD_NARROW(offset, ref_reg) \ - TEST_F(Thumb2RelativePatcherTest, \ - BakerOffsetNarrow##offset##_##ref_reg) { \ - TestBakerFieldNarrow(offset, ref_reg); \ +TEST_F(Thumb2RelativePatcherTest, BakerOffsetNarrow) { + struct TestCase { + uint32_t offset; + uint32_t ref_reg; + }; + static const TestCase test_cases[] = { + { 0, 0u }, + { 8, 3u }, + { 28, 7u }, + }; + for (const TestCase& test_case : test_cases) { + Reset(); + TestBakerFieldNarrow(test_case.offset, test_case.ref_reg); } - -TEST_BAKER_FIELD_NARROW(/* offset */ 0, /* ref_reg */ 0) -TEST_BAKER_FIELD_NARROW(/* offset */ 8, /* ref_reg */ 3) -TEST_BAKER_FIELD_NARROW(/* offset */ 28, /* ref_reg */ 7) +} TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddle) { // One thunk in the middle with maximum distance branches to it from both sides. diff --git a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc index 9e3bb978fb..0fc4610909 100644 --- a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc +++ b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc @@ -783,174 +783,242 @@ TEST_F(Arm64RelativePatcherTestDefault, CallOtherJustTooFarBefore) { EXPECT_TRUE(CheckThunk(thunk_offset)); } -TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry1) { - TestNopsAdrpLdr(0u, 0x12345678u, 0x1234u); -} - -TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry2) { - TestNopsAdrpLdr(0u, -0x12345678u, 0x4444u); -} - -TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry3) { - TestNopsAdrpLdr(0u, 0x12345000u, 0x3ffcu); -} - -TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry4) { - TestNopsAdrpLdr(0u, 0x12345000u, 0x4000u); +TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry) { + struct TestCase { + uint32_t bss_begin; + uint32_t string_entry_offset; + }; + static const TestCase test_cases[] = { + { 0x12345678u, 0x1234u }, + { -0x12345678u, 0x4444u }, + { 0x12345000u, 0x3ffcu }, + { 0x12345000u, 0x4000u } + }; + for (const TestCase& test_case : test_cases) { + Reset(); + TestNopsAdrpLdr(/*num_nops=*/ 0u, test_case.bss_begin, test_case.string_entry_offset); + } } -TEST_F(Arm64RelativePatcherTestDefault, StringReference1) { - TestNopsAdrpAdd(0u, 0x12345678u); +TEST_F(Arm64RelativePatcherTestDefault, StringReference) { + for (uint32_t string_offset : { 0x12345678u, -0x12345678u, 0x12345000u, 0x12345ffcu}) { + Reset(); + TestNopsAdrpAdd(/*num_nops=*/ 0u, string_offset); + } } -TEST_F(Arm64RelativePatcherTestDefault, StringReference2) { - TestNopsAdrpAdd(0u, -0x12345678u); +template <typename Test> +void TestForAdrpOffsets(Test test, std::initializer_list<uint32_t> args) { + for (uint32_t adrp_offset : { 0xff4u, 0xff8u, 0xffcu, 0x1000u }) { + for (uint32_t arg : args) { + test(adrp_offset, arg); + } + } } -TEST_F(Arm64RelativePatcherTestDefault, StringReference3) { - TestNopsAdrpAdd(0u, 0x12345000u); +TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryLdur) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t string_entry_offset) { + Reset(); + bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); + TestAdrpLdurLdr(adrp_offset, has_thunk, /*bss_begin=*/ 0x12345678u, string_entry_offset); + }, + { 0x1234u, 0x1238u }); } -TEST_F(Arm64RelativePatcherTestDefault, StringReference4) { - TestNopsAdrpAdd(0u, 0x12345ffcu); +TEST_F(Arm64RelativePatcherTestDenver64, StringBssEntryLdur) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t string_entry_offset) { + Reset(); + TestAdrpLdurLdr(adrp_offset, + /*has_thunk=*/ false, + /*bss_begin=*/ 0x12345678u, + string_entry_offset); + }, + { 0x1234u, 0x1238u }); } -#define TEST_FOR_OFFSETS(test, disp1, disp2) \ - test(0xff4u, disp1) test(0xff8u, disp1) test(0xffcu, disp1) test(0x1000u, disp1) \ - test(0xff4u, disp2) test(0xff8u, disp2) test(0xffcu, disp2) test(0x1000u, disp2) - -#define DEFAULT_LDUR_LDR_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## Ldur ## disp) { \ - bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \ - TestAdrpLdurLdr(adrp_offset, has_thunk, 0x12345678u, disp); \ - } - -TEST_FOR_OFFSETS(DEFAULT_LDUR_LDR_TEST, 0x1234, 0x1238) - -#define DENVER64_LDUR_LDR_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDenver64, StringBssEntry ## adrp_offset ## Ldur ## disp) { \ - TestAdrpLdurLdr(adrp_offset, false, 0x12345678u, disp); \ - } - -TEST_FOR_OFFSETS(DENVER64_LDUR_LDR_TEST, 0x1234, 0x1238) - // LDR <Wt>, <label> is always aligned. We should never have to use a fixup. -#define LDRW_PCREL_LDR_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## WPcRel ## disp) { \ - TestAdrpLdrPcRelLdr(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u, 0x1234u); \ - } - -TEST_FOR_OFFSETS(LDRW_PCREL_LDR_TEST, 0x1234, 0x1238) +TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryWPcRel) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t pcrel_disp) { + Reset(); + TestAdrpLdrPcRelLdr(kLdrWPcRelInsn, + pcrel_disp, + adrp_offset, + /*has_thunk=*/ false, + /*bss_begin=*/ 0x12345678u, + /*string_entry_offset=*/ 0x1234u); + }, + { 0x1234u, 0x1238u }); +} // LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8. -#define LDRX_PCREL_LDR_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## XPcRel ## disp) { \ - bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(disp)); \ - bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned; \ - TestAdrpLdrPcRelLdr(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u, 0x1234u); \ - } - -TEST_FOR_OFFSETS(LDRX_PCREL_LDR_TEST, 0x1234, 0x1238) +TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryXPcRel) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t pcrel_disp) { + Reset(); + bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(pcrel_disp)); + bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned; + TestAdrpLdrPcRelLdr(kLdrXPcRelInsn, + pcrel_disp, + adrp_offset, + has_thunk, + /*bss_begin=*/ 0x12345678u, + /*string_entry_offset=*/ 0x1234u); + }, + { 0x1234u, 0x1238u }); +} // LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed. -#define LDRW_SPREL_LDR_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## WSpRel ## disp) { \ - TestAdrpLdrSpRelLdr(kLdrWSpRelInsn, (disp) >> 2, adrp_offset, false, 0x12345678u, 0x1234u); \ - } - -TEST_FOR_OFFSETS(LDRW_SPREL_LDR_TEST, 0, 4) - -#define LDRX_SPREL_LDR_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## XSpRel ## disp) { \ - TestAdrpLdrSpRelLdr(kLdrXSpRelInsn, (disp) >> 3, adrp_offset, false, 0x12345678u, 0x1234u); \ - } - -TEST_FOR_OFFSETS(LDRX_SPREL_LDR_TEST, 0, 8) - -#define DEFAULT_LDUR_ADD_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## Ldur ## disp) { \ - bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \ - TestAdrpLdurAdd(adrp_offset, has_thunk, disp); \ - } - -TEST_FOR_OFFSETS(DEFAULT_LDUR_ADD_TEST, 0x12345678, 0xffffc840) - -#define DENVER64_LDUR_ADD_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDenver64, StringReference ## adrp_offset ## Ldur ## disp) { \ - TestAdrpLdurAdd(adrp_offset, false, disp); \ - } - -TEST_FOR_OFFSETS(DENVER64_LDUR_ADD_TEST, 0x12345678, 0xffffc840) - -#define DEFAULT_SUBX3X2_ADD_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubX3X2 ## disp) { \ - /* SUB unrelated to "ADRP x0, addr". */ \ - uint32_t sub = kSubXInsn | (100 << 10) | (2u << 5) | 3u; /* SUB x3, x2, #100 */ \ - TestAdrpInsn2Add(sub, adrp_offset, false, disp); \ - } +TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryWSpRel) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t disp) { + Reset(); + TestAdrpLdrSpRelLdr(kLdrWSpRelInsn, + /*sprel_disp_in_load_units=*/ disp >> 2, + adrp_offset, + /*has_thunk=*/ false, + /*bss_begin=*/ 0x12345678u, + /*string_entry_offset=*/ 0x1234u); + }, + { 0u, 4u }); +} -TEST_FOR_OFFSETS(DEFAULT_SUBX3X2_ADD_TEST, 0x12345678, 0xffffc840) +TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryXSpRel) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t disp) { + Reset(); + TestAdrpLdrSpRelLdr(kLdrXSpRelInsn, + /*sprel_disp_in_load_units=*/ (disp) >> 3, + adrp_offset, + /*has_thunk=*/ false, + /*bss_begin=*/ 0x12345678u, + /*string_entry_offset=*/ 0x1234u); + }, + { 0u, 8u }); +} -#define DEFAULT_SUBSX3X0_ADD_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubsX3X0 ## disp) { \ - /* SUBS that uses the result of "ADRP x0, addr". */ \ - uint32_t subs = kSubsXInsn | (100 << 10) | (0u << 5) | 3u; /* SUBS x3, x0, #100 */ \ - TestAdrpInsn2Add(subs, adrp_offset, false, disp); \ - } +TEST_F(Arm64RelativePatcherTestDefault, StringReferenceLdur) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t string_offset) { + Reset(); + bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); + TestAdrpLdurAdd(adrp_offset, has_thunk, string_offset); + }, + { 0x12345678u, 0xffffc840u }); +} -TEST_FOR_OFFSETS(DEFAULT_SUBSX3X0_ADD_TEST, 0x12345678, 0xffffc840) +TEST_F(Arm64RelativePatcherTestDenver64, StringReferenceLdur) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t string_offset) { + Reset(); + TestAdrpLdurAdd(adrp_offset, /*has_thunk=*/ false, string_offset); + }, + { 0x12345678u, 0xffffc840U }); +} -#define DEFAULT_ADDX0X0_ADD_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddX0X0 ## disp) { \ - /* ADD that uses the result register of "ADRP x0, addr" as both source and destination. */ \ - uint32_t add = kSubXInsn | (100 << 10) | (0u << 5) | 0u; /* ADD x0, x0, #100 */ \ - TestAdrpInsn2Add(add, adrp_offset, false, disp); \ - } +TEST_F(Arm64RelativePatcherTestDefault, StringReferenceSubX3X2) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t string_offset) { + Reset(); + /* SUB unrelated to "ADRP x0, addr". */ \ + uint32_t sub = kSubXInsn | (100 << 10) | (2u << 5) | 3u; /* SUB x3, x2, #100 */ + TestAdrpInsn2Add(sub, adrp_offset, /*has_thunk=*/ false, string_offset); + }, + { 0x12345678u, 0xffffc840u }); +} -TEST_FOR_OFFSETS(DEFAULT_ADDX0X0_ADD_TEST, 0x12345678, 0xffffc840) +TEST_F(Arm64RelativePatcherTestDefault, StringReferenceSubsX3X0) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t string_offset) { + Reset(); + /* SUBS that uses the result of "ADRP x0, addr". */ \ + uint32_t subs = kSubsXInsn | (100 << 10) | (0u << 5) | 3u; /* SUBS x3, x0, #100 */ + TestAdrpInsn2Add(subs, adrp_offset, /*has_thunk=*/ false, string_offset); + }, + { 0x12345678u, 0xffffc840u }); +} -#define DEFAULT_ADDSX0X2_ADD_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddsX0X2 ## disp) { \ - /* ADDS that does not use the result of "ADRP x0, addr" but overwrites that register. */ \ - uint32_t adds = kAddsXInsn | (100 << 10) | (2u << 5) | 0u; /* ADDS x0, x2, #100 */ \ - bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \ - TestAdrpInsn2Add(adds, adrp_offset, has_thunk, disp); \ - } +TEST_F(Arm64RelativePatcherTestDefault, StringReferenceAddX0X0) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t string_offset) { + Reset(); + /* ADD that uses the result register of "ADRP x0, addr" as both source and destination. */ + uint32_t add = kSubXInsn | (100 << 10) | (0u << 5) | 0u; /* ADD x0, x0, #100 */ + TestAdrpInsn2Add(add, adrp_offset, /*has_thunk=*/ false, string_offset); + }, + { 0x12345678u, 0xffffc840 }); +} -TEST_FOR_OFFSETS(DEFAULT_ADDSX0X2_ADD_TEST, 0x12345678, 0xffffc840) +TEST_F(Arm64RelativePatcherTestDefault, StringReferenceAddsX0X2) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t string_offset) { + Reset(); + /* ADDS that does not use the result of "ADRP x0, addr" but overwrites that register. */ + uint32_t adds = kAddsXInsn | (100 << 10) | (2u << 5) | 0u; /* ADDS x0, x2, #100 */ + bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); + TestAdrpInsn2Add(adds, adrp_offset, has_thunk, string_offset); + }, + { 0x12345678u, 0xffffc840u }); +} // LDR <Wt>, <label> is always aligned. We should never have to use a fixup. -#define LDRW_PCREL_ADD_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WPcRel ## disp) { \ - TestAdrpLdrPcRelAdd(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u); \ - } - -TEST_FOR_OFFSETS(LDRW_PCREL_ADD_TEST, 0x1234, 0x1238) +TEST_F(Arm64RelativePatcherTestDefault, StringReferenceWPcRel) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t pcrel_disp) { + Reset(); + TestAdrpLdrPcRelAdd(kLdrWPcRelInsn, + pcrel_disp, + adrp_offset, + /*has_thunk=*/ false, + /*string_offset=*/ 0x12345678u); + }, + { 0x1234u, 0x1238u }); +} // LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8. -#define LDRX_PCREL_ADD_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XPcRel ## disp) { \ - bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(disp)); \ - bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned; \ - TestAdrpLdrPcRelAdd(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u); \ - } - -TEST_FOR_OFFSETS(LDRX_PCREL_ADD_TEST, 0x1234, 0x1238) +TEST_F(Arm64RelativePatcherTestDefault, StringReferenceXPcRel) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t pcrel_disp) { + Reset(); + bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(pcrel_disp)); + bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned; + TestAdrpLdrPcRelAdd(kLdrXPcRelInsn, + pcrel_disp, + adrp_offset, + has_thunk, + /*string_offset=*/ 0x12345678u); + }, + { 0x1234u, 0x1238u }); +} // LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed. -#define LDRW_SPREL_ADD_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WSpRel ## disp) { \ - TestAdrpLdrSpRelAdd(kLdrWSpRelInsn, (disp) >> 2, adrp_offset, false, 0x12345678u); \ - } - -TEST_FOR_OFFSETS(LDRW_SPREL_ADD_TEST, 0, 4) - -#define LDRX_SPREL_ADD_TEST(adrp_offset, disp) \ - TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XSpRel ## disp) { \ - TestAdrpLdrSpRelAdd(kLdrXSpRelInsn, (disp) >> 3, adrp_offset, false, 0x12345678u); \ - } +TEST_F(Arm64RelativePatcherTestDefault, StringReferenceWSpRel) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t disp) { + Reset(); + TestAdrpLdrSpRelAdd(kLdrWSpRelInsn, + /*sprel_disp_in_load_units=*/ (disp) >> 2, + adrp_offset, + /*has_thunk=*/ false, + /*string_offset=*/ 0x12345678u); + }, + { 0u, 4u }); +} -TEST_FOR_OFFSETS(LDRX_SPREL_ADD_TEST, 0, 8) +TEST_F(Arm64RelativePatcherTestDefault, StringReferenceXSpRel) { + TestForAdrpOffsets( + [&](uint32_t adrp_offset, uint32_t disp) { + Reset(); + TestAdrpLdrSpRelAdd(kLdrXSpRelInsn, + /*sprel_disp_in_load_units=*/ (disp) >> 3, + adrp_offset, + /*has_thunk=*/ false, + /*string_offset=*/ 0x12345678u); + }, + { 0u, 8u }); +} void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) { uint32_t valid_regs[] = { @@ -1039,15 +1107,22 @@ void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) } } -#define TEST_BAKER_FIELD(offset, ref_reg) \ - TEST_F(Arm64RelativePatcherTestDefault, \ - BakerOffset##offset##_##ref_reg) { \ - TestBakerField(offset, ref_reg); \ +TEST_F(Arm64RelativePatcherTestDefault, BakerOffset) { + struct TestCase { + uint32_t offset; + uint32_t ref_reg; + }; + static const TestCase test_cases[] = { + { 0u, 0u }, + { 8u, 15u}, + { 0x3ffcu, 29u }, + }; + for (const TestCase& test_case : test_cases) { + Reset(); + TestBakerField(test_case.offset, test_case.ref_reg); } +} -TEST_BAKER_FIELD(/* offset */ 0, /* ref_reg */ 0) -TEST_BAKER_FIELD(/* offset */ 8, /* ref_reg */ 15) -TEST_BAKER_FIELD(/* offset */ 0x3ffc, /* ref_reg */ 29) TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) { // One thunk in the middle with maximum distance branches to it from both sides. diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 9d1a4e7b44..9ef2875017 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -216,11 +216,10 @@ inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode, // TODO: compile_pic should be a test argument. std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_options_, kRequestedImageBase, - /*compile_app_image*/false, storage_mode, oat_filename_vector, dex_file_to_oat_index_map, - /*dirty_image_objects*/nullptr)); + /*dirty_image_objects=*/ nullptr)); { { jobject class_loader = nullptr; diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 2b2b02941a..a3fc1cdf2d 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -145,10 +145,15 @@ static ArrayRef<const uint8_t> MaybeCompressData(ArrayRef<const uint8_t> source, // Separate objects into multiple bins to optimize dirty memory use. static constexpr bool kBinObjects = true; +ObjPtr<mirror::ClassLoader> ImageWriter::GetClassLoader() { + CHECK_EQ(class_loaders_.size(), compiler_options_.IsAppImage() ? 1u : 0u); + return compiler_options_.IsAppImage() ? *class_loaders_.begin() : nullptr; +} + // Return true if an object is already in an image space. bool ImageWriter::IsInBootImage(const void* obj) const { gc::Heap* const heap = Runtime::Current()->GetHeap(); - if (!compile_app_image_) { + if (compiler_options_.IsBootImage()) { DCHECK(heap->GetBootImageSpaces().empty()); return false; } @@ -165,7 +170,7 @@ bool ImageWriter::IsInBootImage(const void* obj) const { bool ImageWriter::IsInBootOatFile(const void* ptr) const { gc::Heap* const heap = Runtime::Current()->GetHeap(); - if (!compile_app_image_) { + if (compiler_options_.IsBootImage()) { DCHECK(heap->GetBootImageSpaces().empty()); return false; } @@ -204,7 +209,7 @@ bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) { PruneNonImageClasses(); // Remove junk } - if (compile_app_image_) { + if (compiler_options_.IsAppImage()) { TimingLogger::ScopedTiming t("ClearDexFileCookies", timings); // Clear dex file cookies for app images to enable app image determinism. This is required // since the cookie field contains long pointers to DexFiles which are not deterministic. @@ -226,7 +231,7 @@ bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) { // Used to store information that will later be used to calculate image // offsets to string references in the AppImage. std::vector<HeapReferencePointerInfo> string_ref_info; - if (ClassLinker::kAppImageMayContainStrings && compile_app_image_) { + if (ClassLinker::kAppImageMayContainStrings && compiler_options_.IsAppImage()) { // Count the number of string fields so we can allocate the appropriate // amount of space in the image section. TimingLogger::ScopedTiming t("AppImage:CollectStringReferenceInfo", timings); @@ -248,7 +253,7 @@ bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) { } // Obtain class count for debugging purposes - if (VLOG_IS_ON(compiler) && compile_app_image_) { + if (VLOG_IS_ON(compiler) && compiler_options_.IsAppImage()) { ScopedObjectAccess soa(self); size_t app_image_class_count = 0; @@ -268,7 +273,7 @@ bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) { VLOG(compiler) << "Dex2Oat:AppImage:classCount = " << app_image_class_count; } - if (ClassLinker::kAppImageMayContainStrings && compile_app_image_) { + if (ClassLinker::kAppImageMayContainStrings && compiler_options_.IsAppImage()) { // Use the string reference information obtained earlier to calculate image // offsets. These will later be written to the image by Write/CopyMetadata. TimingLogger::ScopedTiming t("AppImage:CalculateImageOffsets", timings); @@ -580,7 +585,7 @@ void ImageWriter::VerifyNativeGCRootInvariants() const REQUIRES_SHARED(Locks::mu } void ImageWriter::CopyMetadata() { - CHECK(compile_app_image_); + DCHECK(compiler_options_.IsAppImage()); CHECK_EQ(image_infos_.size(), 1u); const ImageInfo& image_info = image_infos_.back(); @@ -613,8 +618,22 @@ bool ImageWriter::Write(int image_fd, CHECK(!oat_filenames.empty()); CHECK_EQ(image_filenames.size(), oat_filenames.size()); + Thread* self = Thread::Current(); { - ScopedObjectAccess soa(Thread::Current()); + // Preload deterministic contents to the dex cache arrays we're going to write. + ScopedObjectAccess soa(self); + ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader(); + std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self); + for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) { + if (IsInBootImage(dex_cache.Ptr())) { + continue; // Boot image DexCache is not written to the app image. + } + PreloadDexCache(dex_cache, class_loader); + } + } + + { + ScopedObjectAccess soa(self); for (size_t i = 0; i < oat_filenames.size(); ++i) { CreateHeader(i); CopyAndFixupNativeData(i); @@ -623,12 +642,12 @@ bool ImageWriter::Write(int image_fd, { // TODO: heap validation can't handle these fix up passes. - ScopedObjectAccess soa(Thread::Current()); + ScopedObjectAccess soa(self); Runtime::Current()->GetHeap()->DisableObjectValidation(); CopyAndFixupObjects(); } - if (compile_app_image_) { + if (compiler_options_.IsAppImage()) { CopyMetadata(); } @@ -656,7 +675,7 @@ bool ImageWriter::Write(int image_fd, return false; } - if (!compile_app_image_ && fchmod(image_file->Fd(), 0644) != 0) { + if (!compiler_options_.IsAppImage() && fchmod(image_file->Fd(), 0644) != 0) { PLOG(ERROR) << "Failed to make image file world readable: " << image_filename; image_file->Erase(); return EXIT_FAILURE; @@ -1209,7 +1228,7 @@ bool ImageWriter::PruneAppImageClassInternal( std::unordered_set<mirror::Object*>* visited) { DCHECK(early_exit != nullptr); DCHECK(visited != nullptr); - DCHECK(compile_app_image_); + DCHECK(compiler_options_.IsAppImage()); if (klass == nullptr || IsInBootImage(klass.Ptr())) { return false; } @@ -1313,7 +1332,8 @@ bool ImageWriter::KeepClass(ObjPtr<mirror::Class> klass) { if (klass == nullptr) { return false; } - if (compile_app_image_ && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { + if (!compiler_options_.IsBootImage() && + Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { // Already in boot image, return true. return true; } @@ -1321,7 +1341,7 @@ bool ImageWriter::KeepClass(ObjPtr<mirror::Class> klass) { if (!compiler_options_.IsImageClass(klass->GetDescriptor(&temp))) { return false; } - if (compile_app_image_) { + if (compiler_options_.IsAppImage()) { // For app images, we need to prune boot loader classes that are not in the boot image since // these may have already been loaded when the app image is loaded. // Keep classes in the boot image space since we don't want to re-resolve these. @@ -1409,8 +1429,79 @@ void ImageWriter::VisitClassLoaders(ClassLoaderVisitor* visitor) { Runtime::Current()->GetClassLinker()->VisitClassLoaders(visitor); } -void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> class_loader) { +void ImageWriter::PruneDexCache(ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) { + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + const DexFile& dex_file = *dex_cache->GetDexFile(); + // Prune methods. + dex::TypeIndex last_class_idx; // Initialized to invalid index. + ObjPtr<mirror::Class> last_class = nullptr; + mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods(); + for (size_t slot_idx = 0, num = dex_cache->NumResolvedMethods(); slot_idx != num; ++slot_idx) { + auto pair = + mirror::DexCache::GetNativePairPtrSize(resolved_methods, slot_idx, target_ptr_size_); + uint32_t stored_index = pair.index; + ArtMethod* method = pair.object; + if (method == nullptr) { + continue; // Empty entry. + } + // Check if the referenced class is in the image. Note that we want to check the referenced + // class rather than the declaring class to preserve the semantics, i.e. using a MethodId + // results in resolving the referenced class and that can for example throw OOME. + const DexFile::MethodId& method_id = dex_file.GetMethodId(stored_index); + if (method_id.class_idx_ != last_class_idx) { + last_class_idx = method_id.class_idx_; + last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader); + if (last_class != nullptr && !KeepClass(last_class)) { + last_class = nullptr; + } + } + if (last_class == nullptr) { + dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_); + } + } + // Prune fields. + mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields(); + last_class_idx = dex::TypeIndex(); // Initialized to invalid index. + last_class = nullptr; + for (size_t slot_idx = 0, num = dex_cache->NumResolvedFields(); slot_idx != num; ++slot_idx) { + auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_fields, slot_idx, target_ptr_size_); + uint32_t stored_index = pair.index; + ArtField* field = pair.object; + if (field == nullptr) { + continue; // Empty entry. + } + // Check if the referenced class is in the image. Note that we want to check the referenced + // class rather than the declaring class to preserve the semantics, i.e. using a FieldId + // results in resolving the referenced class and that can for example throw OOME. + const DexFile::FieldId& field_id = dex_file.GetFieldId(stored_index); + if (field_id.class_idx_ != last_class_idx) { + last_class_idx = field_id.class_idx_; + last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader); + if (last_class != nullptr && !KeepClass(last_class)) { + last_class = nullptr; + } + } + if (last_class == nullptr) { + dex_cache->ClearResolvedField(stored_index, target_ptr_size_); + } + } + // Prune types. + for (size_t slot_idx = 0, num = dex_cache->NumResolvedTypes(); slot_idx != num; ++slot_idx) { + mirror::TypeDexCachePair pair = + dex_cache->GetResolvedTypes()[slot_idx].load(std::memory_order_relaxed); + uint32_t stored_index = pair.index; + ObjPtr<mirror::Class> klass = pair.object.Read(); + if (klass != nullptr && !KeepClass(klass)) { + dex_cache->ClearResolvedType(dex::TypeIndex(stored_index)); + } + } + // Strings do not need pruning. +} + +void ImageWriter::PreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) { // To ensure deterministic contents of the hash-based arrays, each slot shall contain // the candidate with the lowest index. As we're processing entries in increasing index // order, this means trying to look up the entry for the current index if the slot is @@ -1419,7 +1510,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); const DexFile& dex_file = *dex_cache->GetDexFile(); - // Prune methods. + // Preload the methods array and make the contents deterministic. mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods(); dex::TypeIndex last_class_idx; // Initialized to invalid index. ObjPtr<mirror::Class> last_class = nullptr; @@ -1439,27 +1530,20 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, if (method_id.class_idx_ != last_class_idx) { last_class_idx = method_id.class_idx_; last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader); - if (last_class != nullptr && !KeepClass(last_class)) { - last_class = nullptr; - } } if (method == nullptr || i < stored_index) { if (last_class != nullptr) { // Try to resolve the method with the class linker, which will insert // it into the dex cache if successful. method = class_linker->FindResolvedMethod(last_class, dex_cache, class_loader, i); - // If the referenced class is in the image, the defining class must also be there. - DCHECK(method == nullptr || KeepClass(method->GetDeclaringClass())); DCHECK(method == nullptr || dex_cache->GetResolvedMethod(i, target_ptr_size_) == method); } } else { DCHECK_EQ(i, stored_index); - if (last_class == nullptr) { - dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_); - } + DCHECK(last_class != nullptr); } } - // Prune fields and make the contents of the field array deterministic. + // Preload the fields array and make the contents deterministic. mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields(); last_class_idx = dex::TypeIndex(); // Initialized to invalid index. last_class = nullptr; @@ -1484,19 +1568,17 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, } if (field == nullptr || i < stored_index) { if (last_class != nullptr) { + // Try to resolve the field with the class linker, which will insert + // it into the dex cache if successful. field = class_linker->FindResolvedFieldJLS(last_class, dex_cache, class_loader, i); - // If the referenced class is in the image, the defining class must also be there. - DCHECK(field == nullptr || KeepClass(field->GetDeclaringClass())); DCHECK(field == nullptr || dex_cache->GetResolvedField(i, target_ptr_size_) == field); } } else { DCHECK_EQ(i, stored_index); - if (last_class == nullptr) { - dex_cache->ClearResolvedField(stored_index, target_ptr_size_); - } + DCHECK(last_class != nullptr); } } - // Prune types and make the contents of the type array deterministic. + // Preload the types array and make the contents deterministic. // This is done after fields and methods as their lookup can touch the types array. for (size_t i = 0, end = dex_cache->GetDexFile()->NumTypeIds(); i < end; ++i) { dex::TypeIndex type_idx(i); @@ -1507,15 +1589,10 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::Class> klass = pair.object.Read(); if (klass == nullptr || i < stored_index) { klass = class_linker->LookupResolvedType(type_idx, dex_cache, class_loader); - if (klass != nullptr) { - DCHECK_EQ(dex_cache->GetResolvedType(type_idx), klass); - stored_index = i; // For correct clearing below if not keeping the `klass`. - } - } else if (i == stored_index && !KeepClass(klass)) { - dex_cache->ClearResolvedType(dex::TypeIndex(stored_index)); + DCHECK(klass == nullptr || dex_cache->GetResolvedType(type_idx) == klass); } } - // Strings do not need pruning, but the contents of the string array must be deterministic. + // Preload the strings array and make the contents deterministic. for (size_t i = 0, end = dex_cache->GetDexFile()->NumStringIds(); i < end; ++i) { dex::StringIndex string_idx(i); uint32_t slot_idx = dex_cache->StringSlotIndex(string_idx); @@ -1560,25 +1637,15 @@ void ImageWriter::PruneNonImageClasses() { VisitClassLoaders(&class_loader_visitor); VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes"; class_loader = class_loader_visitor.GetClassLoader(); - DCHECK_EQ(class_loader != nullptr, compile_app_image_); + DCHECK_EQ(class_loader != nullptr, compiler_options_.IsAppImage()); } // Clear references to removed classes from the DexCaches. - std::vector<ObjPtr<mirror::DexCache>> dex_caches; - { - ReaderMutexLock mu2(self, *Locks::dex_lock_); - dex_caches.reserve(class_linker->GetDexCachesData().size()); - for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { - if (self->IsJWeakCleared(data.weak_root)) { - continue; - } - dex_caches.push_back(self->DecodeJObject(data.weak_root)->AsDexCache()); - } - } + std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self); for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) { // Pass the class loader associated with the DexCache. This can either be // the app's `class_loader` or `nullptr` if boot class loader. - PruneAndPreloadDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader); + PruneDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader); } // Drop the array class cache in the ClassLinker, as these are roots holding those classes live. @@ -1588,6 +1655,20 @@ void ImageWriter::PruneNonImageClasses() { prune_class_memo_.clear(); } +std::vector<ObjPtr<mirror::DexCache>> ImageWriter::FindDexCaches(Thread* self) { + std::vector<ObjPtr<mirror::DexCache>> dex_caches; + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + ReaderMutexLock mu2(self, *Locks::dex_lock_); + dex_caches.reserve(class_linker->GetDexCachesData().size()); + for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { + if (self->IsJWeakCleared(data.weak_root)) { + continue; + } + dex_caches.push_back(self->DecodeJObject(data.weak_root)->AsDexCache()); + } + return dex_caches; +} + void ImageWriter::CheckNonImageClassesRemoved() { auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { if (obj->IsClass() && !IsInBootImage(obj)) { @@ -1619,7 +1700,7 @@ mirror::String* ImageWriter::FindInternedString(mirror::String* string) { return found.Ptr(); } } - if (compile_app_image_) { + if (!compiler_options_.IsBootImage()) { Runtime* const runtime = Runtime::Current(); ObjPtr<mirror::String> found = runtime->GetInternTable()->LookupStrong(self, string); // If we found it in the runtime intern table it could either be in the boot image or interned @@ -1716,7 +1797,7 @@ ObjPtr<ObjectArray<Object>> ImageWriter::CreateImageRoots( Handle<ObjectArray<Object>> dex_caches(hs.NewHandle(CollectDexCaches(self, oat_index))); // build an Object[] of the roots needed to restore the runtime - int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_); + int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compiler_options_.IsAppImage()); Handle<ObjectArray<Object>> image_roots(hs.NewHandle(ObjectArray<Object>::Alloc( self, GetClassRoot<ObjectArray<Object>>(class_linker), image_roots_size))); image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get()); @@ -1729,14 +1810,14 @@ ObjPtr<ObjectArray<Object>> ImageWriter::CreateImageRoots( runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow()); image_roots->Set<false>(ImageHeader::kNoClassDefFoundError, runtime->GetPreAllocatedNoClassDefFoundError()); - if (!compile_app_image_) { + if (!compiler_options_.IsAppImage()) { DCHECK(boot_image_live_objects != nullptr); image_roots->Set<false>(ImageHeader::kBootImageLiveObjects, boot_image_live_objects.Get()); } else { DCHECK(boot_image_live_objects == nullptr); } - for (int32_t i = 0, num = ImageHeader::NumberOfImageRoots(compile_app_image_); i != num; ++i) { - if (compile_app_image_ && i == ImageHeader::kAppImageClassLoader) { + for (int32_t i = 0; i != image_roots_size; ++i) { + if (compiler_options_.IsAppImage() && i == ImageHeader::kAppImageClassLoader) { // image_roots[ImageHeader::kAppImageClassLoader] will be set later for app image. continue; } @@ -1773,7 +1854,7 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, mirror::Class* as_klass = obj->AsClass(); mirror::DexCache* dex_cache = as_klass->GetDexCache(); DCHECK(!as_klass->IsErroneous()) << as_klass->GetStatus(); - if (compile_app_image_) { + if (compiler_options_.IsAppImage()) { // Extra sanity, no boot loader classes should be left! CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass(); } @@ -1784,7 +1865,7 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, // belongs. oat_index = GetOatIndexForDexCache(dex_cache); ImageInfo& image_info = GetImageInfo(oat_index); - if (!compile_app_image_) { + if (!compiler_options_.IsAppImage()) { // Note: Avoid locking to prevent lock order violations from root visiting; // image_info.class_table_ is only accessed from the image writer. image_info.class_table_->InsertWithoutLocks(as_klass); @@ -1887,7 +1968,7 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, // class loader. mirror::ClassLoader* class_loader = obj->AsClassLoader(); if (class_loader->GetClassTable() != nullptr) { - DCHECK(compile_app_image_); + DCHECK(compiler_options_.IsAppImage()); DCHECK(class_loaders_.empty()); class_loaders_.insert(class_loader); ImageInfo& image_info = GetImageInfo(oat_index); @@ -2065,7 +2146,7 @@ void ImageWriter::CalculateNewObjectOffsets() { Runtime* const runtime = Runtime::Current(); VariableSizedHandleScope handles(self); MutableHandle<ObjectArray<Object>> boot_image_live_objects = handles.NewHandle( - compile_app_image_ + compiler_options_.IsAppImage() ? nullptr : IntrinsicObjects::AllocateBootImageLiveObjects(self, runtime->GetClassLinker())); std::vector<Handle<ObjectArray<Object>>> image_roots; @@ -2100,7 +2181,8 @@ void ImageWriter::CalculateNewObjectOffsets() { for (auto* m : image_methods_) { CHECK(m != nullptr); CHECK(m->IsRuntimeMethod()); - DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image"; + DCHECK_EQ(!compiler_options_.IsBootImage(), IsInBootImage(m)) + << "Trampolines should be in boot image"; if (!IsInBootImage(m)) { AssignMethodOffset(m, NativeObjectRelocationType::kRuntimeMethod, GetDefaultOatIndex()); } @@ -2152,7 +2234,7 @@ void ImageWriter::CalculateNewObjectOffsets() { // For app images, there may be objects that are only held live by the boot image. One // example is finalizer references. Forward these objects so that EnsureBinSlotAssignedCallback // does not fail any checks. - if (compile_app_image_) { + if (compiler_options_.IsAppImage()) { for (gc::space::ImageSpace* space : heap->GetBootImageSpaces()) { DCHECK(space->IsImageSpace()); gc::accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap(); @@ -2453,7 +2535,7 @@ ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) { } const void* ImageWriter::GetIntrinsicReferenceAddress(uint32_t intrinsic_data) { - DCHECK(!compile_app_image_); + DCHECK(compiler_options_.IsBootImage()); switch (IntrinsicObjects::DecodePatchType(intrinsic_data)) { case IntrinsicObjects::PatchType::kIntegerValueOfArray: { const uint8_t* base_address = @@ -2846,7 +2928,7 @@ void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) { FixupClassVisitor visitor(this, copy); ObjPtr<mirror::Object>(orig)->VisitReferences(visitor, visitor); - if (kBitstringSubtypeCheckEnabled && compile_app_image_) { + if (kBitstringSubtypeCheckEnabled && compiler_options_.IsAppImage()) { // When we call SubtypeCheck::EnsureInitialize, it Assigns new bitstring // values to the parent of that class. // @@ -3027,7 +3109,7 @@ void ImageWriter::FixupDexCache(DexCache* orig_dex_cache, DexCache* copy_dex_cac const uint8_t* ImageWriter::GetOatAddress(StubType type) const { DCHECK_LE(type, StubType::kLast); // If we are compiling an app image, we need to use the stubs of the boot image. - if (compile_app_image_) { + if (!compiler_options_.IsBootImage()) { // Use the current image pointers. const std::vector<gc::space::ImageSpace*>& image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); @@ -3277,7 +3359,7 @@ void ImageWriter::UpdateOatFileLayout(size_t oat_index, cur_image_info.oat_data_begin_ = cur_image_info.oat_file_begin_ + oat_data_offset; cur_image_info.oat_size_ = oat_data_size; - if (compile_app_image_) { + if (compiler_options_.IsAppImage()) { CHECK_EQ(oat_filenames_.size(), 1u) << "App image should have no next image."; return; } @@ -3316,7 +3398,6 @@ void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_hea ImageWriter::ImageWriter( const CompilerOptions& compiler_options, uintptr_t image_begin, - bool compile_app_image, ImageHeader::StorageMode image_storage_mode, const std::vector<const char*>& oat_filenames, const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map, @@ -3324,7 +3405,6 @@ ImageWriter::ImageWriter( : compiler_options_(compiler_options), global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)), image_objects_offset_begin_(0), - compile_app_image_(compile_app_image), target_ptr_size_(InstructionSetPointerSize(compiler_options.GetInstructionSet())), image_infos_(oat_filenames.size()), dirty_methods_(0u), @@ -3334,9 +3414,11 @@ ImageWriter::ImageWriter( oat_filenames_(oat_filenames), dex_file_oat_index_map_(dex_file_oat_index_map), dirty_image_objects_(dirty_image_objects) { + DCHECK(compiler_options.IsBootImage() || compiler_options.IsAppImage()); CHECK_NE(image_begin, 0U); std::fill_n(image_methods_, arraysize(image_methods_), nullptr); - CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()) + CHECK_EQ(compiler_options.IsBootImage(), + Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()) << "Compiling a boot image should occur iff there are no boot image spaces loaded"; } diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index ccd529aa13..3c377a3ca5 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -78,7 +78,6 @@ class ImageWriter final { public: ImageWriter(const CompilerOptions& compiler_options, uintptr_t image_begin, - bool compile_app_image, ImageHeader::StorageMode image_storage_mode, const std::vector<const char*>& oat_filenames, const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map, @@ -112,10 +111,7 @@ class ImageWriter final { return true; } - ObjPtr<mirror::ClassLoader> GetClassLoader() { - CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u); - return compile_app_image_ ? *class_loaders_.begin() : nullptr; - } + ObjPtr<mirror::ClassLoader> GetClassLoader(); template <typename T> T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) { @@ -460,9 +456,18 @@ class ImageWriter final { // Remove unwanted classes from various roots. void PruneNonImageClasses() REQUIRES_SHARED(Locks::mutator_lock_); - // Remove unwanted classes from the DexCache roots and preload deterministic DexCache contents. - void PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> class_loader) + // Remove unwanted classes from the DexCache roots. + void PruneDexCache(ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::classlinker_classes_lock_); + + // Preload deterministic DexCache contents. + void PreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::classlinker_classes_lock_); + + // Find dex caches for pruning or preloading. + std::vector<ObjPtr<mirror::DexCache>> FindDexCaches(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::classlinker_classes_lock_); @@ -745,9 +750,6 @@ class ImageWriter final { // Oat index map for objects. std::unordered_map<mirror::Object*, uint32_t> oat_index_map_; - // Boolean flags. - const bool compile_app_image_; - // Size of pointers on the target architecture. PointerSize target_ptr_size_; diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 28287bd083..7f2877fc08 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -1500,7 +1500,8 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { } ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(Thread::Current(), *dex_file); const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_); + ObjPtr<mirror::Class> klass = + class_linker_->LookupResolvedType(class_def.class_idx_, dex_cache, class_loader_); if (klass != nullptr) { for (ArtMethod& method : klass->GetCopiedMethods(pointer_size_)) { // Find origin method. Declaring class and dex_method_idx @@ -1550,24 +1551,11 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(self, *dex_file_); ArtMethod* resolved_method; if (writer_->GetCompilerOptions().IsBootImage()) { - const InvokeType invoke_type = method.GetInvokeType( - dex_file_->GetClassDef(class_def_index_).access_flags_); - // Unchecked as we hold mutator_lock_ on entry. - ScopedObjectAccessUnchecked soa(self); - StackHandleScope<1> hs(self); - resolved_method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - method.GetIndex(), - hs.NewHandle(dex_cache), - ScopedNullHandle<mirror::ClassLoader>(), - /* referrer */ nullptr, - invoke_type); + resolved_method = class_linker_->LookupResolvedMethod( + method.GetIndex(), dex_cache, /*class_loader=*/ nullptr); if (resolved_method == nullptr) { - LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: " + LOG(FATAL) << "Unexpected failure to look up a method: " << dex_file_->PrettyMethod(method.GetIndex(), true); - self->AssertPendingException(); - mirror::Throwable* exc = self->GetException(); - std::string dump = exc->Dump(); - LOG(FATAL) << dump; UNREACHABLE(); } } else { diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h index 9725570570..56ff0ef3ca 100644 --- a/dex2oat/linker/relative_patcher_test.h +++ b/dex2oat/linker/relative_patcher_test.h @@ -50,7 +50,7 @@ class RelativePatcherTest : public CommonCompilerTest { compiled_methods_(), patched_code_(), output_(), - out_("test output stream", &output_) { + out_(nullptr) { // Override CommonCompilerTest's defaults. instruction_set_ = instruction_set; number_of_threads_ = 1u; @@ -61,10 +61,7 @@ class RelativePatcherTest : public CommonCompilerTest { OverrideInstructionSetFeatures(instruction_set_, variant_); CommonCompilerTest::SetUp(); - patcher_ = RelativePatcher::Create(compiler_options_->GetInstructionSet(), - compiler_options_->GetInstructionSetFeatures(), - &thunk_provider_, - &method_offset_map_); + Reset(); } void TearDown() override { @@ -73,6 +70,24 @@ class RelativePatcherTest : public CommonCompilerTest { CommonCompilerTest::TearDown(); } + // Reset the helper to start another test. Creating and tearing down the Runtime is expensive, + // so we merge related tests together. + void Reset() { + thunk_provider_.Reset(); + method_offset_map_.map.clear(); + patcher_ = RelativePatcher::Create(compiler_options_->GetInstructionSet(), + compiler_options_->GetInstructionSetFeatures(), + &thunk_provider_, + &method_offset_map_); + bss_begin_ = 0u; + string_index_to_offset_map_.clear(); + compiled_method_refs_.clear(); + compiled_methods_.clear(); + patched_code_.clear(); + output_.clear(); + out_.reset(new VectorOutputStream("test output stream", &output_)); + } + MethodReference MethodRef(uint32_t method_idx) { CHECK_NE(method_idx, 0u); return MethodReference(nullptr, method_idx); @@ -127,7 +142,7 @@ class RelativePatcherTest : public CommonCompilerTest { DCHECK(output_.empty()); uint8_t dummy_trampoline[kTrampolineSize]; memset(dummy_trampoline, 0, sizeof(dummy_trampoline)); - out_.WriteFully(dummy_trampoline, kTrampolineSize); + out_->WriteFully(dummy_trampoline, kTrampolineSize); offset = kTrampolineSize; static const uint8_t kPadding[] = { 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u @@ -135,14 +150,14 @@ class RelativePatcherTest : public CommonCompilerTest { uint8_t dummy_header[sizeof(OatQuickMethodHeader)]; memset(dummy_header, 0, sizeof(dummy_header)); for (auto& compiled_method : compiled_methods_) { - offset = patcher_->WriteThunks(&out_, offset); + offset = patcher_->WriteThunks(out_.get(), offset); uint32_t alignment_size = CodeAlignmentSize(offset); CHECK_LE(alignment_size, sizeof(kPadding)); - out_.WriteFully(kPadding, alignment_size); + out_->WriteFully(kPadding, alignment_size); offset += alignment_size; - out_.WriteFully(dummy_header, sizeof(OatQuickMethodHeader)); + out_->WriteFully(dummy_header, sizeof(OatQuickMethodHeader)); offset += sizeof(OatQuickMethodHeader); ArrayRef<const uint8_t> code = compiled_method->GetQuickCode(); if (!compiled_method->GetPatches().empty()) { @@ -179,10 +194,10 @@ class RelativePatcherTest : public CommonCompilerTest { } } } - out_.WriteFully(&code[0], code.size()); + out_->WriteFully(&code[0], code.size()); offset += code.size(); } - offset = patcher_->WriteThunks(&out_, offset); + offset = patcher_->WriteThunks(out_.get(), offset); CHECK_EQ(offset, output_size); CHECK_EQ(output_.size(), output_size); } @@ -270,6 +285,10 @@ class RelativePatcherTest : public CommonCompilerTest { *debug_name = value.GetDebugName(); } + void Reset() { + thunk_map_.clear(); + } + private: class ThunkKey { public: @@ -342,7 +361,7 @@ class RelativePatcherTest : public CommonCompilerTest { std::vector<std::unique_ptr<CompiledMethod>> compiled_methods_; std::vector<uint8_t> patched_code_; std::vector<uint8_t> output_; - VectorOutputStream out_; + std::unique_ptr<VectorOutputStream> out_; }; } // namespace linker diff --git a/disassembler/Android.bp b/disassembler/Android.bp index a7c1802515..5aa159e98b 100644 --- a/disassembler/Android.bp +++ b/disassembler/Android.bp @@ -69,8 +69,7 @@ art_cc_library { defaults: ["libart-disassembler-defaults"], shared_libs: [ // For disassembler_arm*. - "libvixl-arm", - "libvixl-arm64", + "libvixl", ], } @@ -82,7 +81,6 @@ art_cc_library { ], shared_libs: [ // For disassembler_arm*. - "libvixld-arm", - "libvixld-arm64", + "libvixld", ], } diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 5010f683d7..6a667bc7a7 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -54,6 +54,10 @@ cc_defaults { "libziparchive", "libz", ], + // Exclude the version script from Darwin host since it's not + // supported by the linker there. That means ASan checks on Darwin + // might trigger ODR violations. + version_script: "libartbase.map", }, host: { shared_libs: [ @@ -61,16 +65,31 @@ cc_defaults { "libz", ], }, + linux_glibc: { + version_script: "libartbase.map", + }, + windows: { + version_script: "libartbase.map", + }, }, generated_sources: ["art_libartbase_operator_srcs"], cflags: ["-DBUILDING_LIBART=1"], shared_libs: [ "liblog", - // For ashmem. + // For ashmem. "libcutils", // For common macros. "libbase", ], + + // Utilities used by various ART libs and tools are linked in statically + // here to avoid shared lib dependencies outside the ART APEX. No target + // there should depend on these separately. + whole_static_libs: [ + "liblz4", + "liblzma", + ], + export_include_dirs: ["."], // ART's macros.h depends on libbase's macros.h. // Note: runtime_options.h depends on cmdline. But we don't really want to export this diff --git a/libartbase/base/membarrier.cc b/libartbase/base/membarrier.cc index def949e3a7..4c86b6b0f6 100644 --- a/libartbase/base/membarrier.cc +++ b/libartbase/base/membarrier.cc @@ -25,7 +25,6 @@ #if defined(__BIONIC__) #include <atomic> -#include <android/get_device_api_level.h> #include <linux/membarrier.h> #define CHECK_MEMBARRIER_CMD(art_value, membarrier_value) \ diff --git a/libartbase/libartbase.map b/libartbase/libartbase.map new file mode 100644 index 0000000000..6249930dc1 --- /dev/null +++ b/libartbase/libartbase.map @@ -0,0 +1,15 @@ +# This is used only to hide data symbols that get imported through +# whole_static_libs, or else they might trigger the ASan odr-violation check. +# Before adding symbols here, please make sure that it doesn't give rise to a +# real ODR problem. All these symbols are either in .rodata or .data.rel.ro +# sections. +LIBARTBASE { + local: + PPMD7_kExpEscape; + XZ_SIG; + g_AlignedAlloc; + g_Alloc; + g_BigAlloc; + g_MidAlloc; + k7zSignature; +}; diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h index ad8a1842fc..4b389043d7 100644 --- a/libdexfile/dex/dex_instruction.h +++ b/libdexfile/dex/dex_instruction.h @@ -173,7 +173,7 @@ class Instruction { }; enum VerifyFlag : uint32_t { - kVerifyNone = 0x0000000, + kVerifyNothing = 0x0000000, kVerifyRegA = 0x0000001, kVerifyRegAWide = 0x0000002, kVerifyRegB = 0x0000004, diff --git a/libdexfile/dex/dex_instruction_list.h b/libdexfile/dex/dex_instruction_list.h index 9f0aba421a..b9540a6e6f 100644 --- a/libdexfile/dex/dex_instruction_list.h +++ b/libdexfile/dex/dex_instruction_list.h @@ -19,7 +19,7 @@ // V(opcode, instruction_code, name, format, index, flags, extended_flags, verifier_flags); #define DEX_INSTRUCTION_LIST(V) \ - V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNone) \ + V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNothing) \ V(0x01, MOVE, "move", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ V(0x02, MOVE_FROM16, "move/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ V(0x03, MOVE_16, "move/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \ @@ -33,7 +33,7 @@ V(0x0B, MOVE_RESULT_WIDE, "move-result-wide", k11x, kIndexNone, kContinue, 0, kVerifyRegAWide) \ V(0x0C, MOVE_RESULT_OBJECT, "move-result-object", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \ V(0x0D, MOVE_EXCEPTION, "move-exception", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \ - V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNone) \ + V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNothing) \ V(0x0F, RETURN, "return", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \ V(0x10, RETURN_WIDE, "return-wide", k11x, kIndexNone, kReturn, 0, kVerifyRegAWide) \ V(0x11, RETURN_OBJECT, "return-object", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \ @@ -134,7 +134,7 @@ V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \ V(0x71, INVOKE_STATIC, "invoke-static", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArg) \ V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \ - V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNone) \ + V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNothing) \ V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \ V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \ V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \ diff --git a/libdexfile/dex/dex_instruction_test.cc b/libdexfile/dex/dex_instruction_test.cc index 02400f4a14..3f79abf1d8 100644 --- a/libdexfile/dex/dex_instruction_test.cc +++ b/libdexfile/dex/dex_instruction_test.cc @@ -26,7 +26,7 @@ TEST(StaticGetters, PropertiesOfNopTest) { EXPECT_EQ(Instruction::k10x, Instruction::FormatOf(nop)); EXPECT_EQ(Instruction::kIndexNone, Instruction::IndexTypeOf(nop)); EXPECT_EQ(Instruction::kContinue, Instruction::FlagsOf(nop)); - EXPECT_EQ(Instruction::kVerifyNone, Instruction::VerifyFlagsOf(nop)); + EXPECT_EQ(Instruction::kVerifyNothing, Instruction::VerifyFlagsOf(nop)); } static void Build45cc(uint8_t num_args, uint16_t method_idx, uint16_t proto_idx, diff --git a/oatdump/Android.bp b/oatdump/Android.bp index 596a94664d..45f853b06e 100644 --- a/oatdump/Android.bp +++ b/oatdump/Android.bp @@ -100,8 +100,7 @@ art_cc_binary { ], static_libs: [ "libart-disassembler", - "libvixl-arm", - "libvixl-arm64", + "libvixl", ], } @@ -124,8 +123,7 @@ art_cc_binary { }, static_libs: [ "libartd-disassembler", - "libvixld-arm", - "libvixld-arm64", + "libvixld", ], group_static_libs: true, } diff --git a/runtime/Android.bp b/runtime/Android.bp index 86ee952022..410901ea47 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -377,7 +377,6 @@ libart_cc_defaults { include_dirs: [ "art/sigchainlib", "external/icu/icu4c/source/common", - "external/lz4/lib", "external/zlib", ], header_libs: [ @@ -390,7 +389,6 @@ libart_cc_defaults { "libnativebridge", "libnativeloader", "libbacktrace", - "liblz4", "liblog", // For atrace, properties, ashmem, set_sched_policy. "libcutils", @@ -422,8 +420,6 @@ libart_static_cc_defaults { "libbase", "libcutils", "liblog", - "liblz4", - "liblzma", "libnativebridge", "libnativeloader", "libunwindstack", @@ -661,8 +657,7 @@ art_cc_test { ], shared_libs: [ "libartd-compiler", - "libvixld-arm", - "libvixld-arm64", + "libvixld", ], } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index cc4f56cc06..35379cc251 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1543,26 +1543,6 @@ void AppImageLoadingHelper::HandleAppImageStrings(gc::space::ImageSpace* space) } } -// Update the class loader. Should only be used on classes in the image space. -class UpdateClassLoaderVisitor { - public: - UpdateClassLoaderVisitor(gc::space::ImageSpace* space, ObjPtr<mirror::ClassLoader> class_loader) - : space_(space), - class_loader_(class_loader) {} - - bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) { - // Do not update class loader for boot image classes where the app image - // class loader is only the initiating loader but not the defining loader. - if (klass->GetClassLoader() != nullptr) { - klass->SetClassLoader(class_loader_); - } - return true; - } - - gc::space::ImageSpace* const space_; - ObjPtr<mirror::ClassLoader> const class_loader_; -}; - static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file, const char* location, std::string* error_msg) @@ -2036,9 +2016,17 @@ bool ClassLinker::AddImageSpace( ScopedTrace trace("AppImage:UpdateClassLoaders"); // Update class loader and resolved strings. If added_class_table is false, the resolved // strings were forwarded UpdateAppImageClassLoadersAndDexCaches. - UpdateClassLoaderVisitor visitor(space, class_loader.Get()); + ObjPtr<mirror::ClassLoader> loader(class_loader.Get()); for (const ClassTable::TableSlot& root : temp_set) { - visitor(root.Read()); + // Note: We probably don't need the read barrier unless we copy the app image objects into + // the region space. + ObjPtr<mirror::Class> klass(root.Read()); + // Do not update class loader for boot image classes where the app image + // class loader is only the initiating loader but not the defining loader. + // Avoid read barrier since we are comparing against null. + if (klass->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) { + klass->SetClassLoader</*kCheckTransaction=*/ false>(loader); + } } } @@ -2527,6 +2515,35 @@ ClassPathEntry FindInClassPath(const char* descriptor, return ClassPathEntry(nullptr, nullptr); } +bool ClassLinker::FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa, + Thread* self, + const char* descriptor, + size_t hash, + Handle<mirror::ClassLoader> class_loader, + /*out*/ ObjPtr<mirror::Class>* result) { + ArtField* field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader.Get()); + if (raw_shared_libraries == nullptr) { + return true; + } + + StackHandleScope<2> hs(self); + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + MutableHandle<mirror::ClassLoader> temp_loader = hs.NewHandle<mirror::ClassLoader>(nullptr); + for (int32_t i = 0; i < shared_libraries->GetLength(); ++i) { + temp_loader.Assign(shared_libraries->Get(i)); + if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, temp_loader, result)) { + return false; // One of the shared libraries is not supported. + } + if (*result != nullptr) { + return true; // Found the class up the chain. + } + } + return true; +} + bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, Thread* self, const char* descriptor, @@ -2542,6 +2559,7 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl if (IsPathOrDexClassLoader(soa, class_loader)) { // For regular path or dex class loader the search order is: // - parent + // - shared libraries // - class loader dex files // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). @@ -2554,6 +2572,13 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl return true; // Found the class up the chain. } + if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) { + return false; // One of the shared library loader is not supported. + } + if (*result != nullptr) { + return true; // Found the class in a shared library. + } + // Search the current class loader classpath. *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader); return true; @@ -2562,6 +2587,7 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl if (IsDelegateLastClassLoader(soa, class_loader)) { // For delegate last, the search order is: // - boot class path + // - shared libraries // - class loader dex files // - parent *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash); @@ -2569,6 +2595,13 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl return true; // The class is part of the boot class path. } + if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) { + return false; // One of the shared library loader is not supported. + } + if (*result != nullptr) { + return true; // Found the class in a shared library. + } + *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader); if (*result != nullptr) { return true; // Found the class in the current class loader @@ -5070,8 +5103,10 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, ArtField* resolved_field = dex_cache->GetResolvedField(field_idx, image_pointer_size_); if (resolved_field == nullptr) { // Populating cache of a dex file which defines `klass` should always be allowed. - DCHECK_EQ(hiddenapi::GetMemberAction( - field, class_loader.Get(), dex_cache.Get(), hiddenapi::kNone), hiddenapi::kAllow); + DCHECK(!hiddenapi::ShouldDenyAccessToMember( + field, + hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()), + hiddenapi::AccessMethod::kNone)); dex_cache->SetResolvedField(field_idx, field, image_pointer_size_); } else { DCHECK_EQ(field, resolved_field); @@ -8102,8 +8137,9 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass, } DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); if (resolved != nullptr && - hiddenapi::GetMemberAction( - resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) { + hiddenapi::ShouldDenyAccessToMember(resolved, + hiddenapi::AccessContext(class_loader, dex_cache), + hiddenapi::AccessMethod::kLinking)) { resolved = nullptr; } if (resolved != nullptr) { @@ -8133,11 +8169,9 @@ static bool CheckNoSuchMethod(ArtMethod* method, ObjPtr<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { return method == nullptr || - hiddenapi::GetMemberAction(method, - class_loader, - dex_cache, - hiddenapi::kNone) // do not print warnings - == hiddenapi::kDeny; + hiddenapi::ShouldDenyAccessToMember(method, + hiddenapi::AccessContext(class_loader, dex_cache), + hiddenapi::AccessMethod::kNone); // no warnings } ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr<mirror::Class> klass, @@ -8273,8 +8307,10 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); } if (resolved != nullptr && - hiddenapi::GetMemberAction( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { + hiddenapi::ShouldDenyAccessToMember( + resolved, + hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()), + hiddenapi::AccessMethod::kLinking)) { resolved = nullptr; } return resolved; @@ -8373,8 +8409,9 @@ ArtField* ClassLinker::FindResolvedField(ObjPtr<mirror::Class> klass, } if (resolved != nullptr && - hiddenapi::GetMemberAction( - resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) { + hiddenapi::ShouldDenyAccessToMember(resolved, + hiddenapi::AccessContext(class_loader, dex_cache), + hiddenapi::AccessMethod::kLinking)) { resolved = nullptr; } @@ -8399,8 +8436,9 @@ ArtField* ClassLinker::FindResolvedFieldJLS(ObjPtr<mirror::Class> klass, resolved = mirror::Class::FindField(self, klass, name, type); if (resolved != nullptr && - hiddenapi::GetMemberAction( - resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) { + hiddenapi::ShouldDenyAccessToMember(resolved, + hiddenapi::AccessContext(class_loader, dex_cache), + hiddenapi::AccessMethod::kLinking)) { resolved = nullptr; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 60e68d5f78..a65299a514 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -868,6 +868,15 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); + bool FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa, + Thread* self, + const char* descriptor, + size_t hash, + Handle<mirror::ClassLoader> class_loader, + /*out*/ ObjPtr<mirror::Class>* result) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_); + // Finds the class in the classpath of the given class loader. It only searches the class loader // dex files and does not recurse into its parent. // The method checks that the provided class loader is either a PathClassLoader or a diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index ed3a18db28..b11e368871 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -66,7 +66,7 @@ bool DexoptTest::Dex2Oat(const std::vector<std::string>& args, std::string* erro } runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); - if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) { + if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kDisabled) { argv.push_back("--runtime-arg"); argv.push_back("-Xhidden-api-checks"); } diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index f3552765c5..3b7b938d50 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -44,38 +44,53 @@ static constexpr bool kLogAllAccesses = false; static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { switch (value) { - case kNone: + case AccessMethod::kNone: LOG(FATAL) << "Internal access to hidden API should not be logged"; UNREACHABLE(); - case kReflection: + case AccessMethod::kReflection: os << "reflection"; break; - case kJNI: + case AccessMethod::kJNI: os << "JNI"; break; - case kLinking: + case AccessMethod::kLinking: os << "linking"; break; } return os; } -static constexpr bool EnumsEqual(EnforcementPolicy policy, hiddenapi::ApiList apiList) { - return static_cast<int>(policy) == static_cast<int>(apiList); -} - -// GetMemberAction-related static_asserts. -static_assert( - EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, hiddenapi::ApiList::kDarkGreylist) && - EnumsEqual(EnforcementPolicy::kBlacklistOnly, hiddenapi::ApiList::kBlacklist), - "Mismatch between EnforcementPolicy and ApiList enums"); -static_assert( - EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList && - EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, - "EnforcementPolicy values ordering not correct"); - namespace detail { +// Do not change the values of items in this enum, as they are written to the +// event log for offline analysis. Any changes will interfere with that analysis. +enum AccessContextFlags { + // Accessed member is a field if this bit is set, else a method + kMemberIsField = 1 << 0, + // Indicates if access was denied to the member, instead of just printing a warning. + kAccessDenied = 1 << 1, +}; + +static int32_t GetMaxAllowedSdkVersionForApiList(ApiList api_list) { + SdkCodes sdk = SdkCodes::kVersionNone; + switch (api_list) { + case ApiList::kWhitelist: + case ApiList::kLightGreylist: + sdk = SdkCodes::kVersionUnlimited; + break; + case ApiList::kDarkGreylist: + sdk = SdkCodes::kVersionO_MR1; + break; + case ApiList::kBlacklist: + sdk = SdkCodes::kVersionNone; + break; + case ApiList::kNoList: + LOG(FATAL) << "Unexpected value"; + UNREACHABLE(); + } + return static_cast<int32_t>(sdk); +} + MemberSignature::MemberSignature(ArtField* field) { class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_); member_name_ = field->GetName(); @@ -137,6 +152,7 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, hiddenapi::Api LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ") << Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")"; } + #ifdef ART_TARGET_ANDROID // Convert an AccessMethod enum to a value for logging from the proto enum. // This method may look odd (the enum values are current the same), but it @@ -145,13 +161,13 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, hiddenapi::Api // future. inline static int32_t GetEnumValueForLog(AccessMethod access_method) { switch (access_method) { - case kNone: + case AccessMethod::kNone: return android::metricslogger::ACCESS_METHOD_NONE; - case kReflection: + case AccessMethod::kReflection: return android::metricslogger::ACCESS_METHOD_REFLECTION; - case kJNI: + case AccessMethod::kJNI: return android::metricslogger::ACCESS_METHOD_JNI; - case kLinking: + case AccessMethod::kLinking: return android::metricslogger::ACCESS_METHOD_LINKING; default: DCHECK(false); @@ -159,9 +175,9 @@ inline static int32_t GetEnumValueForLog(AccessMethod access_method) { } #endif -void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) { +void MemberSignature::LogAccessToEventLog(AccessMethod access_method, bool access_denied) { #ifdef ART_TARGET_ANDROID - if (access_method == kLinking || access_method == kNone) { + if (access_method == AccessMethod::kLinking || access_method == AccessMethod::kNone) { // Linking warnings come from static analysis/compilation of the bytecode // and can contain false positives (i.e. code that is never run). We choose // not to log these in the event log. @@ -170,7 +186,7 @@ void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action act } ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED); log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method)); - if (action_taken == kDeny) { + if (access_denied) { log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1); } const std::string& package_name = Runtime::Current()->GetProcessPackageName(); @@ -183,10 +199,42 @@ void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action act log_maker.Record(); #else UNUSED(access_method); - UNUSED(action_taken); + UNUSED(access_denied); #endif } +void MemberSignature::NotifyHiddenApiListener(AccessMethod access_method) { + if (access_method != AccessMethod::kReflection && access_method != AccessMethod::kJNI) { + // We can only up-call into Java during reflection and JNI down-calls. + return; + } + + Runtime* runtime = Runtime::Current(); + if (!runtime->IsAotCompiler()) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + + ScopedLocalRef<jobject> consumer_object(soa.Env(), + soa.Env()->GetStaticObjectField( + WellKnownClasses::dalvik_system_VMRuntime, + WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer)); + // If the consumer is non-null, we call back to it to let it know that we + // have encountered an API that's in one of our lists. + if (consumer_object != nullptr) { + std::ostringstream member_signature_str; + Dump(member_signature_str); + + ScopedLocalRef<jobject> signature_str( + soa.Env(), + soa.Env()->NewStringUTF(member_signature_str.str().c_str())); + + // Call through to Consumer.accept(String memberSignature); + soa.Env()->CallVoidMethod(consumer_object.get(), + WellKnownClasses::java_util_function_Consumer_accept, + signature_str.get()); + } + } +} + static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) { return true; } @@ -205,116 +253,68 @@ static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member) } template<typename T> -Action GetMemberActionImpl(T* member, - hiddenapi::ApiList api_list, - Action action, - AccessMethod access_method) { - DCHECK_NE(action, kAllow); - - // Get the signature, we need it later. - MemberSignature member_signature(member); +bool ShouldDenyAccessToMemberImpl(T* member, + hiddenapi::ApiList api_list, + AccessMethod access_method) { + DCHECK(member != nullptr); Runtime* runtime = Runtime::Current(); + EnforcementPolicy policy = runtime->GetHiddenApiEnforcementPolicy(); + + const bool deny_access = + (policy == EnforcementPolicy::kEnabled) && + (runtime->GetTargetSdkVersion() > GetMaxAllowedSdkVersionForApiList(api_list)); + + MemberSignature member_signature(member); // Check for an exemption first. Exempted APIs are treated as white list. - // We only do this if we're about to deny, or if the app is debuggable. This is because: - // - we only print a warning for light greylist violations for debuggable apps - // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs. - // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever - // possible. - const bool shouldWarn = kLogAllAccesses || runtime->IsJavaDebuggable(); - if (shouldWarn || action == kDeny) { - if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) { - action = kAllow; - // Avoid re-examining the exemption list next time. - // Note this results in no warning for the member, which seems like what one would expect. - // Exemptions effectively adds new members to the whitelist. - MaybeWhitelistMember(runtime, member); - return kAllow; - } + if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) { + // Avoid re-examining the exemption list next time. + // Note this results in no warning for the member, which seems like what one would expect. + // Exemptions effectively adds new members to the whitelist. + MaybeWhitelistMember(runtime, member); + return false; + } - if (access_method != kNone) { - // Print a log message with information about this class member access. - // We do this if we're about to block access, or the app is debuggable. + if (access_method != AccessMethod::kNone) { + // Print a log message with information about this class member access. + // We do this if we're about to deny access, or the app is debuggable. + if (kLogAllAccesses || deny_access || runtime->IsJavaDebuggable()) { member_signature.WarnAboutAccess(access_method, api_list); } - } - if (kIsTargetBuild && !kIsTargetLinux) { - uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate(); - // Assert that RAND_MAX is big enough, to ensure sampling below works as expected. - static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small"); - if (eventLogSampleRate != 0 && - (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) { - member_signature.LogAccessToEventLog(access_method, action); + // If there is a StrictMode listener, notify it about this violation. + member_signature.NotifyHiddenApiListener(access_method); + + // If event log sampling is enabled, report this violation. + if (kIsTargetBuild && !kIsTargetLinux) { + uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate(); + // Assert that RAND_MAX is big enough, to ensure sampling below works as expected. + static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small"); + if (eventLogSampleRate != 0 && + (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) { + member_signature.LogAccessToEventLog(access_method, deny_access); + } } - } - - if (action == kDeny) { - // Block access - return action; - } - - // Allow access to this member but print a warning. - DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast); - if (access_method != kNone) { - // Depending on a runtime flag, we might move the member into whitelist and - // skip the warning the next time the member is accessed. - MaybeWhitelistMember(runtime, member); - - // If this action requires a UI warning, set the appropriate flag. - if (shouldWarn && - (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag())) { - runtime->SetPendingHiddenApiWarning(true); + // If this access was not denied, move the member into whitelist and skip + // the warning the next time the member is accessed. + if (!deny_access) { + MaybeWhitelistMember(runtime, member); } } - return action; + return deny_access; } // Need to instantiate this. -template Action GetMemberActionImpl<ArtField>(ArtField* member, - hiddenapi::ApiList api_list, - Action action, - AccessMethod access_method); -template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member, - hiddenapi::ApiList api_list, - Action action, - AccessMethod access_method); +template bool ShouldDenyAccessToMemberImpl<ArtField>(ArtField* member, + hiddenapi::ApiList api_list, + AccessMethod access_method); +template bool ShouldDenyAccessToMemberImpl<ArtMethod>(ArtMethod* member, + hiddenapi::ApiList api_list, + AccessMethod access_method); } // namespace detail -template<typename T> -void NotifyHiddenApiListener(T* member) { - Runtime* runtime = Runtime::Current(); - if (!runtime->IsAotCompiler()) { - ScopedObjectAccessUnchecked soa(Thread::Current()); - - ScopedLocalRef<jobject> consumer_object(soa.Env(), - soa.Env()->GetStaticObjectField( - WellKnownClasses::dalvik_system_VMRuntime, - WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer)); - // If the consumer is non-null, we call back to it to let it know that we - // have encountered an API that's in one of our lists. - if (consumer_object != nullptr) { - detail::MemberSignature member_signature(member); - std::ostringstream member_signature_str; - member_signature.Dump(member_signature_str); - - ScopedLocalRef<jobject> signature_str( - soa.Env(), - soa.Env()->NewStringUTF(member_signature_str.str().c_str())); - - // Call through to Consumer.accept(String memberSignature); - soa.Env()->CallVoidMethod(consumer_object.get(), - WellKnownClasses::java_util_function_Consumer_accept, - signature_str.get()); - } - } -} - -template void NotifyHiddenApiListener<ArtMethod>(ArtMethod* member); -template void NotifyHiddenApiListener<ArtField>(ArtField* member); - } // namespace hiddenapi } // namespace art diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 57f1a599cf..ed00e2a892 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -32,11 +32,10 @@ namespace hiddenapi { // This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in // frameworks/base/core/java/android/content/pm/ApplicationInfo.java enum class EnforcementPolicy { - kNoChecks = 0, + kDisabled = 0, kJustWarn = 1, // keep checks enabled, but allow everything (enables logging) - kDarkGreyAndBlackList = 2, // ban dark grey & blacklist - kBlacklistOnly = 3, // ban blacklist violations only - kMax = kBlacklistOnly, + kEnabled = 2, // ban dark grey & blacklist + kMax = kEnabled, }; inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) { @@ -45,55 +44,58 @@ inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) { return static_cast<EnforcementPolicy>(api_policy_int); } -enum Action { - kAllow, - kAllowButWarn, - kAllowButWarnAndToast, - kDeny -}; - -enum AccessMethod { +enum class AccessMethod { kNone, // internal test that does not correspond to an actual access by app kReflection, kJNI, kLinking, }; -// Do not change the values of items in this enum, as they are written to the -// event log for offline analysis. Any changes will interfere with that analysis. -enum AccessContextFlags { - // Accessed member is a field if this bit is set, else a method - kMemberIsField = 1 << 0, - // Indicates if access was denied to the member, instead of just printing a warning. - kAccessDenied = 1 << 1, -}; +struct AccessContext { + public: + explicit AccessContext(bool is_trusted) : is_trusted_(is_trusted) {} -inline Action GetActionFromAccessFlags(ApiList api_list) { - if (api_list == ApiList::kWhitelist) { - return kAllow; - } + explicit AccessContext(ObjPtr<mirror::Class> klass) : is_trusted_(GetIsTrusted(klass)) {} - EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); - if (policy == EnforcementPolicy::kNoChecks) { - // Exit early. Nothing to enforce. - return kAllow; - } + AccessContext(ObjPtr<mirror::ClassLoader> class_loader, ObjPtr<mirror::DexCache> dex_cache) + : is_trusted_(GetIsTrusted(class_loader, dex_cache)) {} + + bool IsTrusted() const { return is_trusted_; } + + private: + static bool GetIsTrusted(ObjPtr<mirror::ClassLoader> class_loader, + ObjPtr<mirror::DexCache> dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Trust if the caller is in is boot class loader. + if (class_loader.IsNull()) { + return true; + } + + // Trust if caller is in a platform dex file. + if (!dex_cache.IsNull()) { + const DexFile* dex_file = dex_cache->GetDexFile(); + if (dex_file != nullptr && dex_file->IsPlatformDexFile()) { + return true; + } + } - // if policy is "just warn", always warn. We returned above for whitelist APIs. - if (policy == EnforcementPolicy::kJustWarn) { - return kAllowButWarn; + return false; } - DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList); - // The logic below relies on equality of values in the enums EnforcementPolicy and - // ApiList, and their ordering. Assertions are in hidden_api.cc. - if (static_cast<int>(policy) > static_cast<int>(api_list)) { - return api_list == ApiList::kDarkGreylist - ? kAllowButWarnAndToast - : kAllowButWarn; - } else { - return kDeny; + + static bool GetIsTrusted(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!klass.IsNull()); + + if (klass->ShouldSkipHiddenApiChecks() && Runtime::Current()->IsJavaDebuggable()) { + // Class is known, it is marked trusted and we are in debuggable mode. + return true; + } + + // Check other aspects of the context. + return GetIsTrusted(klass->GetClassLoader(), klass->GetDexCache()); } -} + + bool is_trusted_; +}; class ScopedHiddenApiEnforcementPolicySetting { public: @@ -114,6 +116,13 @@ class ScopedHiddenApiEnforcementPolicySetting { // Implementation details. DO NOT ACCESS DIRECTLY. namespace detail { +enum class SdkCodes { + kVersionNone = std::numeric_limits<int32_t>::min(), + kVersionUnlimited = std::numeric_limits<int32_t>::max(), + kVersionO_MR1 = 27, + kVersionP = 28, +}; + // Class to encapsulate the signature of a member (ArtField or ArtMethod). This // is used as a helper when matching prefixes, and when logging the signature. class MemberSignature { @@ -146,59 +155,31 @@ class MemberSignature { void WarnAboutAccess(AccessMethod access_method, ApiList list); - void LogAccessToEventLog(AccessMethod access_method, Action action_taken); + void LogAccessToEventLog(AccessMethod access_method, bool access_denied); + + // Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that + // |member| was accessed. This is usually called when an API is on the black, + // dark grey or light grey lists. Given that the callback can execute arbitrary + // code, a call to this method can result in thread suspension. + void NotifyHiddenApiListener(AccessMethod access_method); }; template<typename T> -Action GetMemberActionImpl(T* member, - ApiList api_list, - Action action, - AccessMethod access_method) +bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_); -// Returns true if the caller is either loaded by the boot strap class loader or comes from -// a dex file located in ${ANDROID_ROOT}/framework/. -ALWAYS_INLINE -inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller, - ObjPtr<mirror::ClassLoader> caller_class_loader, - ObjPtr<mirror::DexCache> caller_dex_cache) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (caller_class_loader.IsNull()) { - // Boot class loader. - return true; - } - - if (!caller_dex_cache.IsNull()) { - const DexFile* caller_dex_file = caller_dex_cache->GetDexFile(); - if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) { - // Caller is in a platform dex file. - return true; - } - } - - if (!caller.IsNull() && - caller->ShouldSkipHiddenApiChecks() && - Runtime::Current()->IsJavaDebuggable()) { - // We are in debuggable mode and this caller has been marked trusted. - return true; - } - - return false; -} - } // namespace detail -// Returns true if access to `member` should be denied to the caller of the -// reflective query. The decision is based on whether the caller is trusted or -// not. Because different users of this function determine this in a different -// way, `fn_caller_is_trusted(self)` is called and should return true if the -// caller is allowed to access the platform. +// Returns true if access to `member` should be denied in the given context. +// The decision is based on whether the caller is in a trusted context or not. +// Because determining the access context can be expensive, a lambda function +// "fn_get_access_context" is lazily invoked after other criteria have been +// considered. // This function might print warnings into the log if the member is hidden. template<typename T> -inline Action GetMemberAction(T* member, - Thread* self, - std::function<bool(Thread*)> fn_caller_is_trusted, - AccessMethod access_method) +inline bool ShouldDenyAccessToMember(T* member, + std::function<AccessContext()> fn_get_access_context, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); @@ -210,53 +191,34 @@ inline Action GetMemberAction(T* member, // results, e.g. print whitelist warnings (b/78327881). ApiList api_list = member->GetHiddenApiAccessFlags(); - Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags()); - if (action == kAllow) { - // Nothing to do. - return action; + // Exit early if member is on the whitelist. + if (api_list == ApiList::kWhitelist) { + return false; } - // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. + // Check if caller is exempted from access checks. // This can be *very* expensive. Save it for last. - if (fn_caller_is_trusted(self)) { - // Caller is trusted. Exit. - return kAllow; + if (fn_get_access_context().IsTrusted()) { + return false; } - // Member is hidden and caller is not in the platform. - return detail::GetMemberActionImpl(member, api_list, action, access_method); -} - -inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) { - return !caller.IsNull() && - detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache()); + // Member is hidden and caller is not exempted. Enter slow path. + return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method); } -// Returns true if access to `member` should be denied to a caller loaded with -// `caller_class_loader`. -// This function might print warnings into the log if the member is hidden. +// Helper method for callers where access context can be determined beforehand. +// Wraps AccessContext in a lambda and passes it to the real ShouldDenyAccessToMember. template<typename T> -inline Action GetMemberAction(T* member, - ObjPtr<mirror::ClassLoader> caller_class_loader, - ObjPtr<mirror::DexCache> caller_dex_cache, - AccessMethod access_method) +inline bool ShouldDenyAccessToMember(T* member, + AccessContext access_context, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { - bool is_caller_trusted = - detail::IsCallerTrusted(/* caller= */ nullptr, caller_class_loader, caller_dex_cache); - return GetMemberAction(member, - /* thread= */ nullptr, - [is_caller_trusted] (Thread*) { return is_caller_trusted; }, - access_method); + return ShouldDenyAccessToMember( + member, + [&] () REQUIRES_SHARED(Locks::mutator_lock_) { return access_context; }, + access_method); } -// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that -// |member| was accessed. This is usually called when an API is on the black, -// dark grey or light grey lists. Given that the callback can execute arbitrary -// code, a call to this method can result in thread suspension. -template<typename T> void NotifyHiddenApiListener(T* member) - REQUIRES_SHARED(Locks::mutator_lock_); - - } // namespace hiddenapi } // namespace art diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 1727af016c..627d9a7e1c 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -23,7 +23,7 @@ namespace art { using hiddenapi::detail::MemberSignature; -using hiddenapi::GetActionFromAccessFlags; +using hiddenapi::detail::ShouldDenyAccessToMemberImpl; class HiddenApiTest : public CommonRuntimeTest { protected: @@ -68,6 +68,15 @@ class HiddenApiTest : public CommonRuntimeTest { return art_field; } + bool ShouldDenyAccess(hiddenapi::ApiList list) REQUIRES_SHARED(Locks::mutator_lock_) { + // Choose parameters such that there are no side effects (AccessMethod::kNone) + // and that the member is not on the exemptions list (here we choose one which + // is not even in boot class path). + return ShouldDenyAccessToMemberImpl(/* member= */ class1_field1_, + list, + /* access_method= */ hiddenapi::AccessMethod::kNone); + } + protected: Thread* self_; jobject jclass_loader_; @@ -88,41 +97,33 @@ class HiddenApiTest : public CommonRuntimeTest { }; TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) { - runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist), hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist), hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist), hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist), hiddenapi::kAllow); + ScopedObjectAccess soa(self_); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDisabled); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false); runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist), - hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist), - hiddenapi::kAllowButWarn); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist), - hiddenapi::kAllowButWarn); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist), - hiddenapi::kAllowButWarn); - - runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist), - hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist), - hiddenapi::kAllowButWarn); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist), - hiddenapi::kDeny); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist), - hiddenapi::kDeny); - - runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist), - hiddenapi::kAllow); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist), - hiddenapi::kAllowButWarn); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist), - hiddenapi::kAllowButWarnAndToast); - ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist), - hiddenapi::kDeny); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); + runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1)); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); + runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionP)); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), true); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true); } TEST_F(HiddenApiTest, CheckMembersRead) { diff --git a/runtime/interpreter/mterp/arm/other.S b/runtime/interpreter/mterp/arm/other.S index 340038c83e..fcdde1e72a 100644 --- a/runtime/interpreter/mterp/arm/other.S +++ b/runtime/interpreter/mterp/arm/other.S @@ -159,6 +159,9 @@ cmp r0, #0 bne MterpException FETCH_ADVANCE_INST 1 + ldr r0, [rSELF, #THREAD_USE_MTERP_OFFSET] + cmp r0, #0 + beq MterpFallback GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction @@ -179,6 +182,9 @@ cmp r0, #0 @ failed? bne MterpException FETCH_ADVANCE_INST 1 @ before throw: advance rPC, load rINST + ldr r0, [rSELF, #THREAD_USE_MTERP_OFFSET] + cmp r0, #0 + beq MterpFallback GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm64/other.S b/runtime/interpreter/mterp/arm64/other.S index 024a5c8a07..f1d0ef34dc 100644 --- a/runtime/interpreter/mterp/arm64/other.S +++ b/runtime/interpreter/mterp/arm64/other.S @@ -146,6 +146,8 @@ bl artLockObjectFromCode cbnz w0, MterpException FETCH_ADVANCE_INST 1 + ldr w0, [xSELF, #THREAD_USE_MTERP_OFFSET] + cbz w0, MterpFallback GET_INST_OPCODE ip // extract opcode from rINST GOTO_OPCODE ip // jump to next instruction @@ -165,6 +167,8 @@ bl artUnlockObjectFromCode // w0<- success for unlock(self, obj) cbnz w0, MterpException FETCH_ADVANCE_INST 1 // before throw: advance rPC, load rINST + ldr w0, [xSELF, #THREAD_USE_MTERP_OFFSET] + cbz w0, MterpFallback GET_INST_OPCODE ip // extract opcode from rINST GOTO_OPCODE ip // jump to next instruction diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S index cfb9c7c719..06cd904756 100644 --- a/runtime/interpreter/mterp/x86/invoke.S +++ b/runtime/interpreter/mterp/x86/invoke.S @@ -18,8 +18,7 @@ jz MterpException ADVANCE_PC 3 movl rSELF, %eax - movb THREAD_USE_MTERP_OFFSET(%eax), %al - testb %al, %al + cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax) jz MterpFallback RESTORE_IBASE FETCH_INST @@ -45,8 +44,7 @@ jz MterpException ADVANCE_PC 4 movl rSELF, %eax - movb THREAD_USE_MTERP_OFFSET(%eax), %al - testb %al, %al + cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax) jz MterpFallback RESTORE_IBASE FETCH_INST diff --git a/runtime/interpreter/mterp/x86/main.S b/runtime/interpreter/mterp/x86/main.S index b233f2c522..0621fb468a 100644 --- a/runtime/interpreter/mterp/x86/main.S +++ b/runtime/interpreter/mterp/x86/main.S @@ -91,6 +91,8 @@ unspecified registers or condition codes. #include "asm_support.h" #include "interpreter/cfi_asm_support.h" +#define LITERAL(value) $$(value) + /* * Handle mac compiler specific */ @@ -561,8 +563,7 @@ MterpException: movl rPC, OFF_FP_DEX_PC_PTR(rFP) /* Do we need to switch interpreters? */ movl rSELF, %eax - movb THREAD_USE_MTERP_OFFSET(%eax), %al - testb %al, %al + cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax) jz MterpFallback /* resume execution at catch block */ REFRESH_IBASE diff --git a/runtime/interpreter/mterp/x86/other.S b/runtime/interpreter/mterp/x86/other.S index 5de33813b8..270ccb688c 100644 --- a/runtime/interpreter/mterp/x86/other.S +++ b/runtime/interpreter/mterp/x86/other.S @@ -132,7 +132,12 @@ RESTORE_IBASE testb %al, %al jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + ADVANCE_PC 1 + movl rSELF, %eax + cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax) + jz MterpFallback + FETCH_INST + GOTO_NEXT %def op_monitor_exit(): /* @@ -152,7 +157,12 @@ RESTORE_IBASE testb %al, %al jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + ADVANCE_PC 1 + movl rSELF, %eax + cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax) + jz MterpFallback + FETCH_INST + GOTO_NEXT %def op_move(is_object="0"): /* for move, move-object, long-to-int */ diff --git a/runtime/interpreter/mterp/x86_64/invoke.S b/runtime/interpreter/mterp/x86_64/invoke.S index f727915265..15b48c91dc 100644 --- a/runtime/interpreter/mterp/x86_64/invoke.S +++ b/runtime/interpreter/mterp/x86_64/invoke.S @@ -16,8 +16,7 @@ jz MterpException ADVANCE_PC 3 movq rSELF, %rax - movb THREAD_USE_MTERP_OFFSET(%rax), %al - testb %al, %al + cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax) jz MterpFallback FETCH_INST GOTO_NEXT @@ -40,8 +39,7 @@ jz MterpException ADVANCE_PC 4 movq rSELF, %rax - movb THREAD_USE_MTERP_OFFSET(%rax), %al - testb %al, %al + cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax) jz MterpFallback FETCH_INST GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/main.S b/runtime/interpreter/mterp/x86_64/main.S index 75eb00c461..4609067a5a 100644 --- a/runtime/interpreter/mterp/x86_64/main.S +++ b/runtime/interpreter/mterp/x86_64/main.S @@ -87,6 +87,8 @@ unspecified registers or condition codes. #include "asm_support.h" #include "interpreter/cfi_asm_support.h" +#define LITERAL(value) $$(value) + /* * Handle mac compiler specific */ @@ -527,8 +529,7 @@ MterpException: movq rPC, OFF_FP_DEX_PC_PTR(rFP) /* Do we need to switch interpreters? */ movq rSELF, %rax - movb THREAD_USE_MTERP_OFFSET(%rax), %al - testb %al, %al + cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax) jz MterpFallback /* resume execution at catch block */ REFRESH_IBASE diff --git a/runtime/interpreter/mterp/x86_64/other.S b/runtime/interpreter/mterp/x86_64/other.S index 849155ca8f..412389f3ed 100644 --- a/runtime/interpreter/mterp/x86_64/other.S +++ b/runtime/interpreter/mterp/x86_64/other.S @@ -108,7 +108,12 @@ call SYMBOL(artLockObjectFromCode) # (object, self) testq %rax, %rax jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + ADVANCE_PC 1 + movq rSELF, %rax + cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax) + jz MterpFallback + FETCH_INST + GOTO_NEXT %def op_monitor_exit(): /* @@ -125,7 +130,12 @@ call SYMBOL(artUnlockObjectFromCode) # (object, self) testq %rax, %rax jnz MterpException - ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + ADVANCE_PC 1 + movq rSELF, %rax + cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax) + jz MterpFallback + FETCH_INST + GOTO_NEXT %def op_move(is_object="0"): /* for move, move-object, long-to-int */ diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 9bc2179b63..e292a7612c 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -182,15 +182,16 @@ static mirror::String* GetClassName(Thread* self, ShadowFrame* shadow_frame, siz } template<typename T> -static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame) +static ALWAYS_INLINE bool ShouldDenyAccessToMember(T* member, ShadowFrame* frame) REQUIRES_SHARED(Locks::mutator_lock_) { // All uses in this file are from reflection - constexpr hiddenapi::AccessMethod access_method = hiddenapi::kReflection; - return hiddenapi::GetMemberAction( + constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kReflection; + return hiddenapi::ShouldDenyAccessToMember( member, - frame->GetMethod()->GetDeclaringClass()->GetClassLoader(), - frame->GetMethod()->GetDeclaringClass()->GetDexCache(), - access_method) == hiddenapi::kDeny; + [&]() REQUIRES_SHARED(Locks::mutator_lock_) { + return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass()); + }, + access_method); } void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self, @@ -297,7 +298,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( auto* cl = Runtime::Current()->GetClassLinker(); if (cl->EnsureInitialized(self, h_klass, true, true)) { ArtMethod* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize()); - if (cons != nullptr && ShouldBlockAccessToMember(cons, shadow_frame)) { + if (cons != nullptr && ShouldDenyAccessToMember(cons, shadow_frame)) { cons = nullptr; } if (cons != nullptr) { @@ -342,7 +343,7 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField( } } } - if (found != nullptr && ShouldBlockAccessToMember(found, shadow_frame)) { + if (found != nullptr && ShouldDenyAccessToMember(found, shadow_frame)) { found = nullptr; } if (found == nullptr) { @@ -407,7 +408,7 @@ void UnstartedRuntime::UnstartedClassGetDeclaredMethod( self, klass, name, args); } } - if (method != nullptr && ShouldBlockAccessToMember(method->GetArtMethod(), shadow_frame)) { + if (method != nullptr && ShouldDenyAccessToMember(method->GetArtMethod(), shadow_frame)) { method = nullptr; } result->SetL(method); @@ -445,7 +446,7 @@ void UnstartedRuntime::UnstartedClassGetDeclaredConstructor( } } if (constructor != nullptr && - ShouldBlockAccessToMember(constructor->GetArtMethod(), shadow_frame)) { + ShouldDenyAccessToMember(constructor->GetArtMethod(), shadow_frame)) { constructor = nullptr; } result->SetL(constructor); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 359f97e705..1701ca8a78 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -1274,10 +1274,10 @@ size_t JitCodeCache::ReserveData(Thread* self, class MarkCodeVisitor final : public StackVisitor { public: - MarkCodeVisitor(Thread* thread_in, JitCodeCache* code_cache_in) + MarkCodeVisitor(Thread* thread_in, JitCodeCache* code_cache_in, CodeCacheBitmap* bitmap) : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kSkipInlinedFrames), code_cache_(code_cache_in), - bitmap_(code_cache_->GetLiveBitmap()) {} + bitmap_(bitmap) {} bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); @@ -1299,13 +1299,13 @@ class MarkCodeVisitor final : public StackVisitor { class MarkCodeClosure final : public Closure { public: - MarkCodeClosure(JitCodeCache* code_cache, Barrier* barrier) - : code_cache_(code_cache), barrier_(barrier) {} + MarkCodeClosure(JitCodeCache* code_cache, CodeCacheBitmap* bitmap, Barrier* barrier) + : code_cache_(code_cache), bitmap_(bitmap), barrier_(barrier) {} void Run(Thread* thread) override REQUIRES_SHARED(Locks::mutator_lock_) { ScopedTrace trace(__PRETTY_FUNCTION__); DCHECK(thread == Thread::Current() || thread->IsSuspended()); - MarkCodeVisitor visitor(thread, code_cache_); + MarkCodeVisitor visitor(thread, code_cache_, bitmap_); visitor.WalkStack(); if (kIsDebugBuild) { // The stack walking code queries the side instrumentation stack if it @@ -1320,7 +1320,7 @@ class MarkCodeClosure final : public Closure { code_cache_->LookupMethodHeader(frame.return_pc_, /* method= */ nullptr); if (method_header != nullptr) { const void* code = method_header->GetCode(); - CHECK(code_cache_->GetLiveBitmap()->Test(FromCodeToAllocation(code))); + CHECK(bitmap_->Test(FromCodeToAllocation(code))); } } } @@ -1329,6 +1329,7 @@ class MarkCodeClosure final : public Closure { private: JitCodeCache* const code_cache_; + CodeCacheBitmap* const bitmap_; Barrier* const barrier_; }; @@ -1374,7 +1375,7 @@ bool JitCodeCache::IncreaseCodeCacheCapacity() { void JitCodeCache::MarkCompiledCodeOnThreadStacks(Thread* self) { Barrier barrier(0); size_t threads_running_checkpoint = 0; - MarkCodeClosure closure(this, &barrier); + MarkCodeClosure closure(this, GetLiveBitmap(), &barrier); threads_running_checkpoint = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure); // Now that we have run our checkpoint, move to a suspended state and wait // for other threads to run the checkpoint. @@ -1987,11 +1988,6 @@ void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self, bool osr) { } } -size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { - MutexLock mu(Thread::Current(), lock_); - return mspace_usable_size(reinterpret_cast<const void*>(FromCodeToAllocation(ptr))); -} - void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { DCHECK(!method->IsNative()); diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 126fd441db..a5075638f2 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -71,7 +71,6 @@ template<class T> class ObjectArray; namespace jit { -class JitInstrumentationCache; class ScopedCodeCacheWrite; // Alignment in bits that will suit all architectures. @@ -97,12 +96,6 @@ class JitCodeCache { std::string* error_msg); ~JitCodeCache(); - // Number of bytes allocated in the code cache. - size_t CodeCacheSize() REQUIRES(!lock_); - - // Number of bytes allocated in the data cache. - size_t DataCacheSize() REQUIRES(!lock_); - bool NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!lock_); @@ -177,10 +170,6 @@ class JitCodeCache { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!lock_); - CodeCacheBitmap* GetLiveBitmap() const { - return live_bitmap_.get(); - } - // Perform a collection on the code cache. void GarbageCollectCache(Thread* self) REQUIRES(!lock_) @@ -234,10 +223,6 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - uint64_t GetLastUpdateTimeNs() const; - - size_t GetMemorySizeOfCodePointer(const void* ptr) REQUIRES(!lock_); - void InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* code) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -339,6 +324,12 @@ class JitCodeCache { void FreeCodeAndData(const void* code_ptr) REQUIRES(lock_); // Number of bytes allocated in the code cache. + size_t CodeCacheSize() REQUIRES(!lock_); + + // Number of bytes allocated in the data cache. + size_t DataCacheSize() REQUIRES(!lock_); + + // Number of bytes allocated in the code cache. size_t CodeCacheSizeLocked() REQUIRES(lock_); // Number of bytes allocated in the data cache. @@ -375,6 +366,10 @@ class JitCodeCache { REQUIRES(lock_) REQUIRES_SHARED(Locks::mutator_lock_); + CodeCacheBitmap* GetLiveBitmap() const { + return live_bitmap_.get(); + } + uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_); void FreeCode(uint8_t* code) REQUIRES(lock_); uint8_t* AllocateData(size_t data_size) REQUIRES(lock_); diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc index 5e01b7941f..af86cc0303 100644 --- a/runtime/jni/jni_internal.cc +++ b/runtime/jni/jni_internal.cc @@ -84,20 +84,20 @@ namespace art { // things not rendering correctly. E.g. b/16858794 static constexpr bool kWarnJniAbort = false; -static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames= */ 1)); -} - template<typename T> -ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) +ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - hiddenapi::Action action = hiddenapi::GetMemberAction( - member, self, IsCallerTrusted, hiddenapi::kJNI); - if (action != hiddenapi::kAllow) { - hiddenapi::NotifyHiddenApiListener(member); - } - - return action == hiddenapi::kDeny; + return hiddenapi::ShouldDenyAccessToMember( + member, + [&]() REQUIRES_SHARED(Locks::mutator_lock_) { + // Construct AccessContext from the first calling class on stack. + // If the calling class cannot be determined, e.g. unattached threads, + // we conservatively assume the caller is trusted. + ObjPtr<mirror::Class> caller = GetCallingClass(self, /* num_frames */ 1); + return caller.IsNull() ? hiddenapi::AccessContext(/* is_trusted= */ true) + : hiddenapi::AccessContext(caller); + }, + hiddenapi::AccessMethod::kJNI); } // Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set @@ -259,7 +259,7 @@ static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class, } else { method = c->FindClassMethod(name, sig, pointer_size); } - if (method != nullptr && ShouldBlockAccessToMember(method, soa.Self())) { + if (method != nullptr && ShouldDenyAccessToMember(method, soa.Self())) { method = nullptr; } if (method == nullptr || method->IsStatic() != is_static) { @@ -338,7 +338,7 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con } else { field = c->FindInstanceField(name, field_type->GetDescriptor(&temp)); } - if (field != nullptr && ShouldBlockAccessToMember(field, soa.Self())) { + if (field != nullptr && ShouldDenyAccessToMember(field, soa.Self())) { field = nullptr; } if (field == nullptr) { diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 6b9ba8c8a2..185ae3b2ac 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -1102,6 +1102,16 @@ inline bool Class::CannotBeAssignedFromOtherTypes() { return component->IsPrimitive() || component->CannotBeAssignedFromOtherTypes(); } +template <bool kCheckTransaction> +inline void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) { + if (kCheckTransaction && Runtime::Current()->IsActiveTransaction()) { + SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader); + } else { + DCHECK(!Runtime::Current()->IsActiveTransaction()); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader); + } +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index e33e407149..83d76a98cd 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -427,14 +427,6 @@ bool Class::IsThrowableClass() { return GetClassRoot<mirror::Throwable>()->IsAssignableFrom(this); } -void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) { - if (Runtime::Current()->IsActiveTransaction()) { - SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader); - } else { - SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader); - } -} - template <typename SignatureType> static inline ArtMethod* FindInterfaceMethodWithSignature(ObjPtr<Class> klass, const StringPiece& name, diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index bb54b3d33f..c38cc86c59 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -628,6 +628,7 @@ class MANAGED Class final : public Object { ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ClassLoader* GetClassLoader() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_); + template <bool kCheckTransaction = true> void SetClassLoader(ObjPtr<ClassLoader> new_cl) REQUIRES_SHARED(Locks::mutator_lock_); static constexpr MemberOffset DexCacheOffset() { diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 4d3ad620cc..56e9094983 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -76,12 +76,16 @@ static void EnableDebugger() { } } #endif - // We don't want core dumps, though, so set the core dump size to 0. + // We don't want core dumps, though, so set the soft limit on core dump size + // to 0 without changing the hard limit. rlimit rl; - rl.rlim_cur = 0; - rl.rlim_max = RLIM_INFINITY; - if (setrlimit(RLIMIT_CORE, &rl) == -1) { - PLOG(ERROR) << "setrlimit(RLIMIT_CORE) failed for pid " << getpid(); + if (getrlimit(RLIMIT_CORE, &rl) == -1) { + PLOG(ERROR) << "getrlimit(RLIMIT_CORE) failed for pid " << getpid(); + } else { + rl.rlim_cur = 0; + if (setrlimit(RLIMIT_CORE, &rl) == -1) { + PLOG(ERROR) << "setrlimit(RLIMIT_CORE) failed for pid " << getpid(); + } } } @@ -304,8 +308,7 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, // Our system thread ID, etc, has changed so reset Thread state. thread->InitAfterFork(); runtime_flags = EnableDebugFeatures(runtime_flags); - hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kNoChecks; - bool dedupe_hidden_api_warnings = true; + hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kDisabled; if ((runtime_flags & DISABLE_VERIFIER) != 0) { Runtime::Current()->DisableVerifier(); @@ -372,14 +375,14 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, } } - bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks; + bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled; DCHECK(!(is_system_server && do_hidden_api_checks)) << "SystemServer should be forked with EnforcementPolicy::kDisable"; DCHECK(!(is_zygote && do_hidden_api_checks)) << "Child zygote processes should be forked with EnforcementPolicy::kDisable"; Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy); - Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings); - if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks && + Runtime::Current()->SetDedupeHiddenApiWarnings(true); + if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled && Runtime::Current()->GetHiddenApiEventLogSampleRate() != 0) { // Hidden API checks are enabled, and we are sampling access for the event log. Initialize the // random seed, to ensure the sampling is actually random. We do this post-fork, as doing it @@ -387,9 +390,6 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, std::srand(static_cast<uint32_t>(NanoTime())); } - // Clear the hidden API warning flag, in case it was set. - Runtime::Current()->SetPendingHiddenApiWarning(false); - if (is_zygote) { // If creating a child-zygote, do not call into the runtime's post-fork logic. // Doing so would spin up threads for Binder and JDWP. Instead, the Java side diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index c7b8ad4392..612a4b33b1 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -54,11 +54,12 @@ namespace art { -// Returns true if the first caller outside of the Class class or java.lang.invoke package -// is in a platform DEX file. -static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke. - // This is very expensive. Save this till the last. +// Walks the stack, finds the caller of this reflective call and returns +// a hiddenapi AccessContext formed from its declaring class. +static hiddenapi::AccessContext GetReflectionCaller(Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Walk the stack and find the first frame not from java.lang.Class and not + // from java.lang.invoke. This is very expensive. Save this till the last. struct FirstExternalCallerVisitor : public StackVisitor { explicit FirstExternalCallerVisitor(Thread* thread) : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), @@ -102,50 +103,42 @@ static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) FirstExternalCallerVisitor visitor(self); visitor.WalkStack(); - return visitor.caller != nullptr && - hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass()); -} -// Returns true if the first non-ClassClass caller up the stack is not allowed to -// access hidden APIs. This can be *very* expensive. Never call this in a loop. -ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) - REQUIRES_SHARED(Locks::mutator_lock_) { - hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); - return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self); + // Construct AccessContext from the calling class found on the stack. + // If the calling class cannot be determined, e.g. unattached threads, + // we conservatively assume the caller is trusted. + ObjPtr<mirror::Class> caller = (visitor.caller == nullptr) + ? nullptr : visitor.caller->GetDeclaringClass(); + return caller.IsNull() ? hiddenapi::AccessContext(/* is_trusted= */ true) + : hiddenapi::AccessContext(caller); } // Returns true if the first non-ClassClass caller up the stack should not be // allowed access to `member`. template<typename T> -ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) +ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - hiddenapi::Action action = hiddenapi::GetMemberAction( - member, self, IsCallerTrusted, hiddenapi::kReflection); - if (action != hiddenapi::kAllow) { - hiddenapi::NotifyHiddenApiListener(member); - } - - return action == hiddenapi::kDeny; + return hiddenapi::ShouldDenyAccessToMember( + member, + [&]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); }, + hiddenapi::AccessMethod::kReflection); } // Returns true if a class member should be discoverable with reflection given // the criteria. Some reflection calls only return public members // (public_only == true), some members should be hidden from non-boot class path -// callers (enforce_hidden_api == true). +// callers (hiddenapi_context). template<typename T> ALWAYS_INLINE static bool IsDiscoverable(bool public_only, - bool enforce_hidden_api, + hiddenapi::AccessContext access_context, T* member) REQUIRES_SHARED(Locks::mutator_lock_) { if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) { return false; } - return hiddenapi::GetMemberAction(member, - nullptr, - [enforce_hidden_api] (Thread*) { return !enforce_hidden_api; }, - hiddenapi::kNone) - != hiddenapi::kDeny; + return !hiddenapi::ShouldDenyAccessToMember( + member, access_context, hiddenapi::AccessMethod::kNone); } ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass( @@ -266,15 +259,15 @@ static ObjPtr<mirror::ObjectArray<mirror::Field>> GetDeclaredFields( IterationRange<StrideIterator<ArtField>> ifields = klass->GetIFields(); IterationRange<StrideIterator<ArtField>> sfields = klass->GetSFields(); size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields(); - bool enforce_hidden_api = ShouldEnforceHiddenApi(self); + hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(self); // Lets go subtract all the non discoverable fields. for (ArtField& field : ifields) { - if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) { + if (!IsDiscoverable(public_only, hiddenapi_context, &field)) { --array_size; } } for (ArtField& field : sfields) { - if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) { + if (!IsDiscoverable(public_only, hiddenapi_context, &field)) { --array_size; } } @@ -285,7 +278,7 @@ static ObjPtr<mirror::ObjectArray<mirror::Field>> GetDeclaredFields( return nullptr; } for (ArtField& field : ifields) { - if (IsDiscoverable(public_only, enforce_hidden_api, &field)) { + if (IsDiscoverable(public_only, hiddenapi_context, &field)) { auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, &field, force_resolve); @@ -300,7 +293,7 @@ static ObjPtr<mirror::ObjectArray<mirror::Field>> GetDeclaredFields( } } for (ArtField& field : sfields) { - if (IsDiscoverable(public_only, enforce_hidden_api, &field)) { + if (IsDiscoverable(public_only, hiddenapi_context, &field)) { auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, &field, force_resolve); @@ -459,8 +452,7 @@ static jobject Class_getPublicFieldRecursive(JNIEnv* env, jobject javaThis, jstr StackHandleScope<1> hs(soa.Self()); Handle<mirror::Field> field = hs.NewHandle(GetPublicFieldRecursive( soa.Self(), DecodeClass(soa, javaThis), name_string)); - if (field.Get() == nullptr || - ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) { + if (field.Get() == nullptr || ShouldDenyAccessToMember(field->GetArtField(), soa.Self())) { return nullptr; } return soa.AddLocalReference<jobject>(field.Get()); @@ -477,7 +469,7 @@ static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring nam Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis)); Handle<mirror::Field> result = hs.NewHandle(GetDeclaredField(soa.Self(), h_klass.Get(), h_string.Get())); - if (result == nullptr || ShouldBlockAccessToMember(result->GetArtField(), soa.Self())) { + if (result == nullptr || ShouldDenyAccessToMember(result->GetArtField(), soa.Self())) { std::string name_str = h_string->ToModifiedUtf8(); if (name_str == "value" && h_klass->IsStringClass()) { // We log the error for this specific case, as the user might just swallow the exception. @@ -509,19 +501,19 @@ static jobject Class_getDeclaredConstructorInternal( soa.Self(), DecodeClass(soa, javaThis), soa.Decode<mirror::ObjectArray<mirror::Class>>(args))); - if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { + if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } return soa.AddLocalReference<jobject>(result.Get()); } static ALWAYS_INLINE inline bool MethodMatchesConstructor( - ArtMethod* m, bool public_only, bool enforce_hidden_api) + ArtMethod* m, bool public_only, hiddenapi::AccessContext hiddenapi_context) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(m != nullptr); return m->IsConstructor() && !m->IsStatic() && - IsDiscoverable(public_only, enforce_hidden_api, m); + IsDiscoverable(public_only, hiddenapi_context, m); } static jobjectArray Class_getDeclaredConstructorsInternal( @@ -529,12 +521,12 @@ static jobjectArray Class_getDeclaredConstructorsInternal( ScopedFastNativeObjectAccess soa(env); StackHandleScope<2> hs(soa.Self()); bool public_only = (publicOnly != JNI_FALSE); - bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self()); + hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(soa.Self()); Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis)); size_t constructor_count = 0; // Two pass approach for speed. for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) { - constructor_count += MethodMatchesConstructor(&m, public_only, enforce_hidden_api) ? 1u : 0u; + constructor_count += MethodMatchesConstructor(&m, public_only, hiddenapi_context) ? 1u : 0u; } auto h_constructors = hs.NewHandle(mirror::ObjectArray<mirror::Constructor>::Alloc( soa.Self(), GetClassRoot<mirror::ObjectArray<mirror::Constructor>>(), constructor_count)); @@ -544,7 +536,7 @@ static jobjectArray Class_getDeclaredConstructorsInternal( } constructor_count = 0; for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) { - if (MethodMatchesConstructor(&m, public_only, enforce_hidden_api)) { + if (MethodMatchesConstructor(&m, public_only, hiddenapi_context)) { DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); ObjPtr<mirror::Constructor> constructor = @@ -571,7 +563,7 @@ static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, DecodeClass(soa, javaThis), soa.Decode<mirror::String>(name), soa.Decode<mirror::ObjectArray<mirror::Class>>(args))); - if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { + if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } return soa.AddLocalReference<jobject>(result.Get()); @@ -582,7 +574,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT ScopedFastNativeObjectAccess soa(env); StackHandleScope<2> hs(soa.Self()); - bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self()); + hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(soa.Self()); bool public_only = (publicOnly != JNI_FALSE); Handle<mirror::Class> klass = hs.NewHandle(DecodeClass(soa, javaThis)); @@ -591,7 +583,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT uint32_t modifiers = m.GetAccessFlags(); // Add non-constructor declared methods. if ((modifiers & kAccConstructor) == 0 && - IsDiscoverable(public_only, enforce_hidden_api, &m)) { + IsDiscoverable(public_only, hiddenapi_context, &m)) { ++num_methods; } } @@ -605,7 +597,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) { uint32_t modifiers = m.GetAccessFlags(); if ((modifiers & kAccConstructor) == 0 && - IsDiscoverable(public_only, enforce_hidden_api, &m)) { + IsDiscoverable(public_only, hiddenapi_context, &m)) { DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); ObjPtr<mirror::Method> method = @@ -819,7 +811,7 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) { soa.Self(), ScopedNullHandle<mirror::ObjectArray<mirror::Class>>(), kRuntimePointerSize); - if (UNLIKELY(constructor == nullptr) || ShouldBlockAccessToMember(constructor, soa.Self())) { + if (UNLIKELY(constructor == nullptr) || ShouldDenyAccessToMember(constructor, soa.Self())) { soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;", "%s has no zero argument constructor", klass->PrettyClass().c_str()); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7367706deb..4720599a91 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -269,10 +269,8 @@ Runtime::Runtime() oat_file_manager_(nullptr), is_low_memory_mode_(false), safe_mode_(false), - hidden_api_policy_(hiddenapi::EnforcementPolicy::kNoChecks), - pending_hidden_api_warning_(false), + hidden_api_policy_(hiddenapi::EnforcementPolicy::kDisabled), dedupe_hidden_api_warnings_(true), - always_set_hidden_api_warning_flag_(false), hidden_api_access_event_log_rate_(0), dump_native_stack_on_sig_quit_(true), pruned_dalvik_cache_(false), @@ -1245,8 +1243,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // As is, we're encoding some logic here about which specific policy to use, which would be better // controlled by the framework. hidden_api_policy_ = do_hidden_api_checks - ? hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList - : hiddenapi::EnforcementPolicy::kNoChecks; + ? hiddenapi::EnforcementPolicy::kEnabled + : hiddenapi::EnforcementPolicy::kDisabled; no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain); force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge); diff --git a/runtime/runtime.h b/runtime/runtime.h index e2176c2117..f5aa300649 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -535,10 +535,6 @@ class Runtime { return hidden_api_policy_; } - void SetPendingHiddenApiWarning(bool value) { - pending_hidden_api_warning_ = value; - } - void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) { hidden_api_exemptions_ = exemptions; } @@ -547,10 +543,6 @@ class Runtime { return hidden_api_exemptions_; } - bool HasPendingHiddenApiWarning() const { - return pending_hidden_api_warning_; - } - void SetDedupeHiddenApiWarnings(bool value) { dedupe_hidden_api_warnings_ = value; } @@ -559,14 +551,6 @@ class Runtime { return dedupe_hidden_api_warnings_; } - void AlwaysSetHiddenApiWarningFlag() { - always_set_hidden_api_warning_flag_ = true; - } - - bool ShouldAlwaysSetHiddenApiWarningFlag() const { - return always_set_hidden_api_warning_flag_; - } - void SetHiddenApiEventLogSampleRate(uint32_t rate) { hidden_api_access_event_log_rate_ = rate; } diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 65039bc6d4..5300e2d389 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -119,6 +119,7 @@ jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch; jfieldID WellKnownClasses::dalvik_system_DexFile_cookie; jfieldID WellKnownClasses::dalvik_system_DexFile_fileName; jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList; +jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders; jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements; jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile; jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer; @@ -293,7 +294,7 @@ uint32_t WellKnownClasses::StringInitToEntryPoint(ArtMethod* string_init) { void WellKnownClasses::Init(JNIEnv* env) { hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption( - hiddenapi::EnforcementPolicy::kNoChecks); + hiddenapi::EnforcementPolicy::kDisabled); dalvik_annotation_optimization_CriticalNative = CacheClass(env, "dalvik/annotation/optimization/CriticalNative"); @@ -367,6 +368,7 @@ void WellKnownClasses::Init(JNIEnv* env) { org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;"); dalvik_system_BaseDexClassLoader_pathList = CacheField(env, dalvik_system_BaseDexClassLoader, false, "pathList", "Ldalvik/system/DexPathList;"); + dalvik_system_BaseDexClassLoader_sharedLibraryLoaders = CacheField(env, dalvik_system_BaseDexClassLoader, false, "sharedLibraryLoaders", "[Ljava/lang/ClassLoader;"); dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;"); dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;"); dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 130747c026..0b7ed0994c 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -126,6 +126,7 @@ struct WellKnownClasses { static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch; static jfieldID dalvik_system_BaseDexClassLoader_pathList; + static jfieldID dalvik_system_BaseDexClassLoader_sharedLibraryLoaders; static jfieldID dalvik_system_DexFile_cookie; static jfieldID dalvik_system_DexFile_fileName; static jfieldID dalvik_system_DexPathList_dexElements; diff --git a/simulator/Android.bp b/simulator/Android.bp index 8690426466..223c891da0 100644 --- a/simulator/Android.bp +++ b/simulator/Android.bp @@ -45,7 +45,7 @@ art_cc_library { shared_libs: [ "libart", "libartbase", - "libvixl-arm64", + "libvixl", ], } @@ -58,7 +58,7 @@ art_cc_library { shared_libs: [ "libartd", "libartbased", - "libvixld-arm64", + "libvixld", ], } diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc index 96754c3076..8e3e4eb5ce 100644 --- a/test/674-hiddenapi/hiddenapi.cc +++ b/test/674-hiddenapi/hiddenapi.cc @@ -28,9 +28,9 @@ namespace Test674HiddenApi { extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) { Runtime* runtime = Runtime::Current(); - runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly); + runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); + runtime->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1)); runtime->SetDedupeHiddenApiWarnings(false); - runtime->AlwaysSetHiddenApiWarningFlag(); } extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader( @@ -287,13 +287,5 @@ extern "C" JNIEXPORT jint JNICALL Java_Reflection_getHiddenApiAccessFlags(JNIEnv return static_cast<jint>(kAccHiddenApiBits); } -extern "C" JNIEXPORT jboolean JNICALL Java_ChildClass_hasPendingWarning(JNIEnv*, jclass) { - return Runtime::Current()->HasPendingHiddenApiWarning(); -} - -extern "C" JNIEXPORT void JNICALL Java_ChildClass_clearWarning(JNIEnv*, jclass) { - Runtime::Current()->SetPendingHiddenApiWarning(false); -} - } // namespace Test674HiddenApi } // namespace art diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index db3ba6d8bf..3427b8eba6 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -98,10 +98,8 @@ public class ChildClass { expected = Behaviour.Granted; } else if (hiddenness == Hiddenness.Blacklist) { expected = Behaviour.Denied; - } else if (isDebuggable) { - expected = Behaviour.Warning; } else { - expected = Behaviour.Granted; + expected = Behaviour.Warning; } for (boolean isStatic : booleanValues) { @@ -145,7 +143,7 @@ public class ChildClass { } private static void checkMemberCallback(Class<?> klass, String name, - boolean isPublic, boolean isField) { + boolean isPublic, boolean isField, boolean expectedCallback) { try { RecordingConsumer consumer = new RecordingConsumer(); VMRuntime.setNonSdkApiUsageConsumer(consumer); @@ -168,8 +166,14 @@ public class ChildClass { // only interested in whether the callback is invoked. } - if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) { - throw new RuntimeException("No callback for member: " + name); + boolean actualCallback = consumer.recordedValue != null && + consumer.recordedValue.contains(name); + if (expectedCallback != actualCallback) { + if (expectedCallback) { + throw new RuntimeException("Expected callback for member: " + name); + } else { + throw new RuntimeException("Did not expect callback for member: " + name); + } } } finally { VMRuntime.setNonSdkApiUsageConsumer(null); @@ -181,7 +185,7 @@ public class ChildClass { boolean isPublic = (visibility == Visibility.Public); boolean canDiscover = (behaviour != Behaviour.Denied); - boolean setsWarning = (behaviour == Behaviour.Warning); + boolean invokesMemberCallback = (behaviour != Behaviour.Granted); if (klass.isInterface() && (!isStatic || !isPublic)) { // Interfaces only have public static fields. @@ -243,8 +247,6 @@ public class ChildClass { canDiscover); } - // Finish here if we could not discover the field. - if (canDiscover) { // Test that modifiers are unaffected. @@ -254,44 +256,22 @@ public class ChildClass { // Test getters and setters when meaningful. - clearWarning(); if (!Reflection.canGetField(klass, name)) { throwAccessException(klass, name, true, "Field.getInt()"); } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.getInt()", setsWarning); - } - - clearWarning(); if (!Reflection.canSetField(klass, name)) { throwAccessException(klass, name, true, "Field.setInt()"); } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.setInt()", setsWarning); - } - - clearWarning(); if (!JNI.canGetField(klass, name, isStatic)) { throwAccessException(klass, name, true, "getIntField"); } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "getIntField", setsWarning); - } - - clearWarning(); if (!JNI.canSetField(klass, name, isStatic)) { throwAccessException(klass, name, true, "setIntField"); } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "setIntField", setsWarning); - } } // Test that callbacks are invoked correctly. - clearWarning(); - if (setsWarning || !canDiscover) { - checkMemberCallback(klass, name, isPublic, true /* isField */); - } + checkMemberCallback(klass, name, isPublic, true /* isField */, invokesMemberCallback); } private static void checkMethod(Class<?> klass, String name, boolean isStatic, @@ -304,7 +284,7 @@ public class ChildClass { } boolean canDiscover = (behaviour != Behaviour.Denied); - boolean setsWarning = (behaviour == Behaviour.Warning); + boolean invokesMemberCallback = (behaviour != Behaviour.Granted); // Test discovery with reflection. @@ -354,39 +334,21 @@ public class ChildClass { } // Test whether we can invoke the method. This skips non-static interface methods. - if (!klass.isInterface() || isStatic) { - clearWarning(); if (!Reflection.canInvokeMethod(klass, name)) { throwAccessException(klass, name, false, "invoke()"); } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "invoke()", setsWarning); - } - - clearWarning(); if (!JNI.canInvokeMethodA(klass, name, isStatic)) { throwAccessException(klass, name, false, "CallMethodA"); } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodA()", setsWarning); - } - - clearWarning(); if (!JNI.canInvokeMethodV(klass, name, isStatic)) { throwAccessException(klass, name, false, "CallMethodV"); } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodV()", setsWarning); - } } } // Test that callbacks are invoked correctly. - clearWarning(); - if (setsWarning || !canDiscover) { - checkMemberCallback(klass, name, isPublic, false /* isField */); - } + checkMemberCallback(klass, name, isPublic, false /* isField */, invokesMemberCallback); } private static void checkConstructor(Class<?> klass, Visibility visibility, Hiddenness hiddenness, @@ -403,7 +365,6 @@ public class ChildClass { MethodType methodType = MethodType.methodType(void.class, args); boolean canDiscover = (behaviour != Behaviour.Denied); - boolean setsWarning = (behaviour == Behaviour.Warning); // Test discovery with reflection. @@ -446,70 +407,41 @@ public class ChildClass { canDiscover); } - // Finish here if we could not discover the constructor. - - if (!canDiscover) { - return; - } - - // Test whether we can invoke the constructor. - - clearWarning(); - if (!Reflection.canInvokeConstructor(klass, args, initargs)) { - throwAccessException(klass, fullName, false, "invoke()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, fullName, false, "invoke()", setsWarning); - } - - clearWarning(); - if (!JNI.canInvokeConstructorA(klass, signature)) { - throwAccessException(klass, fullName, false, "NewObjectA"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, fullName, false, "NewObjectA", setsWarning); - } + if (canDiscover) { + // Test whether we can invoke the constructor. - clearWarning(); - if (!JNI.canInvokeConstructorV(klass, signature)) { - throwAccessException(klass, fullName, false, "NewObjectV"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, fullName, false, "NewObjectV", setsWarning); + if (!Reflection.canInvokeConstructor(klass, args, initargs)) { + throwAccessException(klass, fullName, false, "invoke()"); + } + if (!JNI.canInvokeConstructorA(klass, signature)) { + throwAccessException(klass, fullName, false, "NewObjectA"); + } + if (!JNI.canInvokeConstructorV(klass, signature)) { + throwAccessException(klass, fullName, false, "NewObjectV"); + } } } private static void checkNullaryConstructor(Class<?> klass, Behaviour behaviour) throws Exception { boolean canAccess = (behaviour != Behaviour.Denied); - boolean setsWarning = (behaviour == Behaviour.Warning); - clearWarning(); if (Reflection.canUseNewInstance(klass) != canAccess) { throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") + "be able to construct " + klass.getName() + ". " + "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot); } - if (canAccess && hasPendingWarning() != setsWarning) { - throwWarningException(klass, "nullary constructor", false, "newInstance", setsWarning); - } } private static void checkLinking(String className, boolean takesParameter, Behaviour behaviour) throws Exception { boolean canAccess = (behaviour != Behaviour.Denied); - boolean setsWarning = (behaviour == Behaviour.Warning); - clearWarning(); if (Linking.canAccess(className, takesParameter) != canAccess) { throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") + "be able to verify " + className + "." + "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot); } - if (canAccess && hasPendingWarning() != setsWarning) { - throwWarningException( - Class.forName(className), "access", false, "static linking", setsWarning); - } } private static void throwDiscoveryException(Class<?> klass, String name, boolean isField, @@ -528,15 +460,6 @@ public class ChildClass { "everythingWhitelisted = " + everythingWhitelisted); } - private static void throwWarningException(Class<?> klass, String name, boolean isField, - String fn, boolean setsWarning) { - throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") + - klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") + - "set the warning flag. " + - "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " + - "everythingWhitelisted = " + everythingWhitelisted); - } - private static void throwModifiersException(Class<?> klass, String name, boolean isField) { throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() + "." + name + " to not expose hidden modifiers"); @@ -545,7 +468,4 @@ public class ChildClass { private static boolean isParentInBoot; private static boolean isChildInBoot; private static boolean everythingWhitelisted; - - private static native boolean hasPendingWarning(); - private static native void clearWarning(); } diff --git a/test/688-shared-library/check b/test/688-shared-library/check new file mode 100644 index 0000000000..0b6c9e4d2f --- /dev/null +++ b/test/688-shared-library/check @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Copyright (C) 2015 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. + +# Finalizers of DexFile will complain not being able to close +# the main dex file, as it's still open. That's OK to ignore. +sed -e '/^E\/System/d' "$2" > "$2.tmp" + +diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null diff --git a/test/688-shared-library/expected.txt b/test/688-shared-library/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/688-shared-library/expected.txt diff --git a/test/688-shared-library/info.txt b/test/688-shared-library/info.txt new file mode 100644 index 0000000000..2eda65d454 --- /dev/null +++ b/test/688-shared-library/info.txt @@ -0,0 +1,2 @@ +Tests on BaseDexClassLoader shared libraries and their class +loading behavior. diff --git a/test/688-shared-library/src-art/Main.java b/test/688-shared-library/src-art/Main.java new file mode 100644 index 0000000000..d59e7dc10f --- /dev/null +++ b/test/688-shared-library/src-art/Main.java @@ -0,0 +1,194 @@ +/* + * 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 dalvik.system.DelegateLastClassLoader; +import dalvik.system.PathClassLoader; + +public class Main { + static final String TEST_NAME = "688-shared-library"; + static final String MAIN_JAR_FILE = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar"; + static final String EX_JAR_FILE = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + "-ex.jar"; + static ClassLoader bootLoader = Object.class.getClassLoader(); + + public static void main(String[] args) throws Exception { + testNoLibrary(); + testOneLibrary(); + testTwoLibraries1(); + testTwoLibraries2(); + testTransitive1(); + testTransitive2(); + testTransitive3(); + testTransitive4(); + } + + public static void assertIdentical(Object expected, Object actual) { + if (expected != actual) { + throw new Error("Expected " + expected + ", got " + actual); + } + } + + public static void testNoLibrary() throws Exception { + ClassLoader loader = new PathClassLoader(MAIN_JAR_FILE, null, bootLoader); + Class<?> cls = loader.loadClass("Main"); + assertIdentical(loader, cls.getClassLoader()); + } + + public static void testOneLibrary() throws Exception { + ClassLoader[] sharedLibraries = { + new PathClassLoader(EX_JAR_FILE, null, bootLoader), + }; + ClassLoader delegateFirst = + new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries); + Class<?> cls = delegateFirst.loadClass("Main"); + assertIdentical(sharedLibraries[0], cls.getClassLoader()); + cls = delegateFirst.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraries[0], cls.getClassLoader()); + + ClassLoader delegateLast = + new DelegateLastClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries); + cls = delegateLast.loadClass("Main"); + assertIdentical(sharedLibraries[0], cls.getClassLoader()); + cls = delegateLast.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraries[0], cls.getClassLoader()); + } + + public static void testTwoLibraries1() throws Exception { + ClassLoader[] sharedLibraries = { + new PathClassLoader(MAIN_JAR_FILE, null, bootLoader), + new PathClassLoader(EX_JAR_FILE, null, bootLoader), + }; + ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraries); + Class<?> cls = delegateFirst.loadClass("Main"); + assertIdentical(sharedLibraries[0], cls.getClassLoader()); + cls = delegateFirst.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraries[1], cls.getClassLoader()); + + ClassLoader delegateLast = + new DelegateLastClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries); + cls = delegateLast.loadClass("Main"); + assertIdentical(sharedLibraries[0], cls.getClassLoader()); + cls = delegateLast.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraries[1], cls.getClassLoader()); + } + + public static void testTwoLibraries2() throws Exception { + ClassLoader[] sharedLibraries = { + new PathClassLoader(EX_JAR_FILE, null, bootLoader), + new PathClassLoader(MAIN_JAR_FILE, null, bootLoader), + }; + ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraries); + Class<?> cls = delegateFirst.loadClass("Main"); + assertIdentical(sharedLibraries[0], cls.getClassLoader()); + cls = delegateFirst.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraries[0], cls.getClassLoader()); + + ClassLoader delegateLast = new DelegateLastClassLoader("", null, bootLoader, sharedLibraries); + cls = delegateLast.loadClass("Main"); + assertIdentical(sharedLibraries[0], cls.getClassLoader()); + cls = delegateLast.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraries[0], cls.getClassLoader()); + } + + public static void testTransitive1() throws Exception { + ClassLoader[] sharedLibraryLevel2 = { + new PathClassLoader(EX_JAR_FILE, null, bootLoader), + }; + ClassLoader[] sharedLibraryLevel1 = { + new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraryLevel2), + }; + + ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1); + Class<?> cls = delegateFirst.loadClass("Main"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + cls = delegateFirst.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + + ClassLoader delegateLast = + new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1); + cls = delegateLast.loadClass("Main"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + cls = delegateLast.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + } + + public static void testTransitive2() throws Exception { + ClassLoader[] sharedLibraryLevel2 = { + new PathClassLoader(MAIN_JAR_FILE, null, bootLoader), + }; + ClassLoader[] sharedLibraryLevel1 = { + new PathClassLoader(EX_JAR_FILE, null, bootLoader, sharedLibraryLevel2), + }; + + ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1); + Class<?> cls = delegateFirst.loadClass("Main"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + cls = delegateFirst.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader()); + + ClassLoader delegateLast = + new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1); + cls = delegateLast.loadClass("Main"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + cls = delegateLast.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader()); + } + + public static void testTransitive3() throws Exception { + ClassLoader[] sharedLibraryLevel2 = { + new PathClassLoader(MAIN_JAR_FILE, null, bootLoader), + }; + ClassLoader[] sharedLibraryLevel1 = { + new PathClassLoader(EX_JAR_FILE, null, bootLoader, sharedLibraryLevel2), + sharedLibraryLevel2[0], + }; + + ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1); + Class<?> cls = delegateFirst.loadClass("Main"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + cls = delegateFirst.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader()); + + ClassLoader delegateLast = + new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1); + cls = delegateLast.loadClass("Main"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + cls = delegateLast.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader()); + } + + public static void testTransitive4() throws Exception { + ClassLoader[] sharedLibraryLevel2 = { + new PathClassLoader(EX_JAR_FILE, null, bootLoader), + }; + ClassLoader[] sharedLibraryLevel1 = { + new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraryLevel2), + sharedLibraryLevel2[0], + }; + + ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1); + Class<?> cls = delegateFirst.loadClass("Main"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + cls = delegateFirst.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + + ClassLoader delegateLast = + new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1); + cls = delegateLast.loadClass("Main"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + cls = delegateLast.loadClass("SharedLibraryOne"); + assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader()); + } +} diff --git a/test/688-shared-library/src-ex/Main.java b/test/688-shared-library/src-ex/Main.java new file mode 100644 index 0000000000..f6555b95bc --- /dev/null +++ b/test/688-shared-library/src-ex/Main.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 Main { +} diff --git a/test/688-shared-library/src-ex/SharedLibraryOne.java b/test/688-shared-library/src-ex/SharedLibraryOne.java new file mode 100644 index 0000000000..d86755f6b2 --- /dev/null +++ b/test/688-shared-library/src-ex/SharedLibraryOne.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 SharedLibraryOne { +} diff --git a/test/Android.bp b/test/Android.bp index 561f95eb47..d85e2a6fca 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -59,8 +59,7 @@ art_cc_defaults { shared_libs: [ "libartd", "libartd-disassembler", - "libvixld-arm", - "libvixld-arm64", + "libvixld", "libart-gtest", "libdexfiled", "libprofiled", diff --git a/test/knownfailures.json b/test/knownfailures.json index f0eacfae8b..ae20557e2f 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -1026,6 +1026,7 @@ "677-fsi2", "678-quickening", "679-locks", + "688-shared-library", "999-redefine-hiddenapi", "1000-non-moving-space-stress", "1951-monitor-enter-no-suspend", diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py index 14287b0f16..b4a4ada506 100755 --- a/test/testrunner/run_build_test_target.py +++ b/test/testrunner/run_build_test_target.py @@ -62,6 +62,18 @@ custom_env['SOONG_ALLOW_MISSING_DEPENDENCIES'] = 'true' print(custom_env) os.environ.update(custom_env) +# build is just a binary/script that is directly executed to build any artifacts needed for the +# test. +if 'build' in target: + build_command = target.get('build').format( + ANDROID_BUILD_TOP = env.ANDROID_BUILD_TOP, + MAKE_OPTIONS='DX= -j{threads}'.format(threads = n_threads)) + sys.stdout.write(str(build_command) + '\n') + sys.stdout.flush() + if subprocess.call(build_command.split()): + sys.exit(1) + +# make runs soong/kati to build the target listed in the entry. if 'make' in target: build_command = 'build/soong/soong_ui.bash --make-mode' build_command += ' DX=' @@ -107,7 +119,8 @@ if 'run-test' in target: run_test_command += ['--host'] run_test_command += ['--dex2oat-jobs'] run_test_command += ['4'] - run_test_command += ['-b'] + if '--no-build-dependencies' not in test_flags: + run_test_command += ['-b'] run_test_command += ['--verbose'] sys.stdout.write(str(run_test_command) + '\n') diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 9efe435dc2..077129fa6c 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -329,4 +329,11 @@ target_config = { 'art-golem-linux-x64': { 'golem' : 'linux-x64' }, + 'art-linux-bionic-x64': { + 'build': '{ANDROID_BUILD_TOP}/art/tools/build_linux_bionic_tests.sh {MAKE_OPTIONS}', + 'run-test': ['--run-test-option=--bionic', + '--host', + '--64', + '--no-build-dependencies'], + }, } diff --git a/tools/build_linux_bionic_tests.sh b/tools/build_linux_bionic_tests.sh index dba2d50e38..3ee7d506aa 100755 --- a/tools/build_linux_bionic_tests.sh +++ b/tools/build_linux_bionic_tests.sh @@ -79,6 +79,7 @@ bionic_targets=( $soong_out/bin/profmand $soong_out/bin/hiddenapi $soong_out/bin/hprof-conv + $soong_out/bin/timeout_dumper $(find $ANDROID_HOST_OUT/lib64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g") $(find $ANDROID_HOST_OUT/nativetest64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g")) diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index c1cc2e2318..da463d5511 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -61,7 +61,7 @@ while true; do done # Allow to build successfully in master-art. -extra_args=SOONG_ALLOW_MISSING_DEPENDENCIES=true +extra_args="SOONG_ALLOW_MISSING_DEPENDENCIES=true TEMPORARY_DISABLE_PATH_RESTRICTIONS=true" if [[ $mode == "host" ]]; then make_command="make $j_arg $extra_args $showcommands build-art-host-tests $common_targets" @@ -94,4 +94,5 @@ done echo "Executing $make_command" +# Disable path restrictions to enable luci builds using vpython. bash -c "$make_command" |