diff options
91 files changed, 2054 insertions, 981 deletions
diff --git a/Android.mk b/Android.mk index b9f617035c..3388b35aca 100644 --- a/Android.mk +++ b/Android.mk @@ -326,6 +326,53 @@ endif ####################### +# Android Runtime APEX. + +include $(CLEAR_VARS) + +# The Android Runtime APEX comes in two flavors: +# - the release module (`com.android.runtime.release`), containing +# only "release" artifacts; +# - the debug module (`com.android.runtime.debug`), containing both +# "release" and "debug" artifacts, as well as additional tools. +# +# The Android Runtime APEX module (`com.android.runtime`) is an +# "alias" for one of the previous modules. By default, "user" build +# variants contain the release module, while "userdebug" and "eng" +# build variant contain the debug module. However, if +# `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is defined, it overrides +# the previous logic: +# - if `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is set to `false`, the +# build will include the release module (whatever the build +# variant); +# - if `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is set to `true`, the +# build will include the debug module (whatever the build variant). + +art_target_include_debug_build := $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD) +ifneq (false,$(art_target_include_debug_build)) + ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) + art_target_include_debug_build := true + endif +endif +ifeq (true,$(art_target_include_debug_build)) + # Module with both release and debug variants, as well as + # additional tools. + TARGET_RUNTIME_APEX := com.android.runtime.debug +else + # Release module (without debug variants nor tools). + TARGET_RUNTIME_APEX := com.android.runtime.release +endif + +LOCAL_MODULE := com.android.runtime +LOCAL_REQUIRED_MODULES := $(TARGET_RUNTIME_APEX) + +# Clear locally used variable. +art_target_include_debug_build := + +include $(BUILD_PHONY_PACKAGE) + + +####################### # Fake packages for ART # The art-runtime package depends on the core ART libraries and binaries. It exists so we can diff --git a/build/apex/Android.bp b/build/apex/Android.bp index 8bddb5dfe7..159e5c1992 100644 --- a/build/apex/Android.bp +++ b/build/apex/Android.bp @@ -68,12 +68,45 @@ art_tools_host_binaries = [ apex_key { name: "com.android.runtime.key", - public_key: "runtime.avbpubkey", - private_key: "runtime.pem", + public_key: "com.android.runtime.avbpubkey", + private_key: "com.android.runtime.pem", } +// TODO: Introduce `apex_defaults` to factor common parts of `apex` +// module definitions below? + +// Release version of the Runtime APEX module (not containing debug +// variants nor tools), included in user builds. Also used for +// storage-constrained devices in userdebug and eng builds. +apex { + name: "com.android.runtime.release", + compile_multilib: "both", + manifest: "manifest.json", + native_shared_libs: art_runtime_base_native_shared_libs + + art_runtime_fake_native_shared_libs, + multilib: { + both: { + // TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64` + // (see `symlink_preferred_arch` in art/dalvikvm/Android.bp). + binaries: art_runtime_base_binaries_both, + }, + prefer32: { + binaries: art_runtime_base_binaries_prefer32, + }, + first: { + binaries: [], + } + }, + key: "com.android.runtime.key", + // TODO: Also package a `ld.config.txt` config file (to be placed in `etc/`). + // ... +} + +// "Debug" version of the Runtime APEX module (containing both release and +// debug variants, as well as additional tools), included in userdebug and +// eng build. apex { - name: "com.android.runtime", + name: "com.android.runtime.debug", compile_multilib: "both", manifest: "manifest.json", native_shared_libs: art_runtime_base_native_shared_libs @@ -87,7 +120,7 @@ apex { }, prefer32: { binaries: art_runtime_base_binaries_prefer32 - + art_runtime_debug_binaries_prefer32 + + art_runtime_debug_binaries_prefer32, }, first: { binaries: art_tools_binaries, diff --git a/build/apex/runtime.avbpubkey b/build/apex/com.android.runtime.avbpubkey Binary files differindex b0ffc9b11f..b0ffc9b11f 100644 --- a/build/apex/runtime.avbpubkey +++ b/build/apex/com.android.runtime.avbpubkey diff --git a/build/apex/runtime.pem b/build/apex/com.android.runtime.pem index 4c7ce4b30b..4c7ce4b30b 100644 --- a/build/apex/runtime.pem +++ b/build/apex/com.android.runtime.pem diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh index 6af2a8bc5c..86cd8cb758 100755 --- a/build/apex/runtests.sh +++ b/build/apex/runtests.sh @@ -79,7 +79,8 @@ function finish { trap finish EXIT -apex_module="com.android.runtime" +# TODO: Also exercise the Release Runtime APEX (`com.android.runtime.release`). +apex_module="com.android.runtime.debug" # Build the Android Runtime APEX package (optional). $build_apex_p && say "Building package" && make "$apex_module" @@ -190,10 +191,8 @@ check_library libprofiled.so # libsigchain.so # libtombstoned_client.so # libunwindstack.so -# libvixl-arm64.so -# libvixl-arm.so -# libvixld-arm64.so -# libvixld-arm.so +# libvixl.so +# libvixld.so # ... # # ? diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 77b0cea311..67cabef641 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -24,6 +24,7 @@ #include <malloc.h> // For mallinfo #endif +#include "android-base/logging.h" #include "android-base/strings.h" #include "art_field-inl.h" @@ -598,6 +599,29 @@ static void CompileMethodQuick( class_loader, dex_file, dex_cache); + ProfileMethodsCheck check_type = + driver->GetCompilerOptions().CheckProfiledMethodsCompiled(); + if (UNLIKELY(check_type != ProfileMethodsCheck::kNone)) { + bool violation = driver->ShouldCompileBasedOnProfile(method_ref) && + (compiled_method == nullptr); + if (violation) { + std::ostringstream oss; + oss << "Failed to compile " + << method_ref.dex_file->PrettyMethod(method_ref.index) + << "[" << method_ref.dex_file->GetLocation() << "]" + << " as expected by profile"; + switch (check_type) { + case ProfileMethodsCheck::kNone: + break; + case ProfileMethodsCheck::kLog: + LOG(ERROR) << oss.str(); + break; + case ProfileMethodsCheck::kAbort: + LOG(FATAL_WITHOUT_ABORT) << oss.str(); + _exit(1); + } + } + } } if (compiled_method == nullptr && dex_to_dex_compilation_level != diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index 3610f1831b..bef5be1c0d 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -70,6 +70,7 @@ CompilerOptions::CompilerOptions() deduplicate_code_(true), count_hotness_in_compiled_code_(false), resolve_startup_const_strings_(false), + check_profiled_methods_(ProfileMethodsCheck::kNone), register_allocation_strategy_(RegisterAllocator::kRegisterAllocatorDefault), passes_to_run_(nullptr) { } diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 12fa251606..f0970a9e0d 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -44,6 +44,13 @@ enum class InstructionSet; class InstructionSetFeatures; class ProfileCompilationInfo; +// Enum for CheckProfileMethodsCompiled. Outside CompilerOptions so it can be forward-declared. +enum class ProfileMethodsCheck : uint8_t { + kNone, + kLog, + kAbort, +}; + class CompilerOptions final { public: // Guide heuristics to determine whether to compile method if profile data not available. @@ -324,6 +331,10 @@ class CompilerOptions final { return resolve_startup_const_strings_; } + ProfileMethodsCheck CheckProfiledMethodsCompiled() const { + return check_profiled_methods_; + } + private: bool ParseDumpInitFailures(const std::string& option, std::string* error_msg); void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage); @@ -409,6 +420,10 @@ class CompilerOptions final { // profile. bool resolve_startup_const_strings_; + // When running profile-guided compilation, check that methods intended to be compiled end + // up compiled and are not punted. + ProfileMethodsCheck check_profiled_methods_; + RegisterAllocator::Strategy register_allocation_strategy_; // If not null, specifies optimization passes which will be run instead of defaults. diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h index 5a844959c4..c7334a72e7 100644 --- a/compiler/driver/compiler_options_map-inl.h +++ b/compiler/driver/compiler_options_map-inl.h @@ -81,6 +81,9 @@ inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string options->count_hotness_in_compiled_code_ = true; } map.AssignIfExists(Base::ResolveStartupConstStrings, &options->resolve_startup_const_strings_); + if (map.Exists(Base::CheckProfiledMethods)) { + options->check_profiled_methods_ = *map.Get(Base::CheckProfiledMethods); + } if (map.Exists(Base::DumpTimings)) { options->dump_timings_ = true; @@ -145,6 +148,12 @@ inline void AddCompilerOptionsArgumentParserOptions(Builder& b) { .Define({"--count-hotness-in-compiled-code"}) .IntoKey(Map::CountHotnessInCompiledCode) + .Define({"--check-profiled-methods=_"}) + .template WithType<ProfileMethodsCheck>() + .WithValueMap({{"log", ProfileMethodsCheck::kLog}, + {"abort", ProfileMethodsCheck::kAbort}}) + .IntoKey(Map::CheckProfiledMethods) + .Define({"--dump-timings"}) .IntoKey(Map::DumpTimings) diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def index a593240365..c2fac5e017 100644 --- a/compiler/driver/compiler_options_map.def +++ b/compiler/driver/compiler_options_map.def @@ -61,6 +61,7 @@ COMPILER_OPTIONS_KEY (std::string, RegisterAllocationStrategy) COMPILER_OPTIONS_KEY (ParseStringList<','>, VerboseMethods) COMPILER_OPTIONS_KEY (bool, DeduplicateCode, true) COMPILER_OPTIONS_KEY (Unit, CountHotnessInCompiledCode) +COMPILER_OPTIONS_KEY (ProfileMethodsCheck, CheckProfiledMethods) COMPILER_OPTIONS_KEY (Unit, DumpTimings) COMPILER_OPTIONS_KEY (Unit, DumpPassTimings) COMPILER_OPTIONS_KEY (Unit, DumpStats) diff --git a/compiler/driver/compiler_options_map.h b/compiler/driver/compiler_options_map.h index b9bc8b6ea1..af212d66a1 100644 --- a/compiler/driver/compiler_options_map.h +++ b/compiler/driver/compiler_options_map.h @@ -25,6 +25,8 @@ namespace art { +enum class ProfileMethodsCheck : uint8_t; + // Defines a type-safe heterogeneous key->value map. This is to be used as the base for // an extended map. template <typename Base, template <typename TV> class KeyType> diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index d8e442c642..c2e83cd2c2 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -1126,6 +1126,7 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, if (osr) { DCHECK_EQ(info->GetSuspendCheck(), instruction); DCHECK(info->IsIrreducible()); + DCHECK(environment != nullptr); if (kIsDebugBuild) { for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) { HInstruction* in_environment = environment->GetInstructionAt(i); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 14cff05f58..e7212cd479 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -3608,9 +3608,17 @@ void InstructionCodeGeneratorX86_64::DivByPowerOfTwo(HDiv* instruction) { CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>(); if (instruction->GetResultType() == DataType::Type::kInt32) { - __ leal(tmp, Address(numerator, abs_imm - 1)); - __ testl(numerator, numerator); - __ cmov(kGreaterEqual, tmp, numerator); + // When denominator is equal to 2, we can add signed bit and numerator to tmp. + // Below we are using addl instruction instead of cmov which give us 1 cycle benefit. + if (abs_imm == 2) { + __ leal(tmp, Address(numerator, 0)); + __ shrl(tmp, Immediate(31)); + __ addl(tmp, numerator); + } else { + __ leal(tmp, Address(numerator, abs_imm - 1)); + __ testl(numerator, numerator); + __ cmov(kGreaterEqual, tmp, numerator); + } int shift = CTZ(imm); __ sarl(tmp, Immediate(shift)); @@ -3622,11 +3630,16 @@ void InstructionCodeGeneratorX86_64::DivByPowerOfTwo(HDiv* instruction) { } else { DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64); CpuRegister rdx = locations->GetTemp(0).AsRegister<CpuRegister>(); - - codegen_->Load64BitValue(rdx, abs_imm - 1); - __ addq(rdx, numerator); - __ testq(numerator, numerator); - __ cmov(kGreaterEqual, rdx, numerator); + if (abs_imm == 2) { + __ movq(rdx, numerator); + __ shrq(rdx, Immediate(63)); + __ addq(rdx, numerator); + } else { + codegen_->Load64BitValue(rdx, abs_imm - 1); + __ addq(rdx, numerator); + __ testq(numerator, numerator); + __ cmov(kGreaterEqual, rdx, numerator); + } int shift = CTZ(imm); __ sarq(rdx, Immediate(shift)); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 6dd4681847..a73f4e8b94 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -545,6 +545,96 @@ static void GenFPToFPCall(HInvoke* invoke, CodeGeneratorX86* codegen, QuickEntry __ cfi().AdjustCFAOffset(-16); } +static void CreateLowestOneBitLocations(ArenaAllocator* allocator, bool is_long, HInvoke* invoke) { + LocationSummary* locations = + new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); + if (is_long) { + locations->SetInAt(0, Location::RequiresRegister()); + } else { + locations->SetInAt(0, Location::Any()); + } + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); +} + +static void GenLowestOneBit(X86Assembler* assembler, + CodeGeneratorX86* codegen, + bool is_long, + HInvoke* invoke) { + LocationSummary* locations = invoke->GetLocations(); + Location src = locations->InAt(0); + Location out_loc = locations->Out(); + + if (invoke->InputAt(0)->IsConstant()) { + // Evaluate this at compile time. + int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant()); + if (value == 0) { + if (is_long) { + __ xorl(out_loc.AsRegisterPairLow<Register>(), out_loc.AsRegisterPairLow<Register>()); + __ xorl(out_loc.AsRegisterPairHigh<Register>(), out_loc.AsRegisterPairHigh<Register>()); + } else { + __ xorl(out_loc.AsRegister<Register>(), out_loc.AsRegister<Register>()); + } + return; + } + // Nonzero value. + value = is_long ? CTZ(static_cast<uint64_t>(value)) + : CTZ(static_cast<uint32_t>(value)); + if (is_long) { + if (value >= 32) { + int shift = value-32; + codegen->Load32BitValue(out_loc.AsRegisterPairLow<Register>(), 0); + codegen->Load32BitValue(out_loc.AsRegisterPairHigh<Register>(), 1 << shift); + } else { + codegen->Load32BitValue(out_loc.AsRegisterPairLow<Register>(), 1 << value); + codegen->Load32BitValue(out_loc.AsRegisterPairHigh<Register>(), 0); + } + } else { + codegen->Load32BitValue(out_loc.AsRegister<Register>(), 1 << value); + } + return; + } + // Handle non constant case + if (is_long) { + DCHECK(src.IsRegisterPair()); + Register src_lo = src.AsRegisterPairLow<Register>(); + Register src_hi = src.AsRegisterPairHigh<Register>(); + + Register out_lo = out_loc.AsRegisterPairLow<Register>(); + Register out_hi = out_loc.AsRegisterPairHigh<Register>(); + + __ movl(out_lo, src_lo); + __ movl(out_hi, src_hi); + + __ negl(out_lo); + __ adcl(out_hi, Immediate(0)); + __ negl(out_hi); + + __ andl(out_lo, src_lo); + __ andl(out_hi, src_hi); + } else { + if (codegen->GetInstructionSetFeatures().HasAVX2() && src.IsRegister()) { + Register out = out_loc.AsRegister<Register>(); + __ blsi(out, src.AsRegister<Register>()); + } else { + Register out = out_loc.AsRegister<Register>(); + // Do tmp & -tmp + if (src.IsRegister()) { + __ movl(out, src.AsRegister<Register>()); + } else { + DCHECK(src.IsStackSlot()); + __ movl(out, Address(ESP, src.GetStackIndex())); + } + __ negl(out); + + if (src.IsRegister()) { + __ andl(out, src.AsRegister<Register>()); + } else { + __ andl(out, Address(ESP, src.GetStackIndex())); + } + } + } +} + void IntrinsicLocationsBuilderX86::VisitMathCos(HInvoke* invoke) { CreateFPToFPCallLocations(allocator_, invoke); } @@ -657,6 +747,21 @@ void IntrinsicCodeGeneratorX86::VisitMathTanh(HInvoke* invoke) { GenFPToFPCall(invoke, codegen_, kQuickTanh); } +void IntrinsicLocationsBuilderX86::VisitIntegerLowestOneBit(HInvoke* invoke) { + CreateLowestOneBitLocations(allocator_, /*is_long=*/ false, invoke); +} +void IntrinsicCodeGeneratorX86::VisitIntegerLowestOneBit(HInvoke* invoke) { + GenLowestOneBit(GetAssembler(), codegen_, /*is_long=*/ false, invoke); +} + +void IntrinsicLocationsBuilderX86::VisitLongLowestOneBit(HInvoke* invoke) { + CreateLowestOneBitLocations(allocator_, /*is_long=*/ true, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitLongLowestOneBit(HInvoke* invoke) { + GenLowestOneBit(GetAssembler(), codegen_, /*is_long=*/ true, invoke); +} + static void CreateFPFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) { LocationSummary* locations = new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified); @@ -2965,8 +3070,6 @@ UNIMPLEMENTED_INTRINSIC(X86, FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(X86, DoubleIsInfinite) UNIMPLEMENTED_INTRINSIC(X86, IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit) UNIMPLEMENTED_INTRINSIC(X86, CRC32Update) UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf); diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 7db26dc9be..88c766fabc 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2413,59 +2413,64 @@ static void GenOneBit(X86_64Assembler* assembler, } // Handle the non-constant cases. - CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>(); - if (is_high) { - // Use architectural support: basically 1 << bsr. - if (src.IsRegister()) { + if (!is_high && codegen->GetInstructionSetFeatures().HasAVX2() && + src.IsRegister()) { + __ blsi(out, src.AsRegister<CpuRegister>()); + } else { + CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>(); + if (is_high) { + // Use architectural support: basically 1 << bsr. + if (src.IsRegister()) { + if (is_long) { + __ bsrq(tmp, src.AsRegister<CpuRegister>()); + } else { + __ bsrl(tmp, src.AsRegister<CpuRegister>()); + } + } else if (is_long) { + DCHECK(src.IsDoubleStackSlot()); + __ bsrq(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); + } else { + DCHECK(src.IsStackSlot()); + __ bsrl(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); + } + // BSR sets ZF if the input was zero. + NearLabel is_zero, done; + __ j(kEqual, &is_zero); + __ movl(out, Immediate(1)); // Clears upper bits too. if (is_long) { - __ bsrq(tmp, src.AsRegister<CpuRegister>()); + __ shlq(out, tmp); } else { - __ bsrl(tmp, src.AsRegister<CpuRegister>()); + __ shll(out, tmp); } - } else if (is_long) { - DCHECK(src.IsDoubleStackSlot()); - __ bsrq(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); - } else { - DCHECK(src.IsStackSlot()); - __ bsrl(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); - } - // BSR sets ZF if the input was zero. - NearLabel is_zero, done; - __ j(kEqual, &is_zero); - __ movl(out, Immediate(1)); // Clears upper bits too. - if (is_long) { - __ shlq(out, tmp); - } else { - __ shll(out, tmp); - } - __ jmp(&done); - __ Bind(&is_zero); - __ xorl(out, out); // Clears upper bits too. - __ Bind(&done); - } else { - // Copy input into temporary. - if (src.IsRegister()) { + __ jmp(&done); + __ Bind(&is_zero); + __ xorl(out, out); // Clears upper bits too. + __ Bind(&done); + } else { + // Copy input into temporary. + if (src.IsRegister()) { + if (is_long) { + __ movq(tmp, src.AsRegister<CpuRegister>()); + } else { + __ movl(tmp, src.AsRegister<CpuRegister>()); + } + } else if (is_long) { + DCHECK(src.IsDoubleStackSlot()); + __ movq(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); + } else { + DCHECK(src.IsStackSlot()); + __ movl(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); + } + // Do the bit twiddling: basically tmp & -tmp; if (is_long) { - __ movq(tmp, src.AsRegister<CpuRegister>()); + __ movq(out, tmp); + __ negq(tmp); + __ andq(out, tmp); } else { - __ movl(tmp, src.AsRegister<CpuRegister>()); + __ movl(out, tmp); + __ negl(tmp); + __ andl(out, tmp); } - } else if (is_long) { - DCHECK(src.IsDoubleStackSlot()); - __ movq(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); - } else { - DCHECK(src.IsStackSlot()); - __ movl(tmp, Address(CpuRegister(RSP), src.GetStackIndex())); - } - // Do the bit twiddling: basically tmp & -tmp; - if (is_long) { - __ movq(out, tmp); - __ negq(tmp); - __ andq(out, tmp); - } else { - __ movl(out, tmp); - __ negl(tmp); - __ andl(out, tmp); } } } diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 8ce96a4c16..f8bdb16342 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -66,15 +66,20 @@ art_cc_defaults { android: { // For atrace. shared_libs: ["libcutils"], + static_libs: [ + "libz", + ], + }, + host: { + shared_libs: [ + "libz", + ], }, }, generated_sources: ["art_dex2oat_operator_srcs"], shared_libs: [ "libbase", ], - include_dirs: [ - "external/zlib", - ], export_include_dirs: ["."], // For SHA-1 checksumming of build ID @@ -95,6 +100,7 @@ cc_defaults { }, static_libs: [ "libbase", + "libz", ], } @@ -261,6 +267,14 @@ art_cc_binary { lto: { thin: true, }, + static_libs: [ + "libz", + ], + }, + host: { + shared_libs: [ + "libz", + ], }, }, } @@ -284,6 +298,18 @@ art_cc_binary { static_libs: [ "libartd-dex2oat", ], + target: { + android: { + static_libs: [ + "libz", + ], + }, + host: { + shared_libs: [ + "libz", + ], + }, + }, } cc_defaults { @@ -309,6 +335,7 @@ cc_defaults { static_libs: [ "libbase", "libsigchain_dummy", + "libz", ], } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 12a8354007..3a24542221 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -115,6 +115,7 @@ static char** original_argv; static std::string CommandLine() { std::vector<std::string> command; + command.reserve(original_argc); for (int i = 0; i < original_argc; ++i) { command.push_back(original_argv[i]); } @@ -620,7 +621,7 @@ class Dex2Oat final { explicit Dex2Oat(TimingLogger* timings) : compiler_kind_(Compiler::kOptimizing), // Take the default set of instruction features from the build. - image_file_location_oat_checksum_(0), + boot_image_checksum_(0), key_value_store_(nullptr), verification_results_(nullptr), runtime_(nullptr), @@ -1492,7 +1493,7 @@ class Dex2Oat final { key_value_store_->Put(OatHeader::kCompilationReasonKey, compilation_reason_); } - if (IsBootImage() && image_filenames_.size() > 1) { + if (IsBootImage()) { // If we're compiling the boot image, store the boot classpath into the Key-Value store. // We need this for the multi-image case. key_value_store_->Put(OatHeader::kBootClassPathKey, @@ -1512,9 +1513,9 @@ class Dex2Oat final { TimingLogger::ScopedTiming t3("Loading image checksum", timings_); std::vector<gc::space::ImageSpace*> image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); - image_file_location_oat_checksum_ = image_spaces[0]->GetImageHeader().GetOatChecksum(); + boot_image_checksum_ = image_spaces[0]->GetImageHeader().GetImageChecksum(); } else { - image_file_location_oat_checksum_ = 0u; + boot_image_checksum_ = 0u; } // Open dex files for class path. @@ -1570,7 +1571,7 @@ class Dex2Oat final { if (!oat_writers_[i]->WriteAndOpenDexFiles( vdex_files_[i].get(), rodata_.back(), - key_value_store_.get(), + (i == 0u) ? key_value_store_.get() : nullptr, verify, update_input_vdex_, copy_dex_files_, @@ -1929,7 +1930,7 @@ class Dex2Oat final { // ImageWriter, if necessary. // Note: Flushing (and closing) the file is the caller's responsibility, except for the failure // case (when the file will be explicitly erased). - bool WriteOutputFiles() { + bool WriteOutputFiles(jobject class_loader) { TimingLogger::ScopedTiming t("dex2oat Oat", timings_); // Sync the data to the file, in case we did dex2dex transformations. @@ -1964,6 +1965,7 @@ class Dex2Oat final { image_storage_mode_, oat_filenames_, dex_file_oat_index_map_, + class_loader, dirty_image_objects_.get())); // We need to prepare method offsets in the image address space for direct method patching. @@ -2036,14 +2038,6 @@ class Dex2Oat final { oat_writer->GetOatDataOffset(), oat_writer->GetOatSize()); } - - if (IsBootImage()) { - // Have the image_file_location_oat_checksum_ for boot oat files - // depend on the contents of all the boot oat files. This way only - // the primary image checksum needs to be checked to determine - // whether any of the images are out of date. - image_file_location_oat_checksum_ ^= oat_writer->GetOatHeader().GetChecksum(); - } } for (size_t i = 0, size = oat_files_.size(); i != size; ++i) { @@ -2082,7 +2076,7 @@ class Dex2Oat final { elf_writer->EndDataBimgRelRo(data_bimg_rel_ro); } - if (!oat_writer->WriteHeader(elf_writer->GetStream(), image_file_location_oat_checksum_)) { + if (!oat_writer->WriteHeader(elf_writer->GetStream(), boot_image_checksum_)) { LOG(ERROR) << "Failed to write oat header to the ELF file " << oat_file->GetPath(); return false; } @@ -2709,7 +2703,7 @@ class Dex2Oat final { std::unique_ptr<CompilerOptions> compiler_options_; Compiler::Kind compiler_kind_; - uint32_t image_file_location_oat_checksum_; + uint32_t boot_image_checksum_; std::unique_ptr<SafeMap<std::string, std::string> > key_value_store_; std::unique_ptr<VerificationResults> verification_results_; @@ -2846,11 +2840,12 @@ class ScopedGlobalRef { static dex2oat::ReturnCode CompileImage(Dex2Oat& dex2oat) { dex2oat.LoadClassProfileDescriptors(); + jobject class_loader = dex2oat.Compile(); // Keep the class loader that was used for compilation live for the rest of the compilation // process. - ScopedGlobalRef class_loader(dex2oat.Compile()); + ScopedGlobalRef global_ref(class_loader); - if (!dex2oat.WriteOutputFiles()) { + if (!dex2oat.WriteOutputFiles(class_loader)) { dex2oat.EraseOutputFiles(); return dex2oat::ReturnCode::kOther; } @@ -2890,11 +2885,12 @@ static dex2oat::ReturnCode CompileImage(Dex2Oat& dex2oat) { } static dex2oat::ReturnCode CompileApp(Dex2Oat& dex2oat) { + jobject class_loader = dex2oat.Compile(); // Keep the class loader that was used for compilation live for the rest of the compilation // process. - ScopedGlobalRef class_loader(dex2oat.Compile()); + ScopedGlobalRef global_ref(class_loader); - if (!dex2oat.WriteOutputFiles()) { + if (!dex2oat.WriteOutputFiles(class_loader)) { dex2oat.EraseOutputFiles(); return dex2oat::ReturnCode::kOther; } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index baeebd9371..97a5f2453e 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1069,7 +1069,8 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest { void RunTest(const char* class_loader_context, const char* expected_classpath_key, bool expected_success, - bool use_second_source = false) { + bool use_second_source = false, + bool generate_image = false) { std::string dex_location = GetUsedDexLocation(); std::string odex_location = GetUsedOatLocation(); @@ -1080,6 +1081,9 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest { if (class_loader_context != nullptr) { extra_args.push_back(std::string("--class-loader-context=") + class_loader_context); } + if (generate_image) { + extra_args.push_back(std::string("--app-image-file=") + GetUsedImageLocation()); + } auto check_oat = [expected_classpath_key](const OatFile& oat_file) { ASSERT_TRUE(expected_classpath_key != nullptr); const char* classpath = oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey); @@ -1104,6 +1108,10 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest { return GetOdexDir() + "/Context.odex"; } + std::string GetUsedImageLocation() { + return GetOdexDir() + "/Context.art"; + } + const char* kEmptyClassPathKey = "PCL[]"; }; @@ -1202,6 +1210,66 @@ TEST_F(Dex2oatClassLoaderContextTest, ChainContext) { RunTest(context.c_str(), expected_classpath_key.c_str(), true); } +TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrary) { + std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); + std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); + + std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + + "{PCL[" + GetTestDexFileName("MultiDex") + "]}"; + std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; + RunTest(context.c_str(), expected_classpath_key.c_str(), true); +} + +TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibraryAndImage) { + std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); + std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); + + std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + + "{PCL[" + GetTestDexFileName("MultiDex") + "]}"; + std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; + RunTest(context.c_str(), + expected_classpath_key.c_str(), + /*expected_success=*/ true, + /*use_second_source=*/ false, + /*generate_image=*/ true); +} + +TEST_F(Dex2oatClassLoaderContextTest, ContextWithSameSharedLibrariesAndImage) { + std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); + std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); + + std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + + "{PCL[" + GetTestDexFileName("MultiDex") + "]" + + "#PCL[" + GetTestDexFileName("MultiDex") + "]}"; + std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]" + + "#PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; + RunTest(context.c_str(), + expected_classpath_key.c_str(), + /*expected_success=*/ true, + /*use_second_source=*/ false, + /*generate_image=*/ true); +} + +TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrariesDependenciesAndImage) { + std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); + std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); + + std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + + "{PCL[" + GetTestDexFileName("MultiDex") + "]" + + "{PCL[" + GetTestDexFileName("Nested") + "]}}"; + std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]" + + "{PCL[" + CreateClassPathWithChecksums(dex_files1) + "]}}"; + RunTest(context.c_str(), + expected_classpath_key.c_str(), + /*expected_success=*/ true, + /*use_second_source=*/ false, + /*generate_image=*/ true); +} + class Dex2oatDeterminism : public Dex2oatTest {}; TEST_F(Dex2oatDeterminism, UnloadCompile) { diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc index 69dac19df9..64b98cdb37 100644 --- a/dex2oat/linker/image_test.cc +++ b/dex2oat/linker/image_test.cc @@ -74,13 +74,11 @@ TEST_F(ImageTest, ImageHeaderIsValid) { oat_data_begin, oat_data_end, oat_file_end, - /*boot_image_begin*/0U, - /*boot_image_size*/0U, - /*boot_oat_begin*/0U, - /*boot_oat_size_*/0U, + /*boot_image_begin=*/ 0u, + /*boot_image_size=*/ 0u, sizeof(void*), ImageHeader::kDefaultStorageMode, - /*data_size*/0u); + /*data_size=*/ 0u); ASSERT_TRUE(image_header.IsValid()); ASSERT_TRUE(!image_header.IsAppImage()); @@ -110,7 +108,7 @@ TEST_F(ImageTest, TestDefaultMethods) { // Test the pointer to quick code is the same in origin method // and in the copied method form the same oat file. ObjPtr<mirror::Class> iface_klass = - class_linker_->LookupClass(self, "LIface;", /* class_loader */ nullptr); + class_linker_->LookupClass(self, "LIface;", /*class_loader=*/ nullptr); ASSERT_NE(nullptr, iface_klass); ArtMethod* origin = iface_klass->FindInterfaceMethod("defaultMethod", "()V", pointer_size); ASSERT_NE(nullptr, origin); @@ -120,7 +118,7 @@ TEST_F(ImageTest, TestDefaultMethods) { ASSERT_NE(nullptr, code); ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); ObjPtr<mirror::Class> impl_klass = - class_linker_->LookupClass(self, "LImpl;", /* class_loader */ nullptr); + class_linker_->LookupClass(self, "LImpl;", /*class_loader=*/ nullptr); ASSERT_NE(nullptr, impl_klass); ArtMethod* copied = FindCopiedMethod(origin, impl_klass); ASSERT_NE(nullptr, copied); @@ -131,7 +129,7 @@ TEST_F(ImageTest, TestDefaultMethods) { // but the copied method has pointer to interpreter // because these methods are in different oat files. ObjPtr<mirror::Class> iterable_klass = - class_linker_->LookupClass(self, "Ljava/lang/Iterable;", /* class_loader */ nullptr); + class_linker_->LookupClass(self, "Ljava/lang/Iterable;", /*class_loader=*/ nullptr); ASSERT_NE(nullptr, iterable_klass); origin = iterable_klass->FindClassMethod( "forEach", "(Ljava/util/function/Consumer;)V", pointer_size); @@ -143,7 +141,7 @@ TEST_F(ImageTest, TestDefaultMethods) { ASSERT_NE(nullptr, code); ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); ObjPtr<mirror::Class> iterablebase_klass = - class_linker_->LookupClass(self, "LIterableBase;", /* class_loader */ nullptr); + class_linker_->LookupClass(self, "LIterableBase;", /*class_loader=*/ nullptr); ASSERT_NE(nullptr, iterablebase_klass); copied = FindCopiedMethod(origin, iterablebase_klass); ASSERT_NE(nullptr, copied); diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 9ef2875017..c90eaddb4c 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -219,6 +219,7 @@ inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode, storage_mode, oat_filename_vector, dex_file_to_oat_index_map, + /*class_loader=*/ nullptr, /*dirty_image_objects=*/ nullptr)); { { @@ -341,7 +342,7 @@ inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode, } bool header_ok = oat_writer->WriteHeader(elf_writer->GetStream(), - /* image_file_location_oat_checksum */ 0u); + /*boot_image_checksum=*/ 0u); ASSERT_TRUE(header_ok); writer->UpdateOatFileHeader(i, oat_writer->GetOatHeader()); diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index a3fc1cdf2d..75b35556f1 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -19,6 +19,7 @@ #include <lz4.h> #include <lz4hc.h> #include <sys/stat.h> +#include <zlib.h> #include <memory> #include <numeric> @@ -145,9 +146,11 @@ 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; +ObjPtr<mirror::ClassLoader> ImageWriter::GetAppClassLoader() const + REQUIRES_SHARED(Locks::mutator_lock_) { + return compiler_options_.IsAppImage() + ? ObjPtr<mirror::ClassLoader>::DownCast(Thread::Current()->DecodeJObject(app_class_loader_)) + : nullptr; } // Return true if an object is already in an image space. @@ -606,6 +609,58 @@ bool ImageWriter::IsValidAppImageStringReference(ObjPtr<mirror::Object> referred referred_obj->IsString(); } +// Helper class that erases the image file if it isn't properly flushed and closed. +class ImageWriter::ImageFileGuard { + public: + ImageFileGuard() noexcept = default; + ImageFileGuard(ImageFileGuard&& other) noexcept = default; + ImageFileGuard& operator=(ImageFileGuard&& other) noexcept = default; + + ~ImageFileGuard() { + if (image_file_ != nullptr) { + // Failure, erase the image file. + image_file_->Erase(); + } + } + + void reset(File* image_file) { + image_file_.reset(image_file); + } + + bool operator==(std::nullptr_t) { + return image_file_ == nullptr; + } + + bool operator!=(std::nullptr_t) { + return image_file_ != nullptr; + } + + File* operator->() const { + return image_file_.get(); + } + + bool WriteHeaderAndClose(const std::string& image_filename, const ImageHeader* image_header) { + // The header is uncompressed since it contains whether the image is compressed or not. + if (!image_file_->PwriteFully(image_header, sizeof(ImageHeader), 0)) { + PLOG(ERROR) << "Failed to write image file header " << image_filename; + return false; + } + + // FlushCloseOrErase() takes care of erasing, so the destructor does not need + // to do that whether the FlushCloseOrErase() succeeds or fails. + std::unique_ptr<File> image_file = std::move(image_file_); + if (image_file->FlushCloseOrErase() != 0) { + PLOG(ERROR) << "Failed to flush and close image file " << image_filename; + return false; + } + + return true; + } + + private: + std::unique_ptr<File> image_file_; +}; + bool ImageWriter::Write(int image_fd, const std::vector<const char*>& image_filenames, const std::vector<const char*>& oat_filenames) { @@ -622,7 +677,7 @@ bool ImageWriter::Write(int image_fd, { // Preload deterministic contents to the dex cache arrays we're going to write. ScopedObjectAccess soa(self); - ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader(); + ObjPtr<mirror::ClassLoader> class_loader = GetAppClassLoader(); std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self); for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) { if (IsInBootImage(dex_cache.Ptr())) { @@ -651,10 +706,18 @@ bool ImageWriter::Write(int image_fd, CopyMetadata(); } + // Primary image header shall be written last for two reasons. First, this ensures + // that we shall not end up with a valid primary image and invalid secondary image. + // Second, its checksum shall include the checksums of the secondary images (XORed). + // This way only the primary image checksum needs to be checked to determine whether + // any of the images or oat files are out of date. (Oat file checksums are included + // in the image checksum calculation.) + ImageHeader* primary_header = reinterpret_cast<ImageHeader*>(image_infos_[0].image_.Begin()); + ImageFileGuard primary_image_file; for (size_t i = 0; i < image_filenames.size(); ++i) { const char* image_filename = image_filenames[i]; ImageInfo& image_info = GetImageInfo(i); - std::unique_ptr<File> image_file; + ImageFileGuard image_file; if (image_fd != kInvalidFd) { if (strlen(image_filename) == 0u) { image_file.reset(new File(image_fd, unix_file::kCheckSafeUsage)); @@ -677,7 +740,6 @@ bool ImageWriter::Write(int image_fd, 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; } @@ -690,11 +752,11 @@ bool ImageWriter::Write(int image_fd, std::vector<uint8_t> compressed_data; ArrayRef<const uint8_t> image_data = MaybeCompressData(raw_image_data, image_storage_mode_, &compressed_data); + image_header->data_size_ = image_data.size(); // Fill in the data size. // Write out the image + fields + methods. if (!image_file->PwriteFully(image_data.data(), image_data.size(), sizeof(ImageHeader))) { PLOG(ERROR) << "Failed to write image file data " << image_filename; - image_file->Erase(); return false; } @@ -710,28 +772,25 @@ bool ImageWriter::Write(int image_fd, bitmap_section.Size(), bitmap_position_in_file)) { PLOG(ERROR) << "Failed to write image file bitmap " << image_filename; - image_file->Erase(); return false; } int err = image_file->Flush(); if (err < 0) { PLOG(ERROR) << "Failed to flush image file " << image_filename << " with result " << err; - image_file->Erase(); return false; } - // Write header last in case the compiler gets killed in the middle of image writing. - // We do not want to have a corrupted image with a valid header. - // The header is uncompressed since it contains whether the image is compressed or not. - image_header->data_size_ = image_data.size(); - if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_.Begin()), - sizeof(ImageHeader), - 0)) { - PLOG(ERROR) << "Failed to write image file header " << image_filename; - image_file->Erase(); - return false; - } + // Calculate the image checksum. + uint32_t image_checksum = adler32(0L, Z_NULL, 0); + image_checksum = adler32(image_checksum, + reinterpret_cast<const uint8_t*>(image_header), + sizeof(ImageHeader)); + image_checksum = adler32(image_checksum, image_data.data(), image_data.size()); + image_checksum = adler32(image_checksum, + reinterpret_cast<const uint8_t*>(image_info.image_bitmap_->Begin()), + bitmap_section.Size()); + image_header->SetImageChecksum(image_checksum); if (VLOG_IS_ON(compiler)) { size_t separately_written_section_size = bitmap_section.Size() + sizeof(ImageHeader); @@ -748,11 +807,24 @@ bool ImageWriter::Write(int image_fd, CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(), static_cast<size_t>(image_file->GetLength())); - if (image_file->FlushCloseOrErase() != 0) { - PLOG(ERROR) << "Failed to flush and close image file " << image_filename; - return false; + // Write header last in case the compiler gets killed in the middle of image writing. + // We do not want to have a corrupted image with a valid header. + // Delay the writing of the primary image header until after writing secondary images. + if (i == 0u) { + primary_image_file = std::move(image_file); + } else { + if (!image_file.WriteHeaderAndClose(image_filename, image_header)) { + return false; + } + // Update the primary image checksum with the secondary image checksum. + primary_header->SetImageChecksum(primary_header->GetImageChecksum() ^ image_checksum); } } + DCHECK(primary_image_file != nullptr); + if (!primary_image_file.WriteHeaderAndClose(image_filenames[0], primary_header)) { + return false; + } + return true; } @@ -1400,27 +1472,15 @@ class ImageWriter::PruneClassLoaderClassesVisitor : public ClassLoaderVisitor { Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader); class_table->Visit(classes_visitor); removed_class_count_ += classes_visitor.Prune(); - - // Record app image class loader. The fake boot class loader should not get registered - // and we should end up with only one class loader for an app and none for boot image. - if (class_loader != nullptr && class_table != nullptr) { - DCHECK(class_loader_ == nullptr); - class_loader_ = class_loader; - } } size_t GetRemovedClassCount() const { return removed_class_count_; } - ObjPtr<mirror::ClassLoader> GetClassLoader() const REQUIRES_SHARED(Locks::mutator_lock_) { - return class_loader_; - } - private: ImageWriter* const image_writer_; size_t removed_class_count_; - ObjPtr<mirror::ClassLoader> class_loader_; }; void ImageWriter::VisitClassLoaders(ClassLoaderVisitor* visitor) { @@ -1631,13 +1691,10 @@ void ImageWriter::PruneNonImageClasses() { }); // Remove the undesired classes from the class roots. - ObjPtr<mirror::ClassLoader> class_loader; { PruneClassLoaderClassesVisitor class_loader_visitor(this); VisitClassLoaders(&class_loader_visitor); VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes"; - class_loader = class_loader_visitor.GetClassLoader(); - DCHECK_EQ(class_loader != nullptr, compiler_options_.IsAppImage()); } // Clear references to removed classes from the DexCaches. @@ -1645,7 +1702,7 @@ void ImageWriter::PruneNonImageClasses() { 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. - PruneDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader); + PruneDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : GetAppClassLoader()); } // Drop the array class cache in the ClassLinker, as these are roots holding those classes live. @@ -1964,18 +2021,17 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, } } else if (obj->IsClassLoader()) { // Register the class loader if it has a class table. - // The fake boot class loader should not get registered and we should end up with only one - // class loader. + // The fake boot class loader should not get registered. mirror::ClassLoader* class_loader = obj->AsClassLoader(); if (class_loader->GetClassTable() != nullptr) { DCHECK(compiler_options_.IsAppImage()); - DCHECK(class_loaders_.empty()); - class_loaders_.insert(class_loader); - ImageInfo& image_info = GetImageInfo(oat_index); - // Note: Avoid locking to prevent lock order violations from root visiting; - // image_info.class_table_ table is only accessed from the image writer - // and class_loader->GetClassTable() is iterated but not modified. - image_info.class_table_->CopyWithoutLocks(*class_loader->GetClassTable()); + if (class_loader == GetAppClassLoader()) { + ImageInfo& image_info = GetImageInfo(oat_index); + // Note: Avoid locking to prevent lock order violations from root visiting; + // image_info.class_table_ table is only accessed from the image writer + // and class_loader->GetClassTable() is iterated but not modified. + image_info.class_table_->CopyWithoutLocks(*class_loader->GetClassTable()); + } } } AssignImageBinSlot(obj, oat_index); @@ -2253,10 +2309,8 @@ void ImageWriter::CalculateNewObjectOffsets() { ProcessWorkStack(&work_stack); // Store the class loader in the class roots. - CHECK_EQ(class_loaders_.size(), 1u); CHECK_EQ(image_roots.size(), 1u); - CHECK(*class_loaders_.begin() != nullptr); - image_roots[0]->Set<false>(ImageHeader::kAppImageClassLoader, *class_loaders_.begin()); + image_roots[0]->Set<false>(ImageHeader::kAppImageClassLoader, GetAppClassLoader()); } // Verify that all objects have assigned image bin slots. @@ -2519,9 +2573,7 @@ void ImageWriter::CreateHeader(size_t oat_index) { PointerToLowMemUInt32(oat_data_end), PointerToLowMemUInt32(oat_file_end), boot_image_begin, - boot_image_end - boot_image_begin, - boot_oat_begin, - boot_oat_end - boot_oat_begin, + boot_oat_end - boot_image_begin, static_cast<uint32_t>(target_ptr_size_), image_storage_mode_, /*data_size*/0u); @@ -3347,11 +3399,14 @@ void ImageWriter::UpdateOatFileLayout(size_t oat_index, size_t oat_loaded_size, size_t oat_data_offset, size_t oat_data_size) { + DCHECK_GE(oat_loaded_size, oat_data_offset); + DCHECK_GE(oat_loaded_size - oat_data_offset, oat_data_size); + const uint8_t* images_end = image_infos_.back().image_begin_ + image_infos_.back().image_size_; + DCHECK(images_end != nullptr); // Image space must be ready. for (const ImageInfo& info : image_infos_) { DCHECK_LE(info.image_begin_ + info.image_size_, images_end); } - DCHECK(images_end != nullptr); // Image space must be ready. ImageInfo& cur_image_info = GetImageInfo(oat_index); cur_image_info.oat_file_begin_ = images_end + cur_image_info.oat_offset_; @@ -3401,6 +3456,7 @@ ImageWriter::ImageWriter( 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, + jobject class_loader, const HashSet<std::string>* dirty_image_objects) : compiler_options_(compiler_options), global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)), @@ -3409,6 +3465,7 @@ ImageWriter::ImageWriter( image_infos_(oat_filenames.size()), dirty_methods_(0u), clean_methods_(0u), + app_class_loader_(class_loader), boot_image_live_objects_(nullptr), image_storage_mode_(image_storage_mode), oat_filenames_(oat_filenames), diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index 3c377a3ca5..782bbd2fc2 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -81,6 +81,7 @@ class ImageWriter final { 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, + jobject class_loader, const HashSet<std::string>* dirty_image_objects); /* @@ -111,7 +112,7 @@ class ImageWriter final { return true; } - ObjPtr<mirror::ClassLoader> GetClassLoader(); + ObjPtr<mirror::ClassLoader> GetAppClassLoader() const REQUIRES_SHARED(Locks::mutator_lock_); template <typename T> T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) { @@ -771,10 +772,8 @@ class ImageWriter final { // Prune class memoization table to speed up ContainsBootClassLoaderNonImageClass. std::unordered_map<mirror::Class*, bool> prune_class_memo_; - // Class loaders with a class table to write out. There should only be one class loader because - // dex2oat loads the dex files to be compiled into a single class loader. For the boot image, - // null is a valid entry. - std::unordered_set<mirror::ClassLoader*> class_loaders_; + // The application class loader. Null for boot image. + jobject app_class_loader_; // Boot image live objects, null for app image. mirror::ObjectArray<mirror::Object>* boot_image_live_objects_; @@ -794,7 +793,7 @@ class ImageWriter final { // Set of objects known to be dirty in the image. Can be nullptr if there are none. const HashSet<std::string>* dirty_image_objects_; - class ComputeLazyFieldsForClassesVisitor; + class ImageFileGuard; class FixupClassVisitor; class FixupRootVisitor; class FixupVisitor; diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 7f2877fc08..9aaabc49dd 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -99,13 +99,28 @@ const UnalignedDexFileHeader* AsUnalignedDexFileHeader(const uint8_t* raw_data) return reinterpret_cast<const UnalignedDexFileHeader*>(raw_data); } -class ChecksumUpdatingOutputStream : public OutputStream { +inline uint32_t CodeAlignmentSize(uint32_t header_offset, const CompiledMethod& compiled_method) { + // We want to align the code rather than the preheader. + uint32_t unaligned_code_offset = header_offset + sizeof(OatQuickMethodHeader); + uint32_t aligned_code_offset = compiled_method.AlignCode(unaligned_code_offset); + return aligned_code_offset - unaligned_code_offset; +} + +} // anonymous namespace + +class OatWriter::ChecksumUpdatingOutputStream : public OutputStream { public: - ChecksumUpdatingOutputStream(OutputStream* out, OatHeader* oat_header) - : OutputStream(out->GetLocation()), out_(out), oat_header_(oat_header) { } + ChecksumUpdatingOutputStream(OutputStream* out, OatWriter* writer) + : OutputStream(out->GetLocation()), out_(out), writer_(writer) { } bool WriteFully(const void* buffer, size_t byte_count) override { - oat_header_->UpdateChecksum(buffer, byte_count); + if (buffer != nullptr) { + const uint8_t* bytes = reinterpret_cast<const uint8_t*>(buffer); + uint32_t old_checksum = writer_->oat_checksum_; + writer_->oat_checksum_ = adler32(old_checksum, bytes, byte_count); + } else { + DCHECK_EQ(0U, byte_count); + } return out_->WriteFully(buffer, byte_count); } @@ -119,18 +134,9 @@ class ChecksumUpdatingOutputStream : public OutputStream { private: OutputStream* const out_; - OatHeader* const oat_header_; + OatWriter* const writer_; }; -inline uint32_t CodeAlignmentSize(uint32_t header_offset, const CompiledMethod& compiled_method) { - // We want to align the code rather than the preheader. - uint32_t unaligned_code_offset = header_offset + sizeof(OatQuickMethodHeader); - uint32_t aligned_code_offset = compiled_method.AlignCode(unaligned_code_offset); - return aligned_code_offset - unaligned_code_offset; -} - -} // anonymous namespace - // Defines the location of the raw dex file to write. class OatWriter::DexFileSource { public: @@ -379,6 +385,7 @@ OatWriter::OatWriter(const CompilerOptions& compiler_options, vdex_dex_shared_data_offset_(0u), vdex_verifier_deps_offset_(0u), vdex_quickening_info_offset_(0u), + oat_checksum_(adler32(0L, Z_NULL, 0)), code_size_(0u), oat_size_(0u), data_bimg_rel_ro_start_(0u), @@ -675,7 +682,7 @@ bool OatWriter::WriteAndOpenDexFiles( oat_size_ = InitOatHeader(dchecked_integral_cast<uint32_t>(oat_dex_files_.size()), key_value_store); - ChecksumUpdatingOutputStream checksum_updating_rodata(oat_rodata, oat_header_.get()); + ChecksumUpdatingOutputStream checksum_updating_rodata(oat_rodata, this); std::unique_ptr<BufferedOutputStream> vdex_out = std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(vdex_file)); @@ -1482,7 +1489,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { const std::vector<const DexFile*>* dex_files) : OatDexMethodVisitor(writer, offset), pointer_size_(GetInstructionSetPointerSize(writer_->compiler_options_.GetInstructionSet())), - class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr), + class_loader_(writer->HasImage() ? writer->image_writer_->GetAppClassLoader() : nullptr), dex_files_(dex_files), class_linker_(Runtime::Current()->GetClassLinker()) {} @@ -1623,7 +1630,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { offset_(relative_offset), dex_file_(nullptr), pointer_size_(GetInstructionSetPointerSize(writer_->compiler_options_.GetInstructionSet())), - class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr), + class_loader_(writer->HasImage() ? writer->image_writer_->GetAppClassLoader() : nullptr), out_(out), file_offset_(file_offset), class_linker_(Runtime::Current()->GetClassLinker()), @@ -2264,6 +2271,7 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { } if (HasImage()) { + ScopedAssertNoThreadSuspension sants("Init image method visitor", Thread::Current()); InitImageMethodVisitor image_visitor(this, offset, dex_files_); success = VisitDexMethods(&image_visitor); image_visitor.Postprocess(); @@ -2349,7 +2357,7 @@ bool OatWriter::WriteRodata(OutputStream* out) { size_t relative_offset = current_offset - file_offset; // Wrap out to update checksum with each write. - ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get()); + ChecksumUpdatingOutputStream checksum_updating_out(out, this); out = &checksum_updating_out; relative_offset = WriteClassOffsets(out, file_offset, relative_offset); @@ -2658,7 +2666,7 @@ bool OatWriter::WriteCode(OutputStream* out) { CHECK(write_state_ == WriteState::kWriteText); // Wrap out to update checksum with each write. - ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get()); + ChecksumUpdatingOutputStream checksum_updating_out(out, this); out = &checksum_updating_out; SetMultiOatRelativePatcherAdjustment(); @@ -2694,7 +2702,7 @@ bool OatWriter::WriteDataBimgRelRo(OutputStream* out) { CHECK(write_state_ == WriteState::kWriteDataBimgRelRo); // Wrap out to update checksum with each write. - ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get()); + ChecksumUpdatingOutputStream checksum_updating_out(out, this); out = &checksum_updating_out; const size_t file_offset = oat_data_offset_; @@ -2800,11 +2808,18 @@ bool OatWriter::CheckOatSize(OutputStream* out, size_t file_offset, size_t relat return true; } -bool OatWriter::WriteHeader(OutputStream* out, uint32_t image_file_location_oat_checksum) { +bool OatWriter::WriteHeader(OutputStream* out, uint32_t boot_image_checksum) { CHECK(write_state_ == WriteState::kWriteHeader); - oat_header_->SetImageFileLocationOatChecksum(image_file_location_oat_checksum); - oat_header_->UpdateChecksumWithHeaderData(); + oat_header_->SetBootImageChecksum(boot_image_checksum); + + // Update checksum with header data. + DCHECK_EQ(oat_header_->GetChecksum(), 0u); // For checksum calculation. + const uint8_t* header_begin = reinterpret_cast<const uint8_t*>(oat_header_.get()); + const uint8_t* header_end = oat_header_->GetKeyValueStore() + oat_header_->GetKeyValueStoreSize(); + uint32_t old_checksum = oat_checksum_; + oat_checksum_ = adler32(old_checksum, header_begin, header_end - header_begin); + oat_header_->SetChecksum(oat_checksum_); const size_t file_offset = oat_data_offset_; diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index c04951856f..9cd2fd04ea 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -198,7 +198,7 @@ class OatWriter { // Check the size of the written oat file. bool CheckOatSize(OutputStream* out, size_t file_offset, size_t relative_offset); // Write the oat header. This finalizes the oat file. - bool WriteHeader(OutputStream* out, uint32_t image_file_location_oat_checksum); + bool WriteHeader(OutputStream* out, uint32_t boot_image_checksum); // Returns whether the oat file has an associated image. bool HasImage() const { @@ -256,6 +256,7 @@ class OatWriter { } private: + class ChecksumUpdatingOutputStream; class DexFileSource; class OatClassHeader; class OatClass; @@ -400,6 +401,9 @@ class OatWriter { // Offset of section holding quickening info inside Vdex. size_t vdex_quickening_info_offset_; + // OAT checksum. + uint32_t oat_checksum_; + // Size of the .text segment. size_t code_size_; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index af02bfc8bd..7450964fdf 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -236,8 +236,7 @@ class OatTest : public CommonCompilerTest { elf_writer->EndDataBimgRelRo(data_bimg_rel_ro); } - if (!oat_writer.WriteHeader(elf_writer->GetStream(), - /*image_file_location_oat_checksum=*/ 42U)) { + if (!oat_writer.WriteHeader(elf_writer->GetStream(), /*boot_image_checksum=*/ 42u)) { return false; } @@ -416,7 +415,7 @@ TEST_F(OatTest, WriteRead) { const OatHeader& oat_header = oat_file->GetOatHeader(); ASSERT_TRUE(oat_header.IsValid()); ASSERT_EQ(class_linker->GetBootClassPath().size(), oat_header.GetDexFileCount()); // core - ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum()); + ASSERT_EQ(42u, oat_header.GetBootImageChecksum()); ASSERT_TRUE(java_lang_dex_file_ != nullptr); const DexFile& dex_file = *java_lang_dex_file_; @@ -842,29 +841,5 @@ TEST_F(OatTest, ZipFileInputWithEmptyDex) { TestZipFileInputWithEmptyDex(); } -TEST_F(OatTest, UpdateChecksum) { - InstructionSet insn_set = InstructionSet::kX86; - std::string error_msg; - std::unique_ptr<const InstructionSetFeatures> insn_features( - InstructionSetFeatures::FromVariant(insn_set, "default", &error_msg)); - ASSERT_TRUE(insn_features.get() != nullptr) << error_msg; - std::unique_ptr<OatHeader> oat_header(OatHeader::Create(insn_set, - insn_features.get(), - 0u, - nullptr)); - // The starting adler32 value is 1. - EXPECT_EQ(1U, oat_header->GetChecksum()); - - oat_header->UpdateChecksum(OatHeader::kOatMagic, sizeof(OatHeader::kOatMagic)); - EXPECT_EQ(64291151U, oat_header->GetChecksum()); - - // Make sure that null data does not reset the checksum. - oat_header->UpdateChecksum(nullptr, 0); - EXPECT_EQ(64291151U, oat_header->GetChecksum()); - - oat_header->UpdateChecksum(OatHeader::kOatMagic, sizeof(OatHeader::kOatMagic)); - EXPECT_EQ(216138397U, oat_header->GetChecksum()); -} - } // namespace linker } // namespace art diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 09f0b20ca1..32122ebf93 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -34,6 +34,7 @@ #include "android-base/stringprintf.h" #include "base/logging.h" // For VLOG_IS_ON. +#include "base/hiddenapi_flags.h" #include "base/mem_map.h" #include "base/os.h" #include "base/utils.h" @@ -222,15 +223,10 @@ static char* CreateAccessFlagStr(uint32_t flags, AccessFor for_what) { return str; } -static const char* GetHiddenapiFlagStr(uint32_t hiddenapi_flags) { - static const char* const kValue[] = { - "WHITELIST", /* 0x0 */ - "LIGHT_GREYLIST", /* 0x1 */ - "DARK_GREYLIST", /* 0x2 */ - "BLACKLIST", /* 0x3 */ - }; - DCHECK_LT(hiddenapi_flags, arraysize(kValue)); - return kValue[hiddenapi_flags]; +static std::string GetHiddenapiFlagStr(uint32_t hiddenapi_flags) { + std::string api_list(hiddenapi::ApiList::FromDexFlags(hiddenapi_flags).GetName()); + std::transform(api_list.begin(), api_list.end(), api_list.begin(), ::toupper); + return api_list; } static std::string GetSignatureForProtoId(const dex_ir::ProtoId* proto) { @@ -1173,7 +1169,6 @@ void DexLayout::DumpMethod(uint32_t idx, char* type_descriptor = strdup(GetSignatureForProtoId(method_id->Proto()).c_str()); const char* back_descriptor = method_id->Class()->GetStringId()->Data(); char* access_str = CreateAccessFlagStr(flags, kAccessForMethod); - const char* hiddenapi_str = GetHiddenapiFlagStr(hiddenapi_flags); if (options_.output_format_ == kOutputPlain) { fprintf(out_file_, " #%d : (in %s)\n", i, back_descriptor); @@ -1181,7 +1176,10 @@ void DexLayout::DumpMethod(uint32_t idx, fprintf(out_file_, " type : '%s'\n", type_descriptor); fprintf(out_file_, " access : 0x%04x (%s)\n", flags, access_str); if (hiddenapi_flags != 0u) { - fprintf(out_file_, " hiddenapi : 0x%04x (%s)\n", hiddenapi_flags, hiddenapi_str); + fprintf(out_file_, + " hiddenapi : 0x%04x (%s)\n", + hiddenapi_flags, + GetHiddenapiFlagStr(hiddenapi_flags).c_str()); } if (code == nullptr) { fprintf(out_file_, " code : (none)\n"); @@ -1291,7 +1289,6 @@ void DexLayout::DumpSField(uint32_t idx, const char* type_descriptor = field_id->Type()->GetStringId()->Data(); const char* back_descriptor = field_id->Class()->GetStringId()->Data(); char* access_str = CreateAccessFlagStr(flags, kAccessForField); - const char* hiddenapi_str = GetHiddenapiFlagStr(hiddenapi_flags); if (options_.output_format_ == kOutputPlain) { fprintf(out_file_, " #%d : (in %s)\n", i, back_descriptor); @@ -1299,7 +1296,10 @@ void DexLayout::DumpSField(uint32_t idx, fprintf(out_file_, " type : '%s'\n", type_descriptor); fprintf(out_file_, " access : 0x%04x (%s)\n", flags, access_str); if (hiddenapi_flags != 0u) { - fprintf(out_file_, " hiddenapi : 0x%04x (%s)\n", hiddenapi_flags, hiddenapi_str); + fprintf(out_file_, + " hiddenapi : 0x%04x (%s)\n", + hiddenapi_flags, + GetHiddenapiFlagStr(hiddenapi_flags).c_str()); } if (init != nullptr) { fputs(" value : ", out_file_); diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc index 21cdcf1313..2b97fb402a 100644 --- a/dexoptanalyzer/dexoptanalyzer.cc +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -52,6 +52,7 @@ static char** original_argv; static std::string CommandLine() { std::vector<std::string> command; + command.reserve(original_argc); for (int i = 0; i < original_argc; ++i) { command.push_back(original_argv[i]); } diff --git a/dt_fd_forward/Android.bp b/dt_fd_forward/Android.bp index 1ba2323a15..2a2aa188bb 100644 --- a/dt_fd_forward/Android.bp +++ b/dt_fd_forward/Android.bp @@ -41,6 +41,7 @@ cc_defaults { header_libs: [ "javavm_headers", "dt_fd_forward_export", + "art_libartbase_headers", // For strlcpy emulation. ], multilib: { lib32: { diff --git a/dt_fd_forward/dt_fd_forward.cc b/dt_fd_forward/dt_fd_forward.cc index a99f7850c0..d5b6de5ead 100644 --- a/dt_fd_forward/dt_fd_forward.cc +++ b/dt_fd_forward/dt_fd_forward.cc @@ -50,6 +50,8 @@ #include <jni.h> #include <jdwpTransport.h> +#include <base/strlcpy.h> + namespace dt_fd_forward { // Helper that puts line-number in error message. @@ -651,7 +653,7 @@ void FdForwardTransport::Free(void* data) { jdwpTransportError FdForwardTransport::GetLastError(/*out*/char** err) { std::string data = global_last_error_; *err = reinterpret_cast<char*>(Alloc(data.size() + 1)); - strcpy(*err, data.c_str()); + strlcpy(*err, data.c_str(), data.size() + 1); return OK; } diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 6a667bc7a7..58d12a1e04 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -27,6 +27,7 @@ cc_defaults { "base/file_magic.cc", "base/file_utils.cc", "base/hex_dump.cc", + "base/hiddenapi_flags.cc", "base/logging.cc", "base/malloc_arena_pool.cc", "base/membarrier.cc", diff --git a/libartbase/base/hiddenapi_flags.cc b/libartbase/base/hiddenapi_flags.cc new file mode 100644 index 0000000000..6caa75c570 --- /dev/null +++ b/libartbase/base/hiddenapi_flags.cc @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hiddenapi_flags.h" + +namespace art { +namespace hiddenapi { + +constexpr const char* ApiList::kNames[ApiList::kValueCount]; +constexpr SdkVersion ApiList::kMaxSdkVersions[ApiList::kValueCount]; + +} // namespace hiddenapi +} // namespace art diff --git a/libartbase/base/hiddenapi_flags.h b/libartbase/base/hiddenapi_flags.h new file mode 100644 index 0000000000..8e7269cc60 --- /dev/null +++ b/libartbase/base/hiddenapi_flags.h @@ -0,0 +1,131 @@ +/* + * 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. + */ + +#ifndef ART_LIBARTBASE_BASE_HIDDENAPI_FLAGS_H_ +#define ART_LIBARTBASE_BASE_HIDDENAPI_FLAGS_H_ + +#include "sdk_version.h" + +#include "android-base/logging.h" + +namespace art { +namespace hiddenapi { + +/* + * This class represents the information whether a field/method is in + * public API (whitelist) or if it isn't, apps targeting which SDK + * versions are allowed to access it. + */ +class ApiList { + private: + using IntValueType = uint32_t; + + enum class Value : IntValueType { + // Values independent of target SDK version of app + kWhitelist = 0, + kGreylist = 1, + kBlacklist = 2, + + // Values dependent on target SDK version of app. Put these last as + // their list will be extended in future releases. + // The max release code implicitly includes all maintenance releases, + // e.g. GreylistMaxO is accessible to targetSdkVersion <= 27 (O_MR1). + kGreylistMaxO = 3, + + // Special values + kInvalid = static_cast<uint32_t>(-1), + kMinValue = kWhitelist, + kMaxValue = kGreylistMaxO, + }; + + static constexpr const char* kNames[] = { + "whitelist", + "greylist", + "blacklist", + "greylist-max-o", + }; + + static constexpr SdkVersion kMaxSdkVersions[] { + /* whitelist */ SdkVersion::kMax, + /* greylist */ SdkVersion::kMax, + /* blacklist */ SdkVersion::kMin, + /* greylist-max-o */ SdkVersion::kO_MR1, + }; + + static ApiList MinValue() { return ApiList(Value::kMinValue); } + static ApiList MaxValue() { return ApiList(Value::kMaxValue); } + + explicit ApiList(Value value) : value_(value) {} + + const Value value_; + + public: + static ApiList Whitelist() { return ApiList(Value::kWhitelist); } + static ApiList Greylist() { return ApiList(Value::kGreylist); } + static ApiList Blacklist() { return ApiList(Value::kBlacklist); } + static ApiList GreylistMaxO() { return ApiList(Value::kGreylistMaxO); } + static ApiList Invalid() { return ApiList(Value::kInvalid); } + + // Decodes ApiList from dex hiddenapi flags. + static ApiList FromDexFlags(uint32_t dex_flags) { + if (MinValue().GetIntValue() <= dex_flags && dex_flags <= MaxValue().GetIntValue()) { + return ApiList(static_cast<Value>(dex_flags)); + } + return Invalid(); + } + + // Returns the ApiList with a given name. + static ApiList FromName(const std::string& str) { + for (IntValueType i = MinValue().GetIntValue(); i <= MaxValue().GetIntValue(); i++) { + ApiList current = ApiList(static_cast<Value>(i)); + if (str == current.GetName()) { + return current; + } + } + return Invalid(); + } + + bool operator==(const ApiList other) const { return value_ == other.value_; } + bool operator!=(const ApiList other) const { return !(*this == other); } + + bool IsValid() const { return *this != Invalid(); } + + IntValueType GetIntValue() const { + DCHECK(IsValid()); + return static_cast<IntValueType>(value_); + } + + const char* GetName() const { return kNames[GetIntValue()]; } + + SdkVersion GetMaxAllowedSdkVersion() const { return kMaxSdkVersions[GetIntValue()]; } + + static constexpr size_t kValueCount = static_cast<size_t>(Value::kMaxValue) + 1; +}; + +inline std::ostream& operator<<(std::ostream& os, ApiList value) { + os << value.GetName(); + return os; +} + +inline bool AreValidDexFlags(uint32_t dex_flags) { + return ApiList::FromDexFlags(dex_flags).IsValid(); +} + +} // namespace hiddenapi +} // namespace art + + +#endif // ART_LIBARTBASE_BASE_HIDDENAPI_FLAGS_H_ diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index e9e3a98224..f0f14c67ea 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -19,6 +19,7 @@ #include "class_accessor.h" +#include "base/hiddenapi_flags.h" #include "base/leb128.h" #include "class_iterator.h" #include "code_item_accessors-inl.h" @@ -65,7 +66,7 @@ inline void ClassAccessor::Method::Read() { code_off_ = DecodeUnsignedLeb128(&ptr_pos_); if (hiddenapi_ptr_pos_ != nullptr) { hiddenapi_flags_ = DecodeUnsignedLeb128(&hiddenapi_ptr_pos_); - DCHECK(hiddenapi::AreValidFlags(hiddenapi_flags_)); + DCHECK(hiddenapi::AreValidDexFlags(hiddenapi_flags_)); } } @@ -74,7 +75,7 @@ inline void ClassAccessor::Field::Read() { access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); if (hiddenapi_ptr_pos_ != nullptr) { hiddenapi_flags_ = DecodeUnsignedLeb128(&hiddenapi_ptr_pos_); - DCHECK(hiddenapi::AreValidFlags(hiddenapi_flags_)); + DCHECK(hiddenapi::AreValidDexFlags(hiddenapi_flags_)); } } diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index b3e7ad46c6..83f47fec19 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -29,7 +29,6 @@ #include "base/value_object.h" #include "class_iterator.h" #include "dex_file_types.h" -#include "hidden_api_access_flags.h" #include "jni.h" #include "modifiers.h" diff --git a/libdexfile/dex/dex_file_layout.cc b/libdexfile/dex/dex_file_layout.cc index 1e36e05f50..75a31112bb 100644 --- a/libdexfile/dex/dex_file_layout.cc +++ b/libdexfile/dex/dex_file_layout.cc @@ -18,6 +18,7 @@ #include <sys/mman.h> +#include "base/bit_utils.h" #include "dex_file.h" namespace art { diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index 4d33cd59f7..78e4618f04 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -1632,7 +1632,7 @@ bool DexFileVerifier::CheckIntraHiddenapiClassData() { failure = true; return; } - if (!hiddenapi::AreValidFlags(decoded_flags)) { + if (!hiddenapi::AreValidDexFlags(decoded_flags)) { ErrorStringPrintf("Hiddenapi class data flags invalid (%u) for %s %i", decoded_flags, member_type, member.GetIndex()); failure = true; diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h deleted file mode 100644 index 77bfbc99b3..0000000000 --- a/libdexfile/dex/hidden_api_access_flags.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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. - */ - -#ifndef ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_ -#define ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_ - -#include "base/bit_utils.h" -#include "base/macros.h" -#include "dex/modifiers.h" - -/* This class is used for encoding and decoding access flags of class members - * from the boot class path. These access flags might contain additional two bits - * of information on whether the given class member should be hidden from apps - * and under what circumstances. - * - * Two bits are encoded for each class member in the HiddenapiClassData item, - * stored in a stream of uleb128-encoded values for each ClassDef item. - * The two bits correspond to values in the ApiList enum below. - * - * At runtime, two bits are set aside in the uint32_t access flags in the - * intrinsics ordinal space (thus intrinsics need to be special-cased). These are - * two consecutive bits and they are directly used to store the integer value of - * the ApiList enum values. - * - */ - -namespace art { -namespace hiddenapi { - -enum class ApiList { - kWhitelist = 0, - kLightGreylist, - kDarkGreylist, - kBlacklist, - kNoList, -}; - -inline bool AreValidFlags(uint32_t flags) { - return flags <= static_cast<uint32_t>(ApiList::kBlacklist); -} - -inline std::ostream& operator<<(std::ostream& os, ApiList value) { - switch (value) { - case ApiList::kWhitelist: - os << "whitelist"; - break; - case ApiList::kLightGreylist: - os << "light greylist"; - break; - case ApiList::kDarkGreylist: - os << "dark greylist"; - break; - case ApiList::kBlacklist: - os << "blacklist"; - break; - case ApiList::kNoList: - os << "no list"; - break; - } - return os; -} - -} // namespace hiddenapi -} // namespace art - - -#endif // ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_ diff --git a/libdexfile/dex/test_dex_file_builder.h b/libdexfile/dex/test_dex_file_builder.h index 2d8a0bbfe4..072aafb394 100644 --- a/libdexfile/dex/test_dex_file_builder.h +++ b/libdexfile/dex/test_dex_file_builder.h @@ -26,6 +26,7 @@ #include <android-base/logging.h> +#include "base/bit_utils.h" #include "dex/dex_file_loader.h" #include "dex/standard_dex_file.h" diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index e26ec95512..51f60084a1 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -473,8 +473,8 @@ class OatDumper { GetQuickToInterpreterBridgeOffset); #undef DUMP_OAT_HEADER_OFFSET - os << "IMAGE FILE LOCATION OAT CHECKSUM:\n"; - os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum()); + os << "BOOT IMAGE CHECKSUM:\n"; + os << StringPrintf("0x%08x\n\n", oat_header.GetBootImageChecksum()); // Print the key-value store. { @@ -1771,27 +1771,25 @@ class ImageDumper { os << "IMAGE LOCATION: " << image_space_.GetImageLocation() << "\n\n"; - os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n"; + os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n"; + os << "IMAGE SIZE: " << image_header_.GetImageSize() << "\n"; + os << "IMAGE CHECKSUM: " << std::hex << image_header_.GetImageChecksum() << std::dec << "\n\n"; - os << "IMAGE SIZE: " << image_header_.GetImageSize() << "\n\n"; + os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum()) << "\n"; + os << "OAT FILE BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatFileBegin()) << "\n"; + os << "OAT DATA BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatDataBegin()) << "\n"; + os << "OAT DATA END:" << reinterpret_cast<void*>(image_header_.GetOatDataEnd()) << "\n"; + os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n"; + + os << "BOOT IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetBootImageBegin()) + << "\n"; + os << "BOOT IMAGE SIZE: " << image_header_.GetBootImageSize() << "\n\n"; for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) { auto section = static_cast<ImageHeader::ImageSections>(i); os << "IMAGE SECTION " << section << ": " << image_header_.GetImageSection(section) << "\n\n"; } - os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum()); - - os << "OAT FILE BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatFileBegin()) << "\n\n"; - - os << "OAT DATA BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatDataBegin()) << "\n\n"; - - os << "OAT DATA END:" << reinterpret_cast<void*>(image_header_.GetOatDataEnd()) << "\n\n"; - - os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n"; - - os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n"; - { os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots().Ptr()) << "\n"; static_assert(arraysize(image_roots_descriptions_) == diff --git a/openjdkjvmti/ti_logging.cc b/openjdkjvmti/ti_logging.cc index 1d24d3b6b6..60f4340fc7 100644 --- a/openjdkjvmti/ti_logging.cc +++ b/openjdkjvmti/ti_logging.cc @@ -34,6 +34,7 @@ #include "art_jvmti.h" #include "base/mutex.h" +#include "base/strlcpy.h" #include "thread-current-inl.h" namespace openjdkjvmti { @@ -47,13 +48,13 @@ jvmtiError LogUtil::GetLastError(jvmtiEnv* env, char** data) { if (tienv->last_error_.empty()) { return ERR(ABSENT_INFORMATION); } + const size_t size = tienv->last_error_.size() + 1; char* out; - jvmtiError err = tienv->Allocate(tienv->last_error_.size() + 1, - reinterpret_cast<unsigned char**>(&out)); + jvmtiError err = tienv->Allocate(size, reinterpret_cast<unsigned char**>(&out)); if (err != OK) { return err; } - strcpy(out, tienv->last_error_.c_str()); + strlcpy(out, tienv->last_error_.c_str(), size); *data = out; return OK; } diff --git a/profman/profman.cc b/profman/profman.cc index 734cdf498e..2935a05b48 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -60,6 +60,7 @@ static char** original_argv; static std::string CommandLine() { std::vector<std::string> command; + command.reserve(original_argc); for (int i = 0; i < original_argc; ++i) { command.push_back(original_argv[i]); } diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index c5fb7d5f40..53e4c11511 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -399,6 +399,10 @@ inline ArtField* ArtField::FindStaticFieldWithOffset(ObjPtr<mirror::Class> klass return FindFieldWithOffset<kExactOffset>(klass->GetSFields(), field_offset); } +inline mirror::ClassLoader* ArtField::GetClassLoader() { + return GetDeclaringClass()->GetClassLoader(); +} + } // namespace art #endif // ART_RUNTIME_ART_FIELD_INL_H_ diff --git a/runtime/art_field.h b/runtime/art_field.h index 1cf7afa022..99f2a1cd07 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -20,7 +20,6 @@ #include <jni.h> #include "dex/dex_file_types.h" -#include "dex/hidden_api_access_flags.h" #include "dex/modifiers.h" #include "dex/primitive.h" #include "gc_root.h" @@ -35,6 +34,7 @@ class ScopedObjectAccessAlreadyRunnable; namespace mirror { class Class; +class ClassLoader; class DexCache; class Object; class String; @@ -45,6 +45,8 @@ class ArtField final { template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ObjPtr<mirror::Class> GetDeclaringClass() REQUIRES_SHARED(Locks::mutator_lock_); + mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_); + void SetDeclaringClass(ObjPtr<mirror::Class> new_declaring_class) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 0c8fe58252..e391a1549b 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -73,13 +73,17 @@ enum LockLevel : uint8_t { // level lock, it is permitted to acquire a second one - with internal safeguards to ensure that // the second lock acquisition does not result in deadlock. This is implemented in the lock // order by treating the second acquisition of a kThreadWaitLock as a kThreadWaitWakeLock - // acquisition. Thus, acquiring kThreadWaitWakeLock requires holding kThreadWaitLock. + // acquisition. Thus, acquiring kThreadWaitWakeLock requires holding kThreadWaitLock. This entry + // is here near the bottom of the hierarchy because other locks should not be + // acquired while it is held. kThreadWaitLock cannot be moved here because GC + // activity acquires locks while holding the wait lock. kThreadWaitWakeLock, - kThreadWaitLock, kJdwpAdbStateLock, kJdwpSocketLock, kRegionSpaceRegionLock, kMarkSweepMarkStackLock, + // Can be held while GC related work is done, and thus must be above kMarkSweepMarkStackLock + kThreadWaitLock, kCHALock, kJitCodeCacheLock, kRosAllocGlobalLock, diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 7e011371b7..6703205502 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -135,7 +135,7 @@ inline ObjPtr<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx, ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { - resolved_type = DoResolveType(type_idx, referrer->GetDeclaringClass()); + resolved_type = DoResolveType(type_idx, referrer); } return resolved_type; } @@ -149,7 +149,7 @@ inline ObjPtr<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx, ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { - resolved_type = DoResolveType(type_idx, referrer->GetDeclaringClass()); + resolved_type = DoResolveType(type_idx, referrer); } return resolved_type; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index bcc05c7bca..0deb37ce4b 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -798,6 +798,8 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b FindSystemClass(self, "Ljava/lang/StackTraceElement;")); SetClassRoot(ClassRoot::kJavaLangStackTraceElementArrayClass, FindSystemClass(self, "[Ljava/lang/StackTraceElement;")); + SetClassRoot(ClassRoot::kJavaLangClassLoaderArrayClass, + FindSystemClass(self, "[Ljava/lang/ClassLoader;")); // Create conflict tables that depend on the class linker. runtime->FixupConflictTables(); @@ -1092,51 +1094,165 @@ static bool GetDexPathListElementName(ObjPtr<mirror::Object> element, return false; } -static bool FlattenPathClassLoader(ObjPtr<mirror::ClassLoader> class_loader, - std::list<ObjPtr<mirror::String>>* out_dex_file_names, - std::string* error_msg) +static bool GetDexFileNames(ScopedObjectAccessUnchecked& soa, + ObjPtr<mirror::ClassLoader> class_loader, + /*out*/std::list<ObjPtr<mirror::String>>* dex_files, + /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(out_dex_file_names != nullptr); - DCHECK(error_msg != nullptr); - ScopedObjectAccessUnchecked soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> handle(hs.NewHandle(class_loader)); - while (!ClassLinker::IsBootClassLoader(soa, class_loader)) { - if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) != - class_loader->GetClass()) { - *error_msg = StringPrintf("Unknown class loader type %s", - class_loader->PrettyTypeOf().c_str()); - // Unsupported class loader. + // Get element names. Sets error to true on failure. + auto add_element_names = [&](ObjPtr<mirror::Object> element, bool* error) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (element == nullptr) { + *error_msg = "Null dex element"; + *error = true; // Null element is a critical error. + return false; // Had an error, stop the visit. + } + ObjPtr<mirror::String> name; + if (!GetDexPathListElementName(element, &name)) { + *error_msg = "Invalid dex path list element"; + *error = true; // Invalid element, make it a critical error. + return false; // Stop the visit. + } + if (name != nullptr) { + dex_files->push_front(name); + } + return true; // Continue with the next Element. + }; + bool error = VisitClassLoaderDexElements(soa, + handle, + add_element_names, + /*defaultReturn=*/ false); + return !error; +} + +static bool CompareClassLoaderTypes(ScopedObjectAccessUnchecked& soa, + ObjPtr<mirror::ClassLoader> image_class_loader, + ObjPtr<mirror::ClassLoader> class_loader, + std::string* error_msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (ClassLinker::IsBootClassLoader(soa, class_loader)) { + if (!ClassLinker::IsBootClassLoader(soa, image_class_loader)) { + *error_msg = "Hierarchies don't match"; return false; } - // Get element names. Sets error to true on failure. - auto add_element_names = [&](ObjPtr<mirror::Object> element, bool* error) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (element == nullptr) { - *error_msg = "Null dex element"; - *error = true; // Null element is a critical error. - return false; // Had an error, stop the visit. - } - ObjPtr<mirror::String> name; - if (!GetDexPathListElementName(element, &name)) { - *error_msg = "Invalid dex path list element"; - *error = false; // Invalid element is not a critical error. - return false; // Stop the visit. - } - if (name != nullptr) { - out_dex_file_names->push_front(name); - } - return true; // Continue with the next Element. - }; - bool error = VisitClassLoaderDexElements(soa, - handle, - add_element_names, - /* defaultReturn= */ false); - if (error) { - // An error occurred during DexPathList Element visiting. + } else if (ClassLinker::IsBootClassLoader(soa, image_class_loader)) { + *error_msg = "Hierarchies don't match"; + return false; + } else if (class_loader->GetClass() != image_class_loader->GetClass()) { + *error_msg = StringPrintf("Class loader types don't match %s and %s", + image_class_loader->PrettyTypeOf().c_str(), + class_loader->PrettyTypeOf().c_str()); + return false; + } else if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) != + class_loader->GetClass()) { + *error_msg = StringPrintf("Unknown class loader type %s", + class_loader->PrettyTypeOf().c_str()); + // Unsupported class loader. + return false; + } + return true; +} + +static bool CompareDexFiles(const std::list<ObjPtr<mirror::String>>& image_dex_files, + const std::list<ObjPtr<mirror::String>>& loader_dex_files, + std::string* error_msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + bool equal = (image_dex_files.size() == loader_dex_files.size()) && + std::equal(image_dex_files.begin(), + image_dex_files.end(), + loader_dex_files.begin(), + [](ObjPtr<mirror::String> lhs, ObjPtr<mirror::String> rhs) + REQUIRES_SHARED(Locks::mutator_lock_) { + return lhs->Equals(rhs); + }); + if (!equal) { + VLOG(image) << "Image dex files " << image_dex_files.size(); + for (ObjPtr<mirror::String> name : image_dex_files) { + VLOG(image) << name->ToModifiedUtf8(); + } + VLOG(image) << "Loader dex files " << loader_dex_files.size(); + for (ObjPtr<mirror::String> name : loader_dex_files) { + VLOG(image) << name->ToModifiedUtf8(); + } + *error_msg = "Mismatch in dex files"; + } + return equal; +} + +static bool CompareClassLoaders(ScopedObjectAccessUnchecked& soa, + ObjPtr<mirror::ClassLoader> image_class_loader, + ObjPtr<mirror::ClassLoader> class_loader, + bool check_dex_file_names, + std::string* error_msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!CompareClassLoaderTypes(soa, image_class_loader, class_loader, error_msg)) { + return false; + } + + if (ClassLinker::IsBootClassLoader(soa, class_loader)) { + // No need to check further. + return true; + } + + if (check_dex_file_names) { + std::list<ObjPtr<mirror::String>> image_dex_files; + if (!GetDexFileNames(soa, image_class_loader, &image_dex_files, error_msg)) { + return false; + } + + std::list<ObjPtr<mirror::String>> loader_dex_files; + if (!GetDexFileNames(soa, class_loader, &loader_dex_files, error_msg)) { + return false; + } + + if (!CompareDexFiles(image_dex_files, loader_dex_files, error_msg)) { return false; } - class_loader = class_loader->GetParent(); + } + + ArtField* field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + ObjPtr<mirror::Object> shared_libraries_image_loader = field->GetObject(image_class_loader.Ptr()); + ObjPtr<mirror::Object> shared_libraries_loader = field->GetObject(class_loader.Ptr()); + if (shared_libraries_image_loader == nullptr) { + if (shared_libraries_loader != nullptr) { + *error_msg = "Mismatch in shared libraries"; + return false; + } + } else if (shared_libraries_loader == nullptr) { + *error_msg = "Mismatch in shared libraries"; + return false; + } else { + ObjPtr<mirror::ObjectArray<mirror::ClassLoader>> array1 = + shared_libraries_image_loader->AsObjectArray<mirror::ClassLoader>(); + ObjPtr<mirror::ObjectArray<mirror::ClassLoader>> array2 = + shared_libraries_loader->AsObjectArray<mirror::ClassLoader>(); + if (array1->GetLength() != array2->GetLength()) { + *error_msg = "Mismatch in number of shared libraries"; + return false; + } + + for (int32_t i = 0; i < array1->GetLength(); ++i) { + // Do a full comparison of the class loaders, including comparing their dex files. + if (!CompareClassLoaders(soa, + array1->Get(i), + array2->Get(i), + /*check_dex_file_names=*/ true, + error_msg)) { + return false; + } + } + } + + // Do a full comparison of the class loaders, including comparing their dex files. + if (!CompareClassLoaders(soa, + image_class_loader->GetParent(), + class_loader->GetParent(), + /*check_dex_file_names=*/ true, + error_msg)) { + return false; } return true; } @@ -1905,6 +2021,7 @@ bool ClassLinker::AddImageSpace( if (app_image) { ScopedObjectAccessUnchecked soa(Thread::Current()); + ScopedAssertNoThreadSuspension sants("Checking app image", soa.Self()); // Check that the class loader resolves the same way as the ones in the image. // Image class loader [A][B][C][image dex files] // Class loader = [???][dex_elements][image dex files] @@ -1917,21 +2034,12 @@ bool ClassLinker::AddImageSpace( *error_msg = "Unexpected BootClassLoader in app image"; return false; } - std::list<ObjPtr<mirror::String>> image_dex_file_names; - std::string temp_error_msg; - if (!FlattenPathClassLoader(image_class_loader.Get(), &image_dex_file_names, &temp_error_msg)) { - *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'", - temp_error_msg.c_str()); - return false; - } - std::list<ObjPtr<mirror::String>> loader_dex_file_names; - if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) { - *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'", - temp_error_msg.c_str()); - return false; - } - // Add the temporary dex path list elements at the end. + // The dex files of `class_loader` are not setup yet, so we cannot do a full comparison + // of `class_loader` and `image_class_loader` in `CompareClassLoaders`. Therefore, we + // special case the comparison of dex files of the two class loaders, but then do full + // comparisons for their shared libraries and parent. auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements); + std::list<ObjPtr<mirror::String>> loader_dex_file_names; for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) { ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i); if (element != nullptr) { @@ -1942,31 +2050,29 @@ bool ClassLinker::AddImageSpace( } } } - // Ignore the number of image dex files since we are adding those to the class loader anyways. - CHECK_GE(static_cast<size_t>(image_dex_file_names.size()), - static_cast<size_t>(dex_caches->GetLength())); - size_t image_count = image_dex_file_names.size() - dex_caches->GetLength(); - // Check that the dex file names match. - bool equal = image_count == loader_dex_file_names.size(); - if (equal) { - auto it1 = image_dex_file_names.begin(); - auto it2 = loader_dex_file_names.begin(); - for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) { - equal = equal && (*it1)->Equals(*it2); - } - } - if (!equal) { - VLOG(image) << "Image dex files " << image_dex_file_names.size(); - for (ObjPtr<mirror::String> name : image_dex_file_names) { - VLOG(image) << name->ToModifiedUtf8(); - } - VLOG(image) << "Loader dex files " << loader_dex_file_names.size(); - for (ObjPtr<mirror::String> name : loader_dex_file_names) { - VLOG(image) << name->ToModifiedUtf8(); - } - *error_msg = "Rejecting application image due to class loader mismatch"; - // Ignore class loader mismatch for now since these would just use possibly incorrect - // oat code anyways. The structural class check should be done in the parent. + std::string temp_error_msg; + std::list<ObjPtr<mirror::String>> image_dex_file_names; + bool success = GetDexFileNames( + soa, image_class_loader.Get(), &image_dex_file_names, &temp_error_msg); + if (success) { + // Ignore the number of image dex files since we are adding those to the class loader anyways. + CHECK_GE(static_cast<size_t>(image_dex_file_names.size()), + static_cast<size_t>(dex_caches->GetLength())); + size_t image_count = image_dex_file_names.size() - dex_caches->GetLength(); + image_dex_file_names.resize(image_count); + success = success && CompareDexFiles(image_dex_file_names, + loader_dex_file_names, + &temp_error_msg); + success = success && CompareClassLoaders(soa, + image_class_loader.Get(), + class_loader.Get(), + /*check_dex_file_names=*/ false, + &temp_error_msg); + } + if (!success) { + *error_msg = StringPrintf("Rejecting application image due to class loader mismatch: '%s'", + temp_error_msg.c_str()); + return false; } } @@ -8074,14 +8180,22 @@ ObjPtr<mirror::Class> ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, return type; } -ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx, - ObjPtr<mirror::Class> referrer) { +template <typename T> +ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx, T referrer) { StackHandleScope<2> hs(Thread::Current()); Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader())); return DoResolveType(type_idx, dex_cache, class_loader); } +// Instantiate the above. +template ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx, + ArtField* referrer); +template ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx, + ArtMethod* referrer); +template ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx, + ObjPtr<mirror::Class> referrer); + ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) { @@ -8994,21 +9108,14 @@ void ClassLinker::AllocAndSetPrimitiveArrayClassRoot(Thread* self, CheckSystemClass(self, primitive_array_class, descriptor); } -jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, - const std::vector<const DexFile*>& dex_files, - jclass loader_class, - jobject parent_loader) { - CHECK(self->GetJniEnv()->IsSameObject(loader_class, - WellKnownClasses::dalvik_system_PathClassLoader) || - self->GetJniEnv()->IsSameObject(loader_class, - WellKnownClasses::dalvik_system_DelegateLastClassLoader)); - - // SOAAlreadyRunnable is protected, and we need something to add a global reference. - // We could move the jobject to the callers, but all call-sites do this... - ScopedObjectAccessUnchecked soa(self); +ObjPtr<mirror::ClassLoader> ClassLinker::CreateWellKnownClassLoader( + Thread* self, + const std::vector<const DexFile*>& dex_files, + Handle<mirror::Class> loader_class, + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries, + Handle<mirror::ClassLoader> parent_loader) { - // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex. - StackHandleScope<6> hs(self); + StackHandleScope<5> hs(self); ArtField* dex_elements_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements); @@ -9098,8 +9205,8 @@ jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, } // Create the class loader.. - Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class)); - Handle<mirror::Object> h_class_loader = hs.NewHandle(h_loader_class->AllocObject(self)); + Handle<mirror::ClassLoader> h_class_loader = hs.NewHandle<mirror::ClassLoader>( + ObjPtr<mirror::ClassLoader>::DownCast(loader_class->AllocObject(self))); DCHECK(h_class_loader != nullptr); // Set DexPathList. ArtField* path_list_field = @@ -9115,15 +9222,59 @@ jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, "parent", "Ljava/lang/ClassLoader;"); DCHECK(parent_field != nullptr); + if (parent_loader.Get() == nullptr) { + ScopedObjectAccessUnchecked soa(self); + ObjPtr<mirror::Object> boot_loader(soa.Decode<mirror::Class>( + WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self)); + parent_field->SetObject<false>(h_class_loader.Get(), boot_loader); + } else { + parent_field->SetObject<false>(h_class_loader.Get(), parent_loader.Get()); + } + + ArtField* shared_libraries_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + DCHECK(shared_libraries_field != nullptr); + shared_libraries_field->SetObject<false>(h_class_loader.Get(), shared_libraries.Get()); + + return h_class_loader.Get(); +} - ObjPtr<mirror::Object> parent = (parent_loader != nullptr) - ? soa.Decode<mirror::ClassLoader>(parent_loader) - : soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self); - parent_field->SetObject<false>(h_class_loader.Get(), parent); +jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, + const std::vector<const DexFile*>& dex_files, + jclass loader_class, + jobject parent_loader) { + CHECK(self->GetJniEnv()->IsSameObject(loader_class, + WellKnownClasses::dalvik_system_PathClassLoader) || + self->GetJniEnv()->IsSameObject(loader_class, + WellKnownClasses::dalvik_system_DelegateLastClassLoader)); + + // SOAAlreadyRunnable is protected, and we need something to add a global reference. + // We could move the jobject to the callers, but all call-sites do this... + ScopedObjectAccessUnchecked soa(self); + + // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex. + StackHandleScope<3> hs(self); + + Handle<mirror::Class> h_loader_class = + hs.NewHandle<mirror::Class>(soa.Decode<mirror::Class>(loader_class)); + Handle<mirror::ClassLoader> parent = + hs.NewHandle<mirror::ClassLoader>(ObjPtr<mirror::ClassLoader>::DownCast( + (parent_loader != nullptr) + ? soa.Decode<mirror::ClassLoader>(parent_loader) + : nullptr)); + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries = + hs.NewHandle<mirror::ObjectArray<mirror::ClassLoader>>(nullptr); + + ObjPtr<mirror::ClassLoader> loader = CreateWellKnownClassLoader( + self, + dex_files, + h_loader_class, + shared_libraries, + parent); // Make it a global ref and return. ScopedLocalRef<jobject> local_ref( - soa.Env(), soa.Env()->AddLocalReference<jobject>(h_class_loader.Get())); + soa.Env(), soa.Env()->AddLocalReference<jobject>(loader)); return soa.Env()->NewGlobalRef(local_ref.get()); } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index a65299a514..15a7204dd2 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -586,6 +586,16 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); + // Non-GlobalRef version of CreateWellKnownClassLoader + ObjPtr<mirror::ClassLoader> CreateWellKnownClassLoader( + Thread* self, + const std::vector<const DexFile*>& dex_files, + Handle<mirror::Class> loader_class, + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries, + Handle<mirror::ClassLoader> parent_loader) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_); + PointerSize GetImagePointerSize() const { return image_pointer_size_; } @@ -921,8 +931,8 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_); // Implementation of ResolveType() called when the type was not found in the dex cache. - ObjPtr<mirror::Class> DoResolveType(dex::TypeIndex type_idx, - ObjPtr<mirror::Class> referrer) + template <typename T> + ObjPtr<mirror::Class> DoResolveType(dex::TypeIndex type_idx, T referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); ObjPtr<mirror::Class> DoResolveType(dex::TypeIndex type_idx, diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 0bae60a886..029db7e5c7 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -17,6 +17,7 @@ #include "class_loader_context.h" #include <android-base/parseint.h> +#include <android-base/strings.h> #include "art_field-inl.h" #include "base/casts.h" @@ -24,11 +25,14 @@ #include "base/stl_util.h" #include "class_linker.h" #include "class_loader_utils.h" +#include "class_root.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "handle_scope-inl.h" #include "jni/jni_internal.h" +#include "mirror/object_array-alloc-inl.h" +#include "nativehelper/scoped_local_ref.h" #include "oat_file_assistant.h" #include "obj_ptr-inl.h" #include "runtime.h" @@ -107,6 +111,39 @@ std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string } } +static size_t FindMatchingSharedLibraryCloseMarker(const std::string& spec, + size_t shared_library_open_index) { + // Counter of opened shared library marker we've encountered so far. + uint32_t counter = 1; + // The index at which we're operating in the loop. + uint32_t string_index = shared_library_open_index + 1; + size_t shared_library_close = std::string::npos; + while (counter != 0) { + shared_library_close = + spec.find_first_of(kClassLoaderSharedLibraryClosingMark, string_index); + size_t shared_library_open = + spec.find_first_of(kClassLoaderSharedLibraryOpeningMark, string_index); + if (shared_library_close == std::string::npos) { + // No matching closing marker. Return an error. + break; + } + + if ((shared_library_open == std::string::npos) || + (shared_library_close < shared_library_open)) { + // We have seen a closing marker. Decrement the counter. + --counter; + // Move the search index forward. + string_index = shared_library_close + 1; + } else { + // New nested opening marker. Increment the counter and move the search + // index after the marker. + ++counter; + string_index = shared_library_open + 1; + } + } + return shared_library_close; +} + // The expected format is: // "ClassLoaderType1[ClasspathElem1*Checksum1:ClasspathElem2*Checksum2...]{ClassLoaderType2[...]}". // The checksum part of the format is expected only if parse_cheksums is true. @@ -160,7 +197,9 @@ std::unique_ptr<ClassLoaderContext::ClassLoaderInfo> ClassLoaderContext::ParseCl } } - if (class_loader_spec[class_loader_spec.length() - 1] == kClassLoaderSharedLibraryClosingMark) { + if ((class_loader_spec[class_loader_spec.length() - 1] == kClassLoaderSharedLibraryClosingMark) && + (class_loader_spec[class_loader_spec.length() - 2] != kClassLoaderSharedLibraryOpeningMark)) { + // Non-empty list of shared libraries. size_t start_index = class_loader_spec.find_first_of(kClassLoaderSharedLibraryOpeningMark); if (start_index == std::string::npos) { return nullptr; @@ -168,8 +207,43 @@ std::unique_ptr<ClassLoaderContext::ClassLoaderInfo> ClassLoaderContext::ParseCl std::string shared_libraries_spec = class_loader_spec.substr(start_index + 1, class_loader_spec.length() - start_index - 2); std::vector<std::string> shared_libraries; - Split(shared_libraries_spec, kClassLoaderSharedLibrarySeparator, &shared_libraries); - for (const std::string& shared_library_spec : shared_libraries) { + size_t cursor = 0; + while (cursor != shared_libraries_spec.length()) { + size_t shared_library_separator = + shared_libraries_spec.find_first_of(kClassLoaderSharedLibrarySeparator, cursor); + size_t shared_library_open = + shared_libraries_spec.find_first_of(kClassLoaderSharedLibraryOpeningMark, cursor); + std::string shared_library_spec; + if (shared_library_separator == std::string::npos) { + // Only one shared library, for example: + // PCL[...] + shared_library_spec = + shared_libraries_spec.substr(cursor, shared_libraries_spec.length() - cursor); + cursor = shared_libraries_spec.length(); + } else if ((shared_library_open == std::string::npos) || + (shared_library_open > shared_library_separator)) { + // We found a shared library without nested shared libraries, for example: + // PCL[...]#PCL[...]{...} + shared_library_spec = + shared_libraries_spec.substr(cursor, shared_library_separator - cursor); + cursor = shared_library_separator + 1; + } else { + // The shared library contains nested shared libraries. Find the matching closing shared + // marker for it. + size_t closing_marker = + FindMatchingSharedLibraryCloseMarker(shared_libraries_spec, shared_library_open); + if (closing_marker == std::string::npos) { + // No matching closing marker, return an error. + return nullptr; + } + shared_library_spec = shared_libraries_spec.substr(cursor, closing_marker + 1 - cursor); + cursor = closing_marker + 1; + if (cursor != shared_libraries_spec.length() && + shared_libraries_spec[cursor] == kClassLoaderSharedLibrarySeparator) { + // Pass the shared library separator marker. + ++cursor; + } + } std::unique_ptr<ClassLoaderInfo> shared_library( ParseInternal(shared_library_spec, parse_checksums)); if (shared_library == nullptr) { @@ -250,50 +324,24 @@ ClassLoaderContext::ClassLoaderInfo* ClassLoaderContext::ParseInternal( // The class loader spec contains shared libraries. Find the matching closing // shared library marker for it. - // Counter of opened shared library marker we've encountered so far. - uint32_t counter = 1; - // The index at which we're operating in the loop. - uint32_t string_index = first_shared_library_open + 1; - while (counter != 0) { - size_t shared_library_close = - remaining.find_first_of(kClassLoaderSharedLibraryClosingMark, string_index); - size_t shared_library_open = - remaining.find_first_of(kClassLoaderSharedLibraryOpeningMark, string_index); - if (shared_library_close == std::string::npos) { - // No matching closing market. Return an error. - LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec; - return nullptr; - } - - if ((shared_library_open == std::string::npos) || - (shared_library_close < shared_library_open)) { - // We have seen a closing marker. Decrement the counter. - --counter; - if (counter == 0) { - // Found the matching closing marker. - class_loader_spec = remaining.substr(0, shared_library_close + 1); - - // Compute the remaining string to analyze. - if (remaining.size() == shared_library_close + 1) { - remaining = ""; - } else if ((remaining.size() == shared_library_close + 2) || - (remaining.at(shared_library_close + 1) != kClassLoaderSeparator)) { - LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec; - return nullptr; - } else { - remaining = remaining.substr(shared_library_close + 2, - remaining.size() - shared_library_close - 2); - } - } else { - // Move the search index forward. - string_index = shared_library_close + 1; - } - } else { - // New nested opening marker. Increment the counter and move the search - // index after the marker. - ++counter; - string_index = shared_library_open + 1; - } + uint32_t shared_library_close = + FindMatchingSharedLibraryCloseMarker(remaining, first_shared_library_open); + if (shared_library_close == std::string::npos) { + LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec; + return nullptr; + } + class_loader_spec = remaining.substr(0, shared_library_close + 1); + + // Compute the remaining string to analyze. + if (remaining.size() == shared_library_close + 1) { + remaining = ""; + } else if ((remaining.size() == shared_library_close + 2) || + (remaining.at(shared_library_close + 1) != kClassLoaderSeparator)) { + LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec; + return nullptr; + } else { + remaining = remaining.substr(shared_library_close + 2, + remaining.size() - shared_library_close - 2); } } @@ -304,11 +352,11 @@ ClassLoaderContext::ClassLoaderInfo* ClassLoaderContext::ParseInternal( return nullptr; } if (first == nullptr) { - first.reset(info.release()); + first = std::move(info); previous_iteration = first.get(); } else { CHECK(previous_iteration != nullptr); - previous_iteration->parent.reset(info.release()); + previous_iteration->parent = std::move(info); previous_iteration = previous_iteration->parent.get(); } } @@ -571,21 +619,89 @@ static jclass GetClassLoaderClass(ClassLoaderContext::ClassLoaderType type) { UNREACHABLE(); } -static jobject CreateClassLoaderInternal(Thread* self, - const ClassLoaderContext::ClassLoaderInfo& info) +static std::string FlattenClasspath(const std::vector<std::string>& classpath) { + return android::base::Join(classpath, ':'); +} + +static ObjPtr<mirror::ClassLoader> CreateClassLoaderInternal( + Thread* self, + ScopedObjectAccess& soa, + const ClassLoaderContext::ClassLoaderInfo& info, + bool for_shared_library, + VariableSizedHandleScope& map_scope, + std::map<std::string, Handle<mirror::ClassLoader>>& canonicalized_libraries, + bool add_compilation_sources, + const std::vector<const DexFile*>& compilation_sources) REQUIRES_SHARED(Locks::mutator_lock_) { - CHECK(info.shared_libraries.empty()) << "Class loader shared library not implemented yet"; - jobject parent = nullptr; + if (for_shared_library) { + // Check if the shared library has already been created. + auto search = canonicalized_libraries.find(FlattenClasspath(info.classpath)); + if (search != canonicalized_libraries.end()) { + return search->second.Get(); + } + } + + StackHandleScope<3> hs(self); + MutableHandle<mirror::ObjectArray<mirror::ClassLoader>> libraries( + hs.NewHandle<mirror::ObjectArray<mirror::ClassLoader>>(nullptr)); + + if (!info.shared_libraries.empty()) { + libraries.Assign(mirror::ObjectArray<mirror::ClassLoader>::Alloc( + self, + GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(), + info.shared_libraries.size())); + for (uint32_t i = 0; i < info.shared_libraries.size(); ++i) { + // We should only add the compilation sources to the first class loader. + libraries->Set(i, + CreateClassLoaderInternal( + self, + soa, + *info.shared_libraries[i].get(), + /* for_shared_library= */ true, + map_scope, + canonicalized_libraries, + /* add_compilation_sources= */ false, + compilation_sources)); + } + } + + MutableHandle<mirror::ClassLoader> parent = hs.NewHandle<mirror::ClassLoader>(nullptr); if (info.parent != nullptr) { - parent = CreateClassLoaderInternal(self, *info.parent.get()); + // We should only add the compilation sources to the first class loader. + parent.Assign(CreateClassLoaderInternal( + self, + soa, + *info.parent.get(), + /* for_shared_library= */ false, + map_scope, + canonicalized_libraries, + /* add_compilation_sources= */ false, + compilation_sources)); } std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector( info.opened_dex_files); - return Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader( - self, - class_path_files, - GetClassLoaderClass(info.type), - parent); + if (add_compilation_sources) { + // For the first class loader, its classpath comes first, followed by compilation sources. + // This ensures that whenever we need to resolve classes from it the classpath elements + // come first. + class_path_files.insert(class_path_files.end(), + compilation_sources.begin(), + compilation_sources.end()); + } + Handle<mirror::Class> loader_class = hs.NewHandle<mirror::Class>( + soa.Decode<mirror::Class>(GetClassLoaderClass(info.type))); + ObjPtr<mirror::ClassLoader> loader = + Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader( + self, + class_path_files, + loader_class, + libraries, + parent); + if (for_shared_library) { + canonicalized_libraries[FlattenClasspath(info.classpath)] = + map_scope.NewHandle<mirror::ClassLoader>(loader); + } + return loader; } jobject ClassLoaderContext::CreateClassLoader( @@ -598,30 +714,29 @@ jobject ClassLoaderContext::CreateClassLoader( ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); if (class_loader_chain_ == nullptr) { + CHECK(special_shared_library_); return class_linker->CreatePathClassLoader(self, compilation_sources); } - // Create the class loader of the parent. - jobject parent = nullptr; - if (class_loader_chain_->parent != nullptr) { - parent = CreateClassLoaderInternal(self, *class_loader_chain_->parent.get()); - } - - // We set up all the parents. Move on to create the first class loader. - // Its classpath comes first, followed by compilation sources. This ensures that whenever - // we need to resolve classes from it the classpath elements come first. - - std::vector<const DexFile*> first_class_loader_classpath = MakeNonOwningPointerVector( - class_loader_chain_->opened_dex_files); - first_class_loader_classpath.insert(first_class_loader_classpath.end(), - compilation_sources.begin(), - compilation_sources.end()); - - return class_linker->CreateWellKnownClassLoader( - self, - first_class_loader_classpath, - GetClassLoaderClass(class_loader_chain_->type), - parent); + // Create a map of canonicalized shared libraries. As we're holding objects, + // we're creating a variable size handle scope to put handles in the map. + VariableSizedHandleScope map_scope(self); + std::map<std::string, Handle<mirror::ClassLoader>> canonicalized_libraries; + + // Create the class loader. + ObjPtr<mirror::ClassLoader> loader = + CreateClassLoaderInternal(self, + soa, + *class_loader_chain_.get(), + /* for_shared_library= */ false, + map_scope, + canonicalized_libraries, + /* add_compilation_sources= */ true, + compilation_sources); + // Make it a global ref and return. + ScopedLocalRef<jobject> local_ref( + soa.Env(), soa.Env()->AddLocalReference<jobject>(loader)); + return soa.Env()->NewGlobalRef(local_ref.get()); } std::vector<const DexFile*> ClassLoaderContext::FlattenOpenedDexFiles() const { diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 37cef8196d..71f40ac167 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -89,7 +89,10 @@ class ClassLoaderContext { // If the context is empty, this method only creates a single PathClassLoader with the // given compilation_sources. // - // Notes: + // Shared libraries found in the chain will be canonicalized based on the dex files they + // contain. + // + // Implementation notes: // 1) the objects are not completely set up. Do not use this outside of tests and the compiler. // 2) should only be called before the first call to OpenDexFiles(). jobject CreateClassLoader(const std::vector<const DexFile*>& compilation_sources) const; diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index f3e2ac00ba..0756982b65 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -19,12 +19,14 @@ #include <gtest/gtest.h> #include "android-base/strings.h" +#include "art_field-inl.h" #include "base/dchecked_vector.h" #include "base/stl_util.h" #include "class_linker.h" #include "common_runtime_test.h" #include "dex/dex_file.h" #include "handle_scope-inl.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" @@ -284,6 +286,25 @@ TEST_F(ClassLoaderContextTest, ParseEnclosingSharedLibraries) { VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex"); } +TEST_F(ClassLoaderContextTest, ParseComplexSharedLibraries1) { + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create( + "PCL[]{PCL[s4.dex]{PCL[s5.dex]{PCL[s6.dex]}#PCL[s6.dex]}}"); + VerifyContextSize(context.get(), 1); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s4.dex"); +} + +TEST_F(ClassLoaderContextTest, ParseComplexSharedLibraries2) { + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create( + "PCL[]{PCL[s1.dex]{PCL[s2.dex]}#PCL[s2.dex]#" + "PCL[s3.dex]#PCL[s4.dex]{PCL[s5.dex]{PCL[s6.dex]}#PCL[s6.dex]}#PCL[s5.dex]{PCL[s6.dex]}}"); + VerifyContextSize(context.get(), 1); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex"); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 1, "s2.dex"); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 2, "s3.dex"); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 3, "s4.dex"); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 4, "s5.dex"); +} + TEST_F(ClassLoaderContextTest, ParseValidEmptyContextDLC) { std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("DLC[]"); @@ -316,6 +337,10 @@ TEST_F(ClassLoaderContextTest, ParseInvalidValidContexts) { ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]}")); ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]{")); ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC{DLC[s4.dex]}")); + ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{##}")); + ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]#}")); + ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]##}")); + ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]{PCL[s3.dex]}#}")); } TEST_F(ClassLoaderContextTest, OpenInvalidDexFiles) { @@ -605,6 +630,292 @@ TEST_F(ClassLoaderContextTest, CreateClassLoaderWithComplexChain) { soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); } +TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibraries) { + // Setup the context. + std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD"); + + std::string context_spec = + "PCL[" + CreateClassPath(classpath_dex_a) + ":" + CreateClassPath(classpath_dex_b) + "]{" + + "DLC[" + CreateClassPath(classpath_dex_c) + "]#" + + "PCL[" + CreateClassPath(classpath_dex_d) + "]}"; + + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec); + ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "")); + + // Setup the compilation sources. + std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex"); + std::vector<const DexFile*> compilation_sources_raw = + MakeNonOwningPointerVector(compilation_sources); + + // Create the class loader. + jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw); + ASSERT_TRUE(jclass_loader != nullptr); + + // Verify the class loader. + ScopedObjectAccess soa(Thread::Current()); + + StackHandleScope<4> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( + soa.Decode<mirror::ClassLoader>(jclass_loader)); + + // For the first class loader the class path dex files must come first and then the + // compilation sources. + std::vector<const DexFile*> class_loader_1_dex_files = + MakeNonOwningPointerVector(classpath_dex_a); + for (auto& dex : classpath_dex_b) { + class_loader_1_dex_files.push_back(dex.get()); + } + for (auto& dex : compilation_sources_raw) { + class_loader_1_dex_files.push_back(dex); + } + VerifyClassLoaderDexFiles(soa, + class_loader_1, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_1_dex_files); + + // Verify the shared libraries. + ArtField* field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries->GetLength(), 2); + + // Verify the first shared library. + Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); + std::vector<const DexFile*> class_loader_2_dex_files = + MakeNonOwningPointerVector(classpath_dex_c); + VerifyClassLoaderDexFiles(soa, + class_loader_2, + WellKnownClasses::dalvik_system_DelegateLastClassLoader, + class_loader_2_dex_files); + raw_shared_libraries = field->GetObject(class_loader_2.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // Verify the second shared library. + Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries->Get(1)); + std::vector<const DexFile*> class_loader_3_dex_files = + MakeNonOwningPointerVector(classpath_dex_d); + VerifyClassLoaderDexFiles(soa, + class_loader_3, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_3_dex_files); + raw_shared_libraries = field->GetObject(class_loader_3.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // All class loaders should have the BootClassLoader as a parent. + ASSERT_TRUE(class_loader_1->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); +} + +TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesInParentToo) { + // Setup the context. + std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD"); + + std::string context_spec = + "PCL[" + CreateClassPath(classpath_dex_a) + "]{" + + "PCL[" + CreateClassPath(classpath_dex_b) + "]};" + + "PCL[" + CreateClassPath(classpath_dex_c) + "]{" + + "PCL[" + CreateClassPath(classpath_dex_d) + "]}"; + + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec); + ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "")); + + // Setup the compilation sources. + std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex"); + std::vector<const DexFile*> compilation_sources_raw = + MakeNonOwningPointerVector(compilation_sources); + + // Create the class loader. + jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw); + ASSERT_TRUE(jclass_loader != nullptr); + + // Verify the class loader. + ScopedObjectAccess soa(Thread::Current()); + + StackHandleScope<6> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( + soa.Decode<mirror::ClassLoader>(jclass_loader)); + + // For the first class loader the class path dex files must come first and then the + // compilation sources. + std::vector<const DexFile*> class_loader_1_dex_files = + MakeNonOwningPointerVector(classpath_dex_a); + for (auto& dex : compilation_sources_raw) { + class_loader_1_dex_files.push_back(dex); + } + VerifyClassLoaderDexFiles(soa, + class_loader_1, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_1_dex_files); + + // Verify its shared library. + ArtField* field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries->GetLength(), 1); + + Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); + std::vector<const DexFile*> class_loader_2_dex_files = + MakeNonOwningPointerVector(classpath_dex_b); + VerifyClassLoaderDexFiles(soa, + class_loader_2, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_2_dex_files); + raw_shared_libraries = field->GetObject(class_loader_2.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // Verify the parent. + Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent()); + std::vector<const DexFile*> class_loader_3_dex_files = + MakeNonOwningPointerVector(classpath_dex_c); + VerifyClassLoaderDexFiles(soa, + class_loader_3, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_3_dex_files); + + // Verify its shared library. + raw_shared_libraries = field->GetObject(class_loader_3.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries->GetLength(), 1); + + Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(shared_libraries_2->Get(0)); + std::vector<const DexFile*> class_loader_4_dex_files = + MakeNonOwningPointerVector(classpath_dex_d); + VerifyClassLoaderDexFiles(soa, + class_loader_4, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_4_dex_files); + raw_shared_libraries = field->GetObject(class_loader_4.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // Class loaders should have the BootClassLoader as a parent. + ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_4->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); +} + +TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesDependencies) { + // Setup the context. + std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD"); + + std::string context_spec = + "PCL[" + CreateClassPath(classpath_dex_a) + "]{" + + "PCL[" + CreateClassPath(classpath_dex_b) + "]{" + + "PCL[" + CreateClassPath(classpath_dex_c) + "]}};" + + "PCL[" + CreateClassPath(classpath_dex_d) + "]"; + + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec); + ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "")); + + // Setup the compilation sources. + std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex"); + std::vector<const DexFile*> compilation_sources_raw = + MakeNonOwningPointerVector(compilation_sources); + + // Create the class loader. + jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw); + ASSERT_TRUE(jclass_loader != nullptr); + + // Verify the class loader. + ScopedObjectAccess soa(Thread::Current()); + + StackHandleScope<6> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( + soa.Decode<mirror::ClassLoader>(jclass_loader)); + + // For the first class loader the class path dex files must come first and then the + // compilation sources. + std::vector<const DexFile*> class_loader_1_dex_files = + MakeNonOwningPointerVector(classpath_dex_a); + for (auto& dex : compilation_sources_raw) { + class_loader_1_dex_files.push_back(dex); + } + VerifyClassLoaderDexFiles(soa, + class_loader_1, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_1_dex_files); + + // Verify its shared library. + ArtField* field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries->GetLength(), 1); + + Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); + std::vector<const DexFile*> class_loader_2_dex_files = + MakeNonOwningPointerVector(classpath_dex_b); + VerifyClassLoaderDexFiles(soa, + class_loader_2, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_2_dex_files); + + // Verify the shared library dependency of the shared library. + raw_shared_libraries = field->GetObject(class_loader_2.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries_2->GetLength(), 1); + + Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries_2->Get(0)); + std::vector<const DexFile*> class_loader_3_dex_files = + MakeNonOwningPointerVector(classpath_dex_c); + VerifyClassLoaderDexFiles(soa, + class_loader_3, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_3_dex_files); + raw_shared_libraries = field->GetObject(class_loader_3.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // Verify the parent. + Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(class_loader_1->GetParent()); + std::vector<const DexFile*> class_loader_4_dex_files = + MakeNonOwningPointerVector(classpath_dex_d); + VerifyClassLoaderDexFiles(soa, + class_loader_4, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_4_dex_files); + raw_shared_libraries = field->GetObject(class_loader_4.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // Class loaders should have the BootClassLoader as a parent. + ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_4->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); +} TEST_F(ClassLoaderContextTest, RemoveSourceLocations) { std::unique_ptr<ClassLoaderContext> context = @@ -622,6 +933,91 @@ TEST_F(ClassLoaderContextTest, RemoveSourceLocations) { VerifyClassLoaderPCL(context.get(), 0, ""); } +TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSameSharedLibraries) { + // Setup the context. + std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); + + std::string context_spec = + "PCL[" + CreateClassPath(classpath_dex_a) + "]{" + + "PCL[" + CreateClassPath(classpath_dex_b) + "]};" + + "PCL[" + CreateClassPath(classpath_dex_c) + "]{" + + "PCL[" + CreateClassPath(classpath_dex_b) + "]}"; + + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec); + ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "")); + + // Setup the compilation sources. + std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex"); + std::vector<const DexFile*> compilation_sources_raw = + MakeNonOwningPointerVector(compilation_sources); + + // Create the class loader. + jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw); + ASSERT_TRUE(jclass_loader != nullptr); + + // Verify the class loader. + ScopedObjectAccess soa(Thread::Current()); + + StackHandleScope<6> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( + soa.Decode<mirror::ClassLoader>(jclass_loader)); + + // For the first class loader the class path dex files must come first and then the + // compilation sources. + std::vector<const DexFile*> class_loader_1_dex_files = + MakeNonOwningPointerVector(classpath_dex_a); + for (auto& dex : compilation_sources_raw) { + class_loader_1_dex_files.push_back(dex); + } + VerifyClassLoaderDexFiles(soa, + class_loader_1, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_1_dex_files); + + // Verify its shared library. + ArtField* field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries->GetLength(), 1); + + Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); + std::vector<const DexFile*> class_loader_2_dex_files = + MakeNonOwningPointerVector(classpath_dex_b); + VerifyClassLoaderDexFiles(soa, + class_loader_2, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_2_dex_files); + + // Verify the parent. + Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent()); + std::vector<const DexFile*> class_loader_3_dex_files = + MakeNonOwningPointerVector(classpath_dex_c); + VerifyClassLoaderDexFiles(soa, + class_loader_3, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_3_dex_files); + + // Verify its shared library is the same as the child. + raw_shared_libraries = field->GetObject(class_loader_3.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries_2->GetLength(), 1); + ASSERT_EQ(shared_libraries_2->Get(0), class_loader_2.Get()); + + // Class loaders should have the BootClassLoader as a parent. + ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); +} + TEST_F(ClassLoaderContextTest, EncodeInOatFile) { std::string dex1_name = GetTestDexFileName("Main"); std::string dex2_name = GetTestDexFileName("MyClass"); diff --git a/runtime/class_root.h b/runtime/class_root.h index 1cd135f2aa..1ff48457d6 100644 --- a/runtime/class_root.h +++ b/runtime/class_root.h @@ -101,6 +101,7 @@ class VarHandle; M(kLongArrayClass, "[J", mirror::PrimitiveArray<int64_t>) \ M(kShortArrayClass, "[S", mirror::PrimitiveArray<int16_t>) \ M(kJavaLangStackTraceElementArrayClass, "[Ljava/lang/StackTraceElement;", mirror::ObjectArray<mirror::StackTraceElement>) \ + M(kJavaLangClassLoaderArrayClass, "[Ljava/lang/ClassLoader;", mirror::ObjectArray<mirror::ClassLoader>) \ M(kDalvikSystemClassExt, "Ldalvik/system/ClassExt;", mirror::ClassExt) // Well known mirror::Class roots accessed via ClassLinker::GetClassRoots(). diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index b11e368871..f52a0f9430 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -140,13 +140,13 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location, &error_msg)); ASSERT_TRUE(image_header != nullptr) << error_msg; const OatHeader& oat_header = odex_file->GetOatHeader(); - uint32_t combined_checksum = image_header->GetOatChecksum(); + uint32_t boot_image_checksum = image_header->GetImageChecksum(); if (CompilerFilter::DependsOnImageChecksum(filter)) { if (with_alternate_image) { - EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum()); + EXPECT_NE(boot_image_checksum, oat_header.GetBootImageChecksum()); } else { - EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum()); + EXPECT_EQ(boot_image_checksum, oat_header.GetBootImageChecksum()); } } } diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index f45197834f..040a8c5432 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include <setjmp.h> - #include <memory> #include "base/macros.h" diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index 1ab0b0e652..5c2830d96b 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -16,7 +16,6 @@ #include "fault_handler.h" -#include <setjmp.h> #include <string.h> #include <sys/mman.h> #include <sys/ucontext.h> diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h index 02eeefe0a0..24ef0b1e07 100644 --- a/runtime/fault_handler.h +++ b/runtime/fault_handler.h @@ -18,7 +18,6 @@ #ifndef ART_RUNTIME_FAULT_HANDLER_H_ #define ART_RUNTIME_FAULT_HANDLER_H_ -#include <setjmp.h> #include <signal.h> #include <stdint.h> diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc index b39628b1dc..e66a174587 100644 --- a/runtime/gc/accounting/mod_union_table_test.cc +++ b/runtime/gc/accounting/mod_union_table_test.cc @@ -162,9 +162,9 @@ ModUnionTable* ModUnionTableFactory::Create( } default: { UNIMPLEMENTED(FATAL) << "Invalid type " << type; + UNREACHABLE(); } } - return nullptr; } TEST_F(ModUnionTableTest, TestCardCache) { diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index aba1c5ad94..fefe9ab475 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -95,6 +95,7 @@ ConcurrentCopying::ConcurrentCopying(Heap* heap, weak_ref_access_enabled_(true), copied_live_bytes_ratio_sum_(0.f), gc_count_(0), + reclaimed_bytes_ratio_sum_(0.f), young_gen_(young_gen), skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock), measure_read_barrier_slow_path_(measure_read_barrier_slow_path), @@ -110,7 +111,8 @@ ConcurrentCopying::ConcurrentCopying(Heap* heap, force_evacuate_all_(false), gc_grays_immune_objects_(false), immune_gray_stack_lock_("concurrent copying immune gray stack lock", - kMarkSweepMarkStackLock) { + kMarkSweepMarkStackLock), + num_bytes_allocated_before_gc_(0) { static_assert(space::RegionSpace::kRegionSize == accounting::ReadBarrierTable::kRegionSize, "The region space size and the read barrier table region size must match"); CHECK(kEnableGenerationalConcurrentCopyingCollection || !young_gen_); @@ -323,6 +325,7 @@ void ConcurrentCopying::BindBitmaps() { void ConcurrentCopying::InitializePhase() { TimingLogger::ScopedTiming split("InitializePhase", GetTimings()); + num_bytes_allocated_before_gc_ = static_cast<int64_t>(heap_->GetBytesAllocated()); if (kVerboseMode) { LOG(INFO) << "GC InitializePhase"; LOG(INFO) << "Region-space : " << reinterpret_cast<void*>(region_space_->Begin()) << "-" @@ -2091,6 +2094,11 @@ void ConcurrentCopying::ReclaimPhase() { CheckEmptyMarkStack(); + int64_t num_bytes_allocated_after_gc = static_cast<int64_t>(heap_->GetBytesAllocated()); + int64_t diff = num_bytes_allocated_before_gc_ - num_bytes_allocated_after_gc; + auto ratio = static_cast<float>(diff) / num_bytes_allocated_before_gc_; + reclaimed_bytes_ratio_sum_ += ratio; + if (kVerboseMode) { LOG(INFO) << "GC end of ReclaimPhase"; } @@ -3199,6 +3207,7 @@ mirror::Object* ConcurrentCopying::MarkFromReadBarrierWithMeasurements(Thread* c void ConcurrentCopying::DumpPerformanceInfo(std::ostream& os) { GarbageCollector::DumpPerformanceInfo(os); + size_t num_gc_cycles = GetCumulativeTimings().GetIterations(); MutexLock mu(Thread::Current(), rb_slow_path_histogram_lock_); if (rb_slow_path_time_histogram_.SampleSize() > 0) { Histogram<uint64_t>::CumulativeData cumulative_data; @@ -3211,15 +3220,15 @@ void ConcurrentCopying::DumpPerformanceInfo(std::ostream& os) { if (rb_slow_path_count_gc_total_ > 0) { os << "GC slow path count " << rb_slow_path_count_gc_total_ << "\n"; } - float average_ratio = copied_live_bytes_ratio_sum_ / gc_count_; - if (young_gen_) { - os << "Average minor GC copied live bytes ratio " - << average_ratio << " over " << gc_count_ << " minor GCs\n"; - } else { - os << "Average major GC copied live bytes ratio " - << average_ratio << " over " << gc_count_ << " major GCs\n"; - } + os << "Average " << (young_gen_ ? "minor" : "major") << " GC reclaim bytes ratio " + << (reclaimed_bytes_ratio_sum_ / num_gc_cycles) << " over " << num_gc_cycles + << " GC cycles\n"; + + os << "Average " << (young_gen_ ? "minor" : "major") << " GC copied live bytes ratio " + << (copied_live_bytes_ratio_sum_ / gc_count_) << " over " << gc_count_ + << " " << (young_gen_ ? "minor" : "major") << " GCs\n"; + os << "Cumulative bytes moved " << cumulative_bytes_moved_.load(std::memory_order_relaxed) << "\n"; os << "Cumulative objects moved " diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index cd086c4fb8..6535b11d79 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -359,10 +359,12 @@ class ConcurrentCopying : public GarbageCollector { Atomic<uint64_t> cumulative_bytes_moved_; Atomic<uint64_t> cumulative_objects_moved_; - // copied_live_bytes_ratio_sum_ and gc_count_ are read and written by CC per - // GC, in ReclaimPhase, and are read by DumpPerformanceInfo (potentially from - // another thread). However, at present, DumpPerformanceInfo is only called - // when the runtime shuts down, so no concurrent access. + // copied_live_bytes_ratio_sum_ is read and written by CC per GC, in + // ReclaimPhase, and is read by DumpPerformanceInfo (potentially from another + // thread). However, at present, DumpPerformanceInfo is only called when the + // runtime shuts down, so no concurrent access. The same reasoning goes for + // gc_count_ and reclaimed_bytes_ratio_sum_ + // The sum of of all copied live bytes ratio (to_bytes/from_bytes) float copied_live_bytes_ratio_sum_; // The number of GC counts, used to calculate the average above. (It doesn't @@ -371,6 +373,9 @@ class ConcurrentCopying : public GarbageCollector { // space.) size_t gc_count_; + // reclaimed_bytes_ratio = reclaimed_bytes/num_allocated_bytes per GC cycle + float reclaimed_bytes_ratio_sum_; + // Generational "sticky", only trace through dirty objects in region space. const bool young_gen_; // If true, the GC thread is done scanning marked objects on dirty and aged @@ -416,6 +421,9 @@ class ConcurrentCopying : public GarbageCollector { // ConcurrentCopying::SweepArray). MemMap sweep_array_free_buffer_mem_map_; + // Use signed because after_gc may be larger than before_gc. + int64_t num_bytes_allocated_before_gc_; + class ActivateReadBarrierEntrypointsCallback; class ActivateReadBarrierEntrypointsCheckpoint; class AssertToSpaceInvariantFieldVisitor; diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index 5e3692ea9a..0294db7b7e 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -77,8 +77,9 @@ void GarbageCollector::RegisterPause(uint64_t nano_length) { void GarbageCollector::ResetCumulativeStatistics() { cumulative_timings_.Reset(); - total_time_ns_ = 0; - total_freed_objects_ = 0; + total_thread_cpu_time_ns_ = 0u; + total_time_ns_ = 0u; + total_freed_objects_ = 0u; total_freed_bytes_ = 0; MutexLock mu(Thread::Current(), pause_histogram_lock_); pause_histogram_.Reset(); @@ -88,6 +89,7 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) { ScopedTrace trace(android::base::StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName())); Thread* self = Thread::Current(); uint64_t start_time = NanoTime(); + uint64_t thread_cpu_start_time = ThreadCpuNanoTime(); Iteration* current_iteration = GetCurrentIteration(); current_iteration->Reset(gc_cause, clear_soft_references); // Note transaction mode is single-threaded and there's no asynchronous GC and this flag doesn't @@ -102,6 +104,8 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) { total_freed_bytes_ += current_iteration->GetFreedBytes() + current_iteration->GetFreedLargeObjectBytes(); uint64_t end_time = NanoTime(); + uint64_t thread_cpu_end_time = ThreadCpuNanoTime(); + total_thread_cpu_time_ns_ += thread_cpu_end_time - thread_cpu_start_time; current_iteration->SetDurationNs(end_time - start_time); if (Locks::mutator_lock_->IsExclusiveHeld(self)) { // The entire GC was paused, clear the fake pauses which might be in the pause times and add @@ -159,8 +163,9 @@ void GarbageCollector::ResetMeasurements() { pause_histogram_.Reset(); } cumulative_timings_.Reset(); - total_time_ns_ = 0; - total_freed_objects_ = 0; + total_thread_cpu_time_ns_ = 0u; + total_time_ns_ = 0u; + total_freed_objects_ = 0u; total_freed_bytes_ = 0; } diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h index f722e8d855..2857881456 100644 --- a/runtime/gc/collector/garbage_collector.h +++ b/runtime/gc/collector/garbage_collector.h @@ -81,6 +81,9 @@ class GarbageCollector : public RootVisitor, public IsMarkedVisitor, public Mark void SwapBitmaps() REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + uint64_t GetTotalCpuTime() const { + return total_thread_cpu_time_ns_; + } uint64_t GetTotalPausedTimeNs() REQUIRES(!pause_histogram_lock_); int64_t GetTotalFreedBytes() const { return total_freed_bytes_; @@ -146,6 +149,7 @@ class GarbageCollector : public RootVisitor, public IsMarkedVisitor, public Mark std::string name_; // Cumulative statistics. Histogram<uint64_t> pause_histogram_ GUARDED_BY(pause_histogram_lock_); + uint64_t total_thread_cpu_time_ns_; uint64_t total_time_ns_; uint64_t total_freed_objects_; int64_t total_freed_bytes_; diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc index c2a67bf9f6..9f98f6c7f0 100644 --- a/runtime/gc/collector/immune_spaces_test.cc +++ b/runtime/gc/collector/immune_spaces_test.cc @@ -113,22 +113,20 @@ class ImmuneSpacesTest : public CommonRuntimeTest { ImageSection sections[ImageHeader::kSectionCount]; new (image_map.Begin()) ImageHeader( /*image_begin=*/ PointerToLowMemUInt32(image_map.Begin()), - /*image_size=*/ image_map.Size(), + /*image_size=*/ image_size, sections, /*image_roots=*/ PointerToLowMemUInt32(image_map.Begin()) + 1, /*oat_checksum=*/ 0u, // The oat file data in the header is always right after the image space. /*oat_file_begin=*/ PointerToLowMemUInt32(oat_map.Begin()), - /*oat_data_begin=*/PointerToLowMemUInt32(oat_map.Begin()), - /*oat_data_end=*/PointerToLowMemUInt32(oat_map.Begin() + oat_size), - /*oat_file_end=*/PointerToLowMemUInt32(oat_map.Begin() + oat_size), - /*boot_image_begin=*/0u, - /*boot_image_size=*/0u, - /*boot_oat_begin=*/0u, - /*boot_oat_size=*/0u, - /*pointer_size=*/sizeof(void*), + /*oat_data_begin=*/ PointerToLowMemUInt32(oat_map.Begin()), + /*oat_data_end=*/ PointerToLowMemUInt32(oat_map.Begin() + oat_size), + /*oat_file_end=*/ PointerToLowMemUInt32(oat_map.Begin() + oat_size), + /*boot_image_begin=*/ 0u, + /*boot_image_size=*/ 0u, + /*pointer_size=*/ sizeof(void*), ImageHeader::kStorageModeUncompressed, - /*data_size=*/0u); + /*data_size=*/ 0u); return new DummyImageSpace(std::move(image_map), std::move(live_bitmap), std::move(oat_file), diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 19b1fc7878..15e0711948 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -736,7 +736,8 @@ void SemiSpace::ScanObject(Object* obj) { void SemiSpace::ProcessMarkStack() { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); accounting::ContinuousSpaceBitmap* live_bitmap = nullptr; - if (collect_from_space_only_) { + const bool collect_from_space_only = collect_from_space_only_; + if (collect_from_space_only) { // If a bump pointer space only collection (and the promotion is // enabled,) we delay the live-bitmap marking of promoted objects // from MarkObject() until this function. @@ -748,7 +749,7 @@ void SemiSpace::ProcessMarkStack() { } while (!mark_stack_->IsEmpty()) { Object* obj = mark_stack_->PopBack(); - if (collect_from_space_only_ && promo_dest_space_->HasAddress(obj)) { + if (collect_from_space_only && promo_dest_space_->HasAddress(obj)) { // obj has just been promoted. Mark the live bitmap for it, // which is delayed from MarkObject(). DCHECK(!live_bitmap->Test(obj)); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index f0f81fc67e..4359bea421 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1062,13 +1062,21 @@ void Heap::RemoveSpace(space::Space* space) { } } +uint64_t Heap::GetTotalGcCpuTime() { + uint64_t sum = 0; + for (auto* collector : garbage_collectors_) { + sum += collector->GetTotalCpuTime(); + } + return sum; +} + void Heap::DumpGcPerformanceInfo(std::ostream& os) { // Dump cumulative timings. os << "Dumping cumulative Gc timings\n"; uint64_t total_duration = 0; // Dump cumulative loggers for each GC type. uint64_t total_paused_time = 0; - for (auto& collector : garbage_collectors_) { + for (auto* collector : garbage_collectors_) { total_duration += collector->GetCumulativeTimings().GetTotalNs(); total_paused_time += collector->GetTotalPausedTimeNs(); collector->DumpPerformanceInfo(os); @@ -1128,7 +1136,7 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { } void Heap::ResetGcPerformanceInfo() { - for (auto& collector : garbage_collectors_) { + for (auto* collector : garbage_collectors_) { collector->ResetMeasurements(); } total_bytes_freed_ever_ = 0; @@ -1149,7 +1157,7 @@ void Heap::ResetGcPerformanceInfo() { uint64_t Heap::GetGcCount() const { uint64_t gc_count = 0U; - for (auto& collector : garbage_collectors_) { + for (auto* collector : garbage_collectors_) { gc_count += collector->GetCumulativeTimings().GetIterations(); } return gc_count; @@ -1157,7 +1165,7 @@ uint64_t Heap::GetGcCount() const { uint64_t Heap::GetGcTime() const { uint64_t gc_time = 0U; - for (auto& collector : garbage_collectors_) { + for (auto* collector : garbage_collectors_) { gc_time += collector->GetCumulativeTimings().GetTotalNs(); } return gc_time; @@ -3497,7 +3505,7 @@ bool Heap::IsMovableObject(ObjPtr<mirror::Object> obj) const { } collector::GarbageCollector* Heap::FindCollectorByGcType(collector::GcType gc_type) { - for (const auto& collector : garbage_collectors_) { + for (auto* collector : garbage_collectors_) { if (collector->GetCollectorType() == collector_type_ && collector->GetGcType() == gc_type) { return collector; diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index c3ee5267b5..a43f3156f5 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -395,6 +395,8 @@ class Heap { REQUIRES(!Locks::heap_bitmap_lock_) REQUIRES(Locks::mutator_lock_); + uint64_t GetTotalGcCpuTime(); + // Set target ideal heap utilization ratio, implements // dalvik.system.VMRuntime.setTargetHeapUtilization. void SetTargetHeapUtilization(float target); diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 0766999c02..bfb37463e5 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -383,20 +383,16 @@ class ImageSpace::Loader { public: static std::unique_ptr<ImageSpace> InitAppImage(const char* image_filename, const char* image_location, - bool validate_oat_file, const OatFile* oat_file, /*inout*/MemMap* image_reservation, - /*inout*/MemMap* oat_reservation, /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) { TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image)); std::unique_ptr<ImageSpace> space = Init(image_filename, image_location, - validate_oat_file, oat_file, &logger, image_reservation, - oat_reservation, error_msg); if (space != nullptr) { TimingLogger::ScopedTiming timing("RelocateImage", &logger); @@ -438,11 +434,9 @@ class ImageSpace::Loader { static std::unique_ptr<ImageSpace> Init(const char* image_filename, const char* image_location, - bool validate_oat_file, const OatFile* oat_file, TimingLogger* logger, /*inout*/MemMap* image_reservation, - /*inout*/MemMap* oat_reservation, /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) { CHECK(image_filename != nullptr); @@ -479,8 +473,8 @@ class ImageSpace::Loader { } if (oat_file != nullptr) { - // If we have an oat file, check the oat file checksum. The oat file is only non-null for the - // app image case. Otherwise, we open the oat file after the image and check the checksum there. + // If we have an oat file (i.e. for app image), check the oat file checksum. + // Otherwise, we open the oat file after the image and check the checksum there. const uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum(); const uint32_t image_oat_checksum = image_header->GetOatChecksum(); if (oat_checksum != image_oat_checksum) { @@ -517,15 +511,13 @@ class ImageSpace::Loader { return nullptr; } - MemMap map; - // GetImageBegin is the preferred address to map the image. If we manage to map the // image at the image begin, the amount of fixup work required is minimized. // If it is pic we will retry with error_msg for the failure case. Pass a null error_msg to // avoid reading proc maps for a mapping failure and slowing everything down. // For the boot image, we have already reserved the memory and we load the image // into the `image_reservation`. - map = LoadImageFile( + MemMap map = LoadImageFile( image_filename, image_location, *image_header, @@ -583,33 +575,7 @@ class ImageSpace::Loader { std::move(map), std::move(bitmap), image_end)); - - // VerifyImageAllocations() will be called later in Runtime::Init() - // as some class roots like ArtMethod::java_lang_reflect_ArtMethod_ - // and ArtField::java_lang_reflect_ArtField_, which are used from - // Object::SizeOf() which VerifyImageAllocations() calls, are not - // set yet at this point. - if (oat_file == nullptr) { - TimingLogger::ScopedTiming timing("OpenOatFile", logger); - space->oat_file_ = OpenOatFile(*space, image_filename, oat_reservation, error_msg); - if (space->oat_file_ == nullptr) { - DCHECK(!error_msg->empty()); - return nullptr; - } - space->oat_file_non_owned_ = space->oat_file_.get(); - } else { - space->oat_file_non_owned_ = oat_file; - } - - if (validate_oat_file) { - TimingLogger::ScopedTiming timing("ValidateOatFile", logger); - CHECK(space->oat_file_ != nullptr); - if (!ImageSpace::ValidateOatFile(*space->oat_file_, error_msg)) { - DCHECK(!error_msg->empty()); - return nullptr; - } - } - + space->oat_file_non_owned_ = oat_file; return space; } @@ -700,11 +666,9 @@ class ImageSpace::Loader { class FixupVisitor : public ValueObject { public: FixupVisitor(const RelocationRange& boot_image, - const RelocationRange& boot_oat, const RelocationRange& app_image, const RelocationRange& app_oat) : boot_image_(boot_image), - boot_oat_(boot_oat), app_image_(app_image), app_oat_(app_oat) {} @@ -727,8 +691,8 @@ class ImageSpace::Loader { // Return the relocated address of a code pointer (contained by an oat file). ALWAYS_INLINE const void* ForwardCode(const void* src) const { const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src); - if (boot_oat_.InSource(uint_src)) { - return reinterpret_cast<const void*>(boot_oat_.ToDest(uint_src)); + if (boot_image_.InSource(uint_src)) { + return reinterpret_cast<const void*>(boot_image_.ToDest(uint_src)); } if (app_oat_.InSource(uint_src)) { return reinterpret_cast<const void*>(app_oat_.ToDest(uint_src)); @@ -745,7 +709,6 @@ class ImageSpace::Loader { protected: // Source section. const RelocationRange boot_image_; - const RelocationRange boot_oat_; const RelocationRange app_image_; const RelocationRange app_oat_; }; @@ -893,7 +856,7 @@ class ImageSpace::Loader { // We want to use our own class loader and not the one in the image. if (obj->IsClass<kVerifyNone>()) { mirror::Class* as_klass = obj->AsClass<kVerifyNone>(); - FixupObjectAdapter visitor(boot_image_, boot_oat_, app_image_, app_oat_); + FixupObjectAdapter visitor(boot_image_, app_image_, app_oat_); as_klass->FixupNativePointers<kVerifyNone>(as_klass, pointer_size_, visitor); // Deal with the pointer arrays. Use the helper function since multiple classes can reference // the same arrays. @@ -1025,10 +988,8 @@ class ImageSpace::Loader { *error_msg = "Can not relocate app image without boot oat file"; return false; } - const uint32_t boot_image_size = boot_image_end - boot_image_begin; - const uint32_t boot_oat_size = boot_oat_end - boot_oat_begin; + const uint32_t boot_image_size = boot_oat_end - boot_image_begin; const uint32_t image_header_boot_image_size = image_header.GetBootImageSize(); - const uint32_t image_header_boot_oat_size = image_header.GetBootOatSize(); if (boot_image_size != image_header_boot_image_size) { *error_msg = StringPrintf("Boot image size %" PRIu64 " does not match expected size %" PRIu64, @@ -1036,20 +997,10 @@ class ImageSpace::Loader { static_cast<uint64_t>(image_header_boot_image_size)); return false; } - if (boot_oat_size != image_header_boot_oat_size) { - *error_msg = StringPrintf("Boot oat size %" PRIu64 " does not match expected size %" - PRIu64, - static_cast<uint64_t>(boot_oat_size), - static_cast<uint64_t>(image_header_boot_oat_size)); - return false; - } TimingLogger logger(__FUNCTION__, true, false); RelocationRange boot_image(image_header.GetBootImageBegin(), boot_image_begin, boot_image_size); - RelocationRange boot_oat(image_header.GetBootOatBegin(), - boot_oat_begin, - boot_oat_size); RelocationRange app_image(reinterpret_cast<uintptr_t>(image_header.GetImageBegin()), reinterpret_cast<uintptr_t>(target_base), image_header.GetImageSize()); @@ -1061,11 +1012,9 @@ class ImageSpace::Loader { VLOG(image) << "App image " << app_image; VLOG(image) << "App oat " << app_oat; VLOG(image) << "Boot image " << boot_image; - VLOG(image) << "Boot oat " << boot_oat; - // True if we need to fixup any heap pointers, otherwise only code pointers. + // True if we need to fixup any heap pointers. const bool fixup_image = boot_image.Delta() != 0 || app_image.Delta() != 0; - const bool fixup_code = boot_oat.Delta() != 0 || app_oat.Delta() != 0; - if (!fixup_image && !fixup_code) { + if (!fixup_image) { // Nothing to fix up. return true; } @@ -1074,7 +1023,7 @@ class ImageSpace::Loader { const ImageSection& objects_section = image_header.GetObjectsSection(); uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset()); uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End()); - FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat); + FixupObjectAdapter fixup_adapter(boot_image, app_image, app_oat); if (fixup_image) { // Two pass approach, fix up all classes first, then fix up non class-objects. // The visited bitmap is used to ensure that pointer arrays are not forwarded twice. @@ -1085,7 +1034,6 @@ class ImageSpace::Loader { FixupObjectVisitor fixup_object_visitor(visited_bitmap.get(), pointer_size, boot_image, - boot_oat, app_image, app_oat); TimingLogger::ScopedTiming timing("Fixup classes", &logger); @@ -1191,7 +1139,6 @@ class ImageSpace::Loader { FixupArtMethodVisitor method_visitor(fixup_image, pointer_size, boot_image, - boot_oat, app_image, app_oat); image_header.VisitPackedArtMethods(&method_visitor, target_base, pointer_size); @@ -1200,7 +1147,7 @@ class ImageSpace::Loader { { // Only touches objects in the app image, no need for mutator lock. TimingLogger::ScopedTiming timing("Fixup fields", &logger); - FixupArtFieldVisitor field_visitor(boot_image, boot_oat, app_image, app_oat); + FixupArtFieldVisitor field_visitor(boot_image, app_image, app_oat); image_header.VisitPackedArtFields(&field_visitor, target_base); } { @@ -1222,7 +1169,7 @@ class ImageSpace::Loader { WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); ClassTable temp_table; temp_table.ReadFromMemory(target_base + class_table_section.Offset()); - FixupRootVisitor root_visitor(boot_image, boot_oat, app_image, app_oat); + FixupRootVisitor root_visitor(boot_image, app_image, app_oat); temp_table.VisitRoots(root_visitor); } // Fix up the intern table. @@ -1234,7 +1181,7 @@ class ImageSpace::Loader { InternTable temp_intern_table; // Note that we require that ReadFromMemory does not make an internal copy of the elements // so that the VisitRoots() will update the memory directly rather than the copies. - FixupRootVisitor root_visitor(boot_image, boot_oat, app_image, app_oat); + FixupRootVisitor root_visitor(boot_image, app_image, app_oat); temp_intern_table.AddTableFromMemory(target_base + intern_table_section.Offset(), [&](InternTable::UnorderedSet& strings) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1249,49 +1196,6 @@ class ImageSpace::Loader { } return true; } - - static std::unique_ptr<OatFile> OpenOatFile(const ImageSpace& image, - const char* image_path, - /*inout*/MemMap* oat_reservation, - std::string* error_msg) { - const ImageHeader& image_header = image.GetImageHeader(); - std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path); - - CHECK(image_header.GetOatDataBegin() != nullptr); - - uint8_t* oat_data_begin = image_header.GetOatDataBegin(); - if (oat_reservation != nullptr) { - oat_data_begin += oat_reservation->Begin() - image_header.GetOatFileBegin(); - } - std::unique_ptr<OatFile> oat_file(OatFile::Open(/*zip_fd=*/ -1, - oat_filename, - oat_filename, - !Runtime::Current()->IsAotCompiler(), - /*low_4gb=*/ false, - /*abs_dex_location=*/ nullptr, - oat_reservation, - error_msg)); - if (oat_file == nullptr) { - *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s", - oat_filename.c_str(), - image.GetName(), - error_msg->c_str()); - return nullptr; - } - CHECK(oat_data_begin == oat_file->Begin()); - uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum(); - uint32_t image_oat_checksum = image_header.GetOatChecksum(); - if (oat_checksum != image_oat_checksum) { - *error_msg = StringPrintf("Failed to match oat file checksum 0x%x to expected oat checksum 0x%x" - " in image %s", - oat_checksum, - image_oat_checksum, - image.GetName()); - return nullptr; - } - - return oat_file; - } }; class ImageSpace::BootImageLoader { @@ -1351,26 +1255,22 @@ class ImageSpace::BootImageLoader { } uint32_t image_start; uint32_t image_end; - uint32_t oat_end; - if (!GetBootImageAddressRange(filename, &image_start, &image_end, &oat_end, error_msg)) { + if (!GetBootImageAddressRange(filename, &image_start, &image_end, error_msg)) { return false; } if (locations.size() > 1u) { std::string last_filename = GetSystemImageFilename(locations.back().c_str(), image_isa_); uint32_t dummy; - if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, &oat_end, error_msg)) { + if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, error_msg)) { return false; } } MemMap image_reservation; - MemMap oat_reservation; MemMap local_extra_reservation; - if (!ReserveBootImageMemory(image_start, - image_end, - oat_end, + if (!ReserveBootImageMemory(/*reservation_size=*/ image_end - image_start, + image_start, extra_reservation_size, &image_reservation, - &oat_reservation, &local_extra_reservation, error_msg)) { return false; @@ -1380,28 +1280,29 @@ class ImageSpace::BootImageLoader { spaces.reserve(locations.size()); for (const std::string& location : locations) { filename = GetSystemImageFilename(location.c_str(), image_isa_); - spaces.push_back(Load(location, - filename, - /*validate_oat_file=*/ false, - &logger, - &image_reservation, - &oat_reservation, - error_msg)); + spaces.push_back(Load(location, filename, &logger, &image_reservation, error_msg)); if (spaces.back() == nullptr) { return false; } } - if (!CheckReservationsExhausted(image_reservation, oat_reservation, error_msg)) { + for (std::unique_ptr<ImageSpace>& space : spaces) { + static constexpr bool kValidateOatFile = false; + if (!OpenOatFile(space.get(), kValidateOatFile, &logger, &image_reservation, error_msg)) { + return false; + } + } + if (!CheckReservationExhausted(image_reservation, error_msg)) { return false; } MaybeRelocateSpaces(spaces, &logger); InitRuntimeMethods(spaces); - *extra_reservation = std::move(local_extra_reservation); - VLOG(image) << "ImageSpace::BootImageLoader::InitFromDalvikCache exiting " << *spaces.front(); boot_image_spaces->swap(spaces); + *extra_reservation = std::move(local_extra_reservation); if (VLOG_IS_ON(image)) { + LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromSystem exiting " + << boot_image_spaces->front(); logger.Dump(LOG_STREAM(INFO)); } return true; @@ -1421,8 +1322,7 @@ class ImageSpace::BootImageLoader { } uint32_t image_start; uint32_t image_end; - uint32_t oat_end; - if (!GetBootImageAddressRange(cache_filename_, &image_start, &image_end, &oat_end, error_msg)) { + if (!GetBootImageAddressRange(cache_filename_, &image_start, &image_end, error_msg)) { return false; } if (locations.size() > 1u) { @@ -1434,19 +1334,16 @@ class ImageSpace::BootImageLoader { return false; } uint32_t dummy; - if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, &oat_end, error_msg)) { + if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, error_msg)) { return false; } } MemMap image_reservation; - MemMap oat_reservation; MemMap local_extra_reservation; - if (!ReserveBootImageMemory(image_start, - image_end, - oat_end, + if (!ReserveBootImageMemory(/*reservation_size=*/ image_end - image_start, + image_start, extra_reservation_size, &image_reservation, - &oat_reservation, &local_extra_reservation, error_msg)) { return false; @@ -1459,28 +1356,28 @@ class ImageSpace::BootImageLoader { if (!GetDalvikCacheFilename(location.c_str(), dalvik_cache_.c_str(), &filename, error_msg)) { return false; } - spaces.push_back(Load(location, - filename, - validate_oat_file, - &logger, - &image_reservation, - &oat_reservation, - error_msg)); + spaces.push_back(Load(location, filename, &logger, &image_reservation, error_msg)); if (spaces.back() == nullptr) { return false; } } - if (!CheckReservationsExhausted(image_reservation, oat_reservation, error_msg)) { + for (std::unique_ptr<ImageSpace>& space : spaces) { + if (!OpenOatFile(space.get(), validate_oat_file, &logger, &image_reservation, error_msg)) { + return false; + } + } + if (!CheckReservationExhausted(image_reservation, error_msg)) { return false; } MaybeRelocateSpaces(spaces, &logger); InitRuntimeMethods(spaces); - *extra_reservation = std::move(local_extra_reservation); boot_image_spaces->swap(spaces); + *extra_reservation = std::move(local_extra_reservation); - VLOG(image) << "ImageSpace::BootImageLoader::InitFromDalvikCache exiting " << *spaces.front(); if (VLOG_IS_ON(image)) { + LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromDalvikCache exiting " + << boot_image_spaces->front(); logger.Dump(LOG_STREAM(INFO)); } return true; @@ -2013,10 +1910,8 @@ class ImageSpace::BootImageLoader { std::unique_ptr<ImageSpace> Load(const std::string& image_location, const std::string& image_filename, - bool validate_oat_file, TimingLogger* logger, /*inout*/MemMap* image_reservation, - /*inout*/MemMap* oat_reservation, /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) { // Should this be a RDWR lock? This is only a defensive measure, as at @@ -2045,14 +1940,80 @@ class ImageSpace::BootImageLoader { // file name. return Loader::Init(image_filename.c_str(), image_location.c_str(), - validate_oat_file, /*oat_file=*/ nullptr, logger, image_reservation, - oat_reservation, error_msg); } + bool OpenOatFile(ImageSpace* space, + bool validate_oat_file, + TimingLogger* logger, + /*inout*/MemMap* image_reservation, + /*out*/std::string* error_msg) { + // VerifyImageAllocations() will be called later in Runtime::Init() + // as some class roots like ArtMethod::java_lang_reflect_ArtMethod_ + // and ArtField::java_lang_reflect_ArtField_, which are used from + // Object::SizeOf() which VerifyImageAllocations() calls, are not + // set yet at this point. + DCHECK(image_reservation != nullptr); + std::unique_ptr<OatFile> oat_file; + { + TimingLogger::ScopedTiming timing("OpenOatFile", logger); + std::string oat_filename = + ImageHeader::GetOatLocationFromImageLocation(space->GetImageFilename()); + + oat_file.reset(OatFile::Open(/*zip_fd=*/ -1, + oat_filename, + oat_filename, + !Runtime::Current()->IsAotCompiler(), + /*low_4gb=*/ false, + /*abs_dex_location=*/ nullptr, + image_reservation, + error_msg)); + if (oat_file == nullptr) { + *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s", + oat_filename.c_str(), + space->GetName(), + error_msg->c_str()); + return false; + } + const ImageHeader& image_header = space->GetImageHeader(); + uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum(); + uint32_t image_oat_checksum = image_header.GetOatChecksum(); + if (oat_checksum != image_oat_checksum) { + *error_msg = StringPrintf("Failed to match oat file checksum 0x%x to expected oat checksum" + " 0x%x in image %s", + oat_checksum, + image_oat_checksum, + space->GetName()); + return false; + } + ptrdiff_t relocation_diff = space->Begin() - image_header.GetImageBegin(); + CHECK(image_header.GetOatDataBegin() != nullptr); + uint8_t* oat_data_begin = image_header.GetOatDataBegin() + relocation_diff; + if (oat_file->Begin() != oat_data_begin) { + *error_msg = StringPrintf("Oat file '%s' referenced from image %s has unexpected begin" + " %p v. %p", + oat_filename.c_str(), + space->GetName(), + oat_file->Begin(), + oat_data_begin); + return false; + } + } + if (validate_oat_file) { + TimingLogger::ScopedTiming timing("ValidateOatFile", logger); + if (!ImageSpace::ValidateOatFile(*oat_file, error_msg)) { + DCHECK(!error_msg->empty()); + return false; + } + } + space->oat_file_ = std::move(oat_file); + space->oat_file_non_owned_ = space->oat_file_.get(); + return true; + } + // Extract boot class path from oat file associated with `image_filename` // and list all associated image locations. static bool GetBootClassPathImageLocations(const std::string& image_location, @@ -2087,7 +2048,6 @@ class ImageSpace::BootImageLoader { bool GetBootImageAddressRange(const std::string& filename, /*out*/uint32_t* start, /*out*/uint32_t* end, - /*out*/uint32_t* oat_end, /*out*/std::string* error_msg) { ImageHeader system_hdr; if (!ReadSpecificImageHeader(filename.c_str(), &system_hdr)) { @@ -2096,22 +2056,19 @@ class ImageSpace::BootImageLoader { } *start = reinterpret_cast32<uint32_t>(system_hdr.GetImageBegin()); CHECK_ALIGNED(*start, kPageSize); - *end = RoundUp(*start + system_hdr.GetImageSize(), kPageSize); - *oat_end = RoundUp(reinterpret_cast32<uint32_t>(system_hdr.GetOatFileEnd()), kPageSize); + *end = RoundUp(reinterpret_cast32<uint32_t>(system_hdr.GetOatFileEnd()), kPageSize); return true; } - bool ReserveBootImageMemory(uint32_t image_start, - uint32_t image_end, - uint32_t oat_end, + bool ReserveBootImageMemory(uint32_t reservation_size, + uint32_t image_start, size_t extra_reservation_size, /*out*/MemMap* image_reservation, - /*out*/MemMap* oat_reservation, /*out*/MemMap* extra_reservation, /*out*/std::string* error_msg) { DCHECK(!image_reservation->IsValid()); - size_t total_size = - dchecked_integral_cast<size_t>(oat_end - image_start) + extra_reservation_size; + DCHECK_LT(extra_reservation_size, std::numeric_limits<uint32_t>::max() - reservation_size); + size_t total_size = reservation_size + extra_reservation_size; bool relocate = Runtime::Current()->ShouldRelocate(); // If relocating, choose a random address for ALSR. uint32_t addr = relocate ? ART_BASE_ADDRESS + ChooseRelocationOffsetDelta() : image_start; @@ -2140,37 +2097,17 @@ class ImageSpace::BootImageLoader { return false; } } - uint32_t diff = reinterpret_cast32<uint32_t>(image_reservation->Begin()) - image_start; - image_start += diff; - image_end += diff; - oat_end += diff; - DCHECK(!oat_reservation->IsValid()); - *oat_reservation = image_reservation->RemapAtEnd(reinterpret_cast32<uint8_t*>(image_end), - "Boot image oat reservation", - PROT_NONE, - error_msg); - if (!oat_reservation->IsValid()) { - return false; - } return true; } - bool CheckReservationsExhausted(const MemMap& image_reservation, - const MemMap& oat_reservation, - /*out*/std::string* error_msg) { + bool CheckReservationExhausted(const MemMap& image_reservation, /*out*/std::string* error_msg) { if (image_reservation.IsValid()) { *error_msg = StringPrintf("Excessive image reservation after loading boot image: %p-%p", image_reservation.Begin(), image_reservation.End()); return false; } - if (oat_reservation.IsValid()) { - *error_msg = StringPrintf("Excessive oat reservation after loading boot image: %p-%p", - image_reservation.Begin(), - image_reservation.End()); - return false; - } return true; } @@ -2374,12 +2311,11 @@ ImageSpace::~ImageSpace() { std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(const char* image, const OatFile* oat_file, std::string* error_msg) { + // Note: The oat file has already been validated. return Loader::InitAppImage(image, image, - /*validate_oat_file=*/ false, oat_file, /*image_reservation=*/ nullptr, - /*oat_reservation=*/ nullptr, error_msg); } diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index 188c5f353b..d3df7fd38d 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -21,7 +21,6 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/dumpable.h" -#include "base/sdk_version.h" #include "dex/class_accessor-inl.h" #include "scoped_thread_state_change.h" #include "thread-inl.h" @@ -76,21 +75,6 @@ enum AccessContextFlags { kAccessDenied = 1 << 1, }; -static SdkVersion GetMaxAllowedSdkVersionForApiList(ApiList api_list) { - switch (api_list) { - case ApiList::kWhitelist: - case ApiList::kLightGreylist: - return SdkVersion::kMax; - case ApiList::kDarkGreylist: - return SdkVersion::kO_MR1; - case ApiList::kBlacklist: - return SdkVersion::kMin; - case ApiList::kNoList: - LOG(FATAL) << "Unexpected value"; - UNREACHABLE(); - } -} - MemberSignature::MemberSignature(ArtField* field) { class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_); member_name_ = field->GetName(); @@ -264,7 +248,7 @@ uint32_t GetDexFlags(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) { } uint32_t flags = kInvalidDexFlags; - DCHECK(!AreValidFlags(flags)); + DCHECK(!AreValidDexFlags(flags)); ClassAccessor accessor(declaring_class->GetDexFile(), *class_def, @@ -277,7 +261,7 @@ uint32_t GetDexFlags(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) { accessor.VisitFields(fn_visit, fn_visit); CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for field " << field->PrettyField(); - DCHECK(AreValidFlags(flags)); + DCHECK(AreValidDexFlags(flags)); return flags; } @@ -294,7 +278,7 @@ uint32_t GetDexFlags(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { } uint32_t flags = kInvalidDexFlags; - DCHECK(!AreValidFlags(flags)); + DCHECK(!AreValidDexFlags(flags)); ClassAccessor accessor(declaring_class->GetDexFile(), *class_def, @@ -307,7 +291,7 @@ uint32_t GetDexFlags(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { accessor.VisitMethods(fn_visit, fn_visit); CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for method " << method->PrettyMethod(); - DCHECK(AreValidFlags(flags)); + DCHECK(AreValidDexFlags(flags)); return flags; } @@ -323,7 +307,7 @@ bool ShouldDenyAccessToMemberImpl(T* member, const bool deny_access = (policy == EnforcementPolicy::kEnabled) && IsSdkVersionSetAndMoreThan(runtime->GetTargetSdkVersion(), - GetMaxAllowedSdkVersionForApiList(api_list)); + api_list.GetMaxAllowedSdkVersion()); MemberSignature member_signature(member); diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 32bae1127b..eea58e9880 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -19,8 +19,8 @@ #include "art_field.h" #include "art_method.h" +#include "base/hiddenapi_flags.h" #include "base/mutex.h" -#include "dex/hidden_api_access_flags.h" #include "intrinsics_enum.h" #include "mirror/class-inl.h" #include "reflection.h" @@ -177,10 +177,10 @@ ALWAYS_INLINE inline uint32_t CreateRuntimeFlags(const ClassAccessor::BaseItem& uint32_t runtime_flags = 0u; uint32_t dex_flags = member.GetHiddenapiFlags(); - DCHECK(AreValidFlags(dex_flags)); + DCHECK(AreValidDexFlags(dex_flags)); - ApiList api_list = static_cast<hiddenapi::ApiList>(dex_flags); - if (api_list == ApiList::kWhitelist) { + ApiList api_list = ApiList::FromDexFlags(dex_flags); + if (api_list == ApiList::Whitelist()) { runtime_flags |= kAccPublicApi; } @@ -316,7 +316,7 @@ inline bool ShouldDenyAccessToMember(T* member, // Decode hidden API access flags from the dex file. // This is an O(N) operation scaling with the number of fields/methods // in the class. Only do this on slow path and only do it once. - ApiList api_list = static_cast<hiddenapi::ApiList>(detail::GetDexFlags(member)); + ApiList api_list = ApiList::FromDexFlags(detail::GetDexFlags(member)); // Member is hidden and caller is not exempted. Enter slow path. return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method); diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 314d878c66..520dc6d935 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -101,30 +101,32 @@ TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) { 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); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), false); runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn); - 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); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), false); runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); - runtime_->SetTargetSdkVersion(static_cast<uint32_t>(SdkVersion::kO_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_->SetTargetSdkVersion( + static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxO().GetMaxAllowedSdkVersion())); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), true); runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); - runtime_->SetTargetSdkVersion(static_cast<uint32_t>(SdkVersion::kP)); - 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); + runtime_->SetTargetSdkVersion( + static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxO().GetMaxAllowedSdkVersion()) + 1); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), true); + ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), true); } TEST_F(HiddenApiTest, CheckMembersRead) { diff --git a/runtime/image.cc b/runtime/image.cc index 59ac283779..3023cefd66 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '7', '\0' }; // Added CRC32 intrinsic +const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '9', '\0' }; // Remove boot oat extents. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, @@ -39,13 +39,12 @@ ImageHeader::ImageHeader(uint32_t image_begin, uint32_t oat_file_end, uint32_t boot_image_begin, uint32_t boot_image_size, - uint32_t boot_oat_begin, - uint32_t boot_oat_size, uint32_t pointer_size, StorageMode storage_mode, size_t data_size) : image_begin_(image_begin), image_size_(image_size), + image_checksum_(0u), oat_checksum_(oat_checksum), oat_file_begin_(oat_file_begin), oat_data_begin_(oat_data_begin), @@ -53,9 +52,6 @@ ImageHeader::ImageHeader(uint32_t image_begin, oat_file_end_(oat_file_end), boot_image_begin_(boot_image_begin), boot_image_size_(boot_image_size), - boot_oat_begin_(boot_oat_begin), - boot_oat_size_(boot_oat_size), - patch_delta_(0), image_roots_(image_roots), pointer_size_(pointer_size), storage_mode_(storage_mode), @@ -79,7 +75,6 @@ void ImageHeader::RelocateImage(int64_t delta) { oat_data_begin_ += delta; oat_data_end_ += delta; oat_file_end_ += delta; - patch_delta_ += delta; RelocateImageObjects(delta); RelocateImageMethods(delta); } @@ -115,9 +110,6 @@ bool ImageHeader::IsValid() const { if (oat_file_begin_ >= oat_data_begin_) { return false; } - if (!IsAligned<kPageSize>(patch_delta_)) { - return false; - } return true; } diff --git a/runtime/image.h b/runtime/image.h index d925956f7d..f33b9b2a2e 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -93,23 +93,7 @@ class PACKED(4) ImageHeader { }; static constexpr StorageMode kDefaultStorageMode = kStorageModeUncompressed; - ImageHeader() - : image_begin_(0U), - image_size_(0U), - oat_checksum_(0U), - oat_file_begin_(0U), - oat_data_begin_(0U), - oat_data_end_(0U), - oat_file_end_(0U), - boot_image_begin_(0U), - boot_image_size_(0U), - boot_oat_begin_(0U), - boot_oat_size_(0U), - patch_delta_(0), - image_roots_(0U), - pointer_size_(0U), - storage_mode_(kDefaultStorageMode), - data_size_(0) {} + ImageHeader() {} ImageHeader(uint32_t image_begin, uint32_t image_size, @@ -122,8 +106,6 @@ class PACKED(4) ImageHeader { uint32_t oat_file_end, uint32_t boot_image_begin, uint32_t boot_image_size, - uint32_t boot_oat_begin, - uint32_t boot_oat_size, uint32_t pointer_size, StorageMode storage_mode, size_t data_size); @@ -136,7 +118,15 @@ class PACKED(4) ImageHeader { } size_t GetImageSize() const { - return static_cast<uint32_t>(image_size_); + return image_size_; + } + + uint32_t GetImageChecksum() const { + return image_checksum_; + } + + void SetImageChecksum(uint32_t image_checksum) { + image_checksum_ = image_checksum; } uint32_t GetOatChecksum() const { @@ -171,14 +161,6 @@ class PACKED(4) ImageHeader { return pointer_size_; } - int32_t GetPatchDelta() const { - return patch_delta_; - } - - void SetPatchDelta(int32_t patch_delta) { - patch_delta_ = patch_delta; - } - static std::string GetOatLocationFromImageLocation(const std::string& image) { return GetLocationFromImageLocation(image, "oat"); } @@ -322,14 +304,6 @@ class PACKED(4) ImageHeader { return boot_image_size_; } - uint32_t GetBootOatBegin() const { - return boot_oat_begin_; - } - - uint32_t GetBootOatSize() const { - return boot_oat_size_; - } - StorageMode GetStorageMode() const { return storage_mode_; } @@ -390,45 +364,39 @@ class PACKED(4) ImageHeader { uint8_t version_[4]; // Required base address for mapping the image. - uint32_t image_begin_; + uint32_t image_begin_ = 0u; // Image size, not page aligned. - uint32_t image_size_; + uint32_t image_size_ = 0u; + + // Image file checksum (calculated with the checksum field set to 0). + uint32_t image_checksum_ = 0u; // Checksum of the oat file we link to for load time sanity check. - uint32_t oat_checksum_; + uint32_t oat_checksum_ = 0u; // Start address for oat file. Will be before oat_data_begin_ for .so files. - uint32_t oat_file_begin_; + uint32_t oat_file_begin_ = 0u; // Required oat address expected by image Method::GetCode() pointers. - uint32_t oat_data_begin_; + uint32_t oat_data_begin_ = 0u; // End of oat data address range for this image file. - uint32_t oat_data_end_; + uint32_t oat_data_end_ = 0u; // End of oat file address range. will be after oat_data_end_ for // .so files. Used for positioning a following alloc spaces. - uint32_t oat_file_end_; + uint32_t oat_file_end_ = 0u; // Boot image begin and end (app image headers only). - uint32_t boot_image_begin_; - uint32_t boot_image_size_; - - // Boot oat begin and end (app image headers only). - uint32_t boot_oat_begin_; - uint32_t boot_oat_size_; - - // TODO: We should probably insert a boot image checksum for app images. - - // The total delta that this image has been patched. - int32_t patch_delta_; + uint32_t boot_image_begin_ = 0u; + uint32_t boot_image_size_ = 0u; // Includes heap (*.art) and code (.oat). // Absolute address of an Object[] of objects needed to reinitialize from an image. - uint32_t image_roots_; + uint32_t image_roots_ = 0u; // Pointer size, this affects the size of the ArtMethods. - uint32_t pointer_size_; + uint32_t pointer_size_ = 0u; // Image section sizes/offsets correspond to the uncompressed form. ImageSection sections_[kSectionCount]; @@ -437,11 +405,11 @@ class PACKED(4) ImageHeader { uint64_t image_methods_[kImageMethodsCount]; // Storage method for the image, the image may be compressed. - StorageMode storage_mode_; + StorageMode storage_mode_ = kDefaultStorageMode; // Data size for the image data excluding the bitmap and the header. For compressed images, this // is the compressed size in the file. - uint32_t data_size_; + uint32_t data_size_ = 0u; friend class linker::ImageWriter; }; diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index de262a8b82..ff39a66906 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -1837,8 +1837,10 @@ void* JitCodeCache::MoreCore(const void* mspace, intptr_t increment) NO_THREAD_S void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_locations, std::vector<ProfileMethodInfo>& methods) { + Thread* self = Thread::Current(); + WaitUntilInlineCacheAccessible(self); + MutexLock mu(self, lock_); ScopedTrace trace(__FUNCTION__); - MutexLock mu(Thread::Current(), lock_); uint16_t jit_compile_threshold = Runtime::Current()->GetJITOptions()->GetCompileThreshold(); for (const ProfilingInfo* info : profiling_infos_) { ArtMethod* method = info->GetMethod(); diff --git a/runtime/oat.cc b/runtime/oat.cc index 519eed73dc..e931b28a31 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -17,7 +17,6 @@ #include "oat.h" #include <string.h> -#include <zlib.h> #include "android-base/stringprintf.h" @@ -68,7 +67,7 @@ OatHeader::OatHeader(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, uint32_t dex_file_count, const SafeMap<std::string, std::string>* variable_data) - : adler32_checksum_(adler32(0L, Z_NULL, 0)), + : oat_checksum_(0u), instruction_set_(instruction_set), instruction_set_features_bitmap_(instruction_set_features->AsBitmap()), dex_file_count_(dex_file_count), @@ -81,7 +80,7 @@ OatHeader::OatHeader(InstructionSet instruction_set, quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0), quick_to_interpreter_bridge_offset_(0), - image_file_location_oat_checksum_(0) { + boot_image_checksum_(0) { // Don't want asserts in header as they would be checked in each file that includes it. But the // fields are private, so we check inside a method. static_assert(sizeof(magic_) == sizeof(kOatMagic), @@ -143,47 +142,11 @@ const char* OatHeader::GetMagic() const { uint32_t OatHeader::GetChecksum() const { CHECK(IsValid()); - return adler32_checksum_; + return oat_checksum_; } -void OatHeader::UpdateChecksumWithHeaderData() { - UpdateChecksum(&instruction_set_, sizeof(instruction_set_)); - UpdateChecksum(&instruction_set_features_bitmap_, sizeof(instruction_set_features_bitmap_)); - UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_)); - UpdateChecksum(&image_file_location_oat_checksum_, sizeof(image_file_location_oat_checksum_)); - - // Update checksum for variable data size. - UpdateChecksum(&key_value_store_size_, sizeof(key_value_store_size_)); - - // Update for data, if existing. - if (key_value_store_size_ > 0U) { - UpdateChecksum(&key_value_store_, key_value_store_size_); - } - - UpdateChecksum(&executable_offset_, sizeof(executable_offset_)); - UpdateChecksum(&interpreter_to_interpreter_bridge_offset_, - sizeof(interpreter_to_interpreter_bridge_offset_)); - UpdateChecksum(&interpreter_to_compiled_code_bridge_offset_, - sizeof(interpreter_to_compiled_code_bridge_offset_)); - UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(jni_dlsym_lookup_offset_)); - UpdateChecksum(&quick_generic_jni_trampoline_offset_, - sizeof(quick_generic_jni_trampoline_offset_)); - UpdateChecksum(&quick_imt_conflict_trampoline_offset_, - sizeof(quick_imt_conflict_trampoline_offset_)); - UpdateChecksum(&quick_resolution_trampoline_offset_, - sizeof(quick_resolution_trampoline_offset_)); - UpdateChecksum(&quick_to_interpreter_bridge_offset_, - sizeof(quick_to_interpreter_bridge_offset_)); -} - -void OatHeader::UpdateChecksum(const void* data, size_t length) { - DCHECK(IsValid()); - if (data != nullptr) { - const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data); - adler32_checksum_ = adler32(adler32_checksum_, bytes, length); - } else { - DCHECK_EQ(0U, length); - } +void OatHeader::SetChecksum(uint32_t oat_checksum) { + oat_checksum_ = oat_checksum; } InstructionSet OatHeader::GetInstructionSet() const { @@ -353,14 +316,14 @@ void OatHeader::SetQuickToInterpreterBridgeOffset(uint32_t offset) { quick_to_interpreter_bridge_offset_ = offset; } -uint32_t OatHeader::GetImageFileLocationOatChecksum() const { +uint32_t OatHeader::GetBootImageChecksum() const { CHECK(IsValid()); - return image_file_location_oat_checksum_; + return boot_image_checksum_; } -void OatHeader::SetImageFileLocationOatChecksum(uint32_t image_file_location_oat_checksum) { +void OatHeader::SetBootImageChecksum(uint32_t boot_image_checksum) { CHECK(IsValid()); - image_file_location_oat_checksum_ = image_file_location_oat_checksum; + boot_image_checksum_ = boot_image_checksum; } uint32_t OatHeader::GetKeyValueStoreSize() const { diff --git a/runtime/oat.h b/runtime/oat.h index 3d6415e2a0..ee46f426cf 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -31,8 +31,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Remove interpreter alt tables. - static constexpr uint8_t kOatVersion[] = { '1', '6', '3', '\0' }; + // Last oat version changed reason: Image checksums. + static constexpr uint8_t kOatVersion[] = { '1', '6', '4', '\0' }; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDebuggableKey = "debuggable"; @@ -56,8 +56,7 @@ class PACKED(4) OatHeader { std::string GetValidationErrorMessage() const; const char* GetMagic() const; uint32_t GetChecksum() const; - void UpdateChecksumWithHeaderData(); - void UpdateChecksum(const void* data, size_t length); + void SetChecksum(uint32_t checksum); uint32_t GetDexFileCount() const { DCHECK(IsValid()); return dex_file_count_; @@ -94,8 +93,8 @@ class PACKED(4) OatHeader { InstructionSet GetInstructionSet() const; uint32_t GetInstructionSetFeaturesBitmap() const; - uint32_t GetImageFileLocationOatChecksum() const; - void SetImageFileLocationOatChecksum(uint32_t image_file_location_oat_checksum); + uint32_t GetBootImageChecksum() const; + void SetBootImageChecksum(uint32_t boot_image_checksum); uint32_t GetKeyValueStoreSize() const; const uint8_t* GetKeyValueStore() const; @@ -123,7 +122,7 @@ class PACKED(4) OatHeader { uint8_t magic_[4]; uint8_t version_[4]; - uint32_t adler32_checksum_; + uint32_t oat_checksum_; InstructionSet instruction_set_; uint32_t instruction_set_features_bitmap_; @@ -138,7 +137,7 @@ class PACKED(4) OatHeader { uint32_t quick_resolution_trampoline_offset_; uint32_t quick_to_interpreter_bridge_offset_; - uint32_t image_file_location_oat_checksum_; + uint32_t boot_image_checksum_; uint32_t key_value_store_size_; uint8_t key_value_store_[0]; // note variable width data at end diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index af87637d57..c1ccd33774 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -33,6 +33,7 @@ #include "android/dlext.h" #endif +#include <android-base/logging.h> #include "android-base/stringprintf.h" #include "art_method.h" @@ -467,6 +468,7 @@ static void DCheckIndexToBssMapping(OatFile* oat_file, } prev_entry = &entry; } + CHECK(prev_entry != nullptr); CHECK_LT(prev_entry->GetIndex(index_bits), number_of_indexes); } } @@ -1755,11 +1757,15 @@ OatDexFile::OatDexFile(const OatFile* oat_file, } } -OatDexFile::OatDexFile(TypeLookupTable&& lookup_table) : lookup_table_(std::move(lookup_table)) {} +OatDexFile::OatDexFile(TypeLookupTable&& lookup_table) : lookup_table_(std::move(lookup_table)) { + // Stripped-down OatDexFile only allowed in the compiler. + CHECK(Runtime::Current() == nullptr || Runtime::Current()->IsAotCompiler()); +} OatDexFile::~OatDexFile() {} size_t OatDexFile::FileSize() const { + DCHECK(dex_file_pointer_ != nullptr); return reinterpret_cast<const DexFile::Header*>(dex_file_pointer_)->file_size_; } @@ -1779,6 +1785,7 @@ std::unique_ptr<const DexFile> OatDexFile::OpenDexFile(std::string* error_msg) c } uint32_t OatDexFile::GetOatClassOffset(uint16_t class_def_index) const { + DCHECK(oat_class_offsets_pointer_ != nullptr); return oat_class_offsets_pointer_[class_def_index]; } diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index a06be4c719..6f32b98466 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -419,7 +419,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& // starts up. LOG(WARNING) << "Dex location " << dex_location_ << " does not seem to include dex file. " << "Allow oat file use. This is potentially dangerous."; - } else if (file.GetOatHeader().GetImageFileLocationOatChecksum() != image_info->oat_checksum) { + } else if (file.GetOatHeader().GetBootImageChecksum() != image_info->boot_image_checksum) { VLOG(oat) << "Oat image checksum does not match image checksum."; return kOatBootImageOutOfDate; } @@ -574,8 +574,7 @@ OatFileAssistant::ImageInfo::GetRuntimeImageInfo(InstructionSet isa, std::string return nullptr; } - info->oat_checksum = image_header->GetOatChecksum(); - info->patch_delta = image_header->GetPatchDelta(); + info->boot_image_checksum = image_header->GetImageChecksum(); return info; } diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 590ae2254c..09c9d3b3cb 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -246,8 +246,7 @@ class OatFileAssistant { private: struct ImageInfo { - uint32_t oat_checksum = 0; - int32_t patch_delta = 0; + uint32_t boot_image_checksum = 0; std::string location; static std::unique_ptr<ImageInfo> GetRuntimeImageInfo(InstructionSet isa, diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 7ac1ab40a2..2c882ece05 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -118,9 +118,10 @@ const OatFile* OatFileManager::FindOpenedOatFileFromOatLocationLocked( } std::vector<const OatFile*> OatFileManager::GetBootOatFiles() const { - std::vector<const OatFile*> oat_files; std::vector<gc::space::ImageSpace*> image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); + std::vector<const OatFile*> oat_files; + oat_files.reserve(image_spaces.size()); for (gc::space::ImageSpace* image_space : image_spaces) { oat_files.push_back(image_space->GetOatFile()); } @@ -153,6 +154,7 @@ OatFileManager::~OatFileManager() { std::vector<const OatFile*> OatFileManager::RegisterImageOatFiles( const std::vector<gc::space::ImageSpace*>& spaces) { std::vector<const OatFile*> oat_files; + oat_files.reserve(spaces.size()); for (gc::space::ImageSpace* space : spaces) { oat_files.push_back(RegisterOatFile(space->ReleaseOatFile())); } @@ -290,10 +292,12 @@ static bool CheckClassCollision(const OatFile* oat_file, // Generate type index information for each dex file. std::vector<TypeIndexInfo> loaded_types; + loaded_types.reserve(dex_files_loaded.size()); for (const DexFile* dex_file : dex_files_loaded) { loaded_types.push_back(TypeIndexInfo(dex_file)); } std::vector<TypeIndexInfo> unloaded_types; + unloaded_types.reserve(dex_files_unloaded.size()); for (const DexFile* dex_file : dex_files_unloaded) { unloaded_types.push_back(TypeIndexInfo(dex_file)); } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 292a42429e..d69a2a9230 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -278,6 +278,7 @@ Runtime::Runtime() // Initially assume we perceive jank in case the process state is never updated. process_state_(kProcessStateJankPerceptible), zygote_no_threads_(false), + process_cpu_start_time_(ProcessCpuNanoTime()), verifier_logging_threshold_ms_(100) { static_assert(Runtime::kCalleeSaveSize == static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size"); @@ -321,11 +322,20 @@ Runtime::~Runtime() { } if (dump_gc_performance_on_shutdown_) { + process_cpu_end_time_ = ProcessCpuNanoTime(); ScopedLogSeverity sls(LogSeverity::INFO); // This can't be called from the Heap destructor below because it // could call RosAlloc::InspectAll() which needs the thread_list // to be still alive. heap_->DumpGcPerformanceInfo(LOG_STREAM(INFO)); + + uint64_t process_cpu_time = process_cpu_end_time_ - process_cpu_start_time_; + uint64_t gc_cpu_time = heap_->GetTotalGcCpuTime(); + float ratio = static_cast<float>(gc_cpu_time) / process_cpu_time; + LOG_STREAM(INFO) << "GC CPU time " << PrettyDuration(gc_cpu_time) + << " out of process CPU time " << PrettyDuration(process_cpu_time) + << " (" << ratio << ")" + << "\n"; } if (jit_ != nullptr) { diff --git a/runtime/runtime.h b/runtime/runtime.h index 1d58ad784c..0ccc7b79bf 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -1101,6 +1101,9 @@ class Runtime { MemMap protected_fault_page_; + uint64_t process_cpu_start_time_; + uint64_t process_cpu_end_time_; + uint32_t verifier_logging_threshold_ms_; DISALLOW_COPY_AND_ASSIGN(Runtime); diff --git a/runtime/thread.h b/runtime/thread.h index b304cef74d..ccde23614b 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -17,8 +17,6 @@ #ifndef ART_RUNTIME_THREAD_H_ #define ART_RUNTIME_THREAD_H_ -#include <setjmp.h> - #include <atomic> #include <bitset> #include <deque> diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index bd59e73192..f24711a4a1 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -31,7 +31,6 @@ #include "dex/class_accessor-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" -#include "dex/hidden_api_access_flags.h" #include "dex_to_dex_decompiler.h" #include "quicken_info.h" diff --git a/test/411-checker-hdiv-hrem-pow2/src/DivTest.java b/test/411-checker-hdiv-hrem-pow2/src/DivTest.java index a3882e7c15..1a086ef04d 100644 --- a/test/411-checker-hdiv-hrem-pow2/src/DivTest.java +++ b/test/411-checker-hdiv-hrem-pow2/src/DivTest.java @@ -94,6 +94,9 @@ public class DivTest { /// CHECK-START-ARM64: java.lang.Integer DivTest.$noinline$IntDivBy2(int) disassembly (after) /// CHECK: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 /// CHECK: asr w{{\d+}}, w{{\d+}}, #1 + /// CHECK-START-X86_64: java.lang.Integer DivTest.$noinline$IntDivBy2(int) disassembly (after) + /// CHECK-NOT: cmovnl/geq + /// CHECK: add private static Integer $noinline$IntDivBy2(int v) { int r = v / 2; return r; @@ -102,6 +105,9 @@ public class DivTest { /// CHECK-START-ARM64: java.lang.Integer DivTest.$noinline$IntDivByMinus2(int) disassembly (after) /// CHECK: add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31 /// CHECK: neg w{{\d+}}, w{{\d+}}, asr #1 + /// CHECK-START-X86_64: java.lang.Integer DivTest.$noinline$IntDivByMinus2(int) disassembly (after) + /// CHECK-NOT: cmovnl/geq + /// CHECK: add private static Integer $noinline$IntDivByMinus2(int v) { int r = v / -2; return r; @@ -205,6 +211,9 @@ public class DivTest { /// CHECK-START-ARM64: java.lang.Long DivTest.$noinline$LongDivBy2(long) disassembly (after) /// CHECK: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 /// CHECK: asr x{{\d+}}, x{{\d+}}, #1 + /// CHECK-START-X86_64: java.lang.Long DivTest.$noinline$LongDivBy2(long) disassembly (after) + /// CHECK-NOT: cmovnl/geq + /// CHECK: addq private static Long $noinline$LongDivBy2(long v) { long r = v / 2; return r; @@ -213,6 +222,9 @@ public class DivTest { /// CHECK-START-ARM64: java.lang.Long DivTest.$noinline$LongDivByMinus2(long) disassembly (after) /// CHECK: add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63 /// CHECK: neg x{{\d+}}, x{{\d+}}, asr #1 + /// CHECK-START-X86_64: java.lang.Long DivTest.$noinline$LongDivByMinus2(long) disassembly (after) + /// CHECK-NOT: cmovnl/geq + /// CHECK: addq private static Long $noinline$LongDivByMinus2(long v) { long r = v / -2; return r; diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc index d11aa579e5..246426352f 100644 --- a/test/674-hiddenapi/hiddenapi.cc +++ b/test/674-hiddenapi/hiddenapi.cc @@ -30,7 +30,8 @@ namespace Test674HiddenApi { extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) { Runtime* runtime = Runtime::Current(); runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); - runtime->SetTargetSdkVersion(static_cast<uint32_t>(SdkVersion::kO_MR1)); + runtime->SetTargetSdkVersion( + static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxO().GetMaxAllowedSdkVersion())); runtime->SetDedupeHiddenApiWarnings(false); } diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index b1878133f0..077129fa6c 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -331,10 +331,9 @@ target_config = { }, 'art-linux-bionic-x64': { 'build': '{ANDROID_BUILD_TOP}/art/tools/build_linux_bionic_tests.sh {MAKE_OPTIONS}', - # Currently failing on the build-bots due to some library search path issue. - # 'run-test': ['--run-test-option=--bionic', - # '--host', - # '--64', - # '--no-build-dependencies'], + '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 3ee7d506aa..2b178f2cff 100755 --- a/tools/build_linux_bionic_tests.sh +++ b/tools/build_linux_bionic_tests.sh @@ -29,6 +29,7 @@ fi source build/envsetup.sh >&/dev/null # for get_build_var out_dir=$(get_build_var OUT_DIR) +host_out=$(get_build_var HOST_OUT) # TODO(b/31559095) Figure out a better way to do this. # @@ -80,8 +81,8 @@ bionic_targets=( $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")) + $(find $host_out/lib64 -type f | sed "s:$host_out:$soong_out:g") + $(find $host_out/nativetest64 -type f | sed "s:$host_out:$soong_out:g")) echo building ${bionic_targets[*]} diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc index 65a4945355..7f4c546df5 100644 --- a/tools/hiddenapi/hiddenapi.cc +++ b/tools/hiddenapi/hiddenapi.cc @@ -23,14 +23,14 @@ #include "android-base/strings.h" #include "base/bit_utils.h" -#include "base/stl_util.h" +#include "base/hiddenapi_flags.h" #include "base/mem_map.h" #include "base/os.h" +#include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" #include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" -#include "dex/hidden_api_access_flags.h" namespace art { @@ -39,6 +39,7 @@ static char** original_argv; static std::string CommandLine() { std::vector<std::string> command; + command.reserve(original_argc); for (int i = 0; i < original_argc; ++i) { command.push_back(original_argv[i]); } @@ -600,7 +601,7 @@ class HiddenapiClassDataBuilder final { // between BeginClassDef and EndClassDef in the order of appearance of // fields/methods in the class data stream. void WriteFlags(hiddenapi::ApiList flags) { - uint32_t uint_flags = static_cast<uint32_t>(flags); + uint32_t uint_flags = flags.GetIntValue(); EncodeUnsignedLeb128(&data_, uint_flags); class_def_has_non_zero_flags_ |= (uint_flags != 0u); } @@ -934,9 +935,9 @@ class HiddenApi final { // Load dex signatures. std::map<std::string, hiddenapi::ApiList> api_list; - OpenApiFile(light_greylist_path_, api_list, hiddenapi::ApiList::kLightGreylist); - OpenApiFile(dark_greylist_path_, api_list, hiddenapi::ApiList::kDarkGreylist); - OpenApiFile(blacklist_path_, api_list, hiddenapi::ApiList::kBlacklist); + OpenApiFile(light_greylist_path_, api_list, hiddenapi::ApiList::Greylist()); + OpenApiFile(dark_greylist_path_, api_list, hiddenapi::ApiList::GreylistMaxO()); + OpenApiFile(blacklist_path_, api_list, hiddenapi::ApiList::Blacklist()); // Iterate over input dex files and insert HiddenapiClassData sections. for (size_t i = 0; i < boot_dex_paths_.size(); ++i) { @@ -956,7 +957,7 @@ class HiddenApi final { // TODO: Load whitelist and CHECK that entry was found. auto it = api_list.find(boot_member.GetApiEntry()); builder.WriteFlags( - (it == api_list.end()) ? hiddenapi::ApiList::kWhitelist : it->second); + (it == api_list.end()) ? hiddenapi::ApiList::Whitelist() : it->second); }; auto fn_field = [&](const ClassAccessor::Field& boot_field) { fn_shared(DexMember(boot_class, boot_field)); @@ -987,7 +988,8 @@ class HiddenApi final { for (std::string line; std::getline(api_file, line);) { CHECK(api_list.find(line) == api_list.end()) - << "Duplicate entry: " << line << " (" << api_list[line] << " and " << membership << ")"; + << "Duplicate entry: " << line << " (" << api_list.find(line)->second + << " and " << membership << ")"; api_list.emplace(line, membership); } api_file.close(); diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc index 0010b78436..66ce2de5c5 100644 --- a/tools/hiddenapi/hiddenapi_test.cc +++ b/tools/hiddenapi/hiddenapi_test.cc @@ -132,7 +132,7 @@ class HiddenApiTest : public CommonRuntimeTest { CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data"; if (!accessor.HasHiddenapiClassData()) { - return hiddenapi::ApiList::kWhitelist; + return hiddenapi::ApiList::Whitelist(); } for (const ClassAccessor::Field& field : accessor.GetFields()) { @@ -141,7 +141,7 @@ class HiddenApiTest : public CommonRuntimeTest { const uint32_t actual_visibility = field.GetAccessFlags() & kAccVisibilityFlags; CHECK_EQ(actual_visibility, expected_visibility) << "Field " << name << " in class " << accessor.GetDescriptor(); - return static_cast<hiddenapi::ApiList>(field.GetHiddenapiFlags()); + return hiddenapi::ApiList::FromDexFlags(field.GetHiddenapiFlags()); } } @@ -159,7 +159,7 @@ class HiddenApiTest : public CommonRuntimeTest { CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data"; if (!accessor.HasHiddenapiClassData()) { - return hiddenapi::ApiList::kWhitelist; + return hiddenapi::ApiList::Whitelist(); } for (const ClassAccessor::Method& method : accessor.GetMethods()) { @@ -170,7 +170,7 @@ class HiddenApiTest : public CommonRuntimeTest { const uint32_t actual_visibility = method.GetAccessFlags() & kAccVisibilityFlags; CHECK_EQ(actual_visibility, expected_visibility) << "Method " << name << " in class " << accessor.GetDescriptor(); - return static_cast<hiddenapi::ApiList>(method.GetHiddenapiFlags()); + return hiddenapi::ApiList::FromDexFlags(method.GetHiddenapiFlags()); } } @@ -224,7 +224,7 @@ TEST_F(HiddenApiTest, InstanceFieldNoMatch) { OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetIFieldHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldLightGreylistMatch) { @@ -234,7 +234,7 @@ TEST_F(HiddenApiTest, InstanceFieldLightGreylistMatch) { OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetIFieldHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldDarkGreylistMatch) { @@ -244,7 +244,7 @@ TEST_F(HiddenApiTest, InstanceFieldDarkGreylistMatch) { OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetIFieldHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldBlacklistMatch) { @@ -254,7 +254,7 @@ TEST_F(HiddenApiTest, InstanceFieldBlacklistMatch) { OpenStream(blacklist) << "LMain;->ifield:I" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetIFieldHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch1) { @@ -291,7 +291,7 @@ TEST_F(HiddenApiTest, StaticFieldNoMatch) { OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetSFieldHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticFieldLightGreylistMatch) { @@ -301,7 +301,7 @@ TEST_F(HiddenApiTest, StaticFieldLightGreylistMatch) { OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetSFieldHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticFieldDarkGreylistMatch) { @@ -311,7 +311,7 @@ TEST_F(HiddenApiTest, StaticFieldDarkGreylistMatch) { OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetSFieldHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticFieldBlacklistMatch) { @@ -321,7 +321,7 @@ TEST_F(HiddenApiTest, StaticFieldBlacklistMatch) { OpenStream(blacklist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetSFieldHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticFieldTwoListsMatch1) { @@ -358,7 +358,7 @@ TEST_F(HiddenApiTest, InstanceMethodNoMatch) { OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetIMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceMethodLightGreylistMatch) { @@ -368,7 +368,7 @@ TEST_F(HiddenApiTest, InstanceMethodLightGreylistMatch) { OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetIMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceMethodDarkGreylistMatch) { @@ -378,7 +378,7 @@ TEST_F(HiddenApiTest, InstanceMethodDarkGreylistMatch) { OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetIMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceMethodBlacklistMatch) { @@ -388,7 +388,7 @@ TEST_F(HiddenApiTest, InstanceMethodBlacklistMatch) { OpenStream(blacklist) << "LMain;->imethod(J)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetIMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch1) { @@ -425,7 +425,7 @@ TEST_F(HiddenApiTest, StaticMethodNoMatch) { OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetSMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticMethodLightGreylistMatch) { @@ -435,7 +435,7 @@ TEST_F(HiddenApiTest, StaticMethodLightGreylistMatch) { OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetSMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticMethodDarkGreylistMatch) { @@ -445,7 +445,7 @@ TEST_F(HiddenApiTest, StaticMethodDarkGreylistMatch) { OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetSMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticMethodBlacklistMatch) { @@ -455,7 +455,7 @@ TEST_F(HiddenApiTest, StaticMethodBlacklistMatch) { OpenStream(blacklist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetSMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticMethodTwoListsMatch1) { @@ -492,7 +492,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodNoMatch) { OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetINMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetINMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceNativeMethodLightGreylistMatch) { @@ -502,7 +502,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodLightGreylistMatch) { OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetINMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetINMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceNativeMethodDarkGreylistMatch) { @@ -512,7 +512,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodDarkGreylistMatch) { OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetINMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetINMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceNativeMethodBlacklistMatch) { @@ -522,7 +522,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodBlacklistMatch) { OpenStream(blacklist) << "LMain;->inmethod(C)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetINMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetINMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch1) { @@ -559,7 +559,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodNoMatch) { OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetSNMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSNMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticNativeMethodLightGreylistMatch) { @@ -569,7 +569,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodLightGreylistMatch) { OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetSNMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSNMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticNativeMethodDarkGreylistMatch) { @@ -579,7 +579,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodDarkGreylistMatch) { OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetSNMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSNMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticNativeMethodBlacklistMatch) { @@ -589,7 +589,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodBlacklistMatch) { OpenStream(blacklist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl; auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetSNMethodHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSNMethodHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch1) { diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h index da9f058cb4..b58332bf52 100644 --- a/tools/veridex/hidden_api.h +++ b/tools/veridex/hidden_api.h @@ -17,7 +17,7 @@ #ifndef ART_TOOLS_VERIDEX_HIDDEN_API_H_ #define ART_TOOLS_VERIDEX_HIDDEN_API_H_ -#include "dex/hidden_api_access_flags.h" +#include "base/hiddenapi_flags.h" #include "dex/method_reference.h" #include <ostream> @@ -45,20 +45,20 @@ class HiddenApi { hiddenapi::ApiList GetApiList(const std::string& name) const { if (IsInList(name, blacklist_)) { - return hiddenapi::ApiList::kBlacklist; + return hiddenapi::ApiList::Blacklist(); } else if (IsInList(name, dark_greylist_)) { - return hiddenapi::ApiList::kDarkGreylist; + return hiddenapi::ApiList::GreylistMaxO(); } else if (IsInList(name, light_greylist_)) { - return hiddenapi::ApiList::kLightGreylist; + return hiddenapi::ApiList::Greylist(); } else if (IsInList(name, whitelist_)) { - return hiddenapi::ApiList::kWhitelist; + return hiddenapi::ApiList::Whitelist(); } else { - return hiddenapi::ApiList::kNoList; + return hiddenapi::ApiList::Invalid(); } } bool IsInAnyList(const std::string& name) const { - return GetApiList(name) != hiddenapi::ApiList::kNoList; + return GetApiList(name).IsValid(); } static std::string GetApiMethodName(const DexFile& dex_file, uint32_t method_index); @@ -92,7 +92,7 @@ struct HiddenApiStats { uint32_t count = 0; uint32_t reflection_count = 0; uint32_t linking_count = 0; - uint32_t api_counts[5] = { 0, 0, 0, 0, 0 }; + uint32_t api_counts[hiddenapi::ApiList::kValueCount] = {}; // initialize all to zero }; } // namespace art diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc index e24d151069..3cd7c95a23 100644 --- a/tools/veridex/hidden_api_finder.cc +++ b/tools/veridex/hidden_api_finder.cc @@ -180,7 +180,7 @@ void HiddenApiFinder::Dump(std::ostream& os, for (const std::pair<const std::string, std::vector<MethodReference>>& pair : method_locations_) { hiddenapi::ApiList api_list = hidden_api_.GetApiList(pair.first); - stats->api_counts[static_cast<unsigned>(api_list)]++; + stats->api_counts[api_list.GetIntValue()]++; os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):"; os << std::endl; HiddenApiFinder::DumpReferences(os, pair.second); @@ -191,7 +191,7 @@ void HiddenApiFinder::Dump(std::ostream& os, for (const std::pair<const std::string, std::vector<MethodReference>>& pair : field_locations_) { hiddenapi::ApiList api_list = hidden_api_.GetApiList(pair.first); - stats->api_counts[static_cast<unsigned>(api_list)]++; + stats->api_counts[api_list.GetIntValue()]++; os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):"; os << std::endl; HiddenApiFinder::DumpReferences(os, pair.second); @@ -204,8 +204,8 @@ void HiddenApiFinder::Dump(std::ostream& os, for (const std::string& name : strings_) { std::string full_name = cls + "->" + name; hiddenapi::ApiList api_list = hidden_api_.GetApiList(full_name); - stats->api_counts[static_cast<unsigned>(api_list)]++; - if (api_list != hiddenapi::ApiList::kNoList) { + if (api_list.IsValid()) { + stats->api_counts[api_list.GetIntValue()]++; stats->reflection_count++; os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name << " potential use(s):"; diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc index 6aef89f7ee..be99ed29d4 100644 --- a/tools/veridex/precise_hidden_api_finder.cc +++ b/tools/veridex/precise_hidden_api_finder.cc @@ -91,8 +91,7 @@ void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) { std::string cls(info.cls.ToString()); std::string name(info.name.ToString()); std::string full_name = cls + "->" + name; - hiddenapi::ApiList api_list = hidden_api_.GetApiList(full_name); - if (api_list != hiddenapi::ApiList::kNoList) { + if (hidden_api_.IsInAnyList(full_name)) { named_uses[full_name].push_back(ref); } } @@ -102,7 +101,7 @@ void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) { ++stats->reflection_count; const std::string& full_name = it.first; hiddenapi::ApiList api_list = hidden_api_.GetApiList(full_name); - stats->api_counts[static_cast<unsigned>(api_list)]++; + stats->api_counts[api_list.GetIntValue()]++; os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name << " use(s):"; os << std::endl; for (const MethodReference& ref : it.second) { diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index 179e391219..2787950323 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -271,17 +271,17 @@ class Veridex { const VeridexOptions& options) { static const char* kPrefix = " "; if (options.only_report_sdk_uses) { - os << stats.api_counts[static_cast<unsigned>(hiddenapi::ApiList::kWhitelist)] + os << stats.api_counts[hiddenapi::ApiList::Whitelist().GetIntValue()] << " SDK API uses." << std::endl; } else { os << stats.count << " hidden API(s) used: " << stats.linking_count << " linked against, " << stats.reflection_count << " through reflection" << std::endl; - os << kPrefix << stats.api_counts[static_cast<unsigned>(hiddenapi::ApiList::kBlacklist)] + os << kPrefix << stats.api_counts[hiddenapi::ApiList::Blacklist().GetIntValue()] << " in blacklist" << std::endl; - os << kPrefix << stats.api_counts[static_cast<unsigned>(hiddenapi::ApiList::kDarkGreylist)] + os << kPrefix << stats.api_counts[hiddenapi::ApiList::GreylistMaxO().GetIntValue()] << " in dark greylist" << std::endl; - os << kPrefix << stats.api_counts[static_cast<unsigned>(hiddenapi::ApiList::kLightGreylist)] + os << kPrefix << stats.api_counts[hiddenapi::ApiList::Greylist().GetIntValue()] << " in light greylist" << std::endl; } } |