summaryrefslogtreecommitdiff
path: root/runtime/oat_file_assistant.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/oat_file_assistant.cc')
-rw-r--r--runtime/oat_file_assistant.cc334
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;