diff options
Diffstat (limited to 'runtime/oat_file_assistant.cc')
-rw-r--r-- | runtime/oat_file_assistant.cc | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index e262ff7150..f7c74cc23b 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -36,6 +36,7 @@ #include "exec_utils.h" #include "gc/heap.h" #include "gc/space/image_space.h" +#include "hidden_api.h" #include "image.h" #include "oat.h" #include "runtime.h" @@ -181,6 +182,30 @@ bool OatFileAssistant::IsInBootClassPath() { return false; } +bool OatFileAssistant::Lock(std::string* error_msg) { + CHECK(error_msg != nullptr); + CHECK(flock_.get() == nullptr) << "OatFileAssistant::Lock already acquired"; + + // Note the lock will only succeed for secondary dex files and in test + // environment. + // + // The lock *will fail* for all primary apks in a production environment. + // The app does not have permissions to create locks next to its dex location + // (be it system, data or vendor parition). We also cannot use the odex or + // oat location for the same reasoning. + // + // This is best effort and if it fails it's unlikely that we will be able + // to generate oat files anyway. + std::string lock_file_name = dex_location_ + "." + GetInstructionSetString(isa_) + ".flock"; + + flock_ = LockedFile::Open(lock_file_name.c_str(), error_msg); + if (flock_.get() == nullptr) { + unlink(lock_file_name.c_str()); + return false; + } + return true; +} + int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed, bool downgrade, @@ -196,10 +221,72 @@ int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, return -dexopt_needed; } +// Figure out the currently specified compile filter option in the runtime. +// Returns true on success, false if the compiler filter is invalid, in which +// case error_msg describes the problem. +static bool GetRuntimeCompilerFilterOption(CompilerFilter::Filter* filter, + std::string* error_msg) { + CHECK(filter != nullptr); + CHECK(error_msg != nullptr); + + *filter = OatFileAssistant::kDefaultCompilerFilterForDexLoading; + for (StringPiece option : Runtime::Current()->GetCompilerOptions()) { + if (option.starts_with("--compiler-filter=")) { + const char* compiler_filter_string = option.substr(strlen("--compiler-filter=")).data(); + if (!CompilerFilter::ParseCompilerFilter(compiler_filter_string, filter)) { + *error_msg = std::string("Unknown --compiler-filter value: ") + + std::string(compiler_filter_string); + return false; + } + } + } + return true; +} + bool OatFileAssistant::IsUpToDate() { return GetBestInfo().Status() == kOatUpToDate; } +OatFileAssistant::ResultOfAttemptToUpdate +OatFileAssistant::MakeUpToDate(bool profile_changed, + ClassLoaderContext* class_loader_context, + std::string* error_msg) { + // The method doesn't use zip_fd_ and directly opens dex files at dex_locations_. + CHECK_EQ(-1, zip_fd_) << "MakeUpToDate should not be called with zip_fd"; + + CompilerFilter::Filter target; + if (!GetRuntimeCompilerFilterOption(&target, error_msg)) { + return kUpdateNotAttempted; + } + + OatFileInfo& info = GetBestInfo(); + // TODO(calin, jeffhao): the context should really be passed to GetDexOptNeeded: b/62269291. + // This is actually not trivial in the current logic as it will interact with the collision + // check: + // - currently, if the context does not match but we have no collisions we still accept the + // oat file. + // - if GetDexOptNeeded would return kDex2OatFromScratch for a context mismatch and we make + // the oat code up to date the collision check becomes useless. + // - however, MakeUpToDate will not always succeed (e.g. for primary apks, or for dex files + // loaded in other processes). So it boils down to how far do we want to complicate + // the logic in order to enable the use of oat files. Maybe its time to try simplify it. + switch (info.GetDexOptNeeded( + target, profile_changed, /*downgrade*/ false, class_loader_context)) { + case kNoDexOptNeeded: + return kUpdateSucceeded; + + // TODO: For now, don't bother with all the different ways we can call + // dex2oat to generate the oat file. Always generate the oat file as if it + // were kDex2OatFromScratch. + case kDex2OatFromScratch: + case kDex2OatForBootImage: + case kDex2OatForRelocation: + case kDex2OatForFilter: + return GenerateOatFileNoChecks(info, target, class_loader_context, error_msg); + } + UNREACHABLE(); +} + std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() { return GetBestInfo().ReleaseFileForUse(); } @@ -528,6 +615,243 @@ static bool DexLocationToOdexNames(const std::string& location, return true; } +// Prepare a subcomponent of the odex directory. +// (i.e. create and set the expected permissions on the path `dir`). +static bool PrepareDirectory(const std::string& dir, std::string* error_msg) { + struct stat dir_stat; + if (TEMP_FAILURE_RETRY(stat(dir.c_str(), &dir_stat)) == 0) { + // The directory exists. Check if it is indeed a directory. + if (!S_ISDIR(dir_stat.st_mode)) { + *error_msg = dir + " is not a dir"; + return false; + } else { + // The dir is already on disk. + return true; + } + } + + // Failed to stat. We need to create the directory. + if (errno != ENOENT) { + *error_msg = "Could not stat isa dir " + dir + ":" + strerror(errno); + return false; + } + + mode_t mode = S_IRWXU | S_IXGRP | S_IXOTH; + if (mkdir(dir.c_str(), mode) != 0) { + *error_msg = "Could not create dir " + dir + ":" + strerror(errno); + return false; + } + if (chmod(dir.c_str(), mode) != 0) { + *error_msg = "Could not create the oat dir " + dir + ":" + strerror(errno); + return false; + } + return true; +} + +// Prepares the odex directory for the given dex location. +static bool PrepareOdexDirectories(const std::string& dex_location, + const std::string& expected_odex_location, + InstructionSet isa, + std::string* error_msg) { + std::string actual_odex_location; + std::string oat_dir; + std::string isa_dir; + if (!DexLocationToOdexNames( + dex_location, isa, &actual_odex_location, &oat_dir, &isa_dir, error_msg)) { + return false; + } + DCHECK_EQ(expected_odex_location, actual_odex_location); + + if (!PrepareDirectory(oat_dir, error_msg)) { + return false; + } + if (!PrepareDirectory(isa_dir, error_msg)) { + return false; + } + return true; +} + +class Dex2oatFileWrapper { + public: + explicit Dex2oatFileWrapper(File* file) + : file_(file), + unlink_file_at_destruction_(true) { + } + + ~Dex2oatFileWrapper() { + if (unlink_file_at_destruction_ && (file_ != nullptr)) { + file_->Erase(/*unlink*/ true); + } + } + + File* GetFile() { return file_.get(); } + + void DisableUnlinkAtDestruction() { + unlink_file_at_destruction_ = false; + } + + private: + std::unique_ptr<File> file_; + bool unlink_file_at_destruction_; +}; + +OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks( + OatFileAssistant::OatFileInfo& info, + CompilerFilter::Filter filter, + const ClassLoaderContext* class_loader_context, + std::string* error_msg) { + CHECK(error_msg != nullptr); + + Runtime* runtime = Runtime::Current(); + if (!runtime->IsDex2OatEnabled()) { + *error_msg = "Generation of oat file for dex location " + dex_location_ + + " not attempted because dex2oat is disabled."; + return kUpdateNotAttempted; + } + + if (info.Filename() == nullptr) { + *error_msg = "Generation of oat file for dex location " + dex_location_ + + " not attempted because the oat file name could not be determined."; + return kUpdateNotAttempted; + } + const std::string& oat_file_name = *info.Filename(); + const std::string& vdex_file_name = GetVdexFilename(oat_file_name); + + // dex2oat ignores missing dex files and doesn't report an error. + // Check explicitly here so we can detect the error properly. + // TODO: Why does dex2oat behave that way? + struct stat dex_path_stat; + if (TEMP_FAILURE_RETRY(stat(dex_location_.c_str(), &dex_path_stat)) != 0) { + *error_msg = "Could not access dex location " + dex_location_ + ":" + strerror(errno); + return kUpdateNotAttempted; + } + + // If this is the odex location, we need to create the odex file layout (../oat/isa/..) + if (!info.IsOatLocation()) { + if (!PrepareOdexDirectories(dex_location_, oat_file_name, isa_, error_msg)) { + return kUpdateNotAttempted; + } + } + + // Set the permissions for the oat and the vdex files. + // The user always gets read and write while the group and others propagate + // the reading access of the original dex file. + mode_t file_mode = S_IRUSR | S_IWUSR | + (dex_path_stat.st_mode & S_IRGRP) | + (dex_path_stat.st_mode & S_IROTH); + + Dex2oatFileWrapper vdex_file_wrapper(OS::CreateEmptyFile(vdex_file_name.c_str())); + File* vdex_file = vdex_file_wrapper.GetFile(); + if (vdex_file == nullptr) { + *error_msg = "Generation of oat file " + oat_file_name + + " not attempted because the vdex file " + vdex_file_name + + " could not be opened."; + return kUpdateNotAttempted; + } + + if (fchmod(vdex_file->Fd(), file_mode) != 0) { + *error_msg = "Generation of oat file " + oat_file_name + + " not attempted because the vdex file " + vdex_file_name + + " could not be made world readable."; + return kUpdateNotAttempted; + } + + Dex2oatFileWrapper oat_file_wrapper(OS::CreateEmptyFile(oat_file_name.c_str())); + File* oat_file = oat_file_wrapper.GetFile(); + if (oat_file == nullptr) { + *error_msg = "Generation of oat file " + oat_file_name + + " not attempted because the oat file could not be created."; + return kUpdateNotAttempted; + } + + if (fchmod(oat_file->Fd(), file_mode) != 0) { + *error_msg = "Generation of oat file " + oat_file_name + + " not attempted because the oat file could not be made world readable."; + return kUpdateNotAttempted; + } + + std::vector<std::string> args; + args.push_back("--dex-file=" + dex_location_); + args.push_back("--output-vdex-fd=" + std::to_string(vdex_file->Fd())); + args.push_back("--oat-fd=" + std::to_string(oat_file->Fd())); + args.push_back("--oat-location=" + oat_file_name); + args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); + const std::string dex2oat_context = class_loader_context == nullptr + ? OatFile::kSpecialSharedLibrary + : class_loader_context->EncodeContextForDex2oat(/*base_dir*/ ""); + args.push_back("--class-loader-context=" + dex2oat_context); + + if (!Dex2Oat(args, error_msg)) { + return kUpdateFailed; + } + + if (vdex_file->FlushCloseOrErase() != 0) { + *error_msg = "Unable to close vdex file " + vdex_file_name; + return kUpdateFailed; + } + + if (oat_file->FlushCloseOrErase() != 0) { + *error_msg = "Unable to close oat file " + oat_file_name; + return kUpdateFailed; + } + + // Mark that the odex file has changed and we should try to reload. + info.Reset(); + // We have compiled successfully. Disable the auto-unlink. + vdex_file_wrapper.DisableUnlinkAtDestruction(); + oat_file_wrapper.DisableUnlinkAtDestruction(); + + return kUpdateSucceeded; +} + +bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args, + std::string* error_msg) { + Runtime* runtime = Runtime::Current(); + std::string image_location = ImageLocation(); + if (image_location.empty()) { + *error_msg = "No image location found for Dex2Oat."; + return false; + } + + std::vector<std::string> argv; + argv.push_back(runtime->GetCompilerExecutable()); + if (runtime->IsJavaDebuggable()) { + argv.push_back("--debuggable"); + } + runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); + + if (!runtime->IsVerificationEnabled()) { + argv.push_back("--compiler-filter=verify-none"); + } + + if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) { + argv.push_back("--runtime-arg"); + argv.push_back("-Xhidden-api-checks"); + } + + if (runtime->MustRelocateIfPossible()) { + argv.push_back("--runtime-arg"); + argv.push_back("-Xrelocate"); + } else { + argv.push_back("--runtime-arg"); + argv.push_back("-Xnorelocate"); + } + + if (!kIsTargetBuild) { + argv.push_back("--host"); + } + + argv.push_back("--boot-image=" + image_location); + + std::vector<std::string> compiler_options = runtime->GetCompilerOptions(); + argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); + + argv.insert(argv.end(), args.begin(), args.end()); + + std::string command_line(android::base::Join(argv, ' ')); + return Exec(argv, error_msg); +} + bool OatFileAssistant::DexLocationToOdexFilename(const std::string& location, InstructionSet isa, std::string* odex_filename, @@ -561,6 +885,16 @@ bool OatFileAssistant::DexLocationToOatFilename(const std::string& location, return GetDalvikCacheFilename(location.c_str(), cache_dir.c_str(), oat_filename, error_msg); } +std::string OatFileAssistant::ImageLocation() { + Runtime* runtime = Runtime::Current(); + const std::vector<gc::space::ImageSpace*>& image_spaces = + runtime->GetHeap()->GetBootImageSpaces(); + if (image_spaces.empty()) { + return ""; + } + return image_spaces[0]->GetImageLocation(); +} + const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() { if (!required_dex_checksums_attempted_) { required_dex_checksums_attempted_ = true; |