diff options
| -rw-r--r-- | dex2oat/dex2oat.cc | 10 | ||||
| -rw-r--r-- | dex2oat/dex2oat_options.cc | 6 | ||||
| -rw-r--r-- | dex2oat/dex2oat_options.def | 1 | ||||
| -rw-r--r-- | runtime/Android.bp | 8 | ||||
| -rw-r--r-- | runtime/oat.h | 5 | ||||
| -rw-r--r-- | runtime/oat_file_assistant.cc | 17 | ||||
| -rw-r--r-- | runtime/oat_file_assistant_test.cc | 48 | ||||
| -rw-r--r-- | runtime/runtime.cc | 51 | ||||
| -rw-r--r-- | runtime/runtime.h | 16 |
9 files changed, 158 insertions, 4 deletions
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 02afa7c23d..9de5ac5704 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1073,6 +1073,7 @@ class Dex2Oat final { AssignTrueIfExists(args, M::CrashOnLinkageViolation, &crash_on_linkage_violation_); AssignTrueIfExists(args, M::ForceAllowOjInlines, &force_allow_oj_inlines_); AssignIfExists(args, M::PublicSdk, &public_sdk_); + AssignIfExists(args, M::ApexVersions, &apex_versions_argument_); AssignIfExists(args, M::Backend, &compiler_kind_); parser_options->requested_specific_compiler = args.Exists(M::Backend); @@ -1601,6 +1602,11 @@ class Dex2Oat final { key_value_store_->Put( OatHeader::kBootClassPathChecksumsKey, gc::space::ImageSpace::GetBootClassPathChecksums(image_spaces, bcp_dex_files)); + + std::string versions = apex_versions_argument_.empty() + ? runtime->GetApexVersions() + : apex_versions_argument_; + key_value_store_->Put(OatHeader::kApexVersionsKey, versions); } // Open dex files for class path. @@ -2952,6 +2958,10 @@ class Dex2Oat final { // The classpath that determines if a given symbol should be resolved at compile time or not. std::string public_sdk_; + // The apex versions of jars in the boot classpath. Set through command line + // argument. + std::string apex_versions_argument_; + DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat); }; diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc index 26d0ff556b..280db7abe2 100644 --- a/dex2oat/dex2oat_options.cc +++ b/dex2oat/dex2oat_options.cc @@ -416,7 +416,11 @@ Parser CreateDex2oatArgumentParser() { .IntoKey(M::CompileIndividually) .Define("--public-sdk=_") .WithType<std::string>() - .IntoKey(M::PublicSdk);; + .IntoKey(M::PublicSdk) + .Define("--apex-versions=_") + .WithType<std::string>() + .WithHelp("Versions of apexes in the boot classpath, separated by '/'") + .IntoKey(M::ApexVersions); AddCompilerOptionsArgumentParserOptions<Dex2oatArgumentMap>(*parser_builder); diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def index 6bd3d0517e..71a769ab22 100644 --- a/dex2oat/dex2oat_options.def +++ b/dex2oat/dex2oat_options.def @@ -97,5 +97,6 @@ DEX2OAT_OPTIONS_KEY (Unit, CrashOnLinkageViolation) DEX2OAT_OPTIONS_KEY (Unit, CompileIndividually) DEX2OAT_OPTIONS_KEY (std::string, PublicSdk) DEX2OAT_OPTIONS_KEY (Unit, ForceAllowOjInlines) +DEX2OAT_OPTIONS_KEY (std::string, ApexVersions) #undef DEX2OAT_OPTIONS_KEY diff --git a/runtime/Android.bp b/runtime/Android.bp index e8ebc20b9f..f57f59fe75 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -384,6 +384,10 @@ libart_cc_defaults { ], static_libs: [ "libstatslog_art", + "libxml2", + ], + generated_sources: [ + "apex-info-list", ], }, android_arm: { @@ -413,7 +417,9 @@ libart_cc_defaults { }, }, cflags: ["-DBUILDING_LIBART=1"], - generated_sources: ["art_operator_srcs"], + generated_sources: [ + "art_operator_srcs" + ], // asm_support_gen.h (used by asm_support.h) is generated with cpp-define-generator generated_headers: ["cpp-define-generator-asm-support"], // export our headers so the libart-gtest targets can use it as well. diff --git a/runtime/oat.h b/runtime/oat.h index 8205935682..ab45b84888 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } }; - // Last oat version changed reason: Record number of methods in OatClass. - static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '9', '4', '\0' } }; + // Last oat version changed reason: Apex versions in key/value store. + static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '9', '5', '\0' } }; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDebuggableKey = "debuggable"; @@ -42,6 +42,7 @@ class PACKED(4) OatHeader { static constexpr const char* kClassPathKey = "classpath"; static constexpr const char* kBootClassPathKey = "bootclasspath"; static constexpr const char* kBootClassPathChecksumsKey = "bootclasspath-checksums"; + static constexpr const char* kApexVersionsKey = "apex-versions"; static constexpr const char* kConcurrentCopying = "concurrent-copying"; static constexpr const char* kCompilationReasonKey = "compilation-reason"; static constexpr const char* kRequiresImage = "requires-image"; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 0bccc828bb..c74f19bab5 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -400,6 +400,19 @@ bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* err return true; } +static bool ValidateApexVersions(const OatFile& oat_file) { + const char* oat_apex_versions = + oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kApexVersionsKey); + if (oat_apex_versions == nullptr) { + return false; + } + // Some dex files get compiled with a subset of the boot classpath (for + // example currently system server is compiled with DEX2OAT_BOOTCLASSPATH). + // For such cases, the oat apex versions will be a prefix of the runtime apex + // versions. + return android::base::StartsWith(Runtime::Current()->GetApexVersions(), oat_apex_versions); +} + OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) { // Verify the ART_USE_READ_BARRIER state. // TODO: Don't fully reject files due to read barrier state. If they contain @@ -430,6 +443,10 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& VLOG(oat) << "Oat image checksum does not match image checksum."; return kOatBootImageOutOfDate; } + if (!ValidateApexVersions(file)) { + VLOG(oat) << "Apex versions do not match."; + return kOatBootImageOutOfDate; + } } else { VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter; } diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 5631e12a90..376088568a 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -1654,6 +1654,54 @@ TEST_F(OatFileAssistantTest, LoadOatNoArt) { EXPECT_FALSE(oat_file->IsExecutable()); } +TEST_F(OatFileAssistantTest, GetDexOptNeededWithApexVersions) { + std::string dex_location = GetScratchDir() + "/TestDex.jar"; + std::string odex_location = GetOdexDir() + "/TestDex.odex"; + Copy(GetDexSrc1(), dex_location); + + // Test that using the current's runtime apex versions works. + { + std::string error_msg; + std::vector<std::string> args; + args.push_back("--dex-file=" + dex_location); + args.push_back("--oat-file=" + odex_location); + args.push_back("--apex-versions=" + Runtime::Current()->GetApexVersions()); + ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; + + OatFileAssistant oat_file_assistant( + dex_location.c_str(), kRuntimeISA, default_context_.get(), false); + EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); + } + + // Test that a subset of apex versions works. + { + std::string error_msg; + std::vector<std::string> args; + args.push_back("--dex-file=" + dex_location); + args.push_back("--oat-file=" + odex_location); + args.push_back("--apex-versions=" + Runtime::Current()->GetApexVersions().substr(0, 1)); + ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; + + OatFileAssistant oat_file_assistant( + dex_location.c_str(), kRuntimeISA, default_context_.get(), false); + EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); + } + + // Test that different apex versions require to recompile. + { + std::string error_msg; + std::vector<std::string> args; + args.push_back("--dex-file=" + dex_location); + args.push_back("--oat-file=" + odex_location); + args.push_back("--apex-versions=/1/2/3/4"); + ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg; + + OatFileAssistant oat_file_assistant( + dex_location.c_str(), kRuntimeISA, default_context_.get(), false); + EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage, oat_file_assistant.OdexFileStatus()); + } +} + // TODO: More Tests: // * Test class linker falls back to unquickened dex for DexNoOat // * Test class linker falls back to unquickened dex for MultiDexNoOat diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 10999dca23..f111ff8acc 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -173,6 +173,9 @@ #ifdef ART_TARGET_ANDROID #include <android/set_abort_message.h> +#include "com_android_apex.h" +namespace apex = com::android::apex; + #endif // Static asserts to check the values of generated assembly-support macros. @@ -1235,6 +1238,52 @@ static inline void CreatePreAllocatedException(Thread* self, detailMessageField->SetObject</* kTransactionActive= */ false>(exception->Read(), message); } +void Runtime::InitializeApexVersions() { + std::vector<std::string_view> bcp_apexes; + for (const std::string& jar : Runtime::Current()->GetBootClassPathLocations()) { + if (LocationIsOnApex(jar)) { + size_t start = jar.find('/', 1); + if (start == std::string::npos) { + continue; + } + size_t end = jar.find('/', start + 1); + if (end == std::string::npos) { + continue; + } + std::string apex = jar.substr(start + 1, end - start - 1); + bcp_apexes.push_back(apex); + } + } + std::string result; + static const char* kApexFileName = "/apex/apex-info-list.xml"; + // When running on host or chroot, we just encode empty markers. + if (!kIsTargetBuild || !OS::FileExists(kApexFileName)) { + for (uint32_t i = 0; i < bcp_apexes.size(); ++i) { + result += '/'; + } + } else { +#ifdef ART_TARGET_ANDROID + auto info_list = apex::readApexInfoList(kApexFileName); + CHECK(info_list.has_value()); + std::map<std::string_view, const apex::ApexInfo*> apex_infos; + for (const apex::ApexInfo& info : info_list->getApexInfo()) { + if (info.getIsActive()) { + apex_infos.emplace(info.getModuleName(), &info); + } + } + for (const std::string_view& str : bcp_apexes) { + auto info = apex_infos.find(str); + if (info == apex_infos.end() || info->second->getIsFactory()) { + result += '/'; + } else { + android::base::StringAppendF(&result, "/%" PRIu64, info->second->getVersionCode()); + } + } +#endif + } + apex_versions_ = result; +} + bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc. // Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc. @@ -1686,6 +1735,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { } } + InitializeApexVersions(); + CHECK(class_linker_ != nullptr); verifier::ClassVerifier::Init(class_linker_); diff --git a/runtime/runtime.h b/runtime/runtime.h index e8fde6825a..8513cd5a7e 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -995,6 +995,10 @@ class Runtime { const uint8_t* map_end, const std::string& file_name); + const std::string& GetApexVersions() const { + return apex_versions_; + } + private: static void InitPlatformSignalHandlers(); @@ -1035,6 +1039,10 @@ class Runtime { ThreadPool* AcquireThreadPool() REQUIRES(!Locks::runtime_thread_pool_lock_); void ReleaseThreadPool() REQUIRES(!Locks::runtime_thread_pool_lock_); + // Parses /apex/apex-info-list.xml to initialize a string containing versions + // of boot classpath jars and encoded into .oat files. + void InitializeApexVersions(); + // A pointer to the active runtime or null. static Runtime* instance_; @@ -1367,6 +1375,14 @@ class Runtime { metrics::ArtMetrics metrics_; std::unique_ptr<metrics::MetricsReporter> metrics_reporter_; + // Apex versions of boot classpath jars concatenated in a string. The format + // is of the type: + // '/apex1_version/apex2_version//' + // + // When the apex is the factory version, we don't encode it (for example in + // the third entry in the example above). + std::string apex_versions_; + // Note: See comments on GetFaultMessage. friend std::string GetFaultMessageForAbortLogging(); friend class Dex2oatImageTest; |