diff options
51 files changed, 1147 insertions, 488 deletions
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index 29f004cf87..ef9d919c75 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -17,21 +17,20 @@ #include "compiled_method.h" #include "driver/compiled_method_storage.h" -#include "driver/compiler_driver.h" #include "utils/swap_space.h" namespace art { -CompiledCode::CompiledCode(CompilerDriver* compiler_driver, +CompiledCode::CompiledCode(CompiledMethodStorage* storage, InstructionSet instruction_set, const ArrayRef<const uint8_t>& quick_code) - : compiler_driver_(compiler_driver), - quick_code_(compiler_driver_->GetCompiledMethodStorage()->DeduplicateCode(quick_code)), + : storage_(storage), + quick_code_(storage->DeduplicateCode(quick_code)), packed_fields_(InstructionSetField::Encode(instruction_set)) { } CompiledCode::~CompiledCode() { - compiler_driver_->GetCompiledMethodStorage()->ReleaseCode(quick_code_); + GetStorage()->ReleaseCode(quick_code_); } bool CompiledCode::operator==(const CompiledCode& rhs) const { @@ -99,29 +98,29 @@ const void* CompiledCode::CodePointer(const void* code_pointer, InstructionSet i } } -CompiledMethod::CompiledMethod(CompilerDriver* driver, +CompiledMethod::CompiledMethod(CompiledMethodStorage* storage, InstructionSet instruction_set, const ArrayRef<const uint8_t>& quick_code, const ArrayRef<const uint8_t>& vmap_table, const ArrayRef<const uint8_t>& cfi_info, const ArrayRef<const linker::LinkerPatch>& patches) - : CompiledCode(driver, instruction_set, quick_code), - vmap_table_(driver->GetCompiledMethodStorage()->DeduplicateVMapTable(vmap_table)), - cfi_info_(driver->GetCompiledMethodStorage()->DeduplicateCFIInfo(cfi_info)), - patches_(driver->GetCompiledMethodStorage()->DeduplicateLinkerPatches(patches)) { + : CompiledCode(storage, instruction_set, quick_code), + vmap_table_(storage->DeduplicateVMapTable(vmap_table)), + cfi_info_(storage->DeduplicateCFIInfo(cfi_info)), + patches_(storage->DeduplicateLinkerPatches(patches)) { } CompiledMethod* CompiledMethod::SwapAllocCompiledMethod( - CompilerDriver* driver, + CompiledMethodStorage* storage, InstructionSet instruction_set, const ArrayRef<const uint8_t>& quick_code, const ArrayRef<const uint8_t>& vmap_table, const ArrayRef<const uint8_t>& cfi_info, const ArrayRef<const linker::LinkerPatch>& patches) { - SwapAllocator<CompiledMethod> alloc(driver->GetCompiledMethodStorage()->GetSwapSpaceAllocator()); + SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator()); CompiledMethod* ret = alloc.allocate(1); alloc.construct(ret, - driver, + storage, instruction_set, quick_code, vmap_table, @@ -129,14 +128,15 @@ CompiledMethod* CompiledMethod::SwapAllocCompiledMethod( return ret; } -void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m) { - SwapAllocator<CompiledMethod> alloc(driver->GetCompiledMethodStorage()->GetSwapSpaceAllocator()); +void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage, + CompiledMethod* m) { + SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator()); alloc.destroy(m); alloc.deallocate(m, 1); } CompiledMethod::~CompiledMethod() { - CompiledMethodStorage* storage = GetCompilerDriver()->GetCompiledMethodStorage(); + CompiledMethodStorage* storage = GetStorage(); storage->ReleaseLinkerPatches(patches_); storage->ReleaseCFIInfo(cfi_info_); storage->ReleaseVMapTable(vmap_table_); diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 864ce585cf..75790c9f08 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -28,7 +28,6 @@ namespace art { template <typename T> class ArrayRef; -class CompilerDriver; class CompiledMethodStorage; template<typename T> class LengthPrefixedArray; @@ -39,7 +38,7 @@ class LinkerPatch; class CompiledCode { public: // For Quick to supply an code blob - CompiledCode(CompilerDriver* compiler_driver, + CompiledCode(CompiledMethodStorage* storage, InstructionSet instruction_set, const ArrayRef<const uint8_t>& quick_code); @@ -78,8 +77,8 @@ class CompiledCode { template <typename T> static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array); - CompilerDriver* GetCompilerDriver() { - return compiler_driver_; + CompiledMethodStorage* GetStorage() { + return storage_; } template <typename BitFieldType> @@ -96,7 +95,7 @@ class CompiledCode { private: using InstructionSetField = BitField<InstructionSet, 0u, kInstructionSetFieldSize>; - CompilerDriver* const compiler_driver_; + CompiledMethodStorage* const storage_; // Used to store the compiled code. const LengthPrefixedArray<uint8_t>* const quick_code_; @@ -109,7 +108,7 @@ class CompiledMethod final : public CompiledCode { // Constructs a CompiledMethod. // Note: Consider using the static allocation methods below that will allocate the CompiledMethod // in the swap space. - CompiledMethod(CompilerDriver* driver, + CompiledMethod(CompiledMethodStorage* storage, InstructionSet instruction_set, const ArrayRef<const uint8_t>& quick_code, const ArrayRef<const uint8_t>& vmap_table, @@ -119,14 +118,14 @@ class CompiledMethod final : public CompiledCode { virtual ~CompiledMethod(); static CompiledMethod* SwapAllocCompiledMethod( - CompilerDriver* driver, + CompiledMethodStorage* storage, InstructionSet instruction_set, const ArrayRef<const uint8_t>& quick_code, const ArrayRef<const uint8_t>& vmap_table, const ArrayRef<const uint8_t>& cfi_info, const ArrayRef<const linker::LinkerPatch>& patches); - static void ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m); + static void ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage, CompiledMethod* m); bool IsIntrinsic() const { return GetPackedField<IsIntrinsicField>(); diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index fe99eaa51a..04ad10c41e 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -614,7 +614,7 @@ CompiledMethod* DexToDexCompiler::CompileMethod( instruction_set = InstructionSet::kArm; } CompiledMethod* ret = CompiledMethod::SwapAllocCompiledMethod( - driver_, + driver_->GetCompiledMethodStorage(), instruction_set, ArrayRef<const uint8_t>(), // no code ArrayRef<const uint8_t>(quicken_data), // vmap_table @@ -665,7 +665,8 @@ void DexToDexCompiler::UnquickenConflictingMethods() { // There is up to one compiled method for each method ref. Releasing it leaves the // deduped data intact, this means its safe to do even when other threads might be // compiling. - CompiledMethod::ReleaseSwapAllocatedCompiledMethod(driver_, method); + CompiledMethod::ReleaseSwapAllocatedCompiledMethod(driver_->GetCompiledMethodStorage(), + method); } } } diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc index 8b35bd3cb1..9fac2bc98b 100644 --- a/compiler/driver/compiled_method_storage_test.cc +++ b/compiler/driver/compiled_method_storage_test.cc @@ -19,24 +19,13 @@ #include <gtest/gtest.h> #include "compiled_method-inl.h" -#include "compiler_driver.h" -#include "compiler_options.h" -#include "dex/verification_results.h" namespace art { TEST(CompiledMethodStorage, Deduplicate) { - CompilerOptions compiler_options; - VerificationResults verification_results(&compiler_options); - CompilerDriver driver(&compiler_options, - &verification_results, - Compiler::kOptimizing, - /* image_classes */ nullptr, - /* thread_count */ 1u, - /* swap_fd */ -1); - CompiledMethodStorage* storage = driver.GetCompiledMethodStorage(); + CompiledMethodStorage storage(/* swap_fd */ -1); - ASSERT_TRUE(storage->DedupeEnabled()); // The default. + ASSERT_TRUE(storage.DedupeEnabled()); // The default. const uint8_t raw_code1[] = { 1u, 2u, 3u }; const uint8_t raw_code2[] = { 4u, 3u, 2u, 1u }; @@ -76,7 +65,7 @@ TEST(CompiledMethodStorage, Deduplicate) { for (auto&& f : cfi_info) { for (auto&& p : patches) { compiled_methods.push_back(CompiledMethod::SwapAllocCompiledMethod( - &driver, InstructionSet::kNone, c, v, f, p)); + &storage, InstructionSet::kNone, c, v, f, p)); } } } @@ -105,7 +94,7 @@ TEST(CompiledMethodStorage, Deduplicate) { } } for (CompiledMethod* method : compiled_methods) { - CompiledMethod::ReleaseSwapAllocatedCompiledMethod(&driver, method); + CompiledMethod::ReleaseSwapAllocatedCompiledMethod(&storage, method); } } diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 0d0a7f2b6a..77b0cea311 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -276,7 +276,7 @@ CompilerDriver::~CompilerDriver() { compiled_methods_.Visit([this](const DexFileReference& ref ATTRIBUTE_UNUSED, CompiledMethod* method) { if (method != nullptr) { - CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, method); + CompiledMethod::ReleaseSwapAllocatedCompiledMethod(GetCompiledMethodStorage(), method); } }); compiler_->UnInit(); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index fe6abd4999..1db20fcfeb 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -765,7 +765,7 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, ScopedArenaVector<uint8_t> stack_map = codegen->BuildStackMaps(code_item_for_osr_check); CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( - GetCompilerDriver(), + GetCompilerDriver()->GetCompiledMethodStorage(), codegen->GetInstructionSet(), code_allocator->GetMemory(), ArrayRef<const uint8_t>(stack_map), @@ -1222,7 +1222,7 @@ CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(&stack_map_allocator, jni_compiled_method); return CompiledMethod::SwapAllocCompiledMethod( - GetCompilerDriver(), + GetCompilerDriver()->GetCompiledMethodStorage(), jni_compiled_method.GetInstructionSet(), jni_compiled_method.GetCode(), ArrayRef<const uint8_t>(stack_map), diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index baeebd9371..1fa21d51fc 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1202,6 +1202,17 @@ 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); +} + class Dex2oatDeterminism : public Dex2oatTest {}; TEST_F(Dex2oatDeterminism, UnloadCompile) { diff --git a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc index b93e091ae6..d8cbbaf61a 100644 --- a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc +++ b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc @@ -18,6 +18,7 @@ #include "arch/arm/instruction_set_features_arm.h" #include "base/casts.h" +#include "driver/compiler_options.h" #include "linker/relative_patcher_test.h" #include "lock_word.h" #include "mirror/array-inl.h" @@ -196,8 +197,8 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { /*out*/ std::string* debug_name = nullptr) { OptimizingUnitTestHelper helper; HGraph* graph = helper.CreateGraph(); - std::string error_msg; - arm::CodeGeneratorARMVIXL codegen(graph, *compiler_options_); + CompilerOptions compiler_options; + arm::CodeGeneratorARMVIXL codegen(graph, compiler_options); ArenaVector<uint8_t> code(helper.GetAllocator()->Adapter()); codegen.EmitThunkCode(patch, &code, debug_name); return std::vector<uint8_t>(code.begin(), code.end()); diff --git a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc index 0fc4610909..f242ae286b 100644 --- a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc +++ b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc @@ -18,6 +18,7 @@ #include "arch/arm64/instruction_set_features_arm64.h" #include "base/casts.h" +#include "driver/compiler_options.h" #include "linker/relative_patcher_test.h" #include "lock_word.h" #include "mirror/array-inl.h" @@ -175,8 +176,8 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { /*out*/ std::string* debug_name = nullptr) { OptimizingUnitTestHelper helper; HGraph* graph = helper.CreateGraph(); - std::string error_msg; - arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_); + CompilerOptions compiler_options; + arm64::CodeGeneratorARM64 codegen(graph, compiler_options); ArenaVector<uint8_t> code(helper.GetAllocator()->Adapter()); codegen.EmitThunkCode(patch, &code, debug_name); return std::vector<uint8_t>(code.begin(), code.end()); diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h index 56ff0ef3ca..4329ee1697 100644 --- a/dex2oat/linker/relative_patcher_test.h +++ b/dex2oat/linker/relative_patcher_test.h @@ -17,19 +17,17 @@ #ifndef ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ #define ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_ +#include <gtest/gtest.h> + #include "arch/instruction_set.h" #include "arch/instruction_set_features.h" #include "base/array_ref.h" #include "base/globals.h" #include "base/macros.h" -#include "common_compiler_test.h" #include "compiled_method-inl.h" -#include "dex/verification_results.h" #include "dex/method_reference.h" #include "dex/string_reference.h" -#include "driver/compiler_driver.h" -#include "driver/compiler_options.h" -#include "gtest/gtest.h" +#include "driver/compiled_method_storage.h" #include "linker/relative_patcher.h" #include "linker/vector_output_stream.h" #include "oat.h" @@ -39,10 +37,12 @@ namespace art { namespace linker { // Base class providing infrastructure for architecture-specific tests. -class RelativePatcherTest : public CommonCompilerTest { +class RelativePatcherTest : public testing::Test { protected: RelativePatcherTest(InstructionSet instruction_set, const std::string& variant) - : variant_(variant), + : storage_(/*swap_fd=*/ -1), + instruction_set_(instruction_set), + instruction_set_features_(nullptr), method_offset_map_(), patcher_(nullptr), bss_begin_(0u), @@ -51,23 +51,29 @@ class RelativePatcherTest : public CommonCompilerTest { patched_code_(), output_(), out_(nullptr) { - // Override CommonCompilerTest's defaults. - instruction_set_ = instruction_set; - number_of_threads_ = 1u; + std::string error_msg; + instruction_set_features_ = + InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg); + CHECK(instruction_set_features_ != nullptr) << error_msg; + patched_code_.reserve(16 * KB); } void SetUp() override { - OverrideInstructionSetFeatures(instruction_set_, variant_); - CommonCompilerTest::SetUp(); - Reset(); } void TearDown() override { + thunk_provider_.Reset(); compiled_methods_.clear(); patcher_.reset(); - CommonCompilerTest::TearDown(); + bss_begin_ = 0u; + string_index_to_offset_map_.clear(); + compiled_method_refs_.clear(); + compiled_methods_.clear(); + patched_code_.clear(); + output_.clear(); + out_.reset(); } // Reset the helper to start another test. Creating and tearing down the Runtime is expensive, @@ -75,8 +81,8 @@ class RelativePatcherTest : public CommonCompilerTest { void Reset() { thunk_provider_.Reset(); method_offset_map_.map.clear(); - patcher_ = RelativePatcher::Create(compiler_options_->GetInstructionSet(), - compiler_options_->GetInstructionSetFeatures(), + patcher_ = RelativePatcher::Create(instruction_set_, + instruction_set_features_.get(), &thunk_provider_, &method_offset_map_); bss_begin_ = 0u; @@ -99,7 +105,7 @@ class RelativePatcherTest : public CommonCompilerTest { const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>()) { compiled_method_refs_.push_back(method_ref); compiled_methods_.emplace_back(new CompiledMethod( - compiler_driver_.get(), + &storage_, instruction_set_, code, /* vmap_table */ ArrayRef<const uint8_t>(), @@ -351,7 +357,10 @@ class RelativePatcherTest : public CommonCompilerTest { static const uint32_t kTrampolineSize = 4u; static const uint32_t kTrampolineOffset = 0u; - std::string variant_; + CompiledMethodStorage storage_; + InstructionSet instruction_set_; + std::unique_ptr<const InstructionSetFeatures> instruction_set_features_; + ThunkProvider thunk_provider_; MethodOffsetMap method_offset_map_; std::unique_ptr<RelativePatcher> patcher_; diff --git a/libartbase/arch/instruction_set.cc b/libartbase/arch/instruction_set.cc index d47f9362d4..8d4fbf4422 100644 --- a/libartbase/arch/instruction_set.cc +++ b/libartbase/arch/instruction_set.cc @@ -133,11 +133,11 @@ static_assert(ART_FRAME_SIZE_LIMIT < kX86StackOverflowReservedBytes, static_assert(ART_FRAME_SIZE_LIMIT < kX86_64StackOverflowReservedBytes, "Frame size limit too large"); -} // namespace instruction_set_details - NO_RETURN void GetStackOverflowReservedBytesFailure(const char* error_msg) { LOG(FATAL) << error_msg; UNREACHABLE(); } +} // namespace instruction_set_details + } // namespace art diff --git a/libartbase/base/sdk_version.h b/libartbase/base/sdk_version.h new file mode 100644 index 0000000000..4372e5a02f --- /dev/null +++ b/libartbase/base/sdk_version.h @@ -0,0 +1,58 @@ +/* + * 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_SDK_VERSION_H_ +#define ART_LIBARTBASE_BASE_SDK_VERSION_H_ + +#include <cstdint> +#include <limits> + +namespace art { + +enum class SdkVersion : uint32_t { + kMin = 0u, + kUnset = 0u, + kL = 21u, + kL_MR1 = 22u, + kM = 23u, + kN = 24u, + kN_MR1 = 25u, + kO = 26u, + kO_MR1 = 27u, + kP = 28u, + kP_MR1 = 29u, + kMax = std::numeric_limits<uint32_t>::max(), +}; + +inline bool IsSdkVersionSetAndMoreThan(uint32_t lhs, SdkVersion rhs) { + return lhs != static_cast<uint32_t>(SdkVersion::kUnset) && lhs > static_cast<uint32_t>(rhs); +} + +inline bool IsSdkVersionSetAndAtLeast(uint32_t lhs, SdkVersion rhs) { + return lhs != static_cast<uint32_t>(SdkVersion::kUnset) && lhs >= static_cast<uint32_t>(rhs); +} + +inline bool IsSdkVersionSetAndAtMost(uint32_t lhs, SdkVersion rhs) { + return lhs != static_cast<uint32_t>(SdkVersion::kUnset) && lhs <= static_cast<uint32_t>(rhs); +} + +inline bool IsSdkVersionSetAndLessThan(uint32_t lhs, SdkVersion rhs) { + return lhs != static_cast<uint32_t>(SdkVersion::kUnset) && lhs < static_cast<uint32_t>(rhs); +} + +} // namespace art + +#endif // ART_LIBARTBASE_BASE_SDK_VERSION_H_ diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h index fd5c8654c3..77bfbc99b3 100644 --- a/libdexfile/dex/hidden_api_access_flags.h +++ b/libdexfile/dex/hidden_api_access_flags.h @@ -48,27 +48,6 @@ enum class ApiList { kNoList, }; -static const int kAccFlagsShift = CTZ(kAccHiddenApiBits); -static_assert(IsPowerOfTwo((kAccHiddenApiBits >> kAccFlagsShift) + 1), - "kAccHiddenApiBits are not continuous"); - -inline ApiList DecodeFromRuntime(uint32_t runtime_access_flags) { - // This is used in the fast path, only DCHECK here. - DCHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u); - uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift; - return static_cast<ApiList>(int_value); -} - -inline uint32_t EncodeForRuntime(uint32_t runtime_access_flags, ApiList value) { - CHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u); - - uint32_t hidden_api_flags = static_cast<uint32_t>(value) << kAccFlagsShift; - CHECK_EQ(hidden_api_flags & ~kAccHiddenApiBits, 0u); - - runtime_access_flags &= ~kAccHiddenApiBits; - return runtime_access_flags | hidden_api_flags; -} - inline bool AreValidFlags(uint32_t flags) { return flags <= static_cast<uint32_t>(ApiList::kBlacklist); } diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h index c4ea2d39b4..114c8e63e3 100644 --- a/libdexfile/dex/modifiers.h +++ b/libdexfile/dex/modifiers.h @@ -52,7 +52,7 @@ static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (ru static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (runtime, not native) // Used by a class to denote that the verifier has attempted to check it at least once. static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime) -static constexpr uint32_t kAccSkipHiddenApiChecks = 0x00100000; // class (runtime) +static constexpr uint32_t kAccSkipHiddenapiChecks = 0x00100000; // class (runtime) // This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent // that it was copied from its declaring class into another class. All methods marked kAccMiranda // and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_ @@ -84,7 +84,8 @@ static constexpr uint32_t kAccMustCountLocks = 0x04000000; // method (ru // virtual call. static constexpr uint32_t kAccSingleImplementation = 0x08000000; // method (runtime) -static constexpr uint32_t kAccHiddenApiBits = 0x30000000; // field, method +static constexpr uint32_t kAccPublicApi = 0x10000000; // field, method +static constexpr uint32_t kAccHiddenapiBits = 0x30000000; // field, method // Non-intrinsics: Caches whether we can use fast-path in the interpreter invokes. // Intrinsics: These bits are part of the intrinsic ordinal. @@ -103,7 +104,7 @@ static constexpr uint32_t kAccClassIsFinalizable = 0x80000000; // Continuous sequence of bits used to hold the ordinal of an intrinsic method. Flags // which overlap are not valid when kAccIntrinsic is set. -static constexpr uint32_t kAccIntrinsicBits = kAccHiddenApiBits | +static constexpr uint32_t kAccIntrinsicBits = kAccHiddenapiBits | kAccSingleImplementation | kAccMustCountLocks | kAccCompileDontBother | kAccDefaultConflict | kAccPreviouslyWarm | kAccFastInterpreterToInterpreterInvoke; diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 7cd10394d5..cabb758912 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -1427,6 +1427,11 @@ void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr<art::mirror::Class> method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx)); // Clear all the intrinsics related flags. method.SetNotIntrinsic(); + // Disable hiddenapi checks when accessing this method. + // Redefining hiddenapi flags is unsupported for the same reasons as redefining + // access flags. Moreover, ArtMethod loses pointer to the old dex file, so just + // disable the checks completely for consistency. + method.SetAccessFlags(method.GetAccessFlags() | art::kAccPublicApi); } } @@ -1445,6 +1450,11 @@ void Redefiner::ClassRedefinition::UpdateFields(art::ObjPtr<art::mirror::Class> CHECK(new_field_id != nullptr); // We only need to update the index since the other data in the ArtField cannot be updated. field.SetDexFieldIndex(dex_file_->GetIndexForFieldId(*new_field_id)); + // Disable hiddenapi checks when accessing this method. + // Redefining hiddenapi flags is unsupported for the same reasons as redefining + // access flags. Moreover, ArtField loses pointer to the old dex file, so just + // disable the checks completely for consistency. + field.SetAccessFlags(field.GetAccessFlags() | art::kAccPublicApi); } } } diff --git a/runtime/art_field.h b/runtime/art_field.h index dc7f985b91..1cf7afa022 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -180,10 +180,6 @@ class ArtField final { return (GetAccessFlags() & kAccVolatile) != 0; } - hiddenapi::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::DecodeFromRuntime(GetAccessFlags()); - } - // Returns an instance field with this offset in the given class or null if not found. // If kExactOffset is true then we only find the matching offset, not the field containing the // offset. diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index d8da9129ed..f2541160ff 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -367,160 +367,6 @@ inline bool ArtMethod::HasSingleImplementation() { return (GetAccessFlags() & kAccSingleImplementation) != 0; } -inline hiddenapi::ApiList ArtMethod::GetHiddenApiAccessFlags() - REQUIRES_SHARED(Locks::mutator_lock_) { - if (UNLIKELY(IsIntrinsic())) { - switch (static_cast<Intrinsics>(GetIntrinsic())) { - case Intrinsics::kSystemArrayCopyChar: - case Intrinsics::kStringGetCharsNoCheck: - case Intrinsics::kReferenceGetReferent: - case Intrinsics::kMemoryPeekByte: - case Intrinsics::kMemoryPokeByte: - case Intrinsics::kUnsafeCASInt: - case Intrinsics::kUnsafeCASLong: - case Intrinsics::kUnsafeCASObject: - case Intrinsics::kUnsafeGet: - case Intrinsics::kUnsafeGetAndAddInt: - case Intrinsics::kUnsafeGetAndAddLong: - case Intrinsics::kUnsafeGetAndSetInt: - case Intrinsics::kUnsafeGetAndSetLong: - case Intrinsics::kUnsafeGetAndSetObject: - case Intrinsics::kUnsafeGetLong: - case Intrinsics::kUnsafeGetLongVolatile: - case Intrinsics::kUnsafeGetObject: - case Intrinsics::kUnsafeGetObjectVolatile: - case Intrinsics::kUnsafeGetVolatile: - case Intrinsics::kUnsafePut: - case Intrinsics::kUnsafePutLong: - case Intrinsics::kUnsafePutLongOrdered: - case Intrinsics::kUnsafePutLongVolatile: - case Intrinsics::kUnsafePutObject: - case Intrinsics::kUnsafePutObjectOrdered: - case Intrinsics::kUnsafePutObjectVolatile: - case Intrinsics::kUnsafePutOrdered: - case Intrinsics::kUnsafePutVolatile: - case Intrinsics::kUnsafeLoadFence: - case Intrinsics::kUnsafeStoreFence: - case Intrinsics::kUnsafeFullFence: - case Intrinsics::kCRC32Update: - // These intrinsics are on the light greylist and will fail a DCHECK in - // SetIntrinsic() if their flags change on the respective dex methods. - // Note that the DCHECK currently won't fail if the dex methods are - // whitelisted, e.g. in the core image (b/77733081). As a result, we - // might print warnings but we won't change the semantics. - return hiddenapi::ApiList::kLightGreylist; - case Intrinsics::kStringNewStringFromBytes: - case Intrinsics::kStringNewStringFromChars: - case Intrinsics::kStringNewStringFromString: - case Intrinsics::kMemoryPeekIntNative: - case Intrinsics::kMemoryPeekLongNative: - case Intrinsics::kMemoryPeekShortNative: - case Intrinsics::kMemoryPokeIntNative: - case Intrinsics::kMemoryPokeLongNative: - case Intrinsics::kMemoryPokeShortNative: - return hiddenapi::ApiList::kDarkGreylist; - case Intrinsics::kVarHandleFullFence: - case Intrinsics::kVarHandleAcquireFence: - case Intrinsics::kVarHandleReleaseFence: - case Intrinsics::kVarHandleLoadLoadFence: - case Intrinsics::kVarHandleStoreStoreFence: - case Intrinsics::kVarHandleCompareAndExchange: - case Intrinsics::kVarHandleCompareAndExchangeAcquire: - case Intrinsics::kVarHandleCompareAndExchangeRelease: - case Intrinsics::kVarHandleCompareAndSet: - case Intrinsics::kVarHandleGet: - case Intrinsics::kVarHandleGetAcquire: - case Intrinsics::kVarHandleGetAndAdd: - case Intrinsics::kVarHandleGetAndAddAcquire: - case Intrinsics::kVarHandleGetAndAddRelease: - case Intrinsics::kVarHandleGetAndBitwiseAnd: - case Intrinsics::kVarHandleGetAndBitwiseAndAcquire: - case Intrinsics::kVarHandleGetAndBitwiseAndRelease: - case Intrinsics::kVarHandleGetAndBitwiseOr: - case Intrinsics::kVarHandleGetAndBitwiseOrAcquire: - case Intrinsics::kVarHandleGetAndBitwiseOrRelease: - case Intrinsics::kVarHandleGetAndBitwiseXor: - case Intrinsics::kVarHandleGetAndBitwiseXorAcquire: - case Intrinsics::kVarHandleGetAndBitwiseXorRelease: - case Intrinsics::kVarHandleGetAndSet: - case Intrinsics::kVarHandleGetAndSetAcquire: - case Intrinsics::kVarHandleGetAndSetRelease: - case Intrinsics::kVarHandleGetOpaque: - case Intrinsics::kVarHandleGetVolatile: - case Intrinsics::kVarHandleSet: - case Intrinsics::kVarHandleSetOpaque: - case Intrinsics::kVarHandleSetRelease: - case Intrinsics::kVarHandleSetVolatile: - case Intrinsics::kVarHandleWeakCompareAndSet: - case Intrinsics::kVarHandleWeakCompareAndSetAcquire: - case Intrinsics::kVarHandleWeakCompareAndSetPlain: - case Intrinsics::kVarHandleWeakCompareAndSetRelease: - // These intrinsics are on the blacklist and will fail a DCHECK in - // SetIntrinsic() if their flags change on the respective dex methods. - // Note that the DCHECK currently won't fail if the dex methods are - // whitelisted, e.g. in the core image (b/77733081). Given that they are - // exclusively VarHandle intrinsics, they should not be used outside - // tests that do not enable hidden API checks. - return hiddenapi::ApiList::kBlacklist; - default: - // Remaining intrinsics are public API. We DCHECK that in SetIntrinsic(). - return hiddenapi::ApiList::kWhitelist; - } - } else { - return hiddenapi::DecodeFromRuntime(GetAccessFlags()); - } -} - -inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) { - // Currently we only do intrinsics for static/final methods or methods of final - // classes. We don't set kHasSingleImplementation for those methods. - DCHECK(IsStatic() || IsFinal() || GetDeclaringClass()->IsFinal()) << - "Potential conflict with kAccSingleImplementation"; - static const int kAccFlagsShift = CTZ(kAccIntrinsicBits); - DCHECK_LE(intrinsic, kAccIntrinsicBits >> kAccFlagsShift); - uint32_t intrinsic_bits = intrinsic << kAccFlagsShift; - uint32_t new_value = (GetAccessFlags() & ~kAccIntrinsicBits) | kAccIntrinsic | intrinsic_bits; - if (kIsDebugBuild) { - uint32_t java_flags = (GetAccessFlags() & kAccJavaFlagsMask); - bool is_constructor = IsConstructor(); - bool is_synchronized = IsSynchronized(); - bool skip_access_checks = SkipAccessChecks(); - bool is_fast_native = IsFastNative(); - bool is_critical_native = IsCriticalNative(); - bool is_copied = IsCopied(); - bool is_miranda = IsMiranda(); - bool is_default = IsDefault(); - bool is_default_conflict = IsDefaultConflicting(); - bool is_compilable = IsCompilable(); - bool must_count_locks = MustCountLocks(); - hiddenapi::ApiList hidden_api_flags = GetHiddenApiAccessFlags(); - SetAccessFlags(new_value); - DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask)); - DCHECK_EQ(is_constructor, IsConstructor()); - DCHECK_EQ(is_synchronized, IsSynchronized()); - DCHECK_EQ(skip_access_checks, SkipAccessChecks()); - DCHECK_EQ(is_fast_native, IsFastNative()); - DCHECK_EQ(is_critical_native, IsCriticalNative()); - DCHECK_EQ(is_copied, IsCopied()); - DCHECK_EQ(is_miranda, IsMiranda()); - DCHECK_EQ(is_default, IsDefault()); - DCHECK_EQ(is_default_conflict, IsDefaultConflicting()); - DCHECK_EQ(is_compilable, IsCompilable()); - DCHECK_EQ(must_count_locks, MustCountLocks()); - // Only DCHECK that we have preserved the hidden API access flags if the - // original method was not on the whitelist. This is because the core image - // does not have the access flags set (b/77733081). It is fine to hard-code - // these because (a) warnings on greylist do not change semantics, and - // (b) only VarHandle intrinsics are blacklisted at the moment and they - // should not be used outside tests with disabled API checks. - if (hidden_api_flags != hiddenapi::ApiList::kWhitelist) { - DCHECK_EQ(hidden_api_flags, GetHiddenApiAccessFlags()) << PrettyMethod(); - } - } else { - SetAccessFlags(new_value); - } -} - template<ReadBarrierOption kReadBarrierOption, typename RootVisitorType> void ArtMethod::VisitRoots(RootVisitorType& visitor, PointerSize pointer_size) { if (LIKELY(!declaring_class_.IsNull())) { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 9bf31edd52..abfdd5547d 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -33,6 +33,7 @@ #include "dex/dex_instruction.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" +#include "hidden_api.h" #include "interpreter/interpreter.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" @@ -682,23 +683,72 @@ bool ArtMethod::HasAnyCompiledCode() { return GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize()) != nullptr; } +void ArtMethod::SetIntrinsic(uint32_t intrinsic) { + // Currently we only do intrinsics for static/final methods or methods of final + // classes. We don't set kHasSingleImplementation for those methods. + DCHECK(IsStatic() || IsFinal() || GetDeclaringClass()->IsFinal()) << + "Potential conflict with kAccSingleImplementation"; + static const int kAccFlagsShift = CTZ(kAccIntrinsicBits); + DCHECK_LE(intrinsic, kAccIntrinsicBits >> kAccFlagsShift); + uint32_t intrinsic_bits = intrinsic << kAccFlagsShift; + uint32_t new_value = (GetAccessFlags() & ~kAccIntrinsicBits) | kAccIntrinsic | intrinsic_bits; + if (kIsDebugBuild) { + uint32_t java_flags = (GetAccessFlags() & kAccJavaFlagsMask); + bool is_constructor = IsConstructor(); + bool is_synchronized = IsSynchronized(); + bool skip_access_checks = SkipAccessChecks(); + bool is_fast_native = IsFastNative(); + bool is_critical_native = IsCriticalNative(); + bool is_copied = IsCopied(); + bool is_miranda = IsMiranda(); + bool is_default = IsDefault(); + bool is_default_conflict = IsDefaultConflicting(); + bool is_compilable = IsCompilable(); + bool must_count_locks = MustCountLocks(); + uint32_t hiddenapi_flags = hiddenapi::GetRuntimeFlags(this); + SetAccessFlags(new_value); + DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask)); + DCHECK_EQ(is_constructor, IsConstructor()); + DCHECK_EQ(is_synchronized, IsSynchronized()); + DCHECK_EQ(skip_access_checks, SkipAccessChecks()); + DCHECK_EQ(is_fast_native, IsFastNative()); + DCHECK_EQ(is_critical_native, IsCriticalNative()); + DCHECK_EQ(is_copied, IsCopied()); + DCHECK_EQ(is_miranda, IsMiranda()); + DCHECK_EQ(is_default, IsDefault()); + DCHECK_EQ(is_default_conflict, IsDefaultConflicting()); + DCHECK_EQ(is_compilable, IsCompilable()); + DCHECK_EQ(must_count_locks, MustCountLocks()); + // Only DCHECK that we have preserved the hidden API access flags if the + // original method was not on the whitelist. This is because the core image + // does not have the access flags set (b/77733081). It is fine to hard-code + // these because (a) warnings on greylist do not change semantics, and + // (b) only VarHandle intrinsics are blacklisted at the moment and they + // should not be used outside tests with disabled API checks. + if ((hiddenapi_flags & kAccHiddenapiBits) == 0) { + DCHECK_EQ(hiddenapi_flags, hiddenapi::GetRuntimeFlags(this)) << PrettyMethod(); + } + } else { + SetAccessFlags(new_value); + } +} + void ArtMethod::SetNotIntrinsic() { if (!IsIntrinsic()) { return; } - // Query the hidden API access flags of the intrinsic. - hiddenapi::ApiList intrinsic_api_list = GetHiddenApiAccessFlags(); + // Read the existing hiddenapi flags. + uint32_t hiddenapi_runtime_flags = hiddenapi::GetRuntimeFlags(this); // Clear intrinsic-related access flags. ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits); // Re-apply hidden API access flags now that the method is not an intrinsic. - SetAccessFlags(hiddenapi::EncodeForRuntime(GetAccessFlags(), intrinsic_api_list)); - DCHECK_EQ(GetHiddenApiAccessFlags(), intrinsic_api_list); + SetAccessFlags(GetAccessFlags() | hiddenapi_runtime_flags); + DCHECK_EQ(hiddenapi_runtime_flags, hiddenapi::GetRuntimeFlags(this)); } - void ArtMethod::CopyFrom(ArtMethod* src, PointerSize image_pointer_size) { memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src), Size(image_pointer_size)); diff --git a/runtime/art_method.h b/runtime/art_method.h index 4e3ef5366a..5bbee92c14 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -343,8 +343,6 @@ class ArtMethod final { AddAccessFlags(kAccMustCountLocks); } - hiddenapi::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_); - // Returns true if this method could be overridden by a default method. bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 35379cc251..639fa7ec92 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(); @@ -3468,14 +3470,8 @@ void ClassLinker::LoadField(const ClassAccessor::Field& field, dst->SetDexFieldIndex(field_idx); dst->SetDeclaringClass(klass.Get()); - // Get access flags from the DexFile. If this is a boot class path class, - // also set its runtime hidden API access flags. - uint32_t access_flags = field.GetAccessFlags(); - if (klass->IsBootStrapClassLoaded()) { - access_flags = hiddenapi::EncodeForRuntime( - access_flags, static_cast<hiddenapi::ApiList>(field.GetHiddenapiFlags())); - } - dst->SetAccessFlags(access_flags); + // Get access flags from the DexFile and set hiddenapi runtime access flags. + dst->SetAccessFlags(field.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(field)); } void ClassLinker::LoadMethod(const DexFile& dex_file, @@ -3491,13 +3487,8 @@ void ClassLinker::LoadMethod(const DexFile& dex_file, dst->SetDeclaringClass(klass.Get()); dst->SetCodeItemOffset(method.GetCodeItemOffset()); - // Get access flags from the DexFile. If this is a boot class path class, - // also set its runtime hidden API access flags. - uint32_t access_flags = method.GetAccessFlags(); - if (klass->IsBootStrapClassLoaded()) { - access_flags = hiddenapi::EncodeForRuntime( - access_flags, static_cast<hiddenapi::ApiList>(method.GetHiddenapiFlags())); - } + // Get access flags from the DexFile and set hiddenapi runtime access flags. + uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method); if (UNLIKELY(strcmp("finalize", method_name) == 0)) { // Set finalizable flag on declaring class. @@ -9005,21 +8996,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); @@ -9109,8 +9093,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 = @@ -9126,15 +9110,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()); + } - 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); + 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(); +} + +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..47931fec75 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_; } diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 0bae60a886..7ca6f86fe3 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -24,11 +24,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 +110,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 +196,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 +206,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 +323,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); } } @@ -571,20 +618,57 @@ static jclass GetClassLoaderClass(ClassLoaderContext::ClassLoaderType type) { UNREACHABLE(); } -static jobject CreateClassLoaderInternal(Thread* self, - const ClassLoaderContext::ClassLoaderInfo& info) +static ObjPtr<mirror::ClassLoader> CreateClassLoaderInternal( + Thread* self, + ScopedObjectAccess& soa, + const ClassLoaderContext::ClassLoaderInfo& info, + 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; + 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(), + /* 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(), /* add_compilation_sources= */ false, compilation_sources)); } std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector( info.opened_dex_files); + 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))); return Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader( self, class_path_files, - GetClassLoaderClass(info.type), + loader_class, + libraries, parent); } @@ -598,30 +682,21 @@ 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); + ObjPtr<mirror::ClassLoader> loader = + CreateClassLoaderInternal(self, + soa, + *class_loader_chain_.get(), + /* 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_test.cc b/runtime/class_loader_context_test.cc index f3e2ac00ba..e4aae47239 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 = 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/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index 15398672b2..6434828298 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -22,6 +22,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/sdk_version.h" #include "class_linker-inl.h" #include "class_root.h" #include "dex/dex_file-inl.h" @@ -129,8 +130,7 @@ ObjPtr<mirror::Object> CreateAnnotationMember(const ClassData& klass, bool IsVisibilityCompatible(uint32_t actual, uint32_t expected) { if (expected == DexFile::kDexVisibilityRuntime) { - int32_t sdk_version = Runtime::Current()->GetTargetSdkVersion(); - if (sdk_version > 0 && sdk_version <= 23) { + if (IsSdkVersionSetAndAtMost(Runtime::Current()->GetTargetSdkVersion(), SdkVersion::kM)) { return actual == DexFile::kDexVisibilityRuntime || actual == DexFile::kDexVisibilityBuild; } } diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 0b005e0851..2236e61d75 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -22,6 +22,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/enums.h" +#include "base/sdk_version.h" #include "class_linker-inl.h" #include "common_throws.h" #include "dex/dex_file.h" @@ -94,8 +95,9 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, // even going back from boot image methods to the same oat file. However, this is // not currently implemented in the compiler. Therefore crossing dex file boundary // indicates that the inlined definition is not the same as the one used at runtime. - bool target_sdk_pre_p = Runtime::Current()->GetTargetSdkVersion() < 28; - LOG(target_sdk_pre_p ? WARNING : FATAL) + bool target_sdk_at_least_p = + IsSdkVersionSetAndAtLeast(Runtime::Current()->GetTargetSdkVersion(), SdkVersion::kP); + LOG(target_sdk_at_least_p ? FATAL : WARNING) << "Inlined method resolution crossed dex file boundary: from " << method->PrettyMethod() << " in " << method->GetDexFile()->GetLocation() << "/" diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 12136bf476..19498f386c 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -20,6 +20,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "base/mutex.h" +#include "base/sdk_version.h" #include "class_linker-inl.h" #include "dex/dex_file-inl.h" #include "entrypoints/entrypoint_utils-inl.h" @@ -64,9 +65,9 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons soa.Self()->AssertThreadSuspensionIsAllowable(); jobjectArray args_jobj = nullptr; const JValue zero; - int32_t target_sdk_version = Runtime::Current()->GetTargetSdkVersion(); + uint32_t target_sdk_version = Runtime::Current()->GetTargetSdkVersion(); // Do not create empty arrays unless needed to maintain Dalvik bug compatibility. - if (args.size() > 0 || (target_sdk_version > 0 && target_sdk_version <= 21)) { + if (args.size() > 0 || IsSdkVersionSetAndAtMost(target_sdk_version, SdkVersion::kL)) { args_jobj = soa.Env()->NewObjectArray(args.size(), WellKnownClasses::java_lang_Object, nullptr); if (args_jobj == nullptr) { CHECK(soa.Self()->IsExceptionPending()); 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/heap.cc b/runtime/gc/heap.cc index f0f81fc67e..e7f14c39d4 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1062,6 +1062,14 @@ 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"; 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/hidden_api.cc b/runtime/hidden_api.cc index 3b7b938d50..188c5f353b 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -18,8 +18,13 @@ #include <nativehelper/scoped_local_ref.h> +#include "art_field-inl.h" +#include "art_method-inl.h" #include "base/dumpable.h" -#include "thread-current-inl.h" +#include "base/sdk_version.h" +#include "dex/class_accessor-inl.h" +#include "scoped_thread_state_change.h" +#include "thread-inl.h" #include "well_known_classes.h" #ifdef ART_TARGET_ANDROID @@ -71,24 +76,19 @@ enum AccessContextFlags { kAccessDenied = 1 << 1, }; -static int32_t GetMaxAllowedSdkVersionForApiList(ApiList api_list) { - SdkCodes sdk = SdkCodes::kVersionNone; +static SdkVersion GetMaxAllowedSdkVersionForApiList(ApiList api_list) { switch (api_list) { case ApiList::kWhitelist: case ApiList::kLightGreylist: - sdk = SdkCodes::kVersionUnlimited; - break; + return SdkVersion::kMax; case ApiList::kDarkGreylist: - sdk = SdkCodes::kVersionO_MR1; - break; + return SdkVersion::kO_MR1; case ApiList::kBlacklist: - sdk = SdkCodes::kVersionNone; - break; + return SdkVersion::kMin; case ApiList::kNoList: LOG(FATAL) << "Unexpected value"; UNREACHABLE(); } - return static_cast<int32_t>(sdk); } MemberSignature::MemberSignature(ArtField* field) { @@ -235,23 +235,82 @@ void MemberSignature::NotifyHiddenApiListener(AccessMethod access_method) { } } -static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) { +static ALWAYS_INLINE bool CanUpdateRuntimeFlags(ArtField*) { return true; } -static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtMethod* method) { +static ALWAYS_INLINE bool CanUpdateRuntimeFlags(ArtMethod* method) { return !method->IsIntrinsic(); } template<typename T> static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member) REQUIRES_SHARED(Locks::mutator_lock_) { - if (CanUpdateMemberAccessFlags(member) && runtime->ShouldDedupeHiddenApiWarnings()) { - member->SetAccessFlags(hiddenapi::EncodeForRuntime( - member->GetAccessFlags(), hiddenapi::ApiList::kWhitelist)); + if (CanUpdateRuntimeFlags(member) && runtime->ShouldDedupeHiddenApiWarnings()) { + member->SetAccessFlags(member->GetAccessFlags() | kAccPublicApi); } } +static constexpr uint32_t kNoDexFlags = 0u; +static constexpr uint32_t kInvalidDexFlags = static_cast<uint32_t>(-1); + +uint32_t GetDexFlags(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::Class> declaring_class = field->GetDeclaringClass(); + DCHECK(declaring_class != nullptr) << "Fields always have a declaring class"; + + const DexFile::ClassDef* class_def = declaring_class->GetClassDef(); + if (class_def == nullptr) { + return kNoDexFlags; + } + + uint32_t flags = kInvalidDexFlags; + DCHECK(!AreValidFlags(flags)); + + ClassAccessor accessor(declaring_class->GetDexFile(), + *class_def, + /* parse_hiddenapi_class_data= */ true); + auto fn_visit = [&](const ClassAccessor::Field& dex_field) { + if (dex_field.GetIndex() == field->GetDexFieldIndex()) { + flags = dex_field.GetHiddenapiFlags(); + } + }; + accessor.VisitFields(fn_visit, fn_visit); + + CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for field " << field->PrettyField(); + DCHECK(AreValidFlags(flags)); + return flags; +} + +uint32_t GetDexFlags(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass(); + if (declaring_class.IsNull()) { + DCHECK(method->IsRuntimeMethod()); + return kNoDexFlags; + } + + const DexFile::ClassDef* class_def = declaring_class->GetClassDef(); + if (class_def == nullptr) { + return kNoDexFlags; + } + + uint32_t flags = kInvalidDexFlags; + DCHECK(!AreValidFlags(flags)); + + ClassAccessor accessor(declaring_class->GetDexFile(), + *class_def, + /* parse_hiddenapi_class_data= */ true); + auto fn_visit = [&](const ClassAccessor::Method& dex_method) { + if (dex_method.GetIndex() == method->GetDexMethodIndex()) { + flags = dex_method.GetHiddenapiFlags(); + } + }; + accessor.VisitMethods(fn_visit, fn_visit); + + CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for method " << method->PrettyMethod(); + DCHECK(AreValidFlags(flags)); + return flags; +} + template<typename T> bool ShouldDenyAccessToMemberImpl(T* member, hiddenapi::ApiList api_list, @@ -263,7 +322,8 @@ bool ShouldDenyAccessToMemberImpl(T* member, const bool deny_access = (policy == EnforcementPolicy::kEnabled) && - (runtime->GetTargetSdkVersion() > GetMaxAllowedSdkVersionForApiList(api_list)); + IsSdkVersionSetAndMoreThan(runtime->GetTargetSdkVersion(), + GetMaxAllowedSdkVersionForApiList(api_list)); MemberSignature member_signature(member); diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index ed00e2a892..32bae1127b 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -17,10 +17,11 @@ #ifndef ART_RUNTIME_HIDDEN_API_H_ #define ART_RUNTIME_HIDDEN_API_H_ -#include "art_field-inl.h" -#include "art_method-inl.h" +#include "art_field.h" +#include "art_method.h" #include "base/mutex.h" #include "dex/hidden_api_access_flags.h" +#include "intrinsics_enum.h" #include "mirror/class-inl.h" #include "reflection.h" #include "runtime.h" @@ -116,13 +117,6 @@ class ScopedHiddenApiEnforcementPolicySetting { // Implementation details. DO NOT ACCESS DIRECTLY. namespace detail { -enum class SdkCodes { - kVersionNone = std::numeric_limits<int32_t>::min(), - kVersionUnlimited = std::numeric_limits<int32_t>::max(), - kVersionO_MR1 = 27, - kVersionP = 28, -}; - // Class to encapsulate the signature of a member (ArtField or ArtMethod). This // is used as a helper when matching prefixes, and when logging the signature. class MemberSignature { @@ -164,12 +158,136 @@ class MemberSignature { void NotifyHiddenApiListener(AccessMethod access_method); }; +// Locates hiddenapi flags for `field` in the corresponding dex file. +// NB: This is an O(N) operation, linear with the number of members in the class def. +uint32_t GetDexFlags(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); + +// Locates hiddenapi flags for `method` in the corresponding dex file. +// NB: This is an O(N) operation, linear with the number of members in the class def. +uint32_t GetDexFlags(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + template<typename T> bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_); } // namespace detail +// Returns access flags for the runtime representation of a class member (ArtField/ArtMember). +ALWAYS_INLINE inline uint32_t CreateRuntimeFlags(const ClassAccessor::BaseItem& member) { + uint32_t runtime_flags = 0u; + + uint32_t dex_flags = member.GetHiddenapiFlags(); + DCHECK(AreValidFlags(dex_flags)); + + ApiList api_list = static_cast<hiddenapi::ApiList>(dex_flags); + if (api_list == ApiList::kWhitelist) { + runtime_flags |= kAccPublicApi; + } + + DCHECK_EQ(runtime_flags & kAccHiddenapiBits, runtime_flags) + << "Runtime flags not in reserved access flags bits"; + return runtime_flags; +} + +// Extracts hiddenapi runtime flags from access flags of ArtField. +ALWAYS_INLINE inline uint32_t GetRuntimeFlags(ArtField* field) + REQUIRES_SHARED(Locks::mutator_lock_) { + return field->GetAccessFlags() & kAccHiddenapiBits; +} + +// Extracts hiddenapi runtime flags from access flags of ArtMethod. +// Uses hardcoded values for intrinsics. +ALWAYS_INLINE inline uint32_t GetRuntimeFlags(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (UNLIKELY(method->IsIntrinsic())) { + switch (static_cast<Intrinsics>(method->GetIntrinsic())) { + case Intrinsics::kSystemArrayCopyChar: + case Intrinsics::kStringGetCharsNoCheck: + case Intrinsics::kReferenceGetReferent: + case Intrinsics::kMemoryPeekByte: + case Intrinsics::kMemoryPokeByte: + case Intrinsics::kUnsafeCASInt: + case Intrinsics::kUnsafeCASLong: + case Intrinsics::kUnsafeCASObject: + case Intrinsics::kUnsafeGet: + case Intrinsics::kUnsafeGetAndAddInt: + case Intrinsics::kUnsafeGetAndAddLong: + case Intrinsics::kUnsafeGetAndSetInt: + case Intrinsics::kUnsafeGetAndSetLong: + case Intrinsics::kUnsafeGetAndSetObject: + case Intrinsics::kUnsafeGetLong: + case Intrinsics::kUnsafeGetLongVolatile: + case Intrinsics::kUnsafeGetObject: + case Intrinsics::kUnsafeGetObjectVolatile: + case Intrinsics::kUnsafeGetVolatile: + case Intrinsics::kUnsafePut: + case Intrinsics::kUnsafePutLong: + case Intrinsics::kUnsafePutLongOrdered: + case Intrinsics::kUnsafePutLongVolatile: + case Intrinsics::kUnsafePutObject: + case Intrinsics::kUnsafePutObjectOrdered: + case Intrinsics::kUnsafePutObjectVolatile: + case Intrinsics::kUnsafePutOrdered: + case Intrinsics::kUnsafePutVolatile: + case Intrinsics::kUnsafeLoadFence: + case Intrinsics::kUnsafeStoreFence: + case Intrinsics::kUnsafeFullFence: + case Intrinsics::kCRC32Update: + case Intrinsics::kStringNewStringFromBytes: + case Intrinsics::kStringNewStringFromChars: + case Intrinsics::kStringNewStringFromString: + case Intrinsics::kMemoryPeekIntNative: + case Intrinsics::kMemoryPeekLongNative: + case Intrinsics::kMemoryPeekShortNative: + case Intrinsics::kMemoryPokeIntNative: + case Intrinsics::kMemoryPokeLongNative: + case Intrinsics::kMemoryPokeShortNative: + case Intrinsics::kVarHandleFullFence: + case Intrinsics::kVarHandleAcquireFence: + case Intrinsics::kVarHandleReleaseFence: + case Intrinsics::kVarHandleLoadLoadFence: + case Intrinsics::kVarHandleStoreStoreFence: + case Intrinsics::kVarHandleCompareAndExchange: + case Intrinsics::kVarHandleCompareAndExchangeAcquire: + case Intrinsics::kVarHandleCompareAndExchangeRelease: + case Intrinsics::kVarHandleCompareAndSet: + case Intrinsics::kVarHandleGet: + case Intrinsics::kVarHandleGetAcquire: + case Intrinsics::kVarHandleGetAndAdd: + case Intrinsics::kVarHandleGetAndAddAcquire: + case Intrinsics::kVarHandleGetAndAddRelease: + case Intrinsics::kVarHandleGetAndBitwiseAnd: + case Intrinsics::kVarHandleGetAndBitwiseAndAcquire: + case Intrinsics::kVarHandleGetAndBitwiseAndRelease: + case Intrinsics::kVarHandleGetAndBitwiseOr: + case Intrinsics::kVarHandleGetAndBitwiseOrAcquire: + case Intrinsics::kVarHandleGetAndBitwiseOrRelease: + case Intrinsics::kVarHandleGetAndBitwiseXor: + case Intrinsics::kVarHandleGetAndBitwiseXorAcquire: + case Intrinsics::kVarHandleGetAndBitwiseXorRelease: + case Intrinsics::kVarHandleGetAndSet: + case Intrinsics::kVarHandleGetAndSetAcquire: + case Intrinsics::kVarHandleGetAndSetRelease: + case Intrinsics::kVarHandleGetOpaque: + case Intrinsics::kVarHandleGetVolatile: + case Intrinsics::kVarHandleSet: + case Intrinsics::kVarHandleSetOpaque: + case Intrinsics::kVarHandleSetRelease: + case Intrinsics::kVarHandleSetVolatile: + case Intrinsics::kVarHandleWeakCompareAndSet: + case Intrinsics::kVarHandleWeakCompareAndSetAcquire: + case Intrinsics::kVarHandleWeakCompareAndSetPlain: + case Intrinsics::kVarHandleWeakCompareAndSetRelease: + return 0u; + default: + // Remaining intrinsics are public API. We DCHECK that in SetIntrinsic(). + return kAccPublicApi; + } + } else { + return method->GetAccessFlags() & kAccHiddenapiBits; + } +} + // Returns true if access to `member` should be denied in the given context. // The decision is based on whether the caller is in a trusted context or not. // Because determining the access context can be expensive, a lambda function @@ -183,16 +301,9 @@ inline bool ShouldDenyAccessToMember(T* member, REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); - // Decode hidden API access flags. - // NB Multiple threads might try to access (and overwrite) these simultaneously, - // causing a race. We only do that if access has not been denied, so the race - // cannot change Java semantics. We should, however, decode the access flags - // once and use it throughout this function, otherwise we may get inconsistent - // results, e.g. print whitelist warnings (b/78327881). - ApiList api_list = member->GetHiddenApiAccessFlags(); - - // Exit early if member is on the whitelist. - if (api_list == ApiList::kWhitelist) { + // Exit early if member is public API. This flag is also set for non-boot class + // path fields/methods. + if ((GetRuntimeFlags(member) & kAccPublicApi) != 0) { return false; } @@ -202,6 +313,11 @@ inline bool ShouldDenyAccessToMember(T* member, return false; } + // 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)); + // 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 627d9a7e1c..314d878c66 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -16,6 +16,7 @@ #include "hidden_api.h" +#include "base/sdk_version.h" #include "common_runtime_test.h" #include "jni/jni_internal.h" #include "proxy_test.h" @@ -112,14 +113,14 @@ TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) { ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false); runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); - runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1)); + 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_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); - runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionP)); + 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); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 359f97e705..1701ca8a78 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -1274,10 +1274,10 @@ size_t JitCodeCache::ReserveData(Thread* self, class MarkCodeVisitor final : public StackVisitor { public: - MarkCodeVisitor(Thread* thread_in, JitCodeCache* code_cache_in) + MarkCodeVisitor(Thread* thread_in, JitCodeCache* code_cache_in, CodeCacheBitmap* bitmap) : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kSkipInlinedFrames), code_cache_(code_cache_in), - bitmap_(code_cache_->GetLiveBitmap()) {} + bitmap_(bitmap) {} bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); @@ -1299,13 +1299,13 @@ class MarkCodeVisitor final : public StackVisitor { class MarkCodeClosure final : public Closure { public: - MarkCodeClosure(JitCodeCache* code_cache, Barrier* barrier) - : code_cache_(code_cache), barrier_(barrier) {} + MarkCodeClosure(JitCodeCache* code_cache, CodeCacheBitmap* bitmap, Barrier* barrier) + : code_cache_(code_cache), bitmap_(bitmap), barrier_(barrier) {} void Run(Thread* thread) override REQUIRES_SHARED(Locks::mutator_lock_) { ScopedTrace trace(__PRETTY_FUNCTION__); DCHECK(thread == Thread::Current() || thread->IsSuspended()); - MarkCodeVisitor visitor(thread, code_cache_); + MarkCodeVisitor visitor(thread, code_cache_, bitmap_); visitor.WalkStack(); if (kIsDebugBuild) { // The stack walking code queries the side instrumentation stack if it @@ -1320,7 +1320,7 @@ class MarkCodeClosure final : public Closure { code_cache_->LookupMethodHeader(frame.return_pc_, /* method= */ nullptr); if (method_header != nullptr) { const void* code = method_header->GetCode(); - CHECK(code_cache_->GetLiveBitmap()->Test(FromCodeToAllocation(code))); + CHECK(bitmap_->Test(FromCodeToAllocation(code))); } } } @@ -1329,6 +1329,7 @@ class MarkCodeClosure final : public Closure { private: JitCodeCache* const code_cache_; + CodeCacheBitmap* const bitmap_; Barrier* const barrier_; }; @@ -1374,7 +1375,7 @@ bool JitCodeCache::IncreaseCodeCacheCapacity() { void JitCodeCache::MarkCompiledCodeOnThreadStacks(Thread* self) { Barrier barrier(0); size_t threads_running_checkpoint = 0; - MarkCodeClosure closure(this, &barrier); + MarkCodeClosure closure(this, GetLiveBitmap(), &barrier); threads_running_checkpoint = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure); // Now that we have run our checkpoint, move to a suspended state and wait // for other threads to run the checkpoint. @@ -1987,11 +1988,6 @@ void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self, bool osr) { } } -size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { - MutexLock mu(Thread::Current(), lock_); - return mspace_usable_size(reinterpret_cast<const void*>(FromCodeToAllocation(ptr))); -} - void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { DCHECK(!method->IsNative()); diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 126fd441db..a5075638f2 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -71,7 +71,6 @@ template<class T> class ObjectArray; namespace jit { -class JitInstrumentationCache; class ScopedCodeCacheWrite; // Alignment in bits that will suit all architectures. @@ -97,12 +96,6 @@ class JitCodeCache { std::string* error_msg); ~JitCodeCache(); - // Number of bytes allocated in the code cache. - size_t CodeCacheSize() REQUIRES(!lock_); - - // Number of bytes allocated in the data cache. - size_t DataCacheSize() REQUIRES(!lock_); - bool NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!lock_); @@ -177,10 +170,6 @@ class JitCodeCache { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!lock_); - CodeCacheBitmap* GetLiveBitmap() const { - return live_bitmap_.get(); - } - // Perform a collection on the code cache. void GarbageCollectCache(Thread* self) REQUIRES(!lock_) @@ -234,10 +223,6 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - uint64_t GetLastUpdateTimeNs() const; - - size_t GetMemorySizeOfCodePointer(const void* ptr) REQUIRES(!lock_); - void InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* code) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -339,6 +324,12 @@ class JitCodeCache { void FreeCodeAndData(const void* code_ptr) REQUIRES(lock_); // Number of bytes allocated in the code cache. + size_t CodeCacheSize() REQUIRES(!lock_); + + // Number of bytes allocated in the data cache. + size_t DataCacheSize() REQUIRES(!lock_); + + // Number of bytes allocated in the code cache. size_t CodeCacheSizeLocked() REQUIRES(lock_); // Number of bytes allocated in the data cache. @@ -375,6 +366,10 @@ class JitCodeCache { REQUIRES(lock_) REQUIRES_SHARED(Locks::mutator_lock_); + CodeCacheBitmap* GetLiveBitmap() const { + return live_bitmap_.get(); + } + uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_); void FreeCode(uint8_t* code) REQUIRES(lock_); uint8_t* AllocateData(size_t data_size) REQUIRES(lock_); diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc index 6769368ee4..a61a48a29b 100644 --- a/runtime/jni/java_vm_ext.cc +++ b/runtime/jni/java_vm_ext.cc @@ -23,6 +23,7 @@ #include "art_method-inl.h" #include "base/dumpable.h" #include "base/mutex-inl.h" +#include "base/sdk_version.h" #include "base/stl_util.h" #include "base/systrace.h" #include "check_jni.h" @@ -1030,7 +1031,7 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym); int version = (*jni_on_load)(this, nullptr); - if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) { + if (IsSdkVersionSetAndAtMost(runtime_->GetTargetSdkVersion(), SdkVersion::kL)) { // Make sure that sigchain owns SIGSEGV. EnsureFrontOfChain(SIGSEGV); } diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index c38cc86c59..4e551adfde 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -211,12 +211,12 @@ class MANAGED Class final : public Object { } ALWAYS_INLINE bool ShouldSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) { - return (GetAccessFlags() & kAccSkipHiddenApiChecks) != 0; + return (GetAccessFlags() & kAccSkipHiddenapiChecks) != 0; } ALWAYS_INLINE void SetSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t flags = GetAccessFlags(); - SetAccessFlags(flags | kAccSkipHiddenApiChecks); + SetAccessFlags(flags | kAccSkipHiddenapiChecks); } ALWAYS_INLINE void SetRecursivelyInitialized() REQUIRES_SHARED(Locks::mutator_lock_) { diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 49b71cd801..e213dc79b8 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -29,6 +29,7 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "arch/instruction_set.h" #include "art_method-inl.h" #include "base/enums.h" +#include "base/sdk_version.h" #include "class_linker-inl.h" #include "common_throws.h" #include "debugger.h" @@ -256,12 +257,15 @@ static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sd // where workarounds can be enabled. // Note that targetSdkVersion may be CUR_DEVELOPMENT (10000). // Note that targetSdkVersion may be 0, meaning "current". - Runtime::Current()->SetTargetSdkVersion(target_sdk_version); + uint32_t uint_target_sdk_version = + target_sdk_version <= 0 ? static_cast<uint32_t>(SdkVersion::kUnset) + : static_cast<uint32_t>(target_sdk_version); + Runtime::Current()->SetTargetSdkVersion(uint_target_sdk_version); #ifdef ART_TARGET_ANDROID // This part is letting libc/dynamic linker know about current app's // target sdk version to enable compatibility workarounds. - android_set_application_target_sdk_version(static_cast<uint32_t>(target_sdk_version)); + android_set_application_target_sdk_version(uint_target_sdk_version); #endif } diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 715792876d..33c85973b3 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -323,7 +323,7 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .WithValueMap({{"false", false}, {"true", true}}) .IntoKey(M::SlowDebug) .Define("-Xtarget-sdk-version:_") - .WithType<int>() + .WithType<unsigned int>() .IntoKey(M::TargetSdkVersion) .Define("-Xhidden-api-checks") .IntoKey(M::HiddenApiChecks) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index ccbc2d92e1..f016e874ca 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -61,6 +61,7 @@ #include "base/mutex.h" #include "base/os.h" #include "base/quasi_atomic.h" +#include "base/sdk_version.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" @@ -253,7 +254,7 @@ Runtime::Runtime() preinitialization_transactions_(), verify_(verifier::VerifyMode::kNone), allow_dex_file_fallback_(true), - target_sdk_version_(kUnsetSdkVersion), + target_sdk_version_(static_cast<uint32_t>(SdkVersion::kUnset)), implicit_null_checks_(false), implicit_so_checks_(false), implicit_suspend_checks_(false), @@ -277,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"); @@ -320,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 f6a5634379..3c057f3c41 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -583,11 +583,11 @@ class Runtime { return is_running_on_memory_tool_; } - void SetTargetSdkVersion(int32_t version) { + void SetTargetSdkVersion(uint32_t version) { target_sdk_version_ = version; } - int32_t GetTargetSdkVersion() const { + uint32_t GetTargetSdkVersion() const { return target_sdk_version_; } @@ -793,8 +793,6 @@ class Runtime { return jdwp_provider_; } - static constexpr int32_t kUnsetSdkVersion = 0u; - uint32_t GetVerifierLoggingThresholdMs() const { return verifier_logging_threshold_ms_; } @@ -975,7 +973,7 @@ class Runtime { std::vector<std::string> cpu_abilist_; // Specifies target SDK version to allow workarounds for certain API levels. - int32_t target_sdk_version_; + uint32_t target_sdk_version_; // Implicit checks flags. bool implicit_null_checks_; // NullPointer checks are implicit. @@ -1111,6 +1109,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/runtime_options.cc b/runtime/runtime_options.cc index f8c680db44..12dab158e5 100644 --- a/runtime/runtime_options.cc +++ b/runtime/runtime_options.cc @@ -18,6 +18,7 @@ #include <memory> +#include "base/sdk_version.h" #include "base/utils.h" #include "debugger.h" #include "gc/heap.h" diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index ae1e08f10b..5cec309453 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -116,7 +116,8 @@ RUNTIME_OPTIONS_KEY (std::vector<std::string>, \ ImageCompilerOptions) // -Ximage-compiler-option ... RUNTIME_OPTIONS_KEY (verifier::VerifyMode, \ Verify, verifier::VerifyMode::kEnable) -RUNTIME_OPTIONS_KEY (int, TargetSdkVersion, Runtime::kUnsetSdkVersion) +RUNTIME_OPTIONS_KEY (unsigned int, TargetSdkVersion, \ + static_cast<unsigned int>(SdkVersion::kUnset)) RUNTIME_OPTIONS_KEY (Unit, HiddenApiChecks) RUNTIME_OPTIONS_KEY (std::string, NativeBridge) RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 7b07389057..0b33a0b3c0 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -28,6 +28,7 @@ #include "base/indenter.h" #include "base/logging.h" // For VLOG. #include "base/mutex-inl.h" +#include "base/sdk_version.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" @@ -3677,9 +3678,10 @@ const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { // Note: we do this for unresolved classes to trigger re-verification at runtime. if (C == CheckAccess::kYes && result->IsNonZeroReferenceTypes() && - (api_level_ >= 28u || !result->IsUnresolvedTypes())) { + (IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kP) || !result->IsUnresolvedTypes())) { const RegType& referrer = GetDeclaringClass(); - if ((api_level_ >= 28u || !referrer.IsUnresolvedTypes()) && !referrer.CanAccess(*result)) { + if ((IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kP) || !referrer.IsUnresolvedTypes()) && + !referrer.CanAccess(*result)) { Fail(VERIFY_ERROR_ACCESS_CLASS) << "(possibly) illegal class access: '" << referrer << "' -> '" << *result << "'"; } @@ -4562,7 +4564,9 @@ ArtField* MethodVerifier::GetStaticField(int field_idx) { } if (klass_type.IsUnresolvedTypes()) { // Accessibility checks depend on resolved fields. - DCHECK(klass_type.Equals(GetDeclaringClass()) || !failures_.empty() || api_level_ < 28u); + DCHECK(klass_type.Equals(GetDeclaringClass()) || + !failures_.empty() || + IsSdkVersionSetAndLessThan(api_level_, SdkVersion::kP)); return nullptr; // Can't resolve Class so no more to do here, will do checking at runtime. } @@ -4603,7 +4607,9 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id } if (klass_type.IsUnresolvedTypes()) { // Accessibility checks depend on resolved fields. - DCHECK(klass_type.Equals(GetDeclaringClass()) || !failures_.empty() || api_level_ < 28u); + DCHECK(klass_type.Equals(GetDeclaringClass()) || + !failures_.empty() || + IsSdkVersionSetAndLessThan(api_level_, SdkVersion::kP)); return nullptr; // Can't resolve Class so no more to do here } @@ -4739,7 +4745,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& DCHECK(!can_load_classes_ || self_->IsExceptionPending()); self_->ClearException(); } - } else if (api_level_ >= 28u) { + } else if (IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kP)) { // If we don't have the field (it seems we failed resolution) and this is a PUT, we need to // redo verification at runtime as the field may be final, unless the field id shows it's in // the same class. diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc index 8e3e4eb5ce..d11aa579e5 100644 --- a/test/674-hiddenapi/hiddenapi.cc +++ b/test/674-hiddenapi/hiddenapi.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "base/sdk_version.h" #include "class_linker.h" #include "dex/art_dex_file_loader.h" #include "hidden_api.h" @@ -29,7 +30,7 @@ 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<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1)); + runtime->SetTargetSdkVersion(static_cast<uint32_t>(SdkVersion::kO_MR1)); runtime->SetDedupeHiddenApiWarnings(false); } @@ -284,7 +285,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorV( } extern "C" JNIEXPORT jint JNICALL Java_Reflection_getHiddenApiAccessFlags(JNIEnv*, jclass) { - return static_cast<jint>(kAccHiddenApiBits); + return static_cast<jint>(kAccHiddenapiBits); } } // namespace Test674HiddenApi diff --git a/test/688-shared-library/run b/test/688-shared-library/run new file mode 100644 index 0000000000..fa6ab58da6 --- /dev/null +++ b/test/688-shared-library/run @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# App images are incompatible with what the test is doing: loading one +# dex file multiple times. +exec ${RUN} "${@}" --no-app-image diff --git a/test/999-redefine-hiddenapi/src-redefine/gen.sh b/test/999-redefine-hiddenapi/src-redefine/gen.sh index 6948cbbfc3..f78a025265 100755 --- a/test/999-redefine-hiddenapi/src-redefine/gen.sh +++ b/test/999-redefine-hiddenapi/src-redefine/gen.sh @@ -18,13 +18,21 @@ set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" TMP=`mktemp -d` -CLASS "art/Test999" +CLASS="art/Test999" -(cd "$TMP" && javac -d "${TMP}" "$DIR/${CLASS}.java" && d8 --output . "$TMP/${CLASS}.class") +(cd "$TMP" && \ + javac -d "${TMP}" "$DIR/${CLASS}.java" && \ + d8 --output . "$TMP/${CLASS}.class" && + hiddenapi encode --input-dex="$TMP/classes.dex" \ + --output-dex="$TMP/classes-hiddenapi.dex" \ + --flags="$DIR/../hiddenapi-flags.csv" \ + --no-force-assign-all) echo ' private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(' base64 "${TMP}/${CLASS}.class" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' echo ' private static final byte[] DEX_BYTES = Base64.getDecoder().decode(' base64 "${TMP}/classes.dex" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' +echo ' private static final byte[] DEX_BYTES_HIDDEN = Base64.getDecoder().decode(' +base64 "${TMP}/classes-hiddenapi.dex" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' rm -rf "$TMP" diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java index c6365ac234..4627b4fd22 100644 --- a/test/999-redefine-hiddenapi/src/Main.java +++ b/test/999-redefine-hiddenapi/src/Main.java @@ -19,7 +19,7 @@ import java.lang.reflect.Method; import java.util.Base64; public class Main { - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws ClassNotFoundException { System.loadLibrary(args[0]); // Run the initialization routine. This will enable hidden API checks in @@ -31,35 +31,53 @@ public class Main { // Find the test class in boot class loader and verify that its members are hidden. Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER); - assertMethodIsHidden(klass, "before redefinition"); - assertFieldIsHidden(klass, "before redefinition"); + assertMethodIsHidden(true, klass, "before redefinition"); + assertFieldIsHidden(true, klass, "before redefinition"); - // Redefine the class using JVMTI. + // Redefine the class using JVMTI. Use dex file without hiddenapi flags. art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE); art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES); - // Verify that the class members are still hidden. - assertMethodIsHidden(klass, "after redefinition"); - assertFieldIsHidden(klass, "after redefinition"); + // Verify that the class members are not hidden anymore. + assertMethodIsHidden(false, klass, "after first redefinition"); + assertFieldIsHidden(false, klass, "after first redefinition"); + + // Redefine the class using JVMTI, this time with a dex file with hiddenapi flags. + art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE); + art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES_HIDDEN); + + // Verify that the class members are still accessible. + assertMethodIsHidden(false, klass, "after second redefinition"); + assertFieldIsHidden(false, klass, "after second redefinition"); } - private static void assertMethodIsHidden(Class<?> klass, String msg) throws Exception { + private static void assertMethodIsHidden(boolean expectedHidden, Class<?> klass, String msg) { try { klass.getDeclaredMethod("foo"); - // Unexpected. Should have thrown NoSuchMethodException. - throw new Exception("Method should not be accessible " + msg); + if (expectedHidden) { + // Unexpected. Should have thrown NoSuchMethodException. + throw new RuntimeException("Method should not be accessible " + msg); + } } catch (NoSuchMethodException ex) { - // Expected. + if (!expectedHidden) { + // Unexpected. Should not have thrown NoSuchMethodException. + throw new RuntimeException("Method should be accessible " + msg); + } } } - private static void assertFieldIsHidden(Class<?> klass, String msg) throws Exception { + private static void assertFieldIsHidden(boolean expectedHidden, Class<?> klass, String msg) { try { klass.getDeclaredField("bar"); - // Unexpected. Should have thrown NoSuchFieldException. - throw new Exception("Field should not be accessible " + msg); + if (expectedHidden) { + // Unexpected. Should have thrown NoSuchFieldException. + throw new RuntimeException("Field should not be accessible " + msg); + } } catch (NoSuchFieldException ex) { - // Expected. + if (!expectedHidden) { + // Unexpected. Should not have thrown NoSuchFieldException. + throw new RuntimeException("Field should be accessible " + msg); + } } } @@ -93,19 +111,37 @@ public class Main { "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" + "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA="); private static final byte[] DEX_BYTES = Base64.getDecoder().decode( - "ZGV4CjAzNQD0dZ+IWxOi+cJDSWjfTnUerlZj1Lll3ONIAwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAQ" + - "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAUAgAANAEAAIYB" + + "ZGV4CjAzNQDlfmgFfKulToQpDF+P4dsgeOkgfzzH+5lgAwAAcAAAAHhWNBIAAAAAAAAAALQCAAAQ" + + "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAsAgAANAEAAIYB" + + "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" + + "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" + + "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" + + "AAAAAAAAAAgAAAAAAAAAoAIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" + + "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" + + "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" + + "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" + + "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAHV+fkQ4eyJjb21waWxhdGlv" + + "bi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImQyMmFiNGYxOWI3NTYxNDQ3NTI4" + + "NTdjYTg2YjJjZWU0ZGQ5Y2ExNjYiLCJ2ZXJzaW9uIjoiMS40LjktZGV2In0AAAEBAQABAIGABLQC" + + "AQHUAgAAAAAOAAAAAAAAAAEAAAAAAAAAAQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADM" + + "AAAABAAAAAIAAADkAAAABQAAAAQAAAD0AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIA" + + "AAB0AQAAARAAAAEAAACAAQAAAiAAABAAAACGAQAAACAAAAEAAACgAgAAAxAAAAEAAACwAgAAABAA" + + "AAEAAAC0AgAA"); + private static final byte[] DEX_BYTES_HIDDEN = Base64.getDecoder().decode( + "ZGV4CjAzNQDsgG5ufKulToQpDF+P4dsgeOkgfzzH+5l4AwAAcAAAAHhWNBIAAAAAAAAAAMACAAAQ" + + "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAABEAgAANAEAAIYB" + "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" + "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" + "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" + - "AAAAAAAAAAgAAAAAAAAAhwIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" + + "AAAAAAAAAAgAAAAAAAAAoAIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" + "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" + "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" + "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" + - "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAFx+fkQ4eyJtaW4tYXBpIjox" + - "LCJzaGEtMSI6IjU2YzJlMzBmNTIzM2I4NDRmZjZkZGQ4N2ZiNTNkMzRmYjE3MjM3ZGYiLCJ2ZXJz" + - "aW9uIjoidjEuMi4xNS1kZXYifQAAAQEBAAEAgYAEtAIBAdQCAAAAAAAOAAAAAAAAAAEAAAAAAAAA" + - "AQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0" + - "AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAA" + - "AACGAQAAACAAAAEAAACHAgAAAxAAAAEAAACYAgAAABAAAAEAAACcAgAA"); + "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAHV+fkQ4eyJjb21waWxhdGlv" + + "bi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImQyMmFiNGYxOWI3NTYxNDQ3NTI4" + + "NTdjYTg2YjJjZWU0ZGQ5Y2ExNjYiLCJ2ZXJzaW9uIjoiMS40LjktZGV2In0AAAEBAQABAIGABLQC" + + "AQHUAgAAAAALAAAACAAAAAIAAgAPAAAAAAAAAAEAAAAAAAAAAQAAABAAAABwAAAAAgAAAAcAAACw" + + "AAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0AAAABgAAAAEAAAAUAQAAASAAAAIA" + + "AAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAAAACGAQAAACAAAAEAAACgAgAAAxAA" + + "AAEAAACwAgAAAPAAAAEAAAC0AgAAABAAAAEAAADAAgAA"); } 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/timeout_dumper/Android.bp b/tools/timeout_dumper/Android.bp index dfd54421fd..bb813d4bbb 100644 --- a/tools/timeout_dumper/Android.bp +++ b/tools/timeout_dumper/Android.bp @@ -22,6 +22,11 @@ art_cc_binary { darwin: { enabled: false, }, + linux_bionic: { + sanitize: { + address: false, + }, + }, }, device_supported: false, |