diff options
| -rw-r--r-- | cmds/installd/dexopt.cpp | 165 | ||||
| -rw-r--r-- | cmds/installd/tests/installd_dexopt_test.cpp | 23 |
2 files changed, 174 insertions, 14 deletions
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index 72571cf622..20142aab3b 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -58,6 +58,7 @@ using android::base::EndsWith; using android::base::GetBoolProperty; using android::base::GetProperty; +using android::base::ReadFdToString; using android::base::ReadFully; using android::base::StringPrintf; using android::base::WriteFully; @@ -319,6 +320,7 @@ class RunDex2Oat : public ExecVHelper { bool background_job_compile, int profile_fd, const char* class_loader_context, + const std::string& class_loader_context_fds, int target_sdk_version, bool enable_hidden_api_checks, bool generate_compact_dex, @@ -423,9 +425,14 @@ class RunDex2Oat : public ExecVHelper { target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version); } std::string class_loader_context_arg; + std::string class_loader_context_fds_arg; if (class_loader_context != nullptr) { class_loader_context_arg = StringPrintf("--class-loader-context=%s", class_loader_context); + if (!class_loader_context_fds.empty()) { + class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s", + class_loader_context_fds.c_str()); + } } if (swap_fd >= 0) { @@ -518,6 +525,7 @@ class RunDex2Oat : public ExecVHelper { AddArg(profile_arg); AddArg(base_dir); AddArg(class_loader_context_arg); + AddArg(class_loader_context_fds_arg); if (generate_minidebug_info) { AddArg(kMinidebugDex2oatFlag); } @@ -1514,14 +1522,15 @@ void update_out_oat_access_times(const char* apk_path, const char* out_oat_path) class RunDexoptAnalyzer : public ExecVHelper { public: RunDexoptAnalyzer(const std::string& dex_file, - int vdex_fd, - int oat_fd, - int zip_fd, - const std::string& instruction_set, - const std::string& compiler_filter, - bool profile_was_updated, - bool downgrade, - const char* class_loader_context) { + int vdex_fd, + int oat_fd, + int zip_fd, + const std::string& instruction_set, + const std::string& compiler_filter, + bool profile_was_updated, + bool downgrade, + const char* class_loader_context, + const std::string& class_loader_context_fds) { CHECK_GE(zip_fd, 0); // We always run the analyzer in the background job. @@ -1540,6 +1549,10 @@ class RunDexoptAnalyzer : public ExecVHelper { if (class_loader_context != nullptr) { class_loader_context_arg += class_loader_context; } + std::string class_loader_context_fds_arg = "--class-loader-context-fds="; + if (!class_loader_context_fds.empty()) { + class_loader_context_fds_arg += class_loader_context_fds; + } // program name, dex file, isa, filter AddArg(dex_file_arg); @@ -1560,10 +1573,27 @@ class RunDexoptAnalyzer : public ExecVHelper { } if (class_loader_context != nullptr) { AddArg(class_loader_context_arg.c_str()); + if (!class_loader_context_fds.empty()) { + AddArg(class_loader_context_fds_arg.c_str()); + } } PrepareArgs(dexoptanalyzer_bin); } + + // Dexoptanalyzer mode which flattens the given class loader context and + // prints a list of its dex files in that flattened order. + RunDexoptAnalyzer(const char* class_loader_context) { + CHECK(class_loader_context != nullptr); + + // We always run the analyzer in the background job. + const char* dexoptanalyzer_bin = select_execution_binary( + kDexoptanalyzerPath, kDexoptanalyzerDebugPath, /*background_job_compile=*/ true); + + AddArg("--flatten-class-loader-context"); + AddArg(std::string("--class-loader-context=") + class_loader_context); + PrepareArgs(dexoptanalyzer_bin); + } }; // Prepares the oat dir for the secondary dex files. @@ -1743,6 +1773,95 @@ static bool validate_dexopt_storage_flags(int dexopt_flags, return true; } +static bool get_class_loader_context_dex_paths(const char* class_loader_context, int uid, + /* out */ std::vector<std::string>* context_dex_paths) { + if (class_loader_context == nullptr) { + return true; + } + + LOG(DEBUG) << "Getting dex paths for context " << class_loader_context; + + // Pipe to get the hash result back from our child process. + unique_fd pipe_read, pipe_write; + if (!Pipe(&pipe_read, &pipe_write)) { + PLOG(ERROR) << "Failed to create pipe"; + return false; + } + + pid_t pid = fork(); + if (pid == 0) { + // child -- drop privileges before continuing. + drop_capabilities(uid); + + // Route stdout to `pipe_write` + while ((dup2(pipe_write, STDOUT_FILENO) == -1) && (errno == EINTR)) {} + pipe_write.reset(); + pipe_read.reset(); + + RunDexoptAnalyzer run_dexopt_analyzer(class_loader_context); + run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec); + } + + /* parent */ + pipe_write.reset(); + + std::string str_dex_paths; + if (!ReadFdToString(pipe_read, &str_dex_paths)) { + PLOG(ERROR) << "Failed to read from pipe"; + return false; + } + pipe_read.reset(); + + int return_code = wait_child(pid); + if (!WIFEXITED(return_code)) { + PLOG(ERROR) << "Error waiting for child dexoptanalyzer process"; + return false; + } + + constexpr int kFlattenClassLoaderContextSuccess = 50; + return_code = WEXITSTATUS(return_code); + if (return_code != kFlattenClassLoaderContextSuccess) { + LOG(ERROR) << "Dexoptanalyzer could not flatten class loader context, code=" << return_code; + return false; + } + + if (!str_dex_paths.empty()) { + *context_dex_paths = android::base::Split(str_dex_paths, ":"); + } + return true; +} + +static int open_dex_paths(const std::vector<std::string>& dex_paths, + /* out */ std::vector<unique_fd>* zip_fds, /* out */ std::string* error_msg) { + for (const std::string& dex_path : dex_paths) { + zip_fds->emplace_back(open(dex_path.c_str(), O_RDONLY)); + if (zip_fds->back().get() < 0) { + *error_msg = StringPrintf( + "installd cannot open '%s' for input during dexopt", dex_path.c_str()); + if (errno == ENOENT) { + return kSecondaryDexDexoptAnalyzerSkippedNoFile; + } else { + return kSecondaryDexDexoptAnalyzerSkippedOpenZip; + } + } + } + return 0; +} + +static std::string join_fds(const std::vector<unique_fd>& fds) { + std::stringstream ss; + bool is_first = true; + for (const unique_fd& fd : fds) { + if (is_first) { + is_first = false; + } else { + ss << ":"; + } + ss << fd.get(); + } + return ss.str(); +} + // Processes the dex_path as a secondary dex files and return true if the path dex file should // be compiled. Returns false for errors (logged) or true if the secondary dex path was process // successfully. @@ -1754,7 +1873,7 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set, const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out, std::string* oat_dir_out, bool downgrade, const char* class_loader_context, - /* out */ std::string* error_msg) { + const std::vector<std::string>& context_dex_paths, /* out */ std::string* error_msg) { LOG(DEBUG) << "Processing secondary dex path " << dex_path; int storage_flag; if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag, error_msg)) { @@ -1794,6 +1913,13 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char } } + // Open class loader context dex files. + std::vector<unique_fd> context_zip_fds; + int open_dex_paths_rc = open_dex_paths(context_dex_paths, &context_zip_fds, error_msg); + if (open_dex_paths_rc != 0) { + _exit(open_dex_paths_rc); + } + // Prepare the oat directories. if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set)) { _exit(kSecondaryDexDexoptAnalyzerSkippedPrepareDir); @@ -1825,7 +1951,8 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char instruction_set, compiler_filter, profile_was_updated, downgrade, - class_loader_context); + class_loader_context, + join_fds(context_zip_fds)); run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec); } @@ -1913,10 +2040,16 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins // Check if we're dealing with a secondary dex file and if we need to compile it. std::string oat_dir_str; + std::vector<std::string> context_dex_paths; if (is_secondary_dex) { + if (!get_class_loader_context_dex_paths(class_loader_context, uid, &context_dex_paths)) { + *error_msg = "Failed acquiring context dex paths"; + return -1; // We had an error, logged in the process method. + } + if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid, instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str, - downgrade, class_loader_context, error_msg)) { + downgrade, class_loader_context, context_dex_paths, error_msg)) { oat_dir = oat_dir_str.c_str(); if (dexopt_needed == NO_DEXOPT_NEEDED) { return 0; // Nothing to do, report success. @@ -1928,7 +2061,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins return -1; // We had an error, logged in the process method. } } else { - // Currently these flags are only use for secondary dex files. + // Currently these flags are only used for secondary dex files. // Verify that they are not set for primary apks. CHECK((dexopt_flags & DEXOPT_STORAGE_CE) == 0); CHECK((dexopt_flags & DEXOPT_STORAGE_DE) == 0); @@ -1942,6 +2075,13 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins return -1; } + // Open class loader context dex files. + std::vector<unique_fd> context_input_fds; + if (open_dex_paths(context_dex_paths, &context_input_fds, error_msg) != 0) { + LOG(ERROR) << *error_msg; + return -1; + } + // Create the output OAT file. char out_oat_path[PKG_PATH_MAX]; Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid, @@ -2010,6 +2150,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins background_job_compile, reference_profile_fd.get(), class_loader_context, + join_fds(context_input_fds), target_sdk_version, enable_hidden_api_checks, generate_compact_dex, diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 1d4a2f0e5e..36d5a600b2 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -327,16 +327,21 @@ protected: void CompileSecondaryDex(const std::string& path, int32_t dex_storage_flag, bool should_binder_call_succeed, bool should_dex_be_compiled = true, - /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1) { + /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1, + const char* class_loader_context = nullptr) { if (uid == -1) { uid = kTestAppUid; } + if (class_loader_context == nullptr) { + class_loader_context = "&"; + } std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_)); int32_t dexopt_needed = 0; // does not matter; std::unique_ptr<std::string> out_path = nullptr; // does not matter int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag; std::string compiler_filter = "speed-profile"; - std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&")); + std::unique_ptr<std::string> class_loader_context_ptr( + new std::string(class_loader_context)); std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_)); bool downgrade = false; int32_t target_sdk_version = 0; // default @@ -555,12 +560,26 @@ TEST_F(DexoptTest, DexoptSecondaryCeLink) { /*binder_ok*/ true, /*compile_ok*/ true); } +TEST_F(DexoptTest, DexoptSecondaryCeWithContext) { + LOG(INFO) << "DexoptSecondaryCeWithContext"; + std::string class_loader_context = "PCL[" + secondary_dex_ce_ + "]"; + CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE, + /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str()); +} + TEST_F(DexoptTest, DexoptSecondaryDe) { LOG(INFO) << "DexoptSecondaryDe"; CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE, /*binder_ok*/ true, /*compile_ok*/ true); } +TEST_F(DexoptTest, DexoptSecondaryDeWithContext) { + LOG(INFO) << "DexoptSecondaryDeWithContext"; + std::string class_loader_context = "PCL[" + secondary_dex_de_ + "]"; + CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE, + /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str()); +} + TEST_F(DexoptTest, DexoptSecondaryDoesNotExist) { LOG(INFO) << "DexoptSecondaryDoesNotExist"; // If the file validates but does not exist we do not treat it as an error. |